
Today I Learn ✍🏼
- 오늘 하루 가장 인상 깊었던 배움에는 뭐가 있었지?
MSA를 자주 들었었다. 실제로 공부한 것은 이번이 처음이다.
MONO 방식은 한 서버에 여러 서비스가 들어가 있다. 그래서 하나를 수정하더라도 전체를 수정, 배포해야 한다.
내가 다녔던 회사들도 MSA 아키텍쳐로 만들어져 있다.
서비스 디스커버리:
Eureka에 각각의 서비스 서버를 등록, 관리 해준다. Health Check도 가능하다.
클라이언트 사이드 로드밸런싱:
FeignClient(REST API 호출), Ribbon(로드밸런싱)은 Eureka에 등록된 서버의 이름으로 호출하는 것을 깨닫고는 과거에 업무에서 이렇게 적용했으면 좋았을 걸! 깨달음이 있었다.
서킷 브레이커:
Resilience4j 는 장애를 처리해준다. 장애 데이터를 가지고 Prometheus는 데이터를 수집하고 Grafana는 시각화 해준다.
API GATEWAY:
Spring Cloud Gateway 클라이언트 요청을 라우팅한다. 보안, 로깅, 필터를 적용할 수 있다.
보안 서버 분리:
OAuth2(Spring Security), JWT가 있는 서버를 따로 띄운다. Spring Cloud Gateway에서 필터를 사용해서 인증, 인가가 필요한 경우 인증 서버에게 요청하면 된다.
Config 서버 분리:
Spring Cloud Config 변경 사항을 실시간으로 반영할 수 있다. Config 서버를 따로 띄어서 정적 파일이 수정되었을 때, 알림과 대체 application.properties를 적용할 수 있다. refresh api를 만들때 적용하면 좋을 듯 하다.
- 그 배움까지 다가가는데 어떤 어려움이 있었지?
처음 접하는 MSA라서 좀 졸렸다. 강의 설명이 잘 되어있고 실습 위주라 흐름을 이해하는 데 예상보다 수월했다.
- 그 어려움을 해결하기 위한 나의 시도들은 무엇이 있었지?
전체적으로 강의를 들으면서 전체 흐름을 깨달으려고 했다. 그리고 PDF파일을 다운받아서 메모를 하면서 강의를 들었다.
필기는 이해력을 높인다.
- 그 과정에서 나는 무엇을 깨달았고, 어떤 감정/생각이 들었었지?
예전에 회사에서 했던 업무가 생각이 났다. MSA 공부하면서 해결 방법을 찾았다.
따라서 개발자는 계속해서 공부해야 더 나은 개발을 할 수 있다고 확신했다.
사례)
Dynamo DB에서 조회한 데이터를 서버에 캐시 해놓음
admin에서 short url을 수정 요청 시, 수정 요청 서버가 아닌
다른 서버의 캐시 데이터를 일치 시키기 위해 모든 서버 캐시 refresh하려고 refresh api를 호출해야 했다.
properties에다가 캐시 서버 ip를 넣어두고 for문 돌려 refresh api를 요청 했었는데
시니어가 다른 방법이 없을지 고민하라고 했었다.
Eureka(서버들을 등록, 관리, 헬스체크)와 연동하여
클라이언트 사이드 로드 밸런싱: FeignClient(REST API 호출), Ribbon(로드밸런싱)
Eureka에 등록된 서버들을 호출 할 수 있다.
서비스 디스커버리(Eureka)는 서버 등록, 관리, 헬스 체크를 해준다.
광고회사에 첫 입사 했을 때 모든 서버의 정보를 볼 수 있는 페이지를 받았을 때가 떠오른다.
이렇게 만들었겠구나를 깨달았다. 너무나도 쉬웠다.
- 이 상태에서 이후 더 나은 내가 되려면 무엇을 보완하지?
직접 만들어보면 된다.
MSA
MSA(Microservices Architecture)는 소프트웨어 시스템을 여러 개의 독립적이고 자율적으로 배포 가능한 서비스로 나누어 개발하는 아키텍처 스타일을 말합니다. 이 아키텍처는 각 서비스가 독립적으로 개발, 배포, 확장, 유지보수될 수 있도록 설계되어 있습니다. MSA는 대규모 애플리케이션을 더 작은, 관리 가능한 모듈로 분리하여 개발의 민첩성, 확장성, 안정성을 높이기 위해 사용됩니다.
주요 특징:
- 독립적인 배포:
- 각 서비스는 독립적으로 배포될 수 있으며, 특정 서비스만 수정하거나 재배포할 수 있습니다.
- 이로 인해 서비스 간의 의존성이 줄어들고, 더 빠르고 자주 배포할 수 있습니다.
- 서비스 경계:
- 서비스 간의 통신은 주로 HTTP/REST, 메시지 큐 등을 통해 이루어지며, 서비스들은 네트워크를 통해 서로 상호작용합니다.
- 각 서비스는 데이터 저장소를 독립적으로 가질 수 있고, 다른 서비스와 공유하지 않는 것이 원칙입니다.
- 자율적인 개발:
- 각 서비스는 별도의 팀에 의해 독립적으로 개발될 수 있으며, 서로 다른 기술 스택, 언어, 프레임워크를 사용할 수 있습니다.
- 팀들은 독립적으로 작업할 수 있으므로, 더 빠른 개발 주기와 효율적인 작업이 가능합니다.
- 확장성:
- 특정 서비스의 트래픽이 증가하면 해당 서비스만 독립적으로 확장(수평적 확장)할 수 있습니다.
- 이로 인해 시스템 전체를 확장하지 않고도 효율적으로 리소스를 사용할 수 있습니다.
- 장애 격리:
- 한 서비스에 문제가 발생하더라도, 다른 서비스에 미치는 영향이 최소화됩니다.
- 이로 인해 시스템의 전체적인 안정성이 높아집니다.
장점:
- 유연성: 새로운 기술을 개별 서비스에 적용하기가 쉬워집니다.
- 빠른 배포 주기: 작은 서비스 단위로 개발하고 배포할 수 있으므로 빠른 릴리즈가 가능합니다.
- 스케일링: 개별 서비스 단위로 확장할 수 있어 비용 효율적입니다.
- 유지보수 용이성: 코드베이스가 작고 이해하기 쉬워 유지보수가 용이합니다.
단점:
- 복잡성 증가: 서비스 간 통신, 데이터 일관성 관리, 배포 관리 등에서 복잡성이 증가합니다.
- 운영 오버헤드: 여러 서비스에 대한 모니터링, 로깅, 배포 파이프라인 등 관리해야 할 요소가 많아집니다.
- 네트워크 지연: 서비스 간의 네트워크 통신이 많아지면 지연이 발생할 수 있습니다.
MSA의 사용 사례:
- 대규모 애플리케이션에서 특정 모듈의 독립적 개발, 배포가 필요할 때.
- 특정 부분의 트래픽 증가로 인해 개별 모듈만 확장할 필요가 있을 때.
- 다양한 기술 스택을 사용하여 유연한 개발 환경을 조성하고자 할 때.
MSA는 대규모 애플리케이션을 더 효율적으로 관리하고 운영할 수 있는 방식이지만, 그만큼 초기 설계와 운영의 복잡성을 충분히 고려해야 합니다.
서비스 디스커버리(Service Discovery)
여러 개의 독립적인 서비스들이 서로를 동적으로 찾고 통신할 수 있도록 돕습니다.
서비스 디스커버리는 마이크로서비스 환경에서 개별 서비스들이 서로의 위치(예: IP 주소와 포트)를 동적으로 발견하고 통신할 수 있게 하는 메커니즘입니다. MSA에서는 서비스들이 독립적으로 배포되고, 각 서비스의 인스턴스가 자동으로 확장되거나 축소될 수 있으므로, 서비스의 위치가 고정되지 않습니다. 서비스 디스커버리는 이 문제를 해결하기 위해 사용됩니다.
서비스 디스커버리는 두 가지 방식으로 구현될 수 있습니다:
- 클라이언트 사이드 디스커버리(Client-Side Discovery):
- 서비스 소비자가 직접 서비스 레지스트리에서 필요한 서비스의 위치를 조회하고, 그 위치로 요청을 보냅니다.
- 클라이언트 라이브러리가 이 기능을 구현하며, 넷플릭스의 Eureka와 같은 레지스트리를 사용합니다.
- 서버 사이드 디스커버리(Server-Side Discovery):
- 서비스 소비자가 직접 서비스 레지스트리에서 조회하는 것이 아니라, API 게이트웨이나 로드 밸런서가 이를 대신해줍니다.
- 소비자는 API 게이트웨이로 요청을 보내고, 게이트웨이가 적절한 서비스 인스턴스를 찾아 요청을 전달합니다.
Eureka
Eureka는 넷플릭스가 개발한 서비스 디스커버리 서버이자 클라이언트 라이브러리입니다. 이는 Spring Cloud Netflix 프로젝트의 일부로 통합되어 널리 사용됩니다.
Eureka의 주요 구성 요소:
- Eureka Server:
- 모든 마이크로서비스 인스턴스가 자신의 위치와 상태를 등록하는 중앙 레지스트리입니다.
- 이 서버는 서비스 인스턴스들을 모니터링하고, 새로운 인스턴스의 등록이나 기존 인스턴스의 제거를 관리합니다.
- Eureka Client:
- 마이크로서비스 인스턴스에서 동작하며, 주기적으로 자신을 Eureka 서버에 등록합니다.
- 필요한 다른 서비스의 위치를 Eureka 서버에서 조회하여 통신을 할 수 있습니다.
Eureka의 작동 방식:
- 서비스 등록(Service Registration):
- 서비스 인스턴스가 시작되면, Eureka 클라이언트는 Eureka 서버에 자신을 등록합니다. 이 과정에서 서비스의 ID, IP 주소, 포트 등의 정보가 제공됩니다.
- 서비스 조회(Service Discovery):
- 서비스가 다른 서비스에 요청을 보내야 할 때, Eureka 클라이언트를 통해 Eureka 서버에서 필요한 서비스의 위치를 조회합니다.
- 상태 갱신(Heartbeat):
- Eureka 클라이언트는 정기적으로 자신의 상태를 Eureka 서버에 보고합니다. 만약 일정 시간 동안 상태 보고가 없으면, 해당 서비스 인스턴스는 레지스트리에서 제거됩니다.
- 로드 밸런싱:
- Eureka 클라이언트는 여러 인스턴스가 존재하는 서비스의 경우, 로드 밸런싱 전략을 사용하여 요청을 특정 인스턴스로 라우팅할 수 있습니다.
요약
- 서비스 디스커버리는 MSA 환경에서 서비스가 동적으로 서로를 찾을 수 있도록 하는 메커니즘입니다.
- Eureka는 넷플릭스가 개발한 서비스 디스커버리 도구로, 클라이언트와 서버가 함께 작동하여 마이크로서비스들의 위치를 관리하고, 서비스 간 통신을 가능하게 합니다.
클라이언트 사이드 로드밸런싱(Client-Side Load Balancing)
클라이언트 사이드 로드밸런싱은 클라이언트가 서버의 여러 인스턴스 중 하나를 선택하여 요청을 보내는 방식입니다. 클라이언트가 직접 로드밸런싱을 수행하며, 이는 클라이언트가 서버의 위치 정보(예: IP 주소와 포트)를 알고 있어야 가능합니다. 클라이언트는 이 정보를 기반으로 로드밸런싱 알고리즘(예: 라운드 로빈, 랜덤, 가중치 기반 등)을 사용해 요청을 특정 서버 인스턴스로 라우팅합니다.
이 방식의 장점은 로드밸런서가 클라이언트와 서버 사이에 별도로 존재하지 않으므로, 네트워크 오버헤드가 줄어든다는 것입니다. 그러나 클라이언트가 서버의 위치 정보를 직접 관리해야 하므로, 서비스 디스커버리와 함께 사용됩니다.
Ribbon
Ribbon은 넷플릭스가 개발한 클라이언트 사이드 로드밸런서입니다. Spring Cloud Netflix 프로젝트에서 널리 사용되며, Ribbon은 서비스의 여러 인스턴스 중에서 하나를 선택하여 요청을 라우팅합니다.
Ribbon의 주요 기능:
- 로드밸런싱 알고리즘: 기본적으로 라운드 로빈 방식으로 로드밸런싱을 수행하지만, 커스터마이징이 가능합니다.
- 서비스 디스커버리 통합: Eureka와 같은 서비스 디스커버리 도구와 통합되어, 서비스 인스턴스의 위치를 동적으로 조회하고 관리할 수 있습니다.
- 실패 처리와 회로 차단: 특정 서버 인스턴스가 응답하지 않으면, 자동으로 다른 인스턴스로 요청을 시도합니다.
Ribbon은 Spring Cloud에서 기본적으로 RestTemplate이나 FeignClient와 함께 사용되어, 서비스 간의 호출을 단순화합니다.
FeignClient
Feign은 선언적으로 HTTP 클라이언트를 작성할 수 있게 해주는 넷플릭스 라이브러리입니다. FeignClient를 사용하면, 간단한 인터페이스 정의를 통해 RESTful 서비스와 통신할 수 있습니다. Spring Cloud에서는 Feign과 Ribbon이 통합되어, Feign을 사용하는 클라이언트가 자동으로 클라이언트 사이드 로드밸런싱을 지원할 수 있습니다.
주요 특징:
- 선언적 HTTP 요청: 메소드와 어노테이션을 사용하여 HTTP 요청을 선언적으로 작성할 수 있습니다.
- 로드밸런싱 지원: Ribbon과 통합되어, FeignClient를 통해 보낸 요청이 여러 서버 인스턴스 중 하나로 자동 라우팅됩니다.
- 서비스 디스커버리 통합: Eureka와 같은 서비스 디스커버리 도구와 연동되어, 동적으로 서비스의 위치를 조회합니다.
@FeignClient(name = "user-service")
public interface UserServiceClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
// UserServiceClient는 user-service라는 이름의 서비스와 통신
// Ribbon을 통해 여러 인스턴스 중 하나로 요청 전달
요약
- 클라이언트 사이드 로드밸런싱은 클라이언트가 여러 서버 인스턴스 중 하나를 선택하여 요청을 보내는 방식입니다.
- Ribbon은 넷플릭스가 개발한 클라이언트 사이드 로드밸런서로, 서비스 인스턴스 간 로드밸런싱을 지원합니다.
- FeignClient는 선언적으로 HTTP 클라이언트를 작성할 수 있는 라이브러리로, Ribbon과 통합되어 클라이언트 사이드 로드밸런싱을 자동으로 수행합니다.
서킷 브레이커 (Circuit Breaker)
서킷 브레이커 패턴은 소프트웨어 시스템에서 장애 전파를 막기 위한 안정성 패턴입니다. 이 패턴은 실패 가능성이 높은 외부 서비스나 컴포넌트에 대한 호출을 관리합니다. 만약 서비스 호출이 반복적으로 실패하면 서킷 브레이커는 회로를 열어서 추가적인 호출이 수행되지 않도록 차단합니다. 이후 일정 시간이 지나면 다시 회로를 닫아 호출을 허용합니다.
서킷 브레이커의 주요 상태:
- Closed: 모든 요청을 정상적으로 전달합니다. 실패율이 일정 수준 이상으로 높아지면 Open 상태로 전환합니다.
- Open: 모든 요청을 차단하고 즉시 실패 응답을 반환합니다. 일정 시간이 지나면 Half-Open 상태로 전환합니다.
- Half-Open: 일부 요청을 전달해보고, 성공하면 Closed 상태로 돌아가고 실패하면 다시 Open 상태로 전환합니다.
서킷 브레이커는 마이크로서비스의 신뢰성을 높이고, 외부 서비스의 장애로 인한 전체 시스템의 문제를 방지하는 데 유용합니다.
Resilience4j
Resilience4j는 서킷 브레이커, 제한, 재시도, 버퍼링, 지연 등 다양한 탄력성 패턴을 제공하는 경량의 자바 라이브러리입니다. Resilience4j는 특히 서킷 브레이커 패턴을 쉽게 구현할 수 있도록 도와줍니다.
주요 특징:
- 서킷 브레이커: 외부 서비스나 컴포넌트에 대한 호출을 관리합니다.
- 재시도(Retry): 실패한 요청을 다시 시도할 수 있습니다.
- 제한(Rate Limiter): 일정 시간 동안의 요청 수를 제한합니다.
- 버퍼링(Bulkhead): 시스템 리소스를 보호하기 위해 격리된 스레드 풀을 사용합니다.
- 시간 제한(Time Limiter): 요청의 응답 시간을 제한합니다.
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendService");
Supplier<String> supplier = CircuitBreaker
.decorateSupplier(circuitBreaker, () -> backendService.call());
String result = Try.ofSupplier(supplier)
.recover(throwable -> "Hello from fallback")
.get();
Prometheus
Prometheus는 시스템 및 애플리케이션 성능 모니터링을 위한 오픈소스 도구입니다. 메트릭 수집, 저장, 쿼리, 그리고 경고 기능을 제공합니다. Prometheus는 주로 시계열 데이터를 수집하며, 다양한 서비스나 애플리케이션의 상태와 성능을 모니터링할 수 있습니다.
주요 특징:
- 시계열 데이터 저장: 주기적으로 데이터를 수집하고 시계열 데이터베이스에 저장합니다.
- 다양한 데이터 수집: HTTP 엔드포인트를 통해 메트릭을 수집하며, 다양한 애플리케이션과 인프라를 모니터링할 수 있습니다.
- 경고 및 알림: 조건을 설정하여 특정 상황이 발생할 경우 경고를 발송합니다.
- 쿼리 언어(PromQL): 수집된 데이터를 쿼리하여 시각화하거나 분석할 수 있습니다.
Grafana
Grafana는 데이터 시각화 도구로, Prometheus와 같은 모니터링 도구와 연동하여 수집된 데이터를 대시보드 형태로 시각화할 수 있습니다. 다양한 데이터 소스를 지원하며, 실시간 모니터링과 분석을 위한 강력한 시각화 기능을 제공합니다.
주요 특징:
- 다양한 데이터 소스 지원: Prometheus, InfluxDB, ElasticSearch 등 다양한 데이터 소스와 통합이 가능합니다.
- 대시보드 생성: 커스터마이징 가능한 대시보드를 생성하여 실시간 데이터 시각화를 할 수 있습니다.
- 경고 및 알림 설정: Grafana에서 경고를 설정하고 특정 조건에서 알림을 받을 수 있습니다.
- 플러그인 지원: 다양한 플러그인을 통해 기능을 확장할 수 있습니다.
요약
- 서킷 브레이커: 시스템의 안정성을 위해 외부 서비스 호출을 관리하는 패턴입니다.
- Resilience4j: 서킷 브레이커와 기타 탄력성 패턴을 구현할 수 있는 자바 라이브러리입니다.
- Prometheus: 시스템 및 애플리케이션의 성능 모니터링을 위한 오픈소스 도구입니다.
- Grafana: Prometheus와 같은 도구와 연동하여 데이터를 시각화하는 대시보드를 제공합니다.
Spring Cloud Gateway
Spring Cloud Gateway는 API Gateway를 구현하기 위한 Spring Cloud 프로젝트의 일부로, 마이크로서비스 아키텍처에서 여러 마이크로서비스의 진입점 역할을 합니다. API Gateway는 클라이언트와 서비스 간의 요청을 라우팅하고, 인증, 로깅, 모니터링, 필터링 등의 작업을 수행할 수 있는 중요한 구성 요소입니다.
특징:
- 라우팅:
- Spring Cloud Gateway는 클라이언트 요청을 다양한 마이크로서비스로 라우팅할 수 있습니다. 라우트는 경로, HTTP 메소드, 헤더 등의 조합으로 정의됩니다.
- 예를 들어, /api/orders/** 경로로 오는 요청을 orders-service라는 마이크로서비스로 라우팅할 수 있습니다.
- 필터:
- Spring Cloud Gateway는 전처리 및 후처리를 위한 필터 체인을 제공합니다. 이 필터는 요청 및 응답을 수정하거나, 로깅, 인증, 권한 부여, 로드 밸런싱 등의 작업을 수행할 수 있습니다.
- 필터는 사전 필터(pre-filter)와 사후 필터(post-filter)로 나뉘며, 사전 필터는 요청이 라우팅되기 전에, 사후 필터는 응답이 클라이언트로 반환되기 전에 실행됩니다.
- 로깅 및 모니터링:
- Spring Cloud Gateway는 각 요청에 대한 로깅과 모니터링 기능을 제공합니다. 이를 통해 애플리케이션의 성능을 추적하고, 문제를 진단하는 데 도움을 줄 수 있습니다.
- 로드 밸런싱:
- 내장된 로드 밸런싱 기능을 제공하여, 요청을 여러 인스턴스 간에 분배할 수 있습니다. 이는 Spring Cloud의 Ribbon이나 Spring Cloud LoadBalancer와 연동될 수 있습니다.
- 보안:
- Spring Cloud Gateway는 Spring Security와 통합되어 JWT(JSON Web Token) 인증, OAuth2 등 다양한 인증 및 권한 부여 방식을 쉽게 구현할 수 있습니다.
- 경로 재작성:
- 요청 URL을 재작성하여, 클라이언트에게는 일관된 API 구조를 제공하면서도 내부적으로는 다양한 마이크로서비스에 요청을 전달할 수 있습니다.
//Spring Cloud Gateway를 사용하여 간단한 라우팅을 설정
@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("order_service", r -> r.path("/api/orders/**")
.uri("lb://orders-service"))
.route("product_service", r -> r.path("/api/products/**")
.uri("lb://products-service"))
.build();
}
}
// /api/orders/** 경로로 들어오는 요청은 orders-service로 라우팅
// /api/products/** 경로로 들어오는 요청은 products-service로 라우팅
// lb:// 접두사는 Spring Cloud LoadBalancer를 통해 로드 밸런싱 수행
요약
Spring Cloud Gateway는 Spring 생태계와 잘 통합된 API Gateway 솔루션으로, 마이크로서비스 아키텍처에서 중요한 역할을 합니다. 라우팅, 필터링, 로드 밸런싱, 보안 등을 손쉽게 설정할 수 있어 클라이언트 요청을 효과적으로 관리하고 처리할 수 있습니다.
보안 서버 분리
OAuth2와 JWT(JSON Web Token)를 활용하여 보안 서버를 분리하고 인증 및 권한 관리를 구현하는 방식은 현대 웹 애플리케이션에서 널리 사용됩니다. Spring Security는 이러한 인증 및 권한 부여 방식을 쉽게 통합할 수 있는 강력한 보안 프레임워크입니다.
보안 서버 분리 개념
보안 서버 분리는 인증 서버와 리소스 서버를 분리하여 관리하는 아키텍처 패턴입니다.
- 인증 서버 (Authorization Server): 사용자의 인증을 처리하고, 인증된 사용자에게 토큰(JWT 등)을 발급합니다.
- 리소스 서버 (Resource Server): 보호된 리소스(API 등)에 접근할 때 인증 서버에서 발급한 토큰을 검증하고, 사용자에게 리소스를 제공합니다.
이러한 분리는 보안을 강화하고, 인증 관련 로직을 중앙 집중화하여 관리할 수 있게 합니다.
OAuth2와 JWT의 역할
- OAuth2: 사용자 인증 및 권한 부여를 위한 개방형 표준입니다. OAuth2를 통해 사용자는 권한 부여를 위한 액세스 토큰을 발급받습니다.
- JWT: JSON 형식의 토큰으로, 사용자의 인증 정보를 안전하게 전달하기 위한 용도로 사용됩니다. JWT는 자체적으로 서명되어 있어 토큰의 무결성을 보장합니다.
Spring Security와 OAuth2, JWT 통합
1. 인증 서버 구현 (Authorization Server): Spring Security의 spring-security-oauth2를 사용하여 인증 서버를 구축합니다. 이 서버는 클라이언트 자격 증명을 통해 OAuth2 토큰(JWT)을 발급합니다.
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client-id")
.secret(passwordEncoder().encode("client-secret"))
.authorizedGrantTypes("password", "refresh_token")
.scopes("read", "write");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter());
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("secret-key"); // 대칭 서명 키
return converter;
}
}
2. 리소스 서버 구현 (Resource Server): 리소스 서버는 보호된 리소스에 접근하기 위해 JWT 토큰을 검증합니다. Spring Security와 spring-security-oauth2-resource-server를 사용하여 구현할 수 있습니다.
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/public").permitAll()
.antMatchers("/api/secure").authenticated();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore());
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("secret-key"); // 인증 서버와 동일한 서명 키
return converter;
}
}
3. JWT를 사용한 통신: 클라이언트는 인증 서버에서 JWT를 발급받아 이를 헤더에 포함시켜 리소스 서버에 요청을 보냅니다. 리소스 서버는 JWT의 서명을 검증하고, 토큰이 유효하면 사용자의 요청을 처리합니다.
요약
- OAuth2는 인증 및 권한 부여를 처리하며, 사용자가 보호된 리소스에 접근할 수 있는 토큰을 발급합니다.
- JWT는 인증 정보를 안전하게 전달하고 검증하는 데 사용됩니다.
- Spring Security를 사용하여 OAuth2와 JWT 기반의 인증 서버와 리소스 서버를 쉽게 구성할 수 있습니다.
이 아키텍처는 보안 서버를 분리하여 관리함으로써 보안 및 확장성을 강화하고, 다양한 클라이언트에서 통합 인증을 관리할 수 있게 합니다.
Config 서버 분리
Spring Cloud Config는 마이크로서비스 아키텍처에서 중앙 집중화된 설정 관리를 제공하는 툴입니다. 이를 통해 애플리케이션의 설정 파일을 외부에서 관리하고, 설정의 변경 사항을 애플리케이션에 실시간으로 반영할 수 있습니다. Config 서버 분리는 이 Spring Cloud Config 서버를 통해 애플리케이션의 설정을 중앙에서 관리하고, 각 서비스가 이 Config 서버를 통해 설정을 가져오도록 하는 아키텍처 패턴입니다.
Spring Cloud Config의 주요 개념
- Config 서버 (Spring Cloud Config Server):
- 중앙 집중화된 설정 저장소 역할을 합니다.
- 설정 파일을 Git, SVN, 파일 시스템 등 다양한 저장소에 저장할 수 있으며, 애플리케이션은 이 Config 서버를 통해 설정을 읽습니다.
- 각 애플리케이션은 고유한 설정 파일을 가질 수 있으며, 공통 설정도 지원합니다.
- Config 클라이언트 (Spring Cloud Config Client):
- Config 서버로부터 설정을 읽어오는 애플리케이션입니다.
- bootstrap.properties 또는 bootstrap.yml 파일을 통해 Config 서버의 위치를 지정합니다.
- 설정 파일의 변경 사항을 실시간으로 반영하는 Spring Cloud Bus와 같은 툴과 통합할 수 있습니다.
Spring Cloud Config 서버 설정
Config 서버를 설정하기 위해서는 @EnableConfigServer 어노테이션을 사용합니다. 예를 들어, Git 저장소에서 설정 파일을 읽어오도록 설정할 수 있습니다.
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
application.yml 파일에 Git 저장소를 지정할 수 있습니다.
server:
port: 8888
spring:
cloud:
config:
server:
git:
uri: https://github.com/your-repo/config-repo
Spring Cloud Config 클라이언트 설정
애플리케이션이 Config 서버로부터 설정을 가져오도록 설정하려면, bootstrap.yml 파일에 Config 서버의 위치를 지정합니다.
spring:
application:
name: your-app-name
cloud:
config:
uri: http://localhost:8888
profile: dev
## 설정에 따라 애플리케이션은
## http://localhost:8888/your-app-name/dev에서 설정 가져옴
설정의 실시간 반영 (Spring Cloud Bus)
- Spring Cloud Bus를 사용하면 설정 파일의 변경 사항을 애플리케이션에 실시간으로 반영할 수 있습니다.
- Spring Cloud Bus는 메시지 브로커(Kafka, RabbitMQ 등)를 통해 구성된 애플리케이션 인스턴스에 변경 사항을 전파합니다.
- 이를 통해 설정 파일을 수정하고 별도의 재배포 없이 애플리케이션에 설정을 적용할 수 있습니다.
장점
- 중앙 집중화된 설정 관리: 여러 마이크로서비스의 설정을 한 곳에서 관리할 수 있어 일관성을 유지하고 관리 부담을 줄입니다.
- 환경별 설정 지원: 개발, 테스트, 운영 환경 등 다양한 환경에 맞춰 설정을 쉽게 관리할 수 있습니다.
- 실시간 설정 반영: 설정 파일의 변경 사항을 애플리케이션 재시작 없이도 반영할 수 있습니다.
요약
Spring Cloud Config는 분산 환경에서 설정 파일을 중앙 집중화하여 관리할 수 있도록 지원하는 툴입니다. Config 서버와 클라이언트를 통해 설정을 외부에서 관리하고, 필요에 따라 실시간으로 설정을 변경할 수 있습니다. 이를 통해 마이크로서비스의 설정 관리와 배포를 보다 효율적으로 수행할 수 있습니다.
'학습 기록 (Learning Logs) > Today I Learned' 카테고리의 다른 글
Map 함수 (0) | 2024.08.14 |
---|---|
Redis, Spring (0) | 2024.08.07 |
Spring Security? OAuth2 ? (0) | 2024.08.06 |
DI, IoC (0) | 2024.07.30 |
패키지 위치에 따라 다른 DB가 자동으로 연결되는 법 (0) | 2024.03.03 |