로컬 HTML 체크리스트를 서버 없이 JSON 파일과 동기화하기 — File System Access API로 사람과 AI가 같은 파일을 쓰게 한 방법
실행 체크리스트는 file:// 로 여는 HTML 한 장입니다. 브라우저의 체크·메모를 AI 에이전트도 읽고 쓰려면 공유 저장소가 필요했는데, 처음 둔 로컬 서버는 켜 두는 게 번거로웠습니다. File System Access API로 JSON 파일 핸들을 IndexedDB에 저장해 두고 변경을 그 파일에 바로 쓰는 구조로 바꿨습니다. 새로고침마다 권한을 다시 묻는 크롬 규칙과, 첫 클릭에 권한을 묻게 한 처리를 정리했습니다
앱 출시 작업을 관리하는 실행 체크리스트가 있습니다. 99개 항목이 들어 있는 HTML 파일 한 장이고, 브라우저 즐겨찾기에서 file:/// 주소로 엽니다. 서버도 빌드도 없습니다.
문제는 이 체크를 사람만 하는 게 아니라는 점입니다. 작업을 끝낸 AI 에이전트가 항목을 체크하고 메모를 남기고, 저는 브라우저에서 체크하고 메모를 고칩니다. 둘이 같은 상태를 봐야 하는데, 브라우저의 체크는 localStorage에만 남아서 에이전트가 읽을 수 없었습니다.
1차: 로컬 서버
처음 해결은 작은 파이썬 서버였습니다. 페이지가 체크·메모를 127.0.0.1의 서버로 보내고, 서버가 checklist-state.json에 쓰고, 에이전트는 같은 파일을 스크립트로 읽고 씁니다. 10초마다 페이지가 서버에서 다시 읽어 상대방 변경을 반영합니다.
잘 돌았지만 서버를 켜 두는 일이 남았습니다. 에이전트가 셸에서 띄운 서버는 셸이 끝나면 같이 죽었고, 그러면 페이지는 옛 상태를 보여 주면서 조용히 로컬에만 저장했습니다. 시작 프로그램에 넣는 방법도 있지만 “천년만년 쓸 것도 아닌 체크리스트 때문에 시작 프로그램에 뭘 넣는 건 별로”라는 게 정직한 평가였습니다.
2차: 페이지가 파일을 직접 읽고 쓴다
file://로 연 페이지는 옆에 있는 파일조차 fetch로 읽을 수 없습니다. 쓰는 건 당연히 안 됩니다. 그런데 크롬에는 File System Access API가 있습니다. 사용자가 파일 선택 창에서 파일을 한 번 고르면 페이지가 그 파일의 핸들을 받고, 핸들로 읽기와 쓰기를 모두 할 수 있습니다. file:// 페이지도 보안 컨텍스트로 쳐 주기 때문에 이 API가 열려 있습니다.
구조는 이렇습니다.
- 연결: 버튼을 누르면
showOpenFilePicker가 뜨고, 사용자가checklist-state.json을 고릅니다. 받은 핸들은 IndexedDB에 저장합니다. 핸들은 직렬화되는 객체라 저장이 됩니다. - 읽기: 10초마다, 그리고 창이 다시 포커스될 때
handle.getFile()로 파일을 읽어 병합합니다. - 쓰기: 체크나 메모가 바뀌면 파일을 다시 읽고, 바뀐 항목만 얹어서
createWritable()로 통째로 씁니다. 쓰기 작업은 약속 체인으로 직렬화해서 겹치지 않게 합니다. - 병합 규칙: 항목마다
{done, at, by}를 두고 시각이 큰 쪽이 이깁니다. 서버 때와 같은 파일 형식이라 에이전트 쪽 스크립트는 한 줄도 고치지 않았습니다. 변경 이력도 같은 파일 안의log에 쌓입니다.
서버 코드는 지웠고, 시작 프로그램 바로가기도 지웠습니다.
크롬이 권한을 다시 묻는다
한 가지 남은 마찰이 있습니다. 핸들은 저장되지만 쓰기 권한은 페이지를 새로 열 때마다 초기화됩니다. 크롬이 “이 사이트가 파일을 수정하도록 허용하시겠습니까”를 다시 묻습니다. 그리고 이 질문은 사용자 동작(클릭) 안에서만 띄울 수 있습니다.
처음에는 상단의 연결 버튼을 다시 눌러야 했습니다. 번거로워서 첫 번째 체크나 메모 클릭이 곧 권한 요청이 되게 바꿨습니다. 체크를 바꾸면 300ms 뒤 저장 루틴이 도는데, 그 시점은 아직 사용자 활성화가 살아 있어서 requestPermission을 부를 수 있습니다. 허용을 누르면 대기 중이던 변경이 그 자리에서 저장됩니다. 상태 표시도 “권한 대기 — 체크를 하나 누르면 크롬이 허용을 묻습니다”로 바꿨습니다.
테스트하다 배운 것
- 토글했다가 되돌린 체크는 파일에 아무 흔적도 남기지 않습니다. 값이 원래대로면 쓰지 않게 만들었기 때문인데, 덕분에 “쓰기가 되는지” 확인하려면 값이 남는 변경을 해야 했습니다.
- 서버가 꺼진 동안 브라우저에 쌓인 변경은 사라지지 않습니다. 연결되는 순간 시각 비교로 합쳐집니다.
- 페이지 머리에
<meta charset="utf-8">이 없었습니다.file://에서는 크롬이 알아서 맞춰 줘서 몰랐는데, 테스트용으로 로컬 HTTP 서버에 올리자 한글이 전부 깨졌습니다. 이번에 넣었습니다.
정리
- 로컬 HTML이 파일을 직접 읽고 쓰려면 File System Access API 하나로 됩니다. 서버가 필요 없습니다.
- 핸들은 IndexedDB에 저장되지만 쓰기 권한은 열 때마다 다시 묻습니다. 첫 사용자 클릭에서 권한을 요청하면 마찰이 거의 없습니다.
- 사람과 에이전트가 같은 파일을 쓰려면 병합 규칙은 “시각이 큰 쪽이 이긴다” 하나면 충분했습니다.
