프로젝트에서 간단하게 '좋아요' 기능을 구현하였다.
단순히 개발하는 것은 어렵지 않지만,
개발과정에서 어떻게 설계를 하고 어떤 방식으로 구현하는 것이 더 효율적인지 고민을 해보려고 노력했다.
설계와 고민
- 좋아요 기능과 취소 기능 API의 분리를 해야할까
좋아요 기능을 개발할 때, 보통 좋아요 API와 취소 API 분리하느냐에 대한 고민들을 많이 한다.
나 역시도 고민을 많이 했는데, 최종적으로는 두 기능을 분리하여 API를 개발하였다.
처음에는 하나의 API로 개발했다.
개인 프로젝트이고, 좋아요자체에 부가적인 기능이 없기 때문에 하나의 API만으로도 충분하다고 생각했다.
그리고 (프론트없이 백엔드만 개발하고 있어서) API 두개보다는 하나로 호출하는 것이 효율적이라고 생각했기 때문이다.
하지만 아래와 같은 이유로 결국 API를 분리하기로 결정했다.
- 비즈니스 요구사항에 따라 좋아요/좋아요 취소 기능 내에 다른 부가 기능이 추가될 수 도 있다.
API를 분리하게되면 백앤드측에서 좋아요/취소에 대한 분기처리를 하지 않아서 성능적 이점이 생기고, 각 API별 동작이 명확해져서 가독성도 높아질 것이라 판단했다. 그리고 추후 유지보수측면에서 이득이라고 생각했다.
- 프론트엔드와의 동기화(?) 문제가 생길 수 있다.
기존처럼 하나의 API로 개발한다면 백엔드에서 좋아요와 좋아요 취소에 따른 분기처리 로직을 구현하게 된다. 프론트엔드에서는 좋아요 상태에 관계없이 좋아요버튼을 클릭할 때, 단순히 API만 호출할 것이다.
이때 만일 사용자가 좋아요를 두번 누르는 경우, 즉 좋아요를 눌렀다가 바로 취소하는 경우에
API가 두번 호출돼서 서버상에서 좋아요가 취소 됐지만, 리프레시나 오류 등의 이유로 화면에서의 하트(좋아요버튼)는 여전히 좋아요가 눌린 상태라면 문제가 생길 수도 있다.
맨 처음 좋아요 기능과 좋아요 취소 기능을 하나의 API로 만들었었는데, 해당 코드는
[Java] 좋아요기능을 위한 Optional 리팩토링하기(isPresent, ifPresent, ifPresentOrElse)에서 확인할 수 있다.
도메인을 설계해보자
매핑은
좋아요 와 식당/회원 = 다대일 단방향으로 설정했다.
식당이나 회원쪽에서 굳이 좋아요 도메인을 알필요가 없다고 생각했다.
그리고 식당을 조회할 때, 해당 식당의 좋아요 수를 알기 위해 식당 도메인에 likeCnt컬럼만 추가해줬다.
원래는 likeCnt 대신 식당별 카운트를 조회하는 API를 만들었었다. 하지만 식당 목록을 조회할 때, 각 식당별로 카운트를 조회하게 되면 식당 수만큼 불필요한 계산로직이 추가되므로 좋은 방향이 아닌 것 같았다.
만약 페이징으로 10개씩만 조회된다고 해도 '식당 목록 + 10번의 카운트API 호출' 이 되어 버리니까 말이다. (뭔가 N+1 문제랑 비슷한 느낌...)
그래서 좋아요를 누르거나 취소할 때 likeCnt 컬럼만 업데이트 하도록 수정하였다.
마이페이지에서 내가 좋아요를 누른 식당 목록을 조회할 때는 LikeController 쪽에 해당 API를 따로 구현해주었다. 이 기능때문에 양방향 매핑을 해야하나 싶었지만 주문, 배송 등과 같은 핵심 기능까진 아니었기에, 단방향으로 구현해도 될 것 같았다.
Service 개발
LikeService의 구현 부분이다.
(createLike()에서 Optional 관련 처리가 살짝 거슬리지만, 어떻게 더 깔끔명료하게 작성해야 좋을지 모르겠다😅 )
보는 것처럼 정말 단순하게 구현했다.
좋아요 수를 위해서
Shop.java에 카운트 처리를 위한 메소드를 추가한 후, LikeService에서는 save 또는 delete를 한 뒤에 likceCnt를 업데이트하는 부분을 추가했다.
어렵지 않게 좋아요 기능을 구현했지만, 개발하면 할 수록 복잡한 실무에서는 어떻게 구현하는지 많이 궁금했다.
개발자 오픈채팅에 어떤식으로 구현하냐고 문의한 적이 있는데, 잘 기억은 안나지만 많은 분들이 여러 방법을 말씀해주셨다. 하도 생소한 방법들이어서 더 기억이 안나는 것 같다. 내가 경험한 수준 이상의 구현방식이었다.
들어도 모르겠는 그런 것들...😱 언젠간 다 이해할 수 있는 날이 오길 !!