1. 이전 포스팅
https://growingsaja.tistory.com/972
2. 현 상황 확인 및 목표
Coffee의 경우 User와 달리, Service 없이 Controller에서 Repository를 바로 불러와 사용하고 있습니다. 그래서 User처럼 Controller -> Service -> Repository 구조로 소스코드를 수정해 적용해줍니다.
3. Entiry - Repository - Service - Controller
// vim Coffee.kt
package com.example.practicekopring.entities
// Jakarta Persistence API(JPA)에서 필요한 어노테이션들을 가져옵니다.
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
// @Entity 어노테이션은 이 클래스가 JPA 엔터티임을 나타냅니다.
@Entity
// Coffee 엔티티 클래스를 정의합니다.
data class Coffee (
// @Id 어노테이션은 이 필드가 엔터티의 기본 키(primary key)임을 나타냅니다.
@Id
// @GeneratedValue 어노테이션은 기본 키의 생성 전략을 나타내며, IDENTITY 전략은 데이터베이스가 자동으로 기본 키 값을 생성하도록 지시합니다.
@GeneratedValue(strategy = GenerationType.IDENTITY)
// "id" 필드는 Coffee 엔터티의 기본 키이며, 기본값은 0입니다.
var id: Int = 0,
val name: String,
)
4. Entiry - Repository - Service - Controller
// vim CoffeeRepository.kt
package com.example.practicekopring.repositories
import com.example.practicekopring.entities.Coffee
import org.springframework.data.jpa.repository.Query
// Spring Data JPA에서 사용하는 쿼리 어노테이션을 가져옵니다.
import org.springframework.data.jpa.repository.JpaRepository
// Spring Data Repository의 기본 기능을 제공하는 CrudRepository를 가져옵니다.
// CrudRepository에서는 findAll(), findById(), save(), deleteById() 등의 크루드(기본 CRUD) 작업을 자동으로 지원하므로 이를 활용할 수 있습니다.
interface CoffeeRepository : JpaRepository<Coffee, Int> {
// CoffeeRepository 인터페이스를 정의하며, Coffee 엔터티를 조작할 수 있는 메서드를 정의합니다.
@Query("SELECT MAX(c.id) FROM Coffee c")
// @Query 어노테이션은 사용자 정의 JPQL(Jakarta Persistence Query Language) 쿼리를 정의합니다.
fun findMaxId(): Int?
// "findMaxId" 메서드는 Coffee 엔터티의 최대 ID 값을 찾는 쿼리를 실행하고, 결과를 Int로 반환합니다. 결과가 없을 경우 nullable한 Int를 반환합니다.
}
5. Entiry - Repository - Service - Controller
// vim services/CoffeeService.kt
package com.example.practicekopring.services
import com.example.practicekopring.entities.Coffee
import com.example.practicekopring.repositories.CoffeeRepository
import org.springframework.stereotype.Service
import java.util.*
@Service
class CoffeeService(private val coffeeRepository: CoffeeRepository) {
fun getAllCoffees(): MutableIterable<Coffee> {
return coffeeRepository.findAll()
}
fun getCoffeeById(id: Int): Optional<Coffee> {
return coffeeRepository.findById(id)
}
fun createCoffee(coffee: Coffee): Coffee {
return coffeeRepository.save(coffee)
}
fun updateCoffee(id: Int, updatedCoffee: Coffee): Optional<Coffee> {
val existingCoffee = coffeeRepository.findById(id)
if (existingCoffee.isPresent) {
val coffeeToUpdate = existingCoffee.get().copy(name = updatedCoffee.name)
coffeeRepository.save(coffeeToUpdate)
return Optional.of(coffeeToUpdate)
}
return Optional.empty()
}
fun deleteCoffee(id: Int) {
coffeeRepository.deleteById(id)
}
}
6. Entiry - Repository - Service - Controller
// vim controllers/CoffeeController.kt
package com.example.practicekopring.controllers
import com.example.practicekopring.entities.Coffee
import com.example.practicekopring.services.CoffeeService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/api/coffees")
class CoffeeController(private val coffeeService: CoffeeService) {
@GetMapping
fun getAllCoffees(): MutableIterable<Coffee> {
return coffeeService.getAllCoffees()
}
@GetMapping("/{id}")
fun getCoffeeById(@PathVariable id: Int): ResponseEntity<Coffee> {
val coffee = coffeeService.getCoffeeById(id)
return if (coffee.isPresent) {
ResponseEntity(coffee.get(), HttpStatus.OK)
} else {
ResponseEntity(HttpStatus.NOT_FOUND)
}
}
@PostMapping
fun createCoffee(@RequestBody coffee: Coffee): ResponseEntity<Coffee> {
val createdCoffee = coffeeService.createCoffee(coffee)
return ResponseEntity(createdCoffee, HttpStatus.CREATED)
}
@PutMapping("/{id}")
fun updateCoffee(@PathVariable id: Int, @RequestBody updatedCoffee: Coffee): ResponseEntity<Coffee> {
val updatedCoffeeOptional = coffeeService.updateCoffee(id, updatedCoffee)
return if (updatedCoffeeOptional.isPresent) {
ResponseEntity(updatedCoffeeOptional.get(), HttpStatus.OK)
} else {
ResponseEntity(HttpStatus.NOT_FOUND)
}
}
@DeleteMapping("/{id}")
fun deleteCoffee(@PathVariable id: Int): ResponseEntity<Unit> {
coffeeService.deleteCoffee(id)
return ResponseEntity(HttpStatus.NO_CONTENT)
}
}