복습

ORM (Object Relational Mapping)프레임워크

- MyBatis, JPA

 

JSP Model 2 - MyBatis 게시판 프로그램 

- 이전에 만들었던 JSP Model 2 게시판 프로그램에서 DB 연동을 Connection Pool 대신 MyBatis 와 연동한 것이다

- Connection Pool 사용한 JSP Model 2 게시판 프로그램  : https://laker99.tistory.com/129

 

컬럼 설명

-- 모델2 게시판
select * from tab;
select * from seq;
select * from model22;

create table model22(
	board_num number,
	board_name varchar2(20),
	board_pass varchar2(15),
	board_subject varchar2(50),
	board_content varchar2(2000),
	board_file varchar2(50),
	board_re_ref number,
	board_re_lev number,
	board_re_seq number,
	board_readcount number,
	board_date timestamp,
	primary key(board_num)
);

create sequence model22_seq
start with 1
increment by 1
nocache;
출처: https://laker99.tistory.com/140 [레이커 갓생일기:티스토리]

- 번호값이 들어가는 컬럼이 주로 Primary Key 가 된다

- 원문, 댓글 상관없이 board_num 은 sequence 가 들어간다

 

글 개수 (board.xml 부분)

	<!-- 글갯수 -->
	<select id="board_count" resultType="int">
	 select count(*) from model22
	</select>

- 돌려줄 값의 자료형이 int 이므로 returnType 에 int 를 써야 한다


JSP Model 2 - MyBatis 게시판 프로그램 : 원문 글 작성 기능

- index.jsp 또는 프로젝트를 실행하면 바로 목록을 가져오는 요청인 "/BoardListAction.do" 로 요청한다

- index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

 모델2 게시판
 
<%
	response.sendRedirect("./BoardListAction.do");
%> 

<script>
//	location.href="./BoardListAction.do";
</script>

</body>
</html>

- location.href 로 목록을 가져오기 위한 "/BoardListAction.do" 요청을 해도 되고, sedRedirect 로 "/BoardListAction.do" 로 포워딩해서 요청해도 된다.

- Controller -> Service -> DAO -> Service -> Controller View 파일로 가면 목록이 나타나고 공유된 값들을 출력시켜줌

 

- 목록 페이지에서 '글쓰기' 를 누르면 "/BoardForm.do" 로 요청한다

- qna_board_list.jsp 부분

<a href="./BoardForm.do">글쓰기</a> <br>

 

- Controller 클래스로 간다

- Controller 클래스에서 "/BoardForm.do" 부분만

		// 글작성 폼	
		}else if(command.equals("/BoardForm.do")) {	
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/board/qna_board_write.jsp");
		}

 

- 바로 View 페이지 qna_board_write.jsp 로 이동한다

- qna_board_write.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"%>

<html>
<head>
	<title>MVC 게시판</title>
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script src="<%=request.getContextPath() %>/board/script.js"></script>
</head>
<body>

<form action="<%=request.getContextPath() %>/BoardAddAction.do" method="post" 
	  enctype="multipart/form-data">
<table cellpadding="0" cellspacing="0" align=center border=1>
	<tr align="center" valign="middle">
		<td colspan="5">MVC 게시판</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">글쓴이</div>
		</td>
		<td>
			<input name="board_name" id="board_name" type="text" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">비밀번호</div>
		</td>
		<td>
			<input name="board_pass" id="board_pass" type="password" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">제 목</div>
		</td>
		<td>
			<input name="board_subject" id="board_subject" type="text" size="50" maxlength="100" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12">
			<div align="center">내 용</div>
		</td>
		<td>
			<textarea name="board_content" id="board_content" cols="67" rows="15"></textarea>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12">
			<div align="center">파일 첨부</div>
		</td>
		<td>
			<input name="board_file" type="file"/>
		</td>
	</tr>
	<tr bgcolor="cccccc">
		<td colspan="2" style="height:1px;">
		</td>
	</tr>
	<tr><td colspan="2">&nbsp;</td></tr>
	<tr align="center" valign="middle">
		<td colspan="5">			
			<input type=submit value="등록">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

- 첨부파일이 있을때는 form 에서 반드시 post 방식으로 전송, enctype 코드를 써야한다

- 입력하고 '등록' 을 누르면 "/BoardAddAction.do" 로 전송된다

+ 그럼 이 첨부파일을 원문 글 작성 Service 클래스인 BoardAddAction.java 에서 MultipartRequest 로 받아야함

 - "/BoardAddAction.do" 로 요청되었음을 확인 가능

 

- Controller 클래스에서 "/BoardAddAction.do" 부분만

		// 글작성
		if(command.equals("/BoardAddAction.do")) {
			try {
				action = new BoardAddAction();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

- 원문 글 작성 Service 클래스인 BoardAddAction.java 로 이동

- BoardAddAction.java

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;

import dao.BoardDAO;
import model.BoardBean;

public class BoardAddAction implements Action{
	
	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("BoardAddAction");
		
		String path = request.getRealPath("boardupload");
		System.out.println("path:"+path);
		
		int size = 1024 * 1024; // 1MB
		
		MultipartRequest multi = 
			new MultipartRequest(request,
								 path,
					             size,
					             "utf-8",
						         new DefaultFileRenamePolicy());
		
		BoardBean board = new BoardBean();
		board.setBoard_name(multi.getParameter("board_name"));
		board.setBoard_pass(multi.getParameter("board_pass"));
		board.setBoard_subject(multi.getParameter("board_subject"));
		board.setBoard_content(multi.getParameter("board_content"));
		board.setBoard_file(multi.getFilesystemName("board_file"));
		
		BoardDAO dao = BoardDAO.getInstance();
		int result = dao.insert(board);
		if(result==1) {
			System.out.println("insert");
		}
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(true);
		forward.setPath("./BoardListAction.do");
		
		return forward;
	}

}

첨부파일을 업로드 하기 위해 할 일

1) 첨부파일이 실제 저장되는 경로인 "boardupload" 의 진짜 path를 구한다

2) 업로드할 첨부파일 최대 크기를 정한다

3) MultipartRequest 객체를 생성하면서 첨부파일을 서버로 업로드시킴

- 매개변수가 5개인 생성자 사용하고 있다

- 여기서 한글값 인코딩을 하므로 request.setCharacterEncoding() 을 쓰지 않아도 됨

 

- DTO 객체 board 를 생성하고 그 객체에 앞의 원문 글 작성폼으로부터 넘어온 값들 (첨부파일명 포함) 을 MultipartRequest 객체 multi.getParameter() 로 받아서 저장시킨다

- 실제 서버에 저장된 첨부파일명을 multi.getFilesystemName() 으로 구해와서 DTO 객체 board 에 저장

+ 클라이언트가 업로드한 파일명을 구하는 메소드는 따로 있다

- DAO 객체 생성 후 DAO 의 insert() 메소드를 호출함, 매개변수로는 삽입할 데이터를 저장한 객체 board 를 넘김

<DAO insert() 에서 돌아온 후>

- ActionForward 객체 forward 를 생성하고 목록을 가져오는 요청인 "/BoardListAction.do" 로 요청

+ 바로 목록 페이지 qna_board_list 로 가면 아무것도 없다, 먼저 목록을 가져오는 요청을 해야함

 

+ 첨부파일이 업로드되는 경로가 콘솔창에 찍혀 나온다

 

- MemberDAO.java 에서 SqlSession 객체를 구해오는 getSession() 부분

	public SqlSession getSession() {
		SqlSession session=null;
		Reader reader=null;			
		try {
			reader = Resources.getResourceAsReader("mybatis-config.xml");
			SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(reader);
			session = sf.openSession(true);	 // auto commit	
		}catch(Exception e) {
			e.printStackTrace();
		}		
		return session;
	}

- MyBatis의 환경설정 파일을 읽어오면서 DB연동을 수행한다

- Builder 객체로 read 객체를 읽어서 SqlSessionFactory 객체 sf 를 생성

- sf 객체로 openSession(true) 로 세션을 구해옴, 이때 자동 커밋한다

 

- MemberDAO.java 에서 getMember() 메소드 부분만

	//글작성(원문작성)
	public int insert(BoardBean board) throws Exception {
		int result=0;
		SqlSession session = getSession();
		result = session.insert("board_insert", board);		
		System.out.println("result:"+result);
		
		return result;
	}

- DAO 클래스에서 SQL문을 실행하는 것은 맞다! Mapper 파일에서 SQL문을 가져와서 여기서 실행함

- 메소드 insert() 는 SqlSession 에서 지원되는 메소드

+ SqlSession 은 MyBatis 에서 지원되는 인터페이스이다

 

- board.xml 에서 원문 글 작성 처리하는 SQL문 부분 (id 가 board_insert)

	<!-- 글작성(원문) -->
	<insert id="board_insert" parameterType="board">
	 insert into model22 values(model22_seq.nextval,#{board_name},
	 #{board_pass},#{board_subject},#{board_content},
	 #{board_file,jdbcType=VARCHAR},model22_seq.nextval,0,0,0,sysdate)
	</insert>

- paramteterType 속성으로 값을 받는다, 받는 값의 자료형을 써야하므로 DTO 의 alias 값인 "board" 가 들어감

- board_num 자리에는 model22_seq.nextval 로 sequence 로 값을 넣는다

- #{board_name} 은 넘어온 객체 board 에서 이름 컬럼을 가져오는 board.getBoard_Name() 의 의미이다

- 원문 글이므로 board_re_ref 컬럼 자리에는 sequence 로 값이 입력된다

- 원문 글 작성이므로 board_re_lev, board_re_seq,board_readcount 자리에는 0 을 입력

 

첨부파일명 컬럼 (board.xml 에서 id 가 "board_insert"인 태그의 부분)

#{board_file,jdbcType=VARCHAR}

- 첨부파일은 사용자가 첨부를 선택할 수도 있고, 하지 않을 수도 있다

- 첨부파일을 선택하지 않으면 객체 board의 board_file 에 null 이 들어감, 그럼 DB에 insert 할때 board_file 에 null 값이 들어감

- MyBatis는 null 값이 들어가는 것을 허용하지 않는다

- ,jdbcType=VARCHAR 를 하면 board_file 컬럼에 null 값을 허용도록 만들어주는 코드이다

- 이 jdbcType=VARCHAR 가 없으면 첨부파일을 선택하지 않고 글 작성시 오류가 발생한다

 

- 원문 글 작성 성공시 콘솔창

 


JSP Model 2 - MyBatis 게시판 프로그램 : 글 목록 가져오기 기능 / 목록 페이지

- 원문 글 작성 후 목록을 가져오는 요청인 "/BoardListAction.do" 로 포워딩하며 요청함

- Controller 클래스로 이동한다

 

- Controller 클래스에서 "/BoardListAction.do" 부분만

		// 글목록	
		}else if(command.equals("/BoardListAction.do")) {
			try {
				action = new BoardListAction();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

- 목록 가져오기 Service 클래스인 BoardListAction.java 로 이동

- BoardListAction.java

package service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;

public class BoardListAction implements Action{
	
	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("BoardListAction");
		
		int page=1;
		int limit=10;
		
		if(request.getParameter("page")!=null) {
			page = Integer.parseInt(request.getParameter("page"));					
		}
		
		int startRow = (page-1) * limit + 1;
		int endRow = page * limit;
		
		List boardlist = null;
		BoardDAO dao = BoardDAO.getInstance();		
		int listcount = dao.getCount();
//		boardlist = dao.getList(startRow, endRow);
//		boardlist = dao.getList(page);

//      Map 처리	----------------------------------	
		Map map = new HashMap();
		map.put("start", startRow);
		map.put("end", endRow);
		
//		boardlist = dao.getList(map);		
		boardlist = dao.getList(page);		
//-------------------------------------------------		
		System.out.println("listcount:"+listcount);
		System.out.println("boardlist:"+boardlist);		
		
		int pageCount = listcount/limit + ((listcount%limit==0) ? 0:1);
		
		int startPage = ((page-1)/10) * limit + 1;
		int endPage = startPage + 10 - 1;
		
		if(endPage > pageCount) endPage = pageCount;
		
		request.setAttribute("page", page);
		request.setAttribute("listcount", listcount);
		request.setAttribute("boardlist", boardlist);
		request.setAttribute("pageCount", pageCount);
		request.setAttribute("startPage", startPage);
		request.setAttribute("endPage", endPage);		
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/board/qna_board_list.jsp");
		
		return forward;
	}

}

- 기본변수 3개, 파생된 변수 5개를 여기 글 목록 가져오기 Service 클래스에서 구해와야한다

+ 출력하는 파일은 따로 있다

<기본변수, 파생변수 구하기>

- 현재 페이지 번호 page의 기본값은 1 페이지로 지정, 한 화면에 10개씩 출력하겠다는 의미로 limit 는 10

- 만약 이 Service 클래스로 올때 페이지번호를 가져왔다면 그 번호가 page 의 값이 된다

- startRow, endRow 는 page, limit 에 의해 구해진다

- listcount 변수는 총 데이터 개수를 저장하고 있다, getCount() 로 부터 돌려받은 값

<총 데이터 개수 구하기>

- DAO 객체 생성 후 총 데이터 개수를 구하는 DAO 의 메소드 getCount() 메소드 호출해서 총 데이터 개수를 돌려받음

<목록 구하기>

- 실제 목록을 잘라서 목록을 구해오는 DAO 의 메소드 getList() 메소드를 호출, 매개변수로는 페이지번호 page 를 전달

* 목록을 가져올때 문제점 아래에 설명

- getList() 는 리스트로 리턴하고, 그 결과는 List 객체 boardlist 로 리턴받음

<DAO getList() 에서 돌아온 후>

- 총 페이지수가 저장될 파생변수 pageCount 를 listcount 와 limit 으로 구한다

+ 나눗셈을하면 정수형으로 값이 구해짐을 주의

- 각 블럭을 시작하는 페이지인 startPage 와 각 블럭의 마지막 페이지인 endPage 를 page 와 limit 로 구한다

- View 페이지에서 페이지 메뉴바에서 페이지 번호를 출력할때 무조건 10번 루프를 돌리므로 실제 존재하지 않는 페이지가 나타나는것을 막기 위해 마지막 페이지일땐 endPage 를 pageCount 로 설정해준다

<공유설정>

- 기본 변수 및 파생 변수인 page, listcount, boardlist, pageCount, startPage, endPage 를 request 객체로 공유설정한다

- View 페이지에서 출력할때 boardlist 는 리스트이므로 forEach 의 items 태그에 들어감, 나머지는 기본변수이므로 ${page} 처럼 출력함

<포워딩 방식, 페이지 지정>

- qna_board_list.jsp 로 포워딩, 거기서 목록을 출력할 것

- request 공유설정했으므로 Dispatcher 방식으로 포워딩해야한다

 

+ Model 2 게시판 프로그램 글 목록 구하기 변수들 설명 : https://laker99.tistory.com/129?category=1080281


목록을 가져올때 문제점

- MyBatis 연동 전에는 startRow 와 endRow 를 getList() 의 매개변수로 전달했었다, select 문의 where 절에 사용했음

- MyBatis 와 연동시 값이든 주소든 단 하나만 SqlSession 지원 메소드 5개의 매개변수로 전달 가능하다

+ Service 클래스에서 DAO 메소드로는 여러개의 값을 매개변수로 전달 가능하지만, SqlSession 지원 메소드의 매개변수로는 첫번째 매개변수에 id 를 넣고, 두번째 매개변수에 값을 전달할 수 있다, 단 하나의 값만 전달 가능

 

해결방법

- 해결방법이 여러개 있지만 여기선 이 방법을 사용하고 있다

- startRow 와 endRow 를 전달하는 대신 페이지 번호인 page 를 전달한다

- 이 page 번호에 따라 startRow 와 endRow 가 정해지므로, limit 는 정해진 값이므로 그 자리에 10을 쓰면 된다

- SQL문 상에서 page 번호로 startRow 와 endRow를 계산하기


또다른 해결방법

- 이 부분에서만 여러 파일의 내용을 변경할 것(주석 풀기 등), 이 부분을 나가면 다시 원래대로

- Map 객체 map 을 생성하고 put() 으로 startRow, endRow 를 저장 후 Map 객체 map 을 전달하면 됨

 

//      Map 처리	----------------------------------	
		Map map = new HashMap();
		map.put("start", startRow);
		map.put("end", endRow);

- Map 객체 map 에 key , value 값으로 startRow 와 endRow 를 모두 저장시켰다

ex) 사용자가 2 페이지 선택시 11 과 20 을 Map 객체에 저장하는 것

- key 값으로 value 를 가져와서 사용할 것

- 그리고 Map 객체를 DAO의 getList() 로 전달

		boardlist = dao.getList(map);

- MemberDAO.java 에서 getList() 메소드 부분에서 주석을 바꿈

	// 데이터 목록
//	public List getList(int page) throws Exception {
	public List getList(Map map) throws Exception {
		List list = new ArrayList();
		SqlSession session=getSession();
		list = session.selectList("board_list", map); 
		
		return list;
	}

- map 객체를 selectList() 의 매개변수로 전달한다

- Mapper 파일인 board.xml 로 가자

 

	<!-- Map 전달 -->
	<select id="board_list" parameterType="Map" resultType="board">
	 select * from (select rownum rnum, board.* from (
	  select * from model22 order by board_re_ref desc,board_re_seq asc) board )
	  where rnum &gt;= #{start} and rnum &lt;= #{end}
	</select>

- parameterType 을 Map 으로 써야한다, HashMap 이어도 괜찮다

- #{start} 와 #{end} 의 "start" 와 "end" 는 map 에 저장한 key 값이다

- #{start} 처럼 key 값을 이용해서 value 값을 구해올 수 있다

 

+ 세번째 방법

- DTO 클래스 안에 startRow 와 endRow 를 저장할 수 있는 필드를 만들고 그 DTO 객체에 startRow, endRow 를 저장해서 넘겨주는 방법도 있다

- 가져올땐 getter 메소드 사용

 

어떤 방법을 써야할까?

- 우리가 쓰고 있는 페이지번호 page 를 넘기고 SQL문 상에서 startRow, endRow 를 계산하는 방법은 쓰지 못할 때가 있다

- 검색기능을 수행시에 '작성자명', '제목' 등으로 검색한다, 그 검색어도 SQL상으로 전달되어야함, 넘어가는 값이 많아질땐 페이지번호를 넘기는 방법 쓰지 못하게 됨

- 이땐 DTO 객체를 쓰거나, Map 객체안에 저장해서 전달해야함

- 주로 DTO 객체를 넘기는 경우가 많다, Map 은 DTO보다 처리속도가 느림


- MemberDAO.java 에서 getCount() 메소드 부분만

	// 총데이터 갯수 구하기
	public int getCount() throws Exception{
		int result=0;
		SqlSession session=getSession();
		result = (Integer)session.selectOne("board_count");
		
		return result;
	}

- group 함수로 구하기때문에 검색하는 데이터는 1개이므로 selectOne() 메소드 사용

- session.selectOne() 에서 리턴자료형은 Object 이므로 다운캐스팅이 원칙이다

 

- board.xml 에서 글 개수를 구해주는 처리하는 SQL문 부분 (id 가 board_count)

	<!-- 글갯수 -->
	<select id="board_count" resultType="int">
	 select count(*) from model22
	</select>

- 돌려주는 데이터는 글 개수 이므로 resultType 은 "int" 이다

+ 주의 : returnType 은 무조건 DTO 가 아니다, 돌려주는 데이터의 자료형임

 

- MemberDAO.java 에서 getList() 메소드 부분만

	// 데이터 목록
	public List getList(int page) throws Exception {
//	public List getList(Map map) throws Exception {
		List list = new ArrayList();
		SqlSession session=getSession();
		list = session.selectList("board_list", page); 
		
		return list;
	}

- 메소드 selectList() 에서도 다른 SqlSession 지원 메소드처럼 단 하나의 값만 매개변수로 전달 가능하다

+ 첫번째 매개변수는 id 값, 두번쨰 매개변수에만 값을 전달 가능

- page 번호만 있으면 startRow, endRow를 계산할 수 있으므로 페이지번호 page 만 전달한다

+ Map 객체로 넘어올때는 주석처리 되어있음

 

- board.xml 에서 목록을 자르고 목록을 가져오는 SQL문 부분 (id 가 board_list)

	<!-- 글목록 -->
	<!-- page 전달-->
	<select id="board_list" parameterType="int" resultType="board">
	 select * from (select rownum rnum, board.* from (
	  select * from model22 order by board_re_ref desc,board_re_seq asc) board )
	  where rnum &gt;= (#{page}-1) * 10 + 1   and rnum &lt;= #{page}*10
	</select>

- selectList() 메소드를 실행하고 결과를 List 로 돌려줌, 이때 결과를 DTO 객체들을 저장한 List 로 전달한다고 해도 resultType 에는 DTO 인 member 를 써야한다

- 리스트에 알아서 순차적으로 저장해서 돌려준다

- 전달받은 값은 페이지번호 이므로 parameterType 은 int 로 작성

<SQL문>

- 첫번째 서브쿼리는 rownum 컬럼에 대한 별칭 rnum 을 준다

- 두번째 서브쿼리는 필요한 내용을 검색하고 정렬한다, 댓글게시판이므로 board_re_ref 로 최근글이 위로 가도록 정렬하고, 부모글과 자식글은 board_re_ref 가 동일하므로 한번 더 정렬조건으로 board_re_seq 를 준다

- 두번쨰 서브쿼리에 대해서 board 라는 별칭을 주고 첫번째 서브쿼리에서 board 의 모든 컬럼을 검색하고 있다

- startRow, endRow 를 전달받은 page 번호로 계산한다

- startRow = (#{page} -1) * 10 + 1 이다, 10 은 limit 을 의미

- endRow = #{page} * 10 이다, 10은 limit 을 의미

- xml 파일에서는 < 와 > 를 잘 인식하지 못함, > 대신 특수문자 &gt; 를 사용, < 대신 특수문자 &li; 을 사용

+ <와 > 를 대신하는 다른 방법도 있다, 여기서는 &gt; 과 &lt; 을 사용

 

- 목록을 성공적으로 가져올때 콘솔창

 

- 이제 목록을 가져오는 작업은 끝났다, Controller 클래스로 갔다가 Dispatcher 방식으로 qna_board_list.jsp 로 포워딩됨

- qna_board_list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
   

<a href="./BoardForm.do">글쓰기</a> <br>
데이터 갯수 : ${listcount} 개

<table border=1 width=700 align=center>
	<caption>게시판 목록</caption>
	<tr>
		<th>번호</th>
		<th>제목</th>
		<th>작성자</th>
		<th>날짜</th>
		<th>조회수</th>
	</tr>
		
	<c:set var="num" value="${listcount - (page-1) * 10 }"/>	
	<c:forEach var="b" items="${boardlist}">
	<tr>
		<td>
			${num}
			<c:set var="num" value="${num-1}"/>
		</td>	
		<td>
		<c:if test="${b.board_re_lev > 0}">
			<c:forEach var="i" begin="0" end="${b.board_re_lev}">
				&nbsp;
			</c:forEach>
		</c:if>
		<c:if test="${b.board_re_lev > 0}">
			Re.
		</c:if>
		<a href="./BoardDetailAction.do?board_num=${b.board_num}&page=${page}">
			 ${b.board_subject}
		</a>
		
		</td>	
		<td>${b.board_name}</td>	
		<td>
		
		<fmt:formatDate value="${b.board_date}" pattern="yyyy-MM-dd H:mm"/>
		
		</td>	
		<td>${b.board_readcount}</td>	
	</tr>
	</c:forEach>

</table><br>


<center>
<c:if test="${listcount > 0}">

	<a href="./BoardListAction.do?page=1" style="text-decoration:none"> < </a>

	<c:if test="${startPage > 10}">
		<a href="./BoardListAction.do?page=${startPage - 10}">[이전]</a>
	</c:if>

	<c:forEach var="i" begin="${startPage}" end="${endPage}">
		<c:if test="${i == page}">
			[${i}]
		</c:if>
		<c:if test="${i != page}">
			<a href="./BoardListAction.do?page=${i}">[${i}]</a>
		</c:if>
	</c:forEach>

	<c:if test="${endPage < pageCount}">
		<a href="./BoardListAction.do?page=${startPage + 10}">[다음]</a>
	</c:if>
	
	<a href="./BoardListAction.do?page=${pageCount}" style="text-decoration:none"> > </a>	

</c:if>
</center>

- JSTL 태그들을 쓰기 위해 코어 라이브러리와 국제화 라이브러리를 불러온다

- '글쓰기' 를 누르면 원문 글 작성 폼으로 가는 요청인 "/BoardForm.do" 으로 요청

- 글개수 출력은 Service 클래스 BoardListAction.java 에서 request 객체로 공유했던 listcount 를 바로 구해서 출력

- 그 페이지 화면출력번호 num 을 구함 * 아래에 설명

<데이터 뿌리기>

- Service 클래스 BoardListAction.java 에서 request 객체로 공유했던 값들을 EL 태그로 구해서 출력하고 있다

- forEach 태그를 통해서 각 데이터(행)를 출력, forEach 태그의 items 에 목록을 가지고 있는 공유된 리스트 boardlist 가 온다, 그럼 각 데이터 하나가 변수 b 에 저장됨

- 제목을 출력할때 ${b.board_subject} 로 출력, 표기법만 이럴뿐 실제 의미는 b.getBoard_Subject() 를 출력하는 것이다

- 국제화 라이브러리 formatDate 태그로 패턴을 지정해서 날짜 ${b.board_date} 를 원하는 포맷으로 출력

<댓글 관련>

- if 태그를 써서 ${b.board_re_lev} > 0 이면 즉, 댓글이면 깊이만큼 제목 앞에 공백 출력, Re. 출력

<상세페이지로 가는 링크>

- 제목에 링크를 걸어서 글 번호와 원래 페이지번호를 가져가고 있다

<페이지 메뉴바>

- forEach 태그의 begin 과 end 속성으로 공유된 값 startPage 와 endPage 를 사용함, startPage ~ endPage 까지를 페이지 메뉴바에 출력

- 현재 페이지가 아닌 경우 링크를 "/BoardListAction.do" 로 걸고 페이지번호 page 를 get 방식으로 전달, 이렇게 하면 해당 페이지의 목록을 가져와서 다시 출력함

- 이렇게 전달시 BoardListAction.java 의 이 코드에서 페이지번호 page 를 받음 (BoardListAction.java 부분)

		if(request.getParameter("page")!=null) {
			page = Integer.parseInt(request.getParameter("page"));					
		}

- < 클릭시 page 번호를 1 을 넘겨주며 "/BoardListAction.do" 로 간다

- > 클릭시 page 번호를 마지막 페이지(= 총 페이지 수) 를 넘겨주며 "/BoardListAction.do" 로 간다 (총 페이지수는 공유된 pageCount값을 가져온 것이다)

- [이전] 메뉴는 startPage > 10 인 경우, 즉 첫번째 페이지가 아닌 경우에만 출력, 링크는 현재 블럭의 startPage 에서 10 을 빼서 이전 블럭의 가장 작은 페이지로 이동

- [다음] 메뉴는 endPage < pageCount 인 경우 출력, 링크는 현재 블럭의 startPage 에서 10 을 더해서 다음 블럭의 가장 작은 페이지로 이동

ex) 1page 일떄 endPage 는 10, pageCount 는 13 이므로 [다음] 메뉴 나타남

- 11page일때 endPage 는 13(보정했었다), pageCount 도 13 이므로 만족하지 않음, [다음] 메뉴 나타나지 않는다

 

+ Model 2 게시판 프로그램 글 목록 출력 설명 : https://laker99.tistory.com/129?category=1080281


화면 출력 번호

- Service 클래스 BoardListAction.java 에서 구하지 않은 마지막 파생변수인 화면 출력 번호를 여기서 정의

	<c:set var="num" value="${listcount - (page-1) * 10 }"/>

- 3개의 기본 변수값을 사용해서 화면 출력 번호 num 을 생성, 10 은 limit 값이다

- limit 값은 고정된 값 (10) 이므로 공유설정하지 않았음

- forEach 로 각각의 행을 출력할때마다 1이 감소됨


- 페이징 처리를 보기 위해 글을 여러개 작성해서 많은 데이터를 넣자
+ BoardAddAction.java 에서 포워딩할때 Dispatcher 방식으로 바꾸고, 포워딩 페이지 설정에서 . 을 빼주면 글 작성 후 새로고침 시 같은 데이터가 다시 들어감

- 여기서 새로고침 눌러서 데이터 많이 넣기

 

- 이렇게 페이지 메뉴바에서 5 를 클릭시

- 페이지번호에 따라 startRow, endRow 가 결정되므로 가져올 글들이 달라짐


JSP Model 2 - MyBatis 게시판 프로그램 : 상세 페이지 기능

- qna_board_list.jsp 에서 제목을 클릭하면 "/BoardDetailAction.do" 로 요청하며 글 번호와 페이지 번호를 가져간다

		<a href="./BoardDetailAction.do?board_num=${b.board_num}&page=${page}">
			 ${b.board_subject}
		</a>

- 여기가 출발점임, 바로 상세페이지를 실행하면 안된다

- 상세페이지로 가거나, 수정하고 오거나, 삭제하거나 할때 원래 페이지로 돌아가기 위해서 페이지 번호를 가져감

- 하나의 글에 대한 상세 정보를 구해와야 하므로 글 번호가 필요함

 

- Controller 클래스에서 "/BoardDetailAction.do" 부분만

		// 상세 페이지	
		}else if(command.equals("/BoardDetailAction.do")) {
			try {
				action = new BoardDetailAction();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

- 상세 페이지 Service 클래스인 BoardDetailAction.java 로 이동

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardDetailAction implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("BoardDetailAction");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		
		BoardDAO dao = BoardDAO.getInstance();
//		BoardBean board = dao.updateContent(board_num);
		
		//조회수 증가
		dao.updateCount(board_num);
		BoardBean board = dao.getContent(board_num); // 상세정보 구하기
		
		request.setAttribute("board", board);
		request.setAttribute("page", page);
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/board/qna_board_view.jsp");
		
		return forward;
	}

}

- 목록페이지에서 넘어온 글 번호와 페이지 번호를 받아서 변수 board_num 와 page 에 저장

- 여기서는 2가지 작업을 해야함, 2개의 DAO 메소드를 호출해야한다

1) updateCount() : 조회수 1 증가

2) getContent() : 상세 정보 구해오기

- 이때 글번호 board_num 을 전달해야 그 글의 조회수를 증가시킬 수 있고, 상세 정보를 구할 수 있다

<DAO updateCount() 와 getContent() 에서 돌아온 후>

- 가져온 상세정보를 DTO 객체 board 로 받음

- 그 객체 board 와 페이지번호 page 를 공유설정함

+ 글 번호는 객체 board 안에 board_num 필드안에 저장되어있으므로 공유설정 따로 하지 않음

+ DTO 객체 board 가 공유되면 View 에서 ${board.필드명} 으로 결과를 출력함

<포워딩>

- 상세정보를 보여줄 View 페이지인 qna_board_view.jsp 로 포워딩 페이지 설정

+ 실제 포워딩은 Controller 로 가서 포워딩한다

 

- MemberDAO.java 에서 updateCount() 메소드 부분만

	// 조회수 증가
	public void updateCount(int board_num) throws Exception{
		SqlSession session = getSession();
		session.update("board_updatecount", board_num);
	}

- 전달받은 글 번호 board_num 을 session.update() 의 두번째 매개변수로 전달함

 

- board.xml 에서 글 개수를 구해주는 처리하는 SQL문 부분 (id 가 board_updatecontent)

	<!-- 조회수 증가 -->
	<update id="board_updatecount" parameterType="int">
     update model22 set board_readcount=board_readcount+1 where board_num = #{board_num}	
	</update>

- 글 번호를 전달받았고, parameterType 에는 전달받은 값의 자료형을 써야하므로 "int"

- 조회수 값을 1 증가시켜주는 update 를 한다, where 절에 #{board_num} 으로 넘어온 글 번호를 쓴다

 

- MemberDAO.java 에서 getContent() 메소드 부분만

	// 상세 페이지, 수정 폼
	public BoardBean getContent(int board_num) throws Exception {
		BoardBean board = new BoardBean();

		SqlSession session=getSession();
		board  = (BoardBean)session.selectOne("board_content", board_num);
		
		return board;
	}

- 1개 글에 대한 상세정보를 구해서 돌려줘야하므로 selectOne() 메소드 호출

- 두번째 매개변수로 글 번호 board_num 을 넘겨준다

- 검색되는 결과가 1개이므로 DTO 객체가 리턴자료형으로 왔다, 검색되는 결과가 여러개일땐 List 가 온다

+ 무조건 DTO 가 온다는건 Mapper 파일의 SQL문 부분에 해당함, DAO에서는 검색되는 결과가 여러개일땐 List 가 온다

 

- board.xml 에서 글 개수를 구해주는 처리하는 SQL문 부분 (id 가 board_content)

	<!-- 상세페이지, 수정폼 -->
	<select id="board_content" parameterType="int" resultType="board">
	 select * from model22 where board_num = #{board_num}
	</select>

- #{board_num} 번호값에 만족하는 데이터를 검색해서 데이터 1개에 대한 상세정보를 자동으로 DTO 객체에 매핑해서 그 DTO 객체를 리턴함

+ 테이블 컬럼명과 DTO 프로퍼티명이 일치할때만 자동 매핑 가능

- resultType 은 DTO alias 인 board

 

- 다음은 이 상세정보를 출력하는 View 페이지인 qna_board_view.jsp

- qna_board_view.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 
    
<table border=1 width=600 align=center>
	<caption>상세 페이지</caption>
	<tr>
		<td>작성자</td>
		<td>${board.board_name}</td>
	</tr>
	<tr>
		<td>조회수</td>
		<td>${board.board_readcount}</td>
	</tr>
	<tr>
		<td>날짜</td>
		<td>
			<fmt:formatDate value="${board.board_date}"
							pattern="yyyy-MM-dd H:mm"/>		
		</td>
	</tr>
	<tr>
		<td>제목</td>
		<td>${board.board_subject}</td>
	</tr>
	<tr>
		<td>내용</td>
		<td><pre>${board.board_content}</pre></td>
	</tr>
	<tr>
		<td>첨부 파일</td>
		<td>
			<c:if test="${board.board_file !=  null }">
				<a href="./board/file_down.jsp?file_name=${board.board_file}">${board.board_file}</a>
			</c:if>
		</td>
	</tr>
	<tr>
		<td colspan=2 align=center>
			<input type="button" value="댓글" 
				   onClick="location.href='./BoardReplyAction.do?board_num=${board.board_num}&page=${page}&board_re_ref=${board.board_re_ref}&board_re_lev=${board.board_re_lev}&board_re_seq=${board.board_re_seq}'">
			<input type="button" value="수정" 
				   onClick="location.href='./BoardModifyAction.do?board_num=${board.board_num}&page=${page}'">
			<input type="button" value="삭제" 
				   onClick="location.href='./BoardDeleteAction.do?board_num=${board.board_num}&page=${page}'">
			<input type="button" value="목록" 
			       onClick="location.href='./BoardListAction.do?page=${page}'">
		</td>
	</tr>
</table>

- Service 클래스 BoardDetailAction.java 에서 공유설정한 객체 board 와 페이지번호 page 를 EL 로 가져와서 활용

ex) 조회수는 ${board.board_readcount} 로 가져와서 출력하고 있다

- if 태그에 ${board.board_file != null} 로, 만약 첨부파일이 있다면 첨부파일명을 출력

- 첨부파일명을 클리하면 file_down.jsp 로 이동하며 다운이 된다

- 내용은 pre 태그로 감싸서 출력해야 줄이 바뀌어서 출력됨

- 상세페이지에 아래에 출력될 버튼을 만들고 링크를 걸었다

'목록' 버튼 클릭시

- 목록을 가져오는 요청인 "/BoardListAction.do" 로 요청, 이때 페이지번호 page 를 전달

+ 페이지 번호 page는 BoardDetailAction.java 에서 공유설정했던 값을 가져왔다

'댓글' 버튼 클릭시

- 댓글을 다는 폼으로 가기 위해 "/BoardReplyAction.do" 로 요청, 이때 5가지의 값을 가져가고 있다

- 부모글이 될 현재 글의 글 번호, 현재 페이지 번호 뿐 아니라 부모글이 될 현재 글의 ref, lev, seq 값도 가져간다

- 만약 글 번호, 페이지 번호만 넘길때는 Service클래스로 가서 부모글에 대한 정보(ref,lev,seq)를 DB 에서 가져와야한다

- 여기선 5가지 값을 다 가져가므로 Service 클래스로 갈 필요 없이 바로 댓글 작성 폼 View 페이지로 가도 된다 

- '수정' 버튼 / '삭제' 버튼 

 


JSP Model 2 - MyBatis 게시판 프로그램 : 댓글 작성 기능

- qna_board_view 에서 출력된 버튼 중 '댓글' 버튼을 클릭시 "/BoardReplyAction.do" 로 요청

<input type="button" value="댓글" 
		onClick="location.href='./BoardReplyAction.do?board_num=${board.board_num}&page=${page}&board_re_ref=${board.board_re_ref}&board_re_lev=${board.board_re_lev}&board_re_seq=${board.board_re_seq}'">

- "/BoardReplyAction.do" 로 가면서 글번호, 페이지번호 뿐 아니라 부모글에 대한 정보인 board_re_ref, board_re_lev, board_re_seq 도 전달

- 그러므로 Service 클래스로 가서 이 정보를 가져오기 위해 DB 연동할 필요 없이 바로 댓글 작성 폼으로 이동 가능

- Service 클래스로 안가고 바로 댓글 작성 폼으로 이동한다

 

- Controller 클래스에서 "/BoardReplyAction.do" 부분만

		// 댓글 폼	
		}else if(command.equals("/BoardReplyAction.do")) {
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/board/qna_board_reply.jsp");
		}

- 즉 댓글 작성 폼인 View 페이지 qna_board_reply.jsp 로 이동한다

- Model 2 게시판 프로그램 작성시에는 댓글작성폼으로 갈때 2개의 값만 전달하고 나머지는 DB와 연동해서 부모글의 정보를 구하는 작업을 선행했었다, 검색어 (게시판 프로그램 : 댓글 작성폼 Service 클래스)

- 여기서는 부모글의 정보도 qna_board_view 에서 전달하므로 DB연동 필요없이 바로 View페이지로 가고 있다

 

- qna_board_reply.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"%>

<html>
<head>
	<title>댓글 게시판</title>
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script src="<%=request.getContextPath() %>/board/script.js"></script>
</head>
<body>

<form action="<%=request.getContextPath() %>/BoardReply.do" method="post">
<input type="hidden" name="board_num" value="${param.board_num}">
<input type="hidden" name="page" value="${param.page}">
<input type="hidden" name="board_re_ref" value="${param.board_re_ref}">
<input type="hidden" name="board_re_lev" value="${param.board_re_lev}">
<input type="hidden" name="board_re_seq" value="${param.board_re_seq}">

<table cellpadding="0" cellspacing="0" align=center border=1>
	<tr align="center" valign="middle">
		<td colspan="5">댓글 게시판</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">글쓴이</div>
		</td>
		<td>
			<input name="board_name" id="board_name" type="text" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">비밀번호</div>
		</td>
		<td>
			<input name="board_pass" id="board_pass" type="password" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">제 목</div>
		</td>
		<td>
			<input name="board_subject" id="board_subject" type="text" size="50" maxlength="100" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12">
			<div align="center">내 용</div>
		</td>
		<td>
			<textarea name="board_content" id="board_content" cols="67" rows="15"></textarea>
		</td>
	</tr>	
	<tr bgcolor="cccccc">
		<td colspan="2" style="height:1px;">
		</td>
	</tr>
	<tr><td colspan="2">&nbsp;</td></tr>
	<tr align="center" valign="middle">
		<td colspan="5">			
			<input type=submit value="댓글">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

- qna_board_view 에서 get 방식으로 전달된 값을 param EL 내장객체로 가져오고 있다

ex) ${param.board_re_ref} 는 request.getParamter("board_re_ref") 를 의미

- get 방식으로 전달된 5개의 값을 qna_board_reply 의 form 에서 "/BoardReply.do" 로 요청하며 hidden 을 통해서 전달

- 댓글을 작성하고 '댓글' 버튼을 누르면 "/BoardReply.do"로 요청

 

- Controller 클래스에서 "/BoardReply.do" 부분만

		// 댓글	
		}else if(command.equals("/BoardReply.do")) {
			try {
				action = new BoardReply();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

 

- 댓글 작성 Service 클래스인 BoardReply.java 로 이동

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardReply implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("BoardReply");
		
		request.setCharacterEncoding("utf-8");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		int board_re_ref = Integer.parseInt(request.getParameter("board_re_ref"));
		int board_re_lev = Integer.parseInt(request.getParameter("board_re_lev"));
		int board_re_seq = Integer.parseInt(request.getParameter("board_re_seq"));
		String page = request.getParameter("page");
		
		BoardBean board = new BoardBean();
		board.setBoard_re_ref(board_re_ref);
		board.setBoard_re_seq(board_re_seq);
		
		BoardDAO dao = BoardDAO.getInstance();
		dao.updateSeq(board); 	// board_re_seq값 증가 		
		
		board.setBoard_re_seq(board_re_seq+1);
		board.setBoard_re_lev(board_re_lev+1);		
		board.setBoard_name(request.getParameter("board_name"));
		board.setBoard_pass(request.getParameter("board_pass"));
		board.setBoard_subject(request.getParameter("board_subject"));
		board.setBoard_content(request.getParameter("board_content"));
					
		dao.boardReply(board); // 댓글 작성 : insert SQL		
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/BoardDetailAction.do?board_num="+board_num+"&page="+page); // 상세페이지로 이동
//		forward.setPath("/BoardListAction.do?page="+page); // 목록페이지로 이동
		
		return forward;
	}

}

- 댓글 작성 폼에서 한글값이 넘어올 수 있으므로 한글값 인코딩

- hidden 으로 넘어오는 5개의 값을 request.getParameter() 로 받아서 변수에 저장

- 또한 사용자가 직접 댓글 작성폼에 작성한 정보 4가지도 받는다

- updateSeq() 메소드에서 부모글의 board_re_ref, board_re_seq 값이 필요하므로 생성한 DTO 객체 board 에 부모글의 board_re_ref, board_re_seq  를 저장하고 그 객체 board 를 updateSeq() 의 매개변수로 전달

<DAO updateSeq() 메소드로부터 돌아온 뒤>

- 댓글을 작성해야하므로 insert 될 데이터를 DTO 객체 board 에 세팅

- 이때 board_re_seq 값과 board_re_lev 값은 부모보다 1 증가된 값으로 세팅

- 또한 사용자가 직접 입력한 4가지 정보도 세팅하고 DAO의 boardReply() 메소드 호출, 매개변수로는 객체 board 를 넘김

<DAO boardReply() 메소드로부터 돌아온 뒤>

- 댓글을 단 이후 상세페이지로 넘어갈땐, "/BoardDetailAction.do" 로 포워딩 페이지 지정하며 글번호와 페이지번호를 전달

+ BoardDetailAction.java 에서 글번호와 페이지번호를 getParameter() 로 받는 작업을 가장 먼저하므로 두 값이 필요

- 댓글을 단 이후 목록페이지로 넘어갈땐, "/BoardListAction.do" 로 포워딩 페이지 지정하며 페이지 번호를 전달

 

이 Service 클래스에서 2번의 DAO 메소드를 실행해야함

1) updateSeq() 메소드 : update SQL문

- update 문을 수행하기 위해 부모글의 ref 값과 seq 값을 전달

- 댓글의 ref 는 부모글의 ref 값과 같은값을 넣고, 부모글의 seq 보다 큰 글들의 seq 를 +1 함

2) boardReply() 메소드 : insert SQL문 

- 댓글을 DB에 insert 함

- Mapper 파일로 처리해야하므로 두 메소드의 SQL문을 하나의 메소드에서 실행 불가, 메소드를 따로 작성 해야함

 

- MemberDAO.java 에서 댓글 출력순서를 조절하기 위한 updateSeq() 부분

	// 댓글 출력 순서 (board_re_seq값 증가)
	public void updateSeq(BoardBean board) {
		SqlSession session = getSession();
		session.update("board_updateseq", board);
	}

- 부모글의 board_re_ref 와 board_re_seq 값을 저장한 객체 board 를 두번째 매개변수로 전달한다

 

- board.xml 에서 원문 글 작성 처리하는 SQL문 부분 (id 가 board_updateseq)

	<!-- 댓글 출력순서 -->
	<update id="board_updateseq" parameterType="board">
	 update model22 set board_re_seq=board_re_seq+1 
	  where board_re_ref = #{board_re_ref} and board_re_seq &gt; #{board_re_seq}
	</update>

- 부모글과 board_re_ref 값이 같으면서 , 부모글보다 board_re_seq 값이 큰 글의 board_re_seq 값을 1 증가시켜줘야함

의미

1) 부모가 원문이면 그 원문의 모든 댓글의 seq 값을 1 씩 증가

2) 부모가 댓글이면 그 부모보다 먼저 달린 형제 댓글들과 그 형제 댓글들의 대댓글들의 seq 값을 1씩 증가

 

- MemberDAO.java 에서 댓글을 삽입하는 boardReply() 부분

	// 댓글작성
	public void boardReply(BoardBean board) {
		SqlSession session = getSession();
		session.insert("board_reply", board);
	}

 

- board.xml 에서 원문 글 작성 처리하는 SQL문 부분 (id 가 board_reply)

	<!-- 댓글 작성 -->
	<insert id="board_reply" parameterType="board">
	 insert into model22 values(model22_seq.nextval,#{board_name},
	 #{board_pass},#{board_subject},#{board_content},
	 #{board_file,jdbcType=VARCHAR},#{board_re_ref},#{board_re_lev},#{board_re_seq},0,sysdate)
	</insert>

- board_num 은 원문이든 댓글이든 모두 sequence 로 들어간다

- 댓글은 첨부파일을 올리지 못하므로 첨부파일명을 저장하는 컬럼 board_file 이 null 이 되어야함

- MyBatis 는 null 값을 허용하지 않는다

- jdbcType=VARCHAR 속성값을 추가하면 null 값을 허용하게 됨 

- 이미 BoardReply.java 에서 부모의 board_re_seq, board_re_lev 에서 1 증가시켜서 board 객체의 board_re_seq 와 board_re_lev에 세팅했으므로 여기선 그대로 가져와서 삽입

- 조회수 컬럼엔 0, 날짜 컬럼엔 sysdate 로 현재 날짜와 시간을 넣어줌

 

 


JSP Model 2 - MyBatis 게시판 프로그램 : 글 수정폼 기능

- qna_board_view 에서 출력된 버튼 중 '수정' 버튼을 클릭시 "/BoardModifyAcion.do" 로 요청

<input type="button" value="수정" 
	onClick="location.href='./BoardModifyAction.do?board_num=${board.board_num}&page=${page}'">

- "/BoardModifyAcion.do" 로 가면서 글번호, 페이지번호 전달

- 이 글번호, 페이지번호는 목록 페이지 -> 상세페이지 -> 수정폼 -> 수정 까지 넘어간다

- 먼저 상세정보를 구해와서 그걸 수정폼에 뿌려야한다

 

- Controller 클래스에서 "/BoardModifyAcion.do" 부분만

		// 수정 폼	
		}else if(command.equals("/BoardModifyAction.do")) {
			try {
				action = new BoardModifyAction();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

- 수정폼에 뿌릴 상세 정보를 가져오는 Service 클래스 BoardModifyAction.java

- BoardModifyAction.java

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardModifyAction implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("BoardModifyAction");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		
		BoardDAO dao = BoardDAO.getInstance();
		BoardBean board = dao.getContent(board_num);
				
		request.setAttribute("board", board);
		request.setAttribute("page", page);
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/board/qna_board_modify.jsp");
		
		return forward;
	}

}

- 상세 페이지 qna_board_view.jsp 에서 get 방식으로 넘어온 글 번호와 페이지 번호를 받아서 변수에 저장

- DAO의 getContent() 메소드로 글 하나에 대한 상세정보를 가져온다, 매개변수로는 글 번호를 넘겨준다

- getContent() 에서 구해온 상세정보를 board 객체에 저장 한다

- board 객체를 공유설정하고 페이지 번호인 page 변수도 request 객체로 공유설정함

+ View인 수정폼에서 가져와서 뿌려줄 수 있도록 공유설정해야한다, DTO 객체가 공유되었다면  View 에서 ${공유되는네임값.필드명} 으로 사용

- 포워딩 페이지는 qna_board_modify.jsp 로 지정한다

 

- MemberDAO.java 에서 getContent() 메소드 부분만 

	// 상세 페이지, 수정 폼
	public BoardBean getContent(int board_num) throws Exception {
		BoardBean board = new BoardBean();

		SqlSession session=getSession();
		board  = (BoardBean)session.selectOne("board_content", board_num);
		
		return board;
	}

- 두번째 매개변수로 글 번호 board_num 전달

 

- board.xml 에서 글 개수를 구해주는 처리하는 SQL문 부분 (id 가 board_content)

	<!-- 상세페이지, 수정폼 -->
	<select id="board_content" parameterType="int" resultType="board">
	 select * from model22 where board_num = #{board_num}
	</select>

- DTO 객체에 상세정보를 매핑해서 리턴해줌

 

- 포워딩되는 페이지이자 수정폼 View 페이지인 qna_board_modify.jsp 를 보자

- qna_board_modify.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"%>

<html>
<head>
	<title>게시판 수정</title>
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script src="<%=request.getContextPath() %>/board/script.js"></script>
</head>
<body>

<form action="<%=request.getContextPath() %>/BoardModify.do" method="post">
<input type="hidden" name="board_num" value="${board.board_num}">
<input type="hidden" name="page" value="${page}">

<table cellpadding="0" cellspacing="0" align=center border=1>
	<tr align="center" valign="middle">
		<td colspan="5">게시판 수정</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">글쓴이</div>
		</td>
		<td>
			<input name="board_name" id="board_name" type="text" size="10" maxlength="10" 
				value="${board.board_name }"/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">비밀번호</div>
		</td>
		<td>
			<input name="board_pass" id="board_pass" type="password" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">제 목</div>
		</td>
		<td>
			<input name="board_subject" id="board_subject" type="text" size="50" maxlength="100" 
				value="${board.board_subject}"/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12">
			<div align="center">내 용</div>
		</td>
		<td>
			<textarea name="board_content" id="board_content" cols="67" rows="15">${board.board_content}</textarea>
		</td>
	</tr>	
	<tr bgcolor="cccccc">
		<td colspan="2" style="height:1px;">
		</td>
	</tr>
	<tr><td colspan="2">&nbsp;</td></tr>
	<tr align="center" valign="middle">
		<td colspan="5">			
			<input type=submit value="수정">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

+ 첨부파일을 수정하는 기능은 빠져있다

- Service 클래스인 BoardModifyAction.java 에서 공유설정했던 DTO 객체 board 의 값을 ${board.필드명} 으로 가져와서 출력하고 있다

ex) ${board.subject} 으로 해당 글의 제목을 가져오고 있다

- 공유설정했던 DTO 객체 board에서 글 번호인 ${board.board_num} 을 hidden 으로 "/BoardModify.do" 로 넘겨준다

- 또한 수정이 끝난 후 원래 페이지로 돌아가기 위해 공유설정된 페이지번호 ${page} 를 hidden 으로 "/BoardModify.do" 로 넘겨준다

- 즉 글 번호와 페이지 번호를 글 수정을 하는 Service 클래스로 넘기고 있음

 

 


JSP Model 2 - MyBatis 게시판 프로그램 : 글 수정 기능

- 수정폼 qna_board_modify.jsp 에서 '수정' 을 누르면 form 을 통해 "/BoardModify.do" 로 요청한다 

- hidden 으로 글 번호와 페이지 번호가 넘어왔음

- 이 글번호, 페이지번호는 목록 페이지 -> 상세페이지 -> 수정폼 -> 수정 까지 넘어간다

- 또한 수정폼에서 사용자가 입력한 값들도 넘어왔다

 

- Controller 클래스에서 "/BoardModify.do" 부분만

		// 수정	
		}else if(command.equals("/BoardModify.do")) {
			try {
				action = new BoardModify();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

- 수정을 하는 Service 클래스인 BoardModify.java

package service;

import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardModify implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("BoardModify");
		
		response.setContentType("text/html; charset=utf-8");
		request.setCharacterEncoding("utf-8");
		
		PrintWriter out = response.getWriter();
		
		String page = request.getParameter("page");
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String board_pass = request.getParameter("board_pass");
		
		BoardBean board = new BoardBean();
		board.setBoard_num(board_num);
		board.setBoard_name(request.getParameter("board_name"));
		board.setBoard_subject(request.getParameter("board_subject"));
		board.setBoard_content(request.getParameter("board_content"));
		
		BoardDAO dao = BoardDAO.getInstance();
		BoardBean old = dao.getContent(board_num);
		
		if(old.getBoard_pass().equals(board_pass)) {//비번 일치시
			dao.update(board);
			
		}else {	// 비번 불일치시
			
			out.println("<script>");
			out.println("alert('비밀번호가 일치하지 않습니다.');");
			out.println("history.go(-1);");
			out.println("</script>");
			out.close();
			
			return null;
		}		
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/BoardDetailAction.do?board_num="+board_num+"&page="+page);
		
		return forward;
	}

}

- hidden 으로 넘어온 글 번호와 페이지 번호를 받아서 변수에 저장한다

- 수정을 하기 위해 필요한 정보들을 저장한 DTO 객체 board 에 세팅

- DAO의 getContent() 메소드를 호출해서 매개변수로 글번호를 전달, 데이터의 상세정보를 구해와서 객체 old 에 저장

- old.getBoard_pass() 인 DB속 비밀번호와 사용자가 수정폼에 입력한 비밀번호 board_pass 가 일치시 글을 수정하는 DAO의 update() 메소드를 호출해서 매개변수로 객체 board 를 넘겨줌

- 비밀번호가 일치하지 않았다면 이전페이지인 수정폼으로 돌아감

- 비밀번호 일치 후 수정을 성공적으로 완료했다면 상세페이지로 가도록 "/BoardDetailAction.do" 로 요청하며 글 번호, 페이지 번호를 넘긴다

 

- MemberDAO.java 에서 getContent() 메소드 부분만 

	// 상세 페이지, 수정 폼
	public BoardBean getContent(int board_num) throws Exception {
		BoardBean board = new BoardBean();

		SqlSession session=getSession();
		board  = (BoardBean)session.selectOne("board_content", board_num);
		
		return board;
	}

 

- board.xml 에서 글 개수를 구해주는 처리하는 SQL문 부분 (id 가 board_content)

	<!-- 상세페이지, 수정폼 -->
	<select id="board_content" parameterType="int" resultType="board">
	 select * from model22 where board_num = #{board_num}
	</select>

 

- MemberDAO.java 에서 update() 메소드 부분만 

	// 글수정
	public void update(BoardBean board) throws Exception {
		SqlSession session=getSession();
		session.update("board_update", board);
	}

- session.update() 는 수정한 글 개수를 자동으로 반환해주지만 여기서는 리턴받지 않았다, select 가 아니면 리턴받을 필요 없음

 

- board.xml 에서 글 개수를 구해주는 처리하는 SQL문 부분 (id 가 board_update)

	<!-- 글수정 -->
	<update id="board_update" parameterType="board">
	 update model22 set board_name=#{board_name}, board_subject=#{board_subject},
	 board_content=#{board_content} where board_num=#{board_num}
	</update>

- 객체 board 가 넘어왔으므로 paramterType 은 DTO alias 인 "board"

- update SQL문을 사용해서 넘어온 DTO 객체의 값들을 #{필드명} 으로 가져옴

ex) #{board_name} 은 board.getBoard_Name() 을 의미

 


JSP Model 2 - MyBatis 게시판 프로그램 : 글 삭제폼 / 글 삭제 기능

- qna_board_view 에서 출력된 버튼 중 '삭제' 버튼을 클릭시 "/BoardDeleteAcion.do" 로 요청

<input type="button" value="삭제" 
	onClick="location.href='./BoardDeleteAction.do?board_num=${board.board_num}&page=${page}'">

- "/BoardDeleteAcion.do" 로 가면서 글번호, 페이지번호 전달

- 이 글번호, 페이지번호는 목록 페이지 -> 상세페이지 -> 삭제폼 -> 삭제 까지 넘어간다

- 먼저 삭제폼으로 가게 된다

 

- Controller 클래스에서 "/BoardDeleteAcion.do" 부분만

		// 삭제 폼	
		}else if(command.equals("/BoardDeleteAction.do")) {
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/board/qna_board_delete.jsp");
		}

- 삭제 폼에서는 DB 접속이 필요하지 않으므로 Service 클래스가 아닌 삭제폼 qna_board_delete.jsp 로 바로 포워딩 설정 후 Controller 클래스 하단에서 포워딩

 

- qna_board_delete.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"%>

<html>
<head>
	<title>게시판 삭제</title>
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script src="<%=request.getContextPath() %>/board/script.js"></script>
</head>
<body>

<form action="<%=request.getContextPath() %>/BoardDelete.do" method="post">
<input type="hidden" name="board_num" value="${param.board_num}">
<input type="hidden" name="page" value="${param.page}">

<table width=300 cellpadding="0" cellspacing="0" align=center border=1>
	<tr align="center" valign="middle">
		<td colspan="5">게시판 삭제</td>
	</tr>	
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">비밀번호</div>
		</td>
		<td>
			<input name="board_pass" id="board_pass" type="password" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>	
	<tr bgcolor="cccccc">
		<td colspan="2" style="height:1px;">
		</td>
	</tr>
	<tr><td colspan="2">&nbsp;</td></tr>
	<tr align="center" valign="middle">
		<td colspan="5">			
			<input type=submit value="삭제">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

- EL 내장객체 param 으로 상세 페이지 qna_board_view.jsp 에서 get 방식으로 넘어온 글 번호와 페이지 번호를 받음

- 받은 글 번호와 페이지번호를 바로 form의 hidden 을 통해 "/BoardDelete.do" 로 전달함

+ 원하는 글 1개를 삭제하기 위해 반드시 글 번호를 전달해야함, 그리고 삭제 후 원래 페이지로 돌아가기 위해 페이지 번호 전달

- 그리고 사용자가 삭제폼에 입력한 비밀번호도 "/BoardDelete.do" 전달

 

- Controller 클래스에서 "/BoardDelete.do" 부분만

		// 삭제	
		}else if(command.equals("/BoardDelete.do")) {
			try {
				action = new BoardDelete();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

- 글 삭제 Service 클래스 BoardDelete.java

package service;

import java.io.File;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardDelete implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("BoardDelete");
		
		response.setContentType("text/html; charset=utf-8"); 
		request.setCharacterEncoding("utf-8");
		
		PrintWriter out = response.getWriter();
		
		String path = request.getRealPath("boardupload");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		String board_pass  = request.getParameter("board_pass");
				
		BoardDAO dao = BoardDAO.getInstance();
		BoardBean old = dao.getContent(board_num); // 상세정보 구하기
		
		if(old.getBoard_pass().equals(board_pass)) { //비번 일치시
			dao.delete(board_num); // delete SQL
			
			if(old.getBoard_file() != null) { // 첨부파일이 있으면
				
				File file = new File(path);
				file.mkdirs();
				
				File[] f = file.listFiles();
				for(int i=0; i<f.length; i++) {
					if(f[i].getName().equals(old.getBoard_file())) {
						f[i].delete();
					}
				}				
			}
			
		}else {	// 비번 불일치시
	
			out.println("<script>");
			out.println("alert('비밀번호가 일치하지 않습니다.');");
			out.println("history.go(-1);");
			out.println("</script>");
			out.close();

			return null;			
		}
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/BoardListAction.do?page="+page);
		
		return forward;
	}

}

- qna_board_delete.jsp 에서 hidden 으로 넘어온 글 번호, 페이지 번호와 사용자가 삭제폼에 입력한 비밀번호를 받아서 변수에 저장한다

- DAO의 getContent() 메소드를 호출, 매개변수로 글 번호를 전달해서 해당 글의 상세정보를 구해온다

- 구해온 상세 정보에서 비밀번호를 가져와서 사용자가 삭제폼에 입력한 비밀번호와 비교

- 비번이 일치시 DAO의 delete() 메소드를 호출해서 글을 삭제함, 글 삭제 이후 첨부파일도 지워준다 * 아래에 설명

- 비번이 불일치시 위에서 생성한 out 객체로 alert 창을 출력하고 이전 페이지인 삭제폼으로 돌아감

- 비번이 일치하고 삭제 성공시 목록페이지로 가기 위해 포워딩 페이지를 목록을 가져오는 요청인 "/BoardListAction.do" 로 지정하고 페이지 번호 전달

 

이 Service 클래스에서 DAO의 메소드 2개를 호출해야한다

1) getContent() : 비번을 가져와서 비교하기 위해 호출

2) delete() : 글 삭제를 위해 호출

 

첨부파일 삭제 (BoardDelete.java 부분)

String path = request.getRealPath("boardupload");

if(old.getBoard_file() != null) { // 첨부파일이 있으면
				
				File file = new File(path);
				file.mkdirs();
				
				File[] f = file.listFiles();
				for(int i=0; i<f.length; i++) {
					if(f[i].getName().equals(old.getBoard_file())) {
						f[i].delete();
					}
				}				
			}

- "boardupload" 폴더의 경로로 File 객체 file 을 구한 후 listFiles() 로 File 배열 f 에 모든 파일을 저장

- 배열에 들은 파일명을 getName() 으로 하나씩 가져와서 DB에서 삭제한 글의 첨부파일명과 일치하면 그 첨부파일 삭제

 

- Model 2 게시판 프로그램에서 첨부파일 삭제 설명하는 부분 : https://laker99.tistory.com/131?category=1080281 

- Model 2 게시판 프로그램에서 첨부파일 삭제 설명하는 부분 검색어 : 첨부파일 지우기 (BoardDelete.java 부분)

 

- MemberDAO.java 에서 getContent() 메소드 부분만 

	// 상세 페이지, 수정 폼
	public BoardBean getContent(int board_num) throws Exception {
		BoardBean board = new BoardBean();

		SqlSession session=getSession();
		board  = (BoardBean)session.selectOne("board_content", board_num);
		
		return board;
	}

 

- board.xml 에서 글 개수를 구해주는 처리하는 SQL문 부분 (id 가 board_content)

	<!-- 상세페이지, 수정폼 -->
	<select id="board_content" parameterType="int" resultType="board">
	 select * from model22 where board_num = #{board_num}
	</select>

 

- MemberDAO.java 에서 delete() 메소드 부분만 

	// 글삭제
	public void delete(int board_num) {
		SqlSession session=getSession();
		session.delete("board_delete", board_num);
	}

- 글 삭제를 위해 글 번호를 넘겨준다

 

- board.xml 에서 글 개수를 구해주는 처리하는 SQL문 부분 (id 가 board_delete)

	<!-- 글삭제 -->
	<delete id="board_delete" parameterType="int">
	 delete from model22 where board_num=#{board_num}
	</delete>

 

- 글을 삭제해보자

 

- 삭제폼으로 갈땐 Service 클래스로 가지 않으므로 콘솔창에 Service 클래스로 간 흔적이 없다

- 삭제 되었음, 후에 목록을 가져오고 목록페이지로 간다

 

복습

Maven

- pom.xml 에 의존 라이브러리 추가시 원격 저장소에서 로컬 저장소로 다운받음

- Spring Project 에서도 로컬 저장소(레포지토리) 는 똑같은 폴더를 쓴다

 

+ Spring

- Spring Project 도 이와 비슷한 폴더 구조로 만들어진다

- Spring Project 에서는 생성 시 부터 pom.xml 에 기본적인 라이브러리들이 들어있다

 

MyBatis 환경설정 파일

- 마켓플레이스에서 플러그인 다운받으면 생성할 수 있었지만 현재는 불가능

- 한개의 파일만 있으면 된다

- 들어가는 내용

1) Alias 설정

2) DB 연동

3) Mapper 파일 불러오기

 

Mapper 파일

- DAO 클래스 안의 SQL문이 들어있는 파일

- 마켓플레이스에서 플러그인 다운받으면 생성 가능

- 검색되는 컬럼명과 DTO 필드명이 다를때는 resultMap 태그로 매핑을 직접 잡아준다

 


JSP Model 2 - MyBatis 연동 : 회원가입 프로그램

Model 2 과 MyBatis 연동

- 클라우드에서 Model 2 과 MyBatis 가 이미 연동이 완료된 회원가입 프로그램 mybatismember 프로젝트 import

- zip 파일을 압축 풀어서 import

- import 하는 프로젝트는 Maven 프로젝트이다

- Maven 프로젝트이므로 구조는 다음과 같다

- src/main/java 안에 확장자가 java 인 파일들이 저장되어있다 (Controller, Service, DAO, DTO)

+ Model 2 이므로 src/main/java 안에서도 DTO, DAO, Service, Controller 가 서로 다른 패키지 안에 들어가있음

- src/main/resources 안에 DB 연동에 필요한 MyBatis 환경설정 파일 등이 들어가 있다

- src/main/webapp 안에 View 파일 등이 들어간다

 

- import한 mybatismember 프로젝트의 pom.xml 파일을 보자

- pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.myhome</groupId>
	<artifactId>mybatismember</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>mybatismember Maven Webapp</name>
	<url>http://maven.apache.org</url>

	<!-- 복사 시작 -->
	<repositories>
		<repository>
			<id>codelds</id>
			<url>https://code.lds.org/nexus/content/groups/main-repo</url>
		</repository>
	</repositories>

	<dependencies>

		<!-- 오라클 JDBC Library -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>

		<!-- MySQL -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.47</version>
		</dependency>

		<!-- iBatis -->
		<dependency>
			<groupId>org.apache.ibatis</groupId>
			<artifactId>ibatis-sqlmap</artifactId>
			<version>2.3.4.726</version>
		</dependency>

		<!-- mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.3</version>
		</dependency>		

		<!-- jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.taglibs</groupId>
			<artifactId>taglibs-standard-impl</artifactId>
			<version>1.2.5</version>
		</dependency>

		<!-- cos -->
		<dependency>
			<groupId>servlets.com</groupId>
			<artifactId>cos</artifactId>
			<version>05Nov2002</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>mybatismember</finalName>
	</build>
</project>

- 프로젝트 시작 시 가장 먼저 라이브러리를 구하는 작업을 해야한다

- dependencies 태그 안에 의존 라이브러리를 dependency 태그로 추가

- 오라클 JDBC, MySQL JDBC, iBatis, MyBatis 3.5.3 ver, JSTL, cos 라이브러리가 추가되었다

- 오라클 공식 저장소에서 다운받는 건 오류 많이 발생

- 오라클은 비공식 저장소 repository 를 원격 저장소 위치로 등록하고 거기서 오라클 JDBC 라이브러리를 불러오기

- MyBatis 연동시에는 반드시 MyBatis 라이브러리가 추가되어 있어야함

 

 

Model 2 에 MyBatis 연동시 구조

- Model 2 에서 MyBatis 를 연동

- Controller -> Service -> DAO -> MyBatis 와 연동해서 DB 연동

 

- db.properties

jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:xe
jdbc.username=totoro
jdbc.password=totoro123

- 여기 db.properties 에 적힌 이 계정으로 접근해서 테이블을 생성해야한다

 

- MyBatis 환경설정 파일인 mybatis-config.xml 파일을 보자

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="db.properties" />
	<typeAliases>
		<typeAlias type="model.MemberDTO" alias="member"></typeAlias>
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="member.xml" />
	</mappers>
</configuration>

 

- Mapper 파일 member.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="mymember">

	<!-- 회원가입 -->
	<insert id="insert" parameterType="member">
	  insert into member0609 values(#{id},#{passwd},#{name},#{jumin1},#{jumin2},
	  #{mailid},#{domain},#{tel1},#{tel2},#{tel3},#{phone1},#{phone2},#{phone3},
	  #{post},#{address},#{gender},#{hobby},#{intro},sysdate)
	</insert>
	
	<!-- ID중복검사, 회원인증 -->
	<select id="idcheck" parameterType="String" resultType="member">
	 select * from member0609 where id = #{id}
	</select>
	
	<!-- 회원정보 수정 -->
	<update id="update" parameterType="member">
	  update member0609 set name=#{name}, jumin1=#{jumin1}, jumin2=#{jumin2}, 
	  mailid=#{mailid}, domain=#{domain}, tel1=#{tel1}, tel2=#{tel2}, tel3=#{tel3},
	  phone1=#{phone1}, phone2=#{phone2}, phone3=#{phone3}, post=#{post}, address=#{address},
	  gender=#{gender}, hobby=#{hobby}, intro=#{intro} where id = #{id}
	</update>

	<!-- 회원 삭제 -->
	<delete id="delete" parameterType="String">
	  delete from member0609 where id = #{id}
	</delete> 

</mapper>

 

 


 

- db.properties 파일에서 totoro 계정으로 설정되어있으므로 totoro 계정에서 테이블을 생성해야함

+ import 시에 테이블이 생성되지는 않음

- sql 폴더의 model2member.sql 에서 totoro 계정으로 연결한 후 member0609 테이블을 생성하자

- 커넥션 프로파일 설정

- model2member.sql

-- 회원관리
select * from tab;
select * from member0609;

create table member0609(
	id varchar2(20) primary key,
	passwd  varchar2(20) not null,
	name varchar2(20) not null,
	jumin1 varchar2(6) not null,
	jumin2 varchar2(7) not null,
	mailid varchar2(30), 
	domain varchar2(30), 
	tel1 varchar2(5),
	tel2 varchar2(5),
	tel3 varchar2(5),
	phone1 varchar2(5),
	phone2 varchar2(5),
	phone3 varchar2(5),
	post varchar2(10),
	address varchar2(200),
	gender varchar2(20),
	hobby varchar2(50),
	intro varchar2(2000),
	register timestamp );

- 테이블 member0609 를 생성하자

- 테이블의 필드명과 DTO 클래스의 멤버변수(필드, 프로퍼티) 명을 같은 이름으로 설정해야 매핑이 자동으로 일어난다


JSP Model 2 - MyBatis 회원관리 프로그램 : 기본 파일들

- index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<!-- 모델2 회원관리 -->

<script>
	location.href="./LoginForm.do";
</script>

</body>
</html>

- index.jsp 실행이나 현재 프로젝트 Run As - Run on Server 로 실행시키자

- 로그인 폼이 나타난다

- '회원가입' 을 눌러 회원가입 폼으로 가서 가입을 시키자

- 이제 로그인 페이지로 넘어간다, 아이디, 비번이 일치하면 로그인 성공한다

- 정보수정을 해보자

- 비번이 일치할때 수정된다!

 

Model 2 흐름

Model 2 요청할때 흐름

- View 에서 Controller 로 찾아갈때 WebServlet 어노테이션의 패턴을 맞춰서 찾아간다

- Controller 에서 해당 Service 클래스로 넘긴다, Service 클래스에서 DAO 의 메소드를 불러서 DB와 연동

 

Model 2 돌아올때 흐름

- DAO 클래스에서 검색한 결과를 Service 로 넘기면 Service 에서 결과를 공유설정한다

- 다시 Controller 클래스로 돌아와서 View 페이지로 포워딩됨

 

- src/main/java 에서 model 패키지의 DTO 클래스인 MemberDTO.java 를 보자

// DTO(Data Transfer Object)

package model;

import java.sql.Timestamp;

public class MemberDTO {
	private String id;
	private String passwd;
	private String name;
	private String jumin1;
	private String jumin2;
	private String mailid;
	private String domain;
	private String tel1;
	private String tel2;
	private String tel3;
	private String phone1;
	private String phone2;
	private String phone3;
	private String post;
	private String address;
	private String gender;
	private String hobby;
	private String intro;
	private Timestamp register;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPasswd() {
		return passwd;
	}
	public void setPasswd(String passwd) {
		this.passwd = passwd;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getJumin1() {
		return jumin1;
	}
	public void setJumin1(String jumin1) {
		this.jumin1 = jumin1;
	}
	public String getJumin2() {
		return jumin2;
	}
	public void setJumin2(String jumin2) {
		this.jumin2 = jumin2;
	}
	public String getMailid() {
		return mailid;
	}
	public void setMailid(String mailid) {
		this.mailid = mailid;
	}
	public String getDomain() {
		return domain;
	}
	public void setDomain(String domain) {
		this.domain = domain;
	}
	public String getTel1() {
		return tel1;
	}
	public void setTel1(String tel1) {
		this.tel1 = tel1;
	}
	public String getTel2() {
		return tel2;
	}
	public void setTel2(String tel2) {
		this.tel2 = tel2;
	}
	public String getTel3() {
		return tel3;
	}
	public void setTel3(String tel3) {
		this.tel3 = tel3;
	}
	public String getPhone1() {
		return phone1;
	}
	public void setPhone1(String phone1) {
		this.phone1 = phone1;
	}
	public String getPhone2() {
		return phone2;
	}
	public void setPhone2(String phone2) {
		this.phone2 = phone2;
	}
	public String getPhone3() {
		return phone3;
	}
	public void setPhone3(String phone3) {
		this.phone3 = phone3;
	}
	public String getPost() {
		return post;
	}
	public void setPost(String post) {
		this.post = post;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public String getHobby() {
		return hobby;
	}
	public void setHobby(String hobby) {
		this.hobby = hobby;
	}
	public String getIntro() {
		return intro;
	}
	public void setIntro(String intro) {
		this.intro = intro;
	}
	public Timestamp getRegister() {
		return register;
	}
	public void setRegister(Timestamp register) {
		this.register = register;
	}

}

- 테이블의 필드명과 DTO 클래스의 멤버변수(필드, 프로퍼티) 명을 같은 이름으로 설정해야 매핑이 자동으로 일어난다

- 프로퍼티명이 컬럼명과 동일하게 되어있다, 쉽게 매핑 가능해짐


- Controller 클래스 MemberController.java 를 보자

package controller;

import java.io.IOException;

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;

import service.Action;
import service.ActionForward;
import service.Delete;
import service.Idcheck;
import service.Login;
import service.MemberInsert;
import service.Update;
import service.UpdateMember;

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

	public void doProcess(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		String requestURI = request.getRequestURI();
		String contextPath = request.getContextPath();               
		String command = requestURI.substring(contextPath.length());  
		
		System.out.println("requestURI:"+requestURI);    // requestURI: /model2member/MemberInsert.do
		System.out.println("contextPath:"+contextPath);  // contextPath: /model2member
		System.out.println("command:"+command);          // command: /MemberInsert.do
		
		Action action = null;
		ActionForward forward = null;
		
		// 회원 가입
		if(command.equals("/MemberInsert.do")) {
			try {
				action = new MemberInsert();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
			
		// 회원가입 폼	
		}else if(command.equals("/MemberForm.do")) {
			forward = new ActionForward();
			forward.setRedirect(true);
			forward.setPath("./member/memberform.jsp");
			
		// ID중복 검사(ajax)	
		}else if(command.equals("/Idcheck.do")) {
			try {
				action = new Idcheck();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
			
		// 로그인(회원인증)	
		}else if(command.equals("/Login.do")) {
			try {
				action = new Login();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
			
		// 로그인 폼	
		}else if(command.equals("/LoginForm.do")) {
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/member/loginform.jsp");	
			
		// 로그 아웃	
		}else if(command.equals("/Logout.do")) {
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/member/logout.jsp");
			
		// 회원정보 수정폼	
		}else if(command.equals("/UpdateMember.do")) {
			try {
				action = new UpdateMember();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
			
		// 회원정보 수정	
		}else if(command.equals("/Update.do")) {
			try {
				action = new Update();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		
		// 회원탈퇴 폼	
		}else if(command.equals("/DeleteMember.do")) {
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/member/deleteform.jsp");
			
		// 회원탈퇴	
		}else if(command.equals("/Delete.do")) {
			try {
				action = new Delete();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
		
		// 포워딩 처리
		if(forward != null) {
			if(forward.isRedirect()) {		// redirect 방식으로 포워딩
				response.sendRedirect(forward.getPath());
			}else {							// dispatcher 방식으로 포워딩
				RequestDispatcher dispatcher = 
						request.getRequestDispatcher(forward.getPath());
				dispatcher.forward(request, response);
			}
		}		
		
	}// doProcess() end	
	
	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		System.out.println("get");
	
		doProcess(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		System.out.println("post");
		
		doProcess(request, response);		
	}

}

- 사용자가 get 방식 요청시 doGet() 이 자동실행되고, post 방식 요청시 doPost() 가 자동 실행됨

- 공통적인 작업을 하기 위한 doProcess() 메소드

 

Controller 클래스로 찾아가는 2가지 방법

1. WebServlet 어노테이션 패턴을 지정하고, 맞는 패턴으로 찾아가는 방법

- 현재 Model 2

2. Spring 에서는 web.xml 파일 안에서 Controller 클래스 이름, 위치가 등록되고, 매핑을 통해 찾아감

- Model 2 도 예전에 이방법을 사용했었다

 


- 다음은 DAO 클래스 MemberDAO.java 를 보자

// DAO(Data Access Object)

package dao;

import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import model.MemberDTO;

public class MemberDAO {
	
	private static MemberDAO instance = new MemberDAO();
	
	public static MemberDAO getInstance() {
		return instance;
	}
	
	public SqlSession getSession() {
		SqlSession session=null;
		Reader reader=null;			
		try {
			reader = Resources.getResourceAsReader("mybatis-config.xml");
			SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(reader);
			session = sf.openSession(true);	 // auto commit	
		}catch(Exception e) {
			e.printStackTrace();
		}		
		return session;
	}
		
		
	// 회원가입
	public int insert(MemberDTO member) throws Exception{
		int result=0;
		SqlSession session = getSession();
		result = session.insert("insert", member);		
		System.out.println("result:"+result);
		
		return result;
	}
	
	// ID중복검사
	public int idcheck(String id) throws Exception{
		int result = 0;
		SqlSession session = getSession();
		MemberDTO member = session.selectOne("idcheck", id);
		if(member != null) {	// 중복 ID	
			result = 1;			
		}else {					// 사용가능한 ID
			result = -1;
		}
		
		return result;
	}
	
	// 로그인(회원인증)
	public int memberAuth(String id, String passwd) throws Exception {
		int result = 0;
		SqlSession session = getSession();
		MemberDTO member = session.selectOne("idcheck", id);
		if(member != null) {		// 중복 ID	
			if(member.getPasswd().equals(passwd)) {
				result = 1;			// 회원인증 성공
			}else {
				result = -1;		// 회원인증 실패
			}
		}

		return result;
	}

	
	// 회원 1명 정보 구하기 : 수정폼, 수정, 삭제
	public MemberDTO getMember(String id) throws Exception{
		SqlSession session = getSession();
		MemberDTO member = session.selectOne("idcheck", id);
		
		return member;
	}
	
	
	// 회원정보 수정
	public int update(MemberDTO member) throws Exception{
		int result = 0;
		SqlSession session = getSession();
		result = session.update("update", member);

		return result;
	}

	// 회원 탈퇴
	public int delete(String id) throws Exception{
		int result = 0;
		SqlSession session = getSession();
		result = session.delete("delete", id);		
		
		return result;
	}	

}

- MyBatis 와 연동하면 DAO 이후부터가 달라지는 내용이다

- SQL문이 Mapper xml 파일로 빠지고, 그 중간에 MyBatis 환경설정 파일이 있음

 

SqlSession 객체를 구해주는 메소드 (MemberDAO.java 부분)

 

	public SqlSession getSession() {
		SqlSession session=null;
		Reader reader=null;			
		try {
			reader = Resources.getResourceAsReader("mybatis-config.xml");
			SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(reader);
			session = sf.openSession(true);	 // auto commit	
		}catch(Exception e) {
			e.printStackTrace();
		}		
		return session;
	}

- MyBatis 환경설정 파일을 읽어와서 Reader 객체를 구하고, SqlSessionFactory 객체를 구하고, 세션을 openSession 으로 열어서 그 세션을 반환

- SqlSession 객체가 만들어지면, SqlSession 가 제공하는 메소드 5개를 사용할 수 있다

- Maven 에 추가했던 MyBatis 라이브러리의 클래스와 인터페이스들을 DAO 에서 import 해야한다


- MyBatis 환경설정 파일인 mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="db.properties" />
	<typeAliases>
		<typeAlias type="model.MemberDTO" alias="member"></typeAlias>
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="member.xml" />
	</mappers>
</configuration>

- property 태그 : 멤버변수를 property 라고 한다, 이 변수 "driver", "url" 등이 모두 프로퍼티 명임, Spring 의 DI 개념

- "driver" 변수에는 오라클과 연동하므로 오라클용 JDBC 드라이버 위치가 저장된 변수값을 사용

 

- ${jdbc.driverClassName} 등은 db.properties 파일에 있는 변수임

+ db.properties

jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:xe
jdbc.username=totoro
jdbc.password=totoro123

 

- mappers 태그 안에서 Mapper 파일을 불러온다

- 같은 폴더 내에 있으므로 이름인 member.xml 만으로 불러온다

- 테이블이 늘어나면 Mapper 파일이 늘어나고, 이 mappers 태그 안의 코드도 늘어남

- 이 과정을 통해 MyBatis 환경 설정파일은 Mapper 파일을 불러오고, DAO 는 MyBatis 환경 설정 파일을 불러오므로 DAO 에서 Mapper 파일의 id 를 사용 가능하다


- 다음은 Mapper 파일인 member.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="mymember">

	<!-- 회원가입 -->
	<insert id="insert" parameterType="member">
	  insert into member0609 values(#{id},#{passwd},#{name},#{jumin1},#{jumin2},
	  #{mailid},#{domain},#{tel1},#{tel2},#{tel3},#{phone1},#{phone2},#{phone3},
	  #{post},#{address},#{gender},#{hobby},#{intro},sysdate)
	</insert>
	
	<!-- ID중복검사, 회원인증 -->
	<select id="idcheck" parameterType="String" resultType="member">
	 select * from member0609 where id = #{id}
	</select>
	
	<!-- 회원정보 수정 -->
	<update id="update" parameterType="member">
	  update member0609 set name=#{name}, jumin1=#{jumin1}, jumin2=#{jumin2}, 
	  mailid=#{mailid}, domain=#{domain}, tel1=#{tel1}, tel2=#{tel2}, tel3=#{tel3},
	  phone1=#{phone1}, phone2=#{phone2}, phone3=#{phone3}, post=#{post}, address=#{address},
	  gender=#{gender}, hobby=#{hobby}, intro=#{intro} where id = #{id}
	</update>

	<!-- 회원 삭제 -->
	<delete id="delete" parameterType="String">
	  delete from member0609 where id = #{id}
	</delete> 

</mapper>

- MyBatis 환경설정 파일에서 이 Mapper 파일을 불러올때 "mapper" 태그 안에서 불러왔으므로 이 Mapper 파일의 루트 엘리먼트도 "mapper" 이어야함

- namespace 값을 써서 다른 Mapper 파일 내에 id 중복 문제를 피한다

- 넘어오는 값의 자료형을 parameterType 에 쓰고, 돌려주는 값의 자료형을 returnType 에 쓴다


JSP Model 2 - MyBatis 연동 구조



JSP Model 2 - MyBatis 회원관리 프로그램 : 회원가입 기능

흐름 설명

1. index.jsp 에서 "/LoginForm.do" 로 요청

2. 어노테이션 패턴에 맞으므로 Controller 클래스로 찾아감, doGet() -> doProcess() 실행됨

- index.jsp 에서 location.href 로 Controller 로 찾아왔다, 폼이 아니므로 Get 방식 전송

3. command 변수에 저장된 내용을 통해 ActionForward 객체 forward 생성 후 포워딩 페이지, 방식 설정 후 밑에서 포워딩

- loginForm.jsp 로 포워딩 됨

- DB 연동안하므로 Service 클래스가 아닌 폼으로 감

4. 로그인 폼인 loginForm.jsp 로 넘어와서 '회원가입' 버튼을 누르면 "/MemberForm.do" 로 요청, 즉 회원가입 폼으로 간다

5.어노테이션 패턴에 맞으므로 Controller 클래스로 찾아감, doGet() -> doProcess() 실행됨

- loginForm.jsp 에서 location.href 로 Controller 찾아왔다, 폼이 아니므로 Get 방식 전송

6. command 변수에 저장된 내용을 통해 ActionForward 객체 forward 생성 후 포워딩 페이지, 방식 설정 후 밑에서 포워딩

- memberForm.jsp 로 포워딩 됨

7. 회원가입 폼인 memberForm.jsp 로 넘어와서 정보 입력 후 '회원가입' 버튼을 누르면 "/MemberInsert.do" 로 요청

- form에서 post 방식으로 전송

8. 어노테이션 패턴에 맞으므로 Controller 클래스로 찾아감. doPost() -> doProcess() 실행됨

9. command 변수에 저장된 내용을 통해 회원가입을 처리해주는 Service 클래스인 MemberInsert.java 로 보내줌

10.MemberInsert.java 에서 DTO 객체생성 후 회원가입 폼에서 입력된 값들을 모두 DTO 객체에 저장

- Spring 에서는 직접 DTO 객체 생성하고,Setter 메소드로 일일히 저장하는 작업 하지 않는다, 어노테이션으로 처리함

11. DAO 객체를 생성 후 DAO 의 insert() 메소드 호출하고, 매개변수로는 값을 설정한 DTO 객체를 전달

12. DAO 의 insert() 메소드안에서 SqlSession 객체를 받고, session.insert("insert',member) 로 id 가 insert 인 Mapper 파일의 SQL문 호출

- 삽입할 데이터를 전달해줘야 하므로, 전달할 값인 DTO 객체 member 를 두번째 매개변수에 써서 전달

13. Mapper 파일 member.xml 에서 id 가 insert 인 태그 안 SQL문 실행 후 삽입된 데이터 개수 1개를 돌려줌

14. DAO 에서 result 로 그 삽입된 데이터 개수 1개를 받아서 Service 클래스로 리턴함

15. Service 에서 - Service 클래스 하단에서 포워딩 방식, 포워딩할 페이지 지정

- Dispatcher 방식, loginform.jsp 페이지로 포워딩할 것

16. Service 클래스를 호출한 Controller 클래스로 돌아간다

17. Controller 클래스에서 forward != null 이므로 설정된 곳으로 포워딩함

- Dispatcher 방식, loginform.jsp 페이지로 포워딩 한다

 

코드 및 설명

- Controller 클래스에서 "/MemberForm.do" 부분만

		// 회원가입 폼	
		}else if(command.equals("/MemberForm.do")) {
			forward = new ActionForward();
			forward.setRedirect(true);
			forward.setPath("./member/memberform.jsp");
		}

- 회원가입 폼 memberform.jsp 는 길어서 코드 붙이지 않았다 * 나중에 ID중복검사 기능 부분 설명때 붙일 것

- Controller 클래스에서 "/MemberInsert.do" 부분만

		// 회원 가입
		if(command.equals("/MemberInsert.do")) {
			try {
				action = new MemberInsert();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

- 회원가입 Service 클래스인 MemberInsert.java

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.MemberDAO;
import model.MemberDTO;

public class MemberInsert implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("MemberInsert");
		
		request.setCharacterEncoding("utf-8");
		
		MemberDTO member = new MemberDTO();
		member.setId(request.getParameter("id"));
		member.setPasswd(request.getParameter("passwd"));
		member.setName(request.getParameter("name"));
		member.setJumin1(request.getParameter("jumin1"));
		member.setJumin2(request.getParameter("jumin2"));
		member.setMailid(request.getParameter("mailid"));
		member.setDomain(request.getParameter("domain"));
		member.setTel1(request.getParameter("tel1"));
		member.setTel2(request.getParameter("tel2"));
		member.setTel3(request.getParameter("tel3"));
		member.setPhone1(request.getParameter("phone1"));
		member.setPhone2(request.getParameter("phone2"));
		member.setPhone3(request.getParameter("phone3"));
		member.setPost(request.getParameter("post"));
		member.setAddress(request.getParameter("address"));
		member.setGender(request.getParameter("gender"));
		
		String h = "";
		String[] h1 = request.getParameterValues("hobby");
		for(String h2 : h1) {
			h += h2+"-";			// 공부-게임-
		}
		member.setHobby(h);
		
		member.setIntro(request.getParameter("intro"));
		
		MemberDAO dao = MemberDAO.getInstance();
		int result = dao.insert(member);
		if(result == 1) {
			System.out.println("회원가입 성공");
		}
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/member/loginform.jsp");
		
		return forward;
	}

}

 

- MemberDAO.java 에서 insert() 메소드 부분만

	// 회원가입
	public int insert(MemberDTO member) throws Exception{
		int result=0;
		SqlSession session = getSession();
		result = session.insert("insert", member);		
		System.out.println("result:"+result);
		
		return result;
	}

- Maven 에 추가했던 MyBatis 라이브러리의 클래스와 인터페이스들을 DAO 에서 import 해야한다

- session.insert("아이디", 전달할 값) 로 Mapper 파일의 SQL문을 불러오고 있다

- session.insert() 실행 후 자동으로 삽입 성공 데이터 개수 (1) 를 돌려준다, 그걸 result 변수에 받아서 리턴하고 있음

- DB 연동을 수행하므로 예외처리를 해야한다

- try - catch 로 직접 예외처리를 해도 되고, 메소드를 호출한 곳에 throws 로 예외를 던져도 된다

 

- member.xml 에서 회원 가입을 처리하는 SQL문 부분 (id 가 insert)

	<!-- 회원가입 -->
	<insert id="insert" parameterType="member">
	  insert into member0609 values(#{id},#{passwd},#{name},#{jumin1},#{jumin2},
	  #{mailid},#{domain},#{tel1},#{tel2},#{tel3},#{phone1},#{phone2},#{phone3},
	  #{post},#{address},#{gender},#{hobby},#{intro},sysdate)
	</insert>

- parameterType 속성값인 "member"는 MyBatis 환경설정 파일에서 Alias 값으로 지정한 별칭이다

- #{mailid} 의 의미는 member.getMailid()

 

+ member.xml 의 namespace

- Mapper 파일은 테이블 수에 비례해서 늘어나므로 namespace 가 있어야 서로 다른 파일에서 id 중복이 되더라도 충돌이 일어나지 않음

 

- 회원가입 시도시 콘솔창

- 회원가입이 성공적으로 되었음


JSP Model 2 - MyBatis 회원관리 프로그램 : 아이디 중복검사 기능

코드와 흐름 같이 설명

- ajax 로 처리

- 회원가입 폼인 memberform.jsp 파일을 보자

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

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 가입 폼</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>

<script src="http://dmaps.daum.net/map_js_init/postcode.v2.js"></script>
<script>
	function openDaumPostcode() {
		new daum.Postcode({
			oncomplete : function(data) {
				// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
				// 우편번호와 주소 정보를 해당 필드에 넣고, 커서를 상세주소 필드로 이동한다.
//				document.getElementById('join_zip1').value = data.postcode1;
//				document.getElementById('join_zip2').value = data.postcode2;
				document.getElementById('post').value = data.zonecode;
				document.getElementById('address').value = data.address;
				
			}
		}).open();
	}
</script>


<!-- 외부 자바스크립트 파일 불러오기 -->
<script src="<%=request.getContextPath() %>/member/member.js"></script>

</head>
<body>

<form method="post" action="<%=request.getContextPath() %>/MemberInsert.do"> 
<table border=1 width=500 align=center>
	<caption>회원 가입</caption>
	<tr><td>ID</td>
		<td><input type=text autofocus="autofocus" id="id" name="id">
			<input type=button value="ID중복검사" id="idcheck">
			<div id="myid"></div>
		</td>
	</tr>
	<tr><td>비밀번호</td>
		<td><input type=password id="passwd" name="passwd"></td>
	</tr>
	<tr><td>성명</td>
		<td><input type=text id="name" name="name"></td>
	</tr>
	<tr><td>주민번호</td>
		<td><input type=text size=6 maxlength=6 id="jumin1" name="jumin1">-
			<input type=text size=7 maxlength=7 id="jumin2" name="jumin2">
		</td>
	</tr>
	<tr><td>E-Mail</td>
		<td><input type=text size=10 id="mailid" name="mailid">@
		    <input type=text size=10 id="domain" name="domain">
		    <select id="email">
		    	<option value="">직접입력</option>
		    	<option value="naver.com">네이버</option>
		    	<option value="daum.net">다음</option>
		    	<option value="nate.com">네이트</option>
		    	<option value="gmail.com">gmail</option>
		    </select>		    
		 </td>
	</tr>
	<tr><td>전화번호</td>
		<td><input type=text size=4 id="tel1" name="tel1" maxlength=4>-
			<input type=text size=4 id="tel2" name="tel2" maxlength=4>-
			<input type=text size=4 id="tel3" name="tel3" maxlength=4>
		</td>
	</tr>
	<tr><td>핸드폰</td>
		<td><select id="phone1" name="phone1">
				<option value="">번호선택</option>
				<option value="010">010</option>
				<option value="011">011</option>
				<option value="016">016</option>
				<option value="018">018</option>
				<option value="019">019</option>
			</select>-
			<input type=text size=4 id="phone2" name="phone2" maxlength=4>-
			<input type=text size=4 id="phone3" name="phone3" maxlength=4>
		</td>
	</tr>
	<tr><td>우편번호</td>
		<td><input type=text size=5 id="post" name="post">
			<input type=button value="우편번호검색" 
			       onClick="openDaumPostcode()">
		</td>
	</tr>
	<tr><td>주소</td>
		<td><input type=text size=45 id="address" name="address"></td>
	</tr>
	<tr><td>성별</td>
		<td>
			<input type=radio id="male" name="gender" value="남자">남자
			<input type=radio id="female" name="gender" value="여자">여자
		</td>
	</tr>
	<tr><td>취미</td>
		<td>
			<input type="checkbox" id="h1" name="hobby" value="공부" checked>공부
			<input type="checkbox" id="h2" name="hobby" value="게임">게임
			<input type="checkbox" id="h3" name="hobby" value="등산">등산
			<input type="checkbox" id="h4" name="hobby" value="낚시">낚시
			<input type="checkbox" id="h5" name="hobby" value="쇼핑">쇼핑
		</td>
	</tr>	
	<tr><td>자기소개</td>
		<td>
			<textarea id="intro" name="intro" rows="5" cols="50" placeholder="자기소개를 100자 이내로 입력하세요"></textarea>
		</td>
	</tr>
	<tr><td colspan=2 align=center>
			<input type=submit value="회원가입">
			<input type=reset value="취소">
		</td>
	</tr>		
</table>
</form>


</body>
</html>

- ID 중복검사 부분의 버튼의 id 는 "idcheck", 메세지를 출력할 div 태그의 id 값은 "myid", ID 입력양식의 id 는 "id"

- member.jsp 파일을 외부 자바 스크립트 파일로 불러온다

 

- member.jsp 파일에서 ID중복검사 부분을 보자

$(document).ready(function(){	
	
	// ID 중복검사
	$("#idcheck").click(function(){
		if($("#id").val()==""){
			alert("ID를 입력하세요");
			$("#id").focus();
			return false;
		}else{
			
			var id = $("#id").val();	
			
			$.ajax({
				type:"post",
				url:"/mybatismember/Idcheck.do",
				data:{"id":id},
				datatype:"text",
				success:function(data){
//					alert(data);
					
					if(data==1){	// 중복 ID
						$("#myid").text("중복ID");
						$("#id").val("").focus();
					}else{			// 사용 가능한 ID
						$("#myid").text("사용 가능한 ID");
						$("#passwd").focus();
					}					
				}
			});			
		}		
	});
})

- jQuery 로 버튼을 가장 먼저 구해오고, click 함수처리 + click 대신 keyup 이벤트로 처리해도 된다

- 입력양식에 값이 입력되지 않아씅면 경고창 띄우기

- 값이 입력되었을때 그 값을 가져와서 변수 id 에 저장

- $.ajax() 를 통해 "Idcheck.do" 로 요청

- 패키지가 다르므로 패키지명도 앞에 써준다

- 값을 가져갈때는 json 형태로 값을 가져간다 "id" 속성명에 id 값을 가져감

- 콜백함수로 데이터를 돌려받음, 돌려받은 뒤 1이 리턴되면 중복 ID 로 처리함, 리턴되는 값이 1 이 아니면 사용가능한 ID로 처리

 

- Controller 클래스로 간다

- Controller 클래스에서 "/Idcheck.do" 부분만

		// ID중복 검사(ajax)	
		}else if(command.equals("/Idcheck.do")) {
			try {
				action = new Idcheck();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

- 여기서 Service 클래스인 Idcheck.java 파일로 보내준다

- Idcheck.java

package service;

import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.MemberDAO;

public class Idcheck implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("Idcheck");
		
		request.setCharacterEncoding("utf-8");
		
		PrintWriter out = response.getWriter();
		
		String id = request.getParameter("id");
		System.out.println("id:"+ id);
		
		MemberDAO dao = MemberDAO.getInstance();
		int result = dao.idcheck(id);
		System.out.println("result:"+ result);	// 1 :  중복ID, -1 : 사용가능한 ID
		out.println(result);	// 브라우저에 출력되는 값이 callback함수로 리턴된다.
		
		return null;
	}

}

- member.js 에서 ajax 로 전달한 사용자가 입력한 id 값을 속성명 "id" 를 사용해서 request.getParameter("id") 로 받음

- DB와 연동하여 중복 ID 인지 아닌지 확인해야하므로 DAO 객체 생성 후 DAO의 idcheck() 메소드 호출

- idcheck() 메소드 호출 시 매개변수로 사용자가 입력한 id 값을 전달함

- idcheck() 에서 리턴된 값을 result 로 받음, 이때 중복 ID는 1, 사용가능한 ID는 -1 로 전달해주기로 함

- 콜백함수로 리턴되는 값은 브라우저에 출력되는 값이므로 out 객체로 result 값을 출력

+ System.out.println() 은 콘솔에 출력되는 값, out.println() 은 브라우저에 출력되는 값

 

- MemberDAO.java 에서 idcheck() 메소드 부분만

	// ID중복검사
	public int idcheck(String id) throws Exception{
		int result = 0;
		SqlSession session = getSession();
		MemberDTO member = session.selectOne("idcheck", id);
		if(member != null) {	// 중복 ID	
			result = 1;			
		}else {					// 사용가능한 ID
			result = -1;
		}
		
		return result;
	}

- 매개변수로 사용자가 입력한 id 값을 전달받음

- SqlSession 객체를 변수 session 으로 받아오고 session.selectOne() 메소드를 호출하면서 Mapper 파일에서 id 가 "idcheck" 인 태그의 SQL문 호출

- 그 SQL문을 호출하면서 사용자가 입력한 id 를 넘김

- 사용자가 입력한 id 에 해당하는 검색되는 결과가 1개이므로 selectOne() 메소드로 검색해야함

- selectOne() 을 호출 후 DTO 객체 member 를 반환함, 그 객체가 있으면 DB에 이미 같은 id 인 데이터가 있다는 의미이므로 1 리턴

- 받아온 객체가 null 이면 같은 id 인 데이터가 없다는 의미이므로 사용가능한 id 라는 의미로 -1 리턴

 

  - member.xml 에서 ID중복검사를 처리하는 SQL문 부분 (id 가 idcheck)

	<!-- ID중복검사, 회원인증 -->
	<select id="idcheck" parameterType="String" resultType="member">
	 select * from member0609 where id = #{id}
	</select>

- 전달되는 값은 id 이므로, 전달되는 값의 자료형인 String 을 parameterType 으로 쓴다

+ parameterType 속성값인 "member"는 MyBatis 환경설정 파일에서 Alias 값으로 지정한 별칭이다

- 돌려주는 데이터 타입은 DTO 이므로, resultType 에 member 를 쓴다

- SQL문에서 #{id} 는 전달되는 값의 변수명인 id 를 써준다

 

- 중복 아닌 ID 입력 후 'ID중복검사' 눌렀을때 콘솔 창

- 중복 ID 입력 후 'ID중복검사' 눌렀을때 콘솔 창


JSP Model 2 - MyBatis 회원관리 프로그램 : 로그인 기능

흐름 설명

1. index.jsp 에서 "/LoginForm.do" 로 요청

2. 어노테이션 패턴에 맞으므로 Controller 클래스로 찾아감, doGet() -> doProcess() 실행됨

- index.jsp 에서 location.href 로 Controller 로 찾아왔다, 폼이 아니므로 Get 방식 전송

3. command 변수에 저장된 내용을 통해 ActionForward 객체 forward 생성 후 포워딩 페이지, 방식 설정 후 밑에서 포워딩

- loginForm.jsp 로 포워딩 됨

- DB 연동안하므로 Service 클래스가 아닌 폼으로 감

4. 로그인 폼인 loginForm.jsp 로 넘어와서 아이디, 비번 입력 후 '로그인' 클릭시 form 태그에서 "/Login.do" 로 요청

- 사용자가 입력한 아이디, 비번 값도 전달함

- post 방식으로 요청했다

5. 어노테이션 패턴에 맞으므로 Controller 클래스로 찾아감, doPost() -> doProcess() 실행됨

- loginForm.jsp 에서 form 에서 post 방식으로 Controller 로 찾아왔다

+ 이땐 ActionForward 객체 forward 가 null 이므로 포워딩 코드에 접근하지 않음

6. command 변수에 저장된 내용을 통해 로그인(회원 인증)을 처리해주는 Service 클래스인 Login.java 로 보내줌

7. Login.java 에서 로그인 성공시 필요한 session 객체와 out 객체를 구한다

8. Login.java 에서 사용자가 로그인 폼에 입력한 아이디, 비밀번호를 받아서 저장

9. 회원 인증을 처리하기 위해 DB와 연동해야하고, DAO 객체를 생성하고 DAO의 memberAuth(id, passwd) 메소드 호출

- 매개변수로 사용자가 전달한 id, passwd 를 전달

10. DAO 클래스에서 memberAuth() 에서 SqlSession 객체 session 을 구하고 session.selectOne("idcheck",id) 호출하며 Mapper 파일에서 id 가 "idcheck" 인 태그의 SQL문 호출

- 1명에 대한 데이터를 가져오므로 selectOne() 사용

- 전달하는 값은 사용자가 입력한 id 이다

11. Mapper 파일 member.xml 에서 id 가 idcheck인 태그 안 SQL문 실행 후 id 에 해당하는 1명에 대한 상세정보를 DTO 객체로 리턴함

12. DAO 클래스에서 selectOne() 실행 후 돌려받은 값을 DTO 객체 member 에 저장한다

13. 그 객체 member 가 null 이 아니면 그 id 로 검색한 데이터가 DB에 존재한다는 뜻이므로 id가 일치한다는 의미

14. 다음엔 사용자가 입력한 비번과 member 에서 가져온 DB의 비번이 각각 일치시 회원 인증 성공으로 보고 1을 리턴

- 일치하지 않으면 회원 인증 실패로 보고 -1 리턴

15. Service 클래스로 돌아와서 memberAuth() 가 1을 리턴하면 성공으로 보고, 세션으로 id 값을 공유설정하고 포워딩 방식과 포워딩 페이지 설정 

- 여기서부터 SESSION 영역 시작

- 포워딩 할 페이지는 main.jsp 로 설정

+ session 공유시엔 상관없고, Service 클래스에서 request 방식 공유를 했을땐 Dispatcher 방식으로 포워딩 설정해야만 공유된 값 사용 가능

- 로그인 실패시 메세지를 뿌리고 이전 페이지로 돌아감

16. Service 클래스를 호출한 Controller 클래스로 돌아가서, 생성되고 값이 설정된 forward 객체를 리턴함

17. Controller 클래스에서 forward != null 이므로 Controller 클래스 하단에서 포워딩할 페이지, 방식을 가져와서 main.jsp 로 Dispatcher 방식 포워딩함

- Service 하단에서 설정했던 포워딩 페이지, 방식을 여기서 구해와서 실제 포워딩하는 것

18. main.jsp 에서 세션을 구해옴, 이때 세션이 있을때와 없을때 다른 화면을 보여줌

 

- MemberDAO.java 에서 memberAuth() 메소드 부분만

	// 로그인(회원인증)
	public int memberAuth(String id, String passwd) throws Exception {
		int result = 0;
		SqlSession session = getSession();
		MemberDTO member = session.selectOne("idcheck", id);
		if(member != null) {		// 중복 ID	
			if(member.getPasswd().equals(passwd)) {
				result = 1;			// 회원인증 성공
			}else {
				result = -1;		// 회원인증 실패
			}
		}

		return result;
	}

 

 

- member.xml 에서 로그인(회원 인증) 처리하는 SQL문 부분 (id 가 idcheck)

	<!-- ID중복검사, 회원인증 -->
	<select id="idcheck" parameterType="String" resultType="member">
	 select * from member0609 where id = #{id}
	</select>

+ ID 중복검사 할때와 같은 SQL문 사용

 

- 맞는 아이디, 비밀번호 입력 후 로그인 시

 

- 마지막으로 로그인 성공 후 포워딩되는 메인 페이지인 main.jsp 를 보자

- main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%-- <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> --%> <!-- 안됨 -->


<!--  세션이 있으면 -->
<%-- <c:if test="${not empty sessionScope.id}"> --%>	<!-- 2개 모두 잘됨 -->
<c:if test="${sessionScope.id != null }">
	${sessionScope.id} 님 환영 합니다. <br><br>
	
	<a href="<%=request.getContextPath() %>/UpdateMember.do">회원정보 수정</a>  <br><br>
	
	<a href="<%=request.getContextPath() %>/Logout.do">로그아웃</a>  <br><br>
	
	<a href="<%=request.getContextPath() %>/DeleteMember.do">회원탈퇴</a> <br><br>
</c:if>

<!-- 세션이 없으면 -->
<%-- <c:if test="${empty sessionScope.id}"> --%>
<c:if test="${sessionScope.id == null}">
	<a href="<%=request.getContextPath() %>/MemberForm.do">회원가입</a> <br><br>
	<a href="<%=request.getContextPath() %>/LoginForm.do">로그인</a> <br><br>
</c:if>

- 가장 먼저 EL 의 ${sessionScope.id != null} 로 네임 "id" 로 세션에 설정된 id 값이 있는지 확인한다

+ 또는 ${not empty sessioNScope.id} 를 if 조건으로 써도 된다

- 세션 공유값이 있으면 ~님 환영합니다 화면이 드고, 세션 공유값이 없으면 회원가입, 로그인 메뉴만 띄움 

 

링크 설정, 요청 네임값

- '회원정보 수정' : "/UpdateMember.do"

- '로그아웃' : "/Logout.do"

- '회원탈퇴' : "/DeleteMember.do"

- '회원가입' : "/MemberForm.do"

- '로그인' : "/LoginForm.do"

 

- main.jsp 를 그냥 실행하면 세션값이 없기때문에 아래 화면이 나타남

- 로그인 후에 main.jsp 로 넘어오면 세션값이 있으므로 아래 화면이 나타남


JSP Model 2 - MyBatis 회원관리 프로그램 : 로그아웃 기능

코드와 흐름 같이 설명

- 메인 페이지 main.jsp 에서 '로그아웃'  클릭시 "/Logout.do" 로 요청함

- Controller 클래스로 찾아간다

 

- Controller 클래스에서 "/Logout.do" 부분만

		// 로그 아웃	
		}else if(command.equals("/Logout.do")) {
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/member/logout.jsp");
		}

- DB연동이 필요없으므로 Service 클래스로 가지 않음

- 바로 여기서 ActionForward 객체 foward 생성 후 포워딩 방식, 포워딩 페이지를 설정함

- logout.jsp 로 Dispatcher 방식으로 포워딩

 

- logout.jsp

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

<%	// 세션 삭제
	session.invalidate();
%>

<script>
	alert("로그아웃");
	location.href="./LoginForm.do";
</script>

logout.jsp 에서 해야할 일

1. 세션 삭제

2. 로그아웃 메세지 출력 후 다시 로그인 폼으로 돌아가기

- 로그인 폼으로 돌아가기 위한 요청인 "/LoginForm.do" 로 요청

 

- 그럼 다시 Controller 클래스에서 로그인 폼으로 포워딩 한다

 

로그아웃 시에는

- Controller 로 갔다가 Service 로 가지 않고 View로 바로 포워딩해서 세션을 끊고 다시 로그인 폼으로 가면 됨

 

- 로그아웃 시도

- '로그아웃' 눌렀을때 콘솔창

 


JSP Model 2 - MyBatis 회원관리 프로그램 : 수정폼 기능

코드와 흐름 같이 설명

+ 수정폼은 DB와 연동해서 상세정보를 구해와서 수정폼에 뿌려줘야함

- 메인 페이지 main.jsp 에서 '회원정보 수정'  클릭시 "/UpdateMember.do" 로 요청함

	<a href="<%=request.getContextPath() %>/UpdateMember.do">회원정보 수정</a>  <br><br>

- 이때 아무 값도 전달하지 않음, id 값은 로그인 성공 시 부터 공유설정되어있는 상태이다

- Controller 클래스로 찾아간다

 

- Controller 클래스에서 "/UpdateMember.do" 부분만

		// 회원정보 수정폼	
		}else if(command.equals("/UpdateMember.do")) {
			try {
				action = new UpdateMember();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

- Controller 클래스에서 해당 Service 클래스로 보내줌

 

- Controller 클래스 포워딩 처리 부분

		// 포워딩 처리
		if(forward != null) {
			if(forward.isRedirect()) {		// redirect 방식으로 포워딩
				response.sendRedirect(forward.getPath());
			}else {							// dispatcher 방식으로 포워딩
				RequestDispatcher dispatcher = 
						request.getRequestDispatcher(forward.getPath());
				dispatcher.forward(request, response);
			}
		}

- Service 에서 돌아온 뒤 여기서 포워딩 처리됨

 

- 수정폼 Service 클래스인 UpdateMember.java 로 이동

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import dao.MemberDAO;
import model.MemberDTO;

public class UpdateMember implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("UpdateMember");
		
		HttpSession session = request.getSession();
		String id = (String)session.getAttribute("id");
		System.out.println("id:"+id);
		
		MemberDAO dao = MemberDAO.getInstance();
		MemberDTO member = dao.getMember(id);
		System.out.println("member:"+member);
		
		String hobby = member.getHobby();	// "공부-게임-등산-"
		String[] h = hobby.split("-");
		
		request.setAttribute("member", member);				
		request.setAttribute("h", h);				
		
		ActionForward forward = new ActionForward();	
		forward.setRedirect(false);        // dispatcher 방식으로 포워딩
		forward.setPath("/member/updateform.jsp");
		
		return forward;
	}

}

<DAO 갔다오기 전>

- session 객체를 생성 후 세션값인 id 를 가장 먼저 구해온다

- getAttribuate() 로 세션값을 구해올때는 Object 형으로 리턴하므로 "id" 네임값에 들어있는 값의 자료형인 String 형으로 다운캐스팅

- DAO 객체를 생성하고 DAO의 getMember() 메소드를 호출해서 1명에 대한 상세정보를 구해온다

- getMember() 의 매개변수로는 세션으로 구해온 id 값을 전달한다

<DAO 갔다온 후>

- DAO의 getMemger(id) 로 구해온 상세정보를 DTO 객체 member 가 받는다

- 그 객체 member 의 주솟값을 찍어봄 * 아래에 캡처

- 받아온 객체 member 를 request 객체로 공유설정해야 View 페이지에서 공유값을 받아서 뿌릴 수 있다

- 문자열들을 잘라서 String 배열 h 에 저장하고 따로 공유 설정

- DTO 객체 member 는 ${member.필드명} 형태로 View 에서 출력, 배열 h 는 forEach 태그의 items 로 들어가서 View 에서 출력

- 메소드 execute() 에서도 DAO 로부터 넘어온 예외를 다시 던짐, 메소드를 호출한 Controller 클래스에서 예외를 처리한다

- ActionForward 객체 forward 생성 후 updateform.jsp 로 Dispatcher 방식으로 포워딩 할 것임을 설정

 

- MemberDAO.java 에서 getMember() 메소드 부분만

	// 회원 1명 정보 구하기 : 수정폼, 수정, 삭제
	public MemberDTO getMember(String id) throws Exception{
		SqlSession session = getSession();
		MemberDTO member = session.selectOne("idcheck", id);
		
		return member;
	}

- 수정폼뿐 아니라 수정, 삭제시에 비번을 꺼낼때도 사용됨

- 검색되는 데이터가 1개이므로 selectOne() 메소드를 사용하고 id 가 "idcheck" 인 태그의 SQL문 호출, 사용자의 id 를 전달

- 예외를 던지므로서 메소드를 호출한 곳(Service 클래스쪽) 에 양도함

+ Service 클래스의 메소드에서도 예외를 던짐, 메소드를 호출한 Controller 클래스에서 예외를 처리한다

 

- member.xml 에서 로그인(회원 인증) 처리하는 SQL문 부분 (id 가 idcheck)

	<!-- ID중복검사, 회원인증 -->
	<select id="idcheck" parameterType="String" resultType="member">
	 select * from member0609 where id = #{id}
	</select>

- 전달되는 값은 id 이므로 parameterType 을 String 으로 하고, 검색한 결과를 돌려줄때는 DTO 객체로 돌려주므로 resultType 은 member 로 한다

+ parameterType 속성값인 "member"는 MyBatis 환경설정 파일에서 Alias 값으로 지정한 별칭이다

- #{id} : selectOne() 을 호출할떄 두번째 매개변수로 넘어온 값으로 변수 id 가 넘어왔으므로 #{id} 로 그 값을 가져옴

- 검색결과를 setter 메소드로 DTO 객체에 저장시킬필요 없이 자동으로 매핑되어 DTO 객체에 저장되서 리턴된다

 

- 다음은 View 부분인 updateform.jsp 를 보자

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 수정 폼</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>

<script src="http://dmaps.daum.net/map_js_init/postcode.v2.js"></script>
<script>
	function openDaumPostcode() {
		new daum.Postcode({
			oncomplete : function(data) {
				// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
				// 우편번호와 주소 정보를 해당 필드에 넣고, 커서를 상세주소 필드로 이동한다.
//				document.getElementById('join_zip1').value = data.postcode1;
//				document.getElementById('join_zip2').value = data.postcode2;
				document.getElementById('post').value = data.zonecode;
				document.getElementById('address').value = data.address;
				
			}
		}).open();
	}
</script>


<!-- 외부 자바스크립트 파일 불러오기 -->
<script src="<%=request.getContextPath() %>/member/member.js"></script>

</head>
<body>

<form method="post" action="<%=request.getContextPath() %>/Update.do"> 
<input type="hidden" name="id" value="${member.id}">
<table border=1 width=500 align=center>
	<caption>회원 수정</caption>
	<tr><td>ID</td>
		<td>${member.id}</td>
	</tr>
	<tr><td>비밀번호</td>
		<td><input type=password id="passwd" name="passwd"></td>
	</tr>
	<tr><td>성명</td>
		<td><input type=text id="name" name="name" value="${member.name}"></td>
	</tr>
	<tr><td>주민번호</td>
		<td><input type=text size=6 maxlength=6 id="jumin1" name="jumin1" value="${member.jumin1}">-
			<input type=text size=7 maxlength=7 id="jumin2" name="jumin2" value="${member.jumin2}">
		</td>
	</tr>
	<tr><td>E-Mail</td>
		<td><input type=text size=10 id="mailid" name="mailid" value="${member.mailid}">@
		    <input type=text size=10 id="domain" name="domain" value="${member.domain}">
		    <select id="email">
		    	<option value="">직접입력</option>
		    	<option value="naver.com">네이버</option>
		    	<option value="daum.net">다음</option>
		    	<option value="nate.com">네이트</option>
		    	<option value="gmail.com">gmail</option>
		    </select>		    
		 </td>
	</tr>
	<tr><td>전화번호</td>
		<td><input type=text size=4 id="tel1" name="tel1" maxlength=4 value="${member.tel1 }">-
			<input type=text size=4 id="tel2" name="tel2" maxlength=4 value="${member.tel2 }">-
			<input type=text size=4 id="tel3" name="tel3" maxlength=4 value="${member.tel3 }">
		</td>
	</tr>
	<tr><td>핸드폰</td>
		<td><select id="phone1" name="phone1">
				<option value="">번호선택</option>
				<option value="010" <c:if test="${member.phone1 == '010' }">${'selected'}</c:if> >010</option>
				<option value="011" <c:if test="${member.phone1 == '011' }">${'selected'}</c:if> >011</option>
				<option value="016" <c:if test="${member.phone1 == '016' }">${'selected'}</c:if> >016</option>
				<option value="018" <c:if test="${member.phone1 == '018' }">${'selected'}</c:if> >018</option>
				<option value="019" <c:if test="${member.phone1 == '019' }">${'selected'}</c:if> >019</option>
			</select>-
			<input type=text size=4 id="phone2" name="phone2" maxlength=4 value="${member.phone2 }">-
			<input type=text size=4 id="phone3" name="phone3" maxlength=4 value="${member.phone3 }">
		</td>
	</tr>
	<tr><td>우편번호</td>
		<td><input type=text size=5 id="post" name="post" value="${member.post }">
			<input type=button value="우편번호검색" 
			       onClick="openDaumPostcode()">
		</td>
	</tr>
	<tr><td>주소</td>
		<td><input type=text size=45 id="address" name="address" value="${member.address }"></td>
	</tr>
	<tr><td>성별</td>
		<td>
		
		<c:if test="${member.gender == '남자' }">
			<input type=radio id="male" name="gender" value="남자" checked="checked">남자
			<input type=radio id="female" name="gender" value="여자">여자
		</c:if>
		<c:if test="${member.gender == '여자' }">
			<input type=radio id="male" name="gender" value="남자">남자
			<input type=radio id="female" name="gender" value="여자" checked="checked">여자
		</c:if>			
			
		</td>
	</tr>
	<tr><td>취미</td>
		<td>
			<input type="checkbox" id="h1" name="hobby" value="공부"
		<c:forEach var="i" items="${h}">
			<c:if test="${i=='공부'}">${'checked'}</c:if>
		</c:forEach> >공부
		
			<input type="checkbox" id="h2" name="hobby" value="게임"
		<c:forEach var="i" items="${h}">	
			<c:if test="${i=='게임'}">${'checked'}</c:if>
		</c:forEach> >게임
			
			<input type="checkbox" id="h3" name="hobby" value="등산"
		<c:forEach var="i" items="${h}">	
			<c:if test="${i=='등산'}">${'checked'}</c:if>
		</c:forEach> >등산
			
			<input type="checkbox" id="h4" name="hobby" value="낚시"
		<c:forEach var="i" items="${h}">	
			<c:if test="${i=='낚시'}">${'checked'}</c:if>
		</c:forEach> >낚시
			
			<input type="checkbox" id="h5" name="hobby" value="쇼핑"
		<c:forEach var="i" items="${h}">	
			<c:if test="${i=='쇼핑'}">${'checked'}</c:if>
		</c:forEach> >쇼핑
			
		</td>
	</tr>	
	<tr><td>자기소개</td>
		<td>
			<textarea id="intro" name="intro" rows="5" cols="50" placeholder="자기소개를 100자 이내로 입력하세요">${member.intro }</textarea>
		</td>
	</tr>
	<tr><td colspan=2 align=center>
			<input type=submit value="회원수정">
			<input type=reset value="취소">
		</td>
	</tr>		
</table>
</form>


</body>
</html>

- 공유설정된 값을 받아서 수정폼에 뿌려준다

- 공유된 객체 member 안의 필드값들은 ${member.필드명} 으로 가져온다 (updateform.jsp 부분)

	<tr><td>성명</td>
		<td><input type=text id="name" name="name" value="${member.name}"></td>
	</tr>

- 공유된 배열 h 는 forEach 태그의 items 안에 "${h}" 가 들어감 (updateform.jsp 부분)

			<input type="checkbox" id="h1" name="hobby" value="공부"
		<c:forEach var="i" items="${h}">
			<c:if test="${i=='공부'}">${'checked'}</c:if>
		</c:forEach> >공부

- id 값은 수정폼에서 입력양식이 아닌 출력만 하고 있으므로 따로 hidden 객체로 id 값을 넘겨줌 

+ 세션이 있지만 바로 전달하는게 좋다

- 수정폼에서 입력 후 '회원수정' 을 누르면 action 값으로 지정된 "/Update.do" 로 요청

 

- 메인 페이지 main.jsp 에서 '회원정보 수정' 을 눌러 수정폼 으로 들어가보자

- 상세정보를 구해와서 값들을 수정폼에 잘 뿌리고 있다

 

- '회원정보 수정' 을 눌렀을때 콘솔창

- DAO 에서 검색해서 돌려받은 객체 member 가 출력됨 (데이터가 저장된 곳의 주소)


수정폼에서 처리하기 힘든 부분 (updateform.jsp 부분)

- jQuery 를 쓰지 않으면 처리하기 힘든 부분들이다

- 수정폼에서 처기하기 힘든 부분들을 jQuery 를 쓰지 않고 어떻게 처리했는지 보자

 

1. 핸드폰 첫번째 자리

	<tr><td>핸드폰</td>
		<td><select id="phone1" name="phone1">
				<option value="">번호선택</option>
				<option value="010" <c:if test="${member.phone1 == '010' }">${'selected'}</c:if> >010</option>
				<option value="011" <c:if test="${member.phone1 == '011' }">${'selected'}</c:if> >011</option>
				<option value="016" <c:if test="${member.phone1 == '016' }">${'selected'}</c:if> >016</option>
				<option value="018" <c:if test="${member.phone1 == '018' }">${'selected'}</c:if> >018</option>
				<option value="019" <c:if test="${member.phone1 == '019' }">${'selected'}</c:if> >019</option>
			</select>-
			<input type=text size=4 id="phone2" name="phone2" maxlength=4 value="${member.phone2 }">-
			<input type=text size=4 id="phone3" name="phone3" maxlength=4 value="${member.phone3 }">
		</td>
	</tr>

- option 태그 안에 JSTL if 태그를 넣어서 특정 숫자일때 ${'selected'} 를 출력해서 선택되게 만듬

 

2. 성별 라디오 버튼

	<tr><td>성별</td>
		<td>
		
		<c:if test="${member.gender == '남자' }">
			<input type=radio id="male" name="gender" value="남자" checked="checked">남자
			<input type=radio id="female" name="gender" value="여자">여자
		</c:if>
		<c:if test="${member.gender == '여자' }">
			<input type=radio id="male" name="gender" value="남자">남자
			<input type=radio id="female" name="gender" value="여자" checked="checked">여자
		</c:if>			
			
		</td>
	</tr>

- JSTL if 태그를 넣어서 member.gender 값이 '남자' 일때 남자 라디오버튼에 checked 를 만들어서 선택되게 함

- if 태그 안에 라디오 버튼 을 넣어서 조건문을 만족할때만 나타남

 

3. 취미 체크박스

	<tr><td>취미</td>
		<td>
			<input type="checkbox" id="h1" name="hobby" value="공부"
		<c:forEach var="i" items="${h}">
			<c:if test="${i=='공부'}">${'checked'}</c:if>
		</c:forEach> >공부
		
			<input type="checkbox" id="h2" name="hobby" value="게임"
		<c:forEach var="i" items="${h}">	
			<c:if test="${i=='게임'}">${'checked'}</c:if>
		</c:forEach> >게임
			
			<input type="checkbox" id="h3" name="hobby" value="등산"
		<c:forEach var="i" items="${h}">	
			<c:if test="${i=='등산'}">${'checked'}</c:if>
		</c:forEach> >등산
			
			<input type="checkbox" id="h4" name="hobby" value="낚시"
		<c:forEach var="i" items="${h}">	
			<c:if test="${i=='낚시'}">${'checked'}</c:if>
		</c:forEach> >낚시
			
			<input type="checkbox" id="h5" name="hobby" value="쇼핑"
		<c:forEach var="i" items="${h}">	
			<c:if test="${i=='쇼핑'}">${'checked'}</c:if>
		</c:forEach> >쇼핑
			
		</td>
	</tr>

- input 태그 안에 JSTL forEach 태그를 넣어서 배열 h 에서 값을 하나씩 가져와서 차레로 변수 i 에 저장

- 변수 i 가 '공부' 이면 공부 체크박스에 ${'checked'} 로 체크가 되게 함 

ex) h[0] = "공부", h[1] = "게임"

- 모든 체크박스마다 forEach 태그를 써서 일일히 찾아야한다

 

- jQuery 를 쓰지 않고 핸드폰 첫번째자리, 성별 라디오버튼, 취미 체크박스에 값을 뿌렸다



JSP Model 2 - MyBatis 회원관리 프로그램 : 회원 정보 수정 기능

코드와 흐름 같이 설명

- updateform.jsp 에서 '회원수정' 버튼을 누르면 "/Update.do" 로 요청함

- Controller 클래스로 찾아간다

 

- Controller 클래스에서 "/Update.do" 부분만

		// 회원정보 수정	
		}else if(command.equals("/Update.do")) {
			try {
				action = new Update();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

- 정보 수정 Service 클래스인 Update.java 로 이동

package service;

import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.MemberDAO;
import model.MemberDTO;

public class Update implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("Update");
		
		response.setContentType("text/html; charset=utf-8");
		request.setCharacterEncoding("utf-8");
		
		PrintWriter out = response.getWriter();
		
		MemberDTO member = new MemberDTO();
		member.setId(request.getParameter("id"));
		member.setPasswd(request.getParameter("passwd"));
		member.setName(request.getParameter("name"));
		member.setJumin1(request.getParameter("jumin1"));
		member.setJumin2(request.getParameter("jumin2"));
		member.setMailid(request.getParameter("mailid"));
		member.setDomain(request.getParameter("domain"));
		member.setTel1(request.getParameter("tel1"));
		member.setTel2(request.getParameter("tel2"));
		member.setTel3(request.getParameter("tel3"));
		member.setPhone1(request.getParameter("phone1"));
		member.setPhone2(request.getParameter("phone2"));
		member.setPhone3(request.getParameter("phone3"));
		member.setPost(request.getParameter("post"));
		member.setAddress(request.getParameter("address"));
		member.setGender(request.getParameter("gender"));
		
		String h="";
		String[] h1 = request.getParameterValues("hobby");
		for(String h2 : h1) {
			h += h2+"-";			// h = "공부-게임-등산-"
		}
		member.setHobby(h);
		member.setIntro(request.getParameter("intro"));
		
		MemberDAO dao = MemberDAO.getInstance();
		MemberDTO old = dao.getMember(member.getId());
		
		// 비번 비교
		if(old.getPasswd().equals(member.getPasswd())) { //비번 일치시
			int result = dao.update(member);
			if(result==1) System.out.println("회원수정 성공");
			
		}else {											 //비번 불일치시
			out.println("<script>");
			out.println("alert('비번이 일치하지 않습니다.');");
			out.println("history.go(-1);");
			out.println("</script>");
			out.close();
			
			return null;
		}	
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/member/main.jsp");
		
		return forward;
	}

}

- 수정폼에서 입력값들이 넘어오므로 한글 인코딩

- id 도 hidden 객체로 넘어왔고, 사용자가 입력한 비번도 넘어왔다

- DTO 객체 member 를 생성해서 setter 메소드로 id 와 사용자가 수정폼에 입력한 값들을 member 에 저장

- DAO 의 메소드를 2번 호출해야함, 비번 확인을 위해 상세정보 가져올때 1번, 수정할때 1번

- DAO 객체 생성 후 DB에 저장된 비번을 가져오기 위해 DAO 의 getMember() 메소드를 호출하면서 수정폼에서 넘겨준 id 를 매개변수로 넘겨준다

<DAO getMember() 에서 돌아온 후>

- old.getPasswd() 는 DB에 저장된 비번, member.getPasswd() 는 사용자가 입력한 비번

- 두 비번이 일치시 수정 하는 메소드인 DAO의 update() 메소드 호출, 수정할 데이터를 저장한 DTO 객체 member 를 넘김

<DAO update() 에서 돌아온 후>

- 수정 성공시 1을 돌려주기로 했으므로, 1을 리턴받으면 "회원수정 성공" 메세지를 콘솔에 출력

- 비번 일치가 되면 수정 후 if-else 문 아래 문장인 ActionForward 객체 foward 생성 후, main.jsp 로 Dispatcher 방식 으로 포워딩 페이지, 방식 지정

- 성공시 Controller 클래스로 가서 main.jsp 로 포워딩하면 아직 세션이 남아있으므로 세션이 있을때의 화면이 뜬다

- 비번이 일치하지 않으면 메세지를 출력한 후 다시 이전페이지인 수정폼으로 돌아간다

 

- MemberDAO.java 에서 getMember() 메소드 부분만

	// 회원 1명 정보 구하기 : 수정폼, 수정, 삭제
	public MemberDTO getMember(String id) throws Exception{
		SqlSession session = getSession();
		MemberDTO member = session.selectOne("idcheck", id);
		
		return member;
	}

- 1명에 대한 상세정보를 구하므로 selectOne() 메소드, id 값이 "idcheck" 인 Mapper 파일의 SQL문 호출

- 사용자가 입력한 id 값을 매개변수로 넘겨준다

- 돌려받은 객체 member 를 Service 클래스로 리턴

 

- member.xml 에서 수정 처리하는 SQL문 부분 (id 가 idcheck)

	<!-- ID중복검사, 회원인증 -->
	<select id="idcheck" parameterType="String" resultType="member">
	 select * from member0609 where id = #{id}
	</select>

- DTO 객체에 상세정보가 자동으로 저장(매핑) 되어 돌려준다

 

- MemberDAO.java 에서 update() 메소드 부분만

	// 회원정보 수정
	public int update(MemberDTO member) throws Exception{
		int result = 0;
		SqlSession session = getSession();
		result = session.update("update", member);

		return result;
	}

- SqlSession 객체를 변수 session 으로 받아오고 update SQL문을 부를 것이므로 session.update() 메소드를 호출

- Mapper 파일에서 id 가 "update" 인 태그의 SQL문 호출, 호출하며 수정할 데이터를 저장한 객체 member 를 넘김

- session.update() 가 잘 실행되면 1 을 리턴하고, 그걸 result 변수로 받아서 다시 Service 클래스로 돌려줌

 

- member.xml 에서 수정 처리하는 SQL문 부분 (id 가 update)

	<!-- 회원정보 수정 -->
	<update id="update" parameterType="member">
	  update member0609 set name=#{name}, jumin1=#{jumin1}, jumin2=#{jumin2}, 
	  mailid=#{mailid}, domain=#{domain}, tel1=#{tel1}, tel2=#{tel2}, tel3=#{tel3},
	  phone1=#{phone1}, phone2=#{phone2}, phone3=#{phone3}, post=#{post}, address=#{address},
	  gender=#{gender}, hobby=#{hobby}, intro=#{intro} where id = #{id}
	</update>

- DTO 객체 member 가 넘어왔으므로 parameterType 은 전달되는 값의 자료형인 DTO alias 값 member 를 작성

- DTO 객체가 전달될때 DTO 객체 안의 내용을 가져오는 방법 : #{} 안에 DTO 객체의 필드명을 쓴다

ex) #{name} 은 member.getName() 과 같은 의미, #{jumin1} 은 member.getJumin1() 과 같은 의미

- 수정된 데이터 개수를 자동으로 돌려준다, 성공시 1 반환


JSP Model 2 - MyBatis 회원관리 프로그램 : 회원 탈퇴 기능

코드와 흐름 같이 설명

- 탈퇴 폼 + 탈퇴 기능 한번에 설명

- DB 연동이 필요하지 않으므로 main.jsp 에서 '회원탈퇴' 클릭시 Controller 클래스로 가서 바로 View 탈퇴 폼으로 가기

	<a href="<%=request.getContextPath() %>/DeleteMember.do">회원탈퇴</a> <br><br>

- 회원 탈퇴 폼으로 가기 위해 "/DeleteMember.do" 로 요청함

- Controller 클래스로 넘어감

 

- Controller 클래스에서 "/DeleteMember.do" 부분만

		// 회원탈퇴 폼	
		}else if(command.equals("/DeleteMember.do")) {
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/member/deleteform.jsp");
		}

- DB연동이 필요하지 않으므로 Service 클래스가 아닌 View 페이지로 바로 이동

- 여기서 ActionForward 객체 forward 생성 후 포워딩 페이지, 방식 지정

- 탈퇴 폼 View 페이지인 deleteform.jsp 로 간다

 

- deleteform.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 탈퇴</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>

<!-- 외부 자바스크립트 파일 불러오기 -->
<script src="<%=request.getContextPath() %>/member/member.js"></script>

</head>
<body>

<form method="post" action="<%=request.getContextPath() %>/Delete.do"> 
<input type="hidden" name="id" value="${sessionScope.id}">
<table border=1 width=500 align=center>
	<caption>회원 탈퇴</caption>	
	<tr><td>비밀번호</td>
		<td><input type=password id="passwd" name="passwd"></td>
	</tr>	
	<tr><td colspan=2 align=center>
			<input type=submit value="회원탈퇴">
			<input type=reset value="취소">
		</td>
	</tr>		
</table>
</form>


</body>
</html>

- 아직 SESSION 영역이므로 공유설정된 세션값인 id 가 남아있다, 

- 세션 값을 ${sessionScope.id} 로 가져와서 hidden 객체를 통해 "id" 변수에 저장해서 세션 값을 "/Delete.do" 로 넘긴다

+ hidden 으로 id 값을 "/Delete.do" 로 넘기지 않아도 Delete.java 에서 공유된 세션에서 id 값을 가져와도 된다

+ 하지만 session 객체도 만들어아하고 번거로우므로 그냥 hidden 으로 비밀번호와 함께 form 으로 같이 넘기는 것

- 탈퇴를 위해선 탈퇴폼에 사용자가 비밀번호를 입력해야함

- 해당하는 회원 1명의 정보를 구하기 위해 id 값을 꼭 넘겨야함

- hidden 으로 넘어가는 id 와 입력양식으로 넘어가는 비밀번호 passwd 가 "/Delete.do" 로 요청하며 post 방식으로 넘어감

 

- Controller 클래스에서 "/Delete.do" 부분만

		// 회원탈퇴	
		}else if(command.equals("/Delete.do")) {
			try {
				action = new Delete();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}

 

- 회원 탈퇴 Service 클래스인 Delete.java 로 이동

package service;

import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import dao.MemberDAO;
import model.MemberDTO;

public class Delete implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("Delete");
		
		response.setContentType("text/html; charset=utf-8");
		request.setCharacterEncoding("utf-8");
		
		PrintWriter out = response.getWriter();
		HttpSession session = request.getSession();
		
		String id = request.getParameter("id");
		String passwd = request.getParameter("passwd");
		
		MemberDAO dao = MemberDAO.getInstance();
		MemberDTO old = dao.getMember(id);
		
		// 비번 비교
		if(old.getPasswd().equals(passwd)) {	// 비번 일치시
			int result = dao.delete(id);
			if(result == 1) System.out.println("회원삭제 성공");
			
			session.invalidate();               // 세션 삭제
			
		}else {									// 비번 불일치시
			out.println("<script>");	
			out.println("alert('비번이 일치하지 않습니다.');");	
			out.println("history.go(-1);");	
			out.println("</script>");	
			out.close();
			
			return null;			
		}
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/LoginForm.do");
		
		return forward;
	}

}

- 회원 삭제(탈퇴) 성공시에 필요한 session 객체, out 객체 생성

- hidden 으로 넘어온 id 값과, 입력양식에서 넘어온 비번 passwd 를 getParameter() 로 받아서 변수 id, passwd 에 저장

- 비번 비교를 위해 DAO의 getMember() 메소드를 호출해서 해당 id 의 회원에 대한 상세정보를 가져온다

- DAO 에서 메소드 호출을 2번 해야한다, 비번을 가져오기 위해 상세정보 가져올때 1번, 회원 삭제할때 1번

<DAO getMember() 에서 돌아온 후>

- old.getPasswd() 는 DB의 비번, passwd 는 사용자가 탈퇴폼에 입력한 비번, 두 비번 일치시 DAO 의 delete() 메소드 호출

- delete() 를 호출하며 id 값을 매개변수로 전달

<DAO update() 에서 돌아온 후>

- 1 을 리턴받으면 성공이라 여기고, "회원삭제 성공" 메세지 출력하고 세션을 삭제한다, SESSION 영역 끝

- 삭제에 성공하면 if-else 문 아래에서 forward 객체를 생성하고 "/LoginForm.do" 로 포워딩 페이지 설정, Dispatcher 방식으로 포워딩 방식 설정

-  삭제에 실패하면 메세지를 출력하고 이전 페이지인 삭제폼으로 이동, return null; 을 추가해서 execute() 를 빠져나가며 아래의 코드를 forward 설정 코드를 실행하지 않도록 함

- 이전 페이지인 삭제폼으로 이동하는데 아래의 forward 설정 코드를 실행하면 오류가 난다. 한번에 두 곳으로 갈 수 없으므로


- MemberDAO.java 에서 getMember() 메소드 부분만

	// 회원 1명 정보 구하기 : 수정폼, 수정, 삭제
	public MemberDTO getMember(String id) throws Exception{
		SqlSession session = getSession();
		MemberDTO member = session.selectOne("idcheck", id);
		
		return member;
	}

- member.xml 에서 수정 처리하는 SQL문 부분 (id 가 idcheck)

	<!-- ID중복검사, 회원인증 -->
	<select id="idcheck" parameterType="String" resultType="member">
	 select * from member0609 where id = #{id}
	</select>

- MemberDAO.java 에서 delete() 메소드 부분만

	// 회원 탈퇴
	public int delete(String id) throws Exception{
		int result = 0;
		SqlSession session = getSession();
		result = session.delete("delete", id);		
		
		return result;
	}

- delete SQL문을 사용하므로 SqlSession 객체의 메소드 delete() 를 사용해야한다

- Mapper 파일에서 id 가 "delete' 인 SQL문을 호출, 매개변수로는 삭제하고자 하는 회원의 id 값을 넘김

- session.delete() 에서 삭제 성공시 리턴하는 1을 result 변수에 저장 후 다시 Service 클래스로 1 리턴

 

- member.xml 에서 수정 처리하는 SQL문 부분 (id 가 delete)

	<!-- 회원 삭제 -->
	<delete id="delete" parameterType="String">
	  delete from member0609 where id = #{id}
	</delete>

- 삭제하고자 하는 회원의 id 값이 넘어오므로 parameterType 은 String 이어야한다

- #{id} : session.delete() 에서 두번째 매개변수로 넘어온 변수 id 의 값을 ${id} 로 가져옴

- 자동으로 삭제된 데이터 개수를 리턴해줌 (성공시 1 리턴)

 

- main.jsp 에서 '회원탈퇴' 클릭시 콘솔창 (탈퇴 폼)

 

- 탈퇴 폼에서 맞는 비밀번호를 입력하고 '회원탈퇴' 클릭시

- 탈퇴에 성공하고 로그인 폼으로 포워딩됨

- 탈퇴 폼에서 '회원탈퇴' 클릭해서 회원탈퇴에 성공 했을때 콘솔 창


JSP Model 2 - MyBatis 연동 구조



JSP Model 2 - MyBatis 연동 : 게시판 프로그램

Model 2 과 MyBatis 연동

- 클라우드에서 Model 2 과 MyBatis 가 이미 연동이 완료된 게시판 로그램 mybatisboard 프로젝트 import

- zip 파일을 압축 풀어서 import

- import 하는 프로젝트는 maven 프로젝트이다

- Maven 프로젝트이므로 구조는 다음과 같다

- src/main/java 안에 확장자가 java 인 파일들이 저장되어있다 (Controller, Service, DAO, DTO)

+ Model 2 이므로 src/main/java 안에서도 DTO, DAO, Service, Controller 가 서로 다른 패키지 안에 들어가있음

- src/main/resources 안에 DB 연동에 필요한 MyBatis 환경설정 파일 등이 들어가 있다 (주로 xml 파일들)

- src/main/webapp 안에 View 파일 등이 들어간다

- Maven 프로젝트는 WEB-INF 폴더 안에 lib 폴더가 생성되지 않는다, 대신 pom.xml 에서 라이브러리 추가함

- boardupload 는 첨부파일이 저장될 폴더이다

 

- import한 mybatismember 프로젝트의 pom.xml 파일을 보자

- pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.myhome</groupId>
	<artifactId>mybatisboard</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>ibatisboard Maven Webapp</name>
	<url>http://maven.apache.org</url>

	<!-- 복사 시작 -->
	<repositories>
		<repository>
			<id>codelds</id>
			<url>https://code.lds.org/nexus/content/groups/main-repo</url>
		</repository>
	</repositories>

	<dependencies>

		<!-- 오라클 JDBC Library -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>

		<!-- MySQL -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.47</version>
		</dependency>

		<!-- iBatis -->
		<dependency>
			<groupId>org.apache.ibatis</groupId>
			<artifactId>ibatis-sqlmap</artifactId>
			<version>2.3.4.726</version>
		</dependency>

		<!-- mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.3</version>
		</dependency>

		<!-- jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- cos -->
		<dependency>
			<groupId>servlets.com</groupId>
			<artifactId>cos</artifactId>
			<version>05Nov2002</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>mybatisboard</finalName>
	</build>
</project>

- 프로젝트 시작 시 가장 먼저 라이브러리를 구하는 작업을 해야한다

- dependencies 태그 안에 의존 라이브러리를 dependency 태그로 추가

- 오라클 JDBC, MySQL JDBC, iBatis, MyBatis 3.5.3 ver, JSTL, cos 라이브러리가 추가되었다

- 오라클 공식 저장소에서 다운받는 건 오류 많이 발생

- 오라클은 비공식 저장소 repository 를 원격 저장소 위치로 등록하고 거기서 오라클 JDBC 라이브러리를 불러오기

- MyBatis 연동시에는 반드시 MyBatis 라이브러리가 추가되어 있어야함

 

- db.properties

jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:xe
jdbc.username=totoro
jdbc.password=totoro123

- DB 접속에 대한 내용들이 들어가 있다

- 테이블을 생성할때 totoro 계정에서 생성해야 한다


- 테이블을 만들자

- db.properties 파일에서 totoro 계정으로 설정되어있으므로 totoro 계정에서 테이블을 생성해야함

+ import 시에 테이블이 생성되지는 않음

- sql 폴더의 model2.sql 에서 totoro 계정으로 연결한 후 model22테이블을 생성하자

- 커넥션 프로파일 설정

- model2.sql

-- 모델2 게시판
select * from tab;
select * from seq;
select * from model22;

create table model22(
	board_num number,
	board_name varchar2(20),
	board_pass varchar2(15),
	board_subject varchar2(50),
	board_content varchar2(2000),
	board_file varchar2(50),
	board_re_ref number,
	board_re_lev number,
	board_re_seq number,
	board_readcount number,
	board_date timestamp,
	primary key(board_num)
);

create sequence model22_seq
start with 1
increment by 1
nocache;

- 테이블 model22 와 시퀀스 model22_seq 를 생성

- board_num 이 primary key 가 되고, board_num 에는 시퀀스 model22_seq 값이 들어갈 것

 

컬럼 설명

- board_re_ref : 원문은 시퀀스, 댓글은 부모의 ref 값과 동일값

- board_re_lev : 원문은 0, 1단계 댓글은 1, 2단계 댓글은 2

- board_re_seq : 원문은 0, 댓글, 대댓글의 board_re_seq 는 계속 바뀌는 값


JSP Model 2 - MyBatis 게시판 프로그램 : 기본 파일들

 

흐름 보기

- index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

 모델2 게시판
 
<%
	response.sendRedirect("./BoardListAction.do");
%> 

<script>
//	location.href="./BoardListAction.do";
</script>

</body>
</html>

- index.jsp 실행이나 현재 프로젝트 Run As - Run on Server 로 실행시키자

- 목록을 요청하는 "/BoardListAction.do" 로 요청하고 있다, 목록을 요청하고 목록을 출력하는 페이지까지 온다

- 그래서 목록 페이지가 보인다

 

- '글쓰기' 를 눌러서 글 작성 폼으로 가서 게시판에 글을 작성해보자

- 글을 '등록' 하면 아래처럼 목록을 다시 가져와서 목록 페이지로 돌아간다

- 글 작성 성공시 콘솔창

path:C:\Users\admin\eclipse-workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\mybatisboard\boardupload

- 첨부파일도 성공적으로 서버에 업로드 되었음을 확인 가능

 

- 글을 클릭하면 상세 페이지로 넘어가면서 조회수를 1 증가시킴

- 댓글을 달아보자

- 글 삭제하기


- DTO 인 BoardBean.java 파일을 보자

- MyBatis 로 자동 매핑이 가능하도록 테이블의 컬럼명과 DTO 의 프로퍼티명을 일치시켰다

package model;

import java.sql.Timestamp;

public class BoardBean {
	private int board_num;
	private String board_name;
	private String board_pass;
	private String board_subject;
	private String board_content;
	private String board_file;
	private int board_re_ref;
	private int board_re_lev;
	private int board_re_seq;
	private int board_readcount;
	private Timestamp board_date;	
	
	public int getBoard_num() {
		return board_num;
	}
	public void setBoard_num(int board_num) {
		this.board_num = board_num;
	}
	public String getBoard_name() {
		return board_name;
	}
	public void setBoard_name(String board_name) {
		this.board_name = board_name;
	}
	public String getBoard_pass() {
		return board_pass;
	}
	public void setBoard_pass(String board_pass) {
		this.board_pass = board_pass;
	}
	public String getBoard_subject() {
		return board_subject;
	}
	public void setBoard_subject(String board_subject) {
		this.board_subject = board_subject;
	}
	public String getBoard_content() {
		return board_content;
	}
	public void setBoard_content(String board_content) {
		this.board_content = board_content;
	}
	public String getBoard_file() {
		return board_file;
	}
	public void setBoard_file(String board_file) {
		this.board_file = board_file;
	}
	public int getBoard_re_ref() {
		return board_re_ref;
	}
	public void setBoard_re_ref(int board_re_ref) {
		this.board_re_ref = board_re_ref;
	}
	public int getBoard_re_lev() {
		return board_re_lev;
	}
	public void setBoard_re_lev(int board_re_lev) {
		this.board_re_lev = board_re_lev;
	}
	public int getBoard_re_seq() {
		return board_re_seq;
	}
	public void setBoard_re_seq(int board_re_seq) {
		this.board_re_seq = board_re_seq;
	}
	public int getBoard_readcount() {
		return board_readcount;
	}
	public void setBoard_readcount(int board_readcount) {
		this.board_readcount = board_readcount;
	}
	public Timestamp getBoard_date() {
		return board_date;
	}
	public void setBoard_date(Timestamp board_date) {
		this.board_date = board_date;
	}
}

- 테이블의 컬럼명과 DTO 의 프로퍼티명을 일치시켜야 자동 매핑이 가능하다


- Controller 클래스 BoardFrontController.java 를 보자

package controller;

import java.io.IOException;

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;

import service.Action;
import service.ActionForward;
import service.BoardAddAction;
import service.BoardDelete;
import service.BoardDetailAction;
import service.BoardListAction;
import service.BoardModify;
import service.BoardModifyAction;
import service.BoardReply;

/**
 * Servlet implementation class BoardFrontController
 */
@WebServlet("*.do")
public class BoardFrontController extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public void doProcess(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
					
		String requestURI = request.getRequestURI();
		String contextPath = request.getContextPath();
		String command = requestURI.substring(contextPath.length());
		
		System.out.println("requestURI:"+requestURI);
		System.out.println("contextPath:"+contextPath);
		System.out.println("command:"+command);
		
		Action action = null;
		ActionForward forward = null;
		
		// 글작성
		if(command.equals("/BoardAddAction.do")) {
			try {
				action = new BoardAddAction();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		
		// 글작성 폼	
		}else if(command.equals("/BoardForm.do")) {	
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/board/qna_board_write.jsp");
			
		// 글목록	
		}else if(command.equals("/BoardListAction.do")) {
			try {
				action = new BoardListAction();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
			
		// 상세 페이지	
		}else if(command.equals("/BoardDetailAction.do")) {
			try {
				action = new BoardDetailAction();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
			
		// 댓글 폼	
		}else if(command.equals("/BoardReplyAction.do")) {
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/board/qna_board_reply.jsp");
			
		// 댓글	
		}else if(command.equals("/BoardReply.do")) {
			try {
				action = new BoardReply();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
			
		// 수정 폼	
		}else if(command.equals("/BoardModifyAction.do")) {
			try {
				action = new BoardModifyAction();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
			
		// 수정	
		}else if(command.equals("/BoardModify.do")) {
			try {
				action = new BoardModify();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
			
		// 삭제 폼	
		}else if(command.equals("/BoardDeleteAction.do")) {
			forward = new ActionForward();
			forward.setRedirect(false);
			forward.setPath("/board/qna_board_delete.jsp");
			
		// 삭제	
		}else if(command.equals("/BoardDelete.do")) {
			try {
				action = new BoardDelete();
				forward = action.execute(request, response);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
		
		
		if(forward != null) {
			if(forward.isRedirect()) {	 // redirect 방식으로 포워딩
				response.sendRedirect(forward.getPath());
				
			}else {						// dispatcher 방식으로 포워딩
				RequestDispatcher dispatcher = 
						request.getRequestDispatcher(forward.getPath());
				dispatcher.forward(request, response);
			}			
		}		
		
	} // doProcess() end
	
	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
//		response.getWriter().append("Served at: ").append(request.getContextPath());
		System.out.println("get");		
	
		doProcess(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
//		doGet(request, response);
		System.out.println("post");
		
		doProcess(request, response);
	}

}

- command 변수에 저장된 값에 따라 다른 Service 클래스로 이동 또는 View로 포워딩

 

+ 인터페이스 Action.java

- Service 클래스들이 인터페이스를 상속함으로서 안의 추상메소드를 반드시 메소드 오버라이딩 해야함 

- 구현클래스가 여러개 있을때 이 구현 클래스들을 통일성있게 설계하기 위해 인터페이스 상속

 

+ 클래스 ActionForward.java

- 어느 페이지로 포워딩할지, 어떤 방식으로 포워딩할지 설정하는 클래스

 

- DAO 클래스인 BoardDAO.java 를 보자

- BoardDAO.java

package dao;

import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import model.BoardBean;

public class BoardDAO {

	private static BoardDAO instance = new BoardDAO();

	public static BoardDAO getInstance() {
		return instance;
	}
		
	public SqlSession getSession() {
		SqlSession session=null;
		Reader reader=null;			
		try {
			reader = Resources.getResourceAsReader("mybatis-config.xml");
			SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(reader);
			session = sf.openSession(true);	 // auto commit	
		}catch(Exception e) {
			e.printStackTrace();
		}		
		return session;
	}
	
	
	//글작성(원문작성)
	public int insert(BoardBean board) throws Exception {
		int result=0;
		SqlSession session = getSession();
		result = session.insert("board_insert", board);		
		System.out.println("result:"+result);
		
		return result;
	}
	
	
	// 총데이터 갯수 구하기
	public int getCount() throws Exception{
		int result=0;
		SqlSession session=getSession();
		result = (Integer)session.selectOne("board_count");
		
		return result;
	}
	
	
	// 데이터 목록
	public List getList(int page) throws Exception {
//	public List getList(Map map) throws Exception {
		List list = new ArrayList();
		SqlSession session=getSession();
		list = session.selectList("board_list", page); 
		
		return list;
	}
	
	
	// 조회수 증가
	public void updateCount(int board_num) throws Exception{
		SqlSession session = getSession();
		session.update("board_updatecount", board_num);
	}
		
	
	// 댓글 출력 순서 (board_re_seq값 증가)
	public void updateSeq(BoardBean board) {
		SqlSession session = getSession();
		session.update("board_updateseq", board);
	}
		

	// 댓글작성
	public void boardReply(BoardBean board) {
		SqlSession session = getSession();
		session.insert("board_reply", board);
	}
	
	
	// 상세 페이지, 수정 폼
	public BoardBean getContent(int board_num) throws Exception {
		BoardBean board = new BoardBean();

		SqlSession session=getSession();
		board  = (BoardBean)session.selectOne("board_content", board_num);
		
		return board;
	}
	
	
	// 글수정
	public void update(BoardBean board) throws Exception {
		SqlSession session=getSession();
		session.update("board_update", board);
	}

	
	// 글삭제
	public void delete(int board_num) {
		SqlSession session=getSession();
		session.delete("board_delete", board_num);
	}

}

- DB와 연동하기 위한 여러 메소드들만으로 구성되어있다

- getSession() 메소드는 SqlSession 객체를 구해오는 메소드이고, 이 객체가 있어야 지원되는 5개 메소드 사용 가능

 

- MyBatis 환경설정 파일

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="db.properties" />
	<typeAliases>
		<typeAlias type="model.BoardBean" alias="board"></typeAlias>
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="board.xml" />
	</mappers>
</configuration>

1) DTO 인 BoardBean 클래스의 alias (별칭)를 "board" 로 설정함

2) db.properties 파일의 변수들을 불러와서 DB 와 관련된 내용들을 작성함

- property 태그의 name 은 정해져있다, "driver", "url", "username", "password" 는 바뀌지 않음

3) Mapper 파일 board.xml 을 mapper 태그로 불러오고 있다

- Mapper 파일은 테이블 개수에 비례한다

- board.xml 에 따로 패키지가 없다, 즉 같은 폴더 하위에 있으므로 패키지명을 써주지 않고 board.xml 만 쓴다

 

- Mapper 파일 board.xml 을 보자

- board.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="myboard">

	<!-- 글작성(원문) -->
	<insert id="board_insert" parameterType="board">
	 insert into model22 values(model22_seq.nextval,#{board_name},
	 #{board_pass},#{board_subject},#{board_content},
	 #{board_file,jdbcType=VARCHAR},model22_seq.nextval,0,0,0,sysdate)
	</insert>
	
	<!-- 글갯수 -->
	<select id="board_count" resultType="int">
	 select count(*) from model22
	</select>
	
	<!-- 글목록 -->
	<!-- page 전달-->
	<select id="board_list" parameterType="int" resultType="board">
	 select * from (select rownum rnum, board.* from (
	  select * from model22 order by board_re_ref desc,board_re_seq asc) board )
	  where rnum &gt;= (#{page}-1) * 10 + 1   and rnum &lt;= #{page}*10
	</select> 
	
	<!-- Map 전달 -->
	<!-- <select id="board_list" parameterType="Map" resultType="board">
	 select * from (select rownum rnum, board.* from (
	  select * from model22 order by board_re_ref desc,board_re_seq asc) board )
	  where rnum &gt;= #{start} and rnum &lt;= #{end}
	</select> -->
	
	<!-- 조회수 증가 -->
	<update id="board_updatecount" parameterType="int">
     update model22 set board_readcount=board_readcount+1 where board_num = #{board_num}	
	</update>
	
	<!-- 상세페이지, 수정폼 -->
	<select id="board_content" parameterType="int" resultType="board">
	 select * from model22 where board_num = #{board_num}
	</select>
	
	<!-- 댓글 출력순서 -->
	<update id="board_updateseq" parameterType="board">
	 update model22 set board_re_seq=board_re_seq+1 
	  where board_re_ref = #{board_re_ref} and board_re_seq &gt; #{board_re_seq}
	</update>
	
	<!-- 댓글 작성 -->
	<insert id="board_reply" parameterType="board">
	 insert into model22 values(model22_seq.nextval,#{board_name},
	 #{board_pass},#{board_subject},#{board_content},
	 #{board_file,jdbcType=VARCHAR},#{board_re_ref},#{board_re_lev},#{board_re_seq},0,sysdate)
	</insert>

	<!-- 글수정 -->
	<update id="board_update" parameterType="board">
	 update model22 set board_name=#{board_name}, board_subject=#{board_subject},
	 board_content=#{board_content} where board_num=#{board_num}
	</update>

	<!-- 글삭제 -->
	<delete id="board_delete" parameterType="int">
	 delete from model22 where board_num=#{board_num}
	</delete>



	<!-- <select id="getSomething" parameterType="int" resultType="#package.#modelname">
		SELECT
		columnname1,
		columnname2,
		columnname3
		FROM tablename1
		WHERE columnname1 = #{value}
	</select>

	<resultMap type="#modelname" id="YourResultSet">
		<id property="param1" column="columnname1" />
		<result property="param2" column="columnname2" />
		<result property="param3" column="columnname3" />
	</resultMap>

	<select id="getAll" resultMap="YourResultSet">
		SELECT * FROM tablename1
	</select>

	<insert id="insertSomething" parameterType="#modelname">
		INSERT INTO tablename1 (columnname1, columnname2, columnname3)
		VALUES(#{value1},#{value2},#{value3})
	</insert>

	<update id="updateSomething" parameterType="#modelname">
		UPDATE tablename1
		SET
		columnname1=#{param1}
		WHERE
		columnname2 = #{param2}
	</update>

	<delete id="deleteSomething" parameterType="int">
		DELETE FROM tablename1 WHERE
		columnname1 = #{param1}
	</delete> -->

</mapper>

- MyBatis 환경설정 파일에서 이 Mapper 파일 board.xml 을 mapper 태그로 불러왔으므로 이 파일 board.xml 의 루트 엘리먼트는 mapper

 

글 목록 가져오기 주의 (board.xml 부분)

	<!-- 글목록 -->
	<!-- page 전달-->
	<select id="board_list" parameterType="int" resultType="board">
	 select * from (select rownum rnum, board.* from (
	  select * from model22 order by board_re_ref desc,board_re_seq asc) board )
	  where rnum &gt;= (#{page}-1) * 10 + 1   and rnum &lt;= #{page}*10
	</select>

- MyBatis 는 값이든 주소값이든 전달할 값을 단 1개만 전달 가능하므로 페이지번호 page 를 넘긴다

- 페이지번호 page 만 있으면 startRow 와 endRow 를 여기서 계산 할 수 있기 때문이다

 


JSP Model 2 - MyBatis 게시판 프로그램 : 글 작성 기능 

- 내일

복습

Maven

- Spring 프로젝트는 Maven 으로만 라이브러리 관리 가능

- Springboo 프로젝트는 Maven, Gradle 둘 다 사용 가능

 

Maven 파일 구조

- src/main/java 에 확장자가 Java 인 파일을 넣는다

- src/main/resource 는 MyBatis 의 환경 설정 파일, 기존에 DAO 에 있었던 SQL문을 따로 빼서 만든 xml 파일

- src/main/webapp 는 View 페이지가 들어감, index 파일도 아 폴더 내에 들어간다 , 기존의 WebContent 폴더 역할

- src/main/webapp/WEB-INF 안에 프로젝트 환경 설정 파일인 web.xml 이 들어감, 그리고 기존의 lib 폴더는 사라짐

- DAO 클래스는 존재하지만 DAO 클래스의 SQL문을 따로 빼서 따로 파일을 만드는데, 그걸 mapper 파일이라고 부른다

- pom.xml 이 Maven 의 환경 설정 파일, 여기에서 의존 라이브러리를 추가

 

pom.xml 환경 설정 파일

- 오라클 jdbc, mysql jdbc, cos, mybatis, jstl 등 많은 라이브러리를 다운 받았었다

 

local repository 위치

 

프로젝트 구분

- Maven 프로젝트는 m , Spring 프로젝트는 s 가 붙어있다

 

Maven 프로젝트 / Spring 프로젝트 주의

- Maven 프로젝트, Spring 프로젝트는 war 파일로 export / import 하면 Dynamic Web Project 로 풀리므로 주의

- Maven 프로젝트, Spring 프로젝트는 프로젝트를 통째로 복사 붙여넣기해서 export 해야한다

- 이클립스에서 Spring 프로젝트 만드려면 플러그인 추가 또는 STS 라는 툴을 다운받아 사용

- Spring 프로젝트는 Mavne 프로젝트와 달리 기본적인 라이브러리가 이미 pom.xml 에 다 들어가 있다

- 아파치 톰캣이 자동으로 추가되지 않는 경우가 만흥므로 직접 추가해 준다


MyBatis

데이터베이스 접속 방식

- DB 접속 방식 3번째인 ORM 프레임워크 중 MyBatis 사용할 것

 

ORM 프레임워크

- ORM 프레임워크에서 Object 는 DTO 객체를 의미, Mapping 은 연결을 의미

- 즉 DTO 클래스의 필드와 테이블 안의 컬럼을 매핑시키는 것, 매핑 처리를 위해선 두 이름을 일치시켜야함

- ORM 프레임워크를 사용시 코드가 간단해진다

- 전자정부 프레임 워크에서는 MyBatis 사용함

- MyBatis 로 Model 1 에서도 연동해보고, Model 2 에서도 연동해 볼 것

 

프로젝트에 MyBatis 연동하기 위해 필요한 파일

- MyBatis 와 연동하기 위해서는 크게 2가지 파일이 필요하다

1. MyBatis 의 환경설정 파일

2. 기존 DAO 파일에 들어있던 내용인 Mapper 파일

- 이 2개 파일이 src/main/resources 폴더 안에 있어야 한다 

 

프로젝트에 MyBatis 연동하는 방법

1. MarketPlace 에서 MyBatis Plugin 프로그램을 설치해야한다

- Mapper 파일을 생성할 수 있는 메뉴가 추가됨

- 과거에는 MarketPlace 에서 플러그인 설치시 MaBatis 환경설정 파일도 만들 수 있었지만 현재는 불가능

- 그리고 이클립스를 재시작한다

 

2. Mapper 파일을 생성해야 한다

- maventest 프로젝트의 resources 폴더에서 오른쪽 마우스 클릭

- 플러그인인을 설치시 Mapper 파일을 생성할 수 있는 메뉴가 나타남

- resources 폴더 안에 Mapper 파일이 들어갔다

- mapper.xml (예시)

- 이 파일 안에 DAO 의 insert, update, delete, select 가 들어감

- xml 파일이므로 루트 엘리먼트가 있어야하고, 루트 엘리먼트가 mapper

- insert 태그 사이에 insert SQL문 작성, 다른 DML 문도 마찬가지, 태그에는 각 SQL문을 구분하기 위한 id 가 있다

 

3. 다음으로는 MyBatis 환경설정 파일이 다운받아진 프로젝트를 import 해서 MaBatis 환경설정 파일을 살펴보자

- 현재는 플러그인 추가해도 MaBatis 환경설정 파일 생성 불가

 

MaBatis 프로젝트 import 하기

- mybatistest 프로젝트 import

- mabatistest.zip 파일을 다운로드 받아서 압축 해제 후 import

- mybatistest 가 import 되었다

- MyBatis 와 연동되는 이 3개 파일들은 모두 src/main/resources 폴더 안에 저장되어 있어야 함

 

MyBatis 연동 관련 파일 살펴보기

1. db.properties

#mysql
#jdbc.driverClassName=com.mysql.jdbc.Driver
#jdbc.url=jdbc:mysql://localhost:3306/jsptest
#jdbc.username=jspid
#jdbc.password=jsppass

#oracle
jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:xe
jdbc.username=scott
jdbc.password=tiger

- DB 접속에 필요한 내용들이 들어간다

- 위에는 mysql 과 연동하는 내용, 아래는 oracle 과 연동하는 내용을 작성했다

- 왼쪽은 변수명, 오른쪽은 변수에 할당될 값이다

- jdbc.driverClassName 변수명은 정해져 있는 이름이다, 경로를 oracle.jdbc.driver.OracleDriver 로 설정

- jdbc.url 변수에는 jdbc:oracle:thin:@localhost:1521:xe 로 DB 경로를 설정

- 현재는 scott 계정으로 연결 설정했다

- # 은 주석

- mybatis-config.xml 파일에서 이 파일 db.properties를 불러온다

 

2. mybatis-config.xml

- MyBatis 의 환경설정 파일이다

+ 환경 설정 파일은 대부분 xml 파일이다

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="db.properties" />
	<typeAliases>
		<typeAlias type="#package.#modelname" alias="#modelname"></typeAlias>
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="#package/#mapper.xml" />
	</mappers>
</configuration>

- 위의 DOCTYPE configuration 내용을 삭제하면 안된다, mybatis.org/dtd/mabatis-3-config.dtd 문서를 불러오고 있다

- 루트 엘리먼트는 configuration

 

Mabatis 환경설정 파일인 mybatis-config.xml 파일 안에 들어가는 내용 3가지

0) properties 태그에서 db.properties 파일을 불러온다

1) typeAliases 태그로 DTO 클래스의 경로를 alias 로 간단한 별칭을 설정

- typeAlias 태그의 type 속성에서 DTO 클래스의 경로를 패키지부터 해당 클래스까지의 경로로 작성

- typeAlias 태그의 alias 속성에서 alias는 별칭을 의미, 그 경로를 짧은 별칭으로 설정하는 것

- DTO 클래스가 많아지면 typealias 태그를 추가해주면 된다

2) environments 태그 안의 property 태그에서 DB 접속 관련 내용들 작성

- property 태그는 클래스의 멤버변수(필드, 프로퍼티) 를 의미, name 속성값은 고정되어있고, values 값으로 db.property 안의 변수명을 이용해서 변수값을 불러오고 있다

+ 변수명으로 불러오는 대신 그 자리에 바로 값을 써도 된다

ex) <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>

- 그렇게 드라이브의 경로, DB 경로, 계정명, 비밀번호 등을 불러옴

- db 접속하는 내용들이 들어간다

- 위에서 db.property 파일을 불러왔으므로 가능함

3) mappers 태그 안의 mapper 태그로 SQL문이 들어간 파일인 Mapper 파일을 불러오기

- mapper 태그의 resource 속성에 패키지명을 적어주고, Mapper 파일명을 써주기

+ 여기서 mapper 태그 안에서 Mapper 파일을 불러오므로 Mapper 파일의 루트 엘리먼트도 mapper 이다

- Mapper 파일은 테이블의 개수에 비례해서 늘어난다

- Mapper 파일이 늘어나면 mapper 태그를 추가해주면 된다

 

3. mapper.xml

- Mapper 파일 이다

- 이름은 반드시 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="#package.#mapperinterfacename">

	<select id="getSomething" parameterType="int" resultType="#package.#modelname">
		SELECT
		columnname1,
		columnname2,
		columnname3
		FROM tablename1
		WHERE columnname1 = #{value}
	</select>

	<resultMap type="#modelname" id="YourResultSet">
		<id property="param1" column="columnname1" />
		<result property="param2" column="columnname2" />
		<result property="param3" column="columnname3" />
	</resultMap>

	<select id="getAll" resultMap="YourResultSet">
		SELECT * FROM tablename1
	</select>

	<insert id="insertSomething" parameterType="#modelname">
		INSERT INTO tablename1 (columnname1, columnname2, columnname3)
		VALUES(#{value1},#{value2},#{value3})
	</insert>

	<update id="updateSomething" parameterType="#modelname">
		UPDATE tablename1
		SET
		columnname1=#{param1}
		WHERE
		columnname2 = #{param2}
	</update>

	<delete id="deleteSomething" parameterType="int">
		DELETE FROM tablename1 WHERE
		columnname1 = #{param1}
	</delete>

</mapper>

- select 문은 select 태그로 감싸준다

- 태그들을 구분하기 위해 id 값을 다르게 설정한다

- DAO 파일에서 이 Mapper 파일의 id 값을 불러서 사용한다

<값 전달하기 : parameterType>

- insert , select 할 데이터는 parameterType 속성으로 값을 받는다

- parameterType 속성값에 전달되는 값의 자료형을 작성

< 검색한 값 돌려받기 : returnType >
- select 해서 검색한 결과를 resultType 속성으로 값을 돌려준다

- select SQL문만 returnType 속성을 사용함

- resultType 속성값에는 값을 돌려줄때의 자료형을 작성

- #{} 에서 getter 메소드로 불러올 값들을 작성함

< 검색한 값 돌려받기 : 데이터 1개 검색 >

- 데이터를 1개 검색했을때는 if(rs.next()) 로 가져와서 DTO 객체에 저장했었다, 일일히 setter 메소드로 저장했었음

- 이제는 일일히 setter 메소드를 작성하지 않고 컬럼명에 일치하는 DTO 프로퍼티가 있을땐 자동으로 setter 메소드를 불러서 매핑해준다

- returnType 에 DTO 경로 별칭만 써주면 테이블명과 DTO 프로퍼티가 일치하는 경우에 자동으로 매핑

< 검색한 값 돌려받기 : 데이터 2개 이상 검색 >

- 2개 이상의 데이터를 검색할때는 리스트를 통해 값을 받았었다, 여기서는 resultType 으로 DTO 클래스의 alias 명을 쓰면 된다, 리스트에 저장시키는 것 까지 자동으로 해줌

- returnType 에 DTO 경로 별칭만 써주면 테이블명과 DTO 프로퍼티가 일치하는 경우에 자동으로 매핑, 리스트 저장까지 해줌

<자료형이 DTO일땐>

- 전달되는 값의 자료형이나 돌려주는 값의 자료형이 DTO일땐, MyBatis 환경설정 파일에서 설정한 DTO 클래스의 alias 값이 파라미터 타입값으로 들어가야한다

<resultMap>

- resultMap 은 컬럼명과 DTO 프로퍼티명이 일치하지 않을때, resultMap 으로 일일히 매핑을 잡아줘야한다

- resultMap 태그 안에서 컬럼과 프로퍼티를 일일히 매핑


JSP - MyBatis 연동

Model 1 과 MyBatis 연동

- 클라우드에서 Model 1 과 MyBatis 가 이미 연동이 완료된 member22 프로젝트 import

- zip 파일을 압축 풀어서 import

+ 그냥 member22 파일을 바로 import 하면 . 으로 시작된 파일들이 import 안된다

+ 반드시 zip 파일을 풀어서 생성된 폴더를 import 하기

- src/main/java 안에 확장자가 java 인 파일들이 저장되어있다

- src/main/resources 안에 DB 연동에 필요한 MyBatis 환경설정 파일 등이 들어가 있다

- src/main/webapp 안에 View 파일 등이 들어간다

 

- import한 member22 프로젝트의 pom.xml 파일을 보자

- pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.myhome</groupId>
	<artifactId>member22</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>member22 Maven Webapp</name>
	<url>http://maven.apache.org</url>

	<!-- 오라클 repository -->
	<repositories>
		<repository>
			<id>codelds</id>
			<url>https://code.lds.org/nexus/content/groups/main-repo</url>
		</repository>
	</repositories>

	<dependencies>

		<!-- 오라클 JDBC Library -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>

		<!-- MySQL -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.47</version>
		</dependency>

		<!-- iBatis -->
		<dependency>
			<groupId>org.apache.ibatis</groupId>
			<artifactId>ibatis-sqlmap</artifactId>
			<version>2.3.4.726</version>
		</dependency>

		<!-- mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.3</version>
		</dependency>

		<!-- jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.taglibs</groupId>
			<artifactId>taglibs-standard-impl</artifactId>
			<version>1.2.5</version>
		</dependency>

		<!-- cos -->
		<dependency>
			<groupId>servlets.com</groupId>
			<artifactId>cos</artifactId>
			<version>05Nov2002</version>
		</dependency>
		
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>member22</finalName>
	</build>
</project>

- dependencies 태그 안에 의존 라이브러리를 dependency 태그로 추가

- 오라클 JDBC, MySQL JDBC, iBatis, MyBatis 3.5.3 ver, JSTL, cos 라이브러리가 추가되었다

- 오라클 공식 저장소에서 다운받는 건 오류 많이 발생

- 오라클은 비공식 저장소 repository 를 원격 저장소 위치로 등록하고 거기서 오라클 JDBC 라이브러리를 불러오기

- MyBatis 연동시에는 반드시 MyBatis 라이브러리가 추가되어 있어야함

 

Model 1 에 MyBatis 연동시 구조

- Model 1 에서 MyBatis 를 연동하면 DAO 이후부터만 달라진다

- 앞부분은 Model 1과 동일하고 달라지는 내용은 DAO 이후 부터 이다

- DAO 메소드 안의 복잡한 내용들이 빠져서 MyBatis 환경설정 파일로 간다

- 안의 SQL문들이 빠져서 Mapper 파일인 member.xml 으로 간다

 

- MyBatis 환경설정 파일인 mybatis-config.xml 파일을 보자

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="db.properties" />
	<typeAliases>
		<typeAlias type="model.Member" alias="member"></typeAlias>
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="member.xml" />
	</mappers>
</configuration>

- model 패키지 안의 DTO 경로를 alias 속성을 통해 member 라는 별칭으로 지정

- mapper 태그로 Mapper 파일인 member.xml 파일을 불러옴, 같은 폴더 안에 있으므로 이름만으로 불러옴

- Mapper 파일이 다른 패키지안에 있으면 불러올 때 패키지명도 명시해줘야함

 

- Mapper 파일인 member.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="memberns">

	<insert id="insert" parameterType="member">
		insert into member22 values (#{id}, #{password})
	</insert>
	
	<select id="list" resultType="member">
		select * from member22
	</select>
	
	<select id="select" parameterType="String" resultType="member">
		select * from member22 where id = #{id}
	</select>	
	
	<update id="update" parameterType="member">
		update member22 set password = #{password} where id = #{id}
	</update>
	
	<delete id="delete" parameterType="String">
		delete from member22 where id = #{id}
	</delete>
	
</mapper>

 

- db.properties

#mysql
#jdbc.driverClassName=com.mysql.jdbc.Driver
#jdbc.url=jdbc:mysql://localhost:3306/jsptest
#jdbc.username=jspid
#jdbc.password=jsppass

#oracle
jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:xe
jdbc.username=totoro
jdbc.password=totoro123

 

- db.properties 파일에서 totoro 계정으로 설정되어있으므로 totoro 계정에서 테이블을 생성해야함

+ import 시에 테이블이 생성되지는 않음

- sql 폴더의 member.sql 에서 totoro 계정으로 연결한 후 member22 테이블을 생성하자

- member.sql

-- model1 과 mybatis 연동
select * from tab;
select * from member22;

create table member22 (
	id varchar2(10) primary key,
	password varchar2(10)
);

- 테이블 member22 를 생성하자

- 테이블의 필드명과 DTO 클래스의 멤버변수(필드, 프로퍼티) 명을 같은 이름으로 설정해야한다

+ 간소화 시킨 테이블임


흐름 설명

- src/main/java 에서 model 패키지의 DTO 클래스인 Member.java 를 보자

package model;

public class Member {
	private String id;
	private String password;

	public String getId() {
		return id;
	}

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

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

- 프로퍼티명이 컬럼명과 동일하게 id, password 로 되어있다, 쉽게 매핑 가능해짐

 

- 회원가입 페이지인 joinForm.jsp 에서 form 의 name 값도 똑같이 id, password 로 일치시킨다

- 그렇게 해야 setProperty 로 쉽게 저장 가능 (Model 1)

- joinForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>

<body>
<h2>회원가입</h2>
<form action="joinPro.jsp">
	아이디 : <input type="text" name="id" required="required"><p>
	암호 : <input type="password" name="password" required="required"><p>
	<input type="submit" value="확인">
</form>
</body>
</html>

 

- loginForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>

<body>
<h2>로그인</h2>
<form action="loginPro.jsp" >
	아이디 : <input type="text" name="id" required="required"><p>
	암호 : <input type="password" name="password" required="required"><p>
	<input type="submit" value="확인">
</form>
<p><a href="joinForm.jsp">회원가입</a>
</body>

</html>

 

- index.jsp

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

<html>
<body>
<h2>Hello World!</h2>

<script>
	location.href="member/loginForm.jsp";
</script>

</body>
</html>

- index 파일은 webapp 폴더 안에 저장되어 있어야함

- index 파일을 실행시켜도 되고, 현재 프로젝트 선택 후 Run As - Run on Server 로 실행 하면 index 파일이 실행됨

- 실행시 index.jsp 파일에서 loginForm.jsp 파일을 실행시킴

- 회원 가입을 해보자, 클릭 시 joinForm.jsp 파일로 넘어감

- '확인' 시 joinPro.jsp 파일로 넘어가면서 id, password 를 네임값으로 해서 입력된 값을 전달

- joinPro.jsp 에서 setProperty 로 DTO 객체 mem에 값 설정 후 insert(mem) 호출

- 가입 후 로그인

- master 란 아이디로 가입시 관리자

- 관리자로 로그인 시 나오는 내용이 달라짐

- 회원명단을 볼 수 있는 메뉴가 나타남

- 즉, 관리자로 로그인 시 나오는 내용이 달라짐

- 회원 수정이나 회원 탈퇴 시킬 수 있다

 

- 앞부분은 Model 1과 동일하고 달라지는 내용은 DAO 이후 부터 이다

- DAO 클래스가 SQL문을 실행하는 것은 같지만, DAO 클래스 안에서 SQL문이 빠져서 Mapper 파일로 간다

 

- Mapper 파일인 member.xml 파일을 보자

-member.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="memberns">

	<insert id="insert" parameterType="member">
		insert into member22 values (#{id}, #{password})
	</insert>
	
	<select id="list" resultType="member">
		select * from member22
	</select>
	
	<select id="select" parameterType="String" resultType="member">
		select * from member22 where id = #{id}
	</select>	
	
	<update id="update" parameterType="member">
		update member22 set password = #{password} where id = #{id}
	</update>
	
	<delete id="delete" parameterType="String">
		delete from member22 where id = #{id}
	</delete>
	
</mapper>

- id 값은 태그를 구별하는 역할, 이 파일 내에선 중복되선 안되는 값이다

- update 문은 update 태그 안에 작성

- DAO 클래스에서 이 id 값을 불러온다, 즉, 안의 SQL문을 불러온다

- 전달되는 값의 자료형을 parameterType 에 쓰고 돌려주는 값의 자료형을 resultType 에 쓴다

- resultType 은 select 태그 (select 문) 에만 사용가능하다

- 전달되는 값이 없을땐 parameterType 속성을 사용하지 않음

- DTO 객체가 전달되는 값의 자료형이거나 돌려주는 값의 자료형일땐 alias (별칭) 값을 쓴다

- 2개 이상의 데이터를 검색할때도 똑같이 DTO 객체인 alias 값을 사용하면 자동으로 리스트에 저장해서 돌려줌

 

- 다음은 MyBatis 환경 설정 파일인 mybatis-config.xml 을 보자

- mybatis-config.xml (중복)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="db.properties" />
	<typeAliases>
		<typeAlias type="model.Member" alias="member"></typeAlias>
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="member.xml" />
	</mappers>
</configuration>

1. 이 alias 를 Mapper 파일안에서 값을 받고 돌려줄때 사용한다

+ alias 를 설정하지 않았다면 패키지부터 클래스까지 전부 써야한다

2. DB 접속에 필요한 내용을 property 태그 안에 작성, db.properties 파일의 변수를 불러와서 설정

+ 변수 불러오지 않아도 바로 적어도 된다

3. Mapper 파일을 불러오고 있다

 

DAO 클래스에서 id 값을 불러오는 원리

- DAO 에서 바로 Mapper 파일의 id를 불러올 수 없다

- 먼저 MyBatis 환경설정 파일 3번째 자리에서 Mapper 파일을 불러온다

- DAO에서는 MyBatis 환경 설정 파일을 불러옴으로서 id 를 불러올 수 있음

- 즉 Mapper 파일을 MyBatis 환경설정 파일에서 불러오고, MyBatis 환경설정 파일을 DAO 에서 불러온다

- 연쇄적으로 불러오기 때문에 Mapper 파일에 설정된 id 를 DAO 클래스에서 불러 올 수 있음

 

- DAO 클래스인 MemberDao.java 를 보자

- MemberDao.java

package dao;

import java.io.IOException;
import java.io.Reader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import model.Member;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MemberDao {
	
	private SqlSession getSession() {
		SqlSession session=null;
		Reader reader=null;
		try {
			reader = Resources.getResourceAsReader("mybatis-config.xml");
			SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(reader);
			session = sf.openSession(true);			// auto commit
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
		return session;
	}

	public int insert(Member member) {
		int result = 0;
		SqlSession session=null;
		try { 
			session = getSession(); 
			result = session.insert("insert", member);			
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}

	public int chk(Member member) {
		int result = 0;
		SqlSession session=null;
		try { session = getSession(); 
			Member mem = (Member) session.selectOne("select", member.getId());
			if (mem.getId().equals(member.getId())) {
				result = -1;
				if (mem.getPassword().equals(member.getPassword())) {
					result = 1;
				}
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}

	public Member select(String id) throws SQLException {
		Member mem = null;
		SqlSession session=null;
		try { session = getSession(); 
		mem = (Member) session.selectOne("select", id);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return mem;
	}

	public List<Member> list() {
		List<Member> list = new ArrayList<Member>();
		SqlSession session=null;
		try { session = getSession(); 
			list = session.selectList("list");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return list;
	}

	public int delete(String id) {
		int result = 0;
		SqlSession session=null;
		try { session = getSession(); 
			result = session.delete("delete", id);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}

	public int update(Member mem) {
		int result = 0;
		SqlSession session=null;
		try { session = getSession(); 
			result = session.update("update", mem);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}
}

- 마찬가지로 DB 연동을 처리하는 클래스이다

- SQL문이 빠지고 Mapper 파일인 member.xml 파일에 들어가있다

- SQL문을 제외한 나머지 내용이 들어가있음, 메소드만으로 구성되어있다

+ MyBatis 지원 클래스/인터페이스 (SqlSession 인터페이스 등) 들을 import 하고 있다 * 아래에 설명

 

SqlSession 객체 구하기 / MyBatis 환경설정 파일 읽어오기 (MemberDao.java 부분 getSession() 메소드)

	private SqlSession getSession() {
		SqlSession session=null;
		Reader reader=null;
		try {
			reader = Resources.getResourceAsReader("mybatis-config.xml");
			SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(reader);
			session = sf.openSession(true);			// auto commit
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
		return session;
	}

- getSession() 메소드에서는 try 안의 3줄을 통해 SqlSession 객체를 구해준다, 그 객체를 통해 SQL문을 실행하는 메소드들을 사용 가능하다

- Reader 객체를 구할때 MyBatis 환경설정 파일인 mybatis-config,xml 을 불러오고 있다, MyBatis 환경설정 파일에서는 Mapper 파일을 불러옴

- 그렇기 때문에 DAO 에서 Mapper 파일의 id 값을 불러올 수 있다, 즉 Mapper 파일의 SQL문을 불러올 수 있음

- sf.openSession(true) 를 하면 자동 커밋이 된다, 자동 커밋이 되지 않는다면 SQL문을 수행 후 일일히 session.commit() 으로 커밋을 수행해야함

* 커밋을 해야 DB에 실제 삽입, 수정, 삭제 등이 됨

 

DAO 안에서 id 로 불러와서 SQL문 실행 (MemberDao.java 부분)

	public int insert(Member member) {
		int result = 0;
		SqlSession session=null;
		try { 
			session = getSession(); 
			result = session.insert("insert", member);			
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}

- 가장 먼저 getSession() 메소드로 SqlSession 객체를 구한다

- DAO 안의 메소드 안에서 그 SQL문을 실행해야하는데, 이때 SqlSession 에서 지원하는 메소드를 사용한다

- insert() 메소드를 사용해서 id 값인 "insert" 로 Mapper 클래스의 insert 문을 불러온다

메소드에서 메소드 첫번째 매개변수에는 id값, 전달될 값이 있다면 두번째 매개변수에 전달될 값을 전달

- 여기서는 전달받은 DTO 객체인 member 를 두번째 매개변수를 통해 전달하고 있음

- insert() 메소드를 실행 후 삽입 성공한 데이터 개수를 자동으로 반환해줌

- 위의 insert() 메소드는 SQL문을 실행시켜주는 메소드 5가지 중 하나이다

 

SQL문을 실행시켜주는 메소드 5가지 (MemberDao.java 부분)

- SQL문에 따라서 메소드가 정해져 있다

- 이 메소드를 지원하는 인터페이스 SqlSession 객체를 구해와야만 이 메소드 5개를 사용 가능하다

- 그래서 위에서 getSession() 메소드로 SqlSession 객체를 구해왔다

+ SqlSession 은 MyBatis 에서 지원되는 인터페이스

 

- insert SQL문은 insert() 메소드로 실행해야한다, update, delete 도 마찬가지

- 검색되는 결과가 1개인 select 문은 selectOne() 메소드로 실행

- 결과를 Object 형으로 돌려준다, 다운캐스팅

+ Member 는 DTO 클래스 이름이다

- 검색되는 결과가 2개 이상인 selec 문은 selectList() 메소드로 실행

- 결과를 List 로 돌려준다

 

- insert() / update() / delete() 메소드는 삽입, 수정, 삭제된 데이터 개수를 자동으로 돌려준다

- 메소드 자체적으로 값을 자동으로 돌려주므로 Mapper 파일에서 insert / update / delete 문은 resultType 을 쓰면 안됨

- resultType 은 select 문만 사용가능

<데이터를 1개 검색시>

- selectOne() 메소드를 사용시에는 값을 Object 형 으로 돌려주므로 다운캐스팅 해야한다

- resultType 에는 DTO 객체를 쓴다

<데이터를 2개 이상 검색시>

- 데이터 2개 이상 검색하는 select문에서도 resultType 에는 DTO 객체만 쓴다, List 를 쓰지 않음! 자동으로 리스트를 잡아서 돌려준다

- 일일히 가져와서 DTO 객체 에 setter 메소드로 저장했었지만 이제는 검색된 데이터의 컬럼명과 DTO 프로퍼티명이 같다면 자동으로 순차적으로 DTO 객체에 저장하면서 List 를 만들어서 돌려줌

 

Mapper 의 insert SQL문을 insert() 메소드로 불러와서 실행 (MemberDao.java 부분, 중복) 

- update(), delete() 도 마찬가지

	public int insert(Member member) {
		int result = 0;
		SqlSession session=null;
		try { 
			session = getSession(); 
			result = session.insert("insert", member);			
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}

- SqlSession 객체가 있어야 insert() 메소드 사용 가능, 이전에 정의한 getSession() 으로 객체를 받아온다

- insert() 메소드 자체적으로 삽입에 성공한 데이터 개수를 돌려주는 기능이 있으므로 Mapper 파일의 insert에 resultType 을 써선 안됨

- 그렇게 돌려받은 값을 result 변수에 받아서 그 변수를 리턴하고 있음

- insert() 메소드에서 메소드 첫번째 매개변수에는 id값, 전달될 값이 있다면 두번째 매개변수에 전달될 값을 전달

- 여기서는 전달받은 DTO 객체인 member 를 두번째 매개변수를 통해 전달하고 있음

- Mapper 파일의 parameterType 에서도 같은 자료형인 DTO 를 써야함

- Mapper 파일 member.xml 부분

	<insert id="insert" parameterType="member">
		insert into member22 values (#{id}, #{password})
	</insert>

- parameterType 에는 전달될 값의 자료형을 적음, DTO 객체를 전달받아야하므로 alias 로 설정한 별칭인 member 를 적는다

- #{} 안에 DTO 클래스의 필드명을 써서, 값을 가져온다.

- 표기만 이럴뿐 DTO 객체에 직접 접근하는 것이 아니라 getter 메소드로 가져오는 것임

- #{id} 의 의미는 member.getId() 로 리턴받은 값을 의미함

- #{password} 의 의미는 member.getPassword() 로 리턴받은 값을 의미

 

+ 단점 : 값을 1개만 전달 가능

 

Mapper 의 select SQL문을 select() 메소드로 불러와서 실행 1(MemberDao.java 부분)

	public Member select(String id) throws SQLException {
		Member mem = null;
		SqlSession session=null;
		try { session = getSession(); 
		mem = (Member) session.selectOne("select", id);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return mem;
	}

- SqlSession 객체가 있어야 select() 메소드 사용 가능, 이전에 정의한 getSession() 으로 객체를 받아온다

- select() 메소드는 돌려주는 데이터가 몇개인지에 따라 다른 메소드를 쓴다, 1개 데이터를 돌려줄때는 selectOne() 사용

- 두번째 매개변수로 사용자의 id 값을 전달

- 마찬가지로 Mapper 파일의 parameterType 도 String 형으로 설정해야함

- Mapper 파일 member.xml 부분

	<select id="select" parameterType="String" resultType="member">
		select * from member22 where id = #{id}
	</select>

- 넘어오는 값은 사용자의 id 값이므로 paramterType 은 String 으로 설정해야함

- #{id} 의 의미는 전달된 변수 id 값을 가져오는 것임

- 데이터 1개를 돌려주므로 resultType 을 DTO 의 alias 인 member 로 설정

- if(rs.next()) 안에서 수행했던 member.setId(rs.getString("id")) 를 더이상 쓰지 않음, 돌려주는 객체 member 에 자동으로 setter 메소드로 값을 다 저장해서 돌려준다

+ DTO 프로퍼티명과 컬럼명이 일치하는 경우에만 가능

 

Mapper 의 select SQL문을 select() 메소드로 불러와서 실행 2(MemberDao.java 부분)

- 위의 select() 와는 다른 메소드, 이건 로그인 시 회원 인증을 해주는 chk() 메소드이다

	public int chk(Member member) {
		int result = 0;
		SqlSession session=null;
		try { session = getSession(); 
			Member mem = (Member) session.selectOne("select", member.getId());
			if (mem.getId().equals(member.getId())) {
				result = -1;
				if (mem.getPassword().equals(member.getPassword())) {
					result = 1; // 회원인증 성공
				}
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}

- 로그인 시 회원인증을 처리하는 부분이다

- mem.getId() 는 DB에 저장된 id 를 의미하고, member.getId()는 사용자가 입력한 id 를 의미

- mem.getPassword() 는 DB에 저장된 password 를 의미하고, member.getPassword()는 사용자가 입력한 password 를 의미

- id, password 가 모두 일치할때는 변수 result 에 1 을 저장해서 리턴함, 이 값으로 로그인 성공을 판별하는 것은 loginPro.jsp 에서 처리함

- id 는 일치하되, passoword 가 일치하지 않을때는 변수 result 에 -1 을 저장해서 리턴, 이것도 loginPro.jsp 에서 처리

 

- Mapper 파일 member.xml 부분

	<select id="select" parameterType="String" resultType="member">
		select * from member22 where id = #{id}
	</select>

회원가입 기능

Model 1 & MyBatis 회원 가입 흐름

1. 처음 index.jsp 실행 -> loginForm.jsp -> '회원가입' 눌러서 joinForm.jsp 로 넘어감

2. joinForm.jsp 에서 입력된 값을 id, password 네임값에 저장해서 joinPro.jsp 로 전달

3. joinPro.jsp 에서 useBean 과 setProperty 로 DTO 객체 mem 생성 후 값 저장, DAO 객체의 insert(mem) 호출함

4. DAO 에서 insert() 메소드를 실행, 그 안에서 Mapper 파일의 insert SQL문을 id 로 불러온다

- insert() 가 잘 수행되면 1을 돌려줌

5. joinPro.jsp 로 돌아오고, 회원가입 성공 시 loginForm.jsp 로 넘어간다, 실패(중복아이디)시 이전페이지로 이동

 

- joinForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>

<body>
<h2>회원가입</h2>
<form action="joinPro.jsp">
	아이디 : <input type="text" name="id" required="required"><p>
	암호 : <input type="password" name="password" required="required"><p>
	<input type="submit" value="확인">
</form>
</body>
</html>

 

- joinPro.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="dao.*"%>

<jsp:useBean id="mem" class="model.Member"/>
<jsp:setProperty property="*" name="mem"/>

<%
MemberDao md = new MemberDao();
int result = md.insert(mem);

if (result>0) {
%> 
	<script type="text/javascript">
		alert("가입성공");
		location.href="loginForm.jsp";
	</script>
<%
} else {
%> 
	<script type="text/javascript">
		alert("가입 실패");
		history.go(-1);
	</script>
<%
}
%>

 

- MemberDao.java 에서 insert() 메소드 부분만

	public int insert(Member member) {
		int result = 0;
		SqlSession session=null;
		try { 
			session = getSession(); 
			result = session.insert("insert", member);			
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}

 


- member.xml 에서 회원 가입을 처리하는 SQL문 부분 (id 가 insert)

	<insert id="insert" parameterType="member">
		insert into member22 values (#{id}, #{password})
	</insert>

로그인 기능

Model 1 & MyBatis 로그인 흐름

1. 처음 index.jsp 실행 -> loginForm.jsp 로 넘어감

2. loginForm.jsp 에서 입력된 값을 id, password 네임값에 저장해서 loginPro.jsp 로 전달

3. loginPro.jsp 에서 useBean 과 setProperty 로 DTO 객체 mem 생성 후 값 저장, DAO 객체의 chk(mem) 호출함

4. DAO 에서 chk() 메소드를 실행, 그 안에서 Mapper 파일의 select SQL문을 id 로 불러온다, selectOne() 가 잘 수행되면 DB에 저장된 사용자의 정보를 가져옴

- 다운캐스팅 해서 DTO 객체에 저장 (다운캐스팅 생략해도 작동한다)

- 그 정보가 사용자가 입력한 값과 일치시 result 에 1을 저장해서 돌려준다

5. loginPro.jsp 로 돌아오고 회원 인증 성공 시 id 를 세션 공유 설정하고 main.jsp 로 넘어간다, 실패(중복아이디)시 이전페이지로 이동

 

- loginForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>

<body>
<h2>로그인</h2>
<form action="loginPro.jsp" >
	아이디 : <input type="text" name="id" required="required"><p>
	암호 : <input type="password" name="password" required="required"><p>
	<input type="submit" value="확인">
</form>
<p><a href="joinForm.jsp">회원가입</a>
</body>

</html>

 

- loginPro.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="dao.MemberDao"%>

<jsp:useBean id="mem" class="model.Member"/>
<jsp:setProperty property="*" name="mem"/>

<%
MemberDao md = new MemberDao();
int result = md.chk(mem);

if (result==1) {
	session.setAttribute("id",mem.getId());
%> 
	<script type="text/javascript">
		alert("환영함");
		location.href="main.jsp";
	</script>
<%
} else if (result == -1) {
%> 
	<script type="text/javascript">
		alert("비번이 다르다");
		history.go(-1);
	</script>
<%
} else {
%> 
	<script type="text/javascript">
		alert("그런 ID가 없습니다.");
		history.go(-1);
	</script>
<%
}
%>

 

- MemberDao.java 에서 chk() 메소드 부분만

	public int chk(Member member) {
		int result = 0;
		SqlSession session=null;
		try { session = getSession(); 
			Member mem = (Member) session.selectOne("select", member.getId());
			if (mem.getId().equals(member.getId())) {
				result = -1;
				if (mem.getPassword().equals(member.getPassword())) {
					result = 1; // 회원인증 성공
				}
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}

 

- member.xml 에서 로그인(회원 인증)을 처리하는 SQL문 부분 (id 가 select)

	<select id="select" parameterType="String" resultType="member">
		select * from member22 where id = #{id}
	</select>

 


DAO 클래스 상단에서 MyBatis 지원 클래스 / 인터페이스 import



중간 페이지 기능

- 로그인 성공시 이동하는 main.jsp 내용을 보자

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>

<%
 	String id = (String) session.getAttribute("id");
if (id == null || id.equals("")) {
	response.sendRedirect("loginForm.jsp");
} else if (id.equals("master")) {
	out.println("관리자 모드!");	
} 
%>

<body>
	로긴함
	<p>
		정보수정<br>
<%
		if (id != null) {
		if (id.equals("master")) {
%>
		<a href="list.jsp">회원명단</a><br>
<%
		}}
%>
		<a href="logout.jsp">로그아웃</a>
		
</body>
</html>

-로그인 성공시 공유한 공유값 id 값을 가져와서 사용

- getAttribute() 로 공유값을 가져올때는 다운캐스팅을 해야함

- 받아온 공유값인 id 가 null 인 경우, Redirect 포워딩 팡식으로 loginForm.jsp 로 넘어감

- 받아온 공유값인 id 가 "master" 인 경우 "관리자 모드!" 라는 메세지가 출력됨

<body 태그 안>

- 받아온 공유값인 id가 "master" 인 경우 "회원명단" 출력, 클릭시 list.jsp 로 이동하도록 설정

- 어느 아이디로 로그인되었느냐에 따라 main.jsp 내용이 달라짐

- '로그아웃' 클릭시 logout.jsp 로 이동 : 세션 삭제, 로그아웃 메세지 출력하고 로그인 폼으로 이동하는 작업을 한다


로그아웃 기능

- '로그아웃' 을 클릭시 넘어가는 logout.jsp 를 보자

- logout.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<%
session.invalidate();
%>

<script type="text/javascript">
	alert("로그아웃 됨");
	location.href="loginForm.jsp";
</script>

</body>
</html>

1. 세션 삭제(끊어줌)

2. 로그아웃 메세지 출력하고 loginForm.jsp 로 이동

- 이 두가지 작업만 한다

- DB 연동 안함


목록 페이지 기능

- '회원명단' 을 클릭시 넘어가는 list.jsp 를 보자

- list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="java.util.List"%>
<%@page import="model.Member"%>
<%@page import="dao.MemberDao"%>

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

회원 명단
<table border=1><tr><th>아이디</th><th>비밀번호</th><th>수정</th><th>삭제</th></tr>

<%
	MemberDao md = new MemberDao();
	List<Member> list = md.list();
	
for (int i = 0;i<list.size();i++){
%>
	<tr><td><%=list.get(i).getId() %></td><td><%=list.get(i).getPassword() %></td>
	<td><input type="button" value="수정" onclick='location.href="updateForm.jsp?id=<%=list.get(i).getId() %>"'></td>
	<td><input type="button" value="삭제" onclick='location.href="delete.jsp?id=<%=list.get(i).getId() %>"'></td></tr>
<%
}
%>
</table>

<a href="main.jsp">메인으로</a>

</body>
</html>

- id 가 master 인 관리자 계정만 볼 수 있다

- DB 연동을 수행해서 전체를 검색한다, DAO 객체 생성해서 전체 회원 목록을 구해오는 list() 메소드 호출

- 결과를 list 로 돌려받은 다음 반복문에서 get() 과 getter 메소드로 값을 가져와서 테이블에 뿌리고 있다

- '수정' 또는 '삭제' 를 클릭시 해당 회원의 아이디값을 넘기면서 updateForm.jsp 또는 delete.jsp 로 이동한다

- 관리자 모드이므로 '삭제' 누르면 바로 삭제되도록 했다

- 이 list() 메소드가 정의되어 있는 DAO 클래스의 부분을 보자

 

- MemberDao.java 에서 list() 메소드 부분만

	public List<Member> list() {
		List<Member> list = new ArrayList<Member>();
		SqlSession session=null;
		try { session = getSession(); 
			list = session.selectList("list");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return list;
	}

- 제네릭을 쓰고 있으므로 리스트에서 get() 으로 가져올때 자료형 생략 가능

- SqlSession 객체를 구해와야만 selectList() 를 사용 가능하므로 getSession() 호출해서 SqlSession 객체 구해오기

- 메소드 getSession() 안에서 DB와 연동하는 것임

- List 객체 list 를 업캐스팅으로 생성

- selectList() 메소드는 검색되는 결과가 2개 이상인 경우 실행하는 메소드

- selectList() 메소드 첫번째 매개변수로 Mapper 파일의 id "list" 를 써서 해당 select SQL문을 호출

- selectList() 메소드에서 전달할 값은 없으므로 두번째 매개변수는 쓰지 않았다

- selectList() 메소드로 돌려받은 리스트를 객체 list 에 저장해서 list.jsp로 리턴

 

MyBatis 연동 전 / 후

- while(rs.next()) 에서 DTO 객체 생성하고 setter 메소드로 저장시킨 후 리스트에 객체를 저장하는 행동을 반복했다

- MyBatis 와 연동하면 이런 작업이 Mapping 을 통해서 자동으로 실행되고, 리스트를 반환시켜줌

 

selectOne() vs selectList()

- 돌려줄 데이터가 1개이면 selectOne() 사용, 2개 이상이면 selectList() 사용

- selectOne() 은 리턴자료형이 Object, selectList() 는 리턴자료형이 List

 

데이터를 2개 이상 검색해서 돌려줄때 주의사항

- Mapper 파일의 select 문에서 데이터를 2개이상 돌려주더라도 resultType 에는 리스트가 아니라 DTO 가 오면 된다

ex) 돌려주는 자료형이 List<int> 이더라도 resultType 에 List 가 아닌 int 를 씀

ex) 돌려주는 자료형이 List<DTO> 이면 resultType 에 List 가 아닌 DTO(alias 별칭 member) 를 씀

 

- Mapper 파일인 member.xml 에서 해당 SQL 문 부분만 살펴보자

- member.xml 에서 전체 목록을 가져오는 SQL문 부분

	<select id="list" resultType="member">
		select * from member22
	</select>

+ select는 반드시 resultType 을 써야함, 받을 값은 없으므로 parameterType 은 쓰지 않았다

- 전체 데이터를 검색하고 있다


정보수정 기능 : 수정폼

- list.jsp 에서 '수정' 을 눌렀을때 수정하는 기능

<td><input type="button" value="수정" onclick='location.href="updateForm.jsp?id=<%=list.get(i).getId() %>"'></td>

- '수정' 클릭시 onClick 에 의해 updateForm.jsp 로 넘어간다, 넘어갈 때 해당 회원의 id 값을 get 방식으로 전달

- 세션에 공유된 id값은 관리자 id값인 "master" 이고 여기서 넘어가는 id값은 각 회원의 id 값임

 

- 먼저 수정폼을 해야함

- updateForm.jsp 파일을 보자

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="model.Member"%>
<%@page import="dao.MemberDao"%>

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<%
	String id = request.getParameter("id");
	MemberDao md = new MemberDao();
	Member mem = md.select(id);
%>

<h2>정보 수정</h2>
<form action="updatePro.jsp">
	<input type="hidden" name="id" value="<%=mem.getId() %>">
	<table><tr><td>아이디</td>
			   <td><input type="text" name="id" value="<%=mem.getId() %>" disabled="disabled"></td></tr>
			<tr><td>암호</td>
				<td><input type="text" name="password" value="<%=mem.getPassword() %>"></td></tr>
			<tr><td colspan="2" align="right">
					<input type="submit" value="변경"> 
					<input type="button" onclick="history.go(-1)" value="취소"></td></tr>
	</table>
</form>

</body>
</html>

- 수정폼이므로 DB와 연동하여 1명에 대한 상세정보를 수정폼에 뿌려줘야한다

- list.jsp 에서 넘어온 회원의 id값을 받고 변수 id 에 저장

- DAO 객체를 생성해서 DAO 의 select() 메소드 호출

- select() 메소드를 호출하며 id 값을 넘긴다, 1명에 대한 상세정보를 구해오므로 리턴자료형 DTO

- 검색결과를 받은 mem 객체를 통해 mem.getId() 와 mem.getPassword() 로 id, password 를 수정폼에 뿌려줌

- id 는 disabled 로 설정되어있으므로 값이 action 의 페이지로 넘어가지 않는다, 그래서 hidden 객체로 따로 id 를 넘김

+ readonly 설정은 값이 넘어감, disabled 는 값이 안넘어감 * HTML readonly vs disabled 속성 설명 아래에

- 이후 수정폼에 상세정보를 뿌린 후, 사용자가 수정폼을 입력하고 '변경' 버튼을 누르면 id, password 를 가지고 updatePro.jsp로 이동한다

 

- MemberDao.java 에서 select() 메소드 부분만

	public Member select(String id) throws SQLException {
		Member mem = null;
		SqlSession session=null;
		try { session = getSession(); 
		mem = (Member) session.selectOne("select", id);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return mem;
	}

- getSession() 으로 SqlSession 객체 구해오기

- 1개 데이터를 검색하므로 selectOne() 메소드 호출

- 첫번째 매개변수에 들어가는 Mapper 파일의 id는 "select", 두번째 매개변수에 들어가는 전달될 값은 매개변수로 받은 id

- selectOne() 을 호출하고 결과를 Object 형으로 돌려주므로 다운캐스팅해서 DTO 객체 member 에 저장하고 그걸 리턴

+ 다운캐스팅 명시 생략 가능

- 이 select() 메소드 실행 후 이 메소드를 호출한 updateForm.jsp 로 돌아감  

 

- member.xml 에서 수정폼을 처리하는 SQL문 부분 (id 가 select)

	<select id="select" parameterType="String" resultType="member">
		select * from member22 where id = #{id}
	</select>

- 각 회원의 id 를 받으므로 paramterType 을 String 으로 설정

- 1명에 대한 상세정보를 리턴하므로 resultType 은 DTO 클래스 별칭인 member

- 전달되는 변수명이 id 이므로, #{id} 로 전달되는 값을 가져옴, parameterType 은 "String"

 

- URL 을 보면, id 값이 get 방식으로 넘어갔음을 볼 수 있다

 

+ 매개변수로 전달된 id 를 전달

- selectOne() 에서 변수 id 에 값이 전달되고 있다

- 그래서 member.xml 에서 #{id} 로 SQL문 where절 작성


정보수정 기능 : 실제 수정

- 이후 수정폼에 상세정보를 뿌린 후, 사용자가 수정폼을 입력하고 '변경' 버튼을 누르면 id, password 를 가지고 updatePro.jsp 로 이동한다

 

- updatePro.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="dao.MemberDao"%>
<%@page import="model.Member"%>

<jsp:useBean id="mem" class="model.Member"/>
<jsp:setProperty property="*" name="mem"/>

<%
	MemberDao md = new MemberDao();
	int result = md.update(mem);

if (result == 1) {
%>
	<script type="text/javascript">
		alert("수정 성공");
		location.href="list.jsp";
	</script>
<%
} else {
%>
	<script type="text/javascript">
		alert("수정 실패");
		history.go(-1);
	</script>
<%
}
%>

- useBean, setProperty action tag 사용해서 수정폼에서 hidden 으로 넘어온 id 값과, 비밀번호 입력양식에서 넘어온 password 값을 받아서 DTO 객체 mem 에 저장

+ 입력양식의 name 값과 DTO 의 프로퍼티명이 일치할때만 가능

- DAO 객체 생성 후 DAO의 update() 메소드를 호출하며 매개변수로 객체 mem 을 전달

- 성공시 메세지 출력 후 list.jsp 로 돌아감, 즉 다시 회원목록을 구해서 뿌려준다

 

- MemberDao.java 에서 update() 메소드 부분만

	public int update(Member mem) {
		int result = 0;
		SqlSession session=null;
		try { session = getSession(); 
			result = session.update("update", mem);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}

- update() 메소드의 첫번째 매개변수에 Mapper 파일의 id 명인 "update" 를, 두번째 매개변수에 넘겨줄 값인 객체 mem 을 적기

- update() 메소드가 실행되고 성공적으로 수정하면 1 을 리턴해줌, 그 1 을 result 변수에 저장해서 updatePro.jsp 로 리턴함

 

- member.xml 에서 정보 수정을 처리하는 SQL문 부분 (id 가 update)

	<update id="update" parameterType="member">
		update member22 set password = #{password} where id = #{id}
	</update>

- DTO 객체가 넘어올때 #{id} 는 member.getId() 를 의미, #{password} 는 member.getPassword() 를 의미

- update SQL문이 수행되고 자동으로 수정한 데이터 개수를 돌려줌, 즉 1 을 리턴해줌


* HTML readonly vs disabled 속성

 

공통점

- 입력 양식의 값을 수정할 수 없도록 비활성화 시켜주는 역할

차이점

- readonly 속성으로 설정된 양식은 action 으로 설정된 페이지로 값이 전달되지만,
- disabled 속성으로 설정된 양식은 action 으로 설정된 페이지로 값이 전달되지 않는다.


정보수정 기능 : 삭제

- list.jsp 에서 '삭제' 를 눌렀을때 수정하는 기능

<td><input type="button" value="삭제" onclick='location.href="delete.jsp?id=<%=list.get(i).getId() %>"'></td></tr>

- '삭제' 클릭시 onClick 에 의해 delete.jsp 로 넘어간다, 넘어갈 때 해당 회원의 id 값을 get 방식으로 전달

- 즉 삭제폼을 거치지 않고 바로 삭제되게 해뒀다

 

- delete.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="dao.MemberDao"%>

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
String id = request.getParameter("id");
MemberDao md = new MemberDao();
int result = md.delete(id);
if (result == 1) {
	%>
	<script type="text/javascript">
	alert("삭제성공");
	location.href="list.jsp";
	</script>
	<%
} else {
	%>
	<script type="text/javascript">
	alert("실패");
	location.href="list.jsp";
	</script>
	<%
}
%>
</body>
</html>

- list.jsp 로 부터 전달받은 id 값을 받아서 저장

- DAO 객체를 생성하고 DAO의 delete() 메소드 실행, 특정 회원 삭제를 해야하므로 매개변수로 id 값을 전달한다

- 메소드 호출하여 삭제 성공시 목록 페이지인 list.jsp 로 돌아간다

 

- MemberDao.java 에서 delete() 메소드 부분만

	public int delete(String id) {
		int result = 0;
		SqlSession session=null;
		try { session = getSession(); 
			result = session.delete("delete", id);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return result;
	}

 

- member.xml 에서 회원 삭제(탈퇴) 를 처리하는 SQL문 부분 (id 가 delete)

	<delete id="delete" parameterType="String">
		delete from member22 where id = #{id}
	</delete>

- #{id} 는 DAO에서 session.delete() 메소드 실행시 두번째 매개변수로 넘어온 변수 id 를 의미함

- delete SQL문이 수행되고 자동으로 삭제한 데이터 개수를 돌려줌, 즉 1 을 리턴해줌

- 자동으로 삭제한 데이터 개수를 돌려주므로 select 외의 insert/update/delete 는 returnType 을 쓰면 안된다

 

#{id} 의 의미

1. 일반 변수 id 가 넘어왔을때 #{id} 는 그 변수 id 값을 의미함

2. DTO 객체 member 가 넘어왔을때 #{id} 는 member.getId() 를 의미, 즉 객체 안의 프로퍼티 값을 의미

- 같은 코드지만 다른 의미이다

 

- master 로 로그인 한 뒤 회원 목록 페이지인 list.jsp 에서 회원을 삭제해보자

 


MyBatis

- DB 연동을 처리하기 위한 프레임 워크

 

프로그램 확장 시

- MyBatis 환경설정 파일은 하나만 있으면 된다

- Mapper 파일은 테이블의 개수에 비례해서 늘어난다

 

태그 맞추기

- Mapper 파일인 member.xml의 루트 엘리먼트는 mapper 이다

- MyBatis 환경 설정 파일에서 태그를 mapper 로 해야한다

- Mapper 파일의 루트 엘리먼트와 MaBatis 환경설정 파일에서 Mapper 파일을 불러올때 태그 이름을 맞춰야한다

 

Mapper 파일의 id 충돌문제

- 같은 Mapper 파일 뿐 아니라 다른 Mapper 파일 끼리도 id 값을 다르게 설정해야한다

- member 테이블, board 테이블 이 있을때 member.xml , board.xml 처럼 Mapper 파일이 각각 생성됨

- 이때 서로 다른 두 Mapper 파일에서도 같은 id값을 쓰면 안된다!, 그래야 DAO에서 불러올때 충돌되지 않음

- id 값이 중복되는 문제 해결하기 위해 namespace

 

namespace

- 논리적인 영역 이름

- namespace를 통해 id값 중복되어 DAO 에서 불러올때 충돌되는 문제 해결가능

+ 일반적으로 namespace를 테이블명+"ns" 로 설정함

- Mapper 파일에서 namespace 값만 서로 다르게 설정하면 다른 Mapper 파일끼리 같은 id값을 사용할 수 있다

- Mapper 파일인 member.xml 부분

- 그래서 insert id 를 불러오는 DAO 코드에서 namespace 를 함께 써서 충돌 피할 수 있다

- 이렇게 하면 다른 테이블의 Mapper 파일에서 id 값을 insert 로 쓴다고 해도 충돌하지 않음

+ Recent posts