본문 바로가기

개발 지식/SpringBoot

[Spring Boot] classpath를 통해 설정 파일 불러오기

반응형

[Spring Boot] classpath를 통해 설정 파일 불러오기

설정 파일 불러오게 된 배경

회사에서 스프링 부트로 마이크로 서비스 아키텍처 기반의 로그 서버를 개발하고 있다. 신입으로 들어와 처음으로 맡은 프로젝트인데, SI성 프로젝트이다 보니, 고객사에서 필요에 따라 바꿀 수 있을 만한 설정값들은 기본 application.properteis 파일 외에 또 다른 properties 파일이나 yaml 파일에 작성할 수 있게끔 설계를 하여 개발을 진행하게 되었다. 가령, 주기적으로 돌아야 하는 스케줄러 작업들에 대한 시간 설정 값, 어떤 제품을 사용하느냐에 따라 바라봐야 하는 테이블명들 등이 주로 따로 빠지는 configuration 파일들이었다.

따라서 이러한 configuration 파일들을 불러와 VO 객체에 mapping 하여 해당 객체를 이용하여 설정값들을 설정 할 수 있도록 개발을 하고 있다가, 이제 테스트 서버에 올릴 때가 되어서 war 파일로 패키징을해서 서버 환경에서 구동을 해보았는데 에러가 터졌다. 개발을 하는 동안은 절대 경로로 해당 파일을 불러와 VO 객체에 mapping을 했기 때문에 테스트 서버의 환경에서는 configuration 파일의 경로를 찾지 못하는 문제가 있었기 때문이었다. 따라서 classpath를 통해 config file 을 가져올 수 있도록 이리저리 뒤져가며 코딩을 하게 되었는데, 이 과정에서 굉장히 많은 삽질이 있어서 두 번 다신 이러한 부분에서 시간을 갉아먹지 않도록 정리를 해본다.


classpath 란?

JVM이 프로그램을 실행할 때, 클래스파일을 찾는데 기준이 되는 경로를 말한다. 그리고 이러한 classpath는 콜론(:) 으로 구분된 디렉토리 및 파일 목록이다.

classpath를 통해 설정 파일 불러오기

내 경우에는 스케줄링 시간등을 yaml 파일에 작성할 수 있도록 설계를 했고 이 설정 파일을 불러와 해당 기능의 스케줄러 주기를 설정할 수 있도록 했다. 다음은 해당 classpath를 기준으로 yaml 형식의 설정 파일을 불러와 jackson을 이용해 해당 VO 객체에 맵핑해주는 방식의 예제 코드다.

준비사항

  • jackson-dataformat-yaml dependency 추가
    • 일반적으로 사용하는 jackson-databind에는 .yaml 파일을 맵핑하는 메소드가 없다. 따라서 yaml 파일의 맵핑을 가능케해주는 jackson-dataformat-yaml dependency 추가해준다.
<dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
  • java.io.InputStream 을 이용하여 해당 설정 파일을 가져온 다음 임시 파일 형태로 만들어 VO 객체에 맵핑을 시켜야한다. 이 떄 FileUtils 라는 객체를 사용하는데 해당 객체는 apache의 commons-io 에 들어있다. 따라서 해당 디펜던시를 추가해줘야 한다.
<dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
   <version>2.6</version>
</dependency>

예제 코드

  1. config/user.yml : 실제 맵핑해야 할 config 파일이다.

    user:
      - id: hochoon1
        name: 호춘1
        department: development
      - id: hochoon2
        name: 호춘2
        department: marketing
      - id: hochoon3
        name: 호춘3
        department: sales
  2. User.java : config 파일 내 user 라는 속성값 안에 있는 내용들을 담고 있는 VO 객체다. 회사에서는 lombok 을 사용하지만 개인적으로는 lombok 보다는 구체적으로 get/set 메소드들을 다 명시해주는 편이 더 직관적이라는 느낌이 들어 아래와 같이 다 작성해준다.

    public class User {
        private String id;
        private String name;
        private String department;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDepartment() {
            return department;
        }
    
        public void setDepartment(String department) {
            this.department = department;
        }
    }
  3. UserConfig.java : config 파일 내 user 라는 속성이 배열로 여러가지를 가지고 있음과 동시에 추가적인 속성값을 필요로 할 경우 리스트로 한번에 다 가지고 있을 수 있도록 맵핑시켜주는 객체다.

    import com.sssukho.test.vo.User;
    
    import java.util.List;
    
    public class UserConfig {
        List<User> user;
    
        public List<User> getUser() {
            return user;
        }
    
        public void setUser(List<User> user) {
            this.user = user;
        }
    }
    
  4. TestService.java : @PostConstruct 로 어플리케이션이 부팅시에 config 파일을 불러와 어플리케이션의 목적에 맞는 서비스 로직을 구현하는 클래스다.

    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
    import org.apache.commons.io.FileUtils;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.PostConstruct;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    
    @Service
    public class TestService {
        private static String CONFIG_FILE_PATH = "/config/user.yml";
    
        ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
    
        UserConfig userConfig;
    
        @PostConstruct
        public void init() {
            mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
            // classpath 기준으로 fsp.yml configuration 파일 불러옴
            ClassPathResource classPathResource = new ClassPathResource(CONFIG_FILE_PATH);
    
            try {
                InputStream inputStream = classPathResource.getInputStream();
                File userConfigFile = File.createTempFile("user", ".yml");
                FileUtils.copyInputStreamToFile(inputStream, userConfigFile);
                userConfig = mapper.readValue(userConfigFile, UserConfig.class);
            } catch(IOException e) {
                e.printStackTrace();
            }
    
            for(User user : userConfig.getUser()) {
                System.out.println("User Name : " + user.getName());
                System.out.println("User Id : " + user.getId());
                System.out.println("User Department : " + user.getDepartment());
            }
        }
    }

결과


요즘에는 .properties 보다는 .yaml 파일로 속성 값들을 정의하는 편인 것 같지만, 이전에 구글링을 해보았을 당시 yaml 파일을 맵핑시켜주는 방식은 많이 없는 것 같아 yaml 파일로 작성을 한 번 해보았다.


Reference

반응형