Application 기동할 때 ApiUrl 동적 경로 등록하기 (1) - 용어 및 기초 테스트

jinvicky·2024년 4월 25일
0

Spring & Java

목록 보기
18/23
post-thumbnail

Intro


보통 스프링에서 DB 정보나 Storage key 등과 같은 중요 정보는 .yml 파일 등을 통해 환경 변수로 등록을 한다.

kakao:
  authUrl: https://kauth.kakao.com/oauth/token
  api: https://kapi.kakao.com
  signOutUrl: https://kapi.kakao.com/v1/user/logout

파일에 등록한 정보들은 profile별로 다른 값을 넣어 처리를 할 수는 있다. 그러나 위에서처럼 설정한 값은 1번 설정되면, 외부에서 변경할 수 없다.

이번 프로젝트는 인프라가 굉장히 복잡했다.
profile별로 domain 경로가 바뀌는 것뿐만 아니라 조건이 또 추가되었다.

  • 내부 통신과 외부 통신시 도메인 url이 달라야 한다. (규칙은 생략)
  • 특정 경우에는 서버 번호를 도메인 url과 조합해야 한다.

사실 이렇게까지 도메인이 동적으로 바뀌는 경우는 드물다.
하지만 애플리케이션 간에도 FeignClient로 통신을 하는데 아래처럼 url을 호출한다.

@FeignClient(name = "aci", url = "${도메인.api.url}")
public interface 도메인Api {

이런 경우 변수로 할당할 수도 없잖아;;;

이와 관련해서 시니어께서 Util을 만드셨는데 그걸 보고 문법을 이용해서 내가 처음부터 분석하고 테스트해본 걸 적어본다.

목표


애플리케이션 기동 시 동적으로 url을 조합해서 특정 환경변수명으로 시스템 내에 등록을 할 것이다.

용어


BeanFactoryPostProcessor

공식 문서를 보자

애플리케이션 컨텍스트의 빈 정의에 대한 사용자 정의 수정을 허용하고 컨텍스트의 기본 빈 팩토리의 빈 속성 값을 적용하는 팩토리 후크입니다.
애플리케이션 컨텍스트에서 구성된 Bean 속성을 재정의하는 시스템 관리자를 대상으로 하는 사용자 정의 구성 파일에 유용합니다.

출처 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanFactoryPostProcessor.html

다른 Bean이 생성되기 전에 얘가 먼저 1번만 적용된다.
마치 @PostConstruct 어노테이션 같다.

EnvironmentAware

Interface to be implemented by any bean that wishes to be notified of the Environment that it runs in.
(빈이 돌아가는 환경으로부터 notify받고 싶은 빈에 의해서 구현되는 인터페이스)

출처 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/EnvironmentAware.html

위 문법으로 아래 간단 코드를 만들었다.

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class ApiPropertyManager implements BeanFactoryPostProcessor, EnvironmentAware {

    private Environment environment;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        ConfigurableEnvironment env = (ConfigurableEnvironment) this.environment;
        MapPropertySource mapPropertySource = getMapPropertySource(env);
        env.getPropertySources().addLast(mapPropertySource);
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    private MapPropertySource getMapPropertySource(ConfigurableEnvironment env) {
        Map<String, Object> properties = addApiUrl();
        return new MapPropertySource("apiUrls", properties);
    }

    private Map<String, Object> addApiUrl() {
        Map<String, Object> map = new HashMap<>();
        map.put("cms.api.url", "https://cms.co.kr");
        map.put("cmi.api.url", "https://cms.co.kr");
        return map;
    }
}

일단 초간단하게 addApiUrl()로 특정 경로를 하드코딩해서 map에 담고
env.getPropertySources().addLast(mapPropertySource);에서 저장했다.

이게 실제로는 manager와 builder 2개로 나뉜다.
builder는 만드는 사람이고, builder의 메서드로 manager가 등록을 한다.
다음에 분리해보겠다.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ApiTestController {

    @Value("${cms.api.url}")
    private String cmsUrl;

    @GetMapping("/system")
    public String getApiUrls() {
        return cmsUrl;
    }
}

컨트롤러에서 위와 같이 호출해서 아래 결과가 나온다.

profile
Front-End와 Back-End 경험, 지식을 공유합니다.

0개의 댓글