Kotlin으로 Spring Boot Web 만들기

Kotlin 언어를 사용하여 간단한 Spring Boot Web 생성

개요

Kotlin 언어를 사용하여 간단한 Spring Boot Web를 만들어 보겠다.

프로젝트 생성

아래와 같이 curl 명령어를 사용하여 Spring Boot 초기 프로젝트를 생성한다.

curl https://start.spring.io/starter.tgz \
-d bootVersion=2.4.11 \
-d dependencies=web \
-d baseDir=spring-boot-hello-world \
-d groupId=com.devkuma \
-d artifactId=spring-boot-hello-world \
-d packageName=com.devkuma.hello \
-d applicationName=HelloApplication \
-d packaging=jar \
-d language=kotlin \
-d javaVersion=11 \
-d type=gradle-project | tar -xzvf -

위 명령어를 실행하게 되면 Java 11, Spring Boot 버전은 2.4.11로 web 프로젝트가 생성된다.

생성된 프로젝트의 파일 구조는 아래와 같이 구성된다.

.
├── HELP.md
├── build.gradle.kts
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src
    ├── main
    │   ├── kotlin
    │   │   └── com
    │   │       └── devkuma
    │   │           └── hello
    │   │               └── HelloApplication.kt
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        └── kotlin
            └── com
                └── devkuma
                    └── hello
                        └── HelloApplicationTests.kt

생성된 프로젝트 확인

build.gradle.kts

생성된 파일에 빌드 파일의 확장자가 .kts으로 코틀린 스크립트 파일로 된것을 볼 수 있다.
(참고로, Java에서는 빌드 파일의 확장자는 .gradle이고, Go언어로 되어 있다.)

/build.gradle.kts

// ... 생략 ...

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
	testImplementation("org.springframework.boot:spring-boot-starter-test")
}

// ... 생략 ...

파일 내용 중에 의존성 라이브러리를 확인해 보면 Spring 프레임워크(org.springframework.boot), 코틀린용 Jackson(com.fasterxml.jackson.module), 코틀린 라이브러리(org.jetbrains.kotlin)가 추가되어 있는 것을 볼수 있다.

Application Context

/src/main/kotlin/com/devkuma/hello/controller/HelloController.kt

package com.devkuma.hello

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class HelloApplication

fun main(args: Array<String>) {
	runApplication<HelloApplication>(*args)
}

Spring Boot를 실행하기 위한 @SpringBootApplication 어노테이션과 main 함수가 보이는 것을 확인 할 수 있다.
(전반적으로 자바 코드와 비슷해 보인다.)

Controller 생성

Controller 파일을 생성하여 아래와 같이 작성한다.

/src/main/kotlin/com/devkuma/hello/controller/HelloController.kt

package com.devkuma.hello.controller

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class HelloController {

    @GetMapping("/hello")
    fun hello(): String {
        return "hello world"
    }
}

테스트 코드를 생성하고, 실행해 본다.

/src/test/kotlin/com/devkuma/hello/controller/HelloControllerTests.kt

package com.devkuma.hello.controller

import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.ResultActions
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status


@WebMvcTest(HelloController::class)
class HelloControllerTests {

    @Autowired
    lateinit var mockMvc: MockMvc

    @Test
    @DisplayName("Hello")
    fun hello() {
        // when
        val resultActions: ResultActions = mockMvc.perform(get("/hello"))
            .andDo(print())

        // then
        resultActions
            .andExpect(status().is2xxSuccessful)
            .andExpect(content().string("hello world"))
            .andDo(print())
    }
}

실행 결과에 에러가 없는지 확인하다.

실제 동작 여부도 확인을 위해 프로젝트를 실행 후에 아래 curl 명령어로 작동 여부를 확인한다.

% curl localhost:8080/hello
hello world%

Service 생성

Service 파일을 생성하고 아래와 같이 작성한다.

/src/main/kotlin/com/devkuma/hello/service/HelloService.kt

package com.devkuma.hello.service

import org.springframework.stereotype.Service

@Service
class HelloService {

    fun getHello(): String {
        return "hello service"
    }
}

Controller 파일에서 아래와 같이 ‘helloService()‘를 추가한다.

/src/main/kotlin/com/devkuma/hello/controller/HelloController.kt

package com.devkuma.hello.controller

import com.devkuma.hello.service.HelloService
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class HelloController(val helloService: HelloService) {

    // ...중간 생략...

    @GetMapping("/hello-service")
    fun helloService(): String {
        return helloService.getHello()
    }
}

아래와 같이 테스트 코드 helloService()를 추가하고, 실행해 본다.

/src/test/kotlin/com/devkuma/hello/controller/HelloControllerTests.kt

package com.devkuma.hello.controller

import com.devkuma.hello.service.HelloService
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.ResultActions
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status


@WebMvcTest(HelloController::class)
class HelloControllerTests {

    @Autowired
    lateinit var mockMvc: MockMvc
    @MockBean
    lateinit var helloService: HelloService

    // ...중간 생략...

    @Test
    @DisplayName("Hello Service")
    fun helloService() {
        // given
        given(helloService.getHello()).willReturn("hello service");

        // when
        val resultActions: ResultActions = mockMvc.perform(get("/hello-service"))
            .andDo(print())

        // then
        resultActions
            .andExpect(status().is2xxSuccessful)
            .andExpect(content().string("hello service"))
            .andDo(print())
    }
}

실제 동작 여부도 확인을 위해 프로젝트를 실행 후에 아래 curl 명령어로 작동 여부를 확인한다.

% curl localhost:8080/hello-service
hello service%

DTO 생성

DTO 파일을 생성하고 아래와 같이 작성한다.

/src/main/kotlin/com/devkuma/hello/dto/HelloDto.kt

package com.devkuma.hello.dto

class HelloDto(val greeting: String)

Controller 파일에서 아래와 같이 ‘helloDto()‘를 추가한다.

/src/main/kotlin/com/devkuma/hello/controller/HelloController.kt

@RestController
class HelloController(val helloService: HelloService) {

    // ...중간 생략...

    @GetMapping("/hello-dto")
    fun helloDto(): HelloDto {
        return HelloDto("hello dto")
    }
}

아래와 같이 테스트 코드 helloDto()를 추가하고, 실행해 본다.

/src/test/kotlin/com/devkuma/hello/controller/HelloControllerTests.kt

package com.devkuma.hello.controller

import com.devkuma.hello.service.HelloService
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.ResultActions
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*


@WebMvcTest(HelloController::class)
class HelloControllerTests {

    // ...중간 생략...

    @Test
    @DisplayName("Hello DTO")
    fun helloDto() {

        // when
        val resultActions: ResultActions = mockMvc.perform(get("/hello-dto"))
            .andDo(print())

        // then
        resultActions
            .andExpect(status().is2xxSuccessful)
            .andExpect(jsonPath("greeting").value("hello dto"))
            .andDo(print())
    }
}

실제 동작 여부도 확인을 위해 프로젝트를 실행 후에 아래 curl 명령어로 작동 여부를 확인한다.

% curl localhost:8080/hello-dto
{"greeting":"hello dto"}%

그밖에




최종 수정 : 2021-09-26