이 시리즈의 마지막 편은 성공담이 아니라 정직한 중간 점검입니다. 기능은 다 만들었고 테스트도 24건이 통과하지만, 그게 "배포해도 안전하다"는 뜻은 아니라는 걸 스스로 짚고 넘어가려고 합니다.
로컬 개발은 H2(인메모리)로 했지만, 배포 환경은 MySQL + nginx + Spring Boot 세 컨테이너로 구성했습니다.
services:
mysql:
image: mysql:8.0
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
backend:
build: ./backend
depends_on:
mysql:
condition: service_healthy
environment:
SPRING_PROFILES_ACTIVE: mysql
# DB 접속 정보·AWS 자격증명은 환경변수로 주입 (실제 값은 .env 등 별도 관리)
frontend:
build: ./frontend # vite 빌드 → nginx 정적 서빙
depends_on:
- backend
nginx가 정적 번들을 서빙하면서 /api, /swagger-ui 요청을 백엔드로 프록시하도록 구성했습니다. 이렇게 하면 프론트가 동일 출처 상대경로(/api/v1)를 쓰게 되어, 배포 환경에서 CORS 설정 자체가 필요 없어집니다. 개발 중엔 Vite dev server가 이 프록시 역할을 대신합니다.
여기서 중요하게 생각한 건, "다 만들었다"와 "다 확인했다"를 섞지 않는 것이었습니다.
| 항목 | 상태 |
|---|---|
| 견적 산출 로직 (경계값·필터) | ✅ 단위 테스트 |
| REST API 계약·에러 포맷 | ✅ 통합 테스트 |
| PDF 내용 검증 | ✅ 텍스트 재추출 테스트 |
| 화면 E2E (샘플 데이터 기준) | ✅ Playwright |
| Docker 이미지 빌드 | ❌ 아직 한 번도 빌드해본 적 없음 |
| MySQL 실제 동작 | ❌ 로컬은 계속 H2로만 돌렸음 |
| 벤더 API 실제 응답 파싱 | ❌ 로직만 구현, 실 계정 검증 전 |
이 표를 만들면서 스스로도 좀 찔렸습니다. docker-compose.yml을 다 써놓고 "이대로 배포하면 되겠다"고 생각하기 쉬운데, 실제로는 한 번도 실행해보지 않은 설정 파일이었습니다. 코드 리뷰만으로는 못 잡는 문제들이 있다는 걸 알기 때문에, 배포 전에 반드시 로컬에서 직접 띄워봐야 한다고 결론 내렸습니다.
실행은 못 해봤지만, 코드를 다시 읽으면서 "이거 터질 것 같다"고 예상한 지점들은 미리 적어뒀습니다.
1. 타임존 불일치 가능성
견적 생성 시각은 OffsetDateTime으로 저장하는데, MySQL에는 타임존을 인식하는 컬럼 타입이 없습니다. 이력 조회의 기간 필터가 시스템 기본 타임존 기준으로 시작/종료 시각을 계산하는데:
public OffsetDateTime fromDateTime() {
return from.atStartOfDay(ZoneId.systemDefault()).toOffsetDateTime();
}
컨테이너의 기본 타임존과 DB 접속 URL의 타임존 설정이 어긋나면, "오늘 만든 견적이 오늘 날짜 필터에서 안 나온다"는 식의 버그가 생길 수 있습니다. 이건 H2로 돌리는 테스트로는 절대 드러나지 않는 종류의 문제라, MySQL 프로파일로 직접 확인이 필요한 1순위 항목입니다.
2. 컨테이너 시작 순서
depends_on은 컨테이너가 "시작됐는지"만 확인하지 "요청을 받을 준비가 됐는지"는 보장하지 않습니다. mysql에는 헬스체크를 걸어서 backend가 실제로 DB가 준비될 때까지 기다리게 했지만, nginx → backend 사이에는 아직 같은 안전장치가 없습니다. nginx가 시작 시점에 백엔드 호스트명을 한 번만 확인하는 구조라면, 백엔드가 아직 안 떴을 때 nginx 자체가 죽는 상황이 생길 수 있습니다.
이 목록을 정리하면서 내린 결론은 하나입니다. "배포 스크립트가 있다"와 "배포할 준비가 됐다"는 다르다. 아래 순서를 지키기로 했습니다.
docker compose up -d --build로 이미지가 실제로 빌드되고 컨테이너 3개가 뜨는지 확인배포처를 정하는 논의는 이 검증이 끝난 다음 순서라고 판단했습니다. 검증되지 않은 설정 파일 위에 도메인과 서버를 얹는 건, 문제를 확인할 기회만 뒤로 미루는 일이라고 생각했기 때문입니다.
이 프로젝트에서 반복해서 배운 것은 크게 세 가지였습니다.
여기까지 읽어주셔서 감사합니다. 저장소는 공개되어 있으니, 코드 레벨의 디테일이 궁금하시면 직접 살펴보셔도 좋습니다.