Skip to content

Ktor로 Kotlin에서 RESTful API를 생성하는 방법

코드 예시: tutorial-server-restful-api

사용된 플러그인:

라우팅
라우팅은 서버 애플리케이션에서 수신 요청을 처리하기 위한 핵심 플러그인입니다.
,
정적 콘텐츠
스타일시트, 스크립트, 이미지 등과 같은 정적 콘텐츠를 제공하는 방법을 알아보세요.
,
콘텐츠 협상
ContentNegotiation 플러그인은 두 가지 주요 목적을 가지고 있습니다: 클라이언트와 서버 간의 미디어 타입 협상 및 특정 형식으로 콘텐츠 직렬화/역직렬화.
, kotlinx.serialization

이 튜토리얼에서는 Kotlin과 Ktor를 사용하여 백엔드 서비스를 구축하는 방법과 JSON 파일을 생성하는 RESTful API의 예제를 설명합니다.

이전 튜토리얼
작업 관리 애플리케이션을 구축하여 Ktor와 함께 Kotlin에서 라우팅, 요청 처리 및 매개변수의 기본 사항을 배우세요.
에서는 유효성 검사, 오류 처리 및 단위 테스트의 기본 사항을 소개했습니다. 이 튜토리얼에서는 작업 관리를 위한 RESTful 서비스를 생성하여 이러한 주제를 확장할 것입니다.

다음 내용을 학습하게 됩니다:

  • JSON 직렬화를 사용하는 RESTful 서비스를 생성합니다.
  • 콘텐츠 협상
    ContentNegotiation 플러그인은 두 가지 주요 목적을 가지고 있습니다: 클라이언트와 서버 간의 미디어 타입 협상 및 특정 형식으로 콘텐츠 직렬화/역직렬화.
    프로세스를 이해합니다.
  • Ktor 내에서 REST API를 위한 경로를 정의합니다.

사전 준비 사항

이 튜토리얼을 독립적으로 진행할 수 있지만,

요청을 처리하고 응답을 생성하는
작업 관리 애플리케이션을 구축하여 Ktor와 함께 Kotlin에서 라우팅, 요청 처리 및 매개변수의 기본 사항을 배우세요.
방법을 배우기 위해 이전 튜토리얼을 완료하는 것을 강력히 권장합니다.

IntelliJ IDEA를 설치하는 것을 권장하지만, 다른 원하는 IDE를 사용할 수도 있습니다.

Hello RESTful 작업 관리자

이 튜토리얼에서는 기존 작업 관리자를 RESTful 서비스로 재작성할 것입니다. 이를 위해 여러 Ktor

플러그인
플러그인은 직렬화, 콘텐츠 인코딩, 압축 등과 같은 일반적인 기능을 제공합니다.
을 사용합니다.

기존 프로젝트에 수동으로 추가할 수도 있지만, 새 프로젝트를 생성한 다음 이전 튜토리얼의 코드를 점진적으로 추가하는 것이 더 간단합니다. 코드를 진행하면서 모든 코드를 반복하므로 이전 프로젝트를 준비할 필요가 없습니다.

  1. Ktor 프로젝트 생성기로 이동합니다.

  2. Project artifact 필드에 com.example.ktor-rest-task-app 를 프로젝트 아티팩트 이름으로 입력합니다. Ktor 프로젝트 생성기에서 프로젝트 아티팩트 이름 지정

  3. 플러그인 섹션에서 다음 플러그인을 검색하고 Add 버튼을 클릭하여 추가합니다.

    1. Routing
    2. Content Negotiation
    3. Kotlinx.serialization
    4. Static Content

    Ktor 프로젝트 생성기에서 플러그인 추가 플러그인을 추가하면, 프로젝트 설정 아래에 네 개의 플러그인 모두가 나열됩니다. Ktor 프로젝트 생성기의 플러그인 목록

  4. Download 버튼을 클릭하여 Ktor 프로젝트를 생성하고 다운로드합니다.

  1. IntelliJ IDEA에서 Ktor 프로젝트 열기, 탐색 및 실행 튜토리얼에서 설명한 대로 IntelliJ IDEA에서 프로젝트를 엽니다.

  2. src/main/kotlin/com/example 로 이동하여 model 이라는 하위 패키지를 생성합니다.

  3. model 패키지 안에 새 Task.kt 파일을 생성합니다.

  4. Task.kt 파일을 열고 우선순위를 나타내는 enum과 작업을 나타내는 class를 추가합니다.

    kotlin

    이전 튜토리얼에서는 확장 함수를 사용하여 Task를 HTML로 변환했습니다. 이 경우, Task 클래스는 kotlinx.serialization 라이브러리의 Serializable 타입으로 어노테이션되어 있습니다.

  5. Routing.kt 파일을 열고 기존 코드를 아래 구현으로 바꿉니다.

    kotlin

    이전 튜토리얼과 유사하게, /tasks URL에 대한 GET 요청 경로를 생성했습니다. 이번에는 작업 목록을 수동으로 변환하는 대신 단순히 목록을 반환합니다.

  6. IntelliJ IDEA에서 실행 버튼 (IntelliJ IDEA 실행 아이콘) 을 클릭하여 애플리케이션을 시작합니다.

  7. 브라우저에서 http://0.0.0.0:8080/tasks로 이동합니다. 아래와 같이 작업 목록의 JSON 버전을 볼 수 있습니다.

  8. 브라우저 화면에 표시된 JSON 데이터

    확실히 많은 작업이 우리를 대신하여 수행되고 있습니다. 정확히 무슨 일이 일어나고 있는 걸까요?

콘텐츠 협상 이해하기

브라우저를 통한 콘텐츠 협상

프로젝트를 생성할 때

Content Negotiation
ContentNegotiation 플러그인은 두 가지 주요 목적을 가지고 있습니다: 클라이언트와 서버 간의 미디어 타입 협상 및 특정 형식으로 콘텐츠 직렬화/역직렬화.
플러그인을 포함했습니다. 이 플러그인은 클라이언트가 렌더링할 수 있는 콘텐츠 타입을 현재 서비스가 제공할 수 있는 콘텐츠 타입과 비교하여 일치시킵니다. 따라서 이라는 용어가 사용됩니다.

HTTP에서 클라이언트는 Accept 헤더를 통해 렌더링할 수 있는 콘텐츠 타입을 신호합니다. 이 헤더의 값은 하나 이상의 콘텐츠 타입입니다. 위 경우에는 브라우저에 내장된 개발 도구를 사용하여 이 헤더의 값을 검사할 수 있습니다.

다음 예시를 고려해 보세요:

/의 포함에 주목하세요. 이 헤더는 HTML, XML 또는 이미지를 허용하지만, 다른 어떤 콘텐츠 타입도 허용함을 의미합니다.

콘텐츠 협상 플러그인은 데이터를 브라우저로 다시 보내기 위한 형식을 찾아야 합니다. 프로젝트에 생성된 코드를 살펴보면 src/main/kotlin/com/example 안에 Serialization.kt 라는 파일이 있으며, 여기에는 다음 내용이 포함되어 있습니다.

kotlin

이 코드는 ContentNegotiation 플러그인을 설치하고 kotlinx.serialization 플러그인도 구성합니다. 이렇게 하면 클라이언트가 요청을 보낼 때 서버는 JSON으로 직렬화된 객체를 다시 보낼 수 있습니다.

브라우저에서 온 요청의 경우, ContentNegotiation 플러그인은 JSON만 반환할 수 있다는 것을 알고 있으며, 브라우저는 전송된 모든 것을 표시하려고 시도할 것입니다. 따라서 요청이 성공합니다.

    프로덕션 환경에서는 JSON을 브라우저에 직접 표시하고 싶지 않을 것입니다. 대신 브라우저 내에서 JavaScript 코드가 실행되어 요청을 만들고 단일 페이지 애플리케이션(SPA)의 일부로 반환된 데이터를 표시합니다. 일반적으로 이러한 종류의 애플리케이션은 React, Angular, 또는 Vue.js와 같은 프레임워크를 사용하여 작성됩니다.

  1. 이를 시뮬레이션하기 위해, src/main/resources/static 안에 있는 index.html 페이지를 열고 기본 내용을 다음으로 바꿉니다.

    html

    이 페이지에는 HTML 폼과 비어 있는 테이블이 있습니다. 폼을 제출하면 JavaScript 이벤트 핸들러가 /tasks 엔드포인트로 Accept 헤더를 application/json으로 설정하여 요청을 보냅니다. 반환된 데이터는 역직렬화되어 HTML 테이블에 추가됩니다.

  2. IntelliJ IDEA에서 다시 실행 버튼(IntelliJ IDEA 다시 실행 아이콘)을 클릭하여 애플리케이션을 재시작합니다.

  3. URL http://0.0.0.0:8080/static/index.html로 이동합니다. View The Tasks 버튼을 클릭하여 데이터를 가져올 수 있어야 합니다.

    버튼과 HTML 테이블로 표시된 작업을 보여주는 브라우저 창

GET 경로 추가

이제 콘텐츠 협상 프로세스에 익숙해졌으므로,

이전 튜토리얼
작업 관리 애플리케이션을 구축하여 Ktor와 함께 Kotlin에서 라우팅, 요청 처리 및 매개변수의 기본 사항을 배우세요.
의 기능을 이 튜토리얼로 이전하는 것을 계속 진행하겠습니다.

작업 리포지토리 재사용

작업을 위한 리포지토리는 수정 없이 재사용할 수 있으므로 먼저 그렇게 하겠습니다.

  1. model 패키지 안에 새 TaskRepository.kt 파일을 생성합니다.

  2. TaskRepository.kt 를 열고 아래 코드를 추가합니다.

    kotlin

GET 요청 경로 재사용

이제 리포지토리를 생성했으므로 GET 요청에 대한 경로를 구현할 수 있습니다. 이전 코드는 작업을 HTML로 변환하는 것에 대해 더 이상 걱정할 필요가 없으므로 간단해질 수 있습니다.

  1. src/main/kotlin/com/exampleRouting.kt 파일로 이동합니다.

  2. Application.configureRouting() 함수 내의 /tasks 경로에 대한 코드를 다음 구현으로 업데이트합니다.

    kotlin

    이를 통해 서버는 다음 GET 요청에 응답할 수 있습니다.

    • /tasks는 리포지토리의 모든 작업을 반환합니다.
    • /tasks/byName/{taskName}은 지정된 taskName으로 필터링된 작업을 반환합니다.
    • /tasks/byPriority/{priority}는 지정된 priority로 필터링된 작업을 반환합니다.
  3. IntelliJ IDEA에서 다시 실행 버튼(IntelliJ IDEA 다시 실행 아이콘)을 클릭하여 애플리케이션을 재시작합니다.

기능 테스트

    브라우저에서 이 경로들을 테스트할 수 있습니다. 예를 들어, http://0.0.0.0:8080/tasks/byPriority/Medium으로 이동하여 Medium 우선순위를 가진 모든 작업이 JSON 형식으로 표시되는 것을 확인하세요.

    중간 우선순위를 가진 작업이 JSON 형식으로 표시된 브라우저 창

    이러한 종류의 요청은 일반적으로 JavaScript에서 오기 때문에, 더 세밀한 테스트가 선호됩니다. 이를 위해 Postman과 같은 전문 도구를 사용할 수 있습니다.

  1. Postman에서 URL http://0.0.0.0:8080/tasks/byPriority/Medium으로 새 GET 요청을 생성합니다.

  2. Headers 창에서 Accept 헤더의 값을 application/json으로 설정합니다.

  3. Send 를 클릭하여 요청을 보내고 응답 뷰어에서 응답을 확인합니다.

    중간 우선순위를 가진 작업이 JSON 형식으로 표시된 Postman의 GET 요청

    IntelliJ IDEA Ultimate 내에서는 HTTP 요청 파일에서 동일한 단계를 수행할 수 있습니다.

  1. 프로젝트 루트 디렉토리에 새 REST Task Manager.http 파일을 생성합니다.

  2. REST Task Manager.http 파일을 열고 다음 GET 요청을 추가합니다.

    http
  3. IntelliJ IDE 내에서 요청을 보내려면 옆에 있는 거터 아이콘 (IntelliJ IDEA 거터 아이콘)을 클릭합니다.

  4. 이것은 Services 도구 창에서 열리고 실행될 것입니다.

    중간 우선순위를 가진 작업이 JSON 형식으로 표시된 HTTP 파일의 GET 요청

NOTE

경로를 테스트하는 또 다른 방법은 Kotlin Notebook 내에서 khttp 라이브러리를 사용하는 것입니다.

POST 요청 경로 추가

이전 튜토리얼에서는 HTML 폼을 통해 작업을 생성했습니다. 그러나 이제 RESTful 서비스를 구축하고 있으므로 더 이상 그럴 필요가 없습니다. 대신, 대부분의 작업을 수행할 kotlinx.serialization 프레임워크를 사용할 것입니다.

  1. src/main/kotlin/com/example 안에 있는 Routing.kt 파일을 엽니다.

  2. Application.configureRouting() 함수에 새 POST 경로를 다음과 같이 추가합니다.

    kotlin

    다음 새 임포트를 추가합니다.

    kotlin

    POST 요청이 /tasks로 전송될 때 kotlinx.serialization 프레임워크는 요청 본문을 Task 객체로 변환하는 데 사용됩니다. 이 작업이 성공하면 태스크가 리포지토리에 추가됩니다. 역직렬화 프로세스가 실패하면 서버는 SerializationException을 처리해야 하며, 작업이 중복인 경우 IllegalStateException을 처리해야 합니다.

  3. 애플리케이션을 재시작합니다.

  4. 이 기능을 Postman에서 테스트하려면 URL http://0.0.0.0:8080/tasks로 새 POST 요청을 생성합니다.

  5. Body 창에 새 작업을 나타내는 다음 JSON 문서를 추가합니다.

    json
    새 작업을 추가하기 위한 Postman의 POST 요청
  6. Send 를 클릭하여 요청을 보냅니다.

  7. http://0.0.0.0:8080/tasks로 GET 요청을 보내 작업을 추가했는지 확인할 수 있습니다.

  8. IntelliJ IDEA Ultimate 내에서 HTTP 요청 파일에 다음을 추가하여 동일한 단계를 수행할 수 있습니다.

    http

삭제 지원 추가

서비스에 기본 작업을 거의 다 추가했습니다. 이들은 종종 CRUD 작업(Create, Read, Update, Delete의 약자)으로 요약됩니다. 이제 삭제 작업을 구현할 것입니다.

  1. TaskRepository.kt 파일에 TaskRepository 객체 내부에 이름으로 작업을 제거하는 다음 메서드를 추가합니다.

    kotlin
  2. Routing.kt 파일을 열고 routing() 함수에 DELETE 요청을 처리하는 엔드포인트를 추가합니다.

    kotlin
  3. 애플리케이션을 재시작합니다.

  4. HTTP 요청 파일에 다음 DELETE 요청을 추가합니다.

    http
  5. IntelliJ IDE 내에서 DELETE 요청을 보내려면 옆에 있는 거터 아이콘 (IntelliJ IDEA 거터 아이콘)을 클릭합니다.

  6. Services 도구 창에서 응답을 확인할 수 있습니다.

    HTTP 요청 파일의 DELETE 요청

Ktor 클라이언트로 단위 테스트 생성

지금까지 애플리케이션을 수동으로 테스트했지만, 이미 알다시피 이 방법은 시간이 많이 걸리고 확장성이 없습니다. 대신 내장된 client 객체를 사용하여 JSON을 가져오고 역직렬화하는

JUnit 테스트
특수 테스트 엔진을 사용하여 서버 애플리케이션을 테스트하는 방법을 알아보세요.
를 구현할 수 있습니다.

  1. src/test/kotlin/com/example 내의 ApplicationTest.kt 파일을 엽니다.

  2. ApplicationTest.kt 파일의 내용을 다음으로 바꿉니다.

    kotlin

    서버에서와 동일하게 플러그인ContentNegotiationkotlinx.serialization 플러그인을 설치해야 합니다.

  3. gradle/libs.versions.toml에 있는 버전 카탈로그에 다음 의존성을 추가합니다.

    yaml
  4. 새 의존성을 build.gradle.kts 파일에 추가합니다.

    kotlin

JsonPath를 사용한 단위 테스트 생성

Ktor 클라이언트 또는 유사한 라이브러리를 사용하여 서비스를 테스트하는 것은 편리하지만, 품질 보증(QA) 관점에서 단점이 있습니다. JSON을 직접 처리하지 않는 서버는 JSON 구조에 대한 가정을 확신할 수 없습니다.

예를 들어, 다음과 같은 가정입니다.

  • 값이 실제로는 object인데 array에 저장되는 경우.
  • 속성이 실제로는 string인데 number로 저장되는 경우.
  • 멤버가 선언 순서대로 직렬화되지 않는 경우.

서비스가 여러 클라이언트에서 사용될 예정이라면, JSON 구조에 대한 확신을 갖는 것이 중요합니다. 이를 위해 Ktor 클라이언트를 사용하여 서버에서 텍스트를 검색한 다음 JSONPath 라이브러리를 사용하여 이 콘텐츠를 분석합니다.

  1. build.gradle.kts 파일의 dependencies 블록에 JSONPath 라이브러리를 추가합니다.

    kotlin
  2. src/test/kotlin/com/example 폴더로 이동하여 새 ApplicationJsonPathTest.kt 파일을 생성합니다.

  3. ApplicationJsonPathTest.kt 파일을 열고 다음 내용을 추가합니다.

    kotlin

    JsonPath 쿼리는 다음과 같이 작동합니다.

    • $[*].name은 "문서를 배열로 처리하고 각 항목의 이름 속성 값을 반환하라"는 의미입니다.
    • $[?(@.priority == '$priority')].name은 "배열에서 주어진 값과 우선순위가 같은 모든 항목의 이름 속성 값을 반환하라"는 의미입니다.

    이러한 쿼리를 사용하여 반환된 JSON에 대한 이해를 확인할 수 있습니다. 코드를 리팩터링하고 서비스를 재배포할 때, 현재 프레임워크와의 역직렬화를 방해하지 않더라도 직렬화의 모든 변경 사항이 식별됩니다. 이를 통해 공개적으로 사용 가능한 API를 확신을 가지고 다시 게시할 수 있습니다.

다음 단계

축하합니다! 이제 작업 관리자 애플리케이션을 위한 RESTful API 서비스 생성을 완료했으며 Ktor 클라이언트 및 JsonPath를 사용한 단위 테스트의 모든 세부 사항을 학습했습니다.

다음 튜토리얼
Kotlin과 Ktor 및 Thymeleaf 템플릿으로 웹사이트를 구축하는 방법을 알아보세요.
로 이동하여 API 서비스를 재사용하여 웹 애플리케이션을 구축하는 방법을 알아보세요.