서론: 부호의 놀이를 할 재력이 있다면 이미 직장 생활을 그만뒀겠지…
Claude Pro 플랜 하나.
최상위 Max 플랜? API 종량제? 그런 부호의 놀이를 할 재력이 있다면 벌써 직장인 신세를 벗어나 프리랜서가 되었을 텐데… (진심)
필자의 꿈은 단순합니다.
AI 붐이라는 빅웨이브를 타고 한 방에 성공해서, 하루빨리 퇴직하고 평생 이불 속에서 빈둥거리는 것. 이상. 동기가 너무 불순한가? 알고 있다.
매일 아침 만원 지하철에서 영혼을 갈아 넣으며, '이대로 정년까지 손잡이를 붙잡고 있어야 하나…?'라고 생각하며 Claude Code를 혹사시켜 사고 정리 웹 앱을 만들었습니다.
다만 Pro 플랜이라 Opus를 풀가동하면 금방 사용량 초과 알림이 옵니다. 아슬아슬한 순간에 '계속하려면 리셋까지 기다려야 해요♡'. 현자 타임은 피할 수 없습니다. 이불 속에서 전라로 대기(비유)하는 수밖에 없습니다.
그런데 만들고 싶은 것은 하반신적으로 욕심이 가득 찬 세트였습니다.
- 마인드맵과 장문 메모를 한 화면에서 오가고 싶다
- 등록 절차 없이, URL을 여는 순간 바로 쓰고 싶다
- 데이터는 브라우저 안에만 두고 싶다
- 겸사겸사 Word・Excel・PowerPoint도 가져오고 싶고, AI로 맵도 만들고 싶고, 스마트폰에서도 쓰고 싶다
지갑은 Pro 플랜인데, 욕망은 Max급. 이제 뭘 해야 할지 모르겠습니다.
이 해결할 수 없는 모순을 어떻게 메웠는지가 이 글의 후반부 내용입니다.
그렇게 탄생한 것이 Nebulark(네비ュラーク) 입니다.
👉 https://nebulark.app/ (무료・등록 불필요・PC와 스마트폰 브라우저에서 작동합니다)
Nebulark のデモ:目次でメモを移動し、矢印キーでノードを選び、Tab で子を足し、表を書き換え、Ctrl+K でコマンドを探す
참고로, 처음 작동했을 때는 '작동했어! 이겼다! 이걸로 무노소득 프리랜서 생활에 돌입인가!?'라며 뇌가 과부하되었습니다.
그리고 만져보니 UI가 느리고 투박했습니다. 버튼은 넘치고, 글자는 '노드 일…'에서 잘립니다. 순식간에 진지해졌습니다. '이거로는 아무도 안 쓸 거야! 큰일났다!'라며 초조해져 테스트로 빡빡하게 규정하는 수련을 시작했습니다.
규모는 이 정도가 되었습니다.
| 항목 | 숫자 |
|---|
| 계약 | Claude Pro 플랜 1개 |
| |
| 기간 | 약 4주 (첫 커밋이 2026-09-23) |
| 커밋 | 100건 초과 |
| 앱 코드 | 약 3.5만 줄 |
| ... | 1,118건 (그중 수용 시나리오 163건. 모두 flutter test로 실행됨) |
기술 스택은 Flutter(Web)・Riverpod 3・freezed・flutter_quill입니다. 말투는 바보 같지만, 내용은 진지하게 갑니다.
애초에 무엇을 만들었나
3개의 패널이 연동: 트리 × 마인드맵 × Markdown 메모
생각할 때 필자는 항상 '전체를 보는 마인드맵'과 '세부 내용을 쓰는 노트'를 번갈아 가며 사용했습니다. 그리고 매번 '이 메모, 어느 가지 이야기였지…?'라며 길을 잃었습니다. 직장인의 회의록에서 흔히 볼 수 있는 일입니다.
그래서 화면을 왼쪽부터 노드 목록(트리)・캔버스(마인드맵)・메모 세 부분으로 나누고, 어디서 선택하든 다른 두 부분이 같은 노드를 가리키도록 했습니다. 정석적인 3개 패널 구성입니다.
메모는 보이는 그대로 편집할 수 있고(WYSIWYG), 저장 형식은 Markdown 텍스트입니다.
- 제목(H1~H3)부터
목차를 자동 생성하고, 클릭하면 해당 제목으로 부드럽게 이동합니다 -
Excel처럼 편집 가능한 표: '셀 선택 모드'와 '편집 모드'를 분리하여 Tab・화살표 키로 셀을 옮기고 입력하면 내용이 대체됩니다. Excel에서 붙여넣기 해도 그대로 표가 됩니다(직장인의 필수 스킬) -
코드 블록・인용・체크리스트・구분선・글자색・노드 내부 링크
- 이미지는 붙여넣기만 하면 자동으로 축소 및 압축하여 삽입됩니다(1 장 200KB 이하로)
- 줄의 시작 부분에
#
・-
・>
라고 치면 제목・글머리 기호・인용이 되고, ---
으로 바꾸고 엔터를 치면 구분선이 됩니다.
통신 없이 파일 가져오기
Word(.docx)・Excel(.xlsx)・PowerPoint(.pptx)・Markdown・CSV / TSV・HTML・텍스트를 서버로 보내지 않고, 브라우저 안에서만 분석하여 트리로 만듭니다. 회사의 자료를 외부에 내보내는 것은 직장인 인생이 끝나는 것이므로, 이 부분은 양보할 수 없었습니다.
Office 파일은 사실 내용물이 ZIP 압축 파일 형태입니다. 그래서 내부의 XML을 기기에서 읽습니다.
- Word는 제목 스타일, 목록, 번호 매김, 표를 읽어 들여 제목의 계층 구조를 그대로 트리로 만듭니다.
- Excel은 XML 트리를 (DOM) 만들지 않고 처음부터 순서대로 읽습니다. 5,000행 × 20열 북에서도 가져오기가
1.3~1.6초에서 0.3초로 단축되었습니다 - 파일을 화면에 드롭하는 것만으로 여러 개를 한 번에 불러올 수 있습니다.
BYOK (자신의 API 키)로 사용하는 AI
AI는 사용자의 키로 작동하며, 브라우저에서 직접 호출합니다 (Google Gemini API 또는 OpenAI 호환 엔드포인트). 기본 모델은 무료 사용량으로 쓰기 쉬운 gemini-flash-lite-latest입니다.
금전적으로 부담이 적습니다.
| 작업 | 단축키 | 내용 |
|---|
| 매핑화 | Ctrl+Enter | 노드의 제목・메모・이미지에서 자식 노드와 각 노드의 메모를 만듭니다. |
| ... | | |
| 어떤 것도 Ctrl+Z 한 번으로 되돌릴 수 있습니다. 무료 사용량에서 자주 발생하는 429(횟수 제한)나 503(혼잡)에는 대기열과 자동 재시도(2 → 4 → 8 → 16초…)로 대응하고 있습니다. | | |
사용 한도에 좌절하는 사람이 더더욱 사용하는 사람에게 같은 현자 타임을 느끼게 하고 싶지 않았거든요.
PC는 키보드만으로, 스마트폰은 2개 화면으로
- Ctrl+K 명령어 팔레트: 모든 기능과 모든 캔버스 노드를 이름으로 검색하고 실행합니다. -
공간 화살표 내비게이션: 부모-자식 관계가 아니라, 화면상의 배치로 '눌렀던 방향에 있는 가장 가까운 노드'로 이동합니다. -
Alt+화살표 키로 형제 간의 교체・계층 올리기/내리기, Tab으로 자식 추가, F2로 이름 변경, C로 색상 변경
스마트폰 및 태블릿에서는 세 가지 중 두 개를 위아래(가로 모드라면 좌우)로 배치하는 전용 화면이 됩니다. 만원 지하철에서 한 손으로 아이디어를 메모하기 위해서입니다. 직장인의 지혜.
개발 비하인드①: Pro 플랜으로 구동하기 위한 'Opus × Sonnet' 3단계 분업
방침: Opus 파이센에게 코딩하게 하지 말고, 생각하게 하라
처음에는 아무 생각 없이 모든 것을 Opus에게 맡겼습니다. 그리고 무사히 사망했습니다.
코드를 치는 작업은 양이 많아서 그만큼 사용량을 많이 먹습니다. Opus에게 계속해서 코드만 쓰게 하는 것은 고급 스시야의 대가에게 컵라면 물을 붓게 하는 것과 같습니다. 너무 아까워서 눈물이 날 지경입니다.
진짜로 현명함이 필요한 곳은 '무엇을 만들지', '빠지는 부분은 없는지'를 생각하는 장면 쪽입니다.
그래서, 설계와 감사(Audit)는 두뇌파 Opus 파이센에게 맡기고, 코드를 열심히 쓰는 것은 체력이 좋은 Sonnet에게 전적으로 위임하는 스타일로 했습니다. 정석을 따르는 3단계 분업입니다.
단계는 Claude Code의 커스텀 슬래시 명령어(.claude/commands/)로 시작합니다. 명령어의 프런트매터에 model:를 적어두면, 담당 모델로 자동으로 전환됩니다. 감사합니다!
| 단계 | 명령어 | 담당 | 할 일 | 비유하자면 |
|---|
| 1. 요구사항 정의・설계 | /design | Opus 5.5 | 코드를 쓰지 않고 읽으면서, 예외 케이스를 파악하여 PLAN.md에 작성합니다. | 두뇌파 설계사 파이센 |
| 2. 구현・단위 테스트 | /build | Sonnet 5.5 | PLAN.md대로 구현하고, 테스트를 추가합니다. | 체력이 좋은 목수 |
| 3. 수락・감사 | /check | Opus 5.5 | git diff와 PLAN.md를 대조합니다. 고치지 않고 반송합니다. | 귀신 같은 검사관 |
명령어의 내용은 이런 형태입니다 (발췌).
---
description: Phase 1 요구사항 정의・설계(Opus). PLAN.md를 작성하고 중단합니다.
argument-hint: <만들고 싶은 기능・고치고 싶은 결함 설명>
...
---
description: Phase 2 구현・단위 테스트(Sonnet). PLAN.md에 따라 구현합니다.
model: claude-sonnet-5-5
...
왜 분리하는 것이 좋았는가
1. '생각하는 것'과 '쓰는 것'을 섞지 않기
돌이켜보면, 이 앱의 대부분의 버그는 포커스 분산, 한국어 입력기 변환 중, 웹 환경과 테스트 환경의 차이점 등 '쓰기 전에 알아차릴 수 있었던 조건'들이었습니다.
그래서 Opus Python에는 /design으로 코드를 전혀 만지지 못하게 하고, 읽고 분석하는 것에만 집중하도록 합니다. 만지지 못하게 하는 것이 핵심입니다. 만지게 하면 기분 좋게 쓰기 시작해 버리거든요.
2. Sonnet이 헤매지 않을 정도의 세부 사항까지 작성하기
PLAN.md의 절차가 상세하면, Sonnet은 '어떻게?' 할 정도로 빠르고 정확하게 작성합니다. 반대로 모호한 지시를 내리면, 모호한 것이 돌아옵니다 (이건 당연하죠).
PLAN.md에 없는 설계 판단이 나오면 임의로 확장하지 않고, 추가하여 보고하는 규칙을 세웠습니다.
3. 작성자 본인에게 검수를 맡기지 않기
스스로 쓴 답안지를 스스로 채점하면 대개 만점이 나옵니다. 회사원들의 자기 평가 시트도 마찬가지입니다. AI도 그래요.
/check는 다른 모델이 git diff를 PLAN.md와 CLAUDE.md에 비교하여 보고, 수정하지 않고 되돌려줍니다.
'감사하는 김에 고치니 다른 곳이 망가졌다'라는 비극을 이걸로 막았습니다.
Pro 플랜의 범위를 넘지 않기 위한 규칙들
CLAUDE.md에는 기능 설명뿐만 아니라 '범위를 지키기 위한 규칙'도 적어 놓았습니다. 이 모든 것은 한 번 아픈 경험을 겪고 나서 추가한 것이므로, 즉 전부 회사원의 피와 눈물로 쓰여진 것입니다.
규칙 그 1: 같은 오류로 연속 두 번 실패하면 중단하고 보고하기
내버려 두면 AI는 똑같은 구멍을 전력으로 파헤칩니다. 아주 성실하게요. 상사에게 지시받은 일을 의심하지 않고 계속하는 회사원처럼요. 파낼 때마다 범위가 줄어듭니다.
두 번 파헤치면 손을 멈추고 사람에게 넘기기로 했습니다.
규칙 그 2: 환경 탓의 오류를 깊이 파고들지 않기
테스트 러너 자체 연결 끊김 같은 환경 문제가 가끔 발생합니다. 그리고 똑똑한 모델일수록 여기서 명탐정이 됩니다.
'진실은 언제나 하나!'라며 로그를 뒤지고, 중복 로그로 재실행하며 수수께끼는 풀릴지 몰라도 범위는 사라집니다. 큰일이에요! 그래서 이렇게 적었습니다.
【토큰 절약 규칙: 이미 알려졌거나 환경에 기인한 오류의 깊이 조사 금지】
flutter test와 flutter analyze가 통과했고, 테스트 러너 자체 소켓 끊김, 시간 초과, 'did not complete' 같은 인프라/환경에 기인하는 알려진 현상이 발생했을 때는 원인 조사를 하거나 중복 로그(-v)로 재실행을 스스로 시작하지 않는다.
규칙 그 3: 테스트 로그는 반드시 파일에 남기기
테스트 재실행은 시간과 범위 모두를 소모합니다. -r expanded로 한 건씩 기록을 남기고, Tee-Object로 파일에 쓰면서 실행합니다. 실패하면 그 파일을 읽는 것만으로 충분합니다.
참고로 '출력이 길어서'라고 | Select-Object -First N으로 파이프를 중간에 자르면, PowerShell은 flutter 도구만 멈추고 약 600MB의 flutter_tester가 부모 없이 남아 있게 됩니다. 좀비인가요? 이것도 CLAUDE.md로 갑니다.
규칙 그 4: 막힌 이유는 CLAUDE.md에 쓰기
'무엇을 했는지'가 아니라 '왜 그렇게 했는지'까지 적어두면, 다음 /design은 그것을 전제로 시작합니다. 같은 구멍에 두 번 빠지지 않게 하는 것. 결국 이것이 가장 큰 절약이었습니다.
개발의 뒷이야기 ②: AI에게 '손맛(촉감)'까지 보장하게 만드는 인수 테스트
AI는 코드를 빠르게 작성해 줍니다. 빠른 건 좋습니다. 하지만 가끔 작동은 하지만 무거운 것을, 엄청난 자신감을 가지고 내놓습니다.
실제로 화살표 키를 연타했더니 화면이 끊기고 표정이 굳어졌습니다.
'키를 계속 누르면 따라가지 못한다', '한 글자 입력할 때마다 전체 화면이 다시 그려진다' 같은 저하 현상은 단위 테스트에서는 거의 발견되지 않습니다. 테스트는 전부 녹색인데, 만지면 무겁다. 완전히 이해했다 (아무것도 모른다) 상태입니다.
그래서 CLAUDE.md에 【필수 규칙】으로 다음 문장을 적어 AI를 꽉 묶었습니다.
새로운 기능 추가나 기존 UI 개수정이 이루어졌을 때는, 반드시 integration_test/ 내의 시나리오(Robot 패턴)를 확장 및 추가하고, 손맛이나 성능이 저하되지 않았음을 테스트로 증명한 후에 태스크를 완료할 것.
그 결과, 테스트는 1,118건 (단위/위젯 900건 초과 + 수용 시나리오 163건)에 달했습니다. 테스트 코드의 양은 앱 코드와 거의 같습니다.
Robot 패턴: 사람과 같은 순서로 조작하기
수용 테스트에서는 AppRobot
・MindMapRobot
・EditorRobot
・CommandPaletteRobot
등의 로봇이 사람과 같은 순서로 조작합니다 (integration_test/robots/에 16개 파일).
키를 누른 상태와 놓는 것까지 정확하게 보내고, 문자는 한 글자씩 입력란으로 전달합니다. 로봇에게 인간인 척을 하게 하고 필자의 대신 일하게 만드는 테스트입니다. 이상적인 직장 생활은 로봇이었던 걸까요…?
RebuildCounter
: 재구축(作り直し) 횟수를 세는 기능
debugOnRebuildDirtyWidget에 후킹하여, 카운트하기 시작한 시점에 존재했던 컴포넌트의 재구축만을 타입별/키별로 계산합니다.
final counter = RebuildCounter()..start();
await palette.arrow(LogicalKeyboardKey.arrowRight);
await palette.arrow(LogicalKeyboardKey.arrowRight);
...
지극히 중요한 것이 counter.total > 0 체크입니다.
후킹이 빠져서
⚠️ [IMG:N] 형식 토큰은 이미지 placeholder 입니다. 번역하지 말고 원래 위치에 그대로 유지하세요.
| 하고 싶었던 것 | 첫 번째 안 | 선행 사례 |
|---|
| 표 삽입 | Ctrl+T | 브라우저가 '새 탭'으로 이동한다. 페이지에 키 입력이 전달되지 않아 막을 방법이 없다 |
| 표 삽입 (재도전) | Alt+T | Mac의 Option+T는 문자 '†'로 인식된다. 키 위치를 판별하여 해결 |
| 페인 이동 | Alt+1 | Mac의 Option+1은 '¡'가 된다. 이것 역시 키 위치를 판별하여 처리 |
| AI 제안 | Ctrl+Space | Mac의 입력 소스 전환/일본어 입력 On/Off와 충돌한다. S 키로 변경함 |
| ... | | |
| 표만 넣고 싶었는데, 단검(†)이 박혔습니다. 앗!? | | |
Ctrl+T를 누르니 새 탭이 열렸을 때는 정말 웃음밖에 나오지 않았습니다. 그럴 수밖에요.
AI는 JSON 이전에 인사부터 한다
'JSON 배열만 출력해 달라'고 부탁해도, AI의 답변은 대체로 자유롭습니다. 너무 자유로워요. 마치 정시에 퇴근하는 신입사원처럼요.
키보드 높이를 기준으로 스크롤하고, 테스트에서는 '브라우저 키보드의 높이만'을 교체하여 확인합니다 -
테스트는 통과하는데 버튼이 작동하지 않음: Flutter의 입력란은 외부를 누르면 포커스를 해제합니다. 하지만 손가락 터치로 그렇게 하는 것은 Web일 때만 해당하며, 테스트 환경(Android 취급)의 손가락으로는 해제되지 않습니다. 즉, 'iPhone에서는 붙여넣기 버튼이 작동하지 않는데, 테스트는 전부 녹색'입니다. 이런 건 알아챌 수가 없습니다. 그래서 테스트에서는 버튼을 마우스로 누르는 것으로 하고, Web의 손가락과 같은 조건으로 맞췄습니다 -
아이콘이 그려지지 않음: '선택', '전체 선택' 아이콘이 iPhone 실기에서만 공백으로 나옵니다. PC에서는 나오는데 말이죠. 어쩔 수 없어서 글자 버튼으로 바꿨습니다 -
자동 확대됨: 입력란의 글자가 16px 미만이면, iOS는 배려해서 화면을 확대합니다. 부탁한 게 아닙니다. 보이지 않는 입력란의 13.3px를 16px로 했습니다 -
잠금 및 신용카드 띠가 나옴: iOS가 메모 입력란을 비밀번호/주소란으로 오해하여 키보드 위에 자동 입력 띠를 내고 있었습니다. 메모만 쓰고 싶은데 카드 번호를 물어봅니다. 빈곤의 상처에 소금을 뿌리는 격입니다. autocomplete="off"
을 다시 붙이고 있습니다.
테스트가 실패했을 때는, 직전 60개의 조작/포커스 경로/열려 있는 대화창 등을 작성하는 진단(DIAGNOSTIC DUMP)
을 내리도록 했습니다.
AI가 로그를 한 번 읽기만 하면, 어디서・왜 떨어졌는지 알 수 있도록 하기 위해서입니다. 테스트를 다시 돌릴 필요가 없으니, 이것도 은근히 공간 절약이 됩니다.
요약: 무능력자라도, Pro 플랜으로도 만들 수 있다. 단, 역할 분담이 필요하다
- Opus 파이썬은 설계와 감사만 담당. 구현은 Sonnet에 던지기 -
직접 쓴 사람에게 채점하게 하지 않는다. 감사는 고치지 않고 되돌려준다 -
촉감(사용 경험)을 세어 지킨다. 재작업 횟수와 한 글자 입력 시간을 테스트로 삼는다 -
규칙을 만든다. 같은 구덩이에 빠지지 않는 것이 가장 큰 절약입니다CLAUDE.md
동기는 '이불 속에서 빈둥거리고 싶어서'. 지갑은 Pro 플랜 하나뿐입니다. 그래도, 역할을 나누고, 규칙을 쓰고, 촉감까지 테스트로 묶으니, 여기까지는 만들 수 있었습니다.
이렇게 무능력자도 해냈으니, 아마 당신도 할 수 있을 겁니다.
참고로, 무노동 소득 니트 생활에는 아직 1밀리미터도 가까워지지 않았습니다. 내일도 만원 지하철입니다.
끝으로
Nebulark는 가입 불필요・무료로 지금 바로 브라우저에서 사용할 수 있습니다.
- 데이터는 브라우저 안에만 저장됩니다(사이트 데이터를 지우면 사라지므로, 메뉴의 '데이터 내보내기'에서 JSON 백업을 받으세요)
- AI를 사용할 때만, 설정한 AI(Gemini나 로컬 LLM 등)에게 기기에서 직접 전송됩니다.
'이런 조작이 필요해요', '여기서 걸렸어요'와 같은 피드백을 주시면 감격할 겁니다.
좋아요・스토크를 받아도 Opus의 이용량은 회복되지 않지만, 직장인의 HP는 회복됩니다. 댓글이나 X에서 편하게 부탁드립니다. 감사합니다!
Discussion