
2026-07-28 기준 실제 모듈 구성. settings.gradle.kts 와 대조해 갱신한다.
(Root Project)
├── gradle/
│ └── libs.versions.toml # 의존성·버전 중앙 관리 (Version Catalog)
│
├── build-logic/ # 빌드 공통 로직 (Convention Plugins)
│ ├── AndroidApplicationConventionPlugin.kt # 앱 모듈
│ ├── AndroidLibraryConventionPlugin.kt # 라이브러리 모듈 기초
│ ├── AndroidFeatureConventionPlugin.kt # feature 모듈 조합
│ ├── AndroidLibraryComposeConventionPlugin.kt # Compose 공통 설정
│ ├── AndroidDataConventionPlugin.kt # data 레이어
│ ├── AndroidDomainConventionPlugin.kt # domain 레이어 (Pure Kotlin)
│ ├── AndroidDatastoreConventionPlugin.kt # DataStore
│ ├── AndroidHiltConventionPlugin.kt # Hilt
│ ├── AndroidNavigationConventionPlugin.kt # Navigation
│ ├── AndroidRetrofitConventionPlugin.kt # Retrofit
│ ├── AndroidLintConventionPlugin.kt # KtLint + Compose Rules
│ ├── AndroidCommonConfig.kt # 공통 SDK·컴파일 옵션
│ ├── AfterNoteDependencyScope.kt # 의존성 선언 헬퍼
│ └── ProjectExtensions.kt
│
├── app/ # 애플리케이션 모듈 (진입점, MainActivity, DI 조립)
│
├── core/ # 전역 공통 인프라
│ ├── common/ # 유틸·확장·디스패처·알림 스케줄러
│ ├── data/ # core 레벨 Repository 구현체 (인증·유저·계정·업로드)
│ ├── datastore/ # DataStore (토큰·유저 프로필)
│ ├── di/ # core Repository·Dispatcher DI 모듈
│ ├── domain/ # core Repository 인터페이스·도메인 예외
│ ├── model/ # 여러 기능이 공유하는 순수 모델 (DTO 아님)
│ ├── network/ # Retrofit·OkHttp·인터셉터·토큰 재발급·공용 ApiService
│ ├── startup/ # App Startup 초기화 (카카오 SDK, 알림)
│ └── ui/ # 디자인 시스템·공통 Compose 컴포넌트 (theme, button, topbar …)
│
├── feature/ # 기능별 모듈 — 각각 data / domain / presentation
│ ├── afternote/ # 애프터노트 (작성자 + 수신자 열람)
│ ├── mindrecord/ # 마음의 기록 (일기·데일리질문·주간리포트)
│ ├── onboarding/ # 온보딩·로그인·회원가입
│ ├── receiver/ # 수신자 공통
│ ├── setting/ # 설정
│ └── timeletter/ # 타임레터 (+ res 모듈)
│
└── konsist/ # 레이어 의존 방향 회귀 테스트
konsist 테스트가 빌드에서 잡는다.{ status, code, message, data } 형태다 (core/network 의 BaseResponse).status 가 200 이 아니면 실패다. requireData() / requireStatus() 가 이 검사를 하고 ApiException 을 던진다.ApiException.serverMessage 는 서버가 준 사용자 노출용 문구고, 없으면 null 이다. message 는 로그·Crashlytics 용 폴백이라 사용자에게 보여주면 안 된다.AuthInterceptor 가 한다.TokenAuthenticator 가 맡고, 실제 회전은 두 경로 모두 TokenReissuer 의 단일 락을 지나므로 동시에 갱신되지 않는다.