H
하베스트
AI로 요약된 콘텐츠

Feed-Entity: 당근 피드의 심장

이 요약은 당근의 피드 시스템에서 Feed-Entity라는 개념이 어떻게 탄생했고, 어떤 문제들을 해결했으며, 앞으로 어떻게 발전해 나갈지에 대한 내용을 담고 있어요. Feed-Entity는 당근 내 다양한 콘텐츠를 표준화된 형태로 관리하고 시스템 확장성을 높여 사용자에게 더욱 풍부하고 개인화된 피드 경험을 제공하기 위한 핵심적인 프로젝트랍니다.


1. Feed-Entity의 탄생 배경 ✨

안녕하세요! 저는 당근 피드인프라팀에서 소프트웨어 엔지니어로 일하는 Lebron이라고 해요. 저희 팀은 하루에 수백만 명의 사용자들이 당근 앱을 열었을 때 가장 먼저 보게 되는 피드 경험을 책임지고 있답니다. 사용자의 관심사에 맞춰 맞춤형 콘텐츠를 적절한 위치에 보여주기 위해 복잡한 피드 시스템을 운영하고, 대규모 트래픽도 안정적으로 처리할 수 있는 인프라를 구축하고 있어요.

당근 피드에는 중고차, 부동산, 중고거래, 알바 등 정말 다양한 콘텐츠들이 끊임없이 올라오고, 이 콘텐츠들은 여러 맥락으로 연결되어 사용자들에게 보여져요. 그런데 이렇게 다양한 콘텐츠들을 다루다 보니 "어떻게 하면 데이터를 좀 더 일관성 있게 저장하고 활용할 수 있을까?" 하는 고민이 자주 생겼어요.

바로 이런 고민에서 Feed-Entity라는 프로젝트가 시작되었어요! Feed-Entity는 단순히 데이터를 표준화하는 것을 넘어, 당근의 피드 시스템이 더욱 확장 가능하고 유연하게 발전할 수 있도록 만들었답니다. 덕분에 피드에 새로운 서비스를 더 빠르게 통합하고, 사용자들에게 훨씬 다양하고 풍부한 콘텐츠를 제공할 수 있었죠.

Feed-Entity가 도입되기 전에는 피드 시스템이 여러 서비스에 분산되어 운영되고 있었어요. 예를 들어, 중고거래 서비스는 자체 데이터베이스에 상품 정보를 저장하고, 알바 서비스는 또 다른 저장소에 구인구직 정보를 보관하는 식이었죠. 피드 시스템은 이 여러 저장소에서 데이터를 끌어와 사용자에게 보여주는 역할을 했어요.

초기에는 이런 구조가 단순하고 괜찮았지만, 당근 서비스가 다양해지고 규모가 커지면서 여러 문제점이 나타나기 시작했어요. 각 서비스마다 데이터 구조와 저장 방식이 달라서, 새로운 서비스를 피드에 추가할 때마다 많은 통합 작업이 필요했거든요. 😢

이런 문제들을 해결하기 위해 저희는 Feed-Entity라는 새로운 개념을 도입했어요. Feed-Entity를 통해 이루고 싶었던 목표들은 다음과 같아요.

  • 데이터 구조의 표준화: 각 서비스마다 달랐던 데이터 구조와 통신 방식을 표준화해서 시스템 간에 일관된 인터페이스를 제공하고 싶었어요.
  • 시스템 확장성 개선: 새로운 서비스를 피드에 더 쉽고 빠르게 추가할 수 있게 만들고 싶었어요.
  • 데이터 일관성 확보: 다양한 콘텐츠를 더 일관성 있게 다루고 싶었어요.
  • 통합 관리 시스템 구축: 중고거래, 알바, 중고차, 부동산, 동네생활 등 모든 서비스의 콘텐츠를 한곳에서 관리하고 싶었어요.
  • 사용자 경험 향상: 궁극적으로 사용자들에게 더 다양하고 풍부한 콘텐츠를 보여주고 싶었죠.

2. Feed-Entity: 피드 시스템의 Single Source of Truth 🌟

그렇다면 Feed-Entity는 정확히 무엇일까요? Feed-Entity는 피드에서 노출할 수 있는, 당근 내에서 발행될 수 있는 가장 작은 단위의 콘텐츠를 의미해요. 피드 시스템 안에서 Single Source of Truth(SSOT), 즉 단 하나의 진실된 정보원으로 정의된 단위라고 할 수 있죠.

Feed-Entity에는 콘텐츠 자체를 나타내는 요소들만 정의된답니다. 예를 들어, 중고거래 게시글, 동네생활* 게시글, 동네생활 댓글, **비즈프로필** **등이 Feed-Entity가 될 수 있어요. 하지만 사진, 텍스트, 관심 수나 채팅 수 같은 속성들은 Feed-Entity의 구성 요소가 될 수 없어요.

💡 *동네생활: 동네에 대한 이야기와 정보를 나눌 수 있는 커뮤니티 공간이에요.
💡 **비즈프로필: 동네 업체에 대한 정보를 담고 있는 프로필이랍니다.

위 그림처럼 중고거래, 알바, 부동산 같은 다양한 콘텐츠들이 Feed-Entity라는 표준화된 형태로 변환되어 저장돼요. Feed-Entity는 기본적으로 아래와 같은 구성 요소를 가지고 있답니다.

  • ID: Feed-Entity를 고유하게 식별할 수 있는 번호예요.
  • 타입: 중고거래, 알바, 동네생활 등 어떤 종류의 콘텐츠인지를 나타내죠.
  • 소스 정보: 원본 콘텐츠(SourceContent*)의 출처와 관련된 정보를 담아요.
  • 생성자: 콘텐츠를 작성한 사용자에 대한 정보를 담고 있어요.
  • 생성/수정/노출 시간: 콘텐츠가 언제 만들어지고 수정되었는지에 대한 정보를 나타내요.
  • 상태: Feed-Entity가 생성되었는지, 현재 노출 중인지, 아니면 미노출 상태인지와 같은 상태 정보를 나타내요.
  • Entity: 변환된 실제 콘텐츠 정보를 담는 곳이에요.

Feed-Entity는 이렇게 표준화된 구조를 통해 다양한 서비스의 콘텐츠를 일관되게 관리할 수 있도록 해주었어요. 덕분에 새로운 서비스를 피드에 통합하는 과정이 훨씬 간단해졌고, 개발자들은 데이터 구조보다는 실제 비즈니스 로직에 더 집중할 수 있게 되었죠. 👍

💡 *SourceContent: Feed-Entity로 변환되기 전의 원본 콘텐츠 데이터예요.


3. Feed-Entity 데이터 파이프라인 🚀

Feed-Entity의 데이터 파이프라인은 콘텐츠 제공자(Content Provider)로부터 데이터를 모아서, Feed-Entity 형태로 변환한 다음 저장하는 모든 과정을 담당해요. 이 파이프라인은 크게 데이터 수집, 변환, 저장의 단계로 이루어져 있어요. 각 단계는 모듈화되어 있어서 새로운 콘텐츠 타입이 추가되더라도 전체 시스템을 고치지 않고 해당 모듈만 추가하면 되는 유연한 구조를 가지고 있답니다.

위 이미지는 Feed-Entity 데이터 파이프라인을 자세히 보여주고 있어요. 이 파이프라인은 크게 네 단계로 구성되어 있는데, 각 단계별로 자세히 살펴볼게요.

3.1. 데이터 수집 📦

첫 번째 단계는 데이터를 모으는 과정이에요. 중고거래, 알바, 동네생활, 부동산 등 다양한 서비스에서 만들어진 원본 데이터가 Feed-Entity 시스템으로 들어와요. 각 서비스마다 데이터 구조와 형식이 다르기 때문에, 이 단계에서는 다양한 형태의 데이터를 처리할 수 있는 유연한 메커니즘이 필요하죠.

Feed-Entity 시스템은 이런 유연성을 위해 데이터 유형별로 별도의 큐를 운영해요. 각 큐에서 데이터를 처리할 때는 멱등성(Idempotency)을 보장하도록 설계했어요. 덕분에 같은 SourceContent가 여러 번 전달되더라도 단 한 번만 처리된답니다. 또한 실시간 데이터 스트림을 통해 새로운 콘텐츠나 업데이트된 콘텐츠를 계속해서 모아서 사용자에게 항상 최신 정보를 제공할 수 있어요.

3.2. 데이터 변환 🔄

두 번째 단계는 수집된 다양한 형태의 데이터를 표준화된 Feed-Entity 형식으로 변환하는 과정이에요. 이 과정에서 각 콘텐츠 타입에 맞는 변환 로직이 적용된답니다. 예를 들어, 중고거래 게시글은 상품 정보, 가격, 위치 같은 필드를 가지지만, 동네생활 게시글은 본문 내용, 카테고리, 댓글 수 등 다른 구조를 가져요.

변환 단계에서는 이렇게 다양한 구조의 데이터들을 공통 필드(ID, 타입, 생성 시간 등)를 가진 Feed-Entity라는 통일된 형식으로 래핑해요. 그러면서도 각 콘텐츠의 고유한 특성은 Entity 필드 내부에 그대로 보존하여 변환하죠.

변환된 Feed-Entity 데이터는 별도의 저장소에 저장되어 피드 시스템 전체에서 일관되게 접근할 수 있게 돼요. 모듈화된 시스템 구조 덕분에 새로운 콘텐츠 타입이 추가되더라도 전체 시스템을 수정하지 않고 해당 모듈만 추가하면 되는 유연한 구조를 갖추고 있답니다. 이는 시스템의 확장성을 크게 향상시키고, 새로운 서비스를 빠르게 통합할 수 있게 해주는 아주 중요한 장점이에요.

3.3. 데이터 검증 및 DLQ 처리 🧐

세 번째 단계는 데이터를 검증하는 과정이에요. SourceContent나 Feed-Entity 데이터가 검증에 실패하면, 해당 데이터는 Dead Letter Queue(DLQ)로 이동하게 돼요. DLQ는 처리에 실패한 메시지를 저장해 두는 특별한 큐인데, 이를 통해 데이터 손실 없이 나중에 다시 처리할 수 있도록 해준답니다.

예를 들어, 원본 데이터의 형식이 변경되었거나 필수 필드가 누락되었을 때 데이터 변환 과정에서 오류가 발생할 수 있겠죠? 이런 경우 해당 데이터는 DLQ로 이동하고, 개발자들은 오류의 원인을 분석한 후 필요한 수정을 거쳐 데이터를 다시 처리할 수 있어요. 이러한 메커니즘을 통해 시스템의 안정성을 높이고, 데이터 무결성을 보장했답니다.

또한 DLQ 시스템은 문제가 발생한 데이터를 자동으로 모니터링하고 알림을 보내는 기능도 포함하고 있어요. 이를 통해 개발팀이 문제를 빠르게 인지하고 대응할 수 있게 되었죠. 이는 데이터 파이프라인의 신뢰성을 높이고, 장애 상황에서도 데이터 손실을 최소화할 수 있게 해주었어요.

3.4. 메시지 큐 프로듀스 및 서비스 간 연동 🤝

마지막 단계는 변환된 Feed-Entity 데이터를 별도의 메시지 큐에 프로듀스(Produce)하는 과정이에요. 이를 통해 피드 시스템 외의 다른 서비스들도 이 표준화된 데이터를 활용할 수 있게 되었답니다. 다양한 내부 서비스들이 Feed-Entity를 구독(Subscribe)하여 필요한 정보를 실시간으로 받아볼 수 있게 되었어요. 이런 구조는 서비스 간 데이터 일관성을 유지하고, 각 서비스가 독립적으로 발전할 수 있는 기반이 되었죠.

또한 Feed-Entity 시스템을 처음 도입할 때는 콜드 스타트(Cold Start) 문제를 해결하기 위한 백필(Backfill) 기능도 중요했어요. 백필은 기존에 존재하던 콘텐츠들을 새로운 Feed-Entity 형식으로 변환하여 시스템에 채워 넣는 과정이에요. 이 과정을 통해 새 시스템을 도입하는 순간부터 충분한 양의 데이터를 확보하고, 사용자들에게 풍부한 콘텐츠를 제공할 수 있었답니다.

이러한 메시지 큐 기반의 아키텍처는 시스템 간의 느슨한 결합(Loose Coupling)을 가능하게 하여, 각 서비스가 독립적으로 개발, 배포, 확장될 수 있게 해요. 또한 비동기 처리 방식을 통해 시스템의 부하를 분산시키고, 전체 시스템의 안정성과 확장성을 높이는 데 크게 기여하고 있어요.


4. Feed-Entity 서빙 흐름 💨

Feed-Entity를 잘 저장하는 것만큼이나 효율적으로 서빙하는 것도 정말 중요해요. 특히 피드는 데이터를 쓰는 작업보다 읽는 트래픽이 훨씬 많다는 특성이 있기 때문에, 읽기에 최적화된 구조로 데이터를 저장하고 적절한 캐싱 전략을 설계하는 것이 필수적이에요.

피드인프라팀에서는 Feed-Entity 데이터를 효율적으로 서빙하기 위해 지역별 특성을 고려한 Redis 캐싱 전략을 구현했어요. Feed-Entity는 지역성이 강한 특징이 있기 때문에, 지역별로 다른 캐시를 구성해서 데이터 접근 속도를 크게 향상시켰답니다. 사용자가 특정 지역의 피드를 요청하면, 해당 지역에 최적화된 캐시에서 데이터를 빠르게 가져와요. 🏎️💨

Feed-Entity 서빙 시스템은 효율적인 데이터 접근을 위해 다단계 저장 및 조회 구조를 갖추고 있어요. 이 시스템은 크게 Redis 캐싱 레이어데이터베이스 저장소의 두 계층으로 구성되어 있답니다.

4.1. 데이터 저장 프로세스 💾

Feed-Entity가 생성되거나 업데이트되면 다음과 같은 과정을 거쳐요.

  1. 데이터베이스 저장: 모든 Feed-Entity는 우선 영구 저장소인 데이터베이스에 저장돼요. 이는 데이터의 영구성과 내구성을 보장하기 위함이죠.
  2. 인덱스 생성: 저장과 동시에 해당 Feed-Entity의 메타데이터와 지역 정보를 포함한 인덱스가 생성돼요. 이 인덱스는 빠른 검색과 필터링을 위한 핵심 자료구조랍니다.
  3. 레디스 캐시 업데이트: 생성된 인덱스는 지역별로 구분된 Redis 캐시에 저장돼요. 각 지역마다 별도의 캐시 버킷을 유지해서 지역 기반 쿼리의 성능을 최적화했어요.

4.2. 데이터 조회 프로세스 🔍

사용자가 특정 지역의 피드를 요청하면, 시스템은 다음과 같은 단계로 데이터를 효율적으로 조회해요.

  1. 캐시 조회 (Cache Hit): 먼저 해당 지역의 Redis 캐시에서 인덱스를 조회해요. 이때 인덱스에는 Feed-Entity ID와 기본적인 메타데이터만 포함되어 있어서 메모리 사용을 최소화하면서도 빠른 응답 속도를 제공한답니다.
  2. 데이터베이스 조회 최소화: 캐시에서 인덱스를 찾으면(Cache Hit), 해당 인덱스를 기반으로 필요한 Feed-Entity만 선택적으로 캐시 또는 데이터베이스에서 가져와요. 이를 통해 전체 데이터베이스 스캔 없이도 필요한 데이터만 효율적으로 접근할 수 있어요.
  3. 캐시 미스 처리 (Cache Miss): 만약 캐시에서 해당 지역의 인덱스를 찾지 못하면(Cache Miss), 시스템은 데이터베이스를 직접 조회하여 필요한 Feed-Entity를 검색해요.

4.3. 성능 최적화 📈

이러한 다층 구조는 여러 가지 성능 이점을 가져왔어요.

  • 읽기 트래픽 최적화: 피드 시스템은 특성상 쓰기보다 읽기 작업이 압도적으로 많아요. 인덱스 기반 캐싱을 통해 읽기 성능을 크게 향상시켰죠.
  • 지역별 부하 분산: 지역별로 별도의 캐시를 관리함으로써 특정 지역의 트래픽이 증가하더라도 다른 지역의 성능에 영향을 미치지 않도록 했어요.

이렇게 설계된 Feed-Entity 서빙 시스템은 수백만 사용자의 지역 기반 피드 요청을 매우 낮은 지연 시간(p99 기준 평균 20ms 이하)으로 처리해요. 특히 사용자가 많은 대도시 지역에서도 안정적인 성능을 유지하며 사용자 경험을 크게 개선했답니다. 😊


5. Feed-Entity의 장점과 효과 💡

Feed-Entity를 도입하면서 저희 팀은 정말 많은 중요한 개선 효과를 얻을 수 있었어요. 앞서 Feed-Entity를 통해 이루고 싶었던 다섯 가지 목표를 이야기했었죠? 각 측면에서 경험한 구체적인 장점들을 다시 한번 정리해 드릴게요.

  • 데이터 구조의 표준화: 이전에는 서로 다른 형식의 데이터를 처리하기 위해 각 서비스마다 별도의 로직이 필요했어요. 하지만 Feed-Entity를 통해 모든 콘텐츠를 일관된 형식으로 관리하면서 개발 복잡성이 크게 줄어들었답니다. 📉
  • 시스템 확장성 개선: 기존에는 새로운 콘텐츠 타입을 추가할 때마다 피드 시스템 전체를 수정해야 했어요. 하지만 Feed-Entity 도입 후에는 해당 콘텐츠에 대한 변환 모듈만 추가하면 되는 구조가 되었죠. 이런 유연한 아키텍처 덕분에 빠르게 변화하는 비즈니스 요구사항에 신속하게 대응할 수 있게 되었어요. 🚀
  • 데이터 일관성 확보: DLQ 처리 메커니즘을 통해 데이터 손실 없이 오류 상황을 효과적으로 관리하고, 표준화된 스키마를 통해 데이터 품질을 일관되게 유지할 수 있어요. 이는 서비스 안정성 향상운영 부담 감소로 이어졌답니다.
  • 통합 관리 시스템 구축: 서비스 간 연동이 훨씬 쉬워졌어요! Feed-Entity를 통해 이제는 한 곳에서 모든 콘텐츠를 관리할 수 있게 되었죠. 새로운 서비스를 추가하거나 기존 서비스를 수정할 때도, 표준화된 인터페이스 덕분에 전체 시스템에 미치는 영향이 최소화되었어요. 이런 구조적 이점은 개발 속도와 효율성을 크게 향상시켰고, 팀 간 협업도 더 원활하게 만들었답니다. 🤝
  • 사용자 경험 향상: 다양한 콘텐츠 타입(중고거래, 알바, 부동산, 동네생활 등)을 통합적으로 관리하게 되면서, 사용자에게 더욱 풍부하고 개인화된 피드 경험을 제공할 수 있어요. 사용자의 관심사와 위치에 기반한 맞춤형 콘텐츠를 보여줄 수 있게 되었죠. 💖

이러한 여러 장점들이 모여 피드 시스템은 Feed-Entity를 통해 더 안정적이고, 확장 가능한 시스템을 구축할 수 있었답니다.


6. Feed-Entity NEXT 🌠

Feed-Entity는 현재 당근 피드 시스템에서 핵심적인 역할을 하고 있지만, 아직 해결해야 할 여러 과제가 남아있어요. 사용자 수가 꾸준히 늘어나고 콘텐츠도 다양해지면서, 데이터 처리 효율성과 확장성에 대한 새로운 요구사항이 계속해서 등장하고 있거든요. 또한 사용자별 맞춤형 경험을 더욱 세밀하게 제공하기 위해서는 Feed-Entity 시스템의 고도화가 필요한 상황이에요. 이러한 도전 과제들을 해결하기 위해 저희 팀은 다음과 같은 방향으로 개선을 계획하고 있답니다.

  • 복합적 인덱싱 전략: 현재는 단순히 최신순 지역별 큐만 가지고 있지만, 이것만으로는 다양한 요구를 충족시키기에 부족해요. 앞으로는 거리순, 카테고리별, 인기도별, 최신순 등 다양한 인덱싱 전략을 구현하여 멀티 큐 시스템을 운영할 필요성이 있어요. 이를 통해 사용자가 원하는 방식으로 콘텐츠를 빠르게 탐색할 수 있게 될 거예요. 특히 지리적 거리에 따른 인덱싱은 하이퍼로컬 서비스인 당근의 특성을 더욱 강화할 수 있는 중요한 요소예요. 또한 카테고리별 인덱싱은 사용자가 관심 있는 특정 영역의 콘텐츠만 효율적으로 찾을 수 있게 도와줄 거고요. 이러한 복합적 인덱싱 전략을 구현함으로써 Feed-Entity의 서빙 속도와 정확도를 크게 향상시킬 수 있을 것으로 기대해요! 🎯
  • 추천 모델 학습 활용: Feed-Entity는 아직 추천 시스템 전반에 적용되지는 않고 있어요. 이를 추천 모델 학습과 서빙에 활용한다면, 사용자별 선호도 예측의 정확도를 높이고 개인화된 피드 경험을 제공할 수 있을 것이에요. 또한 통일된 데이터 구조를 기반으로 ML 파이프라인을 구축하면 모델 업데이트와 학습 프로세스를 더욱 효율적으로 관리할 수 있을 거라 기대된답니다. 🤖
  • 탐색 시스템의 SSOT 확장: 현재 Feed-Entity는 해시태그 시스템과 추천 알림에서 SSOT(Single Source of Truth)로 성공적으로 활용되고 있어요. 이러한 성공을 바탕으로 더 많은 시스템으로 Feed-Entity의 활용 범위를 확장할 계획이에요. 이를 통해 전체 서비스에서 일관된 데이터 구조와 높은 신뢰성을 확보할 수 있을 것으로 기대해요.

이러한 과제들을 해결하여 Feed-Entity를 추천 전반에 활용할 수 있게 된다면, 당근의 피드 시스템은 한 단계 더 발전된 형태로 사용자들에게 가치 있는 경험을 제공할 수 있을 것이라고 믿어요.


마치며... 💖

이렇게 피드라는 복잡한 문제를 해결하기 위해 Feed-Entity라는 개념을 도입하고 발전시켜 온 피드인프라팀! 저희는 항상 더 나은 기술과 사용자 경험을 위해 끊임없이 도전하고 있답니다.

혹시 이런 기술적 도전에 함께하고 싶으신가요? 당근 피드인프라팀에서는 수백만 명의 사용자들에게 하이퍼로컬 가치 있는 정보를 효율적으로 전달하기 위한 기술을 개발하고 다양한 실험에 도전할 열정적인 동료를 찾고 있어요.

당근 피드인프라팀과 함께 하이퍼로컬 피드 경험을 만들어 갈 동료를 기다립니다!

당근마켓 채용 페이지: https://team.daangn.com/jobs/

요약 완료: 2025. 11. 12. 오전 5:45:29

이런 요약이 필요하신가요?

하베스트가 원클릭으로 요약해드립니다

5초 요약
AI 자동 분석
📱
모든 기기
웹, iOS, Chrome
🔍
스마트 검색
언제든 재발견
요약 시작하기
나도 요약하기