AND

AND

AND

이제 기초를 익히고 스스로 개발 환경을 갖출 줄도 알게 되었으니 어디 가서 프로그래머라고 말할 수 있게 되었습니다. 다음은 사람들을 이끌고 나갈 수 있는 능력을 갖추는 것입니다.

패턴과 프레임워크, 다소 어려워 보일 수 있는 주제이지만 프로그래머로써 한 단계 거듭나기 위해 거쳐야 할 과제입니다. 이제까지 기본기로만 싸워왔다면 이제부터는 좀 고난이도의 기술과 새로운 무기들을 써 봅시다. 지금부터가 정말 즐거운 프로그래밍의 세계로 들어가는 길입니다.

탈무드에는 배고픈 자식에게 고기를 잡아주기보다 고기를 잡는 방법을 가르쳐주라는 말이 있습니다. 좋은 프로그램을 고기라고 본다면 좋은 프로그램을 만들어주는 것보다 좋은 프로그램을 만드는 방법을 가르쳐야 한다는 말이 되겠죠. 하지만 그러기엔 마소 주니어의 지면은 넉넉지 않습니다. 사실 초급자는 잡지에서 새로운 것을 익히려 하기보다 입문서에서 배워야 합니다.

그렇다면 이 한정된 지면에서 할 수 있는 최선은 자세한 내용을 기술하는 것이 아니라 무엇을 공부해야 하는지, 어디서 정보를 얻을 수 있는지, 요즘 추세는 어떤지를 알려주는 것이겠지요. 말하자면 좋은 프로그램이 아니라 그것을 만드는 방법을 고기로 보고 이것을 잡는 방법을 제시하겠다는 뜻입니다.

필자의 짧은 세 번의 연재에는 모두 이런 생각이 담겨 있고 그래서 기술적인 상세 내용 설명보다는 무엇을 공부해야 하는가에 대한 내용에 치중했습니다. 이번 글 역시 디자인 패턴, 프레임워크, XP 세 가지를 다루고 있지만 각각의 상세한 설명보다는 이런 개념들을 어떻게 이해하고 활용해야 하는가에 초점을 맞출 것입니다.

디자인 패턴과 프레임워크는 먼저 용어에 대한 설명부터 시작합니다. 그리고 그것들의 목적과 가치에 대해 살펴보고 몇 가지 예를 살펴볼 것입니다. XP는 제목에 포함시키긴 했지만 이것은 디자인 패턴과 프레임워크를 XP의 시각에서 바라본다는 의미이고 개별적인 주제로는 다루지 않습니다.

용어로서의 디자인 패턴
디자인 패턴이란 프로그래밍에서 발생하는 여러 가지 문제 영역에 대한 해결 방법들을 모아서 정리해놓은 것입니다. GoF(Gang of Four)가 여러 가지 패턴들을 모아 『디자인 패턴』이라는 책을 내면서부터 디자인 패턴이라는 용어가 일반화되기 시작했습니다. GoF의 디자인 패턴 중 하나를 예로 들면 데코레이터(Decorator)라는 패턴이 있습니다. 랩퍼(Wrapper)라는 말로도 쓰이는데 이것은 어떤 객체에 기본적인 동작은 유지하면서 부가적인 기능을 넣고 싶을 때 사용합니다.

이 패턴의 간단한 예로 JDBC에서 수행한 쿼리를 로그로 남기고 싶을 때 로깅을 위한 데코레이터를 사용할 수 있습니다. 보통 JDBC에서는 Connection 객체를 이용해서 Statement 객체를 생성하고 이것을 이용해서 쿼리를 수행하게 되는데 이 때 Statement 대신 LoggableStatement를 사용하는 것입니다.

LoggableStatement는 Statement를 상속하며 생성자에서 Connection 객체가 만든 Statement 객체를 받아서 멤버로 갖고 있습니다. 그리고 쿼리 요청이 오면 먼저 로깅을 하고 실제 쿼리는 생성자에서 받은 Statement 객체에 위임합니다. 이런 방식으로 Statement의 동작은 그대로 유지하면서 로그를 남기는 기능을 추가할 수 있습니다.

이런 것이 디자인 패턴입니다. 수학 공식처럼 같은 종류의 문제들에 대해 이미 정리된 해결책을 이용함으로써 매번 같은 문제를 해결하는데 중복으로 투자되는 노력을 줄이려는 것이죠.

하지만 디자인 패턴을 그대로 사용하는 것이 좋은 것만은 아닙니다. 오용의 위험성이 크기 때문입니다. 사실 어떤 문제 상황에 어떤 패턴을 적용하면 되는가를 판단하기란 쉬운 일이 아니기 때문에 필요하지도 않은 복잡한 패턴을 도입하는 경우가 많습니다. 그 때문에 간단하게 해결될 문제가 더 복잡해지기도 하죠. 그래서 안티 패턴 같은 이야기도 나오고 있습니다. XP(eXtreme Programming)에서도 디자인 패턴을 의식하지 말라고 합니다.

그보다 원하는대로 동작할 수 있는 가장 간단한 방법으로 코딩을 하다가 중복이 생기거나 코드가 지저분해지면 리팩토링을 합니다. 그러면서 자연스럽게 코드가 디자인 패턴의 모습을 갖추어 나가는 것이 바람직합니다.

이처럼 디자인 패턴의 원래 목적은 문제 해결 시간을 단축시키는 것이었지만 문제 영역과 패턴을 정확히 매칭시키기 어렵고 Over-Engineering이 나오는 경우가 많기 때문에 실제 개발에서는 디자인 패턴을 직접적으로 사용하지 않는 것이 좋습니다.

디자인 패턴의 가치는 그보다도 의사소통 비용을 줄여줄 수 있다는 데 있습니다. 서로가 패턴에 대해 잘 알고 있다면 복잡한 설명 없이도 아주 간단하게 의사소통이 이루어질 수 있죠. 이를테면 이 예를 설명하기 위해 복잡하게 LoggableStatement의 구조를 설명하는 것 대신에 ‘이 LoggableStatement 클래스는 Statement의 데코레이터로 로깅을 추가로 하게 되어 있어’라고 말할 수 있다는 것입니다. 즉 패턴은 해결책으로서보다 의사소통을 위한 용어로서 활용하는 거라고 생각하는 것이 좋습니다.

자바빈즈의 해악
패턴 오용의 한 예로 자바빈즈를 들 수 있습니다. ‘해악’이라는 표현까지 써가면서 자바빈즈를 언급한 것에 대해 놀라는 사람도 많을 것입니다. 자바를 배우면서 보통 자바빈즈의 장점에 대한 이야기를 많이 듣게 되니까요. 하지만 점점 자바빈즈를 해로운 것으로 인식하는 사람들이 많아지고 있습니다. 먼저 자바빈즈가 무엇인지부터 다시 살펴봅시다.

자바빈즈는 원래 컴포넌트 아키텍처를 자바에서 구현하기 위해 도입된 기술입니다. 객체의 프로퍼티의 조작 메커니즘을 제공하여 재사용성이 높고 유연한 컴포넌트를 만드는 것이 목적이죠. 자바빈즈의 특징으로 언급되는 것이 여러 가지가 있지만 다른 자바 객체와 구별되는 특징은 프로퍼티 관리 방식입니다. 프로퍼티로 private 필드를 두고 그 필드에 대한 public getter/setter를 만들어서 이를 이용해서 프로퍼티에 접근을 합니다.

사실 이것은 일반적인 필드 인캡슐레이션(field encapsulation)과 크게 다르지 않습니다. 다른 점은 이 프로퍼티의 이름과 getter/setter의 이름에 네이밍 룰을 부여해서 빈즈 API를 통해서 프로퍼티의 이름으로 이 프로퍼티를 읽거나 쓸 수 있다는 점입니다.

예를 들어 name이라는 프로퍼티가 있다면 getName, setName이라는 이름으로 getter/setter를 만들고 빈즈 API에서는 name이라는 이름으로 프로퍼티에 접근할 수 있습니다. 즉, 일반적인 필드 인캡슐레이션을 하면 프로퍼티에 접근하기 위해 메쏘드명을 코드에 직접 써야 하지만 빈즈 규약에 맞게 getter/setter를 만들고 빈즈 API를 이용하면 String으로 주어진 프로퍼티에 접근하는 것이 가능해진다는 것입니다.

이 점을 이용하면 복잡한 애플리케이션에서 자바빈즈 객체의 프로퍼티를 동적으로 쉽게 조작할 수 있습니다. 그래서 정보 은폐(information hiding, data hiding)와 동적인 프로퍼티 접근의 두 마리 토끼를 동시에 잡는 것이 자바빈즈의 목적입니다.

그러면 무엇이 문제일까요? 우선 첫 번째 목적인 정보 은폐가 제대로 되지 않습니다. 사실 필드 인캡슐레이션은 정보 은폐를 위한 것이라고 하지만 이것은 허구에 불과합니다. 실질적으로 프로퍼티에 대한 접근성은 public field와 private field, public getter/setter가 완전히 동일합니다. 똑같이 읽고 쓸 수 있는데 뭐가 다르겠습니까. 단지 좀 더 조심스럽게 쓰지 않을까라고 추측하는 것뿐입니다. 그래서 자바빈즈를 사용할 때 꼭 필요한 경우가 아니라면 setter는 만들지 않기를 권하기도 합니다.

하지만 일반적인 필드 인캡슐레이션이라면 setter를 만들지 말라는 조언을 할 수 있겠지만 자바빈즈는 그 목적이 프로퍼티를 동적으로 조작하는 것이므로 컴포넌트로서의 역할을 제대로 하려면 setter도 당연히 필요합니다. 결국 필드 인캡슐레이션으로 인해 코드는 길어졌지만 정보 은폐는 달성하지 못했으므로 실질적인 이득이 없습니다.

또 다른 문제는 자바빈즈의 한계에 관한 것입니다. 동적으로 프로퍼티의 내용에 접근할 수는 있지만 프로퍼티를 추가하거나 없애는 정도의 유연성은 소화할 수 없습니다. 많은 경우 자바빈즈를 데이터 전송 객체(Data Transfer Object)로 사용하는데 이런 경우는 프로퍼티의 내용 뿐 아니라 프로퍼티 종류 자체가 자주 변합니다. 그런데 자바빈즈로 만들게 되면 프로퍼티가 늘어날 때마다 필드와 메쏘드를 추가해야 합니다. 그리고 데이터의 종류가 달라질 때마다 새로운 클래스를 만들어야 하구요. 자바 컬렉션에 Map이 있는데 이런 비효율을 감당해야 할 이유는 별로 없습니다.

Map에서는 프로퍼티에 동적으로 접근하는 것은 물론이고 프로퍼티가 자유롭게 늘어나거나 줄어들 수 있습니다. 게다가 빈즈 API는 String으로 주어진 프로퍼티명에 대한 getter/setter를 호출하기 위해 내부적으로 Introspection이라는 기술을 사용하는데 이것은 자바의 Reflection API를 이용합니다. 때문에 성능상에서도 해싱(Hashing)을 이용하는 HashMap에 비해 나은 것이 없습니다.

그래서 자바빈즈의 단점에 대해 많은 사람들이 공감하기 시작했고 최근에 등장하는 각종 프레임워크들도 이런 경향을 반영하고 있습니다. 초기의 프레임워크들은 자바빈즈를 활용하는 기능들을 제공했으나 최신 프레임워크들은 자바의 컬렉션을 적극적으로 활용하고 있습니다.

일반적으로 데이터 전송 객체의 경우는 Map을 그냥 사용하는 것이 좋고 그 외에 복잡한 로직을 수행하는 객체인데 프로퍼티를 많이 사용해야 한다면 Map을 데코레이터로 사용하면 편리합니다. 그리고 일반적으로 클래스를 설계할 때 setter는 되도록 만들지 않는 것이 좋습니다.

프레임워크의 가치
프레임워크(framework)란 단어는 여러 가지 의미로 사용되지만 보통은 어떤 영역의 API들을 사용하기 편리한 형태로 포장해놓은 것을 말합니다. 이를테면 자바 컬렉션 프레임워크는 자바에서 각종 컬렉션을 사용하기 쉽게 모아놓은 것이고 자카르타 프로젝트의 BSF(Bean Scripting Framework)는 자바 애플리케이션에서 자바빈즈나 스크립팅을 하기 쉽게 만든 API의 집합입니다. 그리고 지금 논의하려는 웹 애플리케이션 프레임워크는 웹에서 서블릿 API들을 좀 더 편리하고 객체지향적으로 사용할 수 있게 해주는 것입니다.

현재 웹 애플리케이션 프레임워크의 주류는 단연 스트럿츠(struts)입니다. 전 세계 자바 웹 개발자의 60%가 스트럿츠를 사용하고 있다고 하죠. 하지만 스트럿츠가 있음에도 계속해서 새로운 프레임워크가 등장하고 있습니다. 현재까지 공개된 프레임워크의 숫자는 이미 60개를 넘습니다. 스트럿츠에는 어떤 부족함이 있길래, 그리고 다른 프레임워크들은 뭐가 부족하길래 개발자들이 새로운 프레임워크를 계속 만들까요.


프레임워크의 정의  
스트럭처(structure), 아키텍처(architecture), 프레임워크(framework)란 용어는 기술과 시대가 변하면서 조금씩 그 의미를 달리해 가는 것 같습니다. 스트럭처라는 용어가 Tree와 같은 계층적인 탄탄한 기반 구조를 말하는 반면, 프레임워크는 다소 수평적인 의미를 가지는 하부 구조를 나타냅니다. 또한 아키텍처라는 것은 이 두 부분을 모두 포함하는 더 포괄적인 개념 체계적인 기반 구조를 나타냅니다.

그러나 프레임워크이란 용어는 스트럭처나 아키텍처보다 더 낮은 레벨의 의미를 지닙니다. 즉, 프레임워크의 실체는 때론 API의 집합으로 나타나기도 한다는 것이지요. 썬 마이크로시스템즈의 Java Activation Framework가 그러하고 Java Media Framework가 그러합니다. 그러나 최근에 와서 IBM의 샌프란시스코 프레임워크(San Francisco Framework)라는 용어가 등장하면서 ‘반제품’의 의미를 강하게 띄는 것 같습니다.

알다시피 샌프란시스코 프레임워크는 정형화된 업무를 위한 비즈니스 컴포넌트를 미리 만들어 두고, 이를 조립함으로써 생산성을 극대화시키자는 것이 요지입니다. 어쨌거나 현재의 프레임워크란 것은 ‘기반 틀 구조’라는 모호한 추상적인 개념이기보다는 물리적인 실체이면서 반제품 성격의 구체적이고 체계화된 API를 제공하는 개념이라고 봐야 할 것입니다.
- javaservice.net 이원영님의 글
프레임워크의 가치는 기술적인 영역에서 소모적이고 반복적인 코딩을 프레임워크가 소화하여 개발자가 비즈니스 로직에만 전념할 수 있도록 해주는 데 있습니다. 이를테면 웹 프로그래밍을 한다면 HTTP 프로토콜도 어느 정도 알아야 하고 서블릿 API를 익히고 쿠키도 쓸 줄 알아야 합니다.

하지만 JSF(Java Server Faces) 같은 프레임워크를 이용하면 일반 자바 GUI 프로그래밍을 하듯이 프로그래밍할 수 있습니다. 서블릿 API를 몰라도 웹 애플리케이션을 잘 만들 수 있는 것이죠. 로깅(logging) 프레임워크가 없다면 개발자가 로그를 남기고 싶을 때 직접 파일을 열어서 파일에 내용을 쓰는 등의 복잡한 코드를 써나가야 하지만 log4j와 같은 프레임워크를 쓰면 단순히 Logger 객체에 로그 메시지를 넘겨주는 것으로 로깅을 할 수 있습니다.

EJB도 이런 프레임워크의 일종입니다. EJB가 없다면 분산 트랜잭션이 생길 때마다 개발자가 직접 분산 트랜잭션 관리를 해야 하지만 EJB 엔진이 이를 대신해주면 개발자는 비즈니스 로직의 구현에만 집중할 수 있죠. 이처럼 기술적인 영역이 달라질 때마다 개발자는 새로운 API를 익히고 또 소모적이고 반복적인 코드를 써야 하는데 해당 분야에 좋은 프레임워크가 있으면 중복 코드도 쉽게 제거할 수 있고 기술적인 세부사항에 신경쓰지 않아도 됩니다.

결국 프레임워크의 가치는 개발자의 할 일을 줄여주는 데 있는 것입니다.

하지만 간혹 프레임워크의 가치를 유지보수의 용이함에서 찾는 경우가 있습니다. 프레임워크가 꽉 짜놓은 틀 안에서만 코딩을 하면 개발자들이 모두 일관된 방식으로 개발하기 때문에 다른 사람이 작성한 코드를 파악하기 쉬워서 유지보수하기도 쉽다는 것이죠.

그러나 이렇게 일관성을 필요 이상으로 강조하면 때때로 문제가 될 수 있습니다. PEAA(Patterns of Enterprise Application Architecture)에서는 기업용 애플리케이션(Enterprise Application)에는 모두 각각의 어려움을 가지고 있는데 이 어려움은 모두 종류가 다르기 때문에 이 모든 어려움을 해결하는 하나의 아키텍처는 불가능하다고 말합니다. 다른 문제 영역에는 다른 해결책을 써야한다는 것이죠. 그런데 프레임워크를 통해서 지나치게 틀을 고정시켜 놓게 되면 그 틀에서 수용할 수 없는 문제를 만났을 때 아주 비효율적인 코딩을 하게 됩니다. 물론 이것은 프레임워크 자체가 가져오는 직접적인 문제는 아닙니다.

그보다도 프레임워크의 장점을 ‘일관성’에서 찾는 사람들이 일반적으로 범하는 오류입니다. 일관성의 가치를 지나치게 높게 평가한 나머지 코딩 패턴에 너무 많은 구속을 하게 되고 그 결과로 문제 영역에 맞는 패턴을 사용하는 것이 아니라 패턴에 코드를 끼워 맞추는 코드를 만들게 됩니다. 그래서 결과적으로 프레임워크를 유연하게 활용하지 못하고 오히려 코드가 더 길고 복잡해지는 경우가 많습니다. 앞서 언급한 디자인 패턴의 오용과 같은 것이죠.

사실 일관성의 문제는 프레임워크보다도 컨벤션을 통해 해결해야 하는 것입니다. XP에서는 코드에 대한 소유권을 개발자 한 명이 아니라 모두가 갖게 되는 코드 공동 소유(collective code ownership)를 권장합니다. 그래서 다른 사람의 코드를 읽고 수정하는 일은 아주 일상적인 일입니다. 이런 상황에서 개발자 각각의 개성이 강해서 서로의 코드를 읽기 어렵다면 문제가 크겠죠.

그럼에도 불구하고 XP는 프레임워크에 대해 약간의 부정적인 입장을 취하고 있습니다. 이것은 프레임워크가 디자인 패턴과 같은 문제를 발생시킬 수 있다고 보기 때문이기도 하지만 고정적인 패턴에 따라 코딩된 코드보다도 코드 컨벤션이 통일되고 리팩토링이 잘된 코드가 더 읽기 쉽다고 보기 때문입니다. 실질적으로 코딩 패턴의 강제를 통해 얻을 수 있는 것은 그리 많지 않습니다. 어차피 이해하기 가장 어려운 부분은 비즈니스 로직인데 이 부분까지 패턴이나 프레임워크로 강제할 수는 없기 때문입니다.

결국 아무리 다양한 기능을 제공한다 해도 결과적으로 개발자가 작성해야 하는 코드를 줄여주지 못한다면 좋은 프레임워크라고 할 수 없습니다. 스트럿츠에 대한 대안이 계속 쏟아져 나오고 있는 것도 실제적으로 스트럿츠가 개발자의 일을 많이 줄여주지 못하기 때문입니다.

스트럿츠의 강점으로 컨트롤러(controller)를 꼽는데 사실 스트럿츠의 컨트롤러는 HttpServlet의 역할을 Action으로, web.xml의 역할을 struts-config.xml로 옮겨온 것 밖에 없습니다. 사실 스트럿츠의 컨트롤러 기능 자체는 서블릿 API의 래퍼 몇 개로 완전히 대체할 수 있습니다. 이런 점을 깨달은 개발자들이 정말로 개발자의 일을 줄여주는 것을 목표로 다양한 프레임워크를 쏟아내고 있는 것입니다.

프레임워크의 구성 요소
실제로 어떤 프레임워크를 사용하든 그대로 사용하는 경우는 그다지 많지 않습니다. 다른 프레임워크와 결합해서 쓰기도 하고 상속을 통해서 확장하거나 혹은 직접 소스코드를 고쳐서 사용하기도 합니다. 어차피 모든 영역에 맞는 프레임워크는 없기 때문에 각자의 환경에 맞게 커스터마이징하는 것은 바람직한 일입니다. 그래서 이번에는 프레임워크를 구성하는 요소들에 대해 살펴보겠습니다.

제어 구조의 반전
요즘 나오는 프레임워크들은 공통적으로 제어 구조의 반전(IoC, Inversion of Control)를 내세우고 있습니다. 제어 구조의 반전이라는 말의 의미는 개발자가 제어하던 것을 프레임워크로 옮겨서 프레임워크에서 제어한다는 것입니다.

예를 들면 GUI로 입력을 받는 프로그램을 개발자가 직접 모든 부분을 만든다면 입력 항목을 출력하고 사용자의 입력을 기다린 다음 사용자의 입력을 판단해서 로직을 실행하는 코드를 모두 순차적으로 쓰게 됩니다. 그런데 이걸 GUI 프레임워크를 이용하면 화면과 입력에 관한 부분은 프레임워크가 처리하고 사용자는 단지 사용자가 입력했을 때 반응하는 이벤트 핸들러만 작성합니다. 즉 입력 제어가 개발자에서 프레임워크로 이동하는 것입니다.

윈도우 프로그래밍에서 콜백(callback)이라고 부르는 것도 비슷한 개념입니다. 사실 서블릿 자체도 일종의 프레임워크이고 이미 IoC가 일어나고 있습니다. HTTP 요청을 대기하고 받아들여서 서블릿을 실행하는 코드를 개발자가 작성하는 것이 아니라 서블릿 엔진이 제공하고 개발자는 단지 서블릿만을 작성하게 되죠. 결국 이런 컨트롤의 반전을 통해서 중복 코드를 효과적으로 제거할 수 있습니다. 프레임워크의 목적이 소모적이고 반복적인 코드를 제거하는 것인 만큼 대부분의 프레임워크에는 프레임워크 개발자가 의도했든 아니든 IoC가 사용되고 있습니다.

하지만 IoC 역시 주의해서 사용해야 합니다. 일상적인 프로그램과는 달리 제어 구조가 거꾸로 되기 때문에 디버깅하기도 어렵고 컨트롤이 반전된 부분에 대한 선행 지식이 없으면 이해하기 어려운 코드가 됩니다. 그래서 IoC는 꼭 필요한 경우가 아니면 사용하지 않는 것이 좋습니다.

MVC 패턴
요즘 프레임워크가 IoC를 강조한다면 스트럿츠처럼 좀 오래된 프레임워크들은 MVC(Model-View-Controller)를 내세웠습니다. UI가 포함된 프레임워크에는 거의 필수적으로 사용되는 패턴입니다. 원래 MVC는 웹보다도 일반 GUI 애플리케이션을 개발할 때 UI(User Interface)와 비즈니스 로직을 효과적으로 분리하기 위해서 고안되었습니다. 현재는 웹에서도 그 효과가 입증되었기 때문에 널리 쓰이고 있습니다.

MVC의 구조는 MFC 프로그램에서 등장했던 Document-View 구조를 한 차원 더 발전시킨 것으로 프로그램의 구성 요소를 모델, 뷰, 컨트롤러로 나누어서 각각 다른 역할을 맡게 하는 것입니다. 모델은 비즈니스 로직을 담는 객체를 말하며 이 모델은 PEAA에서 말하는 도메인 모델이 됩니다.

프레임워크의 가치는 기술적인 영역에서 소모적이고 반복적인 코딩을 프레임워크가 소화하여 개발자가 비즈니스 로직에만 전념할 수 있도록 해주는 데 있습니다. 이를테면 웹 프로그래밍을 한다면 HTTP 프로토콜도 어느 정도 알아야 하고 서블릿 API를 익히고 쿠키도 쓸 줄 알아야 합니다.

하지만 JSF(Java Server Faces) 같은 프레임워크를 이용하면 일반 자바 GUI 프로그래밍을 하듯이 프로그래밍할 수 있습니다. 서블릿 API를 몰라도 웹 애플리케이션을 잘 만들 수 있는 것이죠. 로깅(logging) 프레임워크가 없다면 개발자가 로그를 남기고 싶을 때 직접 파일을 열어서 파일에 내용을 쓰는 등의 복잡한 코드를 써나가야 하지만 log4j와 같은 프레임워크를 쓰면 단순히 Logger 객체에 로그 메시지를 넘겨주는 것으로 로깅을 할 수 있습니다.

EJB도 이런 프레임워크의 일종입니다. EJB가 없다면 분산 트랜잭션이 생길 때마다 개발자가 직접 분산 트랜잭션 관리를 해야 하지만 EJB 엔진이 이를 대신해주면 개발자는 비즈니스 로직의 구현에만 집중할 수 있죠. 이처럼 기술적인 영역이 달라질 때마다 개발자는 새로운 API를 익히고 또 소모적이고 반복적인 코드를 써야 하는데 해당 분야에 좋은 프레임워크가 있으면 중복 코드도 쉽게 제거할 수 있고 기술적인 세부사항에 신경쓰지 않아도 됩니다.

결국 프레임워크의 가치는 개발자의 할 일을 줄여주는 데 있는 것입니다.

하지만 간혹 프레임워크의 가치를 유지보수의 용이함에서 찾는 경우가 있습니다. 프레임워크가 꽉 짜놓은 틀 안에서만 코딩을 하면 개발자들이 모두 일관된 방식으로 개발하기 때문에 다른 사람이 작성한 코드를 파악하기 쉬워서 유지보수하기도 쉽다는 것이죠.

그러나 이렇게 일관성을 필요 이상으로 강조하면 때때로 문제가 될 수 있습니다. PEAA(Patterns of Enterprise Application Architecture)에서는 기업용 애플리케이션(Enterprise Application)에는 모두 각각의 어려움을 가지고 있는데 이 어려움은 모두 종류가 다르기 때문에 이 모든 어려움을 해결하는 하나의 아키텍처는 불가능하다고 말합니다. 다른 문제 영역에는 다른 해결책을 써야한다는 것이죠. 그런데 프레임워크를 통해서 지나치게 틀을 고정시켜 놓게 되면 그 틀에서 수용할 수 없는 문제를 만났을 때 아주 비효율적인 코딩을 하게 됩니다. 물론 이것은 프레임워크 자체가 가져오는 직접적인 문제는 아닙니다.

그보다도 프레임워크의 장점을 ‘일관성’에서 찾는 사람들이 일반적으로 범하는 오류입니다. 일관성의 가치를 지나치게 높게 평가한 나머지 코딩 패턴에 너무 많은 구속을 하게 되고 그 결과로 문제 영역에 맞는 패턴을 사용하는 것이 아니라 패턴에 코드를 끼워 맞추는 코드를 만들게 됩니다. 그래서 결과적으로 프레임워크를 유연하게 활용하지 못하고 오히려 코드가 더 길고 복잡해지는 경우가 많습니다. 앞서 언급한 디자인 패턴의 오용과 같은 것이죠.

사실 일관성의 문제는 프레임워크보다도 컨벤션을 통해 해결해야 하는 것입니다. XP에서는 코드에 대한 소유권을 개발자 한 명이 아니라 모두가 갖게 되는 코드 공동 소유(collective code ownership)를 권장합니다. 그래서 다른 사람의 코드를 읽고 수정하는 일은 아주 일상적인 일입니다. 이런 상황에서 개발자 각각의 개성이 강해서 서로의 코드를 읽기 어렵다면 문제가 크겠죠.

그럼에도 불구하고 XP는 프레임워크에 대해 약간의 부정적인 입장을 취하고 있습니다. 이것은 프레임워크가 디자인 패턴과 같은 문제를 발생시킬 수 있다고 보기 때문이기도 하지만 고정적인 패턴에 따라 코딩된 코드보다도 코드 컨벤션이 통일되고 리팩토링이 잘된 코드가 더 읽기 쉽다고 보기 때문입니다. 실질적으로 코딩 패턴의 강제를 통해 얻을 수 있는 것은 그리 많지 않습니다. 어차피 이해하기 가장 어려운 부분은 비즈니스 로직인데 이 부분까지 패턴이나 프레임워크로 강제할 수는 없기 때문입니다.

결국 아무리 다양한 기능을 제공한다 해도 결과적으로 개발자가 작성해야 하는 코드를 줄여주지 못한다면 좋은 프레임워크라고 할 수 없습니다. 스트럿츠에 대한 대안이 계속 쏟아져 나오고 있는 것도 실제적으로 스트럿츠가 개발자의 일을 많이 줄여주지 못하기 때문입니다.

스트럿츠의 강점으로 컨트롤러(controller)를 꼽는데 사실 스트럿츠의 컨트롤러는 HttpServlet의 역할을 Action으로, web.xml의 역할을 struts-config.xml로 옮겨온 것 밖에 없습니다. 사실 스트럿츠의 컨트롤러 기능 자체는 서블릿 API의 래퍼 몇 개로 완전히 대체할 수 있습니다. 이런 점을 깨달은 개발자들이 정말로 개발자의 일을 줄여주는 것을 목표로 다양한 프레임워크를 쏟아내고 있는 것입니다.

프레임워크의 구성 요소
실제로 어떤 프레임워크를 사용하든 그대로 사용하는 경우는 그다지 많지 않습니다. 다른 프레임워크와 결합해서 쓰기도 하고 상속을 통해서 확장하거나 혹은 직접 소스코드를 고쳐서 사용하기도 합니다. 어차피 모든 영역에 맞는 프레임워크는 없기 때문에 각자의 환경에 맞게 커스터마이징하는 것은 바람직한 일입니다. 그래서 이번에는 프레임워크를 구성하는 요소들에 대해 살펴보겠습니다.

제어 구조의 반전
요즘 나오는 프레임워크들은 공통적으로 제어 구조의 반전(IoC, Inversion of Control)를 내세우고 있습니다. 제어 구조의 반전이라는 말의 의미는 개발자가 제어하던 것을 프레임워크로 옮겨서 프레임워크에서 제어한다는 것입니다.

예를 들면 GUI로 입력을 받는 프로그램을 개발자가 직접 모든 부분을 만든다면 입력 항목을 출력하고 사용자의 입력을 기다린 다음 사용자의 입력을 판단해서 로직을 실행하는 코드를 모두 순차적으로 쓰게 됩니다. 그런데 이걸 GUI 프레임워크를 이용하면 화면과 입력에 관한 부분은 프레임워크가 처리하고 사용자는 단지 사용자가 입력했을 때 반응하는 이벤트 핸들러만 작성합니다. 즉 입력 제어가 개발자에서 프레임워크로 이동하는 것입니다.

윈도우 프로그래밍에서 콜백(callback)이라고 부르는 것도 비슷한 개념입니다. 사실 서블릿 자체도 일종의 프레임워크이고 이미 IoC가 일어나고 있습니다. HTTP 요청을 대기하고 받아들여서 서블릿을 실행하는 코드를 개발자가 작성하는 것이 아니라 서블릿 엔진이 제공하고 개발자는 단지 서블릿만을 작성하게 되죠. 결국 이런 컨트롤의 반전을 통해서 중복 코드를 효과적으로 제거할 수 있습니다. 프레임워크의 목적이 소모적이고 반복적인 코드를 제거하는 것인 만큼 대부분의 프레임워크에는 프레임워크 개발자가 의도했든 아니든 IoC가 사용되고 있습니다.

하지만 IoC 역시 주의해서 사용해야 합니다. 일상적인 프로그램과는 달리 제어 구조가 거꾸로 되기 때문에 디버깅하기도 어렵고 컨트롤이 반전된 부분에 대한 선행 지식이 없으면 이해하기 어려운 코드가 됩니다. 그래서 IoC는 꼭 필요한 경우가 아니면 사용하지 않는 것이 좋습니다.

MVC 패턴
요즘 프레임워크가 IoC를 강조한다면 스트럿츠처럼 좀 오래된 프레임워크들은 MVC(Model-View-Controller)를 내세웠습니다. UI가 포함된 프레임워크에는 거의 필수적으로 사용되는 패턴입니다. 원래 MVC는 웹보다도 일반 GUI 애플리케이션을 개발할 때 UI(User Interface)와 비즈니스 로직을 효과적으로 분리하기 위해서 고안되었습니다. 현재는 웹에서도 그 효과가 입증되었기 때문에 널리 쓰이고 있습니다.

MVC의 구조는 MFC 프로그램에서 등장했던 Document-View 구조를 한 차원 더 발전시킨 것으로 프로그램의 구성 요소를 모델, 뷰, 컨트롤러로 나누어서 각각 다른 역할을 맡게 하는 것입니다. 모델은 비즈니스 로직을 담는 객체를 말하며 이 모델은 PEAA에서 말하는 도메인 모델이 됩니다.

프레임워크의 가치는 기술적인 영역에서 소모적이고 반복적인 코딩을 프레임워크가 소화하여 개발자가 비즈니스 로직에만 전념할 수 있도록 해주는 데 있습니다. 이를테면 웹 프로그래밍을 한다면 HTTP 프로토콜도 어느 정도 알아야 하고 서블릿 API를 익히고 쿠키도 쓸 줄 알아야 합니다.

하지만 JSF(Java Server Faces) 같은 프레임워크를 이용하면 일반 자바 GUI 프로그래밍을 하듯이 프로그래밍할 수 있습니다. 서블릿 API를 몰라도 웹 애플리케이션을 잘 만들 수 있는 것이죠. 로깅(logging) 프레임워크가 없다면 개발자가 로그를 남기고 싶을 때 직접 파일을 열어서 파일에 내용을 쓰는 등의 복잡한 코드를 써나가야 하지만 log4j와 같은 프레임워크를 쓰면 단순히 Logger 객체에 로그 메시지를 넘겨주는 것으로 로깅을 할 수 있습니다.

EJB도 이런 프레임워크의 일종입니다. EJB가 없다면 분산 트랜잭션이 생길 때마다 개발자가 직접 분산 트랜잭션 관리를 해야 하지만 EJB 엔진이 이를 대신해주면 개발자는 비즈니스 로직의 구현에만 집중할 수 있죠. 이처럼 기술적인 영역이 달라질 때마다 개발자는 새로운 API를 익히고 또 소모적이고 반복적인 코드를 써야 하는데 해당 분야에 좋은 프레임워크가 있으면 중복 코드도 쉽게 제거할 수 있고 기술적인 세부사항에 신경쓰지 않아도 됩니다.

결국 프레임워크의 가치는 개발자의 할 일을 줄여주는 데 있는 것입니다.

하지만 간혹 프레임워크의 가치를 유지보수의 용이함에서 찾는 경우가 있습니다. 프레임워크가 꽉 짜놓은 틀 안에서만 코딩을 하면 개발자들이 모두 일관된 방식으로 개발하기 때문에 다른 사람이 작성한 코드를 파악하기 쉬워서 유지보수하기도 쉽다는 것이죠.

그러나 이렇게 일관성을 필요 이상으로 강조하면 때때로 문제가 될 수 있습니다. PEAA(Patterns of Enterprise Application Architecture)에서는 기업용 애플리케이션(Enterprise Application)에는 모두 각각의 어려움을 가지고 있는데 이 어려움은 모두 종류가 다르기 때문에 이 모든 어려움을 해결하는 하나의 아키텍처는 불가능하다고 말합니다. 다른 문제 영역에는 다른 해결책을 써야한다는 것이죠. 그런데 프레임워크를 통해서 지나치게 틀을 고정시켜 놓게 되면 그 틀에서 수용할 수 없는 문제를 만났을 때 아주 비효율적인 코딩을 하게 됩니다. 물론 이것은 프레임워크 자체가 가져오는 직접적인 문제는 아닙니다.

그보다도 프레임워크의 장점을 ‘일관성’에서 찾는 사람들이 일반적으로 범하는 오류입니다. 일관성의 가치를 지나치게 높게 평가한 나머지 코딩 패턴에 너무 많은 구속을 하게 되고 그 결과로 문제 영역에 맞는 패턴을 사용하는 것이 아니라 패턴에 코드를 끼워 맞추는 코드를 만들게 됩니다. 그래서 결과적으로 프레임워크를 유연하게 활용하지 못하고 오히려 코드가 더 길고 복잡해지는 경우가 많습니다. 앞서 언급한 디자인 패턴의 오용과 같은 것이죠.

사실 일관성의 문제는 프레임워크보다도 컨벤션을 통해 해결해야 하는 것입니다. XP에서는 코드에 대한 소유권을 개발자 한 명이 아니라 모두가 갖게 되는 코드 공동 소유(collective code ownership)를 권장합니다. 그래서 다른 사람의 코드를 읽고 수정하는 일은 아주 일상적인 일입니다. 이런 상황에서 개발자 각각의 개성이 강해서 서로의 코드를 읽기 어렵다면 문제가 크겠죠.

그럼에도 불구하고 XP는 프레임워크에 대해 약간의 부정적인 입장을 취하고 있습니다. 이것은 프레임워크가 디자인 패턴과 같은 문제를 발생시킬 수 있다고 보기 때문이기도 하지만 고정적인 패턴에 따라 코딩된 코드보다도 코드 컨벤션이 통일되고 리팩토링이 잘된 코드가 더 읽기 쉽다고 보기 때문입니다. 실질적으로 코딩 패턴의 강제를 통해 얻을 수 있는 것은 그리 많지 않습니다. 어차피 이해하기 가장 어려운 부분은 비즈니스 로직인데 이 부분까지 패턴이나 프레임워크로 강제할 수는 없기 때문입니다.

결국 아무리 다양한 기능을 제공한다 해도 결과적으로 개발자가 작성해야 하는 코드를 줄여주지 못한다면 좋은 프레임워크라고 할 수 없습니다. 스트럿츠에 대한 대안이 계속 쏟아져 나오고 있는 것도 실제적으로 스트럿츠가 개발자의 일을 많이 줄여주지 못하기 때문입니다.

스트럿츠의 강점으로 컨트롤러(controller)를 꼽는데 사실 스트럿츠의 컨트롤러는 HttpServlet의 역할을 Action으로, web.xml의 역할을 struts-config.xml로 옮겨온 것 밖에 없습니다. 사실 스트럿츠의 컨트롤러 기능 자체는 서블릿 API의 래퍼 몇 개로 완전히 대체할 수 있습니다. 이런 점을 깨달은 개발자들이 정말로 개발자의 일을 줄여주는 것을 목표로 다양한 프레임워크를 쏟아내고 있는 것입니다.

프레임워크의 구성 요소
실제로 어떤 프레임워크를 사용하든 그대로 사용하는 경우는 그다지 많지 않습니다. 다른 프레임워크와 결합해서 쓰기도 하고 상속을 통해서 확장하거나 혹은 직접 소스코드를 고쳐서 사용하기도 합니다. 어차피 모든 영역에 맞는 프레임워크는 없기 때문에 각자의 환경에 맞게 커스터마이징하는 것은 바람직한 일입니다. 그래서 이번에는 프레임워크를 구성하는 요소들에 대해 살펴보겠습니다.

제어 구조의 반전
요즘 나오는 프레임워크들은 공통적으로 제어 구조의 반전(IoC, Inversion of Control)를 내세우고 있습니다. 제어 구조의 반전이라는 말의 의미는 개발자가 제어하던 것을 프레임워크로 옮겨서 프레임워크에서 제어한다는 것입니다.

예를 들면 GUI로 입력을 받는 프로그램을 개발자가 직접 모든 부분을 만든다면 입력 항목을 출력하고 사용자의 입력을 기다린 다음 사용자의 입력을 판단해서 로직을 실행하는 코드를 모두 순차적으로 쓰게 됩니다. 그런데 이걸 GUI 프레임워크를 이용하면 화면과 입력에 관한 부분은 프레임워크가 처리하고 사용자는 단지 사용자가 입력했을 때 반응하는 이벤트 핸들러만 작성합니다. 즉 입력 제어가 개발자에서 프레임워크로 이동하는 것입니다.

윈도우 프로그래밍에서 콜백(callback)이라고 부르는 것도 비슷한 개념입니다. 사실 서블릿 자체도 일종의 프레임워크이고 이미 IoC가 일어나고 있습니다. HTTP 요청을 대기하고 받아들여서 서블릿을 실행하는 코드를 개발자가 작성하는 것이 아니라 서블릿 엔진이 제공하고 개발자는 단지 서블릿만을 작성하게 되죠. 결국 이런 컨트롤의 반전을 통해서 중복 코드를 효과적으로 제거할 수 있습니다. 프레임워크의 목적이 소모적이고 반복적인 코드를 제거하는 것인 만큼 대부분의 프레임워크에는 프레임워크 개발자가 의도했든 아니든 IoC가 사용되고 있습니다.

하지만 IoC 역시 주의해서 사용해야 합니다. 일상적인 프로그램과는 달리 제어 구조가 거꾸로 되기 때문에 디버깅하기도 어렵고 컨트롤이 반전된 부분에 대한 선행 지식이 없으면 이해하기 어려운 코드가 됩니다. 그래서 IoC는 꼭 필요한 경우가 아니면 사용하지 않는 것이 좋습니다.

MVC 패턴
요즘 프레임워크가 IoC를 강조한다면 스트럿츠처럼 좀 오래된 프레임워크들은 MVC(Model-View-Controller)를 내세웠습니다. UI가 포함된 프레임워크에는 거의 필수적으로 사용되는 패턴입니다. 원래 MVC는 웹보다도 일반 GUI 애플리케이션을 개발할 때 UI(User Interface)와 비즈니스 로직을 효과적으로 분리하기 위해서 고안되었습니다. 현재는 웹에서도 그 효과가 입증되었기 때문에 널리 쓰이고 있습니다.

MVC의 구조는 MFC 프로그램에서 등장했던 Document-View 구조를 한 차원 더 발전시킨 것으로 프로그램의 구성 요소를 모델, 뷰, 컨트롤러로 나누어서 각각 다른 역할을 맡게 하는 것입니다. 모델은 비즈니스 로직을 담는 객체를 말하며 이 모델은 PEAA에서 말하는 도메인 모델이 됩니다.

데이터베이스에 접근하는 것도 모델 객체의 몫입니다. 모델 객체들 자체로 UML 클래스 다이어그램을 그린다면 그 자체로 비즈니스 로직의 표현이 될 수 있습니다. 뷰는 화면 UI를 구성하는 요소이며 일반적으로 웹에서는 JSP가 뷰의 역할을 맡습니다.

그리고 컨트롤러는 사용자의 요청을 받아서 모델 객체를 실행하고 그 결과를 뷰로 전달하는 역할을 맡게 되는데 일반적으로 이 부분은 프레임워크에서 담당하며 개발자는 어떤 모델을 실행하고 어떤 뷰를 선택할 것인지를 컨트롤러에 알려주기만 하면 됩니다. 이런 구조가 이상적인 MVC 패턴의 구조입니다.

MVC 패턴의 가장 큰 장점은 모델과 뷰의 분리를 통해 화면 UI를 위한 코드와 비즈니스 로직을 위한 코드가 섞이지 않는다는 것입니다. 그래서 때때로 화면 UI 개발자와 비즈니스 로직 개발자를 따로 두는 것도 가능합니다. 그리고 이 패턴을 통해 프로그램 디자인의 기본 원칙인 low coupling, high cohesion을 자연스럽게 달성할 수 있죠.

무언가 변경하고 싶을 때 여러 컴포넌트들을 같이 변경해야 하는 coupling은 줄이면서 컴포넌트간의 협력(cohesion)은 컨트롤러를 통해 자유롭게 할 수 있는 것입니다. 또한 모델과 뷰가 분리되면 한 모델에 여러 가지 다양한 뷰를 붙이는 것도 가능하고 그 반대의 경우도 가능합니다. 그래서 요구사항의 복잡도는 높지만 규격이 잘 정해진 애플리케이션을 만들 때는 개발자의 일을 획기적으로 줄일 수 있습니다. MVC 프레임워크란 결국 이런 모델과 뷰의 분리를 효과적으로 할 수 있는 컨트롤러를 제공한다는 데에 그 가치가 있습니다.

XML 설정 파일의 역할
프레임워크에서 또 하나 중요한 것은 설정 파일의 활용입니다. 유행처럼 번져나가기 시작한 XML 설정 파일은 요즘 거의 프레임워크의 필수사항처럼 되어가고 있습니다. 초기에 설정 파일이 등장하기 시작한 이유는 소스의 내용 중 상수에 해당하는 값들을 설정 파일로 빼 놓으면 컴파일을 다시 하지 않아도 동작을 변경할 수 있기 때문입니다. C++ 등의 언어에서는 컴파일 후에 링크까지 해야 하기 때문에 이 차이는 적지 않습니다.

하지만 자바에서는 필요한 부분만 간단하게 컴파일하면 바로 동작하게 할 수 있고 XP의 지속적인 통합(Continuous Integration)이 일반화되면서 소스를 변경하는 비용이 크게 줄어들었죠. 그래서 사실 지금은 XML 설정 파일에 상수를 넣어두는 것이 자바 코드에 넣는 것보다 개발시의 변경 비용이 적다고 하기 힘듭니다.

하지만 여전히 애플리케이션 사용자의 입장에서는 XML이 훨씬 변경하기 쉽습니다. 클라이언트로 배포된 프로그램 혹은 서버에 배치된 프로그램의 설정을 바꾸기 위해 다시 빌드를 할 수는 없는 일이니까요.

하지만 이런 비용적인 측면 외에도 XML 설정 파일은 자바 코드보다 좀 더 정보를 서술적(descriptive)으로 담아놓을 수 있다는 장점이 있습니다.

XML은 구조적인 정보와 메타 정보를 포함한 모든 정보를 한 눈에 알아보기 쉽게 정리할 수 있습니다. 또한 자바 코드에서 여러 클래스로 분산될 수 있는 설정 내용을 개발자가 관리하기 편한 단위로 손쉽게 통합해서 관리할 수 있죠.

자바의 프로퍼티도 설정 파일 용도로 많이 사용되어 왔습니다만 구조적인 정보를 제대로 정의하기 어렵기 때문에 설정 파일로는 부적합합니다. 게다가 프로퍼티는 한글 문제도 있기 때문에 앞으로 프로퍼티는 쓰지 않는 것이 좋다고 봅니다.

뭐든지 잘못 쓰면 해가 된다는 점에서 XML 역시 예외가 아닙니다. XML 설정 파일을 오용하는 대표적인 사례로 Jelly 스크립트가 있습니다. 자카르타 commons 프로젝트의 하나인데 XML로 스크립팅을 할 수 있게 해주는 엔진입니다. Jelly 스크립트의 파워는 여전히 진짜 프로그래밍 언어에 미치지 못하면서 Jelly로 스크립트를 짜면 가독성도 떨어지고 리팩토링하기 어렵기 때문에 점점 지저분한 코드가 되고 맙니다. Ant도 이와 비슷한 문제점이 있습니다. 그래서 복잡한 제어 흐름이 필요한 부분에서는 자바 코드를 직접 이용하고 이것이 무겁다면 좀더 가벼운 스크립트 언어를 자바에 임베딩해서 사용하는 것이 좋습니다.

단, SQL의 경우는 언어 자체가 결과물에 대한 정의를 표현하는 것이기 때문에 복잡한 제어 구조가 필요하지 않아 XML로 별 무리 없이 소화가 가능합니다. 실제로 XML과 JDBC를 결합시킨 iBatis에서 XML로 SQL들을 관리해보면 실제 자바 코드보다 훨씬 쉽고 보기 좋게 코딩할 수 있습니다.

또 한 가지 주의해야 할 것은 XML 설정 파일을 이용하게 만들더라도 XML 없이도 프레임워크가 동작하게 만들어야 한다는 것입니다. 앞서의 iBatis는 구현된 기능들이 정말 편리하고 좋지만 각 컴포넌트들이 지나치게 강하게 결합되어 있고 XML 설정 파일 없이는 아무 것도 하지 못합니다. 그래서 추가 기능을 넣고 싶다거나 개선하고 싶은 부분이 있을 때 상속 등의 방법으로는 해결하기 어렵고 거의 대부분 소스를 수정해야 해결할 수 있죠. 그래서 프레임워크를 만들더라도 일단 자바 코드로 모두 만든 다음 마지막에 XML로 설정 내용들을 빼는 작업을 하는 게 좋습니다.

살펴볼만한 프레임워크
그림도 코드도 없는 지루한 텍스트를 읽어 내려오느라 지루했을 독자들을 위해 구체적인 프레임워크들을 몇 가지 소개할까 합니다. 웹 애플리케이션 프레임워크 뿐 아니라 여러 영역에서 유용하다고 생각되는 것들을 뽑았습니다. 단지 필자의 경험 속에서 유용했던 것들이므로 한 번 살펴볼 가치가 있다는 정도로 받아들이면 될 것입니다.

스트럿츠와 JSF
웹 애플리케이션 프레임워크로는 스트럿츠가 현재의 주류이며 JSF가 일부에서 미래의 주류로 꼽힙니다. 모두 한 사람이 주도한 프레임워크입니다.

필자의 판단을 덧붙이면, 스트럿츠는 실제로 개발자의 일을 거의 줄여주지 못하며 오히려 늘이는 경우조차 간혹 있습니다. 스트럿츠를 쓸 바에는 직접 스트럿츠와 유사한 형태로 서블릿을 직접 사용하도록 재구성하는 것이 좋으리라 생각됩니다. 물론 유효성 검사 기능과 Tiles는 좋은 기능이지만 이들은 스트럿츠와 분리해서도 사용 가능합니다.

JSF는 미래의 주류라고 하기엔 부족함이 너무 많습니다. 썬에서 Java Studio Creator를 통해 멋진 가능성을 내보이긴 했지만 그런 IDE를 사용하지 않을 때의 JSF 코딩은 너무 번잡하고 복잡도도 큽니다. 이는 구조적으로 아직 개선의 여지가 많다는 것이죠. JSF 스펙 역시 꾸준히 발전하고 있으니 미래는 어떨지 모르겠지만 지금 JSF를 사용하는 것은 시기상조라 생각됩니다. 오히려 JSF와 비슷한 구조인 Tapestry가 더 완성도가 높다는 평가를 받고 있습니다.

Turbine
Turbine도 꽤 널리 쓰이는 프레임워크 중 하나입니다. 기본 틀은 스트럿츠와 비슷하나 훨씬 많은 부분들을 커버하고 있기 때문에 코딩량은 상당히 줄어듭니다. 개발자의 편의를 위한 클래스들도 많으며 Turbine 개발 과정에서 만들어진 컴포넌트들이 상당수 독립해서 별도의 프로젝트로 진행되고 있습니다. 전에는 유연성이 다소 떨어진다는 것이 단점이었는데 점점 나아지고 있습니다.

Spring
Spring은 요즘 주목 받는 프레임워크로 AOP(Aspect Oriented Programming)를 내세우고 있으며 low coupling, high cohesion이라는 디자인 원칙이 잘 지켜진 프레임워크입니다. 딱히 웹을 염두에 두었다기보다 종합적인 애플리케이션 프레임워크이기 때문에 부분별로 사용할 수도 있고 다른 프레임워크에 붙여서 사용하기도 합니다. 데이터베이스와 관련해서 풍부한 API를 제공하며, Mock Object를 프레임워크 자체에서 제공하고 있다는 것도 특이한 점입니다. 현재 가장 추천할 만한 프레임워크입니다.

iBatis
데이터베이스와 관련해서는 앞서의 Spring으로도 충분할 수 있지만 XML로 SQL을 관리하고자 한다면 iBatis가 최선의 선택일 것입니다. 내부적인 코드의 품질은 다소 떨어지지만 기능상으로는 별다른 부족함이 없습니다. Spring과 iBatis가 JDBC에 대한 단순 래퍼 수준의 API를 제공한다면 Hibernate는 OR(Object-Relation) 맵핑을 통해서 개발자는 객체만을 바라보고 데이터베이스를 간접적으로 이용할 수 있는 프레임워크입니다. 기본적으로 구조가 깔끔하고 편리하다는 평가를 받고 있으며 비즈니스 로직을 도메인 모델로 직접 다룰 수 있다는 점에서 높이 평가할만 합니다.

OSCache, Quartz, JUnit
이외에 웹에서 캐싱 기법으로 성능을 향상시키기 위한 OSCache, 스케쥴러를 만들기 위한 API인 Quartz도 아주 유용합니다. 물론 테스팅 프레임워크로 JUnit은 언급할 필요가 없을 것입니다. 이런 프레임워크들을 꼭 사용하지 않더라도 한 번 살펴보면 배울 만한 점들을 발견할 수 있고 이런 점들이 나중에 도움이 되는 경우가 적지 않을 것입니다.

많은 논란거리들
패턴이나 프레임워크는 특정 영역에 대해 어느 정도 검증된 해결책입니다. 그래서 때때로 이에 대한 맹신이 나타나기도 합니다. 검증된 방법이라는 논리로 너무 쉽게 문제 영역에 패턴을 적용하거나 프레임워크를 사용하곤 하죠. 하지만 그 문제 영역이 패턴이나 프레임워크를 활용할 수 있는 바로 그 영역이라는 점은 검증하기 힘든 것입니다. 때문에 패턴 중심적인 사고 방식이나 프레임워크에 끼워 맞추는 코딩을 지양하고 문제 영역의 해결에 충실하면서 패턴을 생각하는 것이 더 좋습니다.

바둑 격언 중에 ‘정석은 배우고, 잊어버려라’라는 말이 있습니다. 정석을 익혀서 알고는 있되 실전에서는 정석에 집착하지 말고 상황에 따라 유연하게 대처해야 좋은 바둑을 둘 수 있다는 뜻입니다. 패턴은 프로그래밍의 정석과도 같습니다. 상황에 대한 검증된 해결책이지만 그 상황은 계속 변하고 그 변화에 빠르게 대처해야 합니다.

패턴이나 프레임워크에 대한 맹신으로 빠져드는 것을 경계하기 위해 어떤 사람들은 무엇이든지 장단점이 있으니 너무 집착하지 말라고 말합니다. 이것은 옳은 말이지만 사실 큰 의미가 있는 말은 아닙니다. 무엇이든 장단이 있다는 사실은 누구도 부인할 수 없지만 또한 누구도 부인할 수 없는 자명한 사실이기에 의미 없는 말이기도 합니다. 실제 상황에 맞게 잘 쓰려면 구체적으로 장점이 무엇인지, 단점이 무엇인지를 파악하는 것이 중요합니다. 앞서 패턴과 프레임워크를 다루면서 각각의 단점들을 구구절절이 따지고 들어간 것도 그런 이유입니다.

나날이 신기술이 쏟아져 나오고 있지만 그에 못지않게 패러다임도 많이 변화하고 있습니다. 유연성 높은 컴포넌트 기술로 주목받던 자바빈즈가 해로운 것이라는 평가를 받기 시작하고, 많이 써야 좋은 것이라고 하던 주석을 이제는 적게 쓸수록 좋은 것이라고 합니다.

신기술을 따라가는 것도 필요하지만 이러한 패러다임의 변화를 읽는 것이 더 중요합니다. 현재의 방식이 지금은 가장 효율적인 방식일 수 있겠지만 지금보다 훨씬 더 효율적인 방식이 나오는데 모르고 있으면 도태될 수밖에 없습니다. 흔히 프로그래머는 워낙 기술이 빨리 발전하기 때문에 후배들을 당할 수가 없다는 말을 하곤 합니다.

하지만 현실은 결코 그렇지 않습니다. 기술의 변화는 단지 API가 변하는 것 뿐 그렇게 중요한 것은 아닙니다. 프로그래밍의 주요한 패러다임은 그렇게 빠른 속도로 변하지 않으며 또한 그런 패러다임의 변화를 주도하는 것은 반짝이는 아이디어가 아니라 경험 속에서 축적된 고민들입니다. 끊임없이 고민하면서 프로그래밍을 해왔다면 이런 변화를 따라잡는 것은 어려운 일이 아닙니다.

좋은 프로그래머가 되기 위해서 폭넓은 지식과 빠른 학습 능력은 물론 필수적인 것입니다만 그보다 더 중요한 것은 늘 고민하는 자세와 깊이 있는 사고 능력입니다. 변화를 따라잡는 힘은 빠른 학습 능력에서 나오는 것이 아니라 변화의 중요성을 인식하고 발전을 위해 고뇌하는데서 나오는 것입니다.

이번 연재는 아마 지난 두 번보다 훨씬 더 많은 논란거리들을 담고 있을 것입니다. 패러다임은 한 사람의 고민에 의해서도 발전하지만 많은 사람들의 머리를 맞댄 토론에서도 발전합니다. 필자의 글에 반론이 있거나 더 깊은 논의를 원하는 독자들은 youngrok.com이나 javaservice.net, 혹은 c2.com에 제시해 주세요. 서로를 발전시킬 수 있는 좋은 기회가 있길 바랍니다.@

* 이 기사는 ZDNet Korea의 제휴매체인 마이크로소프트웨어에 게재된 내용입니다.
출처:지디넷
AND

지난 글에서는 자바 웹 프로그래머로서 한 사람 몫을 해내기 위한 기초에 대해 다루었습니다. 다음 단계는 한 사람 몫을 넘어서 팀 전체에 영향을 미치는 일을 할 수 있는 능력을 기르는 것이겠죠. 팀에서 어떠한 역할을 맡아도 잘해낼 수 있으려면 프로그래밍도 잘해야 하지만 개발 환경을 구성하는 방법도 잘 알고 있어야 합니다.

이 글에서는 개발 환경이 잘 갖춰진 팀의 프로그래머는 이미 구축된 환경에 대해 좀 더 잘 이해할 수 있도록, 그렇지 못한 경우는 스스로 개발 환경을 구축해나갈 수 있도록 여러 가지 개발에 필요한 것들을 살펴보겠습니다.

레이싱 경기장에는 피트(pit)라고 부르는 장소가 있습니다. 경주차의 수리나 조정, 타이어 교환, 연료 보급 등을 하는 곳이죠. 이런 일을 담당하는 사람을 미캐닉(mechanic)이라고 부릅니다. 레이싱 경기가 얼핏 보면 차의 성능과 드라이버의 기량만이 승부를 결정짓는 것 같지만, 장거리 레이스가 될수록 미캐닉의 역할이 승부를 가르는 경우도 적지 않습니다. 미캐닉이 잘한다고 레이싱에서 승리하는 것은 아니지만 미캐닉이 못하면 십중팔구 패배하게 됩니다.

웹 프로젝트도 이런 면에서 레이싱과 비슷한 점이 있습니다. 웹 프로젝트의 성공에 있어 가장 중요한 것은 요구사항의 구현이고, 기술적인 성공을 좌우하는 것은 프로그래밍이지만 프로그래밍 외의 기술적인 잡무들은 웹 프로젝트의 실패를 결정지을 수 있는 조건입니다. 소스 관리를 비롯해서 웹의 특성상 자주 발생하는 서버 설치, 프로젝트의 배치(deploy), 서버 리스타트 등이 그런 일들이죠. 이런 일들은 대체로 단순 반복 작업이면서도 실수할 가능성이 있고 또 실수를 하면 그 파급 효과가 클 수 있습니다.

그래서 이런 개발 주변 환경을 제대로 갖춰놓지 않으면 이것이 프로젝트를 실패로 이끌기도 합니다. 고로, 레이싱에서 승리하기 위해서는 좋은 미캐닉과 좋은 장비들을 확보해서 피트에서의 작업을 효율적으로 해내야 하는 것처럼 웹 프로젝트를 성공으로 이끌기 위해서는 개발 환경을 잘 갖춰서 프로그래머들의 잡무 부담을 줄이는 것이 중요합니다.

경우에 따라서 미캐닉처럼 이런 잡무를 전담하는 사람을 따로 두는 경우도 있습니다. 혹은 일부러 전담자를 두지 않더라도 한두 사람만이 수행 방법을 숙지하게 되어 결국 이 사람들에게 잡무 요청이 몰려 실질적인 전담자가 되버리는 경향이 있습니다. XP(eXtreme Programming)의 지속적인 통합(Continuous Integration)도 이런 문제를 이야기합니다. 통합 작업을 자동화해서 누구나 쉽게 할 수 있게 만들지 않으면 한두 사람만이 통합 작업을 할 수 있게 되고 통합을 자주 수행할 수도 없게 됩니다.

그러면 각자 개발한 모듈들을 통합할 때 많은 문제가 발생할 수 있고, 그런 경우 통합이 잘못된 것인지 아니면 각자 개발한 소스가 잘못된 것인지 찾기 힘들게 되죠. 이런 상황을 ‘Integration Hell’이라고 부르는데, 이런 상황을 피하기 위해서는 지속적인 통합을 통해 문제를 조기에 발견하여 문제가 쌓이지 않게 해야 합니다. 이 글에서는 지속적인 통합을 확장해 프로그래머들을 잡무에서 해방시켜 좀 더 창조적인 업무에 전념할 수 있도록 개발 환경을 구축하는 방법들을 이야기할 것입니다.

‘무엇을 해야 하는가’에 대한 고민
좋은 소프트웨어를 만드는 첫걸음은 어떻게 만드는가를 아는 것이 아니라 무엇을 만들어야 하는가를 아는 것입니다. 아무리 고급 코딩 기술과 어려운 알고리즘을 자유자재로 구사한다고 해도 사용자가 원하지 않는 소프트웨어를 만들어 냈다면 실패한 것입니다. 개발 환경 구축도 각종 도구들의 사용법을 익히는 것도 중요하지만 그 전에 자신이 속한 조직에서 어떤 일들이 있고 이런 일들을 잘하려면 무엇을 갖추어야 하는가를 고민하는 것이 첫 번째입니다.

이런 요구사항은 팀의 상황에 따라 많이 달라질 수도 있고 범위를 어떻게 잡느냐에 따라 간단한 빌드 작업으로 한정될 수도 있고 종합적인 프로젝트 관리를 포함하는 광범위한 내용이 될 수도 있습니다. 그럼 웹 프로젝트에는 어떤 요구사항이 있고 이런 요구사항을 충족하기 위해서 어떤 개발 환경이 필요한지를 살펴보겠습니다.

의사소통 관리
비단 웹 프로젝트 뿐 아니라 대부분의 프로젝트들에서 가장 중요한 성공 요인은 프로젝트의 성공을 향한 팀원들의 의지와 팀원간의 효율적인 의사소통(communication)입니다. 얼핏 둘 다 비기술적인 이슈라서 프로그래머의 영역 밖이라고 생각할 수도 있겠지만 의사소통은 이미 개발 방법론의 영역에 들어와 있습니다. 의사소통은 흔히 구성원들의 적극성의 문제, 즉 사람의 문제로 치부되는 경향이 있고 이것이 어느 정도는 사실입니다.

하지만 최근 유행하는 방법론들에서는 환경적 요인이 의사소통에 미치는 영향이 크다고 보고 의사소통을 효율적으로 하기 위한 장치들을 많이 제시하고 있습니다. 일반적인 프로그래밍 작업에서의 의견 교환, 의사 결정을 위한 회의, 문서화, 업무 요청 관리, 고객의 요구사항 관리 등이 모두 의사소통이며 개발 환경을 통해 이런 부분들을 향상시킬 수 있습니다.

XP에서는 보통 의사소통 비용은 물리적인 거리의 제곱에 비례해서 늘어난다고 합니다. 그래서 의사소통을 효율적으로 하기 위한 방법들로 물리적인 요소를 많이 제시합니다. 프로그래밍을 두 사람이 붙어 앉아서 같이 하는 짝 프로그래밍(Pair Programming), 회의가 소모적으로 흐르는 것을 방지하기 위한 기립 회의(Stand up Meeting), 자연스러운 정보 확산과 의견 교환을 위한 정보 방열기(Information Radiator) 등이 그런 것들이죠. 이런 프랙티스(practice)들을 잘 실천하려면 짝 프로그래밍을 할 수 있는 자리 배치, 팀원들이 쉽게 모여서 이야기할 수 있는 공간, 화이트보드 등 물리적 환경을 갖추어 놓아야 합니다.

소프트웨어적으로 지원할 수 있는 부분도 많습니다. 업무 요청 관리는 보통 사내 그룹웨어 등으로 소화하기 마련인데 이 부분이 잘 되어 있지 않으면 일의 진행에 병목현상이 발생하고 때로는 팀원간 마찰의 원인이 되기도 합니다. 단순히 메일을 이용하는 것보다는 업무 요청을 보내는 것, 진행 상황, 결과 등을 종합적으로 관리할 수 있는 소프트웨어가 있는 것이 좋습니다. 이슈 트래커(issue tracker)도 어느 정도 이런 부분을 소화할 수 있으나 팀의 상황에 맞게 개발해두는 것도 좋을 것입니다.

문서화 역시 시스템으로 지원해야 할 부분입니다. 문서화 시스템에서 가장 중요한 것은 문서를 빠르게 작성하고 작성한 문서를 즉시 공유할 수 있는 것입니다. 그리고 버전 관리도 되어야 하죠. 이런 요구사항을 가장 잘 만족하는 것은 위키(wiki)입니다. 대규모 CMS(Content Management System)도 많지만 오히려 단순한 위키가 더 높은 유연성을 발휘하는 경우가 많습니다.

소프트웨어 형상 관리
소프트웨어 개발 환경에서 기술적으로 가장 중요한 것은 소스 버전 관리입니다. 개발팀에서 작성하는 모든 소스는 버전 관리가 되어야 합니다. 프로그램 소스는 물론이고 스크립트, SQL, 각종 문서, 설정 파일들까지 포함합니다. 소스 버전 관리의 목적은 작업 기록의 보존을 통해 문제가 발생했을 때 원인을 추적하거나 이전 상태로 되돌리기 위한 것이죠.

보통 소스 버전 관리를 위해서 CVS(Concurrent Versions System)를 많이 사용합니다. CVS를 좀 더 개선한 Subversion도 있고 여러 가지 상용 툴도 있지만 여전히 CVS가 오픈소스 공동체에서 가장 많이 쓰이며 IDE와도 잘 통합되어 있습니다. CVS에 대한 내용 설명은 『실용주의 프로그래머를 위한 버전 관리 using CVS』라는 책을 추천하는 것으로 대신하겠습니다.

소스 관리는 사실 SCM(Software Configuration Management)의 일부분이기도 합니다. SCM은 소스에 대한 버전 관리 뿐 아니라 소프트웨어의 기능성 수준에 대한 변동 기록, 버그 수정 내역, 요구사항의 변화 등을 종합적으로 관리하는 것을 말합니다. 소프트웨어의 새 버전이 발표되면 보통 릴리스 노트(Release Notes)가 같이 배포되는데 이런 것이 SCM의 대표적인 산출물이죠.

상용 SCM 툴도 많지만 보통은 소스 관리에 CVS를 쓰고 이외에는 앞에서 언급한 이슈 트래커를 쓰는 것으로 SCM의 대부분의 영역이 커버됩니다. 소스 외의 형상 관리는 사실 업무 요청 관리와 기능적으로 아주 비슷하기 때문이죠. 오픈소스와 친한 사람이라면 버그질라(bugzilla)를 접해본 적이 있을 것입니다. 버그를 보고하고 버그가 수정되는 과정을 기록으로 남기고 조회할 수 있게 해주는 시스템이죠.

이런 것을 버그 트래커(bug tracker)라고 하는데 이를 확장한 것이 위에서 언급한 이슈 트래커입니다. 오픈소스 소프트웨어 중에도 Mantis나 TUTOS 등이 있고 위키와 이슈트래커를 합친 Trac이 있습니다. 이런 툴 하나 정도는 갖춰 놓아야할 것입니다.

빌드 자동화
지속적인 통합에서는 빌드 자동화를 가장 중요하게 다룹니다. 빌드는 소스코드를 컴파일하고 여러 가지 변환 작업을 거쳐 프로그램이 동작할 수 있게 구성하는 작업을 말합니다. 일반적인 자바 애플리케이션은 간단한 컴파일만으로 빌드가 완료되지만 웹 프로젝트는 좀 더 할 일이 많습니다. 컴파일한 클래스들이 웹 프로젝트 구조에 맞게 위치해야 하고 각종 라이브러리, TLD 파일들도 적절히 위치시켜 웹 컨테이너(서블릿 엔진)에 배치시켰을 때 정상적으로 동작하도록 해야 합니다.

프로젝트 구조가 복잡하면 그만큼 빌드 자동화도 더 복잡해집니다. 그래서 빌드 자동화 작업을 하기 전에 우선 프로젝트 구조를 잘 구성해 놓아야 합니다. <표 1>은 빌드 자동화 툴인 Ant에서 제시하는 프로젝트 구조에 대한 권고안입니다.


<표 1> Ant에서 제시하는 프로젝트 구조에 대한 권고안

여기서 눈여겨봐야 할 부분은 src와 web입니다. 자바 소스는 src에, 웹 자원은 web 아래에 컨텍스트 구조대로 배치시킵니다. 여러 IDE(Integrated Development Environment)들에서 웹 프로젝트를 구성할 때도 src와 web의 이름은 달라지기도 하지만 기본적으로 src와 web을 나누는 기준은 같습니다. 경우에 따라서 자바 소스는 src 아래에서 다시 한번 분류를 하기도 합니다. src/java, src/test로 소스와 테스트를 나누기도 하고 src/resources 등의 폴더를 만들어서 xml이나 properties를 보관하기도 합니다. 이 구조를 기준으로 보면 웹 프로젝트 빌드 작업은 다음과 같은 단계로 진행됩니다.

[1] 웹 컨테이너에서 웹 애플리케이션의 루트 역할을 하는 CONTEXT_ROOT 디렉토리를 만든다. 앞의 구조에서 web 디렉토리를 그대로 사용하기도 하고 dist/web, dist/<컨텍스트명>과 같이 만들기도 한다.
[2] 자바 클래스를 컴파일하고 CONTEXT_ROOT/WEB-INF/classes로 복사한다.
[3] 클래스패스 리소스를 CONTEXT_ROOT/WEB-INF/classes로 복사한다.
[4] 필요한 라이브러리를 CONTEXT_ROOT/WEB-INF/lib로 복사한다.
[5] JSP, HTML, CSS, JS 등의 웹 자원들을 CONTEXT_ROOT로 복사한다.
[6] tld 파일을 CONTEXT_ROOT/WEB-INF/tld나 CONTEXT_ROOT/WEB-INF/lib로 복사한다.

여기서 CONTEXT_ROOT를 web으로 그대로 사용할 수도 있는데, 그렇다면 4~6번 과정은 필요 없거나 한번만 해도 되는 작업이 됩니다. 대신 경우에 따라 프로젝트에서 빌드한 결과물을 지우고 다시 빌드하고 싶을 때 일이 복잡해질 수 있죠. 보통 IDE에서는 web 디렉토리를 그대로 CONTEXT_ROOT로 사용합니다.

만약 이런 일련의 빌드 과정을 수동으로 한다면 그 비효율은 말할 나위가 없겠죠? 그래서 IDE에서는 간단한 설정만 해두면 자동으로 빌드해줍니다. 하지만 IDE 없이 빌드해야 할 때도 있습니다. 개발할 때는 당연히 IDE로 개발을 하고 빌드도 할 수 있겠지만 IDE에서 지원하는 것 이외의 부가적인 작업을 해야 한다거나 개발자의 PC가 아닌 서버에서 직접 빌드하고 배치해야 할 때도 있습니다.

이럴 때의 빌드를 자동화하기 위해 여러 가지 빌드 자동화 도구를 사용할 수 있습니다. 유닉스/리눅스 환경에서는 오래 전부터 이런 목적을 위해 사용해 온 make라는 도구가 있습니다. Makefile이라는 파일에 빌드를 위한 스크립트들을 make 문법에 맞게 나열해두고 make 명령을 실행하면 빌드가 실행되죠.

하지만 make에서 지원하는 기능이 너무 빈약해서 빌드가 복잡할 경우 Makefile에 모든 빌드 과정을 기술해야 하기 때문에 Makefile이 복잡해져서 유지보수가 힘들다는 문제가 있습니다. 그래서 자바 진영에서는 make를 넘어서는 툴을 만들고자 했고 그래서 Ant가 등장했습니다. 그러나 Ant 역시 한계가 있었기 때문에 Ant를 한 단계 더 발전시킨 Maven이 등장했죠. 현재 대부분의 오픈소스 자바 프로젝트는 Ant나 Maven 둘 중의 하나를 빌드 도구로 사용하고 있습니다. 여기서는 구체적인 도구의 사용법을 설명하기보다 어떠한 도구들이 있는지를 간단히 살펴보고 각 도구의 장단점을 비교해 보도록 하겠습니다.

Ant
Ant는 구조적으로는 make를 그대로 이어 받았습니다. build.xml에 빌드 설정을 해두고 ant 명령을 실행하면 빌드 작업이 수행됩니다. Ant는 자주 하는 작업들을 미리 자바 클래스로 코딩해서 태스크로 만들어 두었고 클래스패스 설정이나 파일, 디렉토리 선택 등을 쉽게 할 수 있는 문법을 갖추어 놓았기 때문에 make보다 훨씬 간단하게 빌드 설정을 할 수 있죠. <리스트 1>의 build.xml의 예를 봅시다.


 <리스트 1> build.xml의 예

<?xmlversion="1.0"encoding="UTF-8"?>
  <projectdefault="build">
    <description></description>

    <targetname="build"depends="compile, resource"description="build project">
    </target>

    <targetname="compile">
        <javacsrcdir="src/java"destdir="web/WEB-INF/classes">
            <classpath>
                <filesetdir="lib">
                    <includename="*.jar"/>
                </fileset>
            </classpath>
        </javac>
    </target>

    <targetname="resource">
        <copytodir="web/WEB-INF/classes">
            <filesetdir="src/conf">
                <includename="*.xml"/>
                <includename="*.properties"/>
            </fileset>
        </copy>
    </target>
</project>

이것은 자바 컴파일을 하고 리소스를 복사하는 빌드 파일입니다. target 태그가 작업의 단위를 정의하는 역할을 하며 다음과 같이 target을 지정하여 실행할 수 있습니다.

ant <target 이름>

앞의 build.xml에서는 build, compile, resource라는 세 개의 target을 정의하고 있고 build라는 target은 compile, resource에 의존하고 있기 때문에 build target을 실행하면 자동으로 compile과 resource가 먼저 실행됩니다. 그리고 project 태그에서 default로 build를 정의하고 있기 때문에 target을 지정하지 않고 ant를 실행하면 자동으로 build가 실행되죠. compile target에서는 javac 태스크를 이용해서 컴파일을 하고 resource 태스크에서는 copy 태스크를 이용해서 복사를 합니다. 이것이 Ant의 기본적인 구조이고 다른 부가적인 기능들도 있지만 대체로 이런 식으로 build.xml을 구성하게 됩니다.

사실 Ant 개발자들은 Ant가 make보다 훨씬 선언적으로 빌드를 정의할 수 있다는 점을 장점으로 내세우지만 이건 사실 javac, copy와 같은 태스크를 미리 자바 클래스로 코딩해 놓았기 때문에 그런 것일 뿐 실제적으로는 Make의 메커니즘과 큰 차이가 없습니다. 그런 반면 build.xml을 선언적으로 작성할 수 있게 하기 위해 build.xml의 문법에 스크립트적인 요소를 최소한으로 줄였고 또 XML 자체가 프로그래밍이 필요한 부분을 기술하기에는 적합하지 않기 때문에 Ant의 파워는 오히려 make보다 낮아졌습니다.

그리고 실제로 자바 클래스 컴파일에 파일 몇 개 복사하는 정도라면 아주 간단하게 빌드 파일을 작성할 수 있지만 복잡해지기 시작하면 build.xml은 점점 이해하기 힘든 코드가 되어갑니다. 그래서 Ant에서는 build.xml에 주석을 충분히 달아놓을 것을 권고하고 있습니다.

하지만 주석을 많이 달아야한다는 것은 주석을 달지 않으면 안될 만큼 지저분한 코드를 만들게 된다는 뜻이기도 합니다. 사실 빌드 스크립트야 한번 만들어두면 계속 쓰니까 이해하건 말건 무슨 상관이냐고 할 수도 있겠지만 프로젝트 규모가 커지고 연관 프로젝트가 많아질수록 빌드 요구사항도 계속 변합니다.

그래서 자주 수정할 수 있도록 빌드 스크립트를 명료하게 유지할 필요가 있죠. 결국 Ant는 make보다는 조금 사정이 나아졌지만 본질적으로는 make와 동일한 문제를 갖고 있습니다.

주석과 나쁜 냄새  

학교에서는 꽤 오랫동안 소스코드에는 주석을 많이 달아야 다른 사람이 유지보수하기 쉽다고 가르쳐 왔습니다. 하지만 이것은 사실이 아닙니다. 오히려 많은 주석은 리팩토링에서 말하는 나쁜 냄새 중 하나입니다.

마틴 파울러가 쓴 『리팩토링』에서는 나쁜 코드를 식별하는 방법으로 냄새라는 표현을 사용합니다. 코드에 좋지 않은 부분이 있으면 ‘냄새 나는 코드’라는 거죠. 마틴 파울러는 주석은 그 자체가 나쁜 냄새를 풍기는 것은 아니지만 보통 코드의 나쁜 냄새를 감추기 위한 탈취제로 사용되기 때문에 주석을 써야 할 필요성을 느낀다면 주석을 쓰는 대신 코드를 리팩토링해서 코드에서 냄새가 나지 않도록 하라고 말합니다. 좋은 코드는 주석을 많이 달아놓은 코드가 아니라 코드 자체만으로도 쉽게 이해할 수 있는 코드라는 뜻이죠.

썬에서 제시하는 자바 코드 컨벤션(Java Code Convention)에서도 구현에 대해 설명하는 주석은 되도록 쓰지 말라고 권고하고 있습니다. 빌드 스크립트라고 예외가 되어선 안됩니다. 개발팀에서 개발하고 유지하는 모든 소스코드는 냄새가 나지 않는 ‘깨끗한 코드’가 되어야 합니다.



Maven
Maven은 이런 점들을 해결하고 나아가 종합적인 프로젝트 관리까지 소화하기 위해 만들어졌습니다. 그래서 기본 구조 자체도 make, Ant와는 상당히 다릅니다. 우선 Ant의 문제점이라고 지적되던 부분, 빌드를 위한 프로세스와 데이터가 섞여서 build.xml에 기술된다는 것을 해결하기 위해 데이터와 프로세스를 완전히 분리했습니다. Maven에서는 make의 Makefile, Ant의 build.xml에 대응되는 것으로 project.xml을 작성하는데 이 project.xml에는 프로세스가 전혀 들어가지 않고 오로지 프로젝트에 대한 설명만이 들어갑니다. <리스트 2>를 봅시다.


 <리스트 2> project.xml의 예

<?xmlversion="1.0"encoding="UTF-8"?>
<project>
    <pomVersion>3</pomVersion>
    <artifactId>fireside</artifactId>
    <name>fireside</name>
    <groupId>fireside</groupId>
    <currentVersion>1.0</currentVersion>
    <inceptionYear>2005</inceptionYear>
    <logo>/images/logo.gif</logo>
    <mailingLists>
        <mailingList>
            <name>${pom.name} Dev List</name>
        </mailingList>
        <mailingList>
            <name>${pom.name}User List</name>
        </mailingList>
    </mailingLists>
    <dependencies>
        <dependency>
            <groupId>jsp-api</groupId>
            <artifactId>jsp-api</artifactId>
            <jar>jsp-api.jar</jar>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>servlet-api</groupId>
            <artifactId>servlet-api</artifactId>
            <jar>servlet-api.jar</jar>
            <type>jar</type>
        </dependency>
    </dependencies>
    <build>
        <sourceDirectory>src/java</sourceDirectory>
        <unitTestSourceDirectory>src/test</unitTestSourceDirectory>
        <unitTest>
            <includes>
                <include>**/*Test.java</include>
            </includes>
        </unitTest>
        <resources>
            <resource>
                <directory>src/conf</directory>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>

이것이 project.xml의 예입니다. 보다시피 어떻게 빌드를 할 것인가에 대한 내용은 전혀 없습니다. 프로젝트의 구조에 대한 설명들만 있죠. 어떻게 빌드할 것인가는 Maven의 플러그인에 있습니다. 플러그인은 Jelly 스크립트라는 XML 기반의 스크립트 언어와 자바 클래스를 결합해서 만들게 되며 goal이라는 것을 정의하는데 이것은 Ant의 target과 비슷하며 다음과 같이 실행할 수 있습니다.

maven <goal 이름>

Maven 플러그인의 Jelly 스크립트는 Ant의 build.xml 보다 훨씬 더 프로그래밍 요소를 많이 포함하고 있기 때문에 더 강력합니다. 게다가 데이터와 프로세스가 완전히 분리되어 있어 사용하는 입장에서는 데이터만 잘 정의해도 됩니다. 따라서 Ant보다 더 선언적이고 작성하기도 쉽습니다.

그리고 Ant는 라이브러리 의존성, 프로젝트간 의존, 상속 관계 등에 대한 지원이 전혀 없었는데 Maven에서는 Repository라는 개념을 도입하여 이런 문제를 해결하고 있습니다. 프로젝트가 어떤 라이브러리를 사용하는지, 어떤 프로젝트의 특성을 상속받는지 등을 project.xml에 설정만 해주면 필요한 jar 파일을 자동으로 Maven Repository에서 다운받아서 빌드를 해주는 것이죠. 데비안 GNU/리눅스를 사용해 봤다면 dselect를 써봤을 것입니다.

dselect에서는 어떤 소프트웨어를 설치하려고 선택하면 그 소프트웨어가 필요로 하는 라이브러리나 다른 소프트웨어를 자동으로 다운로드해서 설치해줍니다. Maven이 제공하는 의존성 관리도 이와 같은 개념입니다.

또 Maven은 프로젝트에 대한 각종 문서가 집약된 사이트를 생성하도록 해줍니다. 오픈소스 프로젝트의 홈페이지를 돌아다니다 보면 ‘built by maven’이라는 딱지가 붙은 사이트가 많습니다. 이런 사이트에는 공통적으로 Project Info와 Project Reports가 있고 이 안에 소스 저장소 연결, Javadoc, 테스트 리포트, checkstyle 리포트 등의 문서들이 있습니다.

이것은 Maven의 site 플러그인이 자동으로 생성한 사이트입니다. 여러 가지 유용한 정보들을 자동으로 생성해주기 때문에 프로젝트에 대한 정보를 팀내에서 쉽게 공유할 수 있죠. 스케쥴러를 이용해서 매일 특정 시간에 빌드하고 사이트를 생성하도록 하면 매일 매일 개발 진척 상황을 살펴볼 수 있습니다.

고민할 필요 없이 Maven을 쓰면 되겠구나 싶지만 아직은 아닙니다. Maven은 지원하는 기능이 많다보니 maven을 제대로 배우는 데는 적지 않은 시간이 걸립니다. 게다가 플러그인들은 여러 가지 구조적인 부분에서 많은 ‘가정’을 포함한 상태로 개발되어 있습니다. 이를테면 Maven으로 빌드하는 jar 파일은 항상 버전 넘버가 뒤에 붙어야 한다는 식이죠.

이런 암묵적인 룰들이 많기 때문에 Maven 플러그인을 확장하고 싶을 때 자신의 생각했던 것과 다르게 동작하는 경우가 많습니다. 게다가 Maven에서 사용하는 스크립트 언어인 Jelly 스크립트는 Ant보다는 낫지만 여전히 복잡한 요구사항을 수용하기에는 ‘진짜 프로그래밍 언어’에 비해 부족한 점이 많습니다. 결국 Jelly 스크립트로 작성한 코드는 Ant의 build.xml 못지 않게 어지러운 코드가 되곤 하죠. 게다가 Maven은 속도가 Ant에 비해 아주 느린데 이 점이 의외로 개발하면서 자주 빌드해야 할 때는 치명적인 문제가 될 수 있습니다.

선택의 문제
이런 점들 때문에 Maven을 무작정 권고하기는 어렵습니다. 그래서 또 다른 대안을 찾는 사람들도 있습니다. Groovy+Ant라는 것도 있습니다. Groovy라는 자바 기반의 스크립트 언어에서 Ant의 태스크들을 쉽게 사용할 수 있게 구성해 놓은 것이죠. 나름대로 괜찮은 대안이지만 아직 Maven의 다양한 기능들을 소화하고 있진 못합니다. 결국 선택은 개발자의 몫입니다. Ant는 한계가 있지만 쉽고 가벼운 툴이고, Maven은 기능이 다양하지만 어렵고 약간의 문제가 있습니다.

일단은 Ant로 구성해 놓고 Maven을 개선한 새로운 툴이 나오기를 기다리는 것도 나쁘지 않을 것입니다. 그리고 사실 빌드 도구 만드는 것이 어려운 일은 아니므로 스스로 자신의 팀에 맞는 빌드 도구를 만들어보는 것도 좋습니다. 목적은 위에서 나열한 웹 프로젝트의 빌드 과정을 효과적으로 해내는 것이지 어떤 도구를 사용하느냐가 중요한 것은 아닙니다.

관리의 자동화
자동화의 대상은 프로젝트의 빌드만이 아닙니다. 그 외에 개발에서 발생하는 수많은 잡무들도 모두 자동화 대상입니다. 다음이 그런 작업들입니다.

[1] 서버 설치 및 서버 환경 구성
[2] 빌드한 프로젝트를 웹 컨테이너에 배치하기
[3] 웹 컨테이너를 스탑/스타트/리스타트 시키기
[4] 주기적으로 프로젝트를 테스트하고 결과를 리포팅하기
[5] 상황에 따라 서버의 셋팅 변경하기

Ant와 Maven은 이런 일에도 상당 부분 도움을 줍니다. 이미 이런 작업들이 Ant 태스크나 Maven 플러그인으로 많이 만들어져 있기도 하고 또한 빌드 자동화와 일관된 방식으로 문제를 해결할 수 있다는 것도 장점입니다. 하지만 사실 이런 부분들은 빌드 툴보다는 bash 같은 셸 스크립트가 더 간편한 경우가 많습니다. 서버에 접속해야 하는 일들도 많고 Ant나 Maven의 태스크보다 리눅스/유닉스의 툴들이 더 유용한 경우가 많습니다. 리눅스/유닉스 세계에서는 이미 오래 전부터 이런 일들을 쉘 스크립트로 해왔습니다.

하지만 여기에도 문제가 있는 것이 셸 스크립트 역시 Ant나 Maven의 Jelly 스크립트처럼 완전한 프로그래밍 언어가 아니기 때문에 파워도 부족하고 문법도 좀 덜 친숙합니다. 그래서 이런 경우는 펄(Perl)이나 파이썬 등의 스크립트 언어를 사용하기도 합니다. 스크립트 언어는 셸 명령을 셸 스크립트에 비해 큰 불편 없이 실행시킬 수 있고 프로그래밍 언어로서의 파워도 갖고 있다는 장점이 있는 반면 배워야 할 언어가 하나 더 늘고 이런 류의 작업을 위해 이미 만들어진 것들이 많지 않다는 문제가 있습니다. 결국 또다시 선택의 문제로 귀결됩니다.

참고로 필자의 선택은 간단한 경우는 셸 스크립트, 좀 복잡해지면 파이썬입니다. 중요한 것은 잡무를 모두 자동화해서 프로그래머가 개발한 것을 쉽게 테스트하고 또 실제 서비스에 쉽게 반영할 수 있게 하는 것입니다. 필요하다면 어떤 도구라도 사용할 수 있다는 유연한 사고방식이 필요합니다.

게으를 수 있는 권리
“인간은 누구나 게으를 권리가 있다.” 폴 라파르크의 『게으를 수 있는 권리』에 나오는 말입니다. 이 책에서는 일의 노예가 되어가는 현대인들에게 삶의 목적에 대해 다시 한 번 생각해볼 것을 요구하며 또한 무작정 부지런하기만 한 것이 효율적이지도 않다고 말합니다.

프로그래머에게도 이 말은 적용됩니다. 주어진 일은 무조건 열심히 하고 보자는 생각에 낡은 방식대로 묵묵히 지겨운 일을 해내는 것은 개인에도 조직에도 바람직하지 않습니다. 이건 조직의 입장에서도 생산성 향상을 할 수 있는 기회를 놓치기 하기 때문에 또다른 의미의 태업입니다. 프로그래머는 귀찮은 일에 대해 게을러져야 더 나은 방법을 찾아낼 수 있습니다. 지퍼의 발명도 구두끈을 매기 귀찮아하는 사람이 해낸 것이고 많은 발명들이 게으름의 소산입니다.

프로그래머에게 이런 게으름은 이미 권리를 넘어서 의무입니다.

물론 게으름만으로는 충분하지 않습니다. 마냥 게을러지기만 해서는 귀찮은 일을 하지 않게 될 뿐이고 더 나은 방식으로 하게 하진 못합니다. 결국 할 일을 하면서 좀 더 게을러지기 위해서는 귀찮음을 해결하는 과정에서의 부지런함은 필요합니다. 구두끈을 매기 귀찮아 하는 사람이 지퍼를 발명하는 수고를 해야 했듯이 말이죠. 그리고 기존의 방식을 개선해야 하기 때문에 어느 정도의 창의성도 있어야 합니다.

‘우리는 이제까지 잘해왔다’는 논리에 빠지는 것 또한 경계 대상입니다. 한번 성공을 거둔 경험이 있는 조직은 자신들의 방식이 성공을 가져왔기 때문에 쉽게 매너리즘에 빠집니다. 하지만 IT 세계는 빠르게 발전하고 있고 예전에는 효율적이었던 방식이 더 효율적인 기술들이 나옴에 따라 오히려 발목을 붙잡고 있는 경우도 많다는 사실을 기억해야 합니다.

그럼 이런 자동화로 절약한 시간들은 어떻게 써야 할까요? 물론 더 게을러지기 위해서 한 일들이지만 그렇다고 정말로 게을러져서는 곤란합니다. 프로그래머의 게을러질 권리는 단순한 반복 작업에만 해당되는 것입니다. 좋은 개발 환경으로 인해 절약한 시간들은 좀 더 창조적인 일을 하는데 재투자해야 합니다. 프로그래밍 자체에 집중하는 것도 좋고 좀 더 높은 수준의 자동화에 도전해보는 것도 좋습니다. 개발 환경 구축 과정에서 경험하게 된 것들을 공유해보는 것도 여러 사람에게 유익한 결과가 될 것입니다.

학창 시절 배웠던 러다이트 운동(Luddite Movement)을 기억할 것입니다. 산업혁명 이후 기계가 인간이 할 일을 대체하기 시작하면서 많은 사람들이 일자리를 잃고 임금도 급락했습니다. 그래서 노동자들이 들고 일어나서 기계를 파괴하고 다니는 비밀 조직을 결성한 것이 바로 러다이트 운동입니다.

프로그래머에게도 이런 일이 닥치지 말란 법이 없습니다. 기계가 할 수 있는 일은 기계에 맡기고 좀 더 창조적인 일을 할 줄 알아야 기계에게 일자리를 빼앗기지 않겠죠. Ant라는 이름에도 이런 생각이 담겨 있습니다. 모 광고 카피처럼 단순 반복 작업은 부지런한 개미에게 맡기고 당신은 프로그래밍을 즐기라는 뜻인 거죠.

사실 자신의 잡무를 스스로 자동화할 수 있는 사람은 프로그래머 밖에 없습니다. 이것이 프로그래머라는 직업의 매력이기도 하죠. 하루 일과에서 가장 많은 시간을 일하면서 보내는데 이 일이 즐거워야 하지 않겠습니까. 즐거운 프로그래밍을 하는 첫걸음은 귀찮고 일을 제거하는 것입니다. 귀찮은 것들일랑 모두 컴퓨터에 맡기고 우리는 프로그래밍을 즐겨 봅시다.@

* 이 기사는 ZDNet Korea의 제휴매체인 마이크로소프트웨어에 게재된 내용입니다 
출처:지디넷
AND