주식 분석 앱을 만들다 보니, 분석 결과를 그리는 컴포넌트 하나가 1270줄까지 불어났습니다. 지금은 잘 돌아갑니다. 근데 바로 그게 함정이에요. 나중에 디자인 한 번 바꾸려다 여기저기 얽힌 코드가 우수수 터지는 그림이 보였거든요.
이런 파일은 어느 날 갑자기 생기지 않습니다. 카드 하나 추가할 때마다 “이번 한 번만 여기에 붙이자”가 쌓인 결과예요. 매번의 결정은 다 합리적이었는데 결과물만 감당이 안 되는, 전형적인 눈덩이입니다.
수업 짬짬이 하는 작업이라 시간이 한정적입니다. “나중에 한 시간 날리느니 지금 30분 투자”가 남는 장사죠. 그래서 이 God 컴포넌트를 카드 단위로 쪼갰습니다.
God 컴포넌트: 너무 많은 일을 혼자 떠안아 거대해진 컴포넌트(파일). 손대기 무섭고, 한 곳 고치면 엉뚱한 데가 깨지기 쉬움.
왜 지금 쪼개나 (내가 남긴 메모 그대로)
당시 AI에게 이렇게 요청했습니다. 가공 없이 옮깁니다.
“다양한 데이터들을 보여주는 카드들이 복잡하게 얽혀 있다면 나중에 프론트엔드 디자인이나 UX를 변경할 때 또 다른 문제가 생길 것 같아. (…) 가능한 한 큰 덩어리로 모듈화시켜서 유지보수가 유리하도록 코드 구조 개선이 필요한 것 같아. (…) 모듈의 덩어리는 크면 클수록 좋을 것 같아.”
핵심 의도는 셋이었습니다.
- 나중에 새 분석 관점(persona) 을 붙일 때, 맞는 카드만 골라 끼우기
- 디자인/UX 변경 시 카드를 자유롭게 재배치 (지금처럼 안 꼬이게)
- 나중에 단타 매매 섹션 추가 시 데이터를 쉽게 가져다 쓰기
모듈화: 한 덩어리 코드를 “독립적으로 갈아끼울 수 있는 부품(모듈)”들로 나누는 것. 레고 블록처럼.
“크면 클수록 좋다”고 한 이유
메모에서 제가 유독 강조한 게 “모듈의 덩어리는 크면 클수록 좋을 것 같아” 입니다. 모듈화라고 하면 보통 “잘게 쪼개기”를 떠올리니 거꾸로 들리죠. 그런데 여기엔 이유가 있었습니다.
쪼개기 자체는 공짜가 아니거든요. 1270줄을 20줄짜리 파일 60개로 만들면 파일 하나하나는 깔끔해집니다. 대신 카드 하나 고치려고 파일 다섯 개를 열고 닫으며 “이게 어디서 오는 거였지”를 추적하게 돼요. 큰 파일 하나를 훑는 괴로움이 작은 파일 사이를 헤매는 괴로움으로 바뀔 뿐, 총량은 그대로거나 오히려 늘어납니다.
그래서 기준은 줄 수가 아니라 “이게 혼자 말이 되는 단위인가” 였습니다. 카드 하나는 “이 데이터를 이렇게 보여준다”는 이야기가 혼자 완결돼요. 반면 카드를 제목 부분, 숫자 부분, 그래프 부분으로 또 쪼개면 각각은 혼자서 아무 의미가 없습니다. 혼자 설 수 있는 가장 큰 덩어리 — 그게 제가 말한 “크면 클수록”이었습니다.
어떻게 안전하게 쪼갰나
한 번에 다 뜯으면 십중팔구 뭔가 깨집니다. 그래서 작은 단계로 나눠(R1a → R1b → R2 → R3), 매 단계마다 타입 검사(tsc)와 회귀 검사를 돌렸습니다.
tsc: 타입스크립트 타입 검사기. 코드를 실행하기 전에 “이 자리에 숫자가 와야 하는데 글자가 왔다” 같은 어긋남을 잡아줍니다. 쪼개다 연결을 놓치면 여기서 먼저 걸립니다.
회귀(regression): 멀쩡하던 기능이 다른 수정 때문에 도로 고장 나는 것. “회귀 검사”는 그게 안 생겼는지 확인하는 것.
AnalysisResults.tsx (1270줄, 모든 카드가 한 파일에)
│ R1a → R1b → R2 → R3 (단계별 추출)
▼
components/analysis/
├─ cards/ ValuationCard · GovernanceCard · TechnicalIndicatorsCard ...
├─ groups/ ValueCardGroup · TechnicalCardGroup · QuantCardGroup
lib/analysis/
└─ selectors.ts (어떤 데이터를 어느 카드에 줄지 고르는 함수)
- cards/: 카드 하나 = 파일 하나. 이제 한 카드만 열어서 고치면 됨.
- groups/: 비슷한 카드끼리 묶음(가치·기술·퀀트). 페이지에선 그룹만 갖다 붙임.
- selectors.ts: 데이터에서 필요한 부분만 골라주는 셀렉터(selector).
셀렉터(selector): 큰 데이터 덩어리에서 “이 카드엔 이 값만” 하고 골라 꺼내주는 함수. 카드는 데이터를 어디서 가져오는지 몰라도 됨 → 갈아끼우기 쉬움.
이 셋 중 셀렉터가 가장 중요했습니다. 카드가 데이터를 직접 파고들면 데이터 모양이 조금만 바뀌어도 카드가 다 같이 깨집니다. 애초에 카드들이 “복잡하게 얽혀” 있던 게 이것 때문이었어요. 중간에 셀렉터를 두니 데이터가 바뀌어도 셀렉터만 고치면 되고, 카드는 자기가 받은 값을 그리는 일만 하면 됩니다. 나중에 단타 매매 섹션에서 같은 데이터를 가져다 쓰기 쉬워진 것도 이 덕분이고요.
단계를 쪼개 검사하니, 한 단계에서 문제가 나도 그 단계만 되돌리면 돼서 밤늦게 디버깅하다 멘붕 오는 일이 없었습니다. 어디서 깨졌는지 범위가 항상 한 단계 안이었으니까요.
그래서 뭘 하면 되나
당장 거대 파일이 있다면, 이 순서가 안전합니다.
| 단계 | 할 일 | 왜 |
|---|---|---|
| 1 | 가장 독립적인 조각 1개부터 분리 | 의존 적어 깨질 위험↓ |
| 2 | 분리할 때마다 tsc(타입검사) + 동작 확인 | 회귀 즉시 발견 |
| 3 | 비슷한 것끼리 그룹으로 묶기 | 페이지는 그룹만 알면 됨 |
| 4 | 데이터 고르는 로직은 셀렉터로 분리 | 카드와 데이터 분리 |
표에서 놓치기 쉬운 건 1번의 “1개부터” 입니다. 거대 파일을 열면 눈에 보이는 게 다 거슬려서 한꺼번에 정리하고 싶어지는데, 그러면 뭐 때문에 깨졌는지 알 수 없는 상태가 됩니다. 가장 독립적인 조각을 고르라는 것도 같은 맥락이에요. 남들과 덜 얽힌 것부터 떼어내야 첫 단계가 성공하고, 그 성공이 다음 단계의 발판이 됩니다.
한 문장: “잘 돌아갈 때”가 리팩터 적기다. 한 번에 다 뜯지 말고, 독립 조각부터 → 매번 검사 → 그룹화 순으로.
리팩터(refactor): 겉보기 동작은 그대로 두고, 속 구조만 깔끔하게 고치는 것.
수학 문제도 풀이 과정을 한 줄씩 검산하는 사람이 실수를 덜 하듯, 코드도 한 단계씩 확인하며 쪼개니 실수가 확 줄었습니다. 고장 난 다음에 고치는 것보다, 안 고장 났을 때 손보는 게 훨씬 쌉니다. 지금은 잘 돌아가니까 나중에 하자는 게 제일 비싼 선택이었어요. 오늘 카드 하나만 떼어내도, 미래의 내가 야근을 면합니다.