배포는 보통 세 단계로 발전합니다.
| 단계 | 특징 | 위험 |
|---|---|---|
| 수동 배포 | 담당자가 직접 접속해 파일 복사 | 사람 실수·재현 어려움 |
| 스크립트 배포 | 배포 절차를 스크립트 1개로 고정 | 서버 차이 반영 필요 |
| 파이프라인 배포 | CI/CD 가 빌드부터 배포까지 자동 실행 | 승인 단계 설계 필요 |
같은 코드를 여러 환경에 배포하되 설정만 다르게 적용합니다.
| 환경 | 목적 | 특징 |
|---|---|---|
| dev | 개발자 테스트 | 자주 배포, 데이터 휘발 |
| stage | 운영 전 최종 검증 | prod 와 같은 구성 |
| prod | 실제 사용자 서비스 | 배포 신중, 승인 필요 |
환경별 값은 코드가 아니라 파일로 분리합니다.
# application-dev.properties
app.port=8080
db.url=jdbc:h2:mem:devdb
log.level=DEBUG배포 시 APP_ENV=prod 같은 환경변수로 어떤 파일을 쓸지 고르고, 필요하면 환경변수가 파일 값을 덮어씁니다. 비밀번호 같은 민감한 값은 파일이 아니라 환경변수나 시크릿 저장소에 둡니다.
배포마다 새 폴더를 만들고, 활성 릴리스만 current 가 가리킵니다.
/opt/app/
├── releases/
│ ├── 1/
│ ├── 2/
│ └── 3/
└── current (활성 릴리스 번호를 적은 파일 또는 심볼릭 링크)Git Bash 에서는 심볼릭 링크 권한 문제가 있어 이 레슨의 스크립트는 current 를 릴리스 번호를 적은 텍스트 파일로 대신합니다. 리눅스 서버에서는 ln -sfn releases/3 current 로 실제 심볼릭 링크를 씁니다.
배포 스크립트는 언제나 같은 순서를 따릅니다.
| 순서 | 단계 | 내용 |
|---|---|---|
| 1 | 복사 | 새 릴리스 폴더에 산출물·설정 복사 |
| 2 | 전환 | current 가 가리키는 대상을 새 릴리스로 변경 |
| 3 | 재시작 | 서비스 재시작으로 새 코드 적용 |
| 4 | 헬스체크 | 재시작 후 정상 응답인지 확인 |
헬스체크는 서버를 띄운 채 오래 기다리지 않고, 짧게 실행해 종료 코드로 판단합니다.
java -jar app.jar --health status.flag
echo "종료 코드: $?" # 0=정상, 1=비정상실제 서비스에서는 /health 같은 HTTP 엔드포인트를 두지만, 이 레슨은 --health 인자로 즉시 종료하는 방식으로 같은 판단 구조를 재현합니다.
헬스체크가 실패하면 두 가지 상황이 있습니다.
current 를 아예 바꾸지 않아 롤백이 필요 없습니다.current 를 이전 릴리스 번호로 다시 쓰고 재시작합니다.두 경우 모두 releases 폴더 자체는 지우지 않아 원인 분석에 남겨 둡니다.
releases 폴더가 계속 쌓이면 디스크가 찹니다. 보통 최근 5~10개만 남기고 오래된 폴더를 지우되, 지우기 전에는 그 릴리스가 current 가 아닌지 확인해야 합니다. 실수로 활성 릴리스를 지우면 다음 재시작이 실패합니다.
원격 서버가 있다면 산출물을 scp 로 올리고 ssh 로 배포 명령을 실행합니다.
scp dist/app.jar deploy@prod-1:/opt/app/releases/12/app.jar
ssh deploy@prod-1 "echo 12 > /opt/app/current && \
sudo systemctl restart app.service"이 레슨은 인터넷·원격 서버 접근이 없는 환경이라 이 명령은 텍스트로만 보여주고, 실제 헬스체크와 전환은 로컬 스크립트로 재현합니다.
리눅스 서버는 보통 systemd 서비스로 앱을 관리합니다.
[Service]
Type=simple
ExecStart=/usr/bin/java -jar /opt/app/current/app.jar
Restart=on-failuresudo systemctl restart app.service 로 재시작하고, Restart=on-failure 를 두면 비정상 종료 시 자동으로 다시 뜹니다. 이 레슨은 문법만 확인하고 실행하지 않습니다.
GitHub Actions 는 environment 키로 배포 대상을 지정하고, 그 environment 에 필수 리뷰어를 걸어 두면 승인 전까지 잡이 멈춥니다.
jobs:
deploy-prod:
needs: build
runs-on: ubuntu-latest
environment:
name: production
url: https://app.example.com
steps:
- run: echo "prod 배포 실행"prod environment 에만 승인을 걸고 dev·stage 는 자동 배포하는 구성이 흔하며, 인터넷 접근이 없는 이 레슨에서는 워크플로 화면 대신 텍스트로만 구조를 보여줍니다.
여기서 다룬 릴리스 폴더 방식은 jar 를 직접 서버에 올리는 전통적인 배포입니다. 컨테이너 이미지로 배포하는 방식은 리눅스 운영 13(Docker) 레슨을 참고하세요. 두 방식 모두 환경 분리, 헬스체크, 롤백이라는 원리는 같고 산출물이 jar 냐 이미지냐만 다릅니다.