๐ค ์คํ๋ง ํด๋ผ์ฐ๋ ์๋ฆฌ์ฆ!
์คํ๋ง ํด๋ผ์ฐ๋ ๊ฒ์ดํธ์จ์ด๋ฅผ ์ธํ ํ๊ณ ํ์ธํด๋ณด์
์ด ๊ธ์ ํด๋น ๊ธ๊ณผ ์ด์ด์ง๋ ๊ธ์ ๋๋ค. 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์์ ๋ค๋ค๋ณด์