본문 바로가기
C.W.K.
Stream
Lesson 05 of 05 · published

낡은 값은 보이는 채로 둔다

~10 min · staleness, caching, calm-ui, freshness

Level 0Open Gate
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"낡은 데이터는 숨길 문제가 아니라 라벨 달 사실이야. 거짓말은 신선한 척하는 쪽이지."

데이터가 낡으면 어떻게 할까

데이터는 나이를 먹어. 아침에 받은 quote 는 오후면 낡고, 지난 금요일 값은 연휴가 끝나면 낡아 있지. high-trigger 앱은 이 낡음을 비상사태로 다뤄. 깜빡이고, 허둥지둥 다시 가져오고, 낡은 숫자를 숨겨 버려. Keep 는 차분한 쪽을 골랐어. 낡은 데이터는 사라지는 대신 라벨을 달고 남는다. 옛 값이 제 나이를 정직하게 밝힌 채 화면에 그대로 있어. "이 날짜 기준, 이만큼 지남" 하고. 그래서 숫자와, 그 숫자를 얼마나 믿어도 되는지를 한눈에 같이 봐. 사라지는 것도 없고, 소리치는 것도 없고, 실제보다 신선한 척하는 것도 없어.

숨기는 쪽이 더 나쁜 이유

같은 낡은 quote 를 두고 앱 두 개를 비교해 보자. 앱 A 는 숨겨. 신선한 값이 올 때까지 spinner 를 돌리지. 근데 provider 가 다운이면? 포트폴리오가 있어야 할 자리의 빈칸을 불안하게 바라보는 수밖에 없어. 앱 B 는 마지막으로 알려진 값을 '4시간 경과' 태그와 함께 보여줘. 정보도 있고 단서도 정확하니 마음이 차분하지. 낡은 값을 숨기는 건 유저를 지켜 주는 게 아니야. 라벨 달린 사실을 불안한 공백으로 바꿔치기할 뿐이지. 정직한 라벨이 숨겨진 진실보다 더 유용하고, 더 차분해.

불확실성엔 라벨을 달아. 숨기지 말고. 나이가 표시된 낡은 값을 본 유저는 제대로 판단할 수 있어. 빈칸이나 spinner 나 신선한 척 위장한 낡은 값을 본 유저는 못 하고. 신선도 메타데이터는 잡동사니가 아니야. 책임 있게 기대도 되는 숫자와 언젠가 배신할 숫자를 가르는 차이야.

정책은 안 바꾸는 캐싱

Keep 는 source 를 들볶지 않으려고 provider 데이터를 캐시해. 캐시 유효 시간은 데이터 종류마다 실제로 얼마나 빨리 변하는지에 맞춰 정했어.

  • 일일 bar → 하루 (장중엔 안 바뀌니까)
  • sparkline 구간 → 한 시간
  • fundamentals → 여섯 시간 (원래 느리게 움직여)
  • 파생 simulation calibration → 하루

캐싱은 속도만 올리고 정책은 건드리지 않아. 캐시에서 나온 값도 똑같이 정직한 provenance 와 나이를 달고 나와. 그리고 일부러 만든 예외가 하나 있어. simulation run 의 seed 가격은 언제나 run 시점에 새로 resolve 해. 캐시에서 꺼내는 일이 절대 없어. 시나리오의 출발점은 정확하고 재현 가능해야 하거든. 이유는 다음 트랙에서 자세히 봐.

캐시 유효 시간은 변화 속도에 대한 믿음의 표현이야. 일일 bar 에 하루, sparkline 에 한 시간을 준 건 아무렇게나 정한 게 아니야. 각각 '이 데이터는 이보다 빨리 의미 있게 변하지 않는다' 는 주장인 거지. TTL 을 고른다는 건 사실 변동성에 대한 주장을 고르는 거고, 틀리면 낡아 보이는 데이터를 내놓거나 쓸데없는 부하를 만들어. TTL 은 데이터의 진짜 박자에 맞춰.

Code

캐시 TTL 은 데이터마다의 진짜 박자에 맞춘다·python
CACHE_TTL = {
    "daily_bars":         24 * 3600,   # bar 는 장중에 안 움직여
    "sparkline":           1 * 3600,   # 짧은 구간, 한 시간마다 갱신
    "fundamentals":        6 * 3600,   # 느리게 움직여
    "sim_calibration":    24 * 3600,   # 파생값, 하루는 안정적
}

def get(kind, key):
    hit = cache.get(kind, key)
    if hit and hit.age < CACHE_TTL[kind]:
        return hit.with_age_label()      # 캐시된 값에도 나이 라벨은 붙는다
    return fetch_and_cache(kind, key)

# simulation 의 seed 가격은 의도된 예외야.
# 언제나 run 시점에 새로 resolve 하고 캐시에선 안 꺼내.
# 재현성은 정확하고 다시 resolve 가능한 출발점을 요구하니까.

External links

Exercise

네 UI 가 외부 source 에서 받아 보여주는 값을 하나 골라 봐. '이게 실제로 얼마나 빨리 변하지?' 를 물어서 정직한 캐시 TTL 을 정해. 그다음 낡은 상태의 화면을 설계해 봐. 숨기거나 spinner 를 돌리는 대신, 나이를 정직하게 밝히면서 유저를 차분하게 두는 라벨은 어떤 모양일까? 낡은 값을 숨기고 source 가 죽으면 공백을 보여주는 앱 A 의 불안과, 마지막 값에 나이를 달아 보여주는 앱 B 의 차분함을 나란히 놓고 대조해 봐.
Hint
TTL 질문의 본질은 '의미 있는 변화가 가장 빨리 일어나는 주기가 얼마냐' 야. 일일 종가는 하루를 캐시해도 되고 live tick 은 초 단위지. 그리고 낡은 상태에 대해선 이거 하나만 기억해. '4시간 지난 값' 이라고 보여주는 도구가 빈칸을 보여주는 도구보다 더 신뢰받아. 낡음을 투명하게 밝히면 신뢰가 쌓이고, 숨기다 들키는 순간 무너져.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.