Phase 0: wbx-spring-core 라이브러리 전환 - java-library 플러그인, WbxAutoConfiguration, Admin 조건부 활성화 - 루트 settings.gradle + build.gradle (멀티모듈) Phase 1: wtm-api 모듈 생성 - 23개 JPA Entity, 14개 Controller, 79개 API 엔드포인트 - Flyway V100~V107 MySQL 마이그레이션 - TimesheetRuleEngine, TimesheetApprovalHandler, P6WbsParser Phase 2: wtm-frontend (Vue 3 + PrimeVue 4) - 10개 도메인 모듈, 17개 View, 5개 서브컴포넌트 - 반응형 레이아웃 (AppLayout, AppSidebar, AppTopbar) - BaseCrudTable, BaseFormDialog, BasePageHeader 표준 컴포넌트 - JWT 인터셉터, 역할 기반 메뉴 필터링 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
660 줄
25 KiB
Markdown
660 줄
25 KiB
Markdown
# 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` 테이블에 직접 매핑 (별도 확장 테이블 불필요)
|