코인충은 2021년도에 만든 워낙 오래된 프로젝트이다. 스택도 그 당시로서는 최선이었으나 지금보면 완전한 레거시. 특히 SSR을 직접 구현했던게 내 개인적으로 공부는 됐지만 지금은 손도 대기 끔찍한 레거시여서 마이그레이션을 해야지 해야지 생각만 하고 있다가, Fable 5가 풀린 상황에서 superpowers를 통해 계획을 세우고 Sonnet 5를 subagent로 쓰면서 `--dangerously-skip-permissions` 하에 알아서 끝까지 작업을 완료하도록 했다. 결과적으로 마이그레이션이 상당히 맘에 드는 수준으로 이뤄져서 정말 놀라웠다.
Nuxt 기반으로 거의 코드를 죄다 갈아엎은 수준이지만, 사용자 입장에선 바뀐 것을 전혀 인지하지 못할 수준으로 잘 이식됐다.
**대상**: coinsect_frontend (Vue 3 Composition + Vuex + axios + 자체 구현 SSR/WAS) → coinsect_nuxt (Nuxt 4 + TanStack Query + Pinia + headless composable)
**기간**: 2026-07-02 ~ 2026-07-06 (커밋 기준)
**결과**: 계획 20개 태스크 전부 완료, 34 커밋, 테스트 164/164 green, 실서비스(coinsect.io) 대비 SEO 패리티 검증, 프로덕션 빌드 확인
주말이 껴 있어서 7월 2일부터 6일로 결과가 나오기는 했는데 사실상 **하루만에** 완료.
---
## 스택 전환
| | 구 (coinsect_frontend) | 신 (coinsect_nuxt) |
|---|---|---|
| 프레임워크 | Vue 3 + Vue CLI | Nuxt 4 |
| 상태관리 | Vuex | Pinia (UI/실시간 상태) |
| 서버 데이터 | Vuex action + 수제 캐시(setTimeout) | TanStack Query (SSR hydration, staleTime) |
| HTTP | axios | `$fetch` (Nuxt 내장) |
| SSR | 자체 Express WAS + `renderToString` + `window.INITIAL_DATA` | Nitro |
| 웹소켓 | 컴포넌트마다 새 커넥션, 재연결 없음 | 거래소별 공유 싱글턴 + ref-count + 자동 재연결 |
| 런타임 의존성 | axios 포함 다수 | **7개뿐**: nuxt, vue, vue-router, @tanstack/vue-query, pinia, @pinia/nuxt, dayjs |
의존성 최소화를 명시적으로 요구했다 — VueUse 같은 유틸 라이브러리도 배제하고 debounce, 이벤트 리스너, 드래그, 웹소켓 재연결까지 전부 직접 구현하도록 시켰다.
---
## 프로세스: 4단계 (이 문단은 Claude Code의 입장)
### 1단계 — 코드 파악 (병렬 탐색)
계획을 세우기 전에 구 레포를 이해해야 했다. 핵심 파일(라우터, 스토어, axios 모듈, App.vue)은 직접 정독하고, 나머지는 세 개의 탐색 에이전트를 **병렬**로 띄웠다:
- 앱 인프라(모달/토스트/툴팁 시스템, 이벤트 버스, 레이아웃 셸)
- 라우트/기능 인벤토리(26개 라우트 전부, 챗 서브시스템, 웹소켓 훅, 커뮤니티)
- SCSS 아키텍처(전역 유틸리티 클래스 의존도, 이식 난이도)
이 조사에서 나온 핵심 판단들이 이후 전체 설계를 결정했다:
- 모달 시스템(`await modal.confirm()` 한 줄로 끝나는 promise-through-store 패턴)은 **보존할 가치가 있는 좋은 설계**로 판정 — Pinia 위에 같은 인체공학으로 재구현.
- SCSS는 이미 `@use` 기반, `::v-deep` 0건, 테마도 100% 네이티브 CSS 변수 — **거의 그대로 이식 가능**. Tailwind 전환은 견적만 수 주 규모로 나와서 기각.
- 실제 인증 라우트가 **0개**(스캐폴딩만 존재)라는 것도 이때 발견 — 이식 대상에서 제외.
### 2단계 — 계획 수립 (`superpowers:writing-plans`)
조사 결과를 바탕으로 20개 태스크짜리 구현 계획을 작성했다. 각 태스크는 파일 경로, 인터페이스 시그니처, (인프라 태스크는) 완성 코드, 보존해야 할 동작 명세, TDD 스텝까지 포함한 자기완결적 브리프였다 — 신선한 컨텍스트의 구현자가 원본을 참고하며 재작성할 수 있도록.
Global Constraints에 "의존성 최소화", "Tailwind 안 함", "죽은 코드 이식 안 함(FoolsDayPrank, TwitterTimeline, 인증 스캐폴딩)" 같은 프로젝트 전역 규칙을 명시해, 이후 모든 태스크가 암묵적으로 따르게 했다.
### 3단계 — 구현 (`superpowers:subagent-driven-development`)
태스크마다 **완전히 새 컨텍스트의 Sonnet 서브에이전트**를 디스패치했다. 각 에이전트는:
1. 브리프를 읽고 원본(구 레포) 코드를 직접 읽어가며 구현
2. 테스트 작성/실행, 실라이브 API(`api.coinsect.io`)에 대고 SSR 검증
3. 자체 리뷰 후 보고 (구현 내용, 검증 증거, 이탈/우려사항)
4. **커밋은 하지 않고 스테이징까지만** — 컨트롤러(Fable 5, 이 세션)가 diff를 확인하고 커밋
컨트롤러는 매 태스크마다: 보고 검토 → (필요시) 진행 레저(`progress.md`)와 계획서 갱신 → 다음 브리프 추출 → 다음 에이전트 디스패치, 를 반복했다. 복잡도가 높은 태스크(모달 시스템, 커뮤니티 게시판, 웹소켓 레이어, 웹챗) 4곳에는 **별도의 스팟 리뷰 에이전트**를 추가로 붙여 구현자와 다른 시각으로 검증했다.
---
## 숫자로 보는 결과
- **계획 태스크**: 20/20 완료
- **커밋**: 34개 (기능 20 + 리뷰 수정 4 + 사용자 QA 후속 수정 10)
- **테스트**: 164/164 green (vitest)
- **런타임 의존성**: 7개 (VueUse/axios/ofetch 등 전부 배제, 직접 구현)
- **번들 크기**: 클라이언트 JS+CSS 약 1MB, `.output` 전체 gzip 991KB
- **SDD 산출물**: 브리프+리포트 40개 파일, 총 1,958줄 — 태스크별 근거 기록
## 왜 이 방식이 통했나 (요약)
1. **코드를 먼저 이해시키고**(병렬 탐색), **계획을 문서로 못박은 뒤**(writing-plans), **신선한 컨텍스트로 하나씩 구현시키고**(subagent-driven-development), **리뷰로 걸러내고**(스팟 리뷰), **사람이 실사용으로 마무리**하는 — 각 단계가 서로 다른 종류의 실수를 잡아낸다. 자동화된 리뷰는 웹소켓 레이스나 CSS 회귀를 못 잡았지만, 실사용 QA가 잡았다. 반대로 KIMP 산술 정밀도나 CDN 검증처럼 사람이 놓치기 쉬운 것은 리뷰 단계가 잡았다.
2. **"구현자는 스테이징까지, 컨트롤러가 커밋"** 규칙 덕분에 모든 변경이 커밋 전에 한 번 더 사람(컨트롤러) 눈을 거쳤다.
3. 의존성 최소화라는 제약이 오히려 "이 라이브러리가 뭘 하는지 몰라도 됨"이 아니라 "우리가 뭘 만들고 있는지 정확히 알아야 함"을 강제해서, 버그의 원인 파악이 항상 우리 코드 안에서 끝났다(서드파티 블랙박스 없이).