#acl +All:read = LanguageBuilding = ''주니어 개발자들을 위한 패턴 언어 - 문제 영역의 언어를 만들어 프로그래밍하는 방법'' <> == The Story 1: The Evolving Vocabulary (Programming) == 두 명의 개발자, 민수와 하나가 '주문 처리 시스템'의 코드를 작성하고 있다. '''민수의 코드 (The Raw Primitives):''' 민수의 코드는 `if-else`와 원시 데이터 연산으로 가득하다. "주문을 처리해요. 재고를 확인하고, 결제를 하고, 배송을 준비하고..." 하지만 민수의 코드를 읽으면 그 의도가 한눈에 들어오지 않는다. `for` 문과 배열 조작 로직 속에 비즈니스의 의미가 묻혀 있기 때문이다. 코드는 기계의 언어로 쓰여 있다. '''하나의 코드 (The Domain Story):''' 하나는 다르게 시작했다. 그녀는 먼저 비즈니스의 개념을 함수와 메서드로 정의했다. {{{ order .validate_inventory() .process_payment() .prepare_shipment() .notify_customer() }}} "우리가 새로운 단어를 만든 거예요. 문제 영역의 언어를 만드는 거죠." 하나는 Lisp 프로그래머들의 전통을 따랐다. "프로그램을 언어를 향해 작성하는 동시에 언어를 프로그램을 향해 만든다." 하나의 코드는 전문화된 어휘로 잘 쓰인 산문처럼 읽혔다. == The Story 2: The Barista's Tongue (Ordinary Life) == 민수와 하나는 커피 전문점에서 바리스타 교육을 받고 있다. '''민수의 표현 (The General Language):''' 민수는 커피 맛을 아주 단순하게만 표현한다. "이 커피는 쓰고 좀 시네요. 뜨거워요." 민수의 언어로는 이 원두와 저 원두의 미묘한 차이를 동료에게 설명하거나 더 나은 맛을 추출하기 위한 구체적인 전략을 세우기 어렵다. 그의 언어는 너무 일반적이다. '''하나의 표현 (The Specialized Vocabulary):''' 하나는 바리스타들만의 전문 용어를 익혔다. "이 원두는 '브루잉' 과정에서 '바디감'이 묵직하게 살아나네요. '산미'는 오렌지 계열이고요." 하나는 '추출 수율', '로스팅 포인트' 같은 단어들을 사용하여 맛의 세계를 정교하게 정의했다. 이 단어들은 하나가 커피라는 복잡한 문제를 다루는 '이론'이 되었고, 동료들과 한 치의 오차도 없이 소통하게 해주었다. 전문적인 어휘가 전문적인 기예를 가능하게 함을 하나는 알고 있었다. == Context == 복잡한 문제 영역을 다루는 프로그램을 작성하고 있다. 코드가 길어질수록 이해하기 어려워지고, 새로운 기능을 추가하기가 점점 더 힘들어진다. 함수와 변수를 만들지만, 전체적으로 무엇을 하는지 점점 불명확해진다. 당신은 '''[[DataAsFoundation]]'''을 통해 데이터 구조의 중요성을 알고 있고, '''[[NamesAsDesign]]'''을 통해 좋은 이름의 가치를 인식하고 있다. 하지만 개별 함수와 데이터 구조를 넘어서, 문제 영역 전체를 표현하는 더 높은 수준의 무언가가 필요하다는 느낌을 받는다. == Problem == '''범용 언어의 원시 요소만으로는 문제 영역을 자연스럽게 표현하기 어렵다.''' * Python, Java, JavaScript 등은 모든 문제를 해결하기 위해 설계되었기에, '특정 문제'를 표현하기에는 너무 일반적입니다. * 비즈니스 로직이 구현 세부사항(for, if, append) 속에 묻히면 의도가 사라집니다. * 도메인 전문가와 개발자 사이의 어휘가 다르면 의사소통 비용이 기하급수적으로 증가합니다. == Solution == '''프로그래밍을 "문제를 표현하는 언어를 만드는 과정"으로 접근하라.''' Lisp 프로그래머들이 수십 년간 해온 방식입니다: 먼저 문제를 잘 표현할 수 있는 언어를 만들고, 그 언어로 해결책을 작성하는 것입니다. === Principle 1: Programming as Theory Building === Peter Naur는 "프로그래밍은 단순히 코드를 작성하는 것이 아니라, 문제 영역에 대한 이론(Theory)을 구축하는 것"이라고 했습니다. 코드는 그 이론의 구현일 뿐이며, 진짜 가치는 문제를 이해하고 어휘를 만드는 이론에 있습니다. === Principle 2: Bottom-Up Language Construction === 아래에서 위로 언어를 만들어 올라가십시오. 1. Level 0: 원시 연산 (filter, map) 2. Level 1: 도메인 개념 정의 (`is_overdue`, `is_urgent`) 3. Level 2: 도메인 언어 합성 (`urgent_overdue_tasks`) 4. Level 3: 문제 해결 === Principle 3: Domain Vocabulary as Functions === 문제 영역의 단어를 함수로 만드십시오. 코드가 도메인 전문가가 이해할 수 있는 단어들로 구성될 때 가독성과 유지보수성이 극대화됩니다. === Principle 4: Two Languages - Problem and Solution === [[TwoWorlds]]를 기억하십시오. 문제 공간의 언어(도메인 개념)와 해결 공간의 언어(기술적 추상화)는 명확히 분리되되, 각각 풍부하게 구축되어야 합니다. == Real Examples == === Example 1: Test DSL - RSpec === RSpec은 테스트를 "User는... correct password로 인증한다"는 명세(Specification)로 읽히게 만들었습니다. 이것은 테스트를 쓰는 새로운 언어입니다. === Example 2: Rails - Convention over Configuration === Rails는 웹 개발을 위한 고유한 언어를 만들었습니다. 선언적 언어로 의도를 표현하면, 프레임워크가 복잡한 세부 사항을 처리합니다. == Common Pitfalls == === "Let's Build a Framework First!" === 문제를 충분히 이해하기 전에 거대한 프레임워크부터 만들려 하지 마십시오. 언어는 필요에 의해 점진적으로 자라나야 합니다([[OrganicGrowth]]). === "DSL for Everything!" === 모든 것에 고유 언어를 만들면 학습 곡선과 유지보수 부담이 커집니다. [[The95PercentRule]]을 기억하고 적절한 지점에서 멈추십시오. == Connection to Other Patterns == * '''[[DataAsFoundation]]''' - 언어의 명사(Nouns)를 결정하는 과정입니다. 단단한 데이터 구조가 좋은 언어의 시작입니다. ''기반'' * '''[[LivingVocabulary]]''' - 구축된 언어는 고정되지 않고, 시스템이 성장함에 따라 함께 살아 움직이며 진화해야 합니다. ''지속적 정제'' * '''[[NamesAsDesign]]''' - 언어를 만드는 구체적인 행위는 본질적으로 '이름 짓기'라는 설계 활동입니다. ''핵심 도구'' * '''[[ComplexityTaming]]''' - 잘 설계된 도메인 언어는 복잡한 비즈니스 로직을 단순한 문장으로 변환하여 복잡성을 길들입니다. ''목표'' == Signs of Success == * 코드를 읽으면 비즈니스 이야기가 들린다. * 도메인 전문가와의 대화가 코드에 그대로 반영된다. * 주석이 거의 필요 없는 자체 문서화된 코드가 된다. * 새로운 기능을 추가할 때 언어를 확장하는 것처럼 자연스럽게 추가된다. == The Ultimate Insight == '''프로그래밍 = 언어 만들기 + 그 언어로 표현하기''' 범용 프로그래밍 언어는 재료일 뿐입니다. 진짜 예술은 이 재료로 문제 영역의 언어를 만드는 것입니다. 문제를 잘 표현하는 언어를 만들면 해결책은 자연스럽게 따라옵니다. ---- CategoryPatternLanguage CategoryProgramming CategoryDesign CategoryLanguage CategoryDSL