본문 바로가기

SpringBoot

ch2 15. forward와 redirect - 실습

-header.html에 login링크 걸어주기

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<div th:fragment="header">
    <div id="header">
        <ul>
            <li><a href="">Home</a></li><li>
            <a href="">Board</a></li><li>
            <a href="">Book</a></li><li>
            <a href="">FAQ</a></li><li>
            <a href="" th:href="@{/login/login}">Login</a></li>
        </ul>
    </div>
</div>
</html>

- LoginController에서 Model 대신에 RedirectAttribute 사용
(redirect 기능을 쓰고, Model의 자손이라 Model의 기능을 다 물려받고 있다. )

    @PostMapping("/login")
    public String login(String id, String pwd, RedirectAttributes model) throws Exception{
        // 1. id, pwd를 확인
        if(loginCheck(id,pwd)) {
            // 2. 일치하면, userInfo.html
            model.addAttribute("id", id);
            model.addAttribute("pwd", pwd);
            return "userInfo"; // userInfo.html
        } else {
            // 일치하지 않으면, login.html로 이동
            String msg = URLEncoder.encode("id 또는 패스워드가 일치하지 않습니다.", "utf-8");
            model.addAttribute("msg", msg);
            return "redirect:/login/login";
            //            return "redirect:/login/login?msg=" + msg; // redirect는 GET요쳥 -> GetMapping("/login") 받음
        }

 

수정 후 서버를 실행하면 다음과 같은 결과를 확인할 수 있다. 

 model에 담긴 내용이 자동으로 쿼리 스트링으로 바뀌어 들어간걸 확인할 수 있다. 

 

* 인코딩이 깨져있는 부분을 해결해보자.

- resources/application.properties에 인코딩 설정을 추가해준다. 

server.servlet.encoding.enabled=true
server.servlet.encoding.charset=UTF-8
        } else {
            // 일치하지 않으면, login.html로 이동
//            String msg = URLEncoder.encode("id 또는 패스워드가 일치하지 않습니다.", "utf-8");
            String msg ="id 또는 패스워드가 일치하지 않습니다.";
            model.addAttribute("msg", msg);
            return "redirect:/login/login";
            //            return "redirect:/login/login?msg=" + msg; // redirect는 GET요쳥 -> GetMapping("/login") 받음
        }

LoginController 코드도 다음과 같이 수정한 후 서버를 재실행한다. 

성공적으로 변환이 이루어진 것을 확인할 수 있다. 

우리는 한 번만 요청했는데 요청이 2번 전달된 것을 확인할 수 있다. 
첫 번째 login은 '수동 요청'이 된 것이고, 두 번째 login?msg... 는 브라우저가 '자동 요청'을 수행한 것이다. 

응답을 보면 302번으로 응답한 것을 확인할 수 있다 (redirect)

해당 Location으로 자동 응답이 이루어진다. 

 

 

 

* addFlashAttribute

        } else {
            // 일치하지 않으면, login.html로 이동
//            String msg = URLEncoder.encode("id 또는 패스워드가 일치하지 않습니다.", "utf-8");
            String msg ="id 또는 패스워드가 일치하지 않습니다.";
            model.addAttribute("msg", msg);
            model.addFlashAttribute("msg","일회용 메시지");

            return "redirect:/login/login";
            //            return "redirect:/login/login?msg=" + msg; // redirect는 GET요쳥 -> GetMapping("/login") 받음
        }

LoginController에 addFlashAttribute()를 사용하는 문장을 추가해준다. 

 

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 th:text = "${param.msg}"></h1>
<h1 th:text = "${msg}"></h1>
<form action="/login/login" method="post">
    id<br>
    <input type="text" name="id"><br>
    pwd:<br>
    <input type="password" name="pwd"><br>
    <input type="submit" value="로그인">

</form>
</body>
</html>

${param.msg}는 쿼리 스트링에서 읽어오는 것 -> addAttribute로 저장된 거 보여줄 때 사용

${msg}는 세션에서 읽어오는 것이다. -> addFlashAttribute로 저장된 거 보여줄 때 사용(RedirectAttribute에서 읽어옴)

첫 번째 addAttribute로 전달한 것은 쿼리 스트링을 통해 전달된 것을 확인할 수 있다. 
addFlashAttribute는 쿼리 스트링에 없는데 어떻게 나온 것일까?

그 이유는 데이터를 세션 객체에 담았다가 전달한 후 지우기 때문이다. (세션 저장은 서버의 부담)

한 번 보여주고 말 때는 쿼리 스트링에 추가되는 것보다 addFlashAttribute를 통해 전달 후 지워버리는 것이 좋다. 

 

 

- LoginController에 Request 객체 선언 

        if(loginCheck(id,pwd)) {
            // 2. 일치하면, userInfo.html
            model.addAttribute("id", id);
            model.addAttribute("pwd", pwd);

            return "userInfo"; // userInfo.html
        } else {
            // 일치하지 않으면, login.html로 이동
//            String msg = URLEncoder.encode("id 또는 패스워드가 일치하지 않습니다.", "utf-8");
            String msg ="id 또는 패스워드가 일치하지 않습니다.";
            model.addAttribute("msg", msg);
            model.addFlashAttribute("msg","일회용 메시지");
            req.setAttribute("msg", "request에 저장된 msg");

            return "redirect:/login/login";
<h1 th:text = "${#request.getAttribute('msg')}"></h1>

request객체에서 읽어올 때 -> request 객체에서 msg를 찾는다. 

예상과는 다르게 일회용 메시지가 2번 출력된다. 

그 이유는 redirect는 요청이 2번 가기 때문에 2번째 요청에는 새로운 요청이 가게 된다.

처음에 저장된 request객체는 사라지고 새로운 request 객체가 만들어진다. 
그래서 이걸 읽을 수 없는 것이다.

login.html 파일에 있는 request의 이름은 같은데 다른 객체에서 msg를 찾으니 msg가 제대로 출력되지 않는 것이다. 

-> 제대로 나오게 하려면 redirect를 forward로 바꿔줘야 한다.  

redirect와 달리 같은 request를 사용하기 때문에 forward로 하면 request에 저장된 게 날라가지 않는다.
(같은 request에 있는거니까!)

 

        if(loginCheck(id,pwd)) {
            // 2. 일치하면, userInfo.html
            model.addAttribute("id", id);
            model.addAttribute("pwd", pwd);

            return "userInfo"; // userInfo.html
        } else {
            // 일치하지 않으면, login.html로 이동
//            String msg = URLEncoder.encode("id 또는 패스워드가 일치하지 않습니다.", "utf-8");
            String msg ="id 또는 패스워드가 일치하지 않습니다.";
            model.addAttribute("msg", msg);
            model.addFlashAttribute("msg","일회용 메시지");
            req.setAttribute("msg", "request에 저장된 msg");

            return "forward:/";

forward로 수정 후 index.html에

<h1 th:text = "${#request.getAttribute('msg')}"></h1>

문장을 추가해준다. 

<div layout:fragment="content">
    <h1>환영합니다.</h1>
    <h1 th:text = "${#request.getAttribute('msg')}"></h1>
</div>

요청이 1번만 간 것을 확인할 수 있다. (forward - 1번 요청 / 1번 응답)

 

            return "redirect:/";

forward를 redirect로 변경하면 다음과 같이 요청이 2번 가게된다. 

 

'SpringBoot' 카테고리의 다른 글

ch3 01. Spring DI의 원리(1)  (0) 2023.07.18
ch2 16. thymeleaf 사용하기  (0) 2023.07.18
ch2 14. forward와 redirect - 이론  (0) 2023.07.14
ch2 13. filter와 interceptor  (0) 2023.07.07
ch2 12. Thymeleaf로 레이아웃 적용하기  (0) 2023.07.07