[Kotlin] 웹 서버 만들기 with Spring Boot

Building web applications with Spring Boot and Kotlin

kotlin 으로 간단한 웹서버 구현해보고싶어서 kotlin spring boot 기본 예제를 따라해보려했는데, 온통 영어에다가 장애물이 너무 많았어서 (이건 단순히 영어 해석이 안돼서였다…) 까먹을까봐 정리하는 것이다.


참고 : https://spring.io/guides/tutorials/spring-boot-kotlin/


0. 준비물

나는 기존에 IntelliJ Community 버전으로 설치되어있어서 이부분은…

없는 경우 설치하면 될 듯 하다.


1. 프로젝트 생성하기

  1. Spring initializr website : 설정을 입력하고 generate 하면 프로젝트 파일을 생성해준다.
  2. Command line : UNIX 계열은 이 방법으로 할 수 있는 것 같다. 안해봐서 잘 모르겠다
  3. Using IntelliJ IDEA : Ultimate edition (유료버전)만 가능해보인다. 나는 무료버전이기 때문에 안될 것 같다.

총 3가지 방법이 있지만, 나는 첫번째 방법을 이용했다.


먼저 https://start.spring.io/ 에 접속해서 다음과 같이 입력한다.

initializr

고급 설정은 하단의 Switch to the full version 을 누르면 볼 수 있다.

그러면 프로젝트 zip 파일 다운로드가 시작된다.

다운로드한 프로젝트 파일 압축을 해제하고, IntelliJ 에서 열려고하면 import 를 하라고 한다.

나같은 경우엔 자동으로 폴더도 생성하고.. 체크 두개를 했던것같은데 이미 했으니까 pass.

프로젝트를 import 하면 build, indexing 작업이 시작된다.

★ 중요 : build.gradle 파일 열어서 위에 뜬거 해줘야되는데 기억이 안난다. sync 비스무리 한 거였는데…. 암튼 이걸 하면 다시한번 download, indexing이 시작된다.

나는 처음에 안하고 무작정 시작했더니

Error:Kotlin: [Internal Error] java.lang.IllegalStateException: The provided plugin org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar is not compatible with this version of compiler

라는 어마무시한 오류가 뜨면서 빌드조차 되지 않았었다.

[추가] 위 오류를 해결할 수 있는 방법은 아래와 같다. 프로젝트 다시 만들어봤더니 아래와 같이 설정 해야한다.

Project bytecode version 을 8로 바꿔야 한다!!!!!!!

(추가적으로 Kotlin compiler에서 target JVM 을 1.8로 설정했던 것 같은데 이게 영향을 미치는진 모르겠다.)


2. 생성된 프로젝트 이해하기

이 부분이 다 영어라서 스킵하고 넘어갔는데, 아마 이 부분을 간과해서 삽질을 오래한게 아닌가 싶다..ㅠㅠ 사실 자세히 읽어보지 않았기 때문에, 그냥 번역기 돌려서 붙여놔야겠다.

Gradle build

Plugins

명백한 Kotlin Gradle 플러그인 외에도 기본 구성은 클래스 및 메소드를 자동으로 여는 kotlin-spring 플러그인을 선언합니다 (Java와 달리 Kotlin의 기본 한정자는 final입니다). 스프링 주석으로 주석을 달거나 메타 주석을 추가합니다. 예를 들어 CGLIB 프록시에 필요한 열린 한정자를 추가하지 않고도 @Configuration 또는 @Transactional Bean을 만들 수있는 경우 유용합니다.

아래 코드는 내 프로젝트 기준으로 복붙한 build.gradle 파일의 일부이다. (앞으로 내 플젝 기준이다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
buildscript {
ext {
kotlinVersion = '1.2.30'
springBootVersion = '2.1.0.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-noarg:${kotlinVersion}")
}
}

apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'kotlin-jpa'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'


Compiler options

Kotlin의 핵심 기능 중 하나는 런타임시 유명한 NullPointerException에 부딪히지 않고 컴파일시 null 값을 깔끔하게 처리하는 null-safety입니다. 이로 인해 응용 프로그램은 null 허용 선언을 통해 안전하고 Optional과 같은 래퍼 비용을 지불하지 않고 “값 또는 값이 없음”의미를 표현합니다. Kotlin은 nullable 값을 가진 함수 생성자를 사용할 수 있습니다. Kotlin null-safety에 대한 포괄적인 가이드를 확인하십시오.

자바는 type-system에서 null-safety를 허용하지 않지만 Spring Framework는 org.springframework.lang 패키지에 선언된 도구 친화적인 주석을 통해 전체 Spring Framework API의 null-safety를 제공합니다. 기본적으로 Kotlin에서 사용되는 Java API의 유형은 null 체크가 완화된 플랫폼 유형으로 인식됩니다. JSR 305 annotations + Spring Nullability Annotations에 대한 Kotlin 지원은 컴파일 타임에 null 관련 문제를 처리할 수 있다는 이점을 가지고 Kotlin 개발자에게 전체 Spring Framework API에 대한 null-safety를 제공합니다.

이 기능은 strict 옵션과 함께 -Xjsr305 컴파일러 플래그를 추가하여 활성화 할 수 있습니다.

Kotlin 컴파일러는 Java 8 바이트 코드 (Java 6의 기본값)를 생성하도록 구성되어 있습니다.

build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
sourceCompatibility = 1.8
compileKotlin {
kotlinOptions {
freeCompilerArgs = ["-Xjsr305=strict"]
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
freeCompilerArgs = ["-Xjsr305=strict"]
jvmTarget = "1.8"
}
}


Dependencies

3 Kotlin 특정 라이브러리는 이러한 Spring Boot 웹 애플리케이션에 필요하며 기본적으로 구성됩니다.

  • kotlin-stdlib-jdk8는 Kotlin 표준 라이브러리의 Java 8 변형입니다.
  • kotlin-reflect는 Kotlin 반영 라이브러리 (Spring Framework 5에서 필수)
  • jackson-module-kotlin은 Kotlin 클래스 및 데이터 클래스의 직렬화 / 비직렬화에 대한 지원을 추가합니다 (단일 생성자 클래스는 자동으로 사용할 수 있고, 보조 생성자 또는 정적 팩토리가있는 클래스도 지원됩니다)

build.gradle

1
2
3
4
5
6
7
8
9
10
dependencies {
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
implementation('org.springframework.boot:spring-boot-starter-mustache')
implementation('org.springframework.boot:spring-boot-starter-web')
implementation('com.fasterxml.jackson.module:jackson-module-kotlin')
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-reflect")
runtimeOnly('com.h2database:h2')
testImplementation('org.springframework.boot:spring-boot-starter-test')
}

Spring Boot Gradle 플러그인은 Kotlin Gradle 플러그인에 선언 된 Kotlin 버전을 자동으로 사용합니다.


Application

src/main/kotlin/blog/BlogApplication.kt

1
2
3
4
5
6
7
8
9
10
11
package blog

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

@SpringBootApplication
class BlogApplication

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

Java와 달리 세미콜론이 없다는 것을 알 수 있습니다. 빈 클래스에 대괄호가 없으며 (@Bean 주석을 통해 빈을 선언해야하는 경우 추가 할 수 있습니다) runApplication top level 함수를 사용할 수 있습니다. runApplication <BlogApplication> (* args)SpringApplication.run (BlogApplication :: class.java, * args)에 대한 Kotlin의 관용적인 대안이며 다음 구문을 사용하여 응용 프로그램을 사용자 정의하는 데 사용할 수 있습니다. (아래와 같이 코드를 추가해야 서버 프로그램이 실행된다.)

1
2
3
4
5
fun main(args: Array<String>) {
runApplication<BlogApplication>(*args) {
setBannerMode(Banner.Mode.OFF)
}
}


3. 첫번째 Kotlin controller 작성하기

간단한 웹페이지를 표시하는 컨트롤러를 만들어보자!

HtmlController 라는 컨트롤러 클래스를 생성하면 된다.

src/main/kotlin/blog/HtmlController.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package blog

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

@Controller
class HtmlController {
@GetMapping("/")
fun blog(model: Model): String {
model["title"] = "안녕, 코틀린! onedelay"
return "blog"
}
}

Kotlin extension을 사용하여 기존의 Spring 타입에 Kotlin 함수나 연산자를 추가할 수 있다. model.addAttribute("title", "Blog") 대신 model["title"] = "Blog"를 작성할 수 있도록 org.springframework.ui.set 확장 함수를 가져온다.


다음으로 관련 Mustache 템플릿을 만들어야한다.


그러나 .mustache 확장자를 인식할 수 없는 문제점이 발생하는데, 추가적으로 plugin을 설치하면 된다.

File -> Settings -> Plugins -> mustache 검색 후 나오는거 설치하고 재시작 하면 끝! (처음에 검색결과 없다고 뜨는데, Search in repositories 누르면 나온다.)

그리고 Settings -> File Types 가서 아래와 같이 추가해주면 된다. (귀찮아서 공홈 캡쳐)


src/main/resources/templates/header.mustache

1
2
3
4
5
<html>
<head>
<title>{{title}}</title>
</head>
<body>

src/main/resources/templates/footer.mustache

1
2
</body>
</html>

src/main/resources/templates/blog.mustache

1
2
3
4
5
{{> header}}

<h1>{{title}}</h1>

{{> footer}}


BlogApplication.kt의 main 함수를 실행해서 웹 응용 프로그램을 시작하고 http://localhost:8080/ 를 띄우면 작성한 헤드 라인이있는 웹 페이지가 나타난다.


이제 더 살을 붙여보고 클라우드 서버에 올리면 되는건가?!

끝!

Share