2024. 6. 10. 09:23ㆍT.I.L
오늘 한 일
- Web 강의 수강 - 1억 연봉 개발자 특강
좋아요 엔티티를 만들기 위해 기존 프로젝트에서 이슈를 생성했다.

이제는 이슈 생성과 pr을 올리는 방법을 어느정도 터득한거같다.. 아직 어렵긴 하지만!
좋아요 엔티티에 대한 요구사항을 보면 하나의 엔티티에서 댓글 좋아요 / 게시글 좋아요를 모두 구현하도록 설계되어있었다.

요구사항을 지키기 위해 하나의 엔티티에서 처리하도록 만들어줬다.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "likes_seq")
private Long likesSeq;
@Column(name = "user_id")
private Long userId;
@Column(name = "content_id", nullable = false)
private Long contentId;
@Column(name = "content_type", nullable = false)
@Enumerated(value = EnumType.STRING)
private LikeEnum contentType;
그리고, 해당 좋아요가 댓글의 좋아요인지 게시물의 좋아요인지 구분하기 위해 contentType을 enum으로 넘겨주었다.
public enum LikeEnum {
COMMENT(ContentType.COMMENT),
NEWSFEED(ContentType.NEWSFEED);
private final String contentType;
LikeEnum(String contentType) {this.contentType = contentType; }
public static class ContentType{
public static final String COMMENT = "COMMENT";
public static final String NEWSFEED = "NEWSFEED";
}
}
기존에 User에서 enum으로 상태를 표현한 경험이 있어 수월하게 구현했다. 문자열로 저장하기 위해 엔티티에서 STRING으로 저장해줬다.
컨트롤러의 경로를 보면 알 수 있겠지만, 게시물과 댓글의 경로는 엄연히 다르다.
하지만 동일한 기능을 구현하고 있기 때문에 따로 분리해주지 않고 하나의 컨트롤러에서 동작하도록 작성해줬다.
좋아요 추가 및 삭제 기능은 GetMapping으로 service로 넘어가면 해당 객체가 존재하는지 아닌지를 판별하고 각 상황에 맞는 동작을 하도록 작성해줬다.
@Tag(name = "Likes API", description = "Likes API 입니다")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/newsfeeds")
public class LikeController {
private final LikeService likeService;
// 게시물별 좋아요 수 조회
@Operation(summary = "getNewsfeedsLikes", description = "뉴스피드별 좋아요 수 조회 기능입니다.")
@GetMapping("/{newsfeedId}/like/count")
public LikeCountResponseDto getNewsfeedsLikes(@PathVariable("newsfeedId") Long newsfeedId) {
return new LikeCountResponseDto(likeService.getLikesCount(newsfeedId, LikeEnum.NEWSFEED));
}
// 댓글별 좋아요 수 조회
@Operation(summary = "getCommentsLikes", description = "댓글별 좋아요 수 조회 기능입니다.")
@GetMapping("/{newsfeedId}/comments/{commentsId}/like/count")
public LikeCountResponseDto getCommentsLikes(@PathVariable("commentsId") Long commentsId) {
return new LikeCountResponseDto(likeService.getLikesCount(commentsId, LikeEnum.COMMENT));
}
// 게시물별 좋아요 추가 및 삭제 (토글)
@Operation(summary = "toggleNewsfeedLike", description = "게시물 좋아요 추가 및 삭제 기능입니다.")
@PostMapping("/{newsfeedId}/like")
public ResponseEntity<String> toggleNewsfeedLike(@PathVariable(name = "newsfeedId") long newsfeedId, @AuthenticationPrincipal UserDetailsImpl userDetails) {
return likeService.toggleLike(userDetails.getUser().getUserSeq(), newsfeedId, LikeEnum.NEWSFEED);
}
// 댓글별 좋아요 추가 및 삭제 (토글)
@Operation(summary = "toggleCommentLike", description = "댓글 좋아요 추가 및 삭제 기능입니다.")
@PostMapping("/{newsfeedId}/comments/{commentId}/like")
public ResponseEntity<String> toggleCommentLike(@PathVariable(name = "newsfeedId") long newsfeedId, @PathVariable(name = "commentId") long commentId, @AuthenticationPrincipal UserDetailsImpl userDetails) {
return likeService.toggleLike(userDetails.getUser().getUserSeq(), commentId, LikeEnum.COMMENT);
}
}
구현하면서도 delete와 save를 하나의 함수에서 사용해도 되는지 정확히 모르겠다.
의도는 토글처럼 하나의 함수를 이용하여 삭제 및 추가가 단 1번만 가능하도록 구현한것인데 이 점은 피드백을 받으며 추후 보완해보려고 한다.
@Service
@RequiredArgsConstructor
public class LikeService {
private final LikeRepository likeRepository;
private final NewsfeedRespository newsfeedRespository;
private final CommentRepository commentRepository;
// 게시물, 댓글별 좋아요 수 count
public int getLikesCount(Long contentId, LikeEnum contentType) {
return likeRepository.countByContentIdAndContentType(contentId, contentType);
}
// 게시물, 댓글별 좋아요 toggle 기능
public ResponseEntity<String> toggleLike(Long userId, Long contentId, LikeEnum contentType) {
validateLikeAction(userId, contentId, contentType);
Optional<Like> likeOptional = findLike(userId, contentId, contentType);
if (likeOptional.isPresent()) {
likeRepository.delete(likeOptional.get());
return ResponseEntity.ok("Like removed");
} else {
Like like = new Like(userId, contentId, contentType);
likeRepository.save(like);
return ResponseEntity.ok("Like added");
}
}
// 좋아요 유효성 검사
private void validateLikeAction(Long userId, Long contentId, LikeEnum contentType) {
if (contentType == LikeEnum.NEWSFEED) {
Newsfeed newsfeed = newsfeedRespository.findById(contentId).orElseThrow(() ->
new CustomException(ErrorCode.NEWSFEED_NOT_FOUND));
if (newsfeed.getUser().getUserSeq().equals(userId)) {
throw new CustomException(ErrorCode.NEWSFEED_SAME_USER);
}
} else if (contentType == LikeEnum.COMMENT) {
Comment comment = commentRepository.findById(contentId).orElseThrow(() ->
new CustomException(ErrorCode.COMMENT_NOT_FOUND));
if (comment.getUser().getUserSeq().equals(userId)) {
throw new CustomException(ErrorCode.COMMENT_SAME_USER);
}
}
}
// 좋아요 객체 찾기
private Optional<Like> findLike(Long userId, Long contentId, LikeEnum contentType) {
return likeRepository.findByUserIdAndContentIdAndContentType(userId, contentId, contentType);
}
}
스프링 부트의 security 부분이 어려워 개인 과제 진도가 더뎠었다. 그래서 스프링 부트에 대한 두려움이 있었는데 좋은 팀원분들을 만나 이해도도 크게 향상되고 팀원 분들의 코드와 내 코드를 비교해보며 크게 성장할 수 있었다.
'T.I.L' 카테고리의 다른 글
[24.06.11] 내일배움캠프 38일차 JAVA TIL - 카카오 로그인 초기설정 (0) | 2024.06.12 |
---|---|
[24.06.10] 내일배움캠프 37일차 JAVA TIL - postman auth token 환경변수처리 (0) | 2024.06.11 |
[24.06.05] 내일배움캠프 35일차 JAVA TIL - 팀플 [회원가입 / 탈퇴 구현] (0) | 2024.06.07 |
[24.06.04] 내일배움캠프 34일차 JAVA TIL - h2, security 설정 (0) | 2024.06.05 |
[24.06.03] 내일배움캠프 33일차 JAVA TIL - 숙련주차 과제 review (0) | 2024.06.04 |