Spring Boot 3.5.0 + Java 21 기반 엔터프라이즈 프레임워크 - Auth: JWT, MFA/TOTP, OAuth2 SSO, PasswordPolicy, LoginHistory - RBAC: Role-Permission, DeptScope, Redis 캐시 - Approval: Handler 패턴 결재 엔진 - Notification: SSE 실시간 알림 - File: Local/Azure Blob/AWS S3/GCP Storage - DataSource: Multi-DB 라우팅 (MySQL/PG/Oracle/MSSQL) - Admin: Thymeleaf 11화면 콘솔 - Compat: WBX FastAPI 호환 (에러/페이징) - Flyway: 4종 DBMS 마이그레이션 - Scripts: install.bat/sh, deploy-prod.sh - Docs: 설치가이드(OnPremise/Cloud), 개발자가이드 PDF Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
18 KiB
18 KiB
WBX Spring Framework — 설치/배포 가이드
소스:
D:\WBX02\wbx-spring-core기술: Spring Boot 3.5.0 + Java 21 + MySQL/PG/Oracle/MSSQL + Redis
1. 사전 요구사항
1-1. 서버 사양
| 항목 | 최소 사양 | 권장 사양 |
|---|---|---|
| OS | RHEL 8+ / Ubuntu 22.04+ / Windows Server 2019+ | RHEL 9 / Rocky Linux 9 |
| CPU | 4 vCPU | 8 vCPU |
| RAM | 8 GB | 16 GB |
| Disk | 50 GB SSD | 100 GB SSD |
| Network | 1 Gbps | 10 Gbps |
1-2. 필수 소프트웨어
| SW | 버전 | 용도 | 필수 |
|---|---|---|---|
| JDK | 21 LTS (Eclipse Temurin 권장) | Spring Boot 런타임 | ✅ |
| Git | 2.x | 소스코드 관리 | ✅ |
| MySQL | 8.0+ | DB (택 1) | ✅ |
| PostgreSQL | 14+ | DB (택 1) | 선택 |
| Oracle | 19c+ | DB (택 1) | 선택 |
| MSSQL | 2019+ | DB (택 1) | 선택 |
| Redis | 7.x | 캐시/세션 | 권장 |
| Docker | 24+ | 컨테이너 배포 | 선택 |
| Nginx/Caddy | latest | 리버스 프록시 (프로덕션) | 선택 |
Gradle은 Wrapper(
gradlew)가 자동 다운로드하므로 별도 설치 불필요
1-3. 포트
| 포트 | 서비스 | 접근 범위 |
|---|---|---|
| 443 | HTTPS (Nginx/Caddy) | 외부 |
| 80 | HTTP → 443 리다이렉트 | 외부 |
| 8080 | Spring Boot | 내부 (localhost) |
| 3306 | MySQL | 내부 |
| 5432 | PostgreSQL | 내부 |
| 1521 | Oracle | 내부 |
| 1433 | MSSQL | 내부 |
| 6379 | Redis | 내부 (bind 127.0.0.1) |
2. JDK 21 설치
Windows
# 방법 A: Winget (권장)
winget install EclipseAdoptium.Temurin.21.JDK
# 방법 B: ZIP 수동 설치
# https://adoptium.net/temurin/releases/?version=21 에서 다운로드
# 환경변수 설정
[System.Environment]::SetEnvironmentVariable('JAVA_HOME', 'D:\tools\jdk-21.0.6+7', 'User')
[System.Environment]::SetEnvironmentVariable('Path', $env:Path + ';D:\tools\jdk-21.0.6+7\bin', 'User')
java -version
Linux (RHEL/Rocky/CentOS)
sudo dnf install -y java-21-openjdk-devel
# 또는 Temurin
sudo dnf install -y https://packages.adoptium.net/artifactory/rpm/centos/9/$(uname -m)/Packages/temurin-21-jdk-*.rpm
echo 'export JAVA_HOME=/usr/lib/jvm/temurin-21-jdk' >> ~/.bashrc
source ~/.bashrc && java -version
Ubuntu/Debian
sudo apt install -y wget apt-transport-https gpg
wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | sudo gpg --dearmor -o /usr/share/keyrings/adoptium.gpg
echo "deb [signed-by=/usr/share/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/adoptium.list
sudo apt update && sudo apt install -y temurin-21-jdk
java -version
macOS
brew install temurin@21
java -version
3. 소스코드 & 로컬 개발
3-1. 소스코드 가져오기
git clone https://github.com/accura0117/wbx-spring-core.git
cd wbx-spring-core
3-2. 빠른 설치 (설치 스크립트)
스크립트가 JDK/Git/Docker 사전 검사, 빌드, .env 템플릿 생성, 디렉토리 생성을 자동 처리합니다.
# Windows
scripts\install.bat
# Linux/macOS
chmod +x scripts/install.sh && ./scripts/install.sh
3-3. 로컬 DB 생성 (Docker Compose 또는 직접)
# 방법 A: Docker Compose (권장)
docker compose -f docker-compose-dev.yml up -d
# → MySQL(:3306) + Redis(:6379) 자동 시작
# PostgreSQL 사용 시:
docker compose -f docker-compose-dev.yml --profile pg up -d
# → PostgreSQL(:5432) + Redis(:6379) 자동 시작
# 방법 B: 직접 설치 후 DB 생성
# MySQL
CREATE DATABASE wbx_spring CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wbxapp'@'localhost' IDENTIFIED BY 'password';
GRANT ALL ON wbx_spring.* TO 'wbxapp'@'localhost';
# PostgreSQL
CREATE USER wbxapp WITH PASSWORD 'password';
CREATE DATABASE wbx_spring OWNER wbxapp ENCODING 'UTF8';
3-4. 빌드 & 실행
설치 스크립트를 사용하지 않는 경우 수동으로 빌드합니다.
# Windows
gradlew.bat build -x test
gradlew.bat bootRun
# Linux/macOS
chmod +x gradlew
./gradlew build -x test
./gradlew bootRun
3-5. 확인
기본 context-path는
/spring입니다. 독립 도메인 시SERVER_CONTEXT_PATH=/로 변경.
http://localhost:8080/spring/health → {"status":"ok","app":"wbx-spring"}
http://localhost:8080/spring/admin/login → Admin Console
http://localhost:8080/spring/swagger-ui → Swagger UI
3-6. 초기 관리자 등록
curl -X POST http://localhost:8080/spring/api/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"admin@company.com","username":"admin","password":"Admin1234!","fullName":"관리자","isAdmin":true}'
4. 프로필 시스템
4-1. 9개 프로필
| 프로필 | 파일 | 용도 |
|---|---|---|
| (기본) | application.yml | 로컬 개발 (MySQL, ddl-auto:update) |
| prod | application-prod.yml | 프로덕션 (Flyway 활성, Swagger 비활성, 로깅) |
| mysql | application-mysql.yml | MySQL 드라이버/Dialect/Flyway |
| postgresql | application-postgresql.yml | PostgreSQL 드라이버/Dialect/Flyway |
| oracle | application-oracle.yml | Oracle 드라이버/Dialect/Flyway |
| mssql | application-mssql.yml | MSSQL 드라이버/Dialect/Flyway |
| azure | application-azure.yml | Azure Entra SSO + Blob Storage |
| aws | application-aws.yml | AWS Cognito SSO + S3 |
| test | application-test.yml | 테스트 (H2 인메모리, Flyway 비활성) |
4-2. 프로필 조합 예시
# On-Premise MySQL
java -jar app.jar --spring.profiles.active=prod,mysql
# On-Premise Oracle
java -jar app.jar --spring.profiles.active=prod,oracle
# Azure + MSSQL
java -jar app.jar --spring.profiles.active=prod,mssql,azure
# AWS + PostgreSQL
java -jar app.jar --spring.profiles.active=prod,postgresql,aws
# 테스트
./gradlew test # application-test.yml 자동 사용
4-3. 환경변수
| 변수 | 기본값 | 설명 | 필수 |
|---|---|---|---|
JWT_SECRET |
(dev key) | JWT 시크릿 (프로덕션 필수 변경!) | ✅ |
SERVER_CONTEXT_PATH |
/spring |
독립 도메인이면 / |
|
DB_HOST |
localhost | DB 호스트 | |
DB_PORT |
3306 | DB 포트 | |
DB_NAME |
wbx_spring | DB 이름 | |
DB_USER |
wbxapp | DB 사용자 | |
DB_PASS |
password | DB 비밀번호 | ✅ |
DB_POOL_SIZE |
20 | HikariCP 커넥션 풀 | |
SPRING_DATA_REDIS_HOST |
localhost | Redis 호스트 | |
CORS_ORIGIN |
https://app.company.com | CORS 허용 도메인 | |
SPRING_PROFILES_ACTIVE |
(없음) | 프로필 조합 | ✅ |
Azure 전용
| 변수 | 설명 |
|---|---|
AZURE_CLIENT_ID |
Entra App Registration Client ID |
AZURE_CLIENT_SECRET |
Entra App Secret |
AZURE_TENANT_ID |
Azure AD Tenant ID |
AZURE_STORAGE_ACCOUNT |
Blob Storage Account Name |
AZURE_STORAGE_KEY |
Blob Storage Access Key |
AZURE_CONTAINER |
Blob Container Name (기본: uploads) |
AWS 전용
| 변수 | 설명 |
|---|---|
AWS_COGNITO_CLIENT_ID |
Cognito User Pool Client ID |
AWS_COGNITO_CLIENT_SECRET |
Cognito Client Secret |
AWS_USER_POOL_ID |
Cognito User Pool ID |
AWS_REGION |
AWS Region (기본: ap-northeast-2) |
AWS_S3_BUCKET |
S3 Bucket Name |
AWS_ACCESS_KEY |
IAM Access Key |
AWS_SECRET_KEY |
IAM Secret Key |
5. 주요 기능 활성화
5-1. MFA/TOTP (Google Authenticator 호환)
wbx:
spring:
mfa:
enabled: true # true로 활성화
force-for-external: true # 외부 사용자 강제
force-for-internal: false # 내부 사용자 (Entra CA로 대체)
totp-issuer: "WBX Platform"
활성화 시 로그인 흐름: POST /api/auth/login → mfa_required: true → POST /api/auth/mfa/verify
5-2. Azure Entra SSO
--spring.profiles.active=prod,mssql,azure + 환경변수 설정으로 자동 활성화.
OAuth2 OIDC 로그인 → WBX 호환 JWT 자동 발급 → 사용자 자동 등록.
5-3. 파일 스토리지 전환
wbx:
spring:
file:
storage-type: local # 기본: 로컬 파일시스템
# storage-type: azure-blob # Azure Blob Storage
# storage-type: aws-s3 # AWS S3
# storage-type: gcp-storage # Google Cloud Storage
5-4. Multi-DataSource 라우팅
wbx:
spring:
datasource:
routing-enabled: true # 활성화
wbxgw:
url: jdbc:mysql://localhost:3306/wbx_gw
username: wbxapp
password: password
서비스에서 @DataSource("wbxgw") 어노테이션으로 DB 전환.
6. 프로덕션 배포
6-1. 서비스 계정 (Linux)
sudo useradd -r -m -s /bin/bash wbxapp
sudo mkdir -p /opt/wbx-app
sudo chown wbxapp:wbxapp /opt/wbx-app
6-2. 타임존 · 인코딩
sudo timedatectl set-timezone Asia/Seoul
sudo localectl set-locale LANG=ko_KR.UTF-8
6-3. 시스템 리밋
# /etc/security/limits.d/wbxapp.conf
wbxapp soft nofile 65535
wbxapp hard nofile 65535
6-4. Redis 보안
sudo sed -i 's/^bind .*/bind 127.0.0.1/' /etc/redis/redis.conf
echo 'requirepass RedisP@ss123' | sudo tee -a /etc/redis/redis.conf
sudo systemctl restart redis
6-5. 배포 디렉토리
/opt/wbx-app/
├── app.jar # Spring Boot Fat JAR
├── .env # 환경변수 (시크릿)
├── logs/ # 로그
├── uploads/ # 파일 업로드 (local storage)
└── backup/ # 백업
6-6. JAR 빌드 & 배포
# 빌드 서버
./gradlew bootJar
# 운영 서버로 전송
scp build/libs/wbx-spring-core-*.jar wbxapp@server:/opt/wbx-app/app.jar
# .env 파일
cat > /opt/wbx-app/.env << 'EOF'
JWT_SECRET=your-production-secret-key-minimum-256-bits
DB_HOST=localhost
DB_PORT=3306
DB_NAME=wbx_spring
DB_USER=wbxapp
DB_PASS=StrongP@ss
SPRING_PROFILES_ACTIVE=prod,mysql
SERVER_CONTEXT_PATH=/
EOF
chmod 600 /opt/wbx-app/.env
6-7. systemd 서비스 (Linux)
# /etc/systemd/system/wbx-app.service
[Unit]
Description=WBX Spring Application
After=network.target
[Service]
Type=simple
User=wbxapp
WorkingDirectory=/opt/wbx-app
EnvironmentFile=/opt/wbx-app/.env
ExecStart=/usr/bin/java \
-XX:+UseG1GC \
-XX:MaxRAMPercentage=75.0 \
-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE} \
-jar /opt/wbx-app/app.jar
Restart=always
RestartSec=5
StandardOutput=append:/opt/wbx-app/logs/app.log
StandardError=append:/opt/wbx-app/logs/app.log
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now wbx-app
journalctl -u wbx-app -f
6-8. Windows 서비스
# NSSM으로 서비스 등록 (https://nssm.cc)
nssm install WbxSpring "D:\tools\jdk-21\bin\java.exe" "-Xmx512m -jar D:\wbx-app\app.jar --spring.profiles.active=prod,mysql"
nssm set WbxSpring AppDirectory D:\wbx-app
nssm set WbxSpring AppEnvironmentExtra JWT_SECRET=your-secret SERVER_CONTEXT_PATH=/
nssm start WbxSpring
6-9. Docker 배포
./gradlew bootJar
docker build -t wbx-spring:latest .
docker compose up -d
7. 리버스 프록시
7-1. Nginx
upstream spring_app {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name app.company.com;
ssl_certificate /etc/ssl/certs/app.pem;
ssl_certificate_key /etc/ssl/private/app.key;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Strict-Transport-Security "max-age=63072000" always;
location / {
proxy_pass http://spring_app;
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 50m;
}
# SSE 알림 (버퍼링 비활성화)
location /api/notifications/stream {
proxy_pass http://spring_app;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_read_timeout 3600s;
}
}
7-2. Caddy
app.company.com {
reverse_proxy localhost:8080
}
# 공유 도메인 (WBX + Spring Boot)
wbx.kr {
handle /spring/* {
reverse_proxy localhost:8080
}
handle {
reverse_proxy localhost:5000
}
}
7-3. WBX FastAPI 동시 운영
# Nginx — URL prefix로 분기
location /api/gw/ {
proxy_pass http://127.0.0.1:8001; # WBX FastAPI
}
location / {
proxy_pass http://127.0.0.1:8080; # Spring Boot
}
JWT SECRET_KEY를 두 시스템이 동일하게 설정해야 SSO가 작동합니다.
8. 방화벽 · 보안
Linux (firewalld)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# DB 포트는 외부 차단 (기본)
SELinux
sudo setsebool -P httpd_can_network_connect 1
보안 체크리스트
- SSH 키 기반 인증 (비밀번호 로그인 비활성화)
- 서비스 계정(wbxapp)에 sudo 권한 없음
- DB 포트 외부 차단 (localhost만 접근)
- Redis bind 127.0.0.1 + requirepass
- .env 파일 600 권한
- Swagger UI 프로덕션 비활성화 (prod 프로필 자동)
- Nginx 보안 헤더 (HSTS, X-Frame, X-Content-Type)
9. Admin Console
11개 화면, 25개 엔드포인트:
| 화면 | URL | 기능 |
|---|---|---|
| 로그인 | /admin/login | 이메일+비밀번호 폼 인증 |
| 대시보드 | /admin | 활성 사용자, 로그인 횟수, 역할 수 |
| 사용자 목록 | /admin/users | 추가, 상세 링크 |
| 사용자 상세 | /admin/users/{id} | 수정, 삭제, 잠금해제, 비밀번호초기화, 역할할당/해제 |
| 역할 목록 | /admin/roles | 추가, 삭제, 상세 링크 |
| 역할 상세 | /admin/roles/{id} | 수정, 권한 추가/삭제 |
| 권한 매트릭스 | /admin/permissions | 전체 역할-모듈-액션 현황, 삭제 |
| 로그인 이력 | /admin/login-history | 최근 50건 |
| 감사 로그 | /admin/audit-logs | 최근 100건 |
| 시스템 설정 | /admin/config | K-V 설정 추가/수정 |
| 시스템 상태 | /admin/system-health | JVM Heap, OS, Java, Spring Boot 버전 |
10. 백업 · 복구
DB 백업 (cron)
#!/bin/bash
# /opt/wbx-app/backup/db-backup.sh
DATE=$(date +%Y%m%d_%H%M%S)
mysqldump -u wbxapp -p wbx_spring | gzip > /opt/wbx-app/backup/wbx_spring_${DATE}.sql.gz
find /opt/wbx-app/backup -name '*.gz' -mtime +30 -delete
# crontab -e
0 2 * * * /opt/wbx-app/backup/db-backup.sh
앱 롤백
cp /opt/wbx-app/app.jar /opt/wbx-app/backup/app-prev.jar
# 새 JAR 배포 후 문제 발생 시:
cp /opt/wbx-app/backup/app-prev.jar /opt/wbx-app/app.jar
sudo systemctl restart wbx-app
11. 모니터링
Actuator + Prometheus
# application-prod.yml에 이미 설정됨
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
# Health Check
curl http://localhost:8080/health
curl http://localhost:8080/actuator/health
curl http://localhost:8080/actuator/prometheus # Prometheus 메트릭
# Grafana 대시보드: ID 12900 (JVM Micrometer) Import
12. Flyway 마이그레이션
src/main/resources/db/migration/
├── common/ # 공통 Seed (INSERT)
│ ├── V001__seed_roles.sql
│ └── V002__seed_system_config.sql
├── mysql/ # MySQL DDL (11 테이블)
│ └── V001__create_tables.sql
├── postgresql/ # PostgreSQL DDL
│ └── V001__create_tables.sql
├── oracle/ # Oracle DDL
│ └── V001__create_tables.sql
└── mssql/ # MSSQL DDL
└── V001__create_tables.sql
- 개발:
hibernate.ddl-auto=update(Flyway 비활성) - 프로덕션:
flyway.enabled=true+ddl-auto=validate(자동)
13. 체크리스트
로컬 개발
[ ] JDK 21 설치 + java -version 확인
[ ] Git clone
[ ] 설치 스크립트 실행 (scripts/install.bat 또는 scripts/install.sh)
[ ] docker-compose-dev.yml up (또는 직접 DB/Redis 설치)
[ ] ./gradlew bootRun → http://localhost:8080/spring/health OK
[ ] 관리자 등록 (POST /spring/api/auth/register)
[ ] Admin Console 로그인 (/spring/admin/login)
[ ] Swagger UI 확인 (/spring/swagger-ui)
프로덕션
[ ] 배포 스크립트 실행 (sudo scripts/deploy-prod.sh) 또는 수동 설정:
[ ] 서비스 계정 (wbxapp) 생성
[ ] 타임존 Asia/Seoul, 로케일 UTF-8
[ ] .env 파일 생성 (JWT_SECRET 필수 변경!)
[ ] DB 생성 + 사용자 권한
[ ] Redis 설치 + bind 127.0.0.1 + requirepass
[ ] JAR 빌드 (./gradlew bootJar)
[ ] 서버 전송 + /opt/wbx-app 배치
[ ] systemd 서비스 등록 + 시작
[ ] Health Check 확인
[ ] 리버스 프록시 설정 (Nginx/Caddy)
[ ] SSL 인증서 설치
[ ] 방화벽 443 오픈, DB 포트 내부만
[ ] SELinux httpd_can_network_connect (RHEL)
[ ] 관리자 등록 + Admin Console 접속 확인
[ ] DB 백업 cron 등록
[ ] 모니터링 Prometheus 연동 (선택)
14. 트러블슈팅
| 증상 | 원인 | 해결 |
|---|---|---|
| BUILD FAILED | JDK 버전 | java -version → 21 확인 |
| DB 연결 실패 | URL/인증 | .env + application-{db}.yml 확인 |
| 포트 충돌 | 8080 사용 중 | server.port=8081 또는 기존 프로세스 종료 |
| Admin 리다이렉트 오류 | context-path | SERVER_CONTEXT_PATH 환경변수 확인 |
| JWT 에러 | 시크릿 키 불일치 | WBX FastAPI와 동일 JWT_SECRET 사용 |
| Redis 연결 실패 | 미실행 | Redis 없이도 동작 (캐시 비활성화) |
| 한글 깨짐 | DB 인코딩 | CHARACTER SET utf8mb4 확인 |
| Flyway 실패 | DDL 호환 | DB 프로필 확인 (oracle/mssql/mysql/postgresql) |
| SSO 오류 | OAuth2 설정 | AZURE_CLIENT_ID, AZURE_TENANT_ID 확인 |
| MFA 안됨 | 미활성화 | wbx.spring.mfa.enabled=true 설정 |
| 502 Bad Gateway | 앱 미기동 | systemctl status wbx-app, logs 확인 |
| OOM (메모리) | 힙 부족 | -XX:MaxRAMPercentage=75 또는 RAM 증설 |