feat: FE 화면 구현 완료 + 샘플 데이터 + 결재라인 연동

- WBS/TEAL 화면 실제 구현 (TreeTable, FileUpload, 버전관리)
- 시수이력/결재이력 화면 구현 (DataTable, Filter, Timeline)
- 비밀번호변경 화면 추가
- 로그인 snake_case 응답 매핑 수정
- Vite 프록시 8081 포트 수정
- auth guard에서 fetchMe 자동 호출
- V108 샘플 데이터 (10명 사용자, 4주 시수 215건, 결재 9건)
- 배너 추가 (WBX Spring)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
이 Commit은 다음에 포함되어 있습니다:
2026-03-25 22:17:32 +09:00
부모 df723f1d59
커밋 9707a6eeb1
33개의 변경된 파일2323개의 추가작업 그리고 20개의 파일을 삭제

파일 보기

@@ -0,0 +1,659 @@
# 12. WTM 프로젝트 구성 계획
> 작성일: 2026-03-25
> 목적: wbx-spring-core를 라이브러리로 전환하고, wtm-api 모듈을 생성하기 위한 구체적 작업 계획
---
## 1. 현황 분석
### wbx-spring-core 현재 구조
| 항목 | 현재 상태 | 문제점 |
|------|----------|--------|
| Gradle 플러그인 | `org.springframework.boot` (fat JAR) | 라이브러리로 사용 불가 |
| 진입점 | `WbxSpringCoreApplication.java` (`@SpringBootApplication`) | 라이브러리에 main() 불필요 |
| application.yml | 하드코딩된 DB/Redis/서버 설정 | 소비 모듈이 자체 설정 불가 |
| DB 드라이버 | MySQL/PG/Oracle/MSSQL 4개 모두 포함 | 소비 모듈이 필요한 것만 선택해야 함 |
| Admin UI | Thymeleaf 세션 기반 (12개 템플릿) | 모든 소비 모듈에 전이 의존성 |
| Enable 어노테이션 | main 클래스에 `@EnableJpaAuditing` 등 집중 | 라이브러리 전환 시 분리 필요 |
### wbx-spring-core 제공 기능 (10개 모듈)
| 패키지 | 기능 | 주요 클래스 | WTM 사용 여부 |
|--------|------|------------|:---:|
| `auth/` | JWT, SSO, MFA, 비밀번호 정책, 리프레시 토큰 | `JwtProvider`, `JwtFilter`, `AuthController` | O |
| `rbac/` | 역할-모듈-액션 권한, dept_scope | `PermissionEvaluator` (bean `"wbx"`) | O |
| `approval/` | 통합 결재 엔진 (Handler Registry) | `ApprovalHandler` interface, `UnifiedApprovalController` | O |
| `notification/` | SSE 실시간 알림 | `SseNotificationService` | O |
| `compat/` | WBX 호환 (detail 에러, skip/limit) | `WbxErrorHandler`, `WbxPaginationConfig` | O |
| `file/` | 파일 스토리지 (Local/Azure/AWS/GCP) | `FileStorageService` interface | O |
| `datasource/` | 멀티 데이터소스 라우팅 | `MultiDataSourceConfig`, `@DataSource` | O |
| `config/` | 시큐리티, CORS, OpenAPI, 중앙 설정 | `SecurityAutoConfig`, `WbxSpringProperties` | O |
| `common/` | 베이스 엔티티, 유틸, 예외 | `BaseEntity`, `SecurityUtils`, `BusinessException` | O |
| `audit/` | 감사 로그 | `AuditLogService` | O |
| `admin/` | 관리 콘솔 UI (Thymeleaf) | `AdminController`, 12개 HTML | 조건부 |
---
## 2. 목표 구조: 멀티프로젝트 플랫폼
### 설계 원칙
- **wbx-spring-core**: 모든 프로젝트가 공유하는 프레임워크 (1개)
- **{project}-api**: 고객/프로젝트별 백엔드 애플리케이션 (N개)
- **{project}-frontend**: 고객/프로젝트별 프론트엔드 (N개, Gradle 외부)
- **새 프로젝트 추가 = 모듈 2개 추가** (settings.gradle에 include만 추가)
### 전체 구조
```
WBX-Spring/
├── settings.gradle # rootProject.name = 'wbx-spring'
│ # include 'wbx-spring-core'
│ # include 'wtm-api' ← 한화오션 WTM
│ # include 'xxx-api' ← 향후 프로젝트 B
│ # include 'yyy-api' ← 향후 프로젝트 C
├── build.gradle # 공통: Java 21, Lombok, Spring BOM
├── wbx-spring-core/ # 🔧 공유 프레임워크 라이브러리
│ ├── build.gradle # java-library (Boot 플러그인 없음)
│ ├── src/main/java/kr/co/accura/wbx/spring/
│ │ ├── auth/ # JWT, SSO, MFA, 비밀번호 정책
│ │ ├── rbac/ # RBAC 권한 (PermissionEvaluator)
│ │ ├── approval/ # 통합 결재 엔진 (Handler Registry)
│ │ ├── notification/ # SSE 실시간 알림
│ │ ├── compat/ # WBX 호환 (detail 에러, skip/limit)
│ │ ├── file/ # 파일 스토리지 (Local/Azure/AWS/GCP)
│ │ ├── datasource/ # 멀티 데이터소스 라우팅
│ │ ├── config/ # 시큐리티, CORS, OpenAPI, 중앙 설정
│ │ ├── common/ # BaseEntity, SecurityUtils, 예외
│ │ ├── audit/ # 감사 로그
│ │ └── admin/ # 관리 콘솔 (조건부: wbx.spring.admin-ui.enabled)
│ └── src/main/resources/
│ ├── META-INF/spring/
│ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
│ └── templates/admin/ # Thymeleaf (admin-ui.enabled=true 시만)
├── wtm-api/ # 🏗️ 한화오션 WTM 백엔드
│ ├── build.gradle # spring-boot + project(':wbx-spring-core')
│ ├── src/main/java/kr/co/accura/wtm/
│ │ ├── WtmApplication.java # @SpringBootApplication
│ │ ├── domain/
│ │ │ ├── user/ # HR 확장 필드 (discipline, location 등)
│ │ │ ├── project/ # 프로젝트, 인력 배정
│ │ │ ├── wbs/ # WBS, Canonical WBS, P6 파서
│ │ │ ├── teal/ # TEAL 버전, Activity
│ │ │ ├── timesheet/ # 시수 3종, 규칙 엔진
│ │ │ ├── approval/handler/ # TimesheetApprovalHandler
│ │ │ ├── report/ # QueryDSL 동적 리포트
│ │ │ ├── config/ # OverheadType, WorkRule
│ │ │ └── audit/ # SaAccessLog
│ │ ├── api/ # REST Controller 13개 (79 API)
│ │ ├── integration/ # SAP BTP, P6, Cognite
│ │ └── config/ # WTM 전용 설정
│ └── src/main/resources/
│ ├── application.yml # WTM 전용 설정
│ ├── application-mysql.yml # 개발 (MySQL)
│ ├── application-mssql.yml # 운영 (Azure SQL)
│ └── db/migration/
│ ├── common/ # ANSI SQL (seed data)
│ ├── mysql/ # 개발용 DDL
│ └── mssql/ # 운영용 DDL
├── wtm-frontend/ # 🎨 한화오션 WTM 프론트엔드
│ ├── package.json # Vue 3 + PrimeVue 4 + Vite
│ └── src/
│ ├── app/ # 셸 (router, plugins)
│ ├── core/ # 공유 인프라
│ ├── modules/ # 도메인 모듈 10개
│ └── assets/ # 스타일, 이미지
├── {향후}-api/ # 🆕 프로젝트 B 백엔드 (동일 패턴)
│ ├── build.gradle # project(':wbx-spring-core')
│ └── src/main/java/kr/co/accura/{project}/
│ ├── {Project}Application.java
│ ├── domain/ # 프로젝트 B 전용 도메인
│ ├── api/ # 프로젝트 B 전용 API
│ └── config/
└── {향후}-frontend/ # 🆕 프로젝트 B 프론트엔드 (동일 패턴)
└── src/
├── app/
├── core/ # 복사 후 커스터마이징 또는 공유 npm 패키지
└── modules/
```
### 프로젝트 간 관계도
```
┌─────────────────────┐
│ wbx-spring-core │ ← 공유 프레임워크
│ (java-library) │
│ 인증/권한/결재/알림 │
└──────┬──────┬───────┘
│ │
┌────────────┘ └────────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ wtm-api │ │ {향후}-api │
│ (spring-boot) │ │ (spring-boot) │
│ 한화오션 WTM │ │ 고객 B 프로젝트 │
└────────┬────────┘ └────────┬────────┘
│ │
┌────────┴────────┐ ┌────────┴────────┐
│ wtm-frontend │ │ {향후}-frontend │
│ (Vue+PrimeVue) │ │ (Vue+PrimeVue) │
└─────────────────┘ └─────────────────┘
```
### 새 프로젝트 추가 절차 (3단계)
**Step 1. settings.gradle에 모듈 추가**
```groovy
include '{project}-api'
```
**Step 2. {project}-api/build.gradle 생성**
```groovy
plugins {
id 'org.springframework.boot' version '3.5.0'
}
dependencies {
implementation project(':wbx-spring-core')
// 프로젝트 전용 의존성 추가
}
```
**Step 3. Application 클래스 + application.yml 생성**
```java
@SpringBootApplication(scanBasePackages = {
"kr.co.accura.wbx.spring",
"kr.co.accura.{project}"
})
@EntityScan(basePackages = {
"kr.co.accura.wbx.spring",
"kr.co.accura.{project}"
})
@EnableJpaRepositories(basePackages = {
"kr.co.accura.wbx.spring",
"kr.co.accura.{project}"
})
public class {Project}Application {
public static void main(String[] args) {
SpringApplication.run({Project}Application.class, args);
}
}
```
> **각 프로젝트는 독립 실행 가능** — 자체 DB, 자체 포트, 자체 JWT 설정.
> wbx-spring-core의 기능(인증/권한/결재/알림)은 `wbx.spring.*` 속성으로 ON/OFF.
### 프로젝트별 커스터마이징 포인트
| 항목 | 설정 위치 | 예시 |
|------|----------|------|
| API prefix | `wbx.spring.api-prefix` | `/api/wtm`, `/api/ems`, `/api/qms` |
| JWT secret | `wbx.spring.jwt.secret` | 프로젝트별 독립 키 |
| DB | `spring.datasource.*` | 프로젝트별 독립 DB |
| 결재 핸들러 | `@Component implements ApprovalHandler` | Bean 등록만으로 자동 연동 |
| SSO (Azure) | `spring.security.oauth2.client.*` | 고객별 Entra ID 테넌트 |
| 파일 스토리지 | `wbx.spring.file.storage-type` | `local`, `azure`, `aws`, `gcp` |
| Admin UI | `wbx.spring.admin-ui.enabled` | `true` / `false` |
| CORS | `wbx.spring.cors.allowed-origins` | 프로젝트별 프론트 URL |
```
---
## 3. 전환 작업 상세
### Phase 0: wbx-spring-core 라이브러리 전환 (선행 작업)
#### 3.1 루트 프로젝트 생성
| 작업 | 파일 | 설명 |
|------|------|------|
| `settings.gradle` 이동 | `WBX-Spring/settings.gradle` | 프로젝트 모듈 등록 |
| 루트 `build.gradle` 생성 | `WBX-Spring/build.gradle` | 공통 설정 (Java 21, repositories, Lombok) |
```groovy
// WBX-Spring/settings.gradle
rootProject.name = 'wbx-spring'
// 공유 프레임워크
include 'wbx-spring-core'
// === 고객 프로젝트 (추가 시 여기에 include) ===
include 'wtm-api' // 한화오션 WTM (시수관리)
// include 'xxx-api' // 향후 프로젝트 B
// include 'yyy-api' // 향후 프로젝트 C
```
```groovy
// WBX-Spring/build.gradle (루트)
plugins {
id 'java'
id 'io.spring.dependency-management' version '1.1.7'
}
subprojects {
group = 'kr.co.accura'
version = '1.0.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:3.5.0"
}
}
dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
}
```
#### 3.2 wbx-spring-core build.gradle 변경
**핵심 변경: `org.springframework.boot` 플러그인 제거 → `java-library` 적용**
```groovy
// wbx-spring-core/build.gradle
plugins {
id 'java-library'
}
dependencies {
// Spring Boot Starters (api로 노출 — 소비 모듈이 사용)
api 'org.springframework.boot:spring-boot-starter-web'
api 'org.springframework.boot:spring-boot-starter-data-jpa'
api 'org.springframework.boot:spring-boot-starter-security'
api 'org.springframework.boot:spring-boot-starter-validation'
api 'org.springframework.boot:spring-boot-starter-data-redis'
api 'org.springframework.boot:spring-boot-starter-cache'
api 'org.springframework.boot:spring-boot-starter-actuator'
api 'org.springframework.boot:spring-boot-starter-oauth2-client'
api 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
// JWT
api 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
// OpenAPI
api 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.6'
// Admin Console (조건부)
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
// Flyway (소비 모듈이 DBMS별 추가)
api 'org.flywaydb:flyway-core'
// DB 드라이버 — compileOnly (소비 모듈이 runtimeOnly로 선택)
compileOnly 'com.mysql:mysql-connector-j'
compileOnly 'org.postgresql:postgresql'
compileOnly 'com.oracle.database.jdbc:ojdbc11:23.6.0.24.10'
compileOnly 'com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre11'
// Micrometer
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
// Test
testRuntimeOnly 'com.h2database:h2'
}
```
#### 3.3 Auto-Configuration 전환
| 작업 | 설명 |
|------|------|
| `WbxSpringCoreApplication.java` 삭제 | main() 제거, 라이브러리에 불필요 |
| `WbxAutoConfiguration.java` 생성 | `@Configuration` + `@EnableJpaAuditing` + `@EnableAsync` + `@EnableScheduling` + `@EnableCaching` |
| `AutoConfiguration.imports` 생성 | Spring Boot 3.x 자동 설정 등록 |
| `application.yml` 정리 | 하드코딩된 설정 제거, 기본값만 `WbxSpringProperties`에서 관리 |
| `HealthController` 이동 | 범용으로 유지 (actuator가 대체 가능) |
```
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
kr.co.accura.wbx.spring.config.WbxAutoConfiguration
```
#### 3.4 Admin UI 조건부 활성화
```java
// wbx-spring-core
@Configuration
@ConditionalOnProperty(name = "wbx.spring.admin-ui.enabled", havingValue = "true", matchIfMissing = true)
public class AdminAutoConfiguration {
// AdminController, AdminLoginController, AdminUserDetailsService 등록
// SecurityAutoConfig의 adminFilterChain도 여기로 이동
}
```
---
### Phase 1: wtm-api 모듈 생성
#### 3.5 wtm-api build.gradle
```groovy
// wtm-api/build.gradle
plugins {
id 'org.springframework.boot' version '3.5.0'
}
dependencies {
// wbx-spring 프레임워크
implementation project(':wbx-spring-core')
// WTM 전용
implementation 'org.apache.poi:poi-ooxml:5.3.0' // P6 WBS, Excel 업로드/다운로드
// QueryDSL (리포트 동적 쿼리)
implementation 'com.querydsl:querydsl-jpa:5.1.0:jakarta'
annotationProcessor 'com.querydsl:querydsl-apt:5.1.0:jakarta'
// MapStruct (DTO 매핑)
implementation 'org.mapstruct:mapstruct:1.6.3'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'
// Flyway — Azure SQL
implementation 'org.flywaydb:flyway-sqlserver'
// DB Driver — 개발: MySQL, 운영: MSSQL (Azure SQL)
runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre11'
// Test
testRuntimeOnly 'com.h2database:h2'
}
```
#### 3.6 WtmApplication.java
```java
@SpringBootApplication(scanBasePackages = {
"kr.co.accura.wbx.spring",
"kr.co.accura.wtm"
})
@EntityScan(basePackages = {
"kr.co.accura.wbx.spring",
"kr.co.accura.wtm"
})
@EnableJpaRepositories(basePackages = {
"kr.co.accura.wbx.spring",
"kr.co.accura.wtm"
})
public class WtmApplication {
public static void main(String[] args) {
SpringApplication.run(WtmApplication.class, args);
}
}
```
> Auto-Configuration 방식 적용 시 `scanBasePackages` 제거 가능
#### 3.7 wtm-api application.yml
```yaml
spring:
application:
name: wtm-api
datasource:
url: jdbc:mysql://${DB_HOST:ws.ubuilder.co.kr}:${DB_PORT:3306}/${DB_NAME:wtm_db}
username: ${DB_USER:jsh}
password: ${DB_PASS:jsh@}
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: validate # Flyway로 스키마 관리
open-in-view: false
flyway:
enabled: true
locations: classpath:db/migration
data:
redis:
host: ${REDIS_HOST:localhost}
port: 6379
server:
port: 8080
wbx:
spring:
api-prefix: /api/wtm
jwt:
secret: ${JWT_SECRET:dev-secret-key-minimum-256-bits}
expiration: 28800
admin-ui:
enabled: false # WTM은 자체 Admin 불필요
cors:
allowed-origins: ${CORS_ORIGINS:http://localhost:5173}
notification:
sse-enabled: true
wtm:
work-rules:
default-min-daily-hours: 8
default-max-weekly-hours: 52
```
---
## 4. WTM 도메인 모듈별 구현 범위
### 신규 JPA Entity (23개)
> DB 스키마 상세: `02-database-schema.md` (Single Source of Truth)
> Flyway 마이그레이션: `02-database-schema.md`의 V1~V7 참조 (본 문서에서 복제하지 않음)
| 도메인 | Entity | 테이블 | Phase |
|--------|--------|--------|:-----:|
| user | `User` | `users` | PH1-1 |
| user | `Role` | `roles` | PH1-1 |
| user | `UserRole` | `user_roles` | PH1-1 |
| user | `OrgHierarchy` | `org_hierarchy` | PH1-1 |
| user | `HrUpload` | `hr_uploads` | PH1-1 |
| project | `Project` | `projects` | PH1-1 |
| project | `ProjectAssignment` | `project_assignments` | PH1-1 |
| project | `ProjectTypeConfig` | `project_type_config` | PH1-1 |
| wbs | `WbsVersion` | `wbs_versions` | PH1-1 |
| wbs | `WbsNode` | `wbs_nodes` | PH1-1 |
| wbs | `CanonicalWbs` | `canonical_wbs` | PH1-1 |
| wbs | `WbsDisciplineAssignment` | `wbs_discipline_assignments` | PH1-1 |
| teal | `TealVersion` | `teal_versions` | PH1-1 |
| teal | `TealEntry` | `teal_entries` | PH1-1 |
| timesheet | `Timesheet` | `timesheets` | PH1-1 |
| timesheet | `TimesheetEntry` | `timesheet_entries` | PH1-1 |
| timesheet | `TimesheetUpload` | `timesheet_uploads` | PH1-1 |
| approval | `TtApproval` | `approvals` | PH1-1 |
| approval | `TtApprovalLine` | `approval_lines` | PH1-1 |
| approval | `TtApprovalComment` | `approval_comments` | PH1-1 |
| config | `OverheadType` | `overhead_types` | PH1-1 |
| config | `WorkRule` | `work_rules` | PH1-1 |
| audit | `SaAccessLog` | `sa_access_logs` | PH1-2 |
> **User Entity 전략 확정**: WTM은 `02-database-schema.md`에 정의된 `users` 테이블을 직접 사용.
> wbx-spring-core의 `WbxUser`와 별도로, WTM 전용 `User` 엔티티가 HR 확장 필드(discipline, location, employment_type 등)를
> 포함한 `users` 테이블에 매핑됨. 인증은 wbx-spring-core의 `WbxUser`(wbx_users)가 담당하며,
> `users.email = wbx_users.email`로 연동.
### 신규 REST Controller (13개, 79 API)
| Controller | API 수 | Phase |
|-----------|:------:|:-----:|
| `AuthController` (WTM 전용 라우팅) | 8 | PH1-1 |
| `UserController` | 8 | PH1-1 ~ PH1-2 |
| `ProjectController` | 7 | PH1-1 |
| `WbsController` | 7 | PH1-1 ~ PH1-2 |
| `TealController` | 4 | PH1-1 |
| `TimesheetController` | 8 | PH1-1 |
| `ApprovalController` | 8 | PH1-1 ~ PH1-2 |
| `ReportController` | 9 | PH1-1 ~ PH2 |
| `HomeController` (대시보드/알림) | 2 | PH1-1 |
| `OverheadTypeController` (SA) | 3 | PH1-1 |
| `WorkRuleController` (SA) | 2 | PH1-1 |
| `ResourceAssignController` | 5 | PH1-1 |
| `HrIntegrationController` | 2 | PH1-2 |
### wbx-spring-core 활용 포인트
```java
// RBAC 권한 체크
@PreAuthorize("@wbx.check('TIMESHEET', 'VIEW')")
// 데이터 필터링 범위
DeptScope scope = evaluator.getScope("TIMESHEET", "VIEW");
// 결재 핸들러 등록 (Bean 등록만으로 자동 연동)
@Component
public class TimesheetApprovalHandler implements ApprovalHandler { ... }
// SSE 실시간 알림
sseNotificationService.sendToUser(approverId, notification);
// 에러 응답 (자동 적용)
throw new BusinessException("주간 합계 54시간 — 최대 52h 초과");
// → {"detail": "주간 합계 54시간 — 최대 52h 초과"}
```
---
## 5. User Entity 전략
### 선택지
| 방안 | 장점 | 단점 |
|------|------|------|
| **A: WbxUser 확장** (WTM 필드를 wbx_users에 추가) | 테이블 하나, 간단 | core 엔티티 오염 |
| **B: WtmUser 별도** (wtm.users + wbx.wbx_users 동기화) | 도메인 분리 깔끔 | 두 테이블 동기화 필요 |
| **C: WtmUserProfile** (wbx_users FK → wtm_user_profiles) | core 불변, 확장 자유 | JOIN 필요 |
### 권장: **방안 C** — WtmUserProfile 확장 테이블
```sql
CREATE TABLE wtm_user_profiles (
id BIGINT IDENTITY PRIMARY KEY,
wbx_user_id BIGINT NOT NULL UNIQUE REFERENCES wbx_users(id),
employee_id VARCHAR(50) UNIQUE,
discipline VARCHAR(100),
location VARCHAR(50),
employment_type VARCHAR(20) DEFAULT 'INTERNAL',
business_unit VARCHAR(100),
division VARCHAR(100),
department VARCHAR(100),
part VARCHAR(100),
created_at DATETIME2 DEFAULT GETDATE()
);
```
**이유**: wbx-spring-core의 `WbxUser` 엔티티를 수정하지 않고, WTM 전용 HR 필드를 별도 테이블로 관리. `wbx_user_id` FK로 연결.
---
## 6. 작업 순서 (Implementation Order)
```
Phase 0 — wbx-spring-core 라이브러리 전환 (1~2일)
├── 0-1. 루트 settings.gradle, build.gradle 생성
├── 0-2. wbx-spring-core build.gradle → java-library 전환
├── 0-3. WbxSpringCoreApplication 삭제, WbxAutoConfiguration 생성
├── 0-4. application.yml → 기본값만 남기고 정리
├── 0-5. Admin UI 조건부 활성화 (@ConditionalOnProperty)
├── 0-6. Enable 어노테이션 분리 → 개별 @Configuration
└── 0-7. 빌드 검증 (./gradlew :wbx-spring-core:build)
Phase 1 — wtm-api 스캐폴딩 (1일)
├── 1-1. wtm-api 디렉토리 + build.gradle 생성
├── 1-2. WtmApplication.java 생성
├── 1-3. application.yml (개발 환경)
├── 1-4. Flyway V1~V7 마이그레이션 SQL 생성
├── 1-5. 빌드 + 부팅 검증 (./gradlew :wtm-api:bootRun)
└── 1-6. wbx-spring-core 연동 검증 (JWT, RBAC, 결재 등)
Phase 2 — WTM 도메인 구현 (W3~W8, 계획서 10-schedule 참조)
├── 2-1. domain/user + domain/project (W3~W4)
├── 2-2. domain/wbs + domain/teal + P6 파서 (W4~W5)
├── 2-3. domain/timesheet + 규칙 엔진 (W5~W7)
├── 2-4. domain/approval/handler (W6~W7)
├── 2-5. domain/report + Excel Export (W7~W8)
└── 2-6. integration/sap (PH1-2)
```
---
## 7. 주의사항
### 어노테이션 프로세서 순서
Lombok + MapStruct + QueryDSL을 함께 사용 시:
```groovy
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'
annotationProcessor 'com.querydsl:querydsl-apt:5.1.0:jakarta'
```
### DB 이중 구조 — Flyway 듀얼 마이그레이션
- **개발**: MySQL (ws.ubuilder.co.kr) — `wtm_db` 스키마
- **운영**: Azure SQL (MSSQL) — Flyway 마이그레이션
DDL 구문 차이(`IDENTITY` vs `AUTO_INCREMENT`, `DATETIME2` vs `DATETIME`, `GETDATE()` vs `NOW()`)를
프로필별 마이그레이션 경로로 해결:
```yaml
# application-mysql.yml (개발)
spring:
flyway:
locations: classpath:db/migration/common,classpath:db/migration/mysql
# application-mssql.yml (운영)
spring:
flyway:
locations: classpath:db/migration/common,classpath:db/migration/mssql
```
```
src/main/resources/db/migration/
├── common/ # ANSI SQL 호환 (seed data, VIEW 등)
│ └── V10__seed_roles.sql
├── mysql/ # MySQL 전용 DDL
│ ├── V1__init_users.sql # AUTO_INCREMENT, DATETIME, NOW()
│ ├── V2__init_projects.sql
│ └── ...
└── mssql/ # Azure SQL 전용 DDL
├── V1__init_users.sql # IDENTITY, DATETIME2, GETDATE()
├── V2__init_projects.sql
└── ...
```
> `02-database-schema.md`의 DDL은 MSSQL 기준. MySQL 버전은 구현 시 변환 생성.
### wbx-spring-core의 WbxUser와 WTM User
- wbx-spring-core의 `wbx_users` 테이블: 인증/권한 용도 (JWT, SSO, MFA)
- WTM의 `users` 테이블: HR 확장 필드 포함 (discipline, location, employment_type 등)
- 연동: `users.email = wbx_users.email` 기준으로 매칭
- WTM `User` 엔티티는 `users` 테이블에 직접 매핑 (별도 확장 테이블 불필요)