오늘 블로그에 뉴스레터 페이지를 새로 추가했습니다. 매주 발송하는 무료 Substack 뉴스레터와 매달 보내는 스폰서 전용 소식지를 포함해, 지금까지 보낸 모든 뉴스레터를 한곳에서 볼 수 있는 목록 페이지입니다. 이 기능은 거의 전부 음성으로 만들었습니다. 저녁을 준비하는 동안 노트북에 대고 말을 걸듯 개발했습니다.
작업에는 ChatGPT 데스크톱 앱의 Codex 탭에서 음성 대화 모드를 사용했고, 로컬 개발 환경을 대상으로 실행했습니다. 실제 모습은 다음과 같습니다.

먼저 로컬에 받아 둔 simonwillisonblog 저장소에서 다음과 같이 입력해 세션을 시작했습니다.
Start dev server and open in browser
이렇게 하면 작업 대상 사이트의 미리보기가 뜨기 때문에, 나중에 새 페이지를 보여 달라고 요청해 진행 상황을 눈으로 확인할 수 있습니다.
그런 다음 "Start new voice chat" 버튼을 눌렀습니다. 마이크 버튼이 아니라 그 오른쪽에 있는 버튼입니다. 그리고 요리하면서 말할 수 있도록 노트북을 주방에 세팅했습니다.
무엇을 만들지는 꽤 명확하게 정리돼 있었고, Django로 구현하기에 간단한 기능이라 모델(이번에는 GPT-6 Astra High)이 충분히 해낼 것이라고 확신했습니다. 필요한 작업은 새 모델, 마이그레이션, 뷰 코드, 템플릿, 그리고 외부 소스에서 데이터베이스로 데이터를 가져오는 임포트 함수 몇 개였습니다.
다음은 Codex가 받아 적은 음성 기록의 일부입니다.
Um, they do not. Um, this is going to be a new type of content. Um, it's not going to show up... Oh, hold on. Yeah, no- I do not want this to show up in my, um, tag pages and date archive pages and... Actually, no, I think... I don't want it on the tag pages. I don't want it on the, um, blog index page. But I think I do want it to show up on the date-based pages. You know, if you navigate to September the 19th, and I sent a newsletter on that page, I think I want that to show up. So... this is- so I think we probably need a new model. The other thing is that I want them searchable, uh the Substack ones are not searchable, because those are actually just copies of other s- on content on my blog. These monthly ones do contain unique content, and spe- and once they're... published, like once they're made public a month after they've gone out, I want them to show up on my search results.
이 정도로도 모델은 제가 만들고 싶은 것을 정확히 이해했습니다. 말 더듬은 부분까지 포함한 전체 기록은 이 Gist에서 볼 수 있습니다.
이런 식으로 저녁을 짓는 시간인 30분 정도 대화를 이어갔습니다. 모델은 답변하다가 가끔 확인 질문을 던졌고, 이어서 코드를 수정했습니다.
음성만으로도 놀라울 만큼 많은 부분을 만들었습니다.
/api/v1/archive에 바로 요청해 보았고, 이어 검색으로 페이지네이션 방법을 찾다가 Karen Spinner가 쓴 이 글을 발견했습니다.배포 직전까지 거의 다 왔습니다. 문제는 임포트였습니다. Astra가 로컬 데이터를 내보내서 프로덕션에 넣는 방법을 제안했지만, 저는 기존 임포트 스크립트처럼 동작하기를 원했습니다. 데이터 일부가 비공개 GitHub 저장소에 있어서 새 API 키를 만들어야 했고, 그러려면 한동안 키보드 앞에 앉아야 한다는 걸 알고 있었습니다.
요리를 마치고 기능이 대체로 완성됐다고 판단한 뒤, Codex에게 브랜치를 만들고 풀 리퀘스트를 열도록 했습니다.
GitHub PR 화면에서 코드를 리뷰했습니다. 거의 원하는 대로였지만, 임포트 스크립트 하나가 서브프로세스로 Git을 호출하도록 작성돼 있었습니다. 임포트 중 하나는 비공개 Git 저장소에서 데이터를 가져와야 했기에 API를 쓰는 편이 낫겠다고 판단했습니다. 여기서부터는 키보드 입력으로 바꿔 Codex에게 API 기반 임포트로 교체하라고 지시했습니다.
리뷰 중에 제가 수정한 내용은 PR에 추가된 커밋에서 확인할 수 있습니다. 임포트 방식을 고치고, 공개 페이지의 표시 방식도 몇 가지 다듬었습니다. 타이핑으로 프롬프트를 입력하며 30분을 더 작업한 끝에 만족스러운 상태가 되어 PR을 머지하고 프로덕션에 배포했습니다.
완성된 모습은 새로 만든 뉴스레터 목록 페이지에서 볼 수 있고, 지난 월간 뉴스레터 페이지도 확인해 보세요.

목록 페이지에는 최근 발행한 주간 Substack 뉴스레터와 GitHub 스폰서용 월간 뉴스레터가 최신순으로 섞여 나옵니다. 페이지 아래쪽에는 연도별 아카이브 페이지로 가는 링크가 있습니다.
페이지 디자인은 GPT-6 Astra가 맡았고, 주방 건너편에서 로컬 미리보기를 흘끗 보고 제가 말로 전한 피드백에 따라 디자인을 다듬었습니다.
OpenAI는 DevDay 같은 행사에서 이런 음성 기반 데모를 무척 좋아하고, 실제로 그런 자리에서는 잘 어울립니다. 하지만 저에게 이것이 매일 쓰는 주력 도구가 될 것 같지는 않습니다.
저는 강아지와 산책하면서 휴대폰의 ChatGPT 음성 모드로 상당한 "일"을 처리한다는 이야기를 전에 쓴 적이 있습니다. 주로 리서치와 브레인스토밍이지만, 가끔은 ChatGPT가 코드 조각을 작성하고 테스트하게 해서 실제 개발 작업을 하기도 합니다.
이번 경험은 느낌이 달랐습니다. 시각적 미리보기가 있고, 말로 전달하기 어려운 내용은 키보드로 입력하거나 붙여 넣을 수 있어서, 코딩 에이전트와 훨씬 강력한 방식으로 소통할 수 있습니다.
다만 세부 사항을 다듬는 단계가 되면 저는 여전히 타이핑으로 돌아갑니다. 예시나 에러 메시지를 붙여 넣거나 고쳐야 할 코드나 기능을 직접 하이라이트하는 편이, 말로 설명하는 것보다 효율적입니다.
저는 주로 집에서 일하는데, 다행입니다. 공용 업무 공간에서 컴퓨터에 이렇게 말을 거는 일은 절대 하고 싶지 않으니까요!
저에게 가장 큰 장점은 멀티태스킹이 가능하다는 점입니다. 요리할 때면 보통 팟캐스트나 TikTok을 틀어 두는데, 이제는 그 시간에 실제로 무언가를 만들 수 있습니다.
지금 보고 계신 것은 제 블로그의 장문 아티클뿐입니다. 모든 글을 받아보려면 /atom/everything/을 구독하시거나, 다른 구독 방법을 확인해 보세요.