stack-analysis
CHAPTER 81 / 90
읽기 약 2분
FUNCTION
소셜 플랫폼 아키텍처: 피드/팔로우/알림
핵심 개념
도메인 모델·읽기/쓰기 트래픽·확장 — Twitter·Instagram 패턴.
본문
핵심 도메인
[User] 사용자
- profile, followers, following
[Post] 게시물
- text, media, hashtags, mentions
[Feed] 피드 (홈 타임라인)
- 자신 + 팔로잉 게시물
[Engagement] 참여
- like, comment, share, save
[Notification] 알림
- new follower, mention, reply, like
[Discovery] 탐색
- explore, trending, search트래픽 패턴 (Twitter 사례)
읽기 : 쓰기 = 100 : 1
[쓰기]
- 새 게시물: 분당 수백 건
[읽기]
- 피드 조회: 분당 수만 건
- 게시물 상세: 분당 수십만
→ 읽기 최적화 우선
→ 캐시 + CDN 적극데이터 모델
CREATE TABLE users (
id UUID PRIMARY KEY,
handle VARCHAR UNIQUE NOT NULL, -- @alice
name VARCHAR NOT NULL,
bio TEXT,
avatar_url VARCHAR,
followers_count INT DEFAULT 0, -- 비정규화 (캐시)
following_count INT DEFAULT 0,
posts_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE follows (
follower_id UUID NOT NULL,
followed_id UUID NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (follower_id, followed_id)
);
CREATE INDEX ON follows(followed_id, created_at DESC);
CREATE INDEX ON follows(follower_id, created_at DESC);
CREATE TABLE posts (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
content TEXT,
media JSONB, -- [{ url, type, ... }]
parent_id UUID REFERENCES posts(id), -- reply
quote_id UUID REFERENCES posts(id), -- quote post
visibility VARCHAR DEFAULT 'public', -- public, followers, private
likes_count INT DEFAULT 0,
comments_count INT DEFAULT 0,
shares_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
deleted_at TIMESTAMP
);
CREATE INDEX ON posts(user_id, created_at DESC) WHERE deleted_at IS NULL;
CREATE INDEX ON posts(parent_id) WHERE parent_id IS NOT NULL;팔로우 + 카운터 갱신
async function followUser(followerId: string, followedId: string) {
if (followerId === followedId) {
throw new BadRequestError('Cannot follow yourself');
}
return db.$transaction(async (tx) => {
// 차단된 사용자인가?
const blocked = await tx.block.findFirst({
where: {
OR: [
{ blockerId: followedId, blockedId: followerId },
{ blockerId: followerId, blockedId: followedId },
],
},
});
if (blocked) throw new ForbiddenError('Blocked');
await tx.follow.create({
data: { followerId, followedId },
});
await tx.user.update({
where: { id: followedId },
data: { followersCount: { increment: 1 } },
});
await tx.user.update({
where: { id: followerId },
data: { followingCount: { increment: 1 } },
});
// 알림
await notify({
userId: followedId,
type: 'new_follower',
title: '새 팔로워',
body: `@${followerHandle}님이 팔로우했습니다`,
link: `/users/${followerHandle}`,
});
});
}피드 모델 비교 (다음 챕터에서 자세히)
[Pull Model] (요청 시 계산)
- 피드 = 팔로잉 사용자의 최근 게시물 SELECT
- 쓰기 비용 0
- 읽기 비용 큼 (팔로잉 수 비례)
[Push Model] (Fan-out on write)
- 게시물 작성 시 모든 팔로워의 피드 캐시에 추가
- 읽기 빠름 (캐시만 조회)
- 쓰기 비용 큼 (팔로워 수 비례)
[Hybrid] (Twitter 방식)
- 일반 사용자: Push
- 메가 인플루언서 (팔로워 수만+): Pull
- 둘을 합쳐서 피드 구성다음 챕터
CH.82 "뉴스피드: 팬아웃 vs 풀 모델".
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 프로젝트의 소셜 아키텍처 부분을 분석해서 실전 분석 + 개선 우선순위를 알려줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
소셜 아키텍처 관련 실제 서비스 5개를 비교 분석해서 패턴 추출를 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 코드베이스에서 소셜 아키텍처 최적화 가능 위치를 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 한국 풀스택 시장의 소셜 아키텍처 트렌드를 솔직히 알려줘.
⭐ 이것만 기억하세요
소셜 플랫폼 아키텍처: 피드/팔로우/알림은 이 3가지만 확실히 잡으세요
1.소셜 플랫폼은 읽기:쓰기 = 100:1 — 읽기 최적화 우선
2.카운터 캐싱(followers_count) — COUNT() 매번 X
3.Pull/Push/Hybrid — 팔로워 수에 따라 선택
공유하기
진행도 81 / 90