[Day 23 | JSP] MVC Model 2

y♡ding·2024년 11월 13일
0

데브코스 TIL

목록 보기
147/163


MVC Model 2는 JSP와 서블릿을 기반으로 하는 웹 애플리케이션 아키텍처 패턴으로, Model-View-Controller(MVC) 구조를 사용하여 서버 측에서 요청과 응답을 처리하는 방식입니다. MVC Model 2는 비즈니스 로직, 데이터, 프레젠테이션(화면) 로직을 명확히 분리하여, 유지보수와 확장성이 높은 구조를 제공합니다. 이 패턴은 Java 웹 애플리케이션에서 JSP와 서블릿의 역할을 명확히 나누고, 데이터 흐름을 구조화합니다.


MVC Model 2의 주요 구성 요소

  1. Model (모델):
    • 비즈니스 로직과 데이터베이스와의 상호작용을 담당합니다.
    • DAO(Data Access Object)와 DTO(Data Transfer Object)를 사용하여 데이터를 관리합니다.
    • 서블릿이 처리한 데이터는 모델을 통해 JSP에 전달됩니다.
  2. View (뷰):
    • 클라이언트에게 보여질 화면을 담당하는 부분으로, JSP가 주로 이 역할을 수행합니다.
    • 화면에 표시할 데이터를 모델로부터 받아와 사용자에게 전달합니다.
    • 비즈니스 로직은 포함하지 않고, 데이터 표시와 UI만을 담당합니다.
  3. Controller (컨트롤러):
    • 클라이언트의 요청을 받아서 어떤 작업을 수행할지 결정하고, 필요한 데이터를 준비한 뒤 적절한 뷰로 데이터를 전달합니다.
    • 서블릿이 컨트롤러 역할을 수행하며, 요청을 받고 모델과 뷰 간의 흐름을 제어합니다.
    • 컨트롤러는 사용자 요청을 해석하고, 비즈니스 로직을 처리하며, 최종적으로 데이터를 뷰에 전달하는 책임을 가집니다.

MVC Model 2의 흐름

  1. 클라이언트의 요청: 사용자가 웹 브라우저에서 특정 URL을 통해 요청을 보내면, 이 요청이 컨트롤러(서블릿)로 전달됩니다.
  2. 컨트롤러의 요청 처리: 컨트롤러는 요청을 해석하고, 필요한 경우 모델(비즈니스 로직 또는 데이터베이스 작업)을 통해 데이터를 처리합니다.
  3. 뷰 선택 및 데이터 전달: 컨트롤러는 요청 처리 후, 결과 데이터를 뷰(JSP)에 전달하고, 클라이언트에게 보여질 페이지를 결정합니다.
  4. 응답 생성 및 반환: JSP 뷰는 전달받은 데이터를 사용자에게 보여줄 HTML 페이지로 렌더링하여 클라이언트에게 반환합니다.

MVC Model 2 간단 예제

아래는 간단한 게시판 예제로, 게시글 리스트를 조회하는 흐름을 통해 MVC Model 2의 구조를 이해해 보겠습니다.

구성 파일

  1. BoardDTO.java - 게시글 데이터를 저장하는 데이터 전송 객체.
  2. BoardDAO.java - 데이터베이스와 연결하여 게시글을 조회하는 데이터 접근 객체.
  3. BoardListServlet.java - 클라이언트 요청을 처리하고 뷰로 데이터를 전달하는 컨트롤러.
  4. boardList.jsp - 클라이언트에게 게시글 목록을 보여주는 뷰.

1. BoardDTO.java (Model)

public class BoardDTO {
    private int id;
    private String title;
    private String content;

    // 기본 생성자 및 getter, setter
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
}
  • BoardDTO는 게시글 데이터를 저장하는 객체로, 데이터베이스에서 조회한 데이터를 JSP로 전달할 때 사용됩니다.

2. BoardDAO.java (Model)

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class BoardDAO {
    public List<BoardDTO> getAllPosts() throws Exception {
        List<BoardDTO> postList = new ArrayList<>();
        Connection conn = null;

        try {
            conn = DriverManager.getConnection("jdbc:mariadb://localhost:3306/boarddb", "root", "password");
            String sql = "SELECT * FROM board ORDER BY id DESC";
            PreparedStatement pstmt = conn.prepareStatement(sql);
            ResultSet rs = pstmt.executeQuery();

            while (rs.next()) {
                BoardDTO post = new BoardDTO();
                post.setId(rs.getInt("id"));
                post.setTitle(rs.getString("title"));
                post.setContent(rs.getString("content"));
                postList.add(post);
            }
        } finally {
            if (conn != null) conn.close();
        }
        return postList;
    }
}
  • BoardDAO는 데이터베이스에서 모든 게시글을 조회하여 BoardDTO 리스트로 반환합니다.

3. BoardListServlet.java (Controller)

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.List;

@WebServlet("/board/list")
public class BoardListServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BoardDAO dao = new BoardDAO();
        try {
            List<BoardDTO> postList = dao.getAllPosts(); // 데이터 조회
            request.setAttribute("postList", postList); // 보낼 데이터 지정
            request.getRequestDispatcher("/WEB-INF/views/boardList.jsp").forward(request, response); // request, response 객체를 보낼 대상 파일 지정 및 포워딩
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }
}
  • BoardListServlet/board/list URL 요청을 처리하는 서블릿으로, 게시글 리스트를 가져와 boardList.jsp로 전달합니다.
  • request.setAttribute 메서드를 통해 게시글 데이터를 뷰에 전달하고, RequestDispatcher를 사용해 boardList.jsp로 포워딩합니다.

4. boardList.jsp (View)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>게시글 목록</title>
</head>
<body>
    <h2>게시글 목록</h2>
    <table border="1">
        <tr>
            <th>번호</th>
            <th>제목</th>
            <th>내용</th>
        </tr>
        <c:forEach var="post" items="${postList}">
            <tr>
                <td>${post.id}</td>
                <td>${post.title}</td>
                <td>${post.content}</td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>
  • boardList.jsp는 서블릿에서 전달받은 postList 데이터를 출력하는 역할을 합니다.
  • 게시글의 각 항목은 <c:forEach> 태그를 사용해 테이블 형식으로 화면에 표시됩니다.

MVC Model 2의 장단점

장점

  1. 유지보수성 향상: 비즈니스 로직, 데이터, 프레젠테이션이 각각 Controller, Model, View로 나뉘어 있어 유지보수가 용이합니다.
  2. 재사용성 증가: Model, View, Controller의 역할이 분리되어 재사용성이 높습니다.
  3. 코드의 가독성 증가: 역할이 명확히 나누어져 있어 코드를 쉽게 이해할 수 있습니다.

단점

  1. 구현 복잡도 증가: 각 부분이 독립적으로 분리되므로 구조가 복잡해질 수 있습니다.
  2. 작은 프로젝트에는 비효율적: 구조가 분리되기 때문에, 간단한 웹 애플리케이션에서는 구현이 비효율적일 수 있습니다.

요약

  • MVC Model 2는 JSP와 서블릿을 기반으로 하는 구조로, 모델, 뷰, 컨트롤러를 분리하여 역할을 명확히 하고 유지보수성을 높입니다.
  • Controller(서블릿)는 클라이언트 요청을 처리하고, Model(DAO, DTO)은 데이터 처리를 담당하며, View(JSP)는 사용자에게 화면을 출력합니다.
  • 이 구조는 대규모 애플리케이션에서 관리와 확장이 용이하며, Java 웹 애플리케이션 개발에서 널리 사용되는 패턴입니다.

참고

MVC Model 2와 서블릿/JSP 관련하여 더 깊이 학습할 수 있는 공식 문서유익한 한글 자료를 추천합니다. 아래 자료들은 서블릿과 JSP, MVC 패턴의 기초부터 실습 예제까지 폭넓게 다루고 있어 학습에 도움이 될 것입니다.

공식 문서

  1. Jakarta EE Documentation
  2. Oracle Java EE Documentation (Java EE 8 기준)
  3. Servlet and JSP Documentation by Apache Tomcat

실습 코드

BoardController.java

package org.example.controller;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.model2.*;

import java.io.IOException;

@WebServlet("/controller")  // "/controller" URL로 매핑된 서블릿
public class BoardController extends HttpServlet {

    // GET 요청이 들어올 때 처리하는 메서드
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doProcess(req, resp);  // GET 요청을 doProcess로 위임
    }

    // POST 요청이 들어올 때 처리하는 메서드
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doProcess(req, resp);  // POST 요청을 doProcess로 위임
    }

    // GET/POST 요청을 공통으로 처리하는 메서드
    protected void doProcess(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        String path = req.getParameter("path");  // 요청 파라미터에서 "path" 값을 가져옴
        String url = "";                         // 포워딩할 URL 초기화
        Action action = null;                    // 실행할 Action 객체 초기화

        // "path" 파라미터에 따라 적절한 Action 객체 생성 및 URL 설정
        if (path == null || path.equals("list")) {      // 게시글 목록 보기
            action = new ListAction();
            action.execute(req, resp);                  // 목록 조회 액션 실행
            url = "/WEB-INF/views/board_list1.jsp";     // 목록 페이지로 포워딩 설정

        } else if (path.equals("view")) {               // 게시글 상세 보기
            action = new ViewAction();
            action.execute(req, resp);                  // 상세 보기 액션 실행
            url = "/WEB-INF/views/board_view1.jsp";     // 상세 페이지로 포워딩 설정

        } else if (path.equals("write")) {              // 게시글 작성 페이지 이동
            action = new WriteAction();
            action.execute(req, resp);                  // 작성 페이지 액션 실행
            url = "/WEB-INF/views/board_write1.jsp";    // 작성 페이지로 포워딩 설정

        } else if (path.equals("write_ok")) {           // 게시글 작성 완료
            action = new WriteOkAction();
            action.execute(req, resp);                  // 작성 완료 액션 실행
            url = "/WEB-INF/views/board_write1_ok.jsp"; // 작성 완료 페이지로 포워딩 설정

        } else if (path.equals("modify")) {             // 게시글 수정 페이지 이동
            action = new ModifyAction();
            action.execute(req, resp);                  // 수정 페이지 액션 실행
            url = "/WEB-INF/views/board_modify1.jsp";   // 수정 페이지로 포워딩 설정

        } else if (path.equals("modify_ok")) {          // 게시글 수정 완료
            action = new ModifyOkAction();
            action.execute(req, resp);                  // 수정 완료 액션 실행
            url = "/WEB-INF/views/board_modify1_ok.jsp";// 수정 완료 페이지로 포워딩 설정

        } else if (path.equals("delete")) {             // 게시글 삭제 페이지 이동
            action = new DeleteAction();
            action.execute(req, resp);                  // 삭제 페이지 액션 실행
            url = "/WEB-INF/views/board_delete1.jsp";   // 삭제 페이지로 포워딩 설정

        } else if (path.equals("delete_ok")) {          // 게시글 삭제 완료
            action = new DeleteOkAction();
            action.execute(req, resp);                  // 삭제 완료 액션 실행
            url = "/WEB-INF/views/board_delete1_ok.jsp";// 삭제 완료 페이지로 포워딩 설정

        } else {
            // 처리되지 않은 path에 대한 추가 처리 필요 시 여기에 작성
        }

        // 최종적으로 지정된 URL로 요청을 포워딩하여 페이지 출력
        RequestDispatcher dispatcher = req.getRequestDispatcher(url);
        dispatcher.forward(req, resp);
    }
}
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>게시글 작성</title>
    <link rel="stylesheet" type="text/css" href="./css/board.css">
    
    <script type="text/javascript">
        window.onload = function() {
            // "쓰기" 버튼 클릭 시 이벤트 핸들러 설정
            document.getElementById('wbtn').onclick = function () {
                
                // 개인정보 수집 및 이용 동의 체크 여부 확인
                if (document.wfrm.info.checked == false) {
                    alert('개인정보 수집 및 이용에 동의하셔야 합니다.');
                    return false;
                }
                
                // 필수 입력 항목 확인
                if (document.wfrm.writer.value.trim() == '') {
                    alert('글쓴이를 입력하셔야 합니다.');
                    return false;
                }
                if (document.wfrm.subject.value.trim() == '') {
                    alert('제목을 입력하셔야 합니다.');
                    return false;
                }
                if (document.wfrm.password.value.trim() == '') {
                    alert('비밀번호를 입력하셔야 합니다.');
                    return false;
                }
                
                // 모든 유효성 검사를 통과하면 폼 전송
                document.wfrm.submit();
            };
        };
    </script>
</head>

<body>
    <!-- 상단 제목과 경로 표시 영역 -->
    <div class="con_title">
        <h3>게시판</h3>
        <p>HOME &gt; 게시판 &gt; <strong>게시글 작성</strong></p>
    </div>

    <!-- 게시글 작성 폼 -->
    <div class="con_txt">
        <form action="./controller" method="post" name="wfrm">
            <input type="hidden" name="path" value="write_ok"/>

            <div class="contents_sub">
                <div class="board_write">
                    <table>
                        <tr>
                            <th class="top">글쓴이</th>
                            <td class="top">
                                <input type="text" name="writer" class="board_view_input_mail" maxlength="5" />
                            </td>
                        </tr>
                        <tr>
                            <th>제목</th>
                            <td>
                                <input type="text" name="subject" class="board_view_input" />
                            </td>
                        </tr>
                        <tr>
                            <th>비밀번호</th>
                            <td>
                                <input type="password" name="password" class="board_view_input_mail" />
                            </td>
                        </tr>
                        <tr>
                            <th>내용</th>
                            <td>
                                <textarea name="content" class="board_editor_area"></textarea>
                            </td>
                        </tr>
                        <tr>
                            <th>이메일</th>
                            <td>
                                <input type="text" name="mail1" class="board_view_input_mail"/> @ 
                                <input type="text" name="mail2" class="board_view_input_mail"/>
                            </td>
                        </tr>
                    </table>
                    
                    <!-- 개인정보 수집 및 이용 동의 안내 -->
                    <table>
                        <tr>
                            <td style="text-align:left;border:1px solid #e0e0e0;background-color:#f9f9f9;padding:5px">
                                <div style="padding-top:7px;padding-bottom:5px;font-weight:bold;padding-left:7px;font-family: Gulim,Tahoma,verdana;">
                                    ※ 개인정보 수집 및 이용에 관한 안내
                                </div>
                                <div style="padding-left:10px;">
                                    <div style="width:97%;height:95px;font-size:11px;letter-spacing:-0.1em;border:1px solid #c5c5c5;background-color:#fff;padding:14px 0 0 14px;">
                                        1. 수집 항목: 회사명, 담당자명, 메일 주소, 전화번호 등 <br />
                                        2. 수집 목적: 원활한 의사소통을 위한 경로 확보 <br />
                                        3. 보유 기간: 조회를 위해 3개월 보관 후 파기 <br />
                                        4. 기타 사항은 개인정보취급방침 준수
                                    </div>
                                </div>
                                <div style="padding-top:7px;padding-left:5px;padding-bottom:7px;font-family: Gulim,Tahoma,verdana;">
                                    <input type="checkbox" name="info" value="1" class="input_radio"> 개인정보 수집 및 이용에 대해 동의합니다.
                                </div>
                            </td>
                        </tr>
                    </table>
                </div>

                <!-- 하단 버튼 영역 -->
                <div class="btn_area">
                    <div class="align_left">
                        <input type="button" value="목록" class="btn_list btn_txt02" style="cursor: pointer;" onclick="location.href='controller?path=list'" />
                    </div>
                    <div class="align_right">
                        <input type="button" id="wbtn" value="쓰기" class="btn_write btn_txt01" style="cursor: pointer;" />
                    </div>
                </div>
            </div>
        </form>
    </div>
</body>
</html>

0개의 댓글

관련 채용 정보