mojo's Blog

MVC 패턴의 이해 본문

JSP

MVC 패턴의 이해

_mojo_ 2021. 12. 31. 01:40

MVC 패턴

 

디자인 패턴이란?

생성, 구조, 행동, 동시실행 등의 문제에 대해 여러 패턴을 제시하고 있으며 UML 클래스 다이어그램을 이용해 구조를 표현하고 있다.

UML이란 Unified Modeling Language의 약어로 객체지향 설계와 구현을 지원하기 위해 만들어진 일종의 모델링 언어다.

 

추상 팩토리 패턴이란?

Factory는 '공장'이라는 의미로 디자인 패턴에서 객체를 생성하는 역할을 의미한다.

Abstract는 자바의 추상 클래스에도 사용되는 표현으로 구체적인 내용의 구현을 하위 객체에 위임하는 모델이다.

따라서 추상 팩토리는 객체를 생성하는 것을 별도로 구현하되 관련된 구체적 구현을 하위 클래스에서 담당하게 하는 설계 모델로 이해할 수 있다.

객체 생성에 대한 문제해결을 위한 추상 팩토리 패턴의 UML 클래스 다이어그램을 보도록 한다.

 

 

 

C# 코드로 보면 이해하기 쉽다!

interface IButton
{
    void Paint();
}

interface IGUIFactory
{
    IButton CreateButton();
}

class WinFactory : IGUIFactory
{
    public IButton CreateButton()
    {
        return new WinButton();
    }
}

class OSXFactory : IGUIFactory
{
    public IButton CreateButton()
    {
        return new OSXButton();
    }
}

class WinButton : IButton
{
    public void Paint()
    {
        //Render a button in a Windows style
    }
}

class OSXButton : IButton
{
    public void Paint()
    {
        //Render a button in a Mac OS X style
    }
}

class Program
{
    static void Main()
    {
        var appearance = Settings.Appearance;

        IGUIFactory factory;
        switch (appearance)
        {
            case Appearance.Win:
                factory = new WinFactory();
                break;
            case Appearance.OSX:
                factory = new OSXFactory();
                break;
            default:
                throw new System.NotImplementedException();
        }

        var button = factory.CreateButton();
        button.Paint();
    }
}

 

 

예를 들어 데이터베이스 연동 프로그램에서 다음과 같은 코드가 있다고 가정해본다.

ProductDAO dao = new ProductDAO();
dao.insertDB(p);
...

 

  • productDAO 클래스는 데이터베이스 연동을 구현한 클래스이다.
  • insertDB() 메서드는 인자로 상품 객체를 전달받아 DB에 저장한다.

 

위 코드는 별다는 문제가 없어 보이지만 ProductDAO라는 클래스에 대한 종속성을 만들게 된다.

예를 들어 현재 시스템에서 오라클 데이터베이스를 사용하고, productDAO 역시 오라클에 맞게 제작된 클래스라 가정한다.

이 경우 데이터베이스를 MySQL 또는 다른 데이터베이스로 교체할 경우 ProductDAO를 MySQL에 맞게 다시 구현해야 한다.

그런데 프로그램이 상황에 맞게 오라클 혹은 MySQL에서 실행되어야 한다면 현재 프로그램의 전반적인 구조를 수정할 수 밖에 없을 것이다.

추상 팩토리 패턴은 이러한 경우 유용하게 사용할 수 있다.

직접적인 객체 생성 대신 팩토리 클래스에 객체 생성을 위임하는 구조이기 때문이다.

 

다음 코드는 패턴 구조를 적용한 경우 클라이언트에서 팩토리를 사용하는 부분만 예로 든 것이다.

ProductDAO dao = DAOFactory.create("oracle");
dao.insertDB(p);
...

 

  • ProductDAO : 추상 클래스 또는 인터페이스다. ProductDAO 추상 클래스를 상속받는 OracleDAO, MySQLDAO 등의 클래스가 존재한다.
  • DAOFactory : 오라클이나 MySQL용으로 구현된 ProductDAO 타입의 객체를 생성해서 리턴한다.

 

MVC 패턴이란?

Model-View-Controller의 약어로 주로 GUI 기반의 애플리케이션 개발에 사용되는 디자인 패턴이다.

이러한 패턴의 공통적 목적은 화면과 데이터 처리를 분리하여 코드 간 종속성을 줄이는데 있다.

즉 구성요소 간 역할을 명확하게 하여 코드를 쉽게 분리하고 협업이 용이하게 만드는 것이다.

다음은 기본적인 MVC 패턴구조를 표현한 그림이다.

 

 

모델

데이터를 처리하는 영역이다.

일반적으로 데이터베이스와 연동을 위한 DAO(Data Access Object) 클래스와 데이터 구조를 표현하는 DO(Data Object), 엔티티 클래스 등으로 구성된다.

모델은 뷰나 컨트롤러에 독립적인 구조로 데이터베이스 처리를 필요로 하는 여러 애플리케이션에서 공유할 수 있으며 웹 애플리케이션이 아닌 경우에도 사용할 수 있는 형태다.

 

DAO, DO는 필수적일까?

효과적 DB 연동 구현을 위해 JPA(Java Persistence API)를 사용하는 경우 DAO는 생략되거나 구현 범위가 축소될 수 있다.

또한 하나의 화면 구성을 위해 여러 데이터베이스 혹은 외부 서비스 등과 연계해야 할 경우 이들을 통합하기 위해 별도의 서비스 객체를 두거나 DO 이외에 DTO(Data Transfer Object) 등을 사용하기도 한다.

 

화면 구성을 담당하는 영역이다.

뷰에서 데이터를 직접 가져오는 방식은 권장하지 않고 주어진 데이터를 출력하는 용도로만 사용하는 것이 바람직하다.

뷰 영역의 구현을 위해 뷰 템플릿 엔진이 사용되며 JSP 역시 이러한 뷰 템플릿 엔진 중 하나다.

HTML 이외에 EL, JSTL 등을 사용해 컨트롤러로부터 전달받은 데이터를 출력하고 HTML, CSS 등을 통해 화면을 디자인한다.

뷰는 기본적으로 모델, 컨트롤러와의 종속성이 없도록(독립적) 구현해야 한다.

 

컨트롤러

MVC 패턴의 핵심으로 모든 사용자 요청의 중심에 위치한다.

사용자 요청은 특정 뷰에 바로 전달되지 않고 컨트롤러를 통해야 하며, 컨트롤러는 사용자 요청에 따라 모델을 통해 데이터베이스와 연동하여 데이터를 처리하고 뷰에 전달한다.

뷰로 전달하기 위해 데이터가 들어 있는 DO 혹은 List<DO> 형태의 객체를 request에 저장한 후 포워딩한다.

컨트롤러는 특정 뷰를 지정해야 하기 때문에 뷰와 종속관계가 발생할 수 밖에 없다.

따라서 프로젝트 규모가 커질수록 컨트롤러는 복잡해지고 관리가 어려워지는 문제가 발생하게 된다.

컨트롤러의 구현은 JSP, 서블릿 모두 가능하며, 간단한 기능을 구현할 때는 JSP가 유리하지만 규모 확장과 향후 스프링 프레임워크로의 확장 등을 고려한다면 서블릿 기반의 구현을 권장한다.

 

컨트롤러를 구성하는 세 가지 방법이 존재한다.

사용자 요청마다 컨트롤러를 만드는 것이다.

예를 들어 로그인 기능을 구현하려면 'loginForm.html'로 데이터를 입력받고 Login Controller에서 처리하고 결과를 보여주는 'main.jsp'가 필요한데, 각각의 기능 구현을 동일한 방식으로 처리하게 되면 기능(ex : 회원 가입, 정보 수정 등)마다 컨트롤러를 만들어야 한다.

 

특정 모듈 단위로 하나의 컨트롤러 안에서 여러 요청 단위를 구분해 처리할 수 있다.

예를 들어 MemberController 안에서 로그인, 회원 가입, 정보 수정 등을 처리할 수 있다.

 

프런트 컨트롤러를 따로 두어 모든 요청을 하나의 컨트롤러로 모은 다음 요청에 따라 구현 컨트롤러를 호출하도록 구성할 수도 있다.

 

서블릿만으로 컨트롤러 구현이 가능할까?

단순히 서블릿만으로 컨트롤러 구현은 어렵지 않지만 실제 개발에서는 시스템 전반을 통합할 수 있는 컨트롤러 구조를 설계해서 일종의 프레임워크 개념으로 만들어둘 필요가 있다.

따라서 이러한 부분을 개인 혹은 회사에서 개발하거나 스프링 프레임워크와 같이 오픈소스 프레임워크를 사용하게 된다.

 

 

서블릿 컨트롤러 설계

 

컨트롤러를 구현하기 위해서는 컨트롤러가 처리해야 하는 주요 기능에 대해 먼저 이해해야 한다.

컨트롤러의 가장 기본적 기능인 클라이언트 요청 처리, 입력값 핸들링, 뷰 이동에 대해 살펴보고 실제 서블릿에서 어떻게 구현해야 하는지 살펴보도록 한다.

 

클라이언트 요청 처리

클라이언트 요청을 단일 컨트롤러에서 처리할 것인지 개별 컨트롤러에서 처리할 것인지 결정해야 한다.

서블릿은 URL 요청을 GET, POST 등의 HTTP 메서드를 통해 처리하는 구조이기 때문에 여러 URL 패턴을 하나의 서블릿에서 처리할 수 있지만 URL에 따라 다른 처리를 구현할 수는 없다.

예를 들어 어떤 쇼핑몰에서 제품을 등록하는 기능과 삭제하는 기능이 필요하다고 가정하자.

각각의 요청 URL은 다음과 같다.

제품 등록 요청 URL : /shop/addProduct
제품 삭제 요청 URL : /shop/delProduct

 

이때 두 URL 요청을 하나의 서블릿으로 처리하도록 URL 매핑 설정을 할 수 있으나, 두 요청 모두 GET 방식이라면 URL이 다르더라도 동일한 doGet() 메서드가 호출되기 때문에 어떤 요청이 호출된 것인지 구분할 수 없다.

따라서 각각의 URL 요청을 별도의 서블릿으로 구현해야 한다.

 

이번에는 다른 예로 회원 관리 프로그램을 개발한다면 회원 가입, 승인, 수정, 탈퇴(삭제), 로그인 등의 기능이 필요하며 각각의 요청을 처리하기 위한 컨트롤러가 있어야 한다.

즉 다음과 같이 입력/요청 화면과 입력 데이터를 받아 이를 처리하는 컨트롤러, 처리된 결과를 보여주기 위한 화면이 필요하다.

가입 양식 입력 화면 -> 가입 처리 컨트롤러 -> 가입 완료 화면
신규 회원 관리자 화면 -> 신규 회원 가입 승인 컨트롤러 -> 승인 완료 화면
로그인 양식 입력 화면 -> 로그인 처리 컨트롤러 -> 메인 화면

 

이 경우 각각의 컨트롤러를 구현해야 하지만 같은 단위의 업무를 하나의 컨트롤러에서 처리하는 것이 구조적으로 관리가 쉬울 수 있다.

물론 하나의 컨트롤러에서 처리할 요청이 지나치게 많은 경우 오히려 코드 관리가 어려울 수 있으므로 주의해야 한다.

 

사용자의 요청을 구분해 하나의 서블릿에서 처리하기 위해서는 다음의 두 가지 방법을 사용할 수 있다.

  • URL 의 파라미터 이용
  • 프런트 컨트롤러 구현

 

URL의 파라미터 이용 방법 :

URL에 action과 같이 별도의 파라미터를 두어 요청을 구분하는 방법이다.

http://xxx.com/member?action=create
http://xxx.com/member?action=login

 

  • member: 서블릿 URL 매핑값이다.
  • action: 요청을 구분하기 위한 파라미터다.
  • 요청에는 회원 정보, 로그인 아이디 등의 사용자 데이터가 추가로 포함된다.
  • GET, POST 방식이 모두 가능하다.
  • 컨트롤러에서는 action 값을 비교하여 별도의 메서드 구현 등의 방식으로 처리한다.

 

비교적 간단한 방법이지만 action 파라미터의 구조가 변경되며 관련된 HTML, JSP, 컨트롤러의 수정이 필요하다는 단점이 있다.

실제 구현은 다음과 같이 분기문 처리가 가능하다.

doGet( ... ) {
    String action = request.getParameter("action");
    switch(action) {
        case "create" : createMember(); break;
        case "login" : loginMember(); break;
        ...
    }
}

 

프런트 컨트롤러 구현 방법 :

모든 요청의 진입점이 되는 컨트롤러가 있고 여기에서 서브 컨트롤러를 호출하는 구조다.

좀 더 복잡한 구조를 체계젹으로 처리할 수 있으며 프런트 컨트롤러 패턴으로 정립되어 있어 여러 구현에 응용되는 디자인 패턴이다.

프런트 컨트롤러를 구현하기 위해서는 우선 모든 요청을 하나로 모으는 방법이 필요하다.

일반적으로는 서블릿 매핑의 구조적 특징을 활용하는데, 예를 들어 URL 요청을 특정 확장자 형식으로 끝나도록 설계하는 방식이 있다.

http://xxx.com/member/create.do
http://xxx.com/member/login.do

 

  • /member: 회원 관리 웹 애플리케이션 콘텍스트 혹은 서비스 구분 경로이다.
  • *.do: 서블릿 URL 매핑값으로 모든 요청은 하나의 서블릿을 호출한다.
  • 컨트롤러에서는  .do 앞의 요청 이름(create, login)으로 구분하여 별도의 메서드 혹은 서브클래스를 통해 실행된다. 

 

요청에 대한 파라미터 없이 명확한 이름(create, login)으로 요청할 수 있으며, 요청에 대한 URL 관리가 필요 없다는 장점이 있다.

반면 전체 시스템에 네이버와 같이 포털 형태로 회원 관리, 블로그, 카페 등으로 세부 시스템이 분리되어 있는 경우 콘텍스트를 분리하는 것은 세션 관리에 부담이 갈 수 있다.

또한 단일 콘텍스트에 경로를 구분하는 경우 프런트 컨트롤러에서 모든 요청을 조건문과 메서드 구현만으로 처리하기에는 컨트롤러 클래스가 너무 비대해지는 문제도 발생할 수 있다.

 

따라서 규모가 어느 수준 이상이 되면 경로에 따라 서브 컨트롤러로 포워딩하는 처리가 필요하다.

서브 컨트롤러를 구현하기 위해서는 먼저 다음과 같이 URL 요청을 분석해 사용자 요청을 구분하는 작업이 필요하다.

@WebServlet("*.do")
public class MemberController extends HttpServlet {
   ...
   doGet( ... ) {
      String uri = request.getRequestURI();
      String conPath = request.getContextPath();
      String command = uri.substring(conPath.length());

      switch(command) {
         case "create.do" : createMember(); break;
         case "login.do" : loginMember(); break;
         ...
      }
   }
}

 

이 구조에서는 action과 같은 파라미터는 없지만 메서드를 이용해 사용자 요청을 분리해서 처리한다.

이렇게 switch(혹은 if) 문을 사용하는 구조는 기능 추가 또는 변경이 필요할 때 조건문도 함께 관리해야 하는 문제가 있다.

이러한 문제점은 Command 패턴을 사용하면 다음과 같이 switch(혹은 if) 구조 없이 해당 요청에 맞는 특정 컨트롤러가 실행되도록 구현할 수 있다.

 

public void init(ServletConfig sc) throws ServletException {
   Map<String, SubController> contList = new HashMap<>();

   contList.put("/create.do", new MemberCreateController());
   contList.put("/login.do", new LoginController());
   ...
}

public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   String url = request.getRequestURI();
   String contextPath = request.getContextPath();
   String path = url.substring(contextPath.length());

   SubController subController = contList.get(path);
   subController.process(request, response);
}

 

  • 처리하고자 하는 요청 URL을 키로 해서 컨트롤러 클래스 인스턴스를 맵에 저장한다.
  • 사용자 요청을 처리할 때 경로 이름(명령)만 가지고도 처리할 컨트롤러 객체를 참조할 수 있다.
  • init() 에서 요청에 따른 컨트롤러 객체를 생성한 후 맵에 추가한다.

 

이제 서브 컨트롤러를 운영하는 프런트 컨트롤러를 설계해보자.

먼저 서브 컨트롤러의 규격을 정의한 인터페이스가 필요하다.

public interface SubController {
   void process(HttpServletRequest request, HttpServletResponse response);
}

 

다음으로 필요한 서브 컨트롤러를 구현한다.

앞의 다중 요청 처리 서블릿을 서브 컨트롤러 규격에 맞게 수정한 것이다.

public class MemberController implements SubController {
    void process(HttpServletRequest request, HttpSErvletResponse response) {
        ... 
    }
}

 

스프링 프레임워크에서 프런트 컨트롤러 구현은 어떻게 할까?

스프링 프레임워크의 경우 애너테이션 방식으로 구현하기 때문에 클래스 생성 시 컨트롤러 클래스로 지정한 다음 메서드 단위로 URL 매핑이 가능해 훨씬 간편하면서도 관리가 용이한 구조의 컨트롤러 운영이 가능하다.

@Controller
@RequestMapping("/member")
public class MemberController {
    @PostMapping("create")
    public void createMember() {
        ...
    }
}

 

  • http://xxx.com/member/create URL로 POST 요청을 처리한다.
  • 같은 방법으로 BlogController, CafeController 등을 두어 운영할 수도 있다.

 

입력값 핸들링

서블릿에서 클라이언트의 입력값을 처리하려면 앞에서 배운 것처럼 request.getParameter()를 이용해야 한다.

파라미터가 한두 개라면 문제없겠지만 회원 가입과 같이 여러 정보가 전달되는 경우 모든 값을 request.getParameter()로 받는 것이 문제가 된다.

또한 DAO 클래스와 연동을 위해서는 입력값을 Member 객체로 만든 후에 전달해야 하므로 기본적으로 다음과 같은 코드 구현이 필요하다.

doGet( ... ) {
    Member m = new Member();
    m.setName(request.getParameter("name"));
    m.setTel(request.getParameter("tel"));
    ...
    dao.create(m);
}

 

JSP에서는 useBean 액션을 통해 입력값을 Member 객체로 쉽게 만들 수 있었다.

하지만 서블릿에서는 그런 기능이 제공되지 않기 때문에 별도의 라이브러리를 사용해야 하는데, 대표적으로 Apache Commons BeanUtils 가 쓰인다.

doGet( ... ) {
    Member m = new Member();
    BeanUtils.populate(m, request.getParameterMap());
    ...
    dao.create(m);
}

 

스프링 프레임워크에서의 입력값 핸들링은 어떻게 할까?

스프링 프레임워크에서는 기본적으로 메서드 파라미터 지정으로 자동 처리가 가능하다.

즉 create라는 URL 요청으로 전달되는 파라미터를 Member 클래스의 멤버 변수에 자동으로 전달해 인자로 제공한다.

@PostMapping("create")
public void createMember(Member m) {
    dao.create(m);
}

 

뷰 이동

컨트롤러에서 사용자 요청을 처리한 다음에는 적절한 뷰로 이동할 수 있어야 한다.

이때 뷰에서 보여줄 데이터를 포함해서 이동해야 하는 경우와 그렇지 않은 경우가 있다.

 

데이터를 포함하지 않는 경우 해당 페이지로 리다렉션할 수 있다.

JSP, 서블릿 모두 response.sendRedirect()를 사용하면 된다.

response.sendRedirect("main.jsp");

 

데이터를 포함하는 경우 request scope object에 속성으로 데이터를 넣은 후 원하는 페이지로 포워딩한다.

데이터 활용 목적에 따라 session 또는 application 을 사용할 수도 있으며 여러 데이터를 포함하는 것도 가능하다.

doGet( ... ) {
    ...
    request.setAttribute("member", m);
    RequestDispatcher dispatcher = request.getRequestDispatcher("userInfo.jsp");
    dispatcher.forward(request, response);
}

 

JSP와 스프링 프레임워크에서의 뷰 이동은 어떻게 할까?

JSP에서 뷰 이동을 구현할 경우 다음과 같이 작성한다.

<%
    request.setAttribute("member", m);
    pageContext.forwared("userInfo.jsp");
%>

 

스프링 프레임워크의 경우 인자로 전달된 Model 객체에 원하는 데이터를 저장하고 뷰 페이지 이름을 리턴하기만 하면 된다.

@GetMapping("info")
public String getMemberInfo(int id, Model model) {
    ...
    model.addAttribute("member", m);
    return "userInfo";
}

 

  • 리턴되는 문자열 값은 뷰 페이지의 이름이며 확장자는 생략된다.

 

컨트롤러 기초 예제: 계산기 구현

 

 

1. 뷰 구현

 

calcForm.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>계산기 컨트롤러</title>
</head>
<body>

	<h2>계산기-컨트롤러</h2>
	<hr>
	<form method="post" action="/jwbook/calcControl">
		<input type="text" name="n1" size="10"> <select name="op">
			<option>+</option>
			<option>-</option>
			<option>*</option>
			<option>/</option>
		</select> <input type="text" name="n2" size="10"> 
		<input type="submit" value="실행">
	</form>

</body>
</html>

 

calcResult.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>계산기-컨트롤러</title>
</head>
<body>
	
	<h2>계산 결과-컨트롤러</h2>
	<hr>
	결과 : ${result}

</body>
</html>

 

2. 컨트롤러 구현

 

calcController.java

package ch08;

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;

/**
 * Servlet implementation class CalcController
 */
@WebServlet("/calcControl")
public class CalcController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public CalcController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		int n1 = Integer.parseInt(request.getParameter("n1"));
		int n2 = Integer.parseInt(request.getParameter("n2"));
		long result = 0;
		
		switch(request.getParameter("op")) {
			case "+" : result = n1 + n2; break;
			case "-" : result = n1 - n2; break;
			case "*" : result = n1 * n2; break;
			case "/" : result = n1 / n2; break;
		}
		
		request.setAttribute("result", result);
		getServletContext().getRequestDispatcher("/ch08/calcResult.jsp").forward(request,response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

 

 

 

고급 컨트롤러 서블릿 구현

 

 

1. 뷰 구현

productList.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>상품 목록</title>
</head>
<body>

	<h2>상품 목록</h2>
	<hr>	
	<table border="1">
		<tr>
			<th>번호</th>
			<th>상품명</th>
			<th>가격</th>
		</tr>
		<c:forEach var="p" varStatus="i" items="${products}">
			<tr>
				<td>${i.count}</td>
				<td><a href="/jwbook/pcontrol?action=info&id=${p.id}">${p.name}</a></td>
				<td>${p.price}</td>
			</tr>
		</c:forEach>
	</table>

</body>
</html>

 

productInfo.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>상품정보 조회</title>
</head>
<body>

	<h2>상품정보 조회</h2>
	<hr>
	<ul>
		<li>상품코드: ${p.id}</li>
		<li>상품명: ${p.name}</li>
		<li>제조사: ${p.maker}</li>
		<li>가격: ${p.price}</li>
		<li>등록일: ${p.date}</li>
	</ul>

</body>
</html>

 

2. 모델 구현

Product.java

package ch08;

public class Product {
	private String id;
	private String name;
	private String maker;
	private int price;
	private String date;
	
	public Product(String id, String name, String maker, int price, String date) {
		this.id = id;
		this.name = name;
		this.maker = maker;
		this.price = price;
		this.date = date;
	}

	public String getId() { 
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getMaker() {
		return maker;
	}

	public void setMaker(String maker) {
		this.maker = maker;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public String getDate() {
		return date;
	}

	public void setDate(String date) {
		this.date = date;
	}
}

 

ProductService.java

package ch08;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ProductService {
	Map<String, Product> products = new HashMap<>();
	
	public ProductService() {
		Product p = new Product("101", "애플사과폰 12", "애플전자", 1200000, "2020.12.12");
		products.put("101", p);
		p = new Product("102", "삼전우주폰 21", "삼전전자", 1300000, "2021.2.2");
		products.put("102", p);
		p = new Product("103", "엘스듀얼폰", "엘스전자", 1500000, "2021.3.2");
		products.put("103", p);
	}
	
	public List<Product> findAll() {
		return new ArrayList<>(products.values());
	}
	
	public Product find(String id) {
		return products.get(id);
	}
}

 

3. 컨트롤러 구현

ProductController.java

package ch08;

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;

/**
 * Servlet implementation class ProductController
 */
@WebServlet("/pcontrol")
public class ProductController extends HttpServlet {
	private static final long serialVersionUID = 1L;
    ProductService service;
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ProductController() {
        service = new ProductService();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		String action = request.getParameter("action");
		String view = "";
		
		if(request.getParameter("action") == null) {
			getServletContext().getRequestDispatcher("/pcontrol?action=list").forward(request, response);
		}
		else {
			switch(action) {
				case "list" : view = list(request, response); break;
				case "info" : view = info(request, response); break;
			}
			System.out.println("action " + action);
			getServletContext().getRequestDispatcher("/ch08/"+view).forward(request, response);
		}
	}
	
	private String list(HttpServletRequest request, HttpServletResponse response) {
		request.setAttribute("products", service.findAll());
		return "productList.jsp";
	}
	
	private String info(HttpServletRequest request, HttpServletResponse response) {
		request.setAttribute("p", service.find(request.getParameter("id")));
		return "productInfo.jsp";
	}

}

 

 

'JSP' 카테고리의 다른 글

SQL 및 JDBC 기본 구조와 API  (0) 2022.01.02
데이터베이스와 JDBC  (0) 2021.12.31
JSP - 커스텀 태그, EL, JSTL  (0) 2021.12.30
JSP 액션 태그  (0) 2021.12.29
JSP 프로그래밍  (0) 2021.12.29
Comments