TinyPilot: Month 38

Michael Lynch

TinyPilot: 38개월 차

한 줄 요약

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

처음 오셨나요?

안녕하세요, 저는 마이클입니다. 소프트웨어 개발자이자 독립 컴퓨터 하드웨어 기업 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월 대부분 기간 동안 Amazon 노출 순위가 하락했던 영향이 크다고 봅니다.

정기 구독 도입에 실패한 과정

지난달에는 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 role을 배포할 때는 운영체제나 하드웨어 아키텍처 간의 차이를 추상화하는 것이 좋은 관행으로 여겨집니다. 예를 들어 파일들을 특정 디렉터리에 복사할 때 “모든 걸 /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의 본질적 개발 업무와 우발적 개발 업무

유명한 에세이 “No Silver Bullet”에서 Fred Brooks는 소프트웨어 작업을 ‘본질적 어려움(essential difficulties)’과 ‘우발적 어려움(accidental difficulties)’으로 나눕니다.

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

우발적 어려움은 도구의 한계 때문에 어쩔 수 없이 해야 하는 일들을 말합니다. 예를 들어 C 언어에서 메모리를 관리하는 일은 자동 참조 추적 기능이나 무한한 RAM이 있다면 신경 쓰지 않아도 될 문제입니다.

최근 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 장비를 제작하기.

원문은 Michael Lynch님이 에 게재했습니다.

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