Files
GA Pro 035175e895 docs: Agent Teams 실행 가이드 + 8개 도메인 spec 추가
00_PROMPTS.md  — Claude Code 실행 프롬프트 1~6
01_DB_SCHEMA.md — V1~V17 Flyway 스키마 + 프로젝트 뼈대
02_COMMON_SPEC.md — ga-common 공통 프레임워크 (모델/예외/MyBatis/AOP/엑셀/공통컴포넌트)
03_CORE_SPEC.md — ga-core VO 설계 원칙 + Mapper 패턴
04_BATCH_SPEC.md — ga-batch 정산 8 Step + DataReceiver/MappingEngine
05_API_SPEC.md — ga-api Controller/Service 표준 패턴 + 엔드포인트 목록
06_FRONTEND_SPEC.md — ga-frontend 13개 화면 + 공통 컴포넌트 사용법
07_INFRA_SPEC.md — Docker/Nginx + README + DEVELOPER_GUIDE
08_DBA_SPEC.md — V13~V15 인덱스/파티셔닝/뷰

실제 구현된 ga-commission-system 기준으로 작성. 코드는 변경 없음.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 21:15:13 +09:00

9.1 KiB

07_INFRA_SPEC — Docker / Nginx / 문서

1. docker-compose.yml

루트에 위치. 5 서비스: postgres / redis / api / batch / frontend.

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: ga
      POSTGRES_USER: ga
      POSTGRES_PASSWORD: ga
      TZ: Asia/Seoul
    ports: ["5432:5432"]
    volumes: [ga-postgres-data:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ga -d ga"]
      interval: 10s

  redis:
    image: redis:7-alpine
    ports: ["6379:6379"]
    volumes: [ga-redis-data:/data]

  api:
    build:
      context: .
      dockerfile: Dockerfile
      args: { MODULE: ga-api }
    depends_on:
      postgres: { condition: service_healthy }
      redis:    { condition: service_started }
    environment:
      SPRING_PROFILES_ACTIVE: docker
      SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/ga
      SPRING_DATASOURCE_USERNAME: ga
      SPRING_DATASOURCE_PASSWORD: ga
      SPRING_DATA_REDIS_HOST: redis
      JWT_SECRET:  ${JWT_SECRET:-please-change-this-jwt-secret-must-be-256-bits}
      ENCRYPT_KEY: ${ENCRYPT_KEY:-please-change-this-32-byte-key!}
    ports: ["8080:8080"]

  batch:
    build: { context: ., dockerfile: Dockerfile, args: { MODULE: ga-batch } }
    depends_on:
      postgres: { condition: service_healthy }
      redis:    { condition: service_started }
      api:      { condition: service_started }
    environment: # api 와 동일
      ...
    ports: ["8081:8081"]

  frontend:
    build: { context: ./ga-frontend, dockerfile: Dockerfile }
    depends_on: [api]
    ports: ["3000:80"]

volumes: { ga-postgres-data: , ga-redis-data: }

2. Dockerfile (루트, ga-api/ga-batch 공용)

멀티스테이지: Gradle 빌드 → 슬림 JRE.

# stage 1: builder
FROM gradle:8.6-jdk17 AS builder
WORKDIR /workspace
COPY settings.gradle build.gradle gradle.properties ./
COPY ga-common/build.gradle  ga-common/
COPY ga-core/build.gradle    ga-core/
COPY ga-api/build.gradle     ga-api/
COPY ga-batch/build.gradle   ga-batch/
COPY ga-admin/build.gradle   ga-admin/
RUN gradle dependencies --no-daemon || true

COPY . .
ARG MODULE=ga-api
RUN gradle :${MODULE}:bootJar --no-daemon -x test
RUN cp /workspace/${MODULE}/build/libs/${MODULE}.jar /workspace/app.jar

# stage 2: runtime
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
RUN apk add --no-cache tzdata && cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime
COPY --from=builder /workspace/app.jar /app/app.jar
EXPOSE 8080 8081
ENTRYPOINT ["java","-XX:+UseContainerSupport","-XX:MaxRAMPercentage=75","-jar","/app/app.jar"]

3. ga-frontend/Dockerfile + nginx.conf

# ga-frontend/Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci || npm install
COPY . .
RUN npm run build      # → /app/dist

FROM nginx:1.27-alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
# ga-frontend/nginx.conf
server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;

    # SPA fallback
    location / {
        try_files $uri $uri/ /index.html;
    }

    # API 프록시
    location /api/ {
        proxy_pass http://api:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        client_max_body_size 100m;
        proxy_read_timeout 300s;          # 엑셀 대용량 다운로드 대비
    }

    # 정적 자원 캐시
    location ~* \.(?:css|js|woff2?|ttf|svg|png|jpg|jpeg|gif|ico)$ {
        expires 7d;
        add_header Cache-Control "public, max-age=604800, immutable";
    }
}

4. README.md (루트)

작성 항목 (현재 README.md 가 이미 갖춘 형태):

  • 기술 스택 요약 표
  • 모듈 구조 다이어그램
  • 시나리오 A — 운영 DB(192.168.0.60:55432/trading_ai/ga) 연결
    • Gradle wrapper 생성 → bootJarbootRun --args='--spring.profiles.active=trading_ai'
    • 환경변수 DB_PASSWORD, REDIS_HOST 오버라이드
  • 시나리오 B — 로컬 PostgreSQL (개발용)
    • docker compose up -d postgres redisbootRun
  • 시나리오 C — Docker Compose 전체 (운영 흐름)
    • docker compose up -d --build
  • DB 마이그레이션 직접 실행 (Python migrate.py)
  • 데이터 확인 표 (보험사 5 / 상품 20 / 설계사 50 / 계약 200 / ...)
  • 트러블슈팅 FAQ
    • gradle wrapper --gradle-version 8.6
    • Failed to determine a suitable driver class → 프로파일 확인
    • NoSuchKey: gacurrentSchema=ga
    • 401 토큰 만료 → 재로그인
    • AG Grid 빈 화면 → CSS import 확인
  • 주요 기능 (백엔드/도메인/배치 8 Step 요약)
  • 보안 (JWT/AES/RBAC/감사로그)
  • 진행 상황 체크리스트
  • 향후 확장
  • 외부 시스템 연결 정보

5. DEVELOPER_GUIDE.md (신입 개발자 온보딩)

1. 환경 설정

  • JDK 17 (java -version 확인)
  • Node 20 + npm
  • PostgreSQL 14+ (운영 또는 Docker)
  • Redis 7+ (선택)
  • IntelliJ IDEA Community + Lombok 플러그인 (자동 설치 권장)

2. 프로젝트 구조 한 장 다이어그램

ga-common ─┬─> ga-core ─┬─> ga-api    (REST API, 8080)
           │            ├─> ga-batch  (Spring Batch, 8081)
           │            └─> ga-admin  (예약 모듈)
           └─> 다른 모듈 모두 의존 (web/security/aop/cache/mybatis/excel...)

ga-frontend (독립 빌드, dist → nginx)

3. 새 화면 만들기 Step-by-Step (예: "공지사항")

Step 1 — DB: notice 테이블 + 메뉴/권한 등록 (V18 마이그레이션)

Step 2 — VO (ga-core/vo/notice/):

  • NoticeVO.java (테이블 1:1)
  • NoticeResp.java (조인 결과)
  • NoticeSaveReq.java (@Valid 검증)
  • NoticeSearchParam.java extends SearchParam

Step 3 — Mapper:

  • mapper/notice/NoticeMapper.java (@Mapper)
  • resources/mapper/notice/NoticeMapper.xml (기존 AgentMapper.xml 복사 후 수정)

Step 4 — Service (ga-api/service/notice/):

  • NoticeService.java (단순 CRUD 패턴 + @Transactional)

Step 5 — Controller:

  • NoticeController.java@RequirePermission(menu="NOTICE", perm="...")
  • CUD 메서드에 @DataChangeLog

Step 6 — 프론트 (ga-frontend/src/):

  • api/notice.ts — 함수 5개 (list/detail/create/update/delete)
  • pages/notice/NoticeList.tsx06_FRONTEND_SPEC.md §7 패턴
  • pages/notice/NoticeForm.tsx — Ant Form

Step 7 — 메뉴 등록:

  • system/menus 화면에서 메뉴/권한 등록 → 사용자 역할에 권한 부여

4. 자주 쓰는 패턴

  • 공통코드 드롭다운: <CodeSelect groupCode="..." />
  • 엑셀 다운로드: <ExcelExportButton url={...} fileName={...} showProgress />
  • 상태 뱃지: <CodeBadge groupCode="..." value={status} />
  • 권한 버튼: <PermissionButton menuCode permCode>...</PermissionButton>
  • AG Grid 서버사이드: <DataGrid dataSource={p => api.list(p)} />

5. 코드 컨벤션

  • VO 네이밍: XxxVO / XxxSearchParam / XxxSaveReq / XxxResp / XxxExcelVO
  • Mapper XML: XxxMapper.xml (resources/mapper/{도메인}/)
  • API URL: /api/{리소스}s (복수형, kebab-case)
  • 금액: 항상 BigDecimal + MoneyUtil 헬퍼
  • 날짜: LocalDate / LocalDateTime (java.util.Date 금지)
  • 예외: BizException(ErrorCode.XXX) (RuntimeException 직접 throw 금지)
  • 로그: lombok @Slf4j + log.info("msg, key={}", value) (한글 메시지 OK)

6. 트러블슈팅 FAQ

  • 권한 에러: @RequirePermission 의 menuCode 가 menu_permission 에 등록되어 있는지 + 사용자 역할에 권한 부여됐는지
  • 공통코드 캐시 안 맞음: CommonCodeService.refreshCache(groupCode) 또는 Redis FLUSH
  • MyBatis 매핑 에러: ResultMap 의 column / property 명 + mapUnderscoreToCamelCase 확인
  • 암호화 컬럼 평문 노출: EncryptTypeHandler 가 컬럼별 명시(typeHandler 속성)되어 있는지
  • JSONB null: JsonTypeHandler@MappedTypes(JsonNode.class) 로 등록됐는지
  • PageHelper 안 먹음: param.startPage() 호출 후 SELECT 1회만 + 같은 트랜잭션
  • AG Grid 빈 화면: @ag-grid-community/styles/ag-theme-quartz.css import 확인

6. 통합 검증 체크리스트

# 1) 빌드
./gradlew clean build           # 모든 모듈 컴파일 + 단위 테스트 통과
cd ga-frontend && npm install && npm run build  # → dist/

# 2) Docker 전체 기동
docker compose up -d --build

# 3) Smoke
curl http://localhost:8080/api/health           # {"status":"UP"}
curl http://localhost:3000/                     # nginx 200
# 브라우저: admin / admin1234! 로그인 → 메뉴 트리 보임 → 설계사 50 행 표시

# 4) 정산 배치
curl -X POST -H "Authorization: Bearer $TOKEN" \
     "http://localhost:8081/api/batch/settlement/run?settleMonth=202605"

# 5) 태그
git tag v1.0.0-alpha

기대 결과:

  • Flyway V1~V17 자동 적용
  • ga-api 8080 / ga-batch 8081 / frontend 3000 모두 OK
  • 단위테스트 76+ 건 통과
  • 운영 DB 시나리오 A 도 동일 동작 (--spring.profiles.active=trading_ai)