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

요리권을 넘나드는 형제들

~14 min · siblings, polymorphism, evidence, inference

Level 0밑준비
0 XP0/43 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

형제 요리가 뭐냐면

읽기 루브릭의 정의는 한 줄이야. 형제는 같은 원리를 상속한 요리고, 다른 요리권에서 오는 경우가 많다는 거지. 피파는 접시 위 요리마다 형제를 적을 수 있고, 형제 하나엔 필드가 5개 붙어. name_original, name_ko, cuisine, 같이 물려받은 principle(위에서 붙인 이름 그대로), 그리고 그 원리를 어떻게 물려받았는지 말하는 why_same이야.

루브릭은 원리가 받쳐 주는 한, 서로 다른 요리권에서 3개에서 5개를 달라고 해. 더 적어도 되고, 하나도 없어도 된대. 코드는 목록을 5개에서 잘라(dish.siblings[:5]). Sizzle이 새 요리를 끌어내는 곳이 바로 여기야. 아빠는 접시 하나를 찍고, 트리는 그 접시가 품은 요리들만큼 자라.

먹을 수 있는 다형성

설계 문서의 예시가 그 모양을 보여 줘. 뿌리 클래스는 기법 하나야. 향을 기름에 먼저 옮기고, 그 기름에서 익히는 거지. 인터페이스는 역할 묶음이야. 대개 향채, 기름, 그리고 그 기름에서 익는 무언가지. 형제마다 그 역할을 다른 재료로 채워.

형제향채기름거기서 익는 것
감바스 알 아히요 (스페인)마늘, 고추올리브유새우
알리오 올리오 (이탈리아)마늘, 고추올리브유버무리는 파스타
버섯 아히요 (스페인)마늘올리브유버섯
파기름 볶음밥 (한국)파식용유밥
바오샹(爆香) 볶음 (중국)마늘, 생강, 파아주 뜨거운 기름웍에 들어갈 나머지 전부
파에야의 소프리토양파, 마늘, 피망올리브유쌀이랑 육수

부엌은 달라도 원리는 하나야. 클래스를 쥔 요리사는 오늘 저녁 그 역할을 채울 재료만 있으면 불 앞에 설 수 있어. oo-quest 트랙 4가 "요리를 가로지르는 Polymorphism"에서 이 읽는 법을 가르쳐. 그리고 그 절 끝에 여기까지 들고 올 만한 경고를 남겨. 같은 구조적 역할은 맛보고 고칠 가설이지, 정체성이 아니라고. 그 표는 요리권을 맛의 축으로 읽어. Sizzle의 원리는 역할 인터페이스를 가진 기법이야. 저장할 수 있고, 냉장고 재료랑 맞춰 볼 수 있고, 사진에서 끌어낼 수 있는 단위로 쪼갠 거지. 그리고 Sizzle은 그 경고까지 필드 하나로 저장해.

코드가 형제마다 하는 일

integrate의 뒷부분이 목록을 돌아. 형제 하나마다 이렇게 해.

  1. 원어 이름도 한국어 이름도 없으면 건너뛰어.
  2. 이미 있는 요리나 변형을 원어 이름으로, 안 되면 한국어 이름으로 찾아. 다른 조회처럼 정규화한 이름으로 비교해.
  3. 부모를 골라. 형제가 댄 원리 이름이 이번 읽기에서 찾은 원리랑 맞으면 그거고, 아니면 그 요리에서 찾은 첫 번째 원리야. 이번 읽기가 그 요리의 원리를 하나도 못 찾았으면 부모는 없어.
  4. 맞는 게 없으면 새 요리를 제안해. origin: "sibling"을 달고, 한국어 이름은 gloss로, 요리권도 넣고, why_same을 요약으로 써.
  5. 요리가 이미 있었으면, 그게 원래 형제 제안이었을 때만 설명을 새로 고쳐. 그리고 이 접시의 바로 그 요리가 아니면 부모 간선을 더해.

놓치기 쉬운 결과가 3가지 있어. 형제 클래스엔 역할 바인딩이 없어. 그 요리가 역할을 어떻게 채우는지 접시로 보인 적이 없으니까, 페이지엔 왜 같은 집안인지만 남아. 3단계에서 원리를 맞출 땐 가벼운 키를 써. 띄어쓰기랑 대소문자만 통일하는 키야. 그래서 피파가 원리 이름을 살짝 다르게 써도, 형제는 허공에 뜨는 대신 요리의 첫 원리 밑으로 가. 다만 이 대체엔 기댈 원리가 있어야 해. 읽기가 그 요리의 원리를 하나도 못 찾았으면, 새 형제는 부모 없이 제안되고 Tree 페이지의 Not yet under a principle, 곧 아직 원리 아래에 없는 목록에서 기다려. 그리고 5단계 덕분에, 트리에 이미 있던 요리도 형제로 언급되면 새 부모를 얻을 수 있어. 원리 2개 제한은 읽기 한 번이 요리 하나에 대해 주장할 수 있는 양이야. 시간이 지나면서 요리가 물려받는 원리 수엔 상한이 없어.

만나기 전까진 추정

형제는 연결된 접시 없이 들어와. 그리고 Sizzle에서 증거는 저장하는 게 아니라 링크로 계산해. 그래서 갓 생긴 형제는 inferred로 읽혀. 웹에선 Pippa's inference, 곧 피파의 추정이라고 뜨고, 폰에선 반짝이 아이콘이야. 엔진 테스트가 바로 이걸 확인해. 감바스 한 접시를 읽고 나면 접시 위 요리는 encountered, 알리오 올리오는 inferred이고, 한국어 이름을 가진 형제도 트리에 있어.

이 상태가 바뀌는 데 고칠 행은 하나도 없어. 나중에 아빠가 알리오 올리오를 먹고 사진을 찍으면, 새 읽기가 그걸 접시 위 요리로 말해. _resolve_dish가 이름으로 기존 형제 클래스를 찾고, 새 접시를 거기 이어. 같은 행이 이제 Eaten, 곧 먹어 봄이 돼. 이건 웹이 encountered에 붙인 이름표인데, encountered가 뜻하는 건 이어진 접시가 하나 이상 있고 그중 집에서 만든 게 없다는 것뿐이야. 아빠가 시장에서 봤다고 적은 접시 하나만 이어져도 그 행은 똑같이 Eaten으로 떠. 이름표가 상태보다 더 많이 말하는 셈이지. 집에서 만든 접시면 Cooked, 곧 직접 요리가 되고. origin은 여전히 sibling이고 상태도 여전히 proposed야. 이어진 접시는 아빠가 그 요리를 만났다는 증거일 뿐이야. 채택은 따로고, 그건 아빠만 해.

원칙: 추론도 진짜 레코드로 저장하고, 믿을 만한지는 증거로 계산해. 그러면 믿음이 바뀌어도 레코드는 안 바뀌어도 돼.

배지는 장식이 아니야

Sizzle 불변식 5번은 증거 상태를 늘 보여 주라고 해. 도메인 모델 문서는 더 대놓고 말해. 추정한 형제가 맛본 요리처럼 보이는 일은 절대 없어야 한다고. 요리 50개 중 40개가 피파 짐작인 트리는, 그 40개가 어느 건지 보일 때만 쓸모가 있어. 배지가 없으면 트리는 아빠가 먹어 본 적도 없는 요리를 안다고 천천히 믿게 만들 거야.

Code

cwkSizzle, backend/reading.py: integrate 뒷부분, 추정한 형제들 (원문 그대로, 224-239행)·python
        for sibling in dish.siblings[:5]:
            name = sibling.name_original or sibling.name_ko
            if not name.strip():
                continue
            existing = sizzle.find_dish(sibling.name_original, sibling.name_ko)
            parent = principle_by_name.get(_key(sibling.principle)) or (principle_ids[0] if principle_ids else None)
            if existing is None:
                sizzle.propose_class(kind="dish", name=name, gloss=sibling.name_ko, cuisine=sibling.cuisine,
                                     summary=sibling.why_same, origin="sibling",
                                     parents=[parent] if parent else [], source_encounter=encounter_id)
                continue
            if existing["origin"] == "sibling":
                _refresh(sizzle, existing, {"gloss": sibling.name_ko, "cuisine": sibling.cuisine,
                                            "summary": sibling.why_same})
            if parent and existing["id"] != dish_id:
                sizzle.ensure_parent(existing["id"], parent)
cwkSizzle, backend/tests/test_tree.py: 접시 하나, 만난 요리 하나, 추정한 형제들 (원문 그대로, 11-23행)·python
def test_a_reading_grows_the_tree_with_evidence(make_client, fake_pippa):
    client = make_client()
    encounter_id = capture(client, "req-tree-001")["encounter"]["id"]
    body = _read(client, encounter_id)
    assert body["view"]["unsure"], "doubts are carried, not hidden"
    tree = client.get("/api/tree").json()
    assert len(tree["principles"]) == 1
    principle = tree["principles"][0]
    assert principle["status"] == "proposed"
    by_name = {d["name"]: d for d in principle["dishes"]}
    assert by_name["Gambas al ajillo"]["evidence"]["status"] == "encountered"
    assert by_name["Aglio e olio"]["evidence"]["status"] == "inferred"
    assert "파기름 볶음밥" in by_name

External links

Exercise

만들고 있는 소프트웨어에 모델이 추론한 제안을 하나 넣어 봐. 글의 관련 글도 좋고, 버그 리포트의 중복 후보나 상품의 비슷한 상품도 좋아. 제안 하나하나를 origin 필드가 붙은 진짜 엔티티로 저장하고, 증거 상태는 플래그로 두지 말고 진짜 레코드와의 링크로 계산해. 새 제안이 inferred로 읽히는지 확인하는 테스트를 써. 이어서 진짜 레코드 하나를 잇고, 제안 행은 하나도 안 고쳤는데도 상태가 확인됨으로 바뀌는지 검사해.
Hint
증거는 아예 테이블 밖에 둬. Sizzle의 evidence()는 클래스를 읽을 때마다 현재 링크를 쿼리해서, 깜빡하고 안 고칠 플래그 자체가 없어. 형제 루프가 많아야 5개까지만 받는 것처럼, 모델 호출 한 번이 더할 수 있는 제안 수에도 상한을 둬.

Progress

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

댓글 0

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

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