package com.sp.app.test4;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller("test4.testController")
@RequestMapping("/test4/*")
public class TestController {

	@GetMapping("write")
	public String form() throws Exception {
		return "test4/write";
	}
	
	@PostMapping("write")
	public String submit(User dto,
			RedirectAttributes rAttr
			) throws Exception {
		String s = "회원가입을 축하합니다.";
		
		// DB 작업
		
		// 리다이렉트한 페이지에 값 넘기기(내부적으로 세션을 활용함)
		rAttr.addFlashAttribute("dto", dto);  // 한번만 dto가 나옴
		rAttr.addFlashAttribute("msg", s);
		
		return "redirect:/test4/complete";
	}
	
	@GetMapping("complete")
	public String complete(@ModelAttribute("dto") User dto) throws Exception {
		// @ModelAttribute("dto") 가 없으면 아래에서는 null이 된다.
		// F5를 눌러 새로 고침을 하면 초기화 되어 아래 이름은 null이 된다.
		// jsp에서 출력되는 내용은 위 메소드의 rAttr.addFlashAttribute("dto", dto); 내용이다. 
		System.out.println(dto.getName() + " : " + dto.getId());
		 
		return "test4/result";
	}
}
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3> 리다이렉트한 페이지에 값 넘기기 </h3>

<form action="${pageContext.request.contextPath}/test4/write"
	method="post">
	<p>이름 : <input type="text" name="name"> </p>
	<p>아이디 : <input type="text" name="id"> </p>
	<p>비밀번호 : <input type="text" name="pwd"> </p>
	<p>생년월일 : <input type="text" name="birth"> </p>
	<p> <button type="submit">확인</button> </p>
</form>

</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3> 결 과 </h3>

<p> 이름 : ${dto.name} </p>
<p> 아이디 : ${dto.id} </p>
<p> 비밀번호 : ${dto.pwd} </p>
<p> 생일 : ${dto.birth} </p>

<p> ${msg} </p>

</body>
</html>
package com.sp.app.test2;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller("test2.testController")
@RequestMapping("/test2/*")
public class TestController {

	@RequestMapping("main")
	public String execute() throws Exception {
		return "test2/main";
	}
	
	@GetMapping("header")
	public String headerInfo(
			@RequestHeader("Accept-Language") String lang,
			@RequestHeader("User-Agent") String agent,
			HttpServletRequest req,
			Model model
			) throws Exception {
		
		String referer = req.getHeader("Referer"); // 이전 주소
		if(referer == null) {
			referer = "";
		}
		
		String s = "헤더 정보...<br>";
		s += "클라이언트 언어 정보 : " + lang + "<br>";
		s += "클라이언트 브라우저 및 OS : " + agent + "<br>";
		s += "이전 클라이언트 URL : "+ referer;
		
		model.addAttribute("msg", s);
		
		return "test2/result";
	}
	
	@RequestMapping("setCookie")
	public String cookieSet(
			HttpServletResponse resp
			) throws Exception {
		// 쿠키 설정하기(유효시간 : 브라우저를 닫으면 쿠키가 제거됨. 기본)
		Cookie ck = new Cookie("subject", "spring");
		resp.addCookie(ck);
		
		return "redirect:/test2/main";
	}
	
	// @CookieValue : 쿠키 가져오기. 기본 required는 true로 쿠키가 없으면 400에러
	// 	defaultValue 속성으로 쿠키값이 없는 경우 초기값 부여 가능
	@RequestMapping("getCookie")
	public String cookieGet(
			@CookieValue(name="subject", defaultValue = "") String subject,
			Model model
			) throws Exception {
		
		String s = "쿠키내용 : " + subject;
		
		model.addAttribute("msg", s);
		return "test2/result";
	}
	
}
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<p>
	<a href="${pageContext.request.contextPath}/test2/header"> 헤더정보확인 </a>
</p>

<p>
	<a href="${pageContext.request.contextPath}/test2/setCookie"> 쿠키설정 </a>
</p>

<p>
	<a href="${pageContext.request.contextPath}/test2/getCookie"> 쿠키확인 </a>
</p>


</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<p>
 ${msg}
</p>

</body>
</html>
package com.sp.app.join;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

/*
 @SessionAttributes
 - 모델의 객체를 세션에 저장하여 뷰(jsp)에서 공유
 - Controller에서 사용
 - 용도
 	스프링 form태그 라이브러리를 이용할 경우
 	여러 단계에 걸쳐 입력된 값을 처리할 경우(지속적으로 값을 유지)
 		double submit 방지 - 브라우저의 뒤로가기 안됨
 
 */

@SessionAttributes("user") // 클래스명과같아야함
@Controller("join.joinController")
@RequestMapping("/join/*")
public class JoinController {
	
	@ModelAttribute("user") // user 이름으로 모든 페이지가 공유할 수 있는 객체를 만듦
	public User command() {
		return new User(); // 세션에 저장할 객체 메모리 할당
	}
	
	@RequestMapping(value = "main", method = RequestMethod.GET)
	public String joinForm(@ModelAttribute("user") User user) throws Exception {
		// 회원가입 처음화면(step1.jsp) 출력
		return "join/step1";
	}
	
	// @ModelAttribute("user")는 @SessionAttributes("user")에서 설정한
	// 설정이름이 동일하므로 세션에 저장된 user을 사용함
	// User 클래스명 첫글자가 소문자인 이름과 동일한 경우 생략가능
	@RequestMapping(value = "step1", method = RequestMethod.POST)
	public String step1Submit(@ModelAttribute("user") User user) throws Exception {
		
		// 회원가입 두번째 화면 출력
		return "join/step2";
	}
	
	@RequestMapping(value = "step2", method = RequestMethod.POST)
	public String step2Submit(@ModelAttribute("user") User user,
			SessionStatus sessionStatus,
			Model model
			) throws Exception {
		// 회원가입 정보를 DB에 저장
		
		String s = "아이디:" +user.getId() + "<br>";
		s += "이름:"+user.getName() + "<br>";
		s += "이메일:"+user.getEmail() + "<br>";
		s += "패스워드:"+user.getPwd() + "<br>";
		s += "전화번호:"+user.getTel() + "<br>";
		
		// 세션에 저장된 내용 지우기
		sessionStatus.setComplete();
		model.addAttribute("msg", s);
		return "join/complete";
	}
	
}
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<form method="post"
	action="${pageContext.request.contextPath}/join/step1">
	
	<p> 이름 : <input type="text" name="name" value="${user.name}"> </p>
	<p> 이메일 : <input type="text" name="email" value="${user.email}"> </p>
	<p>
		<button type="submit">다음단계</button>
	</p>

</form>

</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<form method="post" 
	action="${pageContext.request.contextPath}/join/step2">
	
	<p> 아이디 : <input type="text" name="id" value="${user.id}"> </p>
	<p> 패스워드 : <input type="password" name="pwd"> </p>
	<p> 전화번호: <input type="text" name="tel" value="${user.tel}"> </p>
	<p>
		<button type="button"
			onclick="location.href='${pageContext.request.contextPath}/join/main';" >이전단계</button>
		<button type="submit">회원가입</button>
	</p>

</form>

</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3> 환영합니다. </h3>
<p>
	${msg}
</p>

</body>
</html>
package com.sp.app.note;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller("note.noteController")
@RequestMapping("/note/*")
public class NoteController {
	@Autowired
	private NoteService service;
	
	@RequestMapping(value="request", method=RequestMethod.GET)
	public String form(Model model) {
		
		List<Note> listFriend = service.listFriend();
		
		model.addAttribute("friends", listFriend);
		
		return "note/write";
	}
	
	/*
	@RequestMapping(value = "request", method = RequestMethod.POST)
	public String formSubmit(Note dto, Model model) throws Exception {
		
		model.addAttribute("msg", dto.getMsg());
		model.addAttribute("list", dto.getRecipient());
		return "note/result";
	}
	*/
	@RequestMapping(value = "request", method = RequestMethod.POST)
	public String formSubmit(Note dto, Model model) throws Exception {
		
		model.addAttribute("dto", dto);
		return "note/result";
	}
	
}
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<style type="text/css">
* {
	margin: 0; padding: 0;
    box-sizing: border-box;
}

body {
	font-size: 14px;
	font-family: "맑은 고딕", 나눔고딕, 돋움, sans-serif;
}

a {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}
a:active, a:hover {
	text-decoration: underline;
	color: #F28011;
}

.btn {
	color: #333;
	border: 1px solid #333;
	background-color: #fff;
	padding: 4px 10px;
	border-radius: 4px;
	font-weight: 500;
	cursor:pointer;
	font-size: 14px;
	font-family: "맑은 고딕", 나눔고딕, 돋움, sans-serif;
	vertical-align: baseline;
}
.btn:hover, .btn:active, .btn:focus {
	background-color: #e6e6e6;
	border-color: #adadad;
	color:#333;
}
.boxTF {
	border: 1px solid #999;
	padding: 5px 5px;
	background-color: #fff;
	border-radius: 4px;
	font-family: "맑은 고딕", 나눔고딕, 돋움, sans-serif;
	vertical-align: baseline;
}
.selectField {
	border: 1px solid #999;
	padding: 4px 5px;
	border-radius: 4px;
	font-family: "맑은 고딕", 나눔고딕, 돋움, sans-serif;
	vertical-align: baseline;
}

.boxTA {
    border:1px solid #999;
    height:150px;
    padding:3px 5px;
    border-radius:4px;
    background-color:#fff;
	font-family: "맑은 고딕", 나눔고딕, 돋움, sans-serif;
	resize : none;
	vertical-align: baseline;
}

textarea:focus, input:focus {
	outline: none;
}

.title {
	width:100%;
	font-size: 16px;
	font-weight: bold;
	padding: 13px 0;
}

.container {
    width: 400px;
    margin: 30px auto;
}

.note-table {
	width: 100%;
	border-spacing: 0;
	border-collapse: collapse;
}

.note-table th, .note-table td {
	padding: 5px 0;
}

.left {
	text-align: left;
	padding-left: 7px;
}
.center {
	text-align: center;
}
.right {
	text-align: right;
	padding-right: 7px;
}
</style>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function() {
	$("#btnRight").click(function() {
		$("#friends option:selected").each(function() {
			$(this).appendTo("#recipient");
		});
	});
	
	$("#btnAllRight").click(function() {
		$("#friends option").each(function() {
			$(this).appendTo("#recipient");
		});
	});
	
	$("#btnLeft").click(function() {
		$("#recipient option:selected").each(function() {
			$(this).appendTo("#friends");
		});
	});
	
	$("#btnAllLeft").click(function() {
		$("#recipient option").each(function() {
			$(this).appendTo("#friends");
		});
	});$
});


function sendOk() {
	if($("#recipient option").length === 0) {
		alert("받는 사람을 먼저 추가하세요...");
		return;
	}
	
	$("#recipient option").prop("selected", true);
	
	if(! $("#msg").val().trim() ) {
		alert("내용을 입력하세요...");
		$("#msg").focus();
		return;
	}
	
	var f = document.noteForm;
	
	f.action = "${pageContext.request.contextPath}/note/request";
	f.submit();
}

</script>
</head>
<body>

<div class="container">
	<div class="title">
	   <h3><span>|</span> 쪽지 보내기</h3>
	</div>

	<form name="noteForm" method="post">
	<table class="note-table">
	<tr>
	    <td width="150"><span>친구목록</span></td>
	    <td width="100">&nbsp;</td>
	    <td width="150"><span>받는사람</span></td>
	</tr>
	
	<tr>
	    <td class="left">
	        <select name="friends" id="friends" multiple="multiple" class="selectField" style="width:130px; height:120px;">
	        	<c:forEach var="vo" items="${friends}">
	        		<option value="${vo.userId}">${vo.userName}</option>
	        	</c:forEach>
	        </select>
	    </td>
	    <td class="center">
		    <button type="button" class="btn" id="btnRight" style="display:block; width:80px;"> &gt; </button>
		    <button type="button" class="btn" id="btnAllRight" style="display:block;width:80px;"> &gt;&gt; </button>
		    <button type="button" class="btn" id="btnLeft" style="display:block;width:80px;"> &lt; </button>
		    <button type="button" class="btn" id="btnAllLeft" style="display:block;width:80px;"> &lt;&lt; </button>
	    </td>
	    <td class="left">
	        <select name="recipient" id="recipient" multiple="multiple" class="selectField" style="width:130px; height:120px;">
	        </select>
	    </td>
	</tr>
	<tr>
	    <td colspan="3">
	       <span>메시지</span>
	    </td>
	</tr>
	<tr>
	    <td colspan="3" class="left">
	        <textarea name="msg" id="msg" class="boxTA" style="height:60px; width: 98%;"></textarea>
	    </td>
	</tr>
	</table>
	
	<table class="table">
	<tr>
	    <td class="right">
	        <button type="button" class="btn" onclick="sendOk();"> 쪽지보내기 </button>
	    </td>
	</tr>
	</table>
	</form> 
</div>

</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<style type="text/css">
* {
	margin: 0; padding: 0;
    box-sizing: border-box;
}

body {
	font-size: 14px;
	font-family: "맑은 고딕", 나눔고딕, 돋움, sans-serif;
}

a {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}
a:active, a:hover {
	text-decoration: underline;
	color: #F28011;
}

.title {
	width:100%;
	font-size: 16px;
	font-weight: bold;
	padding: 13px 0;
}

.container {
    width: 400px;
    margin: 30px auto;
}
</style>

</head>
<body>

<div class="container">

    <div style="title">
       <h3><span>|</span> 쪽지 보내기 결과</h3>
    </div>

    <table style="width: 100%; margin: 10px auto 0px;">
    <tr height="30">
        <td width="100">받는사람</td>
        <!-- 
        <c:forEach var="vo" items="${list}" varStatus="status">
	        <td>${vo}</td>        
        </c:forEach>
         -->        
        <td>
	        <c:forEach var="vo" items="${dto.recipient}" varStatus="status">
		        ${vo}&nbsp;&nbsp;
	        </c:forEach>
        </td>                
    </tr>
    
    <tr>
        <td valign="top" style="margin-top: 5px;">메시지</td>
        <td>
        	<span style="white-space:pre;">${dto.msg}</span>
        </td>
    </tr>
    </table>
</div>

</body>
</html>
package com.sp.app.blog;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller("blog.blogController")
@RequestMapping("/blog/*")
public class BlogController {
	@Autowired
	private BlogService service;
	
	@RequestMapping(value = "main", method = RequestMethod.GET)
	public String main(Model model) {
		List<Blog> list = service.listBlog();
		
		model.addAttribute("listBlog", list);
		
		return "blog/main";
	}

/*
 	@PathVariable 애노테이션을 이용한 URI 템플릿
 	- URI 템플릿을 이용하여 REST 방식의 URL 매칭처리를 위한 애노테이션
 	- 블로그, 카페 등을 만들 때 유용
 	- 방법
 	  @RequestMapping 애노테이션의 값으로 {템플릿변수}를 사용
 	  @PathVariable를 이용하여  {템플릿변수}와 동일한 이름을 갖는 파라미터를 추가한다.
 		
 */
	@RequestMapping(value = "{blogIdx}/home")
	public String execute(
			@PathVariable long blogIdx,
			Model model) throws Exception {
		
		Blog dto = null;
		for(Blog vo : service.listBlog()) {
			if(vo.getBlogIdx() == blogIdx) {
				dto = vo;
				break;
			}
		}
		
		model.addAttribute("dto", dto);
		
		return "blog/home";
	}
	
}
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">

<style type="text/css">
* {
	margin: 0; padding: 0;
}

body {
	font-size: 13px; font-family: 맑은 고딕, 돋움;
}

div h3 {
	padding: 5px 5px;
}

div ul>li {
	list-style: none;
	padding: 5px 5px;
}

</style>

</head>
<body>

<div style="width: 500px; margin: 30px auto;">
	<h3> 블로그 리스트 </h3>
	
	<ul>
		<c:forEach var="dto" items="${listBlog}">
			<li>
				<a href="${pageContext.request.contextPath}/blog/${dto.blogIdx}/home">${dto.blogName}</a>
			</li>
		</c:forEach>
	</ul>
	
</div>

</body>
</html>​
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3>${dto.nickName} 블로그</h3>

<p> 블로그 주제 : ${dto.blogName} </p>
<p> ${dto.nickName} 블로그입니다. 방문하셨으면 방명록을 써주세요. </p>
<p>
  <a href="${pageContext.request.contextPath}/blog/main">돌아가기</a>
</p>

</body>
</html>

컨트롤러 및 핸들러 매핑 관련 애노테이션

 

@RequestParam

 - Controller 메소드의 파라미터와 웹 요청 파라미터를 매핑하기 위한 애노테이션이다.

 - @RequestParam이 붙은 메소드의 인수는 필수 요청 파라미터이다.

 요청 파라미터에 @RequestParam으로 설정한 파라미터가 존재하지 않으면 400에러가 발생한다.

 required 옵션 속성값을 false로 설정하면 해당 이름의 요청 파라미터가 존재하지 않은 경우, 메소드의 인수는 null을 갖는다. 

- defaultValue 속성에 기본값을 설정하면 해당 이름의 요청 파라미터가 존재하지 않은 경우, 메소드의 인수는 defaultValue속성에서 설정한 기본값을 갖는다. 

 

@ModelAttribute

1) 개요

 - Controller 메소드의 파라미터나 리턴 값을 Model 객체와 바인딩 하기 위한 애노테이션

 - @ModelAttribute는 실제적으로 ModelMap.addAttribute(이름, 값)와 같은 기능을 한다.

 - 사용 방법

 (1)요청 메소드의 매개변수에 사용

  요청 파라미터는 @ModelAttribute가 적용된 메소드의 파라미터(모델 객체)에 매핑되어 넘어온다.

 @ModelAttribute가 적용된 메소드의 파라미터는 포워딩 되는 View 페이지에서 사용 가능하다.

 (2)메소드 레벨(method level)로 사용

 View에서 사용할 데이터를 설정하는 용도로 사용

 @ModelAttribute가 설정된 메소드는 @RequestMapping이 적용된 메소드보다 먼저 호출

 @ModelAttribute 메소드 실행 결과로 리턴되는 객체는 자동으로 Model에 저장

 @ModelAttribute 메소드 실행 결과로 리턴된 객체를 View 페이지에서 사용 가능

 

2) 옵션

 - value : 바인드 하려는 Model 속성 이름 String

 

@RequestHeader

- HTTP 요청 헤더 값을 컨트롤러 메소드의 매개변수로 전달한다.

- @RequestHeader 로 설정한 헤더는 기본적으로 필수 헤더로 존재하지 않으면 400에러가 발생한다.

- required 속성을 이용해 필수여부를 설정할 수 있다.

- dafaultValue 속성을 이용해 헤더가 존재하지 않은 경우 기본 값으로 설정 가능하다.

 

@CookieValue

- HTTP 쿠키 값을 HttpServletRequest등을 통해 읽을 필요없이 컨트롤러 메소드의 인수로 전달 받을 수 있다.

- @CookieValue로 설정한 쿠키는 필수로 해당 쿠키가 존재하지 않으면 400 에러가 발생한다.

- required 속성을 이용해 필수여부를 설정할 수 있다.

- defaultValue 속성을 이용해 쿠키가 존재하지 않은 경우 기본 값으로 설정 가능하다.

 

@SessionAttributes

- Model 객체를 세션에 저장하고 사용하기 위한 애노테이션

- 클래스 레벨(type level)에서 선언할 수 있다.

- 지속적으로 사용자의 입력 값을 유지시키거나 여러 단계에 걸쳐 submit 되면서 완성되는 폼을 구성하는 경우에 사용 가능하다.

- 사용방법

 @SessionAttributes 는 컨트롤러에서 사용되며 애노테이션에서 설정한 모델객체를 세션에 저장하는 역할을 한다. 이후부터 해당 모델객체가 사용되면 세션에서 불러와서 사용하며, 뷰에서 이름으로 접근이 가능하다.

 @SessionAttribues를 컨트롤러 클래스에 붙이고 모델 이름을 인자로 넣어준다. 컨트롤러의 메소드가 만드는 모델 정보 중에 이름이 같은 것이 있으면 세션에도 저장하고 컨트롤러 메소드의 @ModelAttrbute 인자를 HTTP요청이 아니라 먼저 세션에서 검색하여 사용한다.

 @SessionAttributes로 세션에서 사용완료된 객체는 사용 후에 SessionStatus.setComplete() 메소드로 세션에 저장한 정보를 제거해야 한다.

 

@PathVariable

1) 개요

- @PathVariable은 메소드 매개 변수가 URI 템플릿 변수에 바인딩되어야 함을 나타내는 애노테이션이다. 요청 URI 매핑의 템플릿 변수를 처리하고 이를 메소드 매개 변수로 사용할 수 있다.

- @RequestMapping 애노테이션이 있는 핸들러 메소드에 지원된다.

- 메소드 매개 변수가 Map<String, String>이면 Map이 모든 경로 변수 이름 및 값으로 채워진다.

 

2) 옵션

- name : 바인드 할 경로 변수 이름으로 스프링 4.3.3부터 사용 가능 String

- required : 필수 여부 설정으로 기본값은 true이며 스프링 4.3.3부터 사용가능. boolean

- value : name의 별칭 String

 

3) REST(Representational State Transfer)

 (1) 개요

 - 자원을 이름(자원의 표현)으로 구분하여 해당 자원의 상태(정보)를 주고 받는 모든 것을 의미

 - 월드 와이드 웹(www)과 같은 분산 하이퍼 미디어 시스템을 위한 소프트웨어 개발 아키텍처의 한 형식. REST는 기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하기 때문에 웹의 장점을 최대한 활용할 수 있는 아키텍처 스타일로 네트워크 상에서 Client와  Server 사이의 통신 방식 중 하나

 - HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미

 - REST는 자원 기반의 구조(ROA, Resource Oriented Architecture) 설계의 중심에 Resource가 있고 HTTP Method를 통해 Resource를 처리하도록 설계된 아키텍쳐를 의미

 - 웹사이트의 이미지, 텍스트, DB 내용 등의 모든 자원에 고유한 ID 인 HTTP URI를 부여

 - CRUD Operation : Create : 생성 (POST) READ : 조회 (GET) Update : 수정 (PUT) Delete : 삭제(Delete) HEAD : header 정보 조회(HEAD)

 

 (2) REST API

 - API(Application Programming Interface) 데이터와 기능의 집합을 제공하여 컴퓨터 프로그램간 상호작용을 촉진하며, 서로 정보를 교환가능 하도록 하는 것

 - REST API의 정의

 REST 기반으로 서비스 API를 구현한 것

 OpenAPI, 마이크로 서비스(하나의 큰 애플리케이션을 여러 개의 작은 애플리케이션으로 쪼개어 변경과 조합이 가능하도록 만든 아키텍처) 등을 제공하는 업체 대부분은 REST API를 제공한다.

 - REST API 설계 기본 규칙

 -- URI는 정보의 자원을 표현해야 한다.

 -- 자원에 대한 행위는 HTTP Method(GET, PUT, POST, DELETE 등)로 표현한다.

 --- URI에 HTTP Method가 들어가면 안된다.

 --- URI에 행위에 대한 동사 표현이 들어가면 안된다. (CRUD 기능을 나타내는 것은 URI에 사용하지 않는다.)

 

(3) RESTful 서비스

- RESTful은 일반적으로 REST라는 아키텍처를 구현하는 웹 서비스를 나타내기 위해 사용되는 용어이다. "REST API"를 제공하는 웹 서비스를 "RESTful"하다고 할 수 있다.

- RESTful 서비스는 URI를 사용하여 리소스 이름을 지정한다.

- Spring은 @RequestMapping을 사용하여 요청에 대한 URI 템플릿을 정의하여 RESTful 서비스를 한다. @PathVariable은 템플릿 변수의 값을 추출하고 해당 값을 메소드 매개변수에 할당하는 데 사용한다.

MVC 웹 애플리케이션

컨트롤러 및 핸들러 맵핑

@Controller

- Controller의 역할을 수행한다고 명시.

- Controller는 필요한 비즈니스 로직을 호출하여 처리 결과(Model)와 처리 결과를 출력할 View 정보를 DispatcherServlet에 반환한다.

- Bean으로 등록된다.

- @Component의 구체화된 애노테이션으로 클래스에 붙인다.

- 빈 이름을 지정하지 않으면 클래스 이름의 첫 문자를 소문자로 바꾼 것이 빈의 이름(id)으로 빈을 등록한다.

- 형식 @Controller, @Controller("beanName")

 

@RequestMapping

- 요청에 대해 어떤 Controller, 어떤 메소드가 처리할지를 매핑하기 위한 애노테이션

 url을 class또는 method와 mapping 시켜주는 역할을 한다.

- 선언위치 : 클래스 또는 메소드

 1) class level(type level) 매핑 : 컨트롤러의 모든 메소드에서 처리하는 경우

 2) method level 매핑 : 요펑 url에 대한 해당 메소드에서 처리하는 경우

- GET, POST, PUT, DELETE 등으로 표현되는 모든 HTTP Request method 를 매핑 할 수 있다.

- @RequestMapping 시 따로 HTTP Method를 설정하지 않으면 모든 Method를 허용한다.

- @RequestMapping 애노테이션은 Ant 스타일의 패턴을 지원한다.

 1) ? : 1개의 문자와 매칭

 2) * : 0개 이상의 문자와 매칭

 3) ** : 0개 이상의 디렉터리와 매칭

 4) {spring:[a-z]+} : "spring"이라는 path variable로 정규문법 [a-z]+와 일치

- 상위 클래스에 @RequestMapping이 정의되어 있고 하위 클래스에서는 @RequestMapping을 재정의하지 않은 경우 @RequestMapping는 그대로 상속된다

- 상위 클래스의 클래스에 정의된 @RequestMapping과 하위 클래스의 메소드에 정의된 @RequestMapping의 조합이 가능하다.

- 스프링 4.3부터는 5가지 애노테이션을 추가하여 HTTP 요청 메소드별로 세분화 하였다.

 추가된 HTTP 요청 메소드별 매핑 애노테이션

@GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping

@GetMapping("/hello") 은 @RequestMapping(value="/hello", method=RequestMethod.GET)의 축약형이다.

@PostMaoong("/hello") 은@RequestMapping(value="/hello", method=RequestMethod.POST)의 축약형이다.

 

컨트롤러 메소드의 리턴 타입

1) ModelAndView 객체

- 처리결과를 담은 Model 객체와 View정보를 ModelAndView객체에 담아 반환

- ModelAndView 클래스는 컨트롤러의 처리 결과를 보여줄 뷰와 뷰에 전달할 값을 저장하는 용도로 사용

 

2) String

 - 뷰의 이름을 반환한다.

 

3) Model, ModelMap

 - 처리 결과를 Model 객체에 담아 반환한다.

- Model은 인터페이스이며, ModelMap은 클래스로 사용방법은 유사하다.

- ModelMap 하위 클래스인 ExtendedModelMap은 Model구현 클래스이다.

- 뷰의 이름은 RequestToViewNameTranslator를 통해매핑된 URL로 결정된다.

 URL 이 "/hello/main"인 경우 뷰의 잉름은 "hello/main"이 된다.

- Model 객체 생성 예

 Model model = ExtendedModelMap();

 

4) void

- 메소드의 인수로 HttpServletResponse파라미터가 존재하지 않는 경우 뷰의 이름은 RequestToViewNameTranslator를 통해 매핑된 URL로 결정된다. URL이 "/hello/main"인 경우 뷰의 이름은 "hello/main"이 된다.

- HttpServletResponse등을 사용해서 직접 응답을 처리하는 경우 사용할 수 있다.

 

5) Map

- 처리 결과를 Map 객체에 담아 반환한다.

- 뷰의 이름은 RequestToViewNameTranslator를 통해 매핑된 URL로 결정된다. URL이 "/hello/main"인 경우 뷰의 이름은 "hello/main"이 된다.

 

6) View

- View 객체를 직접 반환. 해당 View객체를 이용하여 뷰를 생성

- AbstractView 클래스 등을 상속받은 커스텀 뷰를 반환할 수 있다.

- 파일 다운로드, PDF 나Excel로 변환하여 다운로드를 구현할 경우에 사용할 수 있다.

 

포워딩과 리다이렉트

 

1) 포워딩(forwarding)

- "forward:"로 시작하는 View이름을 설정하며, forward는 디폴트로 생략 가능하다.

- 컨트롤러 메소드의 리턴 타입이 String인 경우 "bbs/list" 뷰 이름으로 포워딩

return "bbs/list";

- 컨트롤러 메소드의 리턴 타입이 ModelAndView인 경우 "bbs/list" 뷰 이름으로 포워딩

return new ModelAndView("bbs/list");

 

2) 리다이렉트(Redirect)

- RedirectView

 HttpServletResponse의 sendRedirect(location)를 호출해주는 기능을 가진 뷰

 실제 뷰가 생성되는 것이 아니라 URL만 만들어져 다른 페이지로 리다이렉트 된다.

 모델 정보가 있다면 URL 뒤에 파라미터로 추가된다.

- 리다이렉트 방법

 redirect: 로 시작하는 View이름을 설정

1) 컨트롤러 메소드의 리턴 타입이 String인 경우 "/main"으로 리다이렉트

return "redirect:/main";

2) 컨트롤러 메소드의 리턴 타입이 ModelAndView인 경우 "/main"으로 리다이렉트

return new ModelAndView("rediect:/main");

 

RedirectView 오브젝트를 직접 만들어 반환

return new ModelAndView(new RedirectView("/main") );

 

컨트롤러 핸들러 메소드의 매개변수

1) Servlet API

- HttpServletRequest, HttpServletResponse, HttpSession

 

2) java.util.Locale

- DispatcherServlet의 지역정보 리졸버(Locale Resolver)가 결정한 Locale객체

 

3) java.io.InputStream / java.io.Reader

- request(요쳥 정보) body 영역을 직접 읽어 들이기 위한 InputStream 또는 Reader 객체

 

4) java.io.OutputStream / java.io.Writer

- response 정보를 직접 보내기 위한 OutputStream 또는 Writer 객체

 

5) Map, Model, ModelMap

- 뷰에 전달할 모델 데이터 설정

 

6) 커맨트 객체

- HTTP 요청 파라미터를 저장할 객체

 

7) RedirectAttrivutes

- 리다이렉트 되는 페이지에 데이터를 전달한다.

- 주요 메소드

 (1) addAttribute(String attributeName, Object attributeValue)

 - 리다이렉트 되는 URL에 쿼리 스트링으로 데이터를 전달해준다.

 - 쿼리 스트링으로 전달되므로 String으로 변환이 가능한 원시타입만 가능하다.

 - F5를 눌러 새로고침을 해도 값이 유지된다.

 

 (2) addFlashAttribute(String attributeName, Object attributeValue)

 - 리다이렉트 되는 페이지에 데이터를 전달하며, 주소 뒤에 표시되지 않는다.

 - 세션을 이용하여 값을 전달 후에 삭제된다. 즉, 일회성으로 F5를 눌러 새로고침을 하면 값이 사라진다.

 

8) SessionStatus

 - @SessionAttributes 애노테이션을 사용하여 session에 저장된 데이터를 정리하는데 활용하는 인터페이스

 - SessionStatus의 setComplete() 메소드를 호출하면 현재 컨트롤러에서 @SessionAttributes에 의해 세션에 저장된 모델 데이터를 모두 삭제한다.

 

9) Errors, BindingResult

 - Errors는 유효성 검증 결과를 저장할 때 사용

 - Errors의 하위 인터페이스로 폼 값을 커맨드 객체에 바인딩한 결과를 저장하고 에러코드로 메세지를 가져온다.

Spring Web MVC 란 ?

- Spring Web MVC는 Servlet API를 기반으로 구축 된 웹 프레임 워크

- Model 2 MVC 패턴을 지원

- Spring Web MVC는 유연하고 느슨하게 결합된 웹 애플리케이션을 개발하는 데 사용할 수 있는 Model-View-Controller아키텍쳐 및 준비된 구성요소를 제공한다.

- MVC 패턴은 애플리케이션의 다양한 측면(입력 로직, 비즈니스 로직 및 UI로직)을 분리하는 동시에 이러한 요소간에 느슨한 결합을 제공한다.

 1) Model은 애플리케이션 데이터를 캡슐화하며 일반적으로 POJO로 구성

 2) View는 모델 데이터를 렌더링하는 역할을 하며 일반적으로 클라이언트의 브라우저가 해석 할 수 있는 HTML output을 생성

 3) Controller는 사용자 요청을 처리하고 적절한 모델을 구축하고 렌더링을 위해 뷰를 전달

- Spring Web MVC는 모든 HTTP 요청 및 응답을 처리하는 DispatcherServlet을 기반으로 설계되었다.

- 스프링 프레임워크 5 버전부터 리액티브 스택 기반으로 웹 애플리케이션을 개발할 때 사용할 수 있는 스프링 Webflux를 제공

 

Spring MVC Architecture

- Model, View, Controller를 분리한 디자인 패턴

- Model

 1) 애플리케이션의 상태(data)를 나타낸다

 2) 일반적으로 POJO로 구성

 3) Java Beans

- View

 1) 디스플레이 데이터 또는 프리젠테이션

 2) Model data의 렌더링을 담당하며, HTML output을 생성

 3) JSP, 타임리프(Thymeleaf), 그루비(Groovy), 프리마커(Freemarket) 등 여러 Template Engine이 있다.

- Controller

 1) View와 Model 사이의 인터페이스 역할

 2) Model, View에 대한 사용자 입력 및 요청을 수신하여 그에 따라 적절한 결과를 Model에 담아 View에 전달.

 즉, Model Object와 이 Model을 화면에 출력할 View Name을 반환

 

String MVC 처리과정

1) DispatcherServlet이 클라이언트의 요청을 받는다.

2) DispatcherServlet은 적절한 컨트롤러를 선택하는 작업을 HandlerMapping에 전달한다. HandlerMapping은 들어오는 요청 URL에 매핑된 컨트롤러를 선택하고, 선택된 Handler 및 Controller를 DispatcherServlet에 반환한다.

3) DispatcherServlet은 Controller의 비즈니스 로직 실행 작업을 HandlerAdapter에 전달한다.

4) GandlerAdapter는 Controller의 비즈니스 로직 프로세스를 호출한다.

5) 컨트롤러는 비즈니스 로직을 실행하고 처리 결과를 Model에 설정하고 뷰의 논리적 이름을 HandlerAdapter에 반환한다.

6) DispatcherServlet은 View 이름에 해당하는 View를 해결하는 작업을 ViewResolver에 전달한다. ViewResolver는 View 이름에 매핑된 View로 전달한다.

8) View는 모델 데이터를 렌더링하고 응답을 반환한다.

 

Spring MVC 주요 컨포넌트

1) DispatcherSerlvet

- Spring Framework가 제공하는 Servlet 클래스

- 사용자의 요청을 받는다.

- DispatcherServlet이 받은 요청은 HandlerMapping으로 넘어간다.

- Front Controller Pattern

 모든 리소서(Resource)요청을 처리해주는 하나의 컨트롤러(Controller)를 두는 패턴

 DispatcherServlet이 Front Controller의 역할을 수행한다.

 

2) HandlerMapping

- 사용자의 요청을 처리한 Controller를 찾는다.

- 요청 uri에 해당하는 Controller 정보를 저장하는 table을 가진다. 클래스에 @RequestMapping("/uri") annotation을 명시하면 해당 URL에 대한 요청이 들어왔을 때 table에 저장된 정보에 따라 해당 클래스 또는 메소드에 Mapping한다.

 

3) Controller

- 요청에 맵팽된 Controller에게 위임됨(@Controller)

- @RequestMapping을 통하여ㅠ 요청을 처리할 메소드를 호출

- 필요한 비즈니스 로직을 처리

- View에 전달할 결과 데이터(Model)와 이동할 화면(View) 정보를 스프링 MVC가 제공하는 ModelAndView 인스턴스에 담아 DispatcherServlet에 반환

- DispatcherServlet는 ViewResolver에게 뷰 논리정보(View Name)를 전달

 

4) ModelAndView

- Controller에서 처리 결과를 View에 전달할 결과 데이터(Model)와 이동할 화면(View)정보를 담는 클래스

- Controller메소드에서 리턴 타입이 String 인 경우에도 핸들러에서 ModelAndView인스턴스에 View정보를 넣는다.

 

5) ViewResolver

- ViewResolver는 View Name을 이용해 알맞는 view객체를 찾는다.

 1) Controller가 반환한 View Name에 prefix와 suffix를 적용하여 View Object를 반환한다.

 2) View Name : 'home', prefix : '/WEB-INF/views/', suffix : '.jsp'인 경우 => '/WEB-INF/views/home.jsp' 라는 위치의 View(JSP)에 Controller에게 받은 Model을 전달한다.

- View에 Model을 redering 하여 View 인스턴스를 다시 DispatcherServlet에 반환한다.

- DispatcherServlet은 최종 결과를 클라이언트에 응답

 

6) View

- 결과 데이터인 Model 객체를 출력한다.

 

+ Recent posts