Refactoring English: Month 20

Michael Lynch

리팩터링 잉글리시: 20개월 차

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

한 줄 요약

스크래퍼 봇과 싸우며 책 판매량 세 배로 늘리기.

처음 오셨나요?

안녕하세요, 저는 마이클입니다. 소프트웨어 개발자이자 작은 인디 테크 비즈니스를 운영하는 창업자입니다. 지금은 Refactoring English: Effective Writing for Software Developers라는 책을 쓰고 있습니다.

매달 이렇게 회고를 발행해 책 작업과 전반적인 근황이 어떻게 흘러가고 있는지 공유합니다.

하이라이트

  • 7월 책 판매량이 이미 탄탄했던 6월 판매량의 세 배를 기록했습니다.
  • 세상에서 가장 멍청한 스크래퍼 봇과 싸우고 있습니다.
  • AI를 활용해 멀티플레이어 브라우저 게임을 만들고 있습니다.

목표 점검

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

Refactoring English를 소개하려고 팟캐스트 5곳에 섭외 요청하기

  • 결과: 팟캐스트 한 곳에만 제안했습니다
  • 성적: D

Talking Postgres에 제안하려고 꽤 공을 들였는데, 알고 보니 호스트에게는 LinkedIn 외에는 연락처가 없었습니다. 그래서 LinkedIn 메시지를 보냈지만 답이 없었습니다. The TMPDIR Podcast에 게스트로 출연하긴 했지만, 제가 제안하기도 전에 먼저 초대받은 거라 실적으로 셀 수 없습니다.

Refactoring English 웹사이트 순방문자 3만 명 달성하기

  • 결과: 순방문자 2만 3,817명
  • 성적: B

속으로는 이 목표를 “Hacker News 첫 페이지에 글 하나 올리기”라고 생각했는데, 실제로 올리고도 3만 명에는 도달하지 못했습니다.

얼리 액세스 마무리하고 책 1.0 정식 출시 선언하기

  • 결과: 여전히 1.0 미출시
  • 성적: D

독자 피드백에 대응하느라 예상보다 많은 시간을 썼습니다. 피드백 앱은 잘 작동하고 있지만, 예측하기 어려운 추가 작업도 계속 만들어냅니다.

Refactoring English 지표

지표2026년 6월2026년 7월변화
순방문자17,52323,817+6,294 (+36%)
사전 주문 수익$1,441.86$3,908.80+$2,466.94 (+171%)

6월은 킥스타터 이후 책 판매량이 두 번째로 높았던 달이었는데, 7월 판매량은 그 6월의 세 배를 기록했습니다.

판매량이 급증한 가장 큰 이유는 얼리 액세스 할인을 종료했기 때문입니다. 7월 13일에 얼리 액세스 가격이 7월 20일에 종료되며 가격이 30달러에서 49달러로 인상된다고 공지했습니다.

7월 20일에 판매량이 급증한 뒤 판매가 둔화된 모습을 보여주는 그래프

할인 마지막 날, “Why I Stopped ‘Creating Content,’”라는 블로그 글을 올렸는데 이 글이 Hacker News 첫 페이지에 올랐습니다. 그리고 Hacker News 스타일의 좀 더 인디적이고 덜 테크 중심적인 사이트인 bubbles.town에서 그날 가장 인기 있는 글이 되기도 했습니다.

할인 마지막 날 판매량이 폭발적으로 늘었고, 그날 하루에만 1,000달러가 넘는 매출이 발생했습니다.

가격을 49달러로 올리자 예상대로 판매량은 곧바로 급감했습니다. 책이 공식 1.0으로 출시된 뒤에 가격을 두고 더 실험해 볼 계획입니다.

세상에서 가장 멍청한 스크래퍼 봇과 싸우는 중

월요일에 블로그 글을 올리고 방문자가 크게 늘었습니다. 그런데 화요일에 또 한 번 엄청난 급증이 있었습니다.

방문자가 크게 늘어난 걸 보고 인기 블로거가 링크를 걸었나 했는데, 알고 보니 봇이었습니다.

모든 방문자가 Hacker News Popularity Contest로 몰리고 있었습니다. 예전에도 블로거가 콘테스트에서의 순위를 언급하며 제 도구에 링크를 걸어 방문자가 급증한 적은 있었지만, 이렇게 크고 이렇게 오래 지속된 적은 없었습니다.

두 번째 급증을 보고 “와, 이번 주 제대로 흐름 타는데!”라고 생각했습니다. 아내에게 그 얘기를 하던 중 문득 깨달았습니다.

오늘 방문자가 또 5만 명이나 늘었어! 어디서 온 건지 모르겠네. 누가 트윗한 것 같아.

음, 근데 누가 트윗했으면 리퍼러에 Twitter가 떠야 하는데.

아… 봇이네.

로그를 확인해 보니 모든 요청이 정확히 동일한 브라우저 유저 에이전트를 쓰고 있었습니다.

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36

Chrome 125로, 2년 넘게 된 버전입니다. 워낙 오래된 버전이라 봇을 쉽게 식별할 수 있었습니다. 제 블로그를 읽는 사람 중에 아직도 그렇게 오래된 브라우저를 쓰는 사람은 거의 없을 테니까요.

스크래퍼는 콘테스트 메인 페이지를 반복해서 로드한 뒤 모든 링크를 계속 클릭하는 방식인 것 같았습니다. 그래서 살짝 속임수를 써 봤습니다. Bunny에 리라이트 스크립트를 넣어 유저 에이전트를 확인하고, 스크래퍼이면 가짜 응답을 돌려주도록 했습니다. 3,000개가 넘는 링크가 있는 페이지를 만드는 대신 링크가 세 개뿐인 페이지를 생성하도록 한 거죠.

알 수 없는 이유로 가짜 응답은 통하지 않았습니다. 스크래퍼가 이미 전체 URL 목록을 데이터베이스에 넣어 둔 것 같아 홈페이지에서 숨겨도 소용이 없었던 것으로 보입니다.

속도 제한도 시도해 봤지만, Bunny가 지원하는 가장 낮은 제한이 1KB/s인데 앱 데이터 대부분이 몇 KB짜리 파일이라 효과가 없었습니다. 스크래퍼는 100개 정도의 서로 다른 IP 블록을 돌려가며 요청해서 IP로 제한을 걸 수도 없었습니다.

결국 IP 대역으로 차단했습니다. Bunny 로그에서 해당 유저 에이전트를 찾아 공격과 연관된 모든 IP를 모으는 도구를 바이브 코딩으로 만들었습니다. 효과는 있었는데, 일주일 뒤에 새로운 IP와 조금 더 최신의 유저 에이전트로 공격이 다시 시작돼서 스크립트를 다시 돌려 차단할 IP 목록을 업데이트했고, 지금은 잘 막히고 있습니다.

이 공격에서 가장 이상한 점은 봇이 차단당해도 신경 쓰지 않는다는 것입니다. 그냥 계속 서버를 두들깁니다. “지난 2주 동안 TCP 레벨에서 100% 차단된 요청에 컴퓨팅과 대역폭을 낭비할 필요가 없지”라고 판단할 것 같지만, 신경 안 쓰는 모양입니다.

공격자 IP를 차단한 뒤에도 계속 요청을 퍼붓습니다.

공격이 시작된 날 Netlify 지원팀에 연락했지만 도움이 전혀 안 됐습니다. 답장을 받는 데만 일주일이 걸렸습니다. 첫 번째 답장은 존재하지도 않는 설정을 바꾸라는 내용이라 AI가 쓴 것 같았고, 두 번째 답장은 좀 더 사람이 쓴 것 같았지만 요지는 “제가 답변하는 데 2주 걸리는 동안 당신이 이미 문제를 해결한 것 같으니 할 일이 없네요!”라는 것이었습니다. 다행히 요청하자 초과 요금 55달러는 환불해 줬습니다.

정적 사이트는 어디에 호스팅해야 할까?

모든 정적 사이트를 Netlify에서 호스팅하고 있는데, 점점 서비스가 나빠지고 있습니다. 이번 스크래퍼 봇 공격에 완전히 무심한 대응을 보고는 스크래퍼 봇을 더 적극적으로 처리해 주는 업체를 찾아야겠다고 마음먹었습니다.

당연한 답은 “Cloudflare”지만, 인터넷 인프라가 Cloudflare에 너무 집중된 상황이 걱정돼서 그 집중을 더 심화시키고 싶지 않습니다.

VPS나 VPS + Bunny를 CDN으로 쓰는 것도 고려했지만, VPS가 다운되거나 Bunny 캐시 설정을 잘못해서 Hacker News 첫 페이지에 오른 날 사이트가 내려가는 상황을 원치 않습니다. 그냥 돈을 내고 다른 사람이 사이트를 안정적으로 유지해 주는 솔루션을 원합니다.

  • Surge
    • 장점
      • 정적 호스팅에만 집중한다는 점이 제가 원하는 바와 정확히 일치합니다
      • 트래픽이 무제한이라 스크래퍼 봇 공격 비용을 업체가 부담합니다
    • 단점
      • (아마) 1인 운영이라 장애 위험이 큽니다
      • 모든 관리가 터미널 앱으로만 이뤄집니다. 웹 앱이 없습니다
      • 다중 인증을 지원하지 않는 것으로 보입니다. 다만 작업 중인 것 같습니다
      • 업로드 시 변경된 파일만 동기화하는 rsync 방식이 아니라 모든 파일을 무조건 다시 업로드해서, 점진적으로만 바뀌는 제 큰 사이트들에는 불편합니다
  • statichost
    • 장점
      • 1인 운영이라 고객 지원이 빠르고 꼼꼼합니다
      • 불필요한 복잡함 없이 정적 호스팅에 주로 집중합니다
    • 단점
      • 1인 운영이라 장애 위험이 큽니다
      • 업로드 시 변경된 파일만 동기화하는 rsync 방식이 아니라 모든 파일을 무조건 다시 업로드해서, 점진적으로만 바뀌는 제 큰 사이트들에는 불편합니다
      • 봇 스크래퍼 보호 기능이 포함되어 있지 않습니다
      • 사이트 빌드와 호스팅을 묶어서 제공하는데, 저는 호스팅만 원합니다
      • EU 중심이라는 점이 큰 장점으로 내세워지는데, 저는 미국에 있습니다
  • Vercel
    • 장점
      • DDoS/스크래퍼 봇을 막아준다고 합니다
      • rsync 방식의 업로드를 지원하는 것으로 보입니다
    • 단점
      • 거대하고 복잡한 서비스입니다
      • Vercel이 Netlify보다 저를 더 잘 대우해 줄 거라는 믿음이 없습니다
  • Bunny CDN 위에 직접 구축
    • 고려해 봤지만, Bunny 위에 점진적 업로드와 원자적 배포를 구현하는 것 자체가 복잡한 프로젝트가 될 것 같습니다

Mikeville: 미완성 멀티플레이어 브라우저 게임

작은 클라우드 서버를 실험 중인데, CPU 4개와 RAM 8GB를 갖추고도 대부분 유휴 상태로 있습니다. 남는 용량이 꽤 있는 괜찮은 서버가 있는데, 친구들을 위해 호스팅하면 재미있을 만한 게 뭐가 있을까요?

게임은 어떨까 싶었습니다. Valheim이나 ARK 같은 셀프 호스팅 게임들을 봤는데, 그런 게임들은 다른 플레이어가 동시에 접속해 있어야 재미있는 것 같습니다.

몇 달에 한 번만 게임을 하는 저로서는 다른 사람들이 동시에 접속해 있으면 들어가서 함께 즐기고, 아무도 없을 때도 부재중에 무슨 일이 있었는지 보는 재미가 있는 게임을 원합니다. 제가 떠올리는 느낌은 친구들과 공동 소유한 해변 별장이 있어서 가끔 같이 휴가를 보내지만, 따로 방문했을 때는 서로 쪽지와 선물을 남기는 그런 느낌입니다.

문제는 그 해변 별장 같은 느낌을 게임의 구체적인 컨셉으로 어떻게 옮길지 떠오르지 않는다는 것입니다.

지금은 바이브 코딩으로 게임 개발이 어떤 느낌인지 탐색하면서 무엇이 재미있는지 알아보는 중입니다. 이 프로토타입을 구상할 때 염두에 둔 게임은 제가 오랫동안 즐겼던 Stardew Valley와 Ultima Online 두 가지였습니다.

브라우저에서 직접 플레이할 수 있도록 게임을 공개해 두었습니다.

오늘 잠깐 접속해 있을 예정이니 방문하고 싶으면 들러주세요. 소스는 더 나은 git 포지를 찾을 때까지 GitLab에 올려두었습니다.

좋아했던 블로그 글

  • Stripe Just Wants a Number
    • exe.dev 블로그를 좋아하는데, 특히 소규모 소프트웨어 판매자에게 영향을 미치는 소프트웨어 문제를 바라보는 시각이 흥미롭습니다. 제게는 그 정도로 복잡한 결제 로직이 없어서 당장 도움이 되진 않지만, 원칙적으로는 멋진 아이디어라고 생각합니다.
  • 99% of My Website Traffic Is Bots
    • 이번 달에 스크래퍼 봇을 겪은 터라 특히 공감되고 도움이 됐습니다. 직접 그린 일러스트도 좋았습니다.
  • I Regret Migrating to Codeberg
    • 지난 1년간 프로젝트들을 GitHub에서 Codeberg로 옮기고 유료 회원으로 후원도 하고 있었는데, 이제는 그 투자를 후회하고 있습니다. 장애가 너무 잦고 서버가 과부하되는 날이 많습니다. 결정타는 AI를 사용하는 프로젝트를 금지하기로 한 Codeberg의 결정이었습니다. 저는 “과도한” AI 사용이라는 임의의 기준에는 못 미치겠지만, 그래도 다른 곳으로 옮길 계획입니다.
  • Super Mario Derivations
    • Nix를 이용한 멋진 트릭을 소개한 글인데, 작성자가 Nix 속성을 이용해 에뮬레이터 속 Super Mario 3의 플레이 상태를 인코딩했습니다. 예를 들어 nix build '.#level1.rightb.rightb.rightab.rightb'는 레벨 1을 시작해 Right + B(오른쪽으로 달리기)를 두 번, Right + A + B(달리며 점프), 다시 Right + B(오른쪽으로 달리기)를 누르라는 뜻입니다. Nix가 모든 게임 상태를 캐시하므로 버튼 하나를 바꿔도 이전 실행과 달라진 부분만 다시 계산합니다.

마무리

다음 달 목표

  • Refactoring English를 소개하려고 팟캐스트 5곳에 섭외 요청하기.
  • Refactoring English 웹사이트 순방문자 3만 명 달성하기.
  • 책 1.0 정식 출시 선언하기.

도움이 필요한 부분

  • 정적 사이트 호스팅 관련 추천이 있다면 알려주세요.
  • Claire Giordano를 아신다면, 제가 Talking Postgres에 좋은 게스트가 될 거라고(Postgres가 아니라 테크니컬 라이팅에 대해 이야기하러) 전해 주세요.

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

댓글