diff --git a/docs/00-setup-guide.md b/docs/00-setup-guide.md deleted file mode 100644 index ecc0822..0000000 --- a/docs/00-setup-guide.md +++ /dev/null @@ -1,670 +0,0 @@ -# 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 증설 | diff --git a/docs/01-project-structure.md b/docs/01-project-structure.md deleted file mode 100644 index f0bd230..0000000 --- a/docs/01-project-structure.md +++ /dev/null @@ -1,203 +0,0 @@ -# WBX Spring Core — 프로젝트 구조 - -``` -wbx-spring-core/ -├── build.gradle # Gradle 빌드 (Spring Boot 3.5, Java 21) -├── settings.gradle -├── gradlew / gradlew.bat # Gradle Wrapper (별도 설치 불필요) -├── Dockerfile # 컨테이너 빌드 (Temurin 21 JRE Alpine) -├── docker-compose.yml # 프로덕션 (App + MySQL + Redis) -├── docker-compose-dev.yml # 로컬 개발 인프라 (MySQL/PG + Redis) -├── .dockerignore -├── .editorconfig # IDE 공통 코드 스타일 -├── .gitignore -├── .github/workflows/ci.yml # GitHub Actions CI -│ -├── docs/ # ★ 문서 -│ ├── 00-setup-guide.md # 설치/배포 가이드 -│ └── 01-project-structure.md # 프로젝트 구조 (본 문서) -│ -├── src/main/java/kr/co/accura/wbx/spring/ -│ ├── WbxSpringCoreApplication.java # 메인 (@SpringBootApplication) -│ ├── HealthController.java # GET /health -│ │ -│ ├── auth/ # 인증 (18파일) -│ │ ├── WbxUser.java # 사용자 Entity -│ │ ├── WbxUserDetails.java # Spring Security UserDetails -│ │ ├── WbxUserRepository.java -│ │ ├── AuthController.java # login/register/me/refresh/logout/mfa -│ │ ├── JwtProvider.java # JWT 발급/검증 (WBX 호환) -│ │ ├── JwtFilter.java # Bearer Token 필터 -│ │ ├── ApiKeyFilter.java # X-API-Key 서버-서버 인증 -│ │ ├── PasswordPolicy.java # 비밀번호 규칙 검증 -│ │ ├── RefreshTokenService.java # Refresh Token 관리 -│ │ ├── WbxRefreshToken.java # Entity -│ │ ├── RefreshTokenRepository.java -│ │ ├── WbxLoginHistory.java # 로그인 이력 Entity -│ │ ├── LoginHistoryRepository.java -│ │ ├── MfaService.java # TOTP 생성/검증/백업코드 -│ │ ├── MfaController.java # MFA setup/verify/disable -│ │ ├── WbxTotpSecret.java # TOTP 시크릿 Entity -│ │ ├── TotpSecretRepository.java -│ │ └── SsoSuccessHandler.java # Azure Entra OAuth2 성공 핸들러 -│ │ -│ ├── rbac/ # 권한 (7파일) -│ │ ├── DeptScope.java # OWN/DEPT/COMPANY + toSpec() -│ │ ├── PermissionEvaluator.java # @wbx.check() -│ │ ├── WbxRole.java # 역할 Entity -│ │ ├── WbxUserRole.java # 사용자-역할 매핑 -│ │ ├── WbxUserRoleRepository.java -│ │ ├── RolePermission.java # 역할-권한 -│ │ └── RolePermissionRepository.java -│ │ -│ ├── approval/ # 결재 엔진 (9파일) -│ │ ├── ApprovalHandler.java # 핸들러 인터페이스 -│ │ ├── ApprovalHandlerRegistry.java # 자동 수집 Registry -│ │ ├── UnifiedApprovalController.java # 통합 API -│ │ ├── ApprovalCompletedEvent.java # Spring Event -│ │ ├── ApprovalResult.java # 결과 DTO -│ │ ├── ApprovalHistoryDto.java -│ │ ├── ApprovalLineDto.java -│ │ ├── ApprovalPendingDto.java -│ │ └── ActionRequest.java -│ │ -│ ├── notification/ # 알림 (4파일) -│ │ ├── SseNotificationService.java # SSE 실시간 알림 -│ │ ├── NotificationController.java # /notifications/stream -│ │ ├── Notification.java # Entity -│ │ └── NotificationDto.java -│ │ -│ ├── audit/ # 감사 로그 (3파일) -│ │ ├── AuditLogService.java -│ │ ├── WbxAuditLog.java -│ │ └── AuditLogRepository.java -│ │ -│ ├── file/ # 파일 스토리지 (6파일) -│ │ ├── FileStorageService.java # 인터페이스 (upload/download/delete/getPresignedUrl) -│ │ ├── LocalFileStorageService.java # 로컬 구현 -│ │ ├── AzureBlobStorageService.java # Azure Blob (SAS Token) -│ │ ├── AwsS3StorageService.java # AWS S3 -│ │ ├── GcpStorageService.java # GCP Cloud Storage -│ │ └── WbxFileUpload.java # Entity -│ │ -│ ├── datasource/ # Multi-DataSource (4파일) -│ │ ├── DataSource.java # @DataSource 어노테이션 -│ │ ├── WbxRoutingDataSource.java # ThreadLocal 기반 라우팅 -│ │ ├── DataSourceAspect.java # AOP -│ │ └── MultiDataSourceConfig.java # Bean 설정 -│ │ -│ ├── compat/ # WBX 호환 (2파일) -│ │ ├── WbxErrorHandler.java # detail 키 에러 -│ │ └── WbxPaginationConfig.java # skip/limit → Pageable -│ │ -│ ├── config/ # 설정 (5파일) -│ │ ├── WbxSpringProperties.java # wbx.spring.* 설정 -│ │ ├── SecurityAutoConfig.java # 2 FilterChain (Admin+API+SSO) -│ │ ├── CorsAutoConfig.java -│ │ ├── OpenApiConfig.java # Swagger -│ │ └── WbxSystemConfig.java # K-V 설정 Entity -│ │ -│ ├── common/ # 공통 (4파일) -│ │ ├── BaseEntity.java # JPA Auditing -│ │ ├── BusinessException.java -│ │ ├── NotFoundException.java -│ │ └── SecurityUtils.java -│ │ -│ └── admin/ # 관리자 (6파일) -│ ├── AdminViewController.java # Thymeleaf 11화면 + 25 엔드포인트 -│ ├── AdminLoginController.java # /admin/login -│ ├── AdminUserDetailsService.java # 폼 로그인 인증 -│ ├── AdminController.java # Admin REST API -│ ├── WbxRoleRepository.java -│ └── WbxSystemConfigRepository.java -│ -├── src/main/resources/ -│ ├── application.yml # 메인 설정 (로컬 개발) -│ ├── application-prod.yml # 프로덕션 (Flyway, 로깅) -│ ├── application-mysql.yml # MySQL 프로필 -│ ├── application-postgresql.yml # PostgreSQL 프로필 -│ ├── application-oracle.yml # Oracle 프로필 -│ ├── application-mssql.yml # MSSQL 프로필 -│ ├── application-azure.yml # Azure (SSO + Blob) -│ ├── application-aws.yml # AWS (Cognito + S3) -│ ├── application-test.yml # 테스트 (H2 인메모리) -│ ├── templates/admin/ # Thymeleaf 템플릿 (12화면) -│ │ ├── login.html -│ │ ├── dashboard.html -│ │ ├── users.html -│ │ ├── user-detail.html -│ │ ├── roles.html -│ │ ├── role-detail.html -│ │ ├── permissions.html -│ │ ├── login-history.html -│ │ ├── audit-logs.html -│ │ ├── config.html -│ │ ├── system-health.html -│ │ └── fragments.html # 사이드바 공통 -│ ├── static/admin/css/admin.css # Admin UI 스타일 -│ └── db/migration/ # Flyway SQL (4종 DBMS) -│ ├── common/ -│ │ ├── V001__seed_roles.sql -│ │ └── V002__seed_system_config.sql -│ ├── mysql/V001__create_tables.sql -│ ├── postgresql/V001__create_tables.sql -│ ├── oracle/V001__create_tables.sql -│ └── mssql/V001__create_tables.sql -│ -└── src/test/java/ # 테스트 -``` - -## 패키지별 요약 - -| 패키지 | 파일 | 핵심 | -|--------|:----:|------| -| auth | 18 | JWT, Login, Register, Refresh, Password, ApiKey, MFA/TOTP, SSO, LoginHistory | -| rbac | 7 | DeptScope, PermissionEvaluator(@wbx), Role, UserRole, Permission | -| approval | 9 | Handler(interface), Registry, UnifiedController, Event | -| notification | 4 | SSE, Controller, Entity | -| audit | 3 | AuditLogService, Entity | -| file | 6 | StorageService(interface), Local, Azure, AWS, GCP | -| datasource | 4 | @DataSource, RoutingDataSource, AOP, MultiConfig | -| compat | 2 | ErrorHandler, Pagination | -| config | 5 | Properties, Security, CORS, OpenAPI, SystemConfig | -| common | 4 | BaseEntity, Exception, SecurityUtils | -| admin | 6 | Thymeleaf Admin Console (12화면, 24 엔드포인트) | -| **합계** | **70** | | - -## DB 테이블 (11개, Hibernate 자동 생성 / Flyway 마이그레이션) - -| 테이블 | Entity | 용도 | -|--------|--------|------| -| wbx_users | WbxUser | 사용자 (SSO, MFA 필드 포함) | -| wbx_roles | WbxRole | 역할 | -| wbx_user_roles | WbxUserRole | 사용자-역할 | -| wbx_role_permissions | RolePermission | 역할-권한 (module+action+scope) | -| wbx_refresh_tokens | WbxRefreshToken | JWT 갱신 토큰 | -| wbx_totp_secrets | WbxTotpSecret | MFA TOTP 시크릿 (AES 암호화) | -| wbx_login_history | WbxLoginHistory | 로그인 이력 | -| wbx_notifications | Notification | 알림 | -| wbx_audit_logs | WbxAuditLog | 감사 로그 | -| wbx_file_uploads | WbxFileUpload | 파일 이력 | -| wbx_system_config | WbxSystemConfig | 시스템 설정 (K-V) | - -## REST API - -| Method | Path | 인증 | 설명 | -|--------|------|:---:|------| -| GET | /health | - | 헬스 체크 | -| POST | /api/auth/login | - | 로그인 → JWT (MFA 분기) | -| POST | /api/auth/register | - | 회원가입 | -| GET | /api/auth/me | JWT | 내 정보 | -| PUT | /api/auth/password/change | JWT | 비밀번호 변경 | -| POST | /api/auth/refresh | - | 토큰 갱신 | -| POST | /api/auth/logout | JWT | 로그아웃 | -| POST | /api/auth/mfa/verify | - | MFA 2단계 검증 | -| POST | /api/auth/mfa/setup | JWT | MFA 설정 시작 (QR) | -| POST | /api/auth/mfa/setup/verify | JWT | MFA 활성화 + 백업코드 | -| DELETE | /api/auth/mfa | JWT | MFA 비활성화 | -| POST | /api/auth/mfa/backup-verify | - | 백업코드 인증 | -| GET | /api/notifications/stream | JWT | SSE 실시간 알림 | -| GET | /api/notifications/unread-count | JWT | 미읽음 수 | -| POST | /api/approvals/unified/action/{type}/{id}/approve | JWT | 결재 승인 | -| POST | /api/approvals/unified/action/{type}/{id}/reject | JWT | 결재 반려 | -| GET | /api/approvals/unified/pending | JWT | 결재 대기 | diff --git a/scripts/gitea-bulk-create.sh b/scripts/gitea-bulk-create.sh new file mode 100644 index 0000000..8463c6c --- /dev/null +++ b/scripts/gitea-bulk-create.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# ============================================================ +# Gitea 사용자 일괄 등록 +# 사용법: ./scripts/gitea-bulk-create.sh users.csv +# +# CSV 형식 (헤더 없이): +# username,email,password +# hong,hong@company.com,P@ssw0rd123 +# kim,kim@company.com,P@ssw0rd123 +# ============================================================ +set -euo pipefail + +if [ $# -lt 1 ]; then + echo "사용법: $0 " + echo "" + echo "CSV 형식:" + echo " username,email,password" + echo " hong,hong@company.com,P@ssw0rd123" + exit 1 +fi + +CSV_FILE="$1" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +if [ ! -f "$CSV_FILE" ]; then + echo "파일을 찾을 수 없습니다: $CSV_FILE" + exit 1 +fi + +COUNT=0 +FAIL=0 + +while IFS=',' read -r username email password; do + # 빈 줄, 주석 건너뜀 + [[ -z "$username" || "$username" =~ ^# ]] && continue + + # 공백 제거 + username=$(echo "$username" | xargs) + email=$(echo "$email" | xargs) + password=$(echo "$password" | xargs) + + echo "--- ${username} (${email}) ---" + if "${SCRIPT_DIR}/gitea-create-user.sh" "$username" "$email" "$password"; then + COUNT=$((COUNT + 1)) + else + FAIL=$((FAIL + 1)) + fi +done < "$CSV_FILE" + +echo "" +echo "완료: 성공 ${COUNT}건, 실패 ${FAIL}건" diff --git a/scripts/gitea-create-user.sh b/scripts/gitea-create-user.sh new file mode 100644 index 0000000..24a4839 --- /dev/null +++ b/scripts/gitea-create-user.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# ============================================================ +# Gitea 사용자 등록 스크립트 +# 사용법: ./scripts/gitea-create-user.sh +# 환경변수: GITEA_URL, GITEA_ADMIN_USER, GITEA_ADMIN_PASS +# ============================================================ +set -euo pipefail + +# ---------- 색상 ---------- +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' +ok() { echo -e "${GREEN}[OK]${NC} $1"; } +fail() { echo -e "${RED}[FAIL]${NC} $1"; exit 1; } +warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } + +# ---------- 인자 ---------- +if [ $# -lt 3 ]; then + echo "사용법: $0 " + echo "" + echo "예시:" + echo " $0 hong hong@company.com P@ssw0rd123" + echo " $0 kim kim@company.com P@ssw0rd123" + echo "" + echo "환경변수 (선택):" + echo " GITEA_URL Gitea 서버 URL (기본: https://git.wbx.kr)" + echo " GITEA_ADMIN_USER 관리자 계정 (기본: accura)" + echo " GITEA_ADMIN_PASS 관리자 비밀번호" + exit 1 +fi + +USERNAME="$1" +EMAIL="$2" +PASSWORD="$3" + +GITEA_URL="${GITEA_URL:-https://git.wbx.kr}" +GITEA_ADMIN_USER="${GITEA_ADMIN_USER:-accura}" + +if [ -z "${GITEA_ADMIN_PASS:-}" ]; then + read -s -p "Gitea 관리자(${GITEA_ADMIN_USER}) 비밀번호: " GITEA_ADMIN_PASS + echo "" +fi + +API="${GITEA_URL}/api/v1" + +# ---------- 서버 연결 확인 ---------- +HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "${API}/version" 2>/dev/null || echo "000") +if [ "$HTTP_CODE" != "200" ]; then + fail "Gitea 서버 연결 실패: ${GITEA_URL} (HTTP ${HTTP_CODE})" +fi + +# ---------- 중복 확인 ---------- +EXISTS=$(curl -s -o /dev/null -w "%{http_code}" \ + -u "${GITEA_ADMIN_USER}:${GITEA_ADMIN_PASS}" \ + "${API}/users/${USERNAME}" 2>/dev/null) + +if [ "$EXISTS" = "200" ]; then + warn "사용자 '${USERNAME}'이 이미 존재합니다." + exit 0 +fi + +# ---------- 사용자 생성 ---------- +RESPONSE=$(curl -s -w "\n%{http_code}" \ + -X POST "${API}/admin/users" \ + -u "${GITEA_ADMIN_USER}:${GITEA_ADMIN_PASS}" \ + -H "Content-Type: application/json" \ + -d "{ + \"username\": \"${USERNAME}\", + \"email\": \"${EMAIL}\", + \"password\": \"${PASSWORD}\", + \"must_change_password\": true, + \"visibility\": \"public\" + }" 2>/dev/null) + +HTTP_CODE=$(echo "$RESPONSE" | tail -1) +BODY=$(echo "$RESPONSE" | sed '$d') + +if [ "$HTTP_CODE" = "201" ]; then + ok "사용자 생성 완료: ${USERNAME} (${EMAIL})" + echo " → ${GITEA_URL}/${USERNAME}" + echo " → 첫 로그인 시 비밀번호 변경 필요" +else + fail "사용자 생성 실패 (HTTP ${HTTP_CODE}): ${BODY}" +fi diff --git a/src/main/java/kr/co/accura/wbx/spring/admin/AdminViewController.java b/src/main/java/kr/co/accura/wbx/spring/admin/AdminViewController.java index 9d36abb..57c2d83 100644 --- a/src/main/java/kr/co/accura/wbx/spring/admin/AdminViewController.java +++ b/src/main/java/kr/co/accura/wbx/spring/admin/AdminViewController.java @@ -83,6 +83,7 @@ public class AdminViewController { .isAdmin(isAdmin != null && isAdmin) .build(); userRepository.save(user); + ra.addFlashAttribute("message", "사용자가 추가되었습니다: " + email); return "redirect:/admin/users/" + user.getId(); } diff --git a/src/main/java/kr/co/accura/wbx/spring/auth/AuthController.java b/src/main/java/kr/co/accura/wbx/spring/auth/AuthController.java index 2150261..e725ee4 100644 --- a/src/main/java/kr/co/accura/wbx/spring/auth/AuthController.java +++ b/src/main/java/kr/co/accura/wbx/spring/auth/AuthController.java @@ -33,6 +33,7 @@ public class AuthController { @org.springframework.beans.factory.annotation.Autowired(required = false) private MfaService mfaService; + /** * 로그인 — WBX FastAPI 호환 JWT 발급 */ diff --git a/src/main/java/kr/co/accura/wbx/spring/config/WbxSpringProperties.java b/src/main/java/kr/co/accura/wbx/spring/config/WbxSpringProperties.java index 783e9e6..49ab4ac 100644 --- a/src/main/java/kr/co/accura/wbx/spring/config/WbxSpringProperties.java +++ b/src/main/java/kr/co/accura/wbx/spring/config/WbxSpringProperties.java @@ -119,4 +119,5 @@ public class WbxSpringProperties { private String errorFormat = "fastapi"; // detail key private String listKey = "items"; } + }