게시글에 좋아요 표시 여부를 깔끔하게 제공하는 API 만들기

손정우·2021년 5월 18일
34

더 자세하게

목록 보기
2/2
post-thumbnail

배경

게시글 목록을 가져오는 API가 필요하다.
그런데 그냥 게시글 목록을 가져오는 것이 아닌 아래와 같은 상황이 있다면 살짝 복잡해진다.

게시글에 '좋아요' 표시를 할 수 있으면 좋겠어요.
그리고 게시글 목록을 볼 때 유저가 각 게시글을 '좋아요'했는지 확인할 수 있으면 좋겠어요.

이를 API로 표현하면 아래와 같을 것이다.

GET /posts
[
    {
        "id": 51512,
        "title": "title",
        "author": "yeoul",
        "content": "post content",
      	"isLike": true,
    }
]

인터넷에서 많은 토이 프로젝트나 오픈된 소스들에서는 isLike같은 플래그 값을 따로 두었다.

의문

난 저 플래그 값에 의문이 든다.
API를 RESTful하게 짠다고 가정하면 URL은 자원의 식별자가 된다.
즉 위의 API에서 /posts는 게시글 목록이라는 자원의 식별자다.

우선 posts에서 해당 자원이 배열이고 그 안에 post(게시글)이 있다는 것은 쉽게 유추할 수 있다.

하지만 post안에 isLike와 같은 값이 있다고 쉽게 유추할 수 있을까? 아마 아닐 것이다.
게시글이라는 정적인 이름을 가진 개체가 URL이 똑같아도 유저가 다른 것으로 다른 값을 가지는 속성이 있을 것이라 생각하는 사람은 많이 없을 것이다.

isLike post와 곧바로 연결되는 개념이 아니라는 것이다.
실제로 백엔드 코드에서도 이는 다른 객체인 유저와의 관계에서 나타나며 게시물의 스키마에 포함되지 않는다.
아래의 사진을 참고하면 게시물의 스키마는 왼쪽이 아니라 오른쪽처럼 표현된다.

isLike를 사용하면 예측이 쉬운 API를 위해서라면 모든 게시물이 표현되는 곳에서도 사용되어야한다. 즉 게시글 목록을 가져오는 GET /posts 외에도 GET /posts/:idGET /users/:userId/posts/:postId와 같은 모든 곳에서 isLike가 존재해야한다.

이는 백엔드에서 복잡한 관계를 처리해 isLike를 만들어야 하는데 이를 모든 게시물관련 로직에서 중복적으로 거쳐야한다.

정리하면 API 이름과 맞지 않는 속성을 가져 예측하기 힘든 API를 만들고 백엔드에서는 직관적이지 않고 확장하기 어려운 코드가 만들어진다.

고찰

많은 고민이 있었지만 깃허브의 OpenAPI를 참고하는 것에서 가장 큰 도움을 받았다.

깃허브에서 유저가 유저를 팔로우 할 수 있다. 한 유저가 다른 유저를 확인하는 API를 다음과 같이 제공하고 있다.
https://docs.github.com/rest/reference/users#check-if-a-user-follows-another-user


위의 사진들을 보면 username인 유저의 following 목록 중에서 target_user라는 유저가 있는지 확인한다.
만약 있다면 204를 없다면 404로 응답하는 것을 확인 할 수 있다.
이런 방식이면 확실히 isLikeisFollowing와 같은 플래그 값을 쓰는 것보다는 더 명시적이라는 생각이 든다.

이를 게시글에 좋아요로 치환하면 다음과 같을 것이다.
GET /posts/:postID/likers/:userId
likers라고 한 것은 게시글의 입장에서는 유저는 좋아하는 사람이라는 의미를 넣기 위함이였다.
반대로 유저가 게시글을 좋아하는지 확인하는 API는 아래와 같이 만들 수 있을 것이다.
GET /users/:userId/likes/:postId

여기서 응답을 왜 204로 하나 라는 의문이 들 수 있다.
해당 API는 존재하는지 확인만 하기 위해 204로 했으나 사실 확인이라는 동작이 GET과는 바로 매칭이 안된다. 따라서 상황에 따라 204 대신 해당 유저나 게시글 정보를 응답하는 것이 더 좋을 수도 있다는 생각이 들었다.

회고

간단한 SNS와 비슷한 기능의 토이 프로젝트들을 진행하다 보면 이런 상황이 매번 나오지만 그때마다 적당히 고민하다가 넘어가기 일쑤였다.

그럼에도 의문은 늘 남았고 마침 함게 이번 프로젝트를 진행하던 김씨가 함께 고민해주었다.
그 덕에 해당 문제에 대해서 심도있게 고민하고 의논할 수 있었으며 어느 정도의 만족할 답을 얻어낸 거 같다.

여기에 해당 문제에 대해서 김씨가 정리한 글도 있다.

RESTful한 API를 짜다 보면 자원과 그와 관계된 자원을 나타내는 데에는 효과적이지만 지금의 좋아요와 같이 자원과 자원의 관계 그 자체를 나타내려 할 때 어려움이 발생하는 거 같다.
지금은 이렇게 해결 했지만 다른 형태의 관계에서는 어떻게 표현되어야 할지는 새로운 생각거리 인 듯하다. 그래도 지금과 같은 과정을 통해 해답이 있을 것이라는 생각과 내가 찾을 수 있다는 자신감을 얻었고 REST에 대해서도 한 층 더 이해할 수 있었다.

3개의 댓글

comment-user-thumbnail
2021년 5월 25일

안녕하세요 저도 배우는 입장에서 글을 읽고 열심히 생각해봤는데 GET /posts/:postID/likers/:userId api를 사용했을 때 문제점은..
게시글 리스팅 페이지에서 게시글 갯수에 비례하여 요청을 n개만큼 추가로 보내야하고, n + 1번 DB 쿼리를 실행시켜야 한다는 점입니다. isLike 필드를 사용하면 JOIN을 이용해 한 번의 db 쿼리로 해결할 수 있을텐데 말이죠.
그리고 개발자 도구로 인스타그램에서 게시글 api가 어떤 정보를 보내는지 살펴보았는데, json 에서viewer_has_liked라는 필드를 사용해 클라이언트가 좋아요 버튼을 클릭했는지 확인하고 있었습니다.
REST api를 지향하는 것은 좋지만, 앱 성능을 크게 해치지 않는 선에서 적용하는 것이 좋지 않을까 생각합니다.

1개의 답글

아직 실력이 부족해 많이 와닿지는 않지만, 좋은 글 잘봤습니다 :-)

답글 달기