Series/내가 해본

현재 환경에 맞는 값을 가져오기 with enum

Hyunec 2022. 5. 18. 23:25

로직은 같지만 개발과 운영에서 필요한 값이 다른 경우가 있는데, 대표적으로 url이 있습니다.
지금의 회사에서는 enum을 통해 관리하고 있지만, 그 방식이 enum 스럽지 않다고 생각하고 있었습니다.

 

그러던 중 기존 test - dev - stage - prod 구성되었던 환경이 dev - qa - stage - prod 변경되었고, 이 과정에서 코드를 개선한 내용을 기록해 봅니다.

Before

환경 변수 enum

public enum DeploymentEnvironment {

	DEV, QA, STAGE, PROD;

	private static final String AUTH_SERVER_NAME = Optional.ofNullable(System.getenv("AUTH_SERVER_NAME"))
			.orElse("");

	public static boolean isDev() {
		return AUTH_SERVER_NAME.equals(DEV.name().toLowerCase(Locale.ROOT));
	}

	public static boolean isQA() {
		return AUTH_SERVER_NAME.equals(QA.name().toLowerCase(Locale.ROOT));
	}

	public static boolean isStage() {
		return AUTH_SERVER_NAME.equals(STAGE.name().toLowerCase(Locale.ROOT));
	}

	public static boolean isProd() {
		return AUTH_SERVER_NAME.equals(PROD.name().toLowerCase(Locale.ROOT));
	}
}

환경 구분이 enum으로 존재하긴 하지만 모든 환경을 확인하기 위해서는 4개나 되는 if-else가 필요합니다.

 

사용 예시 1

// swagger 설정
public class OpenApiConfig {

	private OpenAPI getOpenAPI() {
		if (DeploymentEnvironment.isDev()) {
			return new OpenAPI()
					.addServersItem(new Server().url("https://dev.aaa.co.kr/"))
					.addServersItem(new Server().url("http://localhost:8820/"));
		}

		if (DeploymentEnvironment.isQA()) {
			return new OpenAPI()
					.addServersItem(new Server().url("https://qa.aaa.co.kr/"))
					.addServersItem(new Server().url("http://localhost:8820/"));
		}

		if (DeploymentEnvironment.isStage()) {
			return new OpenAPI()
					.addServersItem(new Server().url("https://stage.aaa.co.kr/"))
					.addServersItem(new Server().url("http://localhost:8820/"));
		}

		if (DeploymentEnvironment.isProd()) {
			return new OpenAPI()
					.addServersItem(new Server().url("https://api.aaa.com/"))
					.addServersItem(new Server().url("http://localhost:8820/"));
		}

		return new OpenAPI()
				.addServersItem(new Server().url("http://localhost:8820/"));
	}
}

환경별로 다른 url을 설정하기 위해서는 4번의 if-else를 사용해야 했습니다.

 

사용 예시 2

public enum BBBCallbackRequest {

	UPDATE_USER_ID(HttpMethod.PATCH, () -> getBaseUrl() + "/api/shop/user"),
	;

	private final HttpMethod httpMethod;
	private final Supplier<String> urlSupplier;

	private static String getBaseUrl() {
		if (DeploymentEnvironment.isProd()) {
			return "https://shop.bbb.com";
		}

		if (DeploymentEnvironment.isStage()) {
			return "https://shopstage.bbb.co.kr";
		}

		if (DeploymentEnvironment.isQA()) {
			return "https://shopqa.bbb.co.kr";
		}

		throw new IllegalStateException("local, dev 에서는 콜백하지 않습니다.");
	}
}

심지어는 환경에 따라 예외를 발생시켜야 되는 경우도 있었습니다.
무엇보다도 url이 곳곳에 분산되어 관리하기 힘들었고 변경에 들어가는 공수도 컸습니다.

 

After

환경 변수 enum

public enum DeploymentEnvironment {

	LOCAL("", ""),
	DEV("", "https://dev.aaa.co.kr/"),
	QA("https://shopqa.bbb.co.kr", "https://qa.aaa.co.kr/"),
	STAGE("https://shopstage.bbb.co.kr", "https://stage.aaa.co.kr/"),
	PROD("https://shop.bbb.com", "https://api.aaa.com/");

	public static final DeploymentEnvironment CURRENT_DEPLOYMENT_ENVIRONMENT;

	private final String bbbCallbackUrl; 
	private final String baseUrl;

	static {
		final String authServerName = Optional.ofNullable(System.getenv("AUTH_SERVER_NAME"))
				.orElse("")
				.toUpperCase(Locale.ROOT);
		CURRENT_DEPLOYMENT_ENVIRONMENT = Arrays.stream(DeploymentEnvironment.values())
				.filter(environment -> environment.name().equals(authServerName))
				.findFirst()
				.orElseThrow(() -> new IllegalArgumentException("알 수 없는 환경 변수"));
	}
}

현재 환경을 CURRENT_DEPLOYMENT_ENVIRONMENT로 정의하고 정적 초기화합니다.

 

사용 예시

// 사용 예시 1 
public class OpenApiConfig {

	private static OpenAPI getOpenAPI() {
		if (CURRENT_DEPLOYMENT_ENVIRONMENT.getBaseUrl().isBlank()) {
			return new OpenAPI().addServersItem(new Server().url("http://localhost:8820/"));
		}

		return new OpenAPI().addServersItem(new Server().url(CURRENT_DEPLOYMENT_ENVIRONMENT.getBaseUrl()));
	}
}

// 사용 예시 2
public enum BBBCallbackRequest {

	UPDATE_USER_ID(
			HttpMethod.PATCH, () -> CURRENT_DEPLOYMENT_ENVIRONMENT.getBbbCallbackUrl() + "/api/v2/shop/user"),
	;
    
	private final HttpMethod httpMethod;
	private final Supplier<String> urlSupplier;
}

사용 예시가 극단적으로 간결해졌습니다.
무엇보다도 enum을 통해 코드 응집도를 높였기에 환경이 변경되어도 enum만 수정하면 되는 것이 가장 큰 장점입니다.

 

마치며

예시는 2개뿐이지만 실제로는 더 많은 사용이 있었고, 이 수정을 통해 미래의 변경 영향도도 축소할 수 있습니다.

 

만약 환경 변수 관리가 더 민감했다면 Spring Cloud Config 등을 활용할 수 있겠지만, 그것 또한 구성과 운영의 어려움이 있고 지금의 회사에서도 Dockerfile을 통한 관리 정도는 하기에 괜찮다고 이 정도의 개선만 진행했습니다.