MyRSS 프로젝트를 진행하며 스프링 설정 파일을 리팩터링한 내용을 정리해봤습니다.
설정 파일 공개 필요성
spring:
config:
import:
- classpath:/2022-MyRSS-secret/application.yml
- 위 내용은 리팩터링 이전 application.yml 파일 내용의 전부입니다.
- 서브모듈로 Private Repository를 품고 있고, 그 안에 환경 설정 내용들을 담아두었습니다.
- Github Actions, Jenkins에서는 Github Personal Token을 전달해 서브모듈까지 가져와서 CI/CD를 수행합니다.
그대로 계속 작업해도 아무 문제 없지만, 공개 가능한 설정은 공개하도록 리팩터링하고 싶었습니다.
추가적으로, 서브모듈로 관리되는 보안이 필요한 접속정보 없이도 애플리케이션을 기동시킬 수 있게 하고 싶었습니다.
이렇게 하고 싶었던 이유는, 제가 겪었던 불편 때문인데요.
간혹 분석해보고 싶은 프로젝트를 발견해서 이를 기동해보고 디버깅해보고 싶을 때가 있었는데,
수많은 프로퍼티의 부재로 인해 기동까지 가는 데에도 한참 시간이 소요되더라고요.
포트폴리오 성격이 강한 프로젝트이니 제가 작업한 내용을 공개한다는 측면도 있고,
혹여 다른 분이 프로젝트를 clone해서 기동하고자 할 때 즉시 기동되게 하고 싶었습니다.
외부 설정파일의 Optional import
spring:
config:
import: optional:classpath:/2022-MyRSS-secret/application.yml
- spring.config.import 를 사용하는 것은 동일합니다
- 기존에 명시하던 경로 앞에 optional: 선언을 추가했습니다.
- 해당 파일이 존재할 경우 가져오고, 없을 경우 아무 작업도 수행하지 않습니다.
- 만약 optional: 선언이 없고 해당 경로에 파일이 존재하지 않을 경우
ConfigDataLocationNotFoundException 예외가 발생합니다.
- 만약 optional: 선언이 없고 해당 경로에 파일이 존재하지 않을 경우
- 우선 optional 설정을 추가해줌으로써, 해당 파일이 존재할 경우엔 그 설정값을 사용하고,
없을 경우에라도 애플리케이션이 기동되는 분기처리는 완료되었습니다. - 관련 스프링 공식문서 링크입니다.
외부 설정 파일 import 시 외부 설정 값이 우선 사용됨
- 외부 설정 파일을 사용할 때, 선언 순서가 영향을 미치는지 확인해봤습니다.
- application.yml과 외부 설정 파일인 import1.yml이 있고,
둘 다 같은 키에 값을 할당한다면, import 선언 순서가 영향을 미치는지 테스트했습니다. - 선언 순서와 상관없이 같은 key가 선언되어 있다면, 외부 설정 파일이 적용되었습니다.
- 공식 문서에서도 외부에서 import된 값이 우선순위를 지닌다고 명시되어 있습니다.
Values from the imported config file
will take precedence over the file that triggered the import.
다중 설정 파일 Import 시 나중에 선언된 값이 사용됨
- 외부로부터 import하는 설정 파일이 여러 개라면 어떨까요?
- 우선 comma 로 구분해서 여러 외부 설정 파일을 불러올 수 있음을 확인했습니다.
- 외부 import 된 파일들이 같은 key에 대해 값을 설정한다면, 나중에 선언된 값이 사용됩니다.
- 사용할 일이 흔하진 않을 것 같지만, 여러 개의 외부 설정 파일을 import 한다면,
같은 key 값에 대해 설정을 하고 있는지, 그렇다면 선언 순서에 유의해서 사용해야겠습니다.
보안 설정에 대한 대체 설정 구상
- 보안이 필요할 설정값은 MySQL 접속정보, Redis 접속정보, Github OAuth Secret 입니다.
- 이 외의 설정에 대해서는 application.yml 파일에 옮기면 됩니다.
- 그러나 보안이 필요한 설정값을 담은 secret yaml 파일이 없더라도 기동되려면 대체재를 준비해두어야 합니다.
- MySQL은 H2로, Redis는 EmbeddedRedis로 대체할 수 있습니다.
- EmbeddedRedis는 테스트 환경에서도 사용할 수 있으므로 더욱 의미가 있을 것 같습니다.
- Github OAuth는 client_id, client_secret 필드만 남겨두고 값은 숨겨두었습니다.
- 해당 값이 필요하다는 정보를 남겨두는 정도로 마무리 했습니다.
- 만약 Full Operation을 해보고 싶다면, 본인의 github oauth app을 만들어서
토큰을 집어넣어서 간단히 사용할 수 있을 거라 생각했습니다. - github oauth에 대해서도 mocking 처리를 할 방법이 여럿 있을 것 같은데요,
이 부분까진 진행하지 않기로 판단했습니다.
- H2는 의존성을 추가하고, 설정에서 enable설정 등만 간단히 해주면 되므로 생략하겠습니다.
EmbeddedRedis 설정
implementation('it.ozimov:embedded-redis:0.7.3') { exclude group: "org.slf4j", module: "slf4j-simple" }
@Profile({"test"})
@Component
public class EmbeddedRedisConfig {
private final RedisServer redisServer;
public EmbeddedRedisConfig(final RedisConfig redisConfig) {
this.redisServer = RedisServer.builder()
.port(redisConfig.getPort())
.setting("maxmemory 32MB")
.build();
}
@PostConstruct
public void start() {
redisServer.start();
}
@PreDestroy
public void stop() {
redisServer.stop();
}
}
- 기존 RedisConnectionFactory와 RedisTemplate을 Bean 등록하는 코드는 그대로 두었습니다.
- 공개된 설정에선 redis 접속정보를 localhost로 두었고, import되는 설정에선 실제 redis 주소를 두었습니다.
- 즉, 서브모듈 내에 있는 보안 설정 파일이 없을 경우 spring.reids.host 를localhost로 읽게 됩니다.
- RedisServer를 애플리케이션 기동 시점에 start하고 종료 시점에 stop하게 해줍니다.
- 의존성에선 로깅 프레임 워크가 중복 등록되기 때문에 exclude 처리해줍니다.
- Baeldung 포스팅과 Rogal 님의 포스팅의 도움을 받았습니다.
- 설정 완료 후, 로그인 했을 때 세션 정보가 운영 환경에 등록되지 않으면서도,
디버깅 시 세션 정보 조회가 정상 조회되는 것을 확인하여 EmbeddedRedis 기반으로 동작하는 것을 확인했습니다. - 외부 설정에선 profile을 덮어쓰고, 외부 설정이 없을 경우 test profile로 동작하게 했으며,
그렇기에 EmbeddedRedis 서버는 test 프로파일에서만 Bean 등록되고 실행되도록 구성했습니다.
결과
spring:
config:
import: optional:classpath:/2022-MyRSS-secret/application.yml
profiles:
active: test
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb
hikari:
maximum-pool-size: 10
connection-timeout: 5000
data-source-properties:
cachePrepStmts: true
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
useLocalSessionState: true
rewriteBatchedStatements: true
cacheResultSetMetadata: true
cacheServerConfiguration: true
elideSetAutoCommits: true
maintainTimeStats: false
jpa:
properties:
hibernate:
format_sql: true
show_sql: true
jdbc:
time_zone: Asia/Seoul
hibernate:
ddl-auto: create
h2:
console:
enabled: true
path: /h2-console
data:
redis:
repositories:
enabled: false
redis:
host: localhost
port: 6379
password:
session:
store-type: redis
server:
tomcat:
max-connections: 400
accept-count: 50
threads:
min-spare: 10
max: 10
oauth:
github:
accessTokenUrl: https://github.com/login/oauth/access_token
userInfoUrl: https://api.github.com/user
clientId:
clientSecret:
logging:
level:
web: debug
- 서브 모듈 없이 clone 해도 즉시 기동이 가능하도록 처리가 완료되었습니다.
- hikari 설정, tomcat 설정, jpa, redis 설정 등 프로젝트에 사용되는 환경 설정들이 잘 드러내졌습니다.
- 보안 처리된 환경 설정의 경우 MySQL, Redis, ddl-auto, Github OAuth 등의 설정을 재정의합니다.
학습 출처
Spring Docs : Optional Locations
Embedded Redis 를 쓰면서 겪은 문제와 해결방안
Embedded Redis Server with Spring Boot Test
'Java & Spring' 카테고리의 다른 글
Spring Cache Abstraction (0) | 2022.11.28 |
---|---|
응답 DTO, N+1, Open Session In View (2) | 2022.11.27 |
🙌 쓰레드풀을 이용해 두 비동기 작업 처리 완료 후 후속 처리하기 (0) | 2022.09.29 |
🥄 쓰레드풀 한 스푼 (2) | 2022.09.29 |
jitpack, github를 이용한 라이브러리 배포하기 (0) | 2022.08.27 |