시스템 설계 면접 프레임워크
5단계 RADIO 프레임워크(요구 사항, API, 데이터, 인프라, 최적화)를 살펴보고 URL 단축기에 적용하는 연습을 합니다.
시스템 설계 면접 프레임워크은(는) CoddyKit의 무료 DSA Interview Prep 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 DSA Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. DSA Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
면접에서 시스템 설계가 중요한 이유
시스템 설계 면접에서는 대규모 환경에서 사고하는 능력을 평가합니다. 수십억 명의 사용자를 위해 트위터, YouTube 또는 웹 주소 단축 서비스를 어떻게 설계하시겠습니까? 하나의 정답이 있는 코딩 문제와 달리 시스템 설계는 열린 문제이므로, 여러 절충점을 선택하고 그 이유를 설명해야 합니다. 선임 및 책임자급 역할에서는 이 면접 단계에만 30–45분을 할애합니다.
면접관은 요구사항을 명확히 하고, 부하를 추정하고, 상위 수준 아키텍처를 제안하고, 핵심 구성 요소를 자세히 살펴보고, 절충점을 논의하면서도 명확하게 소통할 수 있는지를 평가합니다. 구조화된 프레임워크를 사용하면 장황하게 말하는 것을 막고 모든 중요한 측면을 빠짐없이 다룰 수 있습니다.
# System design is not about a single correct answer.
# Interviewers look for:
eval_criteria = [
'Ability to clarify requirements before designing',
'Back-of-envelope capacity estimation',
'High-level architecture with clear components',
'Data modelling and storage choice',
'Handling scalability (10x, 100x load)',
'Trade-off discussion (consistency vs availability, etc.)',
'Communication: talking through decisions as you make them',
]
for c in eval_criteria:
print('-', c)RADIO 프레임워크 개요
RADIO 프레임워크는 모든 시스템 설계 면접에 적용할 수 있는 반복 가능한 5단계 구조를 제공합니다.
- R — 요구사항: 기능적 요구사항과 비기능적 요구사항
- A — 응용 프로그램 인터페이스 설계: 시스템이 어떤 작업을 제공하는가?
- D — 데이터 모델: 어떤 데이터를 어떻게 저장하는가?
- I — 인프라: 상위 수준 구성 요소(서버, 대기열, 캐시)
- O — 최적화: 병목, 캐싱, 샤딩, 복제
항상 이 단계를 순서대로 진행하되, 새로운 통찰이 생기면 앞선 단계로 돌아가 다듬으십시오. 각 단계에 대략 같은 시간을 할애하십시오. 요구사항을 먼저 명확히 하지 않고 곧바로 상자를 그리기 시작해서는 안 됩니다.
RADIO = {
'R': 'Requirements — What must the system do? What scale?',
'A': 'API — Define endpoints/operations the system exposes',
'D': 'Data Model — Entities, schemas, storage types',
'I': 'Infra — High-level architecture: servers, queues, caches, CDN',
'O': 'Optimise — Identify and address bottlenecks, trade-offs',
}
for step, desc in RADIO.items():
print(f'[{step}] {desc}')
print('\nTiming guide for a 45-min interview:')
print(' R: 5 min | A: 5 min | D: 10 min | I: 15 min | O: 10 min')R단계: 요구사항 명확히 하기
요구사항을 명확히 하지 않고 절대로 설계를 시작하지 마십시오. 기능적 요구사항(시스템이 하는 일)과 비기능적 요구사항(규모, 지연 시간, 가용성)을 질문하십시오. 웹 주소 단축 서비스의 경우는 다음과 같습니다.
- 기능적 요구사항: 웹 주소 단축, 원래 웹 주소로 리디렉션, 선택적으로 사용자 지정 별칭과 만료 지원
- 비기능적 요구사항: 하루에 웹 주소를 몇 개 처리하는가? 읽기 중심인가 쓰기 중심인가? 가용성 요구사항은 무엇인가(99.9%와 99.99% 중 어느 쪽인가)? 허용 가능한 지연 시간은 얼마인가?
가정을 명시적으로 밝히면 숙련도를 보여 줄 수 있습니다. 면접관은 올바른 질문을 하는지 확인하기 위해 일부러 모호한 사양을 제시하는 경우가 많습니다. 2분 동안 요구사항을 명확히 하면 잘못된 시스템을 설계하는 일을 피할 수 있습니다.
# Requirements questions for URL Shortener:
functional = [
'Shorten a given URL to a 7-character alias',
'Redirect short URL to original URL',
'Allow custom aliases (optional)',
'URL expiry (optional)',
'Analytics: click count per URL (optional)',
]
non_functional = [
'100 million new URLs per day (write: ~1160/sec)',
'10:1 read:write ratio => 11,600 redirects/sec',
'Redirects must be < 100ms p99 latency',
'99.99% availability (< 1 hr downtime/year)',
'URLs must be globally accessible',
]
print('Functional:')
for f in functional: print(' -', f)
print('\nNon-functional:')
for nf in non_functional: print(' -', nf)R단계: 개략적 용량 추정
요구사항을 정한 후에는 용량을 추정하십시오. 이는 해결책을 제안하기 전에 규모를 추론할 수 있음을 보여 줍니다. 도출해야 할 주요 수치는 초당 요청 수(RPS), 하루 또는 1년에 필요한 저장 공간, 대역폭, 캐싱에 필요한 메모리입니다.
어림수를 사용하고 자유롭게 근사하십시오. 면접관은 정확한 수치보다 규모의 차수에 관심이 있습니다. 웹 주소 단축 서비스의 예를 들면, 하루 1억 건의 쓰기 ÷ 86400 ≈ 초당 1160건입니다. 하루 100억 건의 읽기 ÷ 86400 ≈ 초당 11.5만 건입니다. 웹 주소 항목 하나가 약 500바이트라면 1억 × 500바이트 = 하루 50 GB, 1년에 18 TB입니다.
# Back-of-envelope for URL Shortener
writes_per_day = 100_000_000 # 100 million URLs/day
read_write_ratio = 100 # 100:1 read/write
reads_per_day = writes_per_day * read_write_ratio
bytes_per_url = 500 # url string + metadata
years_to_store = 5
print('=== Capacity Estimation ===')
print(f'Writes/sec: {writes_per_day / 86400:.0f}')
print(f'Reads/sec: {reads_per_day / 86400:,.0f}')
print(f'Storage/day: {writes_per_day * bytes_per_url / 1e9:.1f} GB')
print(f'Storage total ({years_to_store}y): {writes_per_day * bytes_per_url * 365 * years_to_store / 1e12:.1f} TB')
cache_hit_rate = 0.80
hot_urls = reads_per_day * (1 - cache_hit_rate)
print(f'\n80% cache hit rate: {cache_hit_rate*100}% of reads from cache')
print(f'DB reads/sec: {hot_urls / 86400:,.0f}')A단계: 응용 프로그램 인터페이스 설계
응용 프로그램 인터페이스의 범위를 정의하십시오. 이는 시스템이 클라이언트와 내부 서비스에 제공하는 작업을 의미합니다. 요청 방식, 엔드포인트 경로, 요청 매개변수, 응답 형식을 명확히 지정하십시오. 나머지 설계는 이 인터페이스를 구현하기 위해 존재하므로, 이 작업이 설계 전체의 기준이 됩니다.
웹 주소 단축 서비스의 핵심 인터페이스는 두 가지입니다. (1) 짧은 웹 주소를 생성하는 POST /shorten, (2) 리디렉션하는 GET /{alias}입니다. 선택적으로 삭제를 위한 DELETE /{alias}, 분석을 위한 GET /{alias}/stats도 제공할 수 있습니다. 응답 코드도 지정하십시오(201 생성됨, 301 리디렉션, 404 찾을 수 없음).
# API Design for URL Shortener
apis = [
{
'method': 'POST',
'path': '/api/v1/shorten',
'request': '{"long_url": "https://...", "alias": "optional", "expires_at": "optional"}',
'response': '201 Created: {"short_url": "https://short.ly/abc1234", "alias": "abc1234"}',
},
{
'method': 'GET',
'path': '/{alias}',
'request': 'No body',
'response': '301 Redirect to long_url (or 404 Not Found)',
},
{
'method': 'GET',
'path': '/api/v1/{alias}/stats',
'request': 'Optional: date range query params',
'response': '200 OK: {"clicks": 42000, "unique_visitors": 15000}',
},
]
for api in apis:
print(f'{api["method"]} {api["path"]}')
print(f' Request: {api["request"]}')
print(f' Response: {api["response"]}')
print()D단계: 데이터 모델
데이터 모델은 무엇을 어떻게 저장하는지 정의합니다. 핵심 엔터티와 그 속성을 식별하십시오. 웹 주소 단축 서비스에는 alias(기본 키), long_url, created_at, expires_at, user_id가 있는 urls 테이블이 필요합니다. 분석을 위해 선택적으로 clicks 테이블을 둘 수도 있습니다.
올바른 저장소 유형을 선택하는 것이 중요합니다. 복잡한 쿼리가 있는 구조화된 데이터에는 관계형 데이터베이스를, 대규모 환경에서 O(1) 별칭 조회에는 키-값 저장소(레디스, DynamoDB)를, 큰 데이터 덩어리에는 객체 저장소(에스쓰리)를 사용합니다. 웹 주소 단축 서비스에서는 별칭을 키로 사용하는 키-값 저장소가 읽기에 이상적이며, 쓰기와 관리에는 관계형 데이터베이스를 사용할 수 있습니다.
# Data model for URL Shortener
# Core table (PostgreSQL)
urls_schema = '''
CREATE TABLE urls (
alias VARCHAR(16) PRIMARY KEY, -- e.g., 'abc1234'
long_url TEXT NOT NULL,
user_id UUID REFERENCES users(id),
created_at TIMESTAMP DEFAULT NOW(),
expires_at TIMESTAMP,
click_count BIGINT DEFAULT 0
);
CREATE INDEX ON urls(user_id);
'''
# Cache layer (Redis) for hot reads
redis_schema = '''
alias => long_url # O(1) GET on cache hit
TTL = 24 hours (or until expiry)
Cache eviction: LRU
'''
print('PostgreSQL schema:')
print(urls_schema)
print('Redis cache:')
print(redis_schema)
print('Storage split: Redis for hot reads (~80%), PostgreSQL for writes and cold reads')I단계: 상위 수준 인프라
상위 수준의 인프라를 설계하십시오. 어떤 서버가 어떤 책임을 담당하는지, 구성 요소 사이에서 데이터가 어떻게 흐르는지, 어떤 외부 서비스를 사용하는지를 정합니다. 대규모 웹 주소 단축 서비스의 경우는 다음과 같습니다.
- 부하 분산기: 쓰기 및 읽기 서비스 복제본으로 트래픽을 분산합니다.
- 쓰기 서비스: 별칭을 생성하고, 고유성을 검증하고, 데이터베이스에 기록하고, 캐시를 무효화합니다.
- 읽기/리디렉션 서비스: 먼저 레디스 캐시를 확인하고, 캐시에 없으면 데이터베이스로 대체합니다.
- 관계형 데이터베이스: 기준 데이터 원본입니다(읽기 복제본 포함).
- 레디스 클러스터: 자주 사용되는 웹 주소 연결 정보를 캐시하여 1밀리초 미만의 읽기를 제공합니다.
# ASCII architecture sketch
architecture = '''
Clients
|
[Load Balancer]
/ \\
[Write API] [Read/Redirect API]
| | |
[Alias Gen] [Redis Cache]
| |
[PostgreSQL]---->[Read Replicas]
Alias Generation:
- Base62 encoding of auto-incrementing ID
- 62^7 = 3.5 trillion unique URLs (enough for 5 years at 100M/day)
- OR random 7-char Base62 with collision check
'''
print(architecture)
print('Key design decisions:')
print(' - Read/Write split: separate services for scalability')
print(' - Cache-aside pattern: read from Redis, fallback to DB')
print(' - 301 vs 302 redirect: 301 cached by browser (less load), 302 always hits server (analytics)')O단계: 최적화 및 병목 처리
최적화 단계에서는 병목을 해결하고 시스템을 확장합니다. 웹 주소 단축 서비스의 주요 고려 사항은 리디렉션 지연 시간(웹 사용자와 가까운 곳에 CDN 또는 엣지 캐시를 배치), 대규모 환경에서 별칭의 고유성(중앙 티켓 서버를 사용하거나 충돌 감지 기능이 있는 해시 사용), 데이터베이스 쓰기 병목(대기열을 사용한 일괄 쓰기 또는 비동기 쓰기)입니다.
절충점을 명시적으로 논의하십시오. 301 리디렉션은 서버 부하를 줄이지만 분석 정확도를 떨어뜨리고, 302 리디렉션은 추적할 수 있지만 지연 시간을 추가합니다. 만료 시간을 길게 설정한 캐싱은 데이터베이스 부하를 줄이지만 오래된 웹 주소가 남을 위험이 있습니다. 이러한 상충 관계를 이해하고 있음을 보여 주면 선임 수준의 사고를 드러낼 수 있습니다.
# Key optimisation discussion points
optimisations = {
'Read latency': [
'Redis cluster in multiple regions (CDN-edge caching)',
'301 redirect for non-tracked URLs (client caches)',
'302 redirect when click analytics are needed',
],
'Write throughput': [
'Async writes: accept request, enqueue to Kafka, batch-commit to DB',
'Ticket server (centralised ID generator) to avoid UUID collision',
'Or: hash(user_id + timestamp + random) with retry on collision',
],
'Availability': [
'Multi-AZ PostgreSQL with automatic failover',
'Redis Sentinel or Redis Cluster for HA cache',
'Health checks + circuit breaker on each service',
],
'Storage': [
'Partition urls table by hash(alias) for horizontal scaling',
'Archive expired URLs to cold storage (S3)',
],
}
for area, points in optimisations.items():
print(f'{area}:')
for p in points: print(f' - {p}')
print()별칭 생성: 62진 인코딩
웹 주소 단축 서비스의 핵심 기술 세부 사항은 짧고 고유한 별칭을 생성하는 방법입니다. 표준적인 접근법은 데이터베이스에서 자동 증가 정수 ID를 사용하고 이를 62진수(숫자 0-9, 문자 a-z, A-Z)로 인코딩하는 것입니다. 7자리 62진 문자열은 약 3.5조 개의 고유한 웹 주소를 표현할 수 있으므로, 하루 1억 개를 처리해도 수십 년 동안 충분합니다.
각 ID가 고유하므로 충돌이 발생하지 않으며, 짧고 웹 주소에 안전한 문자열을 생성합니다. ID→별칭 매핑은 결정적이고 되돌릴 수 있습니다. 단점은 순차적인 ID 때문에 별칭을 예측할 수 있다는 점입니다(보안 문제). 62진 문자 집합을 섞거나 카운터 오프셋을 사용하여 예측 가능성을 줄이십시오.
import string
BASE62_CHARS = string.digits + string.ascii_lowercase + string.ascii_uppercase
BASE = 62
def encode_base62(num):
if num == 0:
return BASE62_CHARS[0]
result = ''
while num:
result = BASE62_CHARS[num % BASE] + result
num //= BASE
return result
def decode_base62(s):
result = 0
for c in s:
result = result * BASE + BASE62_CHARS.index(c)
return result
# Generate aliases for IDs 1, 100, 1000, 10^9
for id_ in [1, 100, 1000, 1_000_000, 10**9, 3_521_614_606207]:
alias = encode_base62(id_)
decoded = decode_base62(alias)
print(f'ID {id_:20,} => alias "{alias}" (len={len(alias)}) => decoded={decoded}')트위터 피드 설계에 RADIO 적용하기
프레임워크가 일반화될 수 있음을 보여 주기 위해 트위터와 유사한 뉴스 피드 설계에 RADIO를 간단히 적용해 보겠습니다.
- R: 사용자는 트윗을 게시하고, 다른 사용자를 팔로우하며, 팔로우하는 사람들의 트윗 목록을 봅니다. 규모: 사용자 3억 명, 하루 5억 개의 트윗, 목록은 <2초 안에 불러와야 합니다.
- A:
POST /tweets,GET /feed,GET /timeline/{user_id} - D:
tweets테이블(id, user_id, content, created_at);follows테이블(follower_id, followee_id); 사용자별 피드 캐시 - I: 팬아웃 서비스가 새 트윗을 팔로워별 목록에 기록합니다(미리 계산). 쓰기 중심의 팔로우/트윗 테이블에는 카산드라를, 피드 캐시에는 레디스를 사용합니다.
# Fan-out on Write vs Fan-out on Read trade-off
fan_out_strategies = {
'Fan-out on Write (Push)': {
'How': 'When user A posts, immediately write to all followers feeds',
'Pro': 'O(1) feed read — feed is precomputed',
'Con': 'Celebrities with 10M followers => 10M writes per post; very slow write',
'Best for': 'Users with few followers (regular users)',
},
'Fan-out on Read (Pull)': {
'How': 'When user reads feed, query followees tweets and merge',
'Pro': 'Writes are fast (one DB write per tweet)',
'Con': 'Feed read is slow: must query all N followees',
'Best for': 'Celebrity accounts (few reads per post)',
},
'Hybrid': {
'How': 'Push for regular users, pull for celebrities',
'Pro': 'Balances read and write cost',
'Con': 'More complex implementation',
'Best for': 'Production systems like Twitter',
},
}
for strategy, details in fan_out_strategies.items():
print(f'{strategy}:')
for k, v in details.items(): print(f' {k}: {v}')
print()시스템 설계 면접에서 흔히 하는 실수
시스템 설계 면접을 망치게 하는 다음과 같은 흔한 실수를 피하셔야 합니다:
- 해결책부터 제시하기: 요구 사항을 명확히 하기 전에 상자를 그리는 것은 좋지 않은 엔지니어링 습관을 드러냅니다
- 추정하지 않기: 규모를 파악하지 않고 설계하는 것은 추측에 불과합니다
- 과도하게 설계하기: 문제에서 10,000명이라고 했는데 10억 명을 대상으로 설계하면 면접 시간을 낭비합니다
- 상충 관계를 언급하지 않기: 모든 선택에는 장단점이 있으며, 이를 언급하지 않으면 이해가 피상적이라는 인상을 줍니다
- 침묵하기: 면접관은 사고 과정을 들어야 하므로 결정을 내리는 과정을 말로 설명하셔야 합니다
# Checklist: before you stop talking, verify you covered:
checklist = [
'[ ] Asked clarifying questions about scale and constraints',
'[ ] Made capacity estimates (RPS, storage, bandwidth)',
'[ ] Defined the API surface clearly',
'[ ] Described the data model and storage choices',
'[ ] Drew a high-level architecture with named components',
'[ ] Identified the main bottleneck and proposed a solution',
'[ ] Discussed at least one trade-off explicitly',
'[ ] Verified the design meets the stated requirements',
]
for item in checklist:
print(item)빠른 확인
이 단원에서 다룬 자료 구조 및 알고리즘 — 코딩 면접 준비 개념에 대한 이해도를 확인해 보세요.
단원 요약
이 단원에서는 다음을 배웠습니다: RADIO 프레임워크는 시스템 설계 면접을 요구 사항, 애플리케이션 프로그래밍 인터페이스, 데이터 모델, 인프라, 최적화 단계로 구성합니다. 또한 설계를 제안하기 전에 기능적 요구 사항과 비기능적 요구 사항을 항상 명확히 하고 용량을 추정해야 합니다. 그리고 상충 관계를 명시적으로 논의해야 합니다. 모든 설계 선택에는 면접관이 설명하기를 기대하는 장단점이 있습니다. 다음으로는 접근 패턴, 일관성, 규모를 바탕으로 SQL과 NoSQL 저장 엔진 중에서 선택하는 방법을 살펴보겠습니다.
자주 묻는 질문
“시스템 설계 면접 프레임워크” 강의는 무료인가요?
네 — “시스템 설계 면접 프레임워크” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 DSA Interview Prep 강의 전체를 잠금 해제할 수 있습니다. DSA Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
“시스템 설계 면접 프레임워크”에서 뭘 배우나요?
5단계 RADIO 프레임워크(요구 사항, API, 데이터, 인프라, 최적화)를 살펴보고 URL 단축기에 적용하는 연습을 합니다. 브라우저에서 직접 실행하는 실습 코드로 DSA Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
DSA Interview Prep을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 DSA Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“시스템 설계 면접 프레임워크” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 DSA Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 DSA Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 시스템 설계 면접 프레임워크
- 확장 가능한 데이터 저장소: SQL과 NoSQL
- 캐싱, CDN과 부하 분산
- 속도 제한기 설계와 트위터 피드 설계