> For the complete documentation index, see [llms.txt](https://docs.sysout.co.kr/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sysout.co.kr/web/back-end/spring-boot/spring-controller/redirect.md).

# Redirect

## Redirect란?

Redirect란 클라이언트(브라우저)에게 재접속을 지시하는 행위를 말한다. 예를 들어 다음과 같은 상황이라고 볼 수 있다.

> ### ex) 고객센터에 걸려온 전화
>
> (상담원) 안녕하십니까? Hacademy입니다.
>
> (고객) 안녕하세요. 열혈강사 채널에 광고 문의하려고 전화했는데요
>
> (상담원) 네 그러십니까. 죄송하지만 열혈강사 채널 관련 문의는 다른 팀에서 전담하고 있는데요. (번호를 알려준다) 이 번호로 다시 연락 주시겠습니까? <mark style="color:red;">(Redirect 처리)</mark>
>
> (고객) 네 알겠습니다. (안내받은 번호로 전화를 건다)

## Spring Controller에서의 Redirect

Spring Controller에서는 필요에 따라 Redirect를 사용할 수 있다. 사용하는 방법은 여러 가지가 있다.

1. HttpServletResponse 객체를 사용하는 방법
2. URL에 redirect 접두사를 표기하는 방법
3. RedirectView를 사용하는 방법

## Redirect가 필요한 상황

Redirect가 필요한 상황을 만들기 위해 <mark style="color:blue;">`boot04redirect`</mark> 프로젝트를 생성한다. 간단한 환율 계산기를 만들 예정이다.

<div align="left"><figure><img src="/files/0ciOODGYQRnl8QcLHGJh" alt=""><figcaption></figcaption></figure></div>

## Controller와 Mapping 구현

우리가 만들 기능은 두 가지이다.

* <mark style="color:red;">`/usd2krw`</mark> - 달러(＄)를 원화(￦)로 변환하여 출력하는 기능 (View 없이 구현)
* <mark style="color:red;">`/krw2usd`</mark> - 원화(￦)를 달러(＄)로 변환하여 출력하는 기능 (View 없이 구현)

### Controller 구현

우선 <mark style="color:blue;">`ExchangeController`</mark>라는 클래스를 생성한다.

{% code title="ExchangeController.java" %}

```java
@Controller
public class ExchangeController {

}
```

{% endcode %}

<div align="left"><figure><img src="/files/FyNY4vAX4o7VmvLyVoOf" alt=""><figcaption></figcaption></figure></div>

### Mapping 구현

작성 시점의 환율로 계산하여 사용자에게 출력하도록 Mapping을 구현한다.

{% hint style="info" %}
작성 시점인 2022.08.29 기준 환율은 다음과 같다

* 1 KRW = 0.00074 USD
* 1 USD = 1347.15 KRW
  {% endhint %}

{% code title="ExchangeController.java" %}

```java
@GetMapping("/usd2krw")
@ResponseBody
public String usd2krw(@RequestParam double usd) {
	double krw = 1347.15 * usd;
	return usd + "(USD) → "+krw+"(KRW)";
}
@GetMapping("/krw2usd")
@ResponseBody
public String krw2usd(@RequestParam double krw) {
	double usd = 0.00074 * krw;
	return krw+"(KRW) → "+usd+"(USD)";
}
```

{% endcode %}

## 접속 확인

Spring Boot App을 실행한 뒤 다음 주소로 접속하여 정상적으로 작동되는지 확인한다. 각각 100달러와 10000원에 대한 환전 결과이다.

* <http://localhost:8080/usd2krw?usd=100>
* <http://localhost:8080/krw2usd?krw=10000>

<div align="left"><figure><img src="/files/3UDuh4uXLWizyjwW3cvA" alt=""><figcaption></figcaption></figure></div>

<div align="left"><figure><img src="/files/lFZGnkuXEg9D6NAIcYqD" alt=""><figcaption></figcaption></figure></div>

## 통합 Mapping 구현

지금은 서로 다른 URL로 각각 <mark style="color:red;">`KRW→USD`</mark>, <mark style="color:red;">`USD→KRW`</mark> 를 처리하고 있으나 이를 하나의 URL로 합쳐서 처리할 수도 있지 않을까 하는 의문이 든다. 이를테면 주소에 <mark style="color:red;">`mode`</mark>라는 이름의 파라미터를 추가하여 하고자 하는 작업을 알려주는 방법을 사용할 수 있다.

* <http://localhost:8080/exchange?mode=usd2krw\\&amount=100>
* <http://localhost:8080/exchange?mode=krw2usd\\&amount=10000>

가능하다면 한 페이지에 많은 기능이 있는 것이 좋으므로 <mark style="color:red;">`/exchange`</mark>라는 주소를 만들고, 대신 mode에 따라 <mark style="color:red;">`/usd2krw`</mark>와 <mark style="color:red;">`/krw2usd`</mark>로 **redirect** 하도록 처리한다. 그림으로 표현하면 다음과 같다.

<div align="left"><figure><img src="/files/EBJqw5IaRt48tM35LeaX" alt=""><figcaption></figcaption></figure></div>

### HttpServletResponse 사용

<mark style="color:red;">`/exchange1`</mark> 매핑을 만들어 `mode`, `amount` 파라미터를 수신하고 **redirect** 처리하도록 구현하는 Mapping은 다음과 같다. Redirect는 JSP/Servlet 기본 응답 객체인 <mark style="color:blue;">`HttpServletResponse`</mark>에서 지원하므로 매개변수에 추가하며, <mark style="color:blue;">`IOException`</mark>이 발생하므로 예외 전가 처리까지 해야 한다.

{% code overflow="wrap" %}

```java
@GetMapping("/exchange1")
@ResponseBody
public void exchange1(@RequestParam String mode, @RequestParam double amount, HttpServletResponse response) throws IOException {
	if(mode.equals("usd2krw")) {
		response.sendRedirect("usd2krw?usd="+amount);
	}
	else if(mode.equals("krw2usd")) {
		response.sendRedirect("krw2usd?krw="+amount);
	}
}
```

{% endcode %}

현재 페이지에서는 사용자에게 반환할 내용이 없으므로 반환형은 <mark style="color:red;">`void`</mark>를 사용하였다. 접속하면 다음과 같이 Redirect 처리 됨을 확인할 수 있다. 자세히 살펴볼 내용은 개발자 도구에서 기록이 남는다는 것과, 주소가 <mark style="color:red;">`/exchange`</mark>가 아니라는 점이다.

<div align="left"><figure><img src="/files/cN4vQWlMkO4LoG42cSR6" alt=""><figcaption></figcaption></figure></div>

크롬 개발자 도구에서 네트워크 이력을 확인하면 다음과 같이 **Redirect**가 되었음을 확인할 수 있다.

<div align="left"><figure><img src="/files/hKhVlgPmv7PjzOCStkrS" alt=""><figcaption></figcaption></figure></div>

### Redirect 접두사 사용

Spring에서는 <mark style="color:red;">`redirect:`</mark> 접두사로 redirect 처리가 가능하다. 단, <mark style="color:blue;">`View Resolver`</mark>를 사용해야 한다. <mark style="color:red;">`/exchange2`</mark> 를 만들어 redirect 접두사를 사용하여 처리하도록 구현한다.

```java
@GetMapping("/exchange2")
public String exchange2(@RequestParam String mode, @RequestParam double amount) {
	if(mode.equals("usd2krw")) {
		return "redirect:usd2krw?usd="+amount;
	}
	else if(mode.equals("krw2usd")) {
		return "redirect:krw2usd?krw="+amount;
	}
	return null;
}
```

접속 테스트를 하면 정상적으로 **Redirect**가 이루어지는 것을 확인할 수 있다.

<div align="left"><figure><img src="/files/98ldYeMeZAOsSboBp6Ze" alt=""><figcaption></figcaption></figure></div>

### RedirectView 사용

Spring에서는 **Redirect**만을 위한 <mark style="color:blue;">`RedirectView`</mark> 클래스를 제공한다. <mark style="color:red;">`/exchange3`</mark>을 만들고 테스트를 위한 코드를 작성한다.

```java
@GetMapping("/exchange3")
public RedirectView exchange3(@RequestParam String mode, @RequestParam double amount) {
	if(mode.equals("usd2krw")) {
		return new RedirectView("usd2krw?usd="+amount);
	}
	else if(mode.equals("krw2usd")) {
		return new RedirectView("krw2usd?krw="+amount);
	}
	return null;
}
```

반환형으로 <mark style="color:blue;">`RedirectView`</mark>를 설정하고 객체를 반환하도록 코드를 구성한다. 객체 내부에는 **Redirect** 목표 매핑 주소를 작성한다.&#x20;

테스트 시 정상적으로 **Redirect** 됨을 확인할 수 있다.

<div align="left"><figure><img src="/files/y2BVcmsHJWOtTduPMfoG" alt=""><figcaption></figcaption></figure></div>

## RedirectAttributes

**Redirect** 할 때 파라미터를 작성하는 것이 번거로운 경우가 많다. 파라미터는 ?와 &를 사용하여 작성해야 하므로 수식이 지저분해지는데, <mark style="color:blue;">`RedirectAttributes`</mark>는 **Model**과 동일한 방법으로 **Redirect**시 파라미터를 처리할 수 있도록 Spring에서 제공하는 도구이다.

<div align="left"><figure><img src="/files/19kyafrv8jHrLb6xDUT4" alt=""><figcaption><p>RedirectAttributes는 Model을 확장한 형태이다</p></figcaption></figure></div>

매개변수에 선언한 뒤 Model처럼 `key=value` 형식으로 데이터를 추가한다. **redirect 접두사**와 **RedirectView** 등 **Spring에서 지원하는 기능**에서 사용할 수 있다.

### Redirect 접두사 사용 시

{% code overflow="wrap" %}

```java
@GetMapping("/exchange2-1")
public String exchange2_1(@RequestParam String mode, @RequestParam double amount, RedirectAttributes attr) {
	if(mode.equals("usd2krw")) {
		attr.addAttribute("usd", amount);
		return "redirect:usd2krw";
	}
	else if(mode.equals("krw2usd")) {
		attr.addAttribute("krw", amount);
		return "redirect:krw2usd";
	}
	return null;
}
```

{% endcode %}

### RedirectView 사용 시

{% code overflow="wrap" %}

```java
@GetMapping("/exchange3-1")
public RedirectView exchange3_1(@RequestParam String mode, @RequestParam double amount, RedirectAttributes attr) {
	if(mode.equals("usd2krw")) {
		attr.addAttribute("usd", amount);
		return new RedirectView("usd2krw");
	}
	else if(mode.equals("krw2usd")) {
		attr.addAttribute("krw", amount);
		return new RedirectView("krw2usd");
	}
	return null;
}
```

{% endcode %}

## 주의사항

`Redirect`를 처리할 경우의 주의사항은 다음과 같다.

### 절대 경로에 대한 처리

* JSP/Servlet 방식일 경우 절대경로를 사용하려면 HttpServletRequest 객체를 통한 계산이 필요하다(1번 방식)
  * **상대 경로 -** `response.sendRedirect("usd2krw?usd=100")`
  * **절대 경로 -** `response.sendRedirect(request.getContextPath()+"/usd2krw?usd=100")`
* Spring에서 제공하는 방식(2번, 3번 방식)일 경우 경로 앞에 `/`를 적으면 절대경로로 인식한다.
  * redirect 접두사(2번 방식)를 사용하는 경우
    * **상대 경로 -** `return "usd2krw?usd=100"`
    * **절대 경로 -** `return "/usd2krw?usd=100";`
  * RedirectView를 사용하는 경우
    * **상대 경로 -** `return new RedirectView("usd2krw?usd=100")`
    * **절대 경로**
      * `return new RedirectView("/usd2krw?usd=100")`
      * `return new RedirectView("usd2krw?usd=100", true)`

### Redirect는 Get방식이다

* 원칙적으로 POST방식처럼 파라미터를 숨겨서 전송하는 것이 불가능하다.
* 예외적으로 <mark style="color:blue;">`HttpSession`</mark>을 사용하는 **Flash value 방식**을 지원한다.
  * <mark style="color:red;">(주의)</mark> <mark style="color:red;"></mark><mark style="color:red;">**Flash value**</mark><mark style="color:red;">는 새로고침 시 소멸한다.</mark>

## 소스 코드

### Github

{% embed url="<https://github.com/hiphop5782/spring-boot/tree/main/boot04redirect>" %}

### ExchangeController

<details>

<summary>ExchangeController.java</summary>

```java
package com.hacademy.boot04.controller;

import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;

@Controller
public class ExchangeController {
	@GetMapping("/usd2krw")
	@ResponseBody
	public String usd2krw(@RequestParam double usd) {
		double krw = 1347.15 * usd;
		return usd + "(USD) → "+krw+"(KRW)";
	}
	@GetMapping("/krw2usd")
	@ResponseBody
	public String krw2usd(@RequestParam double krw) {
		double usd = 0.00074 * krw;
		return krw+"(KRW) → "+usd+"(USD)";
	}
	//[1] HttpServletResponse를 사용한 Redirect
	@GetMapping("/exchange1")
	@ResponseBody
	public void exchange1(@RequestParam String mode, @RequestParam double amount, HttpServletResponse response) throws IOException {
		if(mode.equals("usd2krw")) {
			response.sendRedirect("usd2krw?usd="+amount);
		}
		else if(mode.equals("krw2usd")) {
			response.sendRedirect("krw2usd?krw="+amount);
		}
	}
	//[2] Redirect prefix를 사용한 Redirect
	@GetMapping("/exchange2")
	public String exchange2(@RequestParam String mode, @RequestParam double amount) {
		if(mode.equals("usd2krw")) {
			return "redirect:usd2krw?usd="+amount;
		}
		else if(mode.equals("krw2usd")) {
			return "redirect:krw2usd?krw="+amount;
		}
		return null;
	}
	//[2-1] Redirect prefix를 사용한 Redirect + RedirectAttributes 사용
	@GetMapping("/exchange2-1")
	public String exchange2_1(@RequestParam String mode, @RequestParam double amount, RedirectAttributes attr) {
		if(mode.equals("usd2krw")) {
			attr.addAttribute("usd", amount);
			return "redirect:usd2krw";
		}
		else if(mode.equals("krw2usd")) {
			attr.addAttribute("krw", amount);
			return "redirect:krw2usd";
		}
		return null;
	}
	//[3] RedirectView를 사용한 Redirect
	@GetMapping("/exchange3")
	public RedirectView exchange3(@RequestParam String mode, @RequestParam double amount) {
		if(mode.equals("usd2krw")) {
			return new RedirectView("usd2krw?usd="+amount);
		}
		else if(mode.equals("krw2usd")) {
			return new RedirectView("krw2usd?krw="+amount);
		}
		return null;
	}
	//[3-1] RedirectView를 사용한 Redirect + RedirectAttributes 사용
	@GetMapping("/exchange3-1")
	public RedirectView exchange3_1(@RequestParam String mode, @RequestParam double amount, RedirectAttributes attr) {
		if(mode.equals("usd2krw")) {
			attr.addAttribute("usd", amount);
			return new RedirectView("usd2krw");
		}
		else if(mode.equals("krw2usd")) {
			attr.addAttribute("krw", amount);
			return new RedirectView("krw2usd");//상대경로
//			return new RedirectView("krw2usd", true);//절대경로
		}
		return null;
	}
}

```

</details>
