# 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` 테이블에 직접 매핑 (별도 확장 테이블 불필요)