Claude Code로 만든 글 작성 도구
오랫동안 사용하던 Textcube를 떠나 정적 사이트 기반 블로그로 마이그레이션하고 나서 글 마지막에 쓴 것처럼 글 작성 도구를 만들었다. 해당 글에서는 CMS라고 쓰긴 했지만 CMS까진 아닌 것 같아서 글 작성 도구라고 얘기하겠다.
AI를 사용한 글 작성 도구라고 하면 요즘 분위기에서는 AI가 글을 써준다고 생각할 수 있지만 이 블로그는 사람이 글을 쓴다. 내가 글을 써서 조회수를 늘리고 돈을 번다면 빠르게 자극적인(?) 글을 많이 쓰는 게 중요하겠지만 나한테 블로그는 내가 학습하는 과정이자, 생각을 정리하는 과정이기 때문에 항상 글은 직접 작성하려고 한다.
하지만 글을 작성하는 데 번거로운 반복 작업이 있어서 편하게 글을 쓸 수 있는 도구를 만들고 싶었다. 오랫동안 글을 많이 작성했기 때문에 내가 글을 작성하는 방식은 어느 정도 고착화된 편이다. 나는 글 쓰는 게 직업은 아니므로 블로그를 운영한 초기부터 어느 정도의 기준은 정한 편이다. 글을 읽기 좋게 쓰려고 노력하지만 퇴고에 너무 많은 힘을 들이진 않는다. 보통 Sublime Text를 사용해서 Markdown으로 글을 작성하고, 적당히 퇴고한 후 발행하는 편이다.
글 작성 도구
블로그도 이제는 정적 사이트라서 서버가 없다. 어차피 글은 나 혼자 쓰고 로컬 외의 환경에서 작성할 일도 없기 때문에 서버를 사용하되 로컬에서만 쓰는 목적으로 만들었다. 이렇게 하면 보안 등을 걱정할 필요도 없다.
간단히 정리하면 정적 사이트 기반 블로그는 글마다 Markdown 파일이 있고, 이를 빌드해서 HTML로 만든다. 글 작성 도구에서 글을 발행했을 때 Markdown 파일을 만들어 주면 배포 빌드와 자연스럽게 이어진다.
1 2 3 4 |
|
글 작성 도구이지만 종종 기존 글도 수정해야 하므로, 기존 글도 관리하면서 글 작성에 집중할 수 있는 도구를 만들려고 했다. 나는 Markdown으로 글을 작성하는 게 익숙해서 WYSIWYG 에디터는 오히려 거추장스럽고, 요즘 많은 글쓰기 도구가 그러듯이 마크다운을 쉽게 작성할 수 있게 해주는 정도만 원했다. 그리고 Sublime Text에서도 그랬듯이 텍스트 에디터에서 Markdown을 작성할 때 제일 불편한 건 이미지 업로드라서 이걸 해결하고 싶었다.

대충 요청했더니 대충 만들어줬는데 적당히 마음에 들었다.(물론 저 프롬프트 한 번에 이걸 만들어준 건 아니고 대충 과정을 설명하는 거다.) Markdown-ish하게 만들어달라고 했더니 에디터에서 가장 유명한 CodeMirror로 구현해줬는데 만족스럽게 꽤 잘 다뤄주었다. 기본 형태는 커서가 있는 문단만 Markdown으로 보여주고, 다른 부분은 렌더링해서 보여주는 방식이다.
어느 정도 기본적인 레이아웃이 잡히자 디자인을 잡고 싶었다. 디자인이 붙어야 또 감이 잘 오는 부분이 있고 구체화를 하기 좋을 것 같았다.

Claude Design을 설치하고 시안을 요청했더니 4가지를 만들어주었다.




나중에 이런저런 개선을 하다 보니 또 고민되긴 했지만, 시안에서는 네 번째인 Mono가 마음에 들어서 이를 그대로 사용했다. 시안을 보여주려고 바로 웹페이지를 만들어 쉽게 비교까지 할 수 있게 하는 건 정말 잘한다.
왼쪽은 글을 작성하는 에디터이지만 오른쪽에는 렌더링된 프리뷰를 볼 수 있게 구성했다. Markdown이 충분히 직관적이라 보통 프리뷰를 띄워두고 글을 쓰진 않는다. 하지만 렌더링 특성 때문에 화면이 깨지거나 실수로 마크다운 문법을 깨뜨리는 경우도 있어서, 도구를 만드는 김에 우측에서 바로 렌더링된 결과를 프리뷰로 볼 수 있게 구성했다. 물론 에디터 자체도 Markdown-ish하므로 렌더링된 스타일에 최대한 가깝게 만들었다.
대부분의 Markdown 에디터처럼 코드 블록을 위한 ``` 를 입력하면 코드 블록이 생기도록 만들었다. 제품으로 나온 서비스에 비하면 처리가 좀 투박하지만 적당히 내가 쓰기에 불편하지 않은 정도로 구현했다. 글은 드래프트 상태로 여러 글을 작성할 수 있고 발행을 누르면 해당 위치로 폴더와 에셋을 옮기고 글번호가 최신+1로 할당되도록 만들었다. 기본적으로 파일 시스템에 데이터를 모두 쓰기 때문에 별도 DB는 필요 없고, 작성한 글이 없어지지 않게 자동 저장되도록 만들었다.
파일 업로드
앞에서도 말했듯이 텍스트 에디터로 Markdown을 작성하는 건 아무런 불편함이 없지만 파일 업로드는 항상 수동으로 해야 했다.
보통은 글을 쓰면서 이미지가 들어갈 위치에 파일명을 표시해두었다가 Textcube에 글을 올리면서 파일첨부로 해당 부분을 일일이 교체했다. 그리고 블로그에 적절한 사이즈와 이미지 최적화를 위해 그래픽 에디터로 일일이 수정해줘야 했다.
도구를 만드는 김에 이러한 과정을 모두 개선하고 싶었다. 그래서 에디터에 이미지 파일만 드래그 앤드 드롭하면 바로 마크다운 문법으로 삽입해주고, 빌드할 때 사용할 수 있도록 파일 복사도 바로 해줄 수 있게 만들었다. 예전에는 그래픽 에디터로 크롭도 하고 수정도 하고 그랬지만 요즘은 스크린샷 도구들이 좋아서 간편하게 이미지를 만들 수 있으므로 드래그앤드롭으로 충분했다.
이제 모니터가 너무 좋아져서 device pixel ratio(DPR)이 맞지 않으면 이미지가 너무 뿌옇게 보이기 때문에 언젠가부터는 3x 이미지로 이미지를 첨부하고 있다. HTML에는 srcset을 지원하지만 Textcube에는 이런 기능이 없었기 때문에 그냥 3x 이미지로 합의봤지만 모바일 등 화면이 작은 곳에서는 네트워크가 낭비되긴 한다.
파일 업로드를 구현하는 김에 srcset도 지원하고 싶었고, 이미지만 던지면 본문의 width에 맞게 계산해서 1x, 2x, 3x 이미지를 만들고 srcset으로 빌드되도록 만들었다. 이것만으로도 너무 편하게 느껴졌다. 그래도 이미지 계산이 하드코딩된 값이 아니라 현재 디자인의 본문 width를 따르도록 지시했다. 나중에는 AI가 더 좋아질 테니 굳이 안 그래도 될 것 같지만, 그래도 신경 쓰여서 그렇게 만들게 했다.
Assist 사이드바
도구를 만들기 시작했으니 반복 작업은 최대한 자동화하고 싶었다. 당시에는 한달에 두번씩 올리는 뉴스레터를 작성하고 있었기에 Textcube에도 있던 템플릿 기능에서 아이디어를 가져와서 글의 패턴이 있는 글을 관리하도록 만들었다.

뉴스레터를 선택하는 경우 마지막 뉴스레터를 참고해서 번호를 올려주고 내가 뉴스레터를 올리는 날짜는 정해져있으므로 그 날짜에 맞춰서 제목도 자동으로 만들어주도록 구성했다.
뉴스레터를 AI로 자동화하기 좋을 거라는 얘기도 많이 들었지만, 뉴스레터도 발행이 목적이라기보다 내가 글을 읽는 게 목적이라서 뉴스레터 자체는 직접 작성하고 반복 작업을 하나씩 구현했다.
버전 업데이트 찾기
뉴스레터에는 내가 관심가지는 프로젝트의 최신 릴리즈를 올리는 부분이 있는데 별거 아닌 작업이지만 해당 라인의 형식이나 기존 설명을 찾아오는 과정이 번거로운 과정이었다. 예를 들어 Angular의 새버전이 나오면 이전 뉴스레터들을 검색해서 Angular 릴리스 글을 찾아서 이를 가져다가 붙여놓고 버전만 링크만 올려서 작성하곤 했다.

이 부분을 간편하게 처리하려고 프로젝트 이름이나 새로운 릴리스 링크를 올리고, 팝업 메뉴로 버전 업데이트를 선택할 수 있게 했다. 대부분은 기존에 올린 프로젝트를 다시 올리는 경우다. 글 쓰는 중에 키보드에서 손을 떼고 싶지 않아서 단축키도 구현했다.

이전 뉴스레터를 뒤져서 같은 프로젝트라고 판단한 버전 업데이트의 목록을 가져오게 했다. 여러개 가져오게 한 것은 프로젝트 이름이나 링크 등으로 완벽하게 찾아내지 못하기 때문에 내가 선택하게 했다. 원하는 것을 선택하면 이를 가져와서 분석한다.

그동안 운영해 보니 링크가 제대로 연결되지 않거나 공식 프로젝트 링크가 바뀌는 경우가 있었는데, 이 부분도 일일이 체크하기 귀찮아서 자동으로 처리하게 했다.
LLM 사용하기

아까 어시스트 사이드바 상단에서도 나왔지만 AI 모델을 선택할 수 있게 구성했다. 꼭 이렇게 할 필요까지는 없지만 처음 만들어 보면서 어떤 모델이 더 잘 해주는지 비교하고 싶었기 때문에 내가 구독 플랜을 가지고 있는 Gemini, Claude, Codex를 선택하고 모델이나 Effort를 선택할 수 있게 했다. 이후 글을 쓰면서 어떤 모델이 적당한지를 비교해보고자 했고 디버깅 목적으로 대화모드도 추가하고 걸린 시간이나 비용도 보이게 했다. 이게 서비스 목적이 아니라 내 로컬에서만 사용하는거라서 로컬에 CLI가 다 설치되어 있다는 전제하에 로컬 CLI를 사용하게 했는데 잘 동작해서 만족하고 있다.
링크 자동으로 걸기
Assist 사이드바에는 여러 가지 기능을 넣었는데, 웹의 핵심 중 하나는 링크라고 생각해서 가능한 한 링크를 많이 거는 편이다. 하지만 글을 쓰다 보면 특정 회사나 기술 등을 언급할 때가 많은데, 이를 일일이 검색하고 링크를 찾아 거는 건 번거로워서 잘 안 하게 됐다.

그래서 단어를 적고 링크 연결하기 메뉴만 누르면 자동으로 이전 글에서 해당 링크를 찾거나 공식 링크를 찾도록 만들었다. 완전 자동화하기 보다는 내가 한번 확인하고 삽입하도록 만들었고 아까 말한대로 링크는 바뀌는 경우도 많으므로 리다이렉트도 확인하도록 했다. 이전 글에 있는 링크는 잘 찾지만 그렇지 않으면 공식 링크를 잘 못찾는 경우도 많아서 개선은 필요하지만 시간이 지나면서 링크를 더 많이 쌓아가면 쉽게 해결될 문제라고 생각한다. 링크를 확인하고 싶을때도 있어서 macOS의 터미널처럼 에디터에서도 cmd + 클릭으로 링크를 열 수 있게 했다.
같은 글에서는 같은 기술이나 회사 등을 언급할 확률이 높기 때문에 한 번 링크를 걸면 이후에 같은 단어를 작성할 때 자동으로 링크가 걸리도록 했다. 평소에는 귀찮아서 첫 단어에만 한번만 골곤 했는데 쉽게 처리할 수 있게 되었다.
이미지 대체 텍스트 생성

추가로 Assist 사이드바를 만든 김에 파일 업로드를 하면 자동으로 이미지를 분석해서 대체 텍스트도 넣어주도록 만들었다. 이미지의 대체 텍스트도 중요하게 생각하므로 예전에는 최대한 이미지 첨부할 때마다 대체 텍스트를 작성했지만 생각보다 잘 작성하기 어렵기도 하고 귀찮아서 언젠가부터는 잘 작성하지 않았기 때문에 이미지만 올리면 자동으로 분석해서 넣어주도록 만들었다. 이런건 정말 AI의 큰 혜택인거 같다.
메타데이터 관리
블로그 글에는 카테고리, 발행 시간, 태그 등 메타데이터 관리가 필요하기 때문에 이러한 데이터가 저장되는 Frontmatter를 관리할 수 있도록 상단에 구성했다.

많은 글쓰기 도구에서 내가 글에 대한 통계 정보를 넣었다. 엄청 유용하진 않아도 내가 이런 기능을 좋아한다. 글자 수, 단어 수, 문장 수 등을 넣었다. 대부분의 글쓰기 도구가 영어권 도구라 통계가 한국어에서는 어색한 경우가 많은데, 이것도 Claude와 대화하면서 한국어에 적절한 값을 논의했다. 이 글이 어느 정도 분량이고 읽는 데 얼마나 걸리는지, 읽는 난이도는 어떤지 등을 통계로 정리해서 보여주도록 했다. 이게 엄청난 도움까지는 아니어도 공간도 남고 글을 쓰면서 어느정도 분량의 글을 쓰고 있는지 파악하는 용도로 사용한다.
또한, 에디터는 Markdown-ish하게 만들었지만 가끔은 Raw 마크다운이 필요하기 때문에 Raw로 볼 수 있는 기능도 만들어 두었다.
Code 블록 사용 가이드

코드 블록에는 언어를 지정하는 기능과 시작 줄 번호를 지정하는 기능이 있다. 자주 안쓰는 언어나 줄번호 기능은 가끔 사용하려고 할때 헷갈리는 경우가 있어서 코드블록에서 메뉴를 누르면 바로 사용방법을 볼 수 있게 만들었다. 첨엔 리스트만 보여달라고 했는데 알아서 검색도 되게 만들어주어서 좋았다.
Revise 사이드바
보통 글을 다 쓰고 나면 퇴고를 세밀하게 하진 않고 적당히 검토한 후에 한국어 맞춤법 검사기를 돌리는 편이다. 내 글이 좀 장황한 경향도 있고 문장도 깔끔하진 않지만 퇴고에 너무 많은 시간을 쓰고 싶진 않았다. 맞춤법 검사기도 오타 등은 수정하기 위해 하고 있지만 Markdown으로 글을 쓰다 보니 맞춤법 검사 돌릴때도 마크다운 문법도 같이 들어가서 귀찮기도 했다.
Revise 사이드바는 퇴고 과정으로 AI로 자동화하기 위해 만들었다.

LLM을 이용해서 맞춤법 검사를 해주도록 했고 가이드라인 파일은 Markdown으로 따로 만들어서 이후 필요한 규칙은 추가할 생각이다. 꽤 잘 잡아주긴 하는데 다 잡아주는건 아니라서 당분간은 맞춤법 검사와 같이 비교해 볼 생각이다.
검사할 때는 Markdown 문법을 무시하도록 했고, 어디를 고치는지 바로 파악할 수 있게 했다. 맞춤법뿐 아니라 표현도 더 간결하고 깔끔하게 교정하도록 했다.

교정된 내용을 적용할 때 앞뒤의 맥락을 같이 봐야하는 경우가 있어서 해당 영역을 선택하면 에디터에서 해당 위치로 바로 가게 만들어서 쉽게 교정 사항을 적용할 수 있게 했다.

맞춤법이나 표현을 문장단위로 개선한거 외에도 글에 대한 평가도 하도록 했다. 그동안은 시간 관계상 포기했던 부분이지만 AI의 도움을 받아서 계속 피드백 받으면서 개선하다보면 글을 더 잘 쓸 수 있는데 도움이 될것으로 기대하고 총평도 계속 개선할 수 있도록 가이드 파일을 별도로 분리했다.
OpenGraph 사이드바
블로그를 정적 사이트로 바꾸면서 기존에 방치했던 OpenGraph 이미지를 개선했다. 이미 배경화면서 제목과 글의 도입부로 이전보다는 훨씬 나아졌지만 글 작성 도구가 생긴만큼 더 개선할 수 있겠다는 생각이 들었다. 최근 글을 발행했을 때 오픈 그래프 카드가 맘에 들지 않은 경우가 생겨서 프리뷰 역할도 할 겸 사이드바를 만들었다.

오픈그래프에 글의 도입부가 나오는 건 큰 의미는 없는거 같아서 OpenGraph 사이드바에서 LLM으로 글을 요약해서 정리해서 카드 영역에 맞게 추가하도록 했다. 여기도 가이드 파일을 분리해두었지만 요약이 맘에 들지 않을수도 있어서 수동으로 수정해서 적용할 수 있는 기능도 만들었다.
여기서 생성한 OpenGraph의 본문은 Frontmatter에 추가해 두어서 나중에 정적 사이트 빌드시에 이 내용으로 OpenGraph 이미지를 생성할 수 있도록 만들었다.
Electron 앱
블로그를 새로 만들면서 Golang으로 만들었고 프로덕션용 빌드를 하지 않아도 개발에 사용하도록 개발 서버를 만들어서 사용하고 있었다. 안그러면 기능 추가할때마다 빌드해야 한다. 그래서 이 서버에 특정 경로로 접속하면 글 작성 도구가 뜨도록 만들고 있었고 어디에 배포할 게 아니라 로컬에서만 사용하니까 문제도 없었다.
기능을 구현하다보니 서버로 띄워서 웹브라우저에서 사용하는게 아니라 별도의 앱으로 있으면 좋겠다는 생각이 들었다. 만드는 중에는 큰 상관없었지만 글 작성 관점으로 보면 글쓰려고 서버를 띄우고 접속하는게 번거롭게 느껴졌고 글 쓰면서 웹 브라우저에서 검색하는 경우가 많다보니 작성도구도 브라우저에 있는게 좀 불편했다. 모니터에서 옆에 브라우저 열어두고 글 쓰기 도구를 따로 열어두는게 글쓰기가 훨씬 편했다.
어차피 웹으로 만든거라서 Electron으로 패키징하면 어렵지 않겠다고 생각했는데 Claude는 이미 Golang을 쓰고 있으니 훨씬 가벼운 webview_go를 사용하면 Go 바이너리 하나로 가볍게 만들수 있다고 추천했다.
처음 봤지만 추천대로 일단 webview_go로 만들었다. 그냥 패키징하는것이니 앱은 금방 나오긴 했는데 이게 Chrome에서 개발하다가 웹뷰가 되어서 그런지 세부 기능이 동작 하지 않는게 계속 발견됐다. 하나씩 수정을 요청하다가 이미 다 구현하고 고쳤던 기능을 다시 수정하는걸 반복하다 보니 짜증이나서 Electron을 재 검토하게 했다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
설계 문서에 의존성 최소화하고 가볍게 만들겠다고 해서 그런지 이번에는 chrome --app으로 실행하는 방식을 추천했다. 이 방법도 직접 해본적은 없지만 Chrome과는 별도로 실행되기 원했기 때문에 그냥 Electron으로 만들도록 시켰다.
서버로 실행할 때는 워킹 디렉토리가 정해지지만 앱으로 띄우는 경우에는 컨텐츠가 있는 프로젝트 폴더를 지정해야만 사용할 수 있으므로 설정 기능을 넣어서 워킹 디렉토리를 지정할 수 있게 했다.

앱으로 설치하고 글을 쓰고 싶을 때 앱만 실행하면 서버도 자동으로 실행되니까 글쓰기가 훨씬 좋아졌다. 기존 브라우저에서 접속하는 방식도 기능 개발을 할때는 편하므로 그대로 남겨두었다.

블로그의 메인 배경 이미지를 이용해서 앱 아이콘을 만들었다. 아이콘은 처음에는 너무 맘대로 그려버려서 이미지 존중하라고 했더니만 적당한 시안 중에서 골랐다. 앱 이름은 따로 알려주지 않았는데도 적당히 잘 지어주어서 그대로 사용했다.
에필로그
개인적으로 사이드 프로젝트를 안한지가 꽤 오래되었는데 퇴근하고 와서 뭔가 계속 만드니까 기분이 꽤 괜찮다. 블로그도 오랜만에 손대는 거라 할게 많지만 또 뭔가 계속 만들기 시작하니까 다른 아이디어도 조금씩 다시 생겨나고 있다. 은근히 AI로 사이드 프로젝트하는거에 재미를 붙여하고 있는 느낌이라 좋다.
코딩 에이전트로 개발하면서 내 성향을 더 깨닫게 됐는데, 코딩 에이전트가 혼자서 진도를 나가기보다는 세부 사항을 내가 다 제어하기를 더 선호한다는 것을 느꼈다. 글 작성 도구에서는 내 원하는 요구사항이 꽤 명확해서 그럴수도 있지만 조금씩 진도를 나가면서 LLM과 의논도 하고 LLM 준내용 확인하고 찾아도 보면서 계속 발전시켜나가는 게 훨씬 더 좋았다.(자리에 없을 때 일 시키는건 더 연구해 봐야겠다.)
그러다 보니 속도가 빠르진 않아서 5월 중순부터 작업을 시작해서 3주 정도 걸려서 사용할 수 있을 정도로 만들었다. 실제로 써봐야 알게 되는 게 있어서 최근 2개의 글을 작성 도구로 글을 써보면서 느껴지는 불편함을 개선했다.
| Models | Input | Output | Cache Create | Cache Read | Total Tokens | Cost (USD) |
|---|---|---|---|---|---|---|
| opus-4-7 opus-4-8 |
626,115 | 5,542,264 | 27,030,261 | 2,624,332,639 | 2,657,531,279 | $1,724.16 |
저번보다는 많이 사용했다. 지금 Claude Max 5x 요금제를 사용하고 있는데 토큰을 다 쓰기가 쉽지 않아서 진짜 많이 사용하는 사람은 어떻게 사용하는지 궁금해졌다. 토큰이 남아서 사실 맘놓고 쓰고 있는 상황이다.
블로그도 정적 사이트로 바꾸고 글 작성 도구까지 만들고 나니 아주 만족스럽다.
Comments