개요
Ktor는 최신 웹 애플리케이션 및 API 빌드를 위해 설계된 코틀린(Kotlin) 기반 비동기 웹 프레임워크 입니다. 코틀린 (Kotlin) ) 코루틴을 사용한 비동기 프로그래밍을 강력하게 지원 합니다.
이 튜토리얼에서는 Ktor, 코틀린 (Kotlin) 운전자 및 MongoDB Atlas 사용하여 실행 가능한 API 설정하다 방법을 학습 봅니다.
튜토리얼
전제 조건 확인
이 튜토리얼을 시작하기 전에 다음 사항이 있는지 확인하세요.
클러스터 가 있는 MongoDB Atlas 계정. 지침을 보려면 시작하기 가이드 참조하세요.
Ktor 프로젝트 설정
Ktor 프로젝트 생성기를 사용하여 새 Ktor 프로젝트 생성합니다.com.mongodb.fitness-tracker 텍스트 입력 필드 사용하여 프로젝트 아티팩트의 이름을 로 지정합니다.Configure 버튼을 클릭하고 다음 옵션을 지정합니다.
빌드 시스템: Gradle 코틀린 (Kotlin)
엔진: 톰캣
구성: HOCON 파일
새 Ktor 프로젝트 페이지의 Plugins 메뉴를 사용하여 프로젝트 에 다음 플러그인을 추가합니다.
콘텐츠 협상: 클라이언트 와 서버 간의 미디어 유형 협상
GSON: 직렬화 및 역직렬화 지원 제공
라우팅: 수신 요청 관리
Swagger: API 문서 생성
Generate Project 버튼을 클릭하여 새 Ktor 프로젝트 포함된 ZIP 파일 다운로드 .
다운로드한 파일 의 압축을 풀고 프로젝트 디렉토리 의 루트 디렉토리 에서 build.gradle.kts 파일 엽니다. plugins 차단 에 다음 플러그인을 추가합니다.
plugins { kotlin("jvm") version "1.9.22" id("io.ktor.plugin") version "2.3.7" id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22" }
동일한 파일 의 dependencies 차단 에 다음 종속성을 추가합니다.
dependencies { implementation("io.ktor:ktor-server-core-jvm") implementation("io.ktor:ktor-server-swagger-jvm") implementation("io.ktor:ktor-server-content-negotiation-jvm") implementation("io.ktor:ktor-serialization-gson-jvm") implementation("io.ktor:ktor-server-tomcat-jvm") implementation("ch.qos.logback:logback-classic:$logback_version") implementation("io.ktor:ktor-server-config-yaml:2.3.8") // MongoDB implementation("org.mongodb:mongodb-driver-kotlin-coroutine:5.6.1") // Koin dependency injection implementation("io.insert-koin:koin-ktor:3.5.3") implementation("io.insert-koin:koin-logger-slf4j:3.5.3") // Client implementation("io.ktor:ktor-client-core:$ktor_version") implementation("io.ktor:ktor-client-cio:$ktor_version") }
CRUD 작업 구현
src/main/kotlin 디렉토리 아래에 다음 세 개의 패키지를 만듭니다.
applicationdomaininfrastructure
domain 패키지 내에서 entity라는 새 하위 패키지를 만든 다음 entity 내에 Fitness.kt라는 파일 만듭니다. 다음 코드를 파일 에 복사합니다.
package com.mongodb.domain.entity import com.mongodb.application.response.FitnessResponse import org.bson.codecs.pojo.annotations.BsonId import org.bson.types.ObjectId data class Fitness( val id: ObjectId, val exerciseType: String, val notes: String, val details: FitnessDetails ){ fun toResponse() = FitnessResponse( id = id.toString(), exerciseType = exerciseType, notes = notes, details = details ) } data class FitnessDetails( val durationMinutes: Int, val distance: Double, val caloriesBurned: Int )
앞의 코드의 toResponse() 메서드는 아직 FitnessResponse 클래스를 생성하지 않았기 때문에 오류가 발생합니다. application/request 패키지 아래에 request 및 response 패키지를 생성하고 해당 패키지 에 FitnessRequest.kt 및 FitnessResponse.kt 라는 파일 두 개를 생성합니다.
다음 코드를 FitnessRequest.kt 파일 에 복사합니다.
package com.mongodb.application.response import com.mongodb.domain.entity.FitnessDetails data class FitnessResponse( val id: String, val exerciseType: String, val notes: String, val details: FitnessDetails )
다음 코드를 FitnessResponse.kt 파일 에 복사합니다.
package com.mongodb.application.response import com.mongodb.domain.entity.FitnessDetails data class FitnessResponse( val id: String, val exerciseType: String, val notes: String, val details: FitnessDetails )
domain 패키지 아래에 ports 패키지 만들고 ports 패키지 에 FitnessRepository.kt 라는 파일 만듭니다. 이 파일 데이터베이스 와 통신할 인터페이스를 나타냅니다. 다음 코드를 FitnessRepository.kt 파일 에 복사합니다.
package com.mongodb.domain.ports import com.mongodb.domain.entity.Fitness import org.bson.BsonValue import org.bson.types.ObjectId interface FitnessRepository { suspend fun insertOne(fitness: Fitness): BsonValue? suspend fun deleteById(objectId: ObjectId): Long suspend fun findById(objectId: ObjectId): Fitness? suspend fun updateOne(objectId: ObjectId, fitness: Fitness): Long }
infrastructure 패키지 아래에 repository 패키지 만들고 repository 패키지 에 FitnessRepositoryImpl.kt 라는 파일 만듭니다. 이 파일 인터페이스의 메서드를 구현합니다. 다음 코드를 FitnessRepositoryImpl.kt 파일 에 복사합니다.
package com.mongodb.infrastructure.repository import com.mongodb.MongoException import com.mongodb.client.model.Filters import com.mongodb.client.model.UpdateOptions import com.mongodb.client.model.Updates import com.mongodb.domain.entity.Fitness import com.mongodb.domain.ports.FitnessRepository import com.mongodb.kotlin.client.coroutine.MongoDatabase import kotlinx.coroutines.flow.firstOrNull import org.bson.BsonValue import org.bson.types.ObjectId class FitnessRepositoryImpl( private val mongoDatabase: MongoDatabase ) : FitnessRepository { companion object { const val FITNESS_COLLECTION = "fitness" } override suspend fun insertOne(fitness: Fitness): BsonValue? { try { val result = mongoDatabase.getCollection<Fitness>(FITNESS_COLLECTION).insertOne( fitness ) return result.insertedId } catch (e: MongoException) { System.err.println("Unable to insert due to an error: $e") } return null } override suspend fun deleteById(objectId: ObjectId): Long { try { val result = mongoDatabase.getCollection<Fitness>(FITNESS_COLLECTION).deleteOne(Filters.eq("_id", objectId)) return result.deletedCount } catch (e: MongoException) { System.err.println("Unable to delete due to an error: $e") } return 0 } override suspend fun findById(objectId: ObjectId): Fitness? = mongoDatabase.getCollection<Fitness>(FITNESS_COLLECTION).withDocumentClass<Fitness>() .find(Filters.eq("_id", objectId)) .firstOrNull() override suspend fun updateOne(objectId: ObjectId, fitness: Fitness): Long { try { val query = Filters.eq("_id", objectId) val updates = Updates.combine( Updates.set(Fitness::exerciseType.name, fitness.exerciseType), Updates.set(Fitness::notes.name, fitness.notes), Updates.set(Fitness::details.name, fitness.details) ) val options = UpdateOptions().upsert(true) val result = mongoDatabase.getCollection<Fitness>(FITNESS_COLLECTION) .updateOne(query, updates, options) return result.modifiedCount } catch (e: MongoException) { System.err.println("Unable to update due to an error: $e") } return 0 } }
플러그인 구성
다음 코드를 Application.kt 파일에 붙여넣습니다. 이 코드는 다음 작업을 수행합니다.
Gson 포맷터를 사용하여 JSON 직렬화 및 역직렬화를 처리하다 하도록 ContentNegotiation 플러그인을 설정합니다.
Koin을 사용하여 종속성 주입 활성화
/swagger엔드포인트에서 사용할 수 있는 Swagger API 경로를 구성합니다.
package com.mongodb import com.mongodb.application.routes.fitnessRoutes import com.mongodb.domain.ports.FitnessRepository import com.mongodb.infrastructure.repository.FitnessRepositoryImpl import com.mongodb.kotlin.client.coroutine.MongoClient import io.ktor.serialization.gson.gson import io.ktor.server.application.Application import io.ktor.server.application.install import io.ktor.server.plugins.contentnegotiation.ContentNegotiation import io.ktor.server.plugins.swagger.swaggerUI import io.ktor.server.routing.routing import io.ktor.server.tomcat.EngineMain import org.koin.dsl.module import org.koin.ktor.plugin.Koin import org.koin.logger.slf4jLogger fun main(args: Array<String>): Unit = EngineMain.main(args) fun Application.module() { install(ContentNegotiation) { gson { } } install(Koin) { slf4jLogger() modules(module { single { MongoClient.create( environment.config.propertyOrNull("ktor.mongo.uri")?.getString() ?: throw RuntimeException("Failed to access MongoDB URI.") ) } single { get<MongoClient>().getDatabase(environment.config.property("ktor.mongo.database").getString()) } }, module { single<FitnessRepository> { FitnessRepositoryImpl(get()) } }) } routing { swaggerUI(path = "swagger-ui", swaggerFile = "openapi/documentation.yaml") { version = "4.15.5" } fitnessRoutes() } }
application.conf 파일 열고 다음을 붙여넣습니다.
ktor { deployment { port = 8080 } application { modules = [ com.mongodb.ApplicationKt.module ] } mongo { uri = ${?MONGO_URI} database = ${?MONGO_DATABASE} } }
documentation.yaml 파일 열고 다음을 붙여넣습니다.
openapi: 3.0.0 info: title: Fitness API version: 1.0.0 description: | This Swagger documentation file outlines the API specifications for a Fitness Tracker application built with Ktor and MongoDB. The API allows users to manage fitness records including creating new records, updating and deleting records by ID. The API uses the Fitness and FitnessDetails data classes to structure the fitness-related information. paths: /fitness: post: summary: Create a new fitness record requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/FitnessRequest' responses: '201': description: Fitness created successfully '400': description: Bad request /fitness/{id}: get: summary: Retrieve fitness record by ID parameters: - name: id in: path required: true schema: type: string responses: '200': description: Successful response content: application/json: example: {} '404': description: Fitness not found delete: summary: Delete fitness record by ID parameters: - name: id in: path required: true schema: type: string responses: '200': description: Fitness deleted successfully '400': description: Bad request '404': description: Fitness not found patch: summary: Update fitness record by ID parameters: - name: id in: path required: true schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/FitnessRequest' responses: '200': description: Fitness updated successfully '400': description: Bad request '404': description: Fitness not found components: schemas: Fitness: type: object properties: id: type: string format: uuid exerciseType: type: string notes: type: string details: $ref: '#/components/schemas/FitnessDetails' required: - id - notes - details FitnessDetails: type: object properties: durationMinutes: type: integer format: int32 distance: type: number format: double caloriesBurned: type: integer format: int32 required: - durationMinutes - distance - caloriesBurned FitnessRequest: type: object properties: exerciseType: type: string notes: type: string details: $ref: '#/components/schemas/FitnessDetails' required: - exerciseType - notes - details
이 파일 API 가 /swagger-ui 엔드포인트를 통해 제공하는 메서드를 정의합니다.
API 엔드포인트 구현
이 단계에서는 다음 API 엔드포인트를 구현합니다.
GET /fitness/{id}: ID 로 운동 항목을 조회합니다.
POST /Fitness: 새 운동 항목을 생성합니다.
PATCH /fitness/{id}: 지정된 운동 항목을 업데이트합니다.
DELETE /fitness/{id}: 지정된 운동 항목을 삭제합니다.
application 패키지 아래에 routes 패키지 만들고 routes 패키지 에 FitnessRoutes.kt 라는 파일 만듭니다. 다음 코드를 FitnessRoutes.kt 파일 에 복사합니다.
package com.mongodb.application.routes import com.mongodb.application.request.FitnessRequest import com.mongodb.application.request.toDomain import com.mongodb.domain.ports.FitnessRepository import io.ktor.http.HttpStatusCode import io.ktor.server.application.call import io.ktor.server.request.receive import io.ktor.server.response.respond import io.ktor.server.response.respondText import io.ktor.server.routing.route import io.ktor.server.routing.Route import io.ktor.server.routing.post import io.ktor.server.routing.delete import io.ktor.server.routing.get import io.ktor.server.routing.patch import org.bson.types.ObjectId import org.koin.ktor.ext.inject fun Route.fitnessRoutes() { val repository by inject<FitnessRepository>() route("/fitness") { post { val fitness = call.receive<FitnessRequest>() val insertedId = repository.insertOne(fitness.toDomain()) call.respond(HttpStatusCode.Created, "Created fitness with id $insertedId") } delete("/{id?}") { val id = call.parameters["id"] ?: return@delete call.respondText( text = "Missing fitness id", status = HttpStatusCode.BadRequest ) val delete: Long = repository.deleteById(ObjectId(id)) if (delete == 1L) { return@delete call.respondText("Fitness Deleted successfully", status = HttpStatusCode.OK) } return@delete call.respondText("Fitness not found", status = HttpStatusCode.NotFound) } get("/{id?}") { val id = call.parameters["id"] if (id.isNullOrEmpty()) { return@get call.respondText( text = "Missing id", status = HttpStatusCode.BadRequest ) } repository.findById(ObjectId(id))?.let { call.respond(it.toResponse()) } ?: call.respondText("No records found for id $id") } patch("/{id?}") { val id = call.parameters["id"] ?: return@patch call.respondText( text = "Missing fitness id", status = HttpStatusCode.BadRequest ) val updated = repository.updateOne(ObjectId(id), call.receive()) call.respondText( text = if (updated == 1L) "Fitness updated successfully" else "Fitness not found", status = if (updated == 1L) HttpStatusCode.OK else HttpStatusCode.NotFound ) } } }
앞의 코드는 API 엔드포인트와 해당 핸들러를 정의합니다.
애플리케이션 실행 plugins HTTP.ktRouting.kt전에, 및 파일 이 포함된 폴더를 삭제 Serialization.kt Application.kt.
애플리케이션 실행
MongoDB Atlas cluster 에서 연결 URI를 검색합니다. 이 작업을 수행하는 방법에 대한 자세한 내용은 MongoDB 에 연결 가이드 의 연결URI 섹션을 참조하세요.
application.conf 파일 열고 다음 구성 값을 추가합니다.
-DMONGO_URI= <your connection URI> -DMONGO_DATABASE= <your database name>
IntelliJ에서 Application.kt 파일 로 이동하여 Run 버튼을 클릭합니다. 이제 웹 브라우저에서 http://localhost:8080/swagger-ui/ 로 이동하여 API 액세스 수 있습니다.
추가 정보
Ktor에 대해 자세히 학습 Ktor 문서를 참조하세요.
이 튜토리얼의 소스 코드 보려면 GitHub의 코틀린 (Kotlin) Ktor MongoDB Vector Search 리포지토리 참조하세요.