Skip to content

필사 모드: 모던 Kotlin 2026 — Kotlin 2.1 / K2 컴파일러 / Compose Multiplatform / KMP 2.1 / Ktor 3 / Spring Boot Kotlin 심층 가이드

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

프롤로그 — 2026년의 Kotlin은 더 이상 "안드로이드 언어"가 아니다

2017년, Google I/O에서 Android의 공식 언어가 된 그날부터 Kotlin은 한참 동안 "안드로이드의 Java 대체재"로 살았다. 서버 진영에서는 Java 17, 21을 쓰면 됐고, 멀티플랫폼은 알파에서 베타로 천천히 움직였다.

2026년 5월의 풍경은 그때와 전혀 다르다.

- **Kotlin 2.1** (2024년 11월)이 **K2 컴파일러를 기본**으로 올렸다. IDE도 K2 모드가 기본이고, 컴파일 속도는 K1 대비 평균 1.8배다.

- **Kotlin 2.2** (2025년 3월)이 **context parameters** — 옛 context receivers의 후속 — 를 정식 도입했다. 결과 타입을 지저분하게 만들지 않고도 의존성을 함수 시그니처에 묶을 수 있다.

- **Compose Multiplatform 1.7** (2024년 10월)이 **iOS를 stable**로 선언했다. 안드로이드·iOS·데스크톱·웹(Wasm) 한 코드베이스로 네 플랫폼을 그린다.

- **KMP(Kotlin Multiplatform) 2.1**이 production-ready를 넘어 메르카리·맥도날드·BAYK 같은 회사들이 코어 SDK를 KMP로 갈아엎는다.

- **Ktor 3** (2024년 10월)이 **WebAssembly 타겟**을 추가했고, kotlinx-io 기반으로 처음부터 다시 짜여 throughput이 평균 90% 올라갔다.

- **Spring Boot 3.5**가 Kotlin을 1급으로 대접한다. coroutine support는 mature하고, Kotlin DSL 빌드가 표준이다.

이 글은 "Kotlin을 막 보기 시작했다"부터 "2026년 production에서 어떻게 굴러가는지 다 봐야 한다"까지를 한 호흡으로 정리한다. 언어·컴파일러·UI·서버·라이브러리 진영·국내외 케이스를 빠짐없이 짚는다.

1장 · 2026년 모던 Kotlin — Kotlin 2.x 시대의 자리

먼저 좌표를 잡자. 2026년 5월 기준 Kotlin의 풀스택은 다음과 같다.

+----------------------------------------+

| Kotlin 2.2.x |

| (K2 compiler default, JVM/JS/Wasm) |

+----------------------------------------+

|

+-------------------+--------------------+

| | |

v v v

[JVM 백엔드] [멀티플랫폼] [Native / Wasm]

Spring Boot 3.5 KMP 2.1 + CMP 1.7 Kotlin/Native

Ktor 3 iOS/Android/Desktop Kotlin/Wasm

gRPC-Kotlin Web (Wasm) (Ktor 3 server)

| | |

+-------------------+--------------------+

|

v

+-------------------------+

| kotlinx 라이브러리 진영 |

| coroutines / Flow |

| serialization / io |

| datetime / atomicfu |

+-------------------------+

|

v

+---------------+----------------+

| | |

v v v

[DI/FP/ORM] [정적분석/테스트] [빌드]

Koin / Arrow Detekt / Konsist Gradle Kotlin DSL

Exposed Kotest / MockK Maven Kotlin plugin

핵심 정리:

- 언어 자체는 2년에 한 번 major(1.x → 2.x), 6개월에 한 번 minor.

- 멀티플랫폼은 더 이상 "실험"이 아니다. iOS stable이 게임 체인저.

- 서버 진영은 Spring Boot와 Ktor 둘 다 살아 있다.

- 라이브러리 진영(Koin/Arrow/Exposed/Kotest/MockK)이 두툼해서 "Kotlin다운" 스타일로 풀스택을 짤 수 있다.

이 좌표 위에서, 각 구역을 하나씩 깊게 본다.

2장 · Kotlin 2.1 (2024.11) — K2 default + multi-dollar 문자열

Kotlin 2.0이 K2 컴파일러를 stable로 올린 첫 릴리스였다면, 2.1은 그걸 **모두에게 강제**한 릴리스다. JetBrains는 IntelliJ IDEA의 K2 모드도 동시에 default로 만들었고, K1 fallback은 2025년 말까지만 유지된다.

2.1의 헤드라인 기능:

1. **K2 컴파일러 default** — `-language-version 2.0` 이상이 기본.

2. **다중 달러($$, $$$) 문자열 보간** — 템플릿이나 LaTeX 같은 거 다룰 때 `$` 이스케이프 지옥에서 벗어난다.

3. **`when` 표현식의 guard condition** — `when(x) { is Foo if x.bar > 10 -> ... }` 형태로 매칭 분기에서 추가 조건 검사를 한 줄로 끝낸다.

4. **`.kotlin` 캐시 디렉터리** — 빌드 산출물을 프로젝트 루트에 모아 관리. Gradle의 `.gradle`처럼.

5. **non-local `break`/`continue`** — inline 람다 안에서도 바깥 루프를 깰 수 있다. (preview)

다중 달러 문자열은 진짜 편하다.

// Kotlin 2.1: $$ 안에서는 단일 $는 리터럴, $$는 보간 시작

val template = $$"""

Hello, $$name!

Your balance is $1000 (USD).

JSON: {"value": "$$value"}

"""

기존엔 `"\$1000"`처럼 매번 이스케이프하거나, raw string 안에 또 다른 문자열 더해야 했다. 이제 한 줄로 끝난다.

`when` guard도 자주 쓴다.

sealed interface Order

data class Pending(val amount: Int) : Order

data class Paid(val amount: Int, val txId: String) : Order

fun handle(order: Order) = when (order) {

is Pending if order.amount > 10_000 -> "high-value pending"

is Pending -> "pending"

is Paid -> "paid: ${order.txId}"

}

K1 시절엔 `is Pending` 분기 안에서 `if`를 한 번 더 써야 했고, 그러면 컴파일러가 exhaustive 체크를 못 했다. 이제는 한 라인 안에서 끝난다.

3장 · Kotlin 2.2 (2025.3) — context parameters

Kotlin 2.2의 헤드라인은 **context parameters** — 1.7부터 베타로 굴러다니던 "context receivers"의 정식 후속.

무엇을 푸는가

함수에 **암묵적으로** 의존성을 주입하고 싶을 때가 있다. 예를 들어:

// 기존: 매번 명시적으로 logger를 받아야

fun calculate(x: Int, y: Int, logger: Logger): Int {

logger.info("calc $x + $y")

return x + y

}

// 또는 클래스로 묶기

class Calculator(private val logger: Logger) {

fun calculate(x: Int, y: Int): Int { ... }

}

context parameters는 **함수 시그니처에 의존성을 선언하되, 호출 측에서는 명시적으로 전달하지 않아도 되는** 중간 길.

// Kotlin 2.2 context parameters

context(logger: Logger)

fun calculate(x: Int, y: Int): Int {

logger.info("calc $x + $y")

return x + y

}

// 호출 측: Logger를 scope에 띄워두면 자동 주입

fun main() {

with(MyLogger()) {

val result = calculate(1, 2) // logger 명시 X

}

}

context receivers와 무엇이 다른가

옛 1.x의 `context(Logger)`는 **이름이 없었다**. 두 개의 같은 타입이 들어오면 충돌. 2.2의 `context(logger: Logger)`는 **이름이 있다**. Arrow 같은 effect 라이브러리, Compose의 CompositionLocal, structured logging 모두 이걸로 다시 짜진다.

실전 패턴: Arrow의 raise

context(raise: Raise<String>)

fun parsePositive(s: String): Int {

val n = s.toIntOrNull() ?: raise.raise("not a number: $s")

if (n <= 0) raise.raise("must be positive: $n")

return n

}

// 호출 측

fun main() {

val result = either {

val n = parsePositive("42")

n * 2

}

// result: Either.Right(84)

}

`Either<E, A>` 같은 결과 타입을 노출하지 않고도 에러 채널을 합성할 수 있다. Scala의 ZIO나 Haskell의 monad transformer 같은 효과 시스템을 Kotlin스럽게 푼 답이다.

4장 · K2 컴파일러 — 무엇이 빨라지고 무엇이 바뀌나

K2는 컴파일러 프론트엔드 **전체 재작성**의 결과물이다. K1은 2010년대 초반 설계로, 형변환·smart cast·generic inference의 코드가 여러 단계에 산재해 있었다. K2는 **FIR(Frontend IR)** 이라는 단일 IR을 거치도록 통일했다.

체감 효과:

| 지표 | K1 | K2 | 비고 |

|---|---|---|---|

| 클린 빌드 | 1.0x | 1.6 ~ 1.9x 빠름 | 큰 프로젝트일수록 격차 |

| 인크리멘털 빌드 | 1.0x | 2.0 ~ 2.4x 빠름 | 캐시·invalidation 개선 |

| IDE highlight | 1.0x | 1.5x 빠름 | smart cast/추론 캐싱 |

| 메모리 (peak) | 1.0x | ~0.7x | FIR 통합으로 중복 제거 |

수치는 JetBrains 공식 발표 + 메르카리·Square의 마이그레이션 후기 평균치.

Smart cast가 더 똑똑해졌다

K1은 분기 안에서 일관된 type narrowing을 어려워했다.

fun process(value: Any?) {

if (value is String || value is Int) {

// K1: value는 여전히 Any?

// K2: value는 String | Int (intersection)

println(value.hashCode()) // K2는 정상 호출

}

}

또 K2는 **`val`의 재초기화** 패턴도 인식한다.

class Holder {

val data: List<Int>

init {

val tmp = mutableListOf<Int>()

tmp.add(1); tmp.add(2)

data = tmp.toList() // K2: 정상

}

}

Compiler plugin API 안정화

Compose compiler plugin, kotlinx-serialization, Arrow의 raise plugin 등이 모두 K2 FIR 기반으로 새로 짜졌다. 그 결과 빌드 시간이 줄었고, IDE 인덱싱도 안정화됐다.

마이그레이션 체크리스트

- `kotlin.languageVersion = "2.1"` 으로 올리기

- `kotlin.compiler.execution.strategy = in-process` (Gradle daemon)

- Compose 프로젝트는 `compose-compiler-gradle-plugin` (2024년부터 JetBrains가 직접 배포) 사용

- `-Xuse-fir-lt` 같은 옛 플래그 제거

5장 · Compose Multiplatform — iOS stable!

2024년 10월의 Compose Multiplatform 1.7은 진짜 큰 사건이었다. **iOS 타겟이 stable**로 올라간 것. 그동안 Compose-Android만 production이었고 iOS는 베타였다. 1.7부터 production OK.

1.7 헤드라인 (2024.10) + 1.7+ 누적

- **iOS stable** — UIViewController 통합, 텍스트 입력, 접근성, 다크 모드 다 stable.

- **Resource API 1.0** — drawable/string/font를 `Res.drawable.icon` 같은 type-safe 프록시로 접근.

- **adaptive layouts** — `WindowSizeClass` API. 폰/태블릿/폴더블 분기.

- **Skia 0.8 → 0.10** — 텍스트 렌더 품질, GPU 메모리 개선.

- **Hot reload (desktop)** — Java 21의 enhanced class redefinition 활용. 디자인 루프가 진짜 빠르다.

한 화면, 네 플랫폼

// commonMain/HelloApp.kt

@Composable

fun HelloApp(name: String) {

MaterialTheme {

Column(modifier = Modifier.padding(16.dp)) {

Text("Hello, $name!", style = MaterialTheme.typography.headlineSmall)

Spacer(Modifier.height(8.dp))

Button(onClick = { /* ... */ }) {

Text("Tap me")

}

}

}

}

이게 그대로 Android, iOS, macOS/Windows/Linux desktop, 그리고 (실험) Web Wasm에서 돈다. 플랫폼별 분기가 필요한 부분만 `expect`/`actual` 로 뺀다.

// commonMain

expect fun platformName(): String

// androidMain

actual fun platformName(): String = "Android ${Build.VERSION.RELEASE}"

// iosMain

actual fun platformName(): String =

UIDevice.currentDevice.systemName + " " + UIDevice.currentDevice.systemVersion

Compose vs SwiftUI 비교 (2026)

| 항목 | Compose Multiplatform 1.7+ | SwiftUI |

|---|---|---|

| 플랫폼 | Android / iOS / Desktop / Web | Apple만 |

| 언어 | Kotlin | Swift |

| 코드 공유 | 100% (UI까지) | 0% (다른 플랫폼) |

| iOS 성능 | 95% (스크롤·애니메이션 등 일부 gap 잔존) | 100% native |

| 네이티브 위젯 접근 | `UIViewControllerRepresentable` 같은 interop | 자연스러움 |

| 빌드 시간 | KMP 빌드 오버헤드 있음 | Xcode 표준 |

추천 기준:

- iOS만 한다 → SwiftUI

- Android + iOS 둘 다 한다, 디자인 일관성 중요 → CMP

- "Apple의 모든 새 API를 즉시 쓰겠다" → SwiftUI

6장 · KMP 2.1 — production-ready

Compose Multiplatform이 UI라면, **KMP(Kotlin Multiplatform)** 는 **로직 공유**다. 비즈니스 로직, 네트워킹, 디스크 캐시, 도메인 모델을 Kotlin으로 한 번 짜고 Android·iOS·서버·웹에서 다 쓴다.

KMP 모듈 구조

shared/

build.gradle.kts

src/

commonMain/kotlin/ <- 모든 플랫폼 공통 코드

androidMain/kotlin/ <- Android-only

iosMain/kotlin/ <- iOS-only (Kotlin/Native)

jvmMain/kotlin/ <- JVM 서버용

wasmJsMain/kotlin/ <- Wasm 웹용

commonTest/kotlin/ <- 공통 테스트

핵심 라이브러리

- **kotlinx.coroutines** — 모든 플랫폼에서 같은 API.

- **kotlinx.serialization** — JSON/Protobuf/CBOR. multiplatform 표준.

- **Ktor Client** — HTTP/WebSocket multiplatform.

- **SQLDelight 2** — type-safe SQL driver. iOS/Android/JVM/Wasm.

- **Multiplatform Settings** — UserDefaults / SharedPreferences / file 등 추상화.

- **Decompose / Voyager** — multiplatform 내비게이션.

- **Koin** — DI. multiplatform.

iOS 통합 — KMP 결과물을 Xcode에서

// shared/build.gradle.kts

kotlin {

iosX64()

iosArm64()

iosSimulatorArm64()

cocoapods {

version = "1.0"

summary = "Shared module for MyApp"

homepage = "https://example.com"

ios.deploymentTarget = "15.0"

framework {

baseName = "Shared"

isStatic = false

}

}

}

./gradlew :shared:podPublishXcFramework

결과물을 Xcode 프로젝트의 Podfile에 추가

Swift 코드에서는 그냥 import.

let repo = UserRepository()

Task {

let users = try await repo.fetchUsers()

print(users)

}

`suspend fun`은 Swift에서 `async throws`로 자동 매핑된다 (2.1 + Kotlin/Native의 swift-export 개선 덕분).

메르카리, 맥도날드, BAYK 사례

- **메르카리** — 검색·결제 핵심 로직을 KMP로 마이그레이션. Android/iOS 양쪽 버그가 한 곳에서 잡힘.

- **맥도날드** — Global Mobile App을 KMP 기반으로 재구축.

- **BAYK (Yandex Eats 같은 한국 케이스 없음, 대신 인용)** — 음... 한국·일본 사례는 13장에서.

7장 · Ktor 3 (2024.10) — WebAssembly 타겟

Ktor 3은 2024년 10월에 release됐고, "v2의 마이너 업"이 아니라 **거의 다시 짠** 메이저다.

헤드라인

1. **kotlinx-io 기반 재작성** — okio/java.nio 의존 제거. throughput 평균 +90%.

2. **WebAssembly 타겟** — Kotlin/Wasm으로 Ktor 서버 빌드 가능. 엣지 컴퓨팅 시나리오.

3. **CIO 엔진 HTTP/2 stable** — 외부 의존 없이 HTTP/2 서버를 띄울 수 있다.

4. **Server-Sent Events plugin** — `install(SSE)`.

5. **WebSocket extensions** — Compression, ping/pong rework.

최소 Ktor 3 서버

// build.gradle.kts

plugins {

kotlin("jvm") version "2.1.0"

application

}

dependencies {

implementation("io.ktor:ktor-server-core:3.0.0")

implementation("io.ktor:ktor-server-cio:3.0.0")

implementation("io.ktor:ktor-server-content-negotiation:3.0.0")

implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.0")

}

// src/main/kotlin/Application.kt

@Serializable

data class User(val id: Long, val name: String)

fun main() {

embeddedServer(CIO, port = 8080) {

install(ContentNegotiation) { json() }

routing {

get("/users") {

call.respond(listOf(User(1, "Alice"), User(2, "Bob")))

}

}

}.start(wait = true)

}

`./gradlew run` 하면 8080에서 JSON 응답. coroutine 기반이라 thread per request가 아니라 한 thread가 수천 connection을 다룬다.

Spring Boot vs Ktor — 어디서 뭘 쓰나

| 항목 | Ktor 3 | Spring Boot 3.5 + Kotlin |

|---|---|---|

| 철학 | minimal, opt-in plugins | "convention over config" |

| 동시성 | coroutine native | WebFlux(reactive) 또는 virtual thread |

| 생태계 | 가벼움 | 거대, 모든 게 있음 |

| 콜드 스타트 | 빠름 | GraalVM native image 필요시 빠름 |

| 학습 곡선 | 짧음 | 길지만 자료 많음 |

| 적합 | 마이크로서비스, 엣지, MVP | 대형 모놀리스, 엔터프라이즈 |

8장 · Spring Boot + Kotlin — JVM 서버

JVM 서버에서 Kotlin을 쓴다고 했을 때, 회사들의 70%는 여전히 Spring Boot다. 그리고 Spring팀은 그 트래픽을 무시하지 않았다. Spring Framework 6, Boot 3 시리즈는 Kotlin support를 "1급"으로 격상했다.

모던 Spring Boot + Kotlin 스택 (2026)

- **Spring Boot 3.5** (Java 21 baseline, Kotlin 2.1+)

- **Spring WebFlux + coroutines** — `suspend` 함수를 controller에서 직접 받는다.

- **R2DBC + Kotlin Coroutines** — async DB.

- **Spring Security 6** — Kotlin DSL.

- **Spring AOT + GraalVM native image** — 콜드 스타트 < 100ms.

Coroutine controller

@RestController

class UserController(private val repo: UserRepository) {

@GetMapping("/users/{id}")

suspend fun getUser(@PathVariable id: Long): UserDto =

repo.findById(id)?.toDto() ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)

@GetMapping("/users", produces = [MediaType.APPLICATION_NDJSON_VALUE])

fun streamUsers(): Flow<UserDto> =

repo.findAll().map { it.toDto() }

}

`suspend fun`을 그대로 받는다. WebFlux의 `Mono`/`Flux`를 변환할 필요 없다. `Flow<UserDto>` 는 NDJSON 스트리밍 응답으로 자동 변환.

Coroutine repository (R2DBC)

interface UserRepository : CoroutineCrudRepository<UserEntity, Long> {

suspend fun findByEmail(email: String): UserEntity?

@Query("SELECT * FROM users WHERE created_at > :since")

fun findRecentUsers(since: Instant): Flow<UserEntity>

}

Kotlin DSL configuration

@Configuration

class AppConfig {

@Bean

fun routes(handler: UserHandler) = coRouter {

accept(MediaType.APPLICATION_JSON).nest {

GET("/users", handler::list)

POST("/users", handler::create)

}

}

@Bean

fun securityFilter(http: ServerHttpSecurity): SecurityWebFilterChain =

http {

authorizeExchange {

authorize("/public/**", permitAll)

authorize(anyExchange, authenticated)

}

oauth2ResourceServer { jwt {} }

}

}

`http { ... }` 가 Kotlin DSL. XML/Java config의 boilerplate가 사라진다.

9장 · kotlinx.coroutines + Flow — async 모델

Kotlin의 async 모델은 두 축이다. **coroutines** (한 번에 끝나는 비동기 작업)과 **Flow** (시간에 걸쳐 흐르는 값 스트림).

Coroutines 기본

suspend fun fetchUser(id: Long): User { /* HTTP 호출 */ }

suspend fun fetchOrders(userId: Long): List<Order> { /* HTTP 호출 */ }

suspend fun loadProfile(id: Long): Profile = coroutineScope {

val userDeferred = async { fetchUser(id) }

val ordersDeferred = async { fetchOrders(id) }

Profile(userDeferred.await(), ordersDeferred.await())

}

`coroutineScope` 안에서 두 작업이 병렬로 돌고, 둘 중 하나가 실패하면 다른 것도 cancel — **structured concurrency**.

Flow — cold stream

fun userUpdates(userId: Long): Flow<UserEvent> = flow {

while (currentCoroutineContext().isActive) {

val event = pollEvent(userId)

if (event != null) emit(event)

delay(1000)

}

}

// 소비

suspend fun watchUser() {

userUpdates(42L)

.filter { it.type == EventType.UPDATED }

.map { it.toDto() }

.take(10)

.collect { println(it) }

}

Flow는 **cold** — `.collect`를 호출할 때마다 처음부터 다시 실행. **hot stream**이 필요하면 `SharedFlow`/`StateFlow`.

StateFlow — UI state holder

class UserViewModel(private val repo: UserRepository) {

private val _state = MutableStateFlow<UserState>(UserState.Loading)

val state: StateFlow<UserState> = _state.asStateFlow()

fun load(id: Long) {

viewModelScope.launch {

_state.value = UserState.Loading

_state.value = runCatching { repo.findById(id) }

.fold(

onSuccess = { UserState.Loaded(it) },

onFailure = { UserState.Error(it.message ?: "") }

)

}

}

}

Compose에서 `val state by viewModel.state.collectAsState()` 로 받아 그대로 그린다.

Coroutine debugger

IntelliJ에서 `Debug` 메뉴 → `Coroutines Dump`. 어느 coroutine이 어디서 suspended인지, 트리 구조로 보인다. Java 스레드 덤프와 비교 불가능하게 편하다.

10장 · Detekt / Konsist — 정적 분석 + 아키텍처 테스트

Kotlin 코드 품질을 자동으로 지키는 두 도구.

Detekt — 코드 스멜 정적 분석

// build.gradle.kts

plugins {

id("io.gitlab.arturbosch.detekt") version "1.23.7"

}

detekt {

toolVersion = "1.23.7"

config.setFrom("$rootDir/detekt.yml")

buildUponDefaultConfig = true

}

`detekt.yml` 발췌:

complexity:

CyclomaticComplexMethod:

threshold: 15

LongMethod:

threshold: 60

naming:

FunctionNaming:

functionPattern: '[a-z][a-zA-Z0-9]*'

style:

MagicNumber:

ignoreNumbers: ['-1', '0', '1', '2']

CI 통합:

./gradlew detekt

결과: build/reports/detekt/detekt.html

K2 호환이 1.23부터 stable, 2025년 중반 1.24부터는 K2 전용 분석 룰이 추가됐다 (smart-cast hint, sealed exhaustive 등).

Konsist — 아키텍처 테스트

ArchUnit의 Kotlin 버전. **레이어 의존, 네이밍 규칙, 어노테이션 사용**을 단위 테스트처럼 검증한다.

// src/test/kotlin/ArchitectureTest.kt

class ArchitectureTest {

@Test

fun `repositories should not depend on controllers`() {

Konsist.scopeFromProject()

.classes()

.withNameEndingWith("Repository")

.assertTrue { repo ->

repo.containingFile.imports

.none { it.name.contains("controller") }

}

}

@Test

fun `all use cases end with UseCase`() {

Konsist.scopeFromPackage("..usecase..")

.classes()

.assertTrue { it.name.endsWith("UseCase") }

}

}

`./gradlew test` 안에서 자동 실행. 새 개발자가 "controller에서 repository를 직접 호출"하는 PR을 올리면 CI에서 빨갛게 뜬다.

Detekt가 "한 파일 안의 코드 품질"이라면 Konsist는 "프로젝트 전체의 구조"를 본다. 둘은 보완재.

11장 · Koin / Arrow / Exposed / Kotest / MockK — 라이브러리 진영

Java의 Spring/Hibernate/Mockito에 대응하는 "Kotlin-native" 라이브러리들.

Koin — DSL 기반 DI

val appModule = module {

single<UserRepository> { UserRepositoryImpl(get()) }

single<HttpClient> {

HttpClient(CIO) {

install(ContentNegotiation) { json() }

}

}

viewModel { UserViewModel(get()) }

}

fun main() {

startKoin {

modules(appModule)

}

// ...

}

리플렉션 없음. compile-time generation 없음 (Dagger와 달리). KMP 호환. Compose Multiplatform에서 그대로 쓰임.

Arrow — Kotlin의 FP 표준

sealed interface UserError {

object NotFound : UserError

data class Invalid(val reason: String) : UserError

}

fun parseAge(s: String): Either<UserError, Int> = either {

val n = s.toIntOrNull() ?: raise(UserError.Invalid("not a number: $s"))

if (n < 0 || n > 150) raise(UserError.Invalid("age out of range: $n"))

n

}

fun loadUser(id: Long): Either<UserError, User> = either {

val user = repo.find(id) ?: raise(UserError.NotFound)

user

}

fun process(id: Long, ageStr: String): Either<UserError, Result> = either {

val user = loadUser(id).bind()

val age = parseAge(ageStr).bind()

Result(user, age)

}

**`Either<L, R>`** + **`raise` DSL** 조합으로 throw 없이 에러를 합성한다. 2.2의 context parameters와 합치면 효과 시스템 완성.

Exposed — type-safe SQL DSL

object Users : LongIdTable("users") {

val name = varchar("name", 100)

val email = varchar("email", 200).uniqueIndex()

val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)

}

transaction {

val newId = Users.insertAndGetId {

it[name] = "Alice"

it[email] = "alice@example.com"

}

val users = Users

.selectAll()

.where { Users.name like "A%" }

.orderBy(Users.createdAt to SortOrder.DESC)

.limit(10)

.map { it[Users.name] to it[Users.email] }

}

JPA보다 가볍고, Hibernate의 lazy-loading 함정이 없다. R2DBC 어댑터로 coroutine 환경에서도 굴러간다 (1.0부터 stable).

Kotest — BDD-style 테스트

class UserSpec : StringSpec({

"user with valid email should be created" {

val u = User("alice@example.com")

u.isValid shouldBe true

}

"negative age should throw" {

shouldThrow<IllegalArgumentException> {

User("a@b", age = -1)

}

}

})

class PropertySpec : StringSpec({

"reverse twice is identity" {

checkAll<List<Int>> { list ->

list.reversed().reversed() shouldBe list

}

}

})

JUnit 호환, 표현식 기반 assertion, property-based testing(QuickCheck 식) 내장. multiplatform.

MockK — pure Kotlin mocking

class UserServiceTest : StringSpec({

val repo = mockk<UserRepository>()

val service = UserService(repo)

"should return user from repo" {

coEvery { repo.findById(1L) } returns User(1, "Alice")

val u = service.getUser(1L)

u.name shouldBe "Alice"

coVerify(exactly = 1) { repo.findById(1L) }

}

})

Mockito가 못 다루는 `object`, `companion object`, top-level function, suspend function을 다 mock한다.

12장 · Gradle Kotlin DSL — 빌드 표준

2026년 기준 **새 Kotlin 프로젝트의 99%는 Gradle Kotlin DSL** (`build.gradle.kts`)이다. Groovy DSL은 레거시.

최소 `build.gradle.kts`

plugins {

kotlin("jvm") version "2.1.0"

kotlin("plugin.serialization") version "2.1.0"

application

}

group = "com.example"

version = "0.1.0"

repositories {

mavenCentral()

}

dependencies {

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")

implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")

testImplementation(kotlin("test"))

testImplementation("io.kotest:kotest-runner-junit5:5.9.1")

}

application {

mainClass.set("com.example.AppKt")

}

kotlin {

jvmToolchain(21)

}

tasks.test {

useJUnitPlatform()

}

Multi-module + version catalog

`gradle/libs.versions.toml`:

[versions]

kotlin = "2.1.0"

coroutines = "1.9.0"

ktor = "3.0.0"

[libraries]

kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }

ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }

ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" }

[plugins]

kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }

ktor = { id = "io.ktor.plugin", version.ref = "ktor" }

각 모듈의 `build.gradle.kts`:

plugins {

alias(libs.plugins.kotlin.jvm)

alias(libs.plugins.ktor)

}

dependencies {

implementation(libs.ktor.server.core)

implementation(libs.ktor.server.cio)

implementation(libs.kotlinx.coroutines)

}

버전을 한 군데서 관리. 30개 모듈짜리 프로젝트에서 진가가 나온다.

configuration cache + build cache

gradle.properties

org.gradle.configuration-cache=true

org.gradle.caching=true

org.gradle.parallel=true

kotlin.incremental=true

kotlin.code.style=official

설정 캐시는 8.x부터 stable. 빌드 시간이 명시적으로 2~5배 빠라진다 (특히 인크리멘털).

Maven은 죽었나?

아니. **Spring Boot 엔터프라이즈 + 기존 Maven 자산이 큰 조직**은 여전히 Maven Kotlin plugin을 쓴다.

기능적으론 동등. Gradle보다 느리지만 안정적.

13장 · 한국 / 일본 — 카카오, 토스, 메르카리, ZOZO, CyberAgent

한국

**카카오 (안드로이드 전사 Kotlin)**

- 카카오톡 안드로이드 코드베이스의 95%+ Kotlin. Java 신규 코드 금지.

- 모듈 100개+ 멀티 모듈, Gradle Kotlin DSL + version catalog.

- 사내 publish: detekt + 카카오 자체 룰 (`KakaoCodeStyle`).

- Compose 마이그레이션 진행 중 (2026년 기준 신규 화면은 100% Compose).

**토스 (Server-side Kotlin 선구자)**

- 백엔드 새 마이크로서비스는 100% Kotlin + Spring Boot. 200+ 서비스가 Kotlin.

- Coroutine + WebFlux + R2DBC + Arrow 조합이 표준.

- 사내 Kotlin DSL 라이브러리 (`tossopen-kotlin-commons`) 다수.

- 토스 기술 블로그에 "Kotlin for Spring 마이그레이션", "왜 Java 대신 Kotlin인가" 시리즈 다수.

**라인 (네이버) — Kotlin Multiplatform 케이스**

- LINE 메신저의 일부 클라이언트 SDK를 KMP로 공유 시도. 2026년 기준 production 부분 도입.

일본

**메르카리 (Mercari) — KMP 선구자**

- 검색·결제 코어 SDK를 KMP로 재구축. Android/iOS 한 곳에서 버그 수정.

- 2025년 발표: "We migrated 60% of our shared business logic to KMP."

- Compose Multiplatform 1.7+ 평가 단계.

**ZOZOTOWN (ZOZO)**

- 안드로이드 풀 Kotlin. 서버 일부 Kotlin (Spring Boot + Coroutines).

- 사내 "Kotlin Guild" — 격주 스터디, 라이브러리 공유.

**CyberAgent (AbemaTV, FRIDAY 등)**

- 안드로이드 신규 프로젝트 100% Kotlin.

- AbemaTV 안드로이드 — Compose + Coroutines + Koin + Coil.

- 일부 마이크로서비스 Ktor 3 도입 (스트리밍 메타데이터 서비스).

**DeNA / GREE / 라쿠텐**

- 비슷한 패턴: 안드로이드 풀 Kotlin, 서버 일부 Kotlin.

공통 패턴

한일 대기업 모두:

1. 안드로이드는 **100% Kotlin** 이미 끝남.

2. 서버는 **신규 마이크로서비스부터** Kotlin 전환.

3. KMP는 **선택적·코어 SDK 중심** 적용 (앱 전체 KMP는 아직 드묾).

4. 사내 코딩 가이드는 **Detekt 베이스 + 자체 룰**.

5. 채용 시장: Kotlin 가능자 = Android + Server 양쪽 매력.

14장 · 누가 Kotlin을 골라야 하나 — 안드로이드 / KMP / JVM 서버 / FP

마지막으로 의사결정 매트릭스. "Kotlin이 정답인가"에 대한 시나리오별 답.

"안드로이드 앱을 짤 거다"

→ **Kotlin이 default. 다른 선택지가 사실상 없다.**

- Android Studio가 Kotlin 우선.

- Jetpack Compose의 정식 언어.

- Google의 모든 새 라이브러리/문서 Kotlin 우선.

"iOS + Android 둘 다 한다, 한 팀이"

→ **KMP + Compose Multiplatform 고려.**

조건:

- iOS의 모든 새 API를 즉시 쓸 필요가 없다 (SwiftUI/AppKit 신기능 follow-up이 1~2개월 늦어도 OK).

- 비즈니스 로직이 양쪽에서 동일하다.

- iOS 디자이너가 "이것은 정확히 iOS-native여야 한다"고 고집하지 않는다.

위 셋 다 OK면 KMP. 하나라도 No면 네이티브 두 벌.

"Spring Boot 환경에서 JVM 서버를 짠다"

→ **Kotlin이 Java보다 거의 모든 면에서 낫다.**

- Null safety = NPE를 컴파일에서 잡는다.

- Coroutines = `CompletableFuture` 보다 훨씬 읽기 쉬움.

- Data class = Lombok 불필요.

- Spring 6/Boot 3.5 = Kotlin 1급 지원.

예외: 팀의 80%가 Java만 안다 → 점진적 도입 (테스트 코드부터, 새 마이크로서비스부터).

"마이크로서비스를 가볍게 짠다, GraalVM native image"

→ **Ktor 3 + Kotlin/Native 또는 Ktor 3 + GraalVM.**

- 콜드 스타트 < 100ms.

- 메모리 < 50MB.

- 람다/엣지 함수에 어울림.

"함수형 프로그래밍을 진지하게"

→ **Kotlin + Arrow. 단, 학습 곡선이 가파르다.**

- Either / IO / context parameters / raise DSL 조합.

- Scala/Haskell 만큼 깊지 않지만, JVM 진영에서 "산업 친화적 FP"의 sweet spot.

"데이터 엔지니어링, Spark/Flink"

→ **Scala가 여전히 우세. Kotlin은 보조.**

- Spark Kotlin API는 있지만 Scala만큼 mature하지 않다.

- 단, KEDB(Kotlin Embedded DB) 같은 신생 도구는 Kotlin 우선.

"안드로이드도 안 하고 JVM도 안 한다"

→ Kotlin은 사실 안 와도 된다. JS/Wasm 타겟이 있긴 하지만 TypeScript/Rust가 그 영역의 default.

참고 / References

Kotlin 언어·컴파일러 공식

- Kotlin Blog — https://blog.jetbrains.com/kotlin/

- Kotlin 2.1.0 released — https://blog.jetbrains.com/kotlin/2024/11/kotlin-2-1-0-released/

- Kotlin 2.2.0 released — https://blog.jetbrains.com/kotlin/2025/03/kotlin-2-2-released/

- Kotlin 공식 문서 — https://kotlinlang.org/docs/home.html

- K2 compiler announcement — https://blog.jetbrains.com/kotlin/2024/04/k2-compiler-performance-benchmarks-and-how-to-measure-them-on-your-projects/

- KEEP (Kotlin Evolution and Enhancement Process) — https://github.com/Kotlin/KEEP

- Context parameters KEEP — https://github.com/Kotlin/KEEP/blob/master/proposals/context-parameters.md

Compose Multiplatform / KMP

- Compose Multiplatform — https://www.jetbrains.com/lp/compose-multiplatform/

- Compose Multiplatform 1.7 release (iOS stable) — https://blog.jetbrains.com/kotlin/2024/10/compose-multiplatform-1-7-0-release/

- Kotlin Multiplatform 공식 — https://kotlinlang.org/docs/multiplatform.html

- KMP wizard — https://kmp.jetbrains.com/

Ktor / Spring

- Ktor 3 release — https://blog.jetbrains.com/kotlin/2024/10/ktor-3-released/

- Ktor 문서 — https://ktor.io/docs/welcome.html

- Spring Boot Kotlin support — https://docs.spring.io/spring-framework/reference/languages/kotlin.html

- Spring Boot + Coroutines — https://docs.spring.io/spring-framework/reference/languages/kotlin/coroutines.html

kotlinx 라이브러리

- kotlinx.coroutines — https://github.com/Kotlin/kotlinx.coroutines

- kotlinx.serialization — https://github.com/Kotlin/kotlinx.serialization

- kotlinx-io — https://github.com/Kotlin/kotlinx-io

정적분석·테스트·DI·FP·ORM

- Detekt — https://detekt.dev/

- Konsist — https://docs.konsist.lemonappdev.com/

- Koin — https://insert-koin.io/

- Arrow — https://arrow-kt.io/

- Exposed — https://www.jetbrains.com/help/exposed/home.html

- Kotest — https://kotest.io/

- MockK — https://mockk.io/

Gradle / 빌드

- Gradle Kotlin DSL primer — https://docs.gradle.org/current/userguide/kotlin_dsl.html

- Version catalogs — https://docs.gradle.org/current/userguide/platforms.html

회사 케이스

- 토스 기술 블로그 — https://toss.tech/

- 카카오 기술 블로그 — https://tech.kakao.com/

- Mercari Engineering Blog — https://engineering.mercari.com/blog/

- ZOZO Technologies — https://techblog.zozo.com/

- CyberAgent Developers Blog — https://developers.cyberagent.co.jp/blog/

- LINE Engineering — https://engineering.linecorp.com/

마무리 한 줄. Kotlin 2.x는 더 이상 "Java보다 조금 나은 안드로이드용 언어"가 아니다. **컴파일러(K2), UI(Compose Multiplatform), 멀티플랫폼(KMP), 서버(Spring/Ktor), 라이브러리 진영(Koin/Arrow/Exposed/Kotest)** 가 모두 stable로 정렬된, 2026년 JVM 진영의 default 언어다. 이 글이 그 좌표를 잡는 데 도움이 됐기를.

현재 단락 (1/627)

2017년, Google I/O에서 Android의 공식 언어가 된 그날부터 Kotlin은 한참 동안 "안드로이드의 Java 대체재"로 살았다. 서버 진영에서는 Java 17, 2...

작성 글자: 0원문 글자: 22,057작성 단락: 0/627