본문 바로가기
Project/깃터디 (gitudy)

[깃터디/Todo] Todo 수정 api 구현

by J-rain 2024. 5. 11.

 

💡 .java를 클릭시 관련 커밋으로 이동💡

 회고 

    // Todo 수정
    @ApiResponse(responseCode = "200", description = "Todo 수정 성공")
    @PutMapping("/{studyInfoId}/todo/{todoId}")
    public JsonResult<?> updateStudyTodo(@AuthenticationPrincipal User user,
                                         @PathVariable(name = "studyInfoId") Long studyInfoId,
                                         @PathVariable(name = "todoId") Long todoId,
                                         @Valid @RequestBody StudyTodoUpdateRequest studyTodoUpdateRequest) {

        studyMemberService.isValidateStudyLeader(user, studyInfoId);

        studyTodoService.updateStudyTodo(studyTodoUpdateRequest, todoId);

        return JsonResult.successOf("Todo update Success");
}

@Valid: Bean Validation API의 일부로, 이 어노테이션은 요청 본문으로 받은 객체(StudyTodoUpdateRequest)의 유효성 검사를 활성화 한다.

예를 들어, StudyTodoUpdateRequest 클래스 내에는 @NotNull, @Size, @Past 등 다양한 제약 조건 어노테이션이 필드 위에 설정될 수 있는데 @Valid 어노테이션을 통해 이러한 제약 조건이 만족하는지 확인하고, 만족하지 않을 경우 유효성 검사 에러를 반환한다!

 

여기서 @Valid 주로 Bean Validation API를 사용하는데 Bean Validation은 자바 객체의 필드에 대한 제약 조건을 정의하고 검증할 수 있는 API이다.

 

예를 들어, HTTP POST 요청으로 전달되는 데이터가 유효한지 확인해야 하는 경우에 @RequestBody@Valid를 함께 사용할 수 있다. 이렇게 함으로써 스프링은 요청 본문의 데이터를 자바 객체로 변환하고, 변환된 객체의 유효성을 검증하여 필요한 경우에는 예외를 발생시킬 수 있다!!

→ 이점: 유효성 검증을 수행하는 이유는 잘못된 또는 부적절한 데이터가 애플리케이션으로 들어오는 것을 방지하기 위해서 👍 데이터의 유효성을 검증함으로써 애플리케이션의 안정성을 높이고, 예상치 못한 오류를 방지한다.

 

 

StudyTodo.java

 public void updateStudyTodo(String title, String detail, String todoLink, LocalDate todoDate) {
        this.title = title;
        this.detail = detail;
        this.todoLink = todoLink;
        this.todoDate = todoDate;
    }

StudyTodo 클래스에 인스턴스 변수 업데이트(setter) 메서드 추가

 

 

StudyTodoController.java

    // Todo 수정
    @ApiResponse(responseCode = "200", description = "Todo 수정 성공")
    @PutMapping("/{studyInfoId}/todo/{todoId}")
    public JsonResult<?> updateStudyTodo(@AuthenticationPrincipal User user,
                                         @PathVariable(name = "studyInfoId") Long studyInfoId,
                                         @PathVariable(name = "todoId") Long todoId,
                                         @Valid @RequestBody StudyTodoUpdateRequest studyTodoUpdateRequest) {

        studyMemberService.isValidateStudyLeader(user, studyInfoId);

        studyTodoService.updateStudyTodo(studyTodoUpdateRequest, todoId);

        return JsonResult.successOf("Todo update Success");
}

Todo 수정 Controller 구현

Todo 수정역시 리더만 가능 studyMemberService.isValidateStudyLeader(user, studyInfoId);

클라이언트로부터 받은 JSON 요청 본문(Request Body)을 Java 객체로 변환하고, 이 객체에 대한 검증을 수행

 

 

StudyTodoUpdateRequest.java

@Getter
@Builder
@AllArgsConstructor
public class StudyTodoUpdateRequest {

    @Size(max = 20, message = "제목 20자 이내")
    private String title;        // To do 이름

    @Size(max = 50, message = "설명 50자 이내")
    private String detail;       // To do 설명

    private String todoLink;     // To do 링크

    private LocalDate todoDate;  // To do 날짜

}

수정 DTO 추가

 

 

StudyTodoService.java

    // Todo 수정
    @Transactional
    public void updateStudyTodo(StudyTodoUpdateRequest request, Long todoId) {

        // To do 조회
        StudyTodo studyTodo = studyTodoRepository.findById(todoId).orElseThrow(() -> {
            log.warn(">>>> {} : {} <<<<", todoId, ExceptionMessage.TODO_NOT_FOUND.getText());
            return new TodoException(ExceptionMessage.TODO_NOT_FOUND);
        });

        // 기존 To do 업데이트
        studyTodo.updateStudyTodo(
                request.getTitle(),
                request.getDetail(),
                request.getTodoLink(),
                request.getTodoDate());

    }

기존 Todo 업데이트 Service 구현


테스트

 

StudyTodoFixture.java

    // 테스트용 To do 수정
    public static StudyTodoUpdateRequest updateStudyTodoRequest(String title, String detail, String todoLink, LocalDate todoDate) {
        return StudyTodoUpdateRequest.builder()
                .title(title)
                .detail(detail)
                .todoLink(todoLink)
                .todoDate(todoDate)
                .build();
    }

테스트용 Todo Fixture 설정

 

 

StudyTodoControllerTest.java

    @Test
    public void Todo_수정_테스트() throws Exception {

        //given
        User savedUser = userRepository.save(generateAuthUser());
        Map<String, String> map = TokenUtil.createTokenMap(savedUser);
        String accessToken = jwtService.generateAccessToken(map, savedUser);
        String refreshToken = jwtService.generateRefreshToken(map, savedUser);

        StudyInfo studyInfo = StudyInfoFixture.createDefaultPublicStudyInfo(savedUser.getId());
        studyInfoRepository.save(studyInfo);

        StudyTodo studyTodo = StudyTodoFixture.createStudyTodo(studyInfo.getId());
        studyTodoRepository.save(studyTodo);

        StudyTodoUpdateRequest updateRequest = StudyTodoUpdateRequest.builder()
                .title(studyTodo.getTitle())
                .detail(studyTodo.getDetail())
                .todoLink(studyTodo.getTodoLink())
                .todoDate(studyTodo.getTodoDate())
                .build();

        //when
        doNothing().when(studyMemberService).isValidateStudyLeader(any(User.class), any(Long.class));
        doNothing().when(studyTodoService).updateStudyTodo(any(StudyTodoUpdateRequest.class), any(Long.class));

        //then
        mockMvc.perform(put("/study/" + studyInfo.getId() + "/todo/" + studyTodo.getId())
                        .contentType(MediaType.APPLICATION_JSON)
                        .header(AUTHORIZATION, createAuthorizationHeader(accessToken, refreshToken))
                        .content(objectMapper.writeValueAsString(updateRequest)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.res_code").value(200))
                .andExpect(jsonPath("$.res_msg").value("OK"))
                .andExpect(jsonPath("$.res_obj").value("Todo update Success"))
                .andDo(print());

    }

컨트롤러 동작 검증 ( HTTP 요청을 올바르게 처리하고 적절한 응답을 반환하는지 확인)

 

 

StudyTodoServiceTest.java

    @Test
    @DisplayName("Todo 수정 테스트")
    public void updateTodo() {

        //given
        User leader = userRepository.save(generateAuthUser());

        StudyInfo studyInfo = StudyInfoFixture.createDefaultPublicStudyInfo(leader.getId());
        studyInfoRepository.save(studyInfo);

        StudyTodo studyTodo = StudyTodoFixture.createStudyTodo(studyInfo.getId());
        studyTodoRepository.save(studyTodo);

        String updatedTitle = "제목변경";
        String updatedDetail = "설명변경";
        String updatedTodoLink = "링크변경";
        LocalDate updatedTodoDate = LocalDate.now().plusDays(3);

        StudyTodoUpdateRequest request = StudyTodoFixture.updateStudyTodoRequest(updatedTitle, updatedDetail, updatedTodoLink, updatedTodoDate);

        // when
        studyTodoService.updateStudyTodo(request, studyTodo.getId());

        // then
        StudyTodo updatedTodo = studyTodoRepository.findById(studyTodo.getId())
                .orElseThrow(() -> new TodoException(ExceptionMessage.TODO_NOT_FOUND));
        assertEquals(updatedTitle, updatedTodo.getTitle());
        assertEquals(updatedDetail, updatedTodo.getDetail());
        assertEquals(updatedTodoLink, updatedTodo.getTodoLink());
        assertEquals(updatedTodoDate, updatedTodo.getTodoDate());
    }

Todo 수정 Service 테스트

댓글