Kotlin Controller와 DTO 작성 - Web Layer

박미소·2023년 12월 26일
0

코틀린

목록 보기
15/44

Controller와 DTO 작성


공통된 기능은 common, 외부 infra를 사용하면 infra 라는 패키지를 사용한다.

model은 entitiy 나 vo를 모아놓은 하나의 객체이다.

dto 패키지 내 응답할 데이터 클래스 CourseResponse 생성.

전달되는 데이터이기에 변경될 일이 없어 val 사용.

CreateCourseRequest 데이터 클래스에 최대인원(maxApplicants) 지정 안해도 되는 이유는 CourseResponse 데이터 클래스 안에 최대 인원이 이미 지정 되어있기 때문이다.

status 또한 생성이 되면은 open 상태로 자동으로 할당이 된다.

수강인원(numApplicants)도 코스를 새로 생성(POST)할 때 자동으로 0 할당된다.

UpdateCourseRequest 또한 나머지가 자동 할당돼 타이틀이랑 디스크립션만 수정 가능.

코스 삭제(DELETE)를 요청할때도 id 기반으로 하기때문에 삭제(DELETE)는 request 데이터 클래스를 만들 필요가 없다.







DTO를 받는 Controller


컨트롤러를 스프링 빈으로 등록하는 어노테이션 @RestController

View가 아닌 Data만을 응답하기 때문에 @Controller 대신에 @RestController 를 사용

이후, Handler Mapping에게 어떤 url을 담당하는지 알려줘야 한다.

이를 위해 @RequestMapping Annotation을 사용.

Course의 경우 /courses 가 기본 URL.

@RequestMapping("/courses")
@RestController
class CourseController {

}






Handler Method


이후, 각각 Course 관련 Command에 대한 API를 작성한다.

각각의 Verb에 대해 표현해줘야 한다.

@RequestMapping 을 포함하여 Controller 단에서 HTTP 요청의 처리를 위해 지정하는 Annotation들을 Handler Method라 한다.

주로 쓰는 핸들러 메소드로는,

  • @GetMapping , @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping : @RequestMapping 의 파생 어노테이션으로, 요청 URI와 함께 Method를 함께 나타냅니다. 함수에 지정됩니다. Class 상위에 @RequestMapping 이 지정되어있을시 해당 @RequestMapping에 지정된 URI를 base URI로서 인식하고, 이후 @GetMapping 등에 지정된 URI가 추가됩니다.

  • @RequestParam : HTTP 요청 파라미터 값을 매핑하는 데 사용. 보통 GET Method 형태로 RESTful 한 API 를 만들 때 @RequestParam 어노테이션을 사용해서 parameter 를 주입받게 된다.

  • @PathVariable : URI 경로 변수 값을 매핑하는데 사용.

  • @RequestBody : 요청 DTO를 표기할때 사용. 여기서 요청 DTO는 CreateCourseRequest, UpdateCourseRequest이다. 클라이언트에게 요청받았을때 객체로 변환해서 줘야 하기에 이 어노테이션이 사용된다. Json을 받으면 객체로 맵핑(변환)하라고 spring한테 알려주는 것.







Kotlin 에서 @RequestParam 을 사용할 때 기본값을 넣는 방법


일반적인 사용법

@GetMapping('/api/v1/order/{orderItemId}')
fun getOrdersByOrderItemIds(
	@PathVariable orderItemId: Long,
    @RequestParam page: Int,
    @RequestParam size: Int,
)


pagesize 에 대해서는 기본값을 설정.

이렇게 쓰면 null 이 넘어온다.

검색을 해보니 원인은 해당 API 가 호출이 되면 ArgumentResolver를 거치게 되는데 이 때 page 와 sizenull 이 채워진채로 받기 때문에 기본값이 설정되지 않는다고 한다.(RequestParamMethodArgumentResolver)

따라서 이 때는 @RequestParam 어노테이션 자체의 설정을 활용하면 의도한대로 작동하게 된다.



잘못된 예제

@RequestParam page: Int? = 1,
@RequestParam size: Int? = 10,

올바른 사용 예제

@GetMapping('/api/v1/order/{orderItemId}')
fun getOrdersByOrderItemIds(
	@PathVariable orderItemId: Long,
    @RequestParam(defaultValue = "1") page: Int,
    @RequestParam(defaultValue = "10") size: Int,
)




예제 1

클래스 상위에 RequestMapping이 있으면 여기에 뒤로 붙는 URI를 표기

@RequestMapping("/courses")
@RestController
class CourseController {

    @GetMapping
    fun getCourses() {
    }
	
    @GetMapping("/{courseId}")
    fun getCourse() {          // 단일 코스 가져오기
    }
    
    @PutMapping
    fun updateCourses() {
    }
}



예제 2

클래스 상위에 RequestMapping이 없다면 전체 URI를 표기해야 한다.

@RestController
class CourseController {

    @GetMapping("/courses")
    fun getCourses() {

    }

    @GetMapping("/courses/{courseId}")
    fun getCourse() {                  // 단일 코스 가져오기
    }

    @PutMapping("/courses")
    fun updateCourses() {

    }
}






Course-controller 작성 완료


package com.teamsparta.courseregistration.domain.course.controller

import com.teamsparta.courseregistration.domain.course.dto.CourseResponse
import com.teamsparta.courseregistration.domain.course.dto.CreateCourseRequest
import com.teamsparta.courseregistration.domain.course.dto.UpdateCourseRequest
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController




@RequestMapping("/courses")
@RestController
class CourseController {

    @GetMapping()
    fun getCourseList(): ResponseEntity<List<CourseResponse>> {     // CourseResponse 하나하나를 리스트에 담아 Array 형태로 get.
        TODO()
    }


    // 단일 코스 하나만 가져오기
    @GetMapping("/{courseId}")  // 여기에 표기한 PathVariable과 메서드 인자로 받는 PathVariable 네이밍 일치 시킴.
    fun getCourse(@PathVariable cousreId: Long): ResponseEntity<CourseResponse> {       // CourseResponse에서 하나만 id값에 기반해 하나만 get.
        TODO()
    }


    @PostMapping
    fun createCourse(@RequestBody createCourseRequest: CreateCourseRequest): ResponseEntity<CourseResponse> {
        TODO()                                       // JSON이 CreateCourseRequest 데이터 클래스의 프로퍼티로 매핑된다.
                                                     // 생성을 하면 생성된 Response를 줘야함. ResponseEntity는 Spring 프레임워크에서 상세한 Response 객체를 구성하기 위해 제공하는 기능. 이걸 쓰는 이유는 DTO 뿐만이 아니라 statusCode도 같이 줘야하기 때문에.
                                                     // 이 꺽쇠 안에는 CourseResponse란 DTO를 넣어 주면 됨.
    }



    @PutMapping("/{courseId}")
    fun updateCourse(@PathVariable courseId: Long, @RequestBody updateCourseRequest: UpdateCourseRequest): ResponseEntity<CourseResponse>  {
        TODO()
    }


    @DeleteMapping("/{courseId}")
    fun deleteCourse(@PathVariable courseId: Long) {
        TODO()
    }
}

단일 강의 한개 가져올 때 이런 형태로 반환

목록을 가져올 때 리스트 형태로 반환

JSON 이 오브젝트를 value로 담은 것.




API 설계에 맞게 모두 작성한다면,

모든 API 핸들링이 가능해졌다.

Web Layer 작성 완료!







참고:
https://velog.io/@kdohyeon/RequestParam-%EC%97%90-%EA%B8%B0%EB%B3%B8%EA%B0%92-%EB%84%A3%EC%96%B4%EC%A3%BC%EA%B8%B0

0개의 댓글