CloneInstargram 구현 이슈

이동규·2023년 7월 31일

해시 태그 기능 구현 중 영속성 이슈

  • 해시 태그 기능을 구현하는 과정에서 board 와 HashTag 를 ManyToMany 관계를 맺기 위해 중간 테이블 Tag_Board를 생성해 주었다. 이를 통해 만약 Tag_Board에 board와 HashTag가 존재하지 않는 다면 HashTag 에 태그 String을 저장하고 board와 HashTag의 관계를 Tag_Board에 저장하는 방식으로 구현을 하였다. 여기서 createBoard 메서드 상에서 board를 생성하고 생성 과정에서 적은 tag를 처리하는 로직을 board가 1차 캐시에 저장시키기 전에 처리를 하게 되어 영속성 이슈가 생기는 것을 확인 했다.
  • 해결책
    • board를 1차 캐시에 먼저 저장을 한 후 tag 처리 로직을 수행하도록 변경하였다.

      // JPA에서 관계를 맺고 있는 엔티티의 영속성 처리 문제를 위해
      // 먼저 board를 1차 캐시에 save를 보냄
              boardRepository.save(board);
      
              boardRequestDto.setHashtags(boardRequestDto.getHashtags());
      
              for(String hashTag : boardRequestDto.getHashtags()){
                  String hashTagString = hashTag.substring(1);
                  HashTag existHashTag = hashTagRepository.findByHashTag(hashTagString);
                  if(existHashTag != null){
                      Tag_Board tag_board = new Tag_Board(existHashTag, board);
                      tag_boardRepository.save(tag_board);
                  }else {
                      HashTag hashTagTable = new HashTag(hashTagString);
                      hashTagRepository.save(hashTagTable);
                      Tag_Board tag_board = new Tag_Board(hashTagTable, board);
                      tag_boardRepository.save(tag_board);
                  }
              }

새로 구현 해본 기능

  • Following/Follower 기능 구현
    • 이를 구현 시키기 위해 Follow Entity를 생성한 후 Follow 안에 Member와의 연관 관계 Column을 follower와 following 두가지를 처리하기 위해 2개를 선언해 주었다. 이를 통해 Member와 Follow 간의 OneToMany 연관 관계가 2개가 형성되도록 하여 구현을 하였다.
    • 처음에는 구글링을 통해 셀프 참조 형태를 써보려고 했지만 자기 자신도 팔로워 팔로잉 리스트에 들어가는 트러블이 생겨서 위와 같이 수정하였다. 그렇게 특정 유저가 팔로잉 또는 팔로워 수를 조회하는 과정에서도 정확한 수가 입력되는 것도 확인하였다.
      • 초기 셀프 참조

        @ManyToOne
            @JoinColumn
            private User userFollowing = this;
        
            @ManyToOne
            @JoinColumn
            private User userFollower = this;
        
            @OneToMany(mappedBy = "userFollowing")
            private List<User> followingList = new ArrayList<User>();
        
            @OneToMany(mappedBy = "userFollower")
            private List<User> followerList = new ArrayList<User>();
        
            public void addFollowing(User following) {
                this.followingList.add(following);
        
                if(!following.getFollowerList().contains(this)) {
                    following.getFollowerList().add(this);
                }
                //연관관계의 주인을 통한 확인
                if(!following.getUserFollower().getFollowerList().contains(this)) {
                    following.getUserFollower().getFollowerList().add(this);
                }
            }
            public void addFollower(User follower) {
                this.followerList.add(follower);
        
                if(follower.getFollowingList().contains(this)) {
                    follower.getFollowingList().add(this);
                }
                //연관관계의 주인을 통한 확인
                if(!follower.getUserFollowing().getFollowingList().contains(this)) {
                    follower.getUserFollowing().getFollowingList().add(this);
                }
            }
      • 수정 코드

        @Entity
        @Getter
        @NoArgsConstructor
        public class Follow {
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            private Long id;
        
            @ManyToOne(fetch = FetchType.LAZY)
            @JoinColumn(name = "FOLLOWING_ID", referencedColumnName = "id")
            private Member memberFollowing;
        
            @ManyToOne(fetch = FetchType.LAZY)
            @JsonIgnore
            @JoinColumn(name = "FOLLOWER_ID", referencedColumnName = "id")
            private Member memberFollower;
        
            public Follow(Member memberFollowing, Member memberFollower) {
                this.memberFollowing = memberFollowing;
                this.memberFollower = memberFollower;
            }
        }
        
        //Member Entity
        @Builder.Default
            @OneToMany(mappedBy = "memberFollowing", cascade = CascadeType.REMOVE)
            private List<Follow> followingList = new ArrayList<>();
        
            @Builder.Default
            @OneToMany(mappedBy = "memberFollower", cascade = CascadeType.REMOVE)
            private List<Follow> followerList = new ArrayList<>();
        
        //serivce Logic
        @Transactional
        	  public ResponseEntity<ResponseMsgDto<Void>> follow(String nickName,
        																														Member member) {
                Member followMember = memberRepository.findByNickName(nickName)
        																																	.orElseThrow(
                        () -> new CustomException(ErrorCode.NOT_FOUND_USER));
        
                Follow follow = new Follow(member, followMember);
        
                Follow existFollow = followRepository.existFollow(member.getNickName(),
        																											followMember.getNickName());
        
                if (existFollow != null) {
                    followRepository.delete(existFollow);
                    return ResponseEntity.ok(ResponseMsgDto.setSuccess(HttpStatus.OK.
        																										value(), "팔로우 취소", null));
                } else {
                    followRepository.save(follow);
                }
                return ResponseEntity.ok(ResponseMsgDto.setSuccess(HttpStatus.OK.
        																										value(), "팔로우 등록", null));
        
            }
profile
진짜 개발자가 되고 싶다

1개의 댓글

comment-user-thumbnail
2023년 7월 31일

정보 감사합니다.

답글 달기