TinyPilot: Month 38

Michael Lynch

TinyPilot: 38개월 차

원문은 Michael Lynch님이 에 게재했습니다. 이 블로그 구독하기

한 줄 요약

소프트웨어에 제대로 투자하고 있는 걸까?

처음 방문하셨나요?

안녕하세요, 저는 마이클입니다. 소프트웨어 개발자이자 독립 컴퓨터 하드웨어 회사인 TinyPilot의 창업자입니다. 2020년에 회사를 창업했으며, 현재 월 6만~8만 달러의 매출을 올리고 7명의 직원을 고용하고 있습니다.

매달 이렇게 회고를 발행해 비즈니스와 전반적인 직업 생활이 어떻게 흘러가고 있는지 공유합니다.

하이라이트

  • TinyPilot 라이선스의 정기 구독 판매에 실패했다.
  • TinyPilot을 너무 과도하게 설정 가능하게 만들었다는 사실을 깨달았다.
  • TinyPilot 개발에 투자를 잘못하고 있다고 생각했는데, 이번 회고를 쓰면서 대부분 올바른 방향으로 가고 있음을 깨달았다.

목표 평가

매달 초에 달성하고 싶은 목표를 정합니다. 이번 달 목표 달성 결과는 다음과 같습니다.

위탁 제조업체로 생산 이전을 최대한 빠르게 진행하기

  • 결과: 제조업체가 막힌 부분을 빠르게 해소했지만, 일정을 더 앞당길 기회를 놓쳤다.
  • 성적: B-

위탁 제조업체가 제 피드백 때문에 막힐 때마다 빠르고 완전한 답변을 주는 것을 우선순위에 두었고, 그 부분은 잘했다고 생각합니다.

더 능동적으로 프로젝트를 관리했어야 한다는 것을 너무 늦게 깨달았습니다. 제조업체에 프로젝트 매니저가 있으니 그들이 알아서 잘 챙길 거라 생각했지만, 결국 일정이 지연되면 가장 큰 손해를 보는 사람은 저였습니다.

박스 디자인이나 설명서 같은 것에 대해 피드백을 요청받으면 즉시 답변하고는 그들이 다시 연락할 때까지 잊어버리곤 했습니다. 최종 샘플을 발송할 시점이 되어서야 피드백을 준 이후 박스나 설명서의 최종 시안을 한 번도 보지 못했다는 사실을 깨달았습니다. 결국 디자인은 추가 수정이 필요했고, 이는 불필요한 지연으로 이어졌습니다.

TinyPilot 오프라인 사무실 철수를 위한 세부 계획 수립

  • 결과: 목표일과 마일스톤이 포함된 월별 철수 계획을 갖췄다.
  • 성적: A

계획을 세웠고, 일정에 대해 모두가 같은 이해를 공유하고 있습니다.

그래도 팔고 싶은 장비 중 일부는 닭과 달걀 문제처럼 얽혀 있습니다. 예를 들어 프린터를 팔면 남은 물건을 팔 때 배송 라벨을 어떻게 출력할 것인가 하는 식입니다. 하지만 최악의 경우, 남은 물건들을 그냥 집에 보관하고 여기서 팔면 됩니다.

TinyPilot 라이선스 자동 갱신 옵션 테스트

  • 결과: 여러 옵션을 검토했지만 어느 것도 제대로 작동하지 않았다.
  • 성적: B

TinyPilot Pro의 정기 구독을 판매할 수 있는 Shopify 앱을 찾길 바랐지만, 하나도 찾지 못했습니다. 자세한 내용은 아래에서 다루겠습니다.

TinyPilot 통계

지표2023년 7월2023년 8월변화
순 방문자 수7,8006,900-900 (-12%)
판매 수익$79,635.02$91,670.46+$12,035.44 (+15%)
기업용 구독$290.70$290.700
로열티$3,777.52$2,969.62-$807.90 (-21%)
총수익$83,703.24$94,930.78+$11,227.54 (+13%)
이익$26,359.62$28,454.42+$2,094.80 (+8%)

수익과 이익 측면에서는 대체로 안정세를 유지하고 있습니다. 7월보다 수익이 소폭 증가했는데, 이는 주로 7월 대부분 아마존 노출 순위가 하락했던 영향이라고 생각합니다.

정기 구독 도입 실패기

지난달에는 TinyPilot의 라이선스 제한을 더 엄격하게 적용할 가치가 있는지 평가할 방법을 고민했습니다. 가성비가 가장 좋은 해법은 라이선스 구매 시 자동 갱신 옵션을 제공하는 것이라고 결론 내렸습니다.

Shopify는 정기 구독을 기본적으로 지원하지 않기 때문에, 이 기능을 추가하는 50개가 넘는 서드파티 Shopify 앱을 뒤져봐야 했습니다. 문제는 거의 대부분이 실물 상품용으로 설계되었다는 점입니다. 디지털 상품을 지원하는 소수의 앱조차 네이티브 Shopify 스토어에서만 작동하는데, 저는 그런 스토어를 갖고 있지 않습니다.

참고로 Shopify 애드온을 찾아보는 경험은 최악입니다. 공개 데모를 제공하는 앱이 거의 없어서, 실제로 어떤 기능인지 확인하려면 스토어에 직접 설치하고 모든 상품과 고객 데이터에 대한 전체 접근 권한을 줘야 합니다. 저는 그럴 의향이 없어 실제 고객 데이터가 없는 더미 스토어를 이용했지만, 스토어가 완전히 채워져 있지 않으면 기능 상당수가 제대로 작동하지 않습니다. 게다가 애드온이 제 실제 Shopify 이메일 주소를 가져가기 때문에, 한 시간 정도 더미 스토어에 설치했다가 삭제한 앱들로부터 스팸 메일을 엄청나게 받고 있습니다.

현시점에서 제 선택지는 다음과 같습니다.

  1. Shopify 외부에서 갱신형 구독을 판매한다(예: Paddle, LemonSqueezy, Stripe 이용).
  2. TinyPilot의 구매 흐름을 네이티브 Shopify 스토어로 전환한 뒤 Shopify 서드파티 구독 앱을 다시 검토한다.

(1)은 개발팀이 Shopify 외부 결제를 지원하기 위한 인프라를 대거 구축하고, 지원팀이 Shopify 외부에서도 고객 정보에 계속 접근할 수 있도록 해야 합니다.

(2)는 모든 것을 Shopify에 통합해 둘 수 있지만, 역시 대규모 프로젝트입니다. 지난번 견적을 문의했을 때 전환 비용으로 2만 달러를 제시받았습니다. 네이티브 Shopify 스토어는 다른 이점도 많으니 언젠가는 하고 싶지만, 지금 당장 추진할 여력은 없습니다.

TinyPilot의 설정 가능성 줄이기

TinyPilot의 기술 부채 중 가장 큰 원인 중 하나는 Ansible 사용입니다. TinyPilot을 처음 만들 때 저는 Linux에서 소프트웨어를 배포하는 방법을 몰랐습니다. Ansible은 다룰 줄 알았기 때문에, TinyPilot 인스톨러는 Ansible을 실행하는 최소한의 셸 스크립트에 불과했고, 실제 설치의 무거운 작업은 Ansible이 담당했습니다.

시간이 지나면서 Ansible이 이 작업에 적합한 도구가 아니라는 것이 분명해졌습니다. 더 미묘한 실수는 인스톨러를 너무 설정 가능하게 만들었다는 점이었습니다.

Ansible 롤을 배포할 때는 운영체제와 하드웨어 아키텍처 간의 차이를 추상화하는 것이 좋은 관행입니다. 예를 들어 파일들을 특정 디렉터리에 복사할 때, 그냥 “모든 걸 /opt/whatever에 설치해”라고 말하지 않습니다. “모든 걸 {{ my_target_dir }}에 설치해”라고 말하고, defaults.yml 파일에서 my_target_dir: /opt/whatever라고 정의합니다. 이렇게 하면 FreeBSD 시스템에서 다른 위치에 설치하고 싶을 때, FreeBSD 시스템에서만 my_target_dir를 재정의해 /usr/local/whatever 같은 경로를 가리키도록 할 수 있습니다.

하지만 TinyPilot은 단 하나의 OS와 하나의 하드웨어 플랫폼, 즉 Raspberry Pi 4 위의 Debian만 지원합니다.

습관적으로 경로, 이름, 값들을 별도 파일로 추상화했지만, 그 때문에 코드를 이해하기가 훨씬 어려워졌습니다. 실제 설치에서 Ansible이 변수를 어떻게 채우는지 이해하려면 세 개 이상의 파일을 오가며 봐야 하는 경우가 많았습니다.

물론 이 유연성 덕분에 공식적으로 지원하지 않는 시스템에서도 TinyPilot을 쓸 수 있어 좋아하는 사용자들도 있었습니다. 하지만 이들 중 유료 고객은 거의 없었기 때문에, TinyPilot 개발을 후원하는 고객에게 도움이 되지 않는 유연성을 유지하느라 상당한 비용을 치르고 있었던 셈입니다.

최신 TinyPilot 릴리스에서는 Ansible을 제거했을 뿐만 아니라 웹 UI 외부의 대부분 설정 옵션도 없앴습니다. 업그레이드 문제에 대한 보고가 전혀 없었는데, 이는 우리 고객 중 누구도 애초에 이런 설정 가능성이 필요하지 않았음을 강하게 시사합니다.

TinyPilot 개발의 본질적 작업 vs. 부수적 작업

프레드 브룩스는 그의 유명한 에세이 “No Silver Bullet”에서 소프트웨어 작업을 ‘본질적 어려움’과 ‘부수적 어려움’으로 나눕니다.

본질적 어려움에는 요구사항 정의나 UI 설계 같은 것들이 포함됩니다. 완벽한 도구와 무한한 자원이 있더라도, 소프트웨어가 무엇을 해야 하고 사용자가 어떻게 상호작용해야 하는지를 정하지 않으면 유용한 애플리케이션을 만들 수 없습니다.

부수적 어려움에는 도구의 한계 때문에 어쩔 수 없이 해야 하는 일들이 포함됩니다. 예를 들어 C에서 메모리를 관리하는 일은 자동 참조 추적 기능이나 무한한 RAM이 있다면 신경 쓸 필요가 없는 일입니다.

최근 TinyPilot 개발과 관련해 그 에세이를 자주 떠올렸습니다. 우리가 하는 일 상당수가 부수적 어려움처럼 느껴집니다.

TinyPilot의 지난 스프린트 작업들을 ‘본질적 어려움’(초록색)과 ‘부수적 어려움’(빨간색)으로 나눠봤습니다.

TinyPilot 2.6.1의 작업들을 본질적 어려움(초록색)과 부수적 어려움(빨간색)에 따라 색으로 구분한 모습

9개 작업(24%)은 기능 추가나 개선 같은 본질적 어려움에 해당했고, 28개 작업(76%)은 리그레션, 패키지 업데이트, 리팩터링 같은 부수적 어려움에 해당했습니다.

개발 시간 기준으로 노력을 정확히 계량할 방법은 없지만, 부수적 어려움 작업이 평균적으로 본질적 어려움 작업보다 더 오래 걸렸을 것으로 추측합니다. 어쩌면 우리 시간의 90%까지도 부수적 어려움에 쓰고 있을지 모릅니다.

부수적 어려움은 어떻게 줄일 수 있을까?

이 분석을 더 곱씹어보니, 제가 TinyPilot 개발을 바라보는 방식과는 조금 맞지 않는다는 걸 깨달았습니다. 제가 중요하게 생각하는 세 가지 범주가 있고, 각각에 어느 정도 시간을 쓰고 싶은지도 대략 정해져 있습니다.

범주이상적인 노력 비중
제품 개선70%
자동화 및 복잡도 감소20%
정기 유지보수10%

문제는 이 비중을 맞추기가 어렵다는 점입니다. 코드 한 줄이 추가될 때마다 유지보수 비용이 늘어납니다. 5만 줄짜리 코드베이스는 3천 줄짜리 코드베이스보다 최소 한 자릿수 이상 더 많은 유지보수를 필요로 합니다.

물론 복잡도를 줄이는 데 20%를 투자하면 유지보수 비용이 줄어들어야 하지만, 항상 새로운 기능으로 인한 부담을 상쇄하지는 못합니다. 작년에 H.264 비디오 지원을 추가하면서 서드파티 WebRTC 서버인 Janus를 통합해야 했는데, WebRTC 자체가 극도로 복잡해서 그 기능 하나만으로 유지보수 부담이 하룻밤 사이에 20~30% 증가했습니다.

더 생각해보니, 여기에 제 50% 규칙을 적용하면 좋을 것 같습니다. 전체 시간의 50%는 제품 개선에 쓰고, 필요한 유지보수를 수행한 뒤, 남은 시간을 자동화와 복잡도 감소에 쓰는 방식입니다.

그 관점에서 지난 릴리스를 다시 보면 다음과 같습니다.

범주작업 수작업 비중
제품 개선822%
자동화 및 복잡도 감소2670%
정기 유지보수38%

TinyPilot 2.6.1 릴리스의 작업들을 제품 개선(초록색), 자동화 및 복잡도 감소(파란색), 정기 유지보수(빨간색)에 따라 색으로 구분한 모습

Ansible 제거에 크게 힘을 쏟았기 때문에 자동화 쪽으로 치우쳐 있었지만, 생각보다 이상적인 비중과 크게 다르지 않았습니다.

세 가지 범주로 나눠 보니, 팀 규모를 일정하게 유지하면서 기능을 무한히 확장할 수는 없으므로, 올바른 영역에 개발 투자를 하고 있다는 느낌이 듭니다.

마무리

무엇을 해냈나?

배운 점

  • 소프트웨어의 새로운 기능을 영원히 만들 수는 없다.
    • 소프트웨어 프로젝트가 성숙해지면 유지보수 부담을 감당하기 위해 개발자를 추가하거나, 단순화에 더 집중해야 한다.
  • 설정 가능성은 은근한 유지보수 비용을 만든다.
    • 프로젝트의 모든 설정 옵션은 동작을 이해하기 어렵게 만들고 변경 비용을 높인다. 정말 필요한 옵션에만 설정 가능성을 제한하라.
  • 프로젝트 매니저가 프로젝트를 최적으로 관리하고 있다고 가정하지 마라.
    • 위탁 제조업체에 프로젝트 매니저가 있다는 이유로 생산 이전에 대한 프로젝트 관리를 신경 쓰지 않게 되었다. 돌이켜보면 미결 작업을 더 적극적으로 챙겼어야 했다.

다음 달 목표

이 회고를 월말에 늦게 쓰고 있어서 사실상 다음 주 목표라는 점에서 약간의 반칙이다.

  • 위탁 제조업체로 생산 이전을 최대한 빠르게 진행한다.
  • TinyPilot 사무실 정리를 위한 업무를 위임한다.
  • 남은 Raspberry Pi를 모두 사용해 TinyPilot 장치를 만든다.

이 글은 muse-spark-1.2-contributor 모델을 사용해 번역했습니다.

댓글