본문 바로가기
PROJECT/Spring Todo 프로젝트

TodoApp 백엔드 서버 만들기 (3)

by HR_J 2024. 5. 17.

오늘 포스트에선 간단하게 Todo  전체보기 정렬 기준 및 작성자 기준 필터링. 그리고, Valid를 활용해 글자수를 제한하는 기능을 추가할 것이다.

설계 부분은 크게 변동되는점이 없어 별도로 설명은 하지 않겠다.

 

정렬과 필터링

1. 정렬 기준 : 작성시간 기준 오름차순, 내림차순

2. 작성자 기준 필터링

@GetMapping
fun getTodoList(
    @RequestParam(defaultValue = "desc") order: String,
    @RequestParam(defaultValue = "") author: String
): ResponseEntity<List<TodoResponse>> {
    val todos = if (author.isNotBlank()) todoService.getAllTodos()
        .filter { it.author == author } else todoService.getAllTodos()

    val sortedTodos = when (order) {
        "asc" -> todos.sortedBy { it.date }
        "desc" -> todos.sortedByDescending { it.date }
        else -> throw IllegalArgumentException("Invalid order parameter: $order")
    }

    return ResponseEntity.status(HttpStatus.OK).body(sortedTodos)
}

//TodoController.kt

 

작성자 명을 별도로 주지 않을 경우, 전체 사용자의 TodoList를 받아오고, 작성자명이 주어질 경우에, 입력받은 작성자와 todoList내의 작성자들을 비교해 불러오는 방식을 활용했다. (여담이지만, Kotlin에는 참 좋은 함수들이 많다...ㅎ)

TodoList의 정렬과 같은 경우도 Parameter의 default값을 desc로 지정해두었다. 별도의 언급이 없을 경우에는 자동으로 내림차순 정렬을 하고,  별도의 명령이 존재할 경우, 해당 명령에 맞게 정렬을 하였다. 이상한 값이 들어올 경우에 대한 처리도 해두었다.

 

글자수 제한

Todo 항목의 title과 body부분에 글자수 제한이 필요했다. 해당 제한은 Todo를 작성할때, 그리고 수정할 때 두 경우에만 필요하다.

title의 경우에는 1~200자,

body의 경우에는 1~1000자로 제한을 걸어야한다.

 

우선 제일 처음에 작성한 코드는 아래와 같이 private funcion을 따로 만드는 것이었다.

@PostMapping
fun createTodo(@RequestBody createTodoRequest: CreateTodoRequest): ResponseEntity<TodoResponse> {
    try {
        validateTodoLength(createTodoRequest.title, createTodoRequest.body)
    } catch (e: IllegalArgumentException) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).build()
    }
    return ResponseEntity.status(HttpStatus.CREATED).body(todoService.createTodo(createTodoRequest))
}

@PutMapping("/{todoId}")
fun updateTodo(
    @PathVariable todoId: Long,
    @RequestBody updateTodoRequest: UpdateTodoRequest
): ResponseEntity<TodoResponse> {

    try {
        validateTodoLength(updateTodoRequest.title, updateTodoRequest.body)
    } catch (e: IllegalArgumentException) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).build()
    }
    return ResponseEntity .status(HttpStatus.OK).body(todoService.updateTodo(todoId, updateTodoRequest))
}
//TodoController.kt

범위를 체크하고, 범위를 벗어날 경우 exception을 보냈다. exception에 걸릴 경우 400을 보내고, 그렇지 않을 경우에는 기존의 200코드를 보내도록 작성하였다.

private fun validateTodoLength(title: String, body: String) {
    val minTitle = 1
    val maxTitle = 200
    val minBody = 1
    val maxBody = 1000

    if (title.length !in minTitle..maxTitle) {
        throw IllegalArgumentException("The title must be between $minTitle and $maxTitle characters")
    }

    if (body.length !in minBody..maxBody) {
        throw IllegalArgumentException("The body must be between $minBody and $maxBody characters")
    }
}
//TodoController.kt

피드백 과정에서 @Valid라는 키워드를 알게 되었다.

 

@Valid

아무리 봐도 Validation의 약자로 보인다.. Validation은 말 그대로 검정(확인)이라는 뜻. SpringBoot에서는 해당 어노테이션을 사용해 @RequestBody객체를 사용자로부터 가져올때, 들어오는 값들을 검증할 수 있다.

valid annotaion을 사용하기 위해선 gradle에 아래의 dependency를 추가해주어야 한다. (이부분에서 상당히 애를 먹었다. 작동이 안되는 원인... 추가를 안해서..)

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-validation'
}

@Valid에는 다양한 검정 키워드들이 존재하지만, 이번 포스트에서 사용될 키워드는 아무래도, @NotBlank, @Size 일 것 같다.

Annotation Type Description valiable
@Size CharSequence, Collection, Map, Array 해당 크기가 지정된 경계(포함)사이에 있어야 한다. int : max (element의 크기가 작거나 같다.)
int : min (element의 크기가 크거나 같다.)
@NotBlank   null이 아닌 값.
공백이 아닌 문자 하나 이상을 포함한다.
 

 

수정된 코드

data class CreateTodoRequest (
    @field:NotBlank(message = "Title must not be blank")
    @field:Size(min = 1, max = 200, message = "Title must between 1 and 200")
    val title: String,
    @field:NotBlank(message = "Author must not be blank")
    val author: String,
    @field:NotBlank(message = "Body must not be blank")
    @field:Size(min = 1, max = 1000, message = "Body must between 1 and 1000")
    val body: String,
    val date: LocalDateTime,
)
//CreateTodoRequest.kt

 

data class UpdateTodoRequest (
    @field:NotBlank(message = "Title must not be blank")
    @field:Size(min = 1, max = 200, message = "Title must between 1 and 200")
    val title: String,
    @field:NotBlank(message = "Author must not be blank")
    val author: String,
    @field:NotBlank(message = "Body must not be blank")
    @field:Size(min = 1, max = 1000, message = "Body must between 1 and 1000")
    val body: String,
    val date: LocalDateTime,
)
//UpdateTodoRequest.kt
@PostMapping
fun createTodo(@Valid @RequestBody createTodoRequest: CreateTodoRequest,
               bindingResult:BindingResult): ResponseEntity<TodoResponse> {
    if(bindingResult.hasErrors()){
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null)
    }
    return ResponseEntity.status(HttpStatus.CREATED).body(todoService.createTodo(createTodoRequest))
}

@PutMapping("/{todoId}")
fun updateTodo(
    @PathVariable todoId: Long,
    @Valid @RequestBody updateTodoRequest: UpdateTodoRequest,
    bindingResult: BindingResult
): ResponseEntity<TodoResponse> {

    if(bindingResult.hasErrors()){
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).build()
    }
    return ResponseEntity .status(HttpStatus.OK).body(todoService.updateTodo(todoId, updateTodoRequest))
}
//TodoController.kt

 

테스트 결과, 기존에 작성한 코드와 동일한 기능을 한다는 것을 알았다. SpringBoot 자체에서 제공하는 기능들에 대해 더 알아보는 시간을 가진 것 같다. 생각보다 제공해주는 기능이 많아, 이를 잘 활용해야겠다는 생각이 들었다.

'PROJECT > Spring Todo 프로젝트' 카테고리의 다른 글

TodoApp 백엔드 서버 만들기 (2)  (0) 2024.05.16
TodoApp 백엔드 서버 만들기 (1)  (0) 2024.05.14