백엔드 개요
Lumie 백엔드는 app, libs/*, 그리고
lumie-backend/settings.gradle.kts에 포함된 Gradle 서브프로젝트들로
구성된 하나의 Spring Boot 배포 단위입니다. 엔트리포인트는
lumie-backend/app/src/main/java/com/lumie/app/LumieApplication.java이며,
이 클래스는 @SpringBootApplication(scanBasePackages = "com.lumie")와
@EnableAsync로 단일 JVM을 시작합니다.
이 페이지는 overview 문서입니다. 배포 경계, 주요 런타임 흐름,
그리고 모든 백엔드 모듈이 따르는 공통 규칙을 한눈에 보여줍니다.
소스 경로
| Path | 역할 |
|---|---|
lumie-backend/settings.gradle.kts | 백엔드 런타임에 포함되는 서브프로젝트를 선언합니다 |
lumie-backend/app/build.gradle.kts | app, libs/*, 포함된 modules/*로부터 배포 jar를 조립합니다 |
lumie-backend/app/src/main/java/com/lumie/app/LumieApplication.java | Spring Boot 엔트리포인트 |
lumie-backend/app/src/main/resources/application.yaml | 데이터 소스, Flyway, Modulith, RabbitMQ, Redis, MinIO, CORS, rate limit, worker URL에 대한 런타임 설정 |
lumie-backend/libs/common | 공통 tenant, auth, exception, idempotency, logging, base-entity 유틸리티 |
lumie-backend/libs/internal-api | 프로세스 내부 모듈 계약과 모듈 간 이벤트 |
lumie-backend/libs/messaging | RabbitMQ queue, exchange, routing-key 상수 |
lumie-backend/modules/* | 동일한 JVM에 로드되는 제품 및 플랫폼 모듈 |
배포 단위 구조
인프로세스로 함께 배포되는 구성
app/build.gradle.kts는 다음 범주를 백엔드 jar에 연결합니다.
- 부트스트랩 런타임:
app - 공유 라이브러리:
libs:common,libs:internal-api,libs:messaging - 플랫폼 모듈: Tenant Service, Auth Service, Billing Service, Homepage Service, AI Service, Notification Service, File Service
- 교육 모듈: Student Service, Staff Service, Class Service, Attendance Service, Assignment Service, Lecture Service, Content Service, Exam Service, Tuition Service
modules/activity-log는 리포지토리 트리에는 존재하지만
settings.gradle.kts에 포함되어 있지 않으므로 현재 런타임의 일부가
아닙니다.
주요 런타임 흐름
인증된 애플리케이션 요청
modules/*/adapter/in/web아래의 컨트롤러가/v1/**를 받습니다.JwtAuthenticationFilter가Authorization: Bearer ...또는lumie_access_token쿠키를 받아들입니다.- tenant와 user context가
TenantContextHolder와UserContextHolder에 기록됩니다. - 해당 application service가 트랜 잭션 안에서 실행됩니다.
RlsTenantContextAspect가app.tenant_id를 바인딩하여 PostgreSQL RLS가 tenant 범위 row를 필터링할 수 있게 합니다.- 모듈은 동기적으로 커밋하거나 후속 작업을 위해 post-commit event를 발행합니다.
내부 worker 또는 플랫폼 요청
/internal/**요청은 end-user JWT가 아니라InternalHmacAuthFilter로 인증됩니다.- 필터가
X-Tenant-Slug,X-Timestamp,X-Signature를 검증하고, tenant ID를 해석한 뒤, syntheticROLE_INTERNAL을 부여합니다. - 이후 코드는 일반 요청과 동일한 tenant context와 RLS 적용 하에서 실행됩니다.
커밋 후 비동기 후속 처리
- 모듈이 도메인 write와 같은 데이터베이스 트랜잭션 안에서 Spring Modulith event를 발행합니다.
- 이벤트는
public.event_publication에 저장됩니다. - 커밋 후
@ApplicationModuleListener가 프로세스 내부에서 이벤트를 처리하거나 RabbitMQ로 전달합니다. - listener 또는 broker 전송이 실패하면 publication은 미완료 상태로
남고,
spring.modulith.events.republish-outstanding-events-on-restart=true설정에 따라 재시작 시 다시 시도됩니다.
공통 경계 규칙
- 백엔드는 분리 배포되는 Java 서비스 집합이 아니라 모듈러 모놀리스입니다. 모듈 이름은 한 프로세스 내부의 소유 경계를 나타냅니다.
- 모듈 구조는
domain,application,adapter를 따릅니다. 자세한 내용은 Architecture를 참고하세요. - 동기식 모듈 간 호출은
libs/internal-api인터페이스를 거치며, 대개adapter/in/internal/아래에서 구현됩니다. - 내구성이 필요한 내부 이벤트는 직접
@TransactionalEventListener를 연결하지 않고 Spring Modulith의 JDBC outbox를 거칩니다. - 외부 Python worker는 integration입니다. 이들은 tenant data table을 소유하지 않으며, 트랜잭션 write와 tenant-safe read의 source of truth는 여전히 monolith입니다.
유의해야 할 실패 모드
- context에 tenant ID가 없으면 slug가 있어도 RLS가 적용되는 table은 비어 있는 것처럼 보입니다.
- 런타임 데이터베이스 역할에
SUPERUSER또는BYPASSRLS를 부여하면 tenant 격리가 조용히 무력화 되므로RuntimeDbRoleGuard가 startup을 차단합니다. TenantContextHolder나UserContextHolder를 잃어버린 async 코드는 task-decorator 또는 명시적withinContext(...)복원을 사용하지 않으면 실패하거나 잘못된 범위를 읽게 됩니다.- queue 기반 작업은 모듈이 outbox 경로를 통해 발행할 때만 durable합니다. worker로 직접 보내는 HTTP 호출에는 그 내구성이 없습니다.
검증 명령어
cd /Users/bluemayne/Projects/Lumie/lumie-backend
./gradlew test
./gradlew integrationTest
./gradlew :app:test
./gradlew :libs:common:test
이 개요와 관련해 유용한 integration test:
app/src/test/java/com/lumie/app/config/RoutingDataSourceIntegrationTest.javaapp/src/test/java/com/lumie/app/migration/MigrationsRlsIntegrationTest.javalibs/common/src/test/java/com/lumie/common/tenant/RlsTenantContextAspectIntegrationTest.java