OrganicGrowth
주니어 개발자들을 위한 패턴 언어 - 기계적 조립이 아닌 유기적 성장으로 소프트웨어를 만드는 법
Contents
The Story: The Frankenstein Monster vs. The Growing Tree
두 명의 개발자, 민수와 하나가 '블로그 시스템'을 구축하고 있다.
민수의 접근 (The Machine Assembly): 민수는 계획적이다. "완벽한 설계가 우선이야."
- 1주 차: DB 스키마를 완벽하게 설계하고 테이블을 생성했다.
- 2주 차: 모든 API 엔드포인트와 DTO를 구현했다.
- 3주 차: 프론트엔드 컴포넌트를 만들었다.
- 4주 차: "이제 조립해볼까?"
민수가 백엔드와 프론트엔드를 연결하려는 순간, 재앙이 시작되었다. 데이터 형식이 맞지 않았고, 생각하지 못한 UX 이슈로 DB 설계를 뜯어고쳐야 했다. 조립된 괴물(Frankenstein)은 삐걱거렸고, 수정하는 데만 2주가 더 걸렸다.
하나의 접근 (The Organic Growth): 하나는 다르게 시작했다. "일단 텍스트 한 줄이라도 화면에 띄우자."
1일 차: DB에 'Hello'를 저장하고, API를 거쳐, 브라우저에 'Hello'를 띄우는 Walking Skeleton을 만들었다.
- 2일 차: 'Hello' 대신 진짜 게시글 제목을 저장하게 바꿨다.
- 3일 차: 목록 기능을 추가했다.
하나는 4주 내내 "작동하는 블로그"를 가지고 있었다. 처음에는 못생기고 기능이 없었지만, 매일 조금씩 자라났다. 문제가 생기면 그날 바로 알았고, 즉시 수정했다. 4주 후, 그녀의 시스템은 이미 튼튼하게 완성되어 있었다.
Context
새로운 프로젝트나 큰 기능을 시작한다. 만들어야 할 구성 요소(DB, API, UI, 로직)가 많다.
일상적인 상황:
- "DB부터 다 짜고 시작하자"라고 생각한다.
- 프론트엔드 개발자와 백엔드 개발자가 각자 작업하다가 마지막 날에 만난다.
- 개별 부품은 완벽하게 테스트되었는데, 합치니까 작동하지 않는다.
- 프로젝트 초반에는 진도가 빠른 것 같았는데, 후반부 통합 과정에서 일정이 무한정 늘어난다.
당신은 소프트웨어를 공산품 조립하듯이 만들고 있다. 하지만 소프트웨어는 생물처럼 키워야 한다.
Problem
각 부분을 따로 완성해서 마지막에 통합하려는 시도는 "통합 지옥(Integration Hell)"을 부른다.
부분의 합은 전체가 아니다(The whole is more than the sum of its parts).
피드백 지연: 아키텍처의 결함은 모든 레이어가 연결될 때 비로소 드러난다. 마지막에 연결하면 결함을 너무 늦게 발견한다.
잘못된 가정: 백엔드는 프론트엔드가 이렇게 데이터를 줄 거라고 가정하고, 프론트엔드는 저렇게 줄 거라고 가정한다. 이 오해는 코드로 만나기 전까지 발견되지 않는다.
성취감 부재: 사용자에게 가치 있는 기능이 완성되기까지 너무 오랜 시간이 걸린다.
Solution
작동하는 가장 작은 전체(Whole)를 먼저 만들고, 그 위에 기능을 덧붙여라.
Christopher Alexander는 말했다. "도시가 나무처럼 성장해야 하듯, 구조물도 씨앗에서 출발하여 점진적으로 분화되어야 한다."
Principle 1: Walking Skeleton (걸어 다니는 스켈레톤)
가장 먼저 할 일은 시스템의 End-to-End를 관통하는 가장 얇은 실(Thread)을 연결하는 것이다.
- DB - Server - Client가 연결되어 작동하는 "Hello World" 수준의 시스템을 1순위로 만들어라.
- 뼈대가 서고 신경망이 연결되면, 살(Feature)을 붙이는 것은 쉽다.
Principle 2: Vertical Slicing (수직으로 자르기)
기능을 레이어별(가로)로 만들지 말고, 기능별(세로)로 만들어라.
Bad: User 테이블 만들기 -> User API 만들기 -> User UI 만들기
Good: "로그인 기능" (User 테이블 + API + UI) 만들기 -> "글쓰기 기능" (Post 테이블 + API + UI) 만들기
- 수직으로 자르면 각 기능을 완료할 때마다 시스템은 "성장"한다.
Principle 3: Refactor as You Grow (자라면서 구조 변경하기)
식물이 자라면 줄기가 굵어져야 한다. 소프트웨어도 기능이 늘어나면 내부 구조(Refactoring)가 바뀌어야 한다.
- 처음부터 거창한 아키텍처(MSA, Hexagonal)를 잡지 마라.
처음엔 Monolith로 시작하고, 필요할 때 분리(Cellular Division)하라.
BabySteps와 CleanIsolation이 이것을 가능하게 한다.
Real Examples
Example 1: E-commerce System
쇼핑몰을 만든다.
Machine Way:
- 상품 DB 설계, 주문 DB 설계, 결제 DB 설계... (1주)
- 상품 등록 API, 조회 API... (1주)
... 고객은 3주가 지나도 상품을 살 수 없다.
Organic Way:
상품 1개를 하드코딩해서 화면에 보여주고 '구매' 버튼 누르면 '주문 완료' 텍스트 띄우기. (2시간) -> 쇼핑몰 탄생!
- 하드코딩된 상품을 DB에서 가져오게 변경. (반나절)
- 주문 내역을 DB에 저장하게 변경. (반나절)
- 결제 모듈 연동. (1일)
시스템은 항상 "물건을 살 수 있는 상태"를 유지하며 점점 똑똑해진다.
Example 2: Tracer Bullet (예광탄)
실용주의 프로그래머(Pragmatic Programmer)에서 말하는 '예광탄' 코드. 어두운 밤에 목표물을 맞히기 위해, 실제로 빛을 내는 탄환을 먼저 쏴서 궤적을 확인한다. Walking Skeleton은 소프트웨어 프로젝트의 예광탄이다.
Common Pitfalls
"But the skeleton is dirty code!"
초기 스켈레톤은 하드코딩도 많고 구조도 엉성할 수 있다. 괜찮다. WorkingFirst! 작동하는 것이 중요하다. 그 다음 BabySteps로 다듬으면 된다. 살아있어야 다듬을 수 있다.
Fear of Rework (재작업에 대한 두려움)
"나중에 DB 구조 바뀌면 고쳐야 하잖아요." 맞다. 하지만 미리 완벽하게 설계했다고 착각하고 나중에 전부 뜯어고치는 것보다는, 조금씩 고치면서 나가는 비용이 훨씬 싸다.
Layered Architecture Obsession
"Controller, Service, Repository, DTO... 이 구조를 다 갖춰야 시작할 수 있어." 아니다. 필요할 때 갖추면 된다. 처음엔 Controller에서 바로 DB를 불러도 된다(아주 작다면). 복잡해지면 Service를 추출하라.
Connection to Other Patterns
StrongCenter - 성장은 무질서한 확장이 아닙니다. 강한 중심이 내뿜는 중력 안에서 시스템이 분화되어야 전체성을 유지할 수 있습니다. 뿌리와 줄기
DirectPath - 자라나는 과정에서 불필요한 우회로(간접성)가 생기지 않도록 경로를 단순하게 유지하십시오. 가지치기
BabySteps - 유기적 성장은 한 번의 도약이 아니라, 수많은 작은 보폭들이 쌓여 만들어지는 연속적인 진화입니다. 세포 분열
LivingVocabulary - 시스템이 자라나며 새로운 형태를 띨 때, 그 형태를 부르는 이름(어휘)도 함께 진화해야 합니다. 이름의 진화
Signs of Success
- 프로젝트 시작 1~2일 만에 데모 가능한 페이지가 나온다.
- "통합 테스트 기간"이 따로 필요 없다. 매일 통합되어 있으니까.
- 기획자가 "이거 벌써 돼요?"라고 놀란다.
- 아키텍처가 프로젝트 초반과 후반에 자연스럽게 다르다(성장했다).
The Ultimate Insight
복잡한 시스템은 처음부터 복잡하게 설계된 것이 아니라, 작동하는 단순한 시스템이 진화한 것이다. (Gall's Law)
처음부터 복잡한 시스템을 만들려고 하면 결코 작동하지 않는다. 작은 씨앗을 심어라. 그리고 매일 물을 주라. 그것이 거대한 숲을 만드는 유일한 방법이다.
CategoryPatternLanguage CategoryProgramming CategoryAgile CategoryArchitecture
