자바에서 classpath

Anna·2020년 9월 6일
2

시작하며

JUnit을 이용하여 서비스 객체를 테스트할 일이 생겼다. 모든 빈을 등록하도록 스프링 컨테이너를 로드하기위해 아래와 같이 스프링 관련 설정을 읽어오도록 해야한다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
@WebAppConfiguration
public class ServiceTest {

내가 겪었던 어려움은
   1. classpath가 뭐지?
   2. 됐고 test 클래스에서 resources 하단의 파일을 어떻게 불러오지?
이 두가지였다.

이 두가지에 대한 해답을 얻기 위해 모은 자료들과 내가 이해한 내용을 공유하려 한다.

classpath란?

먼저, 자바에서 특히 java 이외의 파일들의 경로를 지정할때

classpath:...

라는 표현을 많이 보았을 것이다.

classpath란 빌드시 컴파일된 class 파일들의 위치 경로이다.

모든 컴파일된 클래스와 자원들은 classPath 하단에 생성되고 여기서 경로를 찾아야한다.

주의해야할 점은, 절대 이클립스에 보이는 구조가 아니라는 것이다.
예를 들어, '저는 src/main/resources에 있는 txt파일에 접근해서 자바 코드로 수정했는데 이클립스에서 해당문서가 변경이 안되었어요' 라고 물을 수 있다.
답은 '당연하다'. 그건 classPath 내의 txt파일을 변경한 것이므로 이클립스에서는 안보인다. 직접 파일경로를 찾아가서 확인해야 한다.
(참고로 이클립스의 경우 또 별도의 classPath를 가지고 있다.)

classpath를 확인하는 방법은 다양하며, 나는 이 방법으로 확인하였다.

프로젝트파일명\target\classes

대부분 찾아보면 위와 같이 설정되어 있을 것이다.

정리하면, classpath는 패키지 계층구조의 '최상위 경로' 인 것이다. (이외에도 jar 파일을 인식하는 경로로 사용되기도 한다.)

예를 들어
output/org/javaguy/coolframework/MyClass.class 를
import org.javaguy.coolframework.MyClass;
라고 불러 올 수 있는 이유는 classpath가 /home/..../output 이라고 설정되어 있기 때문이다.

resources 자원의 경로

그래서 java 패키지 내에서 클래스가 아닌 자원들 ( 컴파일 대상이 아닌 이미지, xml 등 설정문서 )에 접근하기 위해서는 경로를 어떻게 설정해줘야 할까?

먼저, 우리는 .java 파일이 있는 패키지가 아니라 컴파일된 .class 파일들이 있는 경로에서 해당 파일들을 찾아야 한다. ( 당연히 코드가 컴파일 될때 컴파일된 위치에서 타 자원을 찾아야한다. )
따라서 이클립스에서 패키지 모양을 보지말고 얼른 하단의 src/main/java 폴더로 내려온다.

resources파일을 인식하기 위해서는 ClassPathResource 와 같은 유틸 라이브러리를 많이 사용한다.

ClassPathResource resource = new ClassPathResource("config.xml")

위처럼 자원의 경로를 지정해줘야 하는데, 이때 쓰이는 몇가지 방식이 있다.

  • 절대경로, 상대경로 사용 x : "resources/config.xml"
  • 접두사 사용
    • "file : src/main/resources/config.xml"
    • "classpath : config.xml"

첫번째와 같이 .나 /를 쓰지않는 경우는 기본적으로 src/main/webapp 하위를 탐색한다.

classpath 는 기본경로가 src/main/java, src/main/resources 이다. 따라서 이 두 경로의 하위를 탐색한다.

file은 기본경로가 현재 프로젝트의 폴더이다.

배포시에는 경로가 달라진다. 자세한 내용은 참고 문서를..

classpath에 대한 정보는 프로젝트 폴더내의 .classpath에서 확인할 수 있다. 이를 보면 classpath에 대한 이해에 도움이 된다.

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
   <classpathentry kind="src" output="target/classes" path="src/main/java">
      <attributes>
         <attribute name="optional" value="true"/>
         <attribute name="maven.pomderived" value="true"/>
      </attributes>
   </classpathentry>
   <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
      <attributes>
         <attribute name="maven.pomderived" value="true"/>
      </attributes>
   </classpathentry>
   <classpathentry kind="src" output="target/test-classes" path="src/test/java">
      <attributes>
         <attribute name="optional" value="true"/>
         <attribute name="maven.pomderived" value="true"/>
         <attribute name="test" value="true"/>
      </attributes>
   </classpathentry>

classpath는 기본 entry가 src/main/java, src/main/resources로 잡혀있고, 빌드 결과인 output은 target/classes에 저장된다.

test시 resources 자원의 경로

test를 할때는 classpath가 다르게 잡힌다.
src/test/java 하위에 테스트 클래스가 위치해야 한다.
위의 xml파일의 마지막을 보면 path가 src/test/java인 별도의 classpath가 있다.
즉, test 클래스에서 "classpath:" 라는 표현을 쓰면 이는 기본적으로 src/test/java의 하위에서 파일을 탐색한다.

하지만 우리가 탐색하고 싶은 resource 파일은 src/main의 하위에 있으므로, "file: "을 사용하는 것이 가장 편리하다. 또는classpath라는 표현을 쓰고 싶다면 src/test하위에 resources 폴더를 생성하여 해당 폴더 하위에 기존의 resource 파일을 위치시켜서 참조할 수 있도록 한다.

결국 file 접두어를 써서 junit 테스트를 성공적으로 진행할 수 있었다.

package com.sph.cms.biz.mail.service;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"file:src/main/resources/contexts/applicationContext.xml", "file:src/main/resources/contexts/dbDataSourceContext.xml", "file:src/main/resources/contexts/dbMybatisContext.xml"})
@WebAppConfiguration
public class MyBatisTest4 {
   @Autowired
   public MailDao mailDao ;
   
   @Test
   public void test() {
      
      try {
         List<String> resultList  = mailDao.getCompanyEmailList();
         System.out.println(resultList);
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
   
   
}

src/test/java에서 작성된 클래스는 본인과 패키지 구조가 대응되는 java 패키지 및 클래스를 참조한다. 예를 들어, src/test/java/com.abc.biz에 작성된 클래스는 src/main/java/com.abc.biz에 작성된 config 클래스를

@ContextConfiguration(classes="config.class")

위 처럼 표현하여 불러올 수 있다.

하지만 보통은 자원파일은 별도로 위치하므로 우리가 의도하던 바는 아니니 참고만 하자.

참고 : java — 클래스 패스 란 무엇이며 어떻게 설정합니까?

마무리하며

포스팅하는데에 엄청 오래걸렸다.. 원래 이런건가.. 하지만 정리하다보니 포스팅 직전까지도 불확실했던 부분을 잘 알게되어서 기쁘다. 앞으로 포스팅 속도도 점점 나아지겠지.. ㅜㅜ

profile
글쓰는 개발자가 되고싶어요

3개의 댓글

comment-user-thumbnail
2022년 3월 16일

안녕하세요~ 클래스패스 오류나서 구글링하다가 글 보고 해결했어요 :) 제 블로그 TIL에 참고 블로그로 url 적어갈게요~ 감사합니다 :)

답글 달기
comment-user-thumbnail
2022년 7월 4일

It was difficult for me to discover forums with numerous essays until I came across yours, which appears to be ideal for me. If you have the time, would you kindly join me in the fnf mods

답글 달기
comment-user-thumbnail
2022년 11월 25일

your article is perfect for me, so now it is being used. I play papa's games at the same time

답글 달기