“뉴스 피드”는 무엇일까 ? 뉴스 피드는 페이스북, 인스타그램, 트위터(X) 같은 앱에 접속했을 때 가장 먼저 보게 되는 콘텐츠 목록을 말하는데, 사용자 상태 정보 업데이트, 사진, 영상, 팔로우하는 사람들 등 다양한 관심사로부터 나오는 게시물을 뉴스 피드라고 한다.

시스템 설계 면접에서 “뉴스 피드” 문제를 자주 다루는 이유는 무엇일까?

뉴스 피드는 시스템 설계 면접에서 자주 나오는데, 읽기 연산이 매우 많고, 쓰기 연산은 적당히 있는 시스템으로 대규모 분산 환경이나 캐시 전략, 순서, 사용자 맞춤형 필터링 등 다양한 설계 요소가 개입되는 복합적인 시스템이기 때문이다.

  • 사용자 관점에서는 앱에 접속했을 때 내가 보고 싶은(또는 보여주는) 게시물 스트림을 본다.
  • 개발자 관점에서는 높은 읽기 효율이나, 개인화 알고리즘, 분산처리, 캐싱 전략 등이 요구되는 시스템 설계이다.

뉴스 피드 시스템 설계

뉴스 피드는 피드 발행과 뉴스 피드 생성 두 가지 부분으로 나뉘어 있다.

  • 피드 발행 (Feed Publishing)
    • 사용자가 새 게시물을 업로드할 때 발생하는 과정이다. 예를 들어 콘텐츠(스토리)가 데이터베이스에 저장되고, 캐시(Redis 등)에 사용자 타임라인 혹은 팔로워 피드에 쓰일 준비 데이터로 기록된다.
  • 피드 생성 (Feed Generation)
    • 사용자가 자신의 홈 피드를 열었을 때, 어떤 콘텐츠를 보여줄지를 결정하는 부분이다. 이때 두 가지 방식 중 하나를 쓸 수 있다.
      • Pull 모델 : 사용자가 피드를 요청하면 실시간으로 팔로잉한 사용자들의 최신 게시물을 DB/캐시에서 가져와 정렬한다.
      • Push 모델 : 피드 발행 시점에 미리 해당 콘텐츠를 모든 팔로워의 피드에 추가해 놓는다.
    • 예를 들어 유저 A가 B, C를 팔로우하고 있을 때, 유저 A가 앱을 열면, B와 C의 게시물을 모아서, 시간순 혹은 랭킹 순으로 정렬한 뒤, A의 뉴스피드 화면에 출력한다.

그리고 뉴스 피드 시스템에서 웹 서버는 단순히 클라이언트 요청을 받아 처리하는 역활에 그치지 않고 또 다른 중요한 기능들도 함께 수행한다.

  • 당연한 이야기겠지만, 클라이언트가 글을 올리려면 반드시 Authorization 헤더에 유효한 인증 토큰을 담아 API를 호출해야한 한다. 그렇지 않으면 누구든지 허위 요청을 보내거나 다른 사람의 계정으로 콘텐츠를 올리는 문제가 발생할 수 있기 때문에 웹 서버는 토큰 유효성 검사를 통해 사용자가 실제로 로그인한 유저인지를 확인하는 절차가 필요하다.

인증 처리보다 조금 더 중요한 것은 처리율 제한(Rate Limiting)에 대한 부분이다.

  • 악의적인 사용자가 짧은 시간 안에 수십, 수백 개의 게시물을 반복해서 올릴 경우 서버는 과부하에 빠질 뿐만 아니라 다른 유저들의 경험에도 악영향을 줄 수 잇기 때문에 스팸성 게시물, 유해한 콘텐츠 등을 막기 위해 처리율 제한을 두는 것이 좋다.
    • 예를 들어 한 사용자가 분당 또는 시간당 업로드할 수 있는 게시물 수 제한을 둘 수 있다.

뉴스 피드에는 포스팅 전송(팬아웃) 라는 중요한 과정이 있다. 이 과정은 사용자가 게시물을 올렸을 때, 해당 게시물이 팔로워들의 피드에 전달될 수 있도록 하는 단계를 의미한다. Fan-out에는 대표적으로 두가지 모델이 있다.

  • Push 기반 Fan-out(Fan-out Write)
    • 포스팅 시점에 팔로워들의 피드 저장소에 미리 게시글을 복사해 저장한다.
    • 피드를 읽을 때 빠르지만, 팔로워 수가 많을 경우 부하가 매우 커진다.
    • 친구가 많은 사용자의 경우 친구 목록을 가져오고 그 목록에 있는 사용자 모두의 뉴스 피드를 갱신하는 데 많은 시간이 소요될 수 있기 때문에 핫키(hotkey)라고도 부르는 문제가 발생한다.
  • Pull 기반 Fan-out(Fan-out Read)
    • 포스팅 시에는 저장만 해두고, 팔로워가 피드를 요청할 때마다 그때그때 팔로우 목록을 기준으로 새로운 게시글을 조회해 요청한다.
    • 유연하지만, 읽기 성능이 불안정할 수 있다.

뉴스 피드는 빠르게 가져올 수 있도록 하는 것이 아주 중요하기 때문에 대부분의 사용자에 대해서는 푸시 모델을 사용하지만, 연예인이나 인플루언서처럼 수백만 명의 팔로워가 있는 경우, 푸시 방식은 효율이 안좋기 때문에 pull 기반이나 Hybrid 전략을 적용하기도 한다.

이제 마지막으로 캐시에 대해 알아보자. 뉴스 피드는 읽기 요청이 매우 많은 시스템이다. 모든 게시물을 DB에서 일일이 가져와서는 속도와 트래픽을 감당할 수 없기 때문에 캐시를 사용해야 한다. 단순하게 “유저가 피드를 연다. → 서버가 포스트들을 DB나 캐시에서 읽어서 한번에 내려준다. → 끝” 이헣게 될 것처럼 보이지만, 실제로는 한 번에 묶어서 미리 만들어두지는 않는다.

그래서 나눠서 관리하고 필요한 것만 가져와서 쓰는 것이 좋은데, 그러기 위해서 캐시를 5단계 구조로 분리해서 필요한 데이터를 계층적으로 빠르게 조회할 수 있도록 설계할 수 있다.

[1단계] 사용자 타임라인 캐시
  └─> post ID 리스트 저장

[2단계] 개별 포스트 캐시
  └─> post ID → 상세 콘텐츠 매핑

[3단계] 사용자 메타 정보 캐시
  └─> followees, 좋아요 목록 등

[4단계] 콘텐츠 메타 정보 캐시
  └─> 좋아요 수, 댓글 수, 조회 수

[5단계] 랭킹 기반 피드 캐시
  └─> 추천 피드, 인기 피드 등

그레서 위 단계처럼 피드를 구성하는 구성 요소들(글 목록, 본문, 메타 정보 등)을 목적에 따라 따로따로 캐시하고, 필요한 순간에 필요한 것만 조합해서 가져오는 것이 좋다. 전체적으로 뉴스피드에 대한 그림을 그리면 아래와 같다.

사용자 앱
   │
   ▼
로드밸런서 (Load Balancer)
   │
   ▼
웹/API 서버
   │
   ├── 인증 서비스 (Auth)
   ├── 속도 제한기 (Rate Limiter)
   ▼
피드 발행 서비스 (Feed Publishing Service)
   │
   ├── 미디어 저장소 (S3 등)
   ├── 데이터베이스 (PostgreSQL, Cassandra 등)
   ├── 캐시 (Redis 등)
   ├── 큐 (Kafka, SQS 등)
   │     ├── 알림 서비스 (Notification)
   │     └── 팬아웃 서비스 (Fan-out to followers)
   └── 로깅/모니터링