MVC의 개요

기존의 모델1 개발방식에 익숙하신 분들을 위해서 보다 효율적이고 유지보수하기 편한 MVC기법으로 웹프로그래밍 하는 방법에 대해서

강좌를 시작하려고 합니다.

그럼 기존의 모델1과 MVC는 도대체 뭐가 다르냐고 반문하시는 분들이 계실겁니다.

크게 다른점은 모델1의 경우에 jsp파일 하나에 모든 내용이 다 들어간다는것과 MVC는 그러한 부분을 각각의 특성에 맞게 분리했다는겁니다.

그럼 뭘 어떻게 분리하냐가 중요하겠죠.. 여기서 MVC 즉 Model, View, Controller에 대한 이해가 필요합니다.

View는 다들 잘 알다시피 html태그라고 생각하시면 됩니다. Model은 간단하게 데이타베이스에 쿼리하는 부분이라고 할수있습니다.

나머지 Controller 는 2개를 연결시켜주는 역할이라고 보시면 됩니다.

의미상으로는 아주 간단합니다. 앞으로 진행할 강좌는 다음과 같은 단계에 걸쳐 진행하도록 하겠습니다.

1.이클립스에서 톰캣을 활용한 웹프로그래밍 환경구축하기
2.모델1 기법으로 게시판 리스트 화면 만들기(List.jsp)
3.MVC 기법으로 게시판 리스트 화면 만들기
4.위의 2개의 구조를 비교분석하기

다음강좌에서 뵙겠습니다..

 

MVC 첫번째강좌 - 모델1 기법으로 게시판 리스트 만들기

작성자 : 김시웅
테스트환경 : 이클립스2.1.2, mysql, tomcat4.1.30

본강좌는 jsp에 어느정도 익숙하신걸로 가정하고 진행하겠습니다. 일단 환경부터 구축해야겠죠^^.

먼저 DB에 테이블을 하나 만들겠습니다. Board(번호, 이름, 제목, 내용, 등록일) 테이블을 하나 만들고 디폴트로 몇개의 데이타를 넣겠습니다.(Board.sql, insert.sql) 자, 기본적인 준비는 됐습니다.

이제 이클립스에서 자바프로젝트를 하나 만듭니다. 프로젝트명은 MvcBoard 라고 하겠습니다.

프로젝트에서 www 폴더를 만듭니다. 이제 톰캣플러그인이 필요합니다. 설치하지 않으신분은 자료실에서 다운 받으시면 됩니다.

톰캣플러그인이 설치되셨다면 MvcBoard->특성을 누릅니다. 왼편에 Tomcat을 선택하고 Is a Tomcat Project 를 체크합니다. 다음에 Subdirectory to set as web application root(optional) 부분을 /www 로 변경합니다. 변경한 화면은 다음과 같습니다.


확인을 누르면 톰캣설정이 완료되었습니다. 이제 www폴더에 test.jsp를 만들고 내용은 test 로 합니다.

톰캣을 실행하고 브라우저에서 http://localhost:8080/test.jsp 를 입력합니다. 아래화면처럼 나오면 성공입니다.


지금까지는 db를 구성하고 이클립스에서 프로젝트를 톰캣과 연동하는 과정이었습니다. 앞으로 진행할 웹관련 강좌는 이런형태로
프로젝트를 설정하게됩니다.

자 그럼 모델1방식으로 게시판 리스트를 만들어보도록 하겠습니다. www폴더에 List.jsp 를 만들고 다음의 순서대로 내용을 채우겠습니다.

먼저 import 와 contentType을 설정합니다.
<%@ page language="java" import="java.sql.*,java.io.*" contentType="text/html; charset=euc-kr" %>

그 다음에 db에 접속을 해서 커넥션을 맺습니다. db서버주소, db명, 유저명,암호는 여러분의 db에 맞게 직접 넣으시기 바랍니다.
본강좌에서는 mysql을 사용합니다.

<%
   String url = "jdbc:mysql://db서버주소:3306/db명";
   String username = "유저명";
   String password = "암호";
   Connection con = null;
   try {
      Class.forName("org.gjt.mm.mysql.Driver");
      con = DriverManager.getConnection(url, username, password);
   } catch (ClassNotFoundException e) {
      out.println("드라이버를 찾을수 없습니다.");
   } catch (SQLException ex) {
      out.println("접속실패");
   }
%>

커넥션을 얻었다면 ResultSet을 얻습니다. 테이블은 위에서 만든 Board 입니다. Board.sql을 사용하여 테이블을 만들고 insert.sql 을 사용하여 데이타를 먼저 입력해놓겠습니다.(위의 첨부파일을 참조하세요)
<%   
   PreparedStatement ps = null;
   ResultSet rs = null;      
   String query = "select * from Board";
   try {
      ps = con.prepareStatement(query);
      rs = ps.executeQuery();
%>

이제 ResultSet을 사용하여 화면에 리스트를 구성하도록 하겠습니다.
<html>
   <body>
      <table border="1">
         <tr>
            <td>번호</td>
            <td>제목</td>
            <td>이름</td>
            <td>등록일</td>
         </tr>
<%
      while (rs.next()) {            
         String num = rs.getString("num");
         String subject = rs.getString("subject");
         String name = rs.getString("name");
         String regiDate = rs.getString("regiDate");
%>         
         <tr>
            <td><%=num%></td>
            <td><%=subject%></td>
            <td><%=name%></td>
            <td><%=regiDate%></td>
         </tr>
<%
      } 
%>
      </table>
   </body>
</html>

자, 이제 남은건 커넥션을 닫기만 하면 됩니다.
<%
   } catch (SQLException e) {
      out.println(e);
   } finally {
      try {
         if(rs != null) rs.close();         
         if(ps != null) ps.close();         
         if(con != null) con.close();               
      } catch(SQLException e) {
         out.println(e);      
      }
   }
%>

이제 브라우저에서 http://localhost:8080/List.jsp 를 호출하면 다음화면처럼 나올겁니다.


다음 강좌에서는 MVC기법으로 소스를 변경해보도록 하겠습니다.(물론 화면에 나타나는 결과물은 동일할겁니다)

 

MVC 두번째강좌 - MVC 기법으로 게시판 리스트 만들기(컨트롤러)

작성자 : 김시웅
테스트환경 : 이클립스2.1.2, mysql, tomcat4.1.30

이전강좌에서는 전통적인 방식으로 화면을 만들었습니다. 그동안 다들 이렇게 코딩하셨다구요^^..

상관없습니다. 저도 그렇게 해왔었으니까요.

먼저 MVC로 들어가기전에 페이지가 호출되는 순서에 대해서 살펴보겠습니다. 모델1은 그냥 jsp파일을 직접 호출합니다. 하지만 MVC는 컨트롤러 개념이 들어갑니다. 그래서 먼저 컨트롤러를 호출하고 모델에서 데이타를 구성합니다. 마지막으로 jsp를 호출하죠.

이해가 되시나요? 다시 얘기하자면 컨트롤러 -> 모델 -> 뷰 의 순서로 호출됩니다.

그렇다면 컨트롤러를 가장 먼저 만들어야겠죠. 자 이제부터 MVC에 본격적으로 들어갑니다.

컨트롤러는 쉽게 얘기해서 중개자 역할입니다. 일종의 관문인데 모든 호출은 컨트롤러를 거칩니다. 컨트롤러는 서블릿으로 만들겠습니다. jsp로 만들어도 되지만 서블릿이 좀더 컨트롤러가 하는 역할에 적합하다고 볼수있습니다.

이제 이클립스로 가서 MvcBoard->src 에서 팩키지를 하나 만들겠습니다. 팩키지명은 javaforum.board 로 합니다.

javaforum.board 에서 클래스를 하나 만듭니다. 클래스명은 Controller 로 합니다.

다음에 나오는 순서대로 Controller 클래스에 내용을 채우겠습니다.

먼저 필요한 클래스들을 import합니다.
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import javax.servlet.ServletConfig;
import java.io.IOException;

Controller클래스를 HttpServlet으로 상속받도록 변경합니다.
public class Controller extends HttpServlet {

이제 Controller 클래스에 들어갈 함수를 작성합니다.
   public void init(ServletConfig config) throws ServletException {
      super.init(config);            
   }
   
   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws IOException, ServletException {         
      doProcess(request, response);
   }
   
   public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws IOException, ServletException {         
      doProcess(request, response);      
   }
   
   public void doProcess(HttpServletRequest request, HttpServletResponse response) {
   }

이것으로 서블릿이 하나 완성되었습니다. 물론 하는일은 없습니다^^.. 단, 여기서 살펴볼 부분이 doGet, doPost함수에서 doProcess를 호출하도록 했다는겁니다. get방식이든 post방식이든 doProcess함수에서 처리하겠다는 의미가 되겠죠...

근데 doProcess에 아무것도 없다구요? 이제부터 컨트롤러의 실제 내용이 들어가게 될겁니다.

일단 서블릿이 제대로 작동되는지 테스트를 해봐야되니깐 doProcess에 System.out.println("컨트롤러 호출"); 만 입력하겠습니다.

여기서 한가지 환경설정부분이 필요합니다. src에 있는 파일이 컴파일될때 기본적으로 bin폴더로 들어가게 됩니다. 이 경로를 바꿔야되겠습니다.

먼저 www폴더에서 WEB-INF 폴더를 새로 만듭니다. 그리고 WEB-INF 폴더에 classes 폴더를 만듭니다.

다 만드셨으면 MvcBoard->특성을 누릅니다. 왼편에서 Java 빌드 경로를 선택합니다. 기본출력폴더 에서 /MvcBoard/bin 을 /MvcBoard/www/WEB-INF/classes 로 변경합니다.

이제부터 src에는 파일을 저장시 컴파일됨과 동시에 WEB-INF/classes 폴더로 클래스파일이 들어가게됩니다.

서블릿은 만들었고 남은건 web.xml을 만들고 서블릿을 맵핑하면 됩니다. WEB-INF 폴더에 web.xml 파일을 하나 만드세요.

web.xml에 다음과 같은 내용을 입력하세요. 톰캣이 서블릿을 인식하도록 맵핑해주는 부분이 들어가있습니다..
<?xml version="1.0"?> 
<!DOCTYPE web-app PUBLIC 
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
"http://java.sun.com/dtd/web-app_2_3.dtd"> 

<web-app>   
   <servlet>
        <servlet-name>Controller</servlet-name>
        <servlet-class>javaforum.board.Controller</servlet-class>        
    </servlet>
       
    <servlet-mapping>
        <servlet-name>Controller</servlet-name>
        <url-pattern>/servlet/Controller</url-pattern>
    </servlet-mapping>
</web-app>

맵핑하는 방법은 다양하지만 지금은 /servlet/Controller 로 하겠습니다.

자, 톰캣을 재시작합니다. 브라우저에서 http://localhost:8080/servlet/Controller 를 호출해봅니다.
이클립스 콘솔창에 컨트롤러 호출 메시지가 나오면 정상적으로 서블릿이 호출된 상태입니다.


이제 doProcess함수를 다듬어보겠습니다. 컨트롤러에서 파라미터를 하나 받도록 하겠습니다. 파라미터명은 action 으로 합니다.

String action = request.getParameter("action");

action에 대해서 조건문으로 구분합니다. 여기서는 list 일 경우만 넣었습니다.

      if(action.equals("list")) {
      }

여기까지 하고 www 폴더에서 MvcList.jsp 파일을 만듭니다. 내용은 MvcList 라고만 입력합니다.

이제 컨트롤러에서 조건문내에 MvcList.jsp를 호출하는 부분을 넣도록 하겠습니다.

먼저 import를 합니다.
import javax.servlet.ServletContext;
import javax.servlet.RequestDispatcher;

그리고 조건문내에 다음에 나오는 부분을 입력합니다.
         ServletContext sc = getServletContext();   
         RequestDispatcher rd = sc.getRequestDispatcher("/MvcList.jsp");
         try {
            rd.forward(request, response);
         } catch(ServletException se) {
            se.printStackTrace();               
         } catch(IOException ie) {
            ie.printStackTrace();
         }

여기서는 forward를 사용했는데 페이지를 호출하는 방식에는 2가지가 있습니다, forward, sendRedirect 가 있는데 forward는 서버(웹서버)상에서 직접 페이지를 호출합니다. sendRedirect는 서버에서 클라이언트(웹브라우저)에게 해당 페이지를 요청해달라고 요구하면 클라이언트가 다시 페이지를 호출하는 방식입니다. 즉 sendRedirect는 서버와 클라이언트간의 통신을 한번 더 하는 개념입니다.

이제 브라우저에서 http://localhost:8080/servlet/Controller?action=list 로 호출해보세요.


컨트롤러의 개념을 잡기위하여 먼저 뷰를 호출해보았습니다. 순서상으로는 모델호출이 먼저와야 하지만 편의상 뷰를 어떤식으로 호출하는지에 대해서 살펴보았습니다.

이제 데이타를 구성하고 화면에 뿌리는 일만 남았군요. 다음강좌에서 모델부분과 뷰를 완성해보도록 하겠습니다. 그럼 다음강좌에서.....

 

MVC 세번째강좌 - MVC 기법으로 게시판 리스트 만들기(모델, 뷰)

작성자 : 김시웅
테스트환경 : 이클립스2.1.2, mysql, tomcat4.1.30

2장에 이어서 계속 진행하겠습니다. MVC에서 모델의 역할은 데이타를 구성하고 편집하고 결과물을 만드는 역할을 합니다.

일단 아주 간단하게 생각해서 쿼리부분이 들어가는곳이 모델단인데 DB에 접속해서 쿼리후 데이타를 가져오는 부분을 DAO(Data Access Object) 라고 부르겠습니다.

DAO에서 쿼리의 결과물을 어딘가에 담아야하겠죠. 이걸 VO(Value Object) 에 담겠습니다.

그럼 먼저, VO 클래스를 하나 만듭니다. MvcBoard->src->javaforum.board 에서 신규->클래스를 선택합니다. 클래스명은 ListVO 라고 하세요.
ListVO에 4개의 멤버변수를 만듭니다.
   private int num;
   private String subject;
   private String name;
   private String regiDate;

멤버변수하나를 선택한후 마우스 오른쪽버튼을 눌러서 소스->Getter 및 Setter 생성메뉴를 누릅니다. 팝업화면에서 모두선택하고 확인을 누릅니다.

결과물을 담을 객체를 만들었습니다. 이제 DAO에서 쿼리결과를 ListVO에 담으면 됩니다.

이제, DAO클래스를 만들겠습니다. MvcBoard->src->javaforum.board 에서 신규->클래스를 선택합니다. 클래스명은 BoardDAO 라고 하세요.

상단에 import문을 추가합니다.
import java.sql.*;
import java.util.Vector;

커넥션을 얻는 함수를 하나 만들겠습니다.
   public Connection getConnection() {
      String url = "jdbc:mysql://db주소:3306/db명";
      String username = "유저명";
      String password = "암호";
      Connection con = null;
      try {
         Class.forName("org.gjt.mm.mysql.Driver");
         con = DriverManager.getConnection(url, username, password);
      } catch (ClassNotFoundException e) {
         System.out.println("드라이버를 찾을수 없습니다.");
      } catch (SQLException ex) {
         System.out.println("접속실패");
      }
      return con;      
   }

다음에 쿼리한 결과물을 ListVO에 담고 로우수만큼 ListVO를 벡터에 담는 부분입니다.
   public Vector getList() {
      Connection con = this.getConnection();
      PreparedStatement ps = null;
      ResultSet rs = null;      
      String query = "select * from Board";
      try {
         ps = con.prepareStatement(query);
         rs = ps.executeQuery();
         Vector values = new Vector();
         while(rs.next()) {
            ListVO vo = new ListVO();
            vo.setNum(rs.getInt("num"));
            vo.setSubject(rs.getString("subject"));
            vo.setName(rs.getString("name"));
            vo.setRegiDate(rs.getString("regiDate"));
            
            values.addElement(vo);
         }
         return values;
      } catch (SQLException e) {
         e.printStackTrace();
         return null;
      } finally {
         try {
            if(rs != null) rs.close();                                 
            if(ps != null) ps.close();         
            if(con != null) con.close();   
         } catch(SQLException e) {                        
         }
      }      
   }

Board테이블에 5개의 데이타가 들어가있기때문에 ListVO는 5개 만들어지게 됩니다.

이것으로 DAO는 끝났습니다. 그럼 이 벡터를 컨트롤러에서 받아야겠죠. 다시 컨트롤러로 가겠습니다.

import를 하나 추가합니다. import java.util.Vector;

if(action.equals("list")) { 조건문내에 다음부분을 추가하세요.
         BoardDAO dao = new BoardDAO();
         Vector listValues = dao.getList();
         request.setAttribute("listValues", listValues);


dao.getList() 함수를 호출해서 결과를 받았습니다. 그 결과를 request에 저장합니다. setAttribute()의 의미는 서버상의 request메모리영역에 변수를 임시 저장하는 역할을 담당합니다. 물론 나중에 쓸려고 저장하는겁니다.

어디서 쓰냐구요? MvcList.jsp에서 사용해야겠죠. 이제 남은건 MvcList.jsp에서 결과물을 화면으로 구성하면 됩니다.

MvcList.jsp로 다시 이동하겠습니다.

import 와 contentType을 설정합니다.
<%@ page language="java" import="java.util.Vector, javaforum.board.ListVO" contentType="text/html; charset=euc-kr" %>

request에서 getAttribute로 결과값을 다시 꺼냅니다.
<%
   Vector listValues = (Vector) request.getAttribute("listValues");
%>

마지막으로 화면에 뿌려주기만 하면 됩니다.
<html>
   <body>
      <table border="1">
         <tr>
            <td>번호</td>
            <td>제목</td>
            <td>이름</td>
            <td>등록일</td>
         </tr>
<%
      for(int i = 0; i < listValues.size(); i++) {
         ListVO vo = (ListVO) listValues.elementAt(i);
         int num = vo.getNum();
         String subject = vo.getSubject();
         String name = vo.getName();
         String regiDate = vo.getRegiDate();
%>         
         <tr>
            <td><%=num%></td>
            <td><%=subject%></td>
            <td><%=name%></td>
            <td><%=regiDate%></td>
         </tr>
<%
      } 
%>
      </table>
   </body>
</html>

브라우저에서 http://localhost:8080/servlet/Controller?action=list 를 호출합니다.


이상 MVC형태로 게시판 리스트를 만들어보았습니다. 대충 감이 잡히시나요? 다음강좌에서는 둘의 차이점을 비교분석하고 MVC 소스를 약간 변형해보겠습니다.

그럼 다음강좌에서...

 

MVC 네번째강좌 - 모델1과 MVC 소스 비교분석하기, MVC 소스 약간 변경해보기

작성자 : 김시웅
테스트환경 : 이클립스2.1.2, mysql, tomcat4.1.30

이제 모델1과 MVC사이에 어떠한 변화가 일어났는지 살펴볼 시간입니다. 가장 큰 변화는 무엇일까요?

모델1의 List.jsp 에는 커넥션을 맺고 데이타를 가져오는 부분이 있지만 MVC의 MvcList.jsp에는 그 부분이 빠져있습니다.
훨씬 보기가 좋아진것 같나요. 겉으로 보기에도 소스가 간결해진것을 느낄수가 있습니다.

웹프로그래밍이 아닌 전통적인 어플리케이션 개발에서는 모듈화를 해야만 개발이 용이하고 유지보수도 쉽습니다. 하지만 웹으로 넘어오면서 로직이나 화면처리등을 하나의 파일(jsp)에서 처리하는게 익숙해졌고 또 그렇게 해왔습니다. 하지만 이러한 방식은 상당히 소스를 어지럽게 만들기 때문에 유지보수도 어려울뿐더러 코드의 재사용성에도 문제가 됩니다.
물론 모델1기법이 단점만 있는것은 아닙니다. 소규모프로젝트라면 개발기간면에서 mvc보다는 생산성이 높다고 할 수 있습니다.

여하튼 이러한 문제점을 보완하기위해 나온게 MVC인데 요지는 jsp에 있는 소스를 각모듈이 하는 역할에 맞게 분리하자는 겁니다.

1.view : jsp가 됩니다. 단지 화면에 관련된 사항만 처리합니다.
2.controller : jsp에서 넘어오는 파라미터처리후 해당 로직이나 DAO에 연결한 다음 결과값 받아서 jsp에 넘겨주는 역할을 담당합니다.
3.model : 로직이나 DAO는 controller에서 요구한 데이타를 구성해서 결과값을 넘겨주는 역할을 합니다.

자, 그럼 MVC에 좀 더 깊이 들어가보겠습니다. jsp는 화면처리만 담당한다고 했는데 파라미터처리 부분은 어디로 갔을까요? 컨트롤러에서 그부분이 들어갑니다. 바로 String action = request.getParameter("action"); 이 부분이죠.

근데 한가지 짚고 넘어가야할 부분이 있습니다. 서블릿을 호출할때 /servlet/Controller?action=list 이렇게 했습니다. 즉 이것은 MvcList.jsp를 호출하기 위해서죠. 그렇다면 좀더 세련되게 호출할수 없을까라는 의문이 드는군요.

web.xml에서 서블릿을 다르게 맵핑해보겠습니다. url-pattern 에서 /servlet/Controller -> *.action 로 고칩니다.
<url-pattern>*.action</url-pattern>

이제 Controller 로 갑니다. doProcess에서 다음처럼 변경합니다.
      //String action = request.getParameter("action");
      String servletPath = request.getServletPath();
      String actionName = servletPath.substring(servletPath.lastIndexOf("/") + 1);
      if(actionName.equals("MvcList.action")) {


기존의 파라미터는 쓰지 않기때문에 주석처리하고 서블릿경로에서 액션명을 파싱하는 내용입니다. 이렇게해서 컨트롤러를 호출할때 액션명으로 구분하는게 가능하게됩니다.
이제 MvcList.action 으로 호출해야겠죠^^.

그럼 톰캣을 재시작하고 브라우저에서 http://localhost:8080/MvcList.action 이라고 입력합니다.

MvcList.action 을 호출하게 되면 MvcList.jsp를 호출하게 되는것이죠. 컨트롤러를 약간 수정해보았습니다.

이번에는 view로 가볼까요^^.. MvcList.jsp를 에디터에서 여세요. 물론 간결해지기는 했지만 눈에 거슬리는게 한가지 있군요. 태그사이에 스크립틀릿이 들어가서 일관성이 떨어져보입니다.

바로 스크립틀릿을 태그로 바꿀려고 합니다. 아니 태그라니.. 무슨 말인지 이해가 안되신다구요?

커스텀태그를 사용하면 간단하게 해결이 됩니다. 그중에서도 표준인 jstl(jsp standard tag library)을 사용해서 변경해보겠습니다.

변경하기전에 환경부터 갖추도록 하겠습니다. jstl관련 jar파일을 다운받습니다. 자료실에 보면 톰캣4점대, 톰캣5점대용 jstl.jar, standard.jar 가 있습니다.

2개의 파일을 다운받으셨으면 MvcBoard->www->WEB-INF 로 가서 lib 폴더를 만듭니다. jar파일을 lib폴더로 옮겨놓으세요.

MvcList.jsp 로 가서 상단에 다음부분을 추가합니다. jstl core 관련 태그를 사용하겠다는 의미입니다.
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c"%>

jstl 설정은 마쳤습니다. 이제 core 에 들어있는 태그를 사용해서 스크립틀릿 부분을 대체하면 됩니다.
일단 어떻게 변화가 되는지 보여드리겠습니다.
         <c:forEach items="${listValues}" var="data">      
         <tr>
            <td><c:out value="${data.num}"/></td>
            <td><c:out value="${data.subject}"/></td>
            <td><c:out value="${data.name}"/></td>
            <td><c:out value="${data.regiDate}"/></td>
         </tr>
         </c:forEach>


다시 톰캣을 재시작하고 브라우저에서 http://localhost:8080/MvcList.action 을 호출해보세요. 같은결과가 나올겁니다.

변화된 소스를 보면 listValues 를 가져오는 부분이 없습니다. 그리고 for문이 jstl의 forEach 태그로 변했습니다. 마지막으로 출력부분이 c:out 태그를 사용해서 data.num 과 같은 형식으로 변했습니다.

훨씬 보기가 좋군요^^. 어떤가요 소스도 간결해지고 태그화 되어있기때문에 영역구분이 명료해졌습니다.
지금은 core 태그중에 반복문을 표현하는 forEach와 출력문에 사용되는 out태그만을 사용했습니다. jstl에 대한 사용법은 별도의 강좌(커스텀태그와 jstl 강좌)를 통해서 여러분에게 알려드릴 예정입니다.

다음강좌에서는 모델쪽의 DAO클래스에 변화를 줘보도록 하죠. 그럼 다음강좌에서....

MVC 다섯번째강좌 - MVC패턴에서 모델영역에 속하는 DAO클래스 약간 변경하기

작성자 : 김시웅
테스트환경 : 이클립스2.1.2, mysql, tomcat4.1.30

이번 시간에는 DAO클래스에서 DB의 데이타를 가져오는 부분을 변경해보도록 하겠습니다.

getList() 함수를 보면 커넥션을 하나 가져와서 PreparedStatement와 ResultSet을 이용하여 검색된 로우수만큼 컬럼의 값을 가져오는 부분입니다. 하지만 이러한 부분은 중복이 생기는 경우가 많습니다. 그렇기 때문에 이러한 db처리부분을 공통함수화할 필요가 생겼습니다.

그럼 ResultSet과 같은 역할을 하는 MyResultSet을 만들어보죠. MyResultSet에는 로우수만큼 데이타를 가지는 변수가 필요합니다. 그리고 next, getInt, getString 함수를 만들겠습니다.

package javaforum.board;
import java.util.*;
/**
 * @author wwwoong
 */
public class MyResultSet {
   private Vector rowData;
   private int rowPos = -1;
   
   public void setRowData(Vector rowData) {
      this.rowData = rowData;
   }
   
   public int getInt(String columnName) {
      String columnValue = this.getString(columnName);
      try {
         int val = Integer.parseInt(columnValue);
         return val;
      } catch(Exception e) {
         return -1;            
      }
   }
   
   public String getString(String columnName) {
      Hashtable columnData = (Hashtable) this.rowData.elementAt(this.rowPos);
      if(columnData.containsKey(columnName)) {
         String columnValue = (String) columnData.get(columnName);
         return columnValue;
      } else {
         System.out.println("컬럼명이 존재하지 않습니다.");
         return "";   
      }      
   }
   
   public boolean next() {
      if(this.rowPos == this.rowData.size() - 1) {
         return false;
      } else {
         this.rowPos++;
         return true;
      }      
   }
}

MyResultSet은 ResultSet을 흉내낸것이기 때문에 기능이 같습니다. 여러분들이 직접 분석해보시기 바랍니다.

BoardDAO 로 가서 함수 하나를 만들겠습니다. 함수의 내용은 rowData에 쿼리결과를 집어넣고 MyResultSet에 rowData를 넣습니다.
   public MyResultSet selectMultiRow(String query) {         
      Connection con = null;   
      PreparedStatement ps = null;
      ResultSet rs = null;
      ResultSetMetaData rmd = null;
      int columnCount = 0;
      
      MyResultSet myResultSet = new MyResultSet();
      Vector rowData = new Vector();
      try {
         con = getConnection();
         ps = con.prepareStatement(query);
         rs = ps.executeQuery();
         rmd = rs.getMetaData();
         columnCount = rmd.getColumnCount();
         while (rs.next()) {                        
            Hashtable columnData = new Hashtable();
            for(int i = 1; i <= columnCount; i++) {
               String columnName = rmd.getColumnName(i);
               columnData.put(columnName, rs.getString(i));               
            }
            rowData.addElement(columnData);
         } 
         myResultSet.setRowData(rowData);
      } catch (SQLException e) {
         System.out.println("select sql : " + e);
         e.printStackTrace();
      } finally {
         try {
            if(rs != null) rs.close();                                 
            if(ps != null) ps.close();         
            if(con != null) con.close();         
         } catch(SQLException e1) {
            e1.printStackTrace();         
         }
      }
      return myResultSet;
   }

selectMultiRow 는 여러개의 로우결과를 갖기때문에 이렇게 이름지었습니다. 이 함수를 이용해서 getList()를 고쳐보도록 하겠습니다.
   public Vector getList() {
      String query = "select * from Board";
      MyResultSet myResultSet = this.selectMultiRow(query);
      
      Vector values = new Vector();
      while(myResultSet.next()) {          
           ListVO vo = new ListVO(); 
          vo.setNum(myResultSet.getInt("num")); 
          vo.setSubject(myResultSet.getString("subject")); 
          vo.setName(myResultSet.getString("name")); 
          vo.setRegiDate(myResultSet.getString("regiDate")); 
         
          values.addElement(vo); 
      }
      
      return values;
   }

이전의 getList() 함수와 차이가 느껴지나요? 위아래로 반복되는 불필요한 부분을 제거했습니다.

다음강좌에서는 컨트롤러부분을 좀 더 살펴보도록 하죠. 지금까지는 list만으로 예제를 해왔지만 detail, insert, update 부분도 만들어보도록 하겠습니다.

그럼 다음강좌에서...

 

MVC 여섯번째강좌 - MVC기법으로 입력 게시판 만들기

작성자 : 김시웅
테스트환경 : 이클립스2.1.2, mysql, tomcat4.1.30

지금까지는 리스트화면만으로 강좌를 해왔습니다. 이제부터는 나머지화면도 만들어보도록 하겠습니다.

먼저 Controller.java 로 갑니다. doProcess함수에서 jsp로 forward하는 부분을 함수로 만들겠습니다. 함수명은 forward 입니다.
   public void forward(HttpServletRequest request, HttpServletResponse response, String pageName) {
      ServletContext sc = getServletContext();   
      RequestDispatcher rd = sc.getRequestDispatcher(pageName);
      try {
         rd.forward(request, response);
      } catch(ServletException se) {
         se.printStackTrace();               
      } catch(IOException ie) {
         ie.printStackTrace();
      }
   }

그리고 액션명을 얻는 부분도 함수로 만들어보죠. 함수명은 getActionName 입니다.
   public String getActionName(HttpServletRequest request) {
      String servletPath = request.getServletPath();
      String actionName = servletPath.substring(servletPath.lastIndexOf("/") + 1);
      return actionName;
   }

이와같이 2개의 함수를 추가한뒤의 doProcess함수는 다음과 같습니다.
   public void doProcess(HttpServletRequest request, HttpServletResponse response) {
      System.out.println("컨트롤러 호출");
      String actionName = this.getActionName(request);
      if(actionName.equals("MvcList.action")) {
         BoardDAO dao = new BoardDAO();
         Vector listValues = dao.getList();
         request.setAttribute("listValues", listValues);
         
         this.forward(request, response, "/MvcList.jsp");
      }
   }

이제 doProcess함수도 어느정도 정리된 느낌이군요^^. 그럼 insert액션을 추가해보죠. MvcInsert.action 으로 하겠습니다.
      } else if(actionName.equals("MvcInsert.action")) {
      }

조건문내에 DAO를 생성하고 함수를 호출하면 되는데 그전에 한가지 준비해야할게 있습니다. 리스트에서 VO(View Object)를 만들었듯이 인서트에서는 TO(Transact Object)가 필요합니다.
TO는 테이블의 컬럼수와 같다고 보시면 됩니다. DB에 들어갈 오브젝트니까요. InsertTO를 만들겠습니다.
package javaforum.board;

/**
 * @author wwwoong
 */
public class InsertTO {
   private int num;
   private String name;
   private String subject;
   private String content;
   private String regiDate;
}

이와같이 만든후에 getter, setter로 함수를 만드세요.

그리고 DAO에 가서 함수를 하나 만들어야됩니다. insert(InsertTO to) 즉 컨트롤러에서 TO를 만든후 DAO의 insert함수에 건네주는 형태입니다. insert함수를 만들기전에 transact 함수를 먼저 만들겠습니다. 이것 역시 selectMultiRow함수와 마찬가지로 중복을 피하기 위해서 만드는겁니다.
   //insert, update, delete 문 형태
   public boolean transact(String query) {         
      Connection con = null;
      PreparedStatement ps = null;
      try {
         con = getConnection();
         con.setAutoCommit(false);
         ps = con.prepareStatement(query);
         ps.executeUpdate();      
         con.commit();
         return true;    
      } catch (SQLException e) {
         try {
            con.rollback();
         } catch(SQLException re) {
            re.printStackTrace();
         }
         System.out.println("query : " + query);
         System.out.println("transact sql : " + e);
         e.printStackTrace();
         return false;
      } finally {
         try {                              
            if(ps != null) ps.close();         
            if(con != null) con.close();      
         } catch(SQLException e1) {
            e1.printStackTrace();         
         }
      }
   }
   
   //insert, update, delete 문 형태(다중 트랜잭션처리)
   public boolean transact(String querys[]) {         
      Connection con = null;
      PreparedStatement ps = null;
      String query = null;
      try {
         con = getConnection();
         con.setAutoCommit(false);
         for(int i = 0; i < querys.length; i++) {
            query = querys[i];            
            ps = con.prepareStatement(query);
            ps.executeUpdate();
         }                        
         con.commit();
         return true;    
      } catch (SQLException e) {
         try {
            con.rollback();
         } catch(SQLException re) {
            re.printStackTrace();
         }
         System.out.println("query : " + query);
         System.out.println("transact sql : " + e);
         e.printStackTrace();
         return false;
      } finally {
         try {                        
            if(ps != null) ps.close();         
            if(con != null) con.close();      
         } catch(SQLException e1) {
            e1.printStackTrace();         
         }
      }
   }

쿼리가 하나일때와 다수일때에 대한 DB transact함수입니다. 내용은 금방 이해되실겁니다.

마지막으로 Board 테이블의 키인 num의 맥스값을 얻어오는 함수가 필요합니다.
   public int getMaxNum() {
      String query = "select max(num) from Board";      
      String maxStr = this.selectOneCol(query);
      int max = 0;
      if(maxStr != null && !maxStr.equals("")) {
         max = Integer.parseInt(maxStr);
      }               
      max++;
      return max;
   }
   
   //select문 형태(하나의 데이타)
   public String selectOneCol(String query) {
      String resultStr = null;
      Connection con = null;   
      PreparedStatement ps = null;
      ResultSet rs = null;
      ResultSetMetaData rmd = null;
      int columnCount = 0;
      try {
         con = getConnection();
         ps = con.prepareStatement(query);
         rs = ps.executeQuery();
         rmd = rs.getMetaData();
         columnCount = rmd.getColumnCount();
         if (rs.next()) {                        
            resultStr = rs.getString(1);                     
         } 
      } catch (SQLException e) {
         System.out.println("select sql : " + e);
         e.printStackTrace();
      } finally {
         try {
            if(rs != null) rs.close();                                 
            if(ps != null) ps.close();         
            if(con != null) con.close();               
         } catch(SQLException e1) {
            e1.printStackTrace();         
         }
      }
      return resultStr;
   }

getMaxNum 은 맥스값을 얻어서 1증가시킵니다. selectOneCol 은 하나의 데이타만을 얻어오는 함수입니다. 휴 사전공작 힘들군요.
이제 insert함수를 만들수 있습니다^^. 결과값은 boolean으로 넘어옵니다.
   public boolean insert(InsertTO to) {
      String query = 
         "insert into Board values(" +
            to.getNum() + ", " + 
            "'" + to.getName() + "'," +
            "'" + to.getSubject() + "'," +
            "'" + to.getContent() + "'," +
            to.getRegiDate() + ")"; 
      return this.transact(query);
   }

BoardDAO에서 할일은 다 한것같군요. Controller로 가서 redirect 함수를 만들겠습니다.
   public void redirect(HttpServletResponse response, String page) {
      try {
         response.sendRedirect(page);
      }         
      catch(IOException ie) {      
         System.out.println("sendRedirect IOException 에러 : " + ie);
         ie.printStackTrace();
      } catch(Exception e) {
         e.printStackTrace();
      }   
   }

redirect를 쓰게되면 클라이언트로 부터 요청을 받기때문에 이전의 url이 브라우저에 남지 않습니다.
이제 MvcInsert.action 조건문으로 갑니다. 다음을 추가합니다.
         BoardDAO dao = new BoardDAO();
         InsertTO to = new InsertTO();
         boolean isTrue = dao.insert(to);
         if(isTrue) {
            this.redirect(response, "/MvcList.action");
         }          

내용을 살펴보면 InsertTO를 만들고 insert함수를 호출한이후에 결과가 참이면 "/MvcList.action"으로 forward하는군요. 에러가 났다면 현재화면을 보여주면 되니깐 이동할 필요가 없겠죠^^.
아^^. 뭔가 빠진것같다구요? 맞습니다, to에 아무것도 입력하지 않았네요. 다음을 추가해줍니다.
         to.setNum(dao.getMaxNum());
         to.setName(request.getParameter("name"));
         to.setSubject(request.getParameter("subject"));
         to.setContent(request.getParameter("content"));
         to.setRegiDate("now()");

어디에 삽입해야할지는 아실겁니다. 완성된 doProcess함수를 보여드리죠^^.
   public void doProcess(HttpServletRequest request, HttpServletResponse response) {
      System.out.println("컨트롤러 호출");
      String actionName = this.getActionName(request);
      if(actionName.equals("MvcList.action")) {
         BoardDAO dao = new BoardDAO();
         Vector listValues = dao.getList();
         request.setAttribute("listValues", listValues);
         
         this.forward(request, response, "/MvcList.jsp");
      } else if(actionName.equals("MvcInsert.action")) {
         BoardDAO dao = new BoardDAO();
         InsertTO to = new InsertTO();         
         to.setNum(dao.getMaxNum());
         to.setName(request.getParameter("name"));
         to.setSubject(request.getParameter("subject"));
         to.setContent(request.getParameter("content"));
         to.setRegiDate("now()");
         boolean isTrue = dao.insert(to);
         if(isTrue) {
            this.redirect(response, "/MvcList.action");
         }          
      }
   }

근데 한가지 문제가 있군요. 인서트는 초기에 입력화면이 나오는 부분과 submit후에 입력내용을 DB에 넣는 부분이 구별되야합니다. 그래서 MvcInsert.action 을 호출할때 transact 파라미터를 추가하겠습니다. 즉 transact파라미터를 안쓰거나 transact="false" 일때는 초기입력화면이고 transact="true" 일때만 트랜잭션처리를 하도록 변경해야겠습니다.
         String transact = request.getParameter("transact");
         if(transact == null || transact.equals("false")) {
            this.forward(request, response, "/MvcInsert.jsp");
         } else if(transact.equals("true")){
            BoardDAO dao = new BoardDAO();
            InsertTO to = new InsertTO();         
            to.setNum(dao.getMaxNum());
            to.setName(request.getParameter("name"));
            to.setSubject(request.getParameter("subject"));
            to.setContent(request.getParameter("content"));
            to.setRegiDate("now()");
            boolean isTrue = dao.insert(to);
            if(isTrue) {
               this.redirect(response, "/MvcList.action");
            }          
         }

지금까지 insert를 추가하기 위해서 컨트롤러와 DAO의 내용을 변경했습니다. 이제 화면 MvcInsert.jsp만 만들면 됩니다.
<%@ page language="java" contentType="text/html; charset=euc-kr" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c"%>
<script language="javascript">
   function insert() {
      form = document.insertForm;   
      form.submit();         
   }
</script>   
<html>
   <body>
      <form name="insertForm" method="post" action="/MvcInsert.action">
      <input type="hidden" name="transact" value="true">
      <table border="1">
         <tr>
            <td>이름</td><td><input type="text" name="name"></td>
         </tr>
         <tr>
            <td>제목</td><td><input type="text" name="subject"></td>
         </tr>
         <tr>
            <td>내용</td><td><input type="textarea" name="content"></td>
         </tr>
         <tr>
            <td align="center"><a href="javascript:insert()">입력</a></td>
         </tr>
      </table>
      </form>
   </body>
</html>

그리고 MvcList.jsp에 <a href="/MvcInsert.action">글쓰기</a>를 추가합니다.

글입력이 잘되는지 테스트해보시기 바랍니다. 이것으로 MVC기초강좌를 마치겠습니다. 나머지 detail, update, delete는 여러분들이 직접해보시면 좀더 MVC에 대한 이해가 빠를거로 생각됩니다.

앞으로 MVC중급강좌, 커스텀태그와 jstl강좌, 아파치 digester강좌 등등을 준비하고 있습니다.
새로운 강좌에서 뵙겠습니다. 그럼 전 이만.....

'개발도 하냐?' 카테고리의 다른 글

전자세금계산서/세금계산서 업체 정보리스트  (0) 2009.10.29
Anyframe Doc  (0) 2009.09.16
Eclipse + JEUS  (0) 2009.08.19
약도 만들기  (0) 2009.08.14
아키텍쳐에 대한 정의  (0) 2009.08.07

+ Recent posts