물어볼 수 있는 기록 — 노트 서른 권이 못 보여 준 것
9 분먼저 밝혀 둘 것이 있다. 2026년 4월에 이 매거진은 오리건의 30년차 토마토 농부가 헛간의 노트 서른 권을 한 화면으로 옮긴 이야기를 실었다. 실명과 나이와 지역까지 특정된 인터뷰였다.
이 현장은 특정 농가가 아니다. 그래서 원 인터뷰를 그대로 다시 싣지 않는다. 대신 같은 형태를 처음부터 끝까지 직접 만들었다. 아래의 모든 화면과 숫자는 그렇게 만든 것에서 나온 것이고, 어느 한 농가의 기록은 아니다.
이 구분을 첫 문단에 두는 이유는, 원 글의 논지 전체가 "당신도 된다"는 허락이고 그 허락은 사례의 실재에 얹혀 있기 때문이다. 실재를 확인하지 못한 채 같은 문장을 반복하면 그건 허락이 아니라 광고다.
그래서 이 글이 증명하려는 것은 좁아진다. "어떤 농부가 해냈다"가 아니라, "이런 도구는 이렇게 생겼고 실제로 이만큼 된다" 까지다.
원 글의 심장에 실물이 없었다
원 글의 축은 좋았다. 종이는 적는 데서 멈춘다. 꺼내서 맞춰 보는 일은 사람의 기억과 인내에 맡겨진다. 그런데 같은 칸으로 쌓이면 기록이 물어볼 수 있는 것이 된다.
그 물성 전환은 지금도 맞다고 본다. 문제는 그 글이 그것을 한 번도 보여 주지 않았다는 것이다.
- 그가 실제로 무엇을 말했는지 — 요구 원문 0줄
- 그 말이 어떤 화면 정의가 되었는지 — 정의 발췌 0줄
- 어떤 단계를 거쳐 만들어졌는지 — 제작 과정 0단계
- 노트 서른 권 이관 — "틈틈이 옮기고 있다"는 진술뿐
그리고 글의 백미인 "옮긴 지 18일째 첫 꽃" 이라는 발견도 그랬다. 다섯 해 치 데이터가 쌓여야 성립하는 주장인데, 그 데이터도 그것이 드러난 화면도 없었다.
이번엔 셋을 나란히 둔다. 말한 것 → 정의가 된 것 → 실제로 그려진 것.
말한 것
만들면서 요구를 세 문장으로 적었다. 개발 용어를 쓰지 않았다.
- "작물을 고르면 올해 기록이 죽 뜨게."
- "적는 건 한 손으로 되게."
- "작년은 이맘때 어땠는지 옆에 뜨게."
세 문장이 도구 셋이 됐다.
log offers: log.season, log.add, log.compare정의가 된 것
첫 문장이 화면의 이 부분이 됐다. 코드가 아니라 선언이다.
{
"type": "list",
"items": "{{season}}",
"emptyMessage": "Nothing recorded this season yet",
"itemTemplate": {
"type": "linear", "direction": "horizontal", "spacing": 12,
"children": [
{ "type": "text", "text": "{{item.date}}" },
{ "type": "text", "text": "{{item.action}}" },
{ "type": "text", "text": "{{item.tempC}}°C" },
{ "type": "text", "text": "{{item.note}}" }
]
}
}둘째 문장은 버튼 하나가 됐다.
{
"type": "button",
"label": "First flower",
"onTap": {
"type": "tool", "tool": "log.add",
"params": { "crop": "Tomato", "action": "firstFlower", "tempC": 19.5 }
}
}셋째 문장은 화면 하나가 더 생겼다. 라우트 한 줄이다.
"routes": { "/": "ui://pages/today", "/compare": "ui://pages/compare" }앱 전체가 JSON 네 개다. 컴파일은 돌지 않는다.
실제로 그려진 것
오늘 화면. 옮긴 지 며칠인지와 예년 평균이 위에 있다.

「First flower」를 한 번 누르면 줄이 하나 는다.

그리고 해를 겹쳐 본 화면.

18.2일은 어디에도 적혀 있지 않다
이게 이 편에서 가장 중요한 부분이다.
원 글은 "18일"을 결과처럼 적었다. 이 샘플에서 그 숫자는 상수가 아니다. 기록에서 매번 계산된다.
/// Days from transplant to first flower, per season.
///
/// This is the whole "eighteen days" claim, and it is arithmetic over the
/// rows — no constant anywhere. A year with no flower recorded yet is left
/// out rather than guessed at.그리고 검증이 그걸 직접 확인한다. 계산된 값이 소스에 문자열로 들어 있으면 실패시킨다.
src = open(os.path.join(SERVER, "bin", "server.dart")).read()
assert str(cmp["avgDays"]) not in src, "the average must come from the records, not the source"통과 로그다.
average 18.2 computed from records, not present in any source file"기록에서 드러났다"와 "그렇게 적어 뒀다"는 화면에서 똑같이 생겼다. 그래서 구별할 방법을 통과 조건에 넣었다.
예외가 말을 하기 시작한다
원 글에서 가장 좋았던 관찰은 평균이 아니라 그 다음이었다. 닻이 생기니 벗어나는 해가 보이기 시작한다는 것.
계산된 표가 그대로 그것이다.
spans: 2021:22d, 2022:18d, 2023:15d, 2024:18d, 2025:18d, 2026:18d2021년은 22일이었고, 그 줄의 메모에 five cold days in early April 이 붙어 있다. 2023년은 15일이었고 warm week before transplanting 이 붙어 있다.
흩어진 노트에서는 22일도 15일도 "올핸 좀 다르네"로 흘러갔을 것이다. 비교할 기준선이 없으면 예외인 줄도 모른다. 18.2라는 평균이 서고 나서야 그 두 해가 말을 하기 시작한다.
같은 칸이 아니면 겹쳐지지 않는다
이 모든 게 성립하는 조건은 하나다. 기록이 같은 모양이어야 한다.
/// One entry, in the same six slots every time. The whole argument of the
/// article rests on this being fixed: `plot · crop · action · date · note ·
/// tempC`. Paper let every year invent its own shorthand; slots do not.여섯 칸이다. 종이가 못 한 게 이것이다 — 종이는 해마다 다른 약어를 허용한다. 2019년의 "정식"과 2023년의 "옮김"이 같은 뜻인지 아닌지, 노트는 모른다.
그리고 날짜에 관해 한 가지를 일부러 했다.
// The date is the machine's, not the grower's memory. Paper let a row
// be written three days late with no trace; this cannot.판단과 도구의 분담
원 글의 최고 자산은 5단계 분담 표였다. 무엇이 필요한지의 판단은 농부가, 그것을 실어 나르는 일은 도구가. 그 표를 유지하되, 이번엔 각 행이 위에서 본 실물을 하나씩 가리킨다.
| 무엇 | 누가 | 이 글의 실물 |
|---|---|---|
| 무엇을 기록할지 정하기 | 농부 | 여섯 칸 (plot·crop·action·date·note·tempC) |
| 어떻게 보고 싶은지 말하기 | 농부 | 요구 세 문장 |
| 그 말을 화면으로 옮기기 | 도구 | 번들 JSON 4개 |
| 쌓고 추리고 계산하기 | 도구 | log.season · log.add · log.compare |
| 무엇을 할지 정하기 | 농부 | — (도구는 판단하지 않는다) |
마지막 행이 원 글의 절제된 경계 절과 같은 자리다. 화면 아래에 그 문장을 그대로 뒀다.
A tendency, not an instruction. Read this season on its own terms.
도구는 평균을 낼 뿐 심으라고 하지 않는다. 그 선을 넘는 순간 30년의 판단은 쓰이지 않는다.
이 샘플의 범위
농부는 없다. 이 글에 나오는 요구 세 문장은 내가 적은 것이고, 다섯 해 치 기록은 내가 만든 것이다. 그러니 "농부가 이렇게 만들었다"고 읽지 말아 달라. 이 글이 보인 것은 "이런 도구가 이렇게 생겼고, 이만큼 짧고, 평균은 진짜로 계산된다"까지다.
그리고 도메인 전문가가 이 형식을 직접 다룰 수 있는지는 확인하지 않았다. 위 JSON은 사람이 읽을 만하지만, 58세 농부가 저걸 직접 고칠 수 있느냐는 별개의 물음이고 이 시리즈는 답하지 않았다. 원 글이 "말하면 된다"고 한 그 변환 — 사람의 말이 정의가 되는 단계 — 은 여기서 내가 손으로 했다.
노트 서른 권을 옮기는 일도 다루지 않았다. 종이를 칸으로 옮기는 작업량이 실제로 얼마인지는 이 샘플이 재지 않았다.
UI가 영어인 이유도 적어 둔다. 이 시리즈의 샘플은 화면과 코드를 모두 영어로 쓴다. 이 편은 어느 한 농가의 도구가 아니라 구성이므로, 어떤 언어의 독자든 같은 캡처와 같은 소스를 읽는다.
적는 것에서 물어보는 것으로
원 글은 "작게 시작해 한 칸씩 늘려라"로 닫았다. 그 권유는 유지한다.
거기에 만들어 보고 알게 된 한 줄을 붙인다. 칸을 늘리는 것보다 칸을 같게 유지하는 게 어렵다. 여섯 칸을 정하는 데는 십 분이 걸리지 않았지만, 그 여섯 칸이 다섯 해 내내 같아야만 마지막 화면이 성립한다. 종이가 실패한 지점이 정확히 거기다.
그리고 성립하고 나면 기록이 성질을 바꾼다. 적어 두는 것에서 물어볼 수 있는 것으로. 18.2일은 누가 적어 준 게 아니라 물어봐서 나온 답이었다.
확인 과제
설비 예제에서 대답 하나를 골라 그 대답이 인용한 기록을 짚어 보세요. 맞는 기록이 없으면 도구는 무엇을 해야 합니까?