diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..70ce0ac --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Bash(./gradlew balance-core-api:dependencies:*)", + "Bash(./gradlew detekt:*)" + ] + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e4031d6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +max_line_length = 120 +tab_width = 4 +trim_trailing_whitespace = true + +[*.{kt,kts}] +indent_size = 4 +continuation_indent_size = 4 +ij_kotlin_align_multiline_parameters = true +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true + +[*.gradle] +indent_size = 4 + +[*.{yml,yaml}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore index 5a979af..d15e651 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ out/ ### Kotlin ### .kotlin + +### Lint ### +detekt-baseline.xml diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5cf6b78 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,726 @@ +# Spring Boot 멀티모듈 프로젝트 설계 가이드라인 + +## 개요 + +이 문서는 Spring Boot 프로젝트를 멀티모듈 구조로 설계하고 구현하기 위한 가이드라인입니다. +본 프로젝트(balance)의 실제 구조 변경 경험을 바탕으로 작성되었습니다. + +## 목차 + +1. [멀티모듈 구조의 이점](#멀티모듈-구조의-이점) +2. [모듈 분리 원칙](#모듈-분리-원칙) +3. [표준 모듈 구조](#표준-모듈-구조) +4. [Gradle 설정](#gradle-설정) +5. [의존성 관리 전략](#의존성-관리-전략) +6. [패키지 구조 및 네이밍](#패키지-구조-및-네이밍) +7. [주의사항 및 베스트 프랙티스](#주의사항-및-베스트-프랙티스) + +--- + +## 멀티모듈 구조의 이점 + +### 1. 관심사의 분리 (Separation of Concerns) +- 각 모듈이 명확한 책임을 가짐 +- 도메인, 데이터 접근, API 레이어를 물리적으로 분리 + +### 2. 재사용성 (Reusability) +- Storage 모듈처럼 독립적인 모듈은 다른 프로젝트에서 재사용 가능 +- 공통 기능을 별도 모듈로 추출하여 여러 프로젝트에서 활용 + +### 3. 빌드 최적화 +- 변경된 모듈만 재빌드 +- 병렬 빌드 가능 + +### 4. 팀 협업 +- 모듈별로 소유권 분리 가능 +- 충돌 최소화 + +### 5. 의존성 제어 +- 모듈 간 의존성을 명시적으로 관리 +- 순환 참조 방지 + +--- + +## 모듈 분리 원칙 + +### 레이어드 아키텍처 기반 분리 + +``` +┌─────────────────────┐ +│ API/Presentation │ ← balance-core-api +│ (Controller) │ +├─────────────────────┤ +│ Business Logic │ ← balance-core-api +│ (Service) │ +├─────────────────────┤ +│ Data Access │ ← storage +│ (Entity, Repo) │ +└─────────────────────┘ +``` + +### 권장 모듈 분리 전략 + +#### 1. Storage/Domain 모듈 +**목적:** 데이터 엔티티 및 레포지토리 + +**포함 요소:** +- JPA 엔티티 (`@Entity`) +- Repository 인터페이스 (`JpaRepository`) +- 도메인 모델 +- 데이터 접근 로직 + +**특징:** +- 다른 모듈에 의존하지 않음 (독립적) +- Spring Boot 실행 불가 (라이브러리 모듈) +- 다른 프로젝트에서 재사용 가능 + +#### 2. Core API 모듈 +**목적:** REST API 및 비즈니스 로직 + +**포함 요소:** +- REST 컨트롤러 (`@RestController`) +- 서비스 계층 (`@Service`) +- DTO (Data Transfer Objects) +- 애플리케이션 진입점 (`@SpringBootApplication`) + +**특징:** +- Storage 모듈에 의존 +- 실행 가능한 Spring Boot 애플리케이션 +- HTTP 엔드포인트 제공 + +#### 3. (선택) Common 모듈 +**목적:** 공통 유틸리티 및 설정 + +**포함 요소:** +- 공통 유틸리티 클래스 +- 공통 예외 클래스 +- 공통 설정 +- 공통 DTO + +--- + +## 표준 모듈 구조 + +### 디렉토리 구조 예시 + +``` +project-root/ +├── settings.gradle +├── build.gradle +├── storage/ +│ ├── build.gradle +│ └── src/ +│ ├── main/ +│ │ └── kotlin/ +│ │ └── com/company/project/storage/ +│ │ ├── entity/ +│ │ │ ├── User.kt +│ │ │ └── Order.kt +│ │ └── repository/ +│ │ ├── UserRepository.kt +│ │ └── OrderRepository.kt +│ └── test/ +│ └── kotlin/ +│ └── com/company/project/storage/ +│ +├── core-api/ +│ ├── build.gradle +│ └── src/ +│ ├── main/ +│ │ ├── kotlin/ +│ │ │ └── com/company/project/ +│ │ │ ├── Application.kt +│ │ │ ├── controller/ +│ │ │ │ ├── UserController.kt +│ │ │ │ └── OrderController.kt +│ │ │ └── service/ +│ │ │ ├── UserService.kt +│ │ │ └── OrderService.kt +│ │ └── resources/ +│ │ └── application.properties +│ └── test/ +│ └── kotlin/ +│ └── com/company/project/ +│ +└── common/ (선택사항) + ├── build.gradle + └── src/ + └── main/ + └── kotlin/ + └── com/company/project/common/ + ├── util/ + ├── exception/ + └── config/ +``` + +--- + +## Gradle 설정 + +### 1. 루트 프로젝트 `settings.gradle` + +```gradle +rootProject.name = 'project-name' +include 'storage' +include 'core-api' +// include 'common' // 필요시 +``` + +### 2. 루트 프로젝트 `build.gradle` + +```gradle +plugins { + id 'org.jetbrains.kotlin.jvm' version '2.2.21' apply false + id 'org.jetbrains.kotlin.plugin.spring' version '2.2.21' apply false + id 'org.springframework.boot' version '4.0.1' apply false + id 'io.spring.dependency-management' version '1.1.7' apply false + id 'org.hibernate.orm' version '7.2.0.Final' apply false + id 'org.jetbrains.kotlin.plugin.jpa' version '2.2.21' apply false +} + +group = 'com.company' +version = '0.0.1-SNAPSHOT' + +subprojects { + apply plugin: 'org.jetbrains.kotlin.jvm' + apply plugin: 'io.spring.dependency-management' + + repositories { + mavenCentral() + } + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) // 또는 17, 24 + } + } + + kotlin { + compilerOptions { + freeCompilerArgs.addAll '-Xjsr305=strict', '-Xannotation-default-target=param-property' + } + } + + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-reflect' + } + + tasks.withType(Test) { + useJUnitPlatform() + } +} +``` + +### 3. Storage 모듈 `storage/build.gradle` + +```gradle +plugins { + id 'org.jetbrains.kotlin.plugin.spring' + id 'org.springframework.boot' + id 'org.hibernate.orm' + id 'org.jetbrains.kotlin.plugin.jpa' +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + // 데이터베이스 드라이버 + runtimeOnly 'com.h2database:h2' + runtimeOnly 'org.postgresql:postgresql' + // runtimeOnly 'com.mysql:mysql-connector-j' +} + +hibernate { + enhancement { + enableAssociationManagement = true + } +} + +allOpen { + annotation 'jakarta.persistence.Entity' + annotation 'jakarta.persistence.MappedSuperclass' + annotation 'jakarta.persistence.Embeddable' +} + +// 라이브러리 모듈이므로 bootJar 비활성화 +bootJar { + enabled = false +} + +jar { + enabled = true +} +``` + +### 4. Core API 모듈 `core-api/build.gradle` + +```gradle +plugins { + id 'org.jetbrains.kotlin.plugin.spring' + id 'org.jetbrains.kotlin.plugin.jpa' + id 'org.springframework.boot' +} + +dependencies { + // 내부 모듈 의존성 + implementation project(':storage') + + // Spring Boot Starters + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-webmvc' + implementation 'org.springframework.boot:spring-boot-h2console' + implementation 'tools.jackson.module:jackson-module-kotlin' + + // 개발 도구 + developmentOnly 'org.springframework.boot:spring-boot-devtools' + + // 테스트 + testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test' + testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' + testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +// 실행 가능한 jar 생성 (기본값이므로 생략 가능) +// bootJar { +// enabled = true +// } +``` + +--- + +## 의존성 관리 전략 + +### 의존성 방향 규칙 + +``` +core-api ──→ storage + ↓ +common (선택) +``` + +**규칙:** +1. 상위 레이어는 하위 레이어에 의존 가능 +2. 하위 레이어는 상위 레이어에 의존하면 안 됨 +3. 순환 의존성 절대 금지 + +### 모듈 간 의존성 선언 + +```gradle +dependencies { + // 올바른 예 + implementation project(':storage') + implementation project(':common') + + // 잘못된 예 (storage 모듈에서) + // implementation project(':core-api') // ❌ 순환 의존성 +} +``` + +### 외부 의존성 관리 + +**루트 프로젝트에서 버전 관리:** + +```gradle +// build.gradle (root) +ext { + springCloudVersion = '2024.0.0' +} + +subprojects { + apply plugin: 'io.spring.dependency-management' + + dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } + } +} +``` + +--- + +## 패키지 구조 및 네이밍 + +### 패키지 네이밍 컨벤션 + +``` +com.company.project.{module}.{layer} +``` + +**예시:** +``` +com.quantbench.balance.storage.entity +com.quantbench.balance.storage.repository +com.quantbench.balance.controller +com.quantbench.balance.service +``` + +### 모듈별 패키지 구조 + +#### Storage 모듈 +``` +com.quantbench.balance.storage/ +├── entity/ +│ ├── User.kt +│ ├── Order.kt +│ └── Product.kt +└── repository/ + ├── UserRepository.kt + ├── OrderRepository.kt + └── ProductRepository.kt +``` + +#### Core API 모듈 +``` +com.quantbench.balance/ +├── Application.kt +├── controller/ +│ ├── UserController.kt +│ ├── OrderController.kt +│ └── ProductController.kt +├── service/ +│ ├── UserService.kt +│ ├── OrderService.kt +│ └── ProductService.kt +└── dto/ + ├── UserDto.kt + ├── OrderDto.kt + └── ProductDto.kt +``` + +### Spring Boot 애플리케이션 클래스 설정 + +```kotlin +package com.quantbench.balance + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication + +@SpringBootApplication(scanBasePackages = ["com.quantbench.balance"]) +class BalanceApplication + +fun main(args: Array) { + runApplication(*args) +} +``` + +**중요:** +- `scanBasePackages`를 명시하여 모든 모듈의 컴포넌트를 스캔 +- 패키지 구조는 일관성 있게 유지 (`com.quantbench.balance.*`) + +--- + +## 주의사항 및 베스트 프랙티스 + +### 1. 모듈 설계 원칙 + +#### ✅ DO (권장) +- 각 모듈은 단일 책임을 가져야 함 +- 모듈 간 인터페이스는 명확하게 정의 +- Storage 모듈은 가능한 한 독립적으로 유지 +- 공통 기능은 별도 모듈로 분리 + +#### ❌ DON'T (비권장) +- 순환 의존성 생성 +- 너무 많은 모듈로 과도하게 분리 (3-5개 권장) +- 하위 레이어가 상위 레이어에 의존 +- 모듈 간 패키지 직접 접근 + +### 2. Gradle 설정 주의사항 + +#### bootJar 설정 +```gradle +// Storage 모듈 (라이브러리) +bootJar { + enabled = false // 실행 불가능 +} +jar { + enabled = true // 일반 jar 생성 +} + +// API 모듈 (실행 가능) +bootJar { + enabled = true // 기본값이므로 생략 가능 +} +``` + +#### 플러그인 적용 +- 루트 프로젝트: `apply false` 사용 +- 서브 프로젝트: 필요한 플러그인만 적용 + +### 3. Spring Boot 4.0+ 사용 시 주의사항 + +Spring Boot 4.0에서는 일부 어노테이션이 변경되거나 제거되었습니다. + +**컴포넌트 스캔 설정:** +```kotlin +// ✅ 권장 (Spring Boot 4.0+) +@SpringBootApplication(scanBasePackages = ["com.quantbench.balance"]) +class BalanceApplication + +// ❌ 비권장 (구버전 방식, Spring Boot 4.0에서 문제 발생 가능) +@SpringBootApplication +@EntityScan("com.quantbench.balance.storage.entity") +@EnableJpaRepositories("com.quantbench.balance.storage.repository") +class BalanceApplication +``` + +### 4. 빌드 및 실행 + +#### 전체 빌드 +```bash +./gradlew clean build +``` + +#### 특정 모듈 빌드 +```bash +./gradlew :storage:build +./gradlew :core-api:build +``` + +#### 애플리케이션 실행 +```bash +./gradlew :core-api:bootRun +``` + +#### 의존성 확인 +```bash +./gradlew :core-api:dependencies --configuration runtimeClasspath +``` + +### 5. 테스트 전략 + +#### Storage 모듈 테스트 +```kotlin +@DataJpaTest +class UserRepositoryTest { + @Autowired + lateinit var userRepository: UserRepository + + @Test + fun `should save and find user`() { + // 테스트 코드 + } +} +``` + +#### API 모듈 통합 테스트 +```kotlin +@SpringBootTest +@AutoConfigureMockMvc +class UserControllerIntegrationTest { + @Autowired + lateinit var mockMvc: MockMvc + + @Test + fun `should return user by id`() { + mockMvc.perform(get("/api/users/1")) + .andExpect(status().isOk) + } +} +``` + +### 6. 성능 최적화 + +#### Gradle 빌드 최적화 +```properties +# gradle.properties +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configureondemand=true +``` + +#### 모듈별 병렬 빌드 +```bash +./gradlew build --parallel +``` + +--- + +## 실제 적용 예시 (balance 프로젝트) + +### 적용 전 (단일 모듈) +``` +balance/ +├── build.gradle +├── settings.gradle +└── src/ + ├── main/ + │ └── kotlin/com/quantbench/balance/ + │ └── BalanceApplication.kt + └── test/ +``` + +### 적용 후 (멀티 모듈) +``` +balance/ +├── build.gradle # 공통 설정 +├── settings.gradle # 모듈 정의 +├── storage/ # 데이터 접근 레이어 +│ ├── build.gradle +│ └── src/main/kotlin/com/quantbench/balance/storage/ +│ ├── entity/ +│ └── repository/ +└── balance-core-api/ # API 레이어 + ├── build.gradle + └── src/ + ├── main/ + │ ├── kotlin/com/quantbench/balance/ + │ │ ├── BalanceApplication.kt + │ │ ├── controller/ + │ │ └── service/ + │ └── resources/application.properties + └── test/ +``` + +### 핵심 변경 사항 + +1. **모듈 분리:** storage와 balance-core-api로 분리 +2. **의존성 방향:** balance-core-api → storage +3. **실행 가능성:** balance-core-api만 실행 가능, storage는 라이브러리 +4. **재사용성:** storage 모듈은 다른 프로젝트에서 재사용 가능 + +--- + +## 추가 확장 시나리오 + +### 1. Admin API 추가 +``` +balance/ +├── storage/ +├── balance-core-api/ +└── balance-admin-api/ # 관리자 API + ├── build.gradle + └── src/ +``` + +**의존성 구조:** +``` +balance-core-api ──→ storage +balance-admin-api ──→ storage +``` + +### 2. Batch 모듈 추가 +``` +balance/ +├── storage/ +├── balance-core-api/ +└── balance-batch/ # 배치 작업 + ├── build.gradle + └── src/ +``` + +### 3. Common 모듈 추가 +``` +balance/ +├── common/ # 공통 유틸리티 +├── storage/ +├── balance-core-api/ +└── balance-admin-api/ +``` + +**의존성 구조:** +``` +balance-core-api ──→ storage ──→ common +balance-admin-api ──→ storage ──→ common +``` + +--- + +## 문제 해결 (Troubleshooting) + +### 1. 컴파일 에러: Unresolved reference + +**문제:** +``` +e: Unresolved reference 'EntityScan' +e: Unresolved reference 'EnableJpaRepositories' +``` + +**해결:** +- Spring Boot 4.0+에서는 `@EntityScan`, `@EnableJpaRepositories` 대신 +- `@SpringBootApplication(scanBasePackages = [...])`를 사용 + +### 2. bootJar 에러 + +**문제:** +``` +Main class name has not been configured and it could not be resolved +``` + +**해결:** +```gradle +// Storage 모듈에서 +bootJar { + enabled = false +} +jar { + enabled = true +} +``` + +### 3. 컴포넌트 스캔 실패 + +**문제:** Repository나 Service가 스캔되지 않음 + +**해결:** +```kotlin +@SpringBootApplication(scanBasePackages = ["com.company.project"]) +``` +패키지 구조가 일관성 있게 유지되는지 확인 + +### 4. 순환 의존성 + +**문제:** +``` +Circular dependency between the following tasks: +:module-a:jar +:module-b:jar +``` + +**해결:** +- 모듈 간 의존성 구조 재검토 +- 공통 기능은 별도 모듈로 분리 + +--- + +## 마이그레이션 체크리스트 + +기존 프로젝트를 멀티모듈로 마이그레이션할 때: + +- [ ] 1. 모듈 구조 설계 (어떤 모듈로 분리할지 결정) +- [ ] 2. `settings.gradle`에 모듈 추가 +- [ ] 3. 루트 `build.gradle` 수정 (공통 설정 분리) +- [ ] 4. 각 모듈별 `build.gradle` 생성 +- [ ] 5. 디렉토리 구조 생성 +- [ ] 6. 기존 소스 코드를 적절한 모듈로 이동 +- [ ] 7. 패키지 구조 정리 +- [ ] 8. Application 클래스 수정 (컴포넌트 스캔 설정) +- [ ] 9. 빌드 테스트 (`./gradlew clean build`) +- [ ] 10. 애플리케이션 실행 테스트 +- [ ] 11. 단위 테스트 및 통합 테스트 검증 +- [ ] 12. CI/CD 파이프라인 업데이트 (필요시) + +--- + +## 참고 자료 + +- [Spring Boot Reference Documentation](https://docs.spring.io/spring-boot/index.html) +- [Gradle Multi-Project Builds](https://docs.gradle.org/current/userguide/multi_project_builds.html) +- [Kotlin and Spring Boot](https://spring.io/guides/tutorials/spring-boot-kotlin) + +--- + +## 버전 정보 + +- Spring Boot: 4.0.1 +- Kotlin: 2.2.21 +- Gradle: 9.2.1 +- Java: 24 (또는 21, 17) + +--- + +**작성일:** 2026-01-11 +**프로젝트:** balance +**작성자:** Claude Code diff --git a/LINT.md b/LINT.md new file mode 100644 index 0000000..7dc3e86 --- /dev/null +++ b/LINT.md @@ -0,0 +1,344 @@ +# Kotlin Lint 설정 가이드 + +## 개요 + +이 프로젝트는 Kotlin 코드 품질을 유지하기 위해 **ktlint**를 사용합니다. + +## 사용 가능한 Lint 도구 + +### 1. ktlint (활성화됨 ✅) + +Kotlin 공식 코드 스타일 가이드를 따르는 린터 및 포맷터입니다. + +**특징:** +- 코드 스타일 체크 +- 자동 포맷팅 기능 +- Kotlin 2.2.21과 완벽 호환 + +### 2. detekt (현재 비활성화됨 ⚠️) + +정적 코드 분석 도구로 코드 냄새, 복잡도, 잠재적 버그를 탐지합니다. + +**비활성화 이유:** +- Kotlin 2.2.21과 호환성 문제 +- detekt 1.23.7은 Kotlin 2.0.10까지만 지원 +- 향후 detekt가 Kotlin 2.2.x를 지원하면 재활성화 예정 + +**활성화 방법:** +1. `build.gradle`에서 detekt 관련 주석 제거 +2. Kotlin 버전을 2.0.x로 다운그레이드하거나 +3. detekt 최신 버전(Kotlin 2.2.x 지원) 출시 대기 + +--- + +## 사용 가능한 Gradle Tasks + +### 코드 스타일 체크 + +```bash +# 모든 모듈의 Kotlin 코드 스타일 체크 +./gradlew ktlintCheck + +# 특정 모듈만 체크 +./gradlew :balance-core-api:ktlintCheck +./gradlew :storage:ktlintCheck +``` + +### 자동 포맷팅 + +```bash +# 모든 모듈의 Kotlin 코드 자동 포맷팅 +./gradlew ktlintFormat + +# 특정 모듈만 포맷팅 +./gradlew :balance-core-api:ktlintFormat +./gradlew :storage:ktlintFormat +``` + +### 빌드와 함께 실행 + +```bash +# 빌드 전에 자동으로 ktlint 체크 실행 +./gradlew clean build + +# 또는 +./gradlew check # 모든 검증 태스크 실행 (ktlint 포함) +``` + +--- + +## ktlint 설정 + +### 1. 기본 설정 (build.gradle) + +```gradle +ktlint { + version = '1.4.1' + android = false + outputToConsole = true + coloredOutput = true + ignoreFailures = false + filter { + exclude('**/generated/**') + exclude('**/build/**') + } +} +``` + +### 2. .editorconfig 설정 + +프로젝트 루트의 `.editorconfig` 파일에서 코드 스타일을 세부 설정할 수 있습니다. + +```ini +[*.{kt,kts}] +indent_size = 4 +continuation_indent_size = 4 +max_line_length = 120 +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true +``` + +--- + +## IDE 통합 + +### IntelliJ IDEA / Android Studio + +#### 1. ktlint 플러그인 설치 + +1. `Preferences` → `Plugins` +2. "ktlint" 검색 +3. 설치 및 재시작 + +#### 2. EditorConfig 활성화 + +1. `Preferences` → `Editor` → `Code Style` +2. "Enable EditorConfig support" 체크 + +#### 3. 저장 시 자동 포맷팅 (선택사항) + +1. `Preferences` → `Tools` → `Actions on Save` +2. "Reformat code" 체크 +3. "Optimize imports" 체크 + +--- + +## CI/CD 통합 + +### GitHub Actions 예시 + +```yaml +name: Lint Check + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 24 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '24' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Run ktlint + run: ./gradlew ktlintCheck + + - name: Upload ktlint reports + if: failure() + uses: actions/upload-artifact@v3 + with: + name: ktlint-reports + path: '**/build/reports/ktlint/' +``` + +--- + +## 일반적인 린트 규칙 + +### 1. 들여쓰기 +- 4 스페이스 사용 (탭 사용 금지) + +### 2. 최대 줄 길이 +- 120자 제한 + +### 3. 임포트 +- 와일드카드 임포트 금지 (java.util.* 제외) +- 사용하지 않는 임포트 제거 + +### 4. 네이밍 +- 클래스: PascalCase +- 함수/변수: camelCase +- 상수: UPPER_SNAKE_CASE + +### 5. 공백 +- 파일 끝에 빈 줄 추가 +- 클래스 본문 시작/끝에 불필요한 빈 줄 제거 +- 불필요한 공백 제거 + +--- + +## 린트 위반 사항 수정 + +### 자동 수정 (권장) + +대부분의 스타일 위반은 자동으로 수정할 수 있습니다. + +```bash +./gradlew ktlintFormat +``` + +### 수동 수정 + +자동 수정이 불가능한 경우, 린트 리포트를 확인하고 수동으로 수정합니다. + +**리포트 위치:** +``` +build/reports/ktlint/ +├── ktlintMainSourceSetCheck.txt +└── ktlintTestSourceSetCheck.txt +``` + +### 특정 규칙 비활성화 (비권장) + +정말 필요한 경우에만 사용합니다. + +```kotlin +// 한 줄 비활성화 +@Suppress("ktlint:standard:max-line-length") +val veryLongVariableName = "..." + +// 파일 전체 비활성화 +@file:Suppress("ktlint:standard:filename") +``` + +--- + +## 커밋 전 체크리스트 + +코드를 커밋하기 전에 다음을 실행하세요: + +```bash +# 1. 코드 포맷팅 +./gradlew ktlintFormat + +# 2. 린트 체크 +./gradlew ktlintCheck + +# 3. 빌드 및 테스트 +./gradlew clean build +``` + +--- + +## Git Hook 설정 (선택사항) + +커밋 전에 자동으로 ktlint를 실행하도록 설정할 수 있습니다. + +### pre-commit hook 생성 + +`.git/hooks/pre-commit` 파일 생성: + +```bash +#!/bin/sh +echo "Running ktlint check..." +./gradlew ktlintCheck + +if [ $? -ne 0 ]; then + echo "❌ ktlint check failed. Please run './gradlew ktlintFormat' to fix." + exit 1 +fi + +echo "✅ ktlint check passed!" +``` + +실행 권한 부여: +```bash +chmod +x .git/hooks/pre-commit +``` + +--- + +## 문제 해결 + +### 1. ktlint 버전 호환성 문제 + +**증상:** +``` +Class org.jetbrains.kotlin.lexer.KtTokens does not have member field... +``` + +**해결:** +- `build.gradle`에서 ktlint 버전을 1.4.1 이상으로 업데이트 +- Kotlin 버전과 호환되는 ktlint 버전 확인 + +### 2. 포맷팅 후에도 체크 실패 + +**원인:** +- IDE와 ktlint의 포맷팅 규칙 불일치 + +**해결:** +```bash +# ktlint 포맷팅 사용 +./gradlew ktlintFormat + +# IDE 포맷팅 사용 금지 또는 .editorconfig 확인 +``` + +### 3. 특정 파일 제외하기 + +`build.gradle`의 ktlint 설정 수정: + +```gradle +ktlint { + filter { + exclude('**/generated/**') + exclude('**/build/**') + exclude('**/MySpecialFile.kt') // 특정 파일 제외 + } +} +``` + +--- + +## 참고 자료 + +- [ktlint 공식 문서](https://pinterest.github.io/ktlint/) +- [Kotlin 코드 스타일 가이드](https://kotlinlang.org/docs/coding-conventions.html) +- [EditorConfig](https://editorconfig.org/) + +--- + +## 향후 계획 + +### detekt 재활성화 + +Kotlin 2.2.x를 지원하는 detekt 버전이 출시되면: + +1. `build.gradle`에서 주석 해제: +```gradle +apply plugin: 'io.gitlab.arturbosch.detekt' +``` + +2. detekt 설정 활성화 + +3. 정적 분석 실행: +```bash +./gradlew detekt +``` + +--- + +**최종 업데이트:** 2026-01-11 +**ktlint 버전:** 1.4.1 +**Kotlin 버전:** 2.2.21 diff --git a/balance-core-api/build.gradle b/balance-core-api/build.gradle new file mode 100644 index 0000000..b8fcd98 --- /dev/null +++ b/balance-core-api/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'org.jetbrains.kotlin.plugin.spring' + id 'org.jetbrains.kotlin.plugin.jpa' + id 'org.springframework.boot' + id 'org.graalvm.buildtools.native' +} + +dependencies { + implementation project(':storage') + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-webmvc' + implementation 'org.springframework.boot:spring-boot-h2console' + implementation 'tools.jackson.module:jackson-module-kotlin' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test' + testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' + testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} diff --git a/src/main/kotlin/com/quantbench/balance/BalanceApplication.kt b/balance-core-api/src/main/kotlin/com/quantbench/balance/BalanceApplication.kt similarity index 78% rename from src/main/kotlin/com/quantbench/balance/BalanceApplication.kt rename to balance-core-api/src/main/kotlin/com/quantbench/balance/BalanceApplication.kt index 988086a..79f6aff 100644 --- a/src/main/kotlin/com/quantbench/balance/BalanceApplication.kt +++ b/balance-core-api/src/main/kotlin/com/quantbench/balance/BalanceApplication.kt @@ -3,7 +3,7 @@ package com.quantbench.balance import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication -@SpringBootApplication +@SpringBootApplication(scanBasePackages = ["com.quantbench.balance"]) class BalanceApplication fun main(args: Array) { diff --git a/src/main/resources/application.properties b/balance-core-api/src/main/resources/application.properties similarity index 100% rename from src/main/resources/application.properties rename to balance-core-api/src/main/resources/application.properties diff --git a/src/test/kotlin/com/quantbench/balance/BalanceApplicationTests.kt b/balance-core-api/src/test/kotlin/com/quantbench/balance/BalanceApplicationTests.kt similarity index 99% rename from src/test/kotlin/com/quantbench/balance/BalanceApplicationTests.kt rename to balance-core-api/src/test/kotlin/com/quantbench/balance/BalanceApplicationTests.kt index 2298bcf..8ade3a6 100644 --- a/src/test/kotlin/com/quantbench/balance/BalanceApplicationTests.kt +++ b/balance-core-api/src/test/kotlin/com/quantbench/balance/BalanceApplicationTests.kt @@ -5,9 +5,7 @@ import org.springframework.boot.test.context.SpringBootTest @SpringBootTest class BalanceApplicationTests { - @Test fun contextLoads() { } - } diff --git a/build.gradle b/build.gradle index 973e302..fd07d9b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,60 +1,85 @@ plugins { - id 'org.jetbrains.kotlin.jvm' version '2.2.21' - id 'org.jetbrains.kotlin.plugin.spring' version '2.2.21' - id 'org.springframework.boot' version '4.0.1' - id 'io.spring.dependency-management' version '1.1.7' - id 'org.hibernate.orm' version '7.2.0.Final' - id 'org.graalvm.buildtools.native' version '0.11.3' - id 'org.jetbrains.kotlin.plugin.jpa' version '2.2.21' + id 'org.jetbrains.kotlin.jvm' version '2.2.21' apply false + id 'org.jetbrains.kotlin.plugin.spring' version '2.2.21' apply false + id 'org.springframework.boot' version '4.0.1' apply false + id 'io.spring.dependency-management' version '1.1.7' apply false + id 'org.hibernate.orm' version '7.2.0.Final' apply false + id 'org.graalvm.buildtools.native' version '0.11.3' apply false + id 'org.jetbrains.kotlin.plugin.jpa' version '2.2.21' apply false + id 'org.jlleitschuh.gradle.ktlint' version '12.1.1' apply false + id 'io.gitlab.arturbosch.detekt' version '1.23.7' apply false } group = 'com.quantbench' version = '0.0.1-SNAPSHOT' description = 'balance' -java { - toolchain { - languageVersion = JavaLanguageVersion.of(24) +subprojects { + apply plugin: 'org.jetbrains.kotlin.jvm' + apply plugin: 'io.spring.dependency-management' + apply plugin: 'org.jlleitschuh.gradle.ktlint' + // detekt는 Kotlin 2.2.21과 호환성 문제가 있어 주석 처리 + // Kotlin 2.0.x로 다운그레이드하거나 detekt 최신 버전 출시 시 활성화 + // apply plugin: 'io.gitlab.arturbosch.detekt' + + repositories { + mavenCentral() } -} -repositories { - mavenCentral() -} - -dependencies { - implementation 'org.springframework.boot:spring-boot-h2console' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-webmvc' - implementation 'org.jetbrains.kotlin:kotlin-reflect' - implementation 'tools.jackson.module:jackson-module-kotlin' - developmentOnly 'org.springframework.boot:spring-boot-devtools' - runtimeOnly 'com.h2database:h2' - runtimeOnly 'org.postgresql:postgresql' - testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test' - testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' - testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' -} - -kotlin { - compilerOptions { - freeCompilerArgs.addAll '-Xjsr305=strict', '-Xannotation-default-target=param-property' + java { + toolchain { + languageVersion = JavaLanguageVersion.of(24) + } } -} -hibernate { - enhancement { - enableAssociationManagement = true + kotlin { + compilerOptions { + freeCompilerArgs.addAll '-Xjsr305=strict', '-Xannotation-default-target=param-property' + } } -} -allOpen { - annotation 'jakarta.persistence.Entity' - annotation 'jakarta.persistence.MappedSuperclass' - annotation 'jakarta.persistence.Embeddable' -} + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-reflect' + // detektPlugins 'io.gitlab.arturbosch.detekt:detekt-formatting:1.23.7' + } -tasks.named('test') { - useJUnitPlatform() + tasks.withType(Test) { + useJUnitPlatform() + } + + // ktlint 설정 + ktlint { + version = '1.4.1' + android = false + outputToConsole = true + coloredOutput = true + ignoreFailures = false + filter { + exclude('**/generated/**') + exclude('**/build/**') + } + } + + // detekt 설정 (현재 비활성화 - Kotlin 2.2.21과 호환성 문제) + // detekt { + // buildUponDefaultConfig = true + // allRules = false + // config.setFrom(files("$rootDir/detekt.yml")) + // // baseline은 존재할 때만 사용 + // def baselineFile = file("$rootDir/detekt-baseline.xml") + // if (baselineFile.exists()) { + // baseline = baselineFile + // } + // ignoreFailures = false + // } + + // tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach { + // reports { + // html.required = true + // xml.required = false + // txt.required = false + // sarif.required = false + // md.required = false + // } + // } } diff --git a/detekt.yml b/detekt.yml new file mode 100644 index 0000000..d5f4f9f --- /dev/null +++ b/detekt.yml @@ -0,0 +1,595 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + complexity: 2 + LongParameterList: 1 + style: 1 + comments: 1 + +config: + validation: true + warningsAsErrors: false + checkExhaustiveness: false + +processors: + active: true + +console-reports: + active: true + +output-reports: + active: true + +comments: + active: true + AbsentOrWrongFileLicense: + active: false + CommentOverPrivateFunction: + active: false + CommentOverPrivateProperty: + active: false + DeprecatedBlockTag: + active: false + EndOfSentenceFormat: + active: false + OutdatedDocumentation: + active: false + UndocumentedPublicClass: + active: false + UndocumentedPublicFunction: + active: false + UndocumentedPublicProperty: + active: false + +complexity: + active: true + ComplexCondition: + active: true + threshold: 4 + ComplexInterface: + active: false + CyclomaticComplexMethod: + active: true + threshold: 15 + ignoreSingleWhenExpression: false + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + LabeledExpression: + active: false + LargeClass: + active: true + threshold: 600 + LongMethod: + active: true + threshold: 60 + LongParameterList: + active: true + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotatedParameter: [] + MethodOverloading: + active: false + NamedArguments: + active: false + NestedBlockDepth: + active: true + threshold: 4 + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + TooManyFunctions: + active: true + thresholdInFiles: 20 + thresholdInClasses: 20 + thresholdInInterfaces: 20 + thresholdInObjects: 20 + thresholdInEnums: 20 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false + +coroutines: + active: true + GlobalCoroutineUsage: + active: false + InjectDispatcher: + active: false + RedundantSuspendModifier: + active: true + SleepInsteadOfDelay: + active: true + SuspendFunSwallowedCancellation: + active: false + SuspendFunWithCoroutineScopeReceiver: + active: false + SuspendFunWithFlowReturnType: + active: true + +empty-blocks: + active: true + EmptyCatchBlock: + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: + active: true + EmptyDefaultConstructor: + active: true + EmptyDoWhileBlock: + active: true + EmptyElseBlock: + active: true + EmptyFinallyBlock: + active: true + EmptyForBlock: + active: true + EmptyFunctionBlock: + active: true + ignoreOverridden: false + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyTryBlock: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: true + methodNames: + - 'equals' + - 'finalize' + - 'hashCode' + - 'toString' + InstanceOfCheckForException: + active: true + NotImplementedDeclaration: + active: false + ObjectExtendsThrowable: + active: false + PrintStackTrace: + active: true + RethrowCaughtException: + active: true + ReturnFromFinally: + active: true + ignoreLabeled: false + SwallowedException: + active: true + ignoredExceptionTypes: + - 'InterruptedException' + - 'MalformedURLException' + - 'NumberFormatException' + - 'ParseException' + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + active: true + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: true + exceptions: + - 'ArrayIndexOutOfBoundsException' + - 'Exception' + - 'IllegalArgumentException' + - 'IllegalMonitorStateException' + - 'IllegalStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + ThrowingNewInstanceOfSameException: + active: true + TooGenericExceptionCaught: + active: true + exceptionNames: + - 'ArrayIndexOutOfBoundsException' + - 'Error' + - 'Exception' + - 'IllegalMonitorStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: true + exceptionNames: + - 'Error' + - 'Exception' + - 'RuntimeException' + - 'Throwable' + +naming: + active: true + BooleanPropertyNaming: + active: false + ClassNaming: + active: true + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + EnumNaming: + active: true + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + FunctionMaxLength: + active: false + FunctionMinLength: + active: false + FunctionNaming: + active: true + excludeClassPattern: '$^' + functionPattern: '[a-z][a-zA-Z0-9]*' + excludeAnnotatedFunction: ['Composable'] + ignoreAnnotated: ['Test'] + FunctionParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + InvalidPackageDeclaration: + active: true + rootPackage: '' + LambdaParameterNaming: + active: false + MatchingDeclarationName: + active: true + mustBeFirst: true + MemberNameEqualsClassName: + active: true + ignoreOverridden: true + NoNameShadowing: + active: true + NonBooleanPropertyPrefixedWithIs: + active: false + ObjectPropertyNaming: + active: true + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: true + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: false + VariableMinLength: + active: false + VariableNaming: + active: true + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + +performance: + active: true + ArrayPrimitive: + active: true + CouldBeSequence: + active: false + ForEachOnRange: + active: true + SpreadOperator: + active: false + UnnecessaryPartOfBinaryExpression: + active: false + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: true + AvoidReferentialEquality: + active: true + forbiddenTypePatterns: + - 'kotlin.String' + CastNullableToNonNullableType: + active: false + CastToNullableType: + active: false + Deprecation: + active: false + DontDowncastCollectionTypes: + active: false + DoubleMutabilityForCollection: + active: true + ElseCaseInsteadOfExhaustiveWhen: + active: false + EqualsAlwaysReturnsTrueOrFalse: + active: true + EqualsWithHashCodeExist: + active: true + ExitOutsideMain: + active: false + ExplicitGarbageCollectionCall: + active: true + HasPlatformType: + active: true + IgnoredReturnValue: + active: true + ImplicitDefaultLocale: + active: true + ImplicitUnitReturnType: + active: false + InvalidRange: + active: true + IteratorHasNextCallsNextMethod: + active: true + IteratorNotThrowingNoSuchElementException: + active: true + LateinitUsage: + active: false + MapGetWithNotNullAssertionOperator: + active: true + MissingPackageDeclaration: + active: false + NullCheckOnMutableProperty: + active: false + NullableToStringCall: + active: false + UnconditionalJumpStatementInLoop: + active: false + UnnecessaryNotNullCheck: + active: false + UnnecessaryNotNullOperator: + active: true + UnnecessarySafeCall: + active: true + UnreachableCatchBlock: + active: true + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: true + UnsafeCast: + active: true + UnusedUnaryOperator: + active: true + UselessPostfixExpression: + active: true + WrongEqualsTypeParameter: + active: true + +style: + active: true + AlsoCouldBeApply: + active: false + BracesOnIfStatements: + active: false + BracesOnWhenStatements: + active: false + CanBeNonNullable: + active: false + CascadingCallWrapping: + active: false + ClassOrdering: + active: false + CollapsibleIfStatements: + active: false + DataClassContainsFunctions: + active: false + DataClassShouldBeImmutable: + active: false + DestructuringDeclarationWithTooManyEntries: + active: true + maxDestructuringEntries: 3 + DoubleNegativeLambda: + active: false + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: false + ExplicitCollectionElementAccessMethod: + active: false + ExplicitItLambdaParameter: + active: false + ExpressionBodySyntax: + active: false + ForbiddenAnnotation: + active: false + ForbiddenComment: + active: true + values: + - 'FIXME:' + - 'STOPSHIP:' + - 'TODO:' + allowedPatterns: '' + ForbiddenImport: + active: false + ForbiddenMethodCall: + active: false + ForbiddenSuppress: + active: false + ForbiddenVoid: + active: true + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: + active: true + ignoreOverridableFunction: true + ignoreActualFunction: true + excludedFunctions: [] + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 1 + MagicNumber: + active: true + excludes: + - '**/test/**' + - '**/androidTest/**' + - '**/commonTest/**' + - '**/jvmTest/**' + - '**/jsTest/**' + - '**/iosTest/**' + ignoreNumbers: + - '-1' + - '0' + - '1' + - '2' + ignoreHashCodeFunction: true + ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: false + ignoreRanges: false + ignoreExtensionFunctions: true + MandatoryBracesLoops: + active: false + MaxChainedCallsOnSameLine: + active: false + MaxLineLength: + active: true + maxLineLength: 120 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: false + MayBeConst: + active: true + ModifierOrder: + active: true + MultilineLambdaItParameter: + active: false + MultilineRawStringIndentation: + active: false + NestedClassesVisibility: + active: true + NewLineAtEndOfFile: + active: true + NoTabs: + active: false + NullableBooleanCheck: + active: false + ObjectLiteralToLambda: + active: true + OptionalAbstractKeyword: + active: true + OptionalUnit: + active: false + PreferToOverPairSyntax: + active: false + ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: + active: false + RedundantHigherOrderMapUsage: + active: true + RedundantVisibilityModifierRule: + active: false + ReturnCount: + active: true + max: 2 + excludedFunctions: + - 'equals' + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: false + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: true + SpacingBetweenPackageAndImports: + active: false + StringShouldBeRawString: + active: false + ThrowsCount: + active: true + max: 2 + excludeGuardClauses: false + TrailingWhitespace: + active: false + TrimMultilineRawString: + active: false + UnderscoresInNumericLiterals: + active: false + UnnecessaryAbstractClass: + active: true + UnnecessaryAnnotationUseSiteTarget: + active: false + UnnecessaryApply: + active: true + UnnecessaryBackticks: + active: false + UnnecessaryBracesAroundTrailingLambda: + active: false + UnnecessaryFilter: + active: true + UnnecessaryInheritance: + active: true + UnnecessaryInnerClass: + active: false + UnnecessaryLet: + active: false + UnnecessaryParentheses: + active: false + UntilInsteadOfRangeTo: + active: false + UnusedImports: + active: false + UnusedParameter: + active: true + allowedNames: 'ignored|expected' + UnusedPrivateClass: + active: true + UnusedPrivateMember: + active: true + allowedNames: '' + UnusedPrivateProperty: + active: true + allowedNames: '_|ignored|expected|serialVersionUID' + UseAnyOrNoneInsteadOfFind: + active: true + UseArrayLiteralsInAnnotations: + active: true + UseCheckNotNull: + active: true + UseCheckOrError: + active: true + UseDataClass: + active: false + UseEmptyCounterpart: + active: false + UseIfEmptyOrIfBlank: + active: false + UseIfInsteadOfWhen: + active: false + UseIsNullOrEmpty: + active: true + UseOrEmpty: + active: true + UseRequire: + active: true + UseRequireNotNull: + active: true + UseSumOfInsteadOfFlatMapSize: + active: false + UselessCallOnNotNull: + active: true + UtilityClassWithPublicConstructor: + active: true + VarCouldBeVal: + active: true + WildcardImport: + active: true + excludeImports: + - 'java.util.*' diff --git a/settings.gradle b/settings.gradle index d0ac8e5..4129e42 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,3 @@ rootProject.name = 'balance' +include 'storage' +include 'balance-core-api' diff --git a/storage/build.gradle b/storage/build.gradle new file mode 100644 index 0000000..3938690 --- /dev/null +++ b/storage/build.gradle @@ -0,0 +1,33 @@ +plugins { + id 'org.jetbrains.kotlin.plugin.spring' + id 'org.springframework.boot' + id 'org.hibernate.orm' + id 'org.jetbrains.kotlin.plugin.jpa' +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + runtimeOnly 'com.h2database:h2' + runtimeOnly 'org.postgresql:postgresql' +} + +hibernate { + enhancement { + enableAssociationManagement = true + } +} + +allOpen { + annotation 'jakarta.persistence.Entity' + annotation 'jakarta.persistence.MappedSuperclass' + annotation 'jakarta.persistence.Embeddable' +} + +// 라이브러리 모듈이므로 bootJar 비활성화 +bootJar { + enabled = false +} + +jar { + enabled = true +}