Kotlin Spring Boot 입문 | 30분 만에 REST API 만들기 [초보자용]
이 글의 핵심
Kotlin Spring Boot 완전 초보자를 위한 입문 튜토리얼. Java보다 간결한 문법으로 REST API 만들기. 설치부터 배포까지 30분이면 충분합니다.
들어가며: Kotlin으로 더 쉽게 시작하기
“요즘 채용 공고 보므로 Kotlin + Spring Boot를 많이 쓰던데요?”
맞습니다. Kotlin은 최근 백엔드 개발에서 빠르게 성장하고 있습니다:
- Coupang, Kakao, Toss, 당근마켓 등 주요 기업 사용
- Java 대비 30-40% 적은 코드량
- Null 안전성으로 NullPointerException 방지
- Google이 Android 공식 언어로 지정
이 튜토리얼은 완전 초보자를 위한 가이드입니다. 설치 → 프로젝트 생성 → REST API → 실행까지 30분이면 충분합니다.
Java vs Kotlin 코드 비교
Java (장황함):
public class User {
private Long id;
private String name;
public User(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
Kotlin (간결함):
data class User(
val id: Long,
var name: String
)
똑같은 기능이지만 Kotlin은 2줄!
이 튜토리얼에서 만들 것
간단한 북마크 관리 REST API:
GET /api/bookmarks- 북마크 목록 조회POST /api/bookmarks- 북마크 추가PUT /api/bookmarks/{id}- 북마크 수정DELETE /api/bookmarks/{id}- 북마크 삭제
사전 요구사항
- Java 17 이상 설치 (Kotlin은 JVM에서 실행)
- IDE: IntelliJ IDEA Community (무료, Kotlin 기본 지원)
1단계: 환경 설정 (3분)
Java 설치 확인
# 터미널에서 확인
java -version
# 출력 예시:
# openjdk version "17.0.2"
Java가 없다면:
- Windows: https://adoptium.net/ → JDK 17 다운로드
- Mac:
brew install openjdk@17 - Linux:
sudo apt install openjdk-17-jdk
IntelliJ IDEA 설치
https://www.jetbrains.com/idea/download/
- Community Edition (무료) 다운로드
- Kotlin 플러그인 자동 포함
2단계: 프로젝트 생성 (5분)
방법 1: Spring Initializr (웹)
- https://start.spring.io/ 접속
- 다음과 같이 설정:
- Project: Gradle - Kotlin
- Language: Kotlin
- Spring Boot: 3.2.x (최신 안정 버전)
- Packaging: Jar
- Java: 17
- Project Metadata:
- Group:
com.example - Artifact:
demo - Name:
demo - Package name:
com.example.demo
- Group:
- Dependencies 추가:
- Spring Web
- Spring Data JPA
- H2 Database
- GENERATE 클릭 → ZIP 다운로드
- 압축 해제 후 IntelliJ로 열기
방법 2: IntelliJ IDEA (권장)
- New Project → Spring Initializr
- 위와 동일하게 설정
- Create
3단계: 첫 REST API 만들기 (10분)
프로젝트 구조
src/main/kotlin/com/example/demo/
├── DemoApplication.kt # 메인
├── controller/
│ └── BookmarkController.kt # REST API
├── model/
│ └── Bookmark.kt # 데이터 모델
├── repository/
│ └── BookmarkRepository.kt # DB 접근
└── service/
└── BookmarkService.kt # 비즈니스 로직
1) 데이터 모델 (Bookmark.kt)
src/main/kotlin/com/example/demo/model/Bookmark.kt:
package com.example.demo.model
import jakarta.persistence.*
@Entity
@Table(name = "bookmarks")
data class Bookmark(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
var title: String,
var url: String,
var description: String? = null
)
Java와 비교:
data class: getter/setter/equals/hashCode 자동 생성val: 불변 (Java의final)var: 가변String?: Null 허용 타입
2) Repository (BookmarkRepository.kt)
src/main/kotlin/com/example/demo/repository/BookmarkRepository.kt:
package com.example.demo.repository
import com.example.demo.model.Bookmark
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface BookmarkRepository : JpaRepository<Bookmark, Long>
한 줄로 끝! Spring Data JPA가 자동으로 구현합니다.
3) Service (BookmarkService.kt)
src/main/kotlin/com/example/demo/service/BookmarkService.kt:
package com.example.demo.service
import com.example.demo.model.Bookmark
import com.example.demo.repository.BookmarkRepository
import org.springframework.stereotype.Service
@Service
class BookmarkService(
private val repository: BookmarkRepository
) {
fun getAllBookmarks(): List<Bookmark> = repository.findAll()
fun getBookmarkById(id: Long): Bookmark? =
repository.findById(id).orElse(null)
fun createBookmark(bookmark: Bookmark): Bookmark =
repository.save(bookmark)
fun updateBookmark(id: Long, bookmarkDetails: Bookmark): Bookmark {
val bookmark = repository.findById(id)
.orElseThrow { RuntimeException("Bookmark not found") }
bookmark.title = bookmarkDetails.title
bookmark.url = bookmarkDetails.url
bookmark.description = bookmarkDetails.description
return repository.save(bookmark)
}
fun deleteBookmark(id: Long) = repository.deleteById(id)
}
Kotlin의 장점:
- 생성자 주입: 파라미터에
val만 붙이면 자동으로 필드 선언 + 초기화 - 표현식 함수:
fun get() = repository.findAll()(중괄호 생략) - Null 안전:
Bookmark?타입으로 명시적 처리
4) Controller (BookmarkController.kt)
src/main/kotlin/com/example/demo/controller/BookmarkController.kt:
package com.example.demo.controller
import com.example.demo.model.Bookmark
import com.example.demo.service.BookmarkService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/api/bookmarks")
class BookmarkController(
private val service: BookmarkService
) {
// GET /api/bookmarks - 전체 조회
@GetMapping
fun getAllBookmarks(): List<Bookmark> = service.getAllBookmarks()
// GET /api/bookmarks/{id} - 단건 조회
@GetMapping("/{id}")
fun getBookmarkById(@PathVariable id: Long): ResponseEntity<Bookmark> {
val bookmark = service.getBookmarkById(id)
return if (bookmark != null) {
ResponseEntity.ok(bookmark)
} else {
ResponseEntity.notFound().build()
}
}
// POST /api/bookmarks - 생성
@PostMapping
fun createBookmark(@RequestBody bookmark: Bookmark): Bookmark =
service.createBookmark(bookmark)
// PUT /api/bookmarks/{id} - 수정
@PutMapping("/{id}")
fun updateBookmark(
@PathVariable id: Long,
@RequestBody bookmarkDetails: Bookmark
): ResponseEntity<Bookmark> {
return try {
val updated = service.updateBookmark(id, bookmarkDetails)
ResponseEntity.ok(updated)
} catch (e: RuntimeException) {
ResponseEntity.notFound().build()
}
}
// DELETE /api/bookmarks/{id} - 삭제
@DeleteMapping("/{id}")
fun deleteBookmark(@PathVariable id: Long): ResponseEntity<Void> {
service.deleteBookmark(id)
return ResponseEntity.ok().build()
}
}
Java 대비 코드량 절반!
5) application.properties 설정
src/main/resources/application.properties:
# 서버 포트
server.port=8080
# H2 데이터베이스 (개발용)
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# JPA 설정
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# H2 콘솔 활성화
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
4단계: 실행 및 테스트 (7분)
서버 실행
# Gradle
./gradlew bootRun
# 또는 IntelliJ에서 DemoApplication.kt 실행 (▶️ 버튼)
출력:
Tomcat started on port 8080
Started DemoApplicationKt in 2.123 seconds
API 테스트 (curl)
# 1. 북마크 추가
curl -X POST http://localhost:8080/api/bookmarks \
-H "Content-Type: application/json" \
-d '{
"title": "Kotlin 공식 문서",
"url": "https://kotlinlang.org",
"description": "Kotlin 배우기"
}'
# 응답:
# {"id":1,"title":"Kotlin 공식 문서","url":"https://kotlinlang.org","description":"Kotlin 배우기"}
# 2. 전체 조회
curl http://localhost:8080/api/bookmarks
# 3. 단건 조회
curl http://localhost:8080/api/bookmarks/1
# 4. 수정
curl -X PUT http://localhost:8080/api/bookmarks/1 \
-H "Content-Type: application/json" \
-d '{
"title": "Kotlin 공식 문서 (수정됨)",
"url": "https://kotlinlang.org/docs",
"description": "깊이 배우기"
}'
# 5. 삭제
curl -X DELETE http://localhost:8080/api/bookmarks/1
브라우저에서 테스트
-
H2 콘솔 접속: http://localhost:8080/h2-console
- JDBC URL:
jdbc:h2:mem:testdb - Username:
sa - Password: (빈칸)
- JDBC URL:
-
SQL 쿼리:
SELECT * FROM bookmarks;
INSERT INTO bookmarks (title, url, description)
VALUES ('Google', 'https://google.com', 'Search engine');
5단계: Kotlin의 핵심 문법 (5분)
1) data class (자동 생성)
// Kotlin: 이것만으로 getter/setter/equals/hashCode/toString 자동 생성
data class User(val id: Long, var name: String)
// Java 동등 코드: 50줄+
2) Null 안전성
// Null 불가 타입
val name: String = "홍길동"
// name = null // 컴파일 에러!
// Null 가능 타입
val description: String? = null
println(description?.length) // Safe call: null이면 null 반환
// Elvis 연산자
val length = description?.length ?: 0 // null이면 0
Java에서는 항상 NullPointerException 위험!
3) 표현식 함수
// 한 줄 함수
fun add(a: Int, b: Int) = a + b
// 조건식도 표현식
fun max(a: Int, b: Int) = if (a > b) a else b
4) 생성자 주입 (간결함)
// Kotlin: 한 줄
class TodoController(private val service: TodoService)
// Java: 여러 줄
public class TodoController {
private final TodoService service;
public TodoController(TodoService service) {
this.service = service;
}
}
5) 스마트 캐스팅
fun process(value: Any) {
if (value is String) {
println(value.length) // 자동으로 String으로 캐스팅!
}
}
실무 팁
자주 하는 실수
1) data class에서 var 남용
// ❌ 나쁜 예: 모두 가변
data class User(var id: Long, var name: String)
// ✅ 좋은 예: ID는 불변
data class User(val id: Long, var name: String)
2) !! 연산자 남용 (Null 강제 언래핑)
// ❌ 위험: NullPointerException 가능
val length = description!!.length
// ✅ 안전: Safe call 또는 Elvis
val length = description?.length ?: 0
3) lateinit 잘못 사용
// ❌ nullable한 변수에는 사용 불가
lateinit var name: String? // 컴파일 에러
// ✅ Non-null 타입에만 사용
lateinit var name: String
디버깅 팁
# application.properties
logging.level.org.springframework=DEBUG
logging.level.com.example=DEBUG
# SQL 로그 상세히
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
IntelliJ 단축키
| 단축키 | 기능 |
|---|---|
Ctrl + Shift + K | Kotlin REPL 실행 |
Ctrl + Alt + L | 코드 포맷팅 |
Alt + Enter | Quick fix |
Ctrl + Space | 자동 완성 |
다음 단계: 실무로 가는 길
이 튜토리얼을 마쳤다면 다음을 학습하세요:
필수 학습 (순서대로)
1) Exception 처리
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException::class)
fun handleNotFound(ex: RuntimeException) =
ResponseEntity.status(404).body(ex.message)
}
2) Validation
data class Bookmark(
@field:NotBlank(message = "제목은 필수입니다")
var title: String,
@field:URL(message = "올바른 URL을 입력하세요")
var url: String
)
@PostMapping
fun create(@Valid @RequestBody bookmark: Bookmark) = ...
3) Kotlin Coroutines (비동기)
@Service
class BookmarkService(private val repository: BookmarkRepository) {
suspend fun getAllBookmarks() = withContext(Dispatchers.IO) {
repository.findAll()
}
}
4) 테스트
@SpringBootTest
class BookmarkControllerTest {
@Test
fun `북마크 생성 테스트`() {
// given, when, then
}
}
5) 프로덕션 DB (PostgreSQL)
// build.gradle.kts
dependencies {
runtimeOnly("org.postgresql:postgresql")
}
# application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=postgres
spring.datasource.password=password
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
학습 로드맵
Kotlin 입문 튜토리얼 (30분)
↓
Kotlin 시리즈 #1-10 (5시간)
↓
Kotlin Coroutines 학습 (2시간)
↓
실전 프로젝트 (1-2주)
↓
포트폴리오 제작
흔한 에러 및 해결
에러 1: unresolved reference: springframework
# build.gradle.kts 확인
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
}
# 의존성 다시 다운로드
./gradlew clean build --refresh-dependencies
에러 2: lateinit property has not been initialized
// ❌ 초기화 전 사용
lateinit var service: BookmarkService
println(service.getAllBookmarks())
// ✅ 생성자 주입 사용
class Controller(private val service: BookmarkService)
에러 3: Type mismatch: inferred type is Long? but Long was expected
// ❌ Nullable을 Non-null에 할당
val id: Long = bookmark.id // bookmark.id는 Long?
// ✅ Null 체크 또는 Elvis
val id: Long = bookmark.id ?: 0L
Java vs Kotlin 실전 비교
엔티티 클래스
Java (34줄):
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
public User() {}
public User(String name, String email) {
this.name = name;
this.email = email;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public boolean equals(Object o) { ... }
@Override
public int hashCode() { ... }
@Override
public String toString() { ... }
}
Kotlin (7줄):
@Entity
@Table(name = "users")
data class User(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
var name: String,
var email: String
)
80% 코드량 감소!
실전 프로젝트 아이디어
이제 배운 내용으로 실전 프로젝트를 만들어 보세요:
초급
- 간단한 북마크 관리 앱
- 메모장 API
- URL 단축 서비스
중급
- JWT 인증이 있는 TODO 앱
- 파일 업로드 서비스
- 실시간 채팅 (Kotlin Coroutines)
고급
- 마이크로서비스 (Spring Cloud)
- Kafka 이벤트 스트리밍
- Redis 캐싱 + PostgreSQL
빠른 레퍼런스
주요 애노테이션
| 애노테이션 | 용도 | Java와 동일 |
|---|---|---|
@RestController | REST API 컨트롤러 | ✅ |
@GetMapping | GET 요청 | ✅ |
@PostMapping | POST 요청 | ✅ |
@RequestBody | JSON → 객체 | ✅ |
@PathVariable | URL 경로 변수 | ✅ |
@Service | 비즈니스 로직 | ✅ |
@Repository | DB 접근 | ✅ |
@Entity | JPA 엔티티 | ✅ |
Kotlin 고유 문법
// Null 안전
val name: String? = null
println(name?.length ?: 0)
// data class
data class User(val id: Long, var name: String)
// 표현식 함수
fun add(a: Int, b: Int) = a + b
// 생성자 주입
class Service(private val repo: Repository)
// when 표현식 (switch 대체)
when (status) {
200 -> "OK"
404 -> "Not Found"
else -> "Error"
}
Spring Boot 내부와 프로덕션에 가까운 메모
서블릿 스택과 임베디드 Tomcat
Spring Boot Web은 기본적으로 서블릿 컨테이너(임베디드 Tomcat 등) 위에서 동작합니다. HTTP 요청은 커넥터 스레드 풀에서 받으며, 디스패처 서블릿이 컨트롤러 매핑으로 넘깁니다. 블로킹 I/O 모델에서는 동시 처리량이 스레드 풀 크기와 직결되므로, 장시간 점유하는 요청이 많으면 스레드 고갈이 납니다. 이때는 WebFlux·비동기 서블릿·외부 워커 큐 같은 아키텍처 변경을 검토합니다.
설정 계층: application.yml과 프로파일
실제 서비스에서는 application-dev.yml, application-prod.yml처럼 프로파일로 DB URL·로그 레벨을 나눕니다. 시크릿은 저장소에 평문으로 두지 않고 환경 변수·시크릿 매니저로 주입합니다.
관측: Actuator와 헬스
운영 환경에서는 spring-boot-starter-actuator로 /health, /metrics를 열으며, Kubernetes라면 liveness/readiness 프로브와 연결합니다. 데이터베이스가 끊긴 상태에서도 프로세스는 살아 있을 수 있으므로 readiness에 DB 검사를 넣는지 팀 규약을 정합니다.
트러블슈팅 빠른 표
| 증상 | 점검 |
|---|---|
Whitelabel Error Page 500 | 서버 로그 스택 트레이스, @ControllerAdvice 없을 때 기본 페이지 |
| 포트 충돌 | server.port, 다른 로컬 프로세스 점유 |
| JPA가 테이블을 안 만듦 | ddl-auto, 엔티티 스캔 패키지, DB 권한 |
| CORS 에러 | @CrossOrigin 또는 API 게이트웨이에서 헤더 — 브라우저만의 증상임을 기억 |
마무리
핵심 정리
- Kotlin은 Java보다 30-40% 코드 감소
- data class로 엔티티 한 줄 정의
- Null 안전성으로 NullPointerException 방지
- Spring Boot의 모든 기능 100% 호환
30분 동안 배운 것
- Kotlin + Spring Boot 프로젝트 생성
- REST API 4가지 (GET, POST, PUT, DELETE)
- data class로 엔티티 정의
- JPA로 데이터베이스 연동
- H2 인메모리 DB 사용
Kotlin을 선택해야 하는 이유
Java 대비 장점:
- ✅ 코드량 30-40% 감소
- ✅ Null 안전성 (컴파일 타임 체크)
- ✅ 간결한 문법 (data class, 표현식)
- ✅ Coroutines (비동기 처리)
- ✅ 100% Java 호환
채용 시장
- Coupang, Kakao, Toss, 당근마켓, 배달의민족
- 스타트업에서 빠르게 도입 중
- Android + 백엔드 동시 개발 가능
축하합니다! 첫 Kotlin Spring Boot API를 만들었습니다. 이제 Kotlin 시리즈 #9 Spring Boot로 넘어가 실무 수준을 익히세요.
관련 글 (내부 링크)
Kotlin과 함께 보면 좋은 백엔드 개발 가이드입니다:
- Spring Boot 입문 튜토리얼 | Java 버전 - Java 버전과 비교
- Kotlin 시리즈 #9 Spring Boot | REST API 서버 - 더 상세한 설명
- Kotlin 시리즈 #1 Kotlin 입문 | 기초 문법 - Kotlin 기초
- Kotlin Coroutine vs Thread 비교 - 비동기 처리
- Java 시리즈 #10 Spring Boot - Java 버전
- Supabase 완벽 가이드 - 백엔드 대안
한 줄 요약: Kotlin은 Java보다 30-40% 적은 코드로 Spring Boot API를 만들 수 있습니다. data class와 Null 안전성으로 생산성이 높아 최근 백엔드 채용에서 주목받고 있습니다.
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Kotlin Spring Boot 완전 초보자를 위한 입문 튜토리얼. Java보다 간결한 문법으로 REST API 만들기. 설치부터 배포까지 30분이면 충분합니다. 코드 복사만으로 따라할 수 있는 단계별 가이드. S… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- Spring Boot 입문 튜토리얼 | 30분 만에 REST API 만들기 [초보자용]
- Kotlin Spring Boot | REST API 서버 만들기
- Kotlin 시작하기 | Android 공식 언어 완벽 입문
- Java Spring Boot | REST API 서버 만들기
이 글에서 다루는 키워드 (관련 검색어)
Kotlin, Spring Boot, 입문, 튜토리얼, REST API, 초보자 등으로 검색하시면 이 글이 도움이 됩니다.