[Spring Cloud] - 5. Zuul Gateway๋ฅผ ์ด์ฉํ Filtering
์ด๋ฒ ํฌ์คํธ์์๋ Routing์ ์ด์ด์ Zuul Gateway๋ฅผ ์ด์ฉํ Filtering์ ๋ํด์ ์ด์ผ๊ธฐํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
Filtering
Spring Boot์์ ํํฐ๋ง์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ PreFilter์ ๊ฐ์ ์๋ํฌ์ธํธ์ ๋ณด์ ๋ฑ์์ ์ฌ์ฉ๋ฉ๋๋ค. ์ด ํํฐ๋ง์ ์ด์ฉํ๊ธฐ ์ํด์๋ Spring Security์์ ์ ๊ณตํ๋ JWT ๋ฑ์ ๋ณด์ ์๋จ์ ์ฌ์ฉํ์ฌ ์ธ์ฆ์ ๋ฐ๊ณ , ์๋ํฌ์ธํธ์ ์ ๊ทผํ๋ ๋ฐฉ์์ด์ฃ .
Zuul Gateway์์๋ ์๋ํฌ์ธํธ์ ๋ณด์์ ์ ์ฉํ ์ ์๋ ํํฐ๋ง ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.

์ ์ํคํ ์ฒ๋ Zuul Gateway์ ์ฝ์ด๋ฅผ ๊ทธ๋ฆฐ ์ํคํ ์ฒ์ ๋๋ค. Zuul Servlet์ ํตํด ๋ค์ด์ค๋ ์์ฒญ์ Routing ํ๊ฒ ๋๋๋ฐ์. ๊ทธ๋ฆฌ๊ณ , ๊ทธ ๋ฐ๋จ์๋ ZuulFilter Runner๊ฐ ์์ด์ ์๋ํฌ์ธํธ์ ๋ํ Filtering์ ์ ์ฉํ๊ฒ ๋ฉ๋๋ค.
Filtering์ ํด๋ผ์ด์ธํธ์ HTTP ์์ฒญ์ ๋ฐ๊ณ , ์๋ตํ๋ ๊ณผ์ ์์ ๋ผ์ฐํ ํ๋ ๋์์ ์ํํ๋ ์ก์  ์ค์ ํ๋์ด๋ค.
- Pre-Filter: API ์๋ฒ๋ก ๋ผ์ฐํ ๋๊ธฐ ์ ์ ์ํํ๋ ํํฐ -> ์ธ์ฆ, ๋ก๊น , ๋๋ฒ๊น ๋ฑ์ ์ฒ๋ฆฌ.
- Route-Filter: ์์ฒญ์ ๋ํ ๋ผ์ฐํ ์ ์ ์ดํ๊ธฐ ์ํด ์ํํ๋ ํํฐ -> Ribbon์ ์ด์ฉํ ํด๋ผ์ด์ธํธ ์์ฒญ ๋์  ๋ผ์ฐํ
- Post-Filter: API ์๋ฒ๋ก ๋ผ์ฐํ ๋ ํ ์ํํ๋ ํํฐ -> ์๋ต์ HTTP ํค๋ ์ถ๊ฐ ๋ฐ API ์๋ต์๋, ๋ฉํธ๋ฆญ ๋ฑ์ ๋ฐ์ดํฐ ์์ง
- Error-Filter: ์ 3๋จ๊ณ ํํฐ์์ ๋ฐ์๋ ์ค๋ฅ ์ํ ํํฐ -> Exception
API ์๋ฒ๋ฅผ ๊ตฌ์ถํ ๋ ์์ฃผ ์ฌ์ฉ๋๋ ํํฐ๋ง๋ค์ด ๋ชจ์ฌ์ ธ ์์ผ๋ฉฐ, API ์์ฒญ/์๋ต์์ ๋ณด์ฌ์ง๋ ๋ผ์ดํ ์ฌ์ดํด์ด๋ผ๊ณ ๋ด๋ ๋ฌด๋ฐฉํฉ๋๋ค.
How to use
์ด์  ๊ทธ๋ผ ์ด Filtering์ ํ ๋ฒ ๋ง๋ค์ด๋ณด๋๋ก ํ์ฃ . ์ฌํ๊น์ง ํ๋ ๊ทธ๋๋ก ์ง๋ Routing์์ ํ๋ Gateway ํ๋ก์ ํธ๋ฅผ ๊ทธ๋๋ก ๊ฐ์ ธ์ ์ด์ฉํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๋จผ์  Pre-Filter๋ฅผ ๊ตฌํํด๋ด ์๋ค.
/**
 * Created by Neon K.I.D on 1/22/20
 * Blog : https://blog.neonkid.xyz
 * Github : http://github.com/NEONKID
 */
class GatewayPreFilter : ZuulFilter() {
    val logger = LogManager.getLogger()
    override fun run() {
        val ctx = RequestContext.getCurrentContext()
        val req = ctx.request
        logger.info("Using Pre Filter: ${req.method} request to ${req.requestURL}")
    }
    override fun shouldFilter() = true
    override fun filterType() = FilterConstants.PRE_TYPE
    override fun filterOrder() = 0
}Pre-Filter๋ ํด๋ผ์ด์ธํธ๊ฐ Gateway๋ก ๊ฑฐ์น ๋ ๋จผ์  ์ง๋๊ฐ๋ ์ ๊ตฌ ์ค ํ๋์ ๋๋ค. API ์๋ฒ์ ๋ผ์ฐํ ๋๊ธฐ ์ด์ ์ ์ฒ๋ฆฌ๋๋ ๊ณณ์ด๋ผ๋ ๊ฒ์ด์ฃ .
๋ฐ๋ผ์ ์๋ฒ์ ๋ก๊ทธ์ ๊ฐ์ฅ ๋งจ ์ ๋ถ๋ถ์ ๋จ๊ธฐ ๋๋ฌธ์, ํด๋ผ์ด์ธํธ๊ฐ ์ด๋ค ๋ฉ์๋๋ก ์ด๋ค URI๋ฅผ ๋ถ๋ ๋์ง๋ฅผ ๋ํ๋ด์ฃผ๋ ๊ฒ์ ๋ถ๋ฌ๋ดค๊ณ , ์ด ๋ ์ฌ์ฉํ ๊ฒ์ ๋ฐ๋ก RequestContext ๊ฐ์ฒด์ ๋๋ค.
๋ค์์ Route-Filter ์ ๋๋ค.
/**
 * Created by Neon K.I.D on 1/22/20
 * Blog : https://blog.neonkid.xyz
 * Github : http://github.com/NEONKID
 */
class GatewayRouteFilter : ZuulFilter() {
    val logger = LogManager.getLogger()
    override fun run() {
        logger.info("Using Route Filter: ")
    }
    override fun shouldFilter(): Boolean {
        return RequestContext.getCurrentContext().routeHost != null
                && RequestContext.getCurrentContext().sendZuulResponse()
    }
    override fun filterType() = FilterConstants.ROUTE_TYPE
    override fun filterOrder() = 0
}Route-Filter๋ ๋ผ์ฐํ ์ ํ๋ ์ญํ ์ด์ง๋ง, shouldFilter๋ผ๋ ์ค๋ฒ๋ผ์ด๋ฉ ๋ฉ์๋๋ฅผ ํตํด์ ํน์  Context์ผ ๋, ํํฐ๋ง์ ์ ๊ณตํ๋๋ก ํ๋ค๋ ์ ์ด ๋ค๋ฆ ๋๋ค.
๋ง์ฝ, ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญํ ์๋น์ค ์ธ์ ๋ค๋ฅธ ์๋น์ค์๋ ํด๋น ์์ฒญ์ ํด์ผ ํ๋ค๋ฉด, ์ด ํํฐ๋ง์ ์ฌ์ฉํ๋ ๊ฒ๋ ๊ด์ฐฎ๊ฒ ์ฃ .
/**
 * Created by Neon K.I.D on 1/22/20
 * Blog : https://blog.neonkid.xyz
 * Github : http://github.com/NEONKID
 */
class GatewayPostFilter : ZuulFilter() {
    val logger = LogManager.getLogger()
    override fun run() {
        val req = RequestContext.getCurrentContext()
        val res = req.response
        res.addHeader("X-Sample", UUID.randomUUID().toString())
        logger.info("Using Post Filter: X-Sample - ${res.getHeader("X-Sample")}")
    }
    override fun shouldFilter() = true
    override fun filterType() = FilterConstants.POST_TYPE
    override fun filterOrder() = 0
}Post-Filter๋ API ํธ์ถ์ด ์ ์์ ์ผ๋ก ์ด๋ฃจ์ด์ง ํ, ๋ค์ Gateway์์ API ์๋ฒ๋ก๋ถํฐ ์๋ต์ด ์์ ๊ฒฝ์ฐ, ํด๋น ์๋ต์ ๋ํ๋ด์ฃผ๋ ํํฐ๋ผ๊ณ ํ ์ ์์ต๋๋ค.
์, ์ด๋ฅผํ ๋ฉด ๋ฐฑ์๋ ๊ตฌ๊ฐ์์ ์ค์ํ ์ ๋ณด๋ฅผ ํํฐ๋งํ์ฌ, ์๋ฒ์ ๋จ๋๋ก ํ๊ณ , ๊ทธ ์ธ์ ์ ๋ณด๋ ํด๋ผ์ด์ธํธ์๊ฒ ๋๊ฒจ์ฃผ๋ ๋ฐฉ์์ผ๋ก๋ ์ฌ์ฉํ ์ ์์ฃ .
/**
 * Created by Neon K.I.D on 1/22/20
 * Blog : https://blog.neonkid.xyz
 * Github : http://github.com/NEONKID
 */
class GatewayErrorFilter : ZuulFilter() {
    val logger = LogManager.getLogger()
    override fun run() {
        val throwable = RequestContext.getCurrentContext().throwable
        logger.error("Using Error Filter: $throwable")
    }
    override fun shouldFilter(): Boolean {
        val ctx = RequestContext.getCurrentContext()
        return ctx.throwable != null
    }
    override fun filterType() = FilterConstants.ERROR_TYPE
    override fun filterOrder() = 0
}๋ง์ง๋ง์ผ๋ก Error-Filter๋ ์ค๋ฅ๊ฐ ๋ฐ์๋์์ ๋ ์ฒ๋ฆฌํ๋ ํํฐ๋ง์ ๋๋ค. ์ ์ฝ๋์ฒ๋ผ ์์ฒญ ์์ ์ ๋ฐ์ํ Exception์ด ์กด์ฌํ ๊ฒฝ์ฐ ์ค๋ฅ๋ฅผ ๋ก๊น ํ๋ ๊ฒ์ด ๊ธฐ๋ณธํ์ ๋๋ค.
What is Zuul Filter ?
์ ๊ทธ๋ฐ๋ฐ, ZuulFilter๋ผ๋ ๊ฒ์ ๋ฌด์์ผ๊น์? Pre, Post, Route, Error ์ด๋ ๊ฒ ์ฃผ์ 4๊ฐ์ง ํํฐ๋ง์ด ๋ฌด์จ ์ญํ ์ ํ๊ณ , ์ด๋ป๊ฒ ์ํํ๋์ง๋ ์๊ฒ ๋๋ฐ, ๊ฐ ํํฐ๋ง๋ง๋ค ๋ถ๋ชจ ํด๋์ค์ธ Zuul Filter๊ฐ ์กด์ฌํฉ๋๋ค.
ZuulFilter ํด๋์ค๋ Zuul Gateway์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ Filter ์ถ์ ํด๋์ค ์ค์ ํ๋์ ๋๋ค. ์ฆ, Zuul Gateway์์ Filter๋ฅผ ๊ตฌํํ๊ธฐ ์ํ ๊ตฌ์กฐ๋ง์ ๋ํ๋ธ ํด๋์ค์ด์ง์. ์ฌ๋ฌ๊ฐ์ง ํํฐ๊ฐ ์์ง๋ง, ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฐ๋จํ Filter๋ฅผ ์ฌ์ฉํ ๋๋ Zuul Filter๋ฅผ ๋ถ๋ชจ ํด๋์ค๋ก ์ฌ์ฉํฉ๋๋ค.
/**
 * Created by Neon K.I.D on 1/22/20
 * Blog : https://blog.neonkid.xyz
 * Github : http://github.com/NEONKID
 */
class GatewayCustomFilter : ZuulFilter() {
    override fun run(): Any {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
    override fun shouldFilter(): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
    override fun filterType(): String {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
    override fun filterOrder(): Int {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}๊ฐ ์ถ์ ๋ฉ์๋๋ค์ ๋ํด์ ๊ฐ๋ตํ๊ฒ ์๊ธฐํด๋ณด๊ฒ ์ต๋๋ค. ZuulFilter ํด๋์ค๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ๊ตฌํํด์ผ ํ๋ ๋ฉ์๋๋ ์๋ 4๊ฐ์ ๋๋ค.
- run: ํด๋น ํํฐ๊ฐ ์คํ๋ ๋ ์ํํ๋ ๋ฉ์๋
- shouldFilter: ์ด ํํฐ๊ฐ ๋ฐ์ํ๋ ์กฐ๊ฑด ๋ฉ์๋
- filterType: ์ด ํํฐ๋ ๋ฌด์จ ํํฐ์ธ์ง ๋ฐํํ๋ ๋ฉ์๋
- filterOrder: ํํฐ์ ์ฐ์ ์์๋ฅผ ๋ฐํํ๋ ๋ฉ์๋
๋ฐ๋ผ์ ์ฐ๋ฆฌ๊ฐ ์์์ ๊ตฌํํ๋ shouldFilter ๋ฉ์๋์ true๋ฅผ ์ฃผ์ด์ค๋ค๋ฉด, ๋ชจ๋ ํํฐ๋ง์ด ๋์ํ๊ฒ ๋ฉ๋๋ค. ํนํ Error Filter์ ๊ฒฝ์ฐ true๋ก ๋ฐํ๊ฐ์ ์ค๋ค๋ฉด, ํญ์ Error-Filter๊ฐ ๋ฐ์ํ๊ฒ ๋๋ ๊ฒ์ด์ฃ .
Test
๊ฐ๋จํ ํ ์คํธ๋ฅผ ์งํํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ํ ์คํธ ์ญ์ ์ง๋ ํฌ์คํธ์ ๋ง์ฐฌ๊ฐ์ง๋ก curl ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ ํ ์คํธ ํด ๋ณผ ์ ์์ต๋๋ค.

2020-01-22 17:47:03.399  INFO 12861 --- [nio-9100-exec-1] x.n.g.filters.GatewayPreFilter           : Using Pre Filter: GET request to http://localhost:9100/v1/member
2020-01-22 17:47:03.400  INFO 12861 --- [nio-9100-exec-1] x.n.g.filters.GatewayRouteFilter         : Using Route Filter: 
2020-01-22 17:47:03.412 ERROR 12861 --- [nio-9100-exec-1] x.n.g.filters.GatewayErrorFilter         : Using Error Filter: com.netflix.zuul.exception.ZuulException: Filter threw Exception
2020-01-22 17:47:03.414  WARN 12861 --- [nio-9100-exec-1] o.s.c.n.z.filters.post.SendErrorFilter   : Error during filtering
com.netflix.zuul.exception.ZuulException: Forwarding error
	at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.handleException(SimpleHostRoutingFilter.java:261) ~[spring-cloud-netflix-zuul-2.2.2.BUILD-SNAPSHOT.jar:2.2.2.BUILD-SNAPSHOT]
	at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.run(SimpleHostRoutingFilter.java:241) ~[spring-cloud-netflix-zuul-2.2.2.BUILD-SNAPSHOT.jar:2.2.2.BUILD-SNAPSHOT]
...
2020-01-22 17:47:03.432  INFO 12861 --- [nio-9100-exec-1] x.n.g.filters.GatewayPostFilter          : Using Post Filter: X-Sample - null
์ ๋ Gateway๋ง ์คํํ ์ํ๋ก Member API๋ฅผ ํธ์ถํด๋ดค์ต๋๋ค. ๋น์ฐํ ์๊ธฐ๊ฒ ์ง๋ง, API ์๋ฒ๊ฐ ๋์ํ๊ณ ์์ง ์๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ์์๋ 500 Internal Server Error ์ค๋ฅ๋ฅผ ๋ฐํํ๊ฒ ๋๊ณ , ์๋ฒ์์๋ ์ด๋ค ์ค๋ฅ๊ฐ ๋ฐ์ํ๋์ง, ์ด๋ค API๋ฅผ ํด๋ผ์ด์ธํธ๊ฐ ํธ์ถํ๋์ง ๋ฑ ์ฐ๋ฆฌ๊ฐ ๋ฏธ๋ฆฌ ์์ฑํ๋ ๋ก์ง๋ค์ด ๋์ํ๋ ๊ฒ์ ๋ณด์ค ์ ์์ต๋๋ค.
๋ง์น๋ฉฐ...
Zuul Gateway์ Filtering ๋ถ๋ถ์ ๊ฐ๋จํ๊ฒ ์ดํด๋ดค์ต๋๋ค. Spring boot์์ ์ด์ฉํ๋ ๊ฒ ๊ทธ๋๋ก Filter๋ฅผ ์ฌ์ฉํ์ฌ ์๋ํฌ์ธํธ์ ๋ณด์ ๋ฑ์ ๊ด๋ฆฌํ ์ ์๊ณ , ์ด๋ฅผ ํตํด ์ธ์ฆ์ ๊ตฌํํ ์๋ ์์ต๋๋ค. ๋ฐ๋ผ์ ๊ธฐ์กด์ Spring boot๋ฅผ ์ด์ฉํ ๋ฐฑ์๋ ๊ฐ๋ฐ์๋ผ๋ฉด ์ฝ๊ฒ Zuul Gateway์ ํํฐ๋ง์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋ค์ ํฌ์คํธ์์๋ Service Discovery์ ๋ํ ์ฃผ์ ๋ก ์ด์ด๊ฐ๋๋ก ํ๊ฒ ์ต๋๋ค.
Ref: Zuul-core (https://medium.com/netflix-techblog/announcing-zuul-edge-service-in-the-cloud-ab3af5be08ee)
'Programming > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Spring Cloud] - 7. Hystrix๋ฅผ ์ด์ฉํ Circuit Breaker (๊ธฐ๋ณธํธ) (0) | 2020.03.28 | 
|---|---|
| [Spring Cloud] - 6. Eureka๋ฅผ ์ด์ฉํ ์๋น์ค ๊ฒ์ (0) | 2020.02.08 | 
| [Spring Cloud] - 4. Zuul Gateway๋ฅผ ์ด์ฉํ Routing (0) | 2020.01.17 | 
| [Spring Cloud] - 3. API ์๋ฒ๋ก ์ค์  ๊ฐ ๋ถ๋ฌ์ค๊ธฐ (0) | 2019.12.29 | 
| [Spring Cloud] - 2. Github and Configuration Server (0) | 2019.12.26 | 

 
			 
			 
		 
                     
                     
                     
			