0Pricing
Cloud & IT Cert Prep · 강의

캐싱 전략: 지연 로딩 및 쓰기 관통

캐시 적중 실패 시 캐시를 채우는 지연 로딩을 구현하고, 모든 데이터베이스 쓰기 작업과 캐시의 일관성을 유지하도록 쓰기 관통을 구현합니다.

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

캐싱 전략이 중요한 이유

애플리케이션과 데이터베이스 사이에 캐시를 삽입하려면 데이터를 언제 캐시에 저장하고, 언제 캐시에서 읽으며, 언제 제거할지를 결정하는 일련의 규칙인 캐싱 전략이 필요합니다. 잘못된 전략을 선택하면 오래된 데이터(캐시가 최신이 아닌 값을 반환), 콜드 캐시 누락(캐시가 비어 있어 모든 요청이 데이터베이스에 도달), 또는 캐시 쇄도(동일한 누락 키를 요청하는 여러 동시 요청이 모두 동시에 데이터베이스에 도달)가 발생합니다. 가장 기본적인 두 전략은 지연 로딩과 쓰기 관통 방식입니다.

지연 로딩(캐시 우회 방식)의 작동 원리

지연 로딩(캐시 우회 방식이라고도 함)은 가장 일반적인 캐싱 패턴입니다. 애플리케이션은 먼저 캐시를 확인합니다. 캐시 적중이면 데이터가 캐시에서 직접 반환되므로 빠르게 처리됩니다. 캐시 누락이면 애플리케이션이 데이터베이스에서 데이터를 가져오고, 그 결과를 TTL과 함께 캐시에 기록한 다음 호출자에게 반환합니다. 실제로 요청된 데이터만 캐시에 채워지므로 '지연' 로딩이라고 합니다. 동일한 키에 대한 다음 요청은 캐시에서 해당 데이터를 찾게 됩니다.

# Lazy loading pattern (Python with redis-py)
def get_user(user_id, redis_client, db):
    cache_key = f'user:{user_id}'

    # 1. Check cache
    cached = redis_client.get(cache_key)
    if cached:
        return json.loads(cached)   # Cache HIT

    # 2. Cache MISS: fetch from DB
    user = db.query('SELECT * FROM users WHERE id = %s', user_id)

    # 3. Populate cache with TTL of 300 seconds
    redis_client.setex(cache_key, 300, json.dumps(user))

    return user

지연 로딩의 장점

지연 로딩에는 세 가지 주요 장점이 있습니다. 요청된 데이터만 캐시됩니다. 아무도 읽지 않는 데이터로 캐시를 채우지 않으므로 메모리를 효율적으로 사용할 수 있습니다. 콜드 캐시가 애플리케이션을 중단시키지 않습니다. 캐시가 누락되면 애플리케이션이 데이터베이스로 대체 처리하므로 ElastiCache가 재시작되거나 노드에 장애가 발생해도 지연 시간이 높아질 뿐 애플리케이션은 계속 작동합니다. 캐시는 데이터베이스와 항상 최종적으로 일관됩니다. 업데이트를 놓친 경우에도 오래된 데이터가 TTL에 따라 만료되기 때문입니다.

지연 로딩의 단점

지연 로딩에는 세 가지 주요 단점이 있습니다. 캐시 누락은 비용이 큽니다. 적중 시에는 한 번의 작업이면 되지만, 누락 시에는 캐시 확인, 데이터베이스 읽기, 캐시 쓰기라는 세 작업이 필요하므로 콜드 요청의 지연 시간이 높아집니다. 오래된 데이터가 발생할 수 있습니다. 데이터베이스가 업데이트된 후에도 TTL이 만료되거나 키가 명시적으로 무효화될 때까지 캐시는 이전 값을 제공합니다(캐시 불일치 구간). 캐시 쇄도가 발생할 수 있습니다. 인기 키가 만료되면 여러 동시 요청이 모두 캐시 누락을 경험하고 동시에 데이터베이스에 도달하여 데이터베이스에 과부하를 줄 수 있습니다.

# Mitigating cache stampede with a probabilistic early expiration
# (Refresh the key before it expires to avoid simultaneous misses)
def get_with_stampede_protection(key, redis_client, db_fetch_fn, ttl=300):
    value = redis_client.get(key)
    ttl_remaining = redis_client.ttl(key)

    # Probabilistically refresh before expiry
    if value is None or (ttl_remaining < 30 and random.random() < 0.1):
        value = db_fetch_fn()
        redis_client.setex(key, ttl, json.dumps(value))
    return json.loads(value)

쓰기 관통 방식의 작동 원리

쓰기 관통 전략에서는 데이터베이스에 쓰는 모든 데이터를 동시에 캐시에도 기록합니다. 애플리케이션은 동일한 작업의 일부로 캐시와 데이터베이스 양쪽에 데이터를 쓰거나, 데이터베이스가 캐시 업데이트를 트리거합니다. 따라서 캐시는 항상 데이터베이스와 동기화되며 오래된 데이터가 발생하는 구간이 없습니다. 쓰기 관통 방식은 캐시의 데이터가 항상 최신 상태임을 보장하므로, 최근에 기록된 데이터에 대한 후속 읽기는 항상 캐시 적중이 됩니다.

# Write-through pattern (Python)
def update_user(user_id, user_data, redis_client, db):
    # 1. Write to database FIRST
    db.execute('UPDATE users SET name=%s WHERE id=%s',
                (user_data['name'], user_id))

    # 2. Update cache immediately (write-through)
    cache_key = f'user:{user_id}'
    redis_client.setex(cache_key, 3600, json.dumps(user_data))

    return user_data

# Every read is now a cache hit for recently updated data

쓰기 관통 방식의 장점

쓰기 관통 방식의 장점은 다음과 같습니다. 캐시 데이터가 항상 최신 상태입니다. 모든 쓰기 작업에서 캐시가 업데이트되므로 오래된 데이터가 없습니다. 읽기가 항상 빠릅니다. 자주 기록되는 인기 데이터는 항상 캐시에 있습니다. 읽기 시 캐시 쇄도가 발생하지 않습니다. 데이터를 읽기 전에 미리 캐시에 채워 두므로 최근에 기록된 데이터에서는 콜드 누락이 발생하지 않습니다. 이 전략은 제품 카탈로그, 가격 시스템 또는 사용자 프로필 캐시처럼 업데이트가 잦고 읽기 중심인 작업 부하에 적합합니다.

쓰기 관통 방식의 단점

쓰기 관통 방식의 단점은 다음과 같습니다. 쓰기 비용이 발생합니다. 모든 쓰기 작업에 데이터베이스와 캐시라는 두 작업이 필요하므로 쓰기의 지연 시간이 늘어납니다. 캐시 오염이 발생합니다. 다시 읽지 않을 데이터도 캐시에 저장되므로 캐시 메모리가 낭비됩니다. 캐시를 재시작하면 콜드 캐시가 됩니다. 캐시 클러스터가 재시작되면 미리 기록해 둔 데이터가 모두 사라지고, 쓰기 작업이나 캐시 준비 과정을 통해 캐시를 다시 채워야 합니다. 거의 읽히지 않는 캐시 데이터가 제한 없이 증가하지 않도록 쓰기 관통 방식과 TTL을 함께 사용하세요.

지연 로딩과 쓰기 관통 방식 결합

실제 운영 환경에서는 두 전략을 결합하는 경우가 많습니다. 자주 업데이트되고 자주 읽히는 데이터(예: 사용자 세션 또는 현재 가격)에는 쓰기 관통 방식을 사용하고, 업데이트는 드물지만 자주 읽히는 데이터(예: 제품 설명 또는 기사 콘텐츠)에는 지연 로딩을 사용합니다. 두 방식 모두에 적절한 TTL을 설정하세요. 쓰기 관통 키는 데이터가 항상 최신이므로 긴 TTL을 사용하고, 지연 로딩 키는 오래된 데이터가 유지되는 구간을 제한하도록 더 짧은 TTL을 사용합니다. 이 하이브리드 접근 방식은 오래된 데이터의 발생을 최소화하면서 캐시 적중률을 높입니다.

# Hybrid: write-through for sessions, lazy loading for product data

# Write-through for session data (always fresh, critical)
def save_session(session_id, data, redis_client, db):
    db.upsert('sessions', session_id, data)
    redis_client.setex(f'session:{session_id}', 3600, json.dumps(data))

# Lazy loading for product catalog (infrequent updates OK)
def get_product(product_id, redis_client, db):
    cached = redis_client.get(f'product:{product_id}')
    if cached:
        return json.loads(cached)
    product = db.query('SELECT * FROM products WHERE id=%s', product_id)
    redis_client.setex(f'product:{product_id}', 86400, json.dumps(product))
    return product

TTL 설계 원칙

캐시된 데이터의 Time-To-Live (TTL)은 오래된 데이터가 유지되는 최대 시간과 캐시의 메모리 사용량을 제어합니다. TTL은 다음을 기준으로 설계하세요. 데이터 업데이트 빈도(세션 데이터는 자주 변경되므로 짧은 TTL, 정적 콘텐츠는 드물게 변경되므로 긴 TTL), 오래된 데이터를 허용할 수 있는 정도(금융 가격은 매우 짧게, 블로그 게시물의 글은 몇 시간 또는 며칠), 캐시 메모리 용량(메모리가 부족하면 오래된 데이터를 더 빨리 제거하도록 짧은 TTL). 항상 TTL을 설정하세요. 만료 없이 데이터를 캐시하면 캐시가 결국 오래된 데이터로 가득 차게 됩니다.

# TTL examples by data type

# API rate limit counter: 60 seconds
redis_client.setex(f'ratelimit:{ip}', 60, count)

# User session: 30 minutes
redis_client.setex(f'session:{id}', 1800, json.dumps(session))

# Product catalog: 24 hours
redis_client.setex(f'product:{id}', 86400, json.dumps(product))

# Stock price: 10 seconds
redis_client.setex(f'price:{symbol}', 10, price)

# Static site content: 7 days
redis_client.setex(f'page:{slug}', 604800, html_content)

업데이트 시 캐시 무효화

TTL 만료에만 의존하지 않고, 기반 데이터가 변경될 때 캐시 키를 명시적으로 무효화(삭제)할 수 있습니다. 이렇게 하면 오래된 데이터가 유지되는 구간을 완전히 제거할 수 있습니다. 일반적인 패턴은 다음과 같습니다. 쓰기 후 삭제(데이터베이스가 업데이트될 때마다 캐시 키를 삭제하고, 다음 읽기에서 지연 로딩을 통해 다시 채움), 이벤트 기반 무효화(DynamoDB Streams 또는 RDS 변경 캡처가 Lambda를 트리거하여 영향을 받은 키를 삭제). 캐시 무효화는 분산 시스템에서 가장 어려운 문제 중 하나이므로, 무효화 논리가 단순할수록 캐시의 신뢰성이 높아집니다.

# Delete-on-write invalidation pattern
def update_product(product_id, new_data, redis_client, db):
    # Update the database
    db.execute('UPDATE products SET ... WHERE id=%s', (product_id,))

    # Invalidate the cache key
    redis_client.delete(f'product:{product_id}')

    # Also invalidate any list/search caches that may include this product
    redis_client.delete('products:list:page:1')
    redis_client.delete(f'products:category:{new_data["category_id"]}')

    # Next read will trigger lazy loading with fresh data

쓰기 지연(쓰기 후 저장) 캐싱

덜 일반적이지만 강력한 패턴으로 쓰기 지연(쓰기 후 저장)이 있습니다. 애플리케이션은 캐시에만 쓰고, 캐시가 백그라운드에서 데이터를 비동기적으로 데이터베이스에 저장합니다. 이 방식은 매우 빠른 쓰기(메모리에만 기록)를 제공하지만, 저장하기 전에 캐시에 장애가 발생하면 데이터가 손실될 수 있습니다. 쓰기 지연은 데이터를 재구성할 수 있거나 소량의 손실이 허용되는 고빈도·대용량 쓰기에 적합합니다. 예를 들면 적중 횟수 카운터, 분석 이벤트 또는 게임 점수 업데이트가 있습니다. ElastiCache는 쓰기 지연을 기본적으로 지원하지 않으므로 애플리케이션 계층에서 구현해야 합니다.

# Write-behind pattern: write to cache, flush to DB asynchronously
# Application writes:
# redis_client.incr('post:42:views')      # fast, in-memory only

# Background job (runs every 60 seconds):
def flush_view_counts(redis_client, db):
    for key in redis_client.scan_iter('post:*:views'):
        count = redis_client.getdel(key)  # atomic get-and-delete
        post_id = key.split(':')[1]
        db.execute('UPDATE posts SET views = views + %s WHERE id = %s',
                   (int(count), post_id))

빠른 확인

이 단원에서 다룬 AWS Solutions Architect (SAA-C03) 개념을 이해했는지 확인해 보세요.

단원 요약

이 단원에서는 다음을 배웠습니다. 지연 로딩은 읽기 누락 시 캐시를 채우고 메모리를 효율적으로 사용하지만 TTL이 만료될 때까지 오래된 데이터를 제공할 수 있습니다. 쓰기 관통은 모든 쓰기에서 캐시를 업데이트하므로 오래된 데이터가 발생하지 않지만 읽히지 않는 데이터에도 메모리를 사용합니다. 명시적 무효화는 데이터베이스가 업데이트될 때 캐시 키를 삭제하여 오래된 데이터가 유지되는 구간을 제거합니다. 다음으로는 ElastiCache를 사용한 세션 저장 및 순위표 패턴을 살펴보겠습니다.

자주 묻는 질문

“캐싱 전략: 지연 로딩 및 쓰기 관통” 강의는 무료인가요?

네 — “캐싱 전략: 지연 로딩 및 쓰기 관통” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Cloud & IT Cert Prep 강의 전체를 잠금 해제할 수 있습니다. Cloud & IT Cert Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

“캐싱 전략: 지연 로딩 및 쓰기 관통”에서 뭘 배우나요?

캐시 적중 실패 시 캐시를 채우는 지연 로딩을 구현하고, 모든 데이터베이스 쓰기 작업과 캐시의 일관성을 유지하도록 쓰기 관통을 구현합니다. 브라우저에서 직접 실행하는 실습 코드로 Cloud & IT Cert Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Cloud & IT Cert Prep을(를) 시작하는 데 경험이 필요한가요?

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

“캐싱 전략: 지연 로딩 및 쓰기 관통” 강의는 얼마나 걸리나요?

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

이 Cloud & IT Cert Prep 강의에서 코드를 작성하고 실행할 수 있나요?

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

이 강의의 모든 강의

  1. Redis와 Memcached 비교: 적합한 엔진 선택
  2. ElastiCache Redis 복제 그룹 및 클러스터 모드
  3. 캐싱 전략: 지연 로딩 및 쓰기 관통
  4. 세션 스토리지 및 순위표 패턴
← Cloud & IT Cert Prep(으)로 돌아가기