Spring Cloud Config Server는 설정 파일을 암호화하여 저장할 수 있다. 이를 위해서는 Config Server가 공개키와 개인키를 포함한 KeyStore(JKS)를 가지고 있어야 한다.
keytool 명령어를 이용하여 KeyStore를 생성하였다.
keytool -genkeypair \
-alias apiEncryptionKey \
-keyalg RSA \
-keystore apiEncryptionKey.jks
생성된 KeyStore는 Config Server에서 다음과 같이 설정한다.
encrypt:
key-store:
location: file:///C:/inspire/keystore/apiEncryptionKey.jks
password: ******
alias: apiEncryptionKey
이후 Config Server의 /encrypt API를 호출하면 평문을 암호화된 문자열로 변환할 수 있고, /decrypt API를 이용하면 다시 원래 값으로 복호화할 수 있다.
실습 중 가장 먼저 발생했던 문제는 다음과 같았다.
Cannot load keys from store
FileNotFoundException
원인은 Config Server에서 지정한 경로에 KeyStore 파일이 존재하지 않았기 때문이다.
따라서
을 통해 문제를 해결하였다.
또한 alias와 password가 KeyStore 생성 시 입력한 값과 일치해야 정상적으로 암호화를 수행할 수 있다는 점도 확인하였다.
Spring Cloud Bus는 RabbitMQ를 이용하여 여러 서비스에 설정 변경 사항을 전달하는 역할을 한다.
이번 실습에서는 Config Repository의 설정을 변경한 뒤
POST /actuator/busrefresh
를 호출하여 서비스를 재시작하지 않고 변경된 설정을 반영하는 과정을 수행하였다.
Bus를 사용하지 않는 경우에는 설정 변경 시 각 서비스를 직접 재시작해야 하지만, Bus를 사용하면 하나의 Refresh 요청으로 여러 서비스에 동시에 변경 사항을 전달할 수 있다.
설정 Repository에 새로운 정보를 추가하였다.
company:
name: NJONE Company
ceo: Hong Gil Dong
그리고 User Service에서는
@Value("${company.name}")
private String companyName;
@Value("${company.ceo}")
private String companyCeo;
를 이용하여 설정값을 주입받고,
GET /users/company
API를 통해 JSON 형태로 반환하도록 구현하였다.
실습 과정에서 Config Repository에 해당 값이 존재하지 않으면
Could not resolve placeholder 'company.name'
오류가 발생하며 애플리케이션이 시작되지 않는다는 점도 확인하였다.
실습 중
DuplicateKeyException
오류가 발생하였다.
원인은
spring:
rabbitmq:
...
rabbitmq:
...
처럼 동일한 Key를 두 번 선언했기 때문이었다.
YAML에서는 동일한 계층에서 같은 Key를 중복하여 사용할 수 없으므로 하나의 블록으로 합쳐 작성해야 한다.
설정을 수정하는 과정에서
No spring.config.import property has been defined
오류도 발생하였다.
Spring Cloud Config Client는 Config Server의 위치를 반드시 알고 있어야 하므로
spring:
config:
import: optional:configserver:http://localhost:8888
설정을 추가하여 해결하였다.
오늘 마지막으로 API Gateway에서 JWT 인증 필터를 실습하였다.
Gateway는 User Service 앞단에서 모든 요청을 검사하며 JWT가 없는 경우 요청을 차단한다.
실제로 브라우저에서
http://localhost:8000/user-service/health-check
를 호출하자
401 Unauthorized
No authorization header
로그가 출력되었다.
브라우저는 Authorization 헤더를 자동으로 보내지 않기 때문에 Gateway의 인증 필터에서 요청이 거부된 것이다.
로그에서도
No authorization header
가 출력되며 JWT 인증이 정상적으로 동작하는 것을 확인할 수 있었다.
공개 API(Health Check 등)는 인증 예외 경로로 등록하거나, Postman에서 Bearer Token을 포함하여 요청해야 정상적으로 접근할 수 있다.
오늘은 Spring Cloud의 여러 구성 요소들이 서로 어떻게 연결되는지 실제 오류를 해결하면서 이해할 수 있었다.
특히 Config Server는 단순히 설정 파일을 제공하는 역할만 하는 것이 아니라 KeyStore를 이용한 암호화 기능도 제공하며, Spring Cloud Bus는 RabbitMQ를 통해 설정 변경 사항을 여러 서비스에 동시에 전달한다는 점을 확인하였다.
또한 API Gateway는 모든 요청의 관문 역할을 하며 JWT를 이용해 인증을 수행하기 때문에, 인증이 필요한 API와 공개 API를 적절히 구분하여 설계해야 한다는 점도 배울 수 있었다.
오늘 실습은 기능 구현보다도 다양한 오류를 직접 해결하는 과정이 많았지만, 그 덕분에 각 서비스의 역할과 설정 구조를 이전보다 훨씬 명확하게 이해할 수 있었다.