티스토리 뷰

Redirect(리다이렉트)

Redirect(리다이렉트)는 서버가 클라이언트에서 요청한 URL에 대한 응답을 다른 URL로 재요청하라고 명령을 보내는 것을 말합니다. 이때, URL 주소를 확인해보시면 주소가 바뀌면서 다시 접속되게 됩니다.

 

즉, Redirect 는 클라이언트의 요청을 완전히 새로운 URL로 전달하여 처리하는 방식입니다.

 

그리고 클라이언트는 새로운 URL로 다시 요청하고, 서버는 이에 대한 응답을 처리합니다.

 

 

리다이렉트는 HTTP 응답 코드 3xx를 사용합니다.

리다이렉트는 영구 리다이렉트와 일시 리다이렉트, 기다 리다이렉트로 분류할 수 있습니다.

 

 

영구 리다이렉트

영구 리다이렉트는 이름 그대로 리소스의 URL이 영구적으로 변경된 상태를 뜻합니다.

301(Moved Permanently), 308(Permanet Redirect) 상태가 영구 리다이렉트에 포함됩니다.

이 형식의 응답은 Location 헤더에 URL이 포함되어 해당 경로로 다시 요청을 전송하게 됩니다.

 

301과 308 응답의 차이점은 사용자 에이전트가 리다이렉트로 새로운 URL로 다시 요청을 보낼 때 HTTP Method 변경이 있는지, 메시지 바디의 본문이 유지되는지의 차이가 있습니다.

 

301 : 처음 요청의 메서드에서 다른 메서드로 변경 할 수 있고(POST -> GET), 본문 내용이 삭제될 수 있습니다.

308 : 새로운 URL로 다시 요청을 보낼 때 메서드가 변경되지 않고 본문 내용이 유지됩니다.

 

 

일시 리다이렉트

일시 리다이렉트는 리소스의 URL이 일시적으로 변경된 상태를 뜻합니다.

302(Found), 307(Temporary Redirect) 상태가 일시 리다이렉트에 포함됩니다.

이 형식 또한 Location 헤더에 URL이 포함되어 해당 경로로 다시 요청을 전송하게 됩니다.

 

302와 307 두 응답도 영구 리다이텍트와 동일하게 메서드 변경 유무와 본문 내용 유지 여부의 차이점이 있습니다.

 

302 : 리다이렉트 시에 요청 메서드가 변경될 수 있고, 본문이 제거될 수 있습니다.

307 : 리다이렉트 시에 메서드가 변경되지 않고 본문 내용이 유지됩니다.

 

 

기타 리다이렉트

기다 리다이렉트에는 캐시와 관련된 304(Modified) 등이 있습니다.

 

지금 글에서는 딱히 중요하지 않으니 자세한 내용은 생략하겠습니다.

 

 

리다이렉트를 왜 사용할까?

리다이렉트는 웹 애플리케이션에서 페이지 간의 이동을 처리하는 방법이라고 생각하시면 됩니다.

 

예를 들어, 상품 주문 페이지와 주문 목록을 보는 페이지, 총 2페이지가 있다고 가정해 봅시다.

우리는 상품 주문 페이지에서 주문을 누르면 주문 목록으로 이동하는 코드를 작성했습니다.

// /add 요청시 상품 주문 페이지 orderForm 렌더링
@GetMapping("/add")
public String ItemOrderForm() {
    return "/orderForm"; 
}
    
// 상품 주문 페이지 orderForm 에서 주문 완료(POST)시 상품목록 페이지(itemForm)로 이동
@PostMapping("/add")
public String ItemOrderCompleteForm(@ModelAttribute("item") Item item) {
    itemRepository.save(item);
    //model.addAttribute("item", item); // @ModelAttribute가 자동 추가, 생략 가능
    return "/itemsForm";
}

// /items URI로 요청시 상품목록 페이지 itemsForm 렌더링
@GetMapping("/items")
public String itemsForm() {
    return "/itemsForm";
}

 

위 코드를 보고 아래 그림을 보시면 이해가 쉬울 겁니다.

 

위 코드에서 return "/orderForm" 이라고 하는 것은 스프링에서 제공하는 기본 뷰 템플릿에

/orderForm.html 파일을 렌더링 한다는 뜻입니다.

  • src/main/resources/templates/orderForm.html

 

그렇다면 return "/itemsForm" 코드는 해당 메서드가 실행되면 itemsForm.html 파일을 렌더링 해서 보여줘라!

이런 뜻이 되겠죠?

 

우선, GET 메서드로 상품 주문 페이지로 이동합니다.

 

그리고 주문 페이지(/orderForm)를 쭉 보다가 아래에 주문하기 버튼을 누르게 되면 POST 요청이 발생하여, 컨트롤러는 /itemsForm 으로 페이지를 이동시킵니다.

 

그렇다면 만약에 주문을 완료하고 상품 목록 페이지에서 새로고침을 한다면 어떻게 될까요?

 

웹 브라우저의 새로 고침은 마지막에 서버에 전송한 데이터를 다시 전송합니다.

마지막으로 서버에 전송한 데이터는 POST /add 이니까 새로고침해도 계속 POST 요청이 나간다는 것입니다.

 

보통 HTTP 메서드 중 GET은 읽어오는(조회), POST는 저장하는(수정, 삭제 등등) 역할을 하지만, 이 예제에서는 GET은 조회, POST는 저장이라고 하면 새로고침할 때마다 계속 상품 주문(저장)을 하게 된다는 것입니다.

 

 

이러한 문제를 해결하기 위해 리다이렉트(Redirect)를 사용하게 됩니다.

아까 위에서 설명하였듯이 리다이렉트는 요청 메서드를 다른 메서드로 변경해줍니다.(GET -> POST)

 

아래 사진과 같이, POST 요청으로 주문을 완료 후 렌더링을 할 때 리다이렉트를 해주면 GET 메서드로 상품 목록 페이지(/itemsForm)을 호출하여 렌더링 해주게 됩니다.

 

이런 리다이렉트를 하는 방법에는 두 가지가 있습니다.

 

 

HttpServletResponse의 sendRedirect()

// 상품 주문 시, /items 경로로 리다이렉트
@PostMapping("/add")
public void ItemOrderCompleteForm(HttpServletResponse response) throws IOException {
    response.sendRedirect("/items");
}

// 상품 주문 시(위 메서드), 이 메서드가 실행되어 /itemsForm 렌더링
@GetMapping("/items")
public String itemsForm() {
    return "/itemsForm";
}

 

 

 

스프링 redirect

// 상품 주문 시, /items 경로로 리다이렉트
@PostMapping("/add")
public String ItemOrderCompleteForm() throws IOException {
    return "redirect:/items";
}

// 상품 주문 시(위 메서드), 이 메서드가 실행되어 /itemsForm 렌더링
@GetMapping("/items")
public String itemsForm() {
    return "/itemsForm";
}

 

스프링을 사용한다면 좀 더 편리한 스프링에서 제공하는 "redirect:" 문법을 사용하는 게 좋습니다.

 

 

헷갈림 주의!

추가로 설명하자면, 단순하게 폼을 렌더링 하게 사용된 return "/itemsForm" 은 파일 경로(명)를 리턴한 것이지만 리다이렉트를 할 때는 해당 컨트롤러의 URI을 적어준다.

 

그렇기 때문에 위 코드에서는 return "redirect:/itemsForm"이 아닌 "redirect:/items" (컨트롤러를 호출) 가 된다.

 


 

RedirectAttribute

만약, 위에 예제에 더해서 URI을 입력할 때 문자열에 변수의 값을 더해서 반환한다고 생각해 보자.

@PostMapping("/add")
public String ItemOrderCompleteForm() throws IOException {
    return "redirect:/items" + data.getId();
}

 

위 예시처럼 문자열에 변수의 값을 더해서 반환하는 경우, URI 인코딩이 안돼서 오류가 발생할 수 있다.

 

이런 경우에는 RedirectAttribute 인터페이스를 사용해 주면 된다.

 

 

파라미터를 통해 RediectAttribute 인터페이스를 전달받고, addAttribute() 를 통해 값을 저장한다.

그럼 redirect URI를 반환할 때 {userId} 처럼 문자열 내에서 해당 값을 사용할 수 있다.

또한, 아래 코드의 status처럼 사용하지 않은 값들은 자동으로 쿼리 파라미터로 들어가게 된다.

@PostMapping("/add")
public String ItemOrderCompleteForm(Data data, RedirectAttribute redirectAttribute) {

    // 첫 번째 파라미터는 키(Key), 두 번째 파라미터는 값(Value) 라고 생각하시면 이해가 쉽다.
    redirectAttribute.addAttribute("dataId", data.getId());
    redirectAttribute.addAttribute("status", "complete");
    return "redirect:/items/{dataId}"; //dataId 라는 키를 호출 -> data.getId() 값이 들어간다.
}

 

위 리다이렉트 후 URL을 확인해 보면 다음과 같다.

  • http://localhost:8080/items/{data.getId() 값}?status=complete

 

참고(출처)

https://velog.io/@jcw1031/%EC%8A%A4%ED%94%84%EB%A7%81-Redirection#redirection%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98

https://coding-ha-da.tistory.com/62

인프런 김영한님의 스프링 MVC 1편 강의