스프링 좋아요 리팩터링

Kim Dong Kyun·2023년 1월 4일
24

Today I learned

목록 보기
28/43

썸네일 링크

개요

좋아요 1탄

메모장 프로젝트에 좋아요 기능을 넣어두고 좋아하던 중, 뜻하지 않은 난관에 마주했다. 바아로 리팩터링에 들어가서 고친 후 기록으로 남겨본다.

문제 1 - 상황

튜터님께서 제안 해 주신 부분이다. 좋아요 기능이 실행되려면 어떤 API가 필요할까?

  • 위와 같은 따봉을 누를 때, 즉 하트 아이콘이 활성화되지 않은 상태일 때 누르면 좋아요가 +1 되어야 한다.

  • 반대로 위 하트 아이콘이 활성화되어 있는 상황에서는 좋아요가 취소되어야 한다.

-> 결국, API는 두개로 분리되어야 하는 것이다.

그렇다면 이전에 만든 서비스 로직과 API는 어땠을까?

  1. 서비스

위와 같이 if문을 사용해서 리퍼지토리에서 findById했을 때 비어있다면 추가를 하고, 이미 존재한다면 delete하는 방식이다. 컨트롤러 단도 살펴보자

  1. 컨트롤러

컨트롤러도 이런 식으로 하나의 API 요청밖에는 없다.

문제 1 문제점

  1. 이렇게되면 프론트엔드 입장에서는 당황스러워진다.

음? like하는 API는 여기 와서 받았는데, 그럼 취소 할 땐 어떤 API를 써야하지?

음...API명세에 없나? 뭐지? 쭉쭉 더 글을 읽다가 마지막에 저~~ 아래에

  • 좋아요 실행할땐 뭐 3020 취소할땐 4321 뭐 이렇게 보낼거에요~
    -> 극대노, 퇴사, 명세를 너무 자세히 읽은 나머지 시력감퇴
  1. 관심사의 분리가 이루어지지 않는다

예를 들어 저 로직 안에서 문제가 발생했다고 하자. 좋아요수가 올라가기는 하는데 내려오질 않아. 그렇다면 저 로직을 다 뜯어고쳐야 한다.

그런데, 이렇게 간단한 게 아니라 엄청 복잡한 로직이면?

테스트 코드 작성 테스트 등등 여러가지 문제를 해결하는데 걸리는 시간과 비용이 어마어마 할 것이다.


우리는 위와 같은 문제점을 살펴봤다. 즉, 이렇게 하면 지구는 멸망하게 된다. 그렇다면 어떻게 해야할까?

문제 1 해결

관심사의 분리가 필요하다. API를 나눠서 다르게 요청을 보내야 한다.

위와같이 API를 나눠서 맵핑한다. 이렇게되면 더 기능의 분리가 명확하고, 혹시 delete부분에 문제가 생겼다고 한다면 delete 부분의 컨트롤러와 서비스 등등을 살펴보면 되고, 테스트 코드 작성에도 훠얼씬 훨씬 용이 할 것이다.


문제 2 - 메모리 효율 문제

서비스 로직을 다시 한 번 살펴보자.

위와 같은 형태로 되어있다. 저렇게 되면 문제가 뭘까? 아래 responseDto의 형태를 보면서 다시 생각해보자.

comments.getlikes 가 문제이다. 왜냐?

-> 하나의 댓글에 따봉이 100만개, 2000만개가 달렸다고 생각해보자. getLikes를 하면 엔티티 테이블을 싹다 훑어서 다 가져온 후에 사이즈만 쏙 빼먹고 다시 돌려준다. DB 입장에서 받는 부하가 너무너무너무 커진다. 이렇게 되면 서버는 Out of memory로 다운되고 지구는 멸망한다.

그렇다면 어떻게 해야하지? repository에 수만 빼오는 방법이 있나? 뭐 countNumberBy...이런게 있어?

문제 2 - 해결

있다.

서비스단에서 적용한 모습이다. 이렇게되면 무슨 장점이 있을까?

  1. Memo가 Likes와 연관관계를 맺지 않아도 된다.
    -> 원래는 Memo가 OneToMany로 List 형태의 Likes를 소유했다. 그러나 위와 같이 바꾸게 되면 Memo엔티티는 좋아요 수 카운트! 만 가지고 있으면 된다. 직접 참조 할 필요가 없다.

  2. DB의 부하가 줄어든다.
    -> Likes 엔티티 전체를 불러오는 게 아니라 count만 불러온다. DB의 부하가 훨 씬 적어지게 된다.


배운 점

  1. 관심사의 분리는 무조건 이행되어야 한다. 명세에서

    좋아요를 누르면 카운트 올라가고 다시 누르면 내려요~

라고 했다고 정말 하나에 다 때려박으면 나중에 아주 곤란해진다.

  1. count만 필요하다면 count만 사용해야 한다.

0개의 댓글