본문 바로가기
JSP 웹프로그래밍

JSP #11 : Model2 방식, 회원 출력, 추가, 삭제/수정(230110)

by haheehee 2023. 1. 11.

++ 모델2 방식 (Model2) ++

- 웹 애플리케이션의 각 기능(클라이언트의 요청 처리, 응답 처리, 비즈니스 로직 처리)을 분리

- 객체 지향 프로그래밍에서 각각의 기능을 모듈화해서 개발

 

- 각 기능이 서로 분리되어 있어 개발 및 유지보수에 용이

- 각 기능(모듈)의 높은 재사용성

- 디자이너와 개발자의 작업을 분업화 --> 쉽게 개발 가능


Model1

출처 : https://seongmun-hong.github.io/images/post/mvc1.png

 

Model2

출처 : https://seongmun-hong.github.io/images/post/mvc2.png


++ MVC ++

https://hhahee.tistory.com/39

 

MVC 모델

Model/View/Controller (MVC) 모델 MVC(Model View Controller) 모델은 중앙 데이터 구조를 갖는다. 시스템을 세 개의 서브 시스템인 Model, View, Controller로 나누어 구성한다. 제어 서브시스템은 뷰 서브시스템과

hhahee.tistory.com

- Model-View-Controller(모델-뷰-컨트롤러)

- 일반적인 프로그램 개발에 사용되는 디자인 패턴을 웹 애플리케이션에 도입한 것

- 웹 애플리케이션을 화면 부분, 요청 처리 부분, 로직 처리 부분으로 나누어 개발하는 방법

 

- 각 기능이 분리 --> 개발 및 유지보수가 편리, 높은 재사용성

- 디자이너와 개발자의 작업 분업화 가능

 

  • Controller : 서블릿  -> 사용자의 요청 및 흐름 제어 담당
  • View : JSP  -> 사용자에게 보여줄 화면 담당
  • Model : DAO, Service  -> 비즈니스 로직 처리

 

Controller Model View
- 서블릿이 컨트롤러의 역할
- 클라이언트의 요청을 분석
- 요청의 필요한 모델 호출
- Model에서 처리한 결과 출력 위해 JSP를 선택
- 비즈니스 로직 수행 (예 : 데이터베이스 연동)
- 일반적으로 DAO와 VO 클래스로 이루어짐
- JSP가 화면 기능 담당
- Model에서 처리한 결과 화면에 표시

** 호출은 무조건 Controller가 받음!!

 


회원 정보 조회 기능 구현

① 브라우저에서 /mem.do(서블릿)로 요청

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

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

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

⑤ 회원 목록창(listMembers.jsp)에서 포워딩한 회원 정보를 목록으로 출력

(출처 : 자바 웹을 다루는 기술 (이병승, 길벗))

 

** request : 클라이언트와 서블릿 간의 저장 객체.

 

 

Server폴더에서

context.xml 파일에

<Resource>태그 추가. (</Context> 위에 추가하면 된다.)

(밑에 <Manager .../>태그는 주석처리)

context.xml의 jdbc방식 (url, user, password 등)을 MemberDAO에서 호출해서 사용

 

데이터베이스는 지난번에 생성했던 membertable 디비의 t_member테이블을 사용할 것이다.

 

package member.test.mvc;

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, Date joinDate) {
		this.id = id;
		this.pwd = pwd;
		this.name = name;
		this.email = email;
		this.joinDate = joinDate;
	}
	...
}

MemberVO.java 

MemberVO 클래스 생성

(Getters and Setters 생성)

 

package member.test.mvc;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class MemberDAO {
	private Connection con;
	private PreparedStatement pstmt;
	private DataSource dataFactory;
	
	public MemberDAO() {
		try {
			javax.naming.Context ctx = new InitialContext();
			Context envContext = (Context) ctx.lookup("java:/comp/env");
			dataFactory = (DataSource) envContext.lookup("jdbc/membertable");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public List listMembers() {
		List list = new ArrayList();
		try {
			con = dataFactory.getConnection();
			String query = "select * from t_member order by joinDate desc";
			System.out.println(query);
			pstmt = con.prepareStatement(query);
			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);
				list.add(memberVo);
			}
			rs.close();
			pstmt.close();
			con.close();
		} catch(Exception e) {
			e.printStackTrace();
		}
		return list;
	}
	
	public void addMember(MemberVO m) {
		try {
			con = 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 = con.prepareStatement(query);
			pstmt.setString(1, id);
			pstmt.setString(2, pwd);
			pstmt.setString(3, name);
			pstmt.setString(4, email);
			pstmt.executeUpdate();
			pstmt.close();
			con.close();
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}

MemberDAO.java

MemberDAO 클래스 생성.

서버(톰캣)의 context.xml에 미리 데이터베이스 연동 코드를 넣어 놓았기 때문에 

url등을 다시 코드로 작성하지 않아도 된다. (connDB()함수도)

 

package member.test.mvc;

import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
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(ServletConfig config) throws ServletException {
		memberDAO = new MemberDAO();
	}
	public void destroy() {	}

	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> memberList = memberDAO.listMembers();
		request.setAttribute("membersList", memberList);
		RequestDispatcher dispatch = request.getRequestDispatcher("/memberMVC/listMembers.jsp");
		dispatch.forward(request, response);
	}
}

/mem.do (서블릿)

MemberController.java(서블릿 파일)생성

 

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" import="java.util.*, member.test.mvc.*" isELIgnored="false" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<% request.setCharacterEncoding("utf-8"); %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>회원정보 출력창</title>
<style type="text/css">
.cls1 { font-size: 40px; text-align: center; }
.cls2 { text-align: center;}
</style>
</head>
<body>
	<table align="center" width="100%" border="1">
		<tr align="center" bgcolor="lightgreen">
			<td width="7%">ID</td>
			<td width="7%">PASSWORD</td>
			<td width="7%">NAME</td>
			<td width="7%">EMAIL</td>
			<td width="7%">JOINDATE</td>
		</tr>
		<c:choose>
			<c:when test="${membersList == null}">
				<tr align="center">
					<td colspan="5" width="7%">
					<b>등록된 회원이 없습니다.</b></td>
				</tr>
			</c:when>
			<c:when test="${membersList != null}">
				<c:forEach var="mem" items="${membersList}">
					<tr align="center">
						<td width="7%">${mem.id}</td>
						<td width="7%">${mem.pwd}</td>
						<td width="7%">${mem.name}</td>
						<td width="7%">${mem.email}</td>
						<td width="7%">${mem.joinDate}</td>
					</tr></c:forEach></c:when></c:choose>
	</table>
	<a href="#"><p class="cls2">회원가입하기</p></a>
</body>
</html>

listMembers.jsp 생성

 

데이터베이스와 연동한

회원정보출력창

① 브라우저에서 /mem.do(서블릿)로 요청

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

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

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

⑤ 회원 목록창(listMembers.jsp)에서 포워딩한 회원 정보를 목록으로 출력

회원 정보 추가 기능 구현

Command Pattern (커맨드 패턴)
- 브라우저가 URL 패턴을 이용해 컨트롤러에게 수행 작업을 요청
- Controller는 HttpServletRequest의 getPathInfo() 메소드를 통해 URL 패턴에서 요청명을 받아 작업을 수행
     (URL패턴 요청 형식 : http://localhost:3306/projsp02/member/listMembers.do)
          -> /member : 회원 기능, /listMembers.do : 조회기능

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

② MemberController에서 HttpServletRequest의 getPathInfo() 메서드를 이용해 요청명인 /addMember.do를 받아옴

③ 요청명에 대해 MemberDAO의 addMember() 메서드를 호출

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

 

database와

MemberDAO.java,

MemberVO.java

는 위 예제와 동일

 

package member.test.mvcCommand;

...
@WebServlet("/memAdd/*")
public class MemberController extends HttpServlet {
	private static final long serialVersionUID = 1L;

	MemberDAO memberDAO; 	// 필드
	
	public void init(ServletConfig config) throws ServletException {
		memberDAO = new MemberDAO();
		System.out.println("init 호출");
	}
	public void destroy() { System.out.println("destroy 호출"); }

	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();
		System.out.println("action : " + action);
		
		if(action == null  || action.equals("/listMembers.do")) {
			List<MemberVO> memberList = memberDAO.listMembers();
			request.setAttribute("memberList", memberList);
			nextPage = "/memberMVCCommand/listMembers.jsp";
		} else if (action.equals("/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 = "/memAdd/listMembers.do";
		} else if (action.equals("/memberForm.do")) {
			nextPage = "/memberMVCCommand/memberForm.jsp";
		} else {
			List<MemberVO> memberList = memberDAO.listMembers();
			request.setAttribute("memberList", memberList);
			nextPage = "/memberMVCCommand/listMembers.jsp";
		}
		RequestDispatcher dispatch = request.getRequestDispatcher(nextPage);
		dispatch.forward(request, response);
	}
}

MemberController.java

@WebServlet("/memAdd/*") 서블릿 수정

특히 doHandle(...)함수

action에 따라 실행되는 창이 달라지도록 한 것이다.

 

...
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
...
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
	...
	<a href="${contextPath}/memAdd/memberForm.do"><p class="cls2">회원가입하기</p></a>
</body>
</html>

listMembers.jsp

상단에 <c:set var="contextPath" value="${pageContext.request.contextPath}" /> 구문 추가,

회원가입하기 텍스트링크(하이퍼링크href)에 href="${contextPath}/memAdd/memberForm.do" 추가

 

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" isELIgnored="false" %>
<%@ taglib prefix="fn" 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>
<head>
<meta charset="utf-8">
<title>회원가입창</title>
<style type="text/css">
.cls1 {	font-size: 40px; text-align: center; }
.cls2 {	text-align: center; }
</style>
</head>
<body>
	<form action="${contextPath}/memAdd/addMember.do" method="post" >
		<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"><input type="text" name="name"></td>
			</tr>
			<tr>
				<td width="200"><p align="right">이메일</p></td>
				<td width="400"><input type="text" name="email"></td>
			</tr>
			<tr>
				<td width="200"></td>
				<td width="80"><input type="submit" value="가입하기">
                <input type="reset" value="다시입력"></td>
			</tr>
		</table>
	</form>
</body>
</html>

memberForm.jsp 추가 (회원가입창)

 

 

--결과--

listMembers.jsp파일을 서버로 실행시키면, 

이렇게 초기화면이 나온다.

회원가입하기를 누루고, 정보를 입력

가입하기

회원가입창에서 입력한 정보가

데이터베이스에 추가된 것을 확인할 수 있다.

데이터베이스 테이블의 데이터에서도 확인 가능하다.

console창


회원 정보 수정삭제 기능 구현

수정 삭제
① 회원 정보 수정창에서 회원 정보를 수정 -> /member/modMember.do로 컨트롤러에 요청
② 컨트롤러는 전송된 회원 수정 정보를 가져온 후 테이블에서 수정
③ 수정을 마친 후 컨트롤러는 다시 회원 list를 표시
① 회원 목록창에서 삭제 요청 ->  /member/delMember.do와 회원 ID를 컨트롤러로 전달
② 컨트롤러는 HttpServletRequest의 getPathInfo() 메서드를 이용해 요청명 얻음
③ 회원 ID를 SQL문으로 전달해 삭제

 

데이터베이스와 

MemberVO.java는 동일하다.

memberForm.jsp에서는 action의 경로만 수정해주면 된다.

 

		if(action == null  || action.equals("/listMembers.do")) {
			List<MemberVO> memberList = memberDAO.listMembers();
			request.setAttribute("memberList", memberList);
			nextPage = "/memberMVCDnM/listMembers.jsp";
		} 
        ...
		else if (action.equals("/modMemberForm.do")) {
			String id = request.getParameter("id");
			MemberVO memInfo = memberDAO.findMember(id);
			request.setAttribute("memInfo", memInfo);
			nextPage = "/memberMVCDnM/modMemberForm.jsp";
		} else if (action.equals("/modMember.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.modMember(memberVO);
			request.setAttribute("msg",  "modified");
			nextPage = "/memDnM/listMembers.do";
		} else if (action.equals("/delMember.do")) {
			String id = request.getParameter("id");
			memberDAO.delMember(id);
			request.setAttribute("msg",  "deleted");
			nextPage = "/memDnM/listMembers.do";
		}
		else {
			List<MemberVO> memberList = memberDAO.listMembers();
			request.setAttribute("memberList", memberList);
			nextPage = "/memberMVCDnM/listMembers.jsp";
		}

MemberController.java 서블릿파일의

서블릿 이름을 /memDnM으로 변경해주었으며, 

doHandle()함수의 if-else문에 수정과 삭제를 추가해주었다.

 

public MemberVO findMember(String _id) {
	MemberVO memberInfo = null;
	try {
		con = dataFactory.getConnection();
		String query = "select * from t_member where id=?";
		pstmt = con.prepareStatement(query);
		pstmt.setString(1,  _id);
		System.out.println(query);
		ResultSet rs = pstmt.executeQuery();
		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");
		memberInfo = new MemberVO(id, pwd, name, email, joinDate);
		pstmt.close();
		con.close();
	} catch (Exception e) {
		e.printStackTrace();
	}
	return memberInfo;
}

public void modMember(MemberVO m) {
	String id = m.getId();
	String pwd = m.getPwd();
	String name = m.getName();
	String email = m.getEmail();
	try {
		con = dataFactory.getConnection();
		String query = "update t_member set pwd=?,name=?,email=? where id=?";
		System.out.println(query);
		pstmt = con.prepareStatement(query);
		pstmt.setString(1,  pwd);
		pstmt.setString(2,  name);
		pstmt.setString(3,  email);
		pstmt.setString(4,  id);
		pstmt.executeUpdate();
		pstmt.close();
		con.close();
	} catch (Exception e) {
		e.printStackTrace();
	}
}

public void delMember(String id) {
	try {
		con = dataFactory.getConnection();
		String query = "delete from t_member where id=?";
		System.out.println(query);
		pstmt = con.prepareStatement(query);
		pstmt.setString(1,  id);
		pstmt.executeUpdate();
	} catch (Exception e) {
		e.printStackTrace();
	}
}

MemberDAO.java 파일에서는

findMember(), modMember(), delMember() 함수를 추가

 

...
<head>
	<c:choose>
			<c:when test='${msg == "addMember"}'>
				<script>
					window.onload = function() {
						alert("회원을 등록했습니다. ");
					}
				</script>
			</c:when>
			<c:when test='${msg == "modified"}'>
				<script>
					window.onload = function() {
						alert("회원 정보를 수정했습니다. ");
					}
				</script>
			</c:when>
			<c:when test='${msg == "deleted"}'>
				<script>
					window.onload = function() {
						alert("회원 정보를 삭제했습니다. ");
					}
				</script>
			</c:when>
		</c:choose>

...
			<td width="7%">수정</td>
			<td width="7%">삭제</td>
...
			<td width="7%"><a href="${contextPath}/memDnM/modMemberForm.do?id=${mem.id}">수정</a></td>
			<td width="7%"><a href="${contextPath}/memDnM/delMember.do?id=${mem.id}">삭제</a></td>
...
	<a href="${contextPath}/memDnM/memberForm.do"><p class="cls2">회원가입하기</p></a>
...

listMembers.jsp파일에서는,

각 테이블에 수정, 삭제 열을 추가하고, 

회원정보 등록/수정/삭제 여부에 따라 alert창으로 알려주는 구문을 추가.

경로 설정도 잊지 말아야한다.

 

위의 예제들에서 추가 파일이 필요하다.

바로 회원정보를 수정하는 창이다.

...
	<h1 class="cls1">회원 정보 수정</h1>
	<form action="${contextPath}/memDnM/modMember.do?id=${memInfo.id}" method="post" >
		<table align="center">
			<tr>
				<td width="200"><p align="right">아이디</p></td>
				<td width="350"><input type="text" name="id" value="${memInfo.id}" disabled></td>
			</tr>
...
				<td width="80"><input type="submit" value="수정하기">
					<input type="reset" value="다시입력"></td>
...

modMemberForm.jsp

회원가입창과 동일하지만, 아이디는 disabled로 설정하여 수정하지 못하도록 설정.

그리고 input태그에 value="${memInfo.id}와 같이, 기존 회원의 정보를 input박스에 출력되도록 했다.

 

 

--결과--

처음 데이터베이스 테이블 데이터

 

처음 회원 가입창

정보를 입력하고 가입하기

입력한 회원정보가 리스트에 출력되었다. (첫번째 줄)

NAME 김오늘의 수정을 누르면

수정할 수 있는 "회원 정보 수정"창이 뜬다.

아이디는 변경 불가이다.

이름과 이메일을 변경해보겠다.

이렇게 alert창이 떠서 수정되었음을 알려주고,

회원 정보 리스트에서 수정된 정보를 보여준다.

데이터베이스에서도 변경된 것을 확인할 수 있다.

 

다시 회원정보 출력창으로 돌아와, 

NAME 김내일의 삭제 버튼을 누르면

alert창으로 삭제되었음을 알려주고, 

회원정보가 삭제된다.

회원정보 출력창과 데이터베이스 데이터에서 삭제된 김내일의 정보

 

 

 

** JSP에서 .do는 가상경로이다. (.abc와 같이 다른 확장자로 해도 전혀 상관없다.)

    다른 서블릿과 URL 충돌을 막기 위해 이런식으로 확장자를 사용하게 되었다고 한다. 

    (참고 : https://withthisclue.tistory.com/entry/JSP-%EC%9B%B9%ED%8E%98%EC%9D%B4%EC%A7%80%EC%97%90%EC%84%9C-%EA%B6%81%EA%B8%88%ED%96%88%EB%8D%98-do-%ED%8C%8C%EC%9D%BC%EC%9D%98-%EC%9D%98%EB%AF%B8)

댓글