0. 환경
- Spring Boot 2.7.16 (Gradle)
- JDK 11(Java 11)
- IntelliJ
- Postman
1. Spring Boot 프로젝트 생성
https://start.spring.io 에 접속해 필요한 환경에 맞게 Spring Boot 프로젝트를 정의해 다운로드합니다.
2. Project 열기(IntelliJ)
- 압축을 풀어 둔 Spring Boot Project를 IntelliJ로 열어줍니다.
- 라이브러리 다운로드가 필요해 시간이 좀 걸립니다.
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.7.16"
id("io.spring.dependency-management") version "1.0.15.RELEASE"
kotlin("jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
kotlin("plugin.jpa") version "1.6.21"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java {
sourceCompatibility = JavaVersion.VERSION_11
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
runtimeOnly("com.h2database:h2")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs += "-Xjsr305=strict"
jvmTarget = "11"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
3. IntelliJ 설정
- 프로젝트 실행 전 성능 등을 위해 간단한 IntelliJ 설정을 합니다.
프로젝트 자바 버전 확인
- 프로젝트가 원하는 자바 버전으로 잘 설정되어 있는지 확인해 봅니다. (Java 11)
- File > Project Structure > Project > Project SDK
- 버전이 맞지 않다면 변경해줍니다.
자바 직접 실행 설정
- 빠른 실행 속도를 위해 자바로 직접 실행하도록 설정을 변경합니다.
- Preferences > Build, Execution, Deployment > Build Tools > Gradle
4. Kopring(Kotlin + Spring) Boot Project 실행
- src > main > kotlin > com.example.kopring > KopringApplication.kt 파일을 클릭 후 실행
5. 간단한 API 예시
- To Do List API를 개발한다고 가정한다.
- 조회, 등록 API 개발
1. 엔티티
- 데이터 클래스를 쉽게 다룰 수 있도록 Kassava 의존성 추가
- Kotlin과 Spring Data JPA를 함께 사용할 때 편의를 위해 no-arg, allopen 플러그인 추가
- Kotlin은 기본 생성자를 자동으로 생성하지 않음
- noArg 블록을 사용하여 JPA가 필요로 하는 엔터티 클래스의 요구 사항을 충족시키기 위해 기본 생성자를 생성합니다.
- allOpen 블록은 클래스에 JPA 관련 어노테이션을 추가하여 JPA에서 클래스를 올바르게 인식하도록 도와줍니다.
- Kotlin으로 작성된 프로젝트에서 어노테이션 프로세싱(Annotation Processing)을 사용하기 위해 Kotlin Annotation Processing Tool (KAPT) 플러그인 활성화
- 멀티 모듈 프로젝트를 위한 serialization 플러그인 추가(생략해도 됨)
- ToDo 엔티티 작성
[build.gradle.kts]
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.7.16"
id("io.spring.dependency-management") version "1.0.15.RELEASE"
kotlin("jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
kotlin("plugin.jpa") version "1.6.21"
kotlin("plugin.allopen") version "1.5.21" // allopen 추가
kotlin("plugin.noarg") version "1.5.21" // noarg 추가
kotlin("kapt") version "1.5.21" // Kotlin Annotation Processing Tool
kotlin("plugin.serialization") version "1.5.21" // serialization (For Multi Module Project)
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java {
sourceCompatibility = JavaVersion.VERSION_11
}
repositories {
maven("https://plugins.gradle.org/m2/") // kassava 다운로드를 위해 추가
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("au.com.console:kassava:2.1.0") // kassava 추가
runtimeOnly("com.h2database:h2")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs += "-Xjsr305=strict"
jvmTarget = "11"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
allOpen {
annotation("javax.persistence.Entity")
annotation("javax.persistence.MappedSuperclass")
annotation("javax.persistence.Embeddable")
}
noArg {
annotation("javax.persistence.Entity")
annotation("javax.persistence.MappedSuperclass")
annotation("javax.persistence.Embeddable")
}
[Todo Entity]
package com.example.kopring.entity
import au.com.console.kassava.kotlinEquals
import au.com.console.kassava.kotlinHashCode
import au.com.console.kassava.kotlinToString
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
@Entity
class Todo (
var title: String,
var completed: Boolean
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null
companion object {
private val properties = arrayOf(
Todo::title,
Todo::completed
)
}
override fun equals(other: Any?): Boolean {
return kotlinEquals(other = other, properties = properties)
}
override fun hashCode(): Int {
return kotlinHashCode(properties = properties)
}
override fun toString(): String {
return kotlinToString(properties = properties)
}
}
2. Repository (JPA)
package com.example.kopring.repository
import com.example.kopring.entity.Todo
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface TodoRepository : JpaRepository<Todo, Long>
3. Service
package com.example.kopring.service
import com.example.kopring.entity.Todo
import com.example.kopring.repository.TodoRepository
import org.springframework.stereotype.Service
@Service
class TodoService(
private val todoRepository: TodoRepository
) {
fun getAllTodos(): Iterable<Todo> {
return todoRepository.findAll()
}
fun addTodo(title: String): Todo {
val todo = Todo(title, false)
return todoRepository.save(todo)
}
}
4. 테스트 코드 작성
- 테스트 코드 작성을 위해 mockk 의존성 추가
[build.gradle.kts]
testImplementation("io.mockk:mockk:1.13.4") // test를 위해 mockk 추가
[TodoServiceTest]
package com.example.kopring.service
import com.example.kopring.entity.Todo
import com.example.kopring.repository.TodoRepository
import io.mockk.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
internal class TodoServiceTest {
private lateinit var todoService: TodoService
private lateinit var todoRepository: TodoRepository
@BeforeEach
fun setUp() {
todoRepository = mockk()
todoService = TodoService(todoRepository)
}
@Test
fun getAllTodos() {
val mockTodos = listOf(Todo( "TO DO API 개발시작", false), Todo( "TO DO API 개발완료", true))
every { todoRepository.findAll() } returns mockTodos
val result = todoService.getAllTodos()
assertEquals(mockTodos, result.toList())
}
@Test
fun addTodo() {
val title = "TO DO API 배포"
val mockTodo = Todo(title, false)
every { todoRepository.save(any()) } returns mockTodo
val result = todoService.addTodo(title)
assertEquals(mockTodo, result)
}
}
5. Controller
package com.example.tdd.controller
import com.example.tdd.entity.Todo
import com.example.tdd.service.TodoService
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/todos")
class TodoController(
private val todoService: TodoService
)
{
@GetMapping("/")
fun getAllTodos(): Iterable<Todo> {
return todoService.getAllTodos()
}
@PostMapping("/")
fun addTodo(@RequestBody title: String): Todo {
return todoService.addTodo(title)
}
}
6. API Test (Post Man )
프로젝트 실행
To Do 등록 API
To Do 조회 API
'Backend > Spring' 카테고리의 다른 글
[Spring Boot] 스프링 스케줄 작업 적용하기(@EnableScheduling, @Scheduled) (0) | 2022.02.02 |
---|---|
[Spring Boot] Filter 를 이용하여 Response Body 핸들링 (HttpServletResponseWrapper) (0) | 2022.01.27 |
[Spring Boot] JWT (JSON Web Token) 토큰 기반 인증 (2) | 2022.01.02 |
[Spring Boot] 스프링 부트 Log4J2 취약점 조치 (CVE-2021-44832) (0) | 2021.12.29 |
[Spring Boot] 스프링 부트 Log4J2 취약점 조치 (CVE-2021-45105) (0) | 2021.12.18 |