27 KiB
GoldenChart 서버 배포 가이드 (exdev.co.kr + Jenkins)
대상 프로젝트:
goldenChart(React Frontend + Spring Boot Backend + MySQL)
배포 서버:exdev.co.kr
문서 버전: 1.0 · 최종 갱신: 2026-05-23
Mac Studio HFT 배포 매뉴얼의 Git Hook(Track A) · Jenkins(Track B) 패턴을 GoldenChart에 맞게 단순화한 문서입니다.
이 프로젝트는 Docker Compose 3서비스(mysql / backend / frontend) 중심이며, 별도 PM2·AI Agent 스택은 포함하지 않습니다.
목차
- 시스템 개요
- 환경 정보 및 경로
- 사전 준비 (최초 1회)
- 케이스 A: 로컬에서 Git push → 자동 배포
- 케이스 B: deploy-home.sh 수동 배포 (개발용)
- 케이스 C: Jenkins 스케줄 자동 배포
- 케이스 D: Jenkins 수동·파라미터 배포
- 케이스 E: 서버에서 직접 배포
- 케이스 F: 서비스별 선택 배포
- 배포 후 검증
- 일상 운영 명령어
- 트러블슈팅
- 부록: deploy.sh · post-receive · Jenkinsfile
1. 시스템 개요
GoldenChart 배포는 공통 스크립트 deploy.sh 를 중심으로 두 가지 트랙을 운영합니다.
┌────────────────────────────────────────────────────────────────────┐
│ deploy.sh (서버 공통 진입점) │
│ 예: /Users/aidev/git/deploy.sh (서버에 1회 설치) │
└───────────────┬────────────────────────────┬───────────────────────┘
│ │
Track A (Express) Track B (Scheduled)
git push main → 즉시 배포 Jenkins → 예약/승인 배포
│ │
docker compose build/up docker compose build/up
(frontend / backend 선택 가능) (backend·DB 마이그레 권장)
| 트랙 | 트리거 | 적합한 변경 | 다운타임 | 실행 도구 |
|---|---|---|---|---|
| Track A | git push exdev main |
UI, 소규모 API, 설정 | 짧음 (컨테이너 재기동) | Git post-receive |
| Track B | Jenkins 크론/수동 | Flyway DB 마이그레, 대규모 백엔드 | 짧음 | Jenkins Freestyle/Pipeline |
| 개발용 | 로컬 ./deploy-home.sh |
긴급·초기 구축 | down 후 재기동 |
SSH + tar 동기화 |
deploy.sh 4단계 (권장)
| Phase | 작업 | 설명 |
|---|---|---|
| Phase 1 | Git checkout | bare repo → work-tree 동기화 |
| Phase 2 | 빌드 | Docker 이미지 빌드 (docker compose build) |
| Phase 3 | 교체 | docker compose up -d (서비스별 --no-deps 가능) |
| Phase 4 | 검증 | Frontend HTTP + Backend API 응답 확인 |
참고: 저장소에 포함된
deploy-home.sh는 Git 없이 tar 압축 + SCP 로 동기화하는 별도 경로입니다. Jenkins·Git Hook 구축 전까지 또는 긴급 배포에 사용합니다.
2. 환경 정보 및 경로
서버 (exdev.co.kr)
| 항목 | 값 (기본) |
|---|---|
| 호스트 | exdev.co.kr |
| SSH 사용자 | aidev |
| SSH 포트 | 22 |
| 프로젝트 work-tree | /Users/aidev/dev/goldenChart |
| Git bare 저장소 (권장) | /Users/aidev/git/goldenChart.git |
| 배포 스크립트 (권장) | /Users/aidev/git/deploy.sh |
| 배포 로그 (권장) | /Users/aidev/logs/goldenchart/deploy.log |
서버 OS·디렉터리는 환경에 맞게 조정하세요.
deploy-home.sh기본값은 work-tree를/Users/aidev/dev/goldenChart로 사용합니다.
로컬 개발 PC
| 항목 | 예시 경로 |
|---|---|
| 소스 | ~/dev/goldenChart |
| Git remote 이름 | exdev (권장) |
서비스 포트 (docker-compose.yml)
| 서비스 | 컨테이너 | 포트 (기본) | URL |
|---|---|---|---|
| Frontend (Nginx) | gc-frontend |
80 (443 금지) | http://stock.exdev.co.kr/ · https://stock.exdev.co.kr/ (Caddy 경유) |
| Backend (Spring Boot) | gc-backend |
8080 | http://stock.exdev.co.kr:8080 (직접) 또는 /api (Nginx 경유) |
| MySQL | gc-mysql |
3306 | localhost:3306 (서버 내부) |
브라우저에서는 Nginx가 /api → backend:8080 으로 프록시합니다.
Docker Compose 서비스
| 서비스명 | 이미지/빌드 | 역할 |
|---|---|---|
mysql |
mysql:8.0 | DB + Flyway 마이그레이션 대상 |
backend |
backend/Dockerfile |
Spring Boot API, WebSocket(STOMP) |
frontend |
frontend/Dockerfile |
React 빌드 + Nginx |
환경 변수 (.env)
서버 work-tree 루트에 .env 파일을 두세요. 예시는 .env.example 참고.
MYSQL_ROOT_PASSWORD=... # 운영 환경에서 반드시 변경
MYSQL_DATABASE=stockAnalyzer
MYSQL_USER=stock
MYSQL_PASSWORD=...
BACKEND_PORT=8080
FRONTEND_PORT=80
# backend/docker-compose 환경
GC_SECRETS_ENCRYPTION_KEY=... # 운영용 강한 키로 변경
3. 사전 준비 (최초 1회)
3-1. 서버(exdev.co.kr) 인프라 확인
서버에 SSH 접속 후 실행합니다.
ssh aidev@exdev.co.kr
# Docker
docker --version
docker compose version
docker ps
# 디렉터리 생성
mkdir -p /Users/aidev/git
mkdir -p /Users/aidev/logs/goldenchart
mkdir -p /Users/aidev/dev/goldenChart
3-2. Git Bare 저장소 생성
# 서버에서 실행
git init --bare /Users/aidev/git/goldenChart.git
# work-tree (배포 대상) — 이미 clone이 있으면 생략 가능
# 최초 1회: bare에서 checkout
git --git-dir=/Users/aidev/git/goldenChart.git \
--work-tree=/Users/aidev/dev/goldenChart \
checkout -f main 2>/dev/null || true
3-3. deploy.sh 설치
서버에 공통 배포 스크립트를 둡니다. 부록 13-1 의 deploy.sh 내용을 복사합니다.
# 로컬에서 서버로 복사 (예시)
scp scripts/deploy.sh aidev@exdev.co.kr:/Users/aidev/git/deploy.sh
# 서버에서
chmod +x /Users/aidev/git/deploy.sh
저장소에
scripts/deploy.sh가 없으면 부록 13-1 을 참고해 서버에 직접 생성하세요.
3-4. post-receive 훅 등록 (Track A)
cat > /Users/aidev/git/goldenChart.git/hooks/post-receive << 'HOOK'
#!/bin/bash
set -euo pipefail
while read oldrev newrev refname; do
branch="${refname#refs/heads/}"
if [ "$branch" = "main" ]; then
echo "[post-receive] main push → deploy.sh"
SERVICE_NAME="${SERVICE_NAME:-all}" /Users/aidev/git/deploy.sh
else
echo "[post-receive] $branch — 배포 생략 (main만 자동 배포)"
fi
done
HOOK
chmod +x /Users/aidev/git/goldenChart.git/hooks/post-receive
3-5. 로컬 PC — Git remote 등록
cd ~/dev/goldenChart
# SSH remote (권장)
git remote add exdev ssh://aidev@exdev.co.kr/Users/aidev/git/goldenChart.git
# SSH 키 등록 (최초 1회)
ssh-copy-id aidev@exdev.co.kr
# 연결 테스트
git ls-remote exdev
3-6. 최초 push (저장소·work-tree 초기화)
cd ~/dev/goldenChart
git checkout main
git push -u exdev main
push 완료 시 서버 post-receive → deploy.sh 가 자동 실행됩니다.
3-7. Jenkins 설치 및 초기 설정 (Track B)
서버에 Jenkins가 없다면 Homebrew + PM2 또는 Docker 중 하나로 설치합니다.
Homebrew 예시:
brew install jenkins-lts openjdk@21
# Jenkins URL: http://127.0.0.1:8080 또는 8090 (설치 방식에 따름)
Jenkins 전역 환경 변수 (필수):
Jenkins → Manage Jenkins → System → Global properties → Environment variables
| 변수명 | 값 (macOS Apple Silicon 예시) |
|---|---|
PATH |
/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin |
JAVA_HOME |
/opt/homebrew/opt/openjdk@21 |
Execute shell 블록 맨 위에 항상 추가:
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
3-8. 서버 .env 및 방화벽
cd /Users/aidev/dev/goldenChart
cp .env.example .env
# vi .env — 비밀번호·암호화 키 변경
# 외부에서 80/8080 접근 필요 시 (서버 방화벽·라우터 설정은 환경별로 확인)
4. 케이스 A: 로컬에서 Git push → 자동 배포
용도: 개발 완료 후 exdev.co.kr에 반영
트리거:git push exdev main
특징: Track A —main브랜치만 자동 배포
4-1. 일반 워크플로
cd ~/dev/goldenChart
git status
git add .
git commit -m "feat: 알림 팝업 레이아웃 개선"
git push exdev main
4-2. 자동 배포 흐름
[로컬] git push exdev main
↓
[exdev] post-receive (main만)
↓
/Users/aidev/git/deploy.sh
↓
Phase 1: git checkout → /Users/aidev/dev/goldenChart
Phase 2: docker compose build
Phase 3: docker compose up -d
Phase 4: HTTP/API 헬스 확인
4-3. 배포 로그 확인 (서버)
ssh aidev@exdev.co.kr
tail -f /Users/aidev/logs/goldenchart/deploy.log
4-4. feature 브랜치 (배포 없음)
git push exdev feature/my-branch
# → post-receive가 main이 아니면 deploy.sh 실행 안 함
4-5. Track A에 적합한 변경
| 변경 유형 | Track A | 비고 |
|---|---|---|
frontend/src/** |
✅ | SERVICE_NAME=frontend 로 더 빠르게 가능 |
backend/src/** (소규모) |
✅ | Flyway 변경 있으면 Track B 권장 |
db/migration/V*.sql 신규 |
⚠️ | Jenkins·장 마감 후 배포 권장 |
docker-compose.yml |
⚠️ | docker-only 또는 전체 재배포 |
5. 케이스 B: deploy-home.sh 수동 배포 (개발용)
용도: Git/Jenkins 미구축 시, 또는 긴급 전체 동기화
실행 위치: 로컬 Mac (goldenChart루트)
cd ~/dev/goldenChart
# 전체 동기화 + Docker 재빌드 (기본, --no-cache)
./deploy-home.sh
# 프론트엔드만
./deploy-home.sh --frontend
# 백엔드만
./deploy-home.sh --backend
# 캐시 사용 (빠름)
./deploy-home.sh --use-cache
# 상태 / 로그 / 중지
./deploy-home.sh --status
./deploy-home.sh --logs
./deploy-home.sh --down
./deploy-home.sh --shell
| 옵션 | 설명 |
|---|---|
| (기본) | tar 동기화 → docker compose down → build → up |
--no-sync |
서버 기존 소스로만 재빌드 |
--use-cache |
Docker 빌드 캐시 사용 |
--frontend / --backend |
해당 서비스만 재빌드 |
환경 변수로 서버 지정:
REMOTE_HOST=exdev.co.kr REMOTE_USER=aidev ./deploy-home.sh
주의:
deploy-home.sh는 서버 디렉터리를 삭제 후 tar 전체 덮어쓰기 합니다. 서버 전용.env·로컬 수정 사항이 있다면 백업 후 사용하세요.
정식 운영은 Git push + deploy.sh 를 권장합니다.
6. 케이스 C: Jenkins 스케줄 자동 배포
용도: Spring Boot + Flyway(DB 스키마) 정기 배포
트리거: Jenkins 크론 또는 Poll SCM
6-1. Jenkins Job 생성 (최초 1회)
- Jenkins UI 접속 (예:
http://127.0.0.1:8090— 서버에서만 접근 가능할 수 있음) - New Item →
goldenChart-Pipeline→ Freestyle project
Source Code Management:
[x] Git
Repository URL : file:///Users/aidev/git/goldenChart.git
Branch : */main
Build Triggers (택 1):
| 시나리오 | 크론 표현식 |
|---|---|
| 월~금 18:00 (장 마감 후) | 0 18 * * 1-5 |
| 매주 토 02:00 | 0 2 * * 6 |
| 1시간마다 변경 감지 | Poll SCM H * * * * |
Build → Execute shell:
#!/bin/bash
set -euo pipefail
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
echo "=== goldenChart Jenkins Build #${BUILD_NUMBER} ==="
echo "시각: $(date)"
SERVICE_NAME=backend /Users/aidev/git/deploy.sh
6-2. Jenkins에서 Git·Docker 경로 확인
빌드 로그에 다음이 출력되어야 합니다.
which git && which docker && docker compose version
7. 케이스 D: Jenkins 수동·파라미터 배포
용도: 배포 시점·대상·승인을 UI에서 제어
7-1. 파라미터 설정
Job → General → This project is parameterized
| 파라미터 | 타입 | 이름 | 선택값 |
|---|---|---|---|
| 배포 서비스 | Choice | SERVICE_NAME |
all / frontend / backend / docker-only |
| 배포 유형 | Choice | DEPLOY_TYPE |
after_market / immediate |
| 배포 승인 | Boolean | DEPLOY_APPROVED |
default: false |
Execute shell:
#!/bin/bash
set -euo pipefail
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
if [ "${DEPLOY_APPROVED:-false}" != "true" ]; then
echo "[ERROR] DEPLOY_APPROVED=true 필요"
exit 1
fi
if [ "${DEPLOY_TYPE:-after_market}" = "after_market" ]; then
HOUR=$(date +%H)
if [ "$HOUR" -ge 9 ] && [ "$HOUR" -lt 18 ]; then
echo "[ERROR] 장중(09~18시) after_market 배포 불가"
exit 1
fi
fi
echo "SERVICE_NAME=${SERVICE_NAME:-all}"
SERVICE_NAME="${SERVICE_NAME:-all}" /Users/aidev/git/deploy.sh
7-2. UI에서 실행
goldenChart-Pipeline→ Build with ParametersSERVICE_NAME,DEPLOY_TYPE,DEPLOY_APPROVED설정- Build
7-3. 파라미터 조합 예시
| 상황 | SERVICE_NAME | DEPLOY_TYPE | DEPLOY_APPROVED |
|---|---|---|---|
| 장 마감 후 백엔드+DB | backend |
after_market |
✅ |
| UI 긴급 수정 | frontend |
immediate |
✅ |
| 컨테이너만 재시작 | docker-only |
immediate |
✅ |
| 전체 배포 | all |
after_market |
✅ |
8. 케이스 E: 서버에서 직접 배포
용도: Jenkins/Git 없이 서버 터미널에서 즉시 배포
ssh aidev@exdev.co.kr
# 전체 배포
/Users/aidev/git/deploy.sh
# Git push (서버에서 개발한 경우)
cd /Users/aidev/dev/goldenChart
git add . && git commit -m "fix: ..."
git push exdev main # remote가 서버 bare를 가리킬 때
Docker만 수동 재시작
cd /Users/aidev/dev/goldenChart
docker compose up -d
docker compose up -d --no-deps backend
docker compose up -d --no-deps frontend
docker compose logs -f gc-backend --tail 100
Backend 수동 빌드 (디버깅)
cd /Users/aidev/dev/goldenChart
docker compose build backend
docker compose up -d --no-deps backend
9. 케이스 F: 서비스별 선택 배포
SERVICE_NAME 환경 변수로 deploy.sh 실행 범위를 제한합니다.
| SERVICE_NAME | 빌드 대상 | 배포 |
|---|---|---|
frontend |
frontend 이미지 |
docker compose up -d --no-deps frontend |
backend |
backend 이미지 |
docker compose up -d --no-deps backend |
docker-only |
스킵 | docker compose up -d |
all (기본) |
전체 | docker compose up -d |
SERVICE_NAME=frontend /Users/aidev/git/deploy.sh
SERVICE_NAME=backend /Users/aidev/git/deploy.sh
SERVICE_NAME=docker-only /Users/aidev/git/deploy.sh
변경 파일 → SERVICE_NAME 가이드
frontend/src/**, frontend/nginx.conf → frontend
backend/src/**, backend/pom.xml → backend
db/migration/V*.sql → backend (Track B 권장)
docker-compose.yml, .env → all 또는 docker-only
10. 배포 후 검증
10-1. 컨테이너 상태
cd /Users/aidev/dev/goldenChart
docker compose ps
기대: gc-mysql, gc-backend, gc-frontend 가 running (healthy).
10-2. Frontend
curl -s -o /dev/null -w "Frontend HTTP: %{http_code}\n" http://localhost:80/
# 기대: 200
브라우저: http://stock.exdev.co.kr/
10-3. Backend API
curl -s -o /dev/null -w "Backend API HTTP: %{http_code}\n" \
http://localhost:8080/api/indicators/list
# 기대: 200
10-4. MySQL
docker exec gc-mysql mysqladmin ping -h localhost \
-u stock -p"${MYSQL_PASSWORD:-analyzer}"
10-5. 배포 로그
grep -E "배포 완료|배포 실패|ERROR" /Users/aidev/logs/goldenchart/deploy.log | tail -20
10-6. 로컬에서 원격 확인
./deploy-home.sh --status
11. 일상 운영 명령어
cd /Users/aidev/dev/goldenChart
# 시작 / 중지
docker compose up -d
docker compose down
docker compose restart backend
# 로그
docker compose logs -f --tail 100
docker logs -f gc-backend --tail 100
docker logs -f gc-frontend --tail 50
# 이미지 정리 (디스크 부족 시)
docker system prune -f
| 하고 싶은 작업 | 명령어 |
|---|---|
| 소스 반영 + 자동 배포 | git push exdev main |
| UI만 배포 | SERVICE_NAME=frontend /Users/aidev/git/deploy.sh |
| API만 배포 | SERVICE_NAME=backend /Users/aidev/git/deploy.sh |
| 긴급 전체 동기화 (로컬) | ./deploy-home.sh |
| Jenkins 수동 배포 | Build with Parameters |
| 배포 로그 | tail -f /Users/aidev/logs/goldenchart/deploy.log |
HTTPS (HTTP + HTTPS 동시 접속)
exdev.co.kr (운영 — Caddy + stock / chatbot 서브도메인)
443 포트는 ex-chatbot Caddy만 사용. GoldenChart·Chatbot은 443 직접 바인딩 금지.
| URL | 처리 |
|---|---|
| https://stock.exdev.co.kr | Caddy → gc-frontend :80 (GoldenChart) |
| https://chatbot.exdev.co.kr/chatbot/* | Caddy → rag-api + Admin |
| https://exdev.co.kr/chatbot/* | → chatbot.exdev.co.kr 301 |
| https://exdev.co.kr/* (그 외) | → stock.exdev.co.kr 301 |
Caddyfile: exAIChatbot2.0/docker/caddy/Caddyfile.exdev
절차: docs/exdev-subdomain-setup.md
curl -sS -o /dev/null -w "stock %{size_download}B\n" https://stock.exdev.co.kr/
curl -sS -o /dev/null -w "chatbot %{size_download}B\n" https://chatbot.exdev.co.kr/chatbot/health
DESKTOP_UPDATE_BASE_URL=https://stock.exdev.co.kr/desktop/updates ./scripts/verify-desktop-updater-endpoint.sh
주의: gc-frontend 443:443 매핑 금지 (Caddy 충돌). frontend는 80만 노출.
# frontend 기동 확인
docker ps --filter name=gc-frontend
curl -s -o /dev/null -w "HTTP:%{http_code}\n" http://stock.exdev.co.kr/
curl -sk -o /dev/null -w "HTTPS:%{http_code}\n" https://stock.exdev.co.kr/
Caddy TLS 인증서: exAIChatbot2.0/docker/nginx/certs/ (obtain-cert-exdev.sh)
단독 서버 (Caddy 없음, nginx 가 직접 TLS)
docker compose -f docker-compose.yml -f docker-compose.https.yml up -d frontend
./scripts/setup-exdev-https.sh --email you@example.com --domain example.com
12. 트러블슈팅
12-1. git push 후 배포가 안 됨
# 훅 권한
ls -la /Users/aidev/git/goldenChart.git/hooks/post-receive
chmod +x /Users/aidev/git/goldenChart.git/hooks/post-receive
# main 브랜치인지
git branch --show-current
# 훅 수동 테스트
echo "0000000000000000000000000000000000000000 $(git rev-parse HEAD) refs/heads/main" | \
/Users/aidev/git/goldenChart.git/hooks/post-receive
12-2. deploy.sh / Jenkins — docker 명령 없음
Jenkins Global PATH 및 shell 상단:
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
which docker && docker compose version
12-3. 배포 락 파일
동시 배포 방지용 (deploy.sh에 구현 시):
cat /tmp/goldenchart_deploy.lock 2>/dev/null
# 프로세스 없을 때만
rm -f /tmp/goldenchart_deploy.lock
12-4. Backend 기동 실패 (Flyway / MySQL)
docker logs gc-backend --tail 150
docker logs gc-mysql --tail 50
docker compose up -d mysql
sleep 20
docker compose up -d --no-deps backend
12-5. Frontend 502 / API 연결 실패
docker compose ps
# backend healthy 후 frontend 재시작
docker compose up -d --no-deps backend
docker compose up -d --no-deps frontend
12-5a. PC 앱 자동 업데이트 — latest.json 형식 오류 / HTTPS 빈 응답
데스크톱 앱(Tauri)은 HTTPS https://stock.exdev.co.kr/desktop/updates/latest.json 만 사용합니다.
증상: 앱 업데이트 모달에 latest.json 형식 오류 또는 JSON parse 실패.
원인 1 (가장 흔함): ex-chatbot Caddy(443) 가 gc-frontend 로 프록시할 때 HTTP 200 + 본문 0바이트 를 반환. nginx(:80) 직접 접속은 정상.
# 진단 (로컬·서버 공통)
DESKTOP_UPDATE_BASE_URL=https://stock.exdev.co.kr/desktop/updates ./scripts/verify-desktop-updater-endpoint.sh
# 수동 비교
curl -sS -o /dev/null -w "HTTPS size=%{size_download}\n" https://stock.exdev.co.kr/desktop/updates/latest.json
curl -sS -o /dev/null -w "HTTP size=%{size_download}\n" http://stock.exdev.co.kr/desktop/updates/latest.json
curl -sS http://stock.exdev.co.kr/desktop/updates/latest.json | head
| 결과 | 의미 |
|---|---|
| HTTPS 0B, HTTP 1KB+ | Caddy 역프록시 설정 오류 — stock.exdev.co.kr 블록에서 reverse_proxy host.docker.internal:80 및 flush_interval -1 확인 |
| HTTPS·HTTP 모두 0B | frontend/public/desktop/updates/latest.json 미생성 — Desktop Jenkins goldenChart-Desktop-Pipeline publish 실행 |
HTTPS OK, platforms {} |
manifest 는 있으나 서명 bundle 없음 — publish-desktop-update.sh 재실행 |
Caddy 수정 후: curl -sS https://stock.exdev.co.kr/desktop/updates/latest.json | jq .version 로 v0.1.x 확인.
임시 우회: PC 프로그램 탭에서 dmg/exe 수동 설치.
12-6. deploy-home.sh SSH 실패
ssh-copy-id -p 22 aidev@exdev.co.kr
ssh aidev@exdev.co.kr "docker info"
./deploy-home.sh --forget-password # 캐시된 비밀번호 초기화
12-7. 서버 .env가 push마다 삭제됨
deploy-home.sh 는 work-tree를 rm -rf 후 재압축 해제 합니다.
운영 .env 는 서버에만 두고, 로컬 .env 는 .gitignore 처리하세요. Git 기반 deploy.sh 는 work-tree를 덮어쓰지 않으므로 .env 가 유지됩니다.
PC 데스크톱 클라이언트 (Tauri)
웹 Docker 배포와 별도 Jenkins Job goldenChart-Desktop-Pipeline 으로 macOS .dmg + Windows NSIS .exe 를 빌드합니다.
# 서버 최초 1회
./scripts/install-desktop-build-deps.sh
./scripts/setup-desktop-updater-keys.sh
./scripts/server-install-desktop-jenkins.sh
# 수동 빌드·배포
./scripts/jenkins-desktop-pipeline.sh
13. 부록: deploy.sh · post-receive · Jenkinsfile
13-1. deploy.sh (서버용)
서버 /Users/aidev/git/deploy.sh 에 저장합니다. 프로젝트에 추가하려면 scripts/deploy.sh 로 복사해 Git에 포함할 수 있습니다.
#!/bin/bash
# GoldenChart — exdev.co.kr 공통 배포 스크립트
set -euo pipefail
GIT_DIR="${GIT_DIR:-/Users/aidev/git/goldenChart.git}"
WORK_TREE="${WORK_TREE:-/Users/aidev/dev/goldenChart}"
LOG_FILE="${LOG_FILE:-/Users/aidev/logs/goldenchart/deploy.log}"
LOCK_FILE="/tmp/goldenchart_deploy.lock"
SERVICE_NAME="${SERVICE_NAME:-all}"
export PATH="/opt/homebrew/bin:/usr/local/bin:${PATH:-/usr/bin:/bin}"
mkdir -p "$(dirname "$LOG_FILE")"
exec >> "$LOG_FILE" 2>&1
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
if [ -f "$LOCK_FILE" ] && kill -0 "$(cat "$LOCK_FILE")" 2>/dev/null; then
log "ERROR: 다른 배포 진행 중 (PID $(cat "$LOCK_FILE"))"
exit 1
fi
echo $$ > "$LOCK_FILE"
trap 'rm -f "$LOCK_FILE"' EXIT
log "========== GoldenChart 배포 시작 (SERVICE_NAME=$SERVICE_NAME) =========="
# Phase 1: checkout
if [ -d "$GIT_DIR" ]; then
log "Phase 1: git checkout -f main → $WORK_TREE"
git --git-dir="$GIT_DIR" --work-tree="$WORK_TREE" checkout -f main
else
log "WARN: GIT_DIR 없음 — work-tree 기존 소스 사용"
fi
cd "$WORK_TREE"
# Phase 2–3: build & up
case "$SERVICE_NAME" in
frontend)
log "Phase 2-3: build & up frontend"
docker compose build frontend
docker compose up -d --no-deps frontend
;;
backend)
log "Phase 2-3: build & up backend"
docker compose build backend
docker compose up -d mysql
docker compose up -d --no-deps backend
;;
docker-only)
log "Phase 2-3: docker compose up (no build)"
docker compose up -d
;;
all|*)
log "Phase 2-3: build & up all"
docker compose build
docker compose up -d
;;
esac
# Phase 4: verify
FRONTEND_PORT="${FRONTEND_PORT:-80}"
BACKEND_PORT="${BACKEND_PORT:-8080}"
HTTP_F=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${FRONTEND_PORT}/" || echo "000")
HTTP_B=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${BACKEND_PORT}/api/indicators/list" || echo "000")
log "검증: Frontend=$HTTP_F Backend=$HTTP_B"
docker compose ps
if [[ "$HTTP_F" =~ ^2 ]] && [[ "$HTTP_B" =~ ^2 ]]; then
log "========== 배포 완료 =========="
else
log "========== 배포 완료 (일부 헬스체크 비정상 — 로그 확인) =========="
fi
13-2. post-receive (요약)
3-4절 과 동일. main push 시에만 deploy.sh 호출.
13-3. Jenkinsfile (선택 — Pipeline Job용)
Pipeline Job을 쓸 경우 저장소 루트에 Jenkinsfile 추가 예시:
pipeline {
agent any
parameters {
choice(name: 'SERVICE_NAME', choices: ['all', 'frontend', 'backend', 'docker-only'], description: '배포 대상')
booleanParam(name: 'DEPLOY_APPROVED', defaultValue: false, description: '배포 승인')
}
stages {
stage('Approve') {
steps {
script {
if (!params.DEPLOY_APPROVED) {
error('DEPLOY_APPROVED=true 필요')
}
}
}
}
stage('Deploy') {
steps {
sh '''
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
SERVICE_NAME=${SERVICE_NAME} /Users/aidev/git/deploy.sh
'''
}
}
}
post {
always {
sh 'tail -30 /Users/aidev/logs/goldenchart/deploy.log || true'
}
}
}
13-4. HFT 매뉴얼과의 차이
| 항목 | HFT (Mac Studio) | GoldenChart (exdev.co.kr) |
|---|---|---|
| 프로젝트명 | goldenAnalysis | goldenChart |
| 배포 스크립트 | /Volumes/ADATA/git/deploy.sh |
/Users/aidev/git/deploy.sh (권장) |
| work-tree | /Users/aidev/apps/goldenChart |
/Users/aidev/dev/goldenChart |
| PM2 / AI Agent | 있음 | 없음 (Docker만) |
| 수동 배포 | — | deploy-home.sh (tar+SSH) |
| 헬스체크 | Actuator /actuator/health |
/api/indicators/list |
GoldenChart · exdev.co.kr · Docker Compose · 구축 가이드 2026-05-23