1. 이전 포스팅

 

https://growingsaja.tistory.com/962

 

[Kotlin][SpringBoot3] Kopring 서버 실습 03 - Swagger 적용하기 (using springdoc)

1. 이전 포스팅 https://growingsaja.tistory.com/954 [Kotlin][SpringBoot3] Kopring 서버 실습 02 - 간단한 api 구현 및 외부 api의 결과값을 가져와 return하는 api 1. 참고하면 좋은 이전 포스팅 https://growingsaja.tistory.com/

growingsaja.tistory.com

 

 

 

 

 

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": "모카 라떼"}'

 

 

 

 

 

+ Recent posts