
PHP ์จ๋ผ์ธ ๊ฐ์
>PHP - ์ต์๊ธ
๐ PHP ์ต์๊ธ - 4์ฃผ์ฐจ: CI/CD (์ง์์ ํตํฉ/์ง์์ ๋ฐฐํฌ) ํ์ดํ๋ผ์ธ ๊ตฌ์ถ- 03 PHP ํ๋ก์ ํธ๋ฅผ ์ํ CI/CD ํ์ดํ๋ผ์ธ ์ค๊ณ ๋ฐ ๊ตฌํ

์ฃผ์ ํ๋ก๊ทธ๋จ ์คํ
- ํ์ : 10.0
- ๋ผ์ด์ ์ค: free
- ์ด์์ฒด์ :
- ํ์ผ ํฌ๊ธฐ: 0
ํผ๋๋ฐฑ ๋ฐ ๋ค์ด๋ก๋
- ์ฌ์ฉ์ ํ์ : 10.0
- ๋ค์ด๋ก๋ ์: 1
- ์กฐํ์: 5
์ ์กฐ์ฌ ๋ฐ ๋ฑ๋ก ์ ๋ณด
- ์ ์์ฌ: LUZENSOFT
- ๋ฑ๋ก์ผ: 2025-10-15 14:14:23
- ์ค๋ช
#PHP์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ CI/CD ํ์ดํ๋ผ์ธ ์ค๊ณ ๊ฐ์
์ง๋ ์๊ฐ์๋ #CI/CD ์ ๊ฐ๋ ๊ณผ ์ฃผ์ ๋๊ตฌ(GitLab CI/CD, GitHub Actions)์ ๋ํด ํ์ตํ์ต๋๋ค. ์ด์ ์ด๋ก ์ ๋ฐํ์ผ๋ก ์ค์ #PHP์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฉ ๊ฐ๋ฅํ CI/CD ํ์ดํ๋ผ์ธ์ ์ค๊ณํ๊ณ ๊ตฌํํ๋ ๊ตฌ์ฒด์ ์ธ ๋ฐฉ๋ฒ์ ์์๋ณด๊ฒ ์ต๋๋ค. ์ด ๊ณผ์ ์ ๊ฐ๋ฐ๋ ์ฝ๋๋ฅผ ์์ ์ ์ผ๋ก ๋น๋ํ๊ณ , ํ ์คํธํ๋ฉฐ, ์ต์ข ์ ์ผ๋ก ์ด์ ํ๊ฒฝ์ ๋ฐฐํฌํ๋ ์ผ๋ จ์ ์๋ํ ๋จ๊ณ๋ฅผ ํฌํจํฉ๋๋ค.
์ฐ๋ฆฌ๊ฐ ๋ชฉํ๋ก ํ๋ #CI/CDํ์ดํ๋ผ์ธ ์ ๋ค์๊ณผ ๊ฐ์ ์ฃผ์ ๋จ๊ณ๋ฅผ ๊ฑฐ์น๊ฒ ๋ฉ๋๋ค.
์ฝ๋ ์ปค๋ฐ(Code Commit): ๊ฐ๋ฐ์๊ฐ Git ์ ์ฅ์์ ์ฝ๋๋ฅผ ํธ์ํฉ๋๋ค
๋น๋(Build): #Composer ์์กด์ฑ์ ์ค์นํ๊ณ , #Docker์ด๋ฏธ์ง ๋ฅผ ๋น๋ํฉ๋๋ค
ํ ์คํธ(Test): ๋จ์ ํ ์คํธ, ์ ์ ๋ถ์(Linter), ์ฝ๋ ์คํ์ผ ๊ฒ์ฌ ๋ฑ์ ์ํํฉ๋๋ค
๋ฐฐํฌ(Deploy): ํ ์คํธ๋ฅผ ํต๊ณผํ #Docker์ด๋ฏธ์ง ๋ฅผ ์ปจํ ์ด๋ ๋ ์ง์คํธ๋ฆฌ์ ํธ์ํ๊ณ , ์ด์ ์๋ฒ์ ๋ฐฐํฌํฉ๋๋ค
์ด๋ฌํ ํ์ดํ๋ผ์ธ์ ํตํด #PHP๊ฐ๋ฐ ํ์ ๋ ๋น ๋ฅด๊ณ ์์ ์ ์ธ ์ํํธ์จ์ด ๋ฆด๋ฆฌ์ค๋ฅผ ๋ฌ์ฑํ ์ ์์ต๋๋ค.
1. #CI/CD ํ์ดํ๋ผ์ธ ์ค๊ณ ๋จ๊ณ
1.1 Git ์ ์ฅ์ ๋ฐ ๋ธ๋์นญ ์ ๋ต
๊ฐ์ฅ ๋จผ์ #CI/CD ์ ํธ๋ฆฌ๊ฑฐ๊ฐ ๋ Git ์ ์ฅ์์ ํจ๊ณผ์ ์ธ ๋ธ๋์นญ ์ ๋ต์ ์๋ฆฝํด์ผ ํฉ๋๋ค.
์ค์ ์ ์ฅ์: GitHub, GitLab, Bitbucket ๋ฑ
๋ธ๋์นญ ์ ๋ต: Git Flow, GitHub Flow ๋ฑ ํ๋ก์ ํธ ๊ท๋ชจ์ ํ์ ํน์ฑ์ ๋ง๋ ์ ๋ต์ ์ ํํฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก Pull Request(Merge Request)๊ฐ ์์ฑ๋๊ฑฐ๋
main
๋ธ๋์น์ ์ฝ๋๊ฐ ๋ณํฉ๋ ๋ #CI/CD ๊ฐ ํธ๋ฆฌ๊ฑฐ๋๋๋ก ์ค์ ํฉ๋๋ค.
1.2 #Docker ๊ธฐ๋ฐ ํ๊ฒฝ ๊ตฌ์ถ
#Docker ๋ #CI/CD ํ์ดํ๋ผ์ธ์ ํต์ฌ์ ๋๋ค. ๋น๋์ ๋ฐฐํฌ์ ์ผ๊ด์ฑ์ ๋ณด์ฅํฉ๋๋ค.
#Dockerfile: #PHP์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ์ต์ ํ๋ #Dockerfile ์ ์์ฑํ์ฌ, ํ์ํ #PHP๋ฒ์ , ํ์ฅ, ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ์์กด์ฑ ๋ฑ์ ์ ์ํฉ๋๋ค.
#DockerCompose: ๊ฐ๋ฐ ํ๊ฒฝ์ ์ํด ์น ์๋ฒ(Nginx/Apache), #PHP-FPM, ๋ฐ์ดํฐ๋ฒ ์ด์ค(MySQL) ๋ฑ์ #DockerCompose ๋ก ์ ์ํ์ฌ ์ผ๊ด๋ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ CI/CD ๊ณผ์ ์์๋ ํ ์คํธ ํ๊ฒฝ์ ๊ตฌ์ถํ๋ ๋ฐ ํ์ฉ๋ ์ ์์ต๋๋ค.
1.3 ํ ์คํธ ์ ๋ต ์๋ฆฝ
์๋ํ๋ ํ ์คํธ๋ #CI/CD ์ ์ ๋ขฐ์ฑ์ ํ๋ณดํ๋ ๊ฐ์ฅ ์ค์ํ ์์์ ๋๋ค.
๋จ์ ํ ์คํธ(Unit Test): PHPUnit ๋ฑ์ ์ฌ์ฉํ์ฌ ๊ฐ ํจ์๋ ํด๋์ค์ ๋์์ ๊ฐ๋ณ์ ์ผ๋ก ๊ฒ์ฆํฉ๋๋ค.
์ ์ ๋ถ์(Static Analysis): #PHPStan, #Psalm, PHP CS Fixer ๋ฑ์ ์ฌ์ฉํ์ฌ ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ ์ฝ๋ ์คํ์ผ ์๋ฐ์ ๋น๋ ๋จ๊ณ์์ ๋ฏธ๋ฆฌ ๊ฐ์งํฉ๋๋ค.
ํตํฉ ํ ์คํธ(Integration Test): ์ฌ๋ฌ ์ปดํฌ๋ํธ ๊ฐ์ ์ํธ์์ฉ์ ๊ฒ์ฆํฉ๋๋ค.
2. #CI/CD ํ์ดํ๋ผ์ธ ๊ตฌํ ์์ (GitHub Actions)
์ด์ ์์์ ์ค๊ณํ ๋ด์ฉ์ ๋ฐํ์ผ๋ก GitHub Actions๋ฅผ ์ฌ์ฉํ์ฌ #PHP์ ํ๋ฆฌ์ผ์ด์
์ ์ํ #CI/CDํ์ดํ๋ผ์ธ ์ ๊ตฌํํด ๋ณด๊ฒ ์ต๋๋ค. ๋ค์์ .github/workflows/main.yml
ํ์ผ์ ์์์
๋๋ค.
YAML
name: PHP CI/CD Pipeline
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build_test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3' # ์ฌ์ฉํ PHP ๋ฒ์
extensions: pdo_mysql, zip, gd # ํ์ํ PHP ํ์ฅ
ini-values: post_max_size=256M, upload_max_filesize=256M
tools: composer # Composer ์ค์น ํฌํจ
- name: Get Composer Cache Directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache Composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist
- name: Run PHPUnit tests
run: vendor/bin/phpunit --configuration phpunit.xml
- name: Run PHPStan (Static Analysis)
run: vendor/bin/phpstan analyse src --level max
- name: Build Docker Image
run: docker build -t your-docker-registry/your-app:${{ github.sha }} .
- name: Push Docker Image to Registry (e.g., Docker Hub)
if: github.ref == 'refs/heads/main' # main ๋ธ๋์น์ ํธ์๋ ๋๋ง ์คํ
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
docker push your-docker-registry/your-app:${{ github.sha }}
deploy:
needs: build_test # build_test Job์ด ์ฑ๊ณตํด์ผ ์คํ
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # main ๋ธ๋์น์ ํธ์๋ ๋๋ง ์คํ
steps:
- name: Deploy to Production Server via SSH
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
# ์๋ฒ์ ์ ์ํ์ฌ ์ต์ Docker ์ด๋ฏธ์ง pull ๋ฐ ์ปจํ
์ด๋ ์ฌ์์
docker pull your-docker-registry/your-app:${{ github.sha }}
docker stop your-app-container || true
docker rm your-app-container || true
docker run -d --name your-app-container -p 80:80 your-docker-registry/your-app:${{ github.sha }}
# ํ์ํ ๊ฒฝ์ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์
๋ฑ ์คํ
# docker exec your-app-container php artisan migrate --force
๐ก GitHub Actions Secret ๊ด๋ฆฌ
์ ์์์์ secrets.DOCKER_PASSWORD
, secrets.DOCKER_USERNAME
, secrets.SSH_HOST
๋ฑ์ GitHub ์ ์ฅ์์ Settings -> Secrets -> Actions์์ ์ค์ ํด์ผ ํ๋ ๋ฏผ๊ฐํ ์ ๋ณด๋ค์
๋๋ค. ์ ๋๋ก ์ฝ๋์ ์ง์ ๋
ธ์ถํด์๋ ์ ๋ฉ๋๋ค.
๊ฒฐ๋ก
์ด๋ก์จ ์ฐ๋ฆฌ๋ #PHP์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ #CI/CDํ์ดํ๋ผ์ธ ์ ์ค๊ณ๋ถํฐ GitHub Actions๋ฅผ ํ์ฉํ ๊ตฌํ๊น์ง ์ดํด๋ณด์์ต๋๋ค. ์ด ํ์ดํ๋ผ์ธ์ ์ฝ๋๊ฐ ํธ์๋๋ ์๊ฐ๋ถํฐ ๋น๋, ํ ์คํธ, ๋ฐฐํฌ์ ์ด๋ฅด๋ ๋ชจ๋ ๊ณผ์ ์ ์๋ํํ์ฌ ๊ฐ๋ฐ ์์ฐ์ฑ์ ๊ทน๋ํํ๊ณ ์๋น์ค์ ์์ ์ฑ์ ๋ณด์ฅํฉ๋๋ค. ์ด์ ๊ฐ๋ฐ์๋ ๋์ฑ ๋น ๋ฅด๊ฒ ํ์ ํ๊ณ , ์ฌ์ฉ์์๊ฒ ๋ ๋์ ๊ฒฝํ์ ์ ๊ณตํ๋ ๋ฐ ์ง์คํ ์ ์์ต๋๋ค.
๋ฌด๋ฃ์ฒดํ, ๋๊น์์ด ๋น ๋ฅธVPN VPN, ๊ตญ๋ด ๋ค๋IP, ๋ชจ๋ฐ์ผ๊ฐ๋ฅ, ๋ณธ์ฌ ํ์ง๊ด๋ฆฌ ์ ๋ขฐํ ์ ์๋ ๋ณด์์ฑ ๋์ VPN ์๋น์ค
https://xn--299ao67b9qbmsf04c.net/