GitHub Actions를 사용하여 Laravel 앱을 DigitalOcean에 자동 배포하는 방법
요약
본 가이드는 GitHub Actions를 활용하여 Laravel 애플리케이션을 DigitalOcean Droplet에 자동으로 배포하는 CI/CD 워크플로우 설정 방법을 설명합니다. main 브랜치에 코드를 푸시할 때마다 SSH 접속을 통해 최신 코드를 가져오고, 의존성 설치, 에셋 빌드, 데이터베이스 마이그레이션 및 Laravel 캐싱 등 일련의 자동화된 배포 프로세스를 실행하여 수동 배포의 번거로움을 줄입니다. 핵심적으로 `package.json`에 배포 스크립트를 정의하고, GitHub Actions 워크플로우 파일(`.github/workflows/deploy.yml`)을 작성하여 이 스크립트가 서버에서 순차적으로 실행되도록 자동화하는 과정을 다룹니다.
핵심 포인트
- GitHub Actions를 사용하여 Laravel 앱의 CI/CD 파이프라인 구축 가능
- 배포 프로세스는 SSH 접속, 코드 Pull, 의존성 설치(Composer), 에셋 빌드(npm), 마이그레이션 및 캐싱 순으로 자동화됨
- `package.json`에 `deploy` 스크립트를 정의하여 배포 명령어를 단일화하는 것이 효율적임
- 프로덕션 환경에서는 보안을 위해 root 대신 제한된 권한의 전용 배포 사용자를 사용하는 것을 강력히 권장함
GitHub Actions를 사용하여 Laravel 앱을 DigitalOcean에 자동 배포하는 방법
Laravel 애플리케이션을 수동으로 배포하는 것은 특히 프로덕션(Production)에 업데이트를 자주 푸시할 때 반복적인 작업이 될 수 있습니다. 매번 SSH를 통해 서버에 로그인하는 대신, GitHub Actions를 사용하여 배포 프로세스를 자동화할 수 있습니다. 이 가이드에서는 메인 브랜치(main branch)에 변경 사항을 푸시할 때마다 Laravel 앱을 DigitalOcean Droplet에 자동으로 배포하는 간단한 CI/CD 워크플로우를 설정하는 방법을 배웁니다. 이 설정을 통해 여러분의 워크플로우는 다음과 같이 구성됩니다:
main으로 푸시 → GitHub Actions 실행 → Droplet에 SSH 접속 → 최신 코드 Pull → 의존성(Dependencies) 설치 → 에셋(Assets) 빌드 → 마이그레이션(Migrations) 실행 → Laravel 최적화 → 큐 워커(Queue workers) 재시작
필요한 사항
시작하기 전에 다음 사항들이 준비되어 있는지 확인하세요:
- GitHub에 호스팅된 Laravel 애플리케이션
- Ubuntu가 실행 중인 DigitalOcean Droplet
- Droplet에서 이미 설정되어 작동 중인 Laravel 앱
- Laravel 앱을 위해 구성된 Nginx 또는 Apache
- 서버에 설치된 PHP, Composer, Node.js 및 npm
- .env 파일에 이미 구성된 데이터베이스 연결
- Droplet에 대한 SSH 접속 권한
- GitHub 리포지토리(Repository) 설정에 대한 기본 지식
보안을 위해 root 사용자 대신 전용 배포(Deploy) 사용자를 사용하는 것을 권장합니다. 하지만 이 가이드에서는 설정을 간단하게 유지하기 위해 예제에서 root를 사용하겠습니다. 프로덕션 애플리케이션을 배포하는 경우, 배포를 위한 별도의 제한된 권한을 가진 사용자를 생성하는 것을 고려하십시오.
1단계: Laravel 배포 스크립트 생성
먼저, package.json 파일 안에 deploy 명령어를 생성하세요. 이를 통해 GitHub Actions가 워크플로우(Workflow) 파일 안에 모든 배포 명령어를 직접 작성하는 대신, 서버에서 하나의 명령어만 실행할 수 있게 됩니다.
package.json 파일을 열고 다음 스크립트(script)를 추가하세요:
"scripts" : { "deploy" : "git pull && COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader && npm ci && npm run build && php artisan migrate --force && php artisan config:cache && php artisan route:cache && php artisan view:cache && php artisan optimize" }
만약 프로젝트에 package-lock.json 파일이 없다면, npm ci 대신 npm install을 사용하세요:
"scripts" : { "deploy" : "git pull && COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader && npm install && npm run build && php artisan migrate --force && php artisan config:cache && php artisan route:cache && php artisan view:cache && php artisan optimize" }
각 명령어의 역할
| 명령어 | 목적 |
|---|---|
| git pull | GitHub에서 최신 코드를 가져옵니다 (Pulls) |
| COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader | 프로덕션(Production) PHP 의존성(Dependencies)을 설치하고 Composer 오토로딩(Autoloading)을 최적화합니다 |
| npm ci | 일관된 빌드를 위해 락(Lock) 파일을 사용하여 JavaScript 의존성을 설치합니다 |
| npm run build | 프로덕션을 위한 프론트엔드 에셋(Assets)을 빌드합니다 |
| php artisan migrate --force | 프로덕션 모드에서 데이터베이스 마이그레이션(Migrations)을 실행합니다 |
| php artisan config:cache | Laravel 설정(Configuration)을 캐싱(Cache)합니다 |
| php artisan route:cache | Laravel 라우트(Routes)를 캐싱합니다 |
| php artisan view:cache | Blade 뷰(Views)를 컴파일하고 캐싱합니다 |
| php artisan optimize | Laravel 최적화(Optimization) 명령어를 실행합니다 |
중요: 프로덕션 환경에서 php artisan migrate --force를 실행하면 데이터베이스 구조가 변경될 수 있습니다. 배포하기 전에 항상 마이그레이션이 테스트되었는지 확인하고 데이터베이스 백업 전략을 갖추고 있는지 확인하십시오.
2단계: GitHub Actions 워크플로우(Workflow) 생성
다음으로, Laravel 프로젝트 내부에 GitHub Actions 워크플로우(Workflow) 파일을 생성합니다.
다음 파일을 생성합니다: .github/workflows/deploy.yml 그런 다음 다음 워크플로우(Workflow)를 추가합니다:
name : Deploy to Production
on : push
branches :
- main
jobs :
deploy :
runs-on : ubuntu-latest
steps :
- name : Deploy via SSH
uses : appleboy/ssh-action@v1.2.0
with :
host : ${{ secrets.DROPLET_HOST }}
username : ${{ secrets.DROPLET_USER }}
key : ${{ secrets.DROPLET_SSH_KEY }}
script : |
cd /var/www/your-app
npm run deploy
php artisan queue:restart
이 경로를 Droplet 상의 실제 Laravel 앱 위치로 교체하세요: /var/www/your-app
예를 들어:
script : |
cd /var/www/my-laravel-app
npm run deploy
php artisan queue:restart
php artisan queue:restart 명령은 Laravel 큐 워커(Queue workers)에게 새로 배포된 코드를 사용할 수 있도록 우아하게(gracefully) 재시작하도록 지시합니다. 만약 큐 워커를 관리하기 위해 Supervisor를 사용 중이라면, Supervisor가 자동으로 워커를 다시 시작할 것입니다.
단계 3: 로컬 머신에서 배포용 SSH 키 생성하기
GitHub Actions는 Droplet에 대한 SSH 접근 권한이 필요합니다. 개인용 SSH 키를 사용하는 대신, 배포 전용 SSH 키를 생성하는 것이 좋습니다. 로컬 머신에서 다음 명령어를 실행하세요:
ssh-keygen -t ed25519 -C "your-app-github-deploy" -f ~/.ssh/your-app-github -N ""
그러면 다음 두 파일이 생성됩니다:
| 파일 | 용도 |
|---|---|
~/.ssh/your-app-github | 개인 키(Private key). GitHub Secrets에 추가됩니다. |
~/.ssh/your-app-github.pub | 공개 키(Public key). Droplet에 추가됩니다. |
개인 키를 공개적으로 공유하지 마세요. 오직 GitHub Secrets에만 저장되어야 합니다.
단계 4: Droplet에 공개 키 추가하기
이제 GitHub Actions가 SSH를 통해 연결할 수 있도록 공개 키를 Droplet에 추가합니다.
Mac 또는 Linux
ssh-copy-id -i ~/.ssh/your-app-github.pub root@YOUR_DROPLET_IP
Windows PowerShell
type $ env : USERPROFILE \.ssh\your-app-github.pub | ssh root @ YOUR_DROPLET_IP "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
YOUR_DROPLET_IP를 실제 DigitalOcean Droplet IP 주소로 교체하세요.
다음 명령어를 사용하여 SSH 연결을 테스트할 수 있습니다: ssh -i ~/.ssh/your-app-github root@YOUR_DROPLET_IP. 연결이 성공하면, 개인 키 (Private Key)를 저장소 비밀값 (Repository Secrets)에 추가한 후 GitHub Actions도 연결할 수 있게 됩니다.
5단계: Droplet에 GitHub 배포 키 (Deploy Key) 생성하기
이제 GitHub Actions 워크플로 (Workflow)가 Droplet에 연결할 수 있지만, Droplet 또한 GitHub 저장소에서 코드를 가져올 (Pull) 수 있는 권한이 필요합니다.
Droplet에 SSH로 접속하세요:
ssh root@YOUR_DROPLET_IP
그 다음, Droplet에서 새로운 SSH 키를 생성하세요:
ssh-keygen -t ed25519 -C "your-app-droplet-deploy" -f ~/.ssh/your-app-github -N ""
공개 키 (Public Key)를 출력하세요:
cat ~/.ssh/your-app-github.pub
출력된 내용을 복사한 후, GitHub 저장소로 이동하세요:
Settings → Deploy keys → Add deploy key
다음 값들을 사용하여 배포 키 (Deploy Key)를 추가하세요:
| 필드 (Field) | 값 (Value) |
|---|---|
| Title | DigitalOcean Droplet Key |
| Key | Droplet에서 복사한 공개 키를 붙여넣으세요 |
| Allow write access | 체크 해제 |
Droplet은 GitHub에서 코드를 가져오기만 하면 되므로 읽기 전용 (Read-only) 액세스만으로 충분합니다.
배포 키를 추가한 후, Droplet에서 GitHub에 연결할 때 이 키를 사용하도록 SSH를 설정하세요:
cat >> ~/.ssh/config << ' EOF ' Host github.com IdentityFile ~/.ssh/your-app-github StrictHostKeyChecking no EOF chmod 600 ~/.ssh/config
Droplet이 GitHub에 연결할 수 있는지 테스트하세요:
ssh -T git@github.com
키가 올바르게 설정되었다면 GitHub에서 연결을 인식할 것입니다.
6단계: GitHub 저장소 비밀값 (Repository Secrets) 추가하기
이제 SSH 연결 세부 정보를 GitHub Secrets에 추가합니다.
GitHub 저장소로 이동하세요:
Settings → Secrets and variables → Actions → New repository secret
다음 비밀값 (Secrets)들을 추가하세요:
| 비밀값 이름 (Secret Name) | 값 (Value) |
|---|---|
| DROPLET_HOST | Droplet의 IP 주소 |
| DROPLET_USER | SSH 사용자 (예: root) |
| DROPLET_SSH_KEY | 로컬 개인 키 (Private Key)의 전체 내용 |
로컬 머신에서 개인 키 내용을 가져오려면:
Mac 또는 Linux
cat ~/.ssh/your-app-github
Windows PowerShell
cat $env:USERPROFILE\.ssh\your-app-github
-----BEGIN OPENSSH PRIVATE KEY-----를 포함한 모든 내용을 복사하세요.
-----END OPENSSH PRIVATE KEY----- 전체 개인 키(private key)를 DROPLET_SSH_KEY 비밀값(secret)에 붙여넣으세요.
7단계: 푸시 및 배포 테스트
배포 스크립트와 워크플로(workflow) 파일 설정을 마쳤다면, 변경 사항을 main 브랜치에 커밋하고 푸시하세요:
git add .github/workflows/deploy.yml package.json
git commit -m "Add auto-deploy pipeline"
git push origin main
그 다음 GitHub 저장소(repository)로 이동하여 Actions 탭을 여세요. 모든 설정이 올바르게 되었다면 배포 워크플로가 실행되는 것을 볼 수 있습니다. 녹색 체크 표시가 나타나면 배포가 성공적으로 완료된 것입니다.
배포 프로세스 작동 방식
전체 프로세스는 다음과 같습니다:
main브랜치에 변경 사항을 푸시합니다. ↓- GitHub Actions가 배포 워크플로를 시작합니다. ↓
- GitHub Actions가 SSH를 통해 Droplet에 연결합니다. ↓
- 워크플로가 Laravel 프로젝트 디렉토리로 이동합니다. ↓
- 서버에서
npm run deploy를 실행합니다. ↓ - Laravel이 GitHub에서 최신 코드를 가져옵니다(pull). ↓
- Composer가 프로덕션 의존성(production dependencies)을 설치합니다. ↓
- Node가 프론트엔드 에셋(frontend assets)을 빌드합니다. ↓
- Laravel이 마이그레이션(migrations) 및 최적화 명령을 실행합니다. ↓
- 큐 워커(Queue workers)가 안정적으로 재시작됩니다. ↓
- 배포가 완료됩니다.
이 방식을 통해 업데이트를 배포할 때마다 매번 서버에 수동으로 SSH 접속을 할 필요가 없어집니다.
문제 해결 (Troubleshooting)
일반적인 문제들
git@github.com: Permission denied (publickey)
이는 보통 Droplet이 GitHub 저장소에서 코드를 가져올(pull) 권한이 없음을 의미합니다. 이를 해결하려면:
- Droplet에서 SSH 키를 생성했는지 확인하세요.
- Droplet의 공개 키(public key)가 GitHub의 배포 키(Deploy Keys)에 추가되었는지 확인하세요.
- 저장소의 원격(remote) 주소가 HTTPS 대신 SSH를 사용하는지 확인하세요.
원격 URL을 확인하려면 다음을 입력하세요:
git remote -v
다음과 같이 보여야 합니다:
git@github.com:username/repository.git
만약 HTTPS를 사용 중이라면, 다음과 같이 업데이트하세요:
git remote set-url origin git@github.com:username/repository.git
composer: Continue as root/super user [yes]?
root 사용자로 배포하는 경우, Composer에서 경고가 표시될 수 있습니다. 배포 스크립트는 이 프롬프트를 피하기 위해 다음 환경 변수를 사용합니다:
COMPOSER_ALLOW_SUPERUSER = 1
더 나은 장기적인 해결책은 root 대신 전용 배포 사용자(deploy user)를 사용하는 것입니다.
npm ci 실패
npm ci 명령은 package-lock.json 파일이 필요합니다. 프로젝트에 해당 파일이 없다면 대신 npm install을 사용하세요. 또한 로컬에서 다음 명령어를 실행하여 lock 파일을 생성할 수 있습니다:
npm install
그 후 생성된 package-lock.json 파일을 커밋하세요.
.ssh/authorized_keys: No such file or directory
.ssh 디렉토리와 authorized_keys 파일을 수동으로 생성하세요:
mkdir -p ~/.ssh
touch ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
그 다음 공개 키(public key)를 다시 추가하세요.
큐 워커(Queue Workers)가 새 코드를 사용하지 않음
배포 후 Laravel 큐 워커(queue workers)를 재시작하세요:
php artisan queue:restart
만약 Supervisor를 사용 중이라면, 워커 상태를 확인하세요:
sudo supervisorctl status
필요한 경우, 워커를 수동으로 재시작하세요:
sudo supervisorctl restart all
GitHub Actions가 서버에 연결되지만 배포에 실패하는 경우
GitHub Actions의 워크플로(workflow) 로그를 확인하세요. 일반적인 원인은 다음과 같습니다:
- 워크플로 파일 내의 잘못된 앱 경로 (Incorrect app path)
- 서버에 Composer가 설치되지 않음
- 서버에 Node.js 또는 npm이 설치되지 않음
- 잘못된 파일 권한 (Wrong file permissions)
- Laravel 마이그레이션(migration) 실패
- 프론트엔드 빌드(frontend build) 실패
.env파일에 환경 변수(environment variables) 누락
서버에서 직접 배포 명령어를 수동으로 테스트할 수도 있습니다:
cd /var/www/your-app
npm run deploy
수동 실행 시 실패한다면, GitHub Actions를 다시 실행하기 전에 서버 측 문제를 먼저 해결하세요.
보안 권장 사항
단순한 개인 프로젝트라면 root로 배포해도 작동할 수 있습니다. 하지만 프로덕션(production) 애플리케이션의 경우, 다음 관행을 따라 보안을 강화하는 것이 좋습니다:
root대신 전용 배포 사용자(deploy user)를 사용하세요.- 배포 사용자에게 애플리케이션 디렉토리에 대한 접근 권한만 부여하세요.
- 개인 SSH 키(private SSH key)는 GitHub Secrets에만 보관하세요. 개인 키를 저장소(repository)에 커밋하지 마세요.
- 코드를 가져올(pulling) 때는 읽기 전용(read-only) GitHub 배포 키(deploy keys)를 사용하세요.
.env파일이 절대 커밋되지 않도록 주의하세요.- 프로덕션 마이그레이션을 실행하기 전에 데이터베이스를 백업하세요.
드디어 끝입니다! 😁🫡
이제 GitHub Actions와 DigitalOcean Droplet을 사용하여 Laravel을 위한 간단한 자동 배포 파이프라인(automated deployment pipeline)을 갖추게 되었습니다.
main 브랜치에 push할 때마다, GitHub Actions는 서버에 접속하여 최신 코드를 pull하고, 의존성(dependencies)을 설치하며, 에셋(assets)을 빌드하고, 마이그레이션(migrations)을 실행하고, Laravel을 최적화하며, 큐 워커(queue workers)를 재시작합니다. 이 설정은 복잡한 배포 플랫폼(deployment platform) 없이도 Laravel 배포를 자동화할 수 있는 실용적인 시작점입니다. 애플리케이션이 성장함에 따라 배포 알림(deployment notifications), 데이터베이스 백업(database backups), 테스트 실행(test runs), 무중단 배포(zero-downtime deployment), 그리고 롤백 지원(rollback support)을 추가하여 이 워크플로우(workflow)를 더욱 개선할 수 있습니다. 이 글이 도움이 되었고 후원을 하고 싶으시다면, 커피 한 잔을 사는 방식으로 쉽게 마음을 전하실 수 있습니다. 여러분의 기여에 진심으로 감사드립니다!
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기