Model 2 Pattern
- 처리는 Servlet 과 Java 클래스들이 담당하고 출력은 JSP 가 담당하도록 하는 패턴
- JSP 페이지에서는 자바 코드를 사용하지 않는 것을 원칙으로 합니다.
HttpServlet
- URL로 호출할 수 있는 Java EE의 클래스
- jsp는 HttpServlet의 변형
1.HttpServlet 의 생성 및 실행
- HttpServlet을 만들 때 매핑이 될 URL을 설정해도 되고 HttpServlet 클래스를 만들고 난 후 web.xml 파일에서 URL 매핑을 해도 됩니다.
- 양쪽에서 모두 하면 에러가 발생
- 서블릿 위에서 어노테이션을 이용했다면 web.xml 에서 매핑을 하면 안됩니다.
- web.xml을 이용해서 매핑을 할려면 서블릿 위의 어노테이션을 삭제해야 합니다.
- 서블릿은 별도의 실행방법이 없고 URL을 호출하면 자동으로 호출되고 이 때 파라미터 전송방식에 따라 doGet 이나 doPost 메소드가 자동으로 호출됩니다.
- 서블릿에서 HTML을 출력할 수 있지만 Model2에서는 서블릿에서는 출력하지 않고 결과 페이지로 포워딩하거나 리다이렉트 해서 결과 페이지에서 출력합니다.
2.실습
- hiservlet 이라는 url이 왔을 때 동작하는 서블릿을 만들고 요청이 오면 FirstServlet이라고 출력
@WebServlet("/hiservlet")
public class FirstController extends HttpServlet {
private static final long serialVersionUID = 1L;
public FirstController() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("FirstServlet");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 실행하고 기본 주소 뒤에 hiservlet 이라고 입력
3.Servlet의 메소드
1) 생성자 : 서블릿 클래스의 인스턴스가 만들어 질 때 호출되는 메소드
2) init : 초기화 메소드 - 생성자가 호출되고 난 후 호출되는 메소드
3) doGet, doPost, dotPut, doDelete
- 요청을 할 때 GET, POST, PUT, DELETE 방식으로 요청하면 호출되는 메소드
- 대부분의 경우는 GET 과 POST만 처리
4) destory : 서블릿이 소멸될 때 호출되는 메소드
4.Servlet 의 수명
- 특별한 설정이 없다면 맨 첫번째 요청이 왔을 때 WAS가 인스턴스를 생성해서 자신이 소유하고 다음 요청부터는 이미 만들어진 서블릿을 가지고 처리
- 서블릿은 개발자가 만들지만 서블릿의 인스턴스는 개발자가 만들지 않고 WAS가 만든다.
- 이러한 것을 IoC(제어의 역전-만들기는 개발자가 만들고 실제 사용은 컨테이너나 프레임워크가 하는것)이라고 한다.
개발자가 직접 만들면 관리에 문제가 생길 수 있다.
WAS가 만들어서 관리하겠다. 개발자는 필요한 내용만 적어라
Spring - IoC, DL AOP
HttpServlet
- URL을 이용해서 호출할 수 있는 Java EE 클래스
1.URL 매핑
1) 어노테이션을 이용하는 방법
클래스 선언문 위에@WebServlet("매핑할 URL")
: 하나의 URL 패턴과 매핑@WebServlet(name="별명", urlPatterns={"매핑할URL", "매핑할URL"...})
이 설정은 직접해도 되지만 서블릿을 만들 때 설정할 수 있습니다.
2) web.xml 파일에 설정하는 방법
<servlet>
<servlet-name>이름</servlet-name>
<servlet-class>서블릿 클래스의 경로</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>이름</servlet-name>
<url-pattern>매핑할URL</url-pattern>
</servlet-mapping>
3) 2가지 중 하나의 방법만 사용해야 합니다.
2.Servlet 메소드의 호출
1) get 방식으로 호출 : doGet 메소드가 호출
- 일반적인 요청
2) post 방식으로 호출:doPost 메소드가 호출
- form에서 method를 post로 지정한 경우
- ajax에서 전송방식을 post로 지정한 경우
3.Controller 패턴
- 요청 라우팅이라고도 해도 됩니다.
- 하나의 요청을 처리하기 위해서 서블릿 1개가 필요
- 10개의 요청이 있으면 서블릿 10개 생성?
- 요청들을 하나의 서블릿이 처리하도록 만들고 모든 요청을 이 서블릿을 통하도록 만듭니다.
- 메인 페이지를 요청하는 것도 서블릿을 통해서 하도록 만듭니다.
- 모든 요청이 하나의 서블릿을 경유해서 처리되기 때문에 제어하기가 편리합니다.
4.URL 패턴
- 여러 개의 URL을 모아서 표현하는 방법
구분 | 내용 |
---|---|
/* |
모든 요청 |
*.확장자 |
확장자로 끝나는 모든 요청 - 공공기관이나 naver가 채택한 방식 |
/디렉토리/* |
중간에 디렉토리가 포함된 모든 요청 - 최근에 권장하는 방식(디렉토리를 이용해서 서비스를 구분) |
/디렉토리/요청 |
요청 1개 |
/ |
서버경로 - 시작 페이지 요청 |
- jsp 페이지나 서블릿, web.xml, html 파일 등에서 /로 시작하면 서버로부터 경로입니다.
5.클라이언트의 요청 URL을 확인
String request.getRequestURI()
: 매번 공통된 ContextPath가 같이 출력String request.getContextPath()
: ContextPath를 리턴(전체 요청 경로에서 공통된 경로를 제거)request.getRequestURI().substring(request.getContextPath().length() + 1);
// +1을 하면 /가 제거되고 경로가 리턴요청방식을 구분 :
String request.getMethod();
// get 방식인지 post 방식인지 구분
6.URL 패턴 실습
1) 디렉토리에 auth가 포함된 요청을 처리하는 서블릿을 만들어서 사용
@WebServlet({"/auth/*"})
- java 코드니까 프로젝트의 src 에 만들어야 된다
- 패키지 이름은 마음대로 만들고, 파일이름은 뭐뭐controller 라고 뒤에 controller를 붙인다.
- java 코드는 수정을 하게되면 톰캣을 다시 시작해야 된다. (서버)
//서블릿을 호출할 URL 패턴을 설정
//@WebServlet("*.do")
@WebServlet({"*.do", "*.nhn"})
*.do
로 끝나는 확장자와*.nhn
끝나는 확장자 든 복수 설정할 수 있다.- 쓸데없는 확장자 붙이지 말고, 좀더 간결하게 알아보기 쉽고 명확하게
@WebServlet("/auth/*")
// auth 디렉토리가 포함된 URL을 처리 - 최근에는 디렉토리 패턴을 많이 사용
// 디렉토리 경로로
package webpro;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//서블릿을 호출할 URL 패턴을 설정
//@WebServlet("*.do")
@WebServlet("/auth/*")
//@WebServlet({"*.do", "*.nhn"})
public class docontroller extends HttpServlet {
private static final long serialVersionUID = 1L;
public docontroller() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// response.getWriter().append("<h3>확장자가 do 인 요청처리<h3>");
response.getWriter().append("<h3>auth directory<h3>");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
2) doGet에 요청을 구분하는 코드를 작성
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//공통된 URL을 제거한 사용자 요청 경로와 전송방식을 출력
String requestURI = request.getRequestURI();
String contextPath = request.getContextPath();
String command = requestURI.substring(contextPath.length() + 1) ;
// 요청 방식 확인
String method = request.getMethod();
// Controller에서는 URL 과 Method를 이용해서 요청을 구분해서 처리
// Spring 에서는 이 작업까지를 해서 우리는 작성만 하면된다.
response.getWriter().append("<h3>" + command + ":" + method + "</h3>");
MVC Model 2 Pattern 의 프로젝트 실습
1.프로젝트에 시작 페이지(index.jsp)를 생성하고 5개의 요청을 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MVC Model2 프로젝트</title>
</head>
<body>
<a href="./item/insert">데이터삽입</a><br/>
<a href="./item/list">전체 데이터 가져오기</a><br/>
</body>
</html>
2.서비스 구조 만들기
1) 사용하고자 하는 데이터베이스 테이블의 하나의 행과 매핑하는 DTO 클래스를 생성
정수 번호
문자열 이름
정수 가격
문자열 보충설명
문자열 원산지
2) domain 패키지에 Item 이라는 클래스로 생성
- 속성들을 private 변수로 선언
- 매개변수가 없는 생성자와 매개변수가 있는 생성자(constructor)를 생성
- 접근자 메소드 생성(getter & setter)
- toString 메소드를 재정의
package domain;
public class Item {
private int num;
private String name;
private int price;
private String description;
private String manufacture;
public Item() {
super();
// TODO Auto-generated constructor stub
}
public Item(int num, String name, int price, String description, String manufacture) {
super();
this.num = num;
this.name = name;
this.price = price;
this.description = description;
this.manufacture = manufacture;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getManufacture() {
return manufacture;
}
public void setManufacture(String manufacture) {
this.manufacture = manufacture;
}
@Override
public String toString() {
return "Item [num=" + num + ", name=" + name + ", price=" + price + ", description=" + description
+ ", manufacture=" + manufacture + "]";
}
}
3) item 디렉토리 패턴을 처리할 수 있는 Controller 역할을 하는 서블릿을 생성
@WebServlet("/item/*")
public class ItemController extends HttpServlet {
private static final long serialVersionUID = 1L;
public ItemController() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- Service 는 템플릿 메소드 패턴(인터페이스 -> 클래스)을 사용합니다.
4) service 의 메소드를 정의한 Service 인터페이스를 생성
- service.ItemService
package service;
public interface ItemService {
}
5) service의 메소드를 구현한 Service 클래스를 생성
- service.ItemServiceImpl
package service;
public class ItemServiceImpl implements ItemService {
}
6) 데이터베이스 연동을 위한 DAO 클래스를 생성
- dao.ItemDao
package dao;
public class ItemDao {
}
3.Controller, Service, Dao의 관계를 구성
Dao -> Service -> Controller
1) ItemServiceImpl 클래스에 Dao 변수를 생성하고 객체를 대입하는 코드를 작성
private ItemDao itemDao;
//생성자와 유사한 역할을 수행하는 초기화 블럭
//인스턴스가 생성될 때 이 코드를 수행
{
itemDao = new ItemDao();
}
2) Controller 클래스에 Service 변수를 생성하고 객체를 대입하는 코드를 작성
//템플릿 메소드 패턴으로 만들어진 클래스는
//변수는 인터페이스 타입으로 선언하고 인스턴스는 클래스 타입으로 생성해서 대입
private ItemService itemService;
public ItemController() {
super();
itemService = new ItemServiceImpl();
}
4.데이터 삽입 요청 처리 코드 만들기
- 데이터 삽입을 클릭하면 데이터 삽입 화면으로 이동하고 데이터를 입력하고 삽입을 누르면 실제 삽입을 수행합니다.
1) index.html에서 데이터 삽입을 클릭하면 데이터 삽입 페이지로 이동하도록 Controller 클래스의 doGet 메소드에 작성
//분기를 위해서 필요한 데이터를 가져오기
String requestURI = request.getRequestURI();
String contextPath = request.getContextPath();
String command = requestURI.substring(contextPath.length());
String method = request.getMethod();
//포워딩에 사용하기 위한 변수
RequestDispatcher dispatcher = null;
//단순 페이지 이동은 포워딩 하는 것이 좋습니다.
if(command.contentEquals("/item/insert") && method.equals("GET")) {
//현재 URL이 /item/insert 라서 WebContent 디렉토리로 이동할려면 item을 제거해야 해서
//../를 추가하고 views/insert.jsp로 포워딩
//WebContent/views/insert.jsp 파일이 존재해야 합니다.
dispatcher = request.getRequestDispatcher("../views/insert.jsp");
dispatcher.forward(request, response);
}
2) WebContent 디렉토리에 views 디렉토리를 만들고 insert.jsp 파일을 추가하고 작성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>데이터 삽입화면</title>
</head>
<body>
<!-- 최근에는 form을 만들 때 무조건 post 방식으로 action을 설정하지 않습니다.
action을 설정하지 않으면 기존 URL을 그대로 사용
작업이 1가지인데 단지 과정이 2가지라서 서로 다른 URL을 2개 사용하지 않도록 하는 것을 권장-->
<form method="post" id="itemform">
<label for="num">번호</label>
<input type="number" name="num" id="num"/><br/>
<label for="name">이름</label>
<input type="text" name="name" id="name"/><br/>
<label for="price">가격</label>
<input type="number" name="price" id="price"/><br/>
<label for="manufacture">원산지</label>
<input type="text" name="manufacture" id="manufacture"/><br/>
<label for="description">보충설명</label>
<input type="text" name="description" id="description"/><br/>
<input type="submit" value="데이터 삽입"/>
</form>
</body>
</html>
3) Dao 클래스에서 삽입을 실제로 처리하는 메소드를 생성
//데이터 삽입, 삭제, 갱신은 리턴타입이 int(영향받은 행의 개수)
//삽입과 갱신은 매개변수가 Domain 클래스 1개
//삭제는 상황에 따라 다르지만 대부분 기본키 1개
public int insert(Item item) {
//return 데이터 삽입 메소드;
return 1;
}
4) Service 인터페이스에서 처리를 위한 메소드를 선언
//데이터 삽입을 위한 메소드
//Service는 Controller로 부터 request를 넘겨받아서 클라이언트의 데이터를 읽고
//작업을 수행한 후 리턴을 합니다.
public int insert(HttpServletRequest request);
5) ServiceImpl 클래스에 처리를 위한 메소드를 구현
- 파라미터를 읽고 읽은 데이터를 가지고 Dao가 사용할 수 있는 매개변수 형태로 만들고 Dao 메소드를 호출하고 그 결과를 Controller에게 리턴
@Override
public int insert(HttpServletRequest request) {
try {
Item item = new Item();
//파라미터 인코딩
request.setCharacterEncoding("utf-8");
//파라미터 읽기
String num = request.getParameter("num");
String name = request.getParameter("name");
String price = request.getParameter("price");
String manufacture = request.getParameter("manufacture");
String description = request.getParameter("description");
//Dao의 매개변수 만들기
item.setNum(Integer.parseInt(num));
item.setName(name);
item.setPrice(Integer.parseInt(price));
item.setManufacture(manufacture);
item.setDescription(description);
//Dao의 메소드를 호출해서 결과를 리턴
return itemDao.insert(item);
}catch(Exception e) {
System.out.println(e.getMessage());
return -1;
}
}
6) Controller 클래스의 doGet 메소드 삽입을 처리해달라는 요청이 왔을 때 수행할 코드를 작성
else if(command.contentEquals("/item/insert") && method.equals("POST")) {
//작업을 수행해야 하는 경우는 Service의 메소드를 호출
itemService.insert(request);
//결과 페이지로 이동 - 삽입, 삭제, 갱신은 반드시 리다이렉트로 이동
//자신의 요청이 /item/insert 이므로 /item/list로 갈때는 공통된 부분은 제외하고 설정
response.sendRedirect("./list");
}