deploy-local
로컬 개발 서버 전체 기동 (BE + FE, hot-reload)
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
로컬 개발 서버 전체 기동 (BE + FE, hot-reload)
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
초기 워크스페이스 세팅 — GitHub Token, Figma MCP, Notion MCP 연결 + 레포 클론 + Cursor 워크스페이스 구성
모든 repo를 main 브랜치로 체크아웃 & pull하여 최신화
풀스택 구현 오케스트레이션 (BE→FE→APP → 리뷰→테스트→PR)
크로스 레포 리서치 오케스트레이션 (BE→FE→APP 순서 위임)
Meeting-to-Develop: Notion 티켓의 미팅 노트/요구사항을 자동 분석하여 개발 초안 작성 & Notion 페이지에 반영
프로젝트 지식 축적 & 관리 — learn(교훈 추출), ingest(소스 학습), query(질의), lint(건강성 ��사), status(현황)
| name | deploy-local |
| description | 로컬 개발 서버 전체 기동 (BE + FE, hot-reload) |
| context | fork |
| allowed-tools | Read, Grep, Glob, Bash |
로컬 개발 서버를 전체 기동한다. SSH 포트포워딩 → BE → FE → APP 순서로 실행.
growit-env/config.json을 설정 소스로 사용한다.
TARGET — (선택) 특정 서비스만 실행. 미지정 시 전체 기동.
be — BE 서버만fe — FE 웹만app — APP만tunnel — SSH 터널만| 서비스 | 경로 | 실행 명령 | 포트 | 비고 |
|---|---|---|---|---|
| SSH Tunnel | — | SSH 포트포워딩 | 5433→RDS:5432 | Bastion 경유 |
| BE | DDD-12-GROWIT-BE | ./gradlew :app:bootRun | 8080 | Spring Boot (env vars from .env.development) |
| FE | DDD-12-GROWIT-FE | npm run dev | 3000 | Next.js (turbopack) |
| APP | DDD-12-GROWIT-APP/growit-mobile | yarn start | 8081 | Expo (Metro bundler) |
/deploy-local [TARGET]
│
▼
Step 1: 사전 검증 (growit-env, 환경변수, 의존성)
▼
Step 2: SSH 포트포워딩 (config.json → RDS 접근)
▼
Step 3: 포트 충돌 검사 & 정리
▼
Step 4: 서비스 기동 (BE → FE → APP)
▼
Step 5: 헬스체크 & 상태 보고
BASE_DIR="$(cd "$(pwd)/.." && pwd)"
ENV_DIR="$BASE_DIR/growit-env"
CONFIG="$ENV_DIR/config.json"
LOG_DIR="$BASE_DIR/.logs"
mkdir -p "$LOG_DIR"
if [ ! -f "$CONFIG" ]; then
echo "❌ growit-env/config.json을 찾을 수 없습니다."
echo " /settings를 먼저 실행하세요."
exit 1
fi
# BE
if [ ! -f "$BASE_DIR/DDD-12-GROWIT-BE/.env.development" ]; then
if [ -f "$ENV_DIR/be/.env.development" ]; then
cp "$ENV_DIR/be/.env.development" "$BASE_DIR/DDD-12-GROWIT-BE/.env.development"
echo "→ BE .env.development 복사"
else
echo "❌ BE 환경변수 없음. /settings를 먼저 실행하세요."
exit 1
fi
fi
# FE
if [ ! -f "$BASE_DIR/DDD-12-GROWIT-FE/.env.development" ]; then
if [ -f "$ENV_DIR/fe/.env.development" ]; then
cp "$ENV_DIR/fe/.env.development" "$BASE_DIR/DDD-12-GROWIT-FE/.env.development"
echo "→ FE .env.development 복사"
fi
fi
# BE: gradlew 실행 가능 여부
if [ -f "$BASE_DIR/DDD-12-GROWIT-BE/gradlew" ]; then
chmod +x "$BASE_DIR/DDD-12-GROWIT-BE/gradlew"
fi
# FE: node_modules
if [ ! -d "$BASE_DIR/DDD-12-GROWIT-FE/node_modules" ]; then
echo "→ FE node_modules 없음, npm install 실행..."
(cd "$BASE_DIR/DDD-12-GROWIT-FE" && npm install 2>&1 | tail -3)
fi
# APP: node_modules (서브디렉토리)
if [ ! -d "$BASE_DIR/DDD-12-GROWIT-APP/growit-mobile/node_modules" ]; then
echo "→ APP node_modules 없음, yarn install 실행..."
(cd "$BASE_DIR/DDD-12-GROWIT-APP/growit-mobile" && yarn install 2>&1 | tail -3)
fi
growit-env/config.json의 ssh 섹션을 읽어 SSH 터널을 생성한다.
python3 -c "
import json, subprocess, os, sys
config_path = '$CONFIG'
env_dir = '$ENV_DIR'
with open(config_path) as f:
config = json.load(f)
ssh_config = config.get('ssh')
if not ssh_config:
print('ℹ️ config.json에 ssh 설정 없음 — 터널 스킵')
sys.exit(0)
bastion = ssh_config['bastion']
key_path = os.path.join(env_dir, bastion['key'])
if not bastion.get('host'):
print('ℹ️ Bastion host 미설정 — 터널 스킵')
print(' BE가 로컬 DB를 사용하는지 확인하세요.')
sys.exit(0)
if not os.path.exists(key_path):
print(f'⚠️ SSH 키 없음: {key_path} — 터널 스킵')
sys.exit(0)
os.chmod(key_path, 0o400)
for tunnel in ssh_config.get('tunnels', []):
local_port = tunnel['local_port']
remote_host = tunnel['remote_host']
remote_port = tunnel['remote_port']
name = tunnel['name']
# 이미 활성인지 확인
result = subprocess.run(['lsof', '-i', f':{local_port}'], capture_output=True, text=True)
if 'ssh' in result.stdout:
print(f'✅ {name}: 터널 이미 활성 (localhost:{local_port})')
continue
# 기존 점유 프로세스 kill
if result.stdout.strip():
pids = set()
for line in result.stdout.strip().split(chr(10))[1:]:
parts = line.split()
if len(parts) > 1:
pids.add(parts[1])
for pid in pids:
subprocess.run(['kill', '-9', pid])
print(f'→ {name}: 포트 {local_port} 기존 프로세스 종료')
# SSH 터널 생성 (백그라운드)
cmd = [
'ssh', '-f', '-N', '-L',
f'{local_port}:{remote_host}:{remote_port}',
'-i', key_path,
'-o', 'StrictHostKeyChecking=no',
'-o', 'ServerAliveInterval=60',
'-p', str(bastion['port']),
f\"{bastion['user']}@{bastion['host']}\"
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print(f'✅ {name}: 터널 생성 (localhost:{local_port} → {remote_host}:{remote_port})')
else:
print(f'❌ {name}: 터널 실패 — {result.stderr.strip()}')
print(f' 네트워크/VPN 상태를 확인하세요.')
"
PORTS=(8080 3000 8081)
SERVICE_NAMES=("BE" "FE" "APP")
for i in "${!PORTS[@]}"; do
PORT=${PORTS[$i]}
NAME=${SERVICE_NAMES[$i]}
PID=$(lsof -ti :$PORT 2>/dev/null)
if [ -n "$PID" ]; then
echo "⚠️ $NAME 포트 $PORT 사용 중 (PID: $PID) — 종료합니다"
kill -9 $PID 2>/dev/null
fi
done
중요:
SPRING_PROFILES_ACTIVE는 비워둔다 (기본application.yml사용).application.yml은${SPRING_DATASOURCE_URL}등 환경변수를 참조하며,.env.development의SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5433/postgres로 SSH 터널을 통해 RDS에 연결된다.
application-local.yml(profile=local)은 로컬 Docker PostgreSQL용 (localhost:5432, DB명 growit_dev). SSH 터널을 사용할 때는 local 프로필을 사용하지 않는다.
BE_DIR="$BASE_DIR/DDD-12-GROWIT-BE"
if [ -f "$BE_DIR/gradlew" ]; then
echo "→ BE 서버 기동 중..."
# .env.development를 환경변수로 export
set -a
source "$BE_DIR/.env.development"
set +a
# SPRING_PROFILES_ACTIVE가 비어있으면 기본 application.yml 사용 (env var 기반)
# local 프로필은 사용하지 않음 (hardcoded DB URL 충돌 방지)
# Gradle bootRun (백그라운드)
(cd "$BE_DIR" && ./gradlew :app:bootRun > "$LOG_DIR/be.log" 2>&1) &
BE_PID=$!
echo " PID: $BE_PID, 로그: .logs/be.log"
fi
FE_DIR="$BASE_DIR/DDD-12-GROWIT-FE"
if [ -d "$FE_DIR" ]; then
echo "→ FE 서버 기동 중..."
(cd "$FE_DIR" && npm run dev > "$LOG_DIR/fe.log" 2>&1) &
FE_PID=$!
echo " PID: $FE_PID, 로그: .logs/fe.log"
fi
APP은
DDD-12-GROWIT-APP/growit-mobile/서브디렉토리에서 실행.yarn start(=expo start) 사용.
APP_DIR="$BASE_DIR/DDD-12-GROWIT-APP/growit-mobile"
if [ -d "$APP_DIR" ]; then
echo "→ APP Metro bundler 기동 중..."
(cd "$APP_DIR" && yarn start > "$LOG_DIR/app.log" 2>&1) &
APP_PID=$!
echo " PID: $APP_PID, 로그: .logs/app.log"
fi
echo "→ BE 헬스체크 대기 중..."
for i in $(seq 1 60); do
if curl -s http://localhost:8080/actuator/health > /dev/null 2>&1; then
echo "✅ BE 헬스체크 통과"
break
fi
if [ $i -eq 60 ]; then
echo "⚠️ BE 60초 내 응답 없음 — 로그 확인: tail -f .logs/be.log"
fi
sleep 1
done
echo "→ FE 헬스체크 대기 중..."
for i in $(seq 1 15); do
if curl -s http://localhost:3000 > /dev/null 2>&1; then
echo "✅ FE 헬스체크 통과"
break
fi
if [ $i -eq 15 ]; then
echo "⚠️ FE 15초 내 응답 없음 — 로그 확인: tail -f .logs/fe.log"
fi
sleep 1
done
🟢 로컬 개발 서버 기동 완료
| 서비스 | 상태 | URL | 로그 |
|--------|------|-----|------|
| SSH Tunnel | ✅/⚠️ | localhost:5433 → RDS | — |
| BE | ✅/⚠️ | http://localhost:8080 | .logs/be.log |
| FE | ✅/⚠️ | http://localhost:3000 | .logs/fe.log |
| APP | ✅/⏭️ | Expo Metro Bundler | .logs/app.log |
💡 유용한 명령어:
- BE 로그: tail -f ~/Desktop/growit/.logs/be.log
- FE 로그: tail -f ~/Desktop/growit/.logs/fe.log
- APP 로그: tail -f ~/Desktop/growit/.logs/app.log
- 터널 상태: lsof -i :5433
- 전체 중지: kill $(lsof -ti :8080,:3000,:8081,:5433) 2>/dev/null
SSH 터널 사용 (기본):
.env.development → SPRING_PROFILES_ACTIVE= (비어있음)
application.yml이 ${SPRING_DATASOURCE_URL} 읽음 → localhost:5433 (터널) → RDS
로컬 Docker PostgreSQL 사용:
SPRING_PROFILES_ACTIVE=local 설정
application-local.yml 적용 → localhost:5432/growit_dev (hardcoded)
이 경우 SSH 터널 불필요
/settings 먼저 실행 안내chmod +x 자동 실행npm install / yarn install 자동 실행