Skip to content

필사 모드: Modern Kotlin 2026 — Kotlin 2.1 / K2 Compiler / Compose Multiplatform / KMP 2.1 / Ktor 3 / Spring Boot Kotlin Deep Dive

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

Prologue — In 2026, Kotlin is no longer "the Android language"

The day Kotlin became an official Android language at Google I/O 2017, it lived for years as "the Java alternative for Android." Server folks used Java 17, then 21. Multiplatform crawled from alpha to beta.

The picture in May 2026 is unrecognizable.

- **Kotlin 2.1** (Nov 2024) made the **K2 compiler the default**. IDE K2 mode is also default, and clean builds run on average 1.8x faster than K1.

- **Kotlin 2.2** (Mar 2025) introduced **context parameters** — the successor to the old context receivers. You can bind dependencies into a function signature without polluting the return type.

- **Compose Multiplatform 1.7** (Oct 2024) declared **iOS stable**. One codebase paints Android, iOS, desktop, and web (Wasm).

- **KMP (Kotlin Multiplatform) 2.1** is past "production-ready" — Mercari, McDonald's, and others have rewritten core SDKs in KMP.

- **Ktor 3** (Oct 2024) added a **WebAssembly target** and was rewritten on top of kotlinx-io; throughput rose ~90% on average.

- **Spring Boot 3.5** treats Kotlin as a first-class language. Coroutine support is mature and Kotlin DSL builds are standard.

This piece tries to take you from "I just started looking at Kotlin" to "I understand what it looks like in 2026 production" in a single read. Language, compiler, UI, server, library ecosystem, and real Korean/Japanese adoption — all of it.

1. Modern Kotlin in 2026 — where Kotlin 2.x sits

First, set coordinates. Here's the Kotlin full stack as of May 2026.

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

| Kotlin 2.2.x |

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

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

|

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

| | |

v v v

[JVM backend] [Multiplatform] [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 libraries |

| coroutines / Flow |

| serialization / io |

| datetime / atomicfu |

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

|

v

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

| | |

v v v

[DI/FP/ORM] [Static analysis] [Build]

Koin / Arrow Detekt / Konsist Gradle Kotlin DSL

Exposed Kotest / MockK Maven Kotlin plugin

The summary points:

- The language ships a major every ~2 years (1.x to 2.x), a minor every 6 months.

- Multiplatform is no longer "experimental." iOS stable was the game changer.

- The server ecosystem is alive with both Spring Boot and Ktor.

- The library ecosystem (Koin/Arrow/Exposed/Kotest/MockK) is thick enough to ship a Kotlin-idiomatic full stack.

We'll walk each box in depth.

2. Kotlin 2.1 (Nov 2024) — K2 default + multi-dollar strings

If Kotlin 2.0 was the first release where K2 became stable, 2.1 is the one that made it **default for everyone**. JetBrains also flipped IntelliJ's K2 mode on by default. K1 fallback is supported only until late 2025.

The 2.1 headline features:

1. **K2 compiler default** — anything at `-language-version 2.0` and above is K2.

2. **Multi-dollar (`$$`, `$$$`) string interpolation** — escape from `$` escaping when dealing with templates or LaTeX.

3. **`when` expression guard conditions** — `when (x) { is Foo if x.bar > 10 -> ... }` lets you add an extra check inline.

4. **`.kotlin` cache directory** — build outputs live under the project root, like Gradle's `.gradle`.

5. **Non-local `break`/`continue`** — you can break an outer loop from inside an inline lambda (preview).

Multi-dollar strings are genuinely nice.

// Kotlin 2.1: inside $$"..."$$, a single $ is literal; $$ starts interpolation

val template = $$"""

Hello, $$name!

Your balance is $1000 (USD).

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

"""

Before, every literal `$` needed escaping or you had to splice raw strings together. Now it's one line.

`when` guards get used constantly:

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}"

}

Under K1 you had to nest an `if` inside the `is Pending` branch, which broke the exhaustive check. Now it stays on one line, exhaustive intact.

3. Kotlin 2.2 (Mar 2025) — context parameters

The headline of Kotlin 2.2 is **context parameters** — the official successor to "context receivers," which had been in beta since 1.7.

What problem does it solve

Sometimes you want to inject dependencies into a function **implicitly**. For example:

// Before: always pass logger explicitly

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

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

return x + y

}

// Or wrap in a class

class Calculator(private val logger: Logger) {

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

}

Context parameters are the **middle road**: declare the dependency in the signature, but the caller doesn't have to pass it explicitly.

// Kotlin 2.2 context parameters

context(logger: Logger)

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

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

return x + y

}

// Caller: bring a Logger into scope and it's auto-injected

fun main() {

with(MyLogger()) {

val result = calculate(1, 2) // no explicit logger

}

}

How is it different from context receivers

The old 1.x `context(Logger)` form had **no name**. Two contexts of the same type clashed. 2.2's `context(logger: Logger)` is **named**. Arrow's effect library, Compose's `CompositionLocal`, and structured logging libraries are all being rewritten on top.

Real pattern: Arrow's 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

}

// Caller

fun main() {

val result = either {

val n = parsePositive("42")

n * 2

}

// result: Either.Right(84)

}

You can compose an error channel without exposing `Either<E, A>` everywhere. This is the Kotlin answer to Scala's ZIO or Haskell's monad transformer style of effect systems.

4. The K2 compiler — what gets faster, what changes

K2 is the result of a **complete rewrite of the compiler frontend**. K1 was designed in the early 2010s, and its smart-cast / generic inference code was scattered across many passes. K2 unified everything around a single intermediate representation called **FIR (Frontend IR)**.

Practical impact:

| Metric | K1 | K2 | Note |

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

| Clean build | 1.0x | 1.6 - 1.9x faster | Larger projects see bigger gains |

| Incremental build | 1.0x | 2.0 - 2.4x faster | Better cache / invalidation |

| IDE highlight | 1.0x | 1.5x faster | Smart-cast / inference caching |

| Peak memory | 1.0x | ~0.7x | FIR dedup |

Numbers blend JetBrains' published benchmarks with migration reports from Mercari, Square, and friends.

Smart cast gets smarter

K1 struggled with consistent narrowing across complex branches.

fun process(value: Any?) {

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

// K1: value is still Any?

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

println(value.hashCode()) // works on K2

}

}

K2 also recognizes **delayed `val` initialization**.

class Holder {

val data: List<Int>

init {

val tmp = mutableListOf<Int>()

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

data = tmp.toList() // K2: OK

}

}

Compiler plugin API stabilization

The Compose compiler plugin, kotlinx-serialization, and Arrow's raise plugin have all been rewritten against K2 FIR. Build times dropped, IDE indexing stabilized.

Migration checklist

- Bump `kotlin.languageVersion = "2.1"`.

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

- Compose projects use `compose-compiler-gradle-plugin` (shipped directly by JetBrains since 2024).

- Drop legacy flags like `-Xuse-fir-lt`.

5. Compose Multiplatform — iOS stable!

Compose Multiplatform 1.7, released October 2024, was a real milestone: **iOS went stable**. Up until then Compose-Android was production-grade but iOS was beta. From 1.7 onward, iOS is OK for production.

1.7 headlines (Oct 2024) plus the 1.7+ trickle

- **iOS stable** — UIViewController integration, text input, accessibility, dark mode all stable.

- **Resource API 1.0** — type-safe `Res.drawable.icon` / `Res.string.welcome` proxies.

- **Adaptive layouts** — `WindowSizeClass` API. Phone/tablet/foldable branches.

- **Skia 0.8 to 0.10** — better text rendering quality and lower GPU memory.

- **Hot reload (desktop)** — leverages Java 21's enhanced class redefinition. The design loop is genuinely fast.

One screen, four platforms

// 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")

}

}

}

}

The exact same code runs on Android, iOS, macOS/Windows/Linux desktop, and (experimentally) Web Wasm. Only platform-specific bits get extracted via `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 in 2026

| Item | Compose Multiplatform 1.7+ | SwiftUI |

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

| Platforms | Android / iOS / Desktop / Web | Apple only |

| Language | Kotlin | Swift |

| Code share | 100% (down to UI) | 0% (other platforms) |

| iOS performance | 95% (some scroll / animation gaps remain) | 100% native |

| Native widget access | `UIViewControllerRepresentable`-style interop | Natural |

| Build time | KMP build overhead | Standard Xcode |

Recommendation:

- iOS only → SwiftUI.

- Android + iOS, design consistency matters → CMP.

- "Use every new Apple API the day it ships" → SwiftUI.

6. KMP 2.1 — production-ready

If Compose Multiplatform is about UI, **KMP (Kotlin Multiplatform)** is about **logic sharing**. Business logic, networking, disk cache, domain model — write it once in Kotlin, reuse it on Android, iOS, server, and web.

KMP module layout

shared/

build.gradle.kts

src/

commonMain/kotlin/ <- code shared by every platform

androidMain/kotlin/ <- Android-only

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

jvmMain/kotlin/ <- JVM server

wasmJsMain/kotlin/ <- Web Wasm

commonTest/kotlin/ <- shared tests

Core libraries

- **kotlinx.coroutines** — same API across every platform.

- **kotlinx.serialization** — JSON/Protobuf/CBOR. Multiplatform standard.

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

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

- **Multiplatform Settings** — abstracts UserDefaults / SharedPreferences / files.

- **Decompose / Voyager** — multiplatform navigation.

- **Koin** — DI, multiplatform.

iOS integration — taking a KMP artifact into 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

Add the artifact to the Xcode project's Podfile

Swift just imports it.

let repo = UserRepository()

Task {

let users = try await repo.fetchUsers()

print(users)

}

A Kotlin `suspend fun` is auto-mapped to Swift `async throws` (thanks to 2.1 plus Kotlin/Native's swift-export improvements).

Mercari, McDonald's, and other case studies

- **Mercari** — migrated search and payment core logic to KMP. Bug fixes on Android and iOS happen in one place.

- **McDonald's** — rebuilt the Global Mobile App on KMP.

- See section 13 for a deeper look at Korean and Japanese companies.

7. Ktor 3 (Oct 2024) — WebAssembly target

Ktor 3 landed in October 2024 and is not "a minor bump on v2" — it's a **near-rewrite** of the framework.

Headlines

1. **Rewritten on kotlinx-io** — dropped okio / java.nio dependencies. Throughput up ~90% on average.

2. **WebAssembly target** — you can build a Ktor server with Kotlin/Wasm. Edge-compute use cases.

3. **CIO engine HTTP/2 stable** — no external dependency for HTTP/2.

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

5. **WebSocket extensions** — compression and ping/pong reworked.

Minimal Ktor 3 server

// 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` and you get JSON at 8080. Coroutine-based, so one thread can handle thousands of connections instead of one-thread-per-request.

Spring Boot vs Ktor — picking one

| Item | Ktor 3 | Spring Boot 3.5 + Kotlin |

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

| Philosophy | Minimal, opt-in plugins | "Convention over config" |

| Concurrency | Coroutine native | WebFlux (reactive) or virtual threads |

| Ecosystem | Lean | Massive, everything exists |

| Cold start | Fast | Fast with GraalVM native image |

| Learning curve | Short | Long but very well documented |

| Best fit | Microservices, edge, MVP | Large monolith, enterprise |

8. Spring Boot + Kotlin — JVM server

When you go to write a JVM service in Kotlin, 70% of companies are still on Spring Boot. And the Spring team did not ignore that traffic. Spring Framework 6 and the Boot 3.x line have promoted Kotlin to first-class.

Modern Spring Boot + Kotlin stack (2026)

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

- **Spring WebFlux + coroutines** — controllers accept `suspend` functions directly.

- **R2DBC + Kotlin coroutines** — async DB access.

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

- **Spring AOT + GraalVM native image** — cold start under 100 ms.

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` directly. No need to convert WebFlux `Mono` / `Flux`. A `Flow<UserDto>` is auto-mapped to an NDJSON streaming response.

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 { ... }` is the Kotlin DSL. The XML / Java config boilerplate is gone.

9. kotlinx.coroutines + Flow — the async model

Kotlin's async model has two axes. **Coroutines** (single async operations that finish) and **Flow** (streams of values over time).

Coroutines basics

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())

}

Inside `coroutineScope`, the two tasks run in parallel, and if either fails the other is canceled — **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)

}

}

// Consume

suspend fun watchUser() {

userUpdates(42L)

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

.map { it.toDto() }

.take(10)

.collect { println(it) }

}

Flow is **cold** — every `.collect` call replays from the start. For hot streams use `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 ?: "") }

)

}

}

}

In Compose you do `val state by viewModel.state.collectAsState()` and render.

Coroutine debugger

In IntelliJ: `Debug` menu → `Coroutines Dump`. You see exactly which coroutine is suspended where, as a tree. Compared to a Java thread dump, it's not even close.

10. Detekt / Konsist — static analysis + architecture testing

Two tools that automate Kotlin code quality.

Detekt — code smell static analysis

// 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

}

Excerpt from `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 integration:

./gradlew detekt

Output at build/reports/detekt/detekt.html

K2 compatibility has been stable since 1.23, and from 1.24 (mid-2025) K2-only rules were added (smart-cast hints, sealed exhaustive checks, and so on).

Konsist — architecture testing

The Kotlin counterpart to ArchUnit. Verifies **layering, naming, and annotation usage** like a unit test.

// 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") }

}

}

Runs as part of `./gradlew test`. A new developer who calls a repository from a controller gets a red CI.

Detekt is "quality inside a file." Konsist is "structure across the project." They complement each other.

11. Koin / Arrow / Exposed / Kotest / MockK — the library ecosystem

The Kotlin-native counterparts to Java's Spring / Hibernate / Mockito.

Koin — DSL-based DI

val appModule = module {

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

single<HttpClient> {

HttpClient(CIO) {

install(ContentNegotiation) { json() }

}

}

viewModel { UserViewModel(get()) }

}

fun main() {

startKoin {

modules(appModule)

}

// ...

}

No reflection. No compile-time codegen (unlike Dagger). KMP-compatible. Used directly inside Compose Multiplatform.

Arrow — Kotlin's de facto FP library

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)

}

Compose errors with `Either<L, R>` plus the `raise` DSL — no throws. Combine that with 2.2 context parameters and you get a complete effect system.

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] }

}

Lighter than JPA, no Hibernate lazy-loading traps. The R2DBC adapter (stable since 1.0) lets it run in coroutine environments.

Kotest — BDD-style testing

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-compatible, expression-based assertions, property-based testing (QuickCheck style) built in. 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) }

}

})

Mocks the things Mockito can't: `object`, `companion object`, top-level functions, suspend functions.

12. Gradle Kotlin DSL — the build standard

As of 2026, **99% of new Kotlin projects use the Gradle Kotlin DSL** (`build.gradle.kts`). The Groovy DSL is legacy.

Minimal `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" }

Per-module `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)

}

Single source of version truth. The payoff is huge once you cross 30 modules.

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

The configuration cache went stable in 8.x. Builds run 2 to 5 times faster (especially incremental).

Is Maven dead?

No. **Enterprise Spring Boot shops with large Maven estates** still use the Maven Kotlin plugin.

Functionally equivalent. Slower than Gradle but rock-solid.

13. Korea / Japan — Kakao, Toss, Mercari, ZOZO, CyberAgent

Korea

**Kakao (Android-wide Kotlin)**

- 95%+ of the KakaoTalk Android codebase is Kotlin. New Java code is banned.

- 100+ modules in a multi-module setup, Gradle Kotlin DSL plus version catalog.

- Internally published Detekt config + a Kakao house style (`KakaoCodeStyle`).

- Compose migration in progress (as of 2026, all new screens are 100% Compose).

**Toss (server-side Kotlin pioneer)**

- 100% of new backend microservices are Kotlin + Spring Boot. 200+ services on Kotlin.

- Coroutines + WebFlux + R2DBC + Arrow is the standard combo.

- Many in-house Kotlin DSL libraries (such as `tossopen-kotlin-commons`).

- The Toss engineering blog has multiple series on "Kotlin migration for Spring" and "Why Kotlin instead of Java."

**LINE (NAVER) — KMP cases**

- Pieces of the LINE messenger's client SDK are being shared via KMP. Partial production adoption as of 2026.

Japan

**Mercari — KMP pioneer**

- Rebuilt search and payment core SDKs on KMP. Fixes ship to Android and iOS at once.

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

- Currently evaluating Compose Multiplatform 1.7+.

**ZOZOTOWN (ZOZO)**

- Android is full Kotlin. Parts of the server are Kotlin (Spring Boot + coroutines).

- In-house "Kotlin Guild" — biweekly study sessions, internal library sharing.

**CyberAgent (AbemaTV, FRIDAY, ...)**

- New Android projects are 100% Kotlin.

- AbemaTV Android — Compose + coroutines + Koin + Coil.

- A few microservices on Ktor 3 (streaming metadata service).

**DeNA / GREE / Rakuten**

- Same pattern: Android fully Kotlin, server selectively Kotlin.

Common patterns

Across Korean and Japanese large companies:

1. Android is already 100% Kotlin.

2. Server adoption begins with **new microservices**.

3. KMP is applied **selectively, around core SDKs** (full-app KMP is still rare).

4. House style guides are **Detekt-based + custom rules**.

5. Hiring: knowing Kotlin = Android + server, double the surface area.

14. Who should choose Kotlin — Android / KMP / JVM server / FP

A decision matrix to wrap up. Scenario-by-scenario answers to "is Kotlin the right call?"

"I'm writing an Android app"

→ **Kotlin is the default. There's no other realistic choice.**

- Android Studio is Kotlin-first.

- Jetpack Compose's primary language.

- Every new Google library / document is Kotlin first.

"iOS and Android both, one team"

→ **Consider KMP + Compose Multiplatform.**

Conditions:

- You don't need every new iOS API the day it lands (one to two months of lag on SwiftUI / AppKit features is OK).

- The business logic is identical on both platforms.

- iOS designers don't insist "this must be exactly iOS-native."

If all three hold, KMP. If any of them fails, ship two native apps.

"JVM server in a Spring Boot shop"

→ **Kotlin beats Java on nearly every axis.**

- Null safety catches NPEs at compile time.

- Coroutines are far more readable than `CompletableFuture`.

- Data classes remove the need for Lombok.

- Spring 6 / Boot 3.5 treat Kotlin as first-class.

Exception: if 80% of the team only knows Java, adopt incrementally (start with test code, then new microservices).

"Lightweight microservices, GraalVM native image"

→ **Ktor 3 + Kotlin/Native, or Ktor 3 + GraalVM.**

- Cold start under 100 ms.

- Memory under 50 MB.

- Great fit for lambdas / edge functions.

"Serious functional programming"

→ **Kotlin + Arrow. But the learning curve is steep.**

- `Either` / `IO` / context parameters / `raise` DSL combined.

- Not as deep as Scala or Haskell, but the industry-friendly FP sweet spot on the JVM.

"Data engineering, Spark / Flink"

→ **Scala is still ahead. Kotlin is supplementary.**

- The Spark Kotlin API exists but is not as mature as Scala's.

- That said, newer tools like KEDB (Kotlin Embedded DB) are Kotlin-first.

"Not Android, not JVM"

→ Kotlin doesn't really need to be on your radar. JS / Wasm targets exist but TypeScript / Rust are the defaults there.

References

Kotlin language and compiler

- 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 docs — 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 official — 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 docs — 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 libraries

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

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

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

Static analysis, testing, 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 / build

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

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

Company case studies

- Toss engineering blog — https://toss.tech/

- Kakao tech blog — 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/

One closing line. Kotlin 2.x is no longer "a slightly nicer Java for Android." The **compiler (K2), UI (Compose Multiplatform), multiplatform (KMP), server (Spring / Ktor), and library ecosystem (Koin / Arrow / Exposed / Kotest)** are now aligned at stable. It's the default language of the 2026 JVM ecosystem. Hopefully this piece helped you place the coordinates.

현재 단락 (1/627)

The day Kotlin became an official Android language at Google I/O 2017, it lived for years as "the Ja...

작성 글자: 0원문 글자: 26,759작성 단락: 0/627