๐ค ์คํ๋ง ํด๋ผ์ฐ๋ ์๋ฆฌ์ฆ!
์คํ๋ง ํด๋ผ์ฐ๋ ๊ฒ์ดํธ์จ์ด๋ฅผ ์ธํ ํ๊ณ ํ์ธํด๋ณด์
์ด ๊ธ์ ํด๋น ๊ธ๊ณผ ์ด์ด์ง๋ ๊ธ์ ๋๋ค. https://velog.io/@borab/Spring-Cloud-Eureka
spring cloud gateway๋ api gateway์ด๋ค. api gateway๋ Client(vue, react๋ก ๋ front-end) ์ back-end(springboot) ๋ฅผ ์ฐ๋ํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๊ฒ๋ก spring cloud gateway ๋ง๊ณ Zuul, Zuul2 ๊ฐ ์๋ค.
ํ์ง๋ง Zuul ๊ฐ์ ๊ฒฝ์ฐ Blocking ๋ฐฉ์์ผ๋ก ๋น๋๊ธฐ๋ฅผ ์ง์ํ์ง ์์ ์๋ต์ ๊ธฐ๋ค๋ฆฐ๋ค๋ ๋จ์ ์ด ์๊ณ , Zuul2๋ ์ด๋ฅผ ๊ฐ์ ํ์ฌ non-blocking ๋ฐฉ์์ผ๋ก ์ง์์ ํ์ง๋ง ์คํ๋ง ํด๋ผ์ฐ๋์ ์ผ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํธํ์ด ๋์ง ์์๋ค.
๊ทธ๋์ ์ต๊ทผ๋ค์ด์๋ spring cloud gateway๊ฐ ์ฃผ๋ชฉ์ ๋ฐ๊ธฐ ์์ํ๊ณ ํ ์ด ํ๋ก์ ํธ์์๋ Spring Cloud Gateway๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ๋ค.
๊ทธ๋ผ ์ ํํ api ๊ฒ์ดํธ์จ์ด๋ ๋ฌด์์ผ๊น?
api gateway๋ Application Programing Interface Gateway๋ก ์์ ์ธ๊ธํ๋ฏ์ด
- Client์ Springboot(back-end) ์๋น์ค๋ฅผ ์ฐ๋
์ธ์๋
- ๋ณด์ ์ธ์ฆ/์ธ๊ฐ
- ์ผ์ ๋ ์ด์์ ์์ฒญ ์ ํ
- ์๋ต ์บ์ฑ
- L/B Routing : ๋ผ์ฐํ , ํํฐ๋ง ํ๋ก์
- Logging
- Circuit Break
๋ก๋ ์ฐ์ธ๋ค.
๊ฐ์ธ์ ์ผ๋ก ์ค๋ํฌ์ธํธ ๋ผ๋ ๋จ์ด๊ฐ spring cloud gateway ๊ฐ ์ง์ํ๋ api gateway ๋ฐฉ์์ ๊ฐ์ฅ ์ ํํํ๋ ๋จ์ด๋ผ๊ณ ์๊ฐํ๋๋ฐ, ์์ฒญ์ ํ ๊ณณ์ ๋ชจ์ ๋ผ์ด๋๋ก๋น ๋ฐฉ์์ผ๋ก ์ ํฉํ ์๋น์ค์ ๋ผ์ฐํ ํด์ฃผ๊ณ , ์์ฒญ์ด ๋ง์ด ์ฌ ๊ฒฝ์ฐ ํธ๋ํฝ์ ์ ํํ๋ค. ๋ํ ๋ฐ๋ณต๋๋ ๋ก์ง์ ์บ์ฑ์ฒ๋ฆฌํ๊ฑฐ๋, ๊ณตํต์ผ๋ก ์ด ์ค๋ํฌ์ธํธ์ ์์ฒญ์ด ๋ค์ด์ ๋ก๊น ์ฒ๋ฆฌ๋ ์์ํ๋ค.
Spring Cloud Gateway ๊ฐ์ ๊ฒฝ์ฐ
Predicates
์Filter
๊ตฌ์ฑ๋์ด ๋์ํ๋ค.
predicate
์์ญ์ ์กฐ๊ฑด์ ์ฃผ์ด์ ํด๋น ๊ฒฝ๋ก๊ฐ ๋ง๋์ง ํ์ธํ๊ณ ๋ง์ผ๋ฉด ํํฐ๋ก ์์ฒญ์ ์ ์กํ๊ณ
filter
์์ฒญ์ ์ํํ๋ค.
https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-gateway/3.0.5
// config
implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-config', version: '3.0.5'
// eureka
implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-eureka-client', version: '3.0.4'
// gateway
implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-gateway', version: '3.0.5'
์์ ๋ฉํฐ๋ชจ๋๋ก ์์ฑํ์๊ธฐ ๋๋ฌธ์ ์ต์์ ๋ฃจํธ build.gradle์๋ ๋กฌ๋ณต ์ค์ ๋ ์ด์ฏค์ถ๊ฐํด์ค๋ค.
// lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
server:
port: 8000
spring:
main:
web-application-type: reactive
cloud:
gateway:
routes:
- id: api-member
uri: http://localhost:9090
predicates:
- Path=/member/**
- id: api-order
uri: http://localhost:9091
predicates:
- Path=/order/**
- id: api-product
uri: http://localhost:9092
predicates:
- Path=/product/**
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
healthcheck:
enabled: true
spring.cloud.gateway.routes
์ ์คํ๋ง ํด๋ผ์ฐ๋ ๊ฒ์ดํธ์จ์ด๊ฐ ํน์ ์กฐ๊ฑด์ ์์ฒญ์ด ๋ค์ด ์ฌ๋ ์ด๋๋ก ๋ผ์ฐํ
ํ ์ง์ ๋ํ ์ค์ ์ด๋ค.spring.cloud.gateway.routes.id
: ๋ผ์ฐํ
ํ ์คํ๋ง ๋ถํธ ์ ํ๋ฆฌ์ผ์ด์
๋ช
์ด๋ค.spring.cloud.gateway.routes.uri
: ๋ผ์ฐํ
ํ ์ฃผ์์ด๋คspring.cloud.gateway.routes.predicates
: ๋ผ์ฐํ
์กฐ๊ฑด์ด๋ค. ์ด ์กฐ๊ฑด์ด ๋ค์ด์ค๋ฉด ํด๋น uri๋ก ์์ฒญ์ด ๋ณด๋ด์ง๋ค.spring:
application:
name: api-gateway
cloud:
config:
uri: http://localhost:8888
์์ ๋ผ์ฐํ
์ค์ ์ ํ์ธ ํด๋ณด๋ฉด member ์๋น์ค๋ฅผ ํธ์ถํ ๋ localhost:9090/member
๋ผ๋ ๊ฒฝ๋ก๋ก ๊ฐ๊ฒ ์ธํ
๋์ด ์๋ค.
๋ฐ๋ผ์ /member
๋ ๋ํดํธ ์ค์ ์ผ๋ก ๋ณํ์ง ์๋๋ค
์ด๋ฐ ๊ฒฝ์ฐ์๋
server.servlet.context-path
์ค์ ์ ์ถ๊ฐํด ์๋ฒ ํฌํธ ์ฒ๋ผ ๊ณ ์ ์ผ๋ก ์ง์ ํด์ฃผ์!
๊ธฐ์กด์ ํฌํธ๋ฅผ ์ค์ ํ๋ yml ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ด ๋ด์ฉ์ ์ถ๊ฐํด์ฃผ์๋ค.
server:
port: 9090
servlet:
context-path: /member
์ด์ gateway๋ก ์ง์ ํ ์๋น์ค๋ฅผ ํธ์ถํด๋ณด์
gateway๋ 8000 ๋ฒ์ผ๋ก ๋์ ๋ค. ๊ทธ๋์ localhost:8000/member/config/profile ๋ผ๋ ๊ฒ์ผ๋ก ๋ฉค๋ฒ์ ์๋ api ๋ฅผ ํธ์ถํ์๋ค.
์ ์๋ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ค๋ฅธ ์๋น์ค์๋ ๊ฐ์ ์ค์ ์ ํด์ฃผ๊ณ ํธ์ถํด๋ดค๋ค.
localhost:8000 ์ผ๋ก ๋ชจ๋ ์๋น์ค๋ฅผ ํธ์ถํ ์ ์๋ค.
front ์์ ํ์ํ api๋ฅผ ํธ์ถํ๊ณ ์ถ์ผ๋ฉด spring cloud gateway ๋ผ๋ ๊ณตํต์ ์๋น์ค๋ฅผ ํธ์ถํ์ฌ spring cloud gateway ๊ฐ ์๋ง๋ ๊ณณ์ผ๋ก ์์ฒญ์ ๋ณด๋ด์ฃผ๊ณ ๊ฐ์ ๋ฐํ ํด์ค๋ค.
์ ๋ ์นด์๋ ์ ํ์ธ๋๋ค.
https://cloud.spring.io/spring-cloud-netflix/multi/multi__service_discovery_eureka_clients.html
์์ ์ ๋ ์นด๋ ์ ํ๋ฒํธ๋ถ ๊ฐ์ ์กด์ฌ๋ก spring cloud gateway์์ ์ฐธ๊ณ ํด์ ๊ฐ์ด ์ฐ์ธ๋ค. ์ด๋ฒ์๋ ์ ๋ ์นด๋ฅผ ์ฐ๋ํด๋ณด์!.
์์ ์ ๋ ์นด ์ฌ์ง์ ๋ณด๋ฉด ๊ฐ ์๋น์ค์ ์ด๋ฆ์ด ๋์ค๋๋ฐ ๊ทธ๊ฒ์ lb://{์๋น์ค} ๋ผ๊ณ ๋ณ๊ฒฝํ๋ค.
spring:
main:
web-application-type: reactive
cloud:
gateway:
routes:
- id: api-member
uri: lb://API-MEMBER
predicates:
- Path=/member/**
- id: api-order
uri: lb://API-ORDER
predicates:
- Path=/order/**
- id: api-product
uri: lb://API-PRODUCT
predicates:
- Path=/product/**
์ด๋ ๊ฒ ๋ณํํ๋ฉด cloud ๊ตฌ์กฐ์์ ์๋ฒ์ ip ๊ฐ ๋ณ๊ฒฝ๋๋ ์๋น์ค ์ด๋ฆ์ ์ฐพ์๊ฐ๋ค.
์๊น ์ธ๊ธํ๋ ๊ฒ์ฒ๋ผ api-gateway๋ ํํฐ๋ฅผ ์ ์ฉํ๋ค. ๊ทธ๋์ ์ด๋ฒ์๋ ์ด ๊ธ๋ก๋ฒ ํํฐ๋ก ์คํ๋ง ํด๋ผ์ฐ๋ ๊ฒ์ดํธ์จ์ด๋ก ๋ค์ด์จ ์์ฒญ์ด ์ด๋๋ก ์จ ์์ฒญ์ด๊ณ , status code๋ฅผ ํ์ธํ๋ ๊ฐ๋จํ ๋ก๊น ์ค์ ์ ์ถ๊ฐํด๋ณด์!
๋จผ์ api-gateway ์ ํ๋ฆฌ์ผ์ด์ ์ application.yml ํ์ผ์ ๋ค์์ ์ค์ ์ ์ถ๊ฐํด์คฌ๋ค.
application.yml
spring:
cloud:
gateway:
default-filters:
- name: GlobalFilter
args:
preLogger: true
postLogger: true
spring.cloud.gateway.default-filters
๋ ๊ธ๋ก๋ฒ ํํฐ๋ฅผ ์ถ๊ฐํ๋ ์ค์ ์ด๋ค.-name
: ํํฐ ์ด๋ฆ์ ๋ฃ์ด์ค๋ค.args
: ์ฌ์ฉํ ๊ทธ๋ฆฌ๊ณ ๋ค์ ์์ค์ฝ๋๋ฅผ ์ถ๊ฐํด์ฃผ์๋ค.
@Component
@Slf4j
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
public GlobalFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> {
ServerHttpRequest req = exchange.getRequest();
ServerHttpResponse res = exchange.getResponse();
if (config.isPreLogger()) {
log.info("[REQ ID : {}][REQUEST PATH] -> {}", req.getId(), req.getPath());
if (!req.getQueryParams().isEmpty()) {
log.info("[REQ ID : {}][REQUEST PATH] -> {}", req.getId(), req.getPath());
}
}
req.getQueryParams();
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
if (config.isPostLogger()) {
log.info("[REQ ID : {}][RESPONSE STATUS CODE] -> {}", req.getId(), res.getStatusCode());
}
}));
});
}
@Data
public static class Config {
private boolean preLogger;
private boolean postLogger;
}
}
๊ทธ๋ฆฌ๊ณ ํ๋ฉด์ ํธ์ถํด๋ณด์
์ด๋ ๊ฒ ๋ก๊ทธ๊ฐ ์ฐํ๋ ๊ฒ๋ ํ์ธํ ์ ์๋ค.
spring cloud gateway๋ ์ด๋ ๊ฒ ๋ก๊ทธ๋ฅผ ๊ณตํต์ผ๋ก ๊ด๋ฆฌํด์ค ๋ฟ๋ง์๋๋ผ ๋ผ์ฐํ ๊ธฐ๋ฅ๋ ์ ๊ณตํด์ค๋ค.
๋ํ ์ฌ๊ธฐ์์๋ ๋ค๋ฃจ์ง ์์์ง๋ง ๋ค์์ ๋ค๋ฃฐ filter๋ฅผ ์ฌ์ฉํด์ ๊ณตํต์ผ๋ก ์ธ์ , ํ ํฐ ๊ด๋ฆฌ๋ฅผ ์ ๊ณตํด์ฃผ์ด ์ธ์ฆ ์ธ๊ฐ์ ๋ํ ๋ฉ์ปค๋์ฆ๋ ๊ตฌํํ ์ ์๊ณ , ์ํท๋ธ๋ ์ด์ปค์ ๋ํ ๊ธฐ๋ฅ๋ ๋ค๋ฃฐ์ ์๋ค.
์์ธํ ๊ฒ์ ์ดํ spring cloud gateway 2์์ ๋ค๋ค๋ณด์