스프링과 JPA 기반 웹 애플리케이션 개발 #43 관심 주제 삭제 (+ Spring Data Jpa 메소드들)

Jake Seo·2021년 6월 9일
0

스프링과 JPA 기반 웹 애플리케이션 개발 #43 관심 주제 삭제

해당 내용은 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발의 강의 내용을 바탕으로 작성된 내용입니다.

강의를 학습하며 요약한 내용을 출처를 표기하고 블로깅 또는 문서로 공개하는 것을 허용합니다 라는 원칙 하에 요약 내용을 공개합니다. 출처는 위에 언급되어있듯, 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발입니다.

제가 학습한 소스코드는 https://github.com/n00nietzsche/jakestudy_webapp 에 지속적으로 업로드 됩니다. 매 커밋 메세지에 강의의 어디 부분까지 진행됐는지 기록해놓겠습니다.


AccounetService

removeTag() 메소드 추가

    public void removeTag(Account account, Tag tag) {
        // And Or 등 무엇무엇이 더 가능한지 찾아보기... Spring Data Jpa 강의의 필요성
        AccountTag accountTag = accountTagRepository.findByAccountAndTag(account, tag);
        accountTagRepository.delete(accountTag);
    }

여기서 처음으로 Spring Data Jpa 에서 제공하는 findByOneAndTwo() 패턴을 써봤다. And 붙이는 것 외에도 Or 등 많다.

참고 링크: https://www.amitph.com/spring-data-jpa-query-methods/

위 참고 링크에 들어가면 Spring Data Jpa 에서 기본으로 제공하는 메소드들을 볼 수 있다.

SettingsController

removeTag() 메소드 추가

    /**
     * 사실 태그를 실 서비스에서 사용할 때는
     * 관리자모드로 사용하지 않거나 참조되지 않는 태그들을 정리할 수 있는 기능도 생각해봐야 함
     */
    @PostMapping(REMOVE_TAGS_MAPPING_PATH)
    @ResponseBody
    public ResponseEntity removeTag(@LoginAccount Account loginAccount, @Valid @RequestBody TagForm tagForm) {
        // Controller 에서의 Repository 이용은 딱 조회까지만 해서 Service 로직에 넘어가야 할 데이터를 만드는 부분까지만!
        // 태그가 존재하지 않는다면, badRequest 반환하기!
        Optional<Tag> optionalTag = tagRepository.findByTitle(tagForm.getTagTitle());

        optionalTag.ifPresent(
                (tag) -> accountService.removeTag(loginAccount, tag)
        );

        if(optionalTag.isEmpty()){
            return ResponseEntity.badRequest().build();
        }

위와 같이 처리했는데, Optional 쓰다보니 상당히 괜찮은 것 같다. ifPresent()해서 값이 있으면 처리하고, isEmpty()로 내부 값이 없는지 확인하고 없다면, badRequest()를 반환했다. a == null 보다 조금 안정적인 느낌이다.

SettingsControllerTest

    @DisplayName("태그 추가 - 정상 케이스")
    @WithAccount(nickname = "jake")
    @Test
    public void addTagCorrect() throws Exception{

        TagForm tagForm = new TagForm();
        tagForm.setTagTitle("안녕하세요.");

        mockMvc.perform(post("/" + SettingsController.ADD_TAGS_MAPPING_PATH)
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(tagForm))
                .with(csrf()))
                .andExpect(status().isOk());

        assertTrue(accountTagRepository.count() == 1);

        List<AccountTag> accountTagList = accountTagRepository.findAll();

        for (AccountTag accountTag : accountTagList) {
            System.out.println("accountTag = " + accountTag.getTag().getTitle() + ", " + accountTag.getAccount().getNickname());
        }
    }

    @DisplayName("태그 추가 후 삭제 - 정상 케이스")
    @WithAccount(nickname = "jake")
    @Test
    public void removeTagCorrect() throws Exception{

        TagForm tagForm = new TagForm();
        tagForm.setTagTitle("안녕하세요.1234");

        mockMvc.perform(post("/" + SettingsController.ADD_TAGS_MAPPING_PATH)
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(tagForm))
                .with(csrf()))
                .andExpect(status().isOk());

        mockMvc.perform(post("/" + SettingsController.REMOVE_TAGS_MAPPING_PATH)
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(tagForm))
                .with(csrf()))
                .andExpect(status().isOk());
    }

    @DisplayName("태그 삭제 - 비정상 케이스")
    @WithAccount(nickname = "jake")
    @Test
    public void removeTagWrong() throws Exception{

        TagForm tagForm = new TagForm();
        tagForm.setTagTitle("안녕하세요.1234");

        mockMvc.perform(post("/" + SettingsController.REMOVE_TAGS_MAPPING_PATH)
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(tagForm))
                .with(csrf()))
                .andExpect(status().is4xxClientError());
    }

괜히 시키지도 않은 테스트를 만들어보았다.

mockMvc.perform()을 수행할 때, 당연히 @RequestBody로 데이터를 받는 컨트롤러 메소드에는 JSON 형태로 데이터를 주었어야 했는데, .contentType(), content()를 기존에 써보지 않아서 처음에 조금 헤맸다.

결국 HTTP 통신의 원리에서 벗어나지 않으니 헷갈려 하지 말자.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글