SILENTSOFT
Published on

Maven 빌드 할 때 라이브러리를 못찾는 경우 해결 방법

Authors

Maven 빌드 할 때 package does not exist 또는 cannot find symbol과 같은 에러는 라이브러리를 찾지 못해서 나는 에러다.

이런 에러는 아래와 같이 크게 두 가지로 나눌 수 있다.

1. 레파지토리에 배포된 라이브러리를 사용하는 경우

pom.xml에 해당 라이브러리 dependency를 아래와 같이 추가하면 된다.

<dependencies>
    <!-- Web dependencies -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
    ...
</dependencies>

2. 외부 라이브러리(3rd party) jar 파일을 사용하는 경우

필자는 이클립스 Luna를 쓰다가 얼마전에 공개된 Neon으로 업데이트 했는데, 업데이트하고 나서 Maven 컴파일 에러때문에 굉장히 당황했다. (당장 급하게 배포해야하는 상황인데, 컴파일이 안되니.. 쩝...) 어찌되었건, 필자는 스프링 프로젝트에 (레파지토리에 배포되지 않은) 외부 라이브러리를 src/main/webapp/WEB-INF/lib 경로에 두는데, 이 jar 파일들을 빌드할 때 참조될 수 있도록 수동으로 dependency를 주입하면 된다.

<properties>
    ...
    <webapp.lib>${basedir}/src/main/webapp/WEB-INF/lib</webapp.lib>
</properties>

<dependencies>
    ...
    <dependency>
        <groupId>whatever</groupId>        <!-- Doesn't matter -->
        <artifactId>example</artifactId>   <!-- Doesn't matter -->
        <version>whatever</version>        <!-- Doesn't matter -->
        <scope>system</scope>
        <systemPath>${webapp.lib}/example-1.0.0.jar</systemPath>
    </dependency>
</dependencies>

(groupId, artifactId, version은 jar 파일과 무관하게 정의해도 상관없다.)

의존성 주입이 안되는 (레파지토리에 배포되지 않은) 외부 라이브러리는 이렇게 해야만 Maven 컴파일 에러가 안나는데, 필자가 개발하고 있는 프로젝트의 외부 jar가 60여개가 넘어가서 도저히손으로는 dependency 태그를 만들 엄두가 안났다. (사실 이클립스 Luna는 이렇게까지 하지 않아도 에러 안났다. 업그레이드 한 Neon이 문제..)

그래서, 그냥 ... 코드로 만들었다.

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Test;

public class DependencyGenerator {

    @Test
    public void generate() throws Exception {
        AtomicInteger dependencyCount = new AtomicInteger(0);
        Files.walkFileTree(Paths.get(System.getProperty("user.dir"), "src", "main", "webapp", "WEB-INF", "lib"), new FileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                String fileName = file.getFileName().toString();

                System.out.println("        <dependency>");
                System.out.println("            <groupId>whatever</groupId>");
                System.out.println(String.format("%s%s%s", "            <artifactId>", fileName.substring(0, fileName.lastIndexOf(".")), "</artifactId>"));
                System.out.println("            <version>whatever</version>");
                System.out.println("            <scope>system</scope>");
                System.out.println(String.format("%s%s%s", "            <systemPath>${webapp.lib}/", fileName, "</systemPath>"));
                System.out.println("        </dependency>");

                dependencyCount.incrementAndGet();

                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                throw new IOException("visitFileFailed");
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                return FileVisitResult.CONTINUE;
            }
        });
        System.out.println(String.format("%s%s%s", ">> Generated ", dependencyCount.get(), " dependencies."));
    }

}

여기서 중요한건, groupId와 artifactId와 version이 전부다 통일되서 같은 의존성이 여러개 생기면 또 컴파일 안된다.

그래서 groupId와 version은 그냥 "whatever"로 정의하고, artifactId만 파일명을 따라가도록 했다.

위에서 잠깐 언급한대로이클립스 Luna를 사용할 때에는 이런류의 컴파일 에러는 나지 않았다. 난다고 하더라도, Alt + F5 후에 .classpath 파일에 생기는아래 라인을 제거하면 쉽사리 해결됐었다.

<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>

왜인지 모르겠지만, Neon은 .classpath 파일에서 위 라인을 지워도 해결이 안된다. 더 좋은 해결책을 찾던가, 아니면.. Neon부터는 이렇게 할 수 밖에...