|
Size: 10545
Comment:
|
Size: 12061
Comment:
|
| Deletions are marked like this. | Additions are marked like this. |
| Line 6: | Line 6: |
| 복잡한 문제나 큰 시스템을 다룰 때. 어디서부터 시작해야 할지 막막하거나, 변경이 미칠 영향을 예측하기 어려운 상황. 새로운 기술을 배우거나 기존 코드를 이해해야 할 때. | 거대한 시스템 앞에서 큰 시스템을 바로 건드려야 하는 상황. 복잡한 곳에서부터 접근해야 할지 막막하거나, 무언가 건드릴 때마다 고장나기 쉬운 상황. 새로운 기능을 짜거나 기존 코드를 고쳐야 할 때. |
| Line 9: | Line 9: |
| '''실험이라는 개념 자체가 없다''' 대부분의 사람들은 프로그래밍을 할 때 **실험**이라는 개념을 아예 가지고 있지 않다. 그들의 접근 방식은 다음과 같다: 1. **계획 → 구현 → 완성** : 머릿속으로 전체를 설계하고, 한번에 구현하려 한다 2. **추측 → 수정 → 기대** : "아마 이렇게 하면 될 것이다"라고 추측하고 수정한 뒤 되기를 기대한다 3. **검색 → 복붙 → 시도** : 스택오버플로에서 답을 찾아 복사하고 작동하기를 바란다 이런 접근법의 문제점: - **피드백이 늦다** : 전체를 다 만들고 나서야 작동하는지 안 하는지 안다 - **실패 비용이 크다** : 잘못되면 처음부터 다시 해야 한다 - **학습이 일어나지 않는다** : 왜 작동하는지 작동하지 않는지 이해하지 못한다 - **복잡성에 압도된다** : 큰 문제 앞에서 어디서부터 손을 대야 할지 모른다 '''진짜 문제: 큰 덩어리에서 작은 부분을 떼어내지 못함''' 핵심 문제는 **큰 복합체에서 관심 있는 작은 부분을 분리해내는 능력**이 없다는 것이다. - 300줄짜리 함수를 보면서 "어디서부터 손을 대야 할까?" 막막해한다 - 버그가 있을 때 "전체 시스템이 복잡해서..." 라며 포기한다 - 새로운 라이브러리를 사용할 때 전체 문서를 다 읽으려 한다 |
'''성급함이라는 거대한 장애물''' 대부분의 프로그래머들은 큰 것을 '''전체를 한번에''' 다루려는 습관에 빠져 있다. 전통적인 접근은 세 단계를 따른다. 첫째로 '''기획 후 작업 후 완성'''이라는 순서를 가진다. 머릿속으로 전체를 상상하고, 한번에 구현하려 한다. 둘째로 '''실행 후 기도 후 결과'''를 기다린다. "아마 이렇게 하면 될 것이다"라고 추정하고 프로그램이 잘 되기를 기대한다. 셋째로 '''검증 후 디버그 후 재시도'''를 반복한다. 실제상황에서만 문제를 찾고 해결하기를 반복한다. 하지만 이런 습관방식은 심각한 문제들을 만든다. '''피드백이 늦다'''. 전체에 대해 무엇이 잘못된 것인지 어떻게 작동하는지 잘 모르는데도 추진한다. '''실수 비용이 크다'''. 잘못되면 처음부터 다시 해야 한다. '''학습이 일어나지 않는다'''. 왜 작동하는지 작동하지 않는지 메커니즘을 놓친다. '''창의성이 막힌다'''. 큰 것이 앞에 서있으면 압도되어 어떤 길로 가야 할지 모른다. 실제 문제는 큰 것들에 대해 우리 대부분이 가지고 있는 '''잘못된 접근 방식'''이다. 다시 정리하면 '''큰 전체 시스템에서 작은 작동 가능한 부분들을 분리해내는 능력'''이 부족하다는 점이다. 300줄짜리 함수를 보면서 "복잡한 것에서부터 어떤 길로 갈까?" 고민한다. 새로운 것이 많은데 "전체 시스템을 이해해서..." 라고 접근한다. 새로운 라이브러리를 배울 때 전체 문서를 다 읽으려 한다. |
| Line 32: | Line 19: |
| Line 36: | Line 24: |
| **큰 복합체에서 실험하고 싶은 작은 부분만 떼어내라.** 이것이 TinyExperiment의 핵심 기술이다. 전체 시스템이 아니라 **울타리를 쳐서 특정 영역만** 분리한다. '''분리 기법들:''' - **함수 단위 분리** : 300줄 함수에서 10줄짜리 핵심 로직만 따로 떼기 - **데이터 단위 분리** : 복잡한 데이터에서 샘플 3-4개만 따로 준비하기 - **시나리오 단위 분리** : 100가지 경우 중 가장 간단한 1가지만 먼저 하기 - **기능 단위 분리** : 전체 시스템에서 한 가지 기능만 독립적으로 실행하기 |
큰 전체 시스템에서 독립적이고 작은 부분만 떼어낸다. 이것이 TinyExperiment의 핵심 시작점이다. 전체 시스템이 아니라 '''극히 작고 특별한 특정 기능만''' 분리한다. 분리를 위한 구체적인 전략들이 있다. '''함수 단위 분리'''에서는 300줄 함수에서 10줄짜리 핵심 기능만 먼저 빼서 만든다. '''데이터 단위 분리'''에서는 데이터베이스 시스템에서 작은 3-4개의 레코드를 만들어 준비한다. '''처리과정 단위 분리'''에서는 100단계 중에서 한 가지 결과를 내는 1단계만 먼저 한다. '''의존성 단위 분리'''에서는 전체 시스템에서 이 작은 기능이 외부로부터 독립적으로 작동하게 한다. |
| Line 48: | Line 30: |
| **분리한 부분을 빠르고 안전하게 실험할 수 있는 환경을 구축하라.** 이 단계에서 **훈련된 창의성**이 필요하다. 기존 도구의 한계를 받아들이지 말고, 현재 상황에서 가능한 최선의 실험 환경을 만들어라. '''실험 환경 구축 사례:''' **구글 앱스 스크립트 예시** (유닛테스트가 없는 환경): |
분리한 부분을 빠르게 실험하고 결과를 볼 수 있는 환경을 만들어라. 이 단계에서 '''밑바닥 창의성'''이 필요하다. 작은 기능이 혼자서도 돌아가도록 하는, 실제 상황에서 떨어진 최소한의 작은 환경을 만든다. 실험 환경을 만드는 몇 가지 예들이 있다. '''작은 테스트 스크립트 만들기'''는 단위테스트와는 다른 환경이다: |
| Line 63: | Line 42: |
| **복잡한 API 호출 테스트**: {{{ // 실제 API 대신 가짜 데이터로 실험 |
'''외부 API 호출 테스트'''는 다음과 같이 한다: {{{ // 실제 API 대신 가짜 데이터로 테스트 |
| Line 67: | Line 47: |
| console.log("사용자 필터링:", filterActiveUsers(mockData)); }}} **데이터베이스 쿼리 실험**: |
console.log("활성화 필터링:", filterActiveUsers(mockData)); }}} '''데이터베이스 작업 시뮬레이션'''은 이렇게 한다: |
| Line 77: | Line 58: |
| console.log("필터 결과:", filterByStatus(testRows, 'active')); | console.log("상태 필터:", filterByStatus(testRows, 'active')); |
| Line 82: | Line 63: |
| **빠른 실험 반복을 통해 학습하라.** 이제 **찔러보고 → 반응 관찰 → 학습 → 조정 → 다시 찔러보기** 사이클을 빠르게 돌린다. '''실험 휠의 구성 요소:''' - **Poke (찔러보기)** : 작은 변경을 가한다 - **Observe (관찰)** : 시스템의 반응을 본다 - **Learn (학습)** : 왜 그런 결과가 나왔는지 이해한다 - **Adjust (조정)** : 다음 실험을 위해 전략을 수정한다 |
작고 빠른 반복으로 계속 학습하라. 이제 '''찔러보고 → 관찰 → 배우기 → 조정 → 다시 찔러보기''' 사이클을 빠르게 돌린다. 실험 휠의 각 단계는 명확한 목적을 가진다. '''Poke (찔러보기)'''에서는 작은 변경을 가한다. '''Observe (관찰)'''에서는 시스템의 반응을 본다. '''Learn (학습)'''에서는 왜 그런 결과가 나오는지 추론한다. '''Adjust (조정)'''에서는 다음 변경을 위해 접근을 수정한다. |
| Line 94: | Line 69: |
| '''왜 빠른 실험 반복이 최고의 학습 환경인가?''' TinyExperiment의 진짜 목적은 **학습**이다. 빠른 실험 반복은 다음을 가능하게 한다: **1. 즉각적인 피드백** - 추측을 몇 초 안에 검증할 수 있다 - 잘못된 가정을 빨리 발견하고 수정한다 - "왜?"에 대한 답을 즉시 얻는다 **2. 안전한 실패** - 실패 비용이 거의 없다 (몇 초면 되돌릴 수 있음) - 실패를 두려워하지 않고 과감하게 시도한다 - 실패에서 학습할 수 있다 **3. 깊은 이해** - 표면적인 지식이 아니라 내부 원리를 이해한다 - "어떻게"가 아니라 "왜"를 안다 - 비슷한 문제에 응용할 수 있는 통찰을 얻는다 |
왜 이런 작은 반복이 최고의 학습 환경인가? TinyExperiment의 진짜 목적은 '''학습'''이다. 작고 빠른 반복을 통해서 깊이를 이해하게 한다. '''삼각측량 피드백'''이 일어난다. 변경과 그 결과 사이에 직접적인 연결을 볼 수 있다. 잘못된 변경이 어떤 결과를 가져오는지 바로 수정할 수 있다. "왜?"라는 질문에 대한 답이 바로 보인다. '''안전한 실패'''가 가능하다. 작은 범위의 실패 비용만 있다 (몇 초만 피드백을 기다리면 됨). 망해도 큰 영향을 끼치지 않고 안전하게 시도한다. 실패에서 학습할 수 있다. '''창조의 경험'''을 얻는다. 표준으로 정해진 것이 아니라 직접 발견하는 기쁨을 느낀다. "정답"이 아니라 "왜"를 안다. 창의적 해결방법을 발견할 수 있는 기회가 온다. |
| Line 115: | Line 79: |
| === 예시 1: 복잡한 정규표현식 이해하기 === **상황**: 기존 코드에 있는 복잡한 정규표현식을 수정해야 함 |
=== 예제 1: 복잡한 정규표현식 이해하기 === '''상황''': 기존 코드에 있는 복잡한 정규표현식을 수정해야 함 |
| Line 122: | Line 87: |
| **전형적인 접근**: 정규표현식 문서를 읽고 전체를 이해하려 시도 **TinyExperiment 접근**: 1. **분리**: 정규표현식을 부분별로 나눔 2. **실험 환경**: 간단한 테스트 함수 생성 |
'''전통적인 접근''': 정규표현식 문서를 읽고 전체를 이해하려 시도 '''TinyExperiment 접근''': 첫째로 '''분리'''한다. 정규표현식을 부분별로 나눈다. 둘째로 '''실험 환경'''을 만든다. 간단한 테스트 함수를 만든다: |
| Line 134: | Line 99: |
| // 각 부분별로 실험 testRegexPart('(?=.*[a-z])', 'Hello123!'); // 소문자 포함 확인 testRegexPart('(?=.*[A-Z])', 'Hello123!'); // 대문자 포함 확인 testRegexPart('(?=.*\\d)', 'Hello123!'); // 숫자 포함 확인 }}} 3. **학습**: 각 부분이 무엇을 하는지 실험으로 이해 4. **조정**: 필요한 부분만 수정 **결과**: 15분만에 복잡한 정규표현식의 구조를 완전히 이해 === 예시 2: 성능 병목 찾기 === **상황**: 페이지 로딩이 8초나 걸리는 문제 **전형적인 접근**: 프로파일러로 전체 분석하려 시도 **TinyExperiment 접근**: 1. **분리**: 로딩 과정을 단계별로 나눔 |
// 각 부분별 테스트 testRegexPart('(?=.*[a-z])', 'Hello123!'); // 소문자 존재 확인 testRegexPart('(?=.*[A-Z])', 'Hello123!'); // 대문자 존재 확인 testRegexPart('(?=.*\\d)', 'Hello123!'); // 숫자 존재 확인 }}} 셋째로 '''학습'''한다. 각 부분이 무엇을 하는지 명확하게 안다. 넷째로 '''조정'''한다. 필요한 부분만 수정한다. '''결과''': 15분만에 복잡한 정규표현식을 자신있게 다룰 수 있게 되었다. === 예제 2: 성능 문제 찾기 === '''상황''': 페이지 로딩이 8초나 걸리는 문제 '''전통적인 접근''': 프로파일러로 전체를 분석하려 시도 '''TinyExperiment 접근''': 첫째로 '''분리'''한다. 로딩 과정을 단계별로 나눈다: |
| Line 159: | Line 124: |
| console.time('데이터 가공'); | console.time('데이터 처리'); |
| Line 161: | Line 126: |
| console.timeEnd('데이터 가공'); // 5초 <- 병목 발견! | console.timeEnd('데이터 처리'); // 5초 <- 문제 발견! |
| Line 168: | Line 133: |
| 2. **실험 환경**: 데이터 가공 부분만 분리해서 테스트 3. **학습**: N+1 쿼리 문제 발견 4. **조정**: 배치 처리로 변경 **결과**: 8초 → 1.5초로 성능 개선 === 예시 3: 새로운 API 익히기 === **상황**: 복잡한 REST API 문서 100페이지 **전형적인 접근**: 문서를 처음부터 끝까지 읽으려 시도 **TinyExperiment 접근**: 1. **분리**: 가장 간단한 API 하나만 선택 2. **실험 환경**: curl이나 Postman으로 바로 호출 |
둘째로 '''실험 환경'''을 만든다. 데이터 처리 부분을 분리해서 테스트한다. 셋째로 '''학습'''한다. N+1 쿼리 문제를 발견한다. 넷째로 '''조정'''한다. 배치 처리로 수정한다. '''결과''': 8초를 1.5초로 성능을 향상시켰다. === 예제 3: 새로운 API 배우기 === '''상황''': 새로운 REST API 문서 100페이지 '''전통적인 접근''': 문서를 처음부터 끝까지 읽으려 시도 '''TinyExperiment 접근''': 첫째로 '''분리'''한다. 하나의 기능을 하는 API 하나만 택한다. 둘째로 '''실험 환경'''을 만든다. curl이나 Postman으로 바로 호출한다: |
| Line 188: | Line 151: |
| 3. **학습**: 응답 구조 파악, 인증 방식 이해 4. **조정**: 다음 실험할 API 선택 **결과**: 2시간만에 API의 핵심 패턴 파악, 실제 개발 시작 가능 |
셋째로 '''학습'''한다. 요청과 응답을 이해하고, 에러 상황을 테스트한다. 넷째로 '''조정'''한다. 다음에 필요한 API를 추가한다. '''결과''': 2시간만에 API의 핵심 동작을 이해하고, 실제 코드를 바로 작성할 수 있게 되었다. |
| Line 195: | Line 157: |
| === 1. 전체 이해하려는 강박 === - **증상**: "전체를 다 이해하고 나서 시작해야 해" - **문제**: 영원히 시작하지 못함 - **해결**: 이해해야 할 최소한만 분리해서 실험 === 2. 완벽한 환경 기다리기 === - **증상**: "제대로 된 테스트 프레임워크부터 세팅하자" - **문제**: 환경 구축에만 시간 소모 - **해결**: console.log라도 활용해서 즉시 시작 === 3. 한번에 너무 많이 바꾸기 === - **증상**: "이것도 고치고 저것도 고치고..." - **문제**: 어떤 변경이 효과가 있었는지 모름 - **해결**: 한 번에 하나씩만 변경 |
첫 번째 안티패턴은 '''전체를 이해하려는 욕구'''다. "전체를 다 이해하고 나서 시작해야 한다"는 생각 때문에 시작도 영원히 하지 못한다. 해결책은 시작해야 할 최소한만 분리해서 시작하는 것이다. 두 번째 안티패턴은 '''완벽한 환경을 기다리기'''다. "실제로 제대로 된 테스트 인프라에서 해봐야지"라고 생각해서 환경 구축에서만 시간을 소모한다. console.log를 활용해서라도 바로 시작하는 것이 낫다. 세 번째 안티패턴은 '''한번에 너무 많이 바꾸기'''다. "이것도 고치고 저것도 고치고..."라고 접근하면 어떤 변경이 효과를 가져왔는지 모르게 된다. 한 번에 하나씩만 바꾸는 것이 원칙이다. |
| Line 212: | Line 165: |
| TinyExperiment를 체화하면 다음과 같은 사고방식이 자연스러워진다: **"이걸 어떻게 작게 쪼갤 수 있을까?"** **"이 부분만 따로 실험해볼 수 있을까?"** **"5분 안에 결과를 확인할 수 있을까?"** **"실패해도 쉽게 되돌릴 수 있을까?"** 이런 질문들이 습관이 되면, 복잡한 문제 앞에서도 당황하지 않는다. 항상 "작은 실험부터 시작하자"는 접근법이 자연스러워진다. |
TinyExperiment를 체화하면 평소에 이런 질문들이 자연스럽게 떠오른다: "이것 중에 가장 작고 성공할만한 것은 무엇인가?" "어떤 부분만 먼저 시험해볼 수 있을까?" "5분 안에 답을 확인할 수 있을까?" "망쳐도 괜찮게 되돌릴 수 있을까?" 이런 질문들이 습관이 되면, 거대한 것이 앞에 와도 상황에서 멈추지 않는다. 항상 "작은 시작점이 어디일까"를 찾는 사고방식을 자연스럽게 갖게 된다. |
| Line 223: | Line 179: |
| **빠른 실험을 위한 도구들:** - **REPL**: 언어별 인터랙티브 셸 - **console.log/print**: 가장 기본적이지만 강력한 관찰 도구 - **단위 테스트**: 작은 부분을 격리해서 실험 - **git stash/branch**: 안전한 실험을 위한 버전 관리 - **디버거**: 코드 실행 과정을 관찰 - **프로파일러**: 성능 병목을 관찰 **핵심은 도구가 아니라 마음가짐**이다. 어떤 환경에서든 실험할 방법을 찾아내는 창의성이 중요하다. |
실험 환경을 위해 활용하는 도구들이 여러 있다. '''REPL'''은 즉석 인터랙티브 환경이다. '''console.log/print'''는 가장 기본적이면서도 강력한 실험과 디버깅 도구이다. '''단위 테스트'''는 작은 부분을 격리해서 실험하는 방법이다. '''git stash/branch'''는 실험을 안전한 별도 공간에서 하기 위한 것이다. '''샌드박스'''는 코드 온라인 실험환경을 활용하는 것이다. '''프로파일러'''는 성능 실험을 위한 도구이다. 하지만 이것들은 '''도구가 아니라 접근방식'''이다. 어떤 환경에서든 최선의 방법을 찾아내는 창의성이 중요하다. |
| Line 235: | Line 185: |
| TinyExperiment를 체화하면: - **복잡성에 압도되지 않는다**: 큰 문제도 작은 실험들의 연속으로 본다 - **학습 속도가 빨라진다**: 빠른 피드백으로 깊은 이해를 얻는다 - **자신감이 생긴다**: 실패를 두려워하지 않고 시도한다 - **문제 해결력이 향상된다**: 체계적인 실험 방법을 가진다 가장 중요한 변화는 **"실험하는 프로그래머"**가 되는 것이다. 추측하고 기대하는 대신, 실험하고 학습한다. |
TinyExperiment를 체화하면 여러 변화가 일어난다. '''창의성이 막히지 않는다'''. 큰 것에도 작은 시작점을 발견하는 능력이 생긴다. '''학습 속도가 빨라진다'''. 빠른 피드백을 통해 깊은 이해를 얻는다. '''자신감이 생긴다'''. 망해도 큰 영향을 끼치지 않기 때문에 과감하게 시도한다. '''문제 해결력이 늘어난다'''. 체계적인 작은 접근법을 가진다. 가장 중요한 변화는 '''"탐구하는 프로그래머"'''가 되는 점이다. 겁내지 않고 호기심을 가지며, 실험하고 학습한다. |
| Line 245: | Line 192: |
| * '''연관:''' [[BabySteps]], [[GreenRefuge]] - 작은 단계와 안전한 되돌림 * '''활용:''' [[DesignThroughTest]], [[DetectiveWork]] - 설계와 디버깅에서의 실험 * '''보완:''' [[TightLoop]], [[AtomicCommit]] - 빠른 피드백과 작은 변경 |
* '''기반:''' [[BabySteps]], [[GreenRefuge]] - 작은 단계와 안전한 피드백 * '''활용:''' [[DesignThroughTest]], [[DetectiveWork]] - 실험적 방법론에서의 응용 * '''연계:''' [[TightLoop]], [[AtomicCommit]] - 빠른 피드백을 위한 협력 |
| Line 250: | Line 198: |
| * [[추론법]] - 가설을 세우고 검증하는 사고 방법 * Kent Beck의 "Test Driven Development" - 작은 테스트로 시작하기 * Gary Klein의 "Sources of Power" - 전문가의 직관적 사고 패턴 |
* [[중급패턴]] - 실험을 통해 고급을 이해하는 단계로 가는 법 * KentBeck의 "Test Driven Development" - 작은 테스트의 철학 * GaryKlein의 "Sources of Power" - 전문가들의 직관과 실험 방법 |
TinyExperiment
Context
거대한 시스템 앞에서 큰 시스템을 바로 건드려야 하는 상황. 복잡한 곳에서부터 접근해야 할지 막막하거나, 무언가 건드릴 때마다 고장나기 쉬운 상황. 새로운 기능을 짜거나 기존 코드를 고쳐야 할 때.
Problem
성급함이라는 거대한 장애물
대부분의 프로그래머들은 큰 것을 전체를 한번에 다루려는 습관에 빠져 있다. 전통적인 접근은 세 단계를 따른다. 첫째로 기획 후 작업 후 완성이라는 순서를 가진다. 머릿속으로 전체를 상상하고, 한번에 구현하려 한다. 둘째로 실행 후 기도 후 결과를 기다린다. "아마 이렇게 하면 될 것이다"라고 추정하고 프로그램이 잘 되기를 기대한다. 셋째로 검증 후 디버그 후 재시도를 반복한다. 실제상황에서만 문제를 찾고 해결하기를 반복한다.
하지만 이런 습관방식은 심각한 문제들을 만든다. 피드백이 늦다. 전체에 대해 무엇이 잘못된 것인지 어떻게 작동하는지 잘 모르는데도 추진한다. 실수 비용이 크다. 잘못되면 처음부터 다시 해야 한다. 학습이 일어나지 않는다. 왜 작동하는지 작동하지 않는지 메커니즘을 놓친다. 창의성이 막힌다. 큰 것이 앞에 서있으면 압도되어 어떤 길로 가야 할지 모른다.
실제 문제는 큰 것들에 대해 우리 대부분이 가지고 있는 잘못된 접근 방식이다. 다시 정리하면 큰 전체 시스템에서 작은 작동 가능한 부분들을 분리해내는 능력이 부족하다는 점이다. 300줄짜리 함수를 보면서 "복잡한 것에서부터 어떤 길로 갈까?" 고민한다. 새로운 것이 많은데 "전체 시스템을 이해해서..." 라고 접근한다. 새로운 라이브러리를 배울 때 전체 문서를 다 읽으려 한다.
Solution
TinyExperiment의 3단계 사이클
1단계: 작은 부분 분리하기 (Detachment)
큰 전체 시스템에서 독립적이고 작은 부분만 떼어낸다. 이것이 TinyExperiment의 핵심 시작점이다. 전체 시스템이 아니라 극히 작고 특별한 특정 기능만 분리한다.
분리를 위한 구체적인 전략들이 있다. 함수 단위 분리에서는 300줄 함수에서 10줄짜리 핵심 기능만 먼저 빼서 만든다. 데이터 단위 분리에서는 데이터베이스 시스템에서 작은 3-4개의 레코드를 만들어 준비한다. 처리과정 단위 분리에서는 100단계 중에서 한 가지 결과를 내는 1단계만 먼저 한다. 의존성 단위 분리에서는 전체 시스템에서 이 작은 기능이 외부로부터 독립적으로 작동하게 한다.
2단계: 실험 가능하게 만들기 (Make it Experiment-able)
분리한 부분을 빠르게 실험하고 결과를 볼 수 있는 환경을 만들어라. 이 단계에서 밑바닥 창의성이 필요하다. 작은 기능이 혼자서도 돌아가도록 하는, 실제 상황에서 떨어진 최소한의 작은 환경을 만든다.
실험 환경을 만드는 몇 가지 예들이 있다. 작은 테스트 스크립트 만들기는 단위테스트와는 다른 환경이다:
function testDataParsing() {
console.log("날짜 파싱:", parseDate("2024-01-15") === "2024/01/15" ? "PASS" : "FAIL");
console.log("빈 데이터:", handleEmpty("") === null ? "PASS" : "FAIL");
console.log("숫자 변환:", toNumber("123") === 123 ? "PASS" : "FAIL");
}외부 API 호출 테스트는 다음과 같이 한다:
// 실제 API 대신 가짜 데이터로 테스트
const mockData = { users: [{ id: 1, name: "test" }] };
console.log("활성화 필터링:", filterActiveUsers(mockData));데이터베이스 작업 시뮬레이션은 이렇게 한다:
// 전체 DB 대신 작은 테스트 데이터로
const testRows = [
{ id: 1, status: 'active', score: 85 },
{ id: 2, status: 'inactive', score: 92 }
];
console.log("상태 필터:", filterByStatus(testRows, 'active'));
3단계: 실험 휠 돌리기 (Flying the Experiment Wheel)
작고 빠른 반복으로 계속 학습하라. 이제 찔러보고 → 관찰 → 배우기 → 조정 → 다시 찔러보기 사이클을 빠르게 돌린다.
실험 휠의 각 단계는 명확한 목적을 가진다. Poke (찔러보기)에서는 작은 변경을 가한다. Observe (관찰)에서는 시스템의 반응을 본다. Learn (학습)에서는 왜 그런 결과가 나오는지 추론한다. Adjust (조정)에서는 다음 변경을 위해 접근을 수정한다.
The Learning Laboratory
왜 이런 작은 반복이 최고의 학습 환경인가? TinyExperiment의 진짜 목적은 학습이다. 작고 빠른 반복을 통해서 깊이를 이해하게 한다.
삼각측량 피드백이 일어난다. 변경과 그 결과 사이에 직접적인 연결을 볼 수 있다. 잘못된 변경이 어떤 결과를 가져오는지 바로 수정할 수 있다. "왜?"라는 질문에 대한 답이 바로 보인다.
안전한 실패가 가능하다. 작은 범위의 실패 비용만 있다 (몇 초만 피드백을 기다리면 됨). 망해도 큰 영향을 끼치지 않고 안전하게 시도한다. 실패에서 학습할 수 있다.
창조의 경험을 얻는다. 표준으로 정해진 것이 아니라 직접 발견하는 기쁨을 느낀다. "정답"이 아니라 "왜"를 안다. 창의적 해결방법을 발견할 수 있는 기회가 온다.
Real Examples
예제 1: 복잡한 정규표현식 이해하기
상황: 기존 코드에 있는 복잡한 정규표현식을 수정해야 함
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/전통적인 접근: 정규표현식 문서를 읽고 전체를 이해하려 시도
TinyExperiment 접근:
첫째로 분리한다. 정규표현식을 부분별로 나눈다. 둘째로 실험 환경을 만든다. 간단한 테스트 함수를 만든다:
function testRegexPart(pattern, testString) {
const regex = new RegExp(pattern);
console.log(`"${testString}" -> ${regex.test(testString) ? 'MATCH' : 'NO MATCH'}`);
}
// 각 부분별 테스트
testRegexPart('(?=.*[a-z])', 'Hello123!'); // 소문자 존재 확인
testRegexPart('(?=.*[A-Z])', 'Hello123!'); // 대문자 존재 확인
testRegexPart('(?=.*\\d)', 'Hello123!'); // 숫자 존재 확인셋째로 학습한다. 각 부분이 무엇을 하는지 명확하게 안다. 넷째로 조정한다. 필요한 부분만 수정한다.
결과: 15분만에 복잡한 정규표현식을 자신있게 다룰 수 있게 되었다.
예제 2: 성능 문제 찾기
상황: 페이지 로딩이 8초나 걸리는 문제
전통적인 접근: 프로파일러로 전체를 분석하려 시도
TinyExperiment 접근:
첫째로 분리한다. 로딩 과정을 단계별로 나눈다:
console.time('데이터 로딩');
const data = await fetchData();
console.timeEnd('데이터 로딩'); // 2초
console.time('데이터 처리');
const processed = processData(data);
console.timeEnd('데이터 처리'); // 5초 <- 문제 발견!
console.time('렌더링');
renderData(processed);
console.timeEnd('렌더링'); // 1초둘째로 실험 환경을 만든다. 데이터 처리 부분을 분리해서 테스트한다. 셋째로 학습한다. N+1 쿼리 문제를 발견한다. 넷째로 조정한다. 배치 처리로 수정한다.
결과: 8초를 1.5초로 성능을 향상시켰다.
예제 3: 새로운 API 배우기
상황: 새로운 REST API 문서 100페이지
전통적인 접근: 문서를 처음부터 끝까지 읽으려 시도
TinyExperiment 접근:
첫째로 분리한다. 하나의 기능을 하는 API 하나만 택한다. 둘째로 실험 환경을 만든다. curl이나 Postman으로 바로 호출한다:
curl -X GET "https://api.example.com/users/1" -H "Authorization: Bearer token"
셋째로 학습한다. 요청과 응답을 이해하고, 에러 상황을 테스트한다. 넷째로 조정한다. 다음에 필요한 API를 추가한다.
결과: 2시간만에 API의 핵심 동작을 이해하고, 실제 코드를 바로 작성할 수 있게 되었다.
Common Anti-Patterns
첫 번째 안티패턴은 전체를 이해하려는 욕구다. "전체를 다 이해하고 나서 시작해야 한다"는 생각 때문에 시작도 영원히 하지 못한다. 해결책은 시작해야 할 최소한만 분리해서 시작하는 것이다.
두 번째 안티패턴은 완벽한 환경을 기다리기다. "실제로 제대로 된 테스트 인프라에서 해봐야지"라고 생각해서 환경 구축에서만 시간을 소모한다. console.log를 활용해서라도 바로 시작하는 것이 낫다.
세 번째 안티패턴은 한번에 너무 많이 바꾸기다. "이것도 고치고 저것도 고치고..."라고 접근하면 어떤 변경이 효과를 가져왔는지 모르게 된다. 한 번에 하나씩만 바꾸는 것이 원칙이다.
The Experiment Mindset
TinyExperiment를 체화하면 평소에 이런 질문들이 자연스럽게 떠오른다:
"이것 중에 가장 작고 성공할만한 것은 무엇인가?"
"어떤 부분만 먼저 시험해볼 수 있을까?"
"5분 안에 답을 확인할 수 있을까?"
"망쳐도 괜찮게 되돌릴 수 있을까?"
이런 질문들이 습관이 되면, 거대한 것이 앞에 와도 상황에서 멈추지 않는다. 항상 "작은 시작점이 어디일까"를 찾는 사고방식을 자연스럽게 갖게 된다.
Tools and Techniques
실험 환경을 위해 활용하는 도구들이 여러 있다. REPL은 즉석 인터랙티브 환경이다. console.log/print는 가장 기본적이면서도 강력한 실험과 디버깅 도구이다. 단위 테스트는 작은 부분을 격리해서 실험하는 방법이다. git stash/branch는 실험을 안전한 별도 공간에서 하기 위한 것이다. 샌드박스는 코드 온라인 실험환경을 활용하는 것이다. 프로파일러는 성능 실험을 위한 도구이다.
하지만 이것들은 도구가 아니라 접근방식이다. 어떤 환경에서든 최선의 방법을 찾아내는 창의성이 중요하다.
Resulting Context
TinyExperiment를 체화하면 여러 변화가 일어난다.
창의성이 막히지 않는다. 큰 것에도 작은 시작점을 발견하는 능력이 생긴다. 학습 속도가 빨라진다. 빠른 피드백을 통해 깊은 이해를 얻는다. 자신감이 생긴다. 망해도 큰 영향을 끼치지 않기 때문에 과감하게 시도한다. 문제 해결력이 늘어난다. 체계적인 작은 접근법을 가진다.
가장 중요한 변화는 "탐구하는 프로그래머"가 되는 점이다. 겁내지 않고 호기심을 가지며, 실험하고 학습한다.
Related Patterns
기반: BabySteps, GreenRefuge - 작은 단계와 안전한 피드백
활용: DesignThroughTest, DetectiveWork - 실험적 방법론에서의 응용
연계: TightLoop, AtomicCommit - 빠른 피드백을 위한 협력
See Also
중급패턴 - 실험을 통해 고급을 이해하는 단계로 가는 법
KentBeck의 "Test Driven Development" - 작은 테스트의 철학
GaryKlein의 "Sources of Power" - 전문가들의 직관과 실험 방법
