소트웍스 앤솔러지 : 소프트웨어 기술과 혁신에 관한 에세이

69
  • date post

    22-Jul-2016
  • Category

    Documents

  • view

    228
  • download

    6

description

마틴 파울러 외 지음 | 이창신 외 옮김 | IT Leaders 시리즈 _ 007 | ISBN: 9788992939249 | 22,000원 | 2009년 02월 26일 발행 | 304쪽

Transcript of 소트웍스 앤솔러지 : 소프트웨어 기술과 혁신에 관한 에세이

소트웍스 앤솔러지

iv

• 목 차 •

01 들어가는말....................................................... 1

02 비즈니스소프트웨어의‘마지막한단계’해결하기..... 7

2.1 ‘마지막한단계’문제의원인.......................................... 8

2.2 문제이해하기.............................................................. 9

2.3 ‘마지막한단계’문제해결하기......................................11

2.4 사람..........................................................................12

2.5 자동화.......................................................................13

2.6 비기능적요구사항을위한자동화된테스트설계하기.........14

2.7 실제업무환경에대한의존성제거하기...........................16

2.8 버전없는소프트웨어...................................................18

03 악당소굴과20개의루비DSL............................. 21

3.1 악당소굴예제............................................................22

3.2 전역함수를사용하는방법............................................25

3.3 객체를사용하는방법...................................................29

클래스메서드와메서드연결하기...................................30

표현빌더...................................................................32

메서드연결하기에대해조금더.....................................36

3.4 클로저를사용하는방법................................................38

3.5 평가맥락...................................................................40

3.6 리터럴컬렉션.............................................................43

가변인자메서드.........................................................49

3.7 동적수신...................................................................50

3.8 마무리.......................................................................53

v

04 프로그래밍언어의울창한숲.............................. 55

4.1 서론..........................................................................55

4.2 표본언어...................................................................55

4.3 다양한변이들.............................................................60

패러다임....................................................................61

타입특성...................................................................62

실행방식...................................................................63

구현모델...................................................................65

4.4 언어의생명수.............................................................66

4.5 흥미롭기는하지만왜이런것을알아야하나?..................67

05 다언어프로그래밍............................................ 69

5.1 다언어프로그래밍......................................................70

5.2 그루비방식으로파일읽기...........................................71

5.3 JRuby와isBlank.........................................................73

5.4 자스켈과함수형언어..................................................75

5.5 자바테스트하기.........................................................78

5.6 다언어프로그래밍의미래............................................80

06 객체지향생활체조.......................................... 83

6.1 오늘날더나은소프트웨어를향한9단계.........................83

6.2 훈련..........................................................................84

규칙..........................................................................85

규칙1:메서드당들여쓰기한번....................................85

규칙2:else예약어금지...............................................87

규칙3:원시값과문자열의포장.....................................89

규칙4:한줄에한점만사용.........................................89

규칙5:축약금지.......................................................92

규칙6:모든엔티티를작게유지....................................92

규칙7:2개이상의인스턴스변수를가진클래스사용금지.93

규칙8:일급콜렉션사용..............................................94

vi

규칙9:게터/세터/속성사용금지...................................95

6.3 결론..........................................................................95

07 반복관리자란무엇인가?................................... 97

7.1 반복관리자란무엇인가?.............................................98

7.2 무엇이좋은반복관리자를만드는가?............................98

7.3 반복관리자역할이아닌것........................................ 100

7.4 반복관리자와팀...................................................... 101

7.5 반복관리자와고객................................................... 103

7.6 반복관리자와반복................................................... 104

7.7 반복관리자와프로젝트............................................. 105

7.8 결론....................................................................... 106

08 프로젝트의활력징후......................................107

8.1 프로젝트활력징후................................................... 108

8.2 프로젝트의활력징후vs프로젝트의건강..................... 108

8.3 프로젝트활력징후vs정보방열기............................... 109

8.4 프로젝트활력징후:범위소모.................................... 109

범위소모에대한정보방열기의예............................... 109

범위측정의단위정의하기......................................... 110

중간이정표를사용해서병목지점을밝혀내기................ 111

범위소모차트에대한자세한설명.............................. 111

8.5 프로젝트활력.......................................................... 114

품질정보방열기의예................................................ 114

버그집계차트에대한상세설명................................... 114

8.6 프로젝트활력징후:예산소모.................................... 115

예산정보방열기의예................................................ 116

예산소모차트에대한보충설명................................. 117

8.7 프로젝트활력징후:현재구현상태............................. 117

현재구현상태에대한정보방열기의예......................... 118

구현상태를정의하기................................................ 119

스토리보드와스토리카드에대한보충설명................... 119

vii

8.8 프로젝트활력징후:팀인지...................................... 120

팀인지에대한정보방열기의예.................................. 120

팀분위기차트상세설명............................................ 121

09 소비자주도계약:서비스진화패턴...................123

9.1 서비스의진화:예제................................................... 125

9.2 스키마버전관리....................................................... 127

확장점..................................................................... 128

9.3 중대한변경.............................................................. 133

스키마트론............................................................... 134

9.4 소비자주도계약....................................................... 136

제공자계약.............................................................. 137

소비자계약.............................................................. 140

소비자주도계약....................................................... 141

계약특성요약.......................................................... 143

구현........................................................................ 143

이점........................................................................ 144

소비자주도계약과SLA............................................. 145

의무........................................................................ 145

결론........................................................................ 147

10 도메인어노테이션..........................................149

10.1 어노테이션을만난도메인주도설계............................ 149

도메인에특화된메타데이터....................................... 150

자바어노테이션과닷넷어트리뷰트............................. 151

도메인어노테이션.................................................... 154

도메인어노테이션을사용하는경우............................. 155

10.2 사례연구:르로이의화물차........................................ 157

도메인모델............................................................. 158

데이터분류.............................................................. 159

대안들.................................................................... 162

Auditor에서의사용................................................... 164

viii

PermissionChecker에서의사용예................................. 165

Loader에서의사용예.................................................. 166

운항힌트................................................................ 167

대안들.................................................................... 170

PermissionChecker에서의사용................................... 171

Loader에서의사용.................................................... 172

10.3 정리....................................................................... 175

11 Ant빌드파일리팩터링하기.............................177

11.1 개론........................................................................ 177

리팩터링은무엇인가?그리고Ant는무엇인가?.............. 177

언제리팩터링을해야하나?언제그냥넘어가야하나?.... 178

Build.xml파일을리팩터링할줄아는가?...................... 179

11.2 Ant리팩터링일람표................................................. 180

Macrodef추출하기................................................... 182

타겟추출하기.......................................................... 185

선언도입하기.......................................................... 186

호출을의존성으로대체하기....................................... 188

프로퍼티를리터럴로대체하기.................................... 189

filtersfile도입하기.................................................... 190

프로퍼티파일도입하기............................................. 192

타겟을래퍼빌드파일로옮기기.................................. 193

주석대신설명쓰기.................................................. 195

배포코드를임포트하기............................................. 196

엘리먼트를antlib에옮기기........................................ 197

커다란라이브러리정의대신Fileset쓰기...................... 200

런타임프로퍼티옮기기............................................. 201

엘리먼트를식별자별로재활용하기............................. 203

프로퍼티를타겟밖으로옮기기................................... 204

Value어트리뷰트대신Location어트리뷰트쓰기........... 206

래퍼스크립트를bulid.xml파일에내려놓기.................. 207

ix

taskname어트리뷰트넣기.......................................... 209

내부타겟을강제하기................................................ 210

출력디렉터리를부모디렉터리로옮기기...................... 211

Exec대신Apply쓰기............................................... 212

CI게시자사용하기................................................... 213

타겟에구분하기쉬운이름붙여주기............................ 213

타겟의이름을명사로다시고쳐짓기............................. 215

11.3 요약....................................................................... 216

11.4 참고문헌................................................................ 216

11.5 리소스.................................................................... 217

12 한방에소프트웨어출시하기.............................219

12.1 지속적인빌드.......................................................... 219

12.2 지속적인빌드를넘어서............................................. 221

12.3 지속적인통합의전체생명주기................................... 221

바이너리관리하기.................................................... 222

12.4 체크인관문............................................................. 223

12.5 인수테스트관문...................................................... 225

12.6 배포준비하기.......................................................... 226

12.7 후속테스트단계들................................................... 229

12.8 프로세스자동화하기................................................. 230

12.9 결론....................................................................... 232

13 엔터프라이즈웹애플리케이션테스트:

애자일대폭포수.............................................233

13.1 개론........................................................................ 233

13.2 테스팅생명주기....................................................... 234

13.3 테스팅의종류........................................................... 238

단위테스팅.............................................................. 239

기능테스팅.............................................................. 239

탐색적테스팅........................................................... 241

x

통합테스팅.............................................................. 241

데이터유효성검증.................................................... 242

사용자인수테스팅(UAT).......................................... 243

성능테스팅.............................................................. 244

비기능적테스팅........................................................ 245

회귀테스팅.............................................................. 246

제품검증................................................................. 246

13.4 테스트환경.............................................................. 247

개발통합환경.......................................................... 248

시스템통합환경....................................................... 249

스테이징환경........................................................... 250

프로덕션환경........................................................... 250

13.5 이슈관리................................................................. 251

13.6 도구들..................................................................... 252

13.7 보고와측정.............................................................. 253

13.8 테스팅역할들........................................................... 254

테스트분석.............................................................. 255

테스트스크립트작성................................................. 256

테스트수행.............................................................. 256

환경관리................................................................. 257

이슈관리................................................................. 257

이슈분석................................................................. 257

13.9 참고자료.................................................................. 258

14 실용적인성능테스팅......................................259

14.1 성능테스팅이란무엇인가?......................................... 260

14.2 요구사항수집........................................................... 261

우리가측정하고있는것은무엇인가?........................... 261

수치를어떻게산정할것인가?..................................... 261

어떻게이것을일반소프트웨어개발프로세스에

접목시키는가?....................................................... 262

개발자들도성능테스팅의요구사항을갖고있지않는가?. 263

xi

성능요구사항에대한관련자를찾을수없다면?.......... 264

고객이그다지전문적이지않고우리가불가능하다고

생각하는것을원한다면?......................................... 265

업무분석가들도이러한요구사항을수집하게하면

어떨까?................................................................... 266

요약........................................................................ 266

14.3 테스트수행하기........................................................ 267

어떤테스트를다시수행할것인가?.............................. 267

언제테스트를수행해야하는가?.................................. 268

어디에서테스트를수행해야하는가?............................ 268

어떻게자그마한테스트장비의테스트결과를제품과

연계시키는가?....................................................... 270

성능테스팅에적합한데이터베이스크기는얼마인가?..... 272

써드파티인터페이스는어떻게다룰것인가?.................. 273

얼마나다양한테스트케이스가필요한가?..................... 273

응답시간과처리량에대해여러측정수단을취하는

이유는무엇인가?................................................... 274

시스템의모든기능을테스트해야하는가?..................... 275

요약........................................................................ 275

14.4 의사소통.................................................................. 276

누가알아야하는가?.................................................. 276

그렇다면여러분이해야할일은보고서를작성하는

것뿐인가?............................................................. 277

요약........................................................................ 278

14.5 프로세스.................................................................. 278

그럼어떻게그것들을연계하는가?............................... 279

여러분이뒤쳐지지않았는지어떻게확인할것인가?........ 279

식별된문제에대해조치가취해졌는지어떻게

보장할것인가?...................................................... 280

14.6 요약........................................................................ 280

참고문헌.............................................................282

xii

역 자 서 문

제마지막번역서로서<소트웍스앤솔러지>는탁월한선택이었다고생각

합니다.공역자로서책의모든내용을전할수는없지만,JRuby를통해관

심가졌던다언어프로그래밍,수년을몸담았던SOA,그리고한동안잊고

있었던코드의미학까지,현장을누비고있는현자들의목소리를듣고우

리말로옮길수있게되어큰행복이었습니다.

하지만,어려움은그행복의크기만큼따라옵니다.특히나원저자의멋

진문장과감칠맛나는호소력을제대로소화해내지못한것같아못내아

쉽습니다.최근‘스튜디오판타지아'라는블로그를통해번역의맛을새롭

게깨달아가긴하지만,이제‘제대로번역할기회’는이번제원고의검토

를통해훌륭한번역의빛을보여주신이대엽님같은열정과고민으로충

만한분들에게가기를소망합니다.

그동안도와주신모든분들께감사또감사드립니다.

-이창신

xiii

소트웍스는리팩터링으로유명한마틴파울러가최고책임과학자Chief

Scientist로일하고있는IT컨설팅기업입니다.애자일방법론을적극적으로

도입,실천하고있기로도유명하거니와CruiseControl,NUnit,Selenium등

다양한오픈소스프로젝트를통해IT커뮤니티전반에크게기여하고있기

도하지요.

<소트웍스앤솔러지>는책제목그대로소트웍스라는기업이가진탁월

한문화들를소개하고있으며이기업에근무중인핵심기술자들의다양

한통찰을에세이형식으로담아내고있습니다.

훌륭한회사에입사하여뛰어난동료들과함께좋은기업문화와업무

방식을배우며실무를익힌다는것은직장인에게는축복과같은일입니다.

하지만훌륭한회사를찾는일이쉽지는않지요.이책을통해이러한문화

를간접적으로체험하고이를독자여러분들께서자신의현재일터에,혹

은앞으로가게될일터에신중하고조심스럽게하나씩적용해보시기바랍

니다.여러분의일터를훌륭한곳으로,여러분의동료들을훌륭한개발자

로변화시키기위해함께노력하는과정에서여러분스스로도훌륭한개발

자가될수있을것이라고생각합니다.저도(아직도직장생활이어설프고

실수도많지만)훌륭한개발자가되기위해열심히노력하고있답니다.

마지막으로,이자리를빌어수차례의일정지연을감내해주신위키북스

사장님께,그리고좋은책의번역에참여할기회를주신이창신님께감사

를드립니다.

-강규영

xiv

저 자 소 개

마틴파울러Martin Fowler

소트웍스사의최고책임과학자Chief Scientist를맡은마틴파울러는리팩터

링이란개념을정립하고널리퍼뜨리는데가장큰공헌을한사람가운데

한명이며초창기부터애자일운동에참여하여그성공에기여했다.그가

블로그(martinfowler.com)에쓴글을계기로‘지속적인통합’의개념을본

격적으로탐구하게되었으며,엔터프라이즈분야에루비의도입을적극적

으로모색했던초창기인물중한명으로서도메인전용언어(DSLDomain

Specific Language)의가능성을제시하고발전시키는데공헌했다.이러한공로

를인정받아<인포메이션위크>지가선정한‘2008년세계IT업계를빛낼

15인’에선정되기도했다.저서로는<리팩터링>,<UMLDistilled>,<엔터프

라이즈애플리케이션아키텍처패턴>등이있다.

소트웍스ThoughtWorks

소트웍스는현재미국에서가장주목받고있는시스템통합및컨설팅회

사다.현재미국,캐나다,일본,중국,호주,유럽등으로활동영역을넓혀

나가고있다.이회사의고속성장에는몇가지요인이있는데우수한인

재확보와애자일방법론적용그리고오픈소스참여,이세가지를꼽을

수있다.특히인재확보는독보적이다.마틴파울러는“100명의지원자당

한명만을고용하고있을정도로경쟁률이높다”고말한바있으며실제로

이회사의지원들이펴낸책만해도수십권은된다.오픈소스프로젝트에

기여하는바도커서CruiseControl,CruiseControl.NET,CruiseControl.rb,

Selenium등수십개의프로젝트를지원한다.

xv

이창신[email protected],http://iasandcb.pe.kr

티맥스소프트WAS실과엔씨소프트오픈마루스튜디오를거쳐현재

ias(iNDIaPPLICATIONsOFTWARE)대표로독립소프트웨어개발에매

진하고있다.

강규영http://jania.pe.kr

다음커뮤니케이션,애자일컨설팅을거쳐현재엔씨소프트오픈마루스튜

디오에서일하고있다.<테스트주도개발>,<자바웹서비스로통하는서

비스지향아키텍쳐>등을공역하였으며블로그http://alankang.tistory.

com및개인위키http://jania.pe.kr을운영중이다.

최재훈http://kaistizen.net

http://kaistizen.net을운영하는SK아이미디어의게임서버개발자이다.

C++,C#,C++/CLI,MSSQL같은윈도플랫폼을주로다루며한달에한

번마이크로소프트웨어에칼럼을쓴다.지속적인통합이나리팩터링,단위

테스트같이인생을편하게해주는기술에도관심이많다.스타크래프트,

문명,토탈워와같은전략시뮬레이션을즐겨하지만폐인이될까무서워

가급적주말에만한다.특히문명과토탈워의전술및전략에대해선언

제라도열정적으로토론에임할준비가되어있다.락밴드는U2가최고라

생각하고,15년째‘배철수의음악캠프’를들은애청자이다.

역 자 소 개

xvi

정지웅http://blog.changtheweb.net

개발자로서삼성전자와오픈마루스튜디오에서근무했고,현재는신규웹

서비스와창업을준비중에있다.웹의변화가곧사회변화를견인할것이

라는믿음을바탕으로관련웹기술과웹비즈니스에관한이야기를블로그

(http://blog.changtheweb.net)에쓰고있다.번역서로‘자바개발자를위한

레일스’등이있다.

안영회[email protected]

(주)아이티와이즈컨설팅SE그룹에서컨설턴트로일하고있으며,국내대

규모프로젝트에서9년간방법론,모델링,아키텍처수립,프로젝트관리

등의다양한역할을수행하고있다.현재는한화S&C에서전사표준개발

프레임워크구축프로젝트관리자로일하고있으며한국스프링사용자모임

(KSUG)의대표를맡고있기도하다.본인의블로그인Younghoe.info를통

해현장경험나누기를즐기고있으니교류를원하는분은언제라도환영

한다.

이대엽

(주)넷스루에서근무하고있으며,제품개발및유지보수업무를담당하고

있다.대학교때시작한소프트웨어개발을천직이라생각하고,어떻게하

면오랫동안재미있게할수있을까생각하곤한다.

1

소트웍스는맞춤형애플리케이션과난센스없는컨설팅을선사하는열정

적이고주도적이며지적인사람들이모인곳이다.소트워커(ThoughtWorker,

소트웍스에서일하는사람)에게회사의어떤점이가장좋으냐고물으면,

아마도거개가회사에서만나고함께일하고그래서배우게되는다른소

트워커라고할것이다.기인geek,관리자,분석가,프로그래머,테스터,그

리고운영자들이다양한문화와인종,그리고교육적배경으로뒤섞여있

는곳이소트웍스이다.이배경과시각의다양성이발상에대한공동의열

정과어울려무척활발한토론을낳게한다.

명석하고자부심강한약1000여명이거의위계질서없이투명성에대한

열광적인몰두로성공적인회사를이루어냈다.물론,우리가내리는성공

에대한소트웍스의정의가전형적인것은아니다.하지만성공은반드시

고객만족을달성해야하고,우리산업에영향을미치며또한우리사회에

도그래야한다.목표는높다.

많은소트워커의목소리가블로고스피어에서,컨퍼런스에서,웹에서,그

리고서가에서울려퍼진다.실은,우리가해온것에대해다음에향상할

수있는방법을모색하기위해무엇을어떻게했는지가차없이비판하는

데에우리는남달랐지않았나싶다.참만족시키기어려운집단인셈이다.

한번어떤것을배우면,남들에게도전하고싶은것이다.

01들어가는 말

The Thoughtworks Anthology

2

우리가쌓아올린고민의흔적은다양한도메인,기술,그리고플랫폼선

택에걸쳐수많은프로젝트로부터생겼다.일상에서조차뭘할지아주많

이생각하긴하지만,우리의생각은사람들을위해많은소프트웨어를만

드는현실세계를바탕으로하고있다.우리역할의순수함이소프트웨어

개발에오롯이몰두할수있게한셈이다.

보통컨설턴트를새로운HR정책을논의하는회의에참석하라고고용하

지는않기때문에,우리의일상은대부분의IT직종보다는소프트웨어산

출에집중하며따라서실용과엄격의조화로귀결된다.

이에세이는소트워커가다루는매우다양한IT문제의총화를훌륭하게

포착한다.단순히더좋은소프트웨어를제작하는몇가지방안을제시하

는것이상을전하려고애쓴다.조직이떠안는IT노력으로부터진정한비

즈니스가치를깨닫는문제와씨름하니말이다.

로이Roy의오프닝에세이는제품환경으로시스템을끌어올리는‘마지

막한걸음’에있어변화를촉구하는어조로시작한다.그의프로그램은폭

넓고야심차다.요구사항수집과코딩그자체만큼이나운영과배포사안

을개발과정의핵심으로만들자는것,다름아닌그것이다.그저코드를

QA부서에넘겨서제품화와배포를맡은운영팀에슬쩍넘길준비가된

것이성공은아님을상기하면,소프트웨어를만드는팀은그소프트웨어

를끝까지보지않은이상‘일을다했다’가아님을안다.그리고로이의주

장은완결과성공에대한꽤명민한재정의들을훌쩍뛰어넘는다.그는언

제,어떻게이해당사자들이관여하게되는지를다시생각해볼것을요구

한다.코딩과정을위해툴을향상하는데에들어간모든지혜(예를들어

자동화된빌드와스크립트기반테스팅,또는리팩터링을위한툴)가그

‘마지막한걸음’문제의상당부분을푸는데에응용될수있다.

에세이를읽어나가며,그들의문제의식에대한답이끊임없이이어짐

을알수있다.예를들어제임스James는성능테스트라는습관적으로넘어

가기쉽고,흔히프로젝트막판까지미루어지는영역을이야기하는데,많

3

01 들어가는 말

은설계상의사결정이이미내려졌고코딩도다된상태라,만들어잘돌아

가는비즈니스기능을망가뜨리지않고성능문제를풀기위해그걸뒤엎

는일은마치열역학제2법칙의위반을기도하는것같다.제임스는적절

히실용적인방식을취하는데,단순히성능요구사항을처음부터요구해야

한다(누가이에토를달수있는가?)라고주장하는것이아니라이해당사

자들로부터유용한요구사항을얻어내는방법을논한다.그저“테스트를

일찍해!”가아니라실질적으로이들테스트를언제어떻게수행할수있을

지를논의한다.

줄리안Julian은앤트Ant리팩터링을맡아다수의표준리팩터링방안을분

류하여각각의명쾌한예제를제시한다.그의에세이는날로성장하고진

화하는빌드스크립트를다루는누구에게라도빼어난참고가될것이다.

데이브Dave의에세이는로이의오프닝과멋진북엔드(옮긴이주-세워놓은책

들이쓰러지지않도록받치는것)짝을이루며원클릭배포를둘러싼개념적프

레임워크의요점을말하고있다.그는몇가지큰이슈를다루는데,예를

들어오늘날의다양한소프트웨어배포환경에서생성·통합되는크고거

추장스러운바이너리를관리하는것이다.효율적인비즈니스소프트웨어

개발을꾀하는모든기법은결국배포도구의세계로모이게되어있다.데

이브의에세이는그런프로그램을진일보시키고있다.

스텔리오Stelio의에세이는프로젝트의건강성을가져다주는소통의방

법을주제로삼고있다.그는객관과주관양쪽의측정수단을거론하며일

상업무에있어똑같은대시보드dashboard를모든프로젝트참여자가보도

록제시하는효율적인방안을화제로삼고있다.그는프로젝트의건강신

호의가시성을가능한한많은관련자들에게전하고있다.이는또다른개

념,즉일종의프로젝트인간학으로이어진다.티파니Tiffany의에세이는마

치마가렛미드Margaret Mead의사모아Samoa에서의발견에대한보고서처럼

읽힌다.그녀는반복iteration관리자라는완전히새로운종류의프로젝트팀

원과조우했고,우리에게그역할이어떻게팀웍으로녹아드는지를설파한

다.그녀는팀을더욱효율적으로운영하기위해팀구성을조금다르게하

The Thoughtworks Anthology

4

는방법을제안한다.그리고이를통해일을돕는역할이우리에게있음을

밝힌다.제프Jeff의‘9가지최고규칙’에세이는프로그래밍의도에대한근

본을말하는대가를떠올리게한다.그규칙은간단하고우아하지만,특히

너무도많은버릇을‘잊어버리기’를요구하기때문에쉽게따르기어렵다.

레베카Rebecca의에세이는다양한언어의분류에대한매력적인읽을거리

로시작하며‘언어전쟁’의문제를강하게파고드는느낌이다.처음읽다

보면,린네(Linnaeus,유명한식물분류학자)가정원을거닐다가어떤식물의

특성을관찰하고는앞으로마주칠임의의식물을분류하기위한프레임워

크로일반화하는상상이떠오른다.레베카는훌륭한기초를닦아놓는다.

하지만반전은끝에있다.이는그저현재유행인몇몇언어들에대한조사

과정이아니라,대신세상에존재하는다양한도구의시연이며특정‘자바

대.NET’언어논쟁은끝나지않는대화의최신반복일뿐이라는것이다.

그러나중요한것은어떤종류의문제를풀려고하는지를알고그문제를

잡기위해어떤종류의도구를쓸수있는지를아는것이다.그녀는마치

엉망인작업장에들어와서는망치와스패너를가려내고서랍에라벨을붙

여정리하여서랍안의물건은어디에쓸모가있는지를나타내는듯하다.

나머지에세이들은훨씬기술적으로초점이맞춰진반면내가동료라고

부르게된이들의다양한재능이더더욱빛을발한다.이언Ian은SOA계약

에대한사고방식에있어포괄적인접근,그것도소비자consumer주도가아

닌고객customer주도로펼쳐보인다.그의에세이는영원한숙제인공유서

비스의구축과진화,그것도비즈니스의요구에맞게변화하는시간의흐

름을타야하고또그러면서도해당서비스의기존고객을절름발이로만

들지않는것에도전한다.그리고에릭Erik은유사한문제를고민하는데,잘

설계된시스템에서기간계층infrastructure layer으로부터도메인모델을분리

함할때기간계층이도메인모델이제공하는메타데이터metadata를써야

한다는점이다.특정도메인요소를표현하기위한클래스의선택과같은

일부암묵적메타데이터는자체조달이가능하지만,유효성검증validation과

같이진정풍부한정보는주어지지않는다.자바나C#과같은현대적언어

5

01 들어가는 말

는어노테이션이나어트리뷰트의형식으로더많은메타데이터를제공하

며,에릭은케이스스터디를통해이런기능들을어떻게활용할지를모색

한다.또,마틴Martin의못된과대망상을향한흥겨운도메인전용언어DSL

유람은아주오래전내가C를익히던시절을떠올리게한다.나는커닝헌

Kerninghan과리치Ritchie를보고공부했는데,그들의문자열복사함수의몇

번안되는반복이가져다주는간결함과우아함에매료되어나도뒤쫓아

프로그래밍에경주했었다.

이에세이집에서일맥생통하는부분을여기저기볼수있다.여기에세

이들은IT문제의에코시스템을아우르며명확하면서도경이로운방법으

로엮여있다.문제풀이에있어주제의폭과접근의다양함은발상을낳는

환경의건강함을반영하며저자가속한모든조직에존재한다.이에세이

에서그러한단면을확인할때마다우리가진정그밖에무엇을할수있을

지더욱궁금하게한다.

마이크아귈라(Mike Aguilar,소트웍스부사장)

2008년2월15일

6

7

테스트주도개발TDD,지속적인통합CI,짝프로그래밍,리팩터링등과같

은애자일실천법으로인해우리는품질이높은소프트웨어를단기간에출

시할수있게되었다.덕분에소프트웨어는출시직후뿐아니라,진화하는

요구사항에맞추어지속적으로수정되는동안에도여전히견고하게잘작

동하게되었다.

하지만모든도전과제가해결된것은아니다.특히소프트웨어개발의

‘마지막한단계’는풀리지않았다.‘마지막한단계’란소프트웨어프로세

스의일부로서,기능적요구사항은모두만족하지만아직실전에투입되어

비즈니스가치를만들어내지는않고있는단계를일컫는다.

소프트웨어개발자들이‘마지막한단계’를쉽게생각하는경향이있는

데,일정압박을받는상황에서특히그러하다.하지만‘마지막한단계’는

비즈니스소프트웨어출시에있어서가장중요한단계이다.

02비즈니스 소프트웨어의

‘마지막 한 단계’ 해결하기

■ 로이 싱헴 (설립자 겸 회장), 마이클 로빈슨 (기술 총괄) 지음

■ 강규영 옮김

The Thoughtworks Anthology

8

2.1 ‘마지막한단계’문제의원인

‘마지막한단계’문제는사업이성장한후에야발생하는경향이있다.획

기적인비즈니스모델을찾아내서이제막창업을한상황이라면시스템도

없고,거래데이터도없고,고객이나수익도없다.비즈니스의가능성을타

진해보려면시스템을간단하게나마개발해야할것이다.이단계에서어느

정도가능성을보았다면,이제기능이나성능을향상시키기위해조금더

투자를하게된다.하지만재정이충분치않을것이기때문에가능하면시

스템을빨리출시해야할것이다.

지금까지의시나리오는빠른출시라는측면에서볼때아주이상적인상

황에가깝다.‘마지막한단계’문제도존재하지않는다.소프트웨어가비

즈니스요구사항을모두만족시키기만하면바로출시를할수있기때문

이다.

이제시간이흐르고회사가성공적으로운영되어수익을내기시작한다.

그간많은고객을유치하였고,2년치거래데이터가쌓인상황이다.그리

고고객관계관리CRM시스템과회계소프트웨어패키지를구매하여,앞서

개발한핵심비즈니스시스템과통합시켜놓았다.

핵심비즈니스시스템에도몇가지기능이추가되었다.하지만최근생

겨난새로운비즈니스기회를잡기위해서는기존과는다른시스템을개발

하고이를기존시스템과통합해야만한다.

이제일이복잡해지기시작했다.두번째비즈니스시스템은기존시스

템및데이터와연동된상태로잘작동해야하기때문에가동전에충분한

테스트를거쳐야만한다.

시간이흐르고이제회사가크게성장했다.사업은다각화되었고,여러

나라에지사를두었으며,많은거래선과시장을거느린상장기업이되었

다.수만명의직원,수백만명의회원이존재한다.수입은높고지속적인

증가추세에있으며투자자들과분석가들에의해면밀히분석되고있다.최

9

02 비즈니스 소프트웨어의 ‘마지막 한 단계’ 해결하기

초의비즈니스시스템은이제8년간의거래데이터를축적하고있으며다

른12개의시스템과연동되어있다.수년간에걸쳐수정되고확장되었으나

이제더이상의거래량증가를수용하기힘든상황이다.변화하는요구사

항에맞추어수정되기도쉽지않다.회사는이오래된시스템을완전히새

로개발된신규시스템으로교체하길원한다.

이신규소프트웨어는‘마지막한단계’문제를가지고있다.

2.2 문제이해하기

기업은소프트웨어가창출할비즈니스가치를보고투자를한다.하지만

새소프트웨어는간혹엄청난재정적재난의상징으로비춰지기도하는데,

다음중한가지상황이라도발생하는경우에특히그렇다.

새소프트웨어가기업이필요로하는만큼의사용자규모나트랜잭•

션을수용하지못한다.

새소프트웨어가기존데이터베이스를망가트린다.•

새소프트웨어가예측불가능하게오작동하거나,기존시스템까지•

불안정하게만든다.

새소프트웨어가기업의민감한정보를신뢰할수없는업체혹은•

개인에게노출시킨다.

새소프트웨어가악의적인사용자가시도하는허가되지않은행위•

를허용한다.

새소프트웨어로인해야기될수있는위험비용이소프트웨어가제공하게

될가치보다클때가있다.이로인해기업의규모가커질수록새소프트웨

어도입에더신중해지는것이다.결국오래된기술과시스템이새것으로

교체되지않고계속수정/변형되어쓰이게되며,이런식으로변형된기존

시스템은다른시스템과통합성이낮기때문에,다시금새시스템도입에

따른비용과위험성을더높이는원인이된다.이악순환은계속반복된다.

The Thoughtworks Anthology

10

이악순환은세월의흐름에따른비즈니스요구사항변화에적응하는데

드는비용과난점을점진적으로높여간다.이러한기술적부담이없는신

생기업들은오래된라이벌기업의시장을파고들수있겠지만,그들도언

젠가는같은문제에봉착하게될것이다.

애자일소프트웨어개발방법론은변화하는요구사항에따른빠른적응

을약속한다.정보기술,유비쿼터스인터넷,세계화등으로인해비즈니스

모델혁신이극도로가속화된현시점에서애자일방법론의약속은매우

매력적이다.

하지만비즈니스스폰서가관심을갖는부분은소프트웨어개발의양쪽

끝,즉‘개발예산승인’과‘작동하는소프트웨어’뿐이다.그사이에서일어

나는일은관심의대상이아니다.따라서비즈니스스폰서에게있어서애

자일방법론이란오로지소프트웨어가얼마나빨리업무에적용될수있는

지를보여줄때에만의미가있는것이다.

크고오래된기업의시스템이라면새소프트웨어를설치,테스트,안정

화하기위해서너달,혹은그이상을소요할수도있다.그렇게해야만새

시스템의안전성을확신할수있기때문이다.이러한환경에서라면애자일

방법론이1~2주만에새기능을만들어내더라도,릴리즈주기상에서의타

이밍이나기능동결freeze을취급하는방식등에따라어쩌면반년이나지

난후에야실제로사용할수있게될지도모른다.

이상황을애자일방법론의성공사례로보고자하는유혹이있을수있

다.두주단위릴리즈!그후반년간의지연은다른사람의문제라고보는

것이다.하지만이러한관점은옳지않다.

11

02 비즈니스 소프트웨어의 ‘마지막 한 단계’ 해결하기

2.3 ‘마지막한단계’문제해결하기

오늘날의애자일소프트웨어개발프로젝트는대략다음과같은형태로진

행된다.

비즈니스스폰서가욕구1. needs를파악한다.

비즈니스스폰서가프로젝트예산을승인한다.2.

개발부서가사용자스토리를나열한다.3.

개발부서가사용자스토리를구현한다.4.

비즈니스스폰서가구현된사용자스토리를검수한다.5.

개발부서가완성된코드를넘긴다.6.

성공적인프로젝트는3,4,5번단계를부드럽고효과적으로지나서일정

에맞게6번단계에다다른다.프로젝트결과로얻어지는것은모든인수

테스트를통과하는한조각의소프트웨어이다.이제이소프트웨어조각은

팀외부로넘겨지는데별문제가없다면몇달후에비즈니스스폰서가실

제로사용하게될것이다.

그러나만에하나넘겨진소프트웨어에문제가생기면소프트웨어는다

시팀내부로돌아올것이다.훌륭한개발자들은잘설계된테스트,짝프

로그래밍등과같은다양한기법을사용하여이런상황을막고자노력하

고,많은경우성공에이른다.하지만위와같은형태로진행되는프로젝트

라면소프트웨어에아무런결함이없다하더라도‘마지막한단계’프로세

스때문에여전히비즈니스가치를전달함에있어서커다란지연이생기게

된다.

‘마지막한단계’문제는애자일소프트웨어개발방법론이소프트웨어

납품공정의처음부터끝까지를모두다루지않는한결코해결되지않을

것이다.즉,소프트웨어를업무현장에적용하기위해필요한사항들을프

로세스의모든단계에서적절히다루어야한다.이제부터이를위해사람

이할일과자동화를통해할일을다시검토해보도록하자.

The Thoughtworks Anthology

12

2.4 사람

애자일소프트웨어운동이가장크게기여한바는소프트웨어개발이근본

적으로는사회적행위라는인식을알린것이다.사람들사이의대화를개

선하면소프트웨어도개선될것이다.애자일개발방법론을도입하는과정

에드는노력중많은부분은기존조직의사회적구조를분해하고이를더

욱효과적인패턴과실천법으로교체하는일에쓰인다.

하지만지금까지는소프트웨어개발자와소프트웨어사용자사이의대

화에만거의모든방점이찍혀있었다.이들사이의대화는좋은요구사항

을도출하고비즈니스목표에대한양자간의공통된이해를이끌어내는역

할을해왔다.하지만비기능적요구사항은어떠한가?누가이러한요구사

항에관여되어있으며,그들은언제어떤방식으로대화에참여하는가?보

통은이러한질문들에대한대답없이프로젝트가진행된다.

완성된소프트웨어를‘팀밖으로던지는단계’를제거하기가장쉬운방

법은비기능적요구사항이나여러분야에걸쳐있는요구사항cross-cutting

requirements에관여하는모든이해당사자를소프트웨어개발이라는사회적

행위내에포함시키는것이다.그들이대화에초기부터,그리고자주참여

하도록하라.이렇게하기위해서는역시나기존의사회적구조를분해하

고이를더효과적인패턴과실천법으로교체해야할것이다

예를들어운영부서는소프트웨어를설치하고설정하는일을맡는다.그

들은또한시스템이업무환경에서잘작동하는지모니터링도해야한다.

뭔가잘못되었을때이를복구하기위해서는복구절차에대한문서도필

요할것이다.메모리,디스크,네트워크,전력,냉각설비등물리적하부구

조에대한요구사항에따른준비도필요하다.그래야소프트웨어를초기에

문제없이설치할수있고,시스템이커져감에따라확장도할수있기때

문이다.

지원부서혹은고객담당부서는시스템에오류가생겼을때실제로쓸모

13

02 비즈니스 소프트웨어의 ‘마지막 한 단계’ 해결하기

가있는오류보고서를볼수있어야하고효과적인장애진단절차도알아

야한다.또그들은사용자가겪게될간단한시스템문제들을해결할수

있어야하고,무엇이심각한결함인지또그것을어떤식으로상부에보고

해야하는지도알아야한다.

몇몇업계는법무지원부서가필요하기도하다.예를들어이들은시스템

이개인정보보호정책이나데이터보호정책등의법적요구를잘구현하였

는지확인할필요가있다.또한시스템이필수감사항목들을잘따르고있

는지도확인해야한다.

이러한이해당사자들은모두실제적이고정당한비즈니스요구사항을

가지고있고이러한요구사항들은모두만족되어야한다.이러한요구사항

들을빨리챙기면챙길수록소프트웨어가실제업무에적용되기까지소요

되는시간이단축될것이다.이해당사자들을대화에참여시키면이들의요

구사항들은더빠르게,그리고더효과적으로처리될수있다.

2.5 자동화

현재는대부분의‘마지막한단계’절차가수동으로,비효율적으로,오류가

발생하기쉬운방식으로처리된다.따라서시간이오래걸리고비용이많

이든다. ‘마지막한단계’에소요되는시간을크게단축하려면자동화될

수있는모든것을최대한자동화할필요가있다.이를위해서는소프트웨

어빌드방식에도변화를주어야한다.

일단개발프로세스를자동화하기시작하면,곧자동화자체에도소프트

웨어개발과동일한문제들이있다는점을알게될것이다.빌드스크립트

도테스트및디버깅이필요하다.테스트가깨지기도하고이를갱신도해

야한다.그러나자동화에관련된산출물들은종종‘소프트웨어가아닌것’

으로인식되기때문에소프트웨어를다루면서어렵게얻어낸각종지혜들

을써먹지않고지나치곤한다.

The Thoughtworks Anthology

14

빌드스크립트,테스트스크립트,설치스크립트,그리고설정파일들은

모두최종시스템에기여하는산출물들이고따라서동등하게취급되어야

한다.‘개발관련파일’과‘실제적용될파일’따위의구별이나동등하지못

한취급은있어서는안된다.이둘모두프로젝트버전관리대상에포함

되어야한다.이둘모두명확하게정리되고일관성있게관리되어야한다.

이둘모두단순성을유지하기위해리팩터링을해주어야하고중복되는

기능을뽑아내서재사용해야한다.마지막으로운영체제,애플리케이션서

버,데이터베이스,방화벽,스토리지등모든시스템구성요소는효율적인

테스트자동화를지원해야한다.전체적인시스템구조는자동화된테스트

라는관점을염두에두고설계,정의되어야한다.

낡은시스템들이얽혀있는환경이라면모든통합지점이자동화된테스

트를지원하게하는것이현실적으로힘들수있다.이런상황이라도아무

것도안하는것보다는낡은시스템에대한모의객체mock objects라도만들

어두는것이좋다.새로개발되는시스템이라면노출되어있는모든통합

지점에대해테스트자동화를위한기반을마련해놓아야한다(테스트시

작,테스트종료,테스트결과기록,테스트결과읽어내기등).

새소프트웨어를업무에적용하기전에검증하기위한유용한테크닉중

하나는현재이미적용되어있는시스템에서특정기간동안발생했던실

제트랜잭션들을그대로새시스템에‘재생’시키고,양쪽시스템의결과가

동일한지비교해보는것이다.새시스템은효과적인‘재생’테스트를위한

기반을제공하는것이좋다.

2.6 비기능적요구사항을위한자동화된테스트 설계하기

비기능적요구사항은시스템의기능명세서의일부가아닐수도있으나,

의심의여지없이중요하고합당한요구사항이며종종중대한비즈니스가

치를갖는다.‘마지막한순간’테스트의상당시간이시스템응답시간,트

15

02 비즈니스 소프트웨어의 ‘마지막 한 단계’ 해결하기

랜잭션처리량,가용성,보안등과같은비기능적요구사항을검증하느라

소비된다.그러나너무도많은프로젝트에서비기능적요구사항에대한테

스트는소프트웨어개발이‘끝난후에’수행된다.

비기능적요구사항에대한테스트를수행하기에적절한순간은코딩을

시작하기전이다.성능이나자원활용에대한요구사항은프로젝트초기에

미리파악및분석되어야하고,개발이진행됨에따라코드와이들요구사

항사이의맵핑을위한모델이만들어져야한다.

전형적인성능테스트는다음과같이진행된다.프로젝트시작시점에서

“이기능에대한시스템응답시간은5초이하여야한다”는식으로요구사

항을명시한다.이제개발자들이소프트웨어를만든다.개발이끝나면운

영부서에서소프트웨어를배치전단계시스템에설치한다.그러면테스터

들이기능을실행하고시계를보며시간이얼마나걸리는지측정한다.

이방식에는두가지문제점이있다.첫째,만약실행을해봤더니5초가

아니라5분이걸리면어떻게될까?업무에적용하기로정한날짜이내로

해당소프트웨어를완전히다시작성해야한다.둘째,만약개발부서가이

문제를피하기위해자동화된성능테스트를작성하여이를지속적통합을

위한테스트모음suite에넣어두었다고하더라도,테스트결과는오로지테

스트환경의응답시간과업무환경의응답시간이일치한다고가정할수

있을때에만유의미할것이다.많은경우이러한가정은옳지않다.

그대신,“업무환경의하드웨어는초당500번의디스크임의접근을처

리할수있으므로이기능을처리할때디스크임의접근은2,500번이하로

만수행되어야한다”는식으로말할수있을것이다.대부분의운영체제는

상세한성능카운터를제공하는데,이수치들은대부분자동화된테스트에

유용하게써먹을수있다.카운터기반성능테스트가응답시간기반성능

테스트에비해갖는장점은테스트가테스트환경과독립적으로수행될수

있다는점이다.또카운터기반성능테스트는훨씬더상세한수준으로작

성할수있다.결과적으로개발부서는소프트웨어를개발하는도중에도성

The Thoughtworks Anthology

16

능이나자원문제에대해더잘알수있게된다.게다가바로이시점이성

능이나자원문제를가장적은비용으로해결할수있는때이기도하다.

성능및자원활용에대한모델은전체프로세스의일부이다.따라서버

전관리도되어야하고적절히유지보수도되어야하며,서로다른다양한

환경에맞게세부조율도필요하다.적절한카운터자료를수집하고내보내

기위한라이브러리도제공되어야한다.그래야개발자들이이를통해자

신들이작성하는코드와성능테스트를통합하는작업을쉽고효과적으로

할수있게된다.모델과테스트가잘관리되기만하면,배치전최종테스

트를빠르게마칠수있을것이고,더이상유쾌하지못한놀라움을대면할

일도없어진다.

2.7 실제업무환경에대한의존성제거하기

지속적통합continuous integration과테스트주도설계test-driven design는고품질

의소프트웨어를빠르게개발하고자할때매우유용한도구이다.이도구

들이제공하는빠른피드백으로인해개발부서는오류를빠르고값싸게잡

아낼수있고,매순간시스템이예상대로작동하고있다는확신을가지고

작업을할수있게된다.

불행히도시스템을업무환경에배치하는단계에서는지속적통합이나

테스트주도설계의장점을거의취하지못한다.이상적으로는,완전히통

합이끝난업무환경에대한사용자테스트및운영테스트를손쉽게작성

할수있어야하고,지속적통합시스템은이러한포괄적인테스트모음을

충분히빠르게실행할수있어야한다.하지만이와같은이상적환경을갖

추는것을방해하는몇가지요인들이있다.

첫째,실제업무환경은종종크고복잡하며비용이많이들고세팅하기

가어렵다.둘째,프로세스의처음부터끝까지를모두포괄하는테스트는

설계하고작성하고검증하기가쉽지않다.셋째,환경이세팅되고테스트

17

02 비즈니스 소프트웨어의 ‘마지막 한 단계’ 해결하기

가모두갖춰졌다하더라도이러한테스트는일반적으로매우느리게수행

된다.보통한번수행하면며칠이걸리곤한다.

세번째문제에대한해결책은테스트를병렬로수행하는것이다.만약

한번에열개의테스트를수행할수있다면테스트를열배나자주수행할

수있게되는것이다.하지만첫번째문제(크고복잡하고비용이많이드

는문제)로인해병렬수행은뭔가현실적이지않다.개발부서가테스트를

자주실행할수있게하기위해서실제업무환경과동일한시스템을열벌

이나마련하고유지하는것은상상할수도없을만큼의사치이다.

하지만만약이비용을줄일수있다면어떨까?업무환경과동일한환

경을순식간에세팅할수있다면?그리고이시설을여러개발프로젝트에

서공유해서쓴다면?그렇게만된다면대량의테스트를병렬로수행한다

는개념이훨씬더실현가능해진다.최근가상화기술이비약적으로발전

하고있는데이를이용하면가능하다.저렴한하드웨어들에가상의환경을

빠르게세팅할수있게해주는제품들이이미존재한다.

물론개발중인시스템이가상의테스트환경과실제업무환경에서다

르게작동한다면이접근법은큰결함을갖게될것이다.자동화된테스트

의결과를신뢰하기도힘들것이고,결과적으로개발및테스트의효율향

상에미치는영향도미미할것이다.

모든비즈니스소프트웨어는배포환경에대한의존성을지니며배포환

경에대한이런저런가정을내포하고있다.이러한가정과의존성은명시

적을수도있고암묵적일수도있다.배포환경에대한암묵적인가정과의

존성이많으면많을수록프로세스전체를포괄하는자동화된테스트를작

성하는일은점점더힘들어진다.

결국,빠르게실행되는포괄적테스트의장점을취하고자한다면,시스

템설계자는반드시암묵적가정과의존성을파악하여이들을명시적이고

테스트가능하게만들어두어야한다.그리고환경에가정과의존성의양을

체계적으로줄여나가는것이좋다.

The Thoughtworks Anthology

18

포괄적이고자동화된시스템테스트모음이일단실현가능한옵션이되

기만하면,이테스트를작성하기위해비용을들이는일도훨씬더매력적

으로느껴질것이다.궁극적인목표는개발부서와운영부서가언제라도시

스템을배포할수있다는확신을갖게해주는지속적통합시스템을갖추

는것이다.

2.8 버전없는소프트웨어

애자일방법론의가치는프로젝트에소요되는전체적인시간을줄여주고,

비즈니스요구사항이떠오른시점에서부터이것이구현되어업무에적용

되는시점사이의비용을줄여주는데에있다.이가치를극단으로끌어올

려보면다음과같은상황을상상해볼수있을것이다.즉,개별기능들이

제안되고,제안된각기능은구현이끝나자마자바로업무에적용된다.

아주작고단순한웹애플리케이션이라면이미이와같은형태로운영이

될것이다.한시간이내에새기능을추가하여배포하기도한다.이런경

우라면개발자는더이상소프트웨어를버전단위로릴리즈할필요가없게

된다.대신,기능단위릴리즈가있을뿐이다.

하지만크고복잡한,그리고오래되고변화에민감한기존시스템이있

는환경에서라면버전없는소프트웨어란잘해봐야저멀리있는원대한

꿈에불과하다.‘마지막한단계’로인한부담때문에여러기능들을하나

의거대한번들로묶어서가끔씩버전단위릴리즈를하게되는데,이러한

현상은가까운미래에도지속될것이다.

하지만현재와같은방식은비용이많이들고지속가능하지가않다.낭

비되는노동력에따른직접비용과잃어버린기회비용에따른간접비용이

모두막대하다.이는소프트웨어개발의총비용에서상당한부분을차지하

고있다.

19

02 비즈니스 소프트웨어의 ‘마지막 한 단계’ 해결하기

어쩌면우린앞으로도버전없는소프트웨어라는비전을달성할수없을

지도모른다.그러나확실히지금보다는훨씬잘할수있을것이다.쉽고

즉각적으로향상시킬수있는부분들이많이있다.똑똑하고동기부여가

된사람들이살펴보면더많은개선점들을찾을수있을것임을의심치않

는다.‘마지막한단계’문제의해법이하루아침에발견되지는않을것이

다.하지만한걸음씩,소프트웨어개발의모든단계에속한사람들이집중

적으로그리고적극적으로협동한다면,결국에는해낼수있을것이다.

20

21

최근들어루비가인기를얻고있는큰이유하나로내장된도메인전용언

어(internal domain-specific language–이하내장DSL)를작성하기에적합한기

반을제공한다는점을꼽을수있다.내장DSL이란호스트언어의문법중

에서적절하게선택된일부분만을사용하는도메인전용언어를말한다.

최근루비로내장DSL을만드는방식이부활하고있다.1

내장DSL은오래된아이디어인데,특히리스프LISP프로그래머들사이에

인기가많다.많은리스프프로그래머들은루비가이분야에새로기여한

바가아무것도없다고말하곤한다.그래도루비의주목할만한특징이하

나있다면,내장DSL을만들때사용할수있는매우다양한기법들이제

공된다는점이다.리스프도몇몇훌륭한메커니즘을제공하기는하지만루

비에비하면그수가적은편이다.

1 (옮긴이)여기에서말하는도메인이란프로그램이사용될분야를말한다.예를들어의료용소프트웨어는의료

분야,즉의료도메인에서사용된다.따라서도메인전용언어란해당분야에서사용하기위해특화된언어라는

뜻이다.

03악당 소굴과 20개의 루비 DSL

■ 마틴 파울러 (최고 책임 과학자) 지음

■ 강규영 옮김

The Thoughtworks Anthology

22

이장의목적은하나의예제를중심으로여러기법들을살펴보고어떤

일들이가능한지에대하여감을잡도록하는것이다.이장을읽고나면여

러방법중여러분의상황에잘맞는방법이무엇인지판단할수있게될

것이다.

3.1 악당소굴예제

이장의나머지부분에서는하나의간단한예제를사용하여여러가지다

양한기법들을보여줄것이다.예제는일반적이면서도흥미로운구성문제

problem of configuration이다.X라는장비가필요한데그러려면X와호환되는

Y가있어야한다는식의상황을많이겪어보았을것이다.컴퓨터를살때,

소프트웨어를설치할때,그밖의좀더일상적인일등온갖상황에서구

성문제를접하게된다.

세계정복을꿈꾸는과대망상증악당에게복잡한장비를공급하는일만

전문으로하는기업이있다고상상해보자.이런종류의악당이나오는영

화가그토록많은것을보면이기업의잠재시장은거대하다.게다가매력

적인비밀요원이악당의소굴에잠입하여장비를계속폭파시키는바람에

시장상황은점점더좋아지고있다.

자,내가제시하고자하는예제는이과대망상증악당이소굴에구비해

놓은각종장비들에대한구성규칙을표현하는것이다.예제에서는아이

템과자원,이렇게두종류의사물을다루게된다.아이템은카메라나염산

용기같은구체적사물이다.자원은전력과같이어떠한양을뜻한다.

이예제에는두종류의자원이나온다.하나는전력electricity이고하나는

산acid이다.자원은여러가지서로다른종류의속성을가질수있고나중

에이속성값을맞춰봐야하는경우가있다고가정하자.예를들면모든아

이템의전력수요가소굴내부의발전기(천재악당은국가에낼전기요금

따위에신경쓰고싶어하지않을것이다)에의해제대로공급되고있는지

23

03 악당 소굴과 20개의 루비 DSL

검사해볼필요가있다.결과적으로각자원별로별도의클래스를만들고

자한다.

자원은다시두개의범주로나뉠수있다고치자.하나는고정된수의

속성만으로구성된간단한자원이다.따라서생성자에속성값들을넘겨주

는방식으로쉽게만들어질수있다(electricity).다른하나는복잡한자

원인데,속성의종류도많고일부는선택적으로쓰이는속성이라서다양한

설정메서드들이필요하다(acid).사실이예제에나오는산acid에는속성

이딱두개밖에없기는하지만,한십여개쯤있다고상상하도록하자.

아이템에대해서는세가지만이야기하면된다.아이템은자원을사용하

고,생산하고,소굴내에있는다른아이템에대한의존성이있을수있다.

궁금한독자들을위해지금까지설명한내용에대한구현코드를먼저

보여주도록하겠다.이장의모든예제에서는이구현을사용하도록한다.

lairs/model.rb

class Item

attr_reader :id, :uses, :provisions, :dependencies

def initialize id

@id = id

@uses = []

@provisions = []

@dependencies = []

end

def add_usage anItem

@uses << anItem

end

def add_provision anItem

@provisions << anItem

end

def add_dependency anItem

@dependencies << anItem

end

end

The Thoughtworks Anthology

24

class Acid

attr_accessor :type, :grade

end

class Electricity

def initialize power

@power = power

end

attr_reader :power

end

위구현을이용한특정구성은configuration객체에저장한다.

lairs/model.rb

class Configuration

def initialize

@items = {}

end

def add_item arg

@items[arg.id] = arg

end

def [] arg

return @items[arg]

end

def items

@items.values

end

end

이제,이장의목적에맞게몇개의아이템과규칙을정의하도록하겠다.

염산용기(• acid_bath)는전력12와5급염산을사용한다.

카메라(• camera)는전력1을사용한다.

소형발전기(• small_power_plant)는전력11을사용하고,비밀환

풍기(secure_air_vent)를필요로한다(즉의존한다).

25

03 악당 소굴과 20개의 루비 DSL

이규칙을아래와같이표현할수있을것이다.

lairs/rules0.rb

config = Configuration.new

config.add_item(Item.new(:secure_air_vent))

config.add_item(Item.new(:acid_bath))

config[:acid_bath].add_usage(Electricity.new(12))

acid = Acid.new

config[:acid_bath].add_usage(acid)

acid.type = :hcl

acid.grade = 5

config.add_item(Item.new(:camera))

config[:camera].add_usage(Electricity.new(1))

config.add_item(Item.new(:small_power_plant))

config[:small_power_plant].add_provision(Electricity. new(11))

config[:small_power_plant].add_dependency(config[:secure_ air_vent])

비록이코드가올바르게작동하기는하겠으나그리부드럽게읽히지는않

는다.이장의나머지부분에서는위규칙을좀더나은방식으로표현할

수있는여러가지방법들을살펴보도록하자.

3.2 전역함수를사용하는방법

함수는프로그램을구조화하는매우기초적인기법이다.함수는소프트웨

어를구조화하고도메인이름을소스코드에써넣을수있게해주는가장

오래된방법중하나이다.

따라서,DSL로가는나의첫시도에서는전역함수호출을사용해보려

고한다.

The Thoughtworks Anthology

26

lairs/rules8.rb

item(:secure_air_vent)

item(:acid_bath)

uses(acid)

acid_type(:hcl)

acid_grade(5)

uses(electricity(12))

item(:camera)

uses(electricity(1))

item(:small_power_plant)

provides(electricity(11))

depends(:secure_air_vent)

함수의이름들을모으면DSL의어휘집합이된다.item함수는아이템을

선언하고uses함수는해당아이템이자원resource을사용한다는것을알려

준다.

이DSL에서구성규칙이란결국모두관계에대한것이다.카메라가

전력을1사용한다고할때이는카메라라고하는자원과전력이라고하

는자원사이에연결고리를만든것과같다.이첫번째예제에서는명

령이수행된순서에따른맥락context에기반하여연결이이루어진다.

uses(electricity(1))이라는명령이카메라객체에적용되는이유는

바로직전에카메라에대한선언이실행되었기때문이다.이관계는명령

의순차적실행이라는맥락에의해암묵적으로맺어졌다고말할수있을

것이다.

여러분은사람이기때문에위DSL문장을읽으면서순서에의한맥락을

유추할수있을것이다.하지만컴퓨터는이DSL을처리하려면약간의도

움이필요하다.맥락정보를유지하기위해특별한변수가필요한데이변

수의이름은맥락변수context variable라부르자.이경우에는현재의아이템

27

03 악당 소굴과 20개의 루비 DSL

을기억하기위한용도의맥락변수하나가필요하다.

lairs/builder8.rb

def item name

$current_item = Item.new(name)

$config.add_item $current_item

end

def uses resource

$current_item.add_usage(resource)

end

내가전역함수를사용하고있기때문에맥락변수또한전역변수가되어

야한다.훌륭한방법은아니다.앞으로보게되겠지만대부분의언어는전

역변수를피하기위한방법들을제공한다.사실전역변수를사용한다는

것은전혀이상적인방법이라할수없지만지금막시작했을뿐이니그럭

저럭괜찮을것이다.

산acid의속성을지정할때에도같은방식을활용할수있겠다.

lairs/builder8.rb

def acid

$current_acid = Acid.new

end

def acid_type type

$current_acid.type = type

end

순서에의해맥락을부여하는방식은아이템과자원을연결할때엔괜찮

았지만아이템간의연결과같이상호간계층구조가없는경우에는그

리좋은방식이아니다.따라서아이템사이의연결을맺어주려면이

를명시적으로표현해야한다.아이템을선언할때식별자를부여하고

(item(:secure_air_vent)),나중에이식별자를사용해서해당아이템

을지칭하도록하였다(depends(:secure_air_vent).한편,비밀환풍

The Thoughtworks Anthology

28

기(secure_air_vent)에의존하는아이템이바로소형발전기(small_

power_plant)라는사실은순서맥락에의해정해질수있다.

여기에서자원객체는에반스가값객체value object라고부르는개념에해

당한다는것을알고있으면유용하다[Eva03_참고문헌참조].결과적으로

자원은자원을소유하고있는아이템에의해서참조되기만할뿐다른식

으로는쓰이지않는다.반면아이템객체는여러의존관계를통해DSL안

에서다양한방식으로쓰일수있다.따라서아이템객체에나중에참조되

기위한용도로모종의식별자를가져야한다.

이와같은용도의식별자를표현하는루비스러운방식ruby way은

:secure_air_vent같은심볼자료형을쓰는것이다.루비에서심볼이란

콜론으로시작하고중간에공백문자가없는문자열이다.대다수의주류언

어에는심볼데이터타입이존재하지않는데,그냥이와같은용도로만쓰

기위해만들어진보통의문자열이라고생각해도좋다.결과적으로심볼에

는많은문자열연산자들을사용할수없긴하지만,동일한심볼은모두동

일한인스턴스를공유하도록설계되어있기때문에성능이더좋다.한편,

내가심볼을사용하는가장중요한이유는그로인해나의의도가명확히

드러나기때문이다.나는문자열이아닌:secure_air_vent라는심볼을

사용하고있는것이며,이같이올바른자료형을선택함으로써나의의도

가분명해졌다.

심볼을쓰는방법이외의다른방법으로는변수를쓰는방법이있다.하

지만나는DSL을만들때변수를될수있으면쓰지않으려한다.하나의

변수에서로다른종류의객체들을넣을수있기때문에변수를쓰게되면

어떤객체가어떤변수에담겨있는지항상기억하고있어야한다.변수는

유용한장치이지만안에뭐가들어있는지를기억하고있어야한다는점은

좋지않다.이러한이유로DSL에서는변수를쓰지않으려하는것이다.반

면식별자는변수와달리내용이변하지않기때문에항상동일한객체를

지칭한다.

29

03 악당 소굴과 20개의 루비 DSL

식별자는의존관계를표현하기위해필요하기도하지만순서에의한맥

락에의존하지않고자원을지칭하고자할때에도쓰일수있다.

lairs/rules7.rb

item(:secure_air_vent)

item(:acid_bath)

uses(:acid_bath, acid(:acid_bath_acid))

acid_type(:acid_bath_acid, :hcl)

acid_grade(:acid_bath_acid, 5)

uses(:acid_bath, electricity(12))

item(:camera)

uses(:camera, electricity(1))

item(:small_power_plant)

provides(:small_power_plant, electricity(11))

depends(:small_power_plant, :secure_air_vent)

식별자를이런식으로사용하면관계를명시적으로표현해줄수있고맥

락을기억하기위해전역변수를사용할필요도없다.이두가지모두일

반적으로는좋은일이다.난뭐든명시적인것이좋고,전역변수라는것

은싫으니말이다.하지만이로인해지불하게되는비용이있는데DSL이

훨씬더너저분해진다는점이다.DSL을좀더읽기좋게만들기위해어느

정도의암묵적방식을사용하는것이좋겠다는생각이든다.

3.3 객체를사용하는방법

함수를사용했던처음방식의근본적문제가운데하나는DSL을만들기

위해여러전역함수를정의했다는점에있다.많은양의전역함수는관리

하기가까다롭다.객체를사용하는것이주는장점하나는여러함수들을

하나의클래스로조직할수있다는점이다.코드를적절히재배열하면여

러DSL함수를하나로잘모을수있다.

The Thoughtworks Anthology

30

클래스 메서드와 메서드 연결하기Method Chaining

객체지향언어에서메서드의범위를제어하는가장명확한방법은클래스

메서드이다.하지만이방식은DSL에중복을만든다.왜냐하면메서드를

호출할때마다클래스이름을반복적으로써주어야하기때문이다.아래

코드에서와같이클래스메서드와메서드연결하기를함께사용하면중복

을어느정도줄여줄수있다.

lairs/rules11.rb

Configuration.item(:secure_air_vent)

Configuration.item(:acid_bath).

uses(Resources.acid.

set_type(:hcl).

set_grade(5)).

uses(Resources.electricity(12))

Configuration.item(:camera).uses(Resources.electricity(1))

Configuration.item(:small_power_plant).

provides(Resources.electricity(11)).

depends_on(:secure_air_vent)

이코드에서는DSL의각절이클래스메서드호출로시작하고있다.이클

래스메서드는다음호출에대한수신자역할을할객체를반환한다.따라

서여러메서드호출을연속해서이어쓸수있다.이런식으로이어서호

출하는코드가부적절해보이는곳에서는다시클래스메서드를호출하는

식으로끊어서썼다.

이예제는안에서무슨일이일어나고있는지좀더깊게살펴볼가치가

있다.그과정에서몇몇좋지않은구현을보게될것이지만이후의예제들

에서고쳐나갈계획이다.

31

03 악당 소굴과 20개의 루비 DSL

아이템을정의하는곳에서부터시작해보자.

lairs/builder11.rb

def self.item arg

new_item = Item.new(arg)

@@current.add_item new_item

return new_item

end

이메서드는새아이템을만들고이를클래스변수에저장한후반환한다.

이코드에서는새로생성된아이템을반환하는부분이핵심인데,이렇게

하기때문에메서드연결하기가가능해지는것이다.

lairs/builder11.rb

def provides arg

add_provision arg

return self

end

provides메서드는일반적인추가하기함수를호출하고있지만이때에도

자기자신을반환하고있다.이로인해메서드호출을연결할수있게되

며,다른메서드들도마찬가지방식으로작동된다.

이런방식의메서드연결하기는여러좋은프로그래밍지침들과상충된

다.대부분의언어들에서수정자modifier(객체의상태를변화시키는메서드)

는아무것도반환하지않는다.이렇게하는이유는명령과쿼리의분리라

는원칙2을따르기위한것이다.이원칙은대부분의경우잘따르는것이

좋다.불행히도이원칙은내장된DSL을작성할때엔맞지않는다.결과

적으로DSL을고안하는입장에서는DSL에서메서드연결하기를지원하

기위한코드를작성하는동안엔이원칙을포기하곤한다.우리예제에서

2 (옮긴이)객체의상태를변화시키는메서드를명령(command)이라고하고,객체의상태를알려주는메서드를

쿼리(query)라고한다.명령이무언가를반환하거나,쿼리가객체의상태를변경시키거나하지않아야한다는

것이명령과쿼리의분리원칙이다.이원칙에따라코드를작성하면다양한설계및성능이점이있기때문에

일반적으로잘따를것을권장한다.

The Thoughtworks Anthology

32

는산의종류와등급을설정할때에도메서드연결하기를적용할것이다.

일상적인원칙에서벗어난부분이하나더있는데,바로코드들여쓰기

이다.DSL에서의도하고있는계층관계를강조하기위해코드들여쓰기

에약간변형을가했다.메서드연결하기를활용하게되면종종메서드호

출사이사이에서줄바꿈을하게된다.

이예제에서는메서드연결하기뿐만아니라팩토리클래스를사용하여

자원resource을생성하는방법도보여주고있다.Electricity클래스에메

서드를추가하는대신Electricity와Acid를생성하는클래스메서드를

가지고있는Resources클래스를정의했다.이방식은종종클래스팩토

리class factory혹은정적팩토리static factory라고불리는데그이유는이러한클

래스에는오로지적절한객체를생성하는데쓰이는클래스(혹은정적)메

서드밖에들어있지않기때문이다.이방식을쓰면DSL이더읽기쉬워질

때가많고,실제모델클래스에부가적인메서드를추가하지않아도된다

는장점이있다.

이렇게쓰고나니위에서소개한DSL코드조각의문제하나가드러났

다.이DSL이실행되게하기위해서나는도메인클래스에다가그다지잘

어울리지않는메서드몇개를추가했다.객체가가지고있는거의모든메

서드는홀로호출될때에도의미적으로올바르게수행되어야한다.하지만

DSL메서드들은DSL문장의표현이라는맥락하에서만의미를갖는다.결

국명령과쿼리의분리원칙을어기는것에더하여,작명규칙을따르기도

어렵다.게다가DSL메서드들은매우맥락의존적일뿐아니라,DSL표현

안에서객체를생성하고자할때에만쓰여야한다.기본적으로좋은DSL

메서드를위한원칙들은일상적인상황에서의원칙들과잘맞지않는다.

표현 빌더Expression Builder

DSL과일상적인API사이의충돌을회피하기위한방법은표현빌더패턴

을사용하는것이다.이패턴의핵심은DSL에서쓰이는메서드가실제도

33

03 악당 소굴과 20개의 루비 DSL

메인객체의메서드와별개인객체에분리되어야한다는것이다.표현빌

더패턴을쓰는몇가지방법이있다.지금소개할방법은동일한DSL언

어를쓰지만도메인객체가아닌빌더객체를생성한다.

이렇게하려면초기에만들었던클래서메서드가빌더객체를반환하도

록수정해야한다.

lairs/builder12.rb

def self.item arg

new_item = ItemBuilder.new(arg)

@@current.add_item new_item.subject

return new_item

end

아이템빌더는DSL메서드를가지고있고,각메서드는실제아이템객체

의메서드를호출하는식으로‘번역’을수행한다.

lairs/builder12.rb

attr_reader :subject

def initialize arg

@subject = Item.new arg

end

def provides arg

subject.add_provision arg.subject

return self

end

일단이방식을도입하고나면현재도메인객체의API가가지는틀을완

전히깨버리고좀더명확한DSL을고안할수있다.다음코드를보자.

lairs/rules14.rb

ConfigurationBuilder.

item(:secure_air_vent).

item(:acid_bath).

uses(Resources.acid.

type(:hcl).

The Thoughtworks Anthology

34

grade(5)).

uses(Resources.electricity(12)).

item(:camera).uses(Resources.electricity(1)).

item(:small_power_plant).

provides(Resources.electricity(11)).

depends_on(:secure_air_vent)

이코드에서는아예시작부터빌더를사용하고있고,빌더에대하여메서

드연결하기를적용하고있다.같은클래스이름을반복해서쓰는것도제

거해줄뿐만아니라이상한클래스변수를쓸필요성도없어졌다.첫번째

호출은클래스메서드호출이고이호출에서ConfigurationBuilder의

새인스턴스가만들어진다.

lairs/builder14.rb

def self.item arg

builder = ConfigurationBuilder.new

builder.item arg

end

def initialize

@subject = Configuration.new

end

def item arg

result = ItemBuilder.new self, arg

@subject.add_item result.subject

return result

end

ConfigurationBuilder를만든후이인스턴스에대해바로item메서드

를호출하고있다.이인스턴스메서드를호출하면새ItemBuilder인스

턴스가생성및반환된다.이때클래스메서드와인스턴스메서드가같은

이름을갖게만들었는데흔치않은경우이다.일반적으로는혼란을피하기

위해이런식으로하지않지만이번에도역시더자연스럽게읽히는DSL

을만들기위해일상적인API작성규칙을어겼다.

35

03 악당 소굴과 20개의 루비 DSL

ItemBuilder는이전코드에서봤던것과동일하게아이템에대한정보

를설정하기위한메서드들을가지고있다.여기에더하여,자체적인item

메서드도가지고있는데,새아이템에대한정의를시작하고자할때이메

서드를호출하면된다.

lairs/builder14.rb

def item arg

@parent.item arg

end

def initialize parent, arg

@parent = parent

@subject = Item.new arg

end

이때상위객체로돌아가야하는데,그게바로내가

ConfigurationBuilder 인스턴스를parent로넘긴이유중하나이다.

또다른이유는ItemBuilder가의존관계를기록할때다른아이템을찾

아야할필요가있기때문이다.

lairs/builder14.rb

def depends_on arg

subject.add_dependency(configuration[arg])

return self

end

def configuration

return @parent.subject

end

이방식을쓰기전에는이를위해전역변수혹은클래스변수를사용해야

했었다.

마지막으로AcidBuilder에있는메서드들의이름을좀잘읽힐수있게

바꿨다.그렇게할수있었던이유는이제더이상도메인객체에있는메

서드와의이름충돌을염려할필요가없어졌기때문이다.

The Thoughtworks Anthology

36

지금까지살펴본표현빌더방식에서는각도메인객체에대응하는빌더

객체를만들었는데,이렇게하는방법말고다른방법도있다.다른방법은

빌더역할을하는단일한객체를만드는것이다.아래코드를보면,위에서

봤던동일한DSL에대하여이새로운방식을어떻게적용할수있는지알

수있다.

lairs/builder13.rb

def self.item arg

result = self.new

result.item arg

return result

end

def initialize

@subject = Configuration.new

end

def item arg

@current_item = Item.new(arg)

@subject.add_item @current_item

return self

end

새객체를만드는대신맥락변수를써서현재사용중인아이템을기록하

고있다.

메서드 연결하기에 대해 조금 더

메서드연결하기기법이참좋아보이긴하는데,몽땅이런식으로해버릴

수는없는걸까?자원에대한팩토리를없애버릴수있을까?사실은그것

도가능하다.그결과로얻게될DSL은다음과같을것이다.

lairs/rules2.rb

ConfigurationBuilder.

item(:secure_air_vent).

37

03 악당 소굴과 20개의 루비 DSL

item(:acid_bath).

uses.acid.

type(:hcl).

grade(5).

uses.electricity(12).

item(:camera).uses.electricity(1).

item(:small_power_plant).

provides.electricity(11).

depends_on(:secure_air_vent)

(가독성을높이기위해빈줄을넣었다–루비코드는이런식으로작성해

도문제가생기지않는다)

DSL을만들다보면메서드연결하기를쓸것인지인자를넘기는방식

을쓸것인지사이에서항상고민을하게된다.grade(5)와같이인자가

단순한상수인경우메서드연결하기를쓰면괜히복잡해질수있다.나

는아주복잡한것보다는매우쉬운것을선호하기때문에이때는쉽게

결정을내릴수있다.문제는uses.electricity…와uses(Resources.

electricity…)사이에서의고민이다.

메서드연결하기를많이사용하면빌더의복잡도가증가한다.특히부차

적인객체를사용하기시작할때그렇다.이코드는이같은복잡도를잘

보여주고있다.Resource는uses와provides라는두문맥에서다쓰이기

때문에,메서드연결하기를사용하려면이둘중어느문맥인지를파악하

고있어야electricity호출을제대로처리할수있다.

한편,인자를넘기는방식에서는메서드연결하기방식에서와달리범위

에대한통제권을잃게되고이에따라인자를생성하는함수에적절한범

위를설정해주어야한다.이코드에서는팩토리클래스의클래스메서드를

사용하여범위를통제하고있다.하지만매번팩토리이름을적어주어야

한다는점은피하고싶은단점이다.

The Thoughtworks Anthology

38

인자를넘기는방식의또다른문제도있다.사용하는입장에서볼때,

어떤경우에는메서드연결을쓰고어떤경우에는인자를넘기는식으로

설계된DSL은쓰기가쉽지않을것이다.

이문제에대한나의결론은모호한데,가이드를제공할만큼DSL에대

하여충분한경험을쌓지못했기때문이다.일단메서드연결하기를사용

하자.단메서드연결을지원하기위해복잡도가증가하지는않는지유심

히살펴보면서가야한다.빌더구현의복잡도가심하게높아지면(참애매

한표현이지만)인자를도입하자.이장의후반부에서인자를도입하여클

래스이름을반복적으로입력하던문제를해결하는방식을보여줄것이다.

하지만이방식은특정언어에서만가능하다.

3.4 클로저를사용하는방법

클로저는여러언어에공통적으로존재하는특성인데,특히나동적언어

에서잘지원한다.그리고동적언어는내장DSL작업에많이쓰인다.클

로저는계층적구조에새로운맥락을쉽게도입할수있게해주기때문에

DSL작성시큰도움이된다.다음은클로저를이용한악당소굴예제이다.

lairs/rules3.rb

ConfigurationBuilder.start do |config|

config.item :secure_air_vent

config.item(:acid_bath) do |item|

item.uses(Resources.acid) do |acid|

acid.type = :hcl

acid.grade = 5

end

item.uses(Resources.electricity(12))

end

config.item(:camera) do |item|

39

03 악당 소굴과 20개의 루비 DSL

item.uses(Resources.electricity(1))

end

config.item(:small_power_plant) do |item|

item.provides(Resources.electricity(11))

item.depends_on(:secure_air_vent)

end

end

이예제의특징이라면,각메서드호출을받는수신자가명확히드러난다

는점과메서드연결하기를사용하지않는다는점을들수있겠다.수신자

는루비언어의클로저문법을이용하여표현하고있는데,DSL에일상적

으로나타나는계층적구조와매우잘맞는방식으로중첩된메서드호출

을나타낼수있어서좋다.

이방식에는바로드러나는시각적장점이하나있는데,루비언어의자

연스러운중첩구조가DSL코드에그대로나타난다는점이다.이덕분에

코드에대한들여쓰기도자연스럽게표현할수있다.item이나acid같은

DSL언어의요소를담고있는변수들도루비언어의블록범위내부로자

연스럽게제한된다.

명확한수신자를사용한다는것은메서드연결하기를쓰지않음을의미

한다.이는곧도메인객체의API를쓸수있는곳에서는빌더사용을피해

도된다는것을뜻한다.이예제의경우아이템item에대해서는빌더를썼

으나산acid에대해서는도메인객체를그대로썼다.

이기법의문제는호스트언어가클로저를지원해야한다는점이다.임

시변수를사용하면대충비슷한흉내를낼수있겠지만,별도의방법을통

해변수의범위를제한하지않는다면온갖범위문제들을다시불러오는

꼴이된다.그리고변수의범위를제한하건하지않건,결과적으로얻어진

DSL표현이위예제처럼자연스럽지도않을것이고,작성중실수를할가

능성도높을것이다.클로저는범위제약과변수선언을통해이문제들을

해결해준다.

The Thoughtworks Anthology

40

3.5 평가맥락Evaluation Context

지금까지는DSL코드가평가되는전체적인맥락에대해언급하지않았다.

다시말해,명시적인수신객체가없는함수호출이나데이터항목에대한

접근을언급하면서이함수나데이터항목이어느맥락에서나오는것인지

명시하지않았다는뜻이다.지금까지는평가맥락이전역global이라고가정

하고있었다.따라서함수foo()는전역함수로취급했었다.메서드연결

하기나클래스메서드를사용하면DSL의함수들을다른평가맥락에서실

행할수있다고했었는데,사실은전체DSL문장의평가맥락을대체하는

방법도존재한다.

가장직접적인방법은전체DSL문장을클래스내부로옮겨넣는것이

다.이렇게하면DSL이자신을포함하고있는클래스어딘가에정의된메

서드와필드를가져다쓸수있다는장점도생긴다.개방된클래스open class3

를지원하는언어라면원하는클래스를바로수정하면되고,그렇지않은

경우라면하위클래스를만들어주면된다.

다음예제를보자.

lairs/rules17.rb

class PrimaryConfigurationRules < ConfigurationBuilder

def run

item(:secure_air_vent)

item(:acid_bath).

uses(acid.

type(:hcl).

grade(5)).

uses(electricity(12))

item(:camera).uses(electricity(1))

item(:small_power_plant).

3 (옮긴이)정의를동적으로수정할수있는클래스

41

03 악당 소굴과 20개의 루비 DSL

provides(electricity(11)).

depends_on(:secure_air_vent)

end

end

DSL문장을하위클래스에넣는방식을쓰면기존의전역실행맥락에서

는할수없었던것들이가능해진다.일단item()을연속적으로호출할때

더이상메서드연결하기를사용하지않아도된다.왜냐하면item을생성

할때빌더의item()메서드를바로쓸수있기때문이다.이와유사하게

산acid과전력electricity을생성할때에도빌더의메서드를바로이용하기때

문에정적팩토리메서드를사용하지않아도된다.

이접근법의단점이라면DSL의시작부분과끝부분에클래스및메서

드선언부같은것을써주어야한다는점을꼽을수있다.

이예제에서는객체의인스턴스라는평가맥락을사용하고있다.이렇

게하면인스턴스변수에도접근할수있다는점이좋다.물론클래스메서

드를쓰면클래스수준의평가맥락을사용할수도있다.하지만나는보통

인스턴스맥락을선호하는데그렇게하면빌더인스턴스를만들어서이를

사용한후에그인스턴스만버리면되기때문이다.이를통해각각의평가

를서로독립적으로수행할수있고,이전실행으로어지럽혀진결과가다

음수행에영향을미치는위험을줄일수있다(특히동시성이개입된경우

에위험성이높아진다).

루비는케익을사서그자리에서바로먹어치울수있는유용한방법을

제공하고있다.루비에는instance_eval이라는메서드가있는데,이메

서드는문자열혹은블록으로된코드를인자로받아서이코드를특정객

체의인스턴스맥락에서수행해준다.이메서드를활용하면파일에DSL

본문만나오도록할수있다.결과는다음과같을것이다.

lairs/rules1.rb

item :secure_air_vent

The Thoughtworks Anthology

42

item(:acid_bath).

uses(acid.

type(:hcl).

grade(5)).

uses(electricity(12))

item(:camera).uses(electricity(1))

item(:small_power_plant).

provides(electricity(11)).

depends_on(:secure_air_vent)

어떤언어에서는클로저를결합하여평가맥락을변경하는방법을제공하

기도한다.루비도클로저로정의된코드를instance_eval에넘기면특

정객체인스턴스의맥락에서수행할수있다.이방법을쓰면아래와같은

DSL문장을적을수있게된다.

lairs/rules18.rb

item :secure_air_vent

item(:acid_bath) do

uses(acid) do

type :hcl

grade 5

end

uses(electricity(12))

end

item(:camera) do

uses(electricity(1))

end

item(:small_power_plant) do

provides(electricity(11))

depends_on(:secure_air_vent)

end

43

03 악당 소굴과 20개의 루비 DSL

결과가제법매력적이다.명시적인수신자로써블록인자를반복하여적지

도않으면서도클로저를구조적으로사용할수있게됐다.하지만이방법

을쓸땐주의할필요가있다.블록의맥락이바뀌는지점들에서많은혼란

을겪을소지가있다.의사변수pseudo-variable인self가각블록안에서서

로다른객체를지칭하기때문에DSL작성자에게혼란을주기쉽다.또블

록내부에서원래의self에접근하고자할때에도문제가생긴다.

이문제는루비의빌더라이브러리를개발하는과정에서도겪은바있

다.초기버전의빌더라이브러리는instance_eval을사용했었는데,막

상써보니혼란스럽고어려웠다.라이브러리작성자인짐웨이리치Jim

Weirich는평가맥락을변경하도록하는방식은호스트언어의일반적인관

례를어기고있다는점에서프로그래머를대상으로하는DSL에서는적절

치않다는결론을내렸다.하지만DSL작성자가비프로그래머인경우라면

이부분은크게문제가되지않는데,왜냐하면비프로그래머는호스트언

어의관례에대해아는바가없기때문이다.현재의내직감은이렇다.내

장DSL이호스트언어와더긴밀하게통합되면될수록호스트언어의일

반적관례를어기지않는것이좋다.하지만지금보는예제와같이호스트

언어와유사하게보일필요가별로없는작은언어를만드는경우라면가

독성이주는장점이더부각될것이다.

3.6 리터럴컬렉션

함수호출문법은내장DSL의중요한구조화기법가운데하나이다.사실

많은언어들에있어서는이게유일한방법이기도하다.하지만몇몇언어

에는또다른유용한기법이존재하는데바로리터럴컬렉션을문장중에

자유롭게쓸수있는특성이다.이기법이제한되어있는언어들도많은데

그이유는리터럴컬렉션을표기하는편의문법이제공되지않거나,편의

문법이있다하더라도이를어디서든원하는만큼쓸수있도록허용하고

있지않기때문이다.

The Thoughtworks Anthology

44

리스트와맵(해시,딕셔너리,연관배열등으로도불린다)이렇게두종류

가유용하게쓰일수있다.거의모든현대언어들은이러한객체들을라이

브러리수준에서제공하고있고이객체를조작하기위한다양한API를제

공한다.이두구조체모두DSL작성에유용하게쓰일수있다.물론리스

프프로그래머라면리스트만있으면맵을흉내낼수있다고말하겠지만.

아래예제는리터럴컬렉션을이용하여acid를정의하고있다.

lairs/rules20.rb

item :secure_air_vent

item(:acid_bath) do

uses(acid(:type => :hcl, :grade => 5))

uses(electricity(12))

end

item(:camera) do

uses(electricity(1))

end

item(:small_power_plant) do

provides(electricity(11))

depends_on(:secure_air_vent)

end

이예제에서는함수호출과리터럴컬렉션을섞어서쓰고있다.동시에,모

호함이없는경우괄호를생략할수있게해주는루비의문법을활용하고

있다.이때acid함수는아래와같을것이다.

lairs/builder20.rb

def acid args

result = Acid.new

result.grade = args[:grade]

result.type = args[:type]

return result

end

45

03 악당 소굴과 20개의 루비 DSL

리터럴해시를인자로쓰는방식은루비에서자주쓰이는코딩패턴이다

(펄의영향중하나이다).선택인자가많은생성메서드등의상황에서아

주훌륭하게활용될수있다.이예제에서는DSL문법을깔끔하게정리해

줄뿐만아니라acid와electricity에대한빌더를만들필요성도제거

해주고있다.객체가필요하면바로만들어버리면된다.

uses,provides,depends_on같은함수호출까지맵으로바꾸는식으

로리터럴을더많이활용해보면어떨까?

lairs/rules4.rb

item :secure_air_vent

item :acid_bath,

:uses => [acid(:type => :hcl,

:grade => 5) ,

electricity(12)]

item :camera,

:uses => electricity(1)

item :small_power_plant,

:provides => electricity(11),

:depends_on => :secure_air_vent

위코드는이러한방식의장점과단점을모두보여주고있다.소형발전기

(small_power_plant)는단순하기때문에이방식이아주잘통한다.하

지만염산용기(acid_bath)같이복잡한경우라면그리좋지가않다.염산

용기가의존하고있는두개의자원이있기때문에acid와electricity

를호출하는코드를리스트에넣어야만했다.리터럴맵안에뭔가를중첩

시켜서넣으면넣을수록코드를읽기가어려워진다.

위방식은또한구현코드의복잡도도증가시킨다.item을호출할때이

름과맵이모두필요해진다.루비에서는이런상황을name인자와다중인

자로처리할수있다.

The Thoughtworks Anthology

46

lairs/builder4.rb

def item name, *args

newItem = Item.new name

process_item_args(newItem, args) unless args.empty?

@config.add_item newItem

return self

end

process_item_args함수는맵의키key에따라다른작업을처리해야하

기때문에switch문도필요하고,값value이단일요소인지리스트인지에

따른구분도처리해주어야한다.

lairs/builder4.rb

def process_item_args anItem, args

args[0].each_pair do |key, value|

case key

when :depends_on

oneOrMany(value) {|i| anItem.add_dependency( @config[i])}

when :uses

oneOrMany(value) {|r| anItem.add_usage r}

when :provides

oneOrMany(value) {|i| anItem.add_provision i}

end

end

end

def oneOrMany(obj, &block)

if obj.kind_of? Array

obj.each(&block)

else

yield obj

end

end

위와같이값이단일요소일수도있고리스트일수도있는상황이라면아

예몽땅리스트를쓰는것이더쉬울수있다.

47

03 악당 소굴과 20개의 루비 DSL

lairs/rules21.rb

item :secure_air_vent

item :acid_bath,

[:uses,

acid(:type => :hcl, :grade => 5),

electricity(12)]

item :camera,

[:uses, electricity(1)]

item :small_power_plant,

[:provides, electricity(11)],

[:depends_on, :secure_air_vent]

여기에서item메서드의인자는아이템의이름과리스트(해시가아니라)

이다.리스트의첫번째항목은키key이고나머지항목은값들이다(이게바

로리스프프로그래머들이해시를리스트로표현하는방법이다).이방법

을따르면중첩도제거되고처리도쉬워진다.

lairs/builder21.rb

def item name, *args

newItem = Item.new name

process_item_args(newItem, args) unless args.empty?

@config.add_item newItem

return self

end

def process_item_args anItem, args

args.each do |e|

case e.head

when :depends_on

e.tail.each {|i| anItem.add_dependency(@config[i])}

when :uses

e.tail.each {|r| anItem.add_usage r}

when :provides

e.tail.each {|i| anItem.add_provision i}

end

The Thoughtworks Anthology

48

end

end

여기에서사용한중요한트릭은리스트를순서가부여된요소들의집합

으로보는대신머리와긴꼬리로이루어진구조라고보는것에있다.해

시를인자두개짜리리스트로바꿔봐야아무것도건질것이없다.그대

신키를리스트의머리로취급하고나머지모든값들을평평하게펴서꼬

리로붙인다.이렇게하면컬렉션안에또다른컬렉션을중첩시킬필요가

없게된다.

머리head와꼬리tail를다루는코드는루비의리스트클래스(루비에서의

이름은Array이다)에존재하지않지만쉽게추가할수있다.

lairs/builder21.rb

class Array

def tail

self[1..-1]

end

alias head first

end

리터럴컬렉션에대한내용을마치기전에각방식의궁극적형태를보여

주는것도좋을것같다.다음은맵과리스트만을사용한방식이다.

lairs/rules22.rb

{:items => [

{:id => :secure_air_vent},

{:id => :acid_bath,

:uses => [

[:acid, {:type => :hcl, :grade => 5}],

[:electricity, 12]]},

{:id => :camera,

:uses => [:electricity, 1]},

{:id => :small_power_plant,

:provides => [:electricity, 11],

49

03 악당 소굴과 20개의 루비 DSL

:depends_on => :secure_air_vent}

]}

다음은리스트만을사용한방식이다.

lairs/rules6.rb

[

[:item, :secure_air_vent],

[:item, :acid_bath,

[:uses,

[:acid,

[:type, :hcl],

[:grade, 5]],

[:electricity, 12]]],

[:item, :camera,

[:uses, [:electricity, 1]]],

[:item, :small_power_plant,

[:provides, [:electricity, 11]],

[:depends_on, :secure_air_vent]]]

가변 인자 메서드

몇몇언어는가변인자메서드를허용하는데,함수호출이라는맥락에서

리터럴리스트를사용하고자할때이를활용할수있다.다음예제에서는

이기법을써서uses메서드를각각한번만호출할수있도록만들었다.

lairs/rules24.rb

item :secure_air_vent

item(:acid_bath) do

uses(acid(:type => :hcl, :grade => 5),

electricity(12))

end

The Thoughtworks Anthology

50

item(:camera) do

uses(electricity(1))

end

item(:small_power_plant) do

provides(electricity(11))

depends_on(:secure_air_vent)

end

메서드를호출하면서인자들을한번에묶어서보내고자할때가변인자

메서드를쓰면편하다.언어가리터럴리스트를편하게사용할수있도록

허용하고있지않다면특히유용하다.

3.7 동적수신Dynamic Reception

동적프로그래밍언어의장점중하나는수신객체에정의되지않은메서

드가호출되더라도이를적절히받아서처리할수있다는점이다.

이번예제에서는이러한부분을살펴보고자한다.지금까지는악당소굴

에서사용되는자원의종류가한정되어있다고가정해왔다.그리고그수

도많지않아서각자원에해당하는코드를수동으로작성할수있을정도

라고가정했다.하지만이러한가정을할수없는경우라면어떻게해야할

까?굉장히많은자원이있고이를모두포함해야한다면?

lairs/rules23.rb

resource :electricity, :power

resource :acid, :type, :grade

item :secure_air_vent

item(:acid_bath).

uses(acid(:type => :hcl, :grade => 5)).

uses(electricity(:power => 12))

item(:camera).

51

03 악당 소굴과 20개의 루비 DSL

uses(electricity(:power => 1))

item(:small_power_plant).

provides(electricity(:power => 11)).

depends_on(:secure_air_vent)

이예제에서도electricity와acid가존재하는데,이번에는각각의메서

드들을내가직접작성하는대신자원목록으로부터적절한메서드들이자

동으로유추되어만들어지길원한다.

이걸실현할방법중하나는method_missing을재정의하는것이다.루

비에서는객체가존재하지않는메서드에대한호출을수신하면자신의

method_missing메서드를호출하도록되어있다.Object클래스에정의

되어있는이메서드의기본구현은예외를던지도록되어있다.이번에사

용할트릭은이메서드를재정의하여뭔가좀더유용한일을하게만드는

것이다.

우선resource메서드에서기반을다지는작업을해야한다.

lairs/builder23.rb

def resource name, *attributes

attributes << :name

new_resource = Struct.new(*attributes)

@configuration.add_resource_type name, new_resource

end

루비에서는structs라불리는익명클래스를만들수있다.특정자원을

요청하면struct를정의하고resource메서드의첫번째인자를이름으로

지정한후여기에나머지인자들을넘겨준다.이때name인자도추가해

준다음,이struct를configuration에추가한다.

이제method_missing을재정의하여위에서만들어준새struct중메

서드이름과일치하는것이있는지살펴보게끔한다.일치하는것이발견

되면전달된인자들을사용하여struct의새인스턴스를만들어낸다.

The Thoughtworks Anthology

52

lairs/builder23.rb

def method_missing sym, *args

super sym, *args unless @configuration.resource_names. include? sym

obj = @configuration.resource_type(sym).new

obj[:name] = sym

args[0].each_pair do |key, value|

obj[key] = value

end

return obj

end

method_missing을재정의할때면항상내가처리할수있는호출인지를

우선살펴보아야한다.만약인식할수없는메서드호출이라면super를

호출해서예외를발생시켜주어야한다.

대부분의동적언어에서는알수없는메서드를처리하는핸들러를재정

의할수있다.이는강력한기법이지만반드시조심스럽게사용해야한다.

프로그램의메서드디스패치시스템을변경할수있도록해주는이기능을

현명하지못하게사용하면여러분이작성한코드를읽는모든사람들을심

각한혼란에빠트릴수있다.

짐와이리치Jim Weirich의루비빌더라이브러리는이기법을훌륭히활용

하고있는예이다.빌더의목적은XML마크업을생성하는것인데,클로저

와method_missing을잘활용하여매우가독성있는코드를쓸수있게

해주고있다.

다음의간단한예제를통해빌더를사용하는코드가얼마나보기좋은지

알수있다.

lairs/frags

builder = Builder::XmlMarkup.new("", 2)

puts builder.person do |b|

b.name("jim")

b.phone("555-1234", "local"=>"yes")

53

03 악당 소굴과 20개의 루비 DSL

b.address("Cincinnati")

end

위코드는아래의마크업을만들어낸다.

lairs/frags

<person>

<name>jim</name>

<phone local="yes">555-1234</phone>

<address>Cincinnati</address>

</person>

3.8 마무리

몇년전데이브토마스가자신의블로그에코드카타code katas라는표현에

대해쓴적이있다.카타란여러가지서로다른방법으로풀어볼수있는

작은프로그래밍문제들인데,이를풀어보면서서로다른해법들이어떻

게작동하는지또각해법들사이의장단점은무엇인지살펴볼수있다.4

이번장도이러한연습의일환이다.최종적인결론같은것은없지만이

장을통해루비로내장DSL을만들때사용할수있는많은선택사항들

을살펴볼수있었다.이중많은기법들은물론다른언어에서도활용될

수있을것이다.

4 (옮긴이)코드카타는가라데의카타(형)에서따온말이다.코드카타의목록은http://codekata.pragprog.com/

을참고.