Skip to content

코딩 컨벤션

truloop-core의 코딩 규칙, 정적 분석 도구 설정, 네이밍 규칙을 설명합니다.


정적 분석 도구

Ktlint (v14.0.1 / CLI v1.8.0)

Kotlin 코드 포맷팅 도구입니다. 일관된 코드 스타일을 자동으로 적용합니다. buildSrc/kotlin-common.gradle.kts에서 전체 모듈에 적용됩니다.

bash
# 포맷 자동 수정
./gradlew ktlintFormat

# 포맷 검증만 수행
./gradlew ktlintCheck

Detekt (v1.23.8)

Kotlin 정적 분석 도구입니다. 코드 냄새, 복잡도, 잠재적 버그를 검출합니다. 설정 파일은 gradle/detekt/detekt.yaml에 위치합니다.

bash
# 정적 분석 실행
./gradlew detekt

통합 실행

bash
# 포맷 + 정적 분석 한 번에 실행
./gradlew ktlintFormat ktlintCheck detekt

네이밍 규칙

패키지

  • 기본 패키지: co.butbeautiful.truloop.*
  • 도메인 기능별 패키지: co.butbeautiful.truloop.{layer}.{feature}

클래스 네이밍

계층패턴예시
Domain Entity{Name}Loop, User, Story (Recap의 내부명)
Value Object{Name}LoopStatus, Location
Use Case{Action}{Entity}UseCaseCreateLoopUseCase, GetUserUseCase
Repository Port{Entity}RepositoryLoopRepository, UserRepository
Query Port{Entity}QueriesLoopQueries, UserQueries
Persistence AdapterExposed{Entity}AdapterExposedLoopAdapter
External Adapter{Service}{Purpose}AdapterFcmNotificationAdapter, S3FileStorageAdapter
Controller (Web){Feature}ControllerLoopController, UserController
Handler (Web){Feature}HandlerMediaUploadHandler
Route{feature}Routes()loopAuthenticatedRoutes(), userPublicRoutes()
Exception{Feature}Exceptions.{Name}LoopExceptions.LoopNotFound
Event Handler{Feature}{Action}Handler / {Feature}EventHandlersKtLoopNotificationEventHandler, UserEventHandlersKt
Table (Exposed){Entity}sTableUsersTable, LoopsTable
DI Module{scope}ModuleinfrastructureModule, persistenceModule

DI 어노테이션 규칙

어노테이션용도적용 대상
@Single싱글턴 (앱 수명 동안 1개)Persistence Adapter, EventHandler
@Factory매 요청마다 새 인스턴스UseCase, Controller/Handler
@InjectedParam런타임 파라미터parametersOf(...) 전달 값

주의

@Provided는 사용하지 않습니다. 모든 의존성은 Koin의 통합 그래프(DSL 모듈 + Annotation Scan)에서 해결됩니다.


ID 규칙

EID (External ID)

  • 외부 공개 ID: TSID 기반의 eid 문자열 사용
  • 내부 DB ID: 내부 FK/관계에만 사용, 절대 외부에 노출하지 않음
  • API 응답에는 항상 eid를 포함
kotlin
// 올바름
data class LoopResponse(val eid: String, ...)

// 잘못됨 - 내부 ID 노출
data class LoopResponse(val id: Long, ...)

Repository 설계 규칙

CQS (Command/Query Separation)

각 Aggregate마다 2개의 인터페이스를 정의합니다:

인터페이스역할예시 메서드
{Aggregate}RepositoryCommand (상태 변경)findByEid, create, update, delete
{Aggregate}QueriesQuery (읽기 전용)findByLoopId, findCompletedByEid, search
  • 단일 구현 클래스가 두 인터페이스를 모두 구현
  • Reader/Writer/QueryRepository/Port 남발 금지 (YAGNI)

Web Layer 규칙

Route 파일 규칙

  1. Route 파일에서 UseCase.execute() 직접 호출 금지
  2. Route는 HTTP 관심사만 처리 (파싱, 상태 코드, 응답)
  3. Controller/Handler가 UseCase 호출과 응답 매핑을 담당

인증 경계 규칙

  • authenticate("auth-jwt"), authenticate("internal-api") 등 인증 경계는 RouteRegistry에서만 설정
  • Feature Route 함수는 auth-neutral (인증 불가지론적)
  • Public/Authenticated 혼합: 별도 함수로 분리 ({feature}PublicRoutes(), {feature}AuthenticatedRoutes())

트랜잭션 규칙

  • Exposed 작업은 UnitOfWork.transactional / transactionalRaise로 감싸기
  • Use Case 레벨에서 트랜잭션 경계 설정
  • 중첩 트랜잭션 지양

이벤트 규칙

  • Domain Event 발행은 EventPublisher Port를 통해 수행
  • Fire-and-forget: 예외를 발생시키지 않으며, try-catch 불필요
  • EventBus (Bootstrap) → EventHandler 디스패치 → 에러 시 Sentry 보고

테스트 규칙

항목규칙
프레임워크Kotest 6.1.4 FunSpec 중심
모킹MockK 1.14.9
격리상태 공유 시 IsolationMode.InstancePerTest 사용
트랜잭션TestUnitOfWork 사용
HTTP 테스트ktor-server-test-host + client mock
커버리지Kover 0.9.7 (./gradlew koverHtmlReport)
통합 테스트bootstrap/src/integrationTest/

설정 및 환경

AppConfig

bootstrap/src/main/resources/application.yaml에서 YAML 형식으로 설정을 관리합니다. 28개 설정 섹션을 지원하며, 필수 설정이 누락되면 시작 시 fast-fail합니다.

Review Mode

reviewMode.enabled가 true일 때, 허용된 전화번호만 통과합니다. 테스트/샌드박스에서 의도치 않게 활성화하지 않도록 주의합니다.

SQS Consumer

aws.sqsMediaUploadQueueUrl 또는 aws.sqsMediaReadyQueueUrl이 설정되고 테스트 모드가 아닌 경우에만 시작합니다.


변경 이력

날짜내용
2026-03-10Ktlint(v14.0.1/CLI v1.8.0), Detekt(v1.23.8), Kotest(6.1.4), MockK(1.14.9), Kover(0.9.7) 버전 정보 갱신. Detekt 설정 파일 경로 명시. Event Handler 클래스 네이밍 패턴 추가.