이 글은 Spring Boot를 공부하며 정리한 글입니다.
이전글들을 통해서 H2+JPA를 이용한 CRUD와 MySQL 연동까지 해봤습니다. 이번에는 MySQL+JPA를 이용한 CRUD입니다. 하지만 이전에는 CRUD만 해보았지만, 이번에는 DTO라는 개념까지 도입해보겠습니다.
이미지 출처 : https://e-una.tistory.com/72
아, 원래는 자료들은 한땀한땀 손으로 만들었었는데, 시간이 부족해서 이번에는 다른분의 개발블로그의 이미지를 첨부했습니다.
DTO는 Data Transfer Object의 약자로 데이터를 전달해주는 객체입니다. Client와 각 요소들 사이의 데이터를 주고받을 때 담는 그릇같은 역활을 합니다.
우리가 만들 예제는 사람의 정보를 CRUD하는 예제입니다. 따라서, DTO 역시 사람의 정보를 전달하게끔 만들어줄것입니다. 다음은 각각 응답과 요청에 해당하는 DTO입니다.
data class HumanCreateRequestDto (
val id : Int,
val name : String,
)
fun HumanCreateRequestDto.toEntity() = Human(
id = id,
name = name,
)
data class HumanResponseDto (
var id : Int,
var name : String,
)
fun Human.toResponse() = HumanResponseDto (
id = id,
name = name,
)
data class HumanUpdateRequestDto (
var name : String,
)
fun HumanUpdateRequestDto.toEntity(id : Int) = Human(
id = id,
name = name,
)
Entity는 테이블과 1대1로 맵핑되는 객체라고 할 수 있습니다. DB에서 Human이라는 테이블에 접근하기 위해서는 Human Entity를 이용하는 것이죠.
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
@Entity
class Human (
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
var id : Int,
var name : String,
)
Entity가 DB에 접근하기 위해서는 Repository를 이용합니다. Repository는 interface로 만들어주세요.
import com.example.mysqlpratice.human.entity.Human
import org.springframework.data.repository.CrudRepository
interface HumanRepository : CrudRepository<Human, Int>
Service는 Controller에서 수행하는 실질적인 비즈니스 로직입니다. DB접근을 위해 Repository를 이용합니다.
@Service
class HumanService {
@Autowired
private lateinit var humanRepository : HumanRepository
fun getHumans() : ResponseEntity<List<HumanResponseDto>> {
val humans = humanRepository.findAll()
return ResponseEntity(humans.map { it.toResponse() }, HttpStatus.OK)
}
fun postHuman(humanCreateDto : HumanCreateRequestDto): ResponseEntity<HumanResponseDto> {
val human = humanRepository.save(humanCreateDto.toEntity())
return ResponseEntity(human.toResponse(), HttpStatus.CREATED)
}
fun putHuman(id : Int, humanUpdateRequestDto : HumanUpdateRequestDto) : ResponseEntity<HumanResponseDto> {
humanRepository.findById(id).orElseThrow { ResponseStatusException(HttpStatus.NOT_FOUND) }
val human = humanRepository.save(humanUpdateRequestDto.toEntity(id))
return ResponseEntity(human.toResponse(), HttpStatus.OK)
}
fun deleteHuman(id : Int) {
return humanRepository.deleteById(id)
}
}
@Service 어노테이션은 해당 class가 Service임을 명시합니다.
드디어 마지막인 Controller 생성입니다. Controller와 Service와 DTO를 이용해서 데이터를 주고받습니다.
@RestController
@RequestMapping("/human")
class HumanController {
@Autowired
private lateinit var humanService : HumanService
@GetMapping
fun getHumans() : ResponseEntity<List<HumanResponseDto>> =
humanService.getHumans()
@PostMapping
fun postHuman(
@RequestBody humanCreateRequestDto: HumanCreateRequestDto
) : ResponseEntity<HumanResponseDto> =
humanService.postHuman(humanCreateRequestDto)
@PutMapping("/{id}")
fun putHuman(
@PathVariable id : Int,
@RequestBody humanUpdateRequestDto: HumanUpdateRequestDto
) : ResponseEntity<HumanResponseDto> =
humanService.putHuman(id, humanUpdateRequestDto)
@DeleteMapping("/{id}")
fun deleteHuman(@PathVariable id : Int) = humanService.deleteHuman(id)
}
비즈니스로직이 분리되어 굉장히 깔끔한 구조로 나타납니다. 결과는 생략하도록 하겠습니다.