Kotlin으로 Spring Boot 시작해보기-1

문지은·2021년 9월 13일
0

kotlin으로 spring boot 서버를 간단하게 만들어보자.

kotlin 공식 홈페이지의 spring boot tutorial을 참고했다.

프로젝트 만들기

인텔리제이에서 코틀린으로 프로젝트를 생성한다.


dependencies를 추가한다.
Mustache는 프론트를 위한 dependency. API에서는 필요 없다.
이제 프로젝트가 어떻게 생성되었는지 살펴보자!

코틀린 프로젝트 폴더 구조


전체적으로 자바 스프링 부트 파일과 폴더 구조는 비슷한 듯 하다.
build.gradle.kts가 build.gradle과 같은 역할

Controller 작성

package com.example.kotlinserver

import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.ui.set
import org.springframework.web.bind.annotation.GetMapping

@Controller
class Controller {

    @GetMapping("/")
    fun blog(model: Model): String{
        model["title"] = "Blog"
        return "blog"
    }

}

String인 "blog"를 반환하는 blog 함수 실행.
여기서 model을 인수로 받는다. 아래는 model의 title에 "Blog"를 set 하는 코드의 구현부이다.

Junit5로 테스트해보기

package com.example.kotlinserver

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.junit.jupiter.MockitoExtension
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get

@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(SpringExtension::class, MockitoExtension::class)
class IntegrationTests {

    @Autowired
    private lateinit var mockMvc: MockMvc

    @Test
    fun `test for test`() {
        assertEquals(4+1, 5)
    }

    @Test
    fun `Assert status code and content`() {
        mockMvc.get("/").andExpect {
            status { isOk() }
            content { "blog" }
        }
    }

    @Test
     fun `not found error page`() {
        mockMvc.get("/notFoundPage").andExpect {
            status { isNotFound() }
        }
    }
}

처음에 intelliJ의 자동생성 기능을 이용해서 테스트를 생성했더니 internal fun으로 생성되서 테스트로 인식을 못하는 문제가 있어서 internal을 제거해줬더니 해결되었다.

대체 왜 intertnal을 붙여서 생성하는 걸까 궁금해서 구글링해서 아래와 같은 글을 찾을 수 있었다.

https://stackoverflow.com/questions/59539044/should-kotlin-test-classes-be-internal

요약하면 java에서 테스트를 private class로 생성하는 것과 같은 원리라고 한다. 같은 모듈의 다른 클래스에서 테스트를 재사용할 수 있게 해준다고 한다. (java에서 테스트 자동 생성하면 private 안 붙이던데,,)

    @BeforeEach
    internal fun setUp() {
        println("Setup")
    }

    @AfterEach
    internal fun tearDown() {
        println("TearDown")
    }

위의 코드처럼 set up과 tear down 코드를 설정할 수 있다.

Kotlin에서는 util 대신에 extensions!

package com.example.kotlinserver.extensions

import java.time.LocalDateTime
import java.time.format.DateTimeFormatterBuilder
import java.time.temporal.ChronoField
import java.util.*

fun LocalDateTime.format() = this.format(englishDateFormatter)

private val daysLookup = (1..31).associate { it.toLong() to getOrdinal(it)}

private val englishDateFormatter = DateTimeFormatterBuilder()
    .appendPattern("yyyy-MM-dd")
    .appendLiteral(" ")
    .appendText(ChronoField.DAY_OF_MONTH, daysLookup)
    .appendLiteral(" ")
    .appendPattern("yyyy")
    .toFormatter(Locale.ENGLISH)

private fun getOrdinal(n: Int) = when {
    n in 11..13 -> "${n}th"
    n % 10 == 1 -> "${n}st"
    n % 10 == 2 -> "${n}nd"
    n % 10 == 3 -> "${n}rd"
    else -> "${n}th"
}

fun String.toSlug() = lowercase(Locale.getDefault())
    .replace("\n", " ")
    .replace("[^a-z\\d\\s]".toRegex(), " ")
    .split(" ")
    .joinToString("-")
    .replace("-+".toRegex(), "-")

코틀린에서는 util 대신에 extension을 쓰는 게 일반적이다. 자바에는 보통 util 클래스를 만들고 이를 static으로 선언해서 사용하지만 코틀린에는 static이 없다!
extension에 대한 설명은 코틀린 공홈에서 가져와 봤다.

Kotlin provides the ability to extend a class with new functionality without having to inherit from the class or use design patterns such as Decorator. This is done via special declarations called extensions.

For example, you can write new functions for a class from a third-party library that you can't modify. Such functions can be called in the usual way, as if they were methods of the original class. This mechanism is called an extension function. There are also extension properties that let you define new properties for existing classes.

해석하면 코틀린은 클래스를 상속할 필요없이 클래스에 새로운 함수를 extend할 수 있고 데코레이터같은 디자인 패턴을 사용 가능하다!
그래서 수정할 수 없는 서드 파티 라이브러리도 수정이 가능하다는 이야기였다. 코틀린을 왜 쓰는지 점점 알 거 같다..

여기서 새로운 문법이 많이 보여서 알아봤다.


사진은 associate의 구현부이다.

associate은 iterable한 오브젝트를 key와 value를 가진 Map으로 변환해준다. 위의 코드에서는 (1..31)을 Map으로 변환해주었다. 그리고 해당 Map의 it를 통해서 순차적으로 getOrdinal을 적용했다.

profile
백엔드 개발자입니다.

2개의 댓글

comment-user-thumbnail
2022년 5월 22일

잘 읽고 갑니다. 너무 유용해요!

답글 달기
comment-user-thumbnail
2023년 2월 28일

검색하다가 우연히 봤는데 잘 보고 갑니다~

답글 달기