회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
AWS 이용 중이라면 월 기본 5% 할인받으세요
제가 운영하고 있는 서비스 푸딩캠프는 두 번 피벗했습니다.
비즈니스에서 피벗(pivot)은 기업이 시장 변화, 고객 요구, 또는 새로운 기회에 대응하기 위해 전략적으로 방향을 전환하는 것을 의미합니다. 이러한 피벗으로 기업은 제품, 서비스, 목표 시장, 수익 모델 등을 근본적으로 바꾸기도 합니다. 성장의 정체, 경쟁 압박, 또는 확장의 어려움에 직면했을 때 필요한 방식인데요. 시장 요구에 더 잘 부합하는 상태로 피벗할 수 있다면 성장 잠재력을 확보하는 데 도움을 줍니다.
개발자이자 경영자인 저는 피벗할 때마다 개발 프레임워크를 새로 구축하기로 했고, 그에 따라 개발할 것들이 많았습니다. 사실상 처음부터 다시 작성했어요. 가끔 코드 전체를 뒤엎는 리팩토링을 해왔어서 그런지, 구현한 것을 폐기하고 새로 작성하는 것에 별 감흥이 있지는 않았습니다. 그보다는 피벗을 해야 하는 상황 자체가 마음 아플 뿐이었지요. 따지고 보면 경영하는 한날과 개발하는 한날 가운데, 경영하는 한날에 더 강하게 빙의해서 피벗을 바라보았습니다.
그래서 이번에는 개발하는 한날을 다독이는 마음으로 글을 준비했습니다. 피벗이라는 사업적 판단이 제품과 개발에 어떤 영향을 미쳐왔는지, 무엇을 배우고 깨달았는지, 그 이야기를 풀어보겠습니다.
엉성한 디자인이 개성이었던, 푸딩캠프의 첫 번째 버전은 학습에 필요한 모든 활동을 웹 사이트에서 제공하는 교육 서비스였습니다.
텍스트와 영상 콘텐츠로 학습하고, 코드를 작성한 다음 곧바로 실행시켜 실습하며, 이에 그치지 않고 퀴즈로 복습하는 것을 목표로 했습니다. 학습하다 궁금한 것은 콘텐츠 단락 단위로 질문을 남길 수 있으며, 질문을 등록하면 인공지능이 도움이 될 법한 의견을 제안합니다. 이 모든 활동을 학습자가 웹 사이트에서 할 수 있어야 하고요.
전부 개발했냐고요? 물론이죠. 6주 정도 걸렸습니다. 저보고 “손이 빠르다”던 다른 개발자들의 평가가 사실일지도 모른다는 걸 이때 깨달았습니다. 그전에는 제 손이 느려서 늘 불만이었거든요.
첫 번째 버전의 근간이자 가장 중요한 구현은 PFM 문서 형식이었습니다. PFM은 Pudding Flavour Markdown을 줄인 이름으로, 마크다운 문법을 확장한 푸딩캠프 자체 문서 형식이지요.
앞서 텍스트와 동영상 콘텐츠를 보고, 실습하고, 퀴즈를 풀고, 단락 단위로 질문을 남기는 모두를 한 페이지에서 한다고 했는데, 이걸 가능하게 하는 것이 바로 PFM이었습니다. 대략 다음과 같이 작성합니다.
안녕하세요, 요즘IT 구독자 여러분.
저는 ==한날==입니다. 푸딩캠프에서 [[about-toystory|토이스토리]] 2기를 모집하고 있습니다.
토이 프로젝트를 만들며 자신을 잘 녹여낸 포트폴리오를 제작하는 프로그램입니다. 자, 여기서 퀴즈.
@@@quiz
question: 다음 중 토이스토리의 코치는 누구일까요?
answers:
- 한날
- 유남주
- 요즘IT
- 댕이
hint: 이 글을 기고한 사람입니다.
@@@
퀴즈를 푸셨다면 정답 풀이를 동영상에서 확인해보세요.
@@@video
provider: youtube
id: hannal
다 이해하셨나요? 그럼 코드를 작성해 실습해볼까요?
@@@code
language: python
initial:|
| from pathlib import Path
|
| path = Path("/home/hannal")
|
@@@
---information---
위 위치는 한날이 실제로 사는 곳이 아닙니다.
---default---
자, 이제 요즘IT로 돌아와 볼까요?
이렇게 작성한 문서는 PFM 변환기를 거쳐 JSON 데이터로 변환됩니다. 노션처럼 블록 단위로 쪼개지고, 각 블록이 서로를 향해 다 대 다(Many to Many) 관계로 연결되어 네트워크를 이룹니다. 노션이나 옵시디언을 애용하는 분이라면 어떤 구조인지 상상이 갈 거예요.
PFM 개발은 꽤 까다로울 뻔했는데, 확장이 괜찮은 마크다운 구문 해석 구현체(parser)를 발견해 예상보다 빠르게 개발했습니다. 제가 마크다운 확장 문법이라고 표현한 이유도, 이 구현체를 확장해 자연스레 마크다운 문법을 계승했기 때문이지요.
하지만 PFM 변환기는 조금 느립니다. 문서가 클수록 그 속도가 확연히 느려졌습니다. 메모리도 많이 사용하고요. 그래서 푸딩캠프에서는 이를 실시간으로 렌더링하지 않고, 한 번 렌더링한 다음 그 결과를 캐시(cache)로 저장해서 사용합니다. 그 때문에 PFM 변환기와 렌더러는 언젠가 러스트(Rust)로 다시 작성할 예정입니다.
기술적으로 새로 경험하고 학습한 것은 웹 어셈블리였습니다. 웹 브라우저에서 파이썬(Python) 코드를 작성하고 실행하는 환경을 제공하고 싶었던 저는, 파이썬 인터프리터를 웹 어셈블리로 감싸서 동작시키는 걸 생각했습니다. 실제 구현체도 이미 존재하는데, PyScript와 Pyodide가 대표적인 구현체입니다.
푸딩캠프는 PyScript로 웹 브라우저에서 파이썬 코드를 실행하며 실습할 수 있게 구현했었습니다.
야심차게 출시한 첫 번째 버전은 사실상 구글 드라이브와 디스코드에 패배했습니다. 구글 드라이브와 디스코드로 충분히, 아니 더 안정적으로 고객에게 제가 주고자 한 가치를 전할 수 있었기 때문이죠. 게다가 실제 고객에게 주는 가치도 제가 가정한 가치와 달랐고요. 물론 처음부터 엎을 생각은 없었습니다. 조금만 덜어내고 당장 사용할 기능 아니면 과감히 지울 생각이었습니다. 그러자 남는 코드가 없었습니다.
참고 글: 3달 만든 코드를 모두 엎고 배운 것
그렇게 시작한 두 번째 버전은 교육 커뮤니티를 목표로 했습니다. 디스코드 서버를 기반으로 소통하고, 구글 드라이브 기반으로 학습 자료를 나누며 활동을 운영하기로 한 겁니다. 그러다 보니 기존 코드 기반은 사용할 게 거의 없어 자연스레 폐기했습니다. 왜 엎게 됐는지 아시겠지요?
그때 푸딩캠프는 파이썬 학습을 위한 파일럿 프로그램을 운영했습니다. 무척 바빴는데, 자동화가 안 되어 있어 몸으로 때우기 일쑤였습니다. 자연스레 무엇을 구현해야 할지 선명히 그려졌습니다.
가장 먼저 개발하기로 결정한 것은 챗봇이었습니다. 발산형 사고방식에 익숙한 저는 멋진 설계부터 시작했습니다. 고객이 실제로 원하는 것을 기민하게 구현해 피드백을 받으며 개선하고 확장할 생각은 하지 않고요. 과거의 저를 인제 와 조금 변명하자면, 혼자 개발하고 혼자 운영하는 상황에서 피드백에 따라 개선하는 순환 주기까지 만들기에는 제 자원이 너무 부족했습니다. 그래서 시간 있을 때 바짝 만들어 조금만 자원을 들여도 빠르게 기능을 확장하고 수정할 구조를 만드는 게 효율적이라 판단했습니다.
물론 시간이 지나고 돌이켜 보면 그런 판단은 거의 언제나 오답이죠. 그다음은 뭔지 아시죠? 이불 속에서 하는 거.
저는 과거에도 챗봇을 한 번 만든 적이 있습니다. 업무용으로 슬랙에서 동작하는 봇이었습니다. 챗봇이란 것이 처음에 기능 한두 개를 구현할 때는 문제가 없지만, 기능이 많아지고, 봇이 인식할 명령어가 복잡해질수록 구현 검증이 까다롭습니다.
특히, 봇이 상태(state)를 관리해야 하는 상황이 오면 혼돈이 시작됩니다. 초기에 상태 관리를 전제하지 않고 무상태로 사용자가 소통하도록 구현하면, if 문이 복잡하게 중첩되거든요. 그럼 이런 상황이 나옵니다.
사용자: 에스프레소를 만들어줘.
봇: 만들었습니다.
사용자: 우유를 넣어줘.
봇: (에스프레소 잔에 우유를 담아준다)
사용자: 아니, 에스프레소에 우유를 넣어줘.
봇: (새 에스프레소를 만들어서 우유를 넣는다) 넣었습니다. 모두 두 잔으로 총 299,792,458원입니다.
봇 입장에서 각 명령어는 모두 분리된 별개 명령어인 셈이지요. 뒤늦게 손을 대려 하면 채팅 클라이언트(어댑터)에 강결합되어 있어서 테스트 코드 작성이 매우 어렵고 난해합니다.
그래서 새로 만들 챗봇은 처음부터 상태 머신(FSM, Finite State Machine)을 적용하고, 자체 문법(DSL, Domain Specific Language)을 적용해 시나리오를 쉽게 작성하기로 결정했습니다. 상태와 시나리오는 챗봇이 똑똑해지는 데 기여하지만, 테스트가 까다로워집니다. 그래서 테스트 코드를 작성하기 좋도록 결합도는 매우 낮게 설계했습니다.
구현은 예상보다 복잡했습니다. 파일럿 프로그램을 운영하며 틈틈이 개발하느라 시간이 부족한 것도 있어, 전체 개발에 6주 정도 걸렸습니다.
첫 번째 기능은 디스코드 커뮤니티 온보딩이었습니다. 처음 접속하는 사람에게 인사하고, 직접 메시지(Direct Message)를 보내 이용약관과 커뮤니티 행동 강령(Code of Conduct)에 대한 동의를 받는 시나리오를 수행합니다. 여기에 서비스 가입비 낸 것을 확인하고자 사용자와 대화하며 이메일 인증을 진행했습니다. 모든 확인이 끝나면, 커뮤니티 활동하는 데 필요한 권한을 부여하고요.
두 번째 기능은 Q&A 서비스였습니다. 질문을 하면 인공지능이 답변해 주고 참고할 URL까지 제시하는 기능이었습니다. 사용자가 명령을 갈무리하면, 챗봇과 주고받은 질문과 답변은 푸딩캠프 데이터베이스에 저장됩니다.
하지만 이렇게 만든 두 기능 모두 실제로 출시하지는 못했습니다. 두 번째 피벗을 했거든요. 구현한 코드는 다시 폐기했습니다.
이유는 첫 번째 피벗과 다를 바 없습니다. 고객에게 발생하는 상황은 훨씬 다양하고 복잡하기에 그 모든 상황에 대응하는 챗봇 시나리오를 만드는 데 들어가는 비용이 제 예상보다 컸습니다. 그런 비용에 비해 매출은 일으키지 못했기 때문이지요. 다양하게 인사하는 챗봇을 인사조차 하지 못하게 다운그레이드하는 것으로 두 번째 버전은 여정을 마쳤습니다.
현재 푸딩캠프는 콘텐츠와 학습 활동을 위한 공간을 지향하며, 저는 일명 ‘랜선 사수’로서 코칭 프로그램을 기획하고 운영합니다.
마지막 세 번째 버전은 버전 1과 버전 2의 주요 구현체를 자연스레 계승했습니다. 콘텐츠를 다루고 표현하는 PFM은 버전 1로부터 승계해 쓰고 있고, 학습 활동을 관리하거나 푸딩캠프 소식을 전달하는 챗봇은 버전 2로부터 승계해 축소한 다음 사용하고 있지요. 피벗할 때마다 코드를 처음부터 작성했지만, 그렇다고 모든 바퀴를 다시 발명하진 않았습니다.
*앞서 기고한 3달 만든 코드를 모두 엎고 배운 것 글은 이 버전 3의 시작 시점에 대한 이야기입니다.
PFM은 버전 3에서도 꾸준히 확장하고 있습니다. 실습 기능은 사용하지 않지만, 푸딩캠프를 운영하며 직면한 불편 사항이나 고객과 상호작용하다 발견한 개선점을 추가하고 있지요.
그들 가운데 ‘내부 링크 미리보기’ 기능은 푸딩캠프 웹 사이트의 최대 사용자인 제가 불편해서 구현한 기능입니다. 푸딩캠프 콘텐츠의 링크를 걸면, 해당 콘텐츠 내용을 비동기로 가져와 화면에 띄우고 보여주는 기능입니다. 옵시디언처럼 콘텐츠 자료 구조를 블록으로 다루는 편집기에서 접할 수 있는, 바로 그 기능입니다.
이를테면 푸딩캠프가 주최한 콘퍼런스 연사자들을 대상으로 한 사전 인터뷰는 연사자별로 A4 용지 20~25장에 이를 만큼 분량이 많았습니다. 그래서 콘텐츠와 콘텐츠 사이를 오가면서도 불편함 없이 현재 화면에서 다 볼 수 있도록, 마치 PIP(Picture in Picture) 기능처럼 동작합니다.
구현은 생각보다 쉬워 2시간도 안 들이고 끝났습니다. (미묘하게 불편한 UX를 가다듬는 데 시간이 더 들기는 했습니다.) PFM 문서는 블록으로 구조화해 다루니 필요한 부분만 쉽게 가져다 렌더링할 수 있었습니다. 또, htmx*와 Tailwind CSS**의 생산성이 좋았기 때문에 가능했습니다. 혼자 개발하고 운영하는 입장에서는 기술 스택과 관리 요소를 최소화해야 하는데, 그런 점에서 생산성이 뛰어난 htmx와 Tailwind CSS는 제 개발 환경에 크게 기여합니다.
*htmx: htmx는 HTML 요소에서 직접 AJAX 요청, CSS 전환, WebSocket 호출 등을 가능하게 하는 경량 자바스크립트 라이브러리입니다. 간단한 마크업으로 현대적인 웹 인터페이스를 구축할 수 있게 해줍니다.
**Tailwind CSS: Tailwind CSS는 유틸리티 우선 접근 방식의 CSS 프레임워크입니다. HTML에 직접 적용할 수 있는 미리 정의된 클래스를 제공하여 빠르고 효율적인 웹 개발을 가능하게 합니다. 커스터마이징이 쉽고, 일관된 디자인을 유지하면서도 세밀한 스타일 조정이 가능합니다.
서버로부터 미리보기 HTML을 불러오는 코드는 이렇습니다.
<div
x-ref="hoverCard"
role="region"
aria-label="Internal Link"
hx-get=" ... "
hx-trigger="fetch"
hx-swap="innerHTML"
class="
z-40
py-5
px-4
shadow-[0_5px_20px_0px_rgba(0,0,0,0.14)]
rounded-lg
bg-white
fixed
lg:block
bottom-0
lg:bottom-auto
left-0
w-full
lg:w-[500px]
lg:max-w-lg
mx-auto
max-h-[60vh]
lg:h-8/12
lg:max-h-[800px]
overflow-y-auto
">
</div>
실은 이 기능은 PFM을 처음 설계할 때부터 고려한 것입니다. 버전 1의 목표가 웹 사이트 한 페이지 안에서 학습에 필요한 모든 활동을 하는 데 있었기 때문입니다. 예를 들어 현재 3장(chapter)을 학습 중인데, 2장을 잠시 살펴보고 싶을 때 페이지를 이동할 필요 없이 내용을 보여주고자 했습니다. 마치 문제집을 보다가 앞장을 파르륵 넘겨 살짝 엿보고 되돌아오는 듯한 사용자 경험을 주도록요.
다만 이 미리보기 기능이 생각보다는 서버 부하를 일으켰습니다. 콘텐츠 페이지는 페이지 통으로 캐시 처리하지만, 미리보기 페이지는 함께 캐시 처리되지 않았기 때문이죠. 그래서 캐시 시스템도 구현을 조금 고쳤습니다.
푸딩캠프에서는 여전히 저 혼자 개발하고, 콘텐츠를 만들고, 운영과 코칭을 하고 있습니다. 그래서 코칭처럼 사람이 개입해야 하는 것을 제외하면 푸딩캠프에서 이뤄지는 동작이나 기능은 최대한 자동화하려고 합니다. 특히 버전 2에서 거의 모든 것을 수동으로 하다 사소하고 간단한 일에 시간을 많이 빼앗긴 만큼, 버전 3에서는 자동화를 가장 중요한 해결 과제로 일찌감치 정해두었습니다.
버전 3 푸딩캠프의 사용자들은 대부분 디스코드 서버에서 활동합니다. 그래서 챗봇 구현체를 꺼낼지 말지 고민했습니다. ‘디스코드 활동을 모두 챗봇으로 자동화해야 하는 것은 아닐까?’ 같은 생각을 했지요. 깊은 고민 끝에 앞서 두 번째 피벗의 경험 등을 고려해 ‘그렇지 않다’라고 결론을 내렸습니다.
사용자들이 활동하는 행태를 분류하고, 제게 필요한 자동화를 본질적인 요소 중심으로 다시 정의했습니다. 가령 ‘토이스토리’는 사용자들이 토이 프로젝트를 만들 때, 제가 코칭하고 멘토링하는 프로그램입니다. 다만 이 프로그램 참여자들의 기록을 찾아보기 어렵다는 문제가 있었습니다. 이 문제 역시 아래와 같이 재정의했습니다.
그 결과, 디스코드를 활용하는 방식 자체에 변화가 생겼습니다. 가장 대표적인 것은 코칭, 멘토링 공간을 디스코드 음성 채널에서 줌(Zoom)으로 이동한 것입니다. 여러 자동화 도구(Automation Tool)와 작업 흐름 도구(Workflow Tool)가 줌 연동을 지원하기 때문이었습니다. 커피챗을 비롯해 토이스토리 코칭과 멘토링 모두 줌에서 녹화하고, 녹화가 끝나면 자동으로 유튜브에 저장되며, 인공지능이 영상 내용을 요약해 참가자에게 제공합니다. 도구보다 문제를 중심으로 자동화한 것입니다.
*이 외에도 여러 부분을 자동화했는데, 보다 자세한 내용은 앞서 기고한 1인 스타트업 대표가 '엔지니어링'으로 비용을 절약하는 법 글에서 다뤘습니다.
지금까지, 힘들게 개발하고는 폐기하고 시간이 지나 다시 꺼내어 활용하는 걸 반복한 과정입니다. 소위 “삽질”의 연속이라 할 만 합니다. 여태껏 작성한 코드 중 남아있는 코드보다 삭제한 코드가 훨씬 많으니 오죽할까요?
현재 푸딩캠프 서비스는 본질로 돌아가 실행과 코칭에 집중하고 있습니다. 체계적인 학습법으로 스터디를 돕거나 토이 프로젝트의 멘토링, 그리고 코칭을 하는 것이지요. 웹 사이트는 코칭 받는 이와 코칭하는 제가 쾌적하고 편하게, 밀도 높은 경험을 하는 환경으로 쓰입니다. 그 기준으로 개발하고, 다듬고, 고치고, 또 제거하고 있습니다.
만약 코드에 인격이 있다면, 제 코드들은 매 피벗을 경연처럼 느꼈을 거예요. 이번 경연 주제는 ‘교육 콘텐츠 서비스’, 다음 경연 주제는 ‘교육 커뮤니티’, 마지막 결승 경연의 주제는 ‘콘텐츠와 학습 활동 공간’. 매 경연을 거치며 살아남은 코드, 부활한 코드가 어우러져 제 역할을 하고 있습니다. 작성한 코드에 비하면 아주 적은 양만 살아남아 쓰이지요.
새 기능을 구현하면 저는 삭제할 기존 코드를 찾습니다. 이처럼 과감히 구현을 쳐내면서도 코드를 지워나가는 것은 ‘피쳐 몬스터(Feature monster)’가 탄생하는 걸 막기 위해서입니다. 피쳐 몬스터는 과도하게 많은 기능이 더해져 복잡해진 소프트웨어 프로젝트를 가리킵니다. 기능 과다, 복잡성 증가, 사용성 저하를 가리키며, 또 그런 상황을 불러오는 현상입니다.
사업 모델 역시 마찬가지입니다. 피벗을 할 때마다 제 상상 속 고객에게만 만족스럽고, 현실에서는 기능하지 않는 사업 전략과 전술을 지워갑니다. 하긴, 맨 처음에는 교육과 학습을 위한 대규모 다중 접속 롤플레잉 게임(MMORPG)을 만들려고 했으니, 지금에 와서 보면 엄청나게 잘라내고 쳐낸 것이네요.
피벗을 방향 전환에 그친 행위가 아닌 덜어내기 위한 전환으로 정의하며 제가 겪은 시행착오를 풀어보았습니다. 여러분의 삶에서도 피벗을 한다면, 무엇을 덜어내기 위해 전환하시겠습니까?
요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.