[CL] v1 배포 문서화 - 100-hours-a-week/6-nemo-wiki GitHub Wiki

1. 완전 수동 배포 방식 (Manual Deployment)

1.1. 개요

완전 수동(Bang-Bang) 배포 방식은 각 서비스에 대해 직접 서버에 접속해 모든 명령어를 수동으로 입력하거나 단일 스크립트 파일을 실행하는 구조입니다.

1.2. 공통 디렉토리 구조

manual-scripts/
├── deploy-frontend.sh   # FE 배포 스크립트
├── deploy-backend.sh    # BE 배포 스크립트
└── deploy-ai.sh         # AI 배포 스크립트

6-nemo-*/
├── .env                 # 환경 변수
├── 소스코드 디렉토리 및 설정 파일
└── (서비스별 빌드 결과물 혹은 가상환경)

1.3. 공통 배포 절차

  1. GCP VM 접속
    • GCP 콘솔 또는 SSH 툴을 사용해 서버 접속
    • sudo su -ubuntu 명령어로 ubuntu 계정 전환
  2. chmod +x deploy-*.sh 로 실행 권한 부여
  3. PM2 상태 및 로그 확인
    • pm2 status
    • pm2 logs [프로세스명]

1.4. BE (Spring Boot)

  • 배포 스크립트 요약
    • 브랜치 기반 소스 clonepull
    • ./gradlew clean bootJar -x test
    • pm2 start "java -jar ..." --name nemo-backend
    • 헬스체크: curl http://localhost:8080/actuator/health
  • 주요 포인트
    • JAR 경로: build/libs/nemo-server-0.0.1-SNAPSHOT.jar
    • 서버에서 직접 빌드
  • BE 배포 깃허브 링크

1.5. FE (Next.js)

  • 배포 스크립트 요약
    • 브랜치 기반 소스 clonepull
    • pnpm install
    • pnpm build
    • pm2 start pnpm --name nemo-frontend -- start
    • 헬스체크: curl http://localhost:3000
  • 주요 포인트
    • ESLint 설정 문제 발생 시 eslint.config.mjs 수정 (트러블 슈팅 링크)
  • FE 배포 깃허브 링크

1.6. AI (FastAPI)

  • 배포 스크립트 요약
    • 브랜치 기반 소스 clonepull
    • python3 -m venv venv && source venv/bin/activate
    • pip install -r requirements.txt
    • pm2 start venv/bin/uvicorn ...
    • 헬스체크: curl http://localhost:8000
  • 주요 포인트
    • PYTHONPATH=./src 설정 필요
    • 가상환경 활성화/비활성화 명시적 수행
  • AI 배포 깃허브 링크

1.7. 한계 및 개선 필요성

완전 수동 배포 방식은 초기 개발 환경에서는 간단하게 사용할 수 있으나, 운영 환경으로 넘어가면서 아래와 같은 뚜렷한 한계가 발생했습니다:

한계점 설명 개선 방향 (스크립트 기반 배포)
1. 롤백 전략 부재 배포 실패 시 수동으로 이전 상태 복구 필요 rollback.sh 스크립트 도입으로 간편 롤백 가능
2. 단일 파일/단일 흐름의 비유연성 하나의 스크립트에 모든 흐름이 담겨 있어 특정 단계만 실행하기 어려움 backup.sh, deploy.sh, healthcheck.sh, rollback.sh, run.sh 등으로 기능 분리
3. 알림 미지원 배포 성공/실패 여부를 다른 파트와 공유할 수 없음 → 커뮤니케이션 비용 증가 Discord Webhook 연동으로 배포 상태 실시간 공유
4. 디렉토리 이동 반복 매번 서비스별 디렉토리 진입 후 명령어/스크립트 실행해야 함 서비스 별 배포 명령어를 alias로 등록하여 단축 실행 가능 (예: deploy-be)
5. 운영 중 헬스체크 부재 배포 당시에는 확인하지만, 이후에는 장애를 자동 인지하기 어려움 healthcheck.sh + crontab으로 주기적 헬스체크 및 실패 시 알림 기능 도입

1.8. 결론

위와 같은 문제들은 운영 안정성과 반복 배포 효율성에 직접적인 영향을 주었습니다.

이에 따라 스크립트 기반 배포 구조를 새롭게 도입하여 다음과 같은 효과를 기대할 수 있습니다.

  • 배포 속도 단축 및 휴먼 에러 감소
  • 이슈 발생 시 빠른 롤백 대응
  • 타 팀과의 협업 시 배포 상태 투명성 확보
  • 운영 중 장애 조기 인지 가능

다음 장에서는 이 개선을 반영한 개선된 배포(Semi-Automated) 방식을 설명합니다.

2. 개선된 배포 방식 (Semi-Automated Deployment)

2.1. 개요

수동 배포의 비효율성을 개선하고, 배포-실행-롤백-헬스체크-백업까지 반복 작업을 스크립트로 자동화한 구조입니다.

2.2. 공통 디렉토리 구조

~/nemo/
├── [서비스명]/
│   ├── [서비스 소스]/
│   ├── [백업 디렉토리]/
│   └── .env
└── cloud/
    └── v1/
        └── [서비스명]/
            └── semi-automated/
                ├── deploy.sh
                ├── run.sh
                ├── rollback.sh
                ├── backup.sh
                └── healthcheck.sh

2.3 공통 alias 등록 (BE 예시)

echo 'alias be-deploy="bash ~/nemo/cloud/v1/backend/semi-automated/deploy.sh"' >> ~/.bashrc
echo 'be-rollback() { bash ~/nemo/cloud/v1/backend/semi-automated/rollback.sh \"$1\"; }' >> ~/.bashrc
echo 'alias be-health="bash ~/nemo/cloud/v1/backend/semi-automated/healthcheck.sh"' >> ~/.bashrc
echo 'alias be-run="bash ~/nemo/cloud/v1/backend/semi-automated/run.sh"' >> ~/.bashrc
echo 'alias be-backup="bash ~/nemo/cloud/v1/backend/semi-automated/backup.sh"' >> ~/.bashrc

source ~/.bashrc

2.4. 명령어 매핑

서비스에 따라 ai-, be-, fe- 접두어로 구분됩니다.

명령어 설명
[서비스]-deploy 전체 배포 수행
[서비스]-rollback 최신 백업 롤백 or 파일 지정 롤백
[서비스]-run PM2로 서비스 재시작
[서비스]-backup 소스 또는 빌드 백업
[서비스]-health 헬스체크 수행 및 결과 출력

2.5. 크론탭 설정

# 실행 권한 부여
chmod +x ~/nemo/cloud/v1/[서비스]/semi-automated/*.sh

# 크론탭 등록 (개발: 5분 주기, 운영: 1분 주기)
crontab -e
*/5 * * * * /home/ubuntu/nemo/cloud/v1/[서비스]/semi-automated/healthcheck.sh
*/1 * * * * /home/ubuntu/nemo/cloud/v1/[서비스]/semi-automated/healthcheck.sh

# 로그 확인
cat /var/log/syslog | grep CRON

2.6. 디스코드 알림

# 디스코드 웹훅
send_discord_notification() {
  local message="$1"
  
  for webhook_url in "$WEBHOOK_CLOUD_URL" "$WEBHOOK_BACKEND_URL"
  do
    curl -H "Content-Type: application/json" \
      -X POST \
      -d "{\"content\": \"$message\"}" \
      "$webhook_url"
  done
}

# 배포 알림
sleep 60
if bash "$SCRIPT_DIR/healthcheck.sh"; then
  send_discord_notification "✅ [배포 성공: $BRANCH] $SERVICE_NAME 배포 완료!"
else
  send_discord_notification "❌ [배포 실패: $BRANCH] $SERVICE_NAME 배포 실패!"
  exit 1
fi

# 헬스체크 알림
if [ "$STATUS" -eq 200 ]; then
  echo "✅ [$SERVICE_NAME] 서버 정상 작동 (HTTP 200)"
else
  echo "❌ [$SERVICE_NAME] 서버 비정상 (HTTP $STATUS)"
  send_discord_alert "🚨 [헬스체크 실패: $BRANCH] $SERVICE_NAME 비정상 상태 감지!"
  exit 1
fi

# 알림 메시지
✅ [배포 성공: dev] nemo-frontend 배포 완료!
❌ [배포 실패: dev] nemo-frontend 배포 실패!
✅ [롤백 성공: dev] nemo-frontend 롤백 완료! (Rollback Point: 20250516-2259)
❌ [롤백 실패: dev] nemo-frontend 롤백 실패! (Rollback Point: 20250516-2259)
🚨 [헬스체크 실패: dev] nemo-frontend 비정상 상태 감지!

2.7 롤백 전략

  • 주요 설계 원칙
    • 백업 기준: 배포 시마다 .jar(BE) 또는 .tar.gz(FE/AI) 파일을 백업 디렉토리에 저장
    • 백업 유지 개수: 최대 7개 버전까지만 보관 (oldest-first 방식으로 자동 삭제)
    • 파일명 규칙: 서비스명-YYYYMMDD-HHMM.jar 형식으로 타임스탬프 포함
  • 롤백 스크립트 작동 방식
    • fe-rollback / be-rollback / ai-rollback

    • 인자 없이 실행 시 → 가장 최근 백업 파일로 롤백

    • 인자 전달 시 → YYYYMMDD-HHMM 포맷을 기반으로 해당 백업 파일로 롤백

      fe-rollback             # 최신 백업으로 롤백
      fe-rollback 20240531-2300  # 특정 시점으로 롤백
      

2.8. 한계 및 개선 필요성

스크립트 기반 배포(Semi-Automated)는 수동 배포의 반복 작업과 실수 가능성을 줄였지만, 여전히 다음과 같은 운영 및 협업상의 한계가 존재했습니다.

  • CI/CD 파이프라인이 부재하여, 배포 및 롤백 시 서버에 수동 접속이 필요했고

    이로 인해 타 파트와의 커뮤니케이션 비용이 지속적으로 발생함

  • 각 스크립트 파일 내부에서 PORT, BRANCH, WEBHOOK환경변수를 개별 관리하여

    환경 설정을 한눈에 파악하거나 일관되게 유지하기 어려웠음

    이를 해결하기 위해 다음과 같은 리팩토링을 진행했습니다.

    • .env 파일을 기준으로 모든 환경 설정을 중앙 집중화하여 관리

    • FE/BE/AI 각각의 서비스 디렉토리에 흩어져 있던 스크립트를

      cloud 레포로 통합해 유지보수성과 구조적 일관성을 확보 (3장)

2.9. 결론

주요 개선 사항은 다음과 같습니다.

  • 배포, 재시작, 롤백, 헬스체크, 백업을 기능별 스크립트로 분리하여 역할 명확화
  • alias 등록을 통해 클라우드 파트 뿐만 아니라 다른 파트 개발자도 명령어 기반 운영 가능
  • Discord Webhook 연동으로 배포 상태 및 헬스체크 실패 알림을 실시간 공유
  • crontab을 통한 헬스체크 주기 자동화 및 장애 조기 인지 체계 구현
  • 백업 + 롤백 전략 도입으로 배포 실패 시 빠른 복구 지원

이 구조는 이후 단계에서 CI/CD 파이프라인으로 자연스럽게 전환할 수 있는 기반이 되었으며, 서비스 안정성과 협업 효율성을 동시에 확보하는 중요한 전환점이었습니다.

3. 스크립트 구조 리팩토링 및 관리 방식 개선

3.1. 개선 배경

초기에는 각 서비스 디렉토리 내부(~/nemo/backend/scripts/)에서 배포 스크립트를 서버에서 직접 생성/수정하여 관리하였습니다.

이러한 구조로 인해 다음과 같은 문제가 발생했습니다.

  • 경로 중복환경 의존성으로 인한 코드 중복
  • PORT, SERVICE_NAME, BRANCH 등이 각 스크립트마다 하드코딩
  • dev/prod 간 구조 불일치로 인해 환경별 관리 어려움
  • 서버 내 수작업 변경으로 인해 이력 추적 불가, 협업 어려움

3.2. 개선 방향: Cloud 레포 기반 통합 관리 구조 도입

모든 배포 스크립트를 cloud 레포로 통합하여 중앙 집중화된 버전 관리 체계를 도입하였으며,

.env 파일을 통해 환경별 설정을 분리하고 스크립트 재사용성을 극대화하였습니다.

  • 통합 디렉토리 구조

    cloud/v1/backend/semi-automated/
    ├── backup.sh
    ├── deploy.sh
    ├── deploy-cicd.sh
    ├── healthcheck.sh
    ├── rollback.sh
    ├── run.sh
    └── README.md
    
    
  • 공통 환경 구성 (.env 예시)

    SERVICE_NAME=nemo-backend
    BRANCH=develop
    PORT=8080
    REPO_URL=https://github.com/100-hours-a-week/6-nemo-be.git
    BACKUP_DIR=$HOME/nemo/backend/jar-backups
    SCRIPT_DIR=$HOME/nemo/cloud/v1/backend/semi-automated
    APP_DIR=$HOME/nemo/backend/backend-service
    JAR_PATH=$APP_DIR/build/libs/nemo-server-0.0.1-SNAPSHOT.jar
    HEALTHCHECK_URL=http://localhost:$PORT/actuator/health
    
    

3.3. Before & After 비교

  • run.sh 예시
    • Before (서비스 디렉토리 내, 직접 관리)

      #!/bin/bash
      set -euo pipefail
      
      SERVICE_NAME="nemo-backend"
      ROOT_DIR="$HOME/nemo/backend"
      PORT=8080
      JAR_FILE="$ROOT_DIR/backend-service/build/libs/nemo-server-0.0.1-SNAPSHOT.jar"
      ENV_FILE="$ROOT_DIR/.env"
      
      # 환경 변수 로드
      if [ -f "$ENV_FILE" ]; then
        set -a
        source "$ENV_FILE"
        set +a
      fi
      
      echo "🚀 PM2로 백엔드 서버 실행 중..."
      pm2 start java --name "$SERVICE_NAME" -- \
        -Duser.timezone=Asia/Seoul \
        -jar "$JAR_FILE" \
        --server.port=$PORT
      pm2 save
      
      
    • After (Cloud 레포 내, 중앙 관리)

      #!/bin/bash
      set -euo pipefail
      
      ENV_FILE="$HOME/nemo/backend/.env"
      
      # 환경 변수 로드
      if [ -f "$ENV_FILE" ]; then
        set -a
        source "$ENV_FILE"
        set +a
      fi
      
      echo "🚀 PM2로 백엔드 서버 실행 중..."
      pm2 start java --name "$SERVICE_NAME" -- \
        -Duser.timezone=Asia/Seoul \
        -jar "$JAR_FILE" \
        --server.port=$PORT
      pm2 save
      
      

3.4. 리팩토링 효과 요약

항목 리팩토링 전 리팩토링 후
스크립트 위치 서비스별 디렉토리 (~/nemo/backend/scripts) Cloud 레포 (cloud/v1/backend/semi-automated)
환경 변수 스크립트 내 하드코딩, 중복 선언 .env 기반 통일 관리
dev/prod 전환 구조 불일치, 별도 유지 필요 동일 스크립트 재사용 + .env만 분기
변경 이력 수동 수정 → 이력 추적 불가 Git으로 버전 관리 및 PR 협업 가능
협업 가능성 개인 작업 중심 팀 단위 유지보수 가능

3.5. 핵심 요약

  • cloud 레포를 통해 스크립트의 통합 관리 체계 확보
  • .env 기반 구조로 운영 환경(dev/prod) 분리 용이
  • 변경 이력을 Git으로 관리하여 협업 생산성 및 신뢰성 향상
  • 각 서비스가 동일한 구조로 배포 흐름을 공유 → 유지보수/확장성 극대화

4. 비고

PM2를 사용한 이유

서버에서 빌드하는 이유

FE 배포 중 일어난 .env 파일 관리 이슈

AI 배포 중 Python 환경 버전 불일치 이슈