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

멍청한 router 가 똑똑한 router 야

~12 min · router, data-driven, dispatch, registry

Level 0툴 빌려 쓰는 사람
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"제일 똑똑한 router 는 아무 의견이 없어. 어느 adapter 가 이 모델을 맡는지 읽고 비켜 주기만 해."

router 가 하는 일, 최소한으로 적으면

모델을 지목한 생성 요청이 들어오면 뭔가는 정해야 해. 이게 LocalAdapter 로 가나, APIAdapter 로 가나? 이걸 순진하게 짜면 분기로 가득한 함수가 나와. 모델 이름이 이걸로 시작하면, 저 폴더에 있으면, 이 패턴에 맞으면. 그리고 새 모델이나 새 vendor 가 올 때마다 분기가 하나씩 늘어. 그 함수가 버그가 사는 자리, 아무도 건드리기 싫어하는 자리가 돼.

판단을 데이터 쪽으로 옮겨

더 나은 설계는 그 판단을 코드에서 registry 로 옮기는 거야. 모델마다 registry 행이 하나 있고, 그 행이 이미 자기 주인 adapter 를 알고 있어. 그러면 router 가 시시해져. 모델 행 찾고, adapter 읽고, 넘기고. 분기도 없고 패턴 맞추기도 없고 늘어나는 if/else 도 없어. registry 가 답을 들고 있으니까 router 는 의견을 가질 필요가 없는 거야.

판단을 코드에서 데이터로 밀어 내. 경우가 늘 때마다 분기가 자라는 로직은 코드가 아니라 데이터에 있어야 해. 모델 하나당 registry 행 하나면 router 는 한 줄도 안 늘고 모델 천 개까지 감당해. 경우마다 분기하는 코드는 경우마다 썩는 코드야.

'멍청함' 이 칭찬인 이유

멍청한 router 는 다시는 손 안 대도 되는 router 야. 새 local checkpoint 를 넣는다고? 훑는 과정에서 'local' 로 표시된 registry 행이 생겨. router 는 이미 뭘 할지 알아. 새 API vendor 를 붙인다고? 그 모델들이 APIAdapter 로 표시된 행을 받아. router 는 이미 알고. router 는 한 번 제대로 쓰이고 그다음엔 바뀔 일이 없어. 모든 변화가 router 가 읽는 데이터 쪽에서 벌어지니까. 여기서 멍청함은 안정성이라는 뜻이야.

영영 안 바뀌는 게 네가 제대로 만든 거야. 계속 고치게 되는 컴포넌트는 자기가 받으면 안 되는 변화를 받고 있는 컴포넌트야. 모델은 계속 늘어나는데 router 가 바뀌기를 멈췄다면, 변화가 제 집인 registry 를 찾아갔고 router 는 제 크기인 '작음' 을 찾은 거야.

registry 가 유일한 기준점이야

이 설계에는 두 번째 보상이 있어. 모델과 adapter 의 짝을 아는 자리가 정확히 하나, registry 뿐이라는 거야. 라우팅 로직이 서로 어긋날 수 있는 두 자리에 나뉘어 있지 않아. 이 모델이 왜 저리로 갔는지 알고 싶어? registry 행을 읽으면 돼. 라우팅을 바꾸고 싶어? 행을 고치면 되고. router 랑 config 파일이랑 예외 처리 몇 개에 흩어진 판단 로직 대신, 찾아볼 수도 있고 고칠 수도 있는 기준점 하나.

같은 걸 정하는 자리가 둘이면 언젠가 어긋나. 라우팅 로직이 router 랑 config 양쪽에(또는 함수 둘에, 또는 코드랑 주석에) 있게 되는 순간 둘은 벌어져. 그리고 버그는 '여기선 되는데 저기선 안 돼' 모양으로 나타나. 데이터 쪽 기준점 하나는 자기 자신과 어긋날 수가 없어. 판단을 한군데로 모으든가, 벌어지는 값을 치르든가야.

피파의 고백

난 똑똑한 분배 로직 짜는 걸 좋아해. 똑똑해 보일 수 있는 자리 같거든. 아빠 설계는 그 장난감을 조용히 뺏어 가고 더 나은 걸 놔줬어. 내가 원했던 똑똑한 router 는 모델이 올 때마다 손을 봐야 하고, 손대는 자리가 곧 내가 버그를 심는 자리야. 내가 버티던 멍청한 router 는 그냥 계속 돌아가고. 그때 배웠어. '어디서 똑똑해질 수 있지' 는 자주 틀린 질문이야. 진짜 똑똑한 수는 router 를 절대 안 깨질 만큼 멍청하게 만드는 거였어.

Code

코드의 분기냐 데이터의 칸이냐·python
# 순진한 쪽 (똑똑한 router, 경우마다 썩음): 분기가 영영 늘어나.
def route_naive(model_name):
    if model_name.startswith("sdxl"):        return local_adapter
    if model_name.startswith("flux"):        return local_adapter
    if model_name in API_VENDOR_MODELS:      return api_adapter
    if "midjourney" in model_name:           return api_adapter
    # ...새 모델이나 vendor 마다 여기 한 줄씩. 끝없이.
    raise ValueError(f"{model_name} 은 어디로 보낼지 모르겠음")

# 멍청한 쪽 (데이터 기반, 안 바뀜): registry 가 이미 알아.
def route_dumb(model_id, registry):
    row = registry.get(model_id)             # 행이 자기 주인을 알아
    return ADAPTERS[row.adapter]             # 'local' 아니면 'api'
# 모델 1000개 추가해도 route_dumb 은 0줄 늘어. 훑는 쪽이 행에 표시해 주니까.

External links

Exercise

네 코드에서 새 경우가 생길 때마다 분기가 자라는 함수를 찾아봐. 분배기든, 타입 switch 든, 포맷 처리기든. 그걸 조회 방식으로 다시 설계해 봐. 함수가 테이블 읽기로 줄어들려면 각 경우가 뭘 들고 있어야 해? 테이블이랑, 분기를 대신할 세 줄짜리 함수를 그려 봐.
Hint
분기는 대개 두 가지를 같이 담고 있어. 이 경우를 어떻게 알아보는지, 그리고 알아본 다음에 뭘 하는지. 알아보는 쪽은 등록할 때 붙이는 표시로 옮기고, 하는 쪽은 그 표시를 키로 쓰는 테이블로 옮겨.

Progress

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

댓글 0

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

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