1. 이전 포스팅
https://growingsaja.tistory.com/962
2. 목표
본 글은 H2 Database와 연동을 진행합니다. H2 Database는 서버 재가동시 데이터가 reset되는 특성이 있습니다. 내부 local 임시 database 형태로 사용할 수 있습니다.
3. 의존성 추가하기
// vim build.gradle.kts
// ...
dependencies {
// ...
// dev tools
developmentOnly("org.springframework.boot:spring-boot-devtools")
// h2 database
runtimeOnly("com.h2database:h2")
}
// ...
4. application.propertise에 H2 데이터베이스 설정
본 H2 Database는 서버 재가동시 데이터가 reset되니 주의가 필요합니다.
# db h2
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:crypto-labs
5. H2 Database 콘솔 화면 접속
http://localhost:8080/h2-console
6. Connect 진행
아래와 같은 오류가 발생한다면, JDBC URL을 application.propertise에 입력한 것과 같게 잘 입력했는지 확인해봅니다.
// 예시
spring.datasource.url=jdbc:h2:mem:crypto-labs
// JDBC URL = jdbc:h2:mem:crypto-labs
spring.datasource.url=jdbc:h2:mem:cafe-saja
// JDBC URL = jdbc:h2:mem:cafe-saja
7. 정상 Connect 완료 확인
데이터베이스의 콘솔 화면입니다.
기본 Table 외에는 정보를 확인할 수 없는 상태입니다.
8. 작성할 파일명 생성 및 해당 파일들 들어갈 패키지 만들기
Entity : 만들어둔 데이터셋 형태에 맞게 H2 DB에 Table을 만들게 됩니다.
9. entity 만들기
// vim Coffee.kt
package com.example.practicekopring.entities
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
@Entity
data class Coffee (
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Int = 0,
val name: String,
)
10. Entities에서 설정한 Table 생성 확인
11. Repository 작성
// vim CoffeeRepository.kt
package com.example.practicekopring.repositories
import com.example.practicekopring.entities.Coffee
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.CrudRepository
// CrudRepository에서는 findAll(), findById(), save(), deleteById()등의 크루드를 자동으로 지원하여 이를 활용하면 됩니다.
interface CoffeeRepository : CrudRepository<Coffee, Int> {
@Query("SELECT MAX(c.id) FROM Coffee c")
fun findMaxId(): Int?
}
12. CoffeeController.kt 작성
// vim CoffeeController.kt
package com.example.practicekopring.controllers
import com.example.practicekopring.entities.Coffee
import com.example.practicekopring.repositories.CoffeeRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import java.util.*
@RestController
@RequestMapping("/coffees")
class CoffeController {
@Autowired
private lateinit var coffeeRepository: CoffeeRepository
@GetMapping
fun getCoffees(): MutableIterable<Coffee> {
return coffeeRepository.findAll()
}
@GetMapping("/{id}")
fun getCoffeById(@PathVariable id: Int): Optional<Coffee> {
return coffeeRepository.findById(id)
}
@PostMapping
fun insertCoffee(@RequestBody coffee: Coffee): ResponseEntity<Coffee> {
coffeeRepository.save(coffee)
return ResponseEntity(coffee, HttpStatus.CREATED)
}
@PutMapping("/{id}")
fun putCoffee(@PathVariable id: Int, @RequestBody coffee: Coffee): ResponseEntity<Coffee> {
val existingCoffee = coffeeRepository.findById(id)
return if (existingCoffee.isPresent) {
val updatedCoffee = existingCoffee.get().copy(name = coffee.name) // Update only the name
coffeeRepository.save(updatedCoffee)
ResponseEntity(updatedCoffee, HttpStatus.OK)
} else {
val oldMaxId = coffeeRepository.findMaxId() ?: 0 // Function to find the max id from the repository
val newCoffee = Coffee(id, coffee.name) // Create a new Coffee with the provided id
coffeeRepository.save(newCoffee)
val createdCoffee = coffeeRepository.findById(id).orElse(null)
if (createdCoffee != null) {
// 데이터가 요청한대로 생성된 경우
return ResponseEntity.of(Optional.of(createdCoffee))
} else {
// 데이터가 요청한대로 생성되지 않은 경우
val maxId = coffeeRepository.findMaxId() ?: 0 // Function to find the max id from the repository
if (oldMaxId == maxId) {
return ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR)
} else {
val maxCoffee = coffeeRepository.findById(maxId).orElse(null)
return if (maxCoffee != null) {
ResponseEntity.of(Optional.of(maxCoffee))
} else {
ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR)
}
}
}
}
}
@DeleteMapping("/{id}")
fun deleteCoffee(@PathVariable id: Int) {
coffeeRepository.deleteById(id)
}
}
13. 서버 정상 작동 확인
14. 쿼리 정상 작동 확인
SELECT * FROM COFFEE
15. 서버 api 정상 작동 확인
http://localhost:8080/coffees
- 전체 coffee get
- id=1 coffee 정보 get
- coffee add : americano
[
{
"id": 0,
"name": "Americano"
}
]
- 추가, 조회, 삭제 api 사용해보기
curl -X POST -H "Content-Type: application/json" -d '{"name":"Americano"}' http://localhost:8080/coffees
curl -X POST -H "Content-Type: application/json" -d '{"name":"Coldbrew"}' http://localhost:8080/coffees
curl -X GET -H "Content-Type: application/json http://localhost:8080/coffees
GET -H "Content-Type: application/json" http://localhost:8080/coffees/0
GET -H "Content-Type: application/json" http://localhost:8080/coffees/1
GET -H "Content-Type: application/json" http://localhost:8080/coffees/2
GET -H "Content-Type: application/json" http://localhost:8080/coffees/3
curl -X DELETE -H "Content-Type: application/json" http://localhost:8080/coffees/2
curl -X DELETE -H "Content-Type: application/json" http://localhost:8080/coffees/3
16. 서버 가동시, 기본적으로 DB table에 들어가있을 데이터 설정하기
해당 방법을 통해, 서버 재가동 즉시 데이터가 아무것도 없는 것이 아니라, 기본값으로 넣을 데이터를 설정할 수 있습니다.
// vim DataLoader.kt
package com.example.practicekopring.entities
import com.example.practicekopring.repositories.CoffeeRepository
import jakarta.annotation.PostConstruct
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@Component
class DataLoader @Autowired constructor(private val coffeeRepository: CoffeeRepository) {
@PostConstruct
fun loadData() {
val initialCoffeeData = listOf(
Coffee(1, "아메리카노"),
Coffee(2, "카페 라떼"),
Coffee(3, "모카")
)
coffeeRepository.saveAll(initialCoffeeData)
}
}
17. get, post, put 써보기
curl -X GET -H "Content-Type: application/json" http://localhost:8080/coffees
curl -X PUT -H "Content-Type: application/json" http://localhost:8080/coffees/1 -d '{"name": "바닐라 라떼"}'
curl -X PUT -H "Content-Type: application/json" http://localhost:8080/coffees/99 -d '{"name": "카라멜 마끼아또"}'
curl -X POST -H "Content-Type: application/json" http://localhost:8080/coffees -d '{"name": "모카 라떼"}'