Refactoring English: Month 13

Michael Lynch

Refactoring English: 13개월 차

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

한 줄 요약

집중에 대한 글을 쓰면서 딴짓하기

처음 방문하셨나요?

안녕하세요, 마이클입니다. 소프트웨어 개발자이자 소규모 인디 테크 비즈니스를 운영하는 창업자입니다. 현재 Refactoring English: Effective Writing for Software Developers라는 책을 집필 중입니다.

매달 이렇게 회고를 발행해 책 집필 상황과 전반적인 근황을 공유하고 있습니다.

하이라이트

  • 구매력 평가(PPP)를 기준으로 책에 지역별 가격을 도입했습니다.
  • 첫 Flutter 앱을 만들었습니다.
  • 첫 크로스 랭귀지 라이브러리를 작성 중입니다.

목표 성적표

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

Refactoring English 웹사이트로 사람들을 끌어모을 게임 출시하기

이 블로그 글은 도박에 가까웠습니다. 해커 뉴스 프론트 페이지에 올라가야만 신규 독자에게 도달할 수 있고, 그 기회는 2026년 첫 2주뿐이었기 때문입니다.

다행히 글은 해커 뉴스 1위에 올랐고 거의 22시간 동안 프론트 페이지에 머물렀습니다. 이는 다른 성공적인 테크 작가들을 조명하는 제 전략을 이어간 것이기도 합니다. 저와 독자, 그리고 소개한 작가 모두에게 이득이 되는 윈윈 전략이라 마음에 듭니다.

해커 뉴스 예측 게임은 80% 정도 완성된 상태로 남아 있습니다. 거의 다 만들었지만 재미가 없다고 느껴져 완성할 동기가 생기지 않아 어떻게 해야 할지 모르겠습니다. 그래도 일단 끝까지 만들어 사람들의 반응을 보고 싶습니다.

Refactoring English 챕터 2개 발행하기

  • 결과: 두 챕터 모두 진척은 있었지만 완성하지 못함
  • 성적: D

아이러니하게도 지금 쓰고 있는 챕터 주제가 동기부여와 집중인데, MeshCore 실험 때문에 계속 집필을 방해받고 있습니다. 새해 들어서는 집중력을 더 잘 유지하고 있고, 오히려 이런 딴짓이 집중을 회복하는 방법에 대해 쓸 신선한 소재가 되어 도움이 되기도 합니다.

재미로 만드는 가족 사진 공유 앱의 디자인 문서 작성하기

이번에도 12월에 MeshCore 실험에 빠져 원하는 만큼 진도를 못 냈습니다. 디자인 문서는 좋아하고 유용하다고 생각하지만, 쓰는 과정은 엄청나게 지루해서 즉각적인 보상이 있는 다른 일로 미루고 싶은 유혹이 늘 있었습니다.

Refactoring English 지표

지표2025년 11월2025년 12월변화
순 방문자 수7,6082,266-5,342 (-70%)
사전 예약 수익$1,018.48$492.55-$525.93 (-52%)
스폰서 수익$48.25$48.25$0.00 (0%)
총수익$1,066.73$540.80-$525.93 (-49%)

사전 판매가 줄어든 건 신규 독자를 끌어올 새 글을 발행하지 않았기 때문입니다(해커 뉴스 글은 1월에 발행했습니다). 그럼에도 ‘패시브 세일즈’가 계속 늘고 있다는 점은 긍정적입니다. 12월 사전 판매액은 거의 500달러였습니다. 방문자 수가 비슷했던 달과 비교하면 5월은 241달러, 8월은 361달러였으니 상승 추세에 있습니다. 책이 더 완성되고 더 많은 독자가 추천할수록, 매달 마케팅에 성공해야 한다는 부담 없이도 패시브 세일즈가 계속 오르길 기대합니다.

책에 지역별 가격 도입하기

11월에 블랙프라이데이 프로모션을 진행했을 때, 아르헨티나의 한 독자가 30% 할인된 가격(US$20)조차 책 한 권 값으로는 여전히 부담스럽다는 메일을 보내왔습니다. 지역별 가격을 고려해 달라는 요청이었고, 아르헨티나에서는 Steam 게임이 보통 미국보다 50% 저렴하다는 말을 들으니 그 정도면 괜찮은 기준점이 되겠다고 생각했습니다.

결제는 Stripe로 받고 있는데, Stripe 대시보드에서 지역별 가격 옵션을 찾을 수 없었습니다. Stripe 지식창고에서 “Geographic pricing in practice: Why it matters and how to implement it.”라는 글을 찾아 반가웠지만, 끝까지 읽어보니 ‘and how to implement it’ 부분은 쓰는 걸 잊어버렸더군요.

즉, Stripe는 지역별 가격을 옹호하면서도 정작 그 기능을 제공하지 않습니다. Stripe가 다른 모든 결제 대행사를 제외하면 최악의 결제 대행사라는 사실을 다시 한번 상기시켜 주는 사례였습니다.

그래서 아르헨티나 독자에게는 일회성으로 할인된 가격의 맞춤형 결제 링크를 수동으로 만들어 드렸습니다. 그 과정에서 아르헨티나 페소로 가격을 설정하면 환전 수수료를 안 내도 된다는 걸 알게 됐습니다. 22,000 ARS(약 US$15)로 설정했고, 독자는 가격과 결제 경험 모두 만족스러워했습니다.

독자는 브라질이나 인도처럼 개발자는 많지만 구매력이 상대적으로 낮은 국가에는 적어도 공개적으로 지역별 가격을 제공해 보라고 제안했습니다.

Stripe에서 네이티브로 지원하지 않아도, 수동으로 한 작업을 자동화하는 게 그리 어렵지는 않아 보였습니다. Sebastien Castiel이 자신의 강좌에 지역별 가격을 도입한 사례에 대한 글을 읽었고, 거기서 Wes Bos가 같은 주제로 쓴 글로 이어졌습니다.

Sebastien은 기술적인 세부사항을 많이 공유했지만, 그의 솔루션은 React 비중이 컸고 제 사이트는 순수 HTML과 JavaScript로 되어 있습니다. 또 할인 코드에 의존하는 방식이었는데, 그러면 대부분의 고객이 자신은 받지 못하는 특가가 있다는 걸 보게 되어 마음에 들지 않았습니다.

몇 시간에 걸쳐 클라우드 함수로 적정 가격을 즉석에서 계산해 Stripe 결제 링크를 동적으로 생성하는 솔루션을 구현했습니다. 그러다 모든 걸 미리 계산해 두면 서버 로직 자체가 필요 없다는 걸 깨닫고 클라우드 함수를 삭제했습니다.

제 구현은 다음과 같습니다.

  1. Stripe가 지원하는 모든 국가/통화 목록을 수동으로 확보한다.
  2. World Bank 데이터를 가져와 목록의 각 국가별 구매력 평가(PPP)를 계산하는 스크립트를 작성한다.
  3. 미국 대비 구매력을 기준으로 각 국가의 할인율을 계산한다.
    • 예: 브라질의 PPP는 미국보다 54% 낮으므로 54% 할인을 받는다.
  4. PPP가 미국과 15% 이내로 차이 나는 국가는 제외한다(할인폭이 너무 작아 의미가 없다).
  5. 할인율이 음수가 되는 국가는 제외한다.
  6. 할인은 최대 75%로 제한한다.
    • 그렇지 않으면 이집트 가격은 US$4가 되고, 환전 수수료를 떼면 3.50달러 정도만 남는다.
  7. 목록에 남은 각 국가별로 국가 전용 Stripe 가격 객체와 Stripe 결제 링크를 자동으로 생성한다.
  8. 모든 국가를 사이트의 HTML 드롭다운에 넣는다.

사용자는 그냥 자신의 국가를 선택하면 해당 국가용 Stripe 구매 링크가 활성화되고, 자국 통화로 결제하면 됩니다.

양심에 맡기는 방식이라 IP 위치 추적이나 VPN 차단은 신경 쓰지 않습니다. 다만 가장 저렴한 옵션을 고르는 걸 막기 위해 국가별 할인율은 숨겨 둡니다. 그리고 각국 통화로 가격을 책정하는 장점 중 하나는, 누군가 속임수를 써서 자기 나라가 아닌 지역을 선택하면 환전 수수료 때문에 오히려 손해를 본다는 점입니다.

숫자가 썩 정확하게 느껴지지는 않습니다. 엄격한 PPP로 따지면 미국의 30달러가 이집트에서는 4달러에 해당하지만, 이집트에서 프로그래머용 정품 책을 4달러에 살 수 있을 것 같지는 않습니다.

Wes Bos가 이 작업을 할 때는 독자들에게 적정 가격을 알려 달라고 했다고 하니, 저도 그렇게 해 보려 합니다. 댓글을 남기거나 메일로 여러분 국가에서 개발자용 책의 일반적인 가격대(현지 통화 기준)를 알려 주세요.

첫 Flutter 앱 만들기

12월에 “My First Impressions of MeshCore Off-Grid Messaging”을 발행했습니다. 기술 자체는 흥미로웠지만, 클라이언트가 모두 클로즈드 소스라는 걸 알고 실망했습니다.

그 시점에서 MeshCore 탐구를 잠시 멈추기로 했는데, MeshCore 기여자 중 한 명인 Frieder Schrempf가 제 글에 흥미로운 관점으로 댓글을 달았습니다.

이 주제에 대한 당신의 생각 중 많은 부분에 공감합니다. 개인적으로 MeshCore의 가치는 소프트웨어 구현체(펌웨어, 앱 등)보다는 프로토콜 자체에 있다고 봅니다. […] MeshCore가 프로토콜로서 성공해 널리 쓰인다면(현재로선 그렇게 되는 것처럼 보입니다), 제대로 유지보수되는 오픈소스 구현체가 뒤따라 나올 겁니다(적어도 그러길 바랍니다).

Frieder의 의견에 동의했고, ‘차라리 MeshCore 오픈소스 앱의 컨셉 증명(proof of concept) 버전을 직접 만들어 볼까?’라는 생각이 들었습니다.

사실 이미 컨셉 증명용 MeshCore 앱이 있었습니다. 공식 MeshCore 앱 개발자인 Liam Cottle이 공식 버전의 프로토타입으로 MeshCore 웹 앱을 만든 적이 있습니다. 공식(독점) MeshCore 앱을 만들면서 지원이 중단됐지만, 프로토타입의 소스 코드는 여전히 공개되어 있었고 제가 필요한 기능 대부분을 갖추고 있었습니다.

이 프로토타입을 모바일로 포팅하는 게 얼마나 어려울까 궁금해졌습니다. MeshCore는 블루투스 접근과 오프라인 모드가 필요해서 웹 앱으로는 쓰기 너무 어렵습니다. 크로스 플랫폼 모바일 개발을 위한 Google 솔루션인 Flutter에 대해 어느 정도 긍정적인 이야기를 들은 적이 있습니다. LLM이라면 제 개입 없이도 웹 프로토타입 코드를 Flutter로 성공적으로 포팅할 수 있을 것 같았습니다.

LLM이 프로토타입을 세 단계에 걸쳐 Flutter로 포팅하게 할 계획이었습니다.

  1. Playwright를 사용해 프로토타입 웹 앱의 종단 간 테스트를 작성한다.
  2. 종단 간 테스트를 그대로 유지해 기능 동등성을 보장하면서 프로토타입 구현을 Flutter 웹 앱으로 포팅한다.
  3. Flutter 프로젝트에 Android 빌드를 추가한다.

계획은 통했지만, 모든 단계가 예상보다 훨씬 더 매끄럽지 않았습니다.

  • 프로토타입에 대한 종단 간 테스트를 작성하기 전에, 입력 라벨 상당수가 그냥 빈 <div>로 되어 있어 시맨틱 HTML과 ARIA 속성을 쓰도록 변환해야 했습니다.
  • Playwright 테스트를 그대로 유지할 수 없었습니다. Flutter가 웹 앱용으로 시맨틱 HTML을 내보내지 않기 때문입니다. Flutter는 자체적인 Flutter 전용 HTML 방언을 만들어 모든 걸 HTML 캔버스에 그립니다. Playwright의 요소 로케이터 대부분은 어찌저찌 동작했지만, 테스트에 Flutter 특화 수정을 많이 해야 했습니다.
  • LLM을 써도 Flutter로 Android 패키지를 빌드하는 방법을 알아내는 데 오래 걸렸습니다.
    • Android 빌드 시스템인 Gradle이 NixOS에서 버그가 많습니다. 알 수 없는 오류로 계속 실패했는데, 결국 홈 디렉터리에 캐시된 오래된 데이터 때문인 것으로 드러나는 경우가 반복됐습니다.
  • Flutter는 블루투스 통신을 놀라울 정도로 어렵게 만듭니다. 웹(적어도 Chrome)에서는 navigator.bluetooth.requestDevice를 호출하면 사실상 공짜로 쓸 수 있지만, Flutter에서는 독점 서드파티 라이브러리를 쓰고 기기 선택 UI도 직접 만들어야 합니다.

몇 시간 만에 뚝딱 끝낼 수 있는 주말 프로젝트라고 생각했습니다. 30시간과 200달러어치 LLM 크레딧을 쓴 뒤에야 겨우 동작하게 만들었습니다.

실제 Android 기기에서 실행 중인 제 MeshCore Flutter 앱

하지만 프로토타입과 기능 동등성을 갖춘 Flutter 구현을 완성한 날, Reddit에 공유하러 갔다가 누군가 방금 meshcore-open이라는 Flutter 기반 MeshCore 클라이언트 구현을 공유한 것을 봤습니다. 제가 생각한 것과 같은 아이디어였지만 훨씬 더 잘 만들어진 것이었습니다.

누군가 먼저 해냈다는 사실에 실망했지만 동시에 안도하기도 했습니다. Flutter를 잠깐 다뤄본 경험으로, 최대한 빨리 Flutter에서 벗어나고 싶었기 때문입니다. 저는 다른 누군가가 이어받길 바라는 마음에 컨셉 증명용으로만 만들고 싶었던 거라, 이제 기능이 풍부한 오픈소스 MeshCore 클라이언트 구현이 생겨 기쁩니다.

어쩌면 MeshCore에 필요한 건 크로스 랭귀지 라이브러리일지도 모른다

MeshCore Flutter 앱을 작업하면서 MeshCore 기기-클라이언트 메시지를 파싱하는 로우레벨 로직을 구현해야 했습니다. MeshCore의 피어 투 피어 프로토콜을 정의하는 공개 스펙이 있긴 하지만, 그마저도 꽤 느슨합니다. 그런데 MeshCore 펌웨어가 탑재된 기기가 블루투스나 USB를 통해 컴패니언 클라이언트(예: Android 앱)와 통신하는 방식에 대한 또 다른 미문서화된 프로토콜이 있습니다.

사실상 레퍼런스 구현은 MeshCore 펌웨어인데, 피어 투 피어 프로토콜 로직과 기기-클라이언트 프로토콜 로직, UI 로직이 뒤섞여 있고 구현이 코드베이스 곳곳에 흩어져 있습니다.

예를 들어 MeshCore 클라이언트는 블루투스를 통해 MeshCore 기기에서 연락처 목록을 가져올 수 있지만, 날것의 바이트를 다시 연락처로 역직렬화해야 합니다. 메시지를 디코딩하는 라이브러리가 없어 각 MeshCore 클라이언트와 라이브러리가 각자 별도로 구현하고 있습니다.

그 구현들에서 눈에 띄는 점은 다음과 같습니다.

  • 권위 있는 곳에 정의된 상수를 참조하는 대신 32 같은 매직 넘버를 써야 합니다.
  • 어느 것도 파서에 대한 자동화된 테스트가 없습니다.
  • 불필요한 로우레벨 작업을 하이레벨 언어로 끌고 들어오고 있습니다. 예를 들어 모두가 outPathoutPathLen 변수를 저장합니다. 이는 배열이 자신의 크기를 모르는 C 구현의 잔재입니다. JavaScript, Python, Dart 같은 언어에서는 배열 크기를 수동으로 추적할 필요가 없습니다.
  • 데이터를 꼼꼼히 검사하지 않아 음수 경로 길이나 지구 범위를 벗어난 GPS 좌표 같은 쓰레기 데이터를 그대로 넘깁니다.
  • 모두 flags 필드를 무시합니다. flags는 어떤 필드가 채워졌는지를 나타내야 하는데 말이죠. 적어도 피어 투 피어 메시지에서는 그래야 합니다. 기기-클라이언트 메시지에서는 의미가 없어 보입니다.

처음에는 protobufCap’n Proto 같은 프로토콜 라이브러리를 써서 로직을 다시 작성할까 생각했지만, 현시점에서 서드파티 라이브러리를 하위 호환되게 통합할 방법을 찾지 못했습니다.

그렇다면 MeshCore 기기-클라이언트 프로토콜의 핵심 구현을 C로 작성하면 어떨까? 언어별 바인딩을 추가하면 Dart, Python, JavaScript 등 어떤 언어로 작성하든 완전히 별개의 구현을 만들 필요가 없어집니다.

그래서 직접 MeshCore 클라이언트 라이브러리를 시작했습니다.

이 라이브러리는 아직 컨셉 증명 데모를 할 준비는 안 됐지만 거의 다 왔습니다.

MeshCore 메인테이너들이 이 아이디어를 마음에 들어 하지 않을 가능성도 충분히 있고, 그들의 동의 없이는 사실상 물 건너간 셈입니다. 그래도 전에 크로스 랭귀지 라이브러리를 써 본 적이 없어 흥미로운 경험이 될 것 같아 일단 진행했습니다.

Python에서 C 코드를 호출해 본 마지막 경험은 20년 전이었고, 그때는 SWIG를 써야 했습니다. 당시에는 고통스럽고 편법처럼 느껴졌는데, 지금은 80% 정도 나아진 것 같습니다.

핵심 구현을 C가 아니라 Zig로 하고 싶어 안달이 났지만, 걸림돌이 너무 많았습니다.

마무리

무엇을 해냈나?

배운 점

  • 진행 중인 프로젝트 최소화하기
    • AI 덕분에 그 어느 때보다 쉽게 새 프로젝트를 시작할 수 있지만, 그것을 프로덕션 수준으로 만드는 병목은 여전히 저 자신입니다. 그 결과 검토 후 발행하기를 기다리는 진행 중인 프로젝트가 많아졌습니다. 잦은 컨텍스트 스위칭과 작업 추적에 정신적 부담이 큽니다.

다음 달 목표

  • Refactoring English 챕터 3개 발행하기.
  • 2025년 연간 회고(8년 차) 발행하기.

도움 요청

여러분이 사는 곳에서 개발자용 책 한 권에 30달러(USD)는 비싼 가격인가요? 그렇다면 여러분 국가에서 Designing Data-Intensive Applications 같은 프로그래밍 책에 보통 얼마를 지불할 것으로 예상하는지(현지 통화 기준) 알려 주세요.

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

댓글