저작권 안내: 저작권자표시 Yes 상업적이용 No 컨텐츠변경 No

스프링5 입문

JSP 2.3

JPA 입문

DDD Start

인프런 객체 지향 입문 강의

모든 개발팀에 공유할 연동 모듈이 HttpClient를 사용한다고 하자. 이 모듈은 HttpClient 3 버전을 사용해서 개발하고 있다. 열심히 개발해서 이 모듈을 배포했다. 그런데 문제가 생겼다. 어떤 팀은 이미 HttpClient 4 버전을 사용했고 어떤 팀은 HttpClient 2 버전을 사용하는 것이었다. 그래서 버전 충돌로 인해 제대로 동작하지 않는 상황이 벌어졌다.


이런 일이 벌어질 때 사용하기 좋은 메이븐 플러그인이 있는데, 그 플러그인은 바로 maven-shade-plugin이다.


maven-shade-plugin


maven-shade-plugin은 다음의 두 기능을 제공한다.

  • 프로젝트의 타입뿐만 아니라 의존하는 jar에 포함된 모든 타입을 포함하는 한 개의 jar 파일을 만들어준다.
  • 패키지 구조를 변경해준다.
설명만 봐서 언뜻 이해가 안 될테니 설정 예를 보자.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.2</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <outputFile>${project.build.directory}/${project.artifactId}-with-common-${project.version}.jar
        </outputFile>
        <minimizeJar>false</minimizeJar>
        <keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope>
        <artifactSet>
            <includes>
                <include>commons-httpclient:commons-httpclient</include>
                <include>commons-logging:commons-logging</include>
                <include>commons-lang:commons-lang</include>
            </includes>
        </artifactSet>
        <relocations>
            <relocation>
                <pattern>org.apache.commons.logging</pattern>
                <shadedPattern>madvirus.relocated.commons.logging</shadedPattern>
            </relocation>
            <relocation>
                <pattern>org.apache.commons.httpclient</pattern>
                <shadedPattern>madvirus.relocated.commons.httpclient</shadedPattern>
            </relocation>
        </relocations>
    </configuration>
</plugin>

이 예에서 artifactSet/includes는 생성할 jar 파일에 포함시킬 의존 대상을 지정한다. 위 코드는 commons-httpclient, commons-logging, commons-lang을 지정했는데, 이 경우 이 세 개의 아티팩트를 위한 jar 파일의 내용과 해당 프로젝트의 클래스/리소스를 모아 하나의 jar를 만든다. 생성할 jar 파일은 outputFile 태그로 지정한다.


relocations은 jar 파일에 담긴 타입의 패키지 구조를 변경한다. 위 예에서는 org.apache.commons.logging 패키지에 속한 타입을 madvirus.relocated.commons.logging 패키지로 바꿔준다. 또한, 변경한 패키지를 사용하는 타입은 바뀐 패키지를 사용하도록 변경한다.


maven-shade-plugin을 이용해서 생성한 jar 파일은 필요한 모든 외부 의존을 포함하고 있기 때문에 jar 파일 한 개만 배포하면 된다. 또한 패키지 구조도 변경했기 때문에, 이 jar 파일을 어디에 배포해도 클래스 충돌 문제가 발생하지 않는다. 위 코드의 경우 HttpClient의 패키지 구조를 변경했으므로, 다른 프로젝트에서 다른 버전의 HttpClient를 사용하더라도 같은 클래스를 사용해서 발생해서 버전 충돌 문제를 피할 수 있다.


이 외에도 몇 가지 기능을 더 제공하는데 이에 대한 내용은 https://maven.apache.org/plugins/maven-shade-plugin/ 페이지를 참고한다.

Posted by 최범균 madvirus

댓글을 달아 주세요

메이븐을 사용하다보면 오라클 JDBC 드라이버처럼 라이선스 문제로 메이븐 중앙 리포지토리나 다른 공개 리포지토리에 존재하지 않는 jar 파일이 존재한다. 이런 경우에는 다음과 같이 maven-install 플러그인을 사용해서 jar 파일을 로컬 메이븐 리포지토리에 등록해주면 된다. (공간상 두 줄로 표시했으며, 실제로는 한 줄로 입력한 것이다.)


mvn install:install-file -Dfile=ojdbc14.jar -DgroupId=com.oracle

  -DartifactId=ojdbc14 -Dversion=10.2.0.2.0 -Dpackaging=jar


여력이 된다면, 로컬 리포지토리에 일일이 등록하는 것 보다는, 내부에 Nexus를 이용해서 메이븐 리포지토리를 구축하는 것이 더 좋다. Nexus에 대한 내용은 "Maven 내부용 리포지토리 만들고 Maven 설정하기" 글을 참고하기 바란다.

Posted by 최범균 madvirus

댓글을 달아 주세요

자바로 테스트 코드 만드는 게 좀 짜증이 나서 편하게 사용할만한 테스트 프레임워크를 뒤지다가 페이스북에서 Spock 이라는 놈을 줏어 들었다. 자바 기반의 프로젝트를 진행중인데, Spock가 그루비 기반이라서 궁합도 잘 맞을 것 같기에 일부 테스트 코드에서 넣어보기로 결심을 했다.


본격 사용하기에 앞서 개발 환경 구축은 필수! 아래의 순서로 진행을 했다.

  1. 이클립스에서 그루비 플러그인 설치
  2. pom.xml에 Spock 실행 위한 설정 추가
  3. src/test/groovy 폴더 생성
  4. Spock 테스트 코드 실행
그루비 플러그인 설치


http://groovy.codehaus.org/Eclipse+Plugin 사이트에 가면 이클립스 버전 별로 업데이트 사이트 주소를 확인할 수 있다. 필자는 이클립스 4.3 (Kepler) 버전을 사용중이서 http://dist.springsource.org/release/GRECLIPSE/e4.3/ 주소를 이용해서 플러그인을 설치했다. 메이븐 플러그인(m2e)와 연동을 해야 하므로, 메이븐 플러그인 커넥터도 함께 설치했다. (사실은 다 설치했다.)


그루비 플러그인을 설치한 다음에는 Windows > Preferences > Groovy > Compiler 에서 컴파일러 버전을 2.0 으로 변경한다. (변경하면 이클립스를 재시작한다.) 2.1이 아닌 2.0을 사용하는 이유는 Spock 0.7 버전이 현재 그루비 2.0 까지만 지원하기 때문이다.


pom.xml 파일 설정


메이븐 pom.xml 설정이 다소 복잡한데, 사용한 설정은 아래와 같다. Spock 0.7 버전은 현재 그루비 1.8 버전 또는 2.0 버전을 지원한다.


<project ...>

    <dependencies>

        ...

        <dependency>

            <groupId>org.spockframework</groupId>

            <artifactId>spock-core</artifactId>

            <version>0.7-groovy-2.0</version>

            <!-- 0.7-groovy-1.8 -->

            <scope>test</scope>

        </dependency>

        <dependency>

            <groupId>org.codehaus.groovy</groupId>

            <artifactId>groovy-all</artifactId>

            <version>2.0.5</version>

            <scope>test</scope>

        </dependency>

    </dependencies>


    <build>

        <plugins>

            <plugin>

                <groupId>org.apache.maven.plugins</groupId>

                <artifactId>maven-compiler-plugin</artifactId>

                <version>3.1</version>

                <configuration>

                    <source>${java.version}</source>

                    <target>${java.version}</target>

                    <encoding>${project.build.sourceEncoding}</encoding>

                    <compilerId>groovy-eclipse-compiler</compilerId>

                </configuration>

                <dependencies>

                    <dependency>

                        <groupId>org.codehaus.groovy</groupId>

                        <artifactId>groovy-eclipse-compiler</artifactId>

                        <version>2.8.0-01</version>

                    </dependency>

                    <dependency>

                        <groupId>org.codehaus.groovy</groupId>

                        <artifactId>groovy-eclipse-batch</artifactId>

                        <version>2.0.7-03</version>

                        <!-- <version>1.8.6-01</version> -->

                    </dependency>

                </dependencies>

            </plugin>

        </plugins>

    </build>


</project>


테스트 목적으로만 쓸 거라서 groovy-all 의존의 범위를 test로 지정하였다.


주의사항:

  • spock 0.7 메이븐 의존은 Junit-dep 4.10 버전에 대한 의존을 갖는다. 따라서, Junit 4.11과 같이 다른 버전의 JUnit을 사용하고 있다면, JUnit 버전을 맞추기 위해 spock 의존 설정에 <exclustion>을 추가해서 junit-dep 의존을 제거한다.
  • groovy-all 용량이 커서, 최초 다운로드에 올래 걸리기도 한다. 메이븐 프로젝트 구성할 때에 인내심이 필요할 수 있다.

src/test/groovy 폴더 생성


그루비로 만든 테스트 코드를 넣기 위해 src/test 폴더 아래 groovy 폴더를 생성한다.


Spock 테스트 코드 실행


이제 남은 건 Spock 테스트 코드를 실행하는 것이다. src/test/groovy 폴더에 Spock 홈페이지에 있는 테스트 코드를 넣어본다. (HelloSpock.groovy)


import spock.lang.*


class HelloSpock extends spock.lang.Specification {

def "length of Spock's and his friends' names"() {

expect:

name.size() == length


where:

name     | length

"Spock"  | 5

"Kirk"   | 4

"Scotty" | 6

}

}


JUnit을 실행하는 것과 동일하게 "JUnit Run"으로 실행한다. 그러면, 아래와 같이 테스트가 통과하는 것을 확인할 수 있다.




Posted by 최범균 madvirus

댓글을 달아 주세요

  1. 미니양 2014.02.11 04:43 신고  댓글주소  수정/삭제  댓글쓰기

    Spock을 한번 따라해 보고 싶어서 작성해 주신 글대로 따라 해 봤습니다.
    Spock의 기본 샘플로 주신건 실행이 되는데,
    def MockSampleObj = Mock(SampleObj)
    이렇게 하면 MockSampleObj.someMethod를 하면 someMethod를 가져오지 못하고 밑줄이 그어지네요.

    혹시 원인을 알 수 있나요?

배치 프로그램이나 명령행에서 실행할 간단한 자바 프로그램을 만들 때가 있는데, 이 프로그램이 스프링 프레임워크를 이용한 경우 관련된 jar 파일들을 함께 배포하고 실행 스크립트에 클래스패스를 설정해주고 하는 것이 귀찮을 때가 있다. 내가 만든 클래스 파일/자원 파일뿐만 아니라 스프링 jar 파일에 포함된 클래스/메타 파일들을 모두 하나의 jar 파일로 만들어서 배포하면 프로그램을 실행하는 스크립트가 매우 간단해질텐데, 메이븐의 어셈블리 플러그인이 제공하는 jar-with-dependencies 기능을 사용하면 이 작업을 매우 쉽게 할 수 있다.


maven-assembly-plugin은 jar-with-dependencies 설정을 제공하는데, 이 설정을 사용하면 내가 만든 클래스 및 자원 파일과 의존하는 jar 파일에 포함된 모든 파일을 묶어서 하나의 jar 파일로 만들어준다. 다음은 설정 예이다.



<build>

        <plugin>

            <groupId>org.apache.maven.plugins</groupId>

            <artifactId>maven-assembly-plugin</artifactId>

            <executions>

                <execution>

                    <id>create-jar</id>

                    <phase>package</phase>

                    <goals>

                        <goal>single</goal>

                    </goals>

                    <configuration>

                        <descriptorRefs>

                            <descriptorRef>

                                jar-with-dependencies

                            </descriptorRef>

                        </descriptorRefs>

                    </configuration>

                </execution>

            </executions>

        </plugin>


        <plugin>

            <groupId>org.apache.maven.plugins</groupId>

            <artifactId>maven-shade-plugin</artifactId>

            <executions>

                <execution>

                    <phase>package</phase>

                    <goals>

                        <goal>shade</goal>

                    </goals>

                    <configuration>

                        <transformers>

                            <transformer

                                implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">

                                <mainClass>com.prototype.springintegration.AppBootstrapper</mainClass>

                            </transformer>

                            <transformer

                                implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

                                <resource>META-INF/spring.handlers</resource>

                            </transformer>

                            <transformer

                                implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

                                <resource>META-INF/spring.schemas</resource>

                            </transformer>

                            <transformer

                                implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

                                <resource>META-INF/spring.factories</resource>

                            </transformer>

                            <transformer

                                implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

                                <resource>META-INF/spring.factories</resource>

                            </transformer>

                            <transformer

                                implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">

                                <resource>META-INF/spring.tooling</resource>

                            </transformer>

                        </transformers>

                    </configuration>

                </execution>

            </executions>

        </plugin>

    </plugins>

</build>


위 코드에서 maven-shade-plugin 을 사용했는데, 이 플러그인을 사용한 이유는 스프링의 각 jar 파일에 포함되어 있는 메타 파일들을 하나의 파일로 합치기 위함이다. 이 플러그인을 사용하지 않으면 스프링의 각 jar 파일에 포함된 메타 파일을 한 개 jar 파일에 모으는 과정에서 합쳐지지 않고 뎝어쓰기때문에 XML 확장 스키마 등의 설정이 올바르게 동작하지 않게 된다.


위와 같이 설정을 했다면 mvn package 명령어로 의존하는 jar 파일에 속한 모든 내용과 내가 만든 클래스/자원 파일들이 포함된 한 개의 jar 파일을 얻을 수 있다.

Posted by 최범균 madvirus

댓글을 달아 주세요

아래 링크에 걸려 있는 정보를 이용해서 Github을 리포지토리로 사용을 해 보았다.


http://stackoverflow.com/questions/14013644/hosting-a-maven-repository-on-github/14013645#14013645


위 글을 보고 열심히 따라서 Maven pom.xml 파일을 작성한 뒤에 흥분되는 마음으로 mvn deploy 명령어를 실행하자, 아래와 같은 오류 메시지가 출력되었다.


[ERROR] Failed to execute goal com.github.github:site-maven-plugin:0.7:site (default) on project jasmine-maven-plugin: Error creating blob: Missing or invalid User Agent string. See http://developer.github.com/v3/#user-agent-required (403) -> [Help 1]


이런! 구글링을 해 보니, 깃헙의 site-maven-plugin 설정에 아래와 같은 내용을 추가해야 함을 알 수 있었다.


            <plugin>

                <groupId>com.github.github</groupId>

                <artifactId>site-maven-plugin</artifactId>

                <version>0.7</version>

                <configuration>

                    <message>Maven artifacts for ${project.version}</message>

                    <noJekyll>true</noJekyll>

                    <outputDirectory>${project.build.directory}/mvn-repo</outputDirectory>

                    <branch>refs/heads/mvn-repo</branch>

                    <merge>true</merge> <!-- 이것도 추가 -->

                    <includes>

                        <include>**/*</include>

                    </includes>

                    <repositoryName>깃헙리포지토리이름</repositoryName>

                    <repositoryOwner>깃헙ID</repositoryOwner>

                </configuration>

                <executions>

                    <execution>

                        <goals>

                            <goal>site</goal>

                        </goals>

                        <phase>deploy</phase>

                    </execution>

                </executions>

                <!-- site-maven-plugin 0.7 버전에서 에러 안 나도록, 아래 내용 추가 -->

                <dependencies>

                    <dependency>

                        <groupId>org.eclipse.mylyn.github</groupId>

                        <artifactId>org.eclipse.egit.github.core</artifactId>

                        <version>2.0.3</version>

                    </dependency>

                </dependencies>

            </plugin>


그리고, <merge> 속성의 값을 true로 지정해 주었다. <merge>가 false일 경우 기존 내용이 삭제되기 때문에, 이전 버전이 유지가 되지 않는다. 이전 버전을 유지하고 싶지 않더라도 Maven 멀티 모듈을 사용하고 있다면 이 속성을 true로 지정해 주어야 한다. 그렇지 않을 경우, 멀티 모듈의 각 모듈이 리포지토리에 배포될 때 마다 이전에 올라간 것들을 지우기 때문에, 최종적으로 한 개의 서브 모듈만 리포지토리에 올라가게 된다.

Posted by 최범균 madvirus

댓글을 달아 주세요

필자는 Maven을 너무 좋아하기에 안드로이드 개발도 Maven 프로젝트로 관리하고 싶었다. 그래서 구글링을 좀 했고, 영어로 된 걸 매번 보고 싶지 않아 나중을 위해 한글로 정리해둔다.


M2E를 위한 Android Connector 설치


먼저 할 일은 이클립스에 설치한 Maven 플러그인과 ADT를 연결하는 위해 Android Connector를 설치하는 것이다. 설치를 하려면 Preferences > Android/Discovery > Open Catalog 메뉴를 실행한 뒤, 아래 그림이 나올 때 android로 검색하면 된다.



검색 결과로 나온 Android Connector를 선택한 뒤 설치하자.


Android용 Archetype 추가 (옵션)


Android Connector를 설치 후, pom.xml 파일에 maven-android-plugin을 설정하면 해당 프로젝트를 ADT와 연동해 준다. 하지만, pom.xml 파일을 처음부터 만들면 (다소) 귀찮을 수 있는데, 그걸 대신 해주는 archetype을 추가해주면 좀 더 편하게 pom.xml 파일을 생성할 수 있다.


Archetype 타입을 추가해주는 방법은 간단하다. New > Maven Project > Next > Select an Archetype 화면에서 [Add Archetype...] 버튼을 클릭한다. 그런 다음 아래와 같이 정보를 입력하고 [OK] 버튼을 클릭하면 해당 Archetype이 추가된다.


* Group Id: de.akquinet.android.archetypes

* Artifact Id: android-quickstart

* Version: 1.0.8


안드로이드 프로젝트 생성하기


안드로이드 프로젝트를 생성하는 방법은 간단하다. 앞서 생성한 android-quickstart Archetype을 이용해서 안드로이드 프로젝트를 생성하면 된다. android-quickstart Archetype을 선택하면 아래 그림과 같은 화면이 나온다. platform의 값에 사용할 안드로이드 플랫폼 버전을 입력해주면 된다.



프로젝트를 생성하면 잠시 후 아래 그림과 같이 Maven 프로젝트가 ADT와 연동된 것을 확인할 수 있다. 아래 그림을 보면 자원 관리를 위한 res 폴더, 자동 생성되는 파일을 위한 gen 폴더 등이 생성된 것을 확인할 수 있다.



생성된 pom.xml 파일을 maven-android-plugin 설정 및 플랫폼 버전 정보 등이 포함된 것을 확인할 수 있다.


<?xml version="1.0" encoding="UTF-8"?>

<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>com.madvirus</groupId>

    <artifactId>NetworkExplorer</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>apk</packaging>

    <name>NetworkExplorer</name>


    <properties>

        <platform.version>2.3.2</platform.version>

    </properties>


    <dependencies>

        <dependency>

            <groupId>com.google.android</groupId>

            <artifactId>android</artifactId>

            <version>${platform.version}</version>

            <scope>provided</scope>

        </dependency>

    </dependencies>


    <build>

        <plugins>

            <plugin>

                <groupId>com.jayway.maven.plugins.android.generation2</groupId>

                <artifactId>android-maven-plugin</artifactId>

                <version>3.1.1</version>

                <configuration>

                    <androidManifestFile>${project.basedir}/AndroidManifest.xml</androidManifestFile>

                    <assetsDirectory>${project.basedir}/assets</assetsDirectory>

                    <resourceDirectory>${project.basedir}/res</resourceDirectory>

                    <nativeLibrariesDirectory>${project.basedir}/src/main/native</nativeLibrariesDirectory>

                    <sdk>

                        <platform>10</platform>

                    </sdk>

                    <undeployBeforeDeploy>true</undeployBeforeDeploy>

                </configuration>

                <extensions>true</extensions>

            </plugin>


            <plugin>

                <artifactId>maven-compiler-plugin</artifactId>

                <version>2.3.2</version>

                <configuration>

                    <source>1.6</source>

                    <target>1.6</target>

                </configuration>

            </plugin>

        </plugins>

    </build>

</project>




참고로, Android Connector와 android-maven-plugin인에 대한 보다 자세한 내용이 궁금하면 아래 사이트를 방문해서 확인하면 된다.


Posted by 최범균 madvirus

댓글을 달아 주세요

  1. 예감 2013.07.29 12:09 신고  댓글주소  수정/삭제  댓글쓰기

    Maven관련 자료 잘보고 갑니다^^