0. 들어가며
마이크로서비스 아키텍처에서는 수많은 서비스가 각자의 설정 파일을 가지고 있다. 데이터베이스 연결 정보, 외부 API 키, 기능 토글(feature toggle), 애플리케이션 파라미터 등 환경마다(개발, 테스트, 운영) 다른 값을 가져야 하는 설정들이 존재한다. 이러한 설정들을 각 서비스마다 개별적으로 관리한다면 어떤 문제가 발생할까?
- 설정 변경의 어려움이다. 동일한 설정 값을 여러 서비스에서 사용하는 경우, 모든 서비스의 설정 파일을 일일이 수정해야 한다.
- 보안 위험이다. 데이터베이스 비밀번호나 API 키와 같은 민감한 정보가 각 서비스의 코드 저장소에 함께 저장될 수 있다.
- 환경 간 일관성 유지가 어렵다. 개발 환경과 운영 환경의 설정이 달라 예상치 못한 문제가 발생할 수 있다.
Spring Cloud Config는 이러한 문제를 해결하기 위한 중앙 집중식 설정 관리 솔루션이다. 모든 설정을 외부 저장소(Git, 파일 시스템 등)에서 관리하고, 각 마이크로서비스는 실행 시점에 필요한 설정을 Config Server로부터 가져온다.
이번 글에서는 Spring Cloud Config Server를 구축하고, 이를 User Service, Catalog Service, Order Service와 연동하는 방법을 살펴본다. 또한 Spring Cloud Bus를 활용하여 설정 변경 사항을 각 서비스에 동적으로 전파하는 방법까지 다룰 예정이다.
1. Spring Cloud Config 개요
1.1. Spring Cloud Config란?
Spring Cloud Config는 분산 시스템에서 외부화된 설정 정보를 중앙에서 관리할 수 있게 해주는 서버와 클라이언트로 구성된 프로젝트다. Config Server는 다양한 백엔드(Git, 파일 시스템, Vault, JDBC 등)에서 설정 정보를 읽어와 클라이언트에게 제공한다.
핵심 구성 요소:
- Config Server: 중앙 설정 저장소 역할을 하는 서버
- Config Client: Config Server에서 설정을 조회하는 각 마이크로서비스
1.2. Spring Cloud Config의 장점
중앙 집중식 관리: 모든 서비스의 설정을 한 곳에서 관리할 수 있어 일관성 유지가 쉽다.
환경별 설정 분리: 개발, 테스트, 운영 환경에 따라 다른 설정을 적용할 수 있다.
동적 설정 변경: 애플리케이션 재배포 없이 설정을 변경할 수 있다(Spring Cloud Bus와 연동 시).
보안 강화: 민감한 정보를 암호화하여 저장할 수 있다.
설정 이력 관리: Git과 연동하면 설정 변경 이력을 추적하고 롤백할 수 있다.
2. Local Git Repository
2.1. 로컬 Git 저장소 생성
먼저 설정 파일을 저장할 로컬 Git 저장소를 생성한다.
# 설정 파일을 저장할 디렉토리 생성
mkdir -p ~/git/config-repo
cd ~/git/config-repo
# Git 저장소 초기화
git init
# 설정 파일을 저장할 디렉토리 구조 생성
mkdir -p user-service catalog-service order-service
2.2. 설정 파일 작성
user-service.yml
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:userdb
username: sa
password: 1234
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
properties:
hibernate:
format_sql: true
token:
expiration_time: 86400000
secret: my-secret-key-which-should-be-very-long-and-secure-for-jwt-token-generation
user-service-dev.yml (개발 환경용)
spring:
datasource:
url: jdbc:h2:mem:userdb-dev
username: sa
password: 1234
jpa:
show-sql: true
token:
expiration_time: 86400000
secret: dev-secret-key
user-service-prod.yml (운영 환경용)
spring:
datasource:
url: jdbc:mariadb://prod-db-server:3306/userdb
username: prod_user
password: prod_password
jpa:
show-sql: false
token:
expiration_time: 3600000 # 1시간
secret: prod-secret-key-that-is-very-long-and-secure
catalog-service.yml
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:catalogdb
username: sa
password: 1234
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
defer-datasource-initialization: true
spring.sql.init.data-locations: classpath:data.sql
order-service.yml
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:orderdb
username: sa
password: 1234
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
2.3. Git 저장소에 커밋
# 모든 설정 파일 추가
git add .
git commit -m "Initial configuration for microservices"
3. Spring Cloud Config - 프로젝트 생성
3.1. Config Server 프로젝트 생성
build.gradle (config-service)
plugins {
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', "2023.0.0")
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-config-server'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
3.2. 메인 애플리케이션 클래스
ConfigServiceApplication.java
package com.example.configservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServiceApplication.class, args);
}
}
3.3. 애플리케이션 설정
application.yml
server:
port: 8888
spring:
application:
name: config-service
cloud:
config:
server:
git:
uri: file://${user.home}/git/config-repo
default-label: main
# Windows의 경우 경로를 file:///C:/Users/사용자명/git/config-repo 형식으로 지정
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: <http://localhost:8761/eureka/>
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
management:
endpoints:
web:
exposure:
include: health,info,busrefresh
3.4. Config Server 실행 및 확인
Config Server를 실행한 후 브라우저에서 다음 URL로 설정 정보를 확인할 수 있다.
# user-service 설정 조회
<http://localhost:8888/user-service/default>
# user-service 개발 환경 설정 조회
<http://localhost:8888/user-service/dev>
# catalog-service 설정 조회
<http://localhost:8888/catalog-service/default>
# order-service 설정 조회
<http://localhost:8888/order-service/default>
응답 예시:
{
"name": "user-service",
"profiles": ["default"],
"label": "main",
"version": "1234567",
"propertySources": [
{
"name": "file:///Users/username/git/config-repo/user-service.yml",
"source": {
"spring.datasource.driver-class-name": "org.h2.Driver",
"spring.datasource.url": "jdbc:h2:mem:userdb",
"spring.datasource.username": "sa",
"spring.datasource.password": "1234",
"token.expiration_time": 86400000,
"token.secret": "my-secret-key-which-should-be-very-long-and-secure-for-jwt-token-generation"
}
}
]
}
4. Users Microservice에서 Spring Cloud Config 연동
4.1. Config Client 의존성 추가
build.gradle (user-service)
dependencies {
// 기존 의존성
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
// ...
}
4.2. Bootstrap 설정 파일 생성
Spring Cloud Config Client는 bootstrap.yml 또는 bootstrap.properties를 통해 Config Server에 연결한다.
bootstrap.yml (user-service)
spring:
application:
name: user-service
cloud:
config:
uri: <http://localhost:8888>
name: user-service
profile: ${spring.profiles.active:default}
label: main
profiles:
active: default
4.3. application.yml 간소화
Config Server에서 설정을 가져오므로, 기존 application.yml에서 Config Server와 관련 없는 설정만 남긴다.
application.yml (user-service) - 간소화 버전
server:
port: 0
spring:
application:
name: user-service
eureka:
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: <http://127.0.0.1:8761/eureka>
logging:
level:
com.example.userservice: DEBUG
4.4. 설정 값 사용 확인
Config Server에서 가져온 설정 값을 사용하려면 @Value 어노테이션이나 @ConfigurationProperties를 사용한다.
UserController.java (설정 값 확인용 엔드포인트 추가)
@RestController
@RequestMapping("/users")
@Slf4j
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
private final Environment env;
@GetMapping("/config-check")
public ResponseEntity<Map<String, Object>> configCheck() {
Map<String, Object> response = new HashMap<>();
// Config Server에서 가져온 설정 값 확인
response.put("token.secret", env.getProperty("token.secret"));
response.put("token.expiration_time", env.getProperty("token.expiration_time"));
response.put("spring.datasource.url", env.getProperty("spring.datasource.url"));
response.put("spring.datasource.username", env.getProperty("spring.datasource.username"));
return ResponseEntity.ok(response);
}
// 기존 메서드들...
}
5. Spring Boot Actuator - Refresh
5.1. 설정 변경과 동적 리프레시
Config Server를 사용하면 설정을 중앙에서 관리할 수 있지만, 설정이 변경되었을 때 각 마이크로서비스에 변경 사항을 적용하려면 애플리케이션을 재시작해야 한다. Spring Boot Actuator의 /refresh 엔드포인트를 사용하면 애플리케이션 재시작 없이 일부 설정을 동적으로 변경할 수 있다.
5.2. RefreshScope 어노테이션
@RefreshScope 어노테이션을 사용하면 해당 빈이 설정 변경 시 동적으로 리프레시된다.
UserController.java (RefreshScope 추가)
@RestController
@RequestMapping("/users")
@Slf4j
@RequiredArgsConstructor
@RefreshScope // 설정 변경 시 리프레시 대상
public class UserController {
private final UserService userService;
private final Environment env;
@Value("${token.secret}")
private String tokenSecret;
@Value("${token.expiration_time}")
private long tokenExpirationTime;
@GetMapping("/token-info")
public ResponseEntity<Map<String, Object>> getTokenInfo() {
Map<String, Object> response = new HashMap<>();
response.put("token.secret", tokenSecret);
response.put("token.expiration_time", tokenExpirationTime);
return ResponseEntity.ok(response);
}
// 기존 메서드들...
}
5.3. Actuator 엔드포인트 활성화
application.yml (user-service)
management:
endpoints:
web:
exposure:
include: refresh, health, info
5.4. 설정 변경 테스트
- Git 저장소의 설정 파일을 변경한다.
# user-service.yml token: expiration_time: 43200000 # 12시간으로 변경 - Git에 커밋한다.
git add user-service.yml git commit -m "Change token expiration time to 12 hours" - User Service에 POST 요청으로 리프레시를 실행한다.
응답:POST <http://localhost>:{랜덤포트}/actuator/refresh Content-Type: application/json["token.expiration_time"] - 변경된 설정 확인
GET <http://localhost>:{랜덤포트}/users/token-info
6. Spring Cloud Gateway에서 Spring Cloud Config 연동
6.1. Config Client 의존성 추가
build.gradle (apigateway-service)
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
// 기존 의존성...
}
6.2. Bootstrap 설정
bootstrap.yml (apigateway-service)
spring:
application:
name: apigateway-service
cloud:
config:
uri: <http://localhost:8888>
name: apigateway-service
profile: ${spring.profiles.active:default}
label: main
6.3. Git 저장소에 Gateway 설정 추가
apigateway-service.yml
server:
port: 8000
spring:
cloud:
gateway:
routes:
- id: user-service-signup
uri: lb://USER-SERVICE
predicates:
- Path=/users/**
- Method=POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/users/(?<segment>.*), /$\\{segment}
- id: user-service-login
uri: lb://USER-SERVICE
predicates:
- Path=/login
- Method=POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/login, /login
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/users/**
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/users/(?<segment>.*), /$\\{segment}
- name: AuthorizationHeaderFilter
- id: catalog-service
uri: lb://CATALOG-SERVICE
predicates:
- Path=/catalogs/**
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/catalogs/(?<segment>.*), /$\\{segment}
- name: AuthorizationHeaderFilter
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/orders/**
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/orders/(?<segment>.*), /$\\{segment}
- name: AuthorizationHeaderFilter
token:
secret: ${token.secret}
6.4. application.yml 간소화
application.yml (apigateway-service)
spring:
application:
name: apigateway-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: <http://127.0.0.1:8761/eureka/>
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
logging:
level:
org.springframework.cloud.gateway: DEBUG
7. Multiple Profiles 사용하기
7.1. 프로필별 설정 파일 구조
Config Server는 프로필에 따라 다른 설정 파일을 제공할 수 있다.
config-repo/
├── user-service.yml # 기본 설정 (모든 프로필 공통)
├── user-service-dev.yml # 개발 환경 설정
├── user-service-prod.yml # 운영 환경 설정
├── catalog-service.yml
├── catalog-service-dev.yml
├── catalog-service-prod.yml
├── order-service.yml
├── order-service-dev.yml
└── order-service-prod.yml
7.2. 프로필 활성화
각 마이크로서비스의 bootstrap.yml에서 프로필을 지정할 수 있다.
bootstrap.yml (user-service - 개발 환경)
spring:
application:
name: user-service
cloud:
config:
uri: <http://localhost:8888>
name: user-service
profile: dev
label: main
profiles:
active: dev
7.3. 실행 시 프로필 지정
# JAR 파일 실행 시 프로필 지정
java -jar -Dspring.profiles.active=dev user-service.jar
# 또는 환경 변수로 지정
export SPRING_PROFILES_ACTIVE=dev
java -jar user-service.jar
8. Remote Git Repository
8.1. GitHub 저장소 사용
로컬 Git 저장소 대신 원격 GitHub 저장소를 사용할 수 있다.
application.yml (config-service)
spring:
cloud:
config:
server:
git:
uri: <https://github.com/username/config-repo.git>
default-label: main
username: ${GITHUB_USERNAME} # 환경 변수 사용
password: ${GITHUB_TOKEN} # 개인 액세스 토큰
basedir: /tmp/config-repo # 클론할 로컬 디렉토리
clone-on-start: true # 서버 시작 시 클론
force-pull: true # 강제 풀
8.2. SSH를 통한 접근
SSH 키를 사용하여 GitHub에 접근할 수도 있다.
spring:
cloud:
config:
server:
git:
uri: git@github.com:username/config-repo.git
ignore-local-ssh-settings: true
private-key: |
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
9. Native File Repository
Git 외에도 파일 시스템을 직접 저장소로 사용할 수 있다.
9.1. 네이티브 프로필 사용
application-native.yml (config-service)
yaml
spring:
profiles: native
cloud:
config:
server:
native:
search-locations: file://${user.home}/config-repo
9.2. 실행 시 프로필 지정
java -jar -Dspring.profiles.active=native config-service.jar
10. Spring Cloud Bus 개요
10.1. Spring Cloud Bus란?
Spring Cloud Bus는 분산 시스템의 노드들을 경량 메시지 브로커(RabbitMQ, Kafka)와 연결하여 상태 변경 등의 이벤트를 전파할 수 있게 해주는 프로젝트다. Config Server와 연동하면 설정 변경 사항을 모든 마이크로서비스에 동시에 전파할 수 있다.
10.2. Spring Cloud Bus의 필요성
앞서 Actuator의 /refresh 엔드포인트를 사용하면 개별 서비스의 설정을 동적으로 리프레시할 수 있었다. 하지만 서비스가 수십, 수백 개로 늘어나면 각 서비스마다 일일이 POST 요청을 보내는 것은 현실적으로 불가능하다. Spring Cloud Bus는 설정 변경 이벤트를 모든 서비스에 자동으로 전파하여 이 문제를 해결한다.
11. RabbitMQ 설치
11.1. Docker로 RabbitMQ 설치
# RabbitMQ 이미지 pull
docker pull rabbitmq:3-management
# RabbitMQ 컨테이너 실행
docker run -d --name rabbitmq \\
-p 5672:5672 \\
-p 15672:15672 \\
rabbitmq:3-management
- 5672: AMQP 프로토콜 포트 (애플리케이션 통신용)
- 15672: 관리 콘솔 웹 포트
11.2. MacOS에서 Homebrew로 설치
# RabbitMQ 설치
brew install rabbitmq
# RabbitMQ 서비스 시작
brew services start rabbitmq
11.3. Windows에서 설치
- Erlang 설치
- RabbitMQ 설치
- RabbitMQ Service Manager에서 서비스 시작
11.4. RabbitMQ 관리 콘솔 접속
브라우저에서 http://localhost:15672에 접속한다.
- 기본 아이디: guest
- 기본 비밀번호: guest
12. AMQP 사용
12.1. 의존성 추가
build.gradle (각 마이크로서비스)
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
12.2. RabbitMQ 연결 설정
application.yml (각 마이크로서비스)
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
management:
endpoints:
web:
exposure:
include: busrefresh, health, info
13. Spring Cloud Bus 테스트
13.1. Config Server에 Bus 의존성 추가
build.gradle (config-service)
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp'
// 기존 의존성...
}
13.2. 전체 서비스 실행
- RabbitMQ 실행
- Eureka Server 실행
- Config Server 실행
- User Service 실행 (2개 이상의 인스턴스)
- Catalog Service 실행
- Order Service 실행
- API Gateway 실행
13.3. 설정 변경 및 전파 테스트
- Git 저장소의 설정 파일을 변경한다.
# user-service.yml token: expiration_time: 21600000 # 6시간으로 변경 - Git에 커밋한다.
- Config Server의 /busrefresh 엔드포인트로 POST 요청을 보낸다.
POST <http://localhost:8888/actuator/busrefresh> Content-Type: application/json - 모든 User Service 인스턴스에서 설정이 변경되었는지 확인한다.
# 첫 번째 인스턴스 GET <http://localhost>:{포트1}/users/token-info # 두 번째 인스턴스 GET <http://localhost>:{포트2}/users/token-info
13.4. 특정 서비스만 리프레시
모든 서비스가 아닌 특정 서비스만 리프레시하려면 destination 파라미터를 사용한다.
# user-service만 리프레시
POST <http://localhost:8888/actuator/busrefresh?destination=user-service>
# 특정 인스턴스만 리프레시
POST <http://localhost:8888/actuator/busrefresh?destination=user-service:8081>
14. 설정 정보의 암호화 처리
14.1. 대칭키와 비대칭키
대칭키(Symmetric Key): 암호화와 복호화에 같은 키를 사용하는 방식이다. 속도가 빠르지만 키 관리가 어렵다.
비대칭키(Asymmetric Key): 공개키와 개인키 쌍을 사용하는 방식이다. 공개키로 암호화하고 개인키로 복호화한다. 키 관리가 용이하지만 속도가 느리다.
14.2. 대칭키를 이용한 암호화
1. Config Server에 암호화 키 설정
bootstrap.yml (config-service)
encrypt:
key: my-symmetric-encryption-key
2. 암호화된 값 생성
Config Server의 /encrypt 엔드포인트를 사용하여 값을 암호화한다.
POST <http://localhost:8888/encrypt>
Content-Type: text/plain
db_password_value
응답으로 암호화된 문자열이 반환된다:
9f86d081884c7d659c2f7a1c77c7f8b8c9e7a3b2...
3. 설정 파일에 암호화된 값 사용
# user-service.yml
spring:
datasource:
password: '{cipher}9f86d081884c7d659c2f7a1c77c7f8b8c9e7a3b2...'
4. 복호화 테스트
POST <http://localhost:8888/decrypt>
Content-Type: text/plain
9f86d081884c7d659c2f7a1c77c7f8b8c9e7a3b2...
14.3. 비대칭키를 이용한 암호화
1. KeyStore 생성
# JKS(Java KeyStore) 생성
keytool -genkeypair -alias config-server-key \\
-keyalg RSA -dname "CN=Config Server,OU=Unit,O=Organization,L=City,S=State,C=KR" \\
-keypass secret -keystore config-server.jks -storepass secret
# 공개키 내보내기
keytool -export -alias config-server-key -keystore config-server.jks \\
-rfc -file public-key.txt -storepass secret
2. Config Server에 KeyStore 설정
bootstrap.yml (config-service)
encrypt:
key-store:
location: classpath:/config-server.jks
password: secret
alias: config-server-key
secret: secret
3. 암호화 및 복호화
대칭키 방식과 동일하게 /encrypt, /decrypt 엔드포인트를 사용한다.
15. 결론 및 모범 사례
15.1. 실무에서의 Spring Cloud Config 활용
환경별 설정 분리: 개발, 테스트, 운영 환경의 설정을 명확히 분리하여 관리한다.
보안 강화: 민감한 정보는 반드시 암호화하여 저장한다. 비대칭키 방식을 권장한다.
버전 관리: Git을 사용하여 설정 변경 이력을 추적하고, 필요시 롤백할 수 있도록 한다.
자동화: CI/CD 파이프라인에 설정 변경 및 전파 과정을 통합한다.
15.2. 모범 사례
- 설정 파일 명명 규칙: {application-name}-{profile}.yml 형식을 일관되게 사용한다.
- 공통 설정 분리: 모든 서비스에 공통으로 적용되는 설정은 application.yml 또는 common.yml으로 분리한다.
- 민감 정보 암호화: 데이터베이스 비밀번호, API 키 등은 반드시 암호화하여 저장한다.
- 설정 변경 감사: 설정 변경 시 누가, 언제, 무엇을 변경했는지 추적할 수 있는 체계를 마련한다.
- 무정지 배포: Spring Cloud Bus를 활용하여 설정 변경 시 서비스 중단 없이 적용한다.
15.3. 문제 해결 팁
Config Server 연결 실패 시 확인사항:
- Config Server가 실행 중인지 확인
- bootstrap.yml의 설정이 올바른지 확인
- 방화벽이나 네트워크 문제가 없는지 확인
설정이 리프레시되지 않을 때:
- @RefreshScope 어노테이션이 올바르게 적용되었는지 확인
- /busrefresh 엔드포인트 호출 후 충분한 시간이 지났는지 확인
- RabbitMQ 연결 상태 확인
암호화 관련 문제:
- JCE(Java Cryptography Extension) 무제한 강도 정책 파일이 설치되었는지 확인
- 키 저장소 비밀번호와 별칭이 올바른지 확인
다음 글에서는 마이크로서비스 간 통신 전략과 동기화 문제를 다룰 예정이다. RestTemplate, Feign Client를 활용한 동기식 통신과 메시지 큐를 활용한 비동기식 통신의 장단점을 비교하고, 데이터 일관성 유지를 위한 패턴들을 살펴보겠다.
'MSA > MSA 기본' 카테고리의 다른 글
| [BASIC #8] Kafka 기반 데이터 동기화 (0) | 2025.09.20 |
|---|---|
| [BASIC #7] Microservice 간 통신 전략 (0) | 2025.09.20 |
| [BASIC #5] 인증 처리와 JWT 기반 보안 (0) | 2025.09.20 |
| [BASIC #4][실습] E-commerce MSA 프로젝트 구조 설계 (0) | 2025.09.20 |
| [BASIC #3] API Gateway (0) | 2025.09.20 |
