Spring MVC 패턴

tabi·2023년 6월 26일
0

Spring

목록 보기
2/15
post-thumbnail

Spring MVC 패턴에 대해 알아보자.

1. Spring MVC

  • MVC의 요청과 처리과정
  • 요청 처리 과정
    브라우저 -> 요청url -> 프론트컨트롤러(클래스명: DispatcherServlet)가 요청을 받음 -> 요청에 대해 일을 처리할 객체를 찾도록 HandlerMapping에게 명령 -> HandlerMapping이 일을 처리할 객체를 찾아 프론트컨트롤러에 반환 -> 프론트 컨틀롤러는 다시 HandlerAdapter라는 스프링 Bean 객체에게 요청을 처리할 것(하위 bean들이 일을 처리할 것)을 명령 -> 요청을 처리하면 다시 HandlerAdapter가 처리한 결과를 받고, 이를 다시 프론트컨트롤러(DispatcherServlet)에게 보냄 -> 프론트컨트롤러는 이를 ViewResolver에게 보내 컨트롤러의 실행 결과를 보여줄 View 검색 -> 프론트컨트롤러가 해당 View에게 응답 생성하도록 요청하고 -> View가 응답생성
  • MVC 주요 구성 요소

2. 실습 준비

  1. 테이블 생성
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 -- 가입일자
);
  1. 패키지 생성
  • 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

  1. 처리 순서
    클라이언트에서 요청(*.htm) -> FrontController
  • 컨트롤러 메서드의 파라미터와 리턴 타입

3. 실습

3-1. Controller 생성

  1. CustomerController.java 코드 작성
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") 등 매핑을 제대로 해주지 않으면 이상하게 위 오류가 뜨는 경우가 있다!
  • 당황하지 말고 제대로 매핑 해주었는지 확인해보자.
  1. server-context.xml 수정
<?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>
  1. NoticeDao.java에 @Repository 붙여주기

3-2. 글쓰기 기능 추가하기

  1. CustomerController.java 코드 수정
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
  1. noticeReg.jsp에 CSRF 토큰 추가
<%@ 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">
									&nbsp;<input name="title"/>
								</dd>
							</dl>				
													
							<dl class="article-detail-row">
								<dt class="article-detail-title">
									첨부파일
								</dt>
								<dd class="article-detail-data">
									&nbsp;<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>

3-3. 글 수정 기능 추가하기

  1. CustomerController.java에 코드 추가
	//글 수정 ?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";
	}
  1. noticeDetail.jsp 코드 수정
  • seq 번호 가지고 이동해 해당 게시글 수정할 수 있도록
<a class="btn-edit button" href="noticeEdit.htm?seq=${notice.seq}">수정</a>
  1. noticeEdit.jsp 코드 수정
  • 고정값에서 ${notice.content} 태그 값들로 바꾸어준다.
  1. 수정된 글 저장을 위해 CustomerController.java에 코드 추가
	//글 수정 제출
	@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";
	}
  1. noticeEdit.jsp의 수정 버튼을 submit으로 바꾸어주고 CSRF 추가
<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>			

3-4. 글 삭제 기능 추가하기

  1. CustomerController.java에 코드 추가
	 //글 삭제
	  @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";
	  }
  1. noticeDetail.jsp에 코드 추가
						<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>
profile
개발 공부중

0개의 댓글