최초 RestController 세팅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package kotlins.pring.snippet.controller
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import kotlins.pring.snippet.service.FruitService
@RestController
@RequestMapping("/v1/fruits")
class FruitController(
private val fruitService: FruitService,
) {
companion object {
const val FRUIT_ID = "fruit_id"
const val FRUIT_NAME = "fruit_name"
const val FRUIT_ORIGIN = "fruit_origin"
const val FRUIT_FILE = "file"
}
// 컨트롤러 메서드 추가
}
파라미터
- 패스 파라미터(path parameter)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @GetMapping(value = ["/{$FRUIT_ID:[0-9]+}"], produces = [MediaType.APPLICATION_JSON_VALUE]) fun retrieveFruit( @PathVariable(name = FRUIT_ID) fruitId: Long, ): ResponseEntity<DataRes<FruitResDto>> { val savedFruit = fruitService.retrieveFruit( fruitId = fruitId ) return ResponseEntity.ok().body( DataRes( FruitResDto( id = savedFruit.id!!, name = savedFruit.name, origin = savedFruit.origin ) ) ) }
- 쿼리 파라미터(query parameter)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
@GetMapping(value = ["/search"], produces = [MediaType.APPLICATION_JSON_VALUE]) fun listFruitByName( @RequestParam(name = FRUIT_NAME) name: String, ): ResponseEntity<DataRes<List<FruitResDto>>> { val savedFruits = fruitService.listFruitByName( name = name ) return ResponseEntity.ok().body( DataRes( savedFruits.map { FruitResDto( id = it.id!!, name = it.name, origin = it.origin ) } ) ) }
- 바디(body) - json
- dto
1 2 3 4 5 6 7 8
@JsonInclude(JsonInclude.Include.NON_NULL) data class FruitReqDto ( @JsonProperty("fruit_name") val name: String, @JsonProperty("fruit_origin") val origin: String, )
- 컨트롤러
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
@PostMapping(value = [""], produces = [MediaType.APPLICATION_JSON_VALUE]) fun createFruit( @RequestBody fruitReqDto: FruitReqDto ): ResponseEntity<DataRes<FruitResDto>> { val savedFruit = fruitService.createFruit( fruitReqDto = fruitReqDto ) return ResponseEntity.ok().body( DataRes( FruitResDto( id = savedFruit.id!!, name = savedFruit.name, origin = savedFruit.origin ) ) ) }
- dto
- 바디(body) - form-data 또는 x-www-form-urlencoded
- dto
1 2 3 4 5
// @ModelAttribute 사용 시 form-data 필드명과 dto 필드명이 항상 일치해야 함 data class FruitReqFormDataDto ( val name: String, val origin: String, )
- 컨트롤러
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
@PostMapping(value = ["/form-data"], produces = [MediaType.APPLICATION_JSON_VALUE]) fun createFruitByFormData( @ModelAttribute fruitReqFormDataDto: FruitReqFormDataDto ): ResponseEntity<DataRes<FruitResDto>> { val savedFruit = fruitService.createFruitByFormData( fruitReqFormDataDto = fruitReqFormDataDto ) return ResponseEntity.ok().body( DataRes( FruitResDto( id = savedFruit.id!!, name = savedFruit.name, origin = savedFruit.origin ) ) ) }
- 혹은 @QueryParam 사용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
@PostMapping(value = ["/form-data-request-param"], produces = [MediaType.APPLICATION_JSON_VALUE]) fun createFruitByFormDataRequestParam( @RequestParam(name = FRUIT_NAME) name: String, @RequestParam(name = FRUIT_ORIGIN) origin: String, ): ResponseEntity<DataRes<FruitResDto>> { val savedFruit = fruitService.createFruitByFormData( fruitReqFormDataDto = FruitReqFormDataDto( name = name, origin = origin ) ) return ResponseEntity.ok().body( DataRes( FruitResDto( id = savedFruit.id!!, name = savedFruit.name, origin = savedFruit.origin ) ) ) }
- dto
- 바디(body) - 파일
- dto
1 2 3 4 5
data class FruitFileDto ( val name: String, val fruitName: String, val fruitOrigin: String, )
- 컨트롤러
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
@PostMapping(value = ["/file"], produces = [MediaType.APPLICATION_JSON_VALUE]) fun createFruitWithFile( @RequestPart(FRUIT_FILE) file: MultipartFile, @ModelAttribute fruitFileDto: FruitFileDto, ): ResponseEntity<DataRes<FruitResDto>> { val savedFruit = fruitService.createFruitWithFile( file = file, fruitFileDto = fruitFileDto ) return ResponseEntity.ok().body( DataRes( FruitResDto( id = savedFruit.id!!, name = savedFruit.name, origin = savedFruit.origin ) ) ) }
- dto
유효성 검사
- javax.validation 의 유효성 검사 애너테이션을 DTO에 적용
- javax.validation 애너테이션 목록
- dto
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@JsonInclude(JsonInclude.Include.NON_NULL) data class FruitSizeDto( @field:NotNull(message = "null 허용 안 함") @field:NotEmpty(message = "\"\" 허용 안 함") @JsonProperty("fruit_name") val name: String, @JsonProperty("fruit_origin") val origin: String, @field:Min(value = 1, message = "1 이상이어야 함") @field:Max(value = 10, message = "10 이하이어야 함") val size: Int, )
- 컨트롤러
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
@PostMapping(value = ["/size"], produces = [MediaType.APPLICATION_JSON_VALUE]) fun createFruitWithSize( @RequestBody @Valid fruitSizeDto: FruitSizeDto, ): ResponseEntity<DataRes<FruitResDto>> { val savedFruit = fruitService.createFruitWithSize( fruitSizeDto = fruitSizeDto ) return ResponseEntity.ok().body( DataRes( FruitResDto( id = savedFruit.id!!, name = savedFruit.name, origin = savedFruit.origin ) ) ) }
페이지네이션
- dto
1 2 3 4 5 6 7 8 9 10 11 12
data class DataPageableRes<T>( val totalCount: Long, val data: List<T>, val page: PageInfo ) data class PageInfo( val totalPages: Int, val number: Int, val size: Int, )
- 컨트롤러
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
@GetMapping(value = [""], produces = [MediaType.APPLICATION_JSON_VALUE]) fun listFruitsPageableByOrigin( @RequestParam(name = FRUIT_ORIGIN) origin: String, pageable: Pageable, ): ResponseEntity<DataPageableRes<Fruit>> { val fruitPage = fruitService.listFruitsPageable( origin = origin, pageable = pageable ) return ResponseEntity.ok().body( DataPageableRes( totalCount = fruitPage.totalElements, data = fruitPage.content, page = PageInfo( totalPages = fruitPage.totalPages, number = fruitPage.number, size = fruitPage.size ) ) ) }
참고
- Fruit 응답 용 DTO
1 2 3 4 5
data class FruitResDto ( val id: Long, val name: String, val origin: String, )
- 응답 용 DTO
1 2 3
data class DataRes<T>( val data: T )