본문 바로가기

학원/복기

[MVC] Model2

모델2 구현하기

 

 

1. 클라이언트의 모든 요청을 받을 수 있도록 서블릿을 설정하여 단일 진입점의 기능을 구현한다. (이를 Front Controller Pattern이라고 부른다.)


기능이 많아지면 servlet 개수가 많아지고 길어지는 문제가 발생하기 때문에 모든 요청을 한 곳에 받아오고 하나의 컨트롤러로 다시 처리로 보내주는 Front-Controller 패턴을 이용한다.

 

컨트롤러(Controller - Servlet) 

: 클라이언트에게 모든 요청을 받아 모델(Model - Class) 역할의 객체로 요청 처리 메소드를 호출하여 클라이언트의 요청을 처리하고 처리결과를 뷰(View - JSP)로 전달하여 응답되도록 프로그램의 흐름을 제공하는 웹프로그램

 

Front Controller Pattern

소프트웨어 디자인 패턴 중 하나로, 웹 애플리케이션에서 클라이언트 요청을 처리하는 중앙 집중화된 컨트롤러를 사용하는 패턴이다. 일반적으로 Front Controller는 클라이언트로부터의 모든 요청을 처리하는 공통된 진입점으로 사용된다.

클라이언트 요청이 들어오면 Front Controller는 요청을 분석하고 해당 요청에 대한 적절한 핸들러(Controller)를 호출하여 처리한다. 이를 통해 애플리케이션의 로직 흐름을 중앙에서 제어하고, 중복되는 코드를 최소화하며, 공통적인 기능(인증, 로깅, 예외 처리 등)을 한 곳에서 처리할 수 있게 된다.

이 패턴은 웹 애플리케이션의 구조를 단순화하고 코드의 재사용성과 유지보수성을 높이는 데 도움을 준다. 

 


@WebServlet("url") : 클래스를 웹프로그램(서블릿)으로 등록하고 요청 URL 주소를 매핑하는 어노테이션
→ 매핑 설정될 URL 주소에 패턴문자(* : 전체, ? : 문자 하나) 를 사용하여 URL 패턴을 등록할 수 있다.
     ex)  @WebServlet("*.do") : 클라이언트가 [XXX.do] 형식의 URL 주소를 요청한 경우 웹프로그램(서블릿)을 실행 
 
@WebServlet 어노테이션 대신 [web.xml] 파일의 엘리먼트를 사용하여 클래스를 웹프로그램으로 등록하고 URL을 매핑 처리할 수 있다. (서블릿이 많지 않을 경우엔 유지보수 측면에서 더 좋기 때문에 권장한다.) 
 
 
방법 1) @WebServlet("url") 이용 

@WebServlet("*.do")

 
방법 2) [web.xml] 파일의 엘리먼트 사용
 
servlet : 클래스를 웹프로그램(서블릿)으로 등록하기 위한 엘리먼트 
servlet-mapping : 웹프로그램(서블릿)에 URL 패턴을 등록하기 위한 엘리먼트

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>mvc</display-name>
  
  <!-- servlet : 클래스를 웹프로그램(서블릿)으로 등록하기 위한 엘리먼트 -->
  <servlet>
  	<servlet-name>controller</servlet-name>
  	<servlet-class>xyz.itwill.mvc.ControllerServlet</servlet-class>
  </servlet>
  
  <!-- servlet-mapping : 웹프로그램(서블릿)에 URL 패턴을 등록하기 위한 엘리먼트 -->
  <servlet-mapping>
  	<servlet-name>controller</servlet-name>
  	<url-pattern>*.do</url-pattern>
  </servlet-mapping>
  
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    <welcome-file>default.htm</welcome-file>
  </welcome-file-list>
</web-app>

 
 
 

2. 요청 url 주소를 이용해 클라이언트의 요청을 분석한다.

ex) http://localhost:8000/mvc/XXX.do


 
HttpServletRequest.getRequestURI()  : 요청 URL 주소에서 URI 주소를 반환하는 메소드
HttpServletRequest.getContextPath() : 요청 URL 주소에서 컨텍스트가 저장된 경로를 반환하는 메소드
 

[ControllerServlet.java]

package xyz.itwill.mvc;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//1.서블릿 설정 - @WebServlet 어노테이션 대신 [web.xml] 파일의 엘리먼트 사용했으므로 주석처리
//@WebServlet("*.do")
public class ControllerServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//2.클라이언트의 요청 분석
		//HttpServletRequest.getRequestURI() : 요청 URL 주소에서 URI 주소를 반환하는 메소드
		String requestURI=request.getRequestURI();
		//System.out.println("requestURI = "+requestURI);//requestURI = /mvc/XXX.do
		
		//HttpServletRequest.getContextPath() : 요청 URL 주소에서 컨텍스트 경로를 반환하는 메소드
		String contextPath=request.getContextPath();
		//System.out.println("contextPath = "+contextPath);//contextPath = /mvc
		
		String command=requestURI.substring(contextPath.length());
		//System.out.println("command = "+command);//command = /XXX.do	
	}

}

 
 

 
3. 모델(Model) 객체를 이용하여 요청 처리 메소드를 호출하여 클라이언트의 요청을 처리하고 뷰(View) 관련 정보를 반환받아 저장한다.
 

→ 하나의 요청에 대해 하나의 모델 객체가 요청을 처리하도록 설정해야 한다. - Command Controller Pattern 
→ 요청 처리 메소드가 선언된 모델 역할의 클래스를 작성해야 한다. - 모델 역할의 클래스는 인터페이스를 상속받아 작성

 

Command Controller Pattern

Command Controller Pattern은 웹 애플리케이션에서 사용되는 디자인 패턴 중 하나로, 요청을 처리하는 컨트롤러(Controller)를 명령(Command) 객체로 추상화하여 사용하는 패턴이다.

이 패턴은 요청의 종류에 따라 적절한 명령 객체를 생성하고 실행함으로써 요청을 처리한다.

Command Controller Pattern에서는 각각의 요청을 처리하는 명령 객체(Command)가 생성되어 컨트롤러에 의해 실행된다. 명령 객체는 요청에 필요한 데이터와 비즈니스 로직을 캡슐화하고, 컨트롤러는 명령 객체를 생성하고 실행함으로써 요청을 처리한다.
이를 통해 요청의 처리 과정을 논리적으로 분리하고, 각각의 명령 객체는 독립적으로 테스트와 유지보수가 가능해진다.

예시) 


회원관리 프로그램에서 클라이언트 요청(Command)에 대한 모델 객체가 매핑되도록 설정한다.
 
예를들어 클라이언트가 [/loginForm.do]를 요청하면, LoginFormModel 클래스로 객체를 생성하여 요청 처리 메소드 호출

[/loginform.do]  >> LoginFormModel 클래스
[/login.do]      >> LoginModel 클래스
[/logout.do]     >> LogoutModel 클래스
[/writeform.do]  >> WriteFormModel 클래스
[/write.do]      >> WriteModel 클래스
[/list.do]       >> ListModel 클래스 
[/view.do]       >> ViewModel 클래스
[/modifyform.do] >> MojdifyFormModel 클래스
[/modify.do]     >> ModifyModel 클래스
[/remove.do]     >> RemoveModel 클래스
[/error.do]      >> ErrorModel 클래스

 


1) 뷰(View) 관련 정보를 저장하기 위한 클래스 선언

 

리다이렉트 이동 : 클라이언트에게 ULR 주소(/XXX.do)를 전달하여 다시 요청하도록 응답 처리하는 것 
→ 클라이언트 브라우저의 요청 URL 주소를 변경한다.

포워드 이동 : 서블릿(컨트롤러)의 스레드를 JSP(뷰)로 이동하여 JSP가 응답 처리하는 것 
→ 클라이언트 브라우저의 요청 URL 주소를 변경하지 않는다.

 

[ActionForward]

package xyz.itwill.mvc;

//뷰(View) 관련 정보를 저장하기 위한 클래스
public class ActionForward {
	//페이지 이동 방식에 대한 정보를 저장하기 위한 필드
	// => false : 리다이렉트 이동 , true : 포워드 이동
	private boolean forward;
	
	//페이지 이동을 위한 웹프로그램의 URL 주소를 저장하기 위한 필드 
	// => 리다이렉트 이동 : /XXX.do, 포워드 : /XXX.jsp
	private String path;
	
	public ActionForward() {
		// TODO Auto-generated constructor stub
	}

	public boolean isForward() {
		return forward;
	}

	public void setForward(boolean forward) {
		this.forward = forward;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}
}

 

 
 2) 모든 모델 역할의 클래스가 반드시 상속받아야 하는 인터페이스 선언
 

→ 모델 역할의 클래스에 요청 처리 메소드에 대한 작성 규칙을 제공한다.
→ 컨트롤러 역할의 서블릿에서 모델 객체로 요청 처리 메소드를 쉽게 호출할 수 있으며 유지보수의 효율성이 증가한다.
 
 
인터페이스에서는 요청 처리 메소드를 추상 메소드로 선언한다.
→ 인터페이스를 상속받은 모든 자식클래스는 반드시 추상메소드를 오버라이드 선언해주어야 한다. - 메소드 작성 규칙 제공 
→ 요청 처리 메소드는 매개변수로 HttpServletRequest 객체와 HttpservletResponse 객체를 전달받을 수 있도록 작성하고 뷰 관련 정보(ActionForward 객체)를 반환하도록 작성해야 한다.
→ 요청 처리 메소드의 명령으로 발생되는 ServletException과 IOException은 예외를 처리하지 않고 요청 처리 메소드를 호출하는 명령으로 전달되도록 작성한다.
 

[Action.java]

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.rowset.serial.SerialException;

public interface Action {
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) 
			throws SerialException, IOException;
}

 

 

3) 모델 역할의 클래스 작성

 
 
 [LoginFormModel.java]
 
클라이언트가 [/loginform.do]로 요청한 경우 객체로 생성될 모델 클래스 [LoginFormModel] 작성 
→ [user_login.jsp]로 포워드 이동하기 위한 정보가 저장된 ActionForward 객체 반환 

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.rowset.serial.SerialException;

public class LoginFormModel implements Action {
	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response)
			throws SerialException, IOException {
		ActionForward actionForward=new ActionForward();
		actionForward.setForward(true);
		actionForward.setPath("/model_two/user_login.jsp");
		return null;
	}
	
}

 
 
 
[LoginModel.java] 
 
클라이언트가 [/login.do]로 요청한 경우 객체로 생성될 모델 역할의 클래스 [LoginModel] 작성 
→  로그인정보를 전달받아 USERINFO 테이블에 저장된 회원정보와 비교하여 인증 처리한 후 인증 성공시 세션에 권한 관련 객체를 속성값으로 저장하고 [/loginform.do] 페이지로 리다이렉트 이동 하기 위한 정보가 저장된 ActionForward 객체 반환
→  인증 실패시 [user_login.jsp] 문서로 포워드 이동하기 위한 정보가 저장된  ActionForward 객체 반환 - 에러메세지와 아이디를 request 객체의 속성값으로 저장하여 JSP 문서에 제공

package xyz.itwill.mvc;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import xyz.itwill.exception.AuthFailException;
import xyz.itwill.service.UserinfoService;

public class LoginModel implements Action {
	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		ActionForward actionForward=new ActionForward();
		//클라이언트 요청에 대한 명령을 실행하면서 발생되는 모든 예외를 처리하기 위한 기능 구현
		try {
			//[/login.do] 페이지를 GET 방식으로 요청한 경우 - 비정상적인 요청
			if(request.getMethod().equals("GET")) {
				throw new Exception();//인위적인 예외 발생
			}
			
			//서블릿(컨트롤러) 요청시 전달된 값을 반환받아 저장
			// => 서블릿(컨트롤러)의 request 객체를 요청 처리 메소드의 매개변수로 전달받아 사용
			String userid=request.getParameter("userid");
			String password=request.getParameter("password");
			
			//모델 클래스의 요청 처리 매소드에서는 Service 클래스의 객체로 메소드를 호출하여
			//데이타 처리 관련 기능이 실행되도록 작성
			
			//UserinfoService 클래스의 auth() 메소드를 호출하여 인증 처리
			// => AuthFailException이 발생된 경우 인증 실패
			UserinfoService.getService().auth(userid, password);
			
			//인증 성공시 세션에 권한 관한 객체를 속성값으로 저장
			HttpSession session=request.getSession();
			//Session Scope : 동일한 세션을 바인딩하여 사용하는 모든 웹프로그램에서 속성값을 객체로 반환받아 사용 가능
			// => 웹브라우저가 종료되면 클라이언트의 정보로 바인딩 세션은 자동으로 삭제 처리
			session.setAttribute("loginUserinfo", UserinfoService.getService().getUserinfo(userid));
			
			actionForward.setForward(false);
			actionForward.setPath(request.getContextPath()+"/loginform.do");
		} catch (AuthFailException e) {
			//인증 실패시 발생되는 예외에 대한 명령 작성
			//Request Scope : 스레드가 이동된 웹프로그램(JSP)에서만 속성값을 객체로 반환받아 사용 가능
			request.setAttribute("message", e.getMessage());
			request.setAttribute("userid", request.getParameter("userid"));
			actionForward.setForward(true);
			actionForward.setPath("/model_two/user_login.jsp");
		} catch (Exception e) {//모든 예외를 전달받아 예외 처리하기 위한 명령 작성
			e.printStackTrace();//서버 콘솔에 예외가 발생한 예외정보 출력
			//[error.do] 페이지로 리다이렉트 이동하기 위한 정보를 ActionForward 객체에 저장
			actionForward.setForward(false);
			actionForward.setPath(request.getContextPath()+"/error.do");
		}
		return actionForward;
	}

}

 
 
[ErrorModel.java]
 
클라이언트가 [/error.do]로 요청한 경우 객체로 생성될 모델 역할의 클래스 [ErrorModel] 작성
→  [user_error.jsp]로 포워드 이동하기 위한 정보가 저장된 ActionForward 객체 반환

package xyz.itwill.mvc;

import java.io.IOException;

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

public class ErrorModel implements Action {
	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		ActionForward actionForward=new ActionForward();
		actionForward.setForward(true);
		actionForward.setPath("/model_two/user_error.jsp");
		return actionForward;
	}
}

 
[WriteFormModel.java]

package xyz.itwill.mvc;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import xyz.itwill.dto.UserinfoDTO;

//클라이언트가 [/writeform.do]로 요청한 경우 객체로 생성될 모델 역할의 클래스
// => 관리자만 요청 가능하도록 권한 설정
public class WriteFormModel implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		ActionForward actionForward=new ActionForward();
		try {
			HttpSession session=request.getSession();
			UserinfoDTO loginUserinfo=(UserinfoDTO)session.getAttribute("loginUserinfo");
			if(loginUserinfo==null || loginUserinfo.getStatus()!=9) {
				throw new Exception();
			} 
			
			actionForward.setForward(true);
			actionForward.setPath("/model_two/user_write.jsp");
		} catch (Exception e) {
			e.printStackTrace();
			actionForward.setForward(false);
			actionForward.setPath(request.getContextPath()+"/error.do");
		}
		return actionForward;
	}

}

 


.do로 들어오는 것이 많기 때문에 ControllerServlet 에서 나눠서 원하는 클래스를 호출해준다. 
 
 [ControllerServlet.java]

		//모델 역할의 클래스가 상속받기 위한 인터페이스로 참조변수 생성
		// => 인터페이스로 생성된 참조변수에는 인스페이스를 상속받은 모든 자식클래스(모델)의 객체 저장 가능
		Action action=null;
		
		//클라이언트 요청정보를 구분하여 요청을 처리하기 위한 모델 역할의 클래스로 객체를 
		//생성하여 인터페이스 참조변수에  저장
		if(command.equals("/loginform.do")) {
			action=new LoginFormModel();
		} else if(command.equals("/login.do")) {
			action=new LoginModel();
		} else if(command.equals("/logout.do")) {
			action=new LogoutModel();
		} else if(command.equals("/writeform.do")) {
			action=new WriteFormModel();
		} else if(command.equals("/write.do")) {
			action=new WriteModel();
		} else if(command.equals("/error.do")) {
			action=new ErrorModel();
		} else {//클라이언트 요청에 대한 모델 역할의 클래스가 없는 경우
			action=new ErrorModel();
		}
		
		//인터페이스 참조변수로 추상메소드를 호출하면 참조변수에 저장된 모델 객체의 요청 
		//처리 메소드를 호출하고 뷰 관련 정보를 반환받아 저장 -  오버라이드의 의한 다형성
		ActionForward actionForward=action.execute(request, response);

 
 

4. 응답 관련 정보가 저장된 ActionForward 객체를 이용하여 응답 처리한다. 

 


[ControllerServlet.java]

        if(actionForward.isForward()) {//ActionForward 객체의 forward 필드값이 [true]인 경우
            //JSP 문서로 포워드 이동하여 JSP 문서의 실행결과(HTML)를 클라이언트에게 전달하여 응답 처리
            request.getRequestDispatcher(actionForward.getPath()).forward(request, response);
        } else {
            //서블릿에서 클라이언트에게 URL 주소(/XXX.do)를 전달하여 응답 처리
            // => URL 주소를 전달받은 클라이언트는 브라우저의 URL 주소를 변경하여 서블릿을 요청
            response.sendRedirect(actionForward.getPath());
        }

 


[ControllerServlet.java] 전체 소스코드

 

package xyz.itwill.mvc;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//@WebServlet("*.do")
public class ControllerServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//System.out.println("ControllerServlet 클래스의 service() 메소드 호출");
		
		//HttpServletRequest.getRequestURI() : 요청 URL 주소에서 URI 주소를 반환하는 메소드
		String requestURI=request.getRequestURI();
		//System.out.println("requestURI = "+requestURI);//requestURI = /mvc/XXX.do
		
		//HttpServletRequest.getContextPath() : 요청 URL 주소에서 컨텍스트 경로를 반환하는 메소드
		String contextPath=request.getContextPath();
		//System.out.println("contextPath = "+contextPath);//contextPath = /mvc
		
		String command=requestURI.substring(contextPath.length());
		//System.out.println("command = "+command);//command = /XXX.do
		
		//모델 역할의 클래스가 상속받기 위한 인터페이스로 참조변수 생성
		// => 인터페이스로 생성된 참조변수에는 인스페이스를 상속받은 모든 자식클래스(모델)의 객체 저장 가능
		Action action=null;
		
		//클라이언트 요청정보를 구분하여 요청을 처리하기 위한 모델 역할의 클래스로 객체를 
		//생성하여 인터페이스 참조변수에  저장
		if(command.equals("/loginform.do")) {
			action=new LoginFormModel();
		} else if(command.equals("/login.do")) {
			action=new LoginModel();
		} else if(command.equals("/logout.do")) {
			action=new LogoutModel();
		} else if(command.equals("/writeform.do")) {
			action=new WriteFormModel();
		} else if(command.equals("/write.do")) {
			action=new WriteModel();
		} else if(command.equals("/error.do")) {
			action=new ErrorModel();
		} else {//클라이언트 요청에 대한 모델 역할의 클래스가 없는 경우
			action=new ErrorModel();
		}
		
		//인터페이스 참조변수로 추상메소드를 호출하면 참조변수에 저장된 모델 객체의 요청 
		//처리 메소드를 호출하고 뷰 관련 정보를 반환받아 저장 -  오버라이드의 의한 다형성
		ActionForward actionForward=action.execute(request, response);
		
		if(actionForward.isForward()) {//ActionForward 객체의 forward 필드값이 [true]인 경우
			//JSP 문서로 포워드 이동하여 JSP 문서의 실행결과(HTML)로 클라이언트에게 전달하여 응답 처리
			request.getRequestDispatcher(actionForward.getPath()).forward(request, response);
		} else {//ActionForward 객체의 forward 필드값이 [false]인 경우
			//서블릿에서 클라이언트에게 URL 주소(/XXX.do)를 전달하여 응답 처리
			// => URL 주소를 전달받은 클라이언트는 브라우저의 URL 주소를 변경하여 서블릿 요청
			response.sendRedirect(actionForward.getPath());
		}
	}

}

 

 


 

모델1과 모델2의 차이

 
- 모델2에서는 jsp 문서가 아닌, 컨트롤러 역할을 하는 서블릿을 요청해야 한다. (이 때, 특별한 확장자를 꼭 붙여주어야 함 - .do) 
- 요청 서블릿(컨트롤러)의 URL 주소 경로와 응답하는 JSP 문서의 경로가 서로 다르므로 웹자원의 경로는 절대경로로 표현하는 것을 권장한다. 
- 모델2에서는 try cath를 모델에서 처리
 
[user_login.jsp]

<%@page import="xyz.itwill.dto.UserinfoDTO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%-- 비로그인 상태의 사용자인 경우 - 사용자로부터 로그인정보를 입력받기 위한 JSP 문서 --%>
<%-- => [로그인] 태그를 클릭한 경우 [/login.do] 페이지 요청 - 입력값 전달 --%>
<%-- 로그인 상태의 사용자인 경우 - 환영메세지를 클라이언트에게 전달하여 응답하는 JSP 문서 --%>
<%-- => [회원목록] 태그를 클릭한 경우 [/list.do] 페이지 요청 --%>
<%-- => [로그아웃] 태그를 클릭한 경우 [/logout.do] 페이지 요청 --%>
<%-- => [회원등록] 태그를 클릭한 경우 [/writeform.do] 페이지 요청 - 관리자에게만 링크 제공 --%>
<%
	UserinfoDTO loginUserinfo=(UserinfoDTO)session.getAttribute("loginUserinfo");

	String message=(String)session.getAttribute("message");
	if(message==null) {
		message="";
	} else {
		session.removeAttribute("message");
	}
	
	String userid=(String)session.getAttribute("userid");
	if(userid==null) {
		userid="";
	} else {
		session.removeAttribute("userid");
	}
%>    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>MVC</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<%-- 요청 서블릿(컨트롤러)의 URL 주소 경로와 응답하는 JSP 문서의 경로가 서로 다르므로
웹자원의 경로는 절대경로로 표현하는 것을 권장 --%>
<link rel=stylesheet href="<%=request.getContextPath() %>/model_two/css/user.css" type="text/css">
<script language="JavaScript">
function userLogin() {
	if ( f.userid.value == "" ) {
		alert("아이디를 입력하십시요.");
		f.userid.focus();
		return;
	} 
	if ( f.password.value == "" ) {
		alert("비밀번호를 입력하십시요.");
		f.password.focus();
		return;
	}	
	
	f.action = "<%=request.getContextPath() %>/login.do";
	f.submit();
}
</script>
</head>
<body bgcolor=#FFFFFF text=#000000 leftmargin=0 topmargin=0 marginwidth=0 marginheight=0>
<br>
<table width=780 border=0 cellpadding=0 cellspacing=0>
	<tr>
	  <td width="20"></td>
	  <td style="color: red;"><%=message%></td>			
	</tr>

	<tr>
	  <td width="20"></td>
	  <td>
  	  <!--contents-->
	  <table width=590 border=0 cellpadding=0 cellspacing=0>
		  <tr>
			<td bgcolor="f4f4f4" height="22">&nbsp;&nbsp;<b>회원관리 - 로그인</b></td>
		  </tr>
	  </table>  
	  <br>
	  
	<% if(loginUserinfo==null) {//비로그인 상태의 사용자인 경우 %>	  
	  <!-- login Form  --> 
	  <form name="f" method="post">
	  <table border="0" cellpadding="0" cellspacing="1" width="590" bgcolor="BBBBBB">
		  <tr>
			<td width=100 align=center bgcolor="E6ECDE" height="22">사용자 아이디</td>
			<td width=490 bgcolor="ffffff" style="padding-left:10px;">
				<input type="text" style="width:150" name="userid" value="<%=userid%>">
			</td>
		  </tr>
		  <tr>
			<td width=100 align=center bgcolor="E6ECDE" height="22">비밀번호</td>
			<td width=490 bgcolor="ffffff" style="padding-left:10px;">
				<input type="password" style="width:150" name="password">
			</td>
		  </tr>
	  </table>
	  </form>
	  <br>
	  
	  <table width=590 border=0 cellpadding=0 cellspacing=0>
		  <tr>
			<td align=center>
				<input type="button" value="로그인" onClick="userLogin();"> &nbsp;
			</td>
		  </tr>
	  </table>
	<% } else {//로그인 상태의 사용자인 경우 %>
	  <table border="0" cellpadding="0" cellspacing="1" width="590" bgcolor="BBBBBB">
		  <tr>
			<td align=center bgcolor="E6ECDE" height="22">
				<%=loginUserinfo.getName() %>님, 환영합니다.
			</td>
		  </tr>
	  </table>
	  <br>
	  
	  <table width=590 border=0 cellpadding=0 cellspacing=0>
		  <tr>
			<td align=center>
				<button type="button" onclick="location.href='<%=request.getContextPath() %>/list.do';">회원목록</button>
				<button type="button" onclick="location.href='<%=request.getContextPath() %>/logout.do';">로그아웃</button>
				<% if(loginUserinfo.getStatus()==9) { %>
				<button type="button" onclick="location.href='<%=request.getContextPath() %>/writeform.do';">회원등록</button>
				<% } %>
			</td>
		  </tr>
	  </table>
	<% } %>	
	  </td>
	</tr>
</table>  

</body>
</html>

 
[user_error.jsp]
 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%-- 에러메세지를 클라이언트에게 전달하여 응답하는 JSP 문서 --%>
<%-- => [메인으로] 태그를 클릭한 경우 [/] 문서 요청 --%>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MVC</title>
<style type="text/css">
body {
	text-align: center;
}

.message {
	color: red;
	font-size: 1.5em;
}
</style>
</head>
<body>
	<h1>에러페이지</h1>
	<hr>
	<p class="message">프로그램 실행에 예기치 못한 오류가 발생하였거나 비정상적인 방법으로
	프로그램을 요청하여 오류가 발생 하였습니다.</p>
	<button type="button" onclick="location.href='<%=request.getContextPath()%>/error.do';">메인으로</button>
</body>
</html>

 


예외클래스 직접 만들기 (서비스 클래스 내에서 데이터 처리할 때 발생될 문제에 대한 정보를 제공하기 위해)

 
예외 클래스는 반드시 Exception 클래스를 상속받아 작성해야한다.
 
 
[ExistsUserinfoException.java]

package xyz.itwill.exception;

//아이디가 중복될 경우 발생될 예외를 표현하기 위한 클래스
// => 예외 클래스는 반드시 Exception 클래스를 상속받아 작성
public class ExistsUserinfoException extends Exception {
	private static final long serialVersionUID = 1L;

	public ExistsUserinfoException() {
		// TODO Auto-generated constructor stub
	}
	
	public ExistsUserinfoException(String message) {
		super(message);
	}
}

 

[UserinfoNotFoundException.java]

package xyz.itwill.exception;

//회원정보가 없는 경우 발생되는 예외를 표현하기 위한 클래스
public class UserinfoNotFoundException extends Exception {
	private static final long serialVersionUID = 1L;

	public UserinfoNotFoundException() {
		// TODO Auto-generated constructor stub
	}
	
	public UserinfoNotFoundException(String message) {
		super(message);
	}
}


[AuthFailException.java]

package xyz.itwill.exception;

//인증 처리시 인증 실패된 경우 발생되는 예외를 표현하기 위한 클래스
public class AuthFailException extends Exception {
	private static final long serialVersionUID = 1L;

	public AuthFailException() {
		// TODO Auto-generated constructor stub
	}
	
	public AuthFailException(String message) {
		super(message);
	}
}


 


클라이언트에서 요청이 들어오면 Controller에서 해당 요청에 맞는 조건을 검색하고, 조건을 찾으면 요청 처리 Controller는 데이터 흐름만 제어하고 나머지는 Serive 클래스에서 수행한다. 

 

 

Service 클래스

: 모델 클래스의 요청 처리 메소드에서 데이터 처리 기능을 메소드로 제공하기 위한 클래스
→ 단위 프로그램(모듈 프로그램) : 컴포넌트(Component)
다수의 DAO 클래스의 메소드를 호출하여 데이터 처리에 필요한 기능을 제공한다. - 모듈화
→ 데이터 처리 기능 구현시 발생되는 모든 문제에 대한 인위적으로 예외를 발생시킨다. - 모델 클래스에서 예외 처리 
 
 
[UserinfoService.java]

package xyz.itwill.service;

import java.sql.SQLException;
import java.util.List;

import xyz.itwill.dao.UserinfoModelTwoDAO;
import xyz.itwill.dto.UserinfoDTO;
import xyz.itwill.exception.AuthFailException;
import xyz.itwill.exception.ExistsUserinfoException;
import xyz.itwill.exception.UserinfoNotFoundException;

public class UserinfoService {
	private static UserinfoService _service;
	
	private UserinfoService() {
		// TODO Auto-generated constructor stub
	}
	
	static {
		_service=new UserinfoService();
	}
	
	public static UserinfoService getService() {
		return _service;
	}
	
	//회원정보를 전달받아 USERINFO 테이블에 회원정보를 삽입하는 메소드
	// => 매개변수로 전달받은 회원정보의 아이디가 USERINFO 테이블에 저장된 기존 회원정보의
	//아이디와 중복될 경우 인위적 예외 발생
	public void addUserinfo(UserinfoDTO userinfo) throws SQLException, ExistsUserinfoException {
		if(UserinfoModelTwoDAO.getDAO().selectUserinfo(userinfo.getUserid())!=null) {
			//사용자 정의 예외클래스를 이용하여 인위적 예외 발생
			throw new ExistsUserinfoException("이미 사용중인 아이디를 입력 하였습니다.");
		}
		UserinfoModelTwoDAO.getDAO().insertUserinfo(userinfo);
	}
	
	//회원정보를 전달받아 USERINFO 테이블에 저장된 회원정보를 변경하는 메소드
	// => 매개변수로 전달받은 회원정보가 USERINFO 테이블에 없는 경우 인위적 예외 발생
	public void modifyUserinfo(UserinfoDTO userinfo) throws SQLException, UserinfoNotFoundException {
		if(UserinfoModelTwoDAO.getDAO().selectUserinfo(userinfo.getUserid())==null) {
			throw new UserinfoNotFoundException("회원정보가 존재하지 않습니다.");
		}
		UserinfoModelTwoDAO.getDAO().updateUserinfo(userinfo);
	}

	//아이디를 전달받아 USERINFO 테이블에 저장된 회원정보를 삭제하는 메소드
	// => 매개변수로 전달받은 아이디의 회원정보가 USERINFO 테이블에 없는 경우 인위적 예외 발생
	public void removeUserinfo(String userid) throws SQLException, UserinfoNotFoundException {
		if(UserinfoModelTwoDAO.getDAO().selectUserinfo(userid)==null) {
			throw new UserinfoNotFoundException("회원정보가 존재하지 않습니다.");
		}
		UserinfoModelTwoDAO.getDAO().deleteUserinfo(userid);
	}
	
	//아이디를 전달받아 USERINFO 테이블에 저장된 회원정보를 검색하여 DTO 객체로 반환하는 메소드
	// => 매개변수로 전달받은 아이디의 회원정보가 USERINFO 테이블에 없는 경우 인위적 예외 발생
	public UserinfoDTO getUserinfo(String userid) throws SQLException, UserinfoNotFoundException {
		UserinfoDTO userinfo=UserinfoModelTwoDAO.getDAO().selectUserinfo(userid);
		if(userinfo==null) {
			throw new UserinfoNotFoundException("회원정보가 존재하지 않습니다.");
		}
		return userinfo;
	}
	
	//USERINFO 테이블에 저장된 모든 회원정보를 검색하여 List 객체로 반환하는 메소드
	public List<UserinfoDTO> getUserinfoList() throws SQLException {
		return UserinfoModelTwoDAO.getDAO().selectUserinfoList();
	}
	
	//로그인정보(아이디와 비밀번호)를 전달받아 인증 처리하는 메소드
	// => 인증 실패시 인위적 예외 발생 - 예외가 발생하지 않은 경우 인증 성공
	public void auth(String userid, String password) throws SQLException, AuthFailException {
		UserinfoDTO userinfo=UserinfoModelTwoDAO.getDAO().selectUserinfo(userid);
		if(userinfo==null || !userinfo.getPassword().equals(password)) {
			throw new AuthFailException("입력된 아이디가 잘못 되었거나 비밀번호가 맞지 않습니다.");
		}
	}
}

 



 순서