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

Primary 하나, 확인받는 Fallback

~11 min · provider-policy, fallback-discipline, confirmation, unattended

Level 0Open Gate
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"네가 눈치채지 못하는 사이에 일어나는 fallback 은 안전망이 아니야. 동의한 적 없는 출처 변경이지."

provider 서열은 명시적이야

equity 와 index 쪽에서 Keep 는 primary provider 하나(Polygon 기반의 Massive)와 이름이 정해진 fallback 둘(Yahoo Finance, Stooq)을 둬. 여기서 힘을 쓰는 단어는 '이름이 정해진' 이야. 되는 놈 아무나 쓰는 흐릿한 체인이 아니라, 지정된 primary 가 있고 정확히 누구인지 아는 대체 후보가 있는 거지. 그리고 하나에서 다른 쪽으로 갈아타는 건 절대 자동이 아니야. equity fallback 은 요청 한 건마다 명시적 확인을 요구해. 사람이 "응, 이번 요청은 대체로 가" 라고 말해 줘야 넘어가.

조용한 fallback 체인이 함정인 이유

편하게 짜자면 체인이지. A 를 시도하고, 실패하면 B, 또 실패하면 C, 뭐라도 받으면 반환. 튼튼해 보여. 실제로는 내 데이터의 출처를 놓쳐 버리는 조용한 방법이야. A 가 비실대는 날 앱이 말없이 B 값을 내놓으면, 숫자의 출처가 슬그머니 바뀌어. ticker 표기법이 다르고, 종가 산정 시각이 다르고, 반올림이 달라. 근데 바뀐 줄을 몰라. 그러다 월요일의 Massive 종가를 화요일의 Stooq 종가에 대고 비교하면서 계산이 왜 이상하지, 하고 갸웃하게 되는 거야. 조용한 fallback 은 눈에 보이는 실패를 눈에 안 보이는 불일치와 맞바꿔. 밑지는 장사야.

숫자의 출처를 바꾸는 건 retry 가 아니라 결정이야. 같은 source 를 다시 시도하는 건 기계적인 일이고 자동화해도 안전해. 다른 source 로 갈아타는 건 숫자의 의미를 바꾸는 일이라, 사람의 명시적인 승낙을 받아야 해. retry 는 자동으로, 대체는 확인받고.

무인 규칙, 예약된 refresh 는 fallback 을 아예 안 해

이 정책의 가장 날카로운 모서리가 여기야. Keep 는 아무도 안 보는 시간에 일일 refresh 를 돌리는데, 그 job 은 fallback 이 아예 금지야. 왜냐고? fallback 의 요체가 사람이 대체를 확인해 주는 건데, 새벽 6시 31분 무인 시간엔 확인해 줄 사람이 없잖아. 그러니 durable 한 일일 스냅샷에 fallback 출처를 몰래 적어 넣는 대신, 예약 job 은 primary 로 성공하거나 정직하게 실패해. 실패하면 경고로 떠오르고. 확인을 조건으로 거는 fallback 과 무인 운영은 애초에 공존할 수 없으니까, 무인 경로는 fallback 을 통째로 사양하는 거야.

사람 있을 땐 안전한데 무인일 땐 위험한 fallback 을 조심해. 확인 다이얼로그는 사람이 앞에 있을 땐 훌륭한 관문이야. 근데 같은 로직을 cron job 에 복사하는 순간 관문이 소리 없이 사라져. 답할 사람이 없으니 영영 멈춰 있거나, 물어봤어야 할 일을 그냥 저질러 버리거든. 사람 확인에 기대는 동작은 무인 경로에서 명시적으로 꺼 둬야 해. '설마 아무도 안 누르겠지' 에 맡기지 말고.
어떤 종목은 정책으로 정한 provider 를 써. WTI 나 Gold 같은 몇몇 값은 비상용 fallback 이 아니라 정책상 특정 provider 를 써. 그건 그 종목들을 위해 일부러 고른 primary 지, 조용한 swap 이 아니야. 규칙은 'provider X 금지' 가 아니라 이거야. 값의 출처는, 그 변경이 이름 붙고 의도된 결정이 아닌 한, 절대 바뀌지 않는다.

Code

retry 는 자동, 대체는 확인 (예시)·python
def quote_interactive(ticker, *, confirmed_source=None):
    try:
        return fetch(PRIMARY, ticker)          # PRIMARY 재시도는 괜찮아
    except ProviderError as e:
        if confirmed_source is None:
            # 조용히 갈아타지 마. 사람한테 먼저 물어봐.
            raise FallbackConfirmationRequired(candidate=next_fallback(ticker))
        return fetch(confirmed_source, ticker)  # 확인받은 대체만

def quote_scheduled(ticker):
    # 무인: 확인할 사람이 없어. 그래서 fallback 도 없어.
    # primary 로 성공하거나 정직하게 실패하거나.
    return fetch(PRIMARY, ticker)   # 실패하면 raise. 경고로 떠올라

External links

Exercise

primary source 하나와 fallback 하나가 있는 fetch 를 설계해 봐. 진입점은 둘로 나눠. 사람이 보고 있는 인터랙티브 경로와, 아무도 없는 예약 경로. 인터랙티브 쪽은 fallback 전에 확인을 요구하게 만들고, 예약 쪽은 fallback 없이 실패 시 어떻게 될지 정해. 마지막으로, 인터랙티브 로직을 예약 job 에 그대로 복사하면 왜 버그가 되는지 설명해 봐.
Hint
함정은 두 경로가 fetch() 함수 하나를 같이 쓰는 데 있어. 인터랙티브에선 확인 프롬프트가 지켜 주지만, cron 에는 프롬프트에 답할 사람이 없어. 그래서 같은 코드가 영원히 멈춰 있거나, 물어봤어야 할 일을 조용히 저지르거나 둘 중 하나가 돼. 무인 경로엔 fallback 없는 자기만의 규칙이 필요해.

Progress

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

댓글 0

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

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