# 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 ```powershell # 방법 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) ```bash 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 ```bash 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 ```bash brew install temurin@21 java -version ``` --- ## 3. 소스코드 & 로컬 개발 ### 3-1. 소스코드 가져오기 ```bash git clone https://github.com/accura0117/wbx-spring-core.git cd wbx-spring-core ``` ### 3-2. 빠른 설치 (설치 스크립트) 스크립트가 JDK/Git/Docker 사전 검사, 빌드, .env 템플릿 생성, 디렉토리 생성을 자동 처리합니다. ```bash # Windows scripts\install.bat # Linux/macOS chmod +x scripts/install.sh && ./scripts/install.sh ``` ### 3-3. 로컬 DB 생성 (Docker Compose 또는 직접) ```bash # 방법 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. 빌드 & 실행 설치 스크립트를 사용하지 않는 경우 수동으로 빌드합니다. ```bash # 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. 초기 관리자 등록 ```bash 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. 프로필 조합 예시 ```bash # 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 호환) ```yaml 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. 파일 스토리지 전환 ```yaml 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 라우팅 ```yaml 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) ```bash sudo useradd -r -m -s /bin/bash wbxapp sudo mkdir -p /opt/wbx-app sudo chown wbxapp:wbxapp /opt/wbx-app ``` ### 6-2. 타임존 · 인코딩 ```bash sudo timedatectl set-timezone Asia/Seoul sudo localectl set-locale LANG=ko_KR.UTF-8 ``` ### 6-3. 시스템 리밋 ```bash # /etc/security/limits.d/wbxapp.conf wbxapp soft nofile 65535 wbxapp hard nofile 65535 ``` ### 6-4. Redis 보안 ```bash 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 빌드 & 배포 ```bash # 빌드 서버 ./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) ```ini # /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 ``` ```bash sudo systemctl daemon-reload sudo systemctl enable --now wbx-app journalctl -u wbx-app -f ``` ### 6-8. Windows 서비스 ```powershell # 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 배포 ```bash ./gradlew bootJar docker build -t wbx-spring:latest . docker compose up -d ``` --- ## 7. 리버스 프록시 ### 7-1. Nginx ```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 # 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) ```bash sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload # DB 포트는 외부 차단 (기본) ``` ### SELinux ```bash 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) ```bash #!/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 ``` ### 앱 롤백 ```bash 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 ```yaml # application-prod.yml에 이미 설정됨 management: endpoints: web: exposure: include: health,info,metrics,prometheus ``` ```bash # 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 증설 |