[Spring Boot] 백오피스 프로젝트에 팔로잉/팔로워 기능 구현하기
- 유저 A는 유저 B를 팔로우 하고 팔로우 취소할 수 있다.
- 각 유저들의 팔로잉/팔로워 목록을 확인할 수 있다.
팔로잉 기능은 유저가 유저를 팔로잉하므로 연관관계를 설계하는 것이 조금 헷갈렸다. 팔로우 엔티티를 만들어서 유저와 두 번 매핑을 했는데, 이때 유저 A가 유저 B를 팔로잉 하는 순간 유저 A는 유저 B의 팔로워가 되므로 팔로우 엔티티와 유저 엔티티는 반대의 이름으로 각각 매핑이 된다... 코드를 통해 조금 더 자세히 보기로 하자
우선 팔로우 테이블과 유저 테이블은 1:n 매핑이다. 다만 유저 한 명이 여러명을 팔로잉 할 수 있고, 또 여러명의 팔로워를 가질 수 있으므로 2번의 1:n 매핑을 가지게 되는 것이다. 팔로우를 하는 유저(follower)와 팔로잉을 당하는 유저(following)가 같이 저장되도록 하였다.
public class Follow {
@ManyToOne
@JoinColumn(name = "follower_id")
private User follower;
@ManyToOne
@JoinColumn(name = "following_id")
private User following;
}
이제 유저 테이블의 @OneToMany
를 살펴보자. follwerList는 following과 mappedby되고, follwingList는 follower와 mappedby된다. 여기서 following은 팔로우당하는 유저(B)이고 생성된 유저 객체 A는 유저 B의 팔로워가 되므로 followerList에 추가되는 것이다. 반대로, 생성된 유저 객체 B가 A의 팔로잉이 되므로 follower A는 followingList에 추가된다.
정리하자면 A(followr)가 B(following)를 팔로우할 때,
- A의 팔로잉 리스트 -> B 추가
- B의 팔로워 리스트 -> A 추가
public class User {
@OneToMany(mappedBy = "following")
private List<Follow> followerList;
@OneToMany(mappedBy = "follower")
private List<Follow> followingList;
}
이제 구현은 어렵지 않게 할 수 있다. 다음은 서비스 로직에서의 follow 메서드이다.
public class FollowService {
public ResponseEntity<String> followUser(Long followingId, User follower) {
if(followingId.equals(follower.getId())){
throw new GlobalCustomException(ExceptionType.SELF_FOLLOW);
}
Optional<Follow> checkFollow = followRepository.findByFollowingIdAndFollowerId(followingId, follower.getId());
if(checkFollow.isPresent()){
throw new GlobalCustomException(ExceptionType.ALREADY_FOLLOW);
}
User following = findUser(followingId);
Follow follow = new Follow(follower, following);
followRepository.save(follow);
return new ResponseEntity<>(following.getNickname() + "님을 팔로우하였습니다.", HttpStatus.OK);
}
}
우선 팔로우할 유저의 id를 받아 해당 user 객체를 find한다. 팔로우를 하는 유저는 현재 로그인 된 유저이다. follower 유저와 folloing 유저를 넣고 follow 객체를 생성한뒤 레포지토리에 저장해주면 끝이다. 언팔로우의 경우 follow repository에서 찾아 삭제해주면 끝이므로 생략하겠다.
이제 유저의 팔로잉 리스트를 조회해보자.
public FollowingResponseDto getFollowings(Long userId) {
User user = findUser(userId);
List<String> followingUserList = new ArrayList<>();
for(Follow follow : user.getFollowingList()){
followingUserList.add(follow.getFollowing().getNickname());
}
return new FollowingResponseDto(followingUserList);
}
이 유저의 팔로잉 목록은 이미 위에서 연관관계 매핑을 해두었기 때문에 user 객체의 followingList를 그대로 가져오면 된다. 다만, followingList는 follow 객체가 저장되어 있기 때문에 유저의 닉네임을 가져와서 새로운 리스트에 저장하도록 구현하였다. 팔로워 리스트의 로직 또한 동일하다. user의 followerList를 get 해주면 된다.
다음과 같이 '홍길동'이라는 유저를 팔로우하고 팔로잉 리스트를 조회하면 '홍길동'이 나오는 것을 확인할 수 있다.