Likes 테이블 문제: 왜 우리는 다형성을 선택했는가

발행: (2026년 2월 22일 오전 01:26 GMT+9)
5 분 소요
원문: Dev.to

Source: Dev.to

Introduction

몇 일 전, 애플리케이션에 Community 섹션을 추가하고 있었습니다. 사용자들은 다음과 같은 작업을 할 수 있어야 했습니다:

  • 게시물 만들기
  • 댓글 달기
  • 게시물에 좋아요 누르기
  • 댓글에 좋아요 누르기

또한 별도의 News 섹션이 있었고, 새로운 요구사항은 사용자가 뉴스 기사에도 좋아요를 누를 수 있어야 한다는 것이었습니다.

Initial Approach: Separate Like Tables

만약 좋아요를 누릴 수 있는 엔터티가 하나뿐이라면(예: News) 스키마는 간단했을 것입니다:

CREATE TABLE news_like (
    user_id   BIGINT   NOT NULL REFERENCES users(id),
    news_id   BIGINT   NOT NULL REFERENCES news(id),
    liked_at  TIMESTAMP NOT NULL DEFAULT now()
);

하지만 posts, comments, news라는 세 가지 엔터티가 있었기 때문에 다음과 같이 세 개의 별도 테이블을 만들 생각을 했습니다:

  • post_likes
  • comment_likes
  • news_likes

각 테이블은 적절한 외래키 관계를 가졌으며, 우리는 다음과 같은 장점을 얻었습니다:

  • 강력한 관계 무결성
  • 쉬운 조인
  • 명시적인 구조

이것이 가장 “순수 관계형” 접근법이지만, 반복적인 느낌이 들었습니다. 앞으로 또 다른 좋아요 대상 엔터티가 추가된다면 또 다른 테이블이 필요하게 되어 스키마가 수평으로 확장되는 문제가 발생했습니다.

Polymorphic Likes Table

여러 테이블 대신, 우리는 어떤 리소스 타입이든 참조할 수 있는 단일 polymorphic likes 테이블을 만들었습니다.

CREATE TABLE likes (
    user_id       BIGINT      NOT NULL REFERENCES users(id),
    resource_id   UUID        NOT NULL,
    resource_type TEXT        NOT NULL CHECK (resource_type IN ('POST','COMMENT','NEWS')),
    liked_at      TIMESTAMP   NOT NULL DEFAULT now()
);

Columns

  • user_id – 아이템을 좋아요한 사람(users에 대한 외래키).
  • resource_id – 좋아요된 아이템의 UUID(외래키 없음).
  • resource_type – 아이템의 타입(POST, COMMENT, NEWS).
  • liked_at – 좋아요가 발생한 타임스탬프.

(resource_id, resource_type) 쌍이 좋아요된 엔터티를 고유하게 식별합니다. 이 설계 덕분에 시스템 전반에 걸쳐 확장하고 재사용하기 쉬운 단일 중앙 테이블을 갖게 됩니다.

Trade‑offs

주된 단점은 resource_id에 대한 직접적인 외래키 강제 적용이 사라진다는 점입니다. PostgreSQL은 여러 테이블을 가리키는 외래키를 강제할 수 없으므로, 참조 무결성은 애플리케이션 코드에서 처리해야 합니다.

특정 리소스에 대한 좋아요 수를 셀 때의 예시 쿼리:

SELECT COUNT(*)
FROM likes
WHERE resource_id = 'some-uuid'
  AND resource_type = 'POST';

리소스 상세 정보와 함께 좋아요를 가져와야 한다면 별도의 쿼리를 실행하거나 애플리케이션 측 로직을 추가해야 할 수도 있습니다. 우리 경우에는 좋아요에 대해 무거운 교차 엔터티 조인을 수행하지 않기 때문에 이 트레이드‑오프가 허용되었습니다.

Conclusion

핵심 인사이트는 “like”는 리소스 전반에 공유되는 행동이며, 게시물, 댓글, 뉴스에 밀접하게 결합된 기능이 아니라는 점이었습니다. 이를 polymorphically 모델링함으로써 “like”를 재사용 가능한 시스템 기능으로 취급하게 되었고, 스키마 중복을 줄이고 애플리케이션이 성장함에 따라 발생할 수 있는 리팩터링 작업을 최소화할 수 있었습니다.

0 조회
Back to Blog

관련 글

더 보기 »

성능 비교: React vs WebForms Core

네트워크 요청, 대역폭 소비 및 클라이언트 실행 모델에 집중하세요. 현대 웹 아키텍처에서 성능은 렌더링 속도만을 의미하지 않습니다. A criti...