TIL(20240621) [아웃소싱프로젝트: 팔로우 구현]
📌 Spring
💡 아웃소싱 프로젝트 : 팔로우 구현
이번 프로젝트의 주제를 익명게시판으로 했기 때문에 익명게시판/비익명게시판으로 구분하였고, 비익명게시판 조회에서 자신이 팔로우한 사람들의 게시물을 최신순으로 조회할 수 있도록 구현해보았다.
- 팔로우 기능 구현
- 특정 사용자를 팔로우 / 언팔로우를 할 수 있습니다.
- 팔로우 기능이 구현되었다면 뉴스피드에 팔로우하는 사용자의 게시물을 볼 수 있습니다.
- 팔로우를 하고 있는 사람들이 작성한 게시물을 볼 때 정렬 기준은 최신순입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public List<PublicPostResponseDto> getFollowedPosts(User user, int page, int pageSize) {
Pageable pageable = PageRequest.of(page, pageSize);
User currentUser = userService.findByUserSeq(user.getUserSeq());
List<Follow> follows = followRepository.findByFromUser(currentUser);
List<PublicPostResponseDto> publicPostResponseDtos = new ArrayList<>();
for (Follow followerList : follows) {
User followedUser = followerList.getToUser();
Page<PublicPost> publicPosts = postRepository.findByUserOrderByCreatedAtDesc(followedUser, pageable);
if (publicPosts.getTotalElements() == 0) {
throw new CustomException(ErrorCode.POST_EMPTY);
}
for(PublicPost publicPost : publicPosts) {
PublicPostResponseDto responseDto = new PublicPostResponseDto(publicPost);
publicPostResponseDtos.add(responseDto);
}
}
return publicPostResponseDtos;
}
구현한 코드에 대해서 간단하게 설명을 해보자면 userService에서 user의 정보를 가져오고 currentUser가 팔로우한 모든 사용자를 가져온 다음 그 모든 사용자의 publicPost를 가져오고 최신순으로 페이지 처리를 한다. publicPosts가 0 이라면 예외를 날리고 아니라면 reponseDto에 객체에 publicPost를 담아서 반환한다.
정말 간단한거 같지만 코드 구현하는데 엄청 머리가 터질뻔했다..ㅎㅎㅎ;;; 너무 복잡하고 일단 FromUser와 ToUser가 헷갈리기 시작하면서 Follwing과 Followers도 헷갈리고..
어쨋든 정리하자면 FromUser = “나” ToUser = “내가 팔로우한 사람” List
1
2
3
if (publicPosts.getTotalElements() == 0) {
throw new CustomException(ErrorCode.POST_EMPTY);
}
그리고 위 부분에서 다른 포스트 전체조회에서는 되지만 나를 팔로우한 사람들의 게시물 전체조회에서는 예외처리가 되지않아 아래와 같이 수정하였다.
1
2
3
if (publicPosts.isEmpty()) {
throw new CustomException(ErrorCode.POST_EMPTY);
}
추가로 controller 쪽에서 page처리
1
2
3
4
5
6
7
8
@GetMapping("/followed")
public ResponseEntity<List<PublicPostResponseDto>> getFollowedPosts(
@AuthenticationPrincipal UserDetailsImpl userDetails, @RequestParam("page") int page, @RequestParam(value = "size", defaultValue = "5") int pageSize) {
return ResponseEntity.status(HttpStatus.OK)
.body(postService.getFollowedPosts(userDetails.getUser(), page - 1, pageSize));
}
이전에는 pagesize를 상수로 지정해놓는 형식으로 하였으나 나중에 추후에 유지보수측면에서 좋지 않을 것으로 판단되어 pageSize를 param으로 받을 수 있도록 했다. 그리고 pageSize를 별도로 넘겨주지 않을 경우 defaultvalue를 통해서 5로 지정해놓았다. (5개만 보이도록 지정해놓았다)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class FollowController {
private final UserService userService;
private final FollowService followService;
// 팔로우
@PostMapping("/follower")
public ResponseEntity<String> followUser(@RequestBody @Valid FollowRequestDto requestDto, @AuthenticationPrincipal UserDetailsImpl userDetails) {
User toUser = userService.findByUserSeq(requestDto.getToUserSeq());
User fromUser = userService.findByUserSeq(userDetails.getUser().getUserSeq());
followService.followUser(toUser, fromUser);
return ResponseEntity.status(HttpStatus.OK).body(toUser.getUserId() + " 님을 팔로우 하였습니다.");
}
// 언팔로우
@DeleteMapping("/follower")
public ResponseEntity<String> deleteFollowUser(@RequestBody @Valid FollowRequestDto requestDto, @AuthenticationPrincipal UserDetailsImpl userDetails) {
User toUser = userService.findByUserSeq(requestDto.getToUserSeq());
User fromUser = userService.findByUserSeq(userDetails.getUser().getUserSeq());
followService.deleteFollowUser(toUser, fromUser);
return ResponseEntity.status(HttpStatus.OK).body(toUser.getUserId() + " 님을 팔로우취소 하였습니다.");
}
// 내가 팔로우한 사람들 조회
@GetMapping("/followers")
public ResponseEntity<List<FollowResponseDto>> getFollowings(@AuthenticationPrincipal UserDetailsImpl userDetails) {
User user = userService.findByUserSeq(userDetails.getUser().getUserSeq());
List<FollowResponseDto> followers = followService.getFollowings(user);
return ResponseEntity.status(HttpStatus.OK).body(followers);
}
}
코드 풀이 필요
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Service
@RequiredArgsConstructor
public class FollowService {
private final FollowRepository followRepository;
@Transactional
public void followUser(User toUser, User fromUser) {
if(fromUser.getUserSeq().equals(toUser.getUserSeq())) {
throw new CustomException(ErrorCode.SAME_USER);
}
Optional<Follow> checkFollow = followRepository.findByFromUserAndToUser(fromUser, toUser);
if(checkFollow.isPresent()) {
throw new CustomException(ErrorCode.ALREADY_FOLLOW);
}
Follow follow = new Follow(fromUser, toUser);
followRepository.save(follow);
}
@Transactional
public void deleteFollowUser(User toUser, User fromUser) {
Optional<Follow> checkFollow = followRepository.findByFromUserAndToUser(fromUser, toUser);
if(checkFollow.isEmpty()) {
throw new CustomException(ErrorCode.RECENT_NOT_FOLLOW);
}
followRepository.delete(checkFollow.get());
}
@Transactional(readOnly = true)
public List<FollowResponseDto> getFollowings(User user) {
List<FollowResponseDto> followResponseDtoList = new ArrayList<>();
for(Follow follow : user.getFollowings()) {
followResponseDtoList.add(new FollowResponseDto(follow.getToUser()));
}
return followResponseDtoList;
}
}
팔로우한 사람들 조회 같은경우 user.getFollowings가 비어있는 경우 예외처리가 필요했으나 하지 않으니…… 빈 리스트가 떴고,
1
2
3
4
5
6
7
8
9
10
11
12
@Transactional(readOnly = true)
public List<FollowResponseDto> getFollowings(User user) {
List<FollowResponseDto> followResponseDtoList = new ArrayList<>();
List<Follow> followings = user.getFollowings();
if (followings.isEmpty()) {
throw new CustomException(ErrorCode.EMPTY_FOLLOW);
}
for (Follow follow : followings) {
followResponseDtoList.add(new FollowResponseDto(follow.getToUser()));
}
return followResponseDtoList;
}
ErrorCode.EMPTY_FOLLOW 해당 에러코드를 만들어 exception처리했다. 정작 코드를 구현할 때는 생각도 못하다가 뒤늦게 테스트를 할 때 이러한 부분을 깨닫곤 한다. 아직 예외처리에 대한 경각심이 부족한게지…..😅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Getter
@Entity
@NoArgsConstructor
public class Follow extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "follow_id")
private Long id;
@ManyToOne
@JoinColumn(name = "from_user")
private User fromUser;
@ManyToOne
@JoinColumn(name = "to_user")
private User toUser;
public Follow(User fromUser, User toUser){
this.fromUser = fromUser;
this.toUser =toUser;
}
}
코드 풀이 필요