MVC - No Servlet & DI_DAO

jinkyung·2021년 1월 22일
0

JSP

목록 보기
17/20

Controller

페이지 컨트롤러에서 servlet 제거하기 (종속 끊기)
req, resp, session 등을 이용하지 않는다
-->model 사용

Controller Interface

package spms.controls;

import java.util.Map;

/*
1)	Front Controller에서 Page Controller를 동일한 방식으로 호출하기 위해 
	정의한 인터페이스
2)	모든 page Controller는 이 인터페이스를 상속받는다 
3) 	Page Controller의 Servlet 종속을 제거하고 POJO 방식으로 만든다 
*/

public interface Controller {
	public String execute(Map<String, Object> model) throws Exception;
}

ContextLoaderListener

package spms.listeners;

import java.sql.Connection;
import java.sql.DriverManager;

import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;

import spms.dao.MemberDao;
import spms.util.DBConnectionPool;

/* JNDI
 * WAS(Web Application Server)의 리소스(자원)에 대한 고유 이름 정의
 * 어플리케이션에서 서버의 리소스를 접근할 때 사용하는 명명 규칙
 * 1) java:comp/env				- 응용 프로그램 환경 항목
 * 2) java:comp/env/jdbc		- JDBC 데이터 소스
 * 3) java:comp/ejb				- EJB 컴포넌트
 * 4) java:comp/UserTransaction - UserTransaction 객체
 * 5) java:comp/env/mail		- JavaMail 연결 객체
 * 6) java:comp/env/url			- URL 정보
 * 7) java:comp/env/jms			- JMS 연결 객체
 * */

public class ContextLoaderListener implements ServletContextListener {
	

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		try {
			System.out.println("contextDestroyed");
			// 우리가 DataSource 객체를 해제하지 않아도
			// tomcat 서버가 알아서 해제한다
		}catch(Exception e) {
			e.printStackTrace();
		}		

	}

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		try {
			System.out.println("contextInitialized");
			ServletContext sc = sce.getServletContext();

			// tomcat서버가 자동으로 생성하고 해제하는 DataSource객체를
			// 찾아서 가져온다
			InitialContext initialContext = new InitialContext();
			DataSource ds = (DataSource)initialContext.lookup(
					"java:comp/env/jdbc/studydb");
			
			MemberDao memberDao = new MemberDao();
			memberDao.setDataSource(ds);	
			
			// conn객체 대신 memberDao객체를 공유
			sc.setAttribute("memberDao", memberDao);
			
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

}

DispatcherServlet

package spms.servlets;

import java.io.IOException;
import java.util.HashMap;

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

import spms.controls.Controller;
import spms.controls.LogInController;
import spms.controls.LogOutController;
import spms.controls.MemberAddController;
import spms.controls.MemberDeleteController;
import spms.controls.MemberListController;
import spms.controls.MemberUpdateController;
import spms.vo.Member;

/* 1) 의미 : tomcat으로 부터 바로 '제어 controller'로 전송하던 방식
 *      -> * tomcat은 Front controller로 전송
 *           Front controller : DispatcherServlet
 *         * Front controller는 PageController로 분기
 *           back  controller : PageController
 *      역할을 분할
 * 2) 이유 : 
 *     2-1) 역할을 보다 상세히 나눠서 규격화/자동화(Framework)하려고 한다
 *     2-2) 스프링 프레임워크의 제작 방식
 *     2-3) Front Controller만 HttpServlet의 상속을 받고
 *          Page Controller는 일반 자바 클래스로 전환(POJO-Plain Old Java Object)
 * 
 * 3) Front Controller(DispatcherServlet)의 역할
 *     3-1) tomcat(서블릿 컨테이너)과 연결되어 다른 Page Controller에 분기
 *     3-2) Page Controller에 전송할 VO 객체 생성 (향후 자동화)
 *     3-3) 서블릿/jsp 이동 처리
 *     3-4) Page Controller에서 발생하는 예외처리
 *     
 * 4) 장점
 *     4-1) 다른 WAS 이삭할 때 Front Controller만 수정하면 된다
 *          (Front Controller만 HttpServlet을 상속받으므로)
 *     4-2) 브라우저한테 노출하고 싶은 않은 페이지를 감출 수 있다
 *     4-3) Page Controller가 하는 공통 역할을 Front Controller에 담았으므로
 *          자동화(Framework)하기 편리하다
 *            
 * 
 * */

@WebServlet("*.do")
@SuppressWarnings("serial")
public class DispatcherServlet extends HttpServlet {

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		response.setContentType("text/html;charset=UTF-8");
		
		// pageController에 분기 전송을 위해 '서블릿 경로'를 얻는다
		String servletPath = request.getServletPath();
		
		try {
			
			Controller pageController = null;
			
			//sc에 memberDao를 set 해놨으므로 가져와야 한다 
			ServletContext sc = this.getServletContext();
			
			HashMap<String, Object> model = 
					new HashMap<String, Object>();
			model.put("memberDao", sc.getAttribute("memberDao"));
			model.put("session", request.getSession());
			
			
			
			if("/member/list.do".equals(servletPath)) {
				// 서블릿 Page Controller -> POJO Page Controller 전환
				pageController = new MemberListController();	
				
			}else if("/member/add.do".equals(servletPath)) {
				pageController = new MemberAddController();	
				
				// 아래 조건이 성립하지 않으면 doGet요청(입력 페이지 전송)
				if(request.getParameter("email")!=null) {
					// doPost (추가할 새로운 데이터가 존재할 때)
					model.put("member", new Member()
							.setEmail(request.getParameter("email"))
							.setPassword(request.getParameter("password"))
							.setName(request.getParameter("name")));
				}
				
			}else if("/member/update.do".equals(servletPath)) {
				pageController = new MemberUpdateController();
				
				// 아래 조건이 성립하지 않으면 doGet(상세정보보기)
				if(request.getParameter("email")!=null) {
					// doPost(수정데이터 db에 반영)
					model.put("member", new Member()
							.setNo(Integer.parseInt(request.getParameter("no")))
							.setEmail(request.getParameter("email"))
							.setName(request.getParameter("name")));
				} else {
					model.put("no", new Integer(request.getParameter("no")));
				}
				
			}else if("/member/delete.do".equals(servletPath)) {
				pageController = new MemberDeleteController();
				model.put("no", new Integer(request.getParameter("no")));
				
			}else if("/auth/login.do".equals(servletPath)) {
				pageController = new LogInController();
				
				if(request.getParameter("email")!=null) {
					model.put("loginInfo", new Member()
							.setEmail(request.getParameter("email"))
							.setPassword(request.getParameter("password")));
				}
				
			}else if("/auth/logout.do".equals(servletPath)) {
				pageController = new LogOutController();
			}
			
			String viewUrl = "";
			
			// POJO Page Controller
			if(pageController != null) {
				
				//execute()에서 반환된 주소를 viewUrl에 대입 
				viewUrl = pageController.execute(model);
				
				//map에 있는 keySet을 가져와서 key 변수에 대입 
				for(String key : model.keySet())
					//request 공간에 key와 value를 저장 -> 저장된 정보를 가지고 페이지 이동을 해야하니까 
                    //jsp는 servlet을 활용해야 하기 때문에 (java로 만들 수 없으니까) request에 저장할 수 밖에 없다.
					request.setAttribute(key, model.get(key));
			} else {
				System.out.println("주소 대상 Controller가 없습니다!");
			}
	

			// 경로가 'redirect:'로 시작하면 바로 이동
			if(viewUrl.startsWith("redirect:")) {
				response.sendRedirect(viewUrl.substring(9));
				return;
				
			}else {
				RequestDispatcher rd = request.getRequestDispatcher(viewUrl);
				rd.include(request, response);
			}
			
		}catch(Exception e) {
			e.printStackTrace();
			
			request.setAttribute("error", e);
			RequestDispatcher rd = 
					request.getRequestDispatcher("/Error.jsp");
			rd.forward(request, response);
		}
	}
}

MemberListController

package spms.controls;

import java.util.Map;

import spms.dao.MemberDao;

public class MemberListController implements Controller {

	@Override
	public String execute(Map<String, Object> model) throws Exception {
		MemberDao memberDao = (MemberDao)model.get("memberDao");
		
		//멤버들의 정보를 담은 members 객체를 model에 넣는다 
		model.put("members", memberDao.selectList());
		
		return "/member/MemberList.jsp";
	}

}

MemberUpdateController

package spms.controls;

import java.util.Map;

import spms.dao.MemberDao;
import spms.vo.Member;

public class MemberUpdateController implements Controller {

	@Override
	public String execute(Map<String, Object> model) throws Exception {
		MemberDao memberDao = (MemberDao)model.get("memberDao");
		
		//get 
		if(model.get("member") == null) {
			Integer no = (Integer)model.get("no");
			Member member = memberDao.selectOne(no);
			model.put("member", member);
			
			return "/member/MemberUpdateForm.jsp";
			
		//post 
		}else {
			Member member = (Member)model.get("member");
			memberDao.update(member);
			
			return "redirect:list.do";
		}
	}

}

MemberDeleteController

package spms.controls;

import java.util.Map;

import spms.dao.MemberDao;

public class MemberDeleteController implements Controller {

	@Override
	public String execute(Map<String, Object> model) throws Exception {
		MemberDao memberDao = (MemberDao)model.get("memberDao");
		
		Integer no = (Integer)model.get("no");
		memberDao.delete(no);
		
		return "redirect:list.do";
	}

}

LogInController

package spms.controls;

import java.util.Map;

import javax.servlet.http.HttpSession;

import spms.dao.MemberDao;
import spms.vo.Member;

public class LogInController implements Controller {

	@Override
	public String execute(Map<String, Object> model) throws Exception {
		if(model.get("loginInfo") == null) {
			return "/auth/LogInForm.jsp";
			
		// post 방식 
		}else {
			MemberDao memberDao = (MemberDao)model.get("memberDao");
			Member loginInfo = (Member)model.get("loginInfo");
			
			Member member = memberDao.exist(loginInfo.getEmail(), 
											loginInfo.getPassword());
			if(member != null) {
				HttpSession session = (HttpSession)model.get("session");
				session.setAttribute("member", member);
				return "redirect:../member/list.do";
			}else {
				return "/auth/LogInFail.jsp";
			}
		}
	}

}

LogOutController

package spms.controls;

import java.util.Map;

import javax.servlet.http.HttpSession;

public class LogOutController implements Controller {

	@Override
	public String execute(Map<String, Object> model) throws Exception {
		HttpSession session = (HttpSession)model.get("session");
		session.invalidate();
		
		return "redirect:login.do";
	}

}

DI_DAO

ContextLoaderListener

package spms.listeners;

import java.sql.Connection;
import java.sql.DriverManager;

import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;

import spms.controls.*;
import spms.dao.MemberDao;
import spms.util.DBConnectionPool;

/* JNDI
 * WAS(Web Application Server)의 리소스(자원)에 대한 고유 이름 정의
 * 어플리케이션에서 서버의 리소스를 접근할 때 사용하는 명명 규칙
 * 1) java:comp/env				- 응용 프로그램 환경 항목
 * 2) java:comp/env/jdbc		- JDBC 데이터 소스
 * 3) java:comp/ejb				- EJB 컴포넌트
 * 4) java:comp/UserTransaction - UserTransaction 객체
 * 5) java:comp/env/mail		- JavaMail 연결 객체
 * 6) java:comp/env/url			- URL 정보
 * 7) java:comp/env/jms			- JMS 연결 객체
 * */

public class ContextLoaderListener implements ServletContextListener {
	

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		try {
			System.out.println("contextDestroyed");
			// 우리가 DataSource 객체를 해제하지 않아도
			// tomcat 서버가 알아서 해제한다
		}catch(Exception e) {
			e.printStackTrace();
		}		

	}

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		try {
			System.out.println("contextInitialized");
			ServletContext sc = sce.getServletContext();

			// tomcat서버가 자동으로 생성하고 해제하는 DataSource객체를
			// 찾아서 가져온다
			InitialContext initialContext = new InitialContext();
			DataSource ds = (DataSource)initialContext.lookup(
					"java:comp/env/jdbc/studydb");
			
			MemberDao memberDao = new MemberDao();
			memberDao.setDataSource(ds);	
			
			// Controller 객체를 '서블릿 컨텍스트' 영역에 저장한다
			sc.setAttribute("/auth/login.do", 
					new LogInController().setMemberDao(memberDao));
			sc.setAttribute("/auth/logout.do", 
					new LogOutController());
			sc.setAttribute("/member/list.do", 
					new MemberListController().setMemberDao(memberDao));
			sc.setAttribute("/member/add.do", 
					new MemberAddController().setMemberDao(memberDao));
			sc.setAttribute("/member/update.do", 
					new MemberUpdateController().setMemberDao(memberDao));
			sc.setAttribute("/member/delete.do", 
					new MemberDeleteController().setMemberDao(memberDao));
			
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

}

MemberListController

package spms.controls;

import java.util.Map;

import spms.dao.MemberDao;

public class MemberListController implements Controller {
	/* DI(Dependency Injection)으로 변경한 이유
	 * 1) 클래스간의 의존성을 낮추기 위해
	 * 2) MemberDao 인터페이스를 선언하고 상속구현함으로써 
	 *    다른 DBMS로의 전환을 용이하게 하려고
	 * 3) 나중에 변경할 자동화 작업에 사용하려고
	 * */
	
	MemberDao memberDao = null;
	
	public MemberListController setMemberDao(MemberDao memberDao) {
		this.memberDao = memberDao;
		return this;
	}

	@Override
	public String execute(Map<String, Object> model) throws Exception {
		//이렇게 꺼내쓰면 의존도가 끈끈해지기 때문에 느슨하게 만들어 준다. 
        	//모든 컨트롤러에서 이부분 지우기
//		MemberDao memberDao = (MemberDao)model.get("memberDao");
		
		
		model.put("members", memberDao.selectList());
		
		return "/member/MemberList.jsp";
	}

}

  • model 객체에도 memberDao를 더이상 put할 필요가 없다.



DAO Interface

MemberDao Interface

package spms.dao;

import java.util.List;

import spms.vo.Member;

/* 굳이 MemberDao 인터페이스를 만든 이유는?
 * 
 * 향후 Oracle, SQL-Server, ...... 확장해 나갈때
 * 이 인터페이스를 상속받아서 기능은 각 DBMS별로 특성에 맞게 구현하되
 * 메서드의 역할/리턴값은 동일하게 해줌으로써
 * DBMS의 교체를 매우 쉽게 해준다
 * */
public interface MemberDao {

	public List<Member> selectList() throws Exception;
	public int insert(Member member) throws Exception;
	public int delete(int no) throws Exception;
	public Member selectOne(int no) throws Exception;
	public int update(Member member) throws Exception;
	public Member exist(String email, String password) throws Exception;
}

MySqlMemberDao

package spms.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import spms.util.DBConnectionPool;
import spms.vo.Member;

public class MySqlMemberDao implements MemberDao{

	DataSource ds = null;
	
	public void setDataSource(DataSource ds) {
		this.ds = ds;
	}

	public List<Member> selectList() throws Exception {
		Connection connection = null;
		Statement stmt = null;
		ResultSet rs = null;
		final String sqlSelect = "SELECT mno,mname,email,cre_date" + "\r\n" + "FROM members" + "\r\n"
				+ "ORDER BY mno ASC";

		try {
			// 커넥션풀에서 Connection객체를 빌려온다
			connection = ds.getConnection();

			stmt = connection.createStatement();
			rs = stmt.executeQuery(sqlSelect);

			ArrayList<Member> members = new ArrayList<Member>();

			while (rs.next()) {
				members.add(new Member().setNo(rs.getInt("mno")).setName(rs.getString("mname"))
						.setEmail(rs.getString("email")).setCreatedDate(rs.getDate("cre_date")));
			}

			return members;

		} catch (Exception e) {
			throw e;
		} finally {
			try {
				if (rs != null)
					rs.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
			try {
				if (stmt != null)
					stmt.close();
			} catch (Exception e) {
				e.printStackTrace();
			}

			/* ds에서 제공하는 Connection객체의 close()의 의미는
			 * 연결을 종료하는 것이 아니라
			 * 객체를 ds내부의 커넥션 풀에 반납한다는 의미이다
			 * */
			try {
				if(connection != null)
					connection.close();
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public int insert(Member member) throws Exception {
		Connection connection = null;
		int result = 0;
		PreparedStatement stmt = null;
		final String sqlInsert = "INSERT INTO members(email,pwd," + "\r\n" + "mname,cre_date,mod_date)" + "\r\n"
				+ "VALUES(?, ?, ?, NOW(), NOW())";

		try {
			// 커넥션풀에서 Connection객체를 빌려온다
			connection = ds.getConnection();

			stmt = connection.prepareStatement(sqlInsert);
			stmt.setString(1, member.getEmail());
			stmt.setString(2, member.getPassword());
			stmt.setString(3, member.getName());
			result = stmt.executeUpdate();
		} catch (Exception e) {
			throw e;
		} finally {
			try {
				if (stmt != null)
					stmt.close();
			} catch (Exception e) {
				e.printStackTrace();
			}

			// 다 썼으면 반납하자
			try {
				if(connection != null)
					connection.close();
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		return result;
	}

	public int delete(int no) throws Exception {
		Connection connection = null;
		int result = 0;
		Statement stmt = null;
		final String sqlDelete = "DELETE FROM MEMBERS WHERE MNO=";

		try {
			// 커넥션풀에서 Connection객체를 빌려온다
			connection = ds.getConnection();

			stmt = connection.createStatement();
			result = stmt.executeUpdate(sqlDelete + no);

		} catch (Exception e) {
			throw e;

		} finally {
			try {
				if (stmt != null)
					stmt.close();
			} catch (Exception e) {
				e.printStackTrace();
			}

			// 다 썼으면 반납하자
			try {
				if(connection != null)
					connection.close();
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		return result;
	}

	public Member selectOne(int no) throws Exception {
		Connection connection = null;
		Member member = null;
		Statement stmt = null;
		ResultSet rs = null;

		final String sqlSelectOne = "SELECT MNO,EMAIL,MNAME,CRE_DATE FROM MEMBERS" + " WHERE MNO=";

		try {
			// 커넥션풀에서 Connection객체를 빌려온다
			connection = ds.getConnection();
			stmt = connection.createStatement();
			rs = stmt.executeQuery(sqlSelectOne + no);
			if (rs.next()) {
				member = new Member().setNo(rs.getInt("MNO")).setEmail(rs.getString("EMAIL"))
						.setName(rs.getString("MNAME")).setCreatedDate(rs.getDate("CRE_DATE"));

			} else {
				throw new Exception("해당 번호의 회원을 찾을 수 없습니다.");
			}

		} catch (Exception e) {
			throw e;
		} finally {
			try {
				if (rs != null)
					rs.close();
			} catch (Exception e) {
			}
			try {
				if (stmt != null)
					stmt.close();
			} catch (Exception e) {
			}

			// 다 썼으면 반납하자
			try {
				if(connection != null)
					connection.close();
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		return member;
	}

	public int update(Member member) throws Exception {
		Connection connection = null;
		int result = 0;
		PreparedStatement stmt = null;
		final String sqlUpdate = "UPDATE MEMBERS SET EMAIL=?,MNAME=?,MOD_DATE=now()" + " WHERE MNO=?";
		try {
			// 커넥션풀에서 Connection객체를 빌려온다
			connection = ds.getConnection();
			stmt = connection.prepareStatement(sqlUpdate);
			stmt.setString(1, member.getEmail());
			stmt.setString(2, member.getName());
			stmt.setInt(3, member.getNo());
			result = stmt.executeUpdate();

		} catch (Exception e) {
			throw e;
		} finally {
			try {
				if (stmt != null)
					stmt.close();
			} catch (Exception e) {
			}

			// 다 썼으면 반납하자
			try {
				if(connection != null)
					connection.close();
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		return result;
	}

	public Member exist(String email, String password) throws Exception {
		Connection connection = null;
		Member member = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		final String sqlExist = "SELECT MNAME,EMAIL FROM MEMBERS" + " WHERE EMAIL=? AND PWD=?";

		try {
			// 커넥션풀에서 Connection객체를 빌려온다
			connection = ds.getConnection();
			
			stmt = connection.prepareStatement(sqlExist);
			stmt.setString(1, email);
			stmt.setString(2, password);
			rs = stmt.executeQuery();
			if (rs.next()) {
				member = new Member().setName(rs.getString("MNAME")).setEmail(rs.getString("EMAIL"));
			} else {
				return null;
			}
		} catch (Exception e) {
			throw e;

		} finally {
			try {
				if (rs != null)
					rs.close();
			} catch (Exception e) {
			}
			try {
				if (stmt != null)
					stmt.close();
			} catch (Exception e) {
			}
			
			try {
				if(connection != null)
					connection.close();// 다 썼으면 반납하자
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		return member;
	}
}

0개의 댓글