# Kai's Note > 명리와 고전, 책과 말에서 건져 올린 생각을 기록합니다. AI와 기술을 활용한 경험, 업무자동화와 개발, 일상의 단상도 함께 나눕니다. Public Ghost content for AI and LLM tooling. This file includes a bounded export of public pages first, then recent public posts. Append `.md` to any post or page URL to get the content in Markdown (for example, `/example-post.md`). ## Pages ### About URL: https://withkai.io/about/ Last updated: 2023-09-03T03:50:51.000Z ![](https://withkai.io/content/images/2023/09/IMG_7941.JPG) ### 앗-하는 사이에 ![](https://withkai.io/content/images/2023/09/IMG_7646.jpeg) 클래식이 될지도 모를만큼 시간이 훅훅 지나갑니다. 개인에 대한 기록을 남길만한 성격은 못되지만, 잡다한. 글감을 인용하거나. 관심있는 분야를 갈무리해두는 용도로 써볼까해요. (... 당분간 어지간하지않으면 업데이트는 진행하지 않기로 해요.😂) #### 이런 분야에 관심이 많습니다. - 시(시를 안다기보다 제목, 시집을 폈을 때 작가의 말이나 평론을 살펴보며 고릅니다) - 문장이나 짤(밈이어도 좋고, 혼자 꽂히는 부분이 있으니까요) - 홈 서버(시놀로지에 도커 열심히 올려서 잘 모르지만 이것저것 건드려봅니다.) - 연관되어 IoT(라고 해도 초보수준이라 그냥 단순히 불끄고 불켜기 정도이지요) - 업무자동화(초보이지만 Google Apps Script로 구글시트를 건드리거나, 구글링을 통한 파이썬, JS 등 살펴보는 것 좋아합니다.) - 살펴보다보니 웹....이라고하기에 이걸 웹이라고 해야하나 싶지만.... - 등등 추후 더 업데이트 해볼게요. (2023-07-09/Imported-23-09-03) ### 개인정보 및 방문 통계 안내 URL: https://withkai.io/privacy/ Last updated: 2026-07-31T01:15:20.000Z 최종 수정일: 2026년 7월 31일 Kai's Note(`withkai.io`, 이하 “이 사이트”)는 댓글과 로그인 기능을 제공하고 사이트 이용 현황을 파악하기 위해 필요한 범위에서만 정보를 처리합니다. ## 1\. 로그인과 댓글 댓글을 작성하려면 이메일 인증을 통한 무료 회원 로그인이 필요합니다. - 필수 정보: 이메일 주소 - 선택 정보: 사용자가 입력한 이름 - 함께 저장될 수 있는 정보: 가입·로그인 상태, 가입 시각, 작성한 댓글과 댓글 작성 시각 - 이용 목적: 인증 링크 발송, 로그인 상태 확인, 댓글 작성 및 관리 로그인은 비밀번호 대신 이메일로 전달되는 일회성 인증 링크를 사용합니다. 인증 메일 전달 과정에서는 외부 이메일 서비스가 사용될 수 있습니다. 이 사이트는 현재 뉴스레터를 발송하지 않으며, 회원 이메일을 광고성 메일 발송에 사용하지 않습니다. ## 2\. 방문 통계 사이트 이용 흐름을 이해하기 위해 운영자가 직접 관리하는 방문 통계 도구를 사용합니다. 수집되는 통계 항목은 다음과 같습니다. - 방문한 페이지 주소와 페이지 제목 - 이전 방문 경로(리퍼러) - 브라우저, 운영체제, 기기 종류 - 화면 크기와 브라우저 언어 - 대략적인 국가·지역 정보 - 방문 시각, 페이지 조회와 세션 정보 방문 통계 도구는 쿠키를 사용하지 않으며 다른 웹사이트를 가로질러 방문자를 추적하지 않습니다. 국가·지역 판별 과정에서 요청 IP 주소가 일시적으로 사용될 수 있지만 방문 통계에는 IP 주소를 저장하지 않습니다. 현재는 기본적인 페이지 조회 통계만 수집하며 이메일 주소, 회원 이름, 댓글 내용 같은 개인정보를 방문 분석 이벤트로 보내지 않습니다. 방문 통계는 운영자가 삭제할 때까지 보관될 수 있습니다. ## 3\. 서비스 운영 기록 보안, 오류 확인과 서비스 운영 과정에서 접속·오류 기록이 생성될 수 있습니다. 기록에는 접속 시각, 요청 경로, 브라우저 정보와 접속 IP 주소 등이 포함될 수 있으며 서비스 점검과 보안 목적에만 사용합니다. ## 4\. 저장 위치와 외부 서비스 - 회원·댓글 정보와 방문 통계는 운영자가 관리하는 시스템에 저장됩니다. - 로그인 인증 메일 전달에는 외부 이메일 서비스가 사용될 수 있습니다. - 사이트 운영에 필요한 경우를 제외하고 회원 정보를 판매하거나 광고 목적으로 제공하지 않습니다. ## 5\. 보관과 삭제 - 회원 정보는 회원 기능 제공과 계정 관리에 필요한 기간 동안 보관합니다. - 댓글은 사용자가 삭제하거나 운영상 삭제할 필요가 생길 때까지 게시될 수 있습니다. - 방문 통계와 서버 로그는 사이트 운영과 보안에 필요한 기간 동안 보관한 뒤 삭제할 수 있습니다. - 관련 법령에서 별도의 보관을 요구하는 경우에는 해당 기간을 따를 수 있습니다. ## 6\. 이용자의 요청 회원은 자신의 정보에 대한 확인, 정정 또는 삭제를 요청할 수 있습니다. 요청 시 가입에 사용한 이메일 주소를 확인할 수 있으며, 본인 확인 후 처리합니다. 문의 및 요청: **[noreply@withkai.io](mailto:noreply@withkai.io)** ## 7\. 안내 변경 수집 항목이나 이용 서비스가 달라지면 이 페이지의 내용과 최종 수정일을 함께 갱신합니다. ## Posts ### [이것도 AI로 될까? — 차 관리 편] 5부 — 집 안에서만 되는 건 절반이었다 URL: https://withkai.io/igeosdo-airo-doelgga-ca-gwanri-pyeon-5bu-jib-aneseoman-doeneun-geon-jeolbanieossda/ Last updated: 2026-08-22T14:27:20.000Z > **이번 편의 요청의 요지** > 정비소나 외출 중에도 내 기록 도구를 쓰게 하되, 집 안의 데이터와 관리 화면은 안전하게 지켜 줘. ## 집 안에서만 되는 것은 절반만 된 것이다 여기까지 만든 것들은 전부 집 NAS 안에서 돈다. 기록을 넣는 곳도, 판정하는 곳도, 화면도 거기 있다. 집에서는 잘 된다. 문제는 정비소가 집에 없다는 것이다. 정비를 받고 나오면서 기록을 넣으려면 밖에서 서버에 닿아야 한다. 화면을 열어 "지금 뭐가 갈 때가 됐지"를 보는 것도 대개 밖에서다. **집 안에서만 되는 도구는 정작 필요한 순간에 안 되는 도구다.** 그래서 AI에게 집 안의 서버를 밖에서도 쓰게 해 달라고 했다. 조건은 두 가지였다. 사람이 쓰는 화면은 인증 뒤에 열리고, 기계가 부르는 길은 필요한 범위만 열려야 했다. 편리함을 늘리면서 집 안의 다른 데이터까지 함께 내놓고 싶지는 않았다. ## 공유기에 구멍을 내는 방법은 쓰지 않았다 밖에서 집 안으로 들어오는 흔한 방법은 공유기에 포트를 여는 것이다. 특정 포트로 들어온 요청을 안쪽 기기로 넘겨 주면 된다. 설정도 몇 분이면 끝난다. 쓰지 않았다. 포트를 열면 그 순간부터 **인터넷 전체가 그 문을 두드릴 수 있다.** 내가 주소를 아무에게도 안 알려 줘도 상관없다. 온종일 모든 IP의 모든 포트를 훑고 다니는 것들이 있고, 열린 포트는 며칠 안에 목록에 오른다. 그 문 뒤에 있는 것이 로그인 화면이라 해도 마음이 놓이지 않았다. 로그인 화면이 있다는 것은 **그 소프트웨어가 인터넷에 노출돼 있다**는 뜻이고, 거기에 취약점이 발견되면 내가 패치를 올리기 전까지는 열려 있는 것이다. 집 NAS에는 정비 기록만 있는 것이 아니다. ## 터널은 안에서 밖으로 연결한다 대신 터널 방식을 썼다. Cloudflare가 제공하는 것이다. ![Cloudflare 연결 상태 화면](https://withkai.io/content/images/2026/08/carlog-5-cloudflare-connected.png) *Cloudflare 연결이 켜진 상태를 확인한 화면이다. 실제 터널은 NAS 안의 프로그램이 바깥으로 연결을 유지한다.* 방향이 반대인 것이 핵심이다. 밖에서 안으로 들어오는 문을 여는 게 아니라, **안에 있는 작은 프로그램이 밖으로 나가서 연결을 유지한다.** 방문자는 Cloudflare에 접속하고, Cloudflare가 그 연결을 통해 내 NAS로 요청을 넘긴다. 그래서 공유기에는 아무 구멍도 나지 않는다. 포트 스캔에도 안 잡힌다. 스캔할 열린 포트가 없기 때문이다. NAS의 실제 주소도 밖에서 보이지 않는다. 설치는 도커 컨테이너 하나 띄우고 관리 화면에서 어느 주소를 어디로 보낼지 정하는 것이 전부였다. 인증서도 알아서 붙는다. ## 열어 놓고 아무나 못 들어오게 터널만 놓으면 그 주소는 인터넷에 공개된 것이다. 포트는 안 열었지만 주소를 아는 사람은 들어올 수 있다. 그래서 앞에 인증을 하나 더 걸었다. Cloudflare가 방문자를 먼저 확인하고, 통과한 사람만 뒤로 보내 준다. **내 서버에 요청이 닿기 전에** 걸러진다는 점이 중요했다. 서버의 로그인 화면이 첫 방어선이면 그 화면 자체가 공격 대상이 되는데, 이 방식에서는 서버가 아예 요청을 받지 않는다. 정책은 단순하게 뒀다. 허용 목록에 이메일 하나. 로그인하면 한동안 유지되고 그 뒤에는 다시 확인한다. 설정하고 나서 확인은 로그인하지 않은 상태로 해 봤다. 브라우저는 이미 로그인돼 있으니 소용이 없다. 터미널에서 주소를 두드려 보니 전부 로그인 화면으로 넘기는 응답이 왔다. **화면이 아니라 응답 코드로 확인해야 한다.** 시크릿 창으로 여는 방법도 있지만 이쪽이 확실하다. ## 주소가 바뀌자 관리 화면이 깨졌다 터널을 붙이고 나서 데이터 관리 화면을 열었더니 껍데기만 나왔다. 로그인은 되는데 그 뒤가 하얗다. 브라우저 개발자 도구를 열어 보니 화면을 그리는 데 필요한 파일들이 전부 없다고 나오고 있었다. 서버는 멀쩡히 돌고 있는데 파일을 못 찾는다. 원인은 그 도구가 **자기 주소를 알고 있어야 한다**는 데 있었다. 설정에 공개 주소를 적는 칸이 있고, 화면에 필요한 파일들의 경로를 그 값으로 만들어 낸다. 나는 그 칸을 집 안에서 쓰던 주소로 둔 채 밖에서 열고 있었다. 서버는 밖에서 온 요청에 대해 안쪽 주소로 된 경로를 돌려주고 있었던 것이다. 값을 새 주소로 고치고 다시 띄우자 바로 열렸다. 터널을 붙이는 것은 앞단의 일이라고 생각했는데, **뒤에 있는 프로그램이 자기가 어디에 있다고 생각하는지**도 함께 맞춰야 했다. 주소를 하나 새로 내면 그 주소를 아는 곳이 여러 군데 생긴다. ## 콘솔이 안 열려서 삼십 분을 썼다 또 하나. 관리 도구에서 컨테이너 안으로 들어가는 콘솔이 밖에서는 안 열렸다. 집 안에서는 되는데 밖에서만 안 된다. 콘솔은 일반 요청이 아니라 **연결을 유지하는 방식**을 쓴다. 처음에 보통의 HTTP로 접속한 다음 그 연결을 다른 방식으로 바꿔 달라고 요청하고, 그때부터 양쪽이 계속 주고받는다. 터미널이니 그래야 한다. 앞단 프록시에 그 전환을 지원하는 설정이 있어서 켜 뒀는데도 안 됐다. 알고 보니 인증을 붙이려고 따로 써 둔 설정 블록이 **기본 설정을 통째로 덮고 있었다.** 전환에 필요한 헤더 세 줄이 그 블록에는 없었다. 그 세 줄을 직접 넣으니 열렸다. 설정을 겹쳐 쓸 때 나중 것이 앞엣것을 부분적으로 보완할 거라고 기대했는데, 실제로는 **그 자리를 통째로 가져갔다.** 이런 것은 문서를 읽어도 잘 안 보인다. 안 되는 것을 만나고 나서야 어느 설정이 어느 설정을 덮는지 들여다보게 된다. ## 로그인을 걸었더니 봇이 로그인 화면을 받았다 여기서 이 편의 진짜 이야기가 시작된다. 같은 방식으로 워크플로 도구에도 인증을 걸려고 했다. 관리 화면이 열려 있으면 위험하니 당연한 수순이라고 생각했다. 그런데 그 도구는 **웹훅을 받는다.** 아이폰 단축어가 명세서 글자를 보내는 곳이고, 텔레그램이 새 메시지를 알려 주는 곳이다. 인증을 걸면 어떻게 되나. 단축어가 요청을 보내면 **로그인 화면이 돌아온다.** 단축어는 로그인할 수 없다. 텔레그램도 마찬가지다. 사람이 브라우저로 들어올 때만 성립하는 방식을 기계가 부르는 곳에 걸면, 그 순간 기계는 전부 막힌다. 당연한 이야기인데 걸어 보기 전까지는 생각하지 못했다. **"보안을 걸었다"와 "쓸 수 있다"가 충돌하는 지점이 있다는 것**을 그때 알았다. ## 사람이 쓰는 곳과 기계가 부르는 곳을 갈랐다 그래서 하나로 덮지 않고 나눴다. | | 누가 부르나 | 어떻게 지키나 | | -------- | ---------- | ---------------------------- | | 화면·관리 도구 | 사람이 브라우저로 | 앞단에서 인증. 통과해야 서버에 닿는다 | | 웹훅 | 기계가 프로그램으로 | 앞단 인증 없음. **대신 비밀 헤더를 요구한다** | 웹훅 쪽은 주소를 아는 것만으로는 안 되게 했다. 요청에 약속된 헤더가 실려 있어야 받아 준다. 단축어와 봇은 그 값을 갖고 있고, 지나가다 주소를 발견한 쪽은 갖고 있지 않다. 완벽한 방식은 아니다. 헤더 값이 새면 그걸로 끝이다. 다만 **기계가 부르는 문에는 기계가 통과할 수 있는 자물쇠를 달아야 한다.** 사람용 자물쇠를 달면 문이 잠기는 게 아니라 기능이 멈춘다. 관리 도구 중 하나는 아예 다른 방식으로 뒀다. 그건 원래 다른 인증 시스템 뒤에 있었고, 굳이 옮기지 않았다. 옮기다가 그 도구 자체를 못 쓰게 되면 **고칠 수단까지 함께 잃는다.** 컨테이너를 관리하는 도구를 컨테이너 관리 도구로 재배포하는 상황은 피하고 싶었다. ## 무엇이 열려 있는지 알고 있어야 한다 정리하고 나니 열린 것과 닫힌 것이 이렇게 됐다. - 공유기 포트: **없음** - 사람이 쓰는 주소: 인증 뒤. 이메일 하나만 - 기계가 부르는 주소: 열려 있고 **헤더로 확인** - 그 밖의 것: 집 안에서만 이 목록을 적어 두는 것이 실은 가장 중요한 일이었다. 하나씩 설정할 때는 각각 합리적인데, 몇 달 지나면 **무엇이 왜 열려 있는지 기억나지 않는다.** 기억나지 않는 문은 닫지도 못한다. 닫으면 뭐가 깨질지 모르니까. 그래서 설치 절차와 함께 이 목록을 문서로 남겼다. 새로 뭔가를 열 때마다 여기에 한 줄을 더하고, 왜 열었는지도 적는다. ## 편한 쪽과 안전한 쪽이 다를 때 이 편에서 두 번 결정을 뒤집었다. 한 번은 포트를 여는 대신 터널을 쓴 것이다. 포트를 여는 쪽이 훨씬 간단했다. 다른 한 번은 모든 곳에 인증을 걸려다 기계가 부르는 곳은 빼기로 한 것이다. 전부 거는 쪽이 마음은 편했다. 두 번 다 "더 안전한 쪽"이 답은 아니었다. 첫 번째는 더 안전한 쪽이 맞았고, 두 번째는 **더 안전하게 만들려던 것이 기능을 멈추게 했다.** 보안은 세게 걸수록 좋은 게 아니라 **무엇을 지키려는지 정하고 거기에 맞춰야** 한다는 것을 두 번째에서 배웠다. 지금 이 시스템에서 지키려는 것은 명확하다. 정비 기록에 차량번호와 정비소 정보가 들어 있고, 같은 NAS에 다른 것들도 있다. 사람이 들어오는 문은 좁게, 기계가 들어오는 문은 열되 열쇠를 요구하는 것. 그 정도면 지금 규모에는 맞는다고 봤다. 이제 정비소를 나와서 사진을 찍고, 알림을 확인하고, 필요하면 기록을 바로 넣을 수 있다. 도구가 집 안에 있다는 사실은 그대로지만, 사용하는 순간만큼은 장소에 묶이지 않는다. 돌아보면 AI가 내 생활을 대신한 것은 아니다. 내가 불편한 장면을 설명하면 AI가 코드를 만들고, 나는 실제로 써 보면서 틀린 곳을 고쳤다. 사진을 보낼지 말지, 버튼을 둘지, 무엇을 자동화하지 않을지는 사람이 정했다. **AI가 코드를 썼고, 그 코드가 내 생활의 한 부분을 덜 번거롭게 만들었다.** 이것도 AI로 될까라는 질문에 대한 내 대답은, 생활의 불편을 구체적으로 설명하고 계속 고칠 수 있다면 된다는 쪽이다. --- ## 제작 기록 이 글의 초고 작성과 자료 정리, 그리고 터널·인증 설정과 확인 절차의 문서화에는 Claude Code가 사용됐다. 시리즈의 제목·본문 윤문과 재구성, 게시용 이미지 편집과 Ghost 드래프트 반영에는 ChatGPT(Codex)가 함께했다. 공개 발행 전 전체 내용과 화면은 사람이 직접 검토하고 수정했다. 본문에는 실제 주소와 정책 이름을 쓰지 않았다. ### [이것도 AI로 될까? — 차 관리 편] 4부 — 기록만 쌓이면 아무도 보지 않는다 URL: https://withkai.io/igeosdo-airo-doelgga-ca-gwanri-pyeon-4bu-girogman-ssahimyeon-amudo-boji-anhneunda/ Last updated: 2026-08-22T14:15:35.000Z > **이번 편의 요청의 요지** > 정비 기록과 주기를 계산해서, 교체할 때가 되면 내가 먼저 확인하지 않아도 알려 줘. ## 기록만 쌓이면 아무도 보지 않는다 사진 한 장으로 정비 기록이 들어가게 됐다. 날짜와 주행거리와 항목이 숫자로 남는다. ![정비 항목별 남은 거리와 상태를 보여 주는 화면 편집본](https://withkai.io/content/images/2026/08/carlog-4-homepage-sanitized-v2.png) *실제 화면의 구조는 유지하고, 주행거리와 날짜·정비 수치는 예시값으로 바꾼 게시용 편집본이다.* 그런데 며칠 지나고 보니 달라진 것이 별로 없었다. 기록은 잘 쌓이는데 내가 그걸 열어 보지 않는다. 열어 보려면 먼저 *지금 뭔가 갈 때가 됐나* 하고 궁금해해야 한다. 궁금해하지 않으면 데이터베이스든 메모장이든 그냥 조용히 있는다. **처음 문제로 돌아온 것이다.** 나는 판정을 넘기고 싶었지 기록만 옮기고 싶었던 게 아니었다. 그래서 AI에게 기록을 쌓는 데서 멈추지 말고, **언제 무엇을 해야 하는지 계산해서 먼저 알려 주는 흐름**을 만들어 달라고 했다. 이번 편의 목표는 입력 자동화가 아니라, 기억과 계산을 내 머리에서 꺼내는 것이었다. ## 취급설명서의 주기표를 그대로 옮겼다 차를 살 때 받은 책에 정비 주기표가 있다. 항목마다 몇 km 또는 몇 개월이라고 적혀 있다. 그걸 그대로 옮겼다. 옮기면서 알게 된 것이 있다. 항목마다 기준이 다르다. 어떤 것은 거리만 있고, 어떤 것은 기간만 있고, 어떤 것은 둘 다 있다. 둘 다 있으면 **먼저 오는 쪽이 기준이다.** 엔진오일은 거리로도 기간으로도 걸린다. 차를 안 타도 오일은 늙는다. 메모장에 적을 때는 이 구분을 하지 않았다. 거리만 적어 두고 기간은 머릿속에 있었다. 그래서 오래 안 탄 해에는 거리가 안 찼다는 이유로 넘어가곤 했다. 또 하나. 표에는 "가혹 조건"이라는 항목이 따로 있었다. 짧은 거리를 자주 타거나, 정체가 많거나, 먼지가 많은 곳을 다니면 주기를 줄이라고 적혀 있다. 그러니까 설명서의 숫자는 하나가 아니라 **조건에 따라 두 벌**이었다. 나는 그동안 앞쪽 숫자만 보고 있었다. ## 내 차는 설명서와 다르게 탄다 엔진오일을 나는 6,000km쯤에서 간다. 설명서의 기본 숫자와 다르다. 짧은 거리를 자주 타고 시내 주행이 많다. 설명서의 기준으로는 가혹 조건에 가깝다. 그래서 이 항목만 내 기준으로 덮어썼다. 미션오일도 마찬가지다. 첫 교체 시점과 그다음 주기를 내가 아는 값으로 넣었다. **주기표를 통째로 옮기는 것보다 이렇게 하나씩 덮어쓸 수 있는 것이 중요했다.** 앞서 메모장에서 힘들었던 것이 바로 이 부분이다. 기준을 바꾸면 그 뒤 계산을 전부 다시 해야 했다. 지금은 숫자 하나만 고치면 나머지는 알아서 다시 계산된다. 그래서 화면에 주기를 고치는 탭을 따로 만들었다. 항목별로 거리와 기간, 며칠 전부터 알릴지를 바꿀 수 있다. 만들 때는 "이걸 자주 쓸까" 싶었는데, 실제로는 주기표를 옮기는 과정에서 내가 가장 많이 쓴 화면이 됐다. ## 알릴 수 없는 항목이 있었다 옮기다 보니 주기를 못 적는 항목이 나왔다. 브레이크 패드, 타이어, 배터리, 와이퍼. 이것들은 몇 km라고 정해 놓기 어렵다. 어떻게 타느냐에 따라 다르고, 결국 **상태를 봐야 아는 것들**이다. 설명서에도 "점검"이라고만 적혀 있지 교체 주기는 없다. 처음에는 적당한 숫자를 넣을까 생각했다. 브레이크 패드는 보통 얼마쯤이라고들 하니까. 그런데 그건 내가 아는 것이 아니라 어디선가 들은 것이다. 그 숫자로 알림이 오면 나는 그게 근거 있는 값인 줄 알고 정비소에 갈 것이다. 그래서 둘을 나눴다. **기록은 남길 수 있지만 알림은 하지 않는 항목**을 따로 뒀다. 패드를 갈았다는 사실은 남는다. 비용도 집계된다. 다만 "이제 갈 때가 됐다"고 시스템이 먼저 말하지는 않는다. 이 구분을 넣고 나서 화면이 조금 복잡해졌다. 항목 목록에 알림 대상과 기록 전용이 섞여 있으니 헷갈린다. 그래서 알림을 켜고 끄는 스위치를 항목마다 뒀다. **모르는 것을 아는 척하지 않게 하는 대신, 그걸 눈에 보이게 만드는 값은 치러야 했다.** ## 화면을 만들었더니 데이터가 안 나왔다 판정 규칙은 PostgreSQL 뷰로 만들어 뒀다. 항목별로 마지막 정비가 언제였는지, 그 뒤로 얼마나 탔는지, 남은 거리와 남은 날짜가 얼마인지를 계산해서 한 번에 보여 준다. 알림이든 화면이든 **이 뷰 하나만 읽으면 되게** 해 두는 것이 목적이었다. 판정 규칙을 바꿀 때 고칠 곳이 한 군데면 좋으니까. 폰에서 볼 화면은 웹앱으로 만들었다. 데이터는 Directus를 앞에 세워 읽는다. 표마다 권한을 걸 수 있어서, 화면 전용 계정을 따로 만들고 필요한 다섯 표에만 읽기 권한을 줬다. 주기를 고치는 표에만 쓰기를 열었다. 그런데 화면을 만들고 열었더니 빈 목록이었다. ![Directus에서 정비 항목을 데이터로 관리하는 화면](https://withkai.io/content/images/2026/08/carlog-4-directus-service-items-sanitized.png) *사용자 화면 뒤에서 Directus가 정비 항목과 표시 이름을 관리하는 모습이다. 내부 레코드 식별자와 사용하지 않는 설정 패널은 게시용 이미지에서 덜어 냈다.* 원인은 Directus 쪽이었다. 원본 표는 권한을 걸어 열어 줄 수 있는데, **SQL 뷰는 그 목록에 나타나지 않았다.** 데이터베이스에 만들어 둔 그 계산 결과를 바깥으로 내보내 주지 않는 구조였다. 한동안 설정을 뒤졌다. 되게 만드는 방법을 찾다가 결국 방향을 바꿨다. **계산을 화면 쪽에서 한 번 더 하기로 했다.** 원본 표 다섯 개를 읽어다가, 뷰가 하던 것과 같은 계산을 자바스크립트로 다시 한다. 같은 계산이 두 곳에 생겼다. 좋은 구조는 아니다. 한쪽만 고치면 화면과 알림이 서로 다른 말을 하게 된다. 그래서 화면 쪽 계산을 별도 파일로 떼어 두고 **시험을 붙였다.** 브라우저와 시험이 같은 파일을 쓰게 해 두고, 실제 값으로 판정 결과를 검사한다. 미션오일이 몇 km 남았는지 같은 것을 숫자로 박아 두었으니, 규칙을 고쳐서 그 값이 달라지면 바로 드러난다. 우회하기로 정했으면 그 우회가 만든 위험도 같이 처리해야 한다. 그러지 않으면 몇 달 뒤에 "화면에는 아직 여유 있다고 나오는데 알림은 왜 왔지" 하고 헤매게 된다. ## 같은 날 기록이 서로를 덮었다 정비 기록을 넣으면 거기 적힌 주행거리가 자동으로 주행거리 이력에도 들어가게 해 뒀다. 명세서에 이미 찍혀 있는 값이니 따로 넣을 이유가 없다. 데이터베이스 트리거로 처리했다. 정비 기록이 들어오면 그 행의 날짜와 주행거리를 주행거리 표에 함께 넣는다. 그런데 정비 기록을 저장했는데 화면의 현재 주행거리가 안 바뀌는 일이 있었다. 65,500km짜리 기록을 넣었는데 화면은 62,300km 그대로였다. 원인은 내가 넣어 둔 규칙이었다. 하루에 여러 번 들어와도 한 줄만 남기려고 날짜에 유일 제약을 걸고, 충돌하면 아무것도 하지 않게 해 뒀다. 그러니까 **같은 날짜에 이미 기록이 있으면 새로 들어온 것을 그냥 버렸다.** "한 줄만 남긴다"는 의도가 "먼저 들어온 것이 이긴다"로 동작하고 있었다. 고치는 방향은 두 가지였다. 나중에 들어온 것이 이기게 하거나, 더 큰 값이 이기게 하거나. **더 큰 값**을 골랐다. 주행거리는 줄어들지 않는다. 그리고 지난 명세서를 뒤늦게 넣는 경우가 있는데, 그때 과거의 작은 값이 현재 값을 끌어내리면 안 된다. 규칙을 고치면서 이미 저장된 기록들도 같은 규칙으로 다시 훑게 했다. 규칙만 고치고 지나가면 **고친 시점 이후의 기록만 맞고 그전 것은 틀린 채로 남는다.** ## API를 붙이면 무엇이 좋아지는지 따져 보았다 여기까지 오니 남은 것은 현재 주행거리였다. 정비를 받을 때마다 갱신되지만 그 사이에는 멈춰 있다. 차에는 통신 기능이 있고 제조사가 개발자용 접속 방법을 공개해 두었다. 등록하면 차의 상태를 프로그램으로 읽어 올 수 있다. 매일 한 번 주행거리를 받아 오면 내가 아무것도 안 해도 판정이 최신으로 유지된다. 그림으로는 깔끔했다. 붙이려고 보니 딸린 것이 많았다. - 인증 절차를 따로 만들어야 한다 - 받은 인증은 만료되므로 갱신하는 장치가 필요하다 - 갱신이 실패하면 알아차릴 방법도 있어야 한다 - 규격이 바뀌면 따라가야 한다 - 가입하고 차량 연동 승인을 받아야 시작할 수 있다 목록을 적어 놓고 보니 이 중 어느 것도 **한 번 만들고 끝나는 것이 아니었다.** 인증 갱신은 계속 돌아야 하고, 규격 변경은 언제 올지 모른다. 만드는 비용보다 데리고 사는 비용이 크다. 고장 나는 방식도 마음에 걸렸다. 이런 연결은 **소리 없이 멈춘다.** 인증이 만료된 날부터 데이터가 안 들어오는데 화면은 멀쩡하다. 마지막으로 받은 값이 그대로 남아 있으니까. 숫자가 이상해 보이지도 않는다. 그냥 어제와 같을 뿐이다. 이걸 만들면서 실제로 비슷한 일을 한 번 겪었다. 코드를 서버로 옮기는 데 쓰던 접속 열쇠에 만료일이 있었다. 그날이 오면 자동으로 돌던 것이 조용히 멈춘다. 화면에는 아무 표시도 없다. 그래서 만료일 없는 방식으로 바꿨다. 같은 종류의 함정을 또 놓고 싶지 않았다. ## 필요한 값은 주행거리 하나뿐이었다 따지다가 판정식을 다시 열어 봤다. 교체 시기를 계산하는 데 들어가는 값은 셋이다. 마지막으로 그 항목을 갈았을 때의 주행거리와 날짜, 그리고 지금 주행거리. 앞의 둘은 내 기록에 이미 있다. **API에서 가져올 것은 지금 주행거리 하나뿐이었다.** 숫자 하나를 자동으로 받자고 인증과 갱신과 규격 추적을 떠안는 셈이다. 그래서 안 붙이기로 했다. 대신 이렇게 한다. 정비를 받으면 명세서에 주행거리가 찍혀 있으니 그때 갱신된다. 그 사이에는 가끔 내가 직접 넣는다. 계기판을 보고 숫자만 보내면 된다. 이 경로는 이미 만들어 둔 것이 있었다. 사진 없이 숫자만 넣는 입력이 필요해서 진작 붙여 뒀는데, 그게 여기서 쓰였다. 그것만으로는 부족한 구석이 있다. 정비소에 안 가는 동안에는 주행거리가 멈춰 있다. 6개월 동안 정비를 안 받고 10,000km를 타면 시스템은 그 사실을 모른다. 그래서 추정식을 하나 만들려고 했다. 기록이 두 개만 쌓이면 하루 평균이 나온다. 마지막 기록에 하루 평균과 지난 날짜를 곱하면 지금을 어림할 수 있다. 실측이 들어올 때마다 평균도 정확해지니 시간이 지나면 나아질 것이다. **만들지 않았다.** 제조사 앱을 다시 열어 보니 주행거리가 그냥 적혀 있었다. 월 주행거리와 월 평균 연비도 있고, 최근 주행 기록을 열두 달까지 볼 수 있고, 내려받을 수도 있었다. 추정은 실측이 없을 때 쓰는 것이다. 실측이 있는데 추정식을 만들면, 나는 그 식을 검증하려고 다시 실측을 찾게 된다. **없는 값을 지어내는 코드를 한 줄이라도 두면 그 값이 맞는지 계속 신경 써야 한다.** 그럴 바에는 숫자를 보고 보내는 편이 정확하고, 하는 일도 적다. 안 만든 것이 다행이었다는 것은 나중에 알았다. 앱에서 주행 기록을 내려받아 열두 달을 세어 봤다. 한 달에 1,182km를 탄 달이 있고 4,296km를 탄 달이 있었다. **3.6배 차이다.** 하루 평균을 곱하는 방식은 이 편차 앞에서 의미가 없다. 더 정확히 말하면, 내가 처음 하루 평균을 재 봤을 때 나온 값은 169km였다. 기록 두 개 사이가 사흘이었기 때문이다. 열한 달로 다시 재니 89km였다. **사흘치로 잰 값이 두 배 가까이 부풀어 있었다.** 그 숫자로 "지금 몇 km쯤"을 계산해서 알림에 실었다면, 나는 그게 틀린 줄도 모르고 정비소에 갔을 것이다. 붙일 자리는 열어 뒀다. 주행거리가 어디서 왔는지 표시하는 칸을 처음부터 만들어 뒀다. 나중에 내려받은 기록을 한꺼번에 넣게 되면 그 칸에 출처만 하나 더하면 된다. 다른 곳은 건드릴 필요가 없다. ## 한 달에 한 번 먼저 말을 건다 이제 마지막 조각이다. 판정한 결과를 내가 찾아가서 보는 게 아니라 먼저 알려 주게 했다. n8n에 워크플로를 하나 더 만들었다. 스케줄로 깨어나서, 알림 대상만 걸러 주는 뷰를 읽고, 보낼 것이 있으면 텔레그램으로 보낸다. 노드 다섯 개짜리다. 기록을 넣는 봇과 같은 채널을 쓴다. 매월 1일 아침에 한 번 확인한다. 교체 시기가 지났거나 다가온 항목이 있으면 이렇게 온다. ``` 🚗 정비 알림 ⚠️ 지금 교체 엔진오일 · 1,200km 초과 🔜 곧 교체 에어클리너 · 800km 남음 📍 주행거리를 34일째 못 받았습니다. /odometer 로 계기판 숫자를 알려주세요. 기준 61,200km · 2026-05-31 ``` 주기를 매일로 하지 않은 데는 이유가 있다. 교체 시기가 지난 항목은 갈기 전까지 계속 걸려 있다. 매일 보내면 같은 문장이 매일 온다. 그러면 읽지 않게 되고, **읽지 않게 되면 정작 필요한 날에도 안 읽는다.** 정비 주기가 수천 km 단위이니 한 달에 한 번이면 놓칠 일도 없다. **보낼 것이 없으면 아무것도 보내지 않는다.** 매달 "이상 없음"을 보내는 것도 같은 이유로 하지 않았다. 조용한 것이 정상이라는 뜻이 되게 했다. 주행거리를 한 달 넘게 못 받았으면 그 이야기도 함께 붙인다. 거리 판정은 그 숫자에 매달려 있는데, 그게 오래됐으면 판정도 그만큼 낡은 것이다. **알림이 스스로 재료를 요구하는 셈이다.** 메시지에 판정 기준이 된 주행거리와 날짜도 함께 적는다. 처음에는 넣지 않았는데, 알림을 받고 "이게 언제 기준이지" 하고 화면을 열어 보게 되더라. 그럴 거면 알림에 적어 두는 게 맞다. ## 자동화의 마지막 한 칸은 비워 둔다 세 편을 통틀어 사람이 하는 일이 둘 남았다. 정비소를 나오면서 명세서를 한 장 찍는 것, 그리고 가끔 계기판 숫자를 알려 주는 것. 둘 다 없앨 수 있었다. 사진은 찍자마자 저장까지 자동으로 이을 수 있었고, 주행거리는 API로 매일 받아 올 수 있었다. 두 번 다 그렇게 하지 않았다. 앞의 것은 읽기가 틀릴 수 있어서다. 한 줄이 통째로 사라지는 것을 봤다. 뒤의 것은 유지 비용 때문이다. 숫자 하나를 위해 계속 돌봐야 하는 장치를 늘리고 싶지 않았다. 만들면서 자꾸 자동화를 늘리려는 쪽으로 손이 갔다. 한 칸이라도 사람이 하는 곳이 남아 있으면 덜 만든 것 같았다. 이번에도 그래서 API 문서를 한참 읽었다. 그런데 다 만들고 보니 **덜어낸 자리가 이 도구를 계속 쓰게 만들고 있다.** 틀린 기록이 조용히 쌓이지 않고, 어느 날 조용히 멈춰 있지도 않다. 고장 나면 내가 알아차릴 수 있는 방식으로 고장 난다. 메모장에 계산을 적던 일은 없어졌다. 그 자리에 사진 한 장과 가끔 보내는 숫자 하나가 들어왔다. 그 정도면 충분히 넘긴 것 같다. 이제 정비 시기를 확인하려고 매번 앱과 메모장을 열지 않는다. 알림이 오면 기준 날짜와 주행거리를 보고 판단하면 된다. **AI가 대신 정비 결정을 내리는 것이 아니라, 내가 결정해야 할 순간을 놓치지 않게 앞에서 알려 주는 것**이다. 이 시리즈에서 가장 크게 달라진 부분도 여기 있다. 사진 한 장으로 기록을 남긴 것이 시작이었다면, 주기를 계산해 알려 주는 순간부터 그 기록이 생활 속 도구가 됐다. 다음 5부에서는 이 도구를 집 밖에서도 써야 한다는 문제가 남는다. 정비소는 집에 없으니, 집 안에서만 되는 시스템은 아직 절반짜리였다. --- ## 제작 기록 이 글의 초고 작성과 자료 정리, 그리고 정비 주기 판정과 알림의 설계·구현·시험에는 Claude Code가 사용됐다. 시리즈의 제목·본문 윤문과 재구성, 게시용 이미지 편집과 Ghost 드래프트 반영에는 ChatGPT(Codex)가 함께했다. 공개 발행 전 전체 내용과 화면은 사람이 직접 검토하고 수정했다. 본문의 주행거리와 항목 숫자는 예시로 바꾼 값이다. ### [이것도 AI로 될까? — 차 관리 편] 3부 — 사진이 실패해도 기록은 남겨야 했다 URL: https://withkai.io/igeosdo-airo-doelgga-ca-gwanri-pyeon-3bu-sajini-silpaehaedo-girogeun-namgyeoya-haessda/ Last updated: 2026-08-22T13:59:33.000Z > **이번 편의 요청의 요지** > 사진을 못 읽는 날에도 버튼 몇 번으로 정비 기록을 끝까지 남길 수 있게 해 줘. ## 사진이 안 되는 날에도 기록은 남아야 했다 앞선 편에서 명세서를 사진으로 찍어 기록하는 이야기를 썼다. 잘 돌 때는 좋다. 문제는 안 돌 때다. 글자를 못 읽는 경우가 있다. 명세서가 구겨졌거나, 손글씨로 적어 준 경우거나, 애초에 종이를 안 받은 경우다. 오일만 갈고 영수증도 없이 나오는 날이 있다. 그럴 때 기록을 못 하면 그 항목은 **다음 계산에서 통째로 빠진다.** 그래서 사진과 무관한 입력 경로를 하나 더 뒀다. 텔레그램 봇이다. 폰에서 메시지 앱을 열고 버튼 몇 번 누르면 끝나는 것. 만들면서 알게 된 것은, 이 "버튼 몇 번"이 사진 경로보다 손이 많이 갔다는 점이다. ![텔레그램 봇이 기록할 대상을 먼저 묻는 시작 화면](https://withkai.io/content/images/2026/08/carlog-3-telegram-start-clean.png) *사용자가 제공한 실사용 캡처. 봇이 먼저 기록 종류를 묻고, 사용자는 버튼으로 다음 단계에 들어간다.* ![텔레그램 봇이 주행거리를 입력받는 화면](https://withkai.io/content/images/2026/08/carlog-3-telegram-odometer-clean.png) *주행거리를 숫자로 받은 뒤 다음 질문으로 넘어가는 흐름이다.* ![텔레그램 봇의 정비 항목 선택 화면](https://withkai.io/content/images/2026/08/carlog-3-telegram-items-clean.png) *정비 항목은 자유롭게 입력하게 두지 않고 버튼 목록으로 보여 준다. 마지막에는 선택 완료와 직접 입력 경로도 둔다.* AI에게 자유롭게 말하면 알아듣는 봇을 시키는 대신, 이번에는 **무엇을 물어볼지와 다음 화면을 미리 정해 달라**고 했다. 사용자가 아무 말이나 잘해야 하는 도구보다, 사용자가 틀릴 자리를 줄이는 도구가 더 필요했기 때문이다. ## 봇을 새로 파야 했던 이유 텔레그램 봇은 이미 하나 쓰고 있었다. 다른 용도로 만들어 둔 것이다. 처음에는 거기에 기능을 얹을 생각이었다. 워크플로를 만들어 올리자 텔레그램 노드 열 개에 **기존 자격증명이 저절로 연결돼 있었다.** 같은 종류의 자격증명이 하나뿐이면 알아서 골라 주는 편의 기능이다. 편리한데, 여기서는 위험했다. ![n8n WF-E 텔레그램 대화형 정비 입력 워크플로](https://withkai.io/content/images/2026/08/carlog-3-n8n-wf-e-3.png) *텔레그램 업데이트를 입력받아 상태를 읽고, 다음 질문과 버튼을 보내고, 확인된 값을 저장하는 흐름을 n8n 워크플로로 나눠 둔 모습이다.* 텔레그램은 **봇 하나에 웹훅 하나**다. 새 워크플로를 그대로 켜면 그 봇의 웹훅 주소가 이쪽으로 바뀐다. 그러면 기존에 쓰던 것은 메시지를 못 받는다. 오류가 나는 것도 아니고 **조용히 멈춘다.** 켜기 전에 알아차린 것이 다행이었다. 봇을 새로 파고 자격증명을 따로 만들어 붙였다. 그리고 워크플로에 커다랗게 메모를 하나 붙여 뒀다. *이 자격증명을 바꾸지 말 것.* 자동으로 채워 주는 값은 대개 편하다. 다만 그게 **무엇을 밀어내고 들어온 값인지**는 봐야 한다. ## 무엇을 물어볼지부터 정했다 대화형은 화면이 없다. 순서가 곧 화면이다. 그래서 무엇을 어떤 순서로 물을지부터 정했다. ``` 주행거리 → 항목 → 비용 → 정비소 → 날짜 → 확인 ``` 주행거리를 맨 앞에 둔 것은 **정비소에서 나오는 순서가 그렇기 때문이다.** 계기판은 차에 타면 바로 보인다. 영수증은 주머니에서 꺼내야 한다. 여기에 지름길을 하나 뒀다. 주행거리만 넣고 싶은 날이 있다. 정비를 받은 게 아니라 그냥 지금 몇 km인지 기록해 두고 싶을 때다. 그때는 명령 하나로 주행거리를 묻고 바로 확인 화면으로 건너뛴다. 나중에 이 지름길이 **꽤 중요해졌다.** 거리 판정이 이 숫자에 매달려 있어서다. 각 단계에는 건너뛰기를 붙였다. 정비소 이름을 모를 수도 있고 금액을 나중에 넣고 싶을 수도 있다. 다만 **주행거리의 건너뛰기는 맨 뒤에 뒀다.** 없으면 판정에 못 쓰는 값이라 누르기 어렵게 만들었다. ## 버튼이 조용히 잘렸다 항목을 고르는 화면이 가장 까다로웠다. 정비 항목이 열넷이고, 여기에 페이지 넘김과 완료와 취소가 붙는다. 텔레그램의 인라인 키보드는 워크플로에서 **버튼 개수를 표현식으로 바꿀 수 없다.** 화면을 만들 때 몇 개짜리인지 고정해 두어야 한다. 그런데 대화 로직은 상황에 따라 다른 개수를 내놓는다. 항목이 열넷이면 한 화면에 다 못 넣으니 페이지를 나눠야 하고, 마지막 페이지는 앞 페이지보다 버튼이 적다. 로직이 내놓은 개수와 화면의 슬롯 수가 어긋나면 **넘치는 것은 그냥 사라진다.** 오류가 나지 않는다. 사용자에게는 "왜 에어클리너가 목록에 없지" 하는 상황으로 보인다. 그래서 슬롯 수를 고정하고, **로직이 항상 그 개수를 채우게** 했다. 빈 자리는 눌러도 아무 일 없는 가운뎃점으로 메운다. 텔레그램이 빈 문자열 버튼을 거부하기 때문에 무언가는 넣어야 했다. 버튼에는 눌렀을 때 서버로 갈 값을 함께 담는데, 여기에도 제한이 있다. **64바이트를 넘으면 텔레그램이 거부한다.** 항목 코드를 그대로 쓰면 한글이 섞였을 때 금방 넘긴다. 그래서 짧은 코드를 따로 두고 그걸 실어 보낸다. ## 대화 중간은 어디에 두나 버튼을 누르면 서버가 깨어나서 다음 화면을 만든다. 그런데 그 서버는 **직전에 무슨 대화를 했는지 기억하지 않는다.** 요청 하나가 끝나면 잊는다. 그래서 대화 중간 상태를 데이터베이스에 뒀다. 작성 중인 기록을 임시 상태로 저장해 두고, 버튼이 눌릴 때마다 그걸 읽어서 다음 단계를 정하고 다시 저장한다. 여기에 규칙을 두 개 걸었다. 하나는 **대화방마다 작성 중인 것은 하나만** 있을 수 있다는 것. 여러 개가 동시에 열려 있으면 어느 것에 값을 넣는지 알 수 없다. 다른 하나는 **오래된 것은 버린다**는 것이다. 대화를 하다 말고 몇 시간 뒤에 돌아와 버튼을 누르면 어떻게 되나. 그때는 이어서 하는 것보다 처음부터 하는 편이 낫다. 자기가 어디까지 입력했는지 이미 잊었을 테니까. ## 저장 직전에 값이 사라졌다 여기서 가장 오래 붙잡은 문제가 나왔다. 확인 화면에서 저장을 눌렀는데 이런 오류가 났다. ``` column "undefined" does not exist ``` 저장 쿼리가 값을 못 받고 있었다. 그런데 그 앞 화면까지는 값이 멀쩡히 있었다. 확인 화면에 날짜도 항목도 다 떠 있었으니까. 원인은 노드 사이를 흐르는 데이터에 있었다. 저장 노드는 상태를 저장하는 노드와 화면을 나누는 분기를 거쳐서 온다. 그런데 **중간의 데이터베이스 노드가 자기 조회 결과로 데이터를 갈아치운다.** 확정 저장 시점에는 그 조회가 아무것도 못 찾아서 0행이었고, 그래서 흐르던 값이 통째로 사라졌다. 빈 값으로 SQL이 조립되니 컬럼 이름 자리에 `undefined`가 들어갔다. 고치는 방법은 간단했다. 바로 앞 노드에서 값을 받지 말고 **이름을 지정해 앞쪽 노드에서 직접 가져오는 것.** 다만 이걸 알아내는 데 시간이 걸렸다. 화면에는 값이 보이는데 저장만 안 되니 엉뚱한 곳을 뒤졌다. 그래서 정적 검사에 규칙을 하나 넣었다. **분기 뒤에 있는 노드가 바로 앞 데이터를 쓰고 있고, 그 앞이 값을 갈아치우는 종류라면 경고한다.** 처음에는 분기 뒤의 모든 참조를 잡게 만들었는데 멀쩡한 것까지 걸려서 조건을 좁혔다. 검사를 만들고는 **일부러 그 형태로 되돌려 봤다.** 안 잡히면 검사가 아니라 장식이다. ## 밑줄 하나로 봇이 멈췄다 메시지를 굵게 쓰려고 HTML 서식을 켜 뒀다. 항목 이름과 정비소 이름을 그 안에 넣어 보냈다. 어느 날 봇이 저장 직전에 아무 반응이 없었다. 서버 기록을 보니 텔레그램이 메시지를 거부하고 있었다. ``` can't parse entities ``` 정비소 이름에 밑줄이 하나 있었다. HTML 서식을 켜면 그런 문자가 태그의 일부로 해석된다. 짝이 안 맞으니 텔레그램은 **메시지 전송 자체를 거부한다.** 사용자에게는 저장 직전에 봇이 먹통이 된 것처럼 보인다. 사람이 쓴 문자열은 전부 한 번 걸러서 넣게 고쳤다. 다만 **버튼에 들어가는 글자는 거르면 안 된다.** 인라인 키보드는 HTML을 해석하지 않아서, 걸러 놓으면 이스케이프한 흔적이 그대로 보인다. 같은 문자열인데 어디에 들어가느냐에 따라 처리가 달라야 했다. 이런 것은 규칙을 안다고 피해지지 않는다. 한 번 겪고 나서야 코드에 자리를 만들어 준다. ## 버튼 개수를 시험이 세게 했다 지금까지 나온 것들에는 공통점이 있다. **올린 뒤에야 드러난다.** 버튼이 잘리는 것, 데이터가 64바이트를 넘는 것, 서식이 깨져 전송이 거부되는 것. 전부 로컬에서 로직만 돌려서는 안 보인다. 그래서 대화 로직을 워크플로 밖으로 빼서 파일 하나에 담고, 그 파일에 시험을 붙였다. 시험이 보는 것은 대화 전이만이 아니다. - 화면마다 로직이 내놓는 **버튼 개수가 슬롯 수와 같은지** - 버튼에 실리는 **값이 64바이트를 넘지 않는지** - 빈 버튼이 섞이지 않는지 - 사진 읽기가 실패했을 때 메뉴로 되돌아가는지 마지막 것은 특히 중요했다. 사진 경로가 실패해도 버튼 경로는 살아 있어야 한다는 것이 이 봇의 존재 이유이기 때문이다. 그래서 읽기가 빈 결과를 낼 때와 아예 죽을 때를 둘 다 시험에 넣었다. 워크플로 안에서 로직만 실행해 보는 기능도 있어서 그것도 썼다. 자격증명이 붙은 노드는 가짜 데이터로 대체되고 코드와 분기만 실제로 돈다. **메시지가 나가지도, 데이터가 바뀌지도 않는다.** 로컬 시험이 못 잡는 것 — 시간대 계산이나 노드 이름 참조 같은 것 — 이 여기서 드러났다. ## 묻는 쪽이 정해져 있으면 헤매지 않는다 지금은 이렇게 쓴다. 정비소에서 나와 차에 타면 계기판이 보인다. 메시지 앱을 열고 봇에게 한 마디 보내면 무엇을 기록할지 묻는다. 주행거리를 치고, 항목을 버튼으로 고르고, 금액을 넣고, 확인을 누른다. 사진 경로보다 손이 몇 번 더 간다. 그런데 **틀릴 일이 없다.** 항목은 목록에서 고르니 표기가 어긋나지 않고, 숫자는 내가 직접 넣으니 잘못 읽힐 일도 없다. 만들기 전에는 대화형이 자유로운 방식이라고 생각했다. 아무 말이나 하면 알아들어 주는 것. 실제로 만들어 보니 정반대였다. **묻는 쪽이 무엇을 물을지 미리 정해 두어야** 쓰는 쪽이 헤매지 않는다. 자유롭게 만들수록 사용자는 "뭐라고 써야 하지"에서 멈춘다. 버튼은 그 제약을 눈에 보이게 만든 것뿐이다. 고를 수 있는 것만 보여 주니 고민할 것이 없다. 사진이 잘 읽히는 날에는 빠른 길을 쓰고, 사진이 실패한 날에는 버튼으로 돌아오면 된다. AI가 만들어 준 것은 대화 상대가 아니라 **기록이 끊기지 않는 우회로**였다. 지금은 정비소에서 영수증을 못 받았거나 사진을 읽지 못해도, 차에 타기 전에 기록을 끝낼 수 있다. 덕분에 다음 계산에서 조용히 빠지는 항목이 줄었다. 다음 4부에서는 기록을 남기는 것만으로는 부족했던 이유를 다룬다. 기록이 쌓여도 내가 열어 보지 않으면 아무 변화가 없었다. 그래서 이번에는 차가 먼저 알려 주게 만들었다. ## 실제 사용 화면 사진이 실패해도 기록이 끊기지 않게 만든 흐름은 실제로 이렇게 보인다. 텔레그램이 질문을 던지고, 사용자가 숫자를 입력하거나 목록에서 고르면, n8n이 다음 화면을 이어 붙인다. --- ## 제작 기록 이 글의 초고 작성과 자료 정리, 그리고 글에 나오는 대화 로직과 시험의 구현에는 Claude Code가 사용됐다. 시리즈의 제목·본문 윤문과 재구성, 게시용 이미지 편집과 Ghost 드래프트 반영에는 ChatGPT(Codex)가 함께했다. 공개 발행 전 전체 내용과 화면은 사람이 직접 검토하고 수정했다. ### [이것도 AI로 될까? — 차 관리 편] 2부 — 사진을 AI에게 보내지 않기로 했다 URL: https://withkai.io/igeosdo-airo-doelgga-ca-gwanri-pyeon-2bu-sajineul-aiege-bonaeji-anhgiro-haessda/ Last updated: 2026-08-22T13:58:47.000Z > **이번 편의 요청의 요지** > 사진을 외부 서비스에 보내지 않고도 필요한 값만 읽어 정비 기록으로 바꿔 줘. ## AI에게 사진을 보내면 더 쉬웠다 1부에서 사진 한 장으로 정비 기록이 들어가게 만든 이야기를 썼다. 그때 AI에게 사진을 보내면 훨씬 간단하게 끝낼 수 있다는 방법도 함께 보였다. 그런데 그 구조에 이르기 전에 **먼저 만들었다가 쓰지 않기로 한 것**이 하나 있다. 처음 계획은 훨씬 단순했다. 텔레그램 봇을 하나 만들고, 명세서 사진을 그 방에 던지면, n8n이 사진을 받아 이미지를 읽을 수 있는 모델에게 보내고, 정리된 결과를 돌려받아 데이터베이스에 넣는다. 요즘 모델들은 이미지를 그대로 이해하니 **글자를 읽는 규칙을 짤 필요조차 없다.** 정규식도, 항목 사전도, 총액 고르는 순서도 필요 없다. 실제로 만들었다. 봇을 파고, 사진 받는 노드를 놓고, 모델에 보내고, 결과를 정리해 저장하는 흐름까지 다 짰다. 돌려 보지도 않고 멈춘 것은 아니다. **구조가 완성된 뒤에 껐다.** 이유는 사진 그 자체에 있었다. AI가 편리한 답을 내놓았지만, 그 답을 그대로 쓰는지는 사람이 결정해야 했다. ## 정비명세서에는 차량번호와 연락처가 찍혀 있다 명세서를 다시 들여다봤다. 위쪽에 차량번호가 있다. 내 이름과 전화번호가 있다. 정비소의 상호와 주소와 사업자등록번호와 대표자 이름이 있다. 아래쪽에는 어떤 부품을 얼마에 갈았는지가 줄줄이 적혀 있다. 내가 필요한 것은 이 중 넷뿐이었다. 날짜, 주행거리, 항목, 총액. 나머지는 **읽을 필요도 없는데 함께 넘어가는 것**들이다. 그리고 이건 내 정보만 담긴 종이가 아니었다. 정비소의 정보도 함께 있다. 내가 동의할 수 있는 것은 내 몫뿐이다. 차량번호와 내 연락처는 내가 감수하겠다고 정할 수 있지만, 남의 사업자등록번호와 대표자 이름은 내가 정할 문제가 아니다. 사진 한 장을 보내는 것과 필요한 값 넷을 보내는 것은 전혀 다른 일이었다. ## 무료 티어의 약관을 읽어 보았다 그래도 편의를 생각하면 아까웠다. 그래서 약관을 읽었다. 무료로 제공되는 쪽에는 이렇게 적혀 있었다. 입력한 내용을 서비스 개선에 쓸 수 있고, **사람이 검토할 수도 있다.** 같은 회사의 유료 요금제 약관에는 그렇게 하지 않는다고 따로 명시돼 있었다. 무료와 유료의 차이가 성능이나 한도가 아니라 **내가 넣은 것을 어떻게 다루는가**에 있었다. 공짜인 이유가 거기 적혀 있는 셈이다. 한도만 보면 무료로도 넉넉했다. 명세서는 한 달에 한두 장이다. 한도의 백분의 일도 안 쓴다. 그런데 그 한두 장에 차량번호와 연락처가 들어 있다. 결제를 붙이면 그 조항에서는 벗어난다. 그 방법도 생각해 봤다. 다만 그러고 나니 내가 왜 이걸 시작했는지를 다시 보게 됐다. 서랍의 종이 몇 장을 정리하겠다고 매달 돈을 내는 것이 맞나. 그리고 돈을 내더라도 **사진은 여전히 남의 서버에 한 번 올라간다.** 약관은 지금 그렇게 적혀 있을 뿐이고, 약관은 바뀐다. 만들어 둔 봇은 지우지 않고 껐다. 언젠가 판단이 바뀔 수도 있으니 구조는 남겨 두기로 했다. ## 집에 있는 것으로 해보기로 했다 집에 NAS가 있다. 사진과 문서를 넣어 두는 용도로 몇 년째 돌고 있다. 늘 켜져 있고, 이미 내 것들이 들어 있다. 여기서 글자를 읽으면 **사진이 집 밖으로 나가지 않는다.** 글자를 읽는 도구는 이미 여럿 공개돼 있다. 성능 비교표도 잘 정리돼 있다. 한글을 잘 읽는다는 것을 고르고, 설치해서 돌리면 된다. 그렇게 생각했다. 비교표에 없던 것이 하나 있었다. **그 도구가 내 CPU에서 도는가.** ## 10년 전 CPU에는 AVX가 없었다 먼저 요즘 많이 쓰는 것을 설치했다. 명령을 치자 한 줄이 나왔다. ``` Illegal instruction ``` 이게 전부였다. 오류 메시지도, 어디서 죽었는지도 없었다. 설정이 잘못됐나 싶어 다시 설치했다. 같은 줄이 나왔다. 다른 버전을 받아도 마찬가지였다. 원인은 CPU였다. 내 NAS는 DS918+이고 그 안에는 인텔 셀러론 J3455가 들어 있다. 2016년에 나온 저전력 칩이다. 이 세대에는 **AVX라는 명령어 묶음이 없다.** SSE4.2까지만 있다. AVX는 여러 개의 숫자를 한 번에 계산하는 명령들이다. 기계학습에는 그런 계산이 많아서, 있으면 몇 배 빠르다. 그래서 요즘 도구들은 대개 **AVX가 있다고 가정하고 미리 빌드된 파일**을 배포한다. 소스를 직접 다시 빌드하면 될 수도 있지만, 그건 그것대로 며칠 걸리는 일이다. 없는 명령을 만나면 프로그램은 설명 없이 그 자리에서 멈춘다. `Illegal instruction`은 정확한 표현이었다. **이 CPU가 모르는 명령이 들어온 것이다.** 오류 메시지가 불친절했던 게 아니라, 메시지를 만들 코드에 닿기도 전에 죽은 것이다. AVX를 요구하지 않는 도구도 있었다. 그건 떴다. 대신 사진 한 장에 수십 초에서 몇 분이 걸렸다. 명세서를 찍고 결과를 기다리는 시간이라고 하기에는 길다. 정비소 앞에서 폰을 들고 1분을 서 있을 수는 없다. **장비가 할 수 있는 일과 내가 하려던 일 사이에 선이 그어졌다.** 이 일이 있고 나서는 새 도구를 붙일 때 성능보다 먼저 그 선을 확인하게 됐다. ## 죽지 않고 도는 것을 찾았다 결국 Tesseract로 돌아갔다. 1985년에 시작해서 지금도 유지되는 물건이다. 순수한 C++로 쓰였고 특별한 명령어를 요구하지 않는다. 요즘 나오는 것들에 비하면 정확도가 떨어진다고 알려져 있다. HTTP로 부를 수 있게 감싼 도커 이미지가 있어서 그걸 NAS 스택에 한 줄 추가했다. 포트는 밖으로 열지 않았다. 같은 도커 네트워크 안의 n8n만 부르면 되니 컨테이너 이름으로 접근한다. 한국어 데이터를 넣고 명세서를 넣었더니 **작은 이미지 한 장을 1초 만에 읽었다.** 정확도도 명세서 정도의 인쇄물에는 쓸 만했다. 한 가지 헷갈리는 것이 있었다. 실행할 때마다 오류 메시지가 함께 나왔다. 요청하지도 않은 번체 중국어 데이터를 열려다 실패하는 메시지였다. 처음에는 이것 때문에 실패한 줄 알았다. 그런데 종료 코드는 정상이고 한글은 제대로 읽혔다. **메시지가 나온다고 실패한 것이 아니었다.** 그 뒤로는 오류 메시지가 아니라 종료 코드로 판정하게 고쳤다. 메시지만 보고 성공 여부를 정했다면 되는 것을 안 된다고 판단하고 다른 길을 찾아 헤맸을 것이다. ## 같은 도구인데 기계마다 다르게 읽었다 읽기는 읽는데 결과가 좀 이상했다. 같은 사진을 맥에서 돌렸을 때와 NAS에서 돌렸을 때의 출력이 달랐다. ``` 맥 엔진오일 66000 NAS 엔 진 오 일 66000 ``` **한글 글자 사이에 공백이 들어갔다.** 같은 도구, 같은 설정, 같은 사진인데 결과가 달랐다. 버전 차이인지 학습 데이터 차이인지는 끝까지 알아내지 못했다. 원인을 모르니 견디는 쪽으로 갔다. 읽어낸 글자에서 공백을 털어낸 다음에 항목 이름을 찾도록 규칙을 고쳤다. 여기에 함정이 하나 있다. 이 방어를 빼도 **맥에서 돌리는 시험은 그대로 통과한다.** 맥에서는 공백이 안 들어가니까. 깨지는 것은 NAS에서뿐이고, 거기서는 아무도 시험을 돌리지 않는다. 시험은 초록불인데 실제로 도는 곳에서만 깨지는 상황이 만들어진다. 그래서 NAS가 내놓은 그 이상한 출력을 시험 자료로 따로 넣어 뒀다. 실제로 겪은 형태를 그대로 남겨 두지 않으면, 나중에 누군가 "이 공백 제거는 왜 있지" 하고 지울 것이다. 그 누군가는 아마 나일 것이다. 더 성가신 것은 사진의 방향이었다. 명세서를 책상에 놓고 찍으면 종이가 눕는다. 이 도구는 방향을 알아서 잡아 주지 않았다. 방향을 추정하는 기능이 따로 있기는 한데, 실제 사진 두 장에서 신뢰도가 들쭉날쭉했다. 네 방향으로 돌려 가며 시도하는 방법도 있지만, 한 번에 5초에서 10초가 걸리는 장비에서 네 번이면 40초다. ## 결국 사진은 주머니 안에서 읽혔다 방향을 맞추는 방법을 찾다가 잠깐 멈췄다. 사진을 찍는 것도 폰이고, 결과를 보는 것도 폰이다. 그 사이에만 NAS가 끼어 있다. 아이폰에는 사진 속 글자를 읽는 기능이 이미 들어 있다. 사진 앱에서 글자를 손가락으로 끌어다 복사할 수 있는 Live Text다. 별도로 설치할 것도 없고, 사진이 기기 밖으로 나가지도 않는다. 나는 그 기능을 **사람이 손으로 쓰는 것**으로만 생각하고 있었다. 그런데 단축어에 같은 것을 부르는 동작이 있다. 그리고 그 아래에는 애플의 Vision 프레임워크가 있다. 확인은 폰이 아니라 맥에서 했다. Vision은 macOS에서도 부를 수 있어서, Swift로 스무 줄짜리 스크립트를 짜서 `VNRecognizeTextRequest`에 한국어와 영어를 지정하고 같은 사진 두 장을 넣었다. **두 장 다 정확히 읽었다.** 날짜도 주행거리도 항목도 나왔다. HEIC를 변환할 필요도 없었고, 종이가 눕게 찍힌 사진도 그대로 읽었다. NAS에서 40초를 들여 해결하려던 방향 문제가 여기서는 문제가 아니었다. 찾던 답이 서버가 아니라 주머니에 있었다. ## 만든 것을 지우지 않고 남겨 뒀다 그러면 NAS에 올린 Tesseract는 헛수고였을까. 지우지 않고 남겨 뒀다. 텔레그램으로 사진을 보내는 경로가 따로 있다. 폰에서 단축어를 쓸 수 없는 상황이거나, 다른 기기에서 급히 넣어야 할 때를 위한 것이다. 그쪽에서는 여전히 NAS가 글자를 읽는다. 그 봇은 결국 처음 계획과 아주 다른 물건이 됐다. AI에게 사진을 보내는 대신, **버튼을 눌러 고르는 대화형**으로 만들었다. 사진을 보내면 Tesseract가 읽어서 화면을 미리 채워 주지만, **못 읽어도 버튼으로 끝까지 갈 수 있다.** 이 순서가 중요했다. 처음에는 사진을 주 경로로 두고 버튼을 예비로 둘 생각이었다. 그런데 그렇게 하면 읽기가 실패한 날에는 기록 자체를 못 한다. 그래서 뒤집었다. **버튼이 본체고 사진은 거들 뿐이다.** 그 봇을 만들면서 겪은 일은 다음 편에서 따로 쓴다. **길이 하나뿐이면 그 길이 막혔을 때 기록 자체가 멈춘다.** 읽기가 실패해도 기록은 남길 수 있어야 한다는 것이 이 시스템의 규칙이 됐다. 그리고 그 규칙이 실제로 한 번 지켜졌다. 단축어가 다섯 번 실패하는 동안에도 텔레그램 쪽은 멀쩡히 돌고 있었다. ## 느린 장비가 설계를 정했다 돌아보면 순서가 뒤바뀌어 있었다. 나는 "어디에 맡길까"부터 정하고 그다음에 방법을 찾고 있었다. 처음에는 남의 서버, 그다음에는 내 서버였다. **기기에서 하는 길은 끝에 가서야 보였다.** 느린 장비가 아니었다면 첫 번째 도구가 그냥 돌았을 것이고, 나는 거기서 멈췄을 것이다. NAS가 사진을 받아 글자를 읽고 결과를 돌려주는 구조로 만들었을 것이다. 그것도 동작은 한다. 다만 사진이 한 번 더 이동하고, 서버가 꺼져 있으면 기록도 못 한다. 지금 구조에서는 사진이 기기를 떠나지 않는다. 글자만 떠난다. 그리고 그 글자에는 이미 필요한 것만 남아 있다. 제약이 답을 좁혀 준 셈이다. **할 수 없는 것이 명확하면 할 수 있는 것을 정확히 보게 된다.** 요즘 도구를 다 쓸 수 있었다면 나는 아마 가장 편한 것을 골랐을 테고, 사진은 지금도 어딘가로 올라가고 있었을 것이다. 결과적으로 편해진 것은 AI를 덜 쓰게 된 것이 아니다. 사진은 폰 안에 남겨 두고, 필요한 글자만 다음 단계로 넘기게 됐다. 정비소에서 찍은 사진을 나중에 지워도 기록은 남고, 기록을 만들기 위해 개인정보 전체를 외부 서비스에 맡길 필요도 없어졌다. AI에게 모든 일을 맡기는 대신, **어디까지 맡길지 내가 정하는 구조**가 된 것이다. 생활 도구에서 편리함과 개인정보 보호가 부딪힐 때 무엇을 선택할지 알게 된 것도 이 편의 결과다. 다음 3부에서는 사진 자체가 실패하는 날을 다룬다. 읽기가 안 되더라도 기록이 멈추지 않게 하려면, AI가 알아서 이해하는 대화보다 사람이 고를 수 있는 버튼이 필요했다. --- ## 제작 기록 이 글의 초고 작성과 자료 정리, 그리고 글에 나오는 읽기 도구의 설치와 시험, 규칙 구현에는 Claude Code가 사용됐다. 시리즈의 제목·본문 윤문과 재구성, 게시용 이미지 편집과 Ghost 드래프트 반영에는 ChatGPT(Codex)가 함께했다. 공개 발행 전 전체 내용은 사람이 직접 검토하고 수정했다. 약관 내용은 확인 시점 기준이며 특정 서비스를 지목하지 않았다. ### [이것도 AI로 될까? — 차 관리 편] 1부 — 사진 한 장으로 정비 기록하기 URL: https://withkai.io/igeosdo-airo-doelgga-ca-gwanri-pyeon-1bu-sajin-han-jangeuro-jeongbi-giroghagi/ Last updated: 2026-08-22T13:58:10.000Z > **이번 편의 요청의 요지** > 정비명세서 사진 한 장에서 날짜·주행거리·항목·총액을 뽑아 정비 기록으로 바꿔 줘. ![아이폰 단축어의 텍스트 추출·확인·저장 흐름을 보여 주는 편집본](https://withkai.io/content/images/2026/08/carlog-1-shortcut-sanitized.png) *단축어의 실제 흐름을 보여 주기 위해 주소 영역을 제거한 게시용 편집본이다.* ## 사진 한 장이면 끝날 줄 알았다 정비소에서 나와 명세서 사진 한 장을 찍으면 기록이 남고, 나중에 교체할 때가 되면 알려 주는 도구를 만들고 싶었다. 예전 같으면 날짜와 주행거리를 메모장에 옮겨 적고, 다음 교체 시점을 다시 계산했을 것이다. 그래서 AI에게 먼저 이렇게 설명했다. **명세서를 사진으로 찍으면 알아서 기록되게 해 줘.** 글자를 읽는 기능은 폰에 이미 있고, 읽은 글자에서 날짜와 숫자를 골라내는 것도 어려운 일 같지 않았다. 딸깍 한 번, 뚝딱 나올 것처럼 보였다. 결론부터 쓰면 딸깍은 없었다. 그런데 **AI가 만든 코드가 문제였던 것도 아니다.** 코드는 정말 빨리 나왔고, 대체로 맞았다. 시간을 잡아먹은 것은 그 코드가 내 폰과 내 명세서와 내 생활 방식에 맞는지 확인하는 일이었다. ## 주기를 바꿀 때마다 계산을 다시 했다 먼저 왜 이걸 하려 했는지부터. 엔진오일은 6,000km마다 간다. 에어클리너는 40,000km다. 항목마다 주기가 다르니 다음 교체 시점도 제각각이다. 나는 그걸 아이폰 메모장에 적어 두고 있었다. 정비를 받으면 날짜와 주행거리를 적고, **다음은 몇 km일지 계산해서 함께 적었다.** 기아 앱도 정비 주기를 알려준다. 이 글을 쓰면서 다시 열어 보고 알았는데, 주기를 내 기준으로 바꾸는 것도 된다. 협력 정비망에서 정비를 받으면 이력이 자동으로 들어오고, 가장 최근 이력을 기준으로 다음 시점을 다시 계산해 준다. **내가 만들려던 것의 절반은 이미 있었다.** 그런데 앱에 넣을 수 있는 항목이 여섯 가지다. 엔진오일과 필터, 에어클리너, 에어컨 필터, 브레이크오일, 냉각수. 내가 챙기는 것은 열네 가지다. 미션오일도 점화플러그도 브레이크 패드도 칸이 없고, **LPG 차인데 LPG 연료필터가 없다.** LPG 연료필터는 사정이 한 겹 더 있다. 이력이 자동으로 들어오는 조건은 **제조사 협력 정비망에서 정비를 받는 것**인데, 그곳에서는 LPG 계통을 잘 다루지 않는다. 직영 사업소는 예약이 밀려 시간이 오래 걸린다. 그래서 LPG를 다루는 정비소나 택시 정비를 하는 곳을 찾아가게 된다. **앱에 칸이 없는 데다, 있었어도 자동으로 채워질 수 없는 자리에 있는 항목이다.** 손으로 넣을 때 적을 수 있는 것도 교체일자와 그때의 누적거리, 둘뿐이다. 정비소도 금액도 들어가지 않는다. 그래서 메모장이 남아 있었다. 앱이 못 해서가 아니라, **앱이 다루는 여섯 가지 밖에 내 차의 나머지가 있어서였다.** 문제는 주기를 바꿀 때 드러났다. 한 항목의 기준을 바꾸면 그 뒤의 계산을 전부 다시 해야 한다. 6,000km를 5,000km로 낮추면 다음 시점도, 그다음 시점도 달라진다. 항목이 열 개가 넘으니 한 번 손대면 한참 걸린다. 게다가 메모장은 내가 적은 것만 알고 있다. 정비소에서 두 가지를 한꺼번에 갈면 두 줄을 적어야 하고, 한 줄을 빠뜨리면 그 항목만 조용히 계산에서 빠진다. **빠진 것은 눈에 띄지 않는다.** 기록이 없어서가 아니었다. 판정을 손으로 하고 있어서 생기는 일이었다. 계산을 넘기려면 먼저 기록이 숫자로 들어가 있어야 한다. 메모장에 적은 문장은 사람만 읽을 수 있다. ## 첫 판은 정말 금방 나왔다 명세서 사진 두 장을 놓고 시작했다. 터미널에서 Claude Code를 띄우고 무엇을 만들 건지 설명했다. 읽어낸 글자에서 날짜를 찾고, 주행거리를 찾고, 정비소 이름을 찾고, 금액과 항목을 뽑아내는 코드. 이건 한 시간이 안 걸렸다. 정규식 몇 개와 후보 중에서 고르는 규칙 몇 줄이다. 실물 두 장을 넣어 보니 **전부 정답으로 나왔다.** 날짜도, 주행거리도, 다섯 개 항목도. 서버 쪽도 금방 붙었다. 집 NAS에 n8n을 띄워 두고 쓰고 있어서, 웹훅으로 글자를 받아 방금 만든 규칙을 돌리고 결과를 돌려주는 흐름을 하나 만들었다. 데이터베이스는 그 안에서 PostgreSQL로 넣는다. 여기서 끝날 줄 알았다. 폰에서 사진만 넘겨주면 되는 일이었다. 그런데 아이폰에서 돌리자 아무것도 안 됐다. ## 다섯 번을 고쳤고 매번 같은 곳에서 끝났다 아이폰 단축어로 만들었다. 사진을 고르고, 글자를 읽고, 서버로 보내는 세 단계다. 첫 판은 사진 대신 파일 이름을 읽었다. 고른 동작이 이미지에서 글자를 읽는 것이 아니라 입력에서 텍스트를 꺼내는 것이었다. 이름이 비슷해서 잘못 골랐다. 동작을 바꿨다. 둘째 판은 아무 반응이 없었다. 셋째 판은 실행하자마자 목록으로 돌아갔다. 읽은 결과에서 항목 이름을 정규식으로 찾아 곧장 보내게 했는데, 하나도 못 찾으면 다음 단계로 가기 전에 끝나 버리는 구조였다. 그래서 넷째 판에서는 그 자동 매칭을 걷어내고 사람이 고르게 했다. 그래도 안 됐다. 다섯째 판에서는 사진을 글자 읽는 동작에 **명시적으로 연결**했다. 맥의 단축어 파일을 열어 연결이 실제로 걸린 것까지 확인했다. 그런데도 결과는 같았다. 다섯 번을 고치는 동안 매번 다른 곳을 손댔다. 매번 같은 곳에서 끝났다. 화면에는 아무 단서도 남지 않았다. **실패했다는 것만 알 수 있었다.** 이때쯤 AI에게 물어봐도 소용이 없다는 것을 알았다. 내가 보고할 수 있는 것이 "안 됩니다"뿐이었기 때문이다. 무엇이 어디까지 갔다가 멈췄는지를 말해 줄 수 없으면 상대가 사람이든 아니든 답이 나오지 않는다. ## 범인은 코드가 아니라 기본값이었다 의심이 엉뚱한 데로 갔다. 이 명세서를 애초에 읽을 수 없는 것 아닐까. 종이는 구겨져 있고 표는 빽빽하다. 글자도 작다. 그래서 문제를 반으로 잘랐다. 단축어를 통째로 걷어내고, **맥에서 인식 엔진만 직접 불러 보기로 했다.** 아이폰 단축어의 「이미지에서 텍스트 추출」과 사진 앱의 Live Text는 같은 것을 쓴다. 애플의 Vision 프레임워크다. 그리고 그건 맥에서도 부를 수 있다. Swift로 스무 줄짜리 스크립트를 하나 짰다. 사진 경로를 받아 `VNRecognizeTextRequest`에 한국어와 영어를 지정해 넘기고, 읽어낸 줄을 그대로 출력하는 것이 전부다. ``` swift vision-ocr.swift 명세서.HEIC ``` **두 장 다 정확히 읽었다.** 사진이 눕게 찍혀 있었는데도 그대로 읽었다. HEIC를 변환할 필요도 없었다. 읽을 수 있다는 것이 확인되자 남은 문제는 하나로 좁혀졌다. **사진이 그 동작까지 가지 못하고 있다.** 이 스크립트는 그 뒤로도 계속 썼다. 새 정비소의 명세서를 만나면 아이폰을 꺼내 단축어를 돌려 보는 대신, 맥에서 이걸 먼저 돌려 본다. 읽히는지, 규칙이 그 글자를 소화하는지 몇 초 만에 안다. 맥에서 같은 단축어를 열어 동작마다 무엇이 들어가는지 하나씩 눌러 봤다. 글자를 읽는 동작의 입력 칸에서 멈췄다. 목록이 이렇게 있었다. - 클립보드 - 현재 앱 - 현재 날짜 - 기기 세부사항 - 단축어 입력 **기본으로 선택된 것은 클립보드였다.** 사진을 넘겨준 적이 없었던 것이다. 글자를 읽는 동작은 처음부터 정상으로 돌고 있었다. 다만 사진이 아니라 클립보드를 읽고 있었다. 칸을 「단축어 입력」으로 바꾸자 곧바로 명세서 글자가 나왔다. 다섯 번의 실패가 설정 하나로 설명됐다. 돌아보면 나는 계속 코드를 의심하고 있었다. 정규식을 고치고 흐름을 바꾸고 동작을 지웠다 다시 넣었다. 정작 봐야 했던 것은 그 동작이 **무엇을 입력으로 받고 있는지**였다. ## 같은 OCR인데 기기마다 결과가 달랐다 글자가 읽히기 시작하자 다음 문제가 나왔다. 맥에서 읽은 결과와 아이폰에서 읽은 결과가 달랐다. 같은 회사의 같은 기능이다. 그런데 줄이 나뉘는 자리가 다르고, 항목이 나오는 순서도 다르고, **맥이 통째로 놓친 줄을 아이폰은 읽었다.** 오일필터 한 줄이 맥에서는 아예 없었다. 이건 꽤 뜻밖이었다. 나는 개발할 때 맥에서 시험하고 있었다. 맥 기준으로 규칙을 짜면 **정작 손에 들고 쓰는 기기에서 어긋난다.** 그래서 실기기에서 나온 글자를 그대로 받아 두고, 그것을 기준으로 규칙을 다시 맞췄다. 읽어낸 글자에는 또 다른 성격이 있었다. 품목 이름과 금액이 다른 줄에 떨어져 나오고, 순서도 눈에 보이는 순서가 아니었다. 어떤 항목은 자기 금액보다 뒤에 나왔다. 표를 표로 읽어 주는 것이 아니라 **눈에 보이는 덩어리마다 끊어서** 내보내기 때문이다. 그래서 항목별 금액은 뽑지 않기로 했다. 짝을 맞출 근거가 없는데 맞추면 틀린 금액이 조용히 들어간다. 실제로 규칙을 잘못 짰을 때 부품번호 가운데 여섯 자리를 떼어 금액으로 읽은 적이 있다. 필요한 것은 항목 목록과 총액이었고, 그 둘은 확실하게 뽑을 수 있었다. **기능을 하나 덜어낸 셈인데, 덜어내고 나니 틀릴 일도 없어졌다.** 숫자를 고르는 일도 까다로웠다. 명세서 아래쪽에는 보증 안내가 붙어 있다. "주행거리 20000km 이내" 같은 문장이다. 규칙을 처음 짰을 때 그 숫자를 주행거리로 읽었다. 지금은 조건을 뜻하는 말이 같은 줄에 있으면 그 줄을 통째로 버린다. 금액도 마찬가지다. 어떤 양식은 부품값과 공임을 더한 소계를 먼저 적고 부가세를 뒤에 붙인다. 소계를 총액으로 잡으면 실제로 낸 돈보다 적게 기록된다. 그래서 총계, 합계, 소계 순으로 찾고 먼저 만나는 것을 쓴다. **읽는 것보다 무엇을 읽지 않을지 정하는 데 시간이 더 들었다.** ## 저장이 안 돼서 설계를 바꿨다 읽기가 끝나자 화면에 요약이 떴다. 남은 것은 저장 버튼 하나였다. 여기서 또 막혔다. 처음 설계는 이랬다. 웹훅이 둘이다. 하나는 읽기용, 하나는 저장용. 읽기 쪽이 결과를 돌려주면 단축어가 그걸 필드별로 꺼내서 저장 요청의 본문을 다시 조립해 보낸다. 날짜는 날짜 칸에, 주행거리는 주행거리 칸에. 그런데 단축어에서 그 조립이 되지 않았다. 「URL 콘텐츠 가져오기」의 본문 형식을 고르는 자리에서 막혔다. JSON을 골라 필드를 하나씩 넣어야 하는데 원하는 대로 되지 않았고, 그 상태로 저장을 누르면 **본문이 빈 채로** 나갔다. n8n 실행 기록을 열어 보면 요청은 도착해 있는데 몸통이 비어 있다. 서버는 "주행거리나 항목 중 하나는 있어야 한다"며 거절했다. 요약은 화면에 잘 떠 있는데 저장만 안 되는 상황이었다. 한동안 그 조립을 되게 만들려고 애썼다. 그러다 방향을 바꿨다. **조립을 없애면 되는 것 아닌가.** 읽어낸 결과는 이미 서버가 갖고 있다. 그러니 단축어가 그걸 다시 보낼 이유가 없다. 같은 곳으로 **"저장해줘"라는 표시 하나만** 더 보내면, 서버가 다시 읽고 저장까지 하면 된다. 고치고 나니 저장 동작은 **읽기 동작을 복제해서 필드 하나를 더한 것**이 됐다. 단축어에서 조립할 것이 없어졌다. 막혔던 설정을 넘을 필요도 없어졌다. 같은 사진을 두 번 읽으니 낭비 아니냐고 할 수 있다. 한 번 더 읽는 데 1초가 안 걸린다. 그 1초로 **단축어에서 틀릴 수 있는 자리를 통째로 없앴다.** 나쁘지 않은 거래였다. ## 화면에 아무것도 안 뜨는 날도 있었다 그다음에는 요약이 화면에 뜨지 않았다. 서버 기록을 보면 응답은 정상이었다. 날짜도 항목도 다 들어 있었다. 그런데 폰에서는 빈 화면이었다. 원인은 응답의 `Content-Type`이었다. 내용은 정상적인 JSON인데 그것이 JSON이라고 알려주는 헤더가 빠져 있었다. 그래서 단축어는 그걸 **그냥 긴 문자열**로 받았고, 「사전 값 가져오기」로 날짜나 항목을 꺼내려 하니 아무것도 안 나왔다. n8n의 응답 노드에서 헤더를 한 줄 명시하자 곧바로 값이 잡혔다. 여기서 하나 더 배웠다. 응답 본문을 만들 때 `JSON.stringify()`로 문자열을 만들어 내보내고 있었는데, 객체를 그대로 넘기는 편이 안전했다. 문자열로 나가면 받는 쪽이 그걸 다시 풀어야 하고, 그 과정에서 방금 같은 일이 생긴다. 비슷한 것이 또 있었다. 확인 화면의 제목 자리에 요약을 넣었는데, 화면에 `summary`라는 **글자가 그대로** 떴다. 변수 칩을 넣어야 할 자리에 변수 이름을 타이핑한 것이다. 단축어 편집기에서는 둘이 비슷해 보인다. 셋 다 코드의 문제가 아니었다. 어느 것도 오류로 잡히지 않고, 그냥 조용히 이상하게 동작했다. **화면을 직접 보지 않으면 알 수 없는 종류였다.** ## 읽어낸 뒤에도 사람이 봐야 했다 한 줄이 통째로 사라질 수 있다는 것을 안 이상 저장까지 자동으로 이을 수 없었다. 지금은 이렇게 동작한다. ``` 사진 → 글자 읽기 → 구조로 바꾸기 → 확인 → 저장 ``` 읽은 결과를 먼저 화면에 보여 준다. ``` 2026-00-00 · 00,000km 예시 정비소 · 000,000원 엔진오일, 오일필터, 에어클리너 ``` ![저장 전에 확인하는 값의 예시 화면. 실제 값 대신 예시값을 넣은 편집본](https://withkai.io/content/images/2026/08/carlog-1-confirmation-example.png) *실제 명세서와 화면의 식별정보를 제거한 예시 화면이다. 저장 전에 날짜·거리·정비소·금액·항목을 사람이 확인한다.* 화면에 무엇을 띄울지도 정해야 했다. 읽어낸 것을 다 보여 주면 사람은 읽지 않는다. 스무 줄이 뜨면 그냥 넘긴다. 그래서 저장될 값만 세 줄로 줄였다. 못 읽은 칸을 어떻게 할지가 더 어려웠다. 그럴듯한 값으로 채우면 화면은 깔끔해진다. 대신 **확인이 무의미해진다.** 채워진 값을 보면 사람은 맞다고 생각하고 넘긴다. 그래서 못 읽은 것은 빈 칸으로 두고, 무엇을 못 읽었는지 아래에 적기로 했다. 화면이 지저분해지는 쪽을 골랐다. 특히 날짜를 못 읽었을 때가 위험하다. 지난 명세서를 뒤늦게 넣는 경우가 있는데, 날짜를 못 읽으면 오늘 날짜로 들어간다. 그러면 과거 기록이 최신 기록 행세를 하고, 주행거리 판정이 통째로 어긋난다. 화면에서 날짜 한 줄만 봐도 그건 막을 수 있다. ![저장 완료 알림만 남긴 편집본](https://withkai.io/content/images/2026/08/carlog-1-save-complete.png) *저장 완료 알림 영역만 남긴 편집본이다. 아래 명세서와 사진 앱의 주소·식별정보는 제거했다.* ## 틀릴 자리를 미리 잡아 두었다 읽기 규칙은 앞으로도 계속 고치게 된다. 새 정비소의 명세서는 양식이 다르고, 다르면 규칙이 어긋난다. 한 장을 고치다 다른 장을 깨뜨리는 일이 실제로 있었다. 그래서 실물 명세서에서 나온 글자를 텍스트 파일로 그대로 저장해 두고, 규칙을 고칠 때마다 전부 돌린다. 기대값은 문서를 눈으로 읽어 적었다. 맥에서 나온 글자와 아이폰에서 나온 글자를 **둘 다** 넣었다. 한쪽만 넣으면 다른 쪽에서 깨지는 것을 못 잡는다. ``` node tools/test-parse.js ``` 시험 도구는 따로 쓰지 않았다. Node로 짠 스크립트 하나가 기대값과 결과를 맞춰 보고 틀린 줄만 빨갛게 찍는다. 항목별 금액의 합이 문서의 부품액과 맞는지까지 본다. 여기에 하나를 더 붙였다. 서버에서 도는 코드는 워크플로 안에 문자열로 들어가 있다. 그래서 **파일을 고치고 워크플로에 옮겨 넣는 것을 잊으면**, 시험은 원본을 돌리고 실제로는 옛 사본이 도는 상황이 된다. 초록불인데 깨져 있는 것이다. 그래서 옮겨 넣는 것도 스크립트로 만들고, 둘이 어긋나면 잡아내는 검사를 파이썬으로 하나 짰다. 지금은 그 검사기가 도커 설정과 nginx 설정, HTML까지 훑는다. **직접 데어 본 것마다 검사를 하나씩 늘렸다.** 한 번은 시험을 만들어 놓고 일부러 규칙을 망가뜨려 봤다. 여섯 가지를 시도했는데 **그중 하나가 통과했다.** 검사하고 있다고 믿었던 부분이 실은 아무것도 보고 있지 않았다. 시험 자료로 넣은 부품번호가 우연히 열 자리여서, 규칙을 풀어도 결과가 같았던 것이다. 시험이 초록불이라는 것과 시험이 무언가를 검사하고 있다는 것은 다르다. 그것을 확인하는 방법은 **일부러 깨뜨려 보는 것뿐이다.** ## AI가 만든 것은 코드보다 기록하는 습관이었다 이제 정비소에서 나오면서 명세서를 한 장 찍는다. 그게 전부다. 날짜와 주행거리와 항목이 숫자로 들어간다. **메모장에 옮겨 적고 다음 교체 시점을 계산하던 일은 사라졌다.** 처음 생각한 것과 결과는 비슷하다. 걸린 시간이 달랐을 뿐이다. 코드는 정말 빨리 나왔다. 글자에서 날짜와 금액을 뽑아내는 부분은 한 시간이 안 걸렸고 처음부터 잘 돌았다. 그 뒤에 시간을 잡아먹은 것들을 세어 보면 이렇다. 목록에서 기본으로 선택돼 있던 항목 하나, 맥과 아이폰의 출력 차이, 조립이 막혀서 바꾼 설계, 응답에 빠져 있던 형식 표시 한 줄. **어느 것도 코드를 잘 짜서 해결되는 문제가 아니었다.** 전부 화면을 열어 하나씩 눌러 보고, 실제 기기에서 돌려 보고, 되는 것과 안 되는 것을 갈라 봐야 알 수 있는 것들이었다. 문제를 반으로 자른 것이 결국 도움이 됐다. 단축어를 걷어내고 글자 인식만 따로 돌려 보자 "읽을 수 있는가"와 "사진이 거기까지 가는가"가 갈라졌다. 앞엣것은 처음부터 문제가 아니었다. 그걸 확인하기 전까지는 두 문제가 한 덩어리로 보였고, 그래서 엉뚱한 곳을 다섯 번 고쳤다. 바이브 코딩이라는 말이 만드는 오해가 있다면, 말로 설명하면 결과까지 나온다는 부분일 것이다. 실제로 나온 것은 **코드까지**였다. 그 코드가 내 폰에서, 내 명세서로, 내가 원하는 방식으로 도는 것은 그다음 일이었고 시간은 대부분 거기에 들었다. 그래서 이 일을 하며 가장 많이 한 것은 코드를 쓰는 일이 아니었다. 단축어 동작을 하나씩 눌러 보고, 서버의 실행 기록을 열어 요청 본문이 비었는지 확인하고, 인식 엔진만 따로 돌려 보고, 일부러 규칙을 망가뜨려 시험이 반응하는지 보는 일이었다. 만든 것들을 세어 보면 읽기 규칙 하나에 **시험 스크립트 넷과 검사기 하나**가 붙어 있다. 기록을 넣는 길도 사진 하나만 두지 않았다. 텔레그램 봇으로 버튼을 눌러 넣는 경로를 따로 만들어 뒀다. 단축어가 다섯 번 실패하는 동안에도 그쪽은 멀쩡히 돌고 있었다. 왜 그런 길을 두 개 두었는지, 그리고 사진을 왜 남의 서버로 보내지 않았는지는 다음 편에서 쓴다. 아직 계산을 넘긴 것도 아니다. 그건 4부에서 다룬다. 다만 계산에 넣을 재료를 손으로 옮겨 적는 일은 없어졌다. AI가 대신한 것은 정비 판단이 아니었다. 내가 무엇을 기록할지 정하고, AI에게 첫 코드를 시키고, 실제로 틀리는 자리를 확인하면서 **매번 하던 귀찮은 일을 한 단계 줄인 것**에 가깝다. 다음 2부에서는 더 쉬워 보였던 방법을 왜 일부러 포기했는지 쓴다. 명세서 사진을 AI에게 보내면 더 빨리 끝날 텐데, 그 사진에는 보내고 싶지 않은 정보도 함께 찍혀 있었다. --- ## 제작 기록 이 글의 초고 작성과 자료 정리, 그리고 글에 나오는 읽기 규칙의 구현과 시험, 단축어와 서버 연결의 설계에는 Claude Code가 사용됐다. 정비 기록을 넣고 보관하는 구조도 같은 방식으로 만들었다. 시리즈의 제목·본문 윤문과 재구성, 게시용 이미지 편집과 Ghost 드래프트 반영에는 ChatGPT(Codex)가 함께했다. 공개 발행 전 전체 내용과 화면은 사람이 직접 검토하고 수정했다. 본문의 주행거리와 금액, 정비소 이름은 예시로 바꾼 값이다. 삽입 이미지는 실제 화면을 바탕으로 개인정보와 운영값을 제거한 편집·재구성본이다. ### 『탄만집』의 두 글자는 어디로 사라졌을까 URL: https://withkai.io/tanmanjib-yi-du-geuljaneun-eodiro-sarajyeosseulgga/ Last updated: 2026-08-08T14:53:28.000Z Windows에서는 보이는데 Mac과 iPhone에서는 보이지 않는 글자가 있었다. 한국고전번역원의 뉴스레터에서 이용휴의 문집 『탄만집』을 읽다가 발견한 일이었다. 한글로 적은 ‘탄만집’은 아무 문제가 없었지만, 한자로 쓴 서명은 Mac에서 두 칸이 빠진 것처럼 보였다. 같은 메일을 iPhone에서 열어 보니 결과는 더 분명했다. 책 제목은 `탄만집(□□集)`처럼 표시됐다. > 『탄만집(𢾡𢿜集)』 보이지 않던 글자는 `𢾡`과 `𢿜`이었다. ![iPhone에서 확장 한자 두 글자가 네모로 표시된 화면](https://withkai.io/content/images/2026/08/iphone-missing-hanja.jpg) *해결 전 iPhone 화면. 글자가 삭제된 것이 아니라, 해당 문자를 그릴 글꼴이 없어 네모 두 칸으로 표시됐다.* 처음에는 복사하는 과정에서 글자가 깨졌거나 블로그가 문자를 제대로 저장하지 못한 것으로 생각했다. 마침 AI와 블로그를 함께 손보던 중이어서 Codex 5.6 Sol Light에게 원인을 물었다. ## AI의 첫 번째 답은 정답이 아니었다 AI가 처음 세운 가설은 문자 인코딩이었다. 두 글자는 흔히 사용하는 한자보다 훨씬 뒤쪽의 유니코드 영역에 들어 있기 때문에, 데이터베이스가 4바이트 UTF-8을 지원하지 않으면 물음표로 바뀔 수 있다는 설명이었다. 그럴듯했다. 하지만 한 가지가 걸렸다. > “Windows 컴퓨터에서는 보이는데 Mac에서는 안 보여. iPhone에서도 두 글자가 네모로 나와.” 내가 이 사실을 다시 알려주자 조사 방향이 바뀌었다. 같은 자료의 같은 문자가 Windows에서는 정상인데 Mac과 iPhone에서만 네모로 보인다면, 저장된 글자가 망가진 것보다는 운영체제마다 대신 선택하는 글꼴이 다른 쪽이 훨씬 그럴듯했다. iPhone의 네모 두 칸은 실패 화면이면서 동시에 중요한 진단 자료였다. AI는 처음 답을 밀어붙이지 않고 가설을 수정했다. 나도 ‘AI가 알아서 해결해 주겠지’라고 기다린 것이 아니라, 서로 다른 환경에서 확인한 결과를 다시 전달했다. 이번 문제에서 결정적인 단서는 코드보다 그 짧은 관찰이었다. ## 두 글자의 정체 조사 결과 두 글자는 모두 ‘CJK 통합한자 확장 B’에 속했다. - `𢾡`: U+22FA1 - `𢿜`: U+22FDC 확장 B는 드물거나 역사적인 한자를 많이 담고 있는 유니코드 영역이다. 일반적인 한글·한자 글꼴이 이 영역의 수만 글자를 모두 포함하기는 어렵다. Windows는 이런 문자를 만났을 때 `SimSun-ExtB` 같은 확장 한자 글꼴을 대신 불러올 수 있다. 반면 내가 사용한 Mac과 iPhone에는 두 글자를 가진 기본 글꼴이 없었다. 글자가 사라진 것이 아니라, 그 모양을 그려 줄 글꼴이 없었던 것이다. ## 후보 글꼴을 하나씩 확인했다 여기서 바로 아무 한자 글꼴이나 설치하지는 않았다. 한문 고전을 다루는 글이므로 글자의 범위뿐 아니라 자형과 라이선스도 중요했다. 먼저 Noto Sans CJK KR을 확인했다. 이름처럼 한국어와 한자를 폭넓게 지원하지만, 실제 글꼴 파일에는 `𢾡`과 `𢿜`이 모두 없었다. BabelStone Han은 더 많은 확장 한자를 지원했지만 `𢾡`만 있고 `𢿜`은 없었다. 중국 본토 자형을 중심으로 한다는 점도 한국 고전 문헌의 보조 글꼴로는 아쉬웠다. HanaMinB는 두 글자를 모두 지원했다. 최종 테마에는 HanaMinB 전체를 웹폰트로 넣지 않고, 필요한 두 글자의 윤곽을 화면 표시용 SVG로 생성하는 보조 소스로 사용했다. 마지막으로 선택한 글꼴은 Jigmo였다. Jigmo는 확장 B부터 최신 확장 영역까지 폭넓게 담고 있고, 배포 파일 안에 CC0 1.0 라이선스 원문도 함께 제공한다. 수정, 서브셋 제작, 웹 배포가 가능하다는 점을 확인할 수 있었다. ## Mac에서 해결하는 방법 일반 사용자는 Jigmo 공식 사이트에서 최신 ZIP 파일을 받은 뒤 다음 세 글꼴을 설치하면 된다. 1. `Jigmo.ttf` 2. `Jigmo2.ttf` 3. `Jigmo3.ttf` Mac에서는 각 파일을 두 번 클릭하고 ‘서체 설치’를 누르면 된다. 이미 열려 있던 브라우저가 새 글꼴을 바로 알아보지 못한다면 브라우저를 완전히 종료했다가 다시 연다. 이 문제의 두 글자는 확장 B이므로 실제로 필요한 파일은 `Jigmo2.ttf`다. 하지만 고전 자료를 자주 읽는다면 세 파일을 함께 설치하는 편이 편하다. 설치 후 Mac에서도 다음 서명이 온전히 보였다. > 『탄만집(𢾡𢿜集)』 ![Jigmo 설치 후 Mac에서 탄만집의 확장 한자가 표시된 화면](https://withkai.io/content/images/2026/08/mac-jigmo-result.png) *한국고전번역원 뉴스레터 아카이브 화면. Jigmo를 설치한 뒤 Mac에서도 두 글자가 정상적으로 표시됐다.* ## 내 컴퓨터에서 보이는 것만으로는 부족했다 내 Mac에 글꼴을 설치했다고 해서 블로그 방문자에게도 보이는 것은 아니다. 방문자의 Mac이나 스마트폰에는 여전히 Jigmo가 없을 수 있다. 그래서 블로그 쪽에는 웹폰트를 포함했다. 다만 3천만 바이트가 넘는 Jigmo2 전체를 모든 방문자에게 보내는 방식은 피했다. 확장 B 영역을 1,024개 코드포인트 단위의 작은 WOFF2 파일 42개로 나눴다. 브라우저는 현재 글에 필요한 유니코드 범위만 내려받는다. `𢾡`과 `𢿜`은 같은 조각에 들어 있으므로 이 글을 읽을 때는 약 256KB짜리 파일 하나만 요청한다. 다른 41개 파일은 내려받지 않는다. ```css @font-face { font-family: "WithKai Extended Hanja"; src: url("extb-22c00-22fff.woff2") format("woff2"); font-display: swap; unicode-range: U+22C00-22FFF; } ``` 이제 해당 글꼴이 설치되지 않은 Mac, Windows, iPhone과 다른 스마트폰에서도 같은 글자를 전달할 기반이 마련됐다. 두 글자만 임시로 넣은 폰트가 아니라 확장 B 전체를 필요할 때 나눠 불러오는 구조라, 이후 다른 고전 문헌을 다룰 때도 다시 사용할 수 있다. 하지만 여기서 끝나지 않았다. 웹폰트 파일이 정상적으로 내려받아지고 두 글자의 글리프도 들어 있는데, Mac과 iPhone의 일부 브라우저에서는 여전히 네모로 표시되는 경우가 있었다. 그래서 테마 1.0.23에서는 두 글자의 원문 유니코드를 DOM에 그대로 남기면서, 화면에만 SVG 글리프 윤곽을 덧씌우는 안전장치를 추가했다. 이 SVG는 HanaMinB의 글리프 윤곽에서 생성했다. 복사·검색·검색엔진·스크린리더에는 `𢾡`과 `𢿜`이 원문 그대로 전달되고, Apple 계열 브라우저에서는 SVG가 화면 표시를 보완한다. ## 사람이 질문하고 AI가 수정한 과정 돌이켜보면 이번 해결의 핵심은 AI가 처음부터 정답을 내놓았다는 데 있지 않았다. AI는 처음에 데이터 저장 문제를 의심했다. 나는 Windows에서 정상적으로 보인다는 관찰을 덧붙였다. AI는 운영체제의 폰트 폴백 차이로 방향을 바꿨다. 내가 다시 “중국 본토 자형보다 한국 고전에 어울려야 한다”, “두 글자만 넣으면 활용성이 떨어진다”, “라이선스는 괜찮은가”라고 물으면서 해결책도 계속 달라졌다. 결국 다음과 같은 역할 분담이 이루어졌다. - 사람은 맥락과 판단 기준을 제공했다. - AI는 후보를 찾고 글꼴 파일의 실제 문자 수록 여부를 검사했다. - 사람은 어색하거나 지나치게 좁은 해결책에 이의를 제기했다. - AI는 가설과 구현 범위를 수정했다. - 마지막에는 사람의 화면으로 결과를 확인했다. AI와의 협업은 한 번의 좋은 질문으로 끝나는 일이 아니었다. 답을 받아들이고, 실제 환경에서 확인하고, 이상한 점을 다시 말하는 반복에 가까웠다. AI의 속도와 사람의 관찰이 함께 있어야 비로소 쓸 만한 답이 되었다. ## 이 글을 쓴 방법도 같은 실험이었다 여기서 한 단계 더 나아가 보기로 했다. 문제 해결 기록만 AI에게 정리시키는 데서 멈추지 않고, 지금 읽고 있는 글 자체도 같은 협업 방식으로 만들어 보았다. Codex는 대화 기록과 조사 결과를 바탕으로 초고를 작성했다. 글꼴 파일에 두 문자가 실제로 들어 있는지 검사했고, 라이선스를 확인했으며, 웹폰트를 필요한 범위별로 나누는 작업과 Apple용 SVG 글리프 fallback을 테마에 적용했다. 글의 분위기에 맞는 대표 이미지를 생성하고, 브라우저 자동화를 이용해 이미지·본문·태그·요약문을 블로그 편집기에 입력한 뒤 초안으로 저장했다. 그렇다고 AI가 혼자 글을 완성한 것은 아니다. 나는 Windows·Mac·iPhone에서 본 결과를 비교해 전달했고, 중국 본토 자형보다는 한국 고전에 어울리는 대안을 요구했다. 두 글자만 보이게 하는 임시방편은 활용성이 낮다고 지적했고, 라이선스와 방문자의 다운로드 부담도 다시 확인하게 했다. 마지막에는 AI가 등록한 초안과 실제 화면을 사람이 직접 검토하고 문장을 다듬었다. 이 글의 갱신에는 7일이 걸렸다. Codex의 토큰 사용량이 커진 데다, 웹폰트가 실제로 내려오는지, 운영체제별 표시가 어떻게 다른지, 테마에 적용한 뒤 공개 페이지에서 정상적으로 보이는지를 여러 차례 확인해야 했기 때문이다. 글을 쓰는 일만 늦어진 것이 아니라, 문제를 재현하고 원인을 좁히고 해결책을 다시 검증하는 과정 전체가 함께 길어진 결과였다. 이 과정이 보여 주는 AI의 가능성은 단순히 ‘글을 대신 써 준다’는 데 있지 않다. 관찰을 받아 가설을 고치고, 자료를 조사하고, 파일을 만들고, 웹사이트에 적용하고, 그 과정을 다시 읽을 수 있는 글로 남기는 일까지 하나의 흐름으로 이어 갈 수 있다는 데 있다. 다만 방향과 기준, 최종 판단은 여전히 사람의 몫이다. ## 작은 글자 두 개가 남긴 것 `𢾡`과 `𢿜`은 사라지지 않았다. 유니코드에도 있었고 웹페이지에도 저장되어 있었다. 단지 내가 보던 화면에 그 모양을 그려 줄 글꼴이 없었을 뿐이다. 그리고 문제를 푼 과정 역시 비슷했다. 답은 AI 안에 완성된 형태로 숨어 있지 않았다. 사람이 본 것을 말하고, AI가 다시 조사하고, 사람이 기준을 더하면서 조금씩 모습을 갖췄다. 고전의 두 글자를 되찾는 일은 생각보다 현대적인 협업이었다. --- ### 참고 자료 - [Jigmo 공식 배포 및 지원 범위](https://kamichikoichi.github.io/jigmo/?ref=withkai.io) - [Microsoft의 CJK 확장 문자 글꼴 폴백 설명](https://learn.microsoft.com/ko-kr/globalization/fonts-layout/font-support?ref=withkai.io) - [한국민족문화대백과사전 「탄만집」](https://encykorea.aks.ac.kr/Article/E0058779?ref=withkai.io) - [Unicode CJK 통합한자 확장 B 코드표](https://www.unicode.org/charts/PDF/U20000.pdf?ref=withkai.io) ### 제작 기록 이 글의 초고 작성, 자료 정리, 글꼴 검사, 웹폰트와 테마 구현, 대표 이미지 생성, 블로그 편집기 입력에는 Codex가 사용됐다. 편집기 입력은 브라우저 자동화로 진행했으며 공개 발행 전 전체 내용과 화면은 사람이 직접 검토하고 수정했다. ### 〖간지서당〗, 미토 중 URL: https://withkai.io/ganjiseodang-mito-jung/ Last updated: 2026-07-14T03:31:39.000Z 출처 : 간지서당: 논어로 보는 천간, 서유기로 보는 지지이야기. 박장금. 북드라망. 💡 미(未)는 가지가 무성하게 자란 나무의 상형이다. 그래서 나무(木)가 정점(一)을 지나고 있는 상태라고 보기도 한다. 이제 더 무성하게 자랄 일이 없는 나무. 여기서 '아니다'라는 뜻이 파생되어 나왔다.(......) 미월(未月)은 음력 6월, 절기로는 소서와 대서다. 미월을 과거엔 재앙이라고 불렀다. 재앙이 될 만큼 혹독한 무더위가 찾아오기 때문이다. 미치도록 더운 삼복 더위가 기승을 부리는 것도 미월이다. 그러나 이 무더위가 없으면 과일엔 맛이 들지 않는다. 죽을 고비를 넘겨야 달콤한 열매를 얻을 수 있는 법이다." (류시성, 손영달, 갑자서당, 178쪽) 재인용. ... "하지만 아픔이란 결국 변화를 인정하지 않을 때 내가 만들어 내는 통증이다. 미토의 지혜로 자신의 습관을 재빨리 내려놓고 다른 존재로 전환하는, 훈련과 기술이 필요하다." ### 〖간지서당〗, 사화 중 URL: https://withkai.io/ganjiseodang-sahwa-jung/ Last updated: 2026-06-30T03:54:02.000Z ### 사화, 화염 속에서 성찰하는 뱀의 힘 출처 : 간지서당: 논어로 보는 천간, 서유기로 보는 지지이야기. 박장금. 북드라망. 💡 그리스 의술의 신 아스클레피오스(Asclepios)의 지팡이에는 뱀이 감겨져 있다. 아스클레피오스는 죽은 자를 치료하던 중 뱀이 들어오자 놀라서 지팡이로 뱀을 때려 죽였다. 그런데 다른 뱀이 약초를 물고 나타나 죽은 뱀의 입 위에 그것을 올려놓았고 그러자 죽은 뱀이 살아났다. 이것을 본 아스클레피오스는 죽은 자에게 그 약초를 올려 사람을 살려냈고, 그후로 그는 지팡이에 뱀을 휘감고 다니며 자신의 상징으로 삼았다. 이 이야기는 허물을 벗으면서 재생하는 뱀의 이미지와 병과 죽음을 치유하는 의사가 오버랩된다. 뱀이 가진 독은 치명적이지만 특정한 맥락 속에서는 생명을 구하는 약이 된다. 중요한 것은 맥락을 읽는 유연함이다. 그 유연함이 없으면 약도 독이 되고 독도 약이 될 수도 있으니까. 바로 뱀은 맥락을 읽는 힘을 가진, 가장 틀에 갇히지 않는 유연한 동물인 것이다. ### 〖아침놀〗 중 URL: https://withkai.io/acimnol-jung/ Last updated: 2026-06-05T03:42:59.000Z 💡 출처 : 리드리히 니체,〖아침놀〗, 이동용 옮김, 세창출판사 박장금, 〖간지서당〗, 북드라망, 재인용 "이 책에서 사람들은 '땅속에서' 일을 하고 있는 한 사람을 발견하게 될 것이다. 그는 굴을 뚫고, 흙을 파내며, 아래로 파고들어 가는 사람이다. 그렇게 깊은 곳에서 이루어지는 일을 위한 눈을 가진 사람들이라면 얼마나 그가 천천히, 신중하게, 부드럽지만 가차 없이 전진하는지 보게 될 것이다. 그는 오랫동안 빛과 공기 없이 지내면서도 힘들다는 소리 한마디 내뱉지 않는다. 사람들은 그가 어둠 속에서 행하고 있는 자신의 일에 스스로 만족하고 있다는 사실도 알게 될 것이다. 어떤 믿음이 그를 인도하고 있고, 또 위로를 해주고 있다는 것이 보이지 않는가? 그는 어쩌면 자기 자신의 기나긴 어둠을 갖고자 하는 것이 아닐까? 자기 자신에 대해 이해가 안 되는 것들, 숨겨진 것들, 수수께기 같은 것들을? 왜냐하면 그는 스스로 결국에는 자기 자신의 아침을, 자기 자신의 구원을, 자기 자신의 아침놀을 가지게 될 것도 알고 있기 때문에? ... 확실하다. 그는 되돌아올 것이다. 그 아래에서 그가 무엇을 원하는지 묻지 말라. 트로포니오스 같은 이 땅속의 인간이 다시 '사람이 되었을' 때, 그는 스스로 그것을 너희들에게 말하게 될 것이다. 그와 같이 그토록 오랫동안 두더지처럼 또 혼자서 지내 보았다면, 사람들은 침묵하는 것을 완전히 잊게 된다." ### 〖홍루몽〗 중 URL: https://withkai.io/hongrumong-jung/ Last updated: 2026-06-05T03:31:18.000Z "그대가 과연 호 자와 료 자를 제대로 들었다면 아주 잘 들은 거요. 세상의 모든 일이란 좋은 일이면 끝나는 거고, 끝나면 좋은 거란 말이오. 만일 끝나지 않으면 좋지 않은 것이며, 만일 좋고자 한다면 반드시 끝나야 하는 거지요. 그래서 내 노래를 '호료가(好了歌)'라고 하지요." 세상 사람 모두 신선 좋은 줄 알면서도, 오로지 부귀공명을 잊지 못한다네! 고금의 장수 재상 지금은 어디에 있나? 황량한 무덤 위엔 들풀만 덮여 있다네. 세상 사람 모두 신선 좋은 줄은 알면서도, 오로지 금과 은을 잊지 못한다네! 하루 종일 모자라다 원망만 하다가는, 돈 많이 모여지면 두 눈 감고 만다! 세상 사람 모두 신선 좋은 줄은 알면서도, 오로지 예쁜 아내만은 잊지 못한다네! 님 살아 있을 땐 날마다 은정 말해도, 님 죽어 떠나면 남을 따라 멀리 간다네. 세상 사람 모두 신선 좋은 줄은 알면서도, 오로지 아들 손자는 잊지 못한다네! 어리석은 부모는 예로부터 많았지만, 효도하는 자손을 그 누가 보았는가? ### 만춘(晩春), 韓愈 URL: https://withkai.io/mancun-wan-chun-han-yu/ Last updated: 2026-05-10T01:34:34.000Z #### 草木知春不久歸 (초목지춘불구귀) 초목은 머지않아 봄이 가는 줄 알므로 #### 百般紅紫鬪芳菲 (백반홍자투방비) 알록달록 갖가지 꽃을 피우며 향기를 다투네 #### 楊花楡莢無才思 (양화유협무재사) 버드나무, 느릅나무는 마땅한 재주가 없어서 #### 惟解漫天作雪飛 (유해만천작설비) 그저 온 마을에 솜 버들 눈송이만 만들어 날리 ### SURVIVING SCHIZOPHRENIA 중 URL: https://withkai.io/surviving-schizophrenia-jung/ Last updated: 2026-05-05T01:58:17.000Z p.57 망상과 환각 ✒️ 마지막으로 신체 경계의 왜곡도 그렇지만 대부분 망상과 환각은 감각이 지나치게 예민해지고 뇌가 자극을 적절하게 해석하고 반응하는 능력을 상실해서 일어난 직접적인 결과다. 망상과 환각은 대부분 그 사람의 뇌가 경험하고 있는 일에서 나오는 논리적 결과다. 제3자의 눈에만 '미친' 것처럼 보일 뿐, 그것을 경험하는 사람에게는 논리적이고 일관된 패턴의 한 부분이다.\* 💡 환각이나 망상 등은 실제 현실에서는 있을 수 없기 때문에 외부 사람들은 이것을 거짓으로 받아들이는 경우가 있다. 하지만 환자 본인의 입장에서는 실제로 경험하고 느끼는 현실이기 떄문에 거짓이 아니고 현실적 상황이다. 이런 점을 인정해주는 것이 환자에게 중요하다. p.67 ✒️ 환각은 조현병에서 매우 흔한 증상으로, 과도하게 예민한 감각에서 시작된 스펙트럼의 가장 끝부분에 해당한다. 시각을 예로 들어보자. 스펙트럼 한쪽 끝에는 시각의 과도한 예민함이 있다. 다시 말해서 빛이 너무 밝고 색상들은 더욱 눈부신 색조를 띤다. 이 스펙트럼의 가운데에는 시각적 자극의 전반적 왜곡(착각illusion이라고도 핟나)이 자리하고 있는데, 이를테면 개가 호랑이처럼 보이는 현상이다. 그리고 스펙트럼 가장 끝에는 아무것도 없는 곳에서 무언가를 보는 현상이 있는데, 이것이 바로 진짜 환각이다. 환자들이 들려주는 경험에는 대개 스펙트럼상의 다양한 지점이 섞여있다. p.162 \~ 조현병의 결과는 어떻게 예측하는가 💡 지금은 남성 환자보다 여성 환자가 더 좋은 결과가 나온다는 사실이 명백히 밝혀졌다. ... 💡 연령이 더 높은 그룹, 특히 30세 이후에 첫 진단을 받은 사람들은 양호한 결과 그룹에 들어갈 가능성이 높다. 💡 발병 유형도 중요한 회복 예측 요인으로, 매우 급작스럽게 발병한 환자들에게서 가장 좋은 결과가 나온다. \~ 자신의 병을 인식하는 것(병식)도 매우 좋은 신호이며, 반면 인식하지 못하는 것(질병인식불능증)은 나쁜 신호다. p. 172 \~ 💡 평균적인 조현병 환자들에게는 30년 경과가 10년 경과보다 더 양호하다는 사실이 지금은 확실히 입증되었다. \~ 장기 예후가 더 좋은 주된 이유는 대부분의 사람에게서 노화가 조현병 증상을 개선하기 떄문이다. \~ 조현병은 생애 과정에서 노화가 이로운 역할을 하는 몇 안 되는 질병 중 하나다. 💡 조현병 환자 대부분에게서 환각, 망상, 사고장애 같은 '양성' 증상들은 세월이 가면서 감소한다. 25세에 이런 증상들로 생활에 심각한 타격을 입었던 사람들도 50세에는 그 증상들의 흔적만 남아 있을 수도 있다. 마치 조현병 과정 자체가 시간이 흐르면서 스스로 소진되고 과거 행적의 흉터만 남기는 것처럼 보인다. 환자들은 환청이 들려도 무시하거나 공공장소에서는 환청에 반응하지 않는 식으로 증상을 안고 살아가는 방법을 익혀간다. ### 환자와 가족은 어떻게 해야 조현병을 이겨낼 수 있을까 💡 조현병 환자와 가족이 조현병에서 살아남기 위해 할 수 있는 가장 중요한 단 하나를 꼽는다면 올바른 태도를 갖는 것이라 말하겠다. 조현병이 몰고 오는 두 개의 괴물, 바로 비난과 수치라는 괴물을 해결하고 나면 올바른 태도는 자연스럽게 생겨난다. ###### 올바른 태도 SAFE - 전체를 볼 줄 아는 감각 Sense of perspective - 병에 대한 수용 Acceptance of the illness - 가족 간의 균형 Family balance - 현실적인 기대 Expectations that are realistic ### 발 ⟪택리지⟫, 정약용 중 URL: https://withkai.io/bal-taegriji-jeongyagyong-jung/ Last updated: 2025-12-28T11:39:25.000Z 💡 출처 : 완역정본택리지, 이중환 저, 안대회 외 옮김, (주)휴머니스트출판그룹 나는 주거지 선택의 이치를 이렇게 논한다 . \~ 물과 땔감을 멀리에서 구하면 힘이 빠지고, 오곡이 잘 자라는 땅이 갖추어지지 않으면 흉년이 자주 찾아온다. 풍속이 문화만을 숭상하면 말이 많고, 무예만을 숭상하면 싸움이 많으며, 이익만을 숭상하면 백성이 속이고 경박하며, 그저 농사만 열심히 지으면 고루하고 성질이 사납다. 산천이 혼탁하고 험악하면 수려하고 빼어난 사람과 물산이 드물고 뜻이 맑지 못하다. 이것이 큰 줄거리이다. ### 『맹자』「공손추」하편 4-1ㄱ ~ URL: https://withkai.io/maengja-gongsoncu-hapyeon-4-1g/ Last updated: 2026-08-02T13:51:58.000Z ##### 출처 : 「하루 한문 공부 우리말 문해력을 높이는 한문교양 365」, 임자헌, 유유출판사 ## 孟子曰, 天時不如地利, 地利不如人和 . 맹자왈, 천시불여지리, 지리불여인화 ## 三里之城, 七里之郭 , 環而攻之而不勝 . 不環而攻之, 必有得天時者矣 . 然而不勝者, 是天時不如地利也 . 삼리지성, 칠리지곽, 환이공지이불승 . 부환이공지, 필유득천시자의 . 연이불승자, 시천시불여지리야 . ## 城非不高也, 池非不深也 , 兵革非不堅利也, 米粟非不多也 , 委而去之 , 是地利不如人和也 . 성비불고야, 지비불심야 , 병혁비불견리야, 미속비부다야, 위이거지 , 시지리불여인화야 . ## 故曰 , 域民, 不以封疆之界 , 固國, 不以山谿之險 , 威天下 , 不以兵革之利 . 고왈, 역민, 불이봉강지계, 고국, 불이산계지험, 위천하 , 불이병혁지리 . ## 得道者, 多助, 失道者, 寡助 . 寡助之至, 親戚畔之, 多助之至, 天下順之 . 以天下之所順, 攻親戚之所畔, 故 , 君子有不戰, 戰必勝矣 . 득도자, 다조, 실도자, 과조 . 과조지지, 친척반지, 다조지지, 천하순지 . 이천하지소순, 공친척지소반, 고, 군자유부전, 전필승의 . ### 태음태양력, 명절 및 잡절 URL: https://withkai.io/taeeumtaeyangryeog-myeongjeol-mic-jabjeol/ Last updated: 2026-08-02T14:05:05.000Z #### 출처 : 한국천문연구원 역서 ([https://astro.kasi.re.kr/almanac/pageView/26](https://astro.kasi.re.kr/almanac/pageView/26?ref=withkai.io)) #### ##### 태음태양력(음력) 태음태양력은 달의 운행과 태양의 운행을 모두 고려하여 만든 역법이다. 순수하게 달의 운행만을 고려한 태음력과는 구분된다. 우리나라의 음력은 태음태양력에 해당한다. #### 한 달의 결정 음력에서의 한 달은 달의 위상 변화를 기준한 삭망월로 결정한다. 즉 달의 합삭일부터 그 다음 합삭일 전 날까지가 음력의 한 달이고, 달의 합삭일이 음력 초하루가 된다. 여기서 합삭이란 달과 태양의 황경이 일치하는 순간이다. 달의 합삭과 다음 합삭까지의 간격은 대략 29.5일이므로, 음력 한 달은 대체로 29일과 30일이 반복하게 되며 각각 소월, 대월이라고 한다. #### 24기 24기는 태양의 운동에 근거한 것으로, 춘분점으로부터 태양이 움직이는 길인 황도(黃道)를 따라 동쪽(반시계방향)으로 15°간격으로 나누어 24점을 정하였을 때, 태양이 각 점을 지나는 시기를 말한다. 태음태양력에서 24기는 중요한 태양력적 요소로서, 12절기와 12중기로 분류되며, 12개의 월을 배치할 때 기준이 된다. 즉, 달의 이름은 그 달에 든 중기를 보고 결정하며, 매년 태음태양력 계산의 기점인 동지가 들어있는 달은 반드시 음력 11월로 삼는다. 다음 표에서 각 달에 포함되는 절기와 중기를 확인할 수 있다. | 음력월 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | | --- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | | 절기 | 입춘 | 경칩 | 청명 | 입하 | 망종 | 소서 | 입추 | 백로 | 한로 | 입동 | 대설 | 소한 | | 중기 | 우수 | 춘분 | 곡우 | 소만 | 하지 | 대서 | 처서 | 추분 | 상강 | 소설 | 동지 | 대한 | ##### 윤달의 삽입 - 무중치윤법(無中置閏法) 음력에서 12달의 길이는 약 354.3671일로 1 태양년의 길이(약 365.2422일)보다 약 11일(10.8751일)이 짧아 서로 맞지 않는다. 이 차이를 보정하기 위해 태음태양력에서는 윤달을 넣는다. 이 때 윤달을 넣는 기준은 12중기와 관련이 있다. 즉, 음력 11월부터 그 다음 해 음력 11월 전까지 삭망월이 13개이면, 최소 하나의 삭망월에는 중기가 들어가지 않게 되는데, 이런 경우에 그 달을 윤달로 정하게 된다. 윤달의 명칭은 그 전달의 이름을 사용하데, 앞에 '윤(閏)'자를 붙여 부른다.(예,윤5월). 이와 같이 중기가 들지 않는 달을 무중월(無中月)이라고 하며, 무중월을 윤달로 하는 법을 무중치윤법이라 한다. 만약 무중월이 2개 이상이면 첫 번째 무중월을 윤달로 정한다. 일반적으로 태음태양력에서는 19년에 7번의 윤달을 두어 태양년의 길이에 맞추고 있다. #### 세차, 월건, 일진 전통적으로 음력에서는 년, 월, 일을 표기할 때 간지를 사용했다. 간지는 10 천간(天干)과 12 지지(地支)를 조합하여 만든 60개의 주기로 생각할 수 있다. 각 년에 배정되는 간지를 세차, 월에 부여되는 간지를 월건 그리고 일에 배정되는 간지를 일진이라 한다. 세차, 월건, 일진은 60개의 간지가 순서대로 연속하여 배치된다. 윤달에는 월건을 배정하지 않는다. #### 명절 및 잡절 ##### 한식 한식은 전년도 동지 이튿날부터 시작하여 105일째 되는 날로써, 양력으로 보통 4월 5일이나 4월 6일이 되는데 청명과 비슷한 시기이다. 이 시기에는 공기가 건조하고 봄바람이 불어 화재가 발생하기 쉬우므로 하루 종일 불을 금하고 찬 음식을 먹는 풍습에서 그 이름이 유래되었다고 한다. 또한 한식은 설날, 단오, 추석과 함께 우리나라 4대 명절의 하나로서, 성묘를 하는 풍습이 있다. ##### 단오 단오는 음력 5월 5일로, 신록이 우거지고 날씨가 따뜻한 때이다. 이 날은 수릿날이라고도 하며, 쑥떡을 만들어 먹고, 여자는 창포물에 머리를 감고 그네를 뛰며 남자는 씨름을 하는 풍습이 있다. ##### 칠석 칠석은 음력 7월 7일로, 이때 내리는 비는 은하수 서쪽의 직녀와 동쪽의 견우가 오작교에서 일 년에 한 번 만나 흘리는 눈물이라는 전설이 있다. ##### 삼복 삼복(三伏)은 여름의 더운 시기를 대변하는 말로 초복, 중복, 말복을 말한다. 초복(初伏), 중복(中伏)은 각각 하지로부터 세 번째 경(庚)일, 네 번째 경(庚)일, 말복(末伏)은 입추로부터 첫 번째 경(庚)일인데, 하지 또는 입추가 경일이면 그 날을 첫 번째로 삼는다. 경(庚)일이라는 것은 일진의 간지 중 경자가 든 날, 즉 경오, 경진, 경인, 경자 등의 날을 말하는 것이다. 초복과 중복 사이의 간격은 10일이나, 중복과 말복 사이의 간격은 10일 또는 20일 간격일 때가 있다. 20일 간격인 때를 월복(越伏)이라 한다. ### [업데이트] 2025·2026년 만세력 (월건·일진·절기) ICS 파일 URL: https://withkai.io/2025nyeondo-manseryeog-weolgeon-iljin-jeolgi-ics-pail/ Last updated: 2026-08-23T02:55:55.000Z \[2026.08.23\. 업데이트\] 2025년과 2026년의 월건·일진·절기, 주요 명절·잡절을 구글·애플 캘린더 등에서 확인할 수 있도록 만든 ICS 파일입니다. 이 파일은 실시간 API로 계산되거나 자동으로 갱신되는 서비스가 아니라, 날짜별 정보를 미리 담은 정적 캘린더 파일입니다. - 기간: 2025년 1월 1일 \~ 2026년 12월 31일 - 구성: 730일의 종일 일정 - 표시: 월건·일진·절기, 주요 명절·잡절 월건은 절기에 따라 달라집니다. 캘린더 일정은 종일 일정으로 표시되므로 절입 시각 자체를 일정의 시작 시각으로 표현하지는 않습니다. 정확한 절입 시각이 필요한 경우에는 별도의 만세력 자료를 함께 확인해 주세요. 파일을 캘린더에 추가한 뒤 날짜별 일정을 열면 해당 날짜의 월건·일진과 절기 정보를 확인할 수 있습니다. [ 2025-2026만세력 2025-2026만세력.ics 216 KB ](https://withkai.io/content/files/2025/11/2025-2026---------.ics) ### 불거지다 URL: https://withkai.io/bulgeojida/ Last updated: 2023-10-15T14:25:38.000Z ### 동사 1. 물체의 거죽으로 둥글게 툭 비어져 나오다. > 헤어진 양말 밖으로 발가락이 불거지다. > > 그는 겉으로 두드러지게 불거진 눈을 갖고 있다. 1. 어떤 사물이나 현상이 두드러지게 커지거나 갑자기 생겨나다. > 커다랗게 불거진 소문. > > 입시 제도에 대한 개혁 문제가 불거지다. ### 관련규범해설 '불거지다'의 의미로 '붉어지다, 불그러지다'를 쓰는 경우가 있으나 '불거지다'만 표준어로 삼는다. ### 「그렇게 쓰면 아무도 안 읽습니다」(전주경, 2023, 윌북) URL: https://withkai.io/geureohge-sseumyeon-amudo-an-ilgseubnida-jeonjugyeong-2023-wilbug/ Last updated: 2026-08-02T14:02:25.000Z ![](https://withkai.io/content/images/2023/10/9791155816301-1.jpg) #### 그렇게 쓰면 아무도 안 읽습니다 읽은 날 : \~ 2023.10.15 브랜드와 서비스의 언어를 가꾸는 UX 라이터의 글쓰기 UX 라이팅에 종사하는 사람도 아니고, 그렇다고 큰 범주에서 IT 등의 업계에서 일하는 사람은 더더욱 아니지만 취미와 취미보다 더 나아간 것으로써 다방면에 관심을 쏟고 있다는 점에서 골랐다. 읽는 것은 좋아하지만 정리하거나 글을 쓰는 실력은 형편이 없어 앞으로 가끔 독서에 대한 몇 문장이나 단상을 정리하면 어떨까 하여 기록을 남길까 한다. 저자는 UX 라이팅이라는 분야에서의 글쓰기에 관해 이야기했음에도 누군가에게 정보를 안내한다는 점에서 '정확성', '간결성', '일관성'의 중요성은 어느 분야에 국한되기 어렵다. 특히 문해력에 대한 우려가 ~~붉어지고~~ 불거지고 있는 요즘... 또, 사실 너무 많은 지식과 상황, 파편화된 데이터가 넘쳐나는 요즘이라면 더욱 그런 것 같다. .... --- ### 인상깊은 몇 가지 문장 > 나는 동료同僚라는 말을 좋아한다. '횃불을 들고 밤에 일하는 사람들'이라는 료僚라는 한자가 마치 야근에 지친 우리들을 의미하는 것 같아서 말이다. (시작하면서) > 나는 수많은 사람들이 느낄 수 있는 다양한 감정의 스펙트럼을 두루 살피지 않고, 배려하지 않는 그런 사람이야말로 꼰대라고 생각한다. \~ 그러니 우리는 사용자의 감정에, 한 사람의 깊은 사연에 대해 그저 한없이 겸손해지고 낮아지는 수밖에 다른 방법이 없다.(보이스와 톤) > \~ 난이도 높은 용어를 써야할 경우에는 사용자를 학습시키는 전략을 동반하면 된다. \~ (중략) \~ 이제 사용자는 휴대폰과 앱과 웹 서비스 화면에서 텍스트를 스캐닝, 스킵하여 필요한 정보만 빠르게 습득하려고 한다. 이처럼 변화한 현시대의 사용자 정보 추구 행태를 정면으로 마주하며 UX 라이터는 공공 글쓰기의 주역으로서 UI 텍스트를 읽는 사용자에 대해 무거운 책임감을 느낀다. (UX 라이팅 실무 이슈)