Search

모듈화 멘탈 모델

씨앗

모듈화는 문제 영역을 탐색하며 얻은 이해를 경계와 연결로 코드 구조에 새겨, 코드를 둘러싼 긴장을 해소하여 균형에 이르는 점진적인 반복 과정이다. 모듈화가 잘 된 코드는 각 모듈의 역할이 명확하게 보여 이해가 쉽고, 변경의 비용이 낮다.

치트시트

문제 영역이란: 코드가 해결하려는 고객의 문제/요구사항. "무엇을 해야 하는가". 기획서는 이에 대한 정제된 언어적 표현이고, 디자인은 시각적 표현이다. 개발자는 이 이해를 코드 구조에 표현한다. 개발자 또한 문제 영역에 대한 이해를 얻게 되므로, 마땅히 이를 기획에 피드백 해야 한다.
탐색이란: 기존에 가진 지식(도메인 맥락, 개발 지식)을 동원해 해결책을 찾아가면서, 동시에 문제에 대한 새로운 이해를 얻는 과정. 이렇게 동원되고 획득된 이해가 코드 구조로 표현된다.
구조란: 코드의 형태에 개발자의 이해가 새겨져, 그 의도가 읽히고 실용적 효과를 내는 것. 경계(What의 본질을 따라 결대로 찢기 — 구분의 방식)와 연결(인터페이스를 통한 협력의 방식)로 구현된다. 좋은 구조는 물이 위에서 아래로 흐르듯 인지가 자연스럽게 흐르고, 나쁜 구조는 흐름이 막혀 고여서 냄새가 난다.
긴장이란: 힘의 존재로 인해 발생하는 불편감. 코드를 보면서 "뭔가 불편하다"고 느끼게 하는 것. 힘은 이해 부족("이게 뭘 하는 건지 모르겠다"), 낮은 응집("여기저기 뛰어다녀야 한다"), 높은 결합("건드리면 줄줄이 딸려온다")으로 분류할 수 있다. 힘이 만든 긴장의 결과, 고려할 것이 많아지면 인지부하가 발생한다.
균형이란: 고객의 요구사항이 충족되고 힘의 긴장이 해소되어 더 이상 움직일 이유가 없는 상태. 개발자의 이해가 문제에 비례한 적정량의 장치로 선명하게 표현되어 "손댈 데가 없다"는 아름다움이 느껴진다.
점진적이란: 한 번에 완벽한 구조를 만들지 않는다. 확정한 것은 기반으로 삼고, 불확실한 것은 옵션으로 남기며, 매 순간 조금씩 나아간다. 그 과정에서도 코드는 계속 돌아간다.

대표 예시

// ❌ Before: 인터페이스가 되는대로 작성되어 구조에 표현된 이해가 없음 <PGSection type="toss" cardNumber={cardNumber} expiry={expiry} cvc={cvc} tossToken={token} tossUserId={userId} handleOrder={handleOrder} /> // → "PGSection"이 뭘 하는 건지? (역할 불명) // → 카드 정보와 토스 정보가 왜 같이? (경계 없음) // → handleOrder가 언제 불리지? (협력 방식 불명) // ✓ After: 인터페이스 구조에 작성자가 이해한 개념이 새겨짐 <PaymentForm method={tossPay} onSubmit={withAnalytics(saveOrder, { event: 'purchase' })} /> // 새겨진 이해: // → "결제를 받는 폼이다" — 이름이 역할을 말함 // → "결제수단은 바뀔 수 있다" — method로 추상화 // → "제출하면 저장하고 추적한다" — onSubmit + withAnalytics // → "카드/토스 내부 구조는 몰라도 된다" — method 안에 캡슐화
TypeScript
복사

핵심 공식

모듈화 = (추상화 + 인터페이스 설계) x 점진적 개선
추상화란: What을 발견하고 확정하는 행위. "이게 뭐야?"라는 질문에 답하며 본질을 드러내고 이름을 붙인다. 본질을 따라 경계를 긋는 기반이 된다. 추상화는 언어 능력과 연관된 인간의 기본적인 인지 능력이다. 일상에서 상대방의 말을 듣고 핵심을 파악해 재진술하는 것처럼, 코드에서도 같은 능력을 전이하여 사용한다.
인터페이스 설계란: 요구사항에 대한 이해를 명명과 의도적인 제약을 통해 구조에 담아내는 일. 이를 통해 모듈과 모듈이 서로 목적에 맞는 관절을 통해 맞물리고 연결되어 협력할 수 있게 된다.
점진적 개선이란: 한 번에 완벽한 구조를 만들지 않는다. 확정한 것은 기반으로 삼고, 불확실한 것은 옵션으로 남기며, 매 순간 조금씩 나아간다. 그 과정에서도 코드는 계속 돌아간다.

핵심 질문 3개

단계
질문
감지
"이 코드가 뭘 하는지 고객 언어로 설명할 수 있나?"
진단
"어떤 긴장이지? 이해 부족? 낮은 응집? 높은 결합?"
점검
"요구사항이 충족되었나? 긴장이 해소되었나? 아름다운가?"

메커니즘 (전체 흐름)

문제 영역 (기획서/디자인) ↓ 탐색 시작 — 기존 이해(도메인 맥락, 개발 지식) 동원 ↓ 감지 — 코드에서 긴장을 느낀다 ("뭔가 불편하다") ↓ 진단 — 어떤 힘인지 파악한다 (이해 부족? 낮은 응집? 높은 결합?) ↓ 구조화 — 이해를 경계와 연결로 새긴다 ↓ 해소 — 긴장이 사라진다 (균형) ↓ 점진적 반복 — 새로운 이해가 쌓인다 ↑ └── 개발자의 이해가 기획에 피드백
Plain Text
복사
모듈화는 한 번에 완성되지 않는다. 긴장을 느낄 때마다 돌아가서 이해를 보강하고 구조를 고친다.
긴장을 느낀다 (불편하다) ↓ "어떤 힘이지?" — 이해 부족? 낮은 응집? 높은 결합? ↓ "구조가 왜 이 모양이지?" — 경계와 연결 검토 ↓ "내가 지금 뭘 모르고 있지?" — 근본 원인 ↓ 탐색 / 재구조화 ↓ 긴장이 해소된다
Plain Text
복사

핵심 개념 상세

문제 영역이란

코드가 해결하려는 고객의 문제/요구사항. 바꿔 말하면, 코드를 통해 "무엇을 해야 하는가".
기획서는 이에 대한 정제된 언어적 표현이고, 디자인은 시각적 표현이다. 개발자는 이 이해를 코드 구조에 표현한다. 개발자 또한 문제 영역에 대한 이해를 얻게 되므로, 마땅히 이를 기획에 피드백 해야 한다.

탐색이란

기존에 가진 지식(도메인 맥락, 개발 지식)을 동원해 해결책을 찾아가면서, 동시에 문제에 대한 새로운 이해를 얻는 과정.
탐색에서 동원되는 기존 지식:
도메인 맥락 — 이 문제 영역에 대해 이미 알고 있는 것
개발 지식 — 일반해, 패턴, 관용적 구조
탐색에서 획득되는 새로운 이해:
코드를 작성하며 문제 영역에 대해 새롭게 발견한 것
시도하고 실패하며 배운 것
이렇게 동원되고 획득된 이해가 코드 구조로 표현된다.
// 탐색 전: 구멍 투성이 for (let i = 0; i < cart.items.length; i++) { if (cart.items[i].category === 'electronics') { total += cart.items[i].price * 0.9 } } // 탐색하며 이해를 획득한다: // "category === 'electronics'가 뭐야?" → "전자제품인지 확인하는 것" const isElectronics = (item) => item.category === 'electronics' // "price * 0.9가 뭐야?" → "10% 할인을 적용하는 것" const applyDiscount = (item, { rate }) => item.price * (1 - rate) // 탐색 후: 이해가 코드에 이름으로 새겨짐 cart.items .filter(isElectronics) .reduce((sum, item) => sum + applyDiscount(item, { rate: 0.1 }), 0)
TypeScript
복사

경계란

What이라는 본질을 따라 결대로 찢는 것. "이건 여기까지."
경계는 자의적으로 긋는 게 아니라, 문제 영역의 본질적 구분을 따른다. 기획서가 '할인'이라 부르면, 코드에서도 '할인'이 경계가 된다. 본질을 따라 찢으면 변경이 올 때 어디를 고쳐야 하는지 예측 가능하다.
// ❌ Props를 통로로 씀: 구체들을 그대로 전달 <결제창 신한카드={{ 카드번호, 유효기간, CVC }} 토스페이={{ 토큰, 사용자키 }} 국민계좌={{ 계좌번호, 예금주 }} /> // → 결제창이 모든 결제수단의 내부 구조를 알아야 함 // → 네이버페이 추가되면? 또 구조 파악해서 추가해야 함 // ✓ Props를 인터페이스로 씀: 본질을 발견하여 경계로 삼음 <결제창 결제수단={토스페이} /> <결제창 결제수단={네이버페이} /> // → "결제수단"이라는 본질이 구체들로부터 발견됨 // → 결제창은 결제수단의 내부를 몰라도 됨 (경계가 명확) // → 새 결제수단 추가해도 결제창은 변경 없음
TypeScript
복사

연결이란

나눈 단위들이 인터페이스를 통해 협력하는 방식.
경계로 나눈 것들은 서로 협력해야 한다. 연결의 형태에 설계자의 의도가 새겨진다. 어떤 협력을 허용하고, 어떤 협력을 제한할지가 인터페이스 구조로 표현된다. 연결의 형태를 떠올릴 때 관절이라는 심상이 도움이 된다 — 문짝 관절은 한 방향만, 어깨 관절은 자유롭게, 무릎 관절은 단계별로 움직이듯, 인터페이스도 허용하는 협력의 자유도가 다르다.
onClose: withFoo(domainFn) // ↑ ↑ ↑ // 구조 층 실제 로직 // 연결의 구조: // 1. onClose — 함수가 들어오도록 허용하는 관절 // 2. withFoo — 층 (관심사를 끼워넣음) // 3. domainFn — 실제 로직 (도메인의 일) // 각 층위가 자기 책임만 지고, 관절을 통해 협력한다
TypeScript
복사

구조란

코드의 형태에 엔지니어의 이해가 새겨져, 그 의도가 읽히고 실용적 효과를 내는 것.
좋은 구조는 의도의 선명함이 느껴진다. 작성자가 문제를 깊이 이해한 다음, 그 이해를 코드에 표현해낸 것. 읽는 사람이 "아, 이 사람은 이 문제를 이렇게 보고 있구나"라는 게 코드만으로 전달된다. 물이 위에서 아래로 흐르듯 인지가 자연스럽게 흐른다:
이름만 보고 뭔지 안다
경계만 보고 어디까지인지 안다
연결만 보고 어떻게 협력하는지 안다
나쁜 구조는 흐름이 막히고 고여서 냄새가 난다:
이름을 봐도 모르겠다
하나를 이해하려면 전부 봐야 한다
연결이 왜 이렇게 되어 있는지 모르겠다

긴장이란

힘의 존재로 인해 발생하는 불편감. 코드를 보면서 "뭔가 불편하다"고 느끼게 하는 것. 힘은 이해 부족("이게 뭘 하는 건지 모르겠다"), 낮은 응집("여기저기 뛰어다녀야 한다"), 높은 결합("건드리면 줄줄이 딸려온다")으로 분류할 수 있다. 힘이 만든 긴장의 결과, 고려할 것이 많아지면 인지부하가 발생한다.
느낌
처방
이해 부족
"이게 뭘 하는 건지 모르겠다"
이해하고 명명
낮은 응집
"여기저기 뛰어다녀야 한다"
모으기
높은 결합
"건드리면 줄줄이 딸려온다"
나누기, 의존 방향 정리
초보자의 작업기억 (긴장이 높은 상태): [변수명][함수길이][중복][책임분리][에러처리][포맷][남 눈치] (7/7 슬롯) → 꽉 참, 고차원 사고 불가 전문가의 작업기억 (긴장이 낮은 상태): [코드스멜][남 눈치][ ][ ][ ][ ][ ] (2/7 슬롯) → 여유 있음, "이 설계가 비즈니스에 맞나?" 생각 가능
Plain Text
복사
// 긴장이 느껴지는 코드 function processData(data, flag, ctx, opts) { // 이해 부족: "이게 뭘 하는 건지 모르겠다" const x = data.items.filter(i => i.t === 'A' && ctx.u.role === 'admin'); // 낮은 응집: "여기저기 뛰어다녀야 한다" const config = getConfig(); // config.js const validated = validate(x); // validators/index.js const transformed = transform(validated, config.rules); // utils/transform.js const result = apply(transformed); // 다시 여기 // 높은 결합: "건드리면 줄줄이 딸려온다" globalState.lastProcessed = result; eventBus.emit('processed', { result, ctx, opts, flag }); cache.invalidate(['data', 'reports', 'dashboard']); return result; }
TypeScript
복사
전문가는 반복된 탐색으로 많은 것을 자동화했기에 긴장이 낮다. 좋은 구조는 읽는 사람의 작업기억 슬롯을 적게 점유하여 긴장을 낮춘다.

균형이란

고객의 요구사항이 충족되고 힘의 긴장이 해소되어 더 이상 움직일 이유가 없는 상태. 개발자의 이해가 문제에 비례한 적정량의 장치로 선명하게 표현되어 "손댈 데가 없다"는 아름다움이 느껴진다.

점진적이란

한 번에 완벽한 구조를 만들지 않는다. 확정한 것은 기반으로 삼고, 불확실한 것은 옵션으로 남기며, 매 순간 조금씩 나아간다. 그 과정에서도 코드는 계속 돌아간다.
불확실성을 다루는 두 가지 원칙:
확정한 것은 기반으로 삼는다 — 이미 아는 것을 구조에 새겨 인지적 발판으로 쓴다
불확실한 것은 옵션으로 남긴다 — 미래 변경 권리를 확보하는 방식으로 열어둔다
// Step 1: 확정한 것만 — "결제를 받는다" <PaymentForm onSubmit={handlePayment} /> // Step 2: 이해가 쌓임 — "결제수단이 여러 개다" <PaymentForm method={cardPay} onSubmit={handlePayment} /> // Step 3: 이해가 더 쌓임 — "제출 전에 검증이 필요하다" <PaymentForm method={cardPay} onSubmit={handlePayment} validate={validateCard} /> // Step 4: 이해가 더 쌓임 — "분석 추적이 필요하다" <PaymentForm method={cardPay} onSubmit={withAnalytics(handlePayment, { event: 'purchase' })} validate={validateCard} /> // 매 단계마다 코드는 돌아간다. // 긴장이 느껴질 때만 구조를 고친다.
TypeScript
복사

이해/지식의 힌트

탐색에서 동원하는 기존 이해/지식은 어디서 오는가? 세 가지 원천이 있다.

도메인 언어

기획서와 디자인이 문제 영역을 부르는 말. 경계의 위치를 알려준다.
"기획서가 '할인'이라 부르면, 코드에서도 '할인'이 경계가 된다."
자의적 언어 대신 도메인 언어를 따르면:
변경이 올 때 어디를 고쳐야 하는지 예측 가능
기획자와 개발자가 같은 말로 대화 가능
요구사항 문서와 코드가 1:1로 대응

일반해

검증된 구조적 패턴. 문제 유형에 대한 축적된 이해. 구조의 뼈대를 제공한다.
예시: Form, List, Dialog, Query, Mutation, Input, Modal...
일반해가 제공하는 것:
뼈대 — "Form이면 input, 모으기, submit이 있다"
연결의 형태 — "value/onChange가 표준이다"
기대되는 협력 — "onSubmit은 제출 시점에 호출된다"
일반해는 발견의 도구지 적용의 법칙이 아니다. "이거 Form 같은데?" → 확인 → 아니면 버림.

협력의 형태

모듈들이 협력하게 만드는 설계 기법. 연결의 방식을 제공한다. (상세는 Appendix 참조)

프랙탈

모듈화의 원리는 특정 층위에 한정되지 않는다. 경계와 연결은 모든 층위에 스며든다. 함수, 컴포넌트, 모듈, 시스템 — 어디서든 같은 질문을 던진다: "이건 뭐야? 어디까지야? 어떻게 협력해?"
층위
역할 (이름)
책임 (경계)
협력 (연결)
함수
함수명
파라미터/반환
호출 규약
컴포넌트
컴포넌트명
Props 범위
children, 콜백
모듈
파일/폴더명
export 범위
import 관계
시스템
서비스명
API 범위
프로토콜

Appendix

A. 구조의 세 축

씨앗에서의 도출: "이해가 새겨진다"는 말에서 — 어떤 이해가 새겨지는가?
질문
이해의 유형
역할
"이건 뭐야?"
정체에 대한 이해
책임
"뭘 해야 해?"
범위에 대한 이해 (경계)
협력
"어떻게 연결돼?"
관계에 대한 이해 (연결)

B. 구조화의 조작

씨앗에서의 도출: "경계와 연결로 새긴다"는 말에서 — 어떻게 새기는가?
이해의 유형
조작
결과물
역할
명명 — 이름을 붙인다
이름
책임
경계 긋기 — 본질을 따라 찢는다
모듈
협력
협력 설계 — 연결 방식을 정한다
인터페이스

C. 힘과 처방

씨앗에서의 도출: "긴장을 해소한다"는 말에서 — 어떤 힘인지 어떻게 아는가?
느낌
처방
이해 부족
"이게 뭔지 모르겠다"
이해하고 명명
이해 부족
"어디까지인지 모르겠다"
경계 긋기
이해 부족
"어떻게 연결되는지 모르겠다"
협력 설계
낮은 응집
"여기저기 뛰어다녀야 한다"
모으기
높은 결합
"건드리면 줄줄이 딸려온다"
의존 방향 정리
높은 결합
"단단히 묶여서 못 건드리겠다"
나누기

D. 협력의 형태 (관절 심상)

연결의 형태를 떠올릴 때 인체의 관절을 심상으로 쓴다. 관절의 형태 자체에 설계자의 의도가 새겨져 있다. 어떤 협력을 허용하고, 어떤 협력을 제한할지가 인터페이스 구조로 표현된다.
관절 유형
움직임 범위
인터페이스 대응
예시
문짝 관절
한 방향
정해진 방향으로만 확장
onClose: () => void
어깨 관절
자유
열린 확장
children: ReactNode
무릎 관절
단계별 걸림
이산적 선택지
variant: 'primary' | 'secondary'
두개골
고정
확정, 열림 없음
직접 구현

E. 협력 설계 기법

기법
하는 일
예시
위임 (IoC)
결정권을 밖으로 민다
onSubmit={...}
슬롯 (Slot)
내용물을 밖에서 채운다
children
설정 (Config)
선택지를 열어둔다
variant="primary"
합성 (Composition)
작은 것들을 조립한다
<Form><Input/></Form>
어댑터 (Adapter)
시그니처를 맞춘다
형태 변환

F. 이해의 상태에 따른 표현

이해의 상태
의미
구조적 표현
예시
확정
안다
직접 구현
return count + 1
방향을 앎
어떻게 변할지 관례가 있다
관용적 연결
onClose, onChange
불확실성을 앎
변할 수 있지만 방향은 모른다
기획서/UI를 따르는 경계
UI 구조와 1:1
무지
모른다는 것도 모른다
반영 안 됨
나중에 힘으로 돌아옴

G. 추상화와의 관계

추상화에서 가져오는 것

추상화 개념
모듈화에서의 역할
본질을 드러내고 이름 붙이기
명명 — 역할을 드러냄
What/How 분리
경계 긋기의 기반
추상화 벽
경계의 원형
결합부위
연결의 원형

모듈화에서 추가되는 것

추가 요소
설명
경계 긋기
추상화는 What/How를 분리하지만, 모듈화는 "어디까지가 이 모듈인가"를 결정
협력 설계
추상화는 결합부위를 "잘 만든다"고 하지만, 모듈화는 연결 유형을 선택
긴장의 감지
추상화보다 더 넓은 "힘"(응집/결합 문제 포함)을 감지
점진성
추상화는 정적 관점이지만, 모듈화는 시간 축을 포함
.