Oracle XE 11g + Tomcat 8.5 + Angular 6 + Nginx를 Docker Compose로 재현하는 포트폴리오 사이드 프로젝트. 핵심 산출물은 동작 코드가 아니라 트러블슈팅 기록 + MIGRATION-GUIDE.md입니다.
레거시 Java/Tomcat WAR 애플리케이션을 Oracle 11g 데이터베이스와 함께 컨테이너 환경으로 재현하고, 실전 서버 이관(expdp/impdp) 절차를 학습·기록한다.
- 스택: Oracle XE 11g / Tomcat 8.5 + JDK8 / Angular 6 / Nginx
- 방법론: 2-패스 (thin-slice → 견고화), 부트스트랩과 수동 이관 분리
- 목표: 1커맨드 재현 + 실시간 트러블슈팅 로그 + 채용 심사용 공개 레포
전 구간 구성(프론트→Nginx(SSL)→Tomcat→Oracle) + Data Pump 이관 흐름 + 트러블슈팅 요약. 원본: docs/architecture.svg
브라우저
│ HTTPS (443)
▼
┌──────────────┐
│ Nginx │ nginx:stable
│ (reverse │ - 정적 파일 (Angular dist/)
│ proxy) │ - /api/ → tomcat:8080/api/
└──────┬───────┘
│ HTTP (8080)
▼
┌──────────────┐
│ Tomcat │ tomcat:8.5-jdk8
│ 8.5 + JDK8 │ - ROOT.war (MembersServlet, context=/)
│ │ - ojdbc8.jar (단일 파일 마운트)
└──────┬───────┘
│ JDBC (1521)
▼
┌──────────────┐
│ Oracle XE │ gvenzl/oracle-xe:11 ← regular (slim 아님)
│ 11g (XE) │ - oradata 영속 볼륨
│ │ - /container-entrypoint-initdb.d init SQL
└──────────────┘
[별도] Angular 빌드
┌──────────────┐
│ angular-build│ node:10 (sha256 다이제스트 핀)
│ (1회성) │ npm ci && ng build --prod → frontend/dist/
└──────────────┘
주의: Oracle 이미지는 반드시
gvenzl/oracle-xe:11(regular) 사용.gvenzl/oracle-xe:11-slim은 Data Pump(expdp/impdp)가 차단됨 (XSL 스타일시트 제거).
# PowerShell 권장 (Git Bash는 경로 앞 /를 자동 변환하여 docker 마운트 오작동 가능)
# 1. 저장소 클론
git clone <repo-url>
cd <repo-dir>
# 2. 환경변수 설정
cp .env.example .env
# .env 파일을 열어 비밀번호 설정:
# ORACLE_PASSWORD=<SYS/SYSTEM 비밀번호>
# APP_USER_PASSWORD=<앱 유저 비밀번호>
# TOMCAT_HOST_PORT=8080 # 포트 충돌 시 변경 (예: 18080)
# 3. ojdbc8.jar 배치 (바이너리 미커밋 — 스크립트로 재현)
bash scripts/fetch-ojdbc.sh
# 결과: tomcat/lib/ojdbc8.jar
# 4. Nginx 자체서명 인증서 생성
bash scripts/gen-cert.sh
# 결과: nginx/certs/migration.local.crt, nginx/certs/migration.local.key
# 5. hosts 파일에 로컬 도메인 등록 (HTTPS 접속용)
# Windows: C:\Windows\System32\drivers\etc\hosts (관리자 권한)
# Linux/Mac: /etc/hosts
# 추가할 줄: 127.0.0.1 migration.local# maven:3-jdk-8 컨테이너로 빌드 (로컬 Maven/JDK 설치 불필요)
# PowerShell에서 실행 — Git Bash는 경로 변환 이슈로 오작동 가능
docker run --rm -v "${PWD}/backend:/app" -w /app maven:3-jdk-8 `
mvn -DskipTests clean package
# 결과: backend/target/migration.war# Step 1: Oracle 기동 (부트스트랩 — 멱등 init SQL 자동 실행)
docker compose up -d oracle
# Oracle 준비 대기: "DATABASE IS READY TO USE!" 로그 확인
docker compose logs -f oracle
# Step 2: Tomcat 기동 (WAR + ojdbc 마운트)
docker compose --profile app up -d tomcat
# 동작 확인
curl http://localhost:${TOMCAT_HOST_PORT:-8080}/api/members # → 200 + JSON
# Step 3: Angular 빌드 (1회성 — dist/ 생성)
docker compose --profile build run --rm angular-build
# Step 4: Nginx 기동 (Angular 정적 파일 + API 프록시)
docker compose --profile app up -d nginx
# Step 5: 전 구간 확인
curl http://localhost/api/members # Nginx 경유
curl -k --resolve migration.local:443:127.0.0.1 https://migration.local/ # HTTPS Angular
curl -k --resolve migration.local:443:127.0.0.1 https://migration.local/api/members # HTTPS API# 볼륨 포함 완전 초기화 — init SQL이 재실행됨
docker compose down -v
docker compose up -d oracle| 구분 | 방법 | 스크립트 | 목적 |
|---|---|---|---|
| 부트스트랩 (멱등 init) | docker compose up oracle |
oracle/init/*.sql |
개발 환경 자동 초기화 (1커맨드 재현) |
| 수동 이관 데모 | expdp → impdp | scripts/export-dump.sh, scripts/import-dump.sh |
실전 Oracle 데이터 이관 학습 |
- 부트스트랩은
oradata볼륨이 없을 때 첫 기동에만 실행된다. - 수동 이관은 별도 검증용 컨테이너에서 실행하며, 앱 스택의 Oracle과 분리한다.
- 두 경로를 혼용하지 않는다.
docs/MIGRATION-GUIDE.mdDay4 참조.
ojdbc8.jar 및 Nginx 인증서는 라이선스·보안 이유로 저장소에 포함되지 않는다. 아래 스크립트로 재현한다:
# ojdbc8.jar — Maven Central (com.oracle.database.jdbc:ojdbc8:21.9.0.0) → tomcat/lib/
bash scripts/fetch-ojdbc.sh
# Nginx 자체서명 인증서 생성 → nginx/certs/ 배치 (CN=migration.local)
bash scripts/gen-cert.sh실전 환경에서는 공인 인증서(Let's Encrypt 등)로 교체한다.
docs/MIGRATION-GUIDE.md Day6 참조.
실제 통합 과정에서 발생한 버그. 상세 내용은 docs/TROUBLESHOOTING.md 참조.
| # | 증상 | 원인 | 해결 |
|---|---|---|---|
| 1 | WAR 배포 후 404 | target/ 디렉터리 전체 :ro 마운트 → WAR 압축해제 불가 |
ROOT.war 단일 파일 마운트 |
| 2 | Tomcat 기동 불가 (ClassNotFoundException: Catalina) |
tomcat/lib/ 디렉터리 전체 마운트 → 기존 JAR 가림 |
ojdbc8.jar 단일 파일 마운트 |
| 3 | 서블릿 배포 실패 (url-pattern not permitted) |
@WebServlet 애너테이션 + web.xml 이중 매핑 |
web.xml 단일 출처, 애너테이션 제거 |
| 4 | JNDI 비밀번호 미해석 (ORA-01017) |
context.xml ${VAR} 는 OS env 아닌 JVM 시스템 프로퍼티에서 치환 |
setenv.sh에서 -DAPP_USER_PASSWORD 승격 |
| 5 | 커넥션 풀 실패 (ORA-01882: timezone region not found) |
JVM 타임존 ID를 Oracle 11g 구형 tz 테이블이 미인식 | setenv.sh에 -Duser.timezone=UTC 추가 |
| 6 | 포트 8080 충돌 | 호스트 기존 서비스 점유 | .env TOMCAT_HOST_PORT=18080 가변화 |
| 7 | expdp ORA-39006/ORA-39213 |
gvenzl/oracle-xe:11-slim은 Data Pump XSL 스타일시트 제거 |
gvenzl/oracle-xe:11 (regular)로 교체 |
| 8 | impdp ORA-31684 USER 충돌 |
gvenzl APP_USER 자동 생성과 impdp DDL 충돌 | 양성 경고, TABLE_EXISTS_ACTION=REPLACE 처리 |
| 9 | ng build 실패 (@angular-devkit not found) |
devDependencies 누락 | @angular-devkit/build-angular@~0.8.9 추가 |
| 10 | ng build 실패 (ENOENT tsconfig.app.json) |
angular.json 경로 불일치 + 누락 파일 |
angular.json 경로 정합 + 파일 생성 |
| 11 | Nginx /api/ → 502/404 |
proxy_pass에 잘못된 컨텍스트 경로 |
proxy_pass http://tomcat:8080/api/ (ROOT.war, context=/) |
| 12 | 화면 "가입일" 컬럼 전부 - |
API created_at(snake) ↔ 프론트 createdAt(camel) 키 불일치 |
서블릿 JSON 키를 createdAt로 정렬 |
서버이관(오라클)/
├─ docker-compose.yml # 4서비스, profiles로 단계 기동
├─ .env.example # 환경변수 템플릿 (cp → .env)
├─ .gitignore
├─ oracle/init/ # 멱등 init SQL (첫 기동 자동 실행)
├─ backend/ # Maven WAR (서블릿 + JDBC/JNDI)
├─ frontend/ # Angular 6 앱
├─ tomcat/ # context.xml, setenv.sh, lib/ojdbc8.jar
├─ nginx/ # default.conf, certs/
├─ dumps/ # expdp 산출 .dmp (gitignore)
├─ scripts/ # fetch-ojdbc.sh, gen-cert.sh, export/import-dump.sh
└─ docs/
├─ MIGRATION-GUIDE.md # 단계별 이관 절차 + 완료 게이트 + 컨테이너→실전 매핑
├─ TROUBLESHOOTING.md # 실시간 트러블슈팅 로그 (🔴🟠🟡, 13건)
├─ architecture.svg # 시스템 아키텍처 다이어그램
├─ evidence/ # 이관 증거 (expdp/impdp, 부트스트랩+assert)
└─ screenshots/ # UI / API / 아키텍처 캡처
└─ ROLLBACK.md # Day별 복구 노트 + Open Questions 종결 상태
docs/MIGRATION-GUIDE.md— 이관 가이드 (Day0~Day6 + 완료 게이트 체크리스트 + 매핑 표)docs/TROUBLESHOOTING.md— 트러블슈팅 실시간 로그 (13건)docs/evidence/— 이관 증거 (expdp/impdp 복원 검증, Oracle 부트스트랩+assert)docs/architecture.svg— 시스템 아키텍처 다이어그램docs/runbook/— 실 서버 이관 런북 (클라우드 인프라·보안그룹·OS Oracle 설치·Eclipse 빌드·SSL·3개월 모니터링) — Docker 데모를 실 클라우드 과업으로 옮기는 단계별 절차서docs/ROLLBACK.md— 복구 절차 + Open Questions 종결 상태






