MVC 패턴을 배워봅시다.
MVC패턴 그림을 보고 구조를 이해해보자
MVC 패턴의 의미
MVC
는 Model-View-Controller 의 약자입니다.
개발 할 때, 3가지 형태로 역할을 나누어 개발하는 방법론입니다.
비지니스 처리 로직과 사용자 인터페이스 요소들을 분리시켜 서로 영향없이 개발 하기 수월하다는 장점이 있습니다.
Model은 어플리케이션이 “무엇”을 할 것인지를 정의 합니다.
내부 비지니스 로직을 처리하기 위한 역할을 할 것입니다.
처리되는 알고리즘, DB 와 상호작용(CRUD Create Read Update Delete), 데이터 등등..
Controller는 모델이 “어떻게” 처리할 지를 알려주는 역할을 할 것이고, 모바일에서는 화면의 로직처리 부분입니다. 화면에서 사용자의 요청을 받아서 처리되는 부분을 구현되게 되며, 요청 내용을 분석해서 Model과 View에 업데이트 요청을 하게 됩니다.
사용자로 부터의 입력 을 받고 Model 또는 View중개인 역할
View는 화면에 “무엇” 인가를 “보여주기 위한 역할”을 합니다. 컨트롤러 하위에 종속되어, 모델이나 컨트롤러가 보여주려고 하는 모든 필요한 것들을 보여줄 것입니다.
최종 사용자에게 “무엇”을 화면(UI)으로 보여줌
그리고 Controller는 Model과 View가 각각 무엇을 해야 할 지를 알고 있고, 통제합니다. 비지니스 로직을 처리하는 Model과 완전히 UI에 의존적인 View가 서로 직접 이야기 할 수 없게 합니다.
◆ JDBC 란 무엇일까요
JDBC(Java Database Connectivity)는 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API이다. JDBC는 데이터베이스에서 자료를 쿼리하거나 업데이트하는 방법을 제공한다.
◆ MyBatis란 무엇이죠?
마이바티스는 개발자가 지정한 SQL, 저장프로시저 그리고 몇가지 고급 매핑을 지원하는 퍼시스턴스 프레임워크이다. 마이바티스는 JDBC로 처리하는 상당부분의 코드와 파라미터 설정및 결과 매핑을 대신해준다. 마이바티스는 데이터베이스 레코드에 원시타입과 Map 인터페이스 그리고 자바 POJO 를 설정해서 매핑하기 위해 XML과 애노테이션을 사용할 수 있다.
- 객체 지향 언어인 자바의 관계형 데이터 베이스 프로그래밍을 보다 쉽게 도와주는 프레임 워크
- 자바에서는 관계형 데이터베이스 프로그래밍을 하기위해 JDBC를 제공
MyBatis는 JDBC의 편한 사용을 위함
각각 구성 요소들의 의미;
MVC(Model, View, Controller) : 데이터 및 논리 제어를 구현하는데 널리 사용되는 소프트웨어 디자인 패턴
Model = Service, DAO, DTO
View = 사용자가 보는 것
Controller = View와 Model를 이어주는 것
DAO(Data Access Object) :
domain logic을 persistence mechanism과 분리하기 위해 사용
데이터 요청이 들어올때마다 DAO에서 Connection 객체를 생성하면, 너무 많은 커넥션으로 인해 오류가 생긴다.
그렇기 때문에 Singleton 패턴으로 하나의 객체만 생성되도록 구성한다.
domain logic : business logic이나 DB와 관련 없는 로직
persistence layer : DB에 data를 CRUD하는 계층
CRUD(Create, Read, Update, Drop) : DB Query
Connection 객체 : DB에 접근해 Query를 수행하는 객체
Query : DB를 사용하는 명령어
Singleton 패턴 : 클래스가 메모리에 할당 될 때 고정된 하나의 객체를 할당하고, 이후 그것만 참조하는 디자인패턴.
DTO(Data Transfer Object) :
계층간 데이터 교환을 위한 Java Beans.
DB에서 받아온 데이터들을 매핑하기위한 객체.
일반적으로 logic을 가지고 있지 않고 getter, setter만 있다.
Java Beans : 자바 관련 소프트웨어 개발에 있어 재사용이 가능한 표준 컴포넌트 모델
private field를 가지고 있다.
getter, setter로 필드에 접근한다.
생성자를 가진다.
Serializable 구현
private field : 인스턴스 변수를 private로 가짐.
Serializable : 자바 내부의 Object나 데이터를 외부에서도 사용할 수 있도록 하는 기술
Service :
Controller가 Request를 받으면 적절한 Service에 전달하고, 전달 받은 Service는 business logic을 처리한다.
Service가 DB에 DAO로 접근하고, 데이터를 DTO로 전달받은 다음, 데이터를 필요에 맞게 가공하여 반환한다.
[출처] Java - Service, DAO, DTO 정리|작성자 브혼
사용환경을 위한 준비
jar 준비
MyBatis
mybatis를 사용하기 위한 설정
DBService.java 객체 생성
sqlmap-config.xml, member.xml 설정
- DBService.java
package mybatis.config;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class DBService {
//DBService: SqlSessionFactory 반환
private SqlSessionFactory factory;
// DBService 생성자: Singleton
private DBService() {
try {
String resource = "mybatis/config/sqlmap-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
factory = new SqlSessionFactoryBuilder().build(inputStream);
} catch(Exception e) {
e.printStackTrace();
}
}
private static DBService service = new DBService();
public static DBService getInstance() {
return service;
}
public SqlSessionFactory getFactory() {
return factory;
}
}
- mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="mybatis.mapper.mapper">
<!--
패키지 : mybatis.mapper
클래스 : mapper
-->
<!--
1. 사용태그
1) <select>
2) <insert>
3) <update>
4) <delete>
2. 쿼리문에 전달할 파라미터가 있는 경우
: parameterType 속성
ex)
<select parameterType="int"> 파라미터가 정수
<select parameterType="dto.LimeDto"> 파라미터가 dto패키지의 LimeDto
<select parameterType="Map">
3. 쿼리문의 결과
: resultType 속성
1) 결과가 List<LimeDto>인 경우 <select resultType="dto.LimeDto">
2) 결과가 LimeDto인 경우 <select resultType="dto.LimeDto">
3) 결과가 0 또는 1인 경우 <insert> <update> <delete> 태그는 resultType을 작성하지 않는다.
-->
<select id="list" resultType="dto.LimeDto">
SELECT * FROM LIME
</select>
<insert id="insert" parameterType="dto.LimeDto">
INSERT INTO
LIME (NO, WRITER, TITLE, CONTENT, POSTDATE)
VALUES (LIME_SEQ.NEXTVAL, #{writer}, #{title}, #{content}, SYSDATE)
</insert>
<select id="view" resultType="dto.LimeDto">
SELECT * FROM LIME WHERE NO = #{no}
</select>
<delete id="delete" parameterType="int">
DELETE FROM LIME WHERE NO = #{no}
</delete>
<update id="update" parameterType="dto.LimeDto">
UPDATE LIME SET TITLE = #{title}, CONTENT = #{content} WHERE NO = #{no}
</update>
</mapper>
- sqlmap-config.xml(member)
먼저 멤버의 값을 여기 추가한다. 나중에 추가될때마다 차차 추가됩니다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="mybatis.mapper.member">
</mapper>
DB
drop sequence member_seq;
create sequence member_seq
start with 1
increment by 1
maxvalue 999999
nocycle
nocache;
drop table member;
create table member
(
mno number primary key,
mid varchar2(30) not null unique,
mpw varchar2(30) not null,
mname varchar2(30),
memail varchar2(50) not null unique,
mphone varchar2(20),
maddress varchar2(100),
mregdate date
);
insert into member values(member_seq.nextval, 'admin', '1111', '관리자', 'admin@myhome.com', '010-0000-0000', '부천', sysdate);
DAO
public class MemberDao {
private SqlSessionFactory factory;
private MemberDao() {
factory = DBService.getInstance().getFactory();
}
private static MemberDao memberDao = new MemberDao();
public static MemberDao getInstance() {
return memberDao;
}
}
DTO
package dto;
import java.sql.Date;
public class MemberDto {
private int mNo;
private String mId;
private String mPw;
private String mName;
private String mEmail;
private String mPhone;
private String mAddress;
private Date mRegdate;
public int getmNo() {
return mNo;
}
public void setmNo(int mNo) {
this.mNo = mNo;
}
...
}
MVC 패턴 구축
pathNRedirect 만들기
controller의 doGet()메소드의 pathNRedirect
package common;
public class PathNRedirect {
private String path;
private boolean isRedirect;
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public boolean isRedirect() {
return isRedirect;
}
public void setRedirect(boolean isRedirect) {
this.isRedirect = isRedirect;
}
}
Command
package command.member;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import common.PathNRedirect;
public interface MemberCommand {
public PathNRedirect execute(HttpServletRequest request, HttpServletResponse response);
}
Controller
package controller;
import java.io.IOException;
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 command.member.MemberCommand;
import command.member.MemberFindIdCommand;
import command.member.MemberLoginCommand;
import command.member.MemberLogoutCommand;
import common.PathNRedirect;
@WebServlet("*.member")
public class MemberController extends HttpServlet {
private static final long serialVersionUID = 1L;
public MemberController() {
super();
// TODO Auto-generated constructor stub
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String requestURI = request.getRequestURI();
String context = request.getContextPath();
String cmd = requestURI.substring(context.length());
MemberCommand command = null;
PathNRedirect pathNRedirect = null;
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
view
index 파일 - header와 footer를 include 해준다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!-- 머리글 포함(파라미터(title)가 있으므로 동적 페이지 포함 -->
<jsp:include page="template/header.jsp"/>
<c:if test="${loginDto.mId eq 'admin' }">
<h1>관리자님 환영합니다.</h1>
<a href="/MyHome/memberList.admin">회원관리</a><br>
<a href="/MyHome/guestList.admin">방명록관리</a><br>
<a href="/MyHome/bbsList.admin">BBS관리</a><br>
<a href="/MyHome/boardList.admin">게시판관리</a><br>
</c:if>
<c:if test="${loginDto.mId ne 'admin'}" >
<h1>MyHome에 오신 걸 환영합니다.</h1>
</c:if>
<!-- 바닥글 포함(파라미터가 없으므로 정적 페이지 포함 -->
<%@include file="template/footer.jsp" %>
header.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%
request.setCharacterEncoding("utf-8");
String title = request.getParameter("title");
if(title == null || title.isEmpty()){
title = "환영합니다.";
}
pageContext.setAttribute("title", title);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${title }</title>
<!-- css -->
<link rel="stylesheet" href="asset/style/common.css?ver0.1">
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script type="text/javascript">
function fn_logout(f){
if(confirm('로그아웃 하시겠습니까?')){
f.action = '/MyHome/logout.member';
f.submit();
}
}
function fn_signOut(){
location.href= '/MyHome/signOutPage.member';
}
</script>
</head>
<body>
<div class="wrap">
<img alt="고양이" src="asset/image/cat-3059075_1920.jpg" style="height: 100px; width: 250px">
<div class="head-wrap">
<!-- 로그인 안 된 상태 -->
<c:if test="${loginDto eq null }">
<input type="button" value="MyHome로그인" onclick="location.href='/MyHome/loginPage.member'"/>
<input type="button" value="회원가입" onclick="location.href='/MyHome/signUpPage.member'"/>
<input type="button" value="마이페이지" onclick="location.href='/MyHome/loginPage.member'"/>
<input type="button" value="방명록" onclick="location.href='/MyHome/'"/>
<input type="button" value="BBS" onclick="location.href='/MyHome/'"/>
<input type="button" value="게시판" onclick="location.href='/MyHome/'"/>
<!-- 2.로그인 상태 -->
</c:if>
<c:if test="${loginDto ne null }">
${loginDto.mName } 님 반갑습니다.
<form>
<input type="button" value="로그아웃" onclick="fn_logout(this.form)"/>
<input type="button" value="회원탈퇴" onclick="fn_signOut()"/>
<input type="button" value="마이페이지" onclick="location.href='/MyHome/myPage.member'"/>
<input type="button" value="방명록" onclick="location.href='/MyHome/'"/>
<input type="button" value="BBS" onclick="location.href='/MyHome/'"/>
<input type="button" value="게시판" onclick="location.href='/MyHome/'"/>
</form>
</c:if>
</div>
<div class="body-wrap">
footer.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
</div>
<div class="foot-wrap">
개인정보보호정책 | 약관 | 저작권
</div>
</div>
</body>
</html>