본문 바로가기

프로그래밍(TA, AA)/개발방법론

[개발방법론] 계층별, 기능별 패키지 구성하기

출처: http://egloos.zum.com/springmvc/v/434948


자바 프로젝트를 진행하면 프로그래머들이 고민하는 부분이 한가지 있습니다. 물론 프로그래밍을 하다보면 많은 부분에서 고민을 해야겠지만 그중에도 가장 쉬우면서 딱히 가이드잡기가 어려운 패키지별로 클래스를 정리하는 과정이 그러합니다. 프로그래머는 도대체 어떤 기준으로 클래스를 분류해야 체계적으로 자신의 프로젝트를 분류가 되었고, 더 나아가 팀으로 작업할 시에 "왜 이 클래스는 이 패키지에 넣은거죠?" 라는 난해한 질문을 받지 않을 수 있을까?


먼저 패키지 분류가 복잡해지더라도 하나의 원칙과 명확한 분류기준이 있다면 그때그때 프로젝트마다 뒤죽박죽으로 바뀌는 마구잡이식 분류법을 피할 수 있습니다. 마구잡이식 프로젝트는 코드의 가독성을 해치고 확장성을 망가뜨리는 나쁜 프로그래밍 방법입니다. 게다가 구분과 명분이 확실해야 차후에 프로젝트를 진행할 때도 미리 명확한 기준에 맞춰 진행할 수 있고 현재 작성된 코드를 재활용하는데도 사용자 간에 특별한 교육없이도 막힘없이 사용할수 있게 도와줍니다. 여기에 조금 플러스 하다면 잘 분류된 클래스는 2인 이상, 3인 이상 할 것없이 수백명의 프로그래머들과 함께 작업을 하더라도 서로 헷갈릴 이유가 없습니다.


더욱이 이런 작업 기준을 미리 정해놓은 중요한 이유는 자바의 패키지 분류작업은 작업이 완료된 시점에서 실행되는 것이 아니라 코딩을 해나가는 과정에서 일어나기 때문입니다. 모든 작업을 완료한 뒤에서야 "아 이건 내가 여기로 분류했어야지.."라고 생각해 생각없이 클래스들을 옮겨댔다간 해당 클래스에 의존하고 있는 다른 클래스의 import문이 엉켜 엉망이 될 수 있습니다. 물론 이클립스는 이런 클래스 파일의 이동을 안전하게 보장해주는 자동 클래스패스 수정 기능이 있지만 이건 자바 파일에만 한정되있을 뿐 jsp나 다른 xml문서에 대해서는 자동수정을 보장해주지 않습니다.


그러므로 이런 보이지 않는 약속같이 중요한 작업은 개인의 독단적인 기준에 의해 분류될 것이 아니라 명확한 분류 기준과 서로 공감대를 가질 수 있는 원칙이 필요합니다. 현재 작성되고 있는 이 문서는 기본적인 패키지 분류법에 대해 기술할 것이며, 주된 목적은 웹기반 서비스들의 패키지 분류법입니다.



패키지 분류 법칙1. 루트 패키지는 x.y.z 식으로 구성한다.


x.y.z 식이란 최초의 프로젝트 생성시 프로젝트의 패키지를 최소 3단계로 분류해야 한다는 뜻입니다. (물론 x.y.z라는 말은 필자가 기억하기 쉬우라고 만든 용어이므로 오용해서는 안됩니다.) 대부분 프레임워크나 상용서비스들을 보면 이런 법칙을 준수해서 만드는 것을 볼 수 있는데 먼저 기본 루트 패키지를 굳이 3단계씩이나 걸쳐서 분류해야 하는지 그 까닭을 살펴보겠습니다.

import org.apache.taglibs.*
import net.sf.cglib.*
import com.sum.accessibility.*


먼저 위의 import문을 보겠습니다. 예리한 눈썰미를 가진 자바 프로그래머라면 위 코드만 보더라도 기존의 오픈소스 그룹들이 분명 보이지 않는 패키지 정렬법을 갖고 있다는 사실을 알 수 있습니다. 사실 오픈소스 그룹들 뿐만이 아닙니다. 대부분의 업계 또한 위의 법칙을 준수합니다.


일단 첫번째로 org와 net으로 시작하는 가장 높은 부모 패키지가 보일 것입니다. 이 패키지는 프로젝트를 이끄는 그룹의 성격을 결정합니다. org는 organization의 약자인데 조직, 기구 단체를 뜻합니다. 즉 비영리 그룹이나 단체가 될 것입니다. 지극히 소규모의 자바 동호회라 할지라도 어쨌든 org의 범주에 속합니다. net은 정확히는 알 수 없지만, 이 라이브러리는 net을 기반으로 하는 기술이라는 것이라고 추측해 볼 수 있습니다. 그리고 그 아래의 com은 company의 약자로 영리 목적의 회사에서 제작한 프레임워크라는 것을 뜻합니다. 만약에 회사에서 프로젝트로 라이브러리나 프레임워크 또는 기본적인 기본적인 기능소스를 구현할지라도 classpath에는 명확히 이 소스의 단체 성격을 밝혀주어야 합니다. ㄹ당장에는 필요없더라도 확장시에 필히 요구되는 사항입니다.


두번째로 apache, sf, sun을 보면 바로 알 수도 있겠지만 이 패키지명은 자사의 그룹 또는 사명을 정해주는 구간입니다. 일반적으로 회사라면 사명이 들어가고 그룹이라면 그룹명이 들어가는게 일반적입니다. 이처럼 빈 루트패키지에 또 다시 빈 하위 패키지를 정의해주는 이유는 만약에 이 소스가 본인 뿐 아니라 회사 내, 또는 그룹 내에서 활발하게 쓰인다면 누구나 프로젝트의 출처를 알 수 있게 되므로 작성자가 소스를 사용함과 동시에 출처를 알게 된다는 것에 큰 의의가 있습니다.  작성자가 출처를 정확히 알고 있다면 기능이 업데이트가 되었을시에 업그레이드된 프레임워크를 사용할 수도 있고, 필요하다면 사용자 피드백을 공유할 수도 있을 것입니다.


세번째로 들어가는 taglibs나 cglib, accessibility는 실제 이 프로젝트의 artifact 구조를 뜻합니다. 위의 두 단계로 프로젝트 그룹의 성격과 그룹명을 정해주었다면 이젠 실질적인 소스의 역할 또는 프로젝트명이 들어가야 할 것입니다. 예를 들어 설명해 보겠습니다. 만약에 내가 삼성의 갤럭시 폰의 자바 프로젝트를 맡게 되었다면 패키지 명은 아래와 같이 정할 수 있습니다.



먼저 com.samsung으로 프로젝트의 그룹을 규정해주고 그 하위 패키지로 galaxy라는 현재 프로젝트의 성격을 규정지어줍니다. 많은 회사와 단체에서 이런 식으로 몇가지 단계를 나누어 패키지를 분류하는 이유는 성격을 구분짓는다는 이유말고도 또 있습니다. 예를 들어 삼성이란 회사에서 galaxy 폰만 판다면 필자는 굳이 위와 같은 분류로 힘들게 븐류하지는 않았을 것입니다. 오히려 아예 패키지를 com.samsung.galaxy로 하지 않고 galaxy로 심플하게 통일했을 수도 있고 패키지를 만들지 않았을지도 모릅니다. 하지만 삼성이란 거대기업은 galaxy만을 팔진 않습니다. 호텔사업도 하고, 놀이동산도 하고, 갤러깃도 2,3,4,5 ... 돈이 될때까지 찍어낼 것입니다. 우리가 단 하나의 프로젝트를 구현하면서도 이런 그룹부분과 구조부분을 나뉘어 주는 것도 다 이런 이유에서입니다. 즉 기업의 확장에 유연하게 대처할 수 있는 분류법을 사용해야 한다는 것입니다. 당신이 이런 기본적인 구조만 갖춘다면 갑작스럽게 닥친 프로젝트에도 유연하게 패키지를 분류하고 빠르게 개발에 착수할 수 있게 됩니다.


더욱이 자바프로그래머로서 확장을 고려하지 않은 사람은 자바란 객체지향언어의 알맹이는 속빼고 껍질만 긁어먹는 사람과 똑같습니다. 정상적인 회사나 그룹이라면은 단체가 확장하고 번창하길 바라지, 축소를 바라는 단체는 없을 것입니다. 게다가 그룹이 커지다 보면 회사나 단체가 책임에 따라 둘로 나뉠 수 있고 처음엔 비영리로 시작한 단체가 안타깝게도 영리단체로 변모할 수도 있습니다. 우리가 이런 상황에 유연하게 대처하려면 이런 x.y.z 식의 구분은 필수로 갖추어야 합니다.



패키지 분류 법칙 2. 기능별로 분류하자


작성중인 프로젝트를 위의 x.y.z식으로 분류한 다음은 어떻게 해야 좋을까요? 이제부터 자바파일을 막 집어넣어도 되는 것일까요? 간단하게 영광스런 제작자의 이름이나 어머니 아버지의 이름을 섞어서 패키지를 만드는 것을 어떨까요? 아니면 간단하게 숫자나 알파벳순서로 패키지를 작성해 나갈까요? 물론 모두 가능한 선택 옵션이지만 올바른 선택은 아닙니다. 먼저 글을 작성하기에 앞서 우리는 웹기반 서비스를 기준으로 패키지를 분류하겠다고 밝혔는데 당신이 뒙기반 서비스를 제작한다면 기본적으로 DB, MVC같은 기술과 계층을 나누는 식의 제작 방식을 도입하려 할 것입니다.


답을 먼저 말하자면 artifact id 이후로 나오게 될 패키지 분류는 가급적 기능의 성격으로 하는 것이 옳습니다. 이 성격이란 구분 지어 질 수 있는 가장 큰 성격을 말하는데 구체적으로 보자면 DB의 테이블이라 할 수도 있습니다. 기본적으로 업무에서 웹프로젝트를 작성할 때 주된 작업은 DB에 있는 자료를 입력, 수정, 삭제하는 식의 작업이라는 사실을 개발경험이 있는 사람이라면 알 수 있을 것입니다. (얼마나 DB 작업이 심하면 실무에서는 입출력만 알면 장땡이라는 말까지 있을 경우입니다.)


그리고 대부분의 DB 작업은 테이블을 우선하여 결정짓게 되는데 테이블은 하나의 기능에 대하여 필요한 정보를 칼럼으로 묶는 식의 설계가 될 것이며 테이블은 한가지 기능에 대한 세부적인 세팅 데이터를 갖게 될 것입니다.


그렇다면 우리는 단순히 테이블을 기준으로 분류를 나누기만 하면 패키지 분류에 대한 명확한 기준과 기능별 분류라는 이점까지 얻을 수 있다는 결론을 얻게 됩니다. 더욱이 중요한 것은 쿼리문은 한줄에 하나의 테이블에 대한 연산만할 수 있도록 설게되어 있다는 것입니다. 물론 JOIN이나 GROUP같은 것을 사용하여 여러 테이블을 묶을 수도 있겠지만 궁극적으로 쿼리문은 한 테이블을 기준으로 동작 가능하게 설계하는 것이 옳습니다. 왜냐하면 코드의 재사용성을 고려하더라도 차라리 트랜잭션을 활용해 원자성을 유지하며 간단한 쿼리문을 사용하는 것이 좋지, 괜히 성능을 향상시킨다고 과도하게 쿼리문에 의존하다간 코드가 뒤죽박죽이 되버릴게 뻔하기 때문입니다.


부득이하게 유지보수를 위하여 하나의 테이블을 여러 테이블로 나누었더라도 이 모든 테이블이 하나의 성격으로 통일될 수 있다면 복수의 테이블을 하나의 기능이라고 생각하고 분류를 나누는 것도 좋습니다. 만약에 'phonebook'이라는 테이블이 있는데 이 테이블을 'phone'과 'book'이라는 테이블로 나뉘어 설계하는게 좋다면 당신은 phonebook.phone과 phonebook.book과 같은 식으로 패키지를 나눌 수도 있을 것입니다.


테이블별로 분류한 당신의 패키지는 DB와 관련된 작업을 편리하게 해주면서 동시에 이 소스를 사용해 개발하는 사람들의 이해도를 대폭 상승시켜 줄 것입니다. 더욱이 다양한 테이블이 서로 연관관계를 갖는 서비스 계층을 만든다 하더라도 사용자는 본인이 원하는 메소드가 어디에 위치한지 바로 알 수 있을 것입니다.



패키지 분류 법칙 3. 계층별로 분류하자


기능별로 패키지를 분류하였다면 이젠 계층별로 분류해보겠습니다. 사실 계층별로 분류하는 것은 기능별로 분류하는 것보다 더욱 중요한데 혹자는 이런 계층별 분류를 티어(tier, tiered) 또는 레이어(Layer)라고 부르기도 합니다. 티어는 사용자가 어떤 디자인 모델을 차용해서 제작하느냐에 따라 갈리므로 작성자가 먼저 어떤 구조로 설계하는지 결정하는게 가장 중요합니다. 기본적인 웹계층이라면 MVC 구조로 많이 제작될 것이며 View는 JSP는 Controller는 서블릿 또는 스프링이나 스트럿츠 같은 프레임워크가 관리하는 형태로 제작될 것입니다. 결국 우리가 클래스로 묶어서 배포할 부분은 모델 부분에 해당합니다. 모델은 직접 DB에 접근하여 데이터를 가져오고 연산하여 컨트롤러에 넘겨주는 주된 역할을 담당합니다.


그러면 모델계층을 한번 분할시켜보겠습니다. 모델은 DAO(Data Access Object, DB에 접속하여 데이터를 가져오는 클래스) 계층과 자바빈과 같이 테이블의 컬럼을 오브젝트로 변환시키는 domain계층, domain과 dao계층을 활용하여 직접적인 기능을 구현하는 service 계층 등으로 나뉠 수 있습니다. 이런 역할분담은 매우 기본적인 것이므로 이런 계층에 따라 패키지를 나누어 주는 것이 좋습니다.


한번 예를 들어보겠습니다. 필자가 삼성이란 기업의 갤럭시 폰 프로젝트에서 user란 테이블을 만들고 이에 대한 작업을 한다면 다음과 같은 패키지 구성을 할 수 있을 것입니다.



각 패키지에는 해당 기술을 추상화하는 인터페이스가 들어갔을 수도 있고 이것을 구체화하는 implement 클래스가 있을 수도 있습니다. 서비스 패키지는 서비스 내부에 또 다양한 패키지로 기능별 구분이 있을 수도 있고 단순히 한가지 서비스에 대한 클래스가 존재할 수도 있습니다.


물론 user에는 이보다 더 많은 컨트롤러의 역할을 해줄 수 있는 com.samsung.galaxy.user.web이나 해당 그룹을 지원해 줄 수 있는 com.samsung.galaxy.user.support 같은 것들도 들어갈 수 있습니다. 위와같은 구성은 참고하여 활용할 수 있는 것이지 절대적인 것은 아닙니다. 중요한 것은 계층별로 패키지를 분류한다는 것입니다.



패키지 분류 법칙 4. 패키지 분류의 이점


패키지를 분류하지 않고 하나의 기본 패키지에 몽땅 집어넣으면서 설계하려는 사람은 최소한 자바프로그래머로서는 존재하지 않을 것입니다. 그런데 이런 분류에 너무 시간이 뺏기고 기본적인 개념이 잡혀있지 않다면 개발하면서 점점 코드가 엉키게 되고 서로 기능과 역할의 경계가 모호해짐과 동시에 실패한 코드가 되고 맙니다.


만약에 당신이 프로젝트가 시작함과 동시에 척척 패키지를 프로젝트 성격에 맞게 분류하고 곧바로 작업에 착수한다면 알만한 사람들은 당신이 어떤 모델계층을 사용하려 하고 있고 어떤 방식으로 개발하려는지 충분히 이해하고 있다 판단하며 당신의 작업방식을 신뢰할 것입니다. 믿지 못하겠지만 매우 간단한 패키지 분류에서조차 얼마나 프로그래밍 실력이 있느냐가 갈리는 것입니다.


현재 작성되고 있는 문서는 웹개발에 초점을 맞추어 작성되었지만 다른 소프트웨어 개발 프로젝트에서도 충분히 응용이 가능한 패턴입니다. 초기세팅에 시간을 허비하는 것만큼 낭비가 없습니다. 만약에 모델의 세부계층에 대해 이해하지 못했다면 검색을 통해 지식을 쌓도록 하는 것이 좋습니다. 모델계층의 계층분할은 곧 역할 분할로 이어지고 효율적이고 체계적인 프로그래밍을 가능케 해줍니다.