
"수연님이 찾아오도록 해요" — 스터디 슬랙 채널의 이 한 마디에서 시작됐다.
서버비 0원 원칙 아래, 10명 스터디원의 피드백을 하나하나 기능으로 만들어가며
한 달 만에 8개 커맨드와 Focus Tree 랜딩페이지를 갖춘 슬랙봇이 된 집중요정의 성장기.
Part1. 쓰던 봇이 유료로 전환됐다고?
0장: 그냥 내가 만들어볼까? (1월 6일)
25년 12월 11일, 스터디 슬랙에 시간 기록용 Remoty 봇을 도입했다.

그런데, 이 봇을 사용한지 1달도 지나지 않아 문제가 생겼다.
Remoty가 AttendanceBot에 편입된다는 것이었고,
AttendanceBot에는 무료 플랜이 없었다. (이런!)

종근님께서는 다른 대체재들을 제시하셨다.
하지만, Clockify는 슬랙 연동이 안됐고,
Sim을 연동해보고 싶지만, 그럴 리소스가 부족했다.
우리 스터디에 필요한 것은
'각자 공부하는 시간을 기록하는 것' 뿐이었다.
Remoty에서도 /in /out 밖에 쓰지 않았으니까.
그래서, 나는 '스스로 타이머 키고 시간 잴까요?'
같은 말을 던져보았다.
그런데, 이걸 본 스터디원이 한 마디를 던졌다.

이 말을 듣고 곰곰이 생각했다.
'그러게, 그냥 내가 만들어볼까?'
Remoty는 좋은 봇이었지만, 우리 스터디에 완전히 딱 맞진 않았다.
하지만, 이렇게 된 김에 스터디원인 내가 직접 만든다면?
정말 우리 스터디에 착붙는 봇을 만들어서 운영 효율을 높일 수 있지 않을까?
토이 프로젝트는 의외로 사소한 계기에서 시작된다.
1장: 만약 슬랙 봇을 만들고 싶다면, 어떻게 해야 해? (1월 7일)
10명이 함께하는 스터디 그룹.
각자 공부 시간을 기록할 때 매번 수동으로 적기는 귀찮다.
슬랙에서 /start 치면 시작, /end 치면 끝.
이 정도면 충분하지 않을까?
머릿속에서 열심히 구상했다.
이틀 뒤, 퇴근 하자마자 Claude에게 질문을 던졌다.

Claude가 Slack API 설정부터 코드가지 단계별로 안내해줬다.
처음에는 Node.js + Bolt SDK를 추천해줬다.
그런데, 나에게 가장 중요한 것은 '무료'였다.
우리는 지금 월 구독료를 피하고 싶어서 직접 만들기까지 하는건데,
여기에서 서버비가 필요하다면 말짱 도루묵이잖아!

서버비 없이 무료로 배포하기
서버비 걱정 없이 영원히 무료로 돌릴 수 있는 방법.
Claude가 제안한 답은 Cloudflare Workers+KV 였다.
일 10만 요청 무료, KV 스토리지 무료, 콜드 스타트 거의 없음.
하루에 50건 요청을 한대도, 무료 한도의 0.05% 수준이었다.
어차피 특정 유저의 시작과 종료 값만 저장하면 되니까 key-value로도 충분했다.
서버비 무료로 어디까지 가능할까! 무료의 한계를 시험해보기로 했다.
1. Cloudflare 가입 -> Workers & Pages -> Create Worker
2. KV 네임 스페이스 생성
3. Worker 설정에서 KV 바인딩 추가
4. 코드 붙여넣고 배포
"Hello World"가 떠 있던 곳에 집중 시간 트래커가 올라갔다.

이름 짓기
일단 Slack에 이 봇을 설치하려면, 이름이 필요했다.
봇에 이름을 붙이는 과정도 재밌었다.
처음에는 "공부 도우미" 같은 평범한 이름을 생각했는데,
봇에 캐릭터성을 부여하는 아이디어가 떠올랐다.
단순한 '툴' 아니라, 캐릭터성을 가진 '동반자'로 느껴질 때
사용자가 더 애착을 가지고 몰입할 수 있다고 생각했다.

Claude와 아이디어를 주고 받다가 "요정"이라는 컨셉을 도출해냈다.
그리고, "집중요정"이라는 이름이 내 마음에 꽂혔다!
영문명은 Focus Fairy로 결정했다.
이미지 생성
슬랙 봇에 프로필 사진이 필요하니, 이젠 이미지를 생성해야 했다.
디자이너님이 이미지 생성에 GPT를 추천했던 것이 떠올라, chatGPT를 프롬프팅 하기 시작했다.
처음엔 단순히 '집중요정'이라는 키워드를 넣었다.
하지만, 한 번에 원하는 결과물이 나오지 않았다.
내가 강조하고 싶은 '시계'와 '집중' 키워드가 강조되어야 한다고 명령하고,
모래시계를 끌어안고 있는 귀여운 요정의 모습을 제시해달라고 했다.
5번 이상의 시도를 거쳐 아래와 같은 결과물을 얻었다.

아날로그 감성을 부여하려면 '도트 캐릭터'를 선택하는 편이 더 좋을 것 같았다.
ai의 애매한 완성도로 불쾌한 골짜기에 머무는 것보다,
애초에 뭉개짐이 허용되는 8비트로 비슷한 이미지를 찍어내고 디벨롭하는 데 낫다는 판단이었다.
그래서 위 캐릭터를 바탕으로 도트 이미지를 생성해달라고 했다.
꽤 괜찮은 결과물이 나왔다.


슬랙 봇 등록하기
이제 모든 게 준비 되었으니 슬랙에 봇을 등록할 차례였다.
바로 스터디 슬랙에 등록하기 보다는 내 개인 채널에서 테스트를 해보는 게 좋을 것 같았다.
그래서 바로 개인 계정으로 슬랙 채널을 하나 만들고 봇을 등록했다.

슬랙 봇을 처음 등록해봤는데, 생각보다 어렵지 않았다.
이 봇에게 허용할 권한을 정하고,
각 커맨드를 입력했을 때 어느 도메인으로 요청을 보낼지 정하기만 하면 됐다.

와, 진짜 커맨드 입력으로 뭔가 작동했다.
Cloudflare의 KV로 이동해서 확인해보니, 진짜 데이터가 쌓이고 있었다.
출시 전 점검하기
1. 워크스페이스 별로 저장된 기록을 분리해서 식별할 수 있을까?
최초에는 유저 아이디와 시간 만을 저장했다.
어느 워크스페이스에서 통계를 출력해도, kv에 기록된 모든 워크스페이스의 통계가 나올 터였다.
그건 내가 원하는게 아니었다.
난 워크스페이스마다 별도로 통계가 관리되는 걸 기대했다.

그래서 KV에 'teamID'를 포함시켜서 기록하도록 수정했다.
만약 실데이터가 쌓인 이후에 수정했다면, 이전 기록들은 통계에서 누락됐을 것이다.
아무리 MVP라고 해도 이런 점은 놓치지 않아서 다행이다!
2. 과다 요청 요금 부과? 외부 유출 문제점?
바이브 코딩으로 무언가 만들었다가 서버비가 과다 부과 되는 이슈들을 많이 봐서
나에게도 그런 일이 일어나지 않을지 두려웠다.

하지만, 무료 티어가 앞서 말했듯 일 10만건 수준이었기 때문에 문제 없어보였다.

그리고 만약 유출된다고 해도, 무료 한도를 소진하는 정도의 이슈가 발생할 것이고,
이상한 데이터가 저장되는 이슈는 있겠지만 이 정도는 사후 삭제로 대처가 가능해보였다.
그리고 만약 이걸 외부로 출시한다면 Signing Secret을 추가해서 대응하기로 했다.
Slack에서 온 요청만 처리하도록 하는 것인데, 지금 수준에서는 오버엔지니어링이라고 판단해 킵해두었다.
2장: 시간체크 슬랙봇 만들어왔습니다 (1월 8일)

두근두근... 스터디 슬랙에 설치하고 커맨드를 입력했다.
집중요정이 스터디 채널에서도 잘 작동했다.

그 사건의 발단 스레드에 찾아가 댓글을 남겼다.

종근님의 반응을 보자마자 마음 속에서 성취감이 차올랐다!
역시 시니어의 칭찬은 주니어를 춤추게 한다!
그리고, 곧이어 바로 개선 요청이 들어왔다.
기간별 통계 뽑기

현 시점에서 통계 관련 커맨드는 /weekly가 있었다.
하지만, 날짜를 지정할 수는 없었고, 이번주(월~일) 또는 저번주(월~일) 이렇게 기간이 묶여있었다.
그런데, 기존 Remoty는 날짜를 정해서 통계를 낼 수 있었기 때문에
집중요정도 그 기능을 지원하는지 물어보신 것이었다.
아마 이건 데이터 저장 형식에 영향이 가는 수정일 것이라는 느낌이 들었다.
그럼 실 데이터가 쌓이기 전에, 지금 바로 고쳐야 한다!!!
늦은 시간이었지만, 이 문제만큼은 바로 해결해두는 게 좋아보였다.

Claude 역시 저장 방식 변경을 제시했고, 빠르게 수정해서 배포했다.
그런데 기존에 저장된 KV 값을 자동으로 지울 순 없었고,
하나씩 골라서 수동으로 지워야 했다.
귀찮으면 KV를 새로 만들고 새롭게 바인딩하는 방안도 제시받았지만,
10개 미만의 데이터여서 손수 지워도 무리는 없었다.
만약 실데이터가 꽤나 쌓인 상황이었다면 번거로웠을 것 같다...

KV 저장 방식과 통계 추출 방식만 살짝 수정하면 돼서
20분 만에 바로 반영할 수 있었다!
빠른 제보와 빠른 피드백!!
성공적인 MVP로 한걸음씩 나아가고 있었다.
Part2. 피드백을 반영해 진짜 제품처럼
1장. 스터디원의 피드백이 기능이 되다 (1월 9일)
배포 다음날, Remoty를 사용하던 팀원이 실수를 했다.

전날 check-in을 한 후, check-out을 까먹은 것이다.
이렇게 되면 실제로 공부한 시간은 2시간 이더라도, 10시간이 넘게 기록된다.
성과 측정 지표가 아니기에 엄격할 필요는 없지만,
실제 본인의 집중 시간과 다르게 기록 된다면 장기적으로 스터디의 흥미를 잃을 수 있었다.
종근님은 이 부분을 바로 캐치하신 후, 새로운 제안을 던지셨다.

안그래도 팀원들이 종료 코멘드를 까먹은 경우를 대응하고 싶었기에,
시간나는 대로 해결해보겠다고 했다.
집중 종료를 깜빡한 유저 케이스를 보완해보자
퇴근 후, 바로 claude를 켜서 문제상황과 제안에 대한 검토를 요청했다.

하지만, Claude는 그 방법을 수행하기 위해서는 상시 서버가 필요하다고 했다.
무료 서버를 어떻게든 지켜보려는 나로서는 선택하기엔 어려운 선택지였다.
대신 다른 방안을 구현해보기로 했다.
Claude와 함께 집중 종료를 누락하는 여러 시나리오를 구상하고,
가장 발생 가능성이 높은 시나리오를 골라서 더 디벨롭했다.
'방식 b의 시나리오를 보여줘'
'경고 문구를 유저 직관적으로 수정해줘'
'버전 c를 디벨롭 해줘'
'c-3 시나리오가 현실에 가까워. 이를 더 다듬어줘'

claude와 티키타카하며 이런 최종안을 만들었다!
/end를 입력할 때, 집중 시간이 6시간 이상인 경우,
진짜 시간을 입력할 수 있도록 되묻기로 했다.
문구를 고쳐서 캐릭터성을 살려보자!
"요정이 고쳐준다"라는 문구를 만들고 보니,
다른 커맨드도 "요정스러운" 컨셉으로 수정하는 게 캐릭터에 몰입하기 좋아보였다.

뭔가 '집중요정'의 '캐릭터성'이 살아나는 게 보였다.
그렇다면, 좀 더 해보자.

/end 커맨드를 입력했을 때 랜덤으로 따뜻한 문구를 띄워보기로 했다.
처음에 뽑아준 문구에서
'요정 컨셉에서 벗어나는 것, 경쟁을 유도하는 것, 감성적이지 않는 것'을 걸러내라는 피드백을 먹였다.
"오늘도 묵묵히 해낸 당신, 멋있어요 🌿"
"작은 노력이 모여 큰 결과가 돼요 🍀"
"포기하지 않는 당신을 응원해요 🌙"
"오늘의 나에게 수고했다고 말해주세요 ☁️"
"천천히, 하지만 꾸준히. 잘하고 있어요 🌱"
"한 뼘 더 성장한 하루였어요 ✨"
"요정이 오늘도 당신을 기억할게요 🧚"
"지치지 않게, 요정이 곁에 있을게요 💫"
"쉬어가도 괜찮아요. 다시 시작하면 돼요 🌼"
그 결과 이런 문구들을 뽑아낼 수 있었다.
AI로 생성은 할 수 있지만,
결국 감성에 와닿는 건 인간의 마지막 손길이 필요하다는 생각이 들었다.
이모지로 더 캐릭터성을 살려보자!
문구는 따뜻한데, 이모지는 차갑다는 생각이 들었다.
커스텀 이모지를 도트 감성으로 넣어두면, 캐릭터성에 더 동화될 것 같다는 생각이 들었다.
슬랙봇 응답에도 커스텀 이모지를 쓸 수 있는지 물으니, 가능하다고 했다!

저번에 집중요정 도트 썸네일을 뽑아줬던 gpt 세션에 다시 방문했다.

오! 반짝반짝한 도트 이모지를 바로 뽑아줬다.
그런데 한 이미지에 이모지를 9개씩 뽑아줘서 자르는데 애를 먹었다.
(한땀한땀 기본 자르기 툴로 잘랐다)
그런데 며칠 뒤 디자이너님께 물어보니,
"오브젝트 별로 한장씩 이미지로 만들어줘. 이미지를 변경하지 말고" 라고 말하면 된다고 알려주셨다.

역시 질문은 전문가에게........... 한 방에 해결됐다
(와중에 다른 팀원은 가위로 오리라고 함. 저기요!)
슬랙 이모지를 벌크로 등록할 수 있는 chrome extension도 알게 되었다.
https://chromewebstore.google.com/detail/neutral-face-emoji-tools/anchoacphlfbdomdlomnbbfhcmcdmjej
하지만, 이것도 내가 하나하나 손으로 등록을 완료한 후 알게 되었다. (ㅎㅎㅠ)
하지만 이번에 lesson & learn 했으니 다음부턴 빨리할 수 있을 것이다!
긍사! (대충 긍정적 사고라는 뜻)
개선 기능 출시 - 실제로 작동!

처음에 제안 받은 방법은 구현하지 못했지만, 대안을 개발했음을 기록으로 남겨두었다.

개선 기능을 출시한 날, 일부러 /end를 입력하지 않고 자고 일어난 후 다음날 아침에 테스트를 해보았다.
8시간이 넘게 기록된 상황이었지만, 다시 30분으로 정정해서 입력할 수 있었다!

이 기능 개선을 출시한 후 9일 뒤,
실제로 집중 종료 커맨드를 전날 수행하는 걸 까먹은 팀원이 다음날 오전에 집중 종료를 입력했다.
개선된 기능 덕분에, 10시간이 아닌 실제 집중 시간인 4시간 30분만 기록할 수 있었다.
감사 인사도 해주셔서 더 뿌듯했다!
2장. 점점 기능 제안이 복잡해져! github로 이사가자! (1월 12일)
한창 집중요정을 디벨롭하는 재미에 들린 주간,
밥을 먹다가 팀원의 피드백을 들었다.
/start를 치면 내 커맨드가 스레드에 남는데, 안 남게 할 수 있어요?
/start /end 같은 커맨드는 스레드에 안남는 게 더 깔끔해보일 것 같다는 의견이었다.
그러고보니, 기성 슬랙봇 제품들은 커맨드가 스레드에 노출되지 않았던 것 같다!
어떻게 개선할 수 있을까?
슬랙의 슬래시 커맨드 응답 방식에는 크게 두 가지가 있다.
response_type: in_channel로 보내면 전체 공개,
response_type: in_ephemeral로 보내면 나만보기
그런데, 나는 '명령어 입력은 나만 보이되, 봇 메시지는 전체 공개'를 하고 싶었다.
Claude가 처음에 'show command in channel' 옵션을 설정하면 된다고 했지만,
그런 옵션은 찾을 수 없었다.
결국 Slack의 'chat.postMessage' api를 직접 호출해야 했다.
슬래시 커맨드 응답은 ephemeral로 간단히 '집중 시작!'만 보여주고,
별도로 Bot token을 사용해 채널에 전체 공개 메시지를 보내는 방식이다.
또, 유저별 집중 시간 통계 레포트를 작성할 때
지금까지 Workers&Pages에 직접 배포하며 한 파일로 잘 버텨왔는데,
이젠 GitHub로 옮길 때가 됐다는 생각이 들었다.
마침 Claude의 대화 세션도 한계에 도달해서 대화를 이어갈 수 없었다.
새 세션을 켜서, Cursor Chat을 쓸지, Claude에서 할지 질문했다.
Claude는 솔직하게 Cursor가 낫다고 말했다 (ㅋㅋ)
Cursor가 더 나을 수도 있어요. 저는 코드를 보여주면 수연님이 복붙해야 하지만,
Cursor는 파일을 직접 만들고 프로젝트 전체를 보면서 작업할 수 있어서요.
뭐 사실, Claude에게 file system 직접 접근 권한이 있어서,
마음만 먹으면 Claude로 계속 작업을 이어나갈 수 있지만, Cursor IDE만큼 편하진 않다.
대신 Cursor에서 쉽게 시작할 수 있도록 프로젝트 컨텍스트를 정리해달라고 했다.
기술 스택, 주요 기능, 현재 상태를 요약한 문서를 만들어줬다.
이걸 그대로 Cursor에 넘겨서 작업했다.
사실 Cursor에서 Opus 모델(claude꺼)을 써서 작업할거라 그게 그거인가 싶기도 하다.
GitHub로 이사하기
npm init -y
npm install wrangler typescript @cloudflare/workers-types --save-dev
npx wrangler init
Cloudflare Workers 개발/배포를 지원하는 Wrangler를 설치했다.
그리고 src/index.ts에 있는 기본 파일을 내 집중요정 파일로 덮어씌웠다.
GitHub에 올릴 때 공개 레포로 만들 생각이었기 때문에, 커밋에 신중해야 했다.
뭐가 노출되면 안되는지 중요했다.
wrangler.jsonc에 KV 바인딩 정보를 입력했는데 문득 서늘한 기분이 들었다.
KV ID 이거 커밋해도 되는거야?
KV ID는 그냥 건물 주소같은 식별자일 뿐이라고 말했다.
주소를 안다고 건물에 들어갈 수 있는게 아니 듯,
KV ID를 알아도 내 Cloudflare 계정으로 로그인 안 하면 접근 자체가 불가능하다고 했다.
KV에 접근하려면 이 셋 중 하나는 있어야 한다고 했다.
- Cloudflare 대시보드 로그인
- Wrangler CLI 인증
- API Token
중요한 정보들은 다 SECRET KEY로 설정해서 문제는 없었다.
채널에 봇을 꼭 초대합시다
예상치 못한 삽질이 꽤 컸는데...
개인 워크스페이스에서는 '커맨드는 ephemeral, 봇 응답은 전체공개'가 잘 작동했는데,
스터디 슬랙에는 자꾸 커맨드가 메시지로 남았다!!!
슬랙 채널에서 계속 테스트하기엔 너무 늦은 시간이었다.
(팀원들에게 알림 갈까봐 막 테스트 하지도 못함)
허둥지둥하다가 비공개 채널을 만들면 된다는 사실을 떠올렸다.
서둘러 #집중요정_테스트 채널을 팠다.

그때의 기록을 보면...
새벽 2시에 눈물을 흘리며 /start와 /end를 반복하던 내 모습이 남아있다.
(등에서 땀나면서 개발함 ㅋㅋㅋㅠㅠㅠ)

하지만, 이건 슬랙 봇이 채널에 초대받지 못한 손님이었기 때문임을 알게 되었다.
봇이 채널에 메시지를 보내려면 명시적으로 채널에 초대되어 있어야 한다.
Slack API를 써서 ephemeral message를 보내려면 채널에 꼭 초대되어 있어야 한다.
버그 발견! 일->월로 넘어가는 순간의 누적 시간

GitHub 이사 시점과 맞물려 또 하나의 버그가 발견됐다.
일->월로 넘어가는 시간에 이번주 누적 시간이 0분으로 표시되는 버그였다.

이에 대해 Opus4.5는 이런 아이디어를 내놨다.
일->월에 걸쳐 기록된 세션은 월요일이 포함된 주간의 세션으로 인정할까요?
흠, 근데 보통 '일요일 시작, 월요일 종료'는 '일요일 공부'로 인식하지 않나?
차라리 시작한 시점을 기준으로 두고 저번 주 시간으로 기록하는 걸 opus4.5에게 제안했다.
그랬더니, 이런 답변을 내놓았다.
일->월에 걸쳐 기록된 세션은 일요일이 포함된 주간의 기록으로 인정하고,
이 케이스에만 '지난 주 누적 시간'으로 표시할까요?
괜찮은 생각 같았다!
일단 수정하고, 제보 들어오면 또 고치기로 했다.

일주일 뒤 일->월 넘어가는 구간에 직접 테스트해봤을 때, 잘 작동했다!
릴리즈 노트를 써보자!
https://github.com/Shimsuyeon/focus-fairy
GitHub - Shimsuyeon/focus-fairy: 집중요정 슬랙봇이 당신의 시간 측정을 도와줄게요!
집중요정 슬랙봇이 당신의 시간 측정을 도와줄게요! Contribute to Shimsuyeon/focus-fairy development by creating an account on GitHub.
github.com
Github로 옮기면서, 그간 fix한 기능들에 대한 릴리즈 노트를 readme에 작성했다.
문득, 이 릴리즈 노트를 팀원들(유저들)에게도 공유해야겠다는 생각이 들었다.
기능 개선을 공유하면 이 제품에 대한 관심도도 올라가고,
집중요정에 다들 더 관심을 가져줄 것 같다는 생각이 들었다.
그래서 GitHub 이전 소식과 기능 개선, 버그 패치 소식을 간단히 정리해서 스레드에 남겼다.

다들 이모지와 댓글로 응원해줘서 기뻤다!
GitHub 템플릿을 정의하자
집중요정을 계속 디벨롭하다 보니,
새로운 기능 개발 계획과 아이디어, 그리고 버그 제보 등이 마구 생겨났다.
내 기억력에 의존해 FIFO 방식으로 해결하기에는 규모가 커졌다.

그래서 Opus4.5를 사용해 간단히 이슈 템플릿을 만들었다.
새로운 기능을 main에서 바로 개발하지 않고, 생성된 issue 번호를 기반으로 브랜치를 만들었다.
또, 팀원들에게 제보받은 버그를 issue에 기록했다.

커맨드를 ephemeral 메시지로 반환하도록 처리했는데도,
이렇게 스레드에 남는 이슈가 발생했는데, 이 스레드에서 즉시 이슈 템플릿으로 버그를 기록했다.

이렇게 하니 task를 더 체계적으로 기록하고 관리할 수 있었고,
팀원의 말처럼 마치 오픈소스를 운영하는 기분이었다. ㅎㅎ
Part3. 기능을 더 발전시켜보자
1장. 내 집중시간을 추출하고 분석해보자 (1월 18일)
직접 기능을 쓰다 보니, 불편한 점이 보였다.
누적 시간은 알 수 있는데, 내가 정확히 언제 몇 시간을 공부했는지 알 수 없었다.
내 공부 기록을 text, graph, csv로 추출해보자
내 기록을 단순히 text로 뱉는 것을 만들어봤다.
이게 잘 되면 graph도 만들어보려고...

일단 무사히 잘 추출되긴 했다. 근데 줄 글이 늘어져 있으니 와닿지 않았다
그래프를 무료로 쓸수는 없나?
뭐 claude한테 물어봐서 손해볼 건 없으니 물어봤다.
claude가 QuickChart.io를 이용하면 무료로 graph를 뽑을 수 있다고 했다!!

csv로도 뽑으면 별도의 고차원적인 분석을 위한 시료로 쓸 수 있을 것 같았다.
그래서 물어봤는데, csv 추출도 된대!!


그런데 csv 구현은 좀 우여곡절이 있었다.
개인 레포트 분석은 ephemeral 메시지로 나에게만 보이도록 구현 중이었는데,
자꾸만 전체 공개로 보내지는 것이었다.
파일 업로드는 ephemeral 메시지로 만들 수 없다고 해서, DM으로 보내는 걸로 바꿔보았다.
근데, 이번엔 또 파일 형태가 아니라 코드 블록 형태로 보내는 것이었다...
결국 Slack의 files.upload API 대신 files.uploadV2를 써서 해결했다.
이제는 무사히 DM으로 csv 파일이 전송된다.
추출 기능 - 병렬 읽기로 속도 보완
추출/분석 기능 출시(1/19) 후 개선 작업 진행(2/5)
그간 데이터가 많이 쌓여서 그런건지, /export 명령어 응답이 자꾸 실패하는 이슈가 생겼다.
wrangler tail을 실행해서, 실시간으로 slack 커맨드의 로그를 확인하며 여러 방향으로 분석했다.
그 결과, 여러 번 요청하다 보면 겨우 한 번 성공하는 것을 알게 되었다.

새로운 사실을 알게 되었다.
Slack 커맨드는 3초 룰이 있구나!
(명령어가 바닥에 떨어진 후 3초 안에 주우면 된다는 룰같은 건가)
여러 번 요청하면 worker가 warm 상태에 걸렸을 때 운좋게 성공하는 거였다.
근본적으로 KV를 병렬적으로 읽어서 읽는 시간을 줄여야했다.

'와 이제 개빠르다!! 근데 리스크는 없니?'
일단 성공해서 기분 좋았는데, 혹시 side effect가 있을까봐 질문했다.
속도만 달라질 뿐 다른 항목에서 기존 명령어와 다른 부분은 없었다.
그래서, 이 방법을 그대로 채택하기로 했다.
내 공부 기록을 분석해보자 (1월 18일)
추출 기능을 만들고 나니, 이젠 분석도 해보고 싶었다.
AI 모델을 붙여서 정밀 분석을 하면 더 좋겠지만,
일단 단순 계산으로 가능한 분석부터 해보기로 했다.

내가 자주 집중하는 시간, 요일, 평균 세션 길이 등을 간단히 계산해서 출력했다.
분석에 그래프도 함께하면 좋을 것 같아 시간대별/요일별 분석 세부 키워드를 만들었다.


시각적 자료로 보니 그럴싸하다.
내가 직장인이다보니 업무 시간을 제외한 주말과 저녁 시간대에 집중을 자주 하는 게 두드러졌다.
하지만, 이걸 좀 더 맞춤 분석해서 실질적으로 집중 패턴 설계에 도움을 주려면 어떻게 해야할까?
그래서, AI를 시도했다.
답은 AI 분석이라고 생각했다.
추출한 자료를 ai에 직접 넣고 돌려도 되지만, 슬랙 내에서 커맨드로 해결된다면 유저 입장에서 더 좋을 것 같았다.
무료로 간편하게 붙일 수 있는 ai가 있을까? claude에게 질문했다.
놀랍게도 Cloudflare Workers AI가 존재한다는 걸 알게 됐다.
일단 가볍게 문구를 생성하는 것부터 성능을 보기로 했다.
이런 간단한 작업도 안된다면 이 AI에 대한 희망은 접어야 하니...
/ai cheer 커맨드를 입력하면, 요정이 AI로 생성한 격려 메시지를 보내주도록 해봤다.
결과는... 솔직히 별로였다.
요정, 요정! 오늘도ยอด일 수 있어요!
무료 모델의 한계였다.
같은 내용이 반복되고, 깨진 문자가 자주 나오고, 한국어 퀄리티가 기대에 못미쳤다.

개발하다 처참한 ai 응답 결과물을 보고 충격받아서, 테스트 채널에 이런 메시지도 남겼다...
결국 Cloudflare AI는 떼어내고, 나중에 GPT 토큰을 쓰거나 Gemini 무료 플랜을 연결하는 방향을 시도해보기로 했다.
실패는 기록이니까.
이런 시도와 판단 과정이 사이드 프로젝트에서 가장 값진 부분이라고 생각한다.
추출/분석 기능 출시 알림

이번에 구현한 /export /pattern 기능의 출시를 팀원들에게 알렸다.
이번에도 다들 긍정적인 반응을 보였다.

종근님 말씀처럼 '진짜 제품'의 모습으로 발전하는 과정에서 뿌듯함을 느꼈다.
그리고, 종근님께서 '이거 인프라는 free tier로 가능한거에요?'라고 질문 주셔서,
아직 여전히 kv와 free tier 요청 선에서 감당하고 있다고 말씀드렸다.
아직도 서버비 무료 원칙을 지키고 있다!
나의 26년 1월 회고를 집중요정과 함께!
사실 제일 해보고 싶었던 건, 이 추출/분석 기능으로 내 공부 시간을 회고하는 거였다!
1월 간 집중요정 사용 기록을 바탕으로 한 달을 되돌아봤다.
한 달 간 총합 82.5시간을 집중했고, 가장 오래 집중한 주간은 26.8시간을 집중했다.
그래프로 정리하니까 한 눈에 보여서 좋았다 ㅎㅎ
26년 1월 회고 - 집중요정과 함께: https://developer-dreamer.tistory.com/213
26년 1월 - 집중요정과 함께 회고
집중요정이란 무엇인가?스터디 팀원들과 함께하는 슬랙에서 공부 시간을 기록하기 위해내가 이다! https://github.com/Shimsuyeon/focus-fairy GitHub - Shimsuyeon/focus-fairy: 집중요정 슬랙봇이 당신의 시간 측정
developer-dreamer.tistory.com
2장. 랜딩 페이지를 발전 시키자
어느 날, 문득 이런 생각이 들었다.
지금 집중요정 도메인에 접속하면, '집중요정 작동 중'이라는 문구만 뜨는데...
여기에 팀원들의 집중 시간 데이터로 뭔가 보여줄 수 있지 않을까?
난 자꾸 데이터가 모이면 이걸 예쁘게 시각화해서 이 데이터를 가치있게 만들고 싶은 본능이 있다.
데이터를 그냥 내버려두면 텍스트 조각이지만, 이를 다듬으면 예술적인 작품이나 가치있는 정보가 될 수 있다.
집중요정이라는 컨셉을 기준으로 생각을 발전시켰더니,
중앙에 나무가 있고 팀원들의 집중 시간이 쌓일수록 열매가 화려해지는 이미지를 떠올렸다.

순수 HTML과 CSS 애니메이션 만으로 만들었다.
사실 내 주 기술인 React를 도입하는 것도 고민했는데,
현재 Cloudflare Workers 환경에서 오버스펙이라고 판단했다.
대신 CSS 변수 정리, 공통 스타일 함수화,
'will-change'와 'transform'을 활용한 애니메이션 성능 최적화에 집중했다.
KV에 실제 팀원 데이터를 읽어와 열매 크기와 색상에 반영하고,
현재 집중 중인 팀원이 있으면 불꽃 이모지가 뜨도록 했다.
제목 폰트는 여러 가지를 로컬에서 비교해본 끝에 Google Fonts의 Tangerine으로 결정했다.
필기체의 우아한 느낌이 요정 컨셉과 잘 어울렸다.
열매를 클릭하면 해당 팀원의 이번주 집중 시간이 뜬다.
이름은 민감 정보니까 빼고, 시간만 표시하기로 했다.

Part4. 개발 프로세스에서 배운 것들
1장. AI와 함께 만든다는 것
이 프로젝트는 처음부터 끝까지 AI와 함께 만들었다.
Claude와의 채팅으로 아이디어를 구체화하고 첫 코드를 짰고,
GPT와 함께 컨셉 이미지를 뽑았으며,
Cursor(w/Opus4.5)에서 모듈화와 기능 확장을 했다.
특히 도움이 됐던 것은 '이게 가능한가?' 같은 열린 질문을 던질 수 있다는 것이었다.
상태 기반 자동 기록이 왜 어려운지,
ephemeral 응답과 in_channel 응답의 차이가 뭔지,
KV ID를 공개해도 괜찮은 이유가 뭔지,
혼자 구글링했으면 시간이 오래 걸렸을 것들을 빠르게 이해할 수 있었다.
물론 AI가 만능은 아니었다.
Cloudflare AI 격려 문구는 실제로 붙여보니 진짜 성능이 붐따였고,
코드도 때로는 기존 데이터 구조를 고려하지 못한 수정을 제안할 때가 있었다.
그리고, Claude한테 'Github 이전 좀 도와줘'라고 했을 때
'그건 Cursor가 잘할 듯'이라고 솔직하게 말한 것 (ㅋㅋ)
(검은 소가 누렁 소보다 일을 잘해요)
AI 도구가 자기 한계를 인정하는 것도 신기했지만,
하나의 AI 모델에 의존하지 않고 여러 AI를 동시에 사용하며 최선의 선택을 하는 것도 좋은 작업 방식이었다.
AI 결과물을 검증하고 판단하는 것은, 결국 사람의 몫이었다.
2장. 이슈 기반 개발
중간부터 github 이슈를 먼저 만들고, 이슈 번호로 브랜치를 따서 작업했다.
feature/3-export
feature/5-export-graph
이슈 템플릿과 pr 템플릿도 만들었다.
사실 혼자 하는 프로젝트라 main에서 바로 개발해도 아무 문제 없긴 하다.
하지만, 나는 이게 어쩌면 오픈소스로 발전할 수 있을지 않을까?라는 생각을 했다.
누가 언제든 합류해도 히스토리를 쉽게 읽을 수 있으면 좋겠다는 마음이었다.
그래서 커밋도 기능별로 구분해서 하고,
이슈와 브랜치도 따로 만들고,
릴리즈 노트도 README에 꾸준히 업데이트했다.
3장. 두 워크스페이스 전략
개인 워크스페이스에서 먼저 테스트하고,
확인 후 스터디 워크스페이스에 적용하는 전략을 사용했다.
실 사용자가 있는 환경에 바로 배포하는 건 심리적 장벽이 꽤 크다.
그래서, 과감한 시도를 하기 꺼려진다.
하지만, 내 개인 워크스페이스가 있으니 뭐든 과감하게 시도해볼 수 있었다.
Cloudflare Workers AI도 성능이 미심쩍었지만, 일단 시도해봤던 것도 그 이유이다.
나에겐 테스트 환경이 있으니까!
내 개인 워크스페이스1(Let's Go라고 대충 이름 지음)
내 개인 워크스페이스2(My Dream 이것도 대충 지음)
이 두 개에서 부계정을 돌려가며 테스트를 하다가,
어느정도 완성됐다 싶으면 스터디 슬랙의 비공개 테스트 채널(#집중요정_테스트)에서 테스트를 했다.
그리고, 마지막으로 스터디의 집중 기록 채널에 실전 테스트를 했다.
개발하다보니 dev1 -> dev2 -> staging -> prod의 테스트 환경이 자연스럽게 생긴 셈이다.
그리고 이렇게 여러 워크스페이스를 번갈아 테스트하는 걸 처음부터 전제하다 보니,
각 워크스페이스별 ID를 KV 데이터에 포함시켜야 한다는 것도 초기에 캐치할 수 있었다.
Part5. 현재 집중요정의 모습
'수연님이 찾아오도록 해요'에서 한 마디로 시작된 집중요정 슬랙봇의 현재 모습은 이렇다.
8개의 커맨드: /start, /end, /today, /mystats, /weekly, /report, /export, /pattern
3종 export: 텍스트, 그래프, csv
15종 커스텀 이모지: 요정 컨셉의 도트 아트 세트
Focus Tree 랜딩 페이지: 실제 데이터 기반 시각화
7개 이슈와 6개 pr: 이슈 템플릿과 pr 템플릿을 갖춘 공개 레포
그리고 무엇보다, 실제로 10명 규모 스터디에서 매일 사용하고 있는 프로덕트이다.
스터디원이 명령어가 스레드에 남는 게 별로라고 한 마디 했을 때,
그 피드백 하나로 슬랙 API 응답 방식을 깊이 공부하게 됐다.
'중간에 잠들어서 시간 기록을 제대로 못했다'라는 사례로,
유저의 행동 패턴을 고려한 제품 디벨롭으로 이어졌다.
'내 기록을 파일로 뽑아보고 싶다'라는 스쳐지나가는 생각으로
export 3종 세트와 패턴 분석 기능이 탄생했다.
사이드 프로젝트의 가장 좋은 점은 이것이다.
유저가 바로 옆에 있고, 피드백이 즉각적이다.
만드는 것 자체가 재밌다!
Part6. 앞으로 해보고 싶은 것
- GPT 토큰(또는 Gemini)을 활용한 AI 격려 문구
- 팀원별 고유 아이콘
- 평균 집중 시간대 분석 + AI 기반 공부 패턴 추천
- Focus Tree 랜딩 페이지에 팀원별 개인 프로젝트 링크 연결
- 연속 출석 기록 + 개인 최고 기록 갱신 알림
작은 슬랙봇 하나가 이렇게 커질 줄 몰랐다.
그리고 아마 계속 커질 것이다.
그나저나 종근님께서, 출시 첫날 이렇게 말씀하셨다.

결국 썼습니다! ㅎㅎ
앞으로도 발전할 집중요정을 지켜봐주시길!
집중요정 github: https://github.com/Shimsuyeon/focus-fairy
GitHub - Shimsuyeon/focus-fairy: 집중요정 슬랙봇이 당신의 시간 측정을 도와줄게요!
집중요정 슬랙봇이 당신의 시간 측정을 도와줄게요! Contribute to Shimsuyeon/focus-fairy development by creating an account on GitHub.
github.com
기술 스택: Cloudflare Workers, Cloudflare KV, Slack API, TypeScript
'개발자 강화 > AI 활용' 카테고리의 다른 글
| 2026년 2월 커피챗 인사이트 정리 - AI와 개발자 (1) | 2026.03.01 |
|---|---|
| 개발 블로그를 좀 더 쉽게 써보자 - Cowork로 흩어진 개발 맥락 모으기 (1) | 2026.02.18 |
| MCP로 생산성 3배 올리는 법 (0) | 2026.02.13 |
| 26년 1월 - 집중요정과 함께 회고 (1) | 2026.02.05 |
| AI 홍수 시대, 프론트엔드 개발자가 Rule을 설계해야 하는 이유 (0) | 2026.01.27 |