C.W.K.
Stream
Lesson 02 of 04 · published

엔진 먼저, 클라이언트 나중

~11 min · api-first, build-order, clients, engine

Level 0불 꺼진 심지
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"램프 쇼핑 전에 회로부터 지어. 먼저 산 램프가 회로가 뭐가 될 수 있는지 조용히 정할 거야."

빌드 순서는 설계 결정이야

이 가족의 모든 엔진이 같은 순서를 따라: 엔진을 완성하고, API 를 열고, 클라이언트는 나중에. 프로젝트 관리처럼 들려. 실은 아키텍처야. 짓는 순서가 어느 컴포넌트가 다른 것한테 요구할 수 있는지를 정해 — 그리고 먼저 짓는 게 뭐든 나머지 전부가 그 주위로 빚어지는 게 돼. 엔진을 먼저 지으면 클라이언트가 깨끗한 API 에 적응해. UI 를 먼저 지으면 API 가 그 UI 에 적응해, 영원히.

UI-먼저가 API 한테 조용히 하는 것

인터페이스로 시작하면 미묘하게 뭔가 틀어져. 필터와 토글이 달린 결과를 보여줄 화면이 필요해서, 정확히 그 화면의 데이터를, 그 화면의 모양으로, 그 화면의 필터 시맨틱으로 돌려주는 엔드포인트를 써. 아름답게 작동해 — 그 화면엔. 그다음 두 번째 클라이언트가 도착해서 API 가 자기가 본 적 없는 UI 를 위해 발명된 방언을 말한다는 걸 발견해: 원치 않는 필드, 풀어야 하는 모양, 공유 안 하는 가정. 첫 UI 의 우연이 API 의 영구 어휘가 됐어.

콘솔은 전구지, 제품이 아냐

그래서 콘솔은 두 번째로 지어지고, 자기 역할에 정직해: 회로에 처음 꽂힌 것이고, 전류가 흐른다는 걸 증명하러 있어. 특권이 없어. 다른 누구나 부를 같은 공개 엔드포인트를 부르고, API 가 안 주는 게 필요하면, 그건 API 에 대한 신호지 사설 뒷문의 핑계가 아냐. 특별 접근을 받는 첫 클라이언트는 클라이언트가 아냐 — 의상을 입은 엔진이고, API 는 다시는 정직하게 테스트 안 돼.

먼저 짓는 게 뭐든 나머지 전부가 그 주위로 빚어지는 게 돼. 이래서 빌드 순서가 일정이 아니라 아키텍처야. 인터페이스의 첫 소비자가 자기 가정을 영구히 각인해. 그러니 일반적인 걸 먼저 짓고 특수한 게 적응하게 두고 — 네 자신의 첫 클라이언트도 특권 없이 공개 경로를 쓰게 만들어, 뒷문 있는 클라이언트는 앞문이 되는지 말해줄 수 없으니까.

증거: 나중 클라이언트가 공짜였어

보상은 둘째, 셋째, 넷째 클라이언트가 도착하는데 엔진이 아예 안 바뀔 때 드러나. 에디터의 완성 팝업, 검색을 HTTP 도구로 부르는 비서, 내부 사이트 검색 뷰 — 각각 같은 엔드포인트에 꽂아 도착하자마자 온전한 능력을 얻었어. 엔진 수정 0. 그게 엔진-먼저의 영수증이야: 클라이언트 추가가 클라이언트 쪽만의 변경이면, API 가 충분히 일반적이었던 거야. 새 클라이언트마다 엔진 변경을 강요하면, UI 의 백엔드를 짓고 엔진이라 부른 거야.

Code

공개 계약 하나; 네 것 포함 모든 클라이언트가 앞문을 써·python
# The engine exposes ONE public contract. Nobody gets a private door.
@app.post("/api/search")
def search(query: str, mode: str = "hybrid", k: int = 8) -> list[CitableResult]:
    ...

# Client 1 — the console. Built SECOND. A lightbulb proving current flows.
results = http.post("/api/search", json={"query": q, "mode": "hybrid"})

# Client 2 — an editor's completion popup. Arrived later. Engine changes: zero.
suggestions = http.post("/api/complete", json={"prefix": tail})

# Client 3 — an assistant calling the engine as a tool over HTTP. Also zero.
slices = http.post("/api/search", json={"query": question, "k": 8})

# The receipt for engine-first: adding clients 2 and 3 was a CLIENT-side change.
# If each new client had forced an engine change, it was never a general engine —
# it was one UI's backend wearing an engine's name.

External links

Exercise

네가 짓거나 쓴 API 를 봐. 물어: 엔드포인트 모양만으로 어느 UI 가 먼저 지어졌는지 알 수 있어? 한 화면에만 말 되는 필드나, 특정 뷰 이름을 딴 필터를 찾아. 그다음 완전히 다른 클라이언트 추가를 상상해 — CLI, 봇, 다른 앱. 그 클라이언트가 우회해야 할 전부를 나열해. 그 목록이 첫 UI 가 API 한테 물린 세금이야.
Hint
신호는 리소스가 아니라 화면 이름을 딴 엔드포인트, 레이아웃을 미러링하는 응답 모양, 특정 뷰의 상태를 인코딩한 파라미터야. 하나하나가 첫 클라이언트의 우연이 계약에 얼어붙어 미래 모든 클라이언트가 이제 함께 살아야 하는 곳이야.

Progress

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

댓글 0

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

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