[Kotlin] 15. 서버 개발 - Spring Boot with Kotlin
Kotlin으로 Spring Boot 서버를 개발하는 방법을 배웁니다.
Spring Boot + Kotlin
프로젝트 생성
Spring Initializr 에서 생성:
- Language: Kotlin
- Build: Gradle - Kotlin
- Dependencies: Spring Web, Spring Data JPA, H2 Database
build.gradle.kts
plugins {
kotlin("jvm") version "1.9.0"
kotlin("plugin.spring") version "1.9.0"
kotlin("plugin.jpa") version "1.9.0"
id("org.springframework.boot") version "3.2.0"
id("io.spring.dependency-management") version "1.1.0"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
runtimeOnly("com.h2database:h2")
}
REST API 만들기
1. Entity
import jakarta.persistence.*
@Entity
@Table(name = "todos")
data class Todo(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
@Column(nullable = false)
var title: String,
var description: String? = null,
var isDone: Boolean = false,
val createdAt: java.time.LocalDateTime = java.time.LocalDateTime.now()
)
2. Repository
import org.springframework.data.jpa.repository.JpaRepository
interface TodoRepository : JpaRepository<Todo, Long> {
fun findByIsDone(isDone: Boolean): List<Todo>
fun findByTitleContaining(keyword: String): List<Todo>
}
3. Service
import org.springframework.stereotype.Service
@Service
class TodoService(private val repository: TodoRepository) {
fun getAll(): List<Todo> = repository.findAll()
fun getById(id: Long): Todo =
repository.findById(id).orElseThrow {
NoSuchElementException("Todo not found: $id")
}
fun create(title: String, description: String?): Todo {
val todo = Todo(title = title, description = description)
return repository.save(todo)
}
fun update(id: Long, title: String?, description: String?, isDone: Boolean?): Todo {
val todo = getById(id)
title?.let { todo.title = it }
description?.let { todo.description = it }
isDone?.let { todo.isDone = it }
return repository.save(todo)
}
fun delete(id: Long) {
repository.deleteById(id)
}
fun search(keyword: String): List<Todo> =
repository.findByTitleContaining(keyword)
}
4. Controller
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/api/todos")
class TodoController(private val service: TodoService) {
// GET /api/todos
@GetMapping
fun getAll(): List<Todo> = service.getAll()
// GET /api/todos/1
@GetMapping("/{id}")
fun getById(@PathVariable id: Long): Todo = service.getById(id)
// POST /api/todos
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun create(@RequestBody request: CreateTodoRequest): Todo =
service.create(request.title, request.description)
// PUT /api/todos/1
@PutMapping("/{id}")
fun update(@PathVariable id: Long, @RequestBody request: UpdateTodoRequest): Todo =
service.update(id, request.title, request.description, request.isDone)
// DELETE /api/todos/1
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun delete(@PathVariable id: Long) = service.delete(id)
// GET /api/todos/search?keyword=공부
@GetMapping("/search")
fun search(@RequestParam keyword: String): List<Todo> =
service.search(keyword)
}
// 요청 DTO
data class CreateTodoRequest(
val title: String,
val description: String? = null
)
data class UpdateTodoRequest(
val title: String? = null,
val description: String? = null,
val isDone: Boolean? = null
)
예외 처리
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(NoSuchElementException::class)
@ResponseStatus(HttpStatus.NOT_FOUND)
fun handleNotFound(e: NoSuchElementException): ErrorResponse {
return ErrorResponse(
status = 404,
message = e.message ?: "리소스를 찾을 수 없습니다"
)
}
@ExceptionHandler(IllegalArgumentException::class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
fun handleBadRequest(e: IllegalArgumentException): ErrorResponse {
return ErrorResponse(
status = 400,
message = e.message ?: "잘못된 요청입니다"
)
}
}
data class ErrorResponse(val status: Int, val message: String)
설정 파일
# src/main/resources/application.yml
server:
port: 8080
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
h2:
console:
enabled: true
path: /h2-console
실행 및 테스트
# 실행
./gradlew bootRun
# API 테스트 (curl)
# 생성
curl -X POST http://localhost:8080/api/todos \
-H "Content-Type: application/json" \
-d '{"title":"Kotlin 공부","description":"코루틴 학습"}'
# 조회
curl http://localhost:8080/api/todos
# 수정
curl -X PUT http://localhost:8080/api/todos/1 \
-H "Content-Type: application/json" \
-d '{"isDone":true}'
# 삭제
curl -X DELETE http://localhost:8080/api/todos/1
Kotlin + Spring 장점
| 기능 | 설명 |
|---|---|
| data class | DTO/Entity 간결하게 정의 |
| null safety | NPE 방지 |
| 확장 함수 | 유틸리티 깔끔하게 추가 |
| 코루틴 | WebFlux와 자연스러운 통합 |
| 기본값 매개변수 | 빌더 패턴 불필요 |
- [Kotlin] 18. 빌드와 배포 - Gradle, APK, JAR
- [Kotlin] 17. 실전 팁 - 자주 쓰는 패턴과 관용구
- [Kotlin] 16. 테스트 - JUnit, 단위 테스트
- [Kotlin] 15. 서버 개발 - Spring Boot with Kotlin
- [Kotlin] 14. 로컬 저장소 - Room, DataStore
- [Kotlin] 13. 네트워크 통신 - Retrofit
- [Kotlin] 12. 상태관리 - ViewModel, State
- [Kotlin] 11. 화면 이동 - Navigation
- [Kotlin] 10. Compose 레이아웃과 리스트
- [Kotlin] 09. Android 개발 기초 - 프로젝트 생성
- [Kotlin] 08. 코루틴 - 비동기 프로그래밍
- [Kotlin] 07. Null 안전성과 예외 처리
- [Kotlin] 06. 컬렉션 - List, Map, Set
- [Kotlin] 05. 클래스와 객체지향 프로그래밍
- [Kotlin] 04. 함수 - 선언, 매개변수, 람다
- [Kotlin] 03. 제어문 - 조건문, 반복문
- [Kotlin] 02. 변수와 데이터 타입
- [Kotlin] 01. Kotlin 소개 및 개발환경 설치