티스토리 뷰

반응형

애플리케이션에서 설정에 관련된 상수는 소스코드와 별도로 관리하는 것이 기본이다. 설정과 관련된 값을 소스코드 내부에서 관리하면.. 값이 바뀔 때마다 매번 컴파일을 해야해서 번거롭기 때문이다. 


 Spring Boot는 프로젝트 설정에 쓰이는 내용 또는 각종 상수를 .yml 이나 .properties 파일에 저장할 수 있게 지원한다. 이번 포스팅에서는 .properties의 상수를 Enum에 초기화 하는 법을 간단히 알아보겠다.



 Spring Boot에서 .properties 파일의 값을 가져올 때는 @Value 라는 어노테이션을 쓴다. 이것을 쓰면 Bean 객체가 초기화 될때 @Value에 지정된 값을 해당 프로퍼티에 초기화 한다. setter를 활용해서 초기화를 하는 것도 가능하다. 아래의 간단한 예제를 보자.


import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Example {
    
    @Value("${example.exampleString}")
    private String initDirect;
    
    private String initWithSetter;
    
    
    @Value("${example.exampleString}")
    public void setInitWithSetter(String initWithSetter) {
        this.initWithSetter = initWithSetter;
    }
}


 위와 같이 필드명 위 또는 setter 위에 @Value 어노테이션을 활용하여 properties 파일의 값을 Spring Bean 객체에 초기화 할 수 있다. Spring에서 Example이라는 Bean 객체를 생성할 때 application.properties 파일의 example.exampleString값을 읽은 후 초기화 해준다.


 그럼 Enum에서도 간단하게 할 수 있지 않을까? 그러나 무작정 접근하면 컴파일 에러와 맞닥뜨리게 된다. Enum은 내부적으로 static과 같기 때문이다. 그러니까,


public enum Fruit {

    APPLE("사과", "apple", true),
    ORANGE("오렌지", "orange", true),
    TOMATO("토마토", "tomato", false);

    private String koreanName;
    private String englishName;
    private boolean isLiked;

    Fruit(String koreanName, String englishName, boolean isLiked) {
        this.koreanName = koreanName;
        this.englishName = englishName;
        this.isLiked = isLiked;
    }
}


 위와 같은 Enum 선언은 아래의 Class 선언과 같다.



public class Fruit {
    
    public final static Fruit APPLE = new Fruit("사과", "apple", true);
    public final static Fruit ORANGE = new Fruit("오렌지", "orange", true);
    public final static Fruit TOMATO = new Fruit("토마토", "tomato", false);

    private final String koreanName;
    private final String englishName;
    private final boolean isLiked;
    
    private Fruit(String koreanName, String englishName, boolean isLiked) {
        this.koreanName = koreanName;
        this.englishName = englishName;
        this.isLiked = isLiked;
    }
}


 예제의 enum에서 englishName 필드에 .properties 파일의 값을 주입한다고 가정해보자. 가능할까? @Value를넣을 수 있을까? 넣어보면 컴파일러가 안된다고 막아설 것이다.


 그렇다면 정녕 enum에 @Value를 활용한 값 주입은 불가능 한 걸까? 다각도로 고민하고 구글링 해본 결과 편법을 알아낼 수 있었다. 바로 정답부터 공개한다.


@Component
public class EnglishName {

    public static String apple;
    public static String orange;
    public static String tomato;

    @Value("${fruit.englishName.apple}")
    public void setApple(String apple) {
        this.apple = apple;
    }

    @Value("${fruit.englishName.orange}")
    public void setOrange(String orange) {
        this.orange = orange;
    }

    @Value("${fruit.englishName.tomato}")
    public void setTomato(String tomato) {
        this.tomato = tomato;
    }
}

 위 예제는 Fruit의 englishName을 따로 선언해놓은 모습이다. 예제를 잘 째려보면 Spring의 관리를 받는 클래스(@Component)에 static 필드가 선언되어 있고, non-static-setter가 세팅되어 있음을 확인할 수 있다. 이렇게 하면 Spring application이 실행될 때 이 클래스의 static 필드에 .properties의 값이 초기화 될 것이다.



 다만 문제는 enum이 내부적으로 static final과 같다는 것인데.. 익히 알려져있듯 static은 별도의 메모리(Heap)에 저장되고 메모리에 적재되는 시점이 조금 다른 것으로 알려져있다. 그런데 여기에서 힌트가 있다.


static 변수는 그 변수를 활용하는 클래스가 로딩될 때 메모리에 적재된다.


 조금 말이 어렵지만 아직 공개하지 못한 소스코드를 조금 더 보면 이해가 바로 될 것이다. 눈에 힘을 주고 위의 EnglishName.class를 Enum에서 어떻게 활용하는지 확인해보자.


public enum Fruit {

    // Bean Object의 static 필드로 englishName을 초기화 했다.
    APPLE("사과", EnglishName.apple, true),
    ORANGE("오렌지", EnglishName.orange, true),
    TOMATO("토마토", EnglishName.tomato, false);

    private String koreanName;
    private String englishName;
    private boolean isLiked;

    Fruit(String koreanName, String englishName, boolean isLiked) {
        this.koreanName = koreanName;
        this.englishName = englishName;
        this.isLiked = isLiked;
    }

    public String getEnglishName() {
        return englishName;
    }
}

예제를 확인하니 어떤가? "이게 될까?" 싶어서 고개를 갸우뚱 할 수 있겠다. Enum값 APPLE을 초기화 할때 EnglishName.apple라는 static 변수로 초기화를 했기 때문이다. 왠지 Spring Bean 초기화 보다 Enum의 초기화가 먼저 동작해서 모든 EnglishName이 null이 될 것 같은 느낌이다.


 그래서 간단한 컨트롤러 예제도 함께 준비했다.


@Controller
public class PreamtreeController {

    @RequestMapping("/")
    public ResponseEntity<String> enumTest() throws Exception{

        // 예상 response: "tomato" 또는 null… 과연??
        return new ResponseEntity<>(Fruit.TOMATO.getEnglishName(), HttpStatus.OK);
    }
}



위 예제의 실행결과는 어떨까? "tomato"일까? null일까? 정답은 "tomato"이다. "tomato"가 된 이유는 바로 Enum 변수의 초기화 시점에 있다.


 EnglishName.class는 @Component가 붙어있는 Bean 객체이다. 이는 Spring Application이 실행될 때 초기화 되고, .properties 파일의 내용이 주입된다. 


 한편 Fruit라는 Enum은 PreamtreeConroller.class 에서 쓰인다. 위에서 잠깐 소개했듯, PreamtreeController가 load될 때 Fruit가 초기화 된다! 즉, Configuration에 무언가 조작을 하지 않는 한 Bean 객체들이 모두 초기화 된 이후에나 초기화된다.


 지금까지 static 변수가 초기화 되는 시점을 교묘하게 이용하여 Enum에 .properties 파일의 내용을 초기화 하는 법을 알아봤다. 사실 yml파일로 관리하면 yml파일의 내용을 손쉽게 POJO나 Collection Framework(Map)에 초기화 할 수 있으므로... 이 방법은 거의 쓰이지 않을 것 같다.


  혹시 Enum을 꼭 사용해야하는 상황에서 .properties 파일의 내용을 활용하고 싶다면 위와 같이 '중간 Bean 객체'를 이용해보자!




-끝-




«   2021/12   »
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
글 보관함
Total
759,110
Today
0
Yesterday
253