Gitea 연동 제거, docs md 정리, Gitea CLI 스크립트 추가
- Gitea 연동 코드 제거 (GiteaService, Properties, Controller 연동) - docs/ md 파일 삭제 (PDF 가이드로 대체) - Gitea 사용자 등록 CLI 스크립트 추가 (gitea-create-user.sh, gitea-bulk-create.sh) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
이 Commit은 다음에 포함되어 있습니다:
@@ -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 증설 |
|
|
||||||
@@ -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 | 결재 대기 |
|
|
||||||
51
scripts/gitea-bulk-create.sh
일반 파일
51
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 <users.csv>"
|
||||||
|
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}건"
|
||||||
82
scripts/gitea-create-user.sh
일반 파일
82
scripts/gitea-create-user.sh
일반 파일
@@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================
|
||||||
|
# Gitea 사용자 등록 스크립트
|
||||||
|
# 사용법: ./scripts/gitea-create-user.sh <username> <email> <password>
|
||||||
|
# 환경변수: 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 <username> <email> <password>"
|
||||||
|
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
|
||||||
@@ -83,6 +83,7 @@ public class AdminViewController {
|
|||||||
.isAdmin(isAdmin != null && isAdmin)
|
.isAdmin(isAdmin != null && isAdmin)
|
||||||
.build();
|
.build();
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
|
|
||||||
ra.addFlashAttribute("message", "사용자가 추가되었습니다: " + email);
|
ra.addFlashAttribute("message", "사용자가 추가되었습니다: " + email);
|
||||||
return "redirect:/admin/users/" + user.getId();
|
return "redirect:/admin/users/" + user.getId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public class AuthController {
|
|||||||
@org.springframework.beans.factory.annotation.Autowired(required = false)
|
@org.springframework.beans.factory.annotation.Autowired(required = false)
|
||||||
private MfaService mfaService;
|
private MfaService mfaService;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 로그인 — WBX FastAPI 호환 JWT 발급
|
* 로그인 — WBX FastAPI 호환 JWT 발급
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -119,4 +119,5 @@ public class WbxSpringProperties {
|
|||||||
private String errorFormat = "fastapi"; // detail key
|
private String errorFormat = "fastapi"; // detail key
|
||||||
private String listKey = "items";
|
private String listKey = "items";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
새 Issue에서 참조
사용자 차단