티스토리 뷰

프로퍼티 파일을 이용한 외부 설정 주입과 운영 환경에 따른 프로퍼티 파일 분리

Overview

현재 진행하고 있는 sns 프로젝트에서는 회원 프로필 이미지를 수정하거나 게시글에 사진을 첨부하는 등 이미지 파일을

다루는 기능들이 많다. 서버로 넘어오는 파일들을 받아 저장하는 기능을 구현하기 위해 파일 경로를 지정했다. 이는 회원

아이디 별로 달라지기 때문에 공통적으로 사용되는 기본 경로를 따로 분리하여 관리하기로 하고 다음과 같이 기본 경로를

지정했다.

@Service
@RequiredArgsConstructor
public class LocalFileService implements FileService {

    private String baseDir = "C:\\Users\\myId\\Desktop\\Project\\sns-server\\images"

    ...
}

이렇게 코드 내에 직접 경로를 지정하게 되면 한 가지 문제점이 발생한다. 만약 어플리케이션 운영 중에 파일 저장 경로를 변경해야한다면 어플리케이션을 중지하고 소스코드를 수정해야 한다는 점이다. 코드를 수정하면 컴파일을 다시 해야되고 어플리케이션의 배포도 다시 해야된다. 또한 지금은 이렇게 한 곳에서만 쓰이지만 여러 곳에서 쓰인다면 유지보수성을 떨어뜨릴 것이다. 따라서, 이러한 설정 정보를 코드가 아닌 외부 설정 파일로 분리하고 이를 주입시키는 방식으로 바꿀 것이다.

 

Refactoring-1: 프로퍼티 파일로 설정값 분리하기

Spring Boot에는 외부 설정을 쉽게 관리할 수 있는 application.properties 파일을 기본적으로 제공해준다.

application.properties 파일은 스프링부트가 구동될 때 자동적으로 로딩되어 어플리케이션에서 파일에 쓰여진 값들을

사용할 수 있게 해준다. 해당 파일에 원하는 이름의 key를 쓰고 value에는 분리하고자 하는 설정값을 쓴다.

application.properties

itda.local.file.base.directory = "C:\\Users\\myId\\Desktop\\Project\\sns-server\\images"

 

그 다음 @Value 어노테이션을 사용하여 프로퍼티 파일에서 지정한 key로 설정값을 읽어와 지정한 변수에 주입한다.

@Service
@RequiredArgsConstructor
public class LocalFileService implements FileService {

    @Value("${itda.local.file.base.directory}")
    private String baseDir;

 

After Refactoring-1

위의 과정을 통해 필요한 설정값들을 하나의 파일에서 따로 관리할 수 있게 되었다. 또한 설정값을 변경해야하는 일이 생

기더라도 서버를 중단하지 않고 쉽게 잘 해결할 수 있다. 그러나 이 서비스를 배포하는 상황을 가정하여 LocalFileService 대신 사용할 S3FileService 를 구현하고 나서 다른 문제가 발생했다.

### Local
itda.local.file.base.directory = "C:\\Users\\myId\\Desktop\\Project\\sns-server\\images"

### S3
itda.aws.s3.base.url = https://sns-itda.s3.ap-northeast-2.amazonaws.com/

itda.aws.s3.region = ap-northeast-2
itda.aws.s3.bucket.name = sns-itda

aws.iam.accessKeyId = accessKeyId
aws.iam.secretAccessKey = secretAccessKey

위와 같이 어느 환경에서 서비스를 운영하느냐에 따라서 필요한 설정값이 다르다. 이 설정들을 모두 application.properties 파일에서 관리한다면 현재 환경에서 필요없는 설정까지 모두 로딩된다. 만약 같은 이름의 설정을 사용하는데 운영 환경마다 다른 설정값이 필요할 수도 있다. 이러한 경우 각각의 설정을 구분하기 위해 해당 Key의 이름을 더 길게 정하거나 운영 환경이 달라질 때마다 해당 환경에서 필요한 설정값을 제외한 나머지 설정값을 주석처리 하는 등 불편함이 뒤따른다. 따라서, 다양한 환경에 따라 프로퍼티 파일을 분리하여 설정을 관리해야 한다.

 

Refactoring-2: Profile에 따라 프로퍼티를 다르게 설정하기

이러한 경우를 대비하여 Spring Boot에서는 Profile 에 따라 프로퍼티를 다르게 설정할 수 있는 기능을 제공해준다.

Profile 이란 간단하게 설명하자면 환경을 구분짓는 것이라고 할 수 있다. application-{profile}.properties 형식의

네임 컨벤션만 지켜준다면 Spring Boot가 자동으로 Profile 을 구분한다. Profile 에 들어갈 단어는 어떤 것이든 상관

없지만 보통 dev, test, prod 와 같은 약어를 많이 사용한다.

application-dev.properties

itda.local.file.base.directory = "C:\\Users\\myId\\Desktop\\Project\\sns-server\\images"

application-prod.properties

### S3
itda.aws.s3.base.url = https://sns-itda.s3.ap-northeast-2.amazonaws.com/

itda.aws.s3.region = ap-northeast-2
itda.aws.s3.bucket.name = sns-itda

aws.iam.accessKeyId = accessKeyId
aws.iam.secretAccessKey = secretAccessKey

 

그리고 application.properties 파일에 spring.profiles.active 설정값에 어떤 Profile을 사용할 것인지 명시하고 이를 활성화한다.

application.properties

spring.profiles.active = dev

 

이렇게 Profile 을 구분하여 사용하게 되면 @Profile 어노테이션도 사용할 수 있게 된다. @Profile 어노테이션은 특정

Profile 에 따라 어떤 클래스를 빈으로 등록할지 지정할 수 있다. 해당 클래스는 지정된 Profile 이 활성화된 경우에만 빈으로 등록된다. 현재 이 프로젝트 같은 경우 개발 환경일 때는 LocalFileService , 실제 운영 환경일 때는 S3FileService 만 필요하다. 따라서, 각각의 클래스를 @Profile 어노테이션으로 어떤 Profile 에서 사용할지 정의한다.

LocalFileService.java

@Service
@RequiredArgsConstructor
@Profile("dev")
public class LocalFileService implements FileService {

    @Value("${itda.local.file.base.directory}")
    private String baseDir;
    ...
}

S3FileService.java

@Service
@RequiredArgsConstructor
@Profile("prod")
public class AwsFileService implements FileService {

    @Value("${itda.aws.s3.base.url}")
    private String baseUrl;

    @Value("${itda.aws.s3.bucket.name}")
    private String bucket;
    ...
}

 

After Refactoring-2

두 번째 리펙토링까지 마치고 나니 서비스 운영 환경에 따라 필요한 설정값과 빈들만 등록하여 사용할 수 있게 되었다. 하지만 아직도 찜찜한 부분이 남아있다. 바로 이 부분이다.

itda.aws.s3.base.url = https://sns-itda.s3.ap-northeast-2.amazonaws.com/

itda.aws.s3.region = ap-northeast-2
itda.aws.s3.bucket.name = sns-itda

aws.iam.accessKeyId = accessKeyId
aws.iam.secretAccessKey = secretAccessKey

aws.iam.accessKeyId, aws.iam.secretAccessKey 설정값은 ID와 비밀번호처럼 AWS API를 사용할 때 인증을 위하여 사용된다. 이를 악의적인 사용자가 갖게되어 멋대로 나의 AWS 계정을 사용하게 된다면 어마무시한 과금으로까지 이어질 수 있기 때문에 외부에 노출되지 않도록 안전하게 관리해야한다. 그렇기 때문에 이 또한 따로 분리해야할 필요성이 있다.

 

Refactoring-3: 비공개 설정값을 별도의 프로퍼티 파일로 분리하기

해당 설정값은 특정 Profile 에서 따로 관리되어야 한다기보다는 보안 유지 목적이 크기 때문에 Git처럼 공개된 장소에 아예 올라가지 않게 해야 한다. 따라서, 별도의 프로퍼티 파일로 분리하고 이를 gitignore 파일에 등록한다.

s3-private.properties

aws.iam.accessKeyId = accessKeyId
aws.iam.secretAccessKey = secretAccessKey

.gitignore

### AWS S3 config ###
s3-private.properties

 

그리고 @PropertySource 애노테이션으로 해당 프로퍼티를 사용할 클래스에 프로퍼티 파일의 경로를 설정하여 값을 읽어 와야 한다. (@PropertySource 애노테이션은 @Configuration 애노테이션이 붙은 클래스에서만 사용할 수 있다.)

AWSS3Config.java

@Configuration
@Profile("prod")
@PropertySource("classpath:/s3-secret.properties")
public class AwsS3Config {
    @Value("${itda.aws.s3.region}")
    String region;

    @Value("${aws.iam.accessKeyId}")
    String accessKeyId;

    @Value("${aws.iam.secretAccessKey}")
    String secretAccessKey;
    ...
}

 

After Refactoring-3

프로퍼티 파일을 활용한 리팩토링 작업을 통해 소스코드를 직접 수정하지 않고도 쉽게 설정값을 관리할 수 있게 되었으며

서비스 운영 환경에 따라 프로퍼티 파일을 분리할 수 있었다. 또한 외부에 노출되지 않아야 하는 중요 정보들도 따로 분리

하여 관리할 수 있었다.

 

 


 

진행 프로젝트

github.com/f-lab-edu/sns-itda

 

f-lab-edu/sns-itda

Contribute to f-lab-edu/sns-itda development by creating an account on GitHub.

github.com

 

댓글