π Introduction to Ktor
π What is Ktor?β
Ktor is a lightweight asynchronous web framework built with Kotlin. It allows you to easily create fast and efficient servers based on coroutines!
π‘ Creating a Projectβ
build.gradle.ktsβ
plugins {
kotlin("jvm") version "1.9.0"
id("io.ktor.plugin") version "2.3.5"
}
dependencies {
implementation("io.ktor:ktor-server-core:2.3.5")
implementation("io.ktor:ktor-server-netty:2.3.5")
implementation("ch.qos.logback:logback-classic:1.4.11")
}
π― First Serverβ
Hello Worldβ
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun main() {
embeddedServer(Netty, port = 8080) {
routing {
get("/") {
call.respondText("Hello, Ktor!")
}
}
}.start(wait = true)
}
After running, visit http://localhost:8080 in your browser!
Multiple Routesβ
fun main() {
embeddedServer(Netty, port = 8080) {
routing {
get("/") {
call.respondText("ννμ΄μ§")
}
get("/hello") {
call.respondText("μλ
νμΈμ!")
}
get("/about") {
call.respondText("μκ° νμ΄μ§")
}
}
}.start(wait = true)
}
π¨ Routingβ
Path Parametersβ
fun Application.configureRouting() {
routing {
get("/user/{id}") {
val id = call.parameters["id"]
call.respondText("μ¬μ©μ ID: $id")
}
get("/product/{category}/{id}") {
val category = call.parameters["category"]
val id = call.parameters["id"]
call.respondText("μΉ΄ν
κ³ λ¦¬: $category, μν ID: $id")
}
}
}
fun main() {
embeddedServer(Netty, port = 8080) {
configureRouting()
}.start(wait = true)
}
// μ μ: http://localhost:8080/user/123
// μΆλ ₯: μ¬μ©μ ID: 123
Query Parametersβ
routing {
get("/search") {
val query = call.request.queryParameters["q"]
val page = call.request.queryParameters["page"]
call.respondText("κ²μμ΄: $query, νμ΄μ§: $page")
}
}
// μ μ: http://localhost:8080/search?q=kotlin&page=1
// μΆλ ₯: κ²μμ΄: kotlin, νμ΄μ§: 1
π§ JSON Responsesβ
Setting up ContentNegotiationβ
plugins {
// build.gradle.ktsμ μΆκ°
implementation("io.ktor:ktor-server-content-negotiation:2.3.5")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.5")
}
JSON Responsesβ
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.plugins.contentnegotiation.*
import kotlinx.serialization.Serializable
@Serializable
data class User(val id: Int, val name: String, val email: String)
fun Application.module() {
install(ContentNegotiation) {
json()
}
routing {
get("/user/{id}") {
val id = call.parameters["id"]?.toInt() ?: 0
val user = User(id, "νκΈΈλ", "hong@example.com")
call.respond(user)
}
get("/users") {
val users = listOf(
User(1, "νκΈΈλ", "hong@example.com"),
User(2, "κΉμ² μ", "kim@example.com")
)
call.respond(users)
}
}
}
π― Practical Exampleβ
Simple APIβ
@Serializable
data class Product(
val id: Int,
val name: String,
val price: Double
)
fun Application.productAPI() {
install(ContentNegotiation) {
json()
}
val products = mutableListOf(
Product(1, "λ
ΈνΈλΆ", 1500000.0),
Product(2, "λ§μ°μ€", 30000.0),
Product(3, "ν€λ³΄λ", 80000.0)
)
routing {
// μ 체 μν μ‘°ν
get("/products") {
call.respond(products)
}
// νΉμ μν μ‘°ν
get("/products/{id}") {
val id = call.parameters["id"]?.toInt()
val product = products.find { it.id == id }
if (product != null) {
call.respond(product)
} else {
call.respondText("μνμ μ°Ύμ μ μμ΅λλ€", status = HttpStatusCode.NotFound)
}
}
}
}
fun main() {
embeddedServer(Netty, port = 8080) {
productAPI()
}.start(wait = true)
}
Status Codesβ
import io.ktor.http.*
routing {
get("/status/ok") {
call.respondText("μ μ", status = HttpStatusCode.OK)
}
get("/status/created") {
call.respondText("μμ±λ¨", status = HttpStatusCode.Created)
}
get("/status/not-found") {
call.respondText("μμ", status = HttpStatusCode.NotFound)
}
get("/status/error") {
call.respondText("μλ² μ€λ₯", status = HttpStatusCode.InternalServerError)
}
}
π₯ Middlewareβ
Loggingβ
import io.ktor.server.plugins.callloging.*
fun Application.module() {
install(CallLogging)
routing {
get("/") {
call.respondText("Hello!")
}
}
}
// μ½μμ μμ² λ‘κ·Έ μΆλ ₯
CORS Configurationβ
import io.ktor.server.plugins.cors.routing.*
fun Application.module() {
install(CORS) {
anyHost() // λͺ¨λ νΈμ€νΈ νμ© (κ°λ° νκ²½)
allowHeader(HttpHeaders.ContentType)
}
routing {
get("/api/data") {
call.respond(mapOf("message" to "CORS μ€μ λ¨"))
}
}
}
π οΈ Application Structureβ
Modularizationβ
// routes/UserRoutes.kt
fun Route.userRoutes() {
route("/users") {
get {
call.respond(listOf("User1", "User2"))
}
get("/{id}") {
val id = call.parameters["id"]
call.respondText("User $id")
}
}
}
// routes/ProductRoutes.kt
fun Route.productRoutes() {
route("/products") {
get {
call.respond(listOf("Product1", "Product2"))
}
}
}
// Application.kt
fun Application.module() {
install(ContentNegotiation) {
json()
}
routing {
userRoutes()
productRoutes()
}
}
π€ Frequently Asked Questionsβ
Q1. Ktor vs. Spring Boot?β
A: Ktor is lightweight and coroutine-friendly!
// Ktor - κ°κ²°νκ³ κ°λ²Όμ
fun Application.module() {
routing {
get("/hello") {
call.respondText("Hello!")
}
}
}
// Spring Boot - λ λ§μ κΈ°λ₯κ³Ό μνκ³
@RestController
class HelloController {
@GetMapping("/hello")
fun hello() = "Hello!"
}
Q2. How do I change the port?β
A: Specify it when creating the server!
// μ½λμμ
embeddedServer(Netty, port = 3000) {
// ...
}.start(wait = true)
// application.conf νμΌ
ktor {
deployment {
port = 3000
}
}
Q3. How do I serve static files?β
A: Use the Static plugin!
import io.ktor.server.http.content.*
routing {
static("/static") {
resources("static")
}
static("/files") {
files("uploads")
}
}
π¬ Conclusionβ
Get started with server development quickly using Ktor!
ν΅μ¬ μ 리:
β
κ²½λ λΉλκΈ° νλ μμν¬
β
μ½λ£¨ν΄ κΈ°λ°
β
κ°λ¨ν λΌμ°ν
β
JSON μ§λ ¬ν μ§μ
β
λͺ¨λν κ°λ₯
Next Step: Build a complete API in REST API!