Spring MVC 패턴에 대해 알아보자.
- 요청 처리 과정
브라우저 -> 요청url -> 프론트컨트롤러(클래스명: DispatcherServlet)가 요청을 받음 -> 요청에 대해 일을 처리할 객체를 찾도록 HandlerMapping에게 명령 -> HandlerMapping이 일을 처리할 객체를 찾아 프론트컨트롤러에 반환 -> 프론트 컨틀롤러는 다시 HandlerAdapter라는 스프링 Bean 객체에게 요청을 처리할 것(하위 bean들이 일을 처리할 것)을 명령 -> 요청을 처리하면 다시 HandlerAdapter가 처리한 결과를 받고, 이를 다시 프론트컨트롤러(DispatcherServlet)에게 보냄 -> 프론트컨트롤러는 이를 ViewResolver에게 보내 컨트롤러의 실행 결과를 보여줄 View 검색 -> 프론트컨트롤러가 해당 View에게 응답 생성하도록 요청하고 -> View가 응답생성
CREATE TABLE NOTICES
(
"SEQ" VARCHAR2(10 BYTE), --글번호
"TITLE" VARCHAR2(200 BYTE), --제목
"WRITER" VARCHAR2(50 BYTE), --작성자
"CONTENT" VARCHAR2(4000 BYTE), --내용
"REGDATE" TIMESTAMP (6), --작성일
"HIT" NUMBER, --조회수
"FILESRC" VARCHAR2(500 BYTE)
);
CREATE TABLE "MEMBER"
(
"ID" VARCHAR2(50 BYTE), -- 회원 ID
"PWD" VARCHAR2(50 BYTE), --비밀번호
"NAME" VARCHAR2(50 BYTE), -- 이름
"GENDER" VARCHAR2(10 BYTE), -- 성별
"BIRTH" VARCHAR2(10 BYTE), --생일
"IS_LUNAR" VARCHAR2(10 BYTE), --음력/양력
"CPHONE" VARCHAR2(15 BYTE), --휴대폰 번호
"EMAIL" VARCHAR2(200 BYTE), -- 이메일
"HABIT" VARCHAR2(200 BYTE), -- 취미
"REGDATE" DATE -- 가입일자
);
org.doit.ik
org.doit.ik.controller = MVC
org.doit.ik.domain = DTO, VO
org.doit.ik.mapper = mybatis
org.doit.ik.persistence = DAO
org.doit.ik.service = Service
src/main/webapp/css, customer(공지사항), images, joinus(회원관리), index, xss, index.jsp 복붙
customer/notice.htm 은 요청 URL
web.xml의 url-pattern *.htm으로 변경
/customer/notice.htm 요청 -> NoticeController
ModelAndView: 응답할 view와 model 객체를 함께 저장
NoticeController, NoticeDetailController를 사용하는 예제
Notices, Meme
package org.doit.ik.controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.doit.ik.domain.NoticeVO;
import org.doit.ik.persistence.NoticeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class CustomerController {
@Autowired
private NoticeDao noticeDao;
//컨트롤러 역할을 하는 메서드 = "컨트롤러 메서드"
//어떤 요청이 들어왔을 때 리퀘스트를 mapping 하는지
//방식은 생략되면 기본적으로 get
//@RequestMapping(value= {"/customer/notice.htm"})
//@RequestMapping("/customer/notice.htm")
@GetMapping("/customer/noticeDetail.htm") //위의 세개 모두 동일한 의미
public ModelAndView noticeDetail(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView("noticeDetail.jsp");
String seq = request.getParameter("seq");
NoticeVO notice = this.noticeDao.getNotice(seq);
mav.addObject("notice", notice);
return mav;
}
@GetMapping("/customer/notice.htm")
public String notices(
@RequestParam(value="page", defaultValue="1") int page ,
@RequestParam(value="field", defaultValue="title") String field ,
@RequestParam(value="query", defaultValue="") String query
, Model model ) throws Exception {
model.addAttribute("message","hello world");
List<NoticeVO> list = this.noticeDao.getNotices(page, field, query);
model.addAttribute("list", list);
return "notice.jsp";
}
}//class
/*
* @Controller public class CustomerController {
*
* @Autowired private NoticeDao noticeDao;
*
* //컨트롤러 역할을 하는 메서드 = "컨트롤러 메서드" //어떤 요청이 들어왔을 때 리퀘스트를 mapping 하는지 //방식은 생략되면
* 기본적으로 get //@RequestMapping(value= {"/customer/notice.htm"})
* //@RequestMapping("/customer/notice.htm")
*
* @GetMapping("/customer/notice.htm") //위의 세개 모두 동일한 의미 public ModelAndView
* noticeDetail(HttpServletRequest request, HttpServletResponse response) throws
* Exception {
*
* ModelAndView mav = new ModelAndView("noticeDetail.jsp"); String seq =
* request.getParameter("seq"); NoticeVO notice = this.noticeDao.getNotice(seq);
* mav.addObject("notice", notice);
*
* return mav; }
*
* public ModelAndView notices( HttpServletRequest request , HttpServletResponse
* response , Model model , HttpSession session ) throws Exception {
*
* ModelAndView mav = new ModelAndView();
* mav.addObject("message","hello world");
*
* //?page=2&field=title&query=홍길동 String ppage= request.getParameter("page");
* String pfield = request.getParameter("field"); String pquery =
* request.getParameter("query");
*
* int page=1; String field = "title"; String query="";
*
* if(ppage != null && !ppage.equals("")) { page = Integer.parseInt(ppage);}
* if(pfield != null && !pfield.equals("")) { field = pfield;} if(pquery != null
* && !pquery.equals("")) { query = pquery;}
*
* List<NoticeVO> list = this.noticeDao.getNotices(page, field, query);
* mav.addObject("list", list);
*
* mav.setViewName("notice.jsp");
*
* return mav; }
*
* }//class
*/
Error! 스프링 java.sql.SQLException: 결과 집합을 모두 소모했음 오류
- @GetMapping("/customer/notice.htm"), @GetMapping("/customer/noticeDetail.htm") 등 매핑을 제대로 해주지 않으면 이상하게 위 오류가 뜨는 경우가 있다!
- 당황하지 말고 제대로 매핑 해주었는지 확인해보자.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<!--<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" /></beans:bean> -->
<context:component-scan base-package="org.doit.ik" />
</beans:beans>
package org.doit.ik.controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.doit.ik.domain.NoticeVO;
import org.doit.ik.persistence.NoticeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/customer/*")
public class CustomerController {
@Autowired
private NoticeDao noticeDao;
//@RequestMapping(value="/noticeReg.htm", method=RequestMethod.POST)
@PostMapping("/noticeReg.htm")
public String noticeReg(NoticeVO notice) throws Exception{
//책 pg 358 커맨드 객체 NoticeVO notice 사용
//로그인 인증(세션) notice.setWriter("kenik");
int insertcount = this.noticeDao.insert(notice);
if(insertcount ==1) {
return "redirect:notice.htm"; //redirect: == response.sendRedirect()
}else
return "noticeReg.jsp?error";
}
//notice.jsp에서 링크태그[글쓰기]버튼 클릭 시
//@RequestMapping(value="/noticeReg.htm", method=RequestMethod.GET)
@GetMapping("/noticeReg.htm")
public String noticeReg() throws Exception{
return "noticeReg.jsp";
}
@GetMapping("/noticeDetail.htm")
public String noticeDetail(
@RequestParam("seq") String seq
, Model model
) throws Exception{
NoticeVO notice = this.noticeDao.getNotice(seq);
model.addAttribute("notice", notice);
return "noticeDetail.jsp";
}
@GetMapping("/notice.htm")
public String notices(
@RequestParam(value="page", defaultValue="1") int page ,
@RequestParam(value="field", defaultValue="title") String field ,
@RequestParam(value="query", defaultValue="") String query
, Model model ) throws Exception {
model.addAttribute("message","hello world");
List<NoticeVO> list = this.noticeDao.getNotices(page, field, query);
model.addAttribute("list", list);
return "notice.jsp";
}
}//class
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>index</title>
<link href="../css/customer.css" type="text/css" rel="stylesheet" />
</head>
<body>
<div id="header">
<div class="top-wrapper">
<h1 id="logo"><a href="/"><img src="../images/logo.png" alt="뉴렉처" /></a></h1>
<h2 class="hidden">메인메뉴</h2>
<ul id="mainmenu" class="block_hlist">
<li>
<a href="">학습가이드</a>
</li>
<li>
<a href="" >과정선택</a>
</li>
<li>
<a href="" >인기과정</a>
</li>
</ul>
<form id="searchform" action="" method="get">
<fieldset>
<legend class="hidden">
과정검색폼
</legend>
<label for="query">과정검색</label>
<input type="text" name="query" />
<input type="submit" class="button" value="검색" />
</fieldset>
</form>
<h3 class="hidden">로그인메뉴</h3>
<ul id="loginmenu" class="block_hlist">
<li>
<a href="../index.jsp">HOME</a>
</li>
<li>
<a href="../joinus/login.jsp">로그인</a>
</li>
<li>
<a href="../joinus/join.jsp">회원가입</a>
</li>
</ul>
<h3 class="hidden">회원메뉴</h3>
<ul id="membermenu" class="clear">
<li>
<a href=""><img src="../images/menuMyPage.png" alt="마이페이지" /></a>
</li>
<li>
<a href="notice.jsp"><img src="../images/menuCustomer.png" alt="고객센터" /></a>
</li>
</ul>
</div>
</div>
<div id="visual" class="customer">
<div class="top-wrapper">
</div>
</div>
<div id="main">
<div class="top-wrapper clear">
<div id="content">
<h2>공지사항</h2>
<h3 class="hidden">방문페이지위치</h3>
<ul id="breadscrumb" class="block_hlist">
<li>HOME</li>
<li>
고객센터
</li>
<li>
공지사항등록
</li>
</ul>
<form action="" method="post">
<div id="notice-article-detail" class="article-detail margin-large" >
<dl class="article-detail-row">
<dt class="article-detail-title">
제목
</dt>
<dd class="article-detail-data">
<input name="title"/>
</dd>
</dl>
<dl class="article-detail-row">
<dt class="article-detail-title">
첨부파일
</dt>
<dd class="article-detail-data">
<input type="file" id="txtFile" name="file" />
</dd>
</dl>
<div class="article-content" >
<textarea id="txtContent" class="txtContent" name="content"></textarea>
</div>
</div>
<p class="article-comment margin-small">
<input class="btn-save button" type="submit" value="저장" />
<a class="btn-cancel button" href="notice.jsp">취소</a>
</p>
<input type="hidden" name="${ _csrf.parameterName }" value="${ _csrf.token }">
</form>
</div>
<div id="navi">
<h2>고객센터</h2>
<h3 class="hidden">고객센터메뉴</h3>
<ul id="navi-menu">
<li>
<a href="">뉴렉처소식</a>
</li>
<li>
<a href="" class="current">공지사항</a>
</li>
<li>
<a href="">1:1 고객문의</a>
</li>
<li>
<a href="">학습도구</a>
</li>
<li>
<a href="">학습안내</a>
</li>
</ul>
<h3 id="fav-title">추천사이트</h3>
<ul class="margin-small">
<li>
<a href="http://www.answeris.net"><img src="../images/answeris.png" alt="앤서이즈" /></a>
</li>
<li>
<a href="http://www.microsoft.com"><img src="../images/microsoft.png" alt="마이크로소프트" /></a>
</li>
<li>
<a href="http://www.w3c.org"><img src="../images/w3c.png" alt="W3C" /></a>
</li>
</ul>
</div>
</div>
</div>
<div id="footer">
<div class="top-wrapper">
<h2><img src="../images/footerLogo.png" alt="뉴렉처"/></h2>
<p>
<address>
사업자등록번호 : 000-00-00000000 통신판매업신고 : 서울 0000-000 관리자 : 홍길동
<br/>
주소 : 서울시 000구 001동 000-0 00빌딩 0층 전화 : 02-000-0000 팩스 : 02-000-0000
</address>
</p>
<p>
Copyright ⓒ newlecture.com 2012-2012 All Right Reserved. Contact master@newlecture.com for more information
</p>
</div>
</div>
</body>
</html>
//글 수정 ?seq=1
@GetMapping("/noticeEdit.htm")
public String noticeEdit(@RequestParam("seq") String seq, Model model) throws Exception{
NoticeVO notice = this.noticeDao.getNotice(seq);
model.addAttribute("notice", notice);
return "noticeEdit.jsp";
}
<a class="btn-edit button" href="noticeEdit.htm?seq=${notice.seq}">수정</a>
//글 수정 제출
@PostMapping("/noticeEdit.htm")
public String noticeEdit(NoticeVO notice) throws Exception{
int updatecount = this.noticeDao.update(notice);
if(updatecount ==1) {
return "redirect:noticeDetail.htm?seq=" + notice.getSeq(); //redirect: == response.sendRedirect()
}else
return "redirect:notice.htm";
}
<p class="article-comment margin-small">
<button class="btn-save button" type="submit">수정</button>
<!-- <a class="btn-save button" href="noticeDetail.htm?seq=${notice.seq}">수정</a> -->
<a class="btn-cancel button" href="noticeDetail.htm?seq=${notice.seq}">취소</a>
</p>
<input type="hidden" name="${ _csrf.parameterName }" value="${ _csrf.token }">
</form>
//글 삭제
@GetMapping("/noticeDel.htm")
public String noticeDel(@RequestParam("seq") String seq) throws Exception {
int deleteCount = this.noticeDao.delete(seq);
if(deleteCount==1) {
return "redirect:notice.htm";
} else return "redirect:noticeDetail.htm?seq=" + seq + "&error";
}
<a class="btn-del button" href="noticeDel.htm?seq=${notice.seq}">삭제</a>
<script>
$(".btn-del.button").on("click", function(event) {
if(!confirm("정말 삭제할까요?")){
event.preventDefault();
}
});
</script>