-
Redirect와 Forward의 차이 ( Spring )프로그래밍/서버 프로그래밍 2020. 11. 9. 00:20
Redirect
redirect는 서버에 요청이 들어온 후 , 서버에서 실제로 클라이언트로 HTTP Stauts Code 302를 전송한다.
전송된 응답을 클라이언트에서 받아서 302 응답코드를 확인하고 응답의
Location
헤더에서 URL을 읽어들여 해당 URL로 다시 요청을 보낸다.Forward
forward는 서버에 요청이 들어온 후, 서버에서 모든 일이 벌어진다. 서블릿 컨테이너는 해당 요청을 그대로 타겟 URL로 포워딩 시킨다. 따라서 URL은 브라우저에서는 변경되지 않을 것이고, 리다이렉트와 다르게 응답은 한번만 내려오게 된다.
스프링에서의 처리
스프링에서
redirect
,forward
는 보통 컨트롤러에서redirect:/hello
,forward:/hello
와 같은 String을 리턴해줌으로써 동작한다. 우리는 단순 스트링을 리턴해주었을 뿐인데 실제로 어떻게해서 다른 곳으로 요청을 전달하는 것일까?정답은 바로
UrlBasedViewResolver
의 코드내에 있다.아래는
UrlBasedViewResolver
의createView
메소드이다./** * Overridden to implement check for "redirect:" prefix. * <p>Not possible in {@code loadView}, since overridden * {@code loadView} versions in subclasses might rely on the * superclass always creating instances of the required view class. * @see #loadView * @see #requiredViewClass */ @Override protected View createView(String viewName, Locale locale) throws Exception { // If this resolver is not supposed to handle the given view, // return null to pass on to the next resolver in the chain. if (!canHandle(viewName, locale)) { return null; } // Check for special "redirect:" prefix. if (viewName.startsWith(REDIRECT_URL_PREFIX)) { String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length()); RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible()); String[] hosts = getRedirectHosts(); if (hosts != null) { view.setHosts(hosts); } return applyLifecycleMethods(REDIRECT_URL_PREFIX, view); } // Check for special "forward:" prefix. if (viewName.startsWith(FORWARD_URL_PREFIX)) { String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length()); InternalResourceView view = new InternalResourceView(forwardUrl); return applyLifecycleMethods(FORWARD_URL_PREFIX, view); } // Else fall back to superclass implementation: calling loadView. return super.createView(viewName, locale); }
위 코드를 보면 해당 클래스에서
redirect:
,forward:
으로 시작하는 경우를 확인한 후, 적절한RedirectView
혹은InternalResourceView
를 만들어서 리턴함으로써 우리는 간단하게 리다이렉션, 포워딩을 사용할 수 있다는 것을 확인했다.Filter, Interceptor 관점에서의 차이
그렇다면 redirect는
요청 -> 302 응답 -> Location에 대한 요청 -> Location에 대한 응답
으로 동작하니 필터에는 처음 요청한 URL과 리다이렉션된 URL 모두가 필터와 인터셉터에 걸릴 것이고, forward에서는요청 -> 서버내에서 target URL로 요청전달 -> target URL에 대한 응답
이므로, 필터에는 처음 요청한 URL만 걸릴 것이고, 인터셉터에는 두가지 요청이 모두 찍힐 수도 있고, 혹은 한가지 요청만 찍힐 수도 있을 것 같아서 이를 확인해봤다.먼저
OncePerRequestFilter
를 상속받은 테스트 필터를 만들어 빈에 등록함으로써 필터를 하나 등록했다.그리고, 테스트용 인터셉터도 만들어서 `WebMvcConfigurer`에 등록했다.
이제 등록된 각각의 필터와 인터셉터가 어떻게 작동하는 지 확인하기위해 각각의 url에 요청을 리다이렉션, 포워딩 시키도록 설정했다.
이제 각각 /redirect와 /forward에 요청해보자.
## localhost:8080/redirect에 요청한 결과
브라우저에서 개발자도구의 네트워크 탭을 확인해보면, 먼저 최초 /redirect 요청에 대한 결과가 위의 설명과 같이 302로 응답이 내려오고 Location 헤더에는 http://localhost:8080/hello 가 들어있는 것을 확인할 수 있고, 이후에 /hello로 요청이 다시 한번 가는 것을 확인할 수 있다.
이제 실제 로그가 우리 예상처럼 필터와 인터셉터 모두 /redirect, /hello 에 대해 모두 걸렸는 지 확인해보자.
필터와 인터셉터 모두 두번 다 걸린 걸 확인할 수 있다.
## localhost:8080/forward에 요청한 결과
forward에 요청하면 브라우저 창에 리다이렉션때와 똑같이 "Hello, World!" 가 뜨지만 실제 URL 은 /forward 그대로인 것을 볼 수 있다.
로그를 한번 봐보자.
1. /forward 에 대한 필터의 doFilterInternal이 시작된다.
2. /forward에 대한 인터셉터의 preHandle, postHandle이 실행된다.
3. /hello 에 대한 인터셉터의 preHandle, postHandle이 실행된다.
4. /hello 의 인터셉트의 afterCompletion이 콜된다.
5. /forward의 인터셉터의 afterCompletion이 콜된다.
6. /forward에 대한 필터의 doFilterInternal이 종료된다.
위 로그를 통해 forward에서는 인터셉터는 두가지 URL에 대해 모두 동작하지만 , 디스패처 서블릿 내부에서 /hello URL 로 포워딩 되기때문에 /hello 에 대해서는 필터가 걸리지 않고, /forward만 필터에 걸리는 것을 확인할 수 있었다.
# 결론
- 리다이렉트는 클라이언트로 실제 응답을 해주고, 클라이언트에서 그 응답을 읽어서 다시 서버로 요청한다.
- 포워드는 서버에서 자체 포워딩을 하기 때문에 처음에 했던 요청을 그대로 서버내에서 포워딩해서 다른 요청으로 처리한다. (브라우저나 클라이언트 입장에서는 모름 )
- 리다이렉트는 두가지 URL 모두 필터와 인터셉터에 걸린다.
- 포워드는 인터셉터는 두가지 모두 걸리는 반면, 필터는 처음에 요청했던 URL만 걸린다. ( 필터는 디스패처 서블릿 바깥에 있기 때문 )
'프로그래밍 > 서버 프로그래밍' 카테고리의 다른 글
[ Spring AOP ] pointcut 안걸릴때 (2) 2020.05.12 Spring Boot + Kotlin + JPA 적용하기 Entity 생성시 생각해볼 점들 (11) 2020.01.28 [JUnit5 , Spring Boot] 인텔리제이에서 JUnit5 DisplayName이 표시되지 않을 때 해결 방법 (1) 2019.12.18 Spring boot S3 Local에서 Test하기 (0) 2019.04.04 Travis CI를 이용해 Maven + SpringBoot에 CI 적용하기 (0) 2019.02.20