본문 바로가기
프로젝트/Recipository

[Dev] 22.12.22. 게시글 수정 2 (feat. Entity field update method)

by 규글 2022. 12. 22.

 Entity는 DB와의 data를 교환하고, DB의 data를 변경할 수 있기 때문에 setter method의 사용이 지양된다고 한다. 이와 같은 내용이 언급된 블로그들을 다른 정보를 찾기 위해서 웹을 돌아다니다가 발견했고, 이에 대해 극단적인 필자는 이를 최대한 피하고자 노력했다. 하지만 필자는 어쩌면 괜한 고집으로 일을 어렵게 만들고 있었는지도 모른다.

 

 '지양' 이라는 단어는 어떠한 것을 하지 않는다는 의미이긴 하지만, 이것이 절대적으로 하면 안된다는 의미로 사용되지는 않는다. 필자가 듣는 강의는 아니지만, 온라인 강의 컨텐츠를 만든 한 강사는 너무 강박을 가질 필요는 없다고 말하면서 method의 이름도 의미있게 만들 것을 제안한다.[각주:1] 필자는 이미 이전 과정에서 Recipe Dto로부터 Recipe Entity로의 변환 시, List<Link>의 각 항목에 대해 foreign key 값을 위해 Recipe 자신의 정보를 setting 하도록 한 method를 활용하고 있었다. 이와 마찬가지로, client로부터 전달받은 data로 Entity의 field 중 일부를 update 하는 method를 작성해 활용하는 방식으로 직전 게시글의 작업을 수정해보려고 한다.

 

 

작업

Recipe.java

    public void updateRecipe(String newTitle, String newContent,
                             String newImagePath, boolean newBePublic,
                             List<Link> newLink){
        this.title = newTitle;
        this.content = newContent;
        if(newImagePath != null){
            this.imagePath = newImagePath;
        }
        this.bePublic = newBePublic;
        this.link = newLink;
    }

  기존에 List<Link> 만을 update 해주던 method를 지우고, client의 update form으로부터 넘겨받은 Recipe Dto와 수정된 List<Link> 를 인자로 받아서 Recipe Entity의 field 값을 수정해주는 method를 만들었다. Image path의 경우는 client로부터 전달받은 data가 null이 아니어서 Recipe Dto에 data가 있을 때만 update 하도록 했다.

 

Link.java

    public void updateLink(String newLink){
        this.link = newLink;
    }

  Link Entity에도 마찬가지로, Entity의 field를 새로운 String 값으로 수정해주는 method를 만들었다.

 

RecipeServiceImpl.java

    // 게시글 수정
    @Override
    public boolean update(Long contentId, RecipeDto recipeDto, MultipartFile imageFile) {
        try {
            // 기존 DB의 data에 대해
            Recipe recipe = recipeRepository.getRecipeByContentId(contentId);

            // 넘겨받은 file이 있다면 수정 정보가 담긴 recipeDto에 setting 하고
            if(imageFile != null){
                String savePath = this.savePath + File.separator;

                // 넘겨받은 file name
                String originFileName = imageFile.getOriginalFilename();
                // save file name에 사용할 UUID String
                String uuid = UUID.randomUUID().toString();
                // save file name
                String saveFileName = uuid + originFileName;

                // directory에 upload file save
                imageFile.transferTo(new File(savePath + saveFileName));
                // RecipeDto에 image save path setting
                recipeDto.setImagePath(savePath + saveFileName);
            }

            // 기존 DB의 Link data 와 새로 변경할 Link data에 대해
            List<Link> linkList = recipe.getLink();
            List<String> newLinkList = recipeDto.getLink();

            int dbLength = linkList.size();
            int newLength = newLinkList.size();

            // link 수가 줄어든 경우
            if(dbLength > newLength){
                // 줄어든 만듬 List에서 제거하고
                linkList.subList(newLength, dbLength).clear();
                // 나머지에 대해 data update
                for(int i = 0; i < newLength; i++){
                    linkList.get(i).updateLink(newLinkList.get(i));
                }
            } else {
                for(int i = 0; i < newLength; i++){
                    // link 수가 늘어난 index 일 경우
                    if(newLength > dbLength && i > dbLength - 1){
                        linkList.add(Link.builder()
                                    .recipe(recipe)
                                    .build());
                    }
                    // 일반적으로는 data를 update
                    linkList.get(i).updateLink(newLinkList.get(i));
                    // link 수가 그대로인 경우 멈춤
                    if(i == dbLength - 1 && dbLength == newLength){
                        break;
                    }
                }
            }

            // 기존 Entity에 수정한 data의 값들을 전달하여 변경한 뒤 save
            recipe.updateRecipe(recipeDto.getTitle(), recipeDto.getContent(),
                                recipeDto.getImagePath(), recipeDto.isBePublic(),
                                linkList);
            recipeRepository.save(recipe);

            return true;
        } catch (Exception e) {
            return false;
        }
    }

 코드에 대한 이미지를 쭉 나열했다. 왼쪽은 Entity의 field를 변경하는 method를 생성하기 전, 오른쪽은 그 후이며 하나만 있는 것은 그대로인 부분이다. 비교해보면 알겠지만 그렇게 큰 변화는 아니다. 괜히 Link Entity에 mapping 된 DB로부터의 data를 잃으면서까지 Recipe Entity를 Dto로 변환하고, 일부 data를 수정하고서 다시 Entity로 변환한 뒤 온전하지 않은 List<Link> 는 따로 다시 setting 하는 과정이 간결해진 것이 전부이다.

 

            // link 수가 줄어든 경우
            if(dbLength > newLength){
                // 줄어든 만듬 List에서 제거하면 link 수가 동일한 것과 같음
                linkList.subList(newLength, dbLength).clear();
            // link 수가 늘어난 index 일 경우
            } else if(dbLength < newLength){
                linkList.add(Link.builder()
                        .recipe(recipe)
                        .build());
            }
            // 앞선 if문 분기로 기존 List와 새 List의 size는 같아진다.
            for(int i = 0; i < newLength; i++){
                // data를 update
                linkList.get(i).updateLink(newLinkList.get(i));
            }

 조금 더 줄이자면 참고 링크의 수가 줄었을 때, List<Link> 에서 항목을 지운 순간 data의 수가 그대로인 것과 동일하기 때문에 if문 안쪽의 for문을 지우면서 분기했던 if문을 독립시키고 for문은 통합할 수 있다.

 

 

 이번 게시글은 각 Entity에 method를 새로 추가하고, 그에 맞게 Service의 update logic을 살짝 수정한 정도의 작업이다. 기능은 이전과 동일하게 동작한다. 이제 게시글을 삭제하는 기능을 간단히 구현한 뒤 사용자의 정보를 조회하는 마이 페이지에 대한 작업을 해볼 것이다.

 

Reference

댓글