SNS 제작 (좋아요 누르기)

개발연습생log·2023년 1월 7일
0

SNS 제작

목록 보기
12/15
post-thumbnail

✨개요

🏃 목표

📢 좋아요 누르기 기능을 구현하자.

📢 요구사항

  • 좋아요 누르기
  • ‘좋아요’는 한번만 누를 수 있다. 중복으로 누르는 경우는 에러 처리.
  • 좋아요 ERD

  • POST /posts/{postId}/likes
  • like를 한번 누를 때 마다 row가 1개씩 추가되는 방식으로 구현
  • 리턴 (JSON 형식)
    {
    	"resultCode":"SUCCESS",
      "result": "좋아요를 눌렀습니다."
    }

📜 접근방법

  • 유저는 여러개의 좋아요를 남길 수 있다.
  • 포스트는 여러개의 좋아요를 가질 수 있다.
  • 좋아요는 하나의 포스트와 한명의 유저로 매핑된다.

✅ TO-DO

  • 좋아요 누르기 컨트롤러 테스트 구현
  • 좋아요 누르기 서비스 테스트 구현
  • 좋아요 컨트롤러 구현
  • 좋아요 서비스 구현
  • 좋아요 리포지토리 구현

🔧 구현

좋아요 누르기 컨트롤러 테스트

좋아요 누르기 성공

<@Test
    @DisplayName("좋아요 누르기 성공")
    @WithMockUser
    void like_SUCCESS() throws Exception {
        when(likeService.like(any(), any()))
                .thenReturn(likeResponse);

        mockMvc.perform(post("/api/v1/posts/1/likes")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk());
    }

좋아요 누르기 실패

  • 유저가 없는 경우
@Test
    @DisplayName("좋아요 누르기 실패(1) : 유저가 없는 경우")
    @WithMockUser
    void like_FAIL_user() throws Exception {
        when(likeService.like(any(), any()))
                .thenThrow(new AppException(ErrorCode.USERNAME_NOT_FOUND, ErrorCode.USERNAME_NOT_FOUND.getMessage()));

        mockMvc.perform(post("/api/v1/posts/1/likes")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isNotFound());
    }
  • 포스트가 없는 경우
@Test
    @DisplayName("좋아요 누르기 실패(2) : 포스트가 없는 경우")
    @WithMockUser
    void like_FAIL_post() throws Exception {
        when(likeService.like(any(), any()))
                .thenThrow(new AppException(ErrorCode.POST_NOT_FOUND, ErrorCode.POST_NOT_FOUND.getMessage()));

        mockMvc.perform(post("/api/v1/posts/1/likes")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isNotFound());
    }

좋아요 누르기 서비스 테스트

좋아요 누르기 성공

@Test
    @DisplayName("좋아요 누르기 성공")
    void like_SUCCESS() {

        when(userRepository.findByUserName(any()))
                .thenReturn(Optional.of(user));
        when(postRepository.findById(any()))
                .thenReturn(Optional.of(post));
        when(likeRepository.save(like))
                .thenReturn(like);

        assertDoesNotThrow(() -> likeService.like(user.getUserName(), post.getId()));
    }

좋아요 누르기 실패

  • 유저가 없는 경우
@Test
    @DisplayName("좋아요 누르기 실패(1) : 유저가 없는 경우")
    void like_FAIL_user() {

        when(userRepository.findByUserName(any()))
                .thenReturn(Optional.empty());
        when(postRepository.findById(any()))
                .thenReturn(Optional.of(post));
        when(likeRepository.save(like))
                .thenReturn(like);

        AppException exception = assertThrows(AppException.class, () -> likeService.like(user.getUserName(), post.getId()));
        assertEquals(ErrorCode.USERNAME_NOT_FOUND, exception.getErrorCode());
    }
  • 포스트가 없는 경우
@Test
    @DisplayName("좋아요 누르기 실패(2) : 포스트가 없는 경우")
    void like_FAIL_post() {

        when(userRepository.findByUserName(any()))
                .thenReturn(Optional.of(user));
        when(postRepository.findById(any()))
                .thenReturn(Optional.empty());
        when(likeRepository.save(like))
                .thenReturn(like);

        AppException exception = assertThrows(AppException.class, () -> likeService.like(user.getUserName(), post.getId()));
        assertEquals(ErrorCode.POST_NOT_FOUND, exception.getErrorCode());
    }

좋아요 컨트롤러 구현

LikeController

@RestController
@RequestMapping("/api/v1/posts/{postId}/likes")
@RequiredArgsConstructor
public class LikeController {
    private final LikeService likeService;

    @PostMapping
    public ResponseEntity<Response> like(Authentication authentication, @PathVariable Long postId){
        String userName = authentication.getName();
        LikeResponse likeResponse = likeService.like(userName,postId);
        return ResponseEntity.ok().body(Response.of("SUCCESS",likeResponse));
    }
}

LikeResponse

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
@Setter
public class LikeResponse {
    private String result;

    public static LikeResponse of(String result) {
        return LikeResponse.builder()
                .result(result)
                .build();
    }
}

좋아요 서비스 구현

LikeService

@Service
@RequiredArgsConstructor
public class LikeService {
    private final LikeRepository likeRepository;
    private final UserRepository userRepository;
    private final PostRepository postRepository;
    public LikeResponse like(String userName, Long postId) {
        //유저체크
        User findUser = AppUtil.findUser(userRepository,userName);
        //포스트체크
        Post findPost = AppUtil.findPost(postRepository,postId);
        //엔티티 생성 후 저장
        Like like = Like.of(findUser,findPost);
        likeRepository.save(like);
        //likeResponse 리턴
        return LikeResponse.of("좋아요를 눌렀습니다.");
    }
}

좋아요 리포지토리 구현

LikeRepository

public interface LikeRepository extends JpaRepository<Like,Long> {
}

Like

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "likes")
@Getter
public class Like extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "userId")
    private User user;

    @ManyToOne
    @JoinColumn(name = "postId")
    private Post post;

    public static Like of(User user, Post post){
        return Like.builder()
                .user(user)
                .post(post)
                .build();
    }
}

🌉 회고

  • 단순한 기능이라 구현하기는 쉬웠다.
  • 하지만 좋아요를 두 번 누를 경우를 처리해야 한다.
  • 두 번 누를 경우 좋아요가 취소되도록 처리하려고 한다.
profile
주니어 개발자를 향해서..

0개의 댓글