글을 작성하게 된 계기
사람들과 스프링 서버를 만들어보는 프로젝트를 진행하며, 설정 파일을 관리하는 방법 에 대해 학습한 내용을 정리하기 위해 글을 작성하게 되었습니다.
프로젝트는 해당 링크에서 보실 수 있습니다.
1. 설정파일 관리 방법
설정 파일을 관리하는 방법은 정말 다양한데요, 이때까지 사용했던 방법들은 다음과 같습니다. 다 괜찮은 방법들이지만 각각의 장/단점이 존재하는데요, 이에 대해 간략하게 살펴보겠습니다.
- 환경 변수
- 서브 모듈
- 암호화
1-1. 환경 변수
환경변수를 사용하는 방법입니다. 이는 간단하고 빠르게 적용 할 수 있지만, 변수의 개수가 증가할수록 등록해야 할 값이 많아진다 라는 단점이 존재합니다.
1
2
3
4
5
6
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: ${URL}
username: ${USERNAME}
password: ${PASSWORD}
1-2. 암호화
Jasypt로 암호화를 해 관리할 수 있습니다. 이는 값을 암호화해서 안전하게 관리 할 수 있지만, 암호화 key를 별도로 관리 해 주어야 하는 단점이 존재합니다. Jasypt 단독으로 사용하기보다 다른 방법과 혼합해 사용하면 좋은 효과를 낼 수 있습니다. 값들이 암호화돼 있으므로, 설사 설정 파일이 노출되더라도 그 값을 바로 알 수 없기 때문입니다.
1-3. 서브 모듈
서브 모듈을 이용하는 방법이 있습니다. 이를 사용하면 중앙집중적으로 설정 파일을 관리할 수 있지만, 설정 파일 업데이트 시 참조 문제가 있으며, 무엇보다 프로젝트가 커지면 이를 사용하는데 한계가 있습니다.
2. Config Server
또 다른 방법으로는 Config 서버 가 있습니다. 이를 통해 필요할 때마다 설정 파일을 다운받아 사용할 수 있으며, 이 값은 배포 후에도 동적으로 변경할 수 있습니다. 이번 프로젝트에서는 이를 채택했는데, 어떻게 적용했으며, 어떤 장/단점이 존재하는지 살펴보겠습니다.
2-1. 적용하기
이는 build.gradle에 다음과 같이 task를 정의해 사용할 수 있습니다. dependsOn을 이용해 다른 task에 의존하거나 순서를 정의할 수도 있는데, 설정값은 빌드 시 모든 곳에서 필요하므로 가장 먼저 실행될 수 있도록 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
......
task downloadYml {
doLast {
def url = new URL("${URL}")
def file = new File(projectDir, "${DIRECTORY_PATH}")
if (!file.parentFile.exists()) {
file.parentFile.mkdirs()
}
url.withInputStream { inputStream -> file.withOutputStream { it << inputStream } }
}
}
tasks.named('processResources') {
dependsOn downloadYml
}
Gradle의 build 시 task 순서는 해당 링크를 참조해주세요.
프로젝트라 간단히 build.gradle에 task를 등록해 구현했지만, 스프링에서는 SpringCloud를 통해 설정 파일을 관리할 방법을 이미 제공하고 있습니다. 자원 관리를 위한 추가적 비용이 조금 발생하긴 하지만, 여러 자원을 한 곳에서 통합해서 관리할 수 있기 때문에 이에 대해서는 별도로 학습해 보실 것을 권장해 드립니다.
2-2. 장점
이는 중앙 집중식으로 설정 파일을 관리하기 때문에 자원을 관리하기 쉬우며, 서버가 늘어나더라도 설정 파일을 일정하게 제공할 수 있습니다.
- 일관된 자원 관리
- 값의 동적 변경
설정파일을 일정하게 제공한다는 것은 다음과 같이 로드 밸런싱/오토 스케일링을 하더라도, 중앙에서 설정 파일을 제공하기 때문에 모두 동일한 파일을 사용할 수 있다는 것입니다.
2-3. 단점
물론 여기에도 단점이 존재합니다. 대표적으로 별도의 서버가 필요하다는 점, 설정 서버의 장애가 모든 서버에 영향을 미칠 수 있다는 점, 보안 등이 있습니다.
- 별도의 서버가 필요
- 설정 서버의 장애가 모든 서버에 영향을 미침
- 보안
이 중 보안에 관해서만 조금 더 살펴보겠습니다. 아무나 설정 서버에 접근해 파일을 내려받으면 안 되며, 이를 위해서는 인증/인가 수단 이 필요합니다. 간단하게 BasicAuth부터, 더 강력한 보안과 확장성을 위해서는 토큰을 사용한 인증을 사용할 수 있습니다. 인프라가 구축돼 있다면, Inbound/Outbound 룰을 등록해 특정 IP에서만 접근할 수 있도록 설정해야 합니다. 이런 보안적 요소를 고려해야 하므로 추가적인 관리 비용/포인트가 늘어날 수 있습니다.
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
task downloadYml {
doLast {
def url = new URL("${URL}")
def connection = url.openConnection()
def username = "${USERNAME}"
def password = "${PASSWORD}"
def basicAuth = "${username}:${password}"
def encoded = Base64.getEncoder().encodeToString(basicAuth.bytes)
// Basic-Auth 헤더 추가
connection.setRequestProperty("Authorization", "Basic ${encoded}")
def file = new File(projectDir, "${DIRECTORY_PATH}")
if (!file.parentFile.exists()) {
file.parentFile.mkdirs()
}
connection.withInputStream { inputStream ->
file.withOutputStream { it << inputStream }
}
}
}
tasks.named('processResources') {
dependsOn downloadYml
}
3. 정리
설정 파일을 어떻게 관리할지에 대해 간략하게 살펴보았습니다. 각 방법은 장/단점이 존재하기 때문에 이를 잘 비교해 자신의 프로젝트에 적용할 수 있도록 합니다.