[Day 13] Docker + 배포 흐름 연결 — 퍼즐이 완성되다

짱효·2026년 5월 4일

프론트엔드 기초 다시 쌓기 챌린지 13일차.
Week 3 "실전 배포 흐름"의 세 번째 수업.
Day 11에서 CI/CD를, Day 12에서 Docker를 각각 배웠다면,
오늘은 이 둘을 합쳐서 완성된 배포 파이프라인을 만드는 시간이었다.


🍳 오늘의 비유: "자동화 시스템 완성"

지금까지 배운 걸 레스토랑으로 쭉 이어보면:

수동 시대 (Day 11 이전)

셰프가 직접 시장 가서 재료 삼
→ 직접 손질
→ 직접 주방 세팅
→ 직접 불 켬
→ 하루 5번이면 셰프가 쓰러짐

CI/CD만 도입 (Day 11)

레시피 제출하면 로봇이 자동으로 처리
→ 근데 2호점은 주방 환경이 달라서 맛이 다름 😩

Docker만 도입 (Day 12)

주방 통째로 박스에 넣어서 보내면 어디서든 같은 맛
→ 근데 박스 만들고 보내는 걸 사람이 직접 해야 함 😩

CI/CD + Docker (오늘!)

셰프가 레시피북에 새 레시피 적어 넣으면 (git push)
→ 로봇이 자동으로 이동식 주방(이미지) 만들고
→ 중앙 창고(Docker Hub)에 보관하고
→ 각 지점(서버)에서 꺼내서 영업 시작
→ 셰프는 레시피만 잘 쓰면 됨!

🔄 전체 파이프라인: git push → 사용자 화면

오늘의 핵심. 개발자가 하는 일은 git push 하나뿐이다. 나머지는 전부 자동.

[1. 개발자]
코드 수정 → git push to main

        ↓

[2. GitHub Actions (CI)]
코드 체크아웃
→ docker build (이미지 생성)
→ 테스트 실행 (lint, 빌드 확인)
→ docker push (이미지를 Docker Hub에 업로드)

        ↓

[3. Docker Hub (창고)]
이미지 보관 중...

        ↓

[4. GitHub Actions (CD)]
SSH로 서버 접속
→ docker pull (이미지 다운로드)
→ docker run (컨테이너 실행)

        ↓

[5. 서버]
컨테이너 안에서 yarn start 자동 실행
→ Nginx가 요청을 컨테이너로 전달

        ↓

[6. 사용자]
사이트 접속 → 새 버전 확인!

📄 실제 GitHub Actions + Docker YAML 파일

Day 11에서는 Docker 없이 배포하는 YAML을 봤다.
오늘은 Docker를 합친 버전이다.

# .github/workflows/deploy.yml
name: Docker 배포 자동화

on:
  push:
    branches: [main]

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    steps:
      # 1단계: 코드 가져오기
      - name: 코드 체크아웃
        uses: actions/checkout@v4

      # 2단계: Docker Hub 로그인
      - name: Docker Hub 로그인
        run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin

      # 3단계: Docker 이미지 빌드
      - name: 이미지 빌드
        run: docker build -t myusername/my-nextjs-app:latest .

      # 4단계: Docker Hub에 업로드
      - name: 이미지 푸시
        run: docker push myusername/my-nextjs-app:latest

  deploy:
    needs: build-and-push    # 위의 job이 끝나야 실행
    runs-on: ubuntu-latest

    steps:
      # 5단계: 서버에 접속해서 새 이미지로 실행
      - name: 서버 배포
        run: |
          ssh -i ${{ secrets.SSH_KEY }} ubuntu@${{ secrets.SERVER_IP }} << 'EOF'
            docker pull myusername/my-nextjs-app:latest
            docker stop my-app || true
            docker rm my-app || true
            docker run -d --name my-app -p 5008:5008 myusername/my-nextjs-app:latest
          EOF

레스토랑 비유로 한 줄씩 해석

코드 체크아웃          → 재료 창고에서 레시피 가져오기
Docker Hub 로그인     → 중앙 창고 출입증 찍기
이미지 빌드           → 이동식 주방 조립 (Dockerfile 기반)
이미지 푸시           → 완성된 주방을 중앙 창고에 보관
서버 배포:
  docker pull         → 지점에서 창고에서 주방 꺼내오기
  docker stop/rm      → 기존 주방 정리
  docker run          → 새 주방 세팅하고 영업 시작!

🔑 오늘 새로 배운 키워드들

needs: build-and-push

Job 간 순서를 지정하는 것이다.

Job 1: build-and-push (이미지 만들고 창고에 올리기)
Job 2: deploy (서버에 배포하기)

needs = "Job 1이 끝나야 Job 2를 시작해"

당연하다. 이미지가 창고에 올라가지도 않았는데 서버에서 꺼내올 수는 없으니까.

docker stop → docker rm → docker run 3단계

docker stop my-app || true    # 기존 컨테이너 멈춤
docker rm my-app || true      # 기존 컨테이너 삭제
docker run -d --name my-app -p 5008:5008 myusername/my-nextjs-app:latest

왜 3단계인가?

1. docker stop  → 기존 푸드트럭 영업 종료
2. docker rm    → 기존 푸드트럭 철거
3. docker run   → 새 푸드트럭 세팅하고 영업 시작

기존 컨테이너가 5008 포트를 쓰고 있으니까, 안 지우고 새 걸 띄우면 포트 충돌이 난다.
하나의 포트에 두 개의 앱이 동시에 붙을 수 없다 (Day 9에서 배운 내용).

|| true는 "혹시 기존 컨테이너가 없어도 에러 내지 말고 넘어가"라는 뜻이다.
처음 배포할 때는 기존 컨테이너가 없을 수 있으니까.

-d 플래그 = 백그라운드 실행

docker run my-app        → 터미널에 로그가 쭉 뜸, 터미널 닫으면 멈춤
docker run -d my-app     → 백그라운드에서 조용히 실행, 터미널 닫아도 계속 돌아감

Day 7에서 배운 "SSH 끊으면 프로세스가 죽는 문제"를 -d가 해결해준다.


📊 Day 11 방식 vs Day 13 방식 비교

Day 11 (Docker 없이)Day 13 (Docker로)
서버에서 하는 일git pull → yarn install → yarn build → pm2 restartdocker pull → docker run
빌드 위치서버에서 직접 빌드GitHub Actions에서 빌드 (이미지 안에 포함)
서버에 필요한 것Node.js, yarn, pm2 전부 설치Docker만 설치
환경 차이 문제발생 가능없음 (이미지 안에 다 들어있음)
서버 2대로 늘릴 때2대 다 환경 세팅 필요docker run만 하면 됨

🔗 Week 1~3 전체 연결

지금까지 배운 모든 게 하나의 파이프라인 안에 들어있다.

[개발자] 코드 작성 + git push
    ↓
[GitHub Actions] CI/CD (Day 11)
    ↓
[Docker] 이미지 빌드 + 푸시 (Day 12)
    ↓
[서버] docker pull + docker run (Day 13 - 오늘)
    ↓
[pm2 또는 Docker] 프로세스 관리 (Day 8)
    ↓
[Nginx] 요청을 앱으로 전달 (Day 9)
    ↓
[사용자] 사이트 접속!
    ↓
[로그] 문제 생기면 확인 (Day 10)

Week 1에서 배운 빌드, Week 2에서 배운 서버·pm2·Nginx,
Week 3에서 배운 CI/CD·Docker가 전부 하나로 연결된다.


🍳 레스토랑 비유 업데이트

레스토랑서버
레시피 적고 레시피북에 넣기git push
로봇이 이동식 주방 자동 조립GitHub Actions + docker build
완성된 주방을 중앙 창고에 보관docker push → Docker Hub
지점에서 창고에서 주방 꺼냄docker pull
기존 주방 철거docker stop + docker rm
새 주방 세팅하고 영업 시작docker run -d
"이전 작업 끝나야 다음 시작"needs: build-and-push

🎯 오늘 배운 것 최종 정리

  1. CI/CD + Docker = 완성된 배포 파이프라인: git push만 하면 이미지 생성 → 창고 업로드 → 서버 배포까지 전부 자동
  2. 개발자가 하는 일은 git push 하나뿐: 나머지는 GitHub Actions가 전부 자동 처리
  3. 서버에서는 docker pull + docker run만: 빌드는 이미지 안에 이미 완료
  4. needs: Job 간 순서 지정 ("이미지 만들기 끝나야 배포 시작")
  5. stop → rm → run 3단계: 기존 컨테이너 정리하고 새 걸로 교체 (포트 충돌 방지)
  6. -d 플래그: 백그라운드 실행 (SSH 끊어도 계속 돌아감)
  7. Week 1~3 전체 연결: 빌드 → CI/CD → Docker → 서버 → Nginx → 사용자

🧪 이해도 체크

Q1. CI/CD + Docker 배포에서 개발자가 하는 일은?
→ 정답: git push 하나뿐. 이후 GitHub Actions가 docker build → docker push → 서버에서 docker pull → docker run까지 전부 자동으로 처리.

Q2. Day 11 방식과 Day 13 방식에서 "서버에서 하는 일"의 차이는?
→ 정답: Docker 없이는 서버에서 git pull → yarn install → yarn build → pm2 restart를 직접 해야 한다. Docker로는 docker pull → docker run만 하면 끝. 빌드는 이미지 안에 이미 완료되어 있기 때문.

Q3. docker stop → docker rm → docker run 3단계를 거치는 이유는?
→ 정답: 기존 컨테이너가 포트를 잡고 있어서, 안 지우고 새 걸 띄우면 포트 충돌이 난다. 기존 걸 멈추고(stop), 지우고(rm), 새로 띄우는(run) 순서.


💭 회고

Day 11에서 CI/CD를 배웠을 때는 "자동화 편하겠다" 정도였고,
Day 12에서 Docker를 배웠을 때는 "환경 통일 좋겠다" 정도였다.

오늘 이 둘을 합치니까 비로소 "아, 이게 현대적인 배포구나" 가 느껴졌다.
git push 하나로 빌드부터 배포까지 전부 자동. 환경도 동일 보장.

Week 1~3에서 배운 빌드, 서버, pm2, Nginx, CI/CD, Docker가
전부 하나의 파이프라인 안에 들어있다는 게 신기했다.
하나하나 따로 배울 때는 몰랐는데, 합치니까 전부 연결된다.


📚 다음 학습 예고

Day 14: 빌드 에러 해결 패턴 + 롤백 개념
자동 배포가 완성됐는데... 배포한 버전에 버그가 있으면?
"되돌리기"는 어떻게 하는가!

#프론트엔드 #Docker #CI/CD #GitHubActions #배포파이프라인 #2년차개발자 #기초다시쌓기

profile
✨🌏확장해 나가는 프론트엔드 개발자입니다✏️

0개의 댓글