-
[Study-Mate] GitHub Actions 배포 자동화(with. docker)NextJS 2026. 4. 6. 17:05
배포 자동화 계획 수립
로컬에서 코드 작성 후 매번 SSH에 직접 접속하여 Git pull을 하는 과정이 너무 번거롭다고 생각이 되어
배포 자동화에 대한 경험을 축적할 겸 Git Actions를 사용하여 배포자동화를 진행해 보았다.
더보기GitHub Actions란?
GitHub에서 제공하는 CI/CD 도구. 레포지토리에 .github/workflows/ 폴더에 yml 파일을 작성하면 특정 이벤트(push, PR 등)가 발생했을 때 자동으로 실행된다
GitHub Actions가 제공하는 서버(runner)에서 실행되기 때문에 별도 서버가 필요 없다.Git Actions을 사용한 배포 자동화 워크플로우
로컬에서 push -> git actions가 이를 감지 -> deploy.yml 내용 따라 워크플로우 진행 -> 자동으로 pull 진행 -> 배포 완료

여기서 git actions가 배포를 하는데 사용할 도구/방법으로는 크게
1. EC2 직접 배포 : SSH에서 스크립트를 직접 실행2. Docker 배포 : Docker 이미지를 사용배포
3. S3 + aws CodeDeploy 배포 : S3와 aws CodeDeploy를 사용한 배포
4. ECR + ECS : Docker AWS 버전

가 있었는데
나는 기존에 이미 Docker로 배포를 진행해 왔기 때문에 Docker를 사용한 배포를 진행하기로 했다.
더보기Docker Hub란?
Docker 이미지를 저장하고 공유하는 레지스트리, GitHub가 코드 저장소라면, Docker Hub는 이미지 저장소이다.
빌드된 이미지를 Docker Hub에 올려두면 EC2에서 언제든 pull해서 실행할 수 있고 버전별로 이미지가 쌓이기 때문에 롤백도 손쉽게 할 수 있다.
**참고지식(무중단 배포 전략)
GitHub Actions를 설정하기 위한 사전 준비
1. Docker Hub 유저 아이디와 액세스 토큰 준비
Docker Hub 로그인 -> Account Settings -> Personal access tokens 에서 read & write 권한으로 액세스 토큰 생성 -> 이후 화면에 나타나는 login username 과 access Token 값을 기록해둔다.2. GitHub secret 값 설정

해당 프로젝트 github 리포지토리 -> settings -> Secrets and variables -> Repository Secrets에 키 등록
Git Actions가 워크플로우를 진행하기 위한 키 값들을 모두 추가해준다.
나는 아래와 같이 값을 넣었다
더보기DOCKERHUB_USERNAMEDocker Hub 유저 아이디 DOCKERHUB_TOKENDocker Hub 액세스 키EC2_HOSTEC2 퍼블릭 IP (예: 13.xxx.xxx.xxx)EC2_USERNAMEubuntuEC2_SSH_KEYEC2 접속용 .pem 파일 내용 전체 복사NEXT_PUBLIC_SUPABASE_URL.env에 있는 값NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY.env에 있는 값NEXT_PUBLIC_STORAGE_BUCKET_POST.env에 있는 값 NEXT_PUBLIC_STORAGE_BUCKET_PROFILE .env에 있는 값 GitHub Actions의 워크플로우를 위한 작업
현재 나의 프로젝트는
git pull
→ EC2에서 git pull, docker compose up
→ docker-compose.yml 실행
→ app: Dockerfile로 이미지 직접 빌드
→ nginx: default.conf 설정으로 컨테이너 실행
→ 두 컨테이너가 실행되어 서버 구동(app 컨테이너는 Next.js 앱을 실행하고, nginx 컨테이너는 외부 요청을 받아 app으로 전달)
이렇게 실행되는데 배포 자동화를 하고 나면
git pull
→ github actions가 이를 감지
→ deploy.yml 파일 실행
더보기deploy.yml 파일에서 실행될 작업
1. github actions 서버에서 dockerfile을 실행하여 도커 이미지 빌드
2. 빌드된 이미지를 Docker hub 저장소에 push
3. docker-compose.yml 파일을 실행하여 저장소에 저장된 dockerfile과 nginx 이미지를 pull
4. nginx의 defaul.conf 파일을 실행하여 설정
5. EC2에서 실행할 스크립트
→ 자동배포 완료
이렇게 된다.
달라진 점은 기존에는 EC2서버에서 도커파일의 이미지를 빌드 했었지만
배포를 자동화를 하게 된다면 GitHub Actions 서버에서 빌드를 하기 때문에 GitHub Actions 서버에서 빌드한 이미지를 배포서버에서 쓸 수 있게, 이미지를 Docker Hub 저장소에 Push하고 배포서버에서 Pull하여 쓴다는 점이 달라졌다.
이렇게 함으로써 Docker 이미지 빌드를 배포서버가 아닌 GitHub Actions 서버에서 하게 되어 서버 부하가 줄어들게 되었다.
나는 deploy.yml파일에서 실행될 작업이 1-2,3-5를 각 Job1과 Job2로 나눠서 실행되도록 하였다.
Deploy.yml 파일 작성시 참고용 내용
* github actions 구성요소
- Workflow (워크플로) : Workflow는 자동화된 작업의 집합입니다. 예를 들어, "코드가 푸시될 때마다 테스트를 실행하고 배포하는" 작업을 하나의 워크플로로 설정할 수 있습니다. 워크플로는 .github/workflows 폴더에 YAML 파일로 저장됩니다.
- Event (이벤트) : Event는 워크플로를 트리거하는 조건입니다. 예를 들어, push 이벤트는 코드가 GitHub에 푸시될 때 워크플로가 실행되도록 합니다. 워크플로는 다양한 이벤트에 반응할 수 있습니다. 예를 들어, pull_request (PR 요청), issues (이슈 작성), workflow_dispatch (수동 실행) 등이 있습니다.
- Job (잡) : Job은 워크플로 내에서 실행되는 작업입니다. 예를 들어, "테스트 실행", "코드 빌드", "배포" 등 각 작업은 하나의 Job으로 정의됩니다.
여러 작업은 병렬로 실행할 수도 있고, 순차적으로 실행할 수도 있습니다. - Steps (스텝) : 각 Job은 여러 개의 Step으로 구성됩니다. Step은 실제로 실행되는 명령어입니다. 예를 들어, "코드 체크아웃", "의존성 설치", "테스트 실행" 등의 작업이 Step입니다.
- Actions (액션) : Actions는 GitHub Actions에서 재사용 가능한 작업 단위입니다. 예를 들어, actions/checkout은 GitHub 리포지토리의 코드를 체크아웃하는 액션입니다.
- Runner : Runner는 GitHub Actions 워크플로를 실행하는 환경입니다. GitHub는 GitHub-hosted runner(GitHub에서 관리하는 실행 환경)를 제공하며, 사용자는 self-hosted runner(자체 서버)를 설정할 수도 있습니다.

* deploy.yml 파일의 구성요소
- name (옵션) : 워크플로의 이름을 설정합니다. 이 이름은 GitHub Actions UI에 표시됩니다.
on (필수) : 워크플로를 실행할 이벤트를 정의합니다. GitHub에서 특정 작업이 발생할 때마다 이 워크플로가 실행됩니다. - push: 코드를 푸시할 때
- pull_request: 풀 리퀘스트가 생성될 때
- workflow_dispatch: 수동으로 워크플로를 실행할 때
- schedule: 특정 시간에 주기적으로 실행할 때
- jobs (필수) : jobs는 워크플로 내에서 실행되는 여러 작업을 정의합니다. 각 작업은 독립적으로 실행되며, 병렬 또는 순차적으로 실행할 수 있습니다. 하나의 워크플로에 여러 개의 잡을 정의할 수 있습니다. 각 job은 runs-on을 통해 어떤 환경에서 실행될지 설정할 수 있습니다.
- runs-on (필수) : 작업이 실행될 환경을 정의합니다. GitHub에서 제공하는 호스팅된 실행 환경을 사용할 수 있습니다.
- steps (필수) : steps는 job 내에서 실행될 개별 작업을 정의합니다. 각 스텝은 명령어를 실행하는 하나의 단위입니다.
각 스텝은 name (스텝 이름), run (실행할 명령어), 또는 uses (다른 GitHub 액션을 사용) 등을 포함할 수 있습니다.

위 내용을 참고하여 Deploy파일을 작성하고, 변경된 워크플로우에 맞게 docker-compose.yml파일을 수정하였다.
작성한 Deploy.yml
name: Deploy to EC2 on: push: branches: [main] env: DOCKER_IMAGE: jeongjaewon1/study-mate jobs: build-and-push: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and Push uses: docker/build-push-action@v6 with: context: . push: true tags: ${{ env.DOCKER_IMAGE }}:latest,${{ env.DOCKER_IMAGE }}:${{ github.sha }} build-args: | NEXT_PUBLIC_SUPABASE_URL=${{ secrets.NEXT_PUBLIC_SUPABASE_URL }} NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=${{ secrets.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY }} NEXT_PUBLIC_STORAGE_BUCKET_POST=${{ secrets.NEXT_PUBLIC_STORAGE_BUCKET_POST }} NEXT_PUBLIC_STORAGE_BUCKET_PROFILE=${{ secrets.NEXT_PUBLIC_STORAGE_BUCKET_PROFILE }} cache-from: type=gha cache-to: type=gha,mode=max deploy: needs: build-and-push runs-on: ubuntu-latest steps: - name: Deploy to EC2 uses: appleboy/ssh-action@v1 with: host: ${{ secrets.EC2_HOST }} username: ${{ secrets.EC2_USERNAME }} key: ${{ secrets.EC2_SSH_KEY }} script: | cd ~/project/study-mate docker pull ${{ env.DOCKER_IMAGE }}:latest docker compose up -d --force-recreate app docker image prune -fdocker-compose.yml
services: app: image: jeongjaewon1/study-mate:latest container_name: study-mate-app restart: unless-stopped env_file: .env environment: - NODE_ENV=production # 배포 자동화 전까지 사용 # build: # context: . # dockerfile: Dockerfile # args: # NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL} # NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: ${NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY} # NEXT_PUBLIC_STORAGE_BUCKET_POST: ${NEXT_PUBLIC_STORAGE_BUCKET_POST} # NEXT_PUBLIC_STORAGE_BUCKET_PROFILE: ${NEXT_PUBLIC_STORAGE_BUCKET_PROFILE} # container_name: study-mate-app # restart: unless-stopped # env_file: .env # environment: # - NODE_ENV=production nginx: image: nginx:alpine container_name: study-mate-nginx ports: - '80:80' - '443:443' volumes: - ./nginx/default.conf:/etc/nginx/conf.d/default.conf - /etc/letsencrypt:/etc/letsencrypt:ro depends_on: - app restart: unless-stoppedGitHub Actions를 사용한 배포 자동화 결과


정상적으로 실행이 됐고 실제로 적용된 것을 확인하였다.
실패한 내용
** docker hub의 액세스키 값 생성시 권한 범위를 잘못하고, git secret의 키 값을 잘 못넣 약간의 시행착오가 있었다.
이후 고려해볼 내용
- 무중단 배포
- 서버 롤백
'NextJS' 카테고리의 다른 글
[Study-Mate] 성능최적화 - 번들 최적화 (0) 2026.04.30 [Study-Mate] 성능 최적화 3. 번들링 / 코드 스플리팅 (0) 2026.04.12 [Study-Mate] HTTPS로 전환하기 (0) 2026.04.05 [Study-Mate] 성능 최적화 2. 이미지 최적화 (0) 2026.03.29 Zustand 도입 이유 (0) 2026.03.28