TwoWorlds
주니어 개발자들을 위한 패턴 언어 - 문제 공간과 해결 공간을 분리하여 올바른 문제를 해결하는 방법
Contents
The Story: The Wrong Solution
한 주니어 개발자가 2주 동안 열심히 코딩했다. 사용자가 주문 내역을 필터링할 수 있는 복잡한 검색 기능을 만들었다. 날짜 범위, 상품 카테고리, 가격 범위, 배송 상태... 10개가 넘는 필터 옵션을 구현했다.
데모 날, 프로덕트 매니저가 말했다. "좋아 보이는데... 그런데 사용자들이 실제로 원했던 건 '지난 주문 다시 하기' 버튼이었어요."
주니어는 당황했다. "하지만 요구사항에 '주문 내역을 쉽게 찾을 수 있어야 한다'고 적혀있었어요!"
시니어 개발자가 끼어들었다. "맞아. 하지만 왜 찾고 싶어하는지 물어봤어?"
"아니요... 바로 구현을 시작했어요. 검색 기능이 필요하다고 생각했거든요."
시니어가 고개를 끄덕였다. "이게 우리가 자주 하는 실수야. 문제를 이해하기 전에 해결책부터 생각하는 거지. 두 세계는 다른 거야."
Context
당신은 새로운 기능을 개발하고 있다. 요구사항을 받았고, 머릿속에 이미 구현 방법이 떠오른다. 코드를 어떻게 짜야 할지, 어떤 라이브러리를 쓸지, 어떤 알고리즘을 적용할지...
일상적인 상황:
- 요구사항 문서를 받자마자 코딩을 시작한다
- "이건 간단해, 그냥 X를 추가하면 돼"라고 생각한다
- 구현하다가 요구사항이 애매해서 추측으로 진행한다
- 완성 후 "이게 아니었는데..."라는 피드백을 받는다
당신은 해결책에 대해 생각하고 있지만, 문제를 충분히 이해하지 못했다.
Problem
많은 프로그래밍 오류는 사고 과정에서 두 관심사를 너무 일찍 섞는 데서 비롯된다:
The Premature Solution
문제를 듣자마자 해결책이 떠오른다. 이것은 경험의 표시처럼 느껴진다. "나는 이걸 어떻게 만들지 알아!" 하지만 이것은 함정이다.
요구사항: "사용자가 주문 내역을 쉽게 찾을 수 있어야 한다" 즉각적 사고: → 검색 기능이 필요하다 → 필터링이 필요하다 → 정렬이 필요하다 → 페이징이 필요하다 → (2주 후) 잘못된 기능 완성
Gerald Weinberg는 "Are Your Lights On?"에서 말한다: 문제가 무엇인지 정의하지 않고는, 문제를 해결할 수 없다. 하지만 우리는 정의하기 전에 해결하려 한다.
The Cost of Wrong Problems
잘못된 문제를 해결하는 것은 자원의 엄청난 낭비다:
- 2주간의 개발 시간
- 복잡한 코드 작성과 테스트
- 리뷰와 QA 시간
- 그리고 결국... 다시 만들어야 함
더 나쁜 것은, 이런 일이 자주 일어난다는 것이다. 왜? 우리가 두 세계를 섞기 때문이다.
Two Worlds Confused
문제 공간 (Problem Space): 무엇을 해결할 것인가?
- 사용자가 진짜 원하는 것은 무엇인가?
- 왜 이것이 필요한가?
- 진짜 목표는 무엇인가?
- 어떤 상황에서 이것을 사용하는가?
해결 공간 (Solution Space): 어떻게 구현할 것인가?
- 어떤 데이터 구조를 쓸 것인가?
- 어떤 알고리즘을 적용할 것인가?
- 어떤 라이브러리를 사용할 것인가?
- 성능은 어떻게 최적화할 것인가?
이 두 세계는 다른 사고 방식을 요구한다. 하지만 우리는 이것들을 섞는다:
"사용자가 주문을 찾고 싶어한다" (문제) → "검색 기능을 만들자" (해결책) → "Elasticsearch를 쓰자" (구현) → "어떻게 인덱싱하지?" (세부사항) 문제 공간을 떠나기가 너무 빨랐다!
Solution
문제 공간을 충분히 탐색하라. 그다음에 해결 공간으로 이동하라. 두 세계를 의식적으로 분리하면, 더 나은 해결책을 찾고, 종종 더 간단한 해결책을 찾는다.
Principle 1: Stay in Problem Space Longer
해결책을 생각하고 싶은 충동을 억제하라. 문제 공간에 머물면서 깊이 탐색하라.
질문하라 - George Polya의 접근:
- 문제를 다른 말로 표현할 수 있는가?
- 핵심 요소는 무엇인가?
- 이미 알고 있는 것은 무엇인가?
- 모르는 것은 무엇인가?
- 제약사항은 무엇인가?
예시:
요구사항: "주문 내역을 쉽게 찾을 수 있어야 한다" 문제 공간 탐색: Q: 사용자가 왜 주문 내역을 찾는가? A: 같은 것을 다시 주문하고 싶어서 Q: 모든 주문을 검색해야 하는가? A: 아니, 주로 최근 3개월 내 주문만 Q: 어떤 정보로 찾는가? A: 대부분 "지난번에 뭘 샀더라?" 진짜 문제: 반복 주문을 쉽게 하고 싶다 → 복잡한 검색 기능이 아니라 "다시 주문하기" 버튼!
깊은 탐색은 눈을 뜬다. 원래 생각했던 것보다 훨씬 간단한 해결책으로 이어질 수 있다.
Principle 2: Define Before Solving
Weinberg의 교훈: 문제 정의가 해결의 절반이다.
명확한 문제 정의:
- 무엇이 현재 상태인가?
- 무엇이 원하는 상태인가?
- 그 사이의 간격은 무엇인가?
- 왜 그 간격이 중요한가?
// 애매한 정의 "검색 기능이 필요하다" // 명확한 정의 현재: 사용자가 자주 주문하는 상품을 다시 찾기 어려움 원하는: 이전 주문을 2클릭 이내에 재주문 가능 간격: 주문 이력 탐색과 재주문의 마찰 중요성: 반복 구매가 매출의 40%
명확한 정의는 해결책을 자연스럽게 제안한다.
Principle 3: Separate Thinking Modes
의식적으로 두 모드 사이를 전환하라:
문제 모드 (Why & What):
- "왜 이것이 문제인가?"
- "진짜 목표는 무엇인가?"
- "다른 방식으로 표현하면?"
해결책 제안을 의도적으로 지연
해결 모드 (How):
- "어떻게 구현할 것인가?"
- "어떤 기술을 사용할 것인가?"
- "어떻게 최적화할 것인가?"
문제 정의는 이미 확정되었음
중요: 한 번에 하나의 모드만 사용하라. 섞지 마라.
// ❌ BAD: 두 모드가 섞임 "사용자가 주문을 찾고 싶어한다 (문제) → 검색을 구현하려면 (해결) → 근데 왜 찾지? (문제) → Elasticsearch가 좋을까? (해결)" // ✅ GOOD: 분리된 사고 [문제 모드] "왜 주문을 찾는가? → 재주문 어떤 주문? → 최근 주문 진짜 필요: 빠른 재주문" [전환] [해결 모드] "최근 주문 리스트 + 재주문 버튼 API: GET /recent-orders UI: 간단한 목록 + 버튼"
Principle 4: Maximize Work Not Done
Agile 원칙: 단순함은 본질적이다 - 하지 않아도 되는 일을 최대화하라.
문제 공간을 깊이 탐색하면, 종종 발견한다:
- 구현할 필요가 없는 기능
- 훨씬 간단한 해결책
- 이미 존재하는 해결책
원래 계획: - 복잡한 검색 UI (3일) - 필터링 로직 (2일) - 정렬 기능 (1일) - 페이징 (1일) - 검색 최적화 (2일) 총: 9일 문제 탐색 후: - "다시 주문하기" 버튼 - 최근 5개 주문 목록 총: 0.5일 '''8.5일의 일을 하지 않았다!'''
이것이 진정한 효율이다: 올바른 문제를 해결하는 것.
Practical Patterns
Pattern 1: The Five Whys
Toyota의 기법: 문제의 근본 원인을 찾기 위해 "왜?"를 5번 물어라.
문제: 배포가 자주 실패한다 왜? → 테스트가 깨진다 왜? → 환경 설정이 다르다 왜? → 개발 환경과 운영 환경의 설정이 다르다 왜? → 설정을 수동으로 관리한다 왜? → 설정 관리 도구가 없다 진짜 문제: 설정 관리 해결책: 복잡한 배포 파이프라인이 아니라, 설정 관리 도구!
Pattern 2: Problem Statement Template
명확한 문제 정의를 위한 템플릿:
[누가]의 [어떤 상황]에서 [현재 문제]가 발생한다. 이것은 [어떤 영향]을 미친다. [원하는 결과]를 달성하고 싶다.
예시:
자주 구매하는 고객의 재주문 상황에서 이전 주문을 찾기 위해 주문 이력을 스크롤해야 하는 문제가 발생한다. 이것은 재주문 포기와 불만을 미친다. 2클릭 이내에 재주문을 완료하고 싶다.
이제 해결책이 명확하다: 빠른 재주문 버튼!
Pattern 3: Solution Space Exploration
문제를 이해한 후, 해결 공간에서 여러 옵션을 탐색하라:
문제 정의: 빠른 재주문 (확정) 해결 옵션들: 1. 최근 주문 + 재주문 버튼 (간단, 빠름) 2. 자주 구매한 상품 추천 (복잡, 머신러닝) 3. 원클릭 재주문 (간단, 하지만 실수 위험) 4. 장바구니에 담기 (기존 플로우 활용) 각 옵션의 트레이드오프를 고려하여 선택
문제가 명확하면, 올바른 해결책을 비교할 수 있다.
Common Pitfalls
"But I Know the Solution!"
경험이 많아질수록, 패턴을 빨리 인식한다. 이것은 좋지만, 함정이기도 하다.
당신이 해결책을 "안다"고 생각할 때, 정말 문제를 이해했는가?
5분만 투자해서 문제를 확인하라. 가끔은 당신이 생각한 것과 다른 문제다.
"We Need to Move Fast!"
"빨리 만들어야 해!" - 그래서 문제 탐색을 건너뛴다.
하지만 잘못된 것을 빨리 만드는 것은 더 느리다:
- 2주 개발 + 2주 재개발 = 4주
- vs. 1일 문제 탐색 + 1주 개발 = 8일
올바른 방향으로 천천히 가는 것이 잘못된 방향으로 빨리 가는 것보다 빠르다.
"The Requirements Are Clear!"
요구사항 문서가 명확해 보인다. 하지만:
명확하게 쓰여진 것과
진짜 이해한 것은 다르다
문서가 있어도, 대화하라. 질문하라. 확인하라.
Connection to Other Patterns
WorkingFirst - 문제를 이해한 후, 가장 간단한 작동하는 해결책부터 시작하라. 완벽한 해결책을 설계하려 하지 마라. 순차 관계
StrongCenter - 문제의 핵심을 파악하는 것은 시스템의 강한 중심을 찾는 것과 같다. 주변부가 아닌 핵심부터. 같은 원리
DataAsFoundation - 문제 공간을 탐색하면, 종종 올바른 데이터 구조가 보인다. 문제가 명확하면 데이터 설계도 명확해진다. 자연스러운 발전
PatternHunting - 문제의 본질을 이해하면, 유사한 문제의 패턴을 인식할 수 있다. 기반
DetectiveWork - 버그도 문제다. 표면적 증상 대신 근본 원인을 찾는 것은 문제 공간 탐색이다. 같은 사고방식
TinyExperiment - 문제가 불명확하면, 작은 실험으로 문제를 명확히 하라. 문제 탐색 도구
CognitiveMicroscope - 자신이 문제 공간에 있는지 해결 공간에 있는지 관찰하라. 두 모드를 의식적으로 전환하는 것도 메타인지다. 인지적 도구
Signs of Success
TwoWorlds를 효과적으로 사용하고 있다는 신호:
질문이 많다 - 코딩 전에 "왜?"를 여러 번 물어본다
더 간단해진다 - 문제를 이해할수록 해결책이 단순해진다
재작업이 줄어든다 - "이게 아니었어"가 거의 없다
빠른 합의 - 문제 정의가 명확하면 팀이 빠르게 동의한다
작은 PR - 올바른 문제는 종종 작은 해결책을 만든다
확신이 있다 - 당신은 올바른 것을 만들고 있다고 확신한다
For Teachers and Mentors
주니어가 바로 구현을 시작하려 할 때:
멈추게 하라: "잠깐, 코딩 전에... 이 문제를 설명해줄 수 있어?"
질문하라:
- "왜 사용자가 이것을 원할까?"
- "이것이 해결하는 진짜 문제는 뭐지?"
- "다른 방식으로 같은 목표를 달성할 수 있을까?"
구분하게 하라: "지금 우리가 문제에 대해 이야기하는 건지, 해결책에 대해 이야기하는 건지 구분해보자."
연습하라: 매 작업마다, 문제 공간과 해결 공간을 명시적으로 구분하는 습관을 만들어라.
The Ultimate Insight
Polya가 말했다: 문제를 이해하는 것이 절반이다.
Weinberg가 말했다: 올바른 문제를 정의하지 않으면, 올바른 해결책을 찾을 수 없다.
Agile이 말한다: 단순함은 본질적이다 - 하지 않아도 되는 일을 최대화하라.
이 모든 지혜는 같은 진리를 가리킨다:
문제 공간에서 시간을 쓰는 것은 투자다. 해결 공간에서 잘못된 것을 만드는 것은 낭비다.
두 세계를 분리하라. 문제를 충분히 탐색하고, 그다음에 해결책을 만들어라. 이것은 단순히 시간을 절약하는 것이 아니다. 올바른 것을 만드는 것이다.
그리고 올바른 것을 만드는 것이 프로그래밍의 본질이다.
CategoryPatternLanguage CategoryProgramming CategoryProblemSolving CategoryDesign
