|
Size: 8918
Comment:
|
← Revision 14 as of 2025-12-30 10:00:26 ⇥
Size: 11561
Comment:
|
| Deletions are marked like this. | Additions are marked like this. |
| Line 5: | Line 5: |
| [[attachment:TinyExperiment.pdf]] == The Story 1: Complex Regex (Programming) == 한 주니어 개발자가 기존 코드에 있는 복잡한 정규표현식을 수정해야 하는 상황에 처했다. {{{ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/ }}} 그는 정규표현식 전체를 한꺼번에 이해하려고 끙끙대며 1시간을 보냈다. 하지만 시니어는 다르게 접근했다. 그는 정규표현식을 부분별로 나누어(Detachment), 아주 작은 테스트 함수에 하나씩 넣어보았다(Experiment-able). "먼저 소문자만 체크해보자. 그다음엔 숫자만." 15분 만에 그는 복잡한 패턴의 정규표현식을 완벽하게 파악하고 안전하게 수정했다. == The Story 2: The Baker's Test (Ordinary Life) == 민수와 하나는 친구의 결혼식 답례품으로 줄 '초콜릿 쿠키' 200개를 굽기로 했다. '''민수의 방식 (The Big Batch):''' 민수는 레시피를 믿고 200개 분량의 반죽을 한꺼번에 만들었다. "레시피대로 했으니 당연히 맛있겠지?" 그는 거대한 오븐에 쿠키들을 가득 넣고 구웠다. 하지만 오븐에서 나온 쿠키들은 너무 딱딱했고 지나치게 달았다. 200개의 쿠키는 모두 쓰레기통으로 향했고, 민수는 재료비와 시간을 모두 날렸다. '''하나의 방식 (The Tiny Experiment):''' 하나는 반죽을 한 숟가락만 떼어내어 작은 오븐에 딱 2개만 먼저 구워보았다. 1. '''분리:''' 전체 양이 아닌 한 조각만 먼저 굽는다. 2. '''관찰:''' 구워진 쿠키를 먹어보니 속이 덜 익고 설탕이 너무 많았다. 3. '''조정:''' 설탕을 줄이고 오븐 온도를 10도 높였다. 하나는 세 번의 '작은 실험' 끝에 완벽한 맛과 식감을 찾아냈고, 그제야 200개의 쿠키를 자신 있게 구워냈다. 큰 실패의 위험을 작은 실험으로 미리 차단한 것이다. |
|
| Line 6: | Line 30: |
| 복잡한 소프트웨어 문제를 해결하거나 새로운 기능을 구현할 때, 또는 버그를 추적할 때. 불확실성이 높고 여러 가능성이 존재하는 상황에서 어떻게 접근해야 할지 막막할 때. | 거대한 시스템 앞에서 큰 시스템을 바로 건드려야 하는 상황. 복잡한 곳에서부터 접근해야 할지 막막하거나, 무언가 건드릴 때마다 고장나기 쉽거나, 새로운 기능을 짜거나 기존 코드를 고쳐야 할 때. |
| Line 9: | Line 34: |
| '''큰 변경의 함정''' | |
| Line 11: | Line 35: |
| 주니어 개발자들이 자주 빠지는 함정이 있다. "이 기능을 구현하려면 A도 바꾸고, B도 수정하고, C도 새로 만들어야 해"라고 생각하며 한 번에 많은 것을 바꾸려 한다. | '''성급함이라는 거대한 장애물''' |
| Line 13: | Line 37: |
| 결과는 참혹하다. 2시간 후 코드는 엉망이 되고, 어디서 문제가 생겼는지 알 수 없다. 원래 상태로 되돌리기도 어렵다. 머릿속 추측만으로는 복잡한 시스템의 동작을 정확히 예측할 수 없고, 긴 피드백 루프는 학습을 늦추며 잘못된 방향으로 오래 가게 만든다. | 대부분의 프로그래머들은 큰 것을 '''전체를 한번에''' 다루려는 습관에 빠져 있다. 전통적인 접근은 세 단계를 따른다. 첫째로 '''기획 후 작업 후 완성'''이라는 순서를 가진다. 머릿속으로 전체를 상상하고, 한번에 구현하려 한다. 둘째로 '''실행 후 기도 후 결과'''를 기다린다. "아마 이렇게 하면 될 것이다"라고 추정하고 프로그램이 잘 되기를 기대한다. 셋째로 '''검증 후 디버그 후 재시도'''를 반복한다. 실제상황에서만 문제를 찾고 해결하기를 반복한다. |
| Line 15: | Line 39: |
| '''요리사의 실수''' | 하지만 이런 습관방식은 심각한 문제들을 만든다. '''피드백이 늦다'''. 전체에 대해 무엇이 잘못된 것인지 어떻게 작동하는지 잘 모르는데도 추진한다. '''실수 비용이 크다'''. 잘못되면 처음부터 다시 해야 한다. '''학습이 일어나지 않는다'''. 왜 작동하는지 작동하지 않는지 메커니즘을 놓친다. '''창의성이 막힌다'''. 큰 것이 앞에 서있으면 압도되어 어떤 길로 가야 할지 모른다. |
| Line 17: | Line 41: |
| 경험 없는 요리사가 새로운 요리를 만들 때를 생각해보자. 레시피를 보고 "설탕 대신 꿀을 넣고, 소금도 줄이고, 향신료도 바꿔보자"며 한 번에 여러 변경을 시도한다. 결과적으로 맛없는 요리가 나오지만, 무엇 때문에 실패했는지 알 수 없다. 설탕 때문인가? 소금 때문인가? 향신료 때문인가? | 실제 문제는 큰 것들에 대해 우리 대부분이 가지고 있는 '''잘못된 접근 방식'''이다. 다시 정리하면 '''큰 전체 시스템에서 작은 작동 가능한 부분들을 분리해내는 능력'''이 부족하다는 점이다. 300줄짜리 함수를 보면서 "복잡한 것에서부터 어떤 길로 갈까?" 고민한다. 새로운 것이 많은데 "전체 시스템을 이해해서..." 라고 접근한다. 새로운 라이브러리를 배울 때 전체 문서를 다 읽으려 한다. |
| Line 20: | Line 45: |
| '''작은 실험의 힘''' | |
| Line 22: | Line 46: |
| 아주 작은 가설을 세우고 빠르게 검증할 수 있는 환경을 만들어라. 마치 과학자가 실험실에서 하나의 변수만 바꿔가며 실험하는 것처럼. | '''TinyExperiment의 3단계 사이클''' |
| Line 24: | Line 48: |
| === 1단계: 명확한 가설 세우기 === 막연한 "아마 이럴 것이다"가 아니라 구체적인 가설을 세워라. |
=== 1단계: 작은 부분 분리하기 (Detachment) === |
| Line 27: | Line 50: |
| 나쁜 예: "이 코드가 느린 것 같다" 좋은 예: "이 반복문에서 데이터베이스 조회가 N번 발생해서 느릴 것이다" |
큰 전체 시스템에서 독립적이고 작은 부분만 떼어낸다. 이것이 TinyExperiment의 핵심 시작점이다. 전체 시스템이 아니라 '''극히 작고 특별한 특정 기능만''' 분리한다. |
| Line 30: | Line 52: |
| === 2단계: 최소 실험 설계 === 가설을 검증할 수 있는 가장 작은 실험을 만들어라. |
분리를 위한 구체적인 전략들이 있다. '''함수 단위 분리'''에서는 300줄 함수에서 10줄짜리 핵심 기능만 먼저 빼서 만든다. '''데이터 단위 분리'''에서는 데이터베이스 시스템에서 작은 3-4개의 레코드를 만들어 준비한다. '''처리과정 단위 분리'''에서는 100단계 중에서 한 가지 결과를 내는 1단계만 먼저 한다. '''의존성 단위 분리'''에서는 전체 시스템에서 이 작은 기능이 외부로부터 독립적으로 작동하게 한다. |
| Line 33: | Line 54: |
| 예시: 데이터베이스 조회 가설을 검증하려면 - 로그를 추가해서 실제 쿼리 횟수 확인 - 작은 데이터셋으로 테스트 - 쿼리 실행 시간 측정 |
=== 2단계: 실험 가능하게 만들기 (Make it Experiment-able) === |
| Line 38: | Line 56: |
| === 3단계: 안전망 구축 === 실패해도 쉽게 되돌아갈 수 있는 안전망을 준비하라. |
분리한 부분을 빠르게 실험하고 결과를 볼 수 있는 환경을 만들어라. 이 단계에서 '''밑바닥 창의성'''이 필요하다. 작은 기능이 혼자서도 돌아가도록 하는, 실제 상황에서 떨어진 최소한의 작은 환경을 만든다. |
| Line 41: | Line 58: |
| - git commit으로 현재 상태 저장 - 브랜치 생성해서 실험 - 테스트 코드로 기존 기능 보호 - 백업 데이터 준비 |
실험 환경을 만드는 몇 가지 예들이 있다. '''작은 테스트 스크립트 만들기'''는 단위테스트와는 다른 환경이다: |
| Line 46: | Line 60: |
| === 4단계: 빠른 피드백 루프 === 결과를 즉시 확인할 수 있는 환경을 만들어라. |
{{{ 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"); } }}} |
| Line 49: | Line 68: |
| - 자동화된 테스트 실행 - 핫 리로드 개발 환경 - 실시간 로그 모니터링 - 간단한 확인 스크립트 |
'''외부 API 호출 테스트'''는 다음과 같이 한다: |
| Line 54: | Line 70: |
| === 5단계: 격리된 환경 === 다른 요소들의 영향을 받지 않도록 격리하라. |
{{{ // 실제 API 대신 가짜 데이터로 테스트 const mockData = { users: [{ id: 1, name: "test" }] }; console.log("활성화 필터링:", filterActiveUsers(mockData)); }}} |
| Line 57: | Line 76: |
| - 테스트용 데이터베이스 사용 - 모의 객체(mock)로 외부 의존성 제거 - 개발 전용 환경에서 실험 - 한 번에 하나의 변수만 변경 |
'''데이터베이스 작업 시뮬레이션'''은 이렇게 한다: |
| Line 62: | Line 78: |
| === 6단계: 가추법 활용 === 관찰된 현상에서 가능한 원인을 추론하고 다시 검증하라. |
{{{ // 전체 DB 대신 작은 테스트 데이터로 const testRows = [ { id: 1, status: 'active', score: 85 }, { id: 2, status: 'inactive', score: 92 } ]; console.log("상태 필터:", filterByStatus(testRows, 'active')); }}} |
| Line 65: | Line 87: |
| 현상: "API 응답이 느리다" 가능한 원인들: - 데이터베이스 쿼리가 느림 → 쿼리 실행 계획 확인 - 네트워크 지연 → 로컬 테스트로 비교 - 메모리 부족 → 메모리 사용량 모니터링 |
=== 3단계: 실험 휠 돌리기 (Flying the Experiment Wheel) === |
| Line 71: | Line 89: |
| == Detailed Examples == | 작고 빠른 반복으로 계속 학습하라. 이제 '''찔러보고 → 관찰 → 배우기 → 조정 → 다시 찔러보기''' 사이클을 빠르게 돌린다. |
| Line 73: | Line 91: |
| === 예시 1: 새로운 기능 구현 === '''상황:''' 사용자 프로필 페이지에 '좋아요' 기능 추가 |
실험 휠의 각 단계는 명확한 목적을 가진다. '''Poke (찔러보기)'''에서는 작은 변경을 가한다. '''Observe (관찰)'''에서는 시스템의 반응을 본다. '''Learn (학습)'''에서는 왜 그런 결과가 나오는지 추론한다. '''Adjust (조정)'''에서는 다음 변경을 위해 접근을 수정한다. |
| Line 76: | Line 93: |
| '''잘못된 접근:''' - 데이터베이스 스키마 변경 - API 엔드포인트 추가 - 프론트엔드 UI 구현 - 알림 기능 추가 - 권한 검증 로직 추가 모든 것을 한 번에 구현 → 3일 후 동작하지 않음 |
|
| Line 84: | Line 94: |
| '''TinyExperiment 접근:''' 1. '''가설:''' "좋아요 데이터를 저장할 수 있다" - 임시 테이블 생성 - 간단한 INSERT/SELECT 쿼리 테스트 - 5분 만에 검증 완료 |
== The Learning Laboratory == |
| Line 90: | Line 96: |
| 2. '''가설:''' "API로 좋아요 데이터를 주고받을 수 있다" - 기본적인 POST/GET 엔드포인트만 구현 - Postman으로 테스트 - 10분 만에 검증 완료 |
왜 이런 작은 반복이 최고의 학습 환경인가? TinyExperiment의 진짜 목적은 '''학습'''이다. 작고 빠른 반복을 통해서 깊이를 이해하게 한다. |
| Line 95: | Line 98: |
| 3. '''가설:''' "프론트엔드에서 API를 호출할 수 있다" - 단순한 버튼과 AJAX 호출만 구현 - UI는 나중에 다듬기 - 15분 만에 검증 완료 |
'''삼각측량 피드백'''이 일어난다. 변경과 그 결과 사이에 직접적인 연결을 볼 수 있다. 잘못된 변경이 어떤 결과를 가져오는지 바로 수정할 수 있다. "왜?"라는 질문에 대한 답이 바로 보인다. |
| Line 100: | Line 100: |
| 각 단계가 성공한 후 다음 단계로 진행. 실패하면 원인을 정확히 알 수 있음. | '''안전한 실패'''가 가능하다. 작은 범위의 실패 비용만 있다 (몇 초만 피드백을 기다리면 됨). 망해도 큰 영향을 끼치지 않고 안전하게 시도한다. 실패에서 학습할 수 있다. |
| Line 102: | Line 102: |
| === 예시 2: 버그 디버깅 === '''상황:''' "로그인 후 대시보드가 가끔 안 보인다"는 신고 |
'''창조의 경험'''을 얻는다. 표준으로 정해진 것이 아니라 직접 발견하는 기쁨을 느낀다. "정답"이 아니라 "왜"를 안다. 창의적 해결방법을 발견할 수 있는 기회가 온다. |
| Line 105: | Line 104: |
| '''잘못된 접근:''' 코드를 훑어보며 "아마 세션 문제일 것이다" 추측하고 세션 관련 코드를 모두 수정 |
|
| Line 108: | Line 105: |
| '''TinyExperiment 접근:''' 1. '''가설:''' "재현 가능한 조건이 있다" - 다양한 브라우저, 사용자로 테스트 - 재현 조건 발견: "IE에서만 발생" |
== Real Examples == |
| Line 113: | Line 107: |
| 2. '''가설:''' "특정 브라우저의 문제다" - 콘솔 로그 확인 - 네트워크 탭에서 요청 확인 - JavaScript 오류 발견 |
=== 예제 1: 복잡한 정규표현식 이해하기 === 상단 'Story 1'의 사례와 같이 복잡한 패턴을 분리하여 개별적으로 검증함으로써 신속하고 정확하게 원리를 파악한 사례. |
| Line 118: | Line 110: |
| 3. '''가설:''' "이 JavaScript 오류가 원인이다" - console.log 추가해서 실행 경로 추적 - 특정 함수에서 오류 발생 확인 |
=== 예제 2: 성능 문제 찾기 === 로딩 과정을 단계별로 `console.time()`으로 측정하여 8초 중 5초가 걸리는 특정 데이터 처리 지점을 발견하고 최적화한 사례. |
| Line 122: | Line 113: |
| 4. '''가설:''' "이 함수의 이 부분이 문제다" - 최소한의 수정으로 오류 해결 - 테스트로 검증 |
=== 예제 3: 새로운 API 배우기 === 100페이지 문서를 다 읽는 대신, `curl`이나 Postman으로 API 하나만 직접 호출해보며 요청과 응답 구조를 2시간 만에 파악한 사례. |
| Line 126: | Line 116: |
| === 예시 3: 성능 개선 === '''상황:''' "페이지 로딩이 너무 느려요" |
|
| Line 129: | Line 117: |
| '''TinyExperiment 접근:''' 1. '''측정부터:''' 브라우저 개발자 도구로 정확한 로딩 시간 확인 2. '''가설 1:''' "이미지가 너무 크다" → 이미지 최적화 후 측정 3. '''가설 2:''' "JavaScript 파일이 크다" → 번들 크기 분석 4. '''가설 3:''' "데이터베이스 쿼리가 느리다" → 쿼리 실행 시간 측정 |
== Common Anti-Patterns == |
| Line 135: | Line 119: |
| 각 개선 후 실제 측정치로 효과 확인. | 첫 번째 안티패턴은 '''전체를 이해하려는 욕구'''다. "전체를 다 이해하고 나서 시작해야 한다"는 생각 때문에 시작도 영원히 하지 못한다. 해결책은 시작해야 할 최소한만 분리해서 시작하는 것이다. |
| Line 137: | Line 121: |
| == 요리사의 지혜 (은유) == 훌륭한 요리사는 새로운 요리를 개발할 때 이렇게 한다: |
두 번째 안티패턴은 '''완벽한 환경을 기다리기'''다. "실제로 제대로 된 테스트 인프라에서 해봐야지"라고 생각해서 환경 구축에서만 시간을 소모한다. console.log를 활용해서라도 바로 시작하는 것이 낫다. |
| Line 140: | Line 123: |
| 1. '''기본 레시피로 시작''' - 검증된 방법부터 2. '''한 번에 하나만 바꾸기''' - 설탕량만 조정, 다른 것은 그대로 3. '''맛을 보며 조정''' - 변경 후 즉시 결과 확인 4. '''기록 남기기''' - 무엇을 바꿨는지, 결과가 어땠는지 5. '''실패해도 괜찮은 환경''' - 작은 양으로 실험, 큰 파티 음식은 나중에 |
세 번째 안티패턴은 '''한번에 너무 많이 바꾸기'''다. "이것도 고치고 저것도 고치고..."라고 접근하면 어떤 변경이 효과를 가져왔는지 모르게 된다. 한 번에 하나씩만 바꾸는 것이 원칙이다. |
| Line 146: | Line 125: |
| 프로그래밍도 마찬가지다. 경험 많은 개발자는 작은 실험을 통해 점진적으로 시스템을 이해하고 개선한다. | |
| Line 148: | Line 126: |
| == Common Anti-Patterns (피해야 할 것들) == | == The Experiment Mindset == |
| Line 150: | Line 128: |
| === "Big Bang" 개발 === - 한 번에 많은 기능을 구현하려고 함 - 실패 시 원인을 찾기 어려움 - 되돌리기 어려움 |
TinyExperiment를 체화하면 평소에 이런 질문들이 자연스럽게 떠오른다: * "이것 중에 가장 작고 성공할만한 것은 무엇인가?" * "어떤 부분만 먼저 시험해볼 수 있을까?" * "5분 안에 답을 확인할 수 있을까?" * "망쳐도 괜찮게 되돌릴 수 있을까?" 이런 질문들이 습관이 되면, 거대한 것이 앞에 와도 상황에서 멈추지 않는다. 항상 "작은 시작점이 어디일까"를 찾는 사고방식을 자연스럽게 갖게 된다. |
| Line 155: | Line 135: |
| === "추측 기반" 디버깅 === - 현상을 제대로 관찰하지 않고 추측으로 수정 - 한 번에 여러 부분을 수정 - 실제 원인과 다른 곳을 수정 === "완벽주의" 함정 === - 실험 환경을 너무 완벽하게 만들려고 함 - 실험보다 환경 구축에 더 많은 시간 소모 - 간단한 실험으로도 충분한 상황에서 과도한 준비 |
|
| Line 167: | Line 138: |
| === 개발 환경 도구 === - '''Hot reload:''' 코드 변경 시 즉시 반영 - '''REPL:''' 작은 코드 조각을 즉시 실행 - '''디버거:''' 단계별 실행과 상태 확인 - '''로깅:''' 실행 경로와 변수 값 추적 |
실험 환경을 위해 활용하는 도구들이 여러 있다. '''REPL'''은 즉석 인터랙티브 환경이다. '''console.log/print'''는 가장 기본적이면서도 강력한 실험과 디버깅 도구이다. '''단위 테스트'''는 작은 부분을 격리해서 실험하는 방법이다. '''git stash/branch'''는 실험을 안전한 별도 공간에서 하기 위한 것이다. '''샌드박스'''는 코드 온라인 실험환경을 활용하는 것이다. '''프로파일러'''는 성능 실험을 위한 도구이다. |
| Line 173: | Line 140: |
| === 테스팅 도구 === - '''Unit tests:''' 작은 단위의 동작 검증 - '''Test fixtures:''' 일관된 테스트 환경 - '''Mock objects:''' 외부 의존성 제거 - '''Test doubles:''' 복잡한 객체의 간단한 대체재 === 프로파일링 도구 === - '''성능 측정:''' 실행 시간, 메모리 사용량 - '''데이터베이스 쿼리 분석:''' 실행 계획, 인덱스 사용 - '''네트워크 모니터링:''' 요청/응답 시간, 데이터 크기 |
|
| Line 185: | Line 142: |
| TinyExperiment를 습관화하면: | |
| Line 187: | Line 143: |
| - '''불확실성이 줄어든다''' - 추측 대신 확실한 지식 축적 - '''학습이 가속화된다''' - 빠른 피드백으로 경험 축적 - '''실패 비용이 낮아진다''' - 작은 실패로 큰 실패 방지 - '''자신감이 향상된다''' - 복잡한 문제도 작은 단위로 분해하여 해결 - '''시스템 이해가 깊어진다''' - 점진적 탐색으로 전체 그림 파악 - '''팀 협업이 개선된다''' - 명확한 가설과 결과로 소통 |
TinyExperiment를 체화하면 창의성이 막히지 않고 학습 속도가 빨라지며 실패에 대한 두려움이 사라집니다. 가장 중요한 변화는 '''"탐구하는 프로그래머"'''가 되는 것입니다. |
| Line 194: | Line 145: |
| == Related Patterns == * '''Enables:''' [[BabyStep]], [[UseAbduction]], [[HereAndNowDebugging]] * '''Used by:''' [[TddIsDesignActivity]], [[PiecemealGrowthCenterFirst]] * '''Complements:''' [[ShortFeedbackCycle]], [[MicroCommit]] * '''Conflicts with:''' BigBangDevelopment, GuessBasedDebugging |
|
| Line 200: | Line 146: |
| == See Also == * [[가추법]] - 관찰에서 가설을 도출하는 사고 방식 * Kent Beck의 "Test Driven Development" - 작은 테스트로 설계하기 * Gary Klein의 "Sources of Power" - 전문가의 직관과 가설 검증 * 과학적 방법론 - 가설-실험-검증의 순환 |
== Connection to Other Patterns == * '''[[BabySteps]]''', '''[[GreenRefuge]]''' - 작은 단계와 안전한 피드백을 보장하는 기반 패턴입니다. ''기반'' * '''[[DesignThroughTest]]''', '''[[DetectiveWork]]''' - 실험적 방법론을 설계와 디버깅에 응용합니다. ''활용'' * '''[[TightLoop]]''', '''[[AtomicCommit]]''' - 실험의 주기를 짧게 유지하고 결과를 안전하게 기록합니다. ''연계'' * '''[[CognitiveMicroscope]]''' - 실험의 결과를 관찰할 때 인지 과정을 확대해서 보면 더 많이 배웁니다. ''관찰'' * '''[[WorkingFirst]]''' - 작동하는 코드를 만들기 위한 가장 강력한 도구가 TinyExperiment입니다. ''실행'' * '''[[ArtisanMind]]''' - 해커 마인드의 핵심은 끊임없는 작은 실험입니다. ''정신'' |
| Line 207: | Line 156: |
| CategoryPattern | CategoryPatternLanguage CategoryProgramming CategoryLearning CategoryAgile |
TinyExperiment
The Story 1: Complex Regex (Programming)
한 주니어 개발자가 기존 코드에 있는 복잡한 정규표현식을 수정해야 하는 상황에 처했다. /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/ 그는 정규표현식 전체를 한꺼번에 이해하려고 끙끙대며 1시간을 보냈다. 하지만 시니어는 다르게 접근했다. 그는 정규표현식을 부분별로 나누어(Detachment), 아주 작은 테스트 함수에 하나씩 넣어보았다(Experiment-able). "먼저 소문자만 체크해보자. 그다음엔 숫자만." 15분 만에 그는 복잡한 패턴의 정규표현식을 완벽하게 파악하고 안전하게 수정했다.
The Story 2: The Baker's Test (Ordinary Life)
민수와 하나는 친구의 결혼식 답례품으로 줄 '초콜릿 쿠키' 200개를 굽기로 했다.
민수의 방식 (The Big Batch): 민수는 레시피를 믿고 200개 분량의 반죽을 한꺼번에 만들었다. "레시피대로 했으니 당연히 맛있겠지?" 그는 거대한 오븐에 쿠키들을 가득 넣고 구웠다. 하지만 오븐에서 나온 쿠키들은 너무 딱딱했고 지나치게 달았다. 200개의 쿠키는 모두 쓰레기통으로 향했고, 민수는 재료비와 시간을 모두 날렸다.
하나의 방식 (The Tiny Experiment): 하나는 반죽을 한 숟가락만 떼어내어 작은 오븐에 딱 2개만 먼저 구워보았다.
분리: 전체 양이 아닌 한 조각만 먼저 굽는다.
관찰: 구워진 쿠키를 먹어보니 속이 덜 익고 설탕이 너무 많았다.
조정: 설탕을 줄이고 오븐 온도를 10도 높였다.
하나는 세 번의 '작은 실험' 끝에 완벽한 맛과 식감을 찾아냈고, 그제야 200개의 쿠키를 자신 있게 구워냈다. 큰 실패의 위험을 작은 실험으로 미리 차단한 것이다.
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: 복잡한 정규표현식 이해하기
상단 'Story 1'의 사례와 같이 복잡한 패턴을 분리하여 개별적으로 검증함으로써 신속하고 정확하게 원리를 파악한 사례.
예제 2: 성능 문제 찾기
로딩 과정을 단계별로 console.time()으로 측정하여 8초 중 5초가 걸리는 특정 데이터 처리 지점을 발견하고 최적화한 사례.
예제 3: 새로운 API 배우기
100페이지 문서를 다 읽는 대신, curl이나 Postman으로 API 하나만 직접 호출해보며 요청과 응답 구조를 2시간 만에 파악한 사례.
Common Anti-Patterns
첫 번째 안티패턴은 전체를 이해하려는 욕구다. "전체를 다 이해하고 나서 시작해야 한다"는 생각 때문에 시작도 영원히 하지 못한다. 해결책은 시작해야 할 최소한만 분리해서 시작하는 것이다.
두 번째 안티패턴은 완벽한 환경을 기다리기다. "실제로 제대로 된 테스트 인프라에서 해봐야지"라고 생각해서 환경 구축에서만 시간을 소모한다. console.log를 활용해서라도 바로 시작하는 것이 낫다.
세 번째 안티패턴은 한번에 너무 많이 바꾸기다. "이것도 고치고 저것도 고치고..."라고 접근하면 어떤 변경이 효과를 가져왔는지 모르게 된다. 한 번에 하나씩만 바꾸는 것이 원칙이다.
The Experiment Mindset
TinyExperiment를 체화하면 평소에 이런 질문들이 자연스럽게 떠오른다:
- "이것 중에 가장 작고 성공할만한 것은 무엇인가?"
- "어떤 부분만 먼저 시험해볼 수 있을까?"
- "5분 안에 답을 확인할 수 있을까?"
- "망쳐도 괜찮게 되돌릴 수 있을까?"
이런 질문들이 습관이 되면, 거대한 것이 앞에 와도 상황에서 멈추지 않는다. 항상 "작은 시작점이 어디일까"를 찾는 사고방식을 자연스럽게 갖게 된다.
Tools and Techniques
실험 환경을 위해 활용하는 도구들이 여러 있다. REPL은 즉석 인터랙티브 환경이다. console.log/print는 가장 기본적이면서도 강력한 실험과 디버깅 도구이다. 단위 테스트는 작은 부분을 격리해서 실험하는 방법이다. git stash/branch는 실험을 안전한 별도 공간에서 하기 위한 것이다. 샌드박스는 코드 온라인 실험환경을 활용하는 것이다. 프로파일러는 성능 실험을 위한 도구이다.
Resulting Context
TinyExperiment를 체화하면 창의성이 막히지 않고 학습 속도가 빨라지며 실패에 대한 두려움이 사라집니다. 가장 중요한 변화는 "탐구하는 프로그래머"가 되는 것입니다.
Connection to Other Patterns
BabySteps, GreenRefuge - 작은 단계와 안전한 피드백을 보장하는 기반 패턴입니다. 기반
DesignThroughTest, DetectiveWork - 실험적 방법론을 설계와 디버깅에 응용합니다. 활용
TightLoop, AtomicCommit - 실험의 주기를 짧게 유지하고 결과를 안전하게 기록합니다. 연계
CognitiveMicroscope - 실험의 결과를 관찰할 때 인지 과정을 확대해서 보면 더 많이 배웁니다. 관찰
WorkingFirst - 작동하는 코드를 만들기 위한 가장 강력한 도구가 TinyExperiment입니다. 실행
ArtisanMind - 해커 마인드의 핵심은 끊임없는 작은 실험입니다. 정신
CategoryPatternLanguage CategoryProgramming CategoryLearning CategoryAgile
