본문 바로가기
Spring Boot

[Kotlin Spring Boot 강의] Spring Data 패키지로 초간단실제 DB 연동하는 CRUD API 구현 방법

by 뷰티풀스택 2025. 2. 3.
반응형

Spring Data CRUD API 구현하는 방법

이 방법은 기존에는 Data Class를 서비스 레이어를 구현해서 DB와 연동하였다면, 이번에는 CRUD에 특화된 서비스라면 복잡하게 별도의 서비스 레이어를 구현할 필요없이 스프링 프레임워크에 내장된 Spring Data 패키지를 활용하면 매우 간략하게 구현할 수 있다.

 

기존 데이터 클래스에 @Table 어노테이션을 붙여주기만 하면 쉽게 변환이 된다.

 

 

1. Data Class --> Table Class 전환

@Table 어노테이션에는 실제 데이터베이스의 테이블 이름을 넣어주면 된다.

package com.example.demo

import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table


@Table("books")
data class Book(@Id val id: Int, val title: String, val author: String)

그리고, 테이블에서 Primary Key가 되는 컬럼은 @Id 어노테이션을 달아준다.

 

2. CrudRespository Interface 추가

package com.example.demo

import org.springframework.data.repository.CrudRepository

interface BookRepository : CrudRepository<Book, Int>

CrudRepository<데이터클래스 이름, Primary Key 타입> 으로 Repository 타입을 Interface로 선언해주면 된다.

 

3. 서비스 Jdbc --> CrudRepository 변환

변환 과정의 핵심은 임의로 작성한 SQL 문을 Spring Data에서 미리정의한 유틸 함수로 변환해주는 것이다.

기존 JDBC 기반 서비스 코드

package com.example.demo

import org.springframework.data.repository.findByIdOrNull
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.jdbc.core.query
import org.springframework.stereotype.Service

@Service
class BookService(private val db: JdbcTemplate) {
    fun findBooks(): List<Book> = db.query("SELECT * FROM books") { response, _ ->
        Book(response.getInt("id"), response.getString("title"), response.getString("author"))
    }

    fun addBook(book: Book): Book {
        db.update(
            "INSERT INTO books (title, author) VALUES (?, ?)",
            book.title, book.author
        )
        return book.copy(title = book.title, author = book.author)
    }

    fun findBookById(id: Int): Book? =
        db.query("SELECT * FROM books WHERE id = ?", id) { response, _ ->
            Book(response.getInt("id"), response.getString("title"), response.getString("author"))
        }.singleOrNull()
}

 

CrudRespository로 변환한 코드

package com.example.demo

import org.springframework.data.repository.findByIdOrNull
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.jdbc.core.query
import org.springframework.stereotype.Service

@Service
class BookService(private val db: BookRepository) {
    fun findBooks(): List<Book> = db.findAll().toList()

    fun findBookById(id: Int): Book? = db.findByIdOrNull(id)

    fun addBook(book: Book): Book = db.save(book)
}

 

4. Controller 업데이트

업데이트된 서비스의 함수 이름과 매개변수들을 맞춰서 minor 업데이트만 해주면 된다.

package com.example.demo

import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.net.URI

@RestController
@RequestMapping("/books")
class BookController(private val service: BookService) {
    @GetMapping
    fun listBooks() = ResponseEntity.ok(service.findBooks())

    @PostMapping
    fun post(@RequestBody book: Book): ResponseEntity<Book> {
        val savedBook = service.addBook(book)
        return ResponseEntity.created(URI.create("/books/${savedBook.id}")).body(savedBook)
    }

    @GetMapping("/{id}")
    fun getBook(@PathVariable id: Int): ResponseEntity<Book> = service.findBookById(id).toResponseEntity()

    private fun Book?.toResponseEntity(): ResponseEntity<Book> = this?.let { ResponseEntity.ok(it)} ?: ResponseEntity.notFound().build()
}

 

끝났다!

얼마나 간결하고 좋은가 ^^

테스트를 해보면 아래와 같이 동일한 결과를 얻을 수 있다.

 

끝!

반응형

댓글