콘텐츠로 이동

Bluetape4k Projects Part 1: 공유 Kotlin/JVM 기반

작은 로봇 작업자들이 Kotlin JVM 백엔드 모듈 블록을 공통 기반 위에 조립하는 일러스트
공통 기반은 잘 드러나지 않습니다. 대신 흔들리면 가장 먼저 티가 납니다.

이 글은 bluetape4k-projects 시리즈의 1편입니다. 전체 시리즈는 Part 1: 공유 기반에서 시작합니다. 이후 Part 2에서는 core, coroutines, logging, tests를 보고, Part 3에서는 I/O, serialization, HTTP, encryption을 다룹니다. Part 4는 data와 infrastructure, Part 5는 utility와 adoption path, Part 6은 Spring Boot 4와 Ktor로 이어집니다.

bluetape4k-projects는 bluetape4k 생태계에서 가장 넓게 깔리는 공통 기반입니다. Spring Boot나 Ktor 애플리케이션을 만들다 보면 매번 다시 꺼내게 되는 Kotlin/JVM 유틸리티, coroutine helper, I/O wrapper, data/infra adapter, testing 기반, BOM을 한곳에 모아 둔 저장소입니다.

그렇다고 모든 기능을 이 저장소에 계속 쌓는 구조는 아닙니다. AWS, Exposed, Image, Text, Leader, JaVers 같은 큰 도메인은 독립 저장소로 분리했습니다. projects는 서비스 코드가 가장 먼저 기대는 공통 기반과 여러 저장소가 같이 쓰는 runtime 구성 요소를 맡습니다. 앞에서 보이는 기능이라기보다 공유 인프라에 가깝습니다. 잘 돌아가면 조용하고, 멈추면 모두가 동시에 쳐다봅니다.

Bluetape4k Projects overview diagram with BOM, foundation, runtime adapters, applications, and split repositories
projects는 BOM과 foundation에서 시작해 runtime adapter와 application support까지 이어지는 공통 층입니다.

Kotlin/JVM 백엔드 개발은 대개 검증된 Java 라이브러리 위에서 출발합니다. 문제는 그 라이브러리를 그대로 가져오면 손에 걸리는 부분이 꽤 많다는 점입니다.

  • Java API는 Kotlin null-safety와 DSL 감각에 맞지 않는 경우가 많습니다.
  • callback, Future, blocking API가 coroutine 코드와 어색하게 만납니다.
  • 테스트에서는 Redis, Kafka, database, 시간, 동시성 fixture를 계속 다시 준비하게 됩니다.
  • 서비스마다 비슷한 codec, serializer, timeout, retry, logging 관례를 다시 만듭니다.

bluetape4k-projects의 역할은 이 반복을 줄이는 것입니다. 새로운 거대한 프레임워크를 만들자는 뜻은 아닙니다. 이미 쓰는 라이브러리를 Kotlin 서비스에서 덜 까끌까끌하게 쓰도록, 자주 걸리는 모서리를 조금 다듬어 두는 얇은 층에 가깝습니다.

implementation(platform("io.github.bluetape4k:bluetape4k-bom:<version>"))
implementation("io.github.bluetape4k:bluetape4k-core")
implementation("io.github.bluetape4k:bluetape4k-coroutines")
implementation("io.github.bluetape4k:bluetape4k-junit5")

루트 README도 같은 방향을 잡고 있습니다. 이 저장소는 idiomatic Kotlin helper, Java library wrapper, coroutine integration, testing infrastructure, Redis/cache/infra 기반 기능을 제공하고, 덩치가 커져 자기 릴리스 리듬이 필요한 도메인 기능은 별도 저장소로 나눕니다.

현재 README.md의 모듈 구조를 보면 이 저장소가 어디까지 맡는지 대략 보입니다.

Bluetape4k Projects module structure diagram grouped by core, I/O, data, infra, cache, Spring Boot, Ktor, utilities, testing, and examples
루트 README의 모듈 구조를 보면 projects가 공통 기반과 runtime adapter를 함께 관리한다는 점이 보입니다.
영역대표 모듈손이 가는 순간
Foundationcore, coroutines, logging, bomKotlin 코드 스타일, 실행 모델, dependency alignment를 맞출 때
I/Oio, okio, csv, protobuf, jackson2/3, http, grpcbyte, file, serializer, HTTP/RPC 접점을 다룰 때
Datajdbc, r2dbc, hibernate, mongodb, cassandrapersistence helper와 coroutine data access가 필요할 때
Infrastructurelettuce, redisson, kafka, nats, opentelemetry, resilience4j운영 시스템과 붙는 adapter가 필요할 때
Applicationspring-boot/*, ktor/*, examples/*서비스 entrypoint와 실행 가능한 예제가 필요할 때
Testingassertions, junit5, testcontainers반복되는 통합 테스트 fixture를 줄일 때

이 표에서 중요한 점은 “한 모듈을 쓰면 생태계 전체가 우르르 따라온다”가 아니라는 점입니다. 필요한 조각만 가져가면 됩니다. BOM은 버전 줄을 맞추고, 각 모듈은 필요한 순간에 따로 집어 들 수 있습니다.

기능이 커지면 projects 안에 계속 두는 것이 오히려 방해가 됩니다. 공구함 하나에 전동드릴, 용접기, 미니 굴착기까지 다 넣을 수는 없습니다. 넣을 수는 있겠지만, 그때부터는 공구함이 아니라 이삿짐입니다. 그래서 bluetape4k는 몇 가지 영역을 독립 저장소로 분리했습니다.

독립 저장소분리한 이유
bluetape4k-awsAWS SDK v2/Kotlin SDK 서비스 범위가 넓고 릴리스 리듬이 따로 움직입니다
bluetape4k-exposedExposed 1.2.x 기반 repository/cache/SQL DSL 영역이 자체 생태계입니다
bluetape4k-imagepure JVM 이미지 처리와 libvips/JNI/FFM 접점은 별도 검증을 요구합니다
bluetape4k-text한국어/일본어 tokenizer와 text-search는 dictionary와 품질 기준이 다릅니다
bluetape4k-leaderleader election API와 backend parity는 독립적으로 발전해야 합니다
bluetape4k-javersaudit/diff persistence는 Redis, Kafka, Exposed와 별도 릴리스 축이 있습니다

대략적인 기준은 이렇습니다. 여러 서비스가 공통으로 쓰는 얇은 Kotlin/JVM 기반이면 projects에 둡니다. 반대로 도메인 자체가 큰 학습 비용과 릴리스 주기를 갖기 시작하면 독립 저장소로 나눕니다. 그래야 공통 기반은 가볍게 움직이고, 큰 도메인은 자기 속도로 발전할 수 있습니다.

공유 기반 저장소에서 BOM은 조용하지만 핵심 역할을 합니다. Kotlin, Spring Boot, Exposed, Jackson, Redis client, testing 도구가 서로 다른 속도로 움직이기 때문입니다. 버전이 각자 춤추기 시작하면 빌드는 금방 댄스 배틀이 됩니다. 보는 재미는 있을 수 있지만, 보통 CI가 먼저 지칩니다.

bluetape4k-bomprojects가 게시하는 모듈들의 버전을 맞춥니다. 애플리케이션은 BOM을 먼저 가져오고 필요한 모듈만 선언합니다.

dependencies {
implementation(platform("io.github.bluetape4k:bluetape4k-bom:<version>"))
implementation("io.github.bluetape4k:bluetape4k-okio")
implementation("io.github.bluetape4k:bluetape4k-r2dbc")
testImplementation("io.github.bluetape4k:bluetape4k-testcontainers")
}

Gradle Kotlin DSL에서는 dependencyManagement { imports }보다 implementation(platform(...)) 방식이 안전합니다. 루트 README도 KGP 2.3.x 충돌을 피하려면 이 방식을 권장합니다. 여기서 중요한 점은 BOM이 “모든 모듈을 쓰라”는 신호가 아니라, “쓰는 모듈의 버전 줄을 맞추라”는 신호라는 점입니다.

처음 보는 저장소라면 아래 순서가 가장 덜 피곤합니다. 처음부터 모든 모듈을 펼치면 README가 아니라 이삿짐 목록을 읽는 기분이 납니다. 먼저 전체 지도를 보고, 지금 서비스가 실제로 부딪힌 문제부터 따라가는 편이 낫습니다.

  1. README.md에서 전체 모듈 지도를 봅니다.
  2. core, coroutines, logging, junit5, testcontainers를 먼저 훑습니다.
  3. 지금 서비스가 만나는 접점에 따라 I/O, data, infra 모듈을 고릅니다.
  4. Spring Boot 또는 Ktor 예제로 실제 application wiring을 확인합니다.
  5. cache, virtual threads, CSV/Okio 같은 주제별 블로그를 필요한 만큼 따라갑니다.
Bluetape4k Projects module composition chart
모듈 수가 많아 보일수록, 먼저 전체 비율을 보고 지금 필요한 칸만 집는 편이 낫습니다.

bluetape4k-projects를 한 문장으로 줄이면 “Kotlin/JVM 백엔드에서 매번 다시 만들기 싫은 공통 기반”입니다. 화려한 도메인 기능은 독립 저장소로 나누고, 이 저장소는 core utility, coroutine, I/O, data, infra, application support, testing, BOM을 안정적으로 맞춥니다.

다음 글에서는 이 기반의 가장 안쪽인 core, coroutines, logging, testing 모듈을 꺼내 보겠습니다. 서비스가 커지기 전에 이런 작은 기반이 먼저 정리되어 있어야, 나중에 장애 회고가 기술 문서로 끝납니다. 정리하지 않으면 장르가 바뀝니다. 보통은 추리물입니다.

댓글

GitHub 계정으로 의견을 남기거나 reaction을 남길 수 있습니다.