'model2'에 해당되는 글 1건

  1. 2009.02.26 Model1, Model2, Struts 개발 방식의 비교 분석

Model1, Model2, Struts 개발 방식의 비교 분석

|

Model1, Model2, Struts 개발 방식의 비교 분석

Summary : 지난 강좌에서는 Struts예제를 간단하게 살펴보았다. 이번 강좌는 Struts 내부를 살펴보기에 앞서 Model1과 Model2개발 방식에 대하여 살펴본다. 각각의 장, 단점과 Struts와 같은 프레임워크가 등장하게 된 배경에 대하여 살펴볼 생각이다. Struts가 Model2개발방식의 확장이기 때문에 Model2를 확실하게 이해하는 것이 필요하다. 이번 강좌에서는 Model2에 대하여 확실하게 이해할 수 있도록 하겠다.

Model 1 개발 방식

Model 1개발 방식은 현재 국내에서 가장 많이 사용되고 있는 개발 방식으로 대부분의 개발자들이 이 방법으로 웹에서 개발을 진행할 것이다. 물론 Model 2를 이용하는 경우도 있지만, 아직까지는 드물게 사용하고 있는 것으로 보인다. 따라서 이 강좌를 읽는 대부분의 개발자들이 웹 개발을 주로 하는 개발자라고 볼 때 Model 1 방식으로 개발을 진행해 보았을 것이다.

Model 1방식으로 개발되는 경우를 살펴보면 다음과 같다.

1. JSP/Servlet만을 이용하여 개발하는 경우.

2. JSP + Java Bean을 이용하여 개발하는 경우.

3. JSP + Custom Tag를 이용하여 개발하는 경우.

4. 1,2,3이 적절히 혼합하여 개발되는 경우.

위 모든 경우가 개발하는 기술들이 다르지만 개발 방식에 있어서는 똑같이 Model 1방식으로 따르고 있다. Java Bean, Custom Tag를 이용하여 재사용 가능한 부분이나 복잡한 로직을 넣어서 개발하는 방법이 가장 많이 사용되지만 그 또한 Model 1 개발방식이다.

Model 1은 대부분의 개발자들이 경험했으며, 현재도 가장 많이 사용하고 있는 개발 방식이라고 생각하여 본 강좌에서는 예제는 다루지 않았다. Model 1개발 방식에 대하여 궁금한 개발자들은 시중에 나와 있는 JSP 책을 공부해보면 대부분의 예제 소스들이 Model 1방식으로 구현되어 있기 때문에 쉽게 접할 수 있을 것이다.

Model 1개발 방식을 사용하면 다음과 같은 장점이 있다.

1. 개발 속도가 빠르다.

2. 개발자의 스킬이 낮아도 배우기 쉬워 빠르게 적용할 수 있다.

그러나 Model 1을 사용할 경우 다음과 같은 단점이 있다.

1. JSP페이지에서 프리젠테이션 로직과 비즈니스 로직을 모두 포함하기 때문에 JSP페이지가 너무 복잡해 진다.

2. 프리젠테이션 로직과 비즈니스 로직이 혼재되어 있기 때문에 개발자와 디자이너의 분리된 작업이 어려워진다.

3. JSP페이지의 코드가 복작해 짐으로 인해 유지보수 하기 어려워진다.

지금까지 Web Application들은 단순한 경우가 많았기 때문에 Model 1방식으로 소화하기 충분했다. 그러나 점차 Web Application이 복잡해지고, 사용자들의 요구가 증가함에 따라 새로운 방식의 개발방식의 요구가 증대되게 되었다. 또한 기존에 Web Application을 개발하여 유지해오던 많은 사용자들이 개발비용보다 유지보수비용도 무시할 수 없다는 것을 느끼게 되었다. 따라서 점차 개발시의 개발 비용보다는 유지보수를 쉽게 할 수 있는 개발 방식을 요구하는 경우가 많아졌다. 이 같은 요구사항을 만족시키기 위한 개발 방법으로 Model 2가 대안으로 대두되게 되었다.

Model 2 개발 방식

Model 1개발 방식에 한계를 느끼면서 Model 2개발 방식이 나오게 되었지만, 대부분의 개발자들이 Model 1 개발 방식에 익숙해져 있어 현재 국내에서는 아직까지 많이 사용되고 있지 않을 것이 현실이다. 하지만 Model 2 개발 방식에 대한 필요성이 증가하고 있으며, 고개들이 요구하는 경우도 빈번하게 발생하고 있다. 따라서 Model 2 개발 방식의 기본 개념에 대하여 살펴보고, Model 2 방법으로 개발하는 방법에 대하여 살펴보도록 하겠다.

Model 2 개발 방식은 이전부터 있었던 개념을 웹에 적용한 개념이다. 다양한 개발에 있어 근간이 되어온 MVC 패턴을 웹에 적용하여 개발이 가능하도록 구현하는 방식을 Model 2방식이라고 한다. MVC 패턴에 대하여 익숙하지 않은 개발자들을 위하여 간략하게 살펴보면 다음과 같다.

MVC는 Model-View-Controller로 각각의 역할을 나누어 작업하고자 하는 일을 분담시키는 것을 말한다.

Model은 Application 로직을 담당하는 부분으로 Database나 Legacy System과의 로직을 담당하는 부분을 말한다. Model은 Application으로부터 UI가 분리된다.

View는 사용자가 직접 사용하는 부분으로 Presentation 로직을 담당하는 부분이다. Controller와 Model에 의해 생성된 결과물을 보여주는 역할을 한다.

Controller는 Business Logic을 담당하는 부분으로 사용자의 요청을 받아 요청에 해당하는 작업을 한 후 작업 결과에 따라 응답을 결정하는 역할을 한다. Model과 View사이에서 데이터를 전달하는 역할을 한다.

Model 2는 이 같은 MVC구조를 웹에 적용하여 개발하는 방식을 말한다. View는 JSP가 담당하고, Controller는 Servlet, Model은 application을 이용하여 개발하는 방식을 말한다. 다음 그림을 보면 Model 2 개발 방식을 더 쉽게 이해할 수 있을 것이다.

Model 2를 이용한 게시판 만들기

Model 2 방식으로 개발하는 과정을 더 깊이 있게 이해하기 위하여 간단한 예제를 만들어 본다. 예제는 간단한 게시판을 하나 만들어 볼 생각이다. 하지만 게시판의 과정을 간소화하여 Model 2에 대한 전체적인 흐름을 이해하는데 있다. 따라서 이 게시판은 미완성된 상태로 만들어질 것이다.

이 같이 간략하게 예제를 생성해 보는 이유는 Model 2에 처음 접하는 개발자들이 데이터베이스 접속이나 쿼리등에 신경쓰지 않고 Model 2에 대해서만 집중하도록 하기 위하여 Model 2에 필요한 부분들만 발췌하여 예제 소스를 구성하였다.

예제를 만들기 전에 단순한 게시판을 만들기 위하여 필요한 작업을 생각해보자.

1. 사용자가 입력한 글의 삽입(insert)

2. 기존의 게시물의 수정(update)

3. 기존의 게시물의 삭제(delete)

4. 게시물 하나의 내용을 보는 경우(view)

5. 게시물에서 특정 게시물의 가져오는 경우(list)

보통 위 5가지가 하나의 게시판을 만들기 위하여 필요한 작업이다. 위 다섯가지 작업을 Model 2개발 방식으로 개발하는 과정에 대하여 살펴본다.

먼저 Model 부분을 생성한다. Model은 데이터베이스에 게시물을 생성, 수정, 삭제, 보기등의 작업을 실질적으로 하는 부분이다. 게시물 하나에 해당하는 Board객체와 실질적으로 Board객체를 생성, 수정, 삭제하는 BoardManager 두개의 클래스로 나뉜다.

package net.javajigi.model2board;

public class Board {
	private int id = 0;
	private String name = null;
	private String title = null;
	private String createDate = null;
	private String email = null;
	private String content = null;

	public String getContent() {
		return content;
	}

	public String getCreateDate() {
		return createDate;
	}

	public String getEmail() {
		return email;
	}

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public String getTitle() {
		return title;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public void setCreateDate(String createDate) {
		this.createDate = createDate;
	}

	public void setEmail(String email) {
		this.email = email;
	}

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

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

	public void setTitle(String title) {
		this.title = title;
	}
}
package net.javajigi.model2board;

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

public class BoardManager {
	public static int insert(Board board) 
		throws SQLException {
			
		/*
		 * 이 부분은 인자로 전달된 board객체의 내용을 데이터베이스에 
		 * 저장하는 소스코드가 들어간다.
		 */			
		return 0;
	}
	
	public static int update(Board board) 
		throws SQLException {
		/*
		 * 이 부분은 인자로 전달된 board객체의 내용을 데이터베이스에 
		 * 저장되어 있는 게시물을 수정하는 소스코드가 들어간다.
		 */			
			
		return 0;			
	}
	
	public static int delete(int id) 
		throws SQLException {
		/*
		 * 이 부분은 인자로 id의 게시물을 삭제하는 
		 * 소스코드가 들어간다.
		 */			

		return 0;			
	}
	
	public static Board view(int id) 
		throws SQLException {
		/*
		 * 이 부분은 인자로 id의 게시물을 선택하여 Board객체에 저장하여 
		 * 전달하는 소스코드가 들어간다.
		 */			

		return null;			
	}			

	public static List list(int currentPage, int pagePerCount) 
		throws SQLException {
		/*
		 * 이 부분은 인자로 현재 페이지와 페이지당 게시물의 수에 
		 * 해당하는 게시물을 선택하여 각각의 게시물을 Board객체에 저장한 후
		 * List에 저장하여 전달하는 소스코드가 들어간다.
		 */			

		return null;			
	}
}

위 두개의 클래스가 MVC에서 Model역할을 하는 클래스들이다.

다음은 Controller에 해당하는 부분을 살펴본다. Controller는 생각보다 복잡하다. 요청시 전달되는 action에 따라 다른 작업을 하도록 구현하였다. 예제 소스를 보면서 Controller가 수행하는 과정을 살펴보기 바란다. 예제는 최대한 단순하게 구성하였다.

package net.javajigi.model2board;

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

import net.javajigi.model2board.action.Action;
import net.javajigi.model2board.action.CommandFactory;

public class BoardServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
		doPost(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
		String responseURL = null;
		Action action = null;
		try {
			/*
			 * CommandFactory에 요청에 의하여 전달된 action을 전달하여
			 * 해당하는 Action객체를 생성한다.
			 */
			CommandFactory cf = CommandFactory.getInstance();
			action = cf.getAction((String)request.getParameter("action"));
			
			responseURL = action.execute(request, response);	
		} catch (Exception e) {
			throw new ServletException(e.getMessage());
		}
		
		/*
		 * Action에 의해서 전달된 Board, List객체들이 response객체를 통하여 전달된다.
		 */
		RequestDispatcher rd = request.getRequestDispatcher(responseURL);
		rd.forward(request, response);
	}
}
package net.javajigi.model2board.action;

public class CommandFactory {
	
	private CommandFactory() {
	}
	
	public static CommandFactory getInstance() {
		return new CommandFactory();
	}
	
	/*
	 * 수행해야할 명령어에 해당하는 Action객체를 생성.
	 */
	public Action getAction(String command) 
		throws IllegalCommandException{
		Action action = null;
		
		if ( command.equals("list") ) {
			action = new ListAction();
		} else if ( command.equals("view") ) {
			action = new ViewAction();
		} else if ( command.equals("insert") ) {
			action = new InsertAction();
		} else if ( command.equals("update") ) {
			action = new UpdateAction();
		} else if ( command.equals("delete") ) {
			action = new DeleteAction();
		} else {
			throw new IllegalCommandException(
				"잘못된 실행명령입니다. 다른 명령을 내려 주십시요");
		}
		
		return action;
	}
}
package net.javajigi.model2board.action;

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

public interface Action {
	public String execute
		(HttpServletRequest request, HttpServletResponse response)
		throws Exception;
}
package net.javajigi.model2board.action;

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

public class ListAction implements Action {

	public String execute(
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {
		/*
		 * BoardManager의 list메써드를 호출하여 
		 * List를 response에 저장하는 소스코드가 들어간다. 
		 * list.jsp에서 response에 저장된 List객체를 이용한다.
		 */
		System.out.println("게시물 List보기");
		
		return "list.jsp";
	}
}
package net.javajigi.model2board.action;

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

public class ViewAction implements Action {

	public String execute(
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {
		/*
		 * BoardManager의 view메써드를 호출하여 반환된 
		 * Board를 response에 저장하는 소스코드가 들어간다. 
		 * view.jsp에서 response에 저장된 Board를 사용하게 된다.
		 */
		System.out.println("게시물 보기");
			
		return "view.jsp";
	}
}
package net.javajigi.model2board.action;

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

public class InsertAction implements Action {

	public String execute(
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {
		/*
		 * request에 저장되어 있는 인자값으로 Board객체를 생성하여 
		 * BoardManager의 insert메써드를 호출하여 새로운 게시물을
		 * 입력한다. 입력을 완료한 후 
		 */
		System.out.println("게시물 생성");
			
		return "insert.jsp";
	}
}}
package net.javajigi.model2board.action;

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

public class UpdateAction implements Action {

	public String execute(
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {
		/*
		 * request에 저장되어 있는 인자값으로 Board객체를 생성하여 
		 * BoardManager의 update메써드를 호출하여 기존의 게시물을 수정한다.
		 * 
		 */
		System.out.println("게시물 수정");
			
		return "update.jsp";
	}
}
package net.javajigi.model2board.action;

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

public class DeleteAction implements Action {

	public String execute(
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {
		/*
		 * request에 전달된 ID 이용. BoardManager의 delete메써드를 
		 * 호출하여 해당 게시물을 삭제하는 소스코드가 들어간다.
		 */
		System.out.println("게시물 삭제");
			
		return "delete.jsp";
	}
}

지금까지 MVC에서 Controller에 해당하는 부분의 예제소스를 살펴보았다. Action을 상속하는 클래스가 많아서 그렇지 그렇게 많은 클래스가 생성되지는 않았다. 각 예제는 간단하기 때문에 개발자들이 조금만 시간을 들여 본다면 쉽게 이해할 수 있을 것으로 생각된다. Sequence Diagram을 같이 제공하는 것이 개발자들의 이해를 빠르게 할 수 있으나 필자의 시간 관계상 같이 첨부하지 못함을 아쉽게 생각한다. 혹 시간 되는 개발자들이 다른 개발자들을 위하여 위 소스의 Sequence Diagram을 그려준다면 고맙게 생각한다.

위 예제 소스를 보면 알겠지만 Action에서 responseURL로 반환되는 값이 실제 게시판을 만들때와 다름을 알 수 있다. 게시물 생성후에 보통 list.jsp로 이동하는 것이 맞다. 그러나 위 예제는 단순히 Model 2를 이해하기 위한 소스이기 때문에 이 강좌를 읽는 개발자들이 Model 2에 대하여 더 쉽게 이해하기 위하여 위처럼 만들었음을 알기 바란다.

위 처럼 Controller에 해당하는 Servlet을 생성하였다면 web.xml에 아래와 같이 mapping해 주어야 한다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">
<web-app>
    <servlet>
        <servlet-name>board</servlet-name>
        <servlet-class>net.javajigi.model2board.BoardServlet</servlet-class>
    </servlet>
    <!-- Board Servlet Mapping -->         
    <servlet-mapping>
        <servlet-name>board</servlet-name>
        <url-pattern>*.m2</url-pattern>
    </servlet-mapping>
 </web-app>

Struts가 Model 2방식을 바탕에 두고 있기 때문에 Model 2예제도 Struts와 비슷하게 생성했다. 확장자가 m2로 끝나는 모든 URL은 BoardServlet으로 mapping하고 있음을 볼 수 있다. 따라서 확장자가 m2이고 action인자를 가질 경우 해당하는 Action이 생성되어 해당하는 작업을 하게 된다.

마지막으로 지금까지 생성한 Controller와 Model을 이용하는 View에 해당하는 JSP를 만들어 보자.

<html>
<body>
<form name="f" method="post" action="board.m2">
이동할 페이지의 명령어를 입력하십시요.<br>
(명령어 : list, view, insert, delete, update)
<br/>
<input type="text" name="action">
<br/>
<input type="submit" name="확인" value="확인"/>
</form>
</body>
</html>

<%= "This is View Page"%>

list.jsp, update.jsp등도 view.jsp와 같은 코드로 작성되어 있다. 위 화면에서 action.jsp에서 view명령어를 입력하여 실행되었을 때의 URL을 살펴보면 http://localhost:8080/zigistruts/model2board/board.m2 와 같이 확장자가 m2로 되어 있음을 확인할 수 있다.

action.jsp에서 다른 명령어를 입력하여 list, update, delete, insert에서도 같은 결과를 확인할 수 있는지 확인해 보기 바란다.

만약 개발자들이 위 예제를 바탕으로 게시판을 완성하고 싶다면 BoardManager와 각 Action클래스의 execute를 채워넣는다면 하나의 게시판을 완성할 수 있을 것이다.

지금까지 Model 2방식으로 개발된 게시판 예제를 살펴보았다. 하지만 Model 2 방식에도 한계가 있다. 개발 시간과 Model 2방식을 익히기에 많은 시간을 요한다는 단점이 있다. 그 외에도 Model 2방식으로 작업할 때 불편한 점이 많다. 위 예제는 간단한 게시판을 하나 만들기 위하여 수많은 Action클래스와 Servlet, Factory등을 생성했다.

새로운 작업이 발생할 경우 Servlet과 Factory에 수정을 가한 후 컴파일등 해야할 작업이 너무 많다. 위 예제는 5개의 작업이기 때문에 Factory에서 if/else를 5번 사용하여 해당 Action클래스를 생성했다. 하지만 작업이 많아질 경우 수많은 if/else를 가져야 한다. 이를 보완하기 위해 수많은 Factory클래스를 만든다고 가정할 경우 Servlet클래스가 수정되거나 Factory클래스 하나에 Servlet클래스 하나씩을 만들어 주어야 하는 불편함이 있다.

Model 2의 이 같은 단점을 보완하기 위하여 나온 것이 Model 2+1이다. 다음 절에서는 Model 2+1에 대하여 간략하게 살펴본다.

Model 2+1 개발 방식

앞에서 살펴본 바와 같이 Model 2 개발 방식에도 한계점이 나타났다. 따라서 새로운 개발 방식으로 Model 2+1방식이 대두되게 되었다. Model 3가 아닌 Model 2+1이 된 이유는 기본 구조가 Model 2개발 방식에 근간을 두고 있기 때문이다.

Model 2+1방식은 위 Model 2에서 본 Servlet이나 에러처리, Request의 인자 처리등 재사용 가능하도록 프레임워크화한 것을 말한다.

또한 Factory클래스에서 문제점으로 나타났던 if/else에 대한 처리는 XML에 모든 정보를 저장하여 해결이 가능하도록 했다. XML에서 새로운 URL을 추가해주면 되기 때문에 클래스를 컴파일하지 않아도 반영이 가능하도록 되었다.

이 같은 Model 2+1방식을 지원하는 대표적인 프레임워크가 Jakarta의 Struts이다. 지금까지 이 강좌를 진행한 목적은 Struts가 나타나게 된 이유를 설명하기 위해서이다. Struts에서는 Servlet으로 ActionServlet하나만을 이용한다. Model 2방식을 이용할 경우 수 많은 Servlet이 Controller역할을 하는 경우가 많지만 Struts의 경우에는 하나의 Servlet만으로 Controller역할을 수행할 수 있다. 또한 Factory클래스에서 if/else로 처리되었던 부분은 struts-config.xml을 두어 XML에서 설정을 해주면 해당 URL을 반영할 수 있도록 하였다.

그외에도 View의 다양한 커스텀 태그, Resource Bundle(Properties파일)의 손쉬운 사용, 에러 처리, 국제화처리등 다양한 부분을 프레임워크화하여 Model 2개발에 익숙하지 않은 개발자들에게 일관되고, 손쉽게 개발이 가능하도록 지원하고 있다.

지금까지 Model 1, Model 2, Model 2+1개발 방식에 대하여 각각 살펴보았다. Struts가 개발되게 된 배경에 대해서도 간략하게 살펴보았다.

다음 강좌부터 Struts의 내부를 세부적으로 살펴보도록 하겠다. 이번 강좌에 대하여 많은 개발자들의 주석이 있기를 기대한다.

참고 자료


'Document' 카테고리의 다른 글

DAO (Data Access Objects)  (0) 2009.02.27
MVC (model-view-controller)  (0) 2009.02.27
자바 에러  (0) 2009.02.02
클래스 다이어그램  (0) 2009.01.21
struct <tag>  (0) 2009.01.19
And
prev | 1 | next