본문 바로가기
IT 공부/자바와 웹 애플리케이션

[스프링] - redirectAttribute.addFlashAttribute() 동작 원리 (feat. 쿠키와 세션)

by exdus3156 2024. 1. 26.

1. addFlashAttirbute()의 특이한 점

스프링에서 아래와 같은 addFlashAttribute() 코드를 통해 일시적으로 데이터를 전달할 수 있다.

@GetMapping("/register")
public void register() { }

@PostMapping("/register")
public void register(@Valid RegisterDTO registerDTO, 
                     BindingResult bindingResult, 
                     RedirectAttribute redirectAttribute) {
                     
    if ( bindingResult.hasErrors ) {
        redirectAttribute.addFlashAttribute("errors", bindingResult.getAllErrors());
        return "redirect:/register";
    }
    
    return "redirect:/index";
}

이렇게 동작하는 원리는 스프링에서 내부적으로 세션을 사용하는 것이다.

언뜻 생각하면 addFlashAttribute()의 동작 원리는 신기하고 오묘하게 느껴지는 기능이다. 조금 생각해보면 어떻게 동작하는지 선뜻 대답하기 힘들다. 왜냐하면 리다이렉트를 통해 클라이언트 측에서 다시 HTTP request 메시지를 요청하는데, 이 요청과 응답은 HTTP의 스펙에 의하면 stateless하기 때문이다. (링크)

리다이렉트 응답 메시지를 받은 클라이언트가 다시 해당 URI로 새로운 요청을 보내면, 웹 서버의 스프링은 요청에 따른 컨트롤러를 실행한 뒤, View 페이지를 렌더링할 것이다. 이때 Model에  바인딩된 값을 가져와 동적으로 html 웹 콘텐츠를 생산한다.

문제는 GET 컨트롤러의 로직을 보면 특별히 Model에 errors를 바인딩하지 않는다는 것이다. 어디까지나 이전 POST 요청에서 발생한 @Valid 문제에 대해 에러를 식별하고 처리했지, 다시 리다이렉트가 되고 난 이후에는 stateless한 HTTP 요청이 어떻게 해당 클라이언트에 대한 에러 정보를 유지할 수 있을까?

 

 

2. 해결 : 세션 저장소 활용

stateless한 HTTP 스펙은 이것을 보완하기 위해 쿠키와 세션을 만든다. 

톰캣은 사용자에게 JsessionID를 쿠키로 전달한다. 클라이언트는 그 정보를 쿠키에 저장하고, 다음 요청 시 쿠키를 함께 전송한다. 만약 웹 서버 측에서 클라이언트가 누군지 식별하고 싶다면 이 쿠키를 긁어와 세션ID 정보를 얻는다. 이 정보로 해당 세션에 맞는 유니크한 정보를 저장하거나 가져올 수 있다.

addFlashAttribute()도 이렇게 동작한다. 문제가 발생하면 클라이언트에게서 쿠키를 가져와 해당 클라이언트만의 세션 저장소에 errors 정보를 등록한다.

그리고 다음 리다이렉트 요청이 올 것이다.

원리 상으로는 이 요청은 Stateless한 HTTP 요청이므로 이전에 이 클라이언트가 POST로 요청을 했는지 안 했는지 관심도 없고, 알 방법도 없다. 

하지만 JSP와 같은 view 템플릿 엔진에서 ${errors} 형태로 데이터 접근을 시도한다. 이때 이 정보를 찾기 위해 아래의 순서로 차근차근 데이터를 찾아 나간다.

  1. 페이지 스코프 (페이지 내부에 <% .. %>로 바인딩한 데이터)
  2. 요청 스코프 (Model에 바인딩된 데이터)
  3. 세션 스코프 (Session 저장소에 바인딩된 데이터)
  4. 애플리케이션 스코프 (서블릿 컨텍스트에 바인딩된 데이터)

${errors}는 바로 3번 세션 스코프에서 발견될 것이다. 

이렇게 한 번 사용되고 나면 addFlashAttribute()로 바인딩된 데이터는 세션에서 삭제된다.

 

 

3. 참고) addAttribute() 원리

redirectAttributes.addAttribute() 메소드는 세션에 저장하는 것이 아니라 그냥 URL에 쿼리스트링으로 바인딩을 하게 해준다. 리다이렉트 시 URL에 쿼리스트링을 추가해주기 위해서는 그냥 간단하게 HTTP response 메시지 헤더의 locaion 항목에 /get?data=100 이런 식으로 쿼리스트링을 그대로 적어주면 되며, 실제로 이 메소드가 이렇게 동작한다.

따라서 redirectAttributes.addAttribute() 메소드 사용 시, 인자로 문자열을 전달해야 한다.