0Pricing
DSA Interview Prep · 강의

캐싱, CDN과 부하 분산

Redis 캐시 계층을 추가하고 정적 자산을 CDN으로 전송하며, 라운드 로빈 및 일관된 해싱 부하 분산기로 복제본에 트래픽을 분배합니다.

캐싱, CDN과 부하 분산은(는) CoddyKit의 무료 DSA Interview Prep 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 DSA Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. DSA Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

대규모 환경에서 캐싱이 필수적인 이유

캐싱은 자주 접근하는 데이터의 복사본을 더 빠른 저장 계층에 저장하여 이후 요청을 느린 기본 저장소(데이터베이스, 외부 서비스 인터페이스)에 접근하지 않고 처리할 수 있도록 합니다. 규모가 커지면 소수의 인기 항목에 대부분의 요청이 집중됩니다. 이때 흔히 80/20 법칙(파레토 원칙)이 적용되며, 항목의 20%가 트래픽의 80%를 차지합니다.

자주 사용되는 20%를 메모리에 담을 수 있는 캐시는 데이터베이스 부하의 80%를 흡수할 수 있습니다. 따라서 레디스 캐시를 추가하면 데이터베이스나 애플리케이션 로직을 크게 변경하지 않고도 데이터베이스 CPU 사용량을 70~90% 줄이고, 캐시 적중 시 p99 지연 시간을 10ms에서 1ms 미만으로 낮추는 경우가 많습니다.

# Demonstrating the 80/20 caching benefit
import random

# Simulate 1000 requests to 100 items with Zipf-like distribution
def zipf_sample(n_items, n_requests):
    access_counts = {}
    weights = [1.0 / (i + 1) for i in range(n_items)]  # Zipf: item 0 most popular
    total = sum(weights)
    probs = [w / total for w in weights]
    for _ in range(n_requests):
        item = random.choices(range(n_items), weights=probs)[0]
        access_counts[item] = access_counts.get(item, 0) + 1
    return access_counts

random.seed(42)
counts = zipf_sample(100, 10000)
top_20_items = sorted(counts, key=counts.get, reverse=True)[:20]
top_20_requests = sum(counts[i] for i in top_20_items)
print(f'Top 20% of items ({20} of 100) handle {top_20_requests/100:.1f}% of requests')

캐시 우선 패턴(지연 로딩)

캐시 우선 패턴(지연 로딩이라고도 함)은 가장 일반적인 캐싱 전략입니다. 애플리케이션 코드가 캐시를 관리합니다. 읽기 요청이 오면 먼저 캐시를 확인합니다. 캐시 적중이면 즉시 반환합니다. 캐시 누락이면 데이터베이스에서 가져와 캐시에 저장한 다음 반환합니다. 쓰기 요청이 오면 데이터베이스를 업데이트하고 캐시 항목을 무효화(delete)하여 다음 읽기 요청에서 새로 고치도록 합니다.

이 패턴을 사용하면 실제로 요청된 데이터만 캐시에 저장되므로 불필요한 사전 로드를 피할 수 있고, 무효화를 통해 데이터베이스와의 일관성도 유지할 수 있습니다. 상충 관계는 캐시가 누락된 후 처음 접근할 때 데이터베이스의 전체 비용을 부담한다는 점입니다(초기 접근).

# Cache-aside pattern in Python
class CacheAsideService:
    def __init__(self, db, cache):
        self.db = db
        self.cache = cache   # e.g., Redis client

    def get_user(self, user_id):
        cache_key = f'user:{user_id}'
        # 1. Check cache
        cached = self.cache.get(cache_key)
        if cached:
            return cached    # cache hit
        # 2. Cache miss: fetch from DB
        user = self.db.query('SELECT * FROM users WHERE id=%s', user_id)
        # 3. Write to cache with TTL
        self.cache.set(cache_key, user, ttl=3600)  # 1 hour TTL
        return user

    def update_user(self, user_id, data):
        # 1. Write to DB
        self.db.execute('UPDATE users SET ... WHERE id=%s', user_id, data)
        # 2. Invalidate cache (delete, not update)
        self.cache.delete(f'user:{user_id}')
        # Next read will re-populate cache from DB

print('Cache-aside: READ from cache, miss? load from DB + write cache')
print('         WRITE to DB, then DELETE from cache (invalidate)')

쓰기 동기화와 지연 쓰기 캐싱

쓰기 동기화: 쓰기가 발생할 때마다 데이터베이스와 캐시를 모두 동기적으로 업데이트합니다. 캐시에는 항상 최신 데이터가 들어 있습니다. 상충 관계는 두 작업을 수행하므로 쓰기가 느려지고, 다시 읽지 않을 수도 있는 데이터까지 캐시에 채워진다는 점입니다.

지연 쓰기(쓰기 후 저장): 쓰기가 발생하면 캐시만 업데이트하고 나중에 데이터베이스에 비동기적으로 반영합니다. 쓰기 속도는 매우 빨라지지만 반영 전에 캐시에 장애가 발생하면 데이터가 손실될 위험이 있습니다. 일부 데이터 손실을 허용할 수 있는 쓰기 중심 작업 부하(예: 조회수 카운터, 분석)에 사용됩니다.

# Write-through vs Write-behind comparison
strategies = {
    'Cache-aside (Lazy)': {
        'read':  'Check cache; miss => DB + populate cache',
        'write': 'Write DB; delete from cache (invalidate)',
        'consistency': 'Strong (invalidation ensures freshness)',
        'write_latency': 'Fast (one DB write)',
        'risk': 'Cache stampede on popular key expiry',
    },
    'Write-through': {
        'read':  'Always check cache; miss => DB',
        'write': 'Write DB AND cache atomically',
        'consistency': 'Strong (cache always has latest)',
        'write_latency': 'Slower (two writes per operation)',
        'risk': 'Cache polluted with rarely-read data',
    },
    'Write-behind': {
        'read':  'Check cache; miss => DB',
        'write': 'Write cache only; async flush to DB',
        'consistency': 'Eventual (flush may be delayed)',
        'write_latency': 'Very fast (cache write only)',
        'risk': 'Data loss if cache crashes before flush',
    },
}
for name, info in strategies.items():
    print(f'\n{name}:')
    for k, v in info.items(): print(f'  {k}: {v}')

캐시 제거 정책

캐시가 가득 차면 제거 정책이 어떤 항목을 삭제할지 결정합니다. 가장 일반적인 정책은 다음과 같습니다:

  • LRU (가장 오래전에 사용된 항목 우선): 가장 오랫동안 접근되지 않은 항목을 제거합니다. 시간적 지역성이 있는 작업 부하에서 잘 작동하며 레디스에서 기본으로 사용됩니다.
  • LFU (가장 적게 사용된 항목 우선): 접근 횟수가 가장 적은 항목을 제거합니다. 일부 항목이 지속적으로 인기가 있지만 LRU가 이를 충분히 반영하지 못하는 작업 부하에 더 적합합니다.
  • FIFO: 가장 먼저 삽입된 항목을 제거합니다. 단순하지만 일반적인 웹 작업 부하에서는 성능이 좋지 않습니다.
  • 무작위: 무작위로 항목을 제거합니다. 실제로는 매우 큰 캐시에서 LRU와 비교해도 놀라울 만큼 경쟁력 있는 성능을 보입니다.
# Implementing LRU cache
from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = OrderedDict()  # maintains insertion/access order

    def get(self, key):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)   # mark as recently used
        return self.cache[key]

    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)   # evict LRU (oldest)

cache = LRUCache(3)
for k, v in [('a',1),('b',2),('c',3)]:
    cache.put(k, v)
print('Get a:', cache.get('a'))   # 1 (a now most recently used)
cache.put('d', 4)                  # evicts 'b' (LRU)
print('Get b:', cache.get('b'))   # -1 (evicted)
print('Get c:', cache.get('c'))   # 3

콘텐츠 전송 네트워크(CDN)

CDN은 지리적으로 분산된 엣지 서버(접속 거점인 PoP 및 PoPs)가 최종 사용자 가까이에서 정적 및 동적 콘텐츠를 캐시하는 네트워크입니다. 모든 사용자의 요청이 하나의 데이터 센터에 있는 원본 서버까지 이동하는 대신, CDN 엣지 노드가 가장 가까운 PoP에서 콘텐츠를 제공하므로 지연 시간이 약 200ms(대륙 간)에서 약 5ms(가까운 PoP)로 줄어듭니다.

CDN은 정적 assets(이미지, CSS, JS), 동영상 스트리밍(HLS 세그먼트), 그리고 점점 더 많은 애플리케이션 프로그래밍 인터페이스 응답과 서버에서 렌더링되는 HTML을 제공하는 데 필수적입니다. CDN은 엣지 캐시를 확인하고, 캐시 미스가 발생하면 원본에서 콘텐츠를 가져와 이후 요청을 위해 캐시합니다.

# CDN architecture flow
cdn_flow = [
    'User requests https://example.com/image.jpg',
    'DNS resolves to the nearest CDN PoP (e.g., Frankfurt for EU users)',
    'CDN edge checks its local cache:',
    '  HIT:  Return cached image directly (5ms latency)',
    '  MISS: Fetch from origin server (e.g., AWS S3 in us-east-1)',
    '        Cache image at edge with Cache-Control: max-age=86400',
    '        Future requests for this image served from edge (HIT)',
    'Cache-Control headers control CDN behaviour:',
    '  max-age=31536000 s-maxage=31536000  -- cache 1 year',
    '  no-cache                             -- always revalidate',
    '  private                              -- CDN must not cache (user-specific)',
]
for step in cdn_flow:
    print(step)

print('\nCDN providers: Cloudflare, AWS CloudFront, Fastly, Akamai')

부하 분산: 트래픽 분배

로드 밸런서는 들어오는 요청을 여러 백엔드 서버에 분산하여 특정 서버 하나가 병목이 되는 것을 방지합니다. 또한 고가용성을 제공합니다. 한 서버에 장애가 발생하면 로드 밸런서가 상태가 정상인 서버로 트래픽을 자동으로 라우팅합니다(5~30초마다 상태 확인).

로드 밸런서는 서로 다른 OSI 계층에서 작동합니다. 4계층(전송 계층 — IP/포트 기준으로 라우팅하며 매우 빠름)과 7계층(애플리케이션 계층 — URL 경로, 헤더, 쿠키 기준으로 라우팅하여 더 지능적인 라우팅이 가능함)입니다. AWS ALB, 엔진엑스, 에이치에이프록시는 일반적인 7계층 로드 밸런서입니다. AWS NLB는 4계층 로드 밸런서입니다.

# Load balancing algorithms
algorithms = {
    'Round Robin': {
        'how': 'Rotate through servers in sequence',
        'best_for': 'Stateless servers with similar capacity',
        'weakness': 'Does not account for server load or response time',
    },
    'Weighted Round Robin': {
        'how': 'Round robin but servers with more capacity get more requests',
        'best_for': 'Heterogeneous server fleet',
        'weakness': 'Static weights; does not adapt to runtime load',
    },
    'Least Connections': {
        'how': 'Send to server with fewest active connections',
        'best_for': 'Long-lived connections (WebSocket, streaming)',
        'weakness': 'More complex tracking of connection state',
    },
    'Consistent Hashing': {
        'how': 'Hash request key (user_id, session) to server',
        'best_for': 'Sticky sessions, cache locality per server',
        'weakness': 'Uneven distribution if hash space is not balanced',
    },
    'Random': {
        'how': 'Choose server at random',
        'best_for': 'Simple stateless workloads',
        'weakness': 'No guarantee of load balance in short windows',
    },
}
for alg, info in algorithms.items():
    print(f'{alg}: {info["how"]}')

일관성 해싱: 노드 추가 및 제거

일관성 해싱은 서버가 추가되거나 제거될 때 캐시 키를 재분배해야 하는 문제를 해결합니다. 단순한 나머지 해싱(server = hash(key) % n)에서는 n이 바뀌면 거의 모든 키가 다시 매핑되어 캐시 stampede가 발생합니다. 일관성 해싱은 키와 서버를 모두 링에 매핑하며, 각 키는 시계 방향으로 가장 가까운 서버에서 제공됩니다. 서버를 하나 추가해도 새 서버와 그 이전 서버 사이의 키만 다시 매핑되므로 전체 키의 약 1/n만 영향을 받습니다.

가상 노드(vnode)는 부하 분산을 개선합니다. 각 물리적 서버에 링 위의 여러 위치를 할당하므로 서버 수가 적어도 키가 더 고르게 분산됩니다.

import hashlib
import bisect

class ConsistentHashRing:
    def __init__(self, replicas=100):
        self.replicas = replicas      # virtual nodes per server
        self.ring = {}
        self.sorted_keys = []

    def add_server(self, server):
        for i in range(self.replicas):
            key = int(hashlib.md5(f'{server}:{i}'.encode()).hexdigest(), 16)
            self.ring[key] = server
            bisect.insort(self.sorted_keys, key)

    def remove_server(self, server):
        for i in range(self.replicas):
            key = int(hashlib.md5(f'{server}:{i}'.encode()).hexdigest(), 16)
            del self.ring[key]
            self.sorted_keys.remove(key)

    def get_server(self, item):
        key = int(hashlib.md5(item.encode()).hexdigest(), 16)
        idx = bisect.bisect(self.sorted_keys, key) % len(self.sorted_keys)
        return self.ring[self.sorted_keys[idx]]

ring = ConsistentHashRing()
for s in ['server-1', 'server-2', 'server-3']:
    ring.add_server(s)
for item in ['user:1', 'user:2', 'product:abc', 'session:xyz']:
    print(f'{item} => {ring.get_server(item)}')

캐시 stampede와 해결책

캐시 stampede(또는 요청 폭주)는 인기 있는 캐시 항목이 만료될 때 많은 동시 요청이 동시에 캐시 미스를 일으켜 동일한 쿼리로 데이터베이스에 쇄도하는 현상입니다. 해결책은 다음과 같습니다.

  • 뮤텍스/잠금: 하나의 요청만 값을 계산하고 나머지 요청은 기다립니다
  • 확률적 조기 만료: TTL보다 약간 이른 시점에 요청이 random하게 캐시를 새로 고칠지 결정하여 동시에 만료되는 것을 방지합니다
  • 오래된 데이터 제공 후 재검증: 오래된 콘텐츠를 즉시 제공하는 동안 비동기적으로 캐시를 새로 고칩니다
  • 백그라운드 새로 고침: 별도의 프로세스가 인기 키가 expire하기 전에 새로 고칩니다
import time, threading, random

# Probabilistic early expiry (XFetch algorithm)
class ProbabilisticCache:
    def __init__(self):
        self._cache = {}

    def get(self, key, ttl, recompute_fn, beta=1.0):
        if key in self._cache:
            value, expiry, delta = self._cache[key]
            # XFetch: decide to refresh early with probability proportional to delta/TTL
            remaining = expiry - time.time()
            if remaining > 0:
                early_refresh_score = delta * beta * (-1) * (remaining / ttl)
                if random.random() > (1 - early_refresh_score):  # simplified
                    pass  # could trigger async refresh here
                return value
        # Cache miss or expired
        start = time.time()
        value = recompute_fn()
        delta = time.time() - start          # computation time
        expiry = time.time() + ttl
        self._cache[key] = (value, expiry, delta)
        return value

print('XFetch: refresh probabilistically before expiry based on computation cost')
print('High-cost computations => refresh earlier to avoid stampede')
print('Low-cost computations => refresh closer to TTL')

CDN 캐시 무효화

캐시 무효화는 악명높게 어렵습니다. ‘컴퓨터 과학에는 어려운 문제가 두 가지뿐이다. 캐시 무효화와 이름 짓기다.’ 원본의 콘텐츠가 변경되면 CDN 엣지 노드는 새 버전을 제공해야 합니다. 전략은 다음과 같습니다.

  • TTL 기반 만료: 콘텐츠가 자연스럽게 만료되도록 둡니다(간단하지만 오래된 상태로 남는 시간이 생김)
  • URL 버전 관리: URL에 콘텐츠 해시를 포함합니다(예: main.a3f2b.js). 새 콘텐츠에는 새 URL이 사용되므로 무효화가 필요하지 않습니다
  • CDN API 무효화: 배포 후 API call로 URL을 명시적으로 무효화합니다(빠르지만 CDN API 연동이 필요함)
# Cache invalidation strategies for CDN/browser
strategies = [
    {
        'name': 'Long TTL + URL versioning (best for static assets)',
        'example': '<script src="/app.a3f2b1c.js"></script>',
        'ttl': 'Cache-Control: max-age=31536000 (1 year)',
        'how': 'Content hash in filename; new deploy = new URL; old URL cached forever (OK)',
    },
    {
        'name': 'Short TTL (for frequently changing content)',
        'example': '/api/v1/config',
        'ttl': 'Cache-Control: max-age=60 (1 minute)',
        'how': 'Simple; content is at most 60s stale; no invalidation needed',
    },
    {
        'name': 'CDN API purge (for news / social media)',
        'example': '/news/breaking-story.html',
        'ttl': 'Cache-Control: s-maxage=3600',
        'how': 'On publish, call CDN.purge(url); edge serves new version immediately',
    },
]
for s in strategies:
    print(f'{s["name"]}:')
    print(f'  Example: {s["example"]}')
    print(f'  TTL: {s["ttl"]}')
    print(f'  Strategy: {s["how"]}\n')

아키텍처: 전체 구성

완전히 확장된 웹 애플리케이션 계층은 세 가지 기법을 함께 사용합니다. 부하 분산은 트래픽을 분산하고, CDN은 정적 요청과 캐시 가능한 API 요청을 흡수하며, 레디스는 동적 데이터를 캐시합니다. 데이터베이스에는 캐시 미스만 도달하며, 일반적으로 전체 요청의 5~20%입니다.

읽기 중심 API의 일반적인 요청 흐름은 다음과 같습니다. 사용자 → DNS → CDN 엣지(캐시 적중: 즉시 제공) → CDN 미스 → 로드 밸런서 → 애플리케이션 서버 풀 → 레디스 캐시(적중: 1ms 응답) → 레디스 미스 → 데이터베이스(10~50ms) → 레디스에 응답 캐시 및 선택적 CDN 캐시 → 사용자. 각 계층이 데이터베이스 부하를 크게 줄입니다.

# Request flow with cache hit rates
request_flow = [
    ('Browser Cache',       '10%',  '0ms',   'Browser caches GET responses per Cache-Control'),
    ('CDN Edge Cache',      '60%',  '5ms',   'CloudFront/Fastly caches cacheable API responses'),
    ('Load Balancer',        None,  '1ms',   'Routes to healthy app server replica'),
    ('App Server',           None,  '2ms',   'Business logic, auth check'),
    ('Redis Cache',         '25%',  '1ms',   'Caches computed data, hot DB rows'),
    ('Database Read Replica','5%',  '10ms',  'Cache miss: query read replica'),
    ('Database Primary',    '0.1%', '15ms',  'Cache+replica miss: query primary (rare for reads)'),
]
print(f'{'Layer':30s} {'Hit Rate':10s} {'Latency':10s} {'Notes'}')
print('-'*80)
for layer, hit_rate, latency, note in request_flow:
    hr = hit_rate if hit_rate else '-'
    print(f'{layer:30s} {hr:10s} {latency:10s} {note}')
print('\nResult: DB sees ~5% of requests; Redis sees ~25%; CDN absorbs 60%; browser 10%')

면접 팁: 캐싱과 부하 분산

시스템 설계 면접에서 캐싱을 논의할 때는 항상 다음 항목을 다루어야 합니다. 무엇을 캐시할지(자주 사용되는 데이터, 계산 비용이 큰 결과), 어디에 캐시할지(브라우저, CDN, 애플리케이션, 데이터베이스 쿼리 캐시), 언제 무효화할지(쓰기 시, TTL 만료 시 또는 백그라운드 새로 고침을 통해), 그리고 어떤 일관성 보장이 허용되는지입니다. 캐시는 일관성 창을 만들므로 이를 명확히 설명해야 합니다.

부하 분산에서는 알고리즘 선택, 상태 확인, 고정 세션의 필요 여부, 상태 비저장 애플리케이션 서버를 수평 확장할 수 있는지를 언급해야 합니다. 애플리케이션에 상태(WebSocket 연결, 세션)가 있다면 복제본 간에 해당 상태를 어떻게 관리할지도 설명해야 합니다.

# Caching design questions checklist
cache_checklist = [
    'What data to cache? (read-heavy, expensive to compute, rarely updated)',
    'Cache layer: client-side / CDN / app-level / DB query cache?',
    'Cache invalidation strategy: TTL / event-driven / write-through?',
    'Eviction policy: LRU / LFU?',
    'Cache key design: ensure uniqueness, avoid hotspots',
    'Consistency window: acceptable staleness in seconds?',
    'Cache stampede prevention: mutex / stale-while-revalidate?',
    'Cache capacity: how much RAM needed for hot set?',
]
lb_checklist = [
    'Layer 4 vs Layer 7: routing by IP or by URL/headers?',
    'Algorithm: round robin / least-connections / consistent hashing?',
    'Health checks: interval, failure threshold, recovery',
    'Session stickiness: needed? Use cookie-based affinity or external session store',
    'Auto-scaling: scale out when CPU > 70%; scale in when < 30%',
]
print('Cache checklist:')
for item in cache_checklist: print(f'  [ ] {item}')
print('\nLoad balancer checklist:')
for item in lb_checklist: print(f'  [ ] {item}')

빠른 확인

이 수업에서 배운 자료 구조 & 알고리즘 — 코딩 면접 준비 개념을 이해했는지 확인해 보십시오.

수업 요약

이 수업에서는 다음을 배웠습니다. 캐싱은 레디스와 CDN 같은 빠른 메모리 계층에 자주 사용되는 데이터를 저장하여 대부분의 읽기를 흡수하고 데이터베이스 부하를 줄입니다. 캐시 어사이드는 가장 일반적인 패턴으로, 미스가 발생하면 데이터베이스에서 불러오고, 적중하면 즉시 반환하며, 쓰기 시에는 캐시에서 삭제합니다. 또한 일관성 해싱은 캐시 키를 노드에 분산하므로 노드를 추가하거나 제거해도 전체를 다시 매핑하는 대신 약 1/n의 키만 다시 매핑합니다. 다음 수업에서는 요청률 제한기와 트위터 피드를 설계하여 시스템 설계의 모든 개념을 종합적인 문제에 적용합니다.

자주 묻는 질문

“캐싱, CDN과 부하 분산” 강의는 무료인가요?

네 — “캐싱, CDN과 부하 분산” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 DSA Interview Prep 강의 전체를 잠금 해제할 수 있습니다. DSA Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

“캐싱, CDN과 부하 분산”에서 뭘 배우나요?

Redis 캐시 계층을 추가하고 정적 자산을 CDN으로 전송하며, 라운드 로빈 및 일관된 해싱 부하 분산기로 복제본에 트래픽을 분배합니다. 브라우저에서 직접 실행하는 실습 코드로 DSA Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

DSA Interview Prep을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 DSA Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“캐싱, CDN과 부하 분산” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 DSA Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 DSA Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 시스템 설계 면접 프레임워크
  2. 확장 가능한 데이터 저장소: SQL과 NoSQL
  3. 캐싱, CDN과 부하 분산
  4. 속도 제한기 설계와 트위터 피드 설계
← DSA Interview Prep(으)로 돌아가기