context

Development/Common 2012. 11. 21. 10:45



Context의 정의

Context는 사용자와 다른 이용자, 사용자와 시스템 혹은 디바이스간의 인터랙션에 영향을 미치는 사람, 장소, 개체의 현재 상황(Situation)을 규정하는 정보들을 말하고 이런 Context는 시스템으로 하여금 다양한 Sensing Device와 Application을 통해 파악되어 모아져서 바로 Action을 하게 하는데 이용되거나 다른 Context와 묶여 제 3의 결론을 내리는 추론을 내리는데 사용되기도 한다. 


Android API의 Context Class 정의

어플리케이션 환경에 관한 전역 정보의 인터페이스. 이 추상 클래스의 구현체는 안드로이드 시스템에 의해 제공되어진다. 어플리케이션의 구체적인 리소스나 클래스에 대한 접근을 허용한다. 뿐만 아니라 런칭 Activity, broadcasting, receiving intent와 같은 어플리케이션 레벨 기능에 대한 상위 호출을 허용한다.


http://developer.android.com/reference/android/content/Context.html


참고 자료

휴휴휴님 블로그의 Android Context?에 대한 글 이해하기 쉽게 써져 있습니다.

참고하세요. http://huewu.blog.me/110085457720?Redirect=Log


public class ContextExam extends Activity{

private static final String TAG = "ContextExam";

public void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

//1. 시스템 서비스 호출

AccessibilityManager accessibilityManager = 

(AccessibilityManager)this.getSystemService(Context.ACCESSIBILITY_SERVICE);

//2. Context application Pakcage에 연관된 database array return

String[] databaseList = this.databaseList();

for(String database : databaseList){

Log.i(TAG, "1. database : " + database);

}

Log.i(TAG, "2. database path : " + getDatabasePath("webview.db").getAbsolutePath());

//3. Return global application object of the current process

Context applicationContext = this.getApplicationContext();

ApplicationInfo applicationInfo = this.getApplicationInfo();

Log.i(TAG, "3. Application ClassName : " + applicationInfo.className);

Log.i(TAG, "4. Application SourceDir : " + applicationInfo.sourceDir);

Log.i(TAG, "5. Application DataDir : " + applicationInfo.dataDir);

//4. Return AssetManager instance 

AssetManager assetManager = this.getAssets();

try{

String[] assetList = assetManager.list("/data/data/com.example.view");

for(String asset : assetList){

Log.i(TAG, "6. asset : " + asset);

}

}catch(IOException e){

e.printStackTrace();

}

//5. CacheDir 경로 

File cacheDir = getCacheDir();

Log.i(TAG, "7. CacheDir = " + cacheDir.getAbsolutePath());

//6. PackageName

String packageName = getPackageName();

Log.i(TAG, "8. PackageName = " + packageName);

Log.i(TAG, "9. ExternalCacheDir = " + getExternalCacheDir());

Log.i(TAG, "10. ExternalFilesDir = " + getExternalFilesDir(null));

Log.i(TAG, "11. FilesDir = " + getFilesDir());

Log.i(TAG, "12. PackageResourcePath = " + getPackageResourcePath());

    String stringName = getResources().getString(R.string.app_name);

Log.i(TAG, "13. R.string.app_name = " + stringName);

Log.i(TAG, "14. R.string.app_name = " + this.getString(R.string.app_name));

Log.i(TAG, "15. isRestricted = " + isRestricted());

//sendBroadcast();

//startActivity();

//startService();

//registerReceiver();

Log.i(TAG, "16. PackageCodePath  = " + getPackageCodePath());

}

}


출처 - http://blog.daum.net/mellowwind/10853723






<안드로이드 Context 는 수수께기가 많은 클래스입니다>


 Android Context Story

 저에게 안드로이드 Context 는 참 어려운 녀석입니다. 안드로이드 어플리케이션을 개발하며서 가장 빈번하게 사용되는 클래스 중 하나인건 분명한데, 지나가는 사람이 Context 가 뭔가요? 라고 물어 본다면, 스스로가 만족할 만큼 속 시원하게 대답할 수 있는 부분이 없습니다.


 관련해서 이런 저런 자료를 뒤져보고, 잘 읽히지도 않는 안드로이드 소스도 살펴보곤 했습니디만, 이거다... 라고 확실하게 짚고 넘어갈 건데기를 건져내지는 못했습니다. 그저 스스로에게 던진 질문 (안드로이드 Context 는 뭐지?)에 대한 나름의 대답이라는 의미로, 아는 한도내에서 Context 의 기능, Context 가 필요한 이유 그리고 Context 는 어떻게 태어나서 어떻게 사라지는지 정리해 봅니다. 


Context 가 뭐지?

 우선은 정석대로. 안드로이드 개발자 사이트의 Context 클래스 오버뷰 내용을 살펴 봅니다.

Class Overview
Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

어플리케이션 환경에 관한 글로벌 정보를 접근하기 위한 인터페이스. Abstract 클래스이며 실재 구현은 안드로이드 시스템에 의해 제공된다. Context 를 통해, 어플리케이션에 특화된 리소스나 클래스에 접근할 수 있을 뿐만 아니라, 추가적으로, 어플리케이션 레벨의 작업 - Activity 실행, Intent 브로드캐스팅, Intent 수신 등, 을 수행하기 위한 API 를 호출 할 수도 있다.
즉, Context  는 크게 두 가지 역할을 수행하는 Abstract 클래스 입니다.
  • 어플리케이션에 관하여 시스템이 관리하고 있는 정보에 접근하기 

  • 안드로이드 시스템 서비스에서 제공하는 API 를 호출 할 수 있는 기능

 Context 인터페이스가 제공하는 API 중, getPackageName(), getResource() 등의 메서드들이 첫 번째 역할을 수행하는 대표적인 메서드입니다. 보통 get 이라는 접두어로 시작하는 메서드들이지요. 그 외에, startActivity() 나 bindService() 와 같은 

메서드들이 두 번째 역할을 수행하기 위한 메서드라고 할 수 있습니다. 


왜 Context 가 필요할까?

  음... Context 의 기능은 대충 알만합니다. Context 와 관련되서 보다 곤란한 질문은 'Context 가 왜 필요하지?' 라고 생각합니다. 전역적인 어플리케이션 정보에 접근하거나 어플리케이션 연관된 시스템 기능을 수행하기 위해, 시스템 함수를 호출하는 일은 안드로이드가 아닌 다른 플랫폼에서도 늘상 일어나는 일입니다. 또, 그런 일들은 대게의 경우 (제가 아는한...) 어떠한 매개체를 거칠 필요없이, 직접적으로 시스템 API 호출하면 됩니다. 반면 안드로이드에서는 Context 라는 인스턴스화된 매개체를 통해야만 유사한 일들을 수행할 수 있습니다.

 예를 들어, C# 에서 어플리케이션 이름을 가져오고, 다른 어플리케이션을 실행시키는 코드는 아래와 같이 작성될 수 있습니다.
 //Get an Application Name.
 String applicationName = System.AppDomain.CurrentDomain.FriendlyName;

 //Start a new process(application)
 System.Diagnostics.Process.Start("test.exe");
 반면에 안드로이드 Activity 에서는 아래와 같이 작성 해야 됩니다.
//Get an application name
String applicationName = this.getPackageName();

//Start a new activity(application)
this.startActivity(new Intent(this, Test.class));
 보시는 것 처럼, C# 의 경우 System 단에서 제공하는 정적 함수(static function)를 호출 함으로서 간단하게 할 수 있는 일들을 안드로이드에서는 Context 에 정의된 인스턴스 함수를 호출해야만 가능하게 되어있습니다. 즉, 위에서 처럼 반드시 인스턴스화된 Context 클래스(여기서는 this) 를 사용해야 되는 셈이지요. 

 왜 이런 차이가 생기게 된걸까요? 

 대답하기 애매한 질문인 경우에, 질문을 거꾸로 뒤짚어 보면 서광이 비치는 경우가 있습니다. 안드로이드가 아닌 플랫폼에서는 어떻게 정적 함수 호출을 통해서 어플리케이션에 관한 정보를 가져오고, 시스템 함수를 호출 할 수 있는 걸까요? 

 제가 OS 에 관한 지식이 짧아, 꼭 찝어 이야기하기에는 어려움이 있지만... 일반적인 경우, 어플리케이션이 프로세스가 아주 긴밀하게 연결되어 있기 때문입니다. OS 커널의 가장 중요한 일 중 하나는 프로세스를 관리하는 것 입니다. (아마도..) 특정 프로세스가 특정 어플리케이션과 맵핑 된다면, 우리는 별다른 매개체 없이 시스템에 직접 프로세스의 정보에 관한 물어 볼 수 있고, 프로세스와 연관된 시스템 함수를 호출 할 수 있습니다. (이미 관리하고 있는 정보에 대해 묻고 있음으로)

 그런데 안드로이드에서 어플리케이션과 프로세스와의 관계는 조금 요상한 구석이 있습니다. 안드로이드에서 어플리케이션과 프로세스는 서로 독립적으로 존재입니다. 이와 관련된 구체적인 내용이나 이러한 구조를 갖는 원인에 대해서는 안드로이드 멀티태스킹에 관한 구글 개발자 블로그 포스트 에서 자세하게 다루어져 있습니다. (블로그에 번역해 두었으니 꼭 한번 읽어 보세요.)


 예를 들자면, 안드로이드 플랫폼에서는 프로세스가 없는 상황에도 어플리케이션은 살아있는 것처럼 사용자에게 표시되기도 하고, 메모리가 부족한 상황이 될 경우, 작동중이던 프로세스가 강제로 종료되고, 대시 해당 프로세스에서 작동중이던 어플리케이션에 관한 일부 정보만 별도로 관리하고, 이 후에 메모리 공간이 확보되면 저장되어있던 어플리케이션 정보를 바탕으로 새로운 프로세스를 시작하는등의 신기한 일이 벌어집니다.


 안드로이드에서도 프로세스는 당연히 OS 커널 (리눅스)에서 관리됩니다. 어플리케이션과 프로세스가 별도로 관리되고 있다면, 어플리케이션 정보는 어디에서 관리하고 있을까요? 안드로이드의 시스템 서비스 중 하나인 ActivityManagerService 에서 그 책임을 집니다. 그렇다면 ActivityManagerService 는 어떤식으로 어플리케이션을 관리하고 있을까요? 이외로 단순 합니다. 특정 토큰을 키값으로 'Key-Value' 쌍으로 이루어진 배열을 이용해 현재 작동중인 어플리케이션 정보를 관리합니다.


 거의 결론에 다다른거 같습니다. Context 는 어플리케이션과 관련된 정보에 접근하고자 하거나 어플리케이션과 연관된 시스템 레벨의 함수를 호출하고자 할 때 사용됩니다. 그런데 안드로이드 시스템에서 어플리케이션 정보를 관리하고 있는 것은 시스템이 아닌, ActivityManagerService 라는 일종의 또 다른 어플리케이션입니다. 따라서 다른 일반적은 플랫폼과는 달리, 안드로이드에서는 어플리케이션과 관련된 정보에 접근하고자 할때는 ActivityManagerService 를 통해야만 합니다. 당연히 정보를 얻고자 하는 어플리케이션이 어떤 어플리케이션인지에 관한 키 값도 필요해집니다.


 즉, 안드로이드 플랫폼상에서의 관점으로 샆펴보면, Context 는 다음과 같은두 가지 역할을 수행하기 때문에 꼭 필요한 존재입니다.

  • 자신이 어떤 어플리케이션을 나타내고 있는지 알려주는 ID 역할 

  • ActivityManagerService 에 접근할 수 있도록 하는 통로 역할 

 정리하자면 이렇습니다.


<나는 너가 누구인지 알고있다.>


 일반 OS 플랫폼에서 어플리케이션은 곧 Process 입니다. 특정 어플리케이션이 OS 에게 내가 어떤 Process 인지만 알려주면 어플리케이션 관련된 정보를 얼마든지 획득 할 수 있습니다. 이른바 자신의 존재 자체가 자신임을 증명해주는 '지문인식' 혹은 '홍채인식' 등의 '생체인식' 과 비슷한 개념이기 떄문에 Context 와 같은 애매한 중간 매개체가 존재할 이유가 없습니다. 


<내 Context 가 내가 누구인지 알고있다.>


 하지만 안드로이드 플랫폼은 조금 다릅니다. 비유하자면 '생체인식' 보다는 'ID카드' 를 통한 보안 시스템과 유사한 구조입니다. 특정 어플리케이션이 자신이 본인임을 확인 받을 수 있는 방법은 자신이 작동중인 Process 를 보여주는 것이 아니라, 자신이 건내받은 ID카드를 제시하는 것 입니다. 이 때 ID카드의 역할을 수행하는 것이 바로 Context 이고, 당연히 이 카드는 위변조가 가능하기때문에, 자신의 권한을 제삼의 어플리케이션에게 넘겨주는 PendingIntent 와 같은 기능도 가능해집니다.

 Context 는 언제 태어날까?

 Context 에 관한 마지막 질문은 약간의 부록과도 같습니다. Context 는 언제 태어날까요? 대답은 당연히 어플리케이션이 태어날 때입니다. 그렇다면 하나의 어플리케이션을 구성하는 각종 컴포넌트들 - Activity,Service,BroadcastReceiver 들은 모두 동일한 Context 를 공유해서 사용하고 있을까요? 저도 처음에는 그렇지 않을까 생각했었는데 알고보니 그렇지는 않더군요.

 Activity 와 Service 가 생성될 때 만들어지는 Context 와 BroadcastReceiver 가 호촐될 때( onReceive() ) 전해지는 Context 는 모두 서로다른 인스턴스입니다. 즉, Context 는 어플리케이션이 시작될 때는 물론이요, 어플리케이션 컴포넌트들이 생성될때마다 태어나는 셈입니다. 물론, 새롭게 생성되는 Context 들이 부모와 완전히 독립되어 있는 존재는 아니고 '거의' 비슷한 내용을 담고 있습니다.

< 파생된 Context 인스턴스들은 언제든지 부모 Context 에 접근할 수 있다.>

 어째서 동일한 Context 인스턴스를 어플리케이션 컴포넌트들이 공유해서 사용하지 않고, 모두 서로 다른 (그러나 알고보면 알맹이는 거의 같은) 인스턴스를 만들어 사용하고 있을까요? 음... 어려운 문제입니다. 잘 모르겠네요. 일단 한 가지 분명한 원인이 있습니다.

 Context 의 기능 중, 시스템 API 를 호출하는 기능과 관련되어 한 가지 문제점이 있습니다. 어떤 어플리케이션 컴포넌트가 시스템 API를 호출하느냐에 따라서 서로 다른 결과가 나타나야 한다는 점입니다. 
예를들어, Service 에서 Activity 실행하기포스트에서 언급한 것 처럼, 동일한 형태로 startActivity 메서드 호출하더라도, 일반적인 Activity 에서는 정상적으로 새로운 Activity 를 시작하게 되지만, Service 에서 호출할 경우에는 예외가 발생합니다. 만일 어플리케이션을 구성하는 Service 와 Activity 가 서로 동일한 Context 인스턴스를 공유하고 있다면 동일한 메서드 호출에 대하여 서로 다른 결과를 나타내도록 구현하지 못했을겁니다.


 따라서, 현재 안드로이드 시스템은 어플리케이션 Context 를 기반으로 컴포넌트를 위한 Context 를 생성할 때 해당 Context 가 어떤 종류의 컴포넌트인지 알 수 있도록 약간의 표시를 해두곤 합니다. 

 또 한가지 안드로이드 개발팀의 코딩 스타일과 관련된 문제가 아닌가 하는 심증을 갖고 있습니다. 안드로이드 어플리케이션 컴포넌트들은 Context 를 포함하는 대신 상속받은 방식으로 구현되어 있습니다. Context 를 포함하는 방식이였다면 아마 작성해야할 코드량이 제법 늘어났을 것으로 짐작됩니다 (개별 컴포넌트 클래스마다 추상 API 들을 반복해서 구현해주어야 하니까...). 하지만 포함대신 상속받는 방식이 적용된만큼 당연하게 어플리케이션 Context 인스턴스를 어플리케이션을 구성하는 개별 컴포넌트들이 동일하게 사용하는 것은 어플리케이션 클래스 자체를 엄청나게 복잡하게 구현하지 않는 이상 불가능한 일이 되어 버렸습니다.

마무리

 결론! 결국, 안드로이드 Context 는 여러가지 이유로 기존 플랫폼과는 다른 방식으로 어플리케이션을 관리하고 있고, 때문에 기존 플랫폼들에서는 단순하게 시스템 API 를 통해 할 수 있는 일들을, Context 인스턴스라는 조금은 귀찮지만 강력한 녀석을 통해 대행 처리하고 있다고 할 수 있겠습니다.

후기?

 휴... 이제 끝입니다. 생각보다 정말로 긴 글이 되었네요. 사실 이 포스트를 준비한 것은 약 3주전인데, Context 라는 것이 도통 이해하기 힘든 부분이 있어서.. 시간을 끌다 끌다, 에휴... 적당히 이해가 가는 부분에 대해서만이라도 글을 남겨 보자... 라는 생각으로 키보드를 두드리게 되었습니다.

 따라서... 이야기를 풀어가는데 여러가지 제 개인적인 추측(혹은 때려짐작...) 이 많이 가미된 내용임을 이해해주시고, Context 관련하여 잘못된 부분과 부족한 부분을 덧글로 남겨주시면 정말 큰 도움이 되겠습니다.


출처 - http://huewu.blog.me/110085457720?Redirect=Log











'Development > Common' 카테고리의 다른 글

MD 확장자  (0) 2013.03.27
SMTP & POP3 Server list  (0) 2013.02.11
accessor 와 mutator  (0) 2012.11.05
ISO 8601 - 날짜와 시간 표기 국제 표준 규격  (1) 2012.10.02
http - Content Length와 Transfer Encoding  (0) 2012.07.18
Posted by linuxism
,


Maven 기초 사용법

Maven 2008/12/17 10:32

필자의 경우 Maven을 사용한지 2년이 넘어가는 데, Maven을 접한 이후 특별한 제약이 없는 한 Maven을 이용해서 프로젝트를 진행하고 있다. 흔히 Maven을 Ant와 비슷한 빌드(build) 도구로 생각하기도 하는데, Ant가 Build에 초점이 맞춰져 있다면 Maven은 프로젝트의 빌드 라이프사이클 기반 프레임워크를 제공하는데 초점이 맞춰져 있다.

본 글에서는 Maven을 이용해서 프로젝트를 생성하는 방법과, 디렉토리 구조, POM 파일 기본 구성, Maven 라이프 사이클 그리고 Maven 프로젝트를 이클립스 프로젝트로 생성하는 방법을 살펴보도록 하겠다.

Maven 설치

http://maven.apache.org/ 사이트를 방문하면 최신 버전의 Maven을 다운로드 받을 수 있다. 현재 최신 버전은 2.0.9이며, apache-maven-2.0.9-bin.zip 파일이나 apache-maven-2.0.9-bin.tar.gz 파일 등 원하는 형식으로 압축된 파일을 다운로드 받으면 된다.

파일을 다운로드 받은 뒤에는 다음과 같은 순서로 설치 작업을 해 주면 된다.

  1. 다운로드 받은 파일의 압축을 푼다. 예를 들면 /usr/local 디렉터리나 C:\ 에 압축을 푼다.
  2. M2_HOME 환경 변수에 설치 디렉터리를 값으로 설정한다. 예, M2_HOME=c:\apache-maven-2.0.9
  3. PATH 환경 변수에 M2를 추가한다. 예, PATH=%M2_HOME%\bin;%PATH%
  4. JAVA_HOME 환경 변수가 JDK 설치 디렉터리를 가리키고 있는 지 확인한다. 예, JAVA_HOME=c:\jdk1.6.0_07

환경 변수를 모두 설정해 주었다면 아래의 명령어를 사용해서 Maven이 제대로 실행되는 지 확인해보자.

C:\Users\madvirus>mvn -version
Maven version: 2.0.9
Java version: 1.5.0_16
OS name: "windows vista" version: "6.0" arch: "x86" Family: "windows"


Maven 프로젝트 생성하기

설치가 끝났다면 Maven 프로젝트를 생성해 보자. 명령 프롬프트에서 아래 명령어를 실행하면 된다. (아래 명령어를 처음 실행할 경우 꽤 오랜 시간이 걸리는데, 그 이유는 Maven이 필요한 플러그인과 모듈을 다운로드 받기 때문이다. Maven 배포판은 최초로 Maven을 사용하는 데 필요한 모듈만 포함하고 있고, 그 외에 archetype 플러그인, compiler 플러그인 등 Maven을 사용하는 데 필요한 모듈은 포함하고 있지 않다. 이들 모듈은 실제로 필요할 때 Maven 중앙 리포지토리에서 로딩된다.)

mvn archetype:generate

위 명령어를 실행하면 Maven 프로젝트를 생성하는 데 필요한 정보를 입력하라는 메시지가 단계적으로 뜨고, 각 항목별로 알맞은 값을 입력해주면 된다. 아래는 실행 화면 예이다. 붉은색으로 표시한 것은 입력한 값이다.

$ mvn archetype:generate
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO]    task-segment: [archetype:generate] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] Preparing archetype:generate
...
Choose archetype:
1: internal -> appfuse-basic-jsf (AppFuse archetype for creating a web application with Hibernate, Spring and JSF)
...
...
15: internal -> maven-archetype-quickstart ()
16: internal -> maven-archetype-site-simple (A simple site generation project)
17: internal -> maven-archetype-site (A more complex site project)
18: internal -> maven-archetype-webapp (A simple Java web application)
...
41: internal -> gmaven-archetype-mojo (Groovy mojo archetype)
Choose a number:  (1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/.../41) 15: : 15
Define value for groupId: : net.daum.cafe
Define value for artifactId: : simple-app
Define value for version:  1.0-SNAPSHOT: :
Define value for package:  net.daum.cafe: :
Confirm properties configuration:
groupId: net.daum.cafe
artifactId: simple-app
version: 1.0-SNAPSHOT
package: net.daum.cafe
 Y: : Y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating OldArchetype: maven-archetype-quickstart:RELEASE
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: net.daum.cafe
[INFO] Parameter: packageName, Value: net.daum.cafe
[INFO] Parameter: basedir, Value: c:\work\eclipse34workspace\personal
[INFO] Parameter: package, Value: net.daum.cafe
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: artifactId, Value: simple-app
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] OldArchetype created in dir: c:\work\eclipse34workspace\personal\simple-app
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3 minutes 5 seconds
[INFO] Finished at: Mon Dec 15 11:37:07 KST 2008
[INFO] Final Memory: 7M/14M
[INFO] ------------------------------------------------------------------------ 

위 과정에서 실제로 입력하는 값은 다음과 같다.

  • groupId - 프로젝트 속하는 그룹 식별 값. 회사, 본부, 또는 단체를 의미하는 값이 오며, 패키지 형식으로 계층을 표현한다. 위에서는 net.daum.cafe를 groupId로 이용하였다.
  • artifactId - 프로젝트 결과물의 식별 값. 프로젝트나 모듈을 의미하는 값이 온다. 위에서는 simple-app을 artifactId로 이용하였다.
  • version - 결과물의 버전을 입력한다. 위에서는 기본 값인 1.0-SNAPSHOT을 사용하였다.
  • package - 기본적으로 생성할 패키지를 입력한다. 별도로 입력하지 않을 경우 groupId와 동일한 구조의 패키지를 생성한다.
기존에는 archetype:create 골을 이용해서 프로젝트를 생성했었다. 하지만, maven-archetype-plugin의 버전이 올라가면서 archetype:generate 골이 추가되었고, archetype:create 골은 deprecated 되었다. 하지만, 하위 호환을 위해 archetype:create 골은 아직 지원되고 있다.

Maven 프로젝트의 기본 디렉토리 구조

archetype:generate 골이 성공적으로 실행되면, artifactId에 입력한 값과 동일한 이름의 디렉터리가 생성된다. 위 경우에는 현재 디렉터리에 simple-app 이라는 하위 디렉터리가 생성된다. 위 과정에서 선택한 archetype은 maven-archetype-quickstart 인데, 이 archetype을 선택했을 때 생성되는 디렉터리 구조는 다음과 같다.

simple-app
├─src
│   ├─main
│   │  └─java
│   │      └─net
│   │          └─daum
│   │              └─cafe
│   │                  └─App.java
│   ├─test
│       └─java
│           └─net
│               └─daum
│                   └─cafe
│                      └─AppTest.java
└─pom.xml

기본적으로 생성되는 디렉터리를 포함한 Maven 프로젝트의 주요 디렉터리는 다음과 같다.

  • src/main/java - 자바 소스 파일이 위치한다.
  • src/main/resources - 프로퍼티나 XML 등 리소스 파일이 위치한다. 클래스패스에 포함된다.
  • src/main/webapp - 웹 어플리케이션 관련 파일이 위치한다. (WEB-INF 디렉터리, JSP 파일 등)
  • src/test/java - 테스트 자바 소스 파일이 위치한다.
  • src/test/resources - 테스트 과정에서 사용되는 리소스 파일이 위치한다. 테스트 시에 사용되는 클래스패스에 포함된다.

기본적으로 생성되지 않은 디렉터리라 하더라도 직접 생성해주면 된다. 예를 들어, src/main 디렉터리에 resources 디렉터리를 생성해주면 Maven은 리소스 디렉터리로 인식한다.

컴파일 해보기/테스트 실행 해보기/패키지 해보기

이제 간단하게 컴파일과 테스트를 실행해보자. 소스 코드를 컴파일 하려면 다음과 같은 명령어를 실행해주면 된다.

mvn compile

컴파일 된 결과는 target/classes 디렉터리에 생성된다.

테스트 클래스를 실행해보고 싶다면 다음과 같은 명령어를 사용하면 된다.

$ mvn test
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building simple-app
[INFO]    task-segment: [test]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [surefire:test]
[INFO] Surefire report directory: c:\work\eclipse34workspace\personal\simple-app\target\surefire-reports
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running net.daum.cafe.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.051 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3 seconds
[INFO] Finished at: Tue Dec 16 14:26:00 KST 2008
[INFO] Final Memory: 4M/8M
[INFO] ------------------------------------------------------------------------

그러면, 테스트 코드를 컴파일한 뒤 테스트 코드를 실행한다. 그리고 테스트 성공 실패 여부를 화면에 출력한다. 컴파일 된 테스트 클래스들은 target/test-classes 디렉터리에 생성되고, 테스트 결과 리포트는 target/surefire-reports 디렉터리에 저장된다.

(아무것도 한게 없으니 당연하지만) 모든 코드가 정상적으로 만들어지고 테스트도 통과했으니, 이제 배포 가능한 jar 파일을 만들어보자. 아래 명령어를 실행하면 프로젝트를 패키징해서 결과물을 생성한다.

$ mvn package
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building simple-app
[INFO]    task-segment: [package]
[INFO] ------------------------------------------------------------------------
...
...
[INFO] [jar:jar]
[INFO] Building jar: c:\work\eclipse34workspace\personal\simple-app\target\simple-app-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4 seconds
[INFO] Finished at: Tue Dec 16 15:20:10 KST 2008
[INFO] Final Memory: 6M/13M
[INFO] ------------------------------------------------------------------------

mvn package가 성공적으로 실행되면, target 디렉터리에 프로젝트 이름과 버전에 따라 알맞은 이름을 갖는 jar 파일이 생성된다. 위 예제의 경우에는 simple-app-1.0-SNAPSHOT.jar 파일이 생성된 것을 확인할 수 있다.

POM 파일 기본

Maven 프로젝트를 생성하면 pom.xml 파일이 프로젝트 루트 디렉터리에 생성된다. 이 pom.xml 파일은 Project Object Model 정보를 담고 있는 파일로서, 이 파일에서 다루는 주요 설정 정보는 다음과 같다.

  • 프로젝트 정보 - 프로젝트의 이름, 개발자 목록, 라이센스 등의 정보를 기술
  • 빌드 설정 - 소스, 리소스, 라이프 사이클 별 실행할 플러그인 등 빌드와 관련된 설정을 기술
  • 빌드 환경 - 사용자 환경 별로 달라질 수 있는 프로파일 정보를 기술
  • POM 연관 정보 - 의존 프로젝트(모듈), 상위 프로젝트, 포함하고 있는 하위 모듈 등을 기술

archetype:create 골 실행시 maven-archetype-quickstart Archetype을 선택한 경우 생성되는 pom.xml 파일은 다음과 같다.

[기본으로 생성되는 pom.xml 파일]

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>net.daum.cafe</groupId>
  <artifactId>simple-app</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>simple-app</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

위 POM 파일에서 프로젝트 정보를 기술하는 태그는 다음과 같다.

  • <name> - 프로젝트 이름
  • <url> - 프로젝트 사이트 URL

POM 연관 정보는 프로젝트간 연관 정보를 기술하는데, 관련 태그는 다음과 같다.

  • <groupId> - 프로젝트의 그룹 ID 설정
  • <artifactId> - 프로젝트의 Artifact ID 설정
  • <version> - 버전 설정
  • <packaging> - 패키징 타입 설정. 위 코드의 경우 프로젝트의 결과 Artifact가 jar 파일로 생성됨을 의미한다. jar 뿐만 아니라 웹 어플리케이션을 위한 war나 JEE를 위한 ear 등의 패키징 타입이 존재한다.
  • <dependencies> - 이 프로젝트에서 의존하는 다른 프로젝트 정보를 기술한다.
    • <dependency> - 의존하는 프로젝트 POM 정보를 기술
      • <groupId> - 의존하는 프로젝트의 그룹 ID
      • <artifactId> - 의존하는 프로젝트의 artifact ID
      • <version> - 의존하는 프로젝트의 버전
      • <scope> - 의존하는 범위를 설정

의존 설정

<dependency> 부분의 설정에 대해서 좀 더 살펴보도록 하자.

Maven을 사용하지 않을 경우 개발자들은 코드에서 필요로 하는 라이브러리를 각각 다운로드 받아야 한다. 예를 들어, 아파치 commons DBCP 라이브러리를 사용하기 위해서는 DBCP 뿐만 아니라 common pool 라이브러리도 다운로드 받아야 한다. 물론, commons logging을 비롯한 라이브러리도 모두 추가로 다운로드 받아 설치해 주어야 한다. 즉, 코드에서 필요로 하는 라이브러리 뿐만 아니라 그 라이브러리가 필요로 하는 또 다른 라이브러리도 직접 찾아서 설치해 주어야 한다.

하지만, Maven을 사용할 경우에는 코드에서 직접적으로 사용하는 모듈에 대한 의존만 추가해주면 된다. 예를 들어, commons-dbcp 모듈을 사용하고 싶은 경우 다음과 같은 <dependency> 코드만 추가해주면 된다.

<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.2.1</version>
</dependency> 

그러면, Maven은 commons-dbcp 뿐만 아니라 commons-dbcp가 의존하는 라이브러리가 자동으로 처리해준다. 실제로 1.2.1 버전의 commons-dbcp 모듈의 pom.xml 파일을 보면 의존 부분이 다음과 같이 설정되어 확인할 수 있다.

  <dependencies>
    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>2.1</version>
    </dependency>
    <dependency>
      <groupId>commons-pool</groupId>
      <artifactId>commons-pool</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>javax.sql</groupId>
      <artifactId>jdbc-stdext</artifactId>
      <version>2.0</version>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>xml-apis</groupId>
      <artifactId>xml-apis</artifactId>
      <version>2.0.2</version>
    </dependency>
    <dependency>
      <groupId>xerces</groupId>
      <artifactId>xerces</artifactId>
      <version>2.0.2</version>
    </dependency>
  </dependencies>

Maven은 commons-dbcp 모듈을 다운로드 받을 때 관련 POM 파일도 함께 다운로드 받는다. (다운로드 받은 파일은 로컬 리포지토리에 저장되는데 이에 대한 내용은 뒤에서 다시 설명하겠다.) 그리고 POM 파일에 명시한 의존 모듈을 함께 다운로드 받는다. 즉, commons-dbcp 1.2.1 버전의 경우 commons-collections 2.1 버전과 commons-pool 1.2 버전 등을 함께 다운로드 받는다. 이런 식으로 반복해서 다운로드 받은 모듈이 필요로 하는 모듈을 다운로드 받고 이들 모듈을 현재 프로젝트에서 사용할 클래스패스에 추가해준다.

따라서, 개발자는 일일이 필요한 모듈을 다운로드 받을 필요가 없으며, 현재 코드에서 직접적으로 필요로 하는 모듈에 대해서만 <dependency>로 추가해주면 된다. 나머지 의존은 모두 Maven이 알맞게 처리해준다.

mvnrepository.com 사이트에서 POM 정보 찾기

Maven을 사용할 때 가장 많이 찾게 되는 사이트가 mvnrepository.com이다. mvnrepository.org는 Maven 중앙 리포지토리에 등록된 POM 정보를 검색해주는 기능을 제공해준다. 이 사이트를 통해서 추가할 라이브러리의 <dependency> 설정 정보를 구할 수 있다.


의존의 scope: compile, runtime, provided, test

앞의 pom.xml 파일에서 <dependency> 부분을 보면 <scope>를 포함하고 있는 것과 그렇지 않은 것이 존재한다는 것을 알 수 있다. <scope>는 의존하는 모듈이 언제 사용되는 지를 설정할 때 사용되며, <scope>에 올 수 있는 값은 다음의 네 가지가 존재한다.

  • compile - 컴파일 할 때 필요. 테스트 및 런타임에도 클래스패스에 포함된다. <scope>를 설정하지 않을 경우 기본 값은 compile 이다.
  • runtime - 런타임에 필요. JDBC 드라이버 등이 예가 된다. 프로젝트의 코드를 컴파일 할 때는 필요하지 않지만, 실행할 때 필요하다는 것을 의미한다. 배포시 포함된다.
  • provided - 컴파일 할 때 필요하지만, 실제 런타임 때에는 컨테이너 같은 것에서 기본으로 제공되는 모듈임을 의미한다. 예를 들어, 서블릿이나 JSP API 등이 이에 해당한다. 배포시 제외된다.
  • test - 테스트 코드를 컴파일 할 때 필요. Mock 테스트를 위한 모듈이 예이다. 테스트 시에 클래스패스에 포함되며, 배포시 제외된다.


원격 리포지토리와 로컬 리포지토리

Maven은 컴파일이나 패키징 등 작업을 실행할 때 필요한 플러그인이나 pom.xml 파일의 <dependency> 등에 설정한 모듈을 Maven 중앙 리포지토리에서 다운로드 받는다. 현재 중앙 리포지토리의 주소는http://repo1.maven.org/maven2/ 이다.

원격 리포지토리에서 다운로드 받은 모듈은 로컬 리포지토리에 저장된다. 로컬 리포지토리는 [USER_HOME]/.m2/repository 디렉터리에 생성되며, 로컬 리포지토리에는 다음과 같은 형식의 디렉터리를 생성한 뒤 다운로드 받은 모듈을 저장한다.

[groupId]/[artifactId]/[version]

예를 들어, commons-dbcp 1.2.1 버전의 경우, 모듈 및 관련 POM 파일이 저장되는 디렉터리는 다음과 같다.

[USER_HOME]/.m2/repository/commons-dbcp/commons-dbcp/1.2.1

위 디렉터리에 저장되는 파일은 패키징 된 모듈 파일, pom 파일, 그리고 소스 코드 다운로드 옵션을 실행한 경우에는 소스 코드를 포함한 jar 파일이 포함된다.

일단 원격 리포지토리로부터 파일을 다운로드해서 로컬 리포지토리에 저장하면, 그 뒤로는 로컬 리포지토리에 저장된 파일을 사용하게 된다.

Maven 라이프사이클(Lifecycle)과 플러그인 실행

본 글의 서두에 Maven은 프로젝트의 라이프사이클 기반 프레임워크를 제공한다고 했다. 앞서 프로젝트를 생성한 뒤 컴파일하고(mvn compile), 테스트 하고(mvn test), 패키징 하는(mvn package) 과정을 정해진 명령어를 이용해서 실행했는데, 이때 compile, test, package는 모두 빌드 라이프사이클에 속하는 단계이다.

Maven은 clean, build (default), site의 세 가지 라이프사이클을 제공하고 있다. 각 라이프사이클은 순서를 갖는 단계(phase)로 구성된다. 또한, 각 단계별로 기본적으로 실행되는 플러그인(plugin) 골(goal)이 정의되어 있어서 각 단계마다 알맞은 작업이 실행된다. 아래 표는 디폴트 라이프사이클을 구성하고 있는 주요 실행 단계를 순서대로 정리한 것이다.

[표] 디폴트 라이프사이클의 주요 단계(phase)

 단계설명 단계에 묶인 플러그인 실행
generate-sources컴파일 과정에 포함될 소스를 생성한다. 예를 들어,  DB 테이블과 매핑되는 자바 코드를 생성해주는 작업이 이 단계에서 실행된다.
process-sources필터와 같은 작업을 소스 코드에 처리한다. 
generate-resources패키지에 포함될 자원을 생성한다.  
process-resources필터와 같은 작업을 자원 파일에 처리하고, 자원 파일을 클래스 출력 디렉토리에 복사한다.resources:resources 
compile소스 코드를 컴파일해서 클래스 출력 디렉터리에 클래스를 생성한다.compiler:compile
generate-test-sources테스트 소스 코드를 생성한다. 예를 들어, 특정 클래스에서 자동으로 테스트 케이스를 만드는 작업이 이 단계에서 실행된다.
process-test-sources필터와 같은 작업을 테스트 소스 코드에 처리한다.resources:testResources 
generate-test-resources테스트를 위한 자원 파일을 생성한다.  
process-test-resources필터와 같은 작업을 테스트 자원 파일에 처리하고, 테스트 자원 파일을 테스트 클래스 출력 디렉터리에 복사한다. 
test-compile테스트 소스 코드를 컴파일해서 테스트 클래스 추력 디렉터리에 클래스를 생성한다.compiler:testCompile
test테스트를 실행한다.surefire:test
package컴파일 된 코드와 자원 파일들을 jar, war와 같은 배포 형식으로 패키징한다.패키징에 따라 다름
jar - jar:jar
war - war:war
pom - site:attach-descriptor
ejb - ejb:ejb
install로컬 리포지토리에 패키지를 복사한다.install:install
deploy생성된 패키지 파일을 원격 리포지토리에 등록하여, 다른 프로젝트에서 사용할 수 있도록 한다.deploy:deploy

라이프사이클의 특정 단계를 실행하려면 다음과 같이 mvn [단계이름] 명령어를 실행하면 된다.

mvn test
mvn deploy

라이프사이클의 특정 단계를 실행하면 그 단계의 앞에 위치한 모든 단계가 실행된다. 예를 들어, test 단계를 실행하면 test 단계를 실행하기에 앞서 'generate-sources' 단계부터 'test-compile' 단계까지 각 단계를 순서대로 실행한다. 각 단계가 실행될 때는 각 단계에 묶인 골(goal)이 실행된다.

플러그인을 직접 실행할 수도 있다. mvn 명령어에 단계 대신 실행할 플러그인을 지정하면 된다.

mvn surefire:test

단, 플러그인 골을 직접 명시한 경우에는 해당 플러그인만 실행되기 때문에 라이프사이클의 단계가 실행되지는 않는다.

플러그인 골(Plugin Goal)

Maven에서 플러그인을 실행할 때에는 '플러그인이름:플러그인지원골'의 형식으로 실행할 기능을 선택한다. 예를 들어, compiler:compile은 'compiler'는 플러그인에서 'compile' 기능(goal)을 실행한다는 것을 뜻한다.


맺음말

이번 글에서는 Maven의 기본 사용법을 살펴봤다. Maven이 제공하는 의존 관리는 개발자를 jar 지옥(?)에서 구해준다는 것을 알 수 있었다. 또한, Maven은 표준화된 라이프사이클을 제공하고 있기 때문에 개발자가 컴파일-테스트-패키징 등의 과정을 손으로 정의하지 않아도 되며, 개발자는 Maven이 제공하는 단계 중 필요한 단계만 실행하면 된다. 그럼, 나머지 작업(컴파일, 테스트 실행, jar 파일 생성)은 모두 Maven이 처리해준다.

다음 글에서는 많이 사용되고 있는 개발툴인 이클립스에서 Maven 프로젝트를 import하는 방법과 컴파일러 버전을 명시하는 방법을 살펴보도록 하겠다.


관련 자료:


출처 - http://javacan.tistory.com/entry/MavenBasic




Posted by linuxism
,

jetty - maven 연동

Web/WAS 2012. 11. 21. 08:54


필자가 Maven에서 가장 좋아하는 기능 중의 하나를 꼽으라면? Maven과 Jetty를 연동하여 웹 어플리케이션을 테스트할 수 있다는 것이다. 이클립스에서 톰캣이나 다른 WAS를 연동해서 테스트 해 본 개발자라면 프로젝트 별로 톰캣 연동 설정을 하는 게 얼마나 귀찮은 일인지 알 것이다. 동일한 프로젝트가 여러 브랜치에서 개발되고 있고, 각 브랜치를 하나의 이클립스에서 동시에 테스트 해야 할 경우 이클립스에서 WAS를 연동해서 테스트 하는 건 더욱 더 성가신 작업이 될 수 있다.

Maven은 웹 어플리케이션을 위한 프로젝트 타입을 제공하고 있을 뿐만 아니라 maven-jetty-plugin을 이용해서 프로젝트 별로 간단한 설정만으로 Jetty를 이용해서 웹 어플리케이션 테스트를 진행할 수 있다. 본 글에서는 Maven을 이용해서 웹 프로젝트를 생성하는 방법과 Jetty를 연동해서 웹 어플리케이션을 실행하고 테스트하는 방법을 살펴보도록 하겠다.

웹 어플리케이션 프로젝트 생성하기

웹 어플리케이션을 생성할 때에는 'archetype:generate' 플러그인 골을 실행한 뒤, Archetype으로 'maven-archetype-webapp'을 선택하면 된다. (archetype:generate 플러그인 골을 실행하는 방법은 필자가 쓴 'Maven 기초 사용법'을 참고하기 바란다.)

maven-archetype-webapp Arcetype을 이용해서 Maven 프로젝트를 생성하면 다음과 같이 디렉터리 구조가 생성된다.

simple-web
└─src
    └─main
        ├─resources
        └─webapp
            └─WEB-INF (web.xml 파일 포함)

src/main/webapp 디렉터리는 웹 어플리케이션에서 필요로 하는 HTML, 이미지, JSP 등의 파일이 위치한다. 웹 어플리케이션에서 필요로 하는 클래스를 추가해야 할 경우 src/main/java 디렉터리를 생성한 뒤 이 디렉터리에 자바 소스 코드를 위치시키면 된다.

maven-archetype-webapp을 통해서 생성된 pom.xml 파일은 다음과 같다.

<project xmlns="http://maven.apache.org/POM/4.0.0
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.daum.cafe</groupId>
    <artifactId>simple-web</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>simple-web Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>simple-web</finalName>
    </build>
</project>

위 코드를 보면 <packaging>의 값이 war인 것을 알 수 있는데, 이는 mvn package 명령어를 실행할 때 생성되는 결과물이 war 파일이라는 것을 의미하며, 이때 생성되는 war 파일의 이름은 <build>/<finalName>의 값을 사용한다. 위 예의 경우에는 simple-web.war 파일이 생성된다. mvn package 명령어로 생성되는 war 파일의 이름을 변경하고 싶다면 <finalName>에 원하는 값을 입력해 주면 된다.

웹 어플리케이션 개발시 주로 사용되는 의존 프로젝트 목록

웹 기반의 엔터프라이즈 어플리케이션을 개발할 때에는 서블릿 API, JSP API, JTA API 등 Sun에서 정의한 API가 필요하다. 문제는 라이센스 때문에 Sun이 제공하는 API를 Maven 중앙 리포지토리에서 다운로드 받을 수 없다는 점이다. 중앙 리포지토리에 없는 라이브러리는 직접 다운로드 받아 로컬 리포지토리에 등록해주어야 하는데, 다행히 Geronimo 프로젝트에서 Sun의 주요 API를 만들어 Maven 리포지토리에 등록하고 있다. 따라서, 서블릿 API나 JSP API 가 필요한 경우 Geronimo에서 제공하는 프로젝트를 pom.xml 파일의 <dependency>에 추가해주면 된다.

아래 표는 Geronimo가 제공하는 Sun의 주요 표준 API 목록을 정리한 것이다. 참고로 아래 표에 정리된 Artifact 들의 groupId는 org.apache.geronimo.specs 이다.

표준표준 버전Artifact ID Artifact 최신 버전
Activation1.0.2 geronimo-activation_1.0.2_spec1.2 
Activation1.1geronimo-activation_1.1_spec1.0.2
EJB2.1geronimo-ejb_2.1_spec1.1 
EJB3.0geronimo-ejb_3.0_spec1.0 .1
J2EE Connector1.5geronimo-j2ee-connector_1.5_spec 2.0.0
J2EE JACC1.0 geronimo-j2ee-jacc_1.0_spec 1.1.1 
JACC1.1 geronimo-jacc_1.1_spec 1.0.1
JavaMail 1.3.1 geronimo-javamail_1.3.1_spec 1.3 
JavaMail1.4 geronimo-javamail_1.4_spec1.5
JMS1.1geronimo-jms_1.1_spec 1.1.1
JPA3.0 geronimo-jpa_3.0_spec 1.1.1
JSP2.0 geronimo-jsp_2.0_spec1.1
JSP2.1 geronimo-jsp_2.1_spec1.0.1
JTA1.0.1Bgeronimo-jta_1.0.1B_spec 1.1.1 
JTA1.1 geronimo-jta_1.1_spec1.1.1
Servlet2.4 geronimo-servlet_2.4_spec1.1.1 
Servlet2.5 geronimo-servlet_2.5_spec1.2

예를 들어, 개발 과정에서 서블릿 2.5 API가 필요할 경우 pom.xml에 다음과 같이 <dependency>를 추가해주면 된다.

<dependency>
    <groupId>org.apache.geronimo.specs</groupId>
    <artifactId>geronimo-servlet_2.5_spec</artifactId>
    <version>1.2</version>
    <scope>provided</scope>
</dependency>

위 코드에서 scope가 provided인 이유는 서블릿이나 JSP API의 경우 컨테이너에 제공하기 때문이다.

Maven과 Jetty 연동을 통한 웹 테스트

Maven을 이용해서 웹 어플리케이션을 개발할 때 필자 개인적으로 가장 매력적인 점은 pom.xml 파일에 maven-jetty-plugin 설정만 추가해주면 Jetty를 이용해 웹 어플리케이션을 테스트 할 수 있다는 점이다. 즉, 톰캣과 같은 WAS를 설치하거나 추가적으로 설정할 필요 없이 웹 어플리케이션에 대한 기능 테스트가 가능하다는 점이다.

maven-jetty-plugin의 설정 방법은 매우 간단한데, 아래와 같이 pom.xml 파일에 설정해주면 된다.

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
    ...
    <build>
        <finalName>main</finalName>
        <plugins>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>maven-jetty-plugin</artifactId>
                <version>6.1.11</version>
                <configuration>
                    <contextPath>/main</contextPath>
                    <scanIntervalSeconds>3</scanIntervalSeconds>
                    <!-- 포트를 변경하고 싶은 경우 connectors 설정 이용 -->
                    <connectors>
                        <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                            <port>80</port>
                            <maxIdleTime>60000</maxIdleTime>
                        </connector>
                    </connectors>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

<configuration>에서 주요 설정 값은 다음과 같다.
  • <contextPath> - 웹 어플리케이션 컨텍스트 경로. 예를 들어, 위 설정의 경우 http://localhost:8080/main 이 웹 어플리케이션의 접근 URL이 된다.
  • <scanIntervalSeconds> - 웹 어플리케이션 컨텍스트의 클래스가 변경되는 지의 여부를 검사하는 시간 간격을 초 단위로 입력한다. 이 값을 0보다 크게 주면, 클래스가 변경될 때 마다 리로드 된다.
<scanTargets>나 <scanTargetPatterns>를 설정하면 클래스 뿐만 아니라 특정 디렉터리나 파일이 변경되는 지의 여부를 검사하도록 설정할 수 있다. 스캔 설정을 비롯한 maven-jetty-plugin의 다양한 설정 옵션을 확인하고 싶다면 'Maven Jetty Plugin Configuration Guide' 문서를 참고하기 바란다.

포트 번호를 지정하지 않을 경우 기본 값은 8080 이다. 따라서 8080 이 아닌 다른 포트를 사용하고 싶다면 위 설정에서 처럼 <connector>를 알맞게 설정해 주어야 한다.

jetty:run을 이용한 실행

maven-jetty-plugin이 제공하는 골에는 run, run-exploaded, run-war, start, stop 등 몇 가지가 존재하는데, 가장 많이 사용되는 건 run 골이다. run 골을 실행하는 방법은 다음과 같다.

$ mvn jetty:run
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'jetty'.
[INFO] ------------------------------------------------------------------------
[INFO] Building javacan3
[INFO]    task-segment: [jetty:run]
[INFO] ------------------------------------------------------------------------
[INFO] Preparing jetty:run
[INFO] [resources:resources]
...
[INFO] [jetty:run]
[INFO] Configuring Jetty for project: javacan3
[INFO] Webapp source directory = C:\work\JavaCan3_maven\src\main\webapp
[INFO] web.xml file = C:\work\JavaCan3_maven\src\main\webapp\WEB-INF\web.xml
[INFO] Classes = C:\work\JavaCan3_maven\target\classes
2008-12-19 15:49:19.210::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
[INFO] Context path = /main
[INFO] Tmp directory =  determined at runtime
[INFO] Web defaults = C:\work\JavaCan3_maven\src\jetty\webdefault.xml
[INFO] Web overrides =  none
[INFO] Webapp directory = C:\work\\JavaCan3_maven\src\main\webapp
[INFO] Starting jetty 6.1.11 ...
...
2008-12-19 15:48:38.584::INFO:  Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server

jetty:run을 실행하면 잠시 뒤에 Jetty가 실행되고 이 후로 웹 브라우저를 이용해서 기능 테스트를 진행할 수 있게 된다.


관련 링크:
  • Archetype 플러그인 사용법: http://maven.apache.org/plugins/maven-archetype-plugin/usage.html
  • Maven Jetty Plugin Configuration Guide: http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin
  • Jetty 서블릿/JSP 컨테이너 기초 사용: http://javacan.tistory.com/entry/136


출처 - http://javacan.tistory.com/entry/WebAppDevelopmentUsingMaven






Posted by linuxism
,