관리 메뉴

거니의 velog

(2) 모델2 방식으로 효율적으로 개발하기 2 본문

Java/Java_JSP Model2

(2) 모델2 방식으로 효율적으로 개발하기 2

Unlimited00 2023. 9. 22. 20:05

3. MVC를 이용한 회원 관리

* 우리는 앞에서 JSP를 이용해 모델1 방식으로 회원 관리 기능을 구현한 적이 있다. 즉, 모델1 방식이었기 때문에 모든 기능을 JSP에서 처리한 것이나 마찬가지이다.

* 이번에는 MVC 방식으로 브라우저의 요청은 서블릿이 맡고, 비즈니스 처리는 모델이 맡고, 화면은 JSP가 맡는 방식으로 회원 관리 기능을 다시 구현해 보자. 결국 같은 프로그램을 개발하는 것이지만 방법을 달리함으로써 개발 원리를 쉽게 익히는 것이다.


(1) 회원 정보 조회 기능 구현

* 다음은 MVC로 구현한 회원 정보 조회 기능을 실행하는 과정이다.

(1) 브라우저에서 /mem.do로 요청한다.

(2) 서블릿 MemberController가 요청을 받아 MemberDAO의 listMembers() 메서드를 호출한다.

(3) MemberDAO의 listMembers() 메서드에서 SQL문으로 회원 정보를 조회한 후 
    회원 정보를 MemberVO에 설정하여 반환한다.

(4) 다시 MemberController에서는 조회한 회원 정보를 회원 목록창(listMembers.jsp)으로
    포워딩한다.
    
(5) 회원 목록창(listMembers.jsp)에서 포워딩한 회원 정보를 목록으로 출력한다.

* 그럼 지금부터 회원 정보 조회 기능을 실제로 구현해 보자.

1. 새 프로젝트 pro17에 sec01.ex01 패키지를 만든 후 MemberController, MemberDAO, MemberVO 클래스를 추가한다. 그리고 test01 폴더를 만들고 listMembers.jsp를 추가한다.

2. 컨트롤러 역할을 하는 서블릿인 MemberController 클래스를 다음과 같이 작성한다. init() 메서드에서 MemberDAO의 listMembers() 메서드를 호출하여 회원 정보를 ArrayList로 반환 받는다. 이때 request에 조회한 회원 정보를 membersList 속성 이름으로 바인딩한다. 그런 다음 RequestDispatcher 클래스를 이용해 회원 목록창(listMembers.jsp)으로 포워딩한다.

package sec01.ex01;

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

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/mem.do")
public class MemberController extends HttpServlet {
	
	private static final long serialVersionUID = 1L;
	MemberDAO memberDAO;

	public void init() throws ServletException {
		memberDAO = new MemberDAO(); // MemberDAO를 생성한다.
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doHandle(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doHandle(request, response);
	}

	private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html; charset=utf-8");
		List<MemberVO> membersList = memberDAO.listMembers(); // 요청에 대한 회원 정보를 조회한다.
		request.setAttribute("membersList", membersList); // 조회한 회원 정보를 request에 바인딩한다.
		RequestDispatcher dispatch = request.getRequestDispatcher("/test01/listMembers.jsp");
		dispatch.forward(request, response); // 컨트롤러에서 표시하고자 하는 JSP로 포워딩한다.
	}

}

3. MemberDAO 클래스를 다음과 같이 작성한다. listMembers() 메서드 호출 시 SQL문을 이용해 회원 정보를 조회한 후 결과를 ArrayList로 반환한다.

package sec01.ex01;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class MemberDAO {
	private DataSource dataFactory;
	private Connection conn;
	private PreparedStatement pstmt;

	public MemberDAO() {
		try {
			Context ctx = new InitialContext();
			Context envContext = (Context) ctx.lookup("java:/comp/env");
			dataFactory = (DataSource) envContext.lookup("jdbc/oracle");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public List<MemberVO> listMembers() {
		List<MemberVO> membersList = new ArrayList();
		try {
			conn = dataFactory.getConnection();
			String query = "select * from  t_member order by joinDate desc"; // SQL문을 작성한다.
			System.out.println(query);
			pstmt = conn.prepareStatement(query); // PrepareStatement 객체를 생성하면서 SQL문을 인자로 전달한다.
			ResultSet rs = pstmt.executeQuery();
			while (rs.next()) {
				String id = rs.getString("id");
				String pwd = rs.getString("pwd");
				String name = rs.getString("name");
				String email = rs.getString("email");
				Date joinDate = rs.getDate("joinDate");
				MemberVO memberVO = new MemberVO(id, pwd, name, email, joinDate); // 조회한 회원 정보를 레코드별로 MemberVO 객체의 속성에 저장한다.
				membersList.add(memberVO); // membersList에 memberVO 객체들을 차례대로 저장한다.
			}
			rs.close();
			pstmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return membersList;
	}

	public void addMember(MemberVO m) {
		try {
			conn = dataFactory.getConnection();
			String id = m.getId();
			String pwd = m.getPwd();
			String name = m.getName();
			String email = m.getEmail();
			String query = "INSERT INTO t_member(id, pwd, name, email)" + " VALUES(?, ? ,? ,?)";
			System.out.println(query);
			pstmt = conn.prepareStatement(query); // PrepareStatement 객체를 생성하면서 SQL문을 인자로 전달한다.
			pstmt.setString(1, id);
			pstmt.setString(2, pwd);
			pstmt.setString(3, name);
			pstmt.setString(4, email);
			pstmt.executeUpdate(); // SQL문을 실행한다.
			pstmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

4. MemberVO 클래스를 다음과 같이 작성한다. 인자 네 개를 갖는 생성자와 인자 다섯 개를 갖는 생성자를 만든다.

package sec01.ex01;

import java.sql.Date;

public class MemberVO {
	
	private String id;
	private String pwd;
	private String name;
	private String email;
	private Date joinDate;
	
	public MemberVO() {
		System.out.println("MemberVO 생성자 호출");
	}
	
	public MemberVO(String id, String pwd, String name, String email) {
		this.id = id;
		this.pwd = pwd;
		this.name = name;
		this.email = email;
	}

	public MemberVO(String id, String pwd, String name, String email, Date joinDate) {
		this.id = id;
		this.pwd = pwd;
		this.name = name;
		this.email = email;
		this.joinDate = joinDate;
	}
	
	public String getId() {
		return id;
	}

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

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public String getName() {
		return name;
	}

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

	public String getEmail() {
		return email;
	}

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

	public Date getJoinDate() {
		return joinDate;
	}

	public void setJoinDate(Date joinDate) {
		this.joinDate = joinDate;
	}
	
}

5. listMembers.jsp를 다음과 같이 작성하여 바인딩된 회원 정보를 차례대로 표시한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	import=" java.util.*,sec01.ex01.*"
	pageEncoding="UTF-8"
	isELIgnored="false" 
%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
	request.setCharacterEncoding("UTF-8");
%>
<html lang="ko">

    <head>
        <meta charset="UTF-8">
        <title>회원 정보 출력창</title>
        <style>
            .cls1 {
                font-size: 40px;
                text-align: center;
            }

            .cls2 {
                font-size: 20px;
                text-align: center;
            }
        </style>
    </head>

    <body>
        <p class="cls1">회원정보</p>
        <table align="center" border="1">
            <tr align="center" bgcolor="lightgreen">
                <td width="7%"><b>아이디</b></td>
                <td width="7%"><b>비밀번호</b></td>
                <td width="7%"><b>이름</b></td>
                <td width="7%"><b>이메일</b></td>
                <td width="7%"><b>가입일</b></td>
            </tr>

            <c:choose>
                <c:when test="${empty membersList}">
                    <tr>
                        <td colspan=5 align="center">
                            <b>등록된 회원이 없습니다.</b>
                        </td>
                    </tr>
                </c:when>
                <c:when test="${!empty membersList }">
                    <c:forEach var="mem" items="${membersList }">
                        <tr align="center">
                            <td>${mem.id }</td>
                            <td>${mem.pwd }</td>
                            <td>${mem.name}</td>
                            <td>${mem.email }</td>
                            <td>${mem.joinDate}</td>
                        </tr>
                    </c:forEach>
                </c:when>
            </c:choose>
        </table>
        <a href="#">
            <p class="cls2">회원 가입하기</p>
        </a>
    </body>

</html>

6. 다음의 주소로 요청하여 실행 결과를 확인한다.

- http://localhost:8090/pro17/mem.do

* 지금까지 회원 정보 조회 기능을 구현해 보았다. 하지만 회원 조회만으로는 큰 의미가 없다. 지금부터는 새 회원을 추가하고 수정, 삭제할 수 있는 기능까지 MVC 방식으로 차례대로 구현해 보자.


(2) 회원 정보 추가 기능 구현

* 이번에는 컨트롤러에서 회원 정보 조회뿐만 아니라 회원 정보 등록까지 구현해 보자. 앞에서보다 브라우저로부터 전달되는 요청 사항이 많아졌기 때문에 우선은 컨트롤러가 브라우저로부터 어떤 요청을 받았는지 알아내야 한다. 그런 다음 그 요청에 대해 해당하는 모델을 선택하여 작업을 요청해야 하는데, 이 역할을 하는 방법을 커맨드(command) 패턴이라고 한다.

* 커맨드 패턴이란 한마디로 브라우저가 URL 패턴을 이용해 컨트롤러에게 수행 작업을 요청하는 방법이다. 컨트롤러는 HttpServletRequest의 getPathInfo() 메서드를 이용해 URL 패턴에서 요청명을 받아와 작업을 수행한다.

* URL을 이용해 컨트롤러에 요청하는 형식은 다음과 같다. 보통 두 단계로 요청이 이루어진다.

- http://localhost:8090/pro17/member/listMembers.do

(1) /member : 첫 번째 단계의 요청은 회원 기능을 의미한다.

(2) /listMembers.do : 두 번째 단계의 요청은 회원 기능 중 회원 조회 기능을 의미한다.

* 다음 그림은 커맨드 패턴을 이용해 회원 가입 기능을 구현하기에 앞서 회원 가입 과정을 그림으로 나타낸 것이다.

(1) 회원 가입창에서 회원 정보를 입력하고 URL 패턴을 /member/addMember.do로 서버에 요청한다.

(2) MemberController에서 getPathInfo() 메서드를 이용해 요청명인 /addMember.do를 받아온다.

(3) 요청명에 대해 MemberDAO의 addMember() 메서드를 호출한다.

(4) addMember() 메서드에서 SQL문으로 테이블에 회원 정보를 추가한다.

1. sec02.ex01 패키지를 만들고 MemberDAO와 MemberVO 클래스는 sec01.ex01 패키지의 것을 복사해 붙여 넣는다. 그리고 test01 폴더의 listMembers.jsp도 복사해 test02 폴더로 붙여 넣는다.

2. 컨트롤러 역할을 하는 MemberController 클래스를 다음과 같이 작성한다. 이 컨트롤러에서는 getPathInfo() 메서드를 이용해 두 단계로 이루어진 요청을 가져온다. action 값에 따라 if문을 분기해서 요청한 작업을 수행하는데 action 값이 null 이거나 /listMembers.do 인 경우에 회원 조회 기능을 수행한다. 만약 action 값이 /memberForm.do면 회원 가입 창을 나타내고 action 값이 /addMember.do면 전송된 회원 정보들을 테이블에 추가한다.

package sec02.ex01;

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

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/member/*") // 브라우저에서 요청 시 두 단계로 요청이 이루어진다.
public class MemberController extends HttpServlet {
	private static final long serialVersionUID = 1L;
	MemberDAO memberDAO;

	public void init() throws ServletException {
		memberDAO = new MemberDAO();
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request, response);
	}

	private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String nextPage = null;
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		String action = request.getPathInfo(); // URL에서 요청명을 가져온다.
		System.out.println("action:" + action);
		
		if (action == null || action.equals("/listMembers.do")) { // 최초 요청이거나 action 값이 /listMembers.do면 회원 목록을 출력한다.
			List<MemberVO> membersList = memberDAO.listMembers();
			request.setAttribute("membersList", membersList);
			nextPage = "/test02/listMembers.jsp"; // test02 폴더의 listMembers.jsp로 포워딩한다.
			
		} else if (action.equals("/addMember.do")) {  // action 값이 /addMember.do면 전송된 회원 정보를 가져와서 테이블에 추가한다.
			String id = request.getParameter("id");
			String pwd = request.getParameter("pwd");
			String name = request.getParameter("name");
			String email = request.getParameter("email");
			MemberVO memberVO = new MemberVO(id, pwd, name, email);
			memberDAO.addMember(memberVO);
			nextPage = "/member/listMembers.do"; // 회원 등록 후 다시 회원 목록을 출력한다.

		} else if (action.equals("/memberForm.do")) { // action 값이 /memberForm.do면 회원 가입창을 화면에 출력한다.
			nextPage = "/test02/memberForm.jsp"; // test02 폴더의 memberForm.jsp로 포워딩한다.
			
		} else { // 그 외 다른 action 값은 회원 목록을 출력한다.
			List<MemberVO> membersList = memberDAO.listMembers();
			request.setAttribute("membersList", membersList);
			nextPage = "/test02/listMembers.jsp";
		}
		
		RequestDispatcher dispatch = request.getRequestDispatcher(nextPage); // nextPage에 지정한 요청명으로 다시 서블릿에 요청한다.
		dispatch.forward(request, response);
	}

}

3. 다음과 같이 listMember.jsp에 회원 가입창으로 이동하는 <a> 태그를 추가한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	import=" java.util.*,sec01.ex01.*"
	pageEncoding="UTF-8"
	isELIgnored="false" 
%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<%
	request.setCharacterEncoding("UTF-8");
%>
<html lang="ko">

    <head>
        <meta charset="UTF-8">
        <title>회원 정보 출력창</title>
        <style>
            .cls1 {
                font-size: 40px;
                text-align: center;
            }

            .cls2 {
                font-size: 20px;
                text-align: center;
            }
        </style>
    </head>

    <body>
        <p class="cls1">회원정보</p>
        <table align="center" border="1">
            <tr align="center" bgcolor="lightgreen">
                <td width="7%"><b>아이디</b></td>
                <td width="7%"><b>비밀번호</b></td>
                <td width="7%"><b>이름</b></td>
                <td width="7%"><b>이메일</b></td>
                <td width="7%"><b>가입일</b></td>
            </tr>

            <c:choose>
                <c:when test="${empty membersList}">
                    <tr>
                        <td colspan="5" align="center">
                            <b>등록된 회원이 없습니다.</b>
                        </td>
                    </tr>
                </c:when>
                <c:when test="${!empty membersList}">
                    <c:forEach var="mem" items="${membersList }">
                        <tr align="center">
                            <td>${mem.id }</td>
                            <td>${mem.pwd }</td>
                            <td>${mem.name}</td>
                            <td>${mem.email }</td>
                            <td>${mem.joinDate}</td>
                        </tr>
                    </c:forEach>
                </c:when>
            </c:choose>
        </table>
        <a href="${contextPath}/member/memberForm.do">
            <p class="cls2">회원 가입하기</p> <%-- 회원가입하기 클릭 시 서블릿에 /member/memberForm.do로 요청한다. --%>
        </a>
    </body>

</html>

4. 회원 가입창에서 회원 정보를 입력하고 action 속성에서 /member/addMember.do로 요청하도록 memberForm.jsp를 작성한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"
	isELIgnored="false" %>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html lang="ko">

    <head>
        <meta charset="UTF-8">
        <title>회원 가입창</title>
    </head>

    <body>
        <form method="post" action="${contextPath}/member/addMember.do">
            <h1 style="text-align:center">회원 가입창</h1>
            <table align="center">
                <tr>
                    <td width="200">
                        <p align="right">아이디</p>
                    </td>
                    <td width="400"><input type="text" name="id"></td>
                </tr>
                <tr>
                    <td width="200">
                        <p align="right">비밀번호</p>
                    </td>
                    <td width="400"><input type="password" name="pwd"></td>
                </tr>
                <tr>
                    <td width="200">
                        <p align="right">이름</p>
                    </td>
                    <td width="400">
                        <p><input type="text" name="name"></p>
                    </td>
                </tr>
                <tr>
                    <td width="200">
                        <p align="right">이메일</p>
                    </td>
                    <td width="400">
                        <p><input type="text" name="email"></p>
                    </td>
                </tr>
                <tr>
                    <td width="200">
                        <p>&nbsp;</p>
                    </td>
                    <td width="400">
                        <input type="submit" value="가입하기">
                        <input type="reset" value="다시입력">
                    </td>
                </tr>
            </table>
        </form>
    </body>

</html>

5. 다음의 주소로 요청하여 회원 목록창이 나타나면 하단에 있는 회원 가입하기를 클릭한다.

- http://localhost:8090/pro17/member/listMembers.do

6. 회원 가입창이 나타나면 다음과 같이 새 회원 차두리의 정보를 입력하고 가입하기를 클릭한다.

7. 위에서 등록한 새 회원(차두리)이 추가된 회원 목록창이 다시 나타난다.