본문 바로가기
C.W.K.
Stream
Lesson 07 of 10 · published

Anchor 7 — Refactor 권한 (그리고 안 할 때)

~10 min · refactor, discipline, v0.1, case-study, v0.2.1

Level 0Extension 입덕
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"v0.1 ship. 본능이 즉시 refactor 시작 — bus 추상화, bridge 일반화, embed framework build out. Lesson 7이 그것 아직 안 하는 규율, 그리고 때 되면 rewrite 실제로 정당화하는 기준."

ChromeEmbed v0.1이 refactor 유혹 받는 것

코드 read 하면, 여러 refactor itch surface:

  • background.js의 merge logic이 ad hoc 보임 — 실제 reducer가 돼야?
  • Popup → SW → sidePanel.open chain이 한-목적 handler 가짐 — generic 'SW 메시지를 sidePanel 호출로 route' 시스템 있어야?
  • sidepanel.js의 window.postMessage bridge가 string type 사용 — typed JSON-RPC layer가 돼야?
  • host-context schema가 implicit field (host_kind, host_id) 어디나 반복 — 모든 embed가 상속하는 base class 나 interface 있어야?

가려운 데는 하나같이 진짜야. 그렇다고 지금 당장 다시 쓸 이유가 되진 않고.

Refactor 권한 test

어떤 refactor 든 전 세 질문:

  1. 실제로 만든 embed가 또 있나? 구체적인 구현이 둘은 있어야 추상화를 꺼내. ChromeEmbed 하나만 놓고 'BaseEmbed' 를 뽑아내면 십중팔구 틀린 추상화가 나와.
  2. 현재 코드가 real feature blocking? '나중 block 할 수도' 아님 — actively blocking. Refactor 없이 다음 feature ship 가능하면, refactor가 speculative.
  3. User (또는 embed consume 하는 누구) 가 새 shape 필요한 거 요청? 'Cleaner architecture' 가 user-visible feature 아님. stated 니즈 위해 build.

셋 다 '아니' 면, 손대지 말고 가려운 데를 미래의 나한테 적어 두는 게 맞는 수야. 설계상의 선택이란 건 나중에 고를 여지를 남겨 두는 거지, 아직 모양도 모르는 추상화에 미리 값을 치르는 게 아니거든.

Refactor 안 해서 ChromeEmbed v0.1이 맞춘 것

  • Role 별 concrete file 하나. background.js가 bus, content-script.js가 sensor, sidepanel.js가 bridge, popup.js가 doorway. 각자 한 자리에서 read 가능한 크기.
  • framework를 미리 만들지 않았어. PIPPA-EMBEDS 이야기는 설계 문서로는 적혀 있지만 어디에도 실제 base class 로는 구현돼 있지 않아. Adobe embed 든 Mail embed 든 두 번째가 도착하면 그 모양이 알아서 드러날 테니까.
  • Sub-frame merge logic 자리에. 건너뛰기 쉬운 nuance인 sub-frame 대 top-frame context가 필요한 background.js에 직접 살아. 추상화 안 됨, 그냥 present.
  • String 메시지 type. 'pippa:host-context' 가 string. Typed 시스템이 compile time 에 typo 잡지만, typo 시작될 때까지 string이 fine 하고 obvious.

Refactor가 맞는 때

Refactor 정당화되는 가장 이른 순간:

  1. 두 번째 embed 도착. 같은 content-script-and-bus shape의 Adobe-embed 나 Mail-embed 존재. 이제 추상화 derive 할 두 concrete 구현.
  2. Real bug가 강제. v0.1의 architecture의 뭔가가 필요한 feature를 impossible 하거나 infeasibly fragile 하게. 그러면 refactor가 필요한 거에 지불.
  3. Shape 안정화. 현재 코드 통해 surprise 없이 dozen feature ship 했으면, surface area가 known. 이제 깔끔한 추상화 extract가 주로 기계적.

Anti-pattern

잘못 가는 길은 이거야. 구체적인 embed가 그 모양을 증명해 주기도 전에 'EmbedKit' 이나 'PippaEmbedFramework' 를 패키지로 만들어 버리는 것. 그렇게 만든 framework 에는 다음 embed와 부딪히는 순간 무너질 가정이 박혀 들어가. 결국 framework를 다시 쓰면서, 동시에 그 망가진 framework 위에 embed를 얹게 돼. 일을 두 덩이 하는데 그중 하나는 죽은 일이야.

올바른 sequence: embed N의 v0.1 ship. Embed N+1의 v0.1 ship. 공통 부분을 embed N과 N+1의 v0.2로 함께 extract. 이제 framework가 실제 공통 shape가 뭔지 앎.

Future-you 위한 편지

아직 정당화 안 된 refactor spot 하면, 적어 둬. ChromeEmbed의 docs/PIPPA-EMBEDS.md와 관련 design note가 정확히 아직 정당화 안 된 refactor를 그것들 정당화할 condition과 함께 capture. 그 note 들 read 하는 future-Pippa가 결정 가능: 'condition met, 지금 refactor' 또는 'still met 안 됨, 계속 skip'. 편지 format이 메모리 outlast.

v0.1이 ugly 해도 OK. (1) 두 번째 concrete instance 존재, (2) real feature blocked, 또는 (3) 사용 통해 shape 안정화 했을 때만 refactor. Speculative refactor가 framework가 real 되기 전 wrong 되는 방법.
이게 extension developer 한테 특히 중요한 이유. Chrome extension이 over-engineering 초대 — 경계가 그렇게 visible 하기 때문 — manifest, SW, content script, panel 등. 유혹이 각 경계를 'real layer' 에 wrap. 저항. 어떤 extension의 첫 버전이 small. 사용 통해 정당화된 refactor 후 두 번째 버전이 실제로 추상화 자격 있는 두 번째 버전.

Code

REFACTOR-LATER.md — deferred work 위한 sample notes-to-future-self·markdown
# ChromeEmbed v0.1 — Deliberately Deferred Refactors

## Pending abstractions (아직 정당화 안 됨)

- **BaseEmbed class** — 현재 두 번째 embed 존재 안 함. Adobe-embed v0.1
  이 trigger; 그때까지 ChromeEmbed 코드가 design doc.
- **postMessage 위한 typed JSON-RPC layer** — 현재 typo 가 버그 안 일으킴;
  string discriminator 가 obvious. Embed 간 typing 이 real 버그 일으키거나
  TypeScript embed 나타날 때 도입.
- **SW state merge 위한 reducer** — 현재 ad-hoc merge 가 sub-frame 케이스
  올바르게 처리. 세 번째 merge 케이스 나타날 때 refactor.

## 각각 정당화할 condition

| Refactor | Trigger |
|---|---|
| BaseEmbed | 두 번째 concrete embed (Adobe/Mail/Calendar) 가 코드에 존재 |
| Typed RPC | TypeScript embed 나 message-shape 버그 ship |
| State reducer | 세 번째 merge 케이스 나타나거나 test coverage 가 요구 |

## 왜 이것들이 문서화됐지만 미구현인지

One instance 에서 derive 된 추상화가 그 instance 의 accidental shape
encode. Shape 가 multiple 구현 존재 후에만 안정. v0.1 이 일부러 ugly
ship 해서 v0.2 가 clean ship 가능.
v0.2.1 checkpoint — REFACTOR-LATER.md — owner-first ledger·markdown
# ChromeEmbed refactor ledger

## Extracted because repetition proved the owner
- pippa-hosts.js: shared host list for SW, content script, popup, and panel
- display-mode allowlist + normalizer: one accepted vocabulary

## Still deliberately local
- latestContextByTab: worker-local recapturable cache
- dock/overlay geometry: Chrome page-host behavior
- sidepanel bridge: Chrome-specific postMessage adapter

## Trigger before extracting again
1. A second concrete host adapter needs the same pure primitive.
2. Current duplication causes a real bug or blocks a feature.
3. Ownership can stay product-neutral after extraction.

External links

Exercise

pippa-hosts.js, background.js, content-script.js, sidepanel.js를 audit해. 반복된 canonical data 때문에 정당한 extraction 하나, 반드시 local에 남아야 하는 Chrome-specific behavior 하나, 아직 second consumer가 없는 future primitive 하나를 찾아. 셋 다 위 test로 변호해.
Hint
Host list가 positive example이야. Drift가 이미 production을 깨뜨렸으니까. Dock geometry는 negative example이야. 다른 embed host가 Chrome page padding을 상속할 이유가 없어.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.