현장

물어볼 수 있는 기록 — 노트 서른 권이 못 보여 준 것

작성: makemind · 2026년 7월 14일

먼저 밝혀 둘 것이 있다. 2026년 4월에 이 매거진은 강원 화천의 30년차 토마토 농부가 헛간의 노트 서른 권을 한 화면으로 옮긴 이야기를 실었다. 실명과 나이와 지역까지 특정된 인터뷰였다.

이 현장은 특정 농가가 아니다. 그래서 원 인터뷰를 그대로 다시 싣지 않는다. 대신 같은 형태를 처음부터 끝까지 직접 만들었다. 아래의 모든 화면과 숫자는 그렇게 만든 것에서 나온 것이고, 어느 한 농가의 기록은 아니다.

이 구분을 첫 문단에 두는 이유는, 원 글의 논지 전체가 "당신도 된다"는 허락이고 그 허락은 사례의 실재에 얹혀 있기 때문이다. 실재를 확인하지 못한 채 같은 문장을 반복하면 그건 허락이 아니라 광고다.

그래서 이 글이 증명하려는 것은 좁아진다. "어떤 농부가 해냈다"가 아니라, "이런 도구는 이렇게 생겼고 실제로 이만큼 된다" 까지다.

원 글의 심장에 실물이 없었다

원 글의 축은 좋았다. 종이는 적는 데서 멈춘다. 꺼내서 맞춰 보는 일은 사람의 기억과 인내에 맡겨진다. 그런데 같은 칸으로 쌓이면 기록이 물어볼 수 있는 것이 된다.

그 물성 전환은 지금도 맞다고 본다. 문제는 그 글이 그것을 한 번도 보여 주지 않았다는 것이다.

  • 그가 실제로 무엇을 말했는지 — 요구 원문 0줄
  • 그 말이 어떤 화면 정의가 되었는지 — 정의 발췌 0줄
  • 어떤 단계를 거쳐 만들어졌는지 — 제작 과정 0단계
  • 노트 서른 권 이관 — "틈틈이 옮기고 있다"는 진술뿐

그리고 글의 백미인 "옮긴 지 18일째 첫 꽃" 이라는 발견도 그랬다. 다섯 해 치 데이터가 쌓여야 성립하는 주장인데, 그 데이터도 그것이 드러난 화면도 없었다.

이번엔 셋을 나란히 둔다. 말한 것 → 정의가 된 것 → 실제로 그려진 것.

말한 것

만들면서 요구를 세 문장으로 적었다. 개발 용어를 쓰지 않았다.

  1. "작물을 고르면 올해 기록이 죽 뜨게."
  2. "적는 건 한 손으로 되게."
  3. "작년은 이맘때 어땠는지 옆에 뜨게."

세 문장이 도구 셋이 됐다.

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 네 개다. 컴파일은 돌지 않는다.

실제로 그려진 것

오늘 화면. 옮긴 지 며칠인지와 예년 평균이 위에 있다.

Today — day 18 since transplanting, past seasons averaged 18.2 d. 실제 렌더 캡처
Today — day 18 since transplanting, past seasons averaged 18.2 d. 실제 렌더 캡처

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

한 번의 탭으로 기록 한 줄 추가 — 2026-04-19 firstFlower recorded
한 번의 탭으로 기록 한 줄 추가 — 2026-04-19 firstFlower recorded

그리고 해를 겹쳐 본 화면.

18.2 days on average — 2021:22d, 2022:18d, 2023:15d, 2024:18d, 2025:18d, 2026:18d
18.2 days on average — 2021:22d, 2022:18d, 2023:15d, 2024:18d, 2025:18d, 2026:18d

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.

그리고 검증이 그걸 직접 확인한다. 계산된 값이 소스에 문자열로 들어 있으면 실패시킨다.

AVG=$(grep -o "average from records: [0-9.]*" captures/run.log | awk '{print $4}')
if grep -RIn --exclude-dir=captures -F "$AVG" farm_server/bin farm_log.mbd bundle_host/lib; then
  echo "   the average $AVG appears literally in the source — it must be computed"
  exit 1
fi

통과 로그다.

   average 18.2 computed from records, not present in any source file

"기록에서 드러났다"와 "그렇게 적어 뒀다"는 화면에서 똑같이 생겼다. 그래서 구별할 방법을 통과 조건에 넣었다.

이 콘텐츠는 개발자 이상이 필요합니다

로그인 후 플랜을 업그레이드하면 계속 읽을 수 있습니다.

플랜 보기
Twitter