C.W.K.
Stream
Lesson 02 of 05 · published

View 는 첫 구현이지 아키텍처가 아니야

~13 min · domain-view-separation, react, architecture, future-proofing

Level 0툴 임차인
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"비즈니스 로직이 버튼 안에 살면, 버튼을 절대 못 바꿔. domain 을 view 밖에 두면, view 가 교체 가능해져."

편안한 실수

UI 짓는 가장 빠른 방법은 로직을 쓰이는 바로 그 자리에 두는 거야: candidate 를 보여주는 컴포넌트가 그걸 fetch 도 하고, lineage 도 추적하고, session 에 어떻게 binding 되는지도 알아. 작동하고, 출하되고, 네 domain 전체를 현재 view framework 에 조용히 용접해. 다른 view 를 원하는 날 — native renderer, 리디자인, 뭐든 — domain 로직이 버리려는 컴포넌트에 엉켜있는 걸 발견해.

Domain core vs View layer

규율은 하나처럼 느껴지는 두 가지를 분리해. domain core 는 앱이 알고 하는 것: workspace 상태, 생성 lineage, 포토샵 session binding, document 모델. view layer 는 앱이 어떻게 보이고 조작되는지: 컴포넌트, 레이아웃, 상호작용. domain core 는 view layer 완전 밖에 살아야 해 — 컴포넌트 안 X, view framework 에 의존 X, 어떻게 표시되는지 모름.

view 는 인터페이스의 첫 구현이지 아키텍처 자체가 아니야. 현재 UI framework 를 domain 을 렌더하고 모는 한 가지 방법으로 다뤄 — 첫째지, 유일이 아니라. domain core 가 혼자 서고 view 가 그냥 그걸 읽고 command 하면, 앱이 근본적으로 뭔지 안 건드리고 view 를 스왑할 수 있어.

왜 이 특정 앱이 이게 필요한지

이건 그 자체를 위한 추상적 future-proofing 이 아니야 — 지평선에 구체적인 둘째 view 가 있어. 계획이 캔버스를 위한 미래 native 렌더링 레이어 옵션을 명시적으로 보존해, 둘째 캔버스가 언젠가 무거운 native renderer 가 되면. 그 미래 view 는 domain core 가 현재 것에 절대 의존 안 했어야만 domain core 를 재사용할 수 있어. 분리가 알려진, 계획된 미래를 재작성 대신 싸게 유지하는 거야.

분리는 둘째 구현이 가설이 아니라 진짜일 때 정확히 보상해. domain/view 분리는 진짜로 view 가 영영 하나뿐이면 over-engineering 이야. 둘째 view 가 진짜로 계획되는 순간 필수고. 판별자는 모든 추상화랑 같아: 둘째 구현이 진짜냐 상상이냐. 여기선 진짜야 — native 캔버스가 이름 붙은 미래야.

테스트: view 스왑에서 뭐가 살아남아?

실제로 분리했는지 확인하는 깨끗한 방법이 있어. 내일 view layer 전체를 삭제하고 다른 framework 로 새로 쓴다고 상상해. 뭐가 살아남아? workspace 상태, lineage 기록, session binding 이 다 안 건드려지고 살아남으면 — 삭제된 컴포넌트에 절대 안 살았으니까 — 분리한 거야. view 삭제가 domain 로직을 같이 데려가면, 로직이 틀린 데 있었던 거야. 사고 실험이 감사야.

view 가 죽을 때 죽는 로직은 domain 로직이 아니었어 — 변장한 view 로직이었어. 엉킴의 가장 깨끗한 신호는 앱이 아는 걸 잃지 않고 UI 제거를 상상 못 하는 거야. lineage 추적이 컴포넌트 트리랑 같이 사라질 거면, 진짜 분리된 적이 없던 거야. 생존 테스트가 네가 만든 줄 몰랐던 용접을 드러내.

피파의 고백

React 가 그냥 상태를 컴포넌트에 두는 걸 너무 쉽게 만들어 — 바로 거기 있고, hook 이 한 줄인데, 왜 별도 domain 레이어를 지어? 아빠가 생존 테스트를 돌리게 했어: 언젠가 native 캔버스로 스왑하면, lineage 로직이 따라와, 아니면 컴포넌트랑 죽어? 내 정직한 첫 초안은 걔네랑 죽었을 거야. 난 '쓰기 편한 데' 를 '속하는 데' 랑 헷갈렸어. domain 은 어떤 단일 view 보다 오래 살 수 있는 데 속해 — 지금 내가 애정 있는 것 포함.

Code

생존 테스트, 코드로·typescript
// 엉킴: domain 로직이 컴포넌트 안에 살아. view 스왑 -> 잃음.
function CandidateCard({ id }) {
  const [lineage, setLineage] = useState(null);
  useEffect(() => {
    // workspace binding + lineage 추적이 React 컴포넌트 안에 묻힘
    const l = computeLineage(id, currentWorkspace, photoshopSession);
    setLineage(l);
    bindToSession(id, currentWorkspace);  // view 안의 domain 변형
  }, [id]);
  return <div>{/* 렌더 */}</div>;
}
// 이 컴포넌트 삭제하면 lineage/binding 로직이 같이 죽어.

// 분리: domain core 가 혼자 섬; view 는 그걸 읽고 command 만.
// domain core (React 어디에도 없음):
class Workspace {
  bindCandidate(id) { /* lineage + session binding 이 여기 살아 */ }
  getLineage(id) { /* ... */ }
}
// view (thin): domain 에서 읽고 command, domain 상태 안 소유
function CandidateCard({ id, workspace }) {
  const lineage = workspace.getLineage(id);   // 그냥 읽기
  return <div>{/* 렌더 */}</div>;
}
// 이 컴포넌트 삭제 -> Workspace 랑 그 모든 지식이 안 건드려지고 살아남아.

External links

Exercise

네가 지은 UI 의 컴포넌트를 열어 뭘 하는지 inventory 해. 책임을 '어떻게 보이고/조작되는지'(view)랑 '앱이 뭘 알고/하는지'(domain)로 분리해. domain 로직이 컴포넌트에 얼마나 숨어 있어? 생존 테스트 돌려: 이 컴포넌트 삭제하면, 안 죽어야 할 무슨 지식이 같이 죽어?
Hint
view 에 domain-로직-숨기는 흔한 범인: 컴포넌트 lifecycle 에 묶인 데이터 fetching, event handler 의 비즈니스 룰, 컴포넌트 hook 안에만 존재하는 앱 상태. 각각 view 가 그냥 읽는 domain 레이어로 들어올릴 후보야.

Progress

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

댓글 0

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

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