게시판 직접 만들어보기 (이어서)

환경 설정 파일 작성(세팅) 순서

1) pom.xml

2) web.xml

3) servlet-context.xml

4) configuration.xml

5) board.xml

6) root-context.xml

- 4), 5), 6) 은 DB 연동 관련 내용

- configuration.xml, board,xml(Mapper 파일) 환경설정 파일은 resources 폴더 안에 넣는다

- configuration.xml 에서 DTO 개수에 따라 typeAlias 설정이 늘어남


게시판 직접 만들어보기 (이어서)

상세 페이지

- 저번에 만든 목록 페이지 boardlist.jsp 에서 제목을 클릭하면 상세페이지로 넘어간다

<a href="boardcontent.do?no=${b.no}&page=${page}">${b.subject}</a>

- "boardContent.do" 로 요청하며 글 번호 no 와 페이지 번호 page 를 가져간다

- 글 번호와 페이지 번호를 최대 3번까지 전달하게 되는데 여기가 전달하는 출발점이다

ex) 목록 페이지 -> 상세 페이지 -> 수정/삭제 폼

 

- Controller 클래스 BoardController.java 에서 "boardContent.do" 요청 부분을 처리하자

	// 상세 페이지 : 조회수 1 증가 + 상세 정보 구하기
	@RequestMapping("boardcontent.do")
	public String boardcontent(int no, int page, Model model) {
		
		bs.updatecount(no);	// 조회수 1 증가
		Board board = bs.getBoard(no);	// 상세 정보 구하기
		String content = board.getContent().replace("\n", "<br>");
		
		model.addAttribute("board", board);
		model.addAttribute("content", content);
		model.addAttribute("page", page);
		
		return "board/boardcontent";
	}

- 앞에서 넘어온 글 번호, 페이지 번호의 변수명과 같은 이름을 써서 바로 값이 들어가게끔 한다, @RuequestParam 생략

- 앞에서 받아온 Service 객체 bs 를 사용해서 updatecount() , getBoard() 메소드를 호출한다

상세 페이지 Controller 처리에서 해야할 DB 연동 2가지

1. updatecount() : 조회수 증가 Update 문

- 글 번호 no 를 전달한다

- 리턴 받지 않기로 함

2. getBoard() : 상세 정보 구하기 Select 문

- 글 번호 no 를 전달한다

- 리턴은 DTO 클래스 Board 형으로 받는다

 

- 이 후 내용 content 의 줄바꿈 처리를 하기 위해 내용을 따로 구하고 replace() 메소드를 써서 바꾼 후 내용을 따로 Model 객체에 저장한다

View 페이지로 가져갈 값 3가지

1. getBoard() 에서 리턴받은 상세 정보 DTO 객체 board

2. 줄이 바뀐 내용 content

3. 목록 페이지에서 넘어온 페이지 번호 page

- 글 번호는 객체 board 안에 들어있으므로 페이지 번호만 따로 가져간다

- 이후 board 폴더 하위에 boardcontent.jsp 를 생성해야함

 

<조회수 증가 Update 문>
- Service 클래스 BoardService.java 에서 updatecontent() 메소드 작성

	public void updatecount(int no) {
		dao.updatecount(no);
	}

- 리턴 받지 않으므로 return 을 쓰지 않는다, 그냥 DAO의 메소드 updatecount() 만 호출함

 

- DAO 클래스 BoardDao.java 에서 updatecontent() 메소드 작성

	public void updatecount(int no) {
		session.update("hit", no);
	}

- Mapper 파일에서 id 가 "hit" 인 SQL문을 불러옴, 전달하는 값은 글 번호인 no

 

- Mapper 파일 board.xml 에서 id 가 "hit" 인 조회수 증가 Update SQL문 작성

	<!-- 조회수 1 증가 -->
	<update id="hit" parameterType="int">
		update myboard set readcount=readcount+1 where no=#{no}
	</update>

 - 넘어온 값이 글 번호 no 이므로 parameterType 은 "int", where 절에는 #{no} 를 사용

 

<상세 정보 구하기 Select 문>

- Service 클래스 BoardService.java 에서 getBoard() 메소드 작성

	public Board getBoard(int no) {
		return dao.getBoard(no);
	}

- 위에서 주입받은 DAO 객체 dao 로 getBoard() 메소드 호출, 글 번호 전달

 

- DAO 클래스 BoardDao.java 에서 getBoard() 메소드 작성

	public Board getBoard(int no) {
		return session.selectOne("content", no);
	}

- Mapper 파일에서 id 가 "content" 인 SQL문을 불러옴, 전달하는 값은 글 번호인 no

- 글 1개 에 대한 상세정보를 가져오므로 selectOne() 메소드 사용

 

- Mapper 파일 board.xml 에서 id 가 "content" 인 조회수 증가 SelectSQL문 작성

	<!-- 상세정보 구하기 -->
	<select id="content" parameterType="int" resultType="board">
		select * from myboard where no = #{no}
	</select>

 

- View 페이지인 boardcontent.jsp 를 WEB-INF/views/board/ 폴더 하위에 생성하기

- boardcontent.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상세 페이지</title>
</head>
<body>

<table border=1 width=400 align=center>
	<caption>상세 페이지</caption>
	<tr>
		<td>작성자</td>
		<td>${board.writer}</td>
	</tr>
	<tr>
		<td>날짜</td>
		<td>
			<fmt:formatDate value="${board.register}"
				pattern="yyyy-MM-dd HH:mm:ss"/>
		</td>
	</tr>
	<tr>
		<td>조회수</td>
		<td>${board.readcount}</td>
	</tr>
	<tr>
		<td>제목</td>
		<td>${board.subject}</td>
	</tr>
	<tr>
		<td>내용</td>
		<td>
			<pre>${board.content}</pre>
			${board.content}
		</td>
	</tr>
	<tr>
		<td colspan=2 align=center>
			<input type="button" value="목록"
			onClick="location.href='boardlist.do?page=${page}'"/>
			<input type="button" value="수정"
			onClick="loacation.href='boardupdateform.do?no=${board.no}&page=${page}'"/>
			<input type="button" value="삭제"
			onClick="loacation.href='boarddeleteform.do?no=${board.no}&page=${page}'"/>
		</td>
	</tr>
</table>

</body>
</html>

- 날짜 시간 패턴을 지정하기 위해 국제화 라이브러리 fmt 를 불러옴

- 내용의 줄바꿈을 처리할때는 <pre></pre> 사용 또는 앞에서 replace() 를 써서 바꿔서 저장한 내용 ${content} 를 불러옴

- 현재는 내용이 한 줄이라 줄바꿈이 제대로 되었는지 확인이 어려움

 

버튼 처리 : 목록 버튼 (boardcontent.jsp 부분)

			<input type="button" value="목록"
			onClick="location.href='boardlist.do?page=${page}'"/>

- 상세 페이지에서 '목록' 버튼을 누르면 다시 목록 페이지로 돌아간다

- 돌아갈때 페이지번호를 전달해서 원래 페이지로 돌아가게끔 한다

- 다시 돌아옴

 

버튼 처리 : 수정 버튼 (boardcontent.jsp 부분)

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

- 수정 폼으로 가도록 "boardupdateform.do" 로 요청한다, 요청하면서 글 번호, 페이지 번호를 전달

- 글 번호는 객체 board 안에 있으므로 ${board.no} 로 가져오기

 

버튼 처리 : 삭제 버튼 (boardcontent.jsp 부분)

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

- 삭제 폼으로 가도록 "boarddeleteform.do" 로 요청한다, 요청하면서 글 번호, 페이지 번호를 전달

- 글 번호는 객체 board 안에 있으므로 ${board.no} 로 가져오기

 


글 수정 폼

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

- 상세 페이지에서 '수정'을 누르면 "boardupdateform.do" 로 요청한다, 요청하면서 글 번호, 페이지 번호를 전달

 

- Controller 클래스 BoardController.java 에서 "boardupdateform.do" 요청 부분을 처리하자

	// 수정 폼
	@RequestMapping("boardupdateform.do")
	public String boardupdateform(int no, int page, Model model) {
		
		Board board = bs.getBoard(no); // 상세 정보 구하기
		
		model.addAttribute("board", board);
		model.addAttribute("page", page);
		
		return "board/boardupdateform";
	}

- 앞에서 넘어온 글 번호, 페이지 번호의 변수명과 같은 이름을 써서 바로 값이 들어가게끔 한다, @RuequestParam 생략

- 앞의 상세 페이지를 처리할때 만든 상세정보를 가져오는 메소드 getBoard() 를 호출해서 상세 정보 객체를 구해온다

- 객체 board 와 페이지 번호 page 를 Model 객체에 저장

 

- getBoard() 메소드에 대해서는 이전에 설명했으므로 생략

 

- View 페이지 boardupdateform.jsp 를 board 폴더 하위에 생성

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

<form method=post action="boardupdate.do">
<input type="hidden" name="no" value="${board.no}"/>
<input type="hidden" name="page" value="${page}"/>
<table border=1 width=400 align=center>
	<caption>글수정</caption>
	<tr><th>작성자명</th>
		<td><input type=text name="writer" required="required"
		value="${board.writer}" autofocus="autofocus"></td>
	</tr>
	<tr><th>비밀번호</th>
		<td><input type=password name="passwd" required="required"></td>
	</tr>
	<tr><th>제목</th>
		<td><input type=text name="subject" required="required"
		value="${board.subject}"></td>
	</tr>
	<tr><th>내용</th>
		<td><textarea cols=40 rows=5 name="content" required="required">
		${board.content}</textarea></td>
	</tr>
	<tr><td colspan=2 align=center>
			<input type=submit value="글수정">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

- boardform.jsp 파일 내용을 복붙 후 수정

- value 속성을 이용해서 앞에서 넘어온 객체 board 안의 값을 뿌려준다

- 수정 폼 boardupdateform.jsp 에서 정보 입력 후 '수정' 을 누르면 "boardupdate.do" 로 요청한다

- hidden 객체를 사용해서 글 번호와 페이지 번호를 "boardupdate.do" 를 요청하며 전달한다

- 수정 위해 글 번호 필요, 수정 후 원래 페이지로 돌아가기 위해 페이지 번호가 필요

- 수정폼에서 넘어간 값들은 받을 때 DTO 객체에 저장해야한다, 페이지 번호 page 만 DTO 안에 들어갈 수 없으므로 따로 받아야한다

- 비밀번호 값이 맞는 경우에만 수정할 것

 


글 수정

- 앞의 글 수정 폼 boardupdateform.jsp 에서 입력 후 "글수정" 을 누르면 "boardupdate.do" 료 요청

 

- Controller 클래스 BoardController.java 에서 "boardupdate.do" 요청 부분을 처리하자

	// 글 수정
	@RequestMapping("boardupdate.do")
	public String boardupdate(Board board, int page, Model model) {
		int result = 0;
		Board old = bs.getBoard(board.getNo());
		
		// 비번 비교
		if(old.getPasswd().equals(board.getPasswd())) { // 비번 일치시
			result = bs.update(board); // 글 수정
		} else { // 비번 불일치시
			result = -1;
		}
		
		model.addAttribute("result", result);
		model.addAttribute("board", board);
		model.addAttribute("page", page);
		
		return "board/updateresult";
	}

- 넘어온 값들은 DTO 객체에 저장해야한다, @ModelAttribute 를 써서 (생략) 넘어온 값들을 바로 DTO 객체 board 에 저장

- 페이지 번호 page 만 DTO 안에 들어갈 수 없으므로 @RequestParam("page") (생략) 을 써서 따로 받는다

- 수정 성공 메세지를 뿌릴 것이므로 result 값을 Model 객체에 저장해서 넘겨줄 것, Model 객체도 선언함

글 수정 요청에서 해야할 DB연동 2가지

1. 비번 비교 Select SQL

- 비번 비교를 위해 hidden 으로 넘어온 값인 글 번호 no 를 써서 DB에 저장된 해당 글의 상세 정보를 가져옴 

- 이때, 상세 정보를 가져오는 메소드인 getBoard() 를 호출해서 상세정보를 구함

2. 글 수정 Update SQL

- Service 클래스의 메소드 update() 호출

- 전달하는 값은 수정할 데이터인 board

- 돌려받는 값은 글 수정 성공 개수, 즉 성공시 1 을 result 에 돌려받음

 

<View 와 관련>

- 이후 View 에서 수정 성공 / 실패 메세지 처리를 할 것이므로 Model 객체에 result 를 저장해서 전달

- View 에서 원래 페이지로 돌아가기 위해 Model 객체에 페이지 번호 page 를 저장해서 전달

- 수정 완료 후 상세페이지로 갈 때는 글 번호 no 와 페이지 번호 page 를 가져가야하므로  Model 객체에 글 번호를 가지고 있는 객체 board 를 Model 객체에 저장해서 전달

- 만약, 목록페이지로 갈 때는 Model 에 객체 board 를 가져갈 필요없다

 

- Service 클래스 BoardService.java 에서 update() 메소드 생성

	public int update(Board board) {
		return dao.update(board);
	}

 

- DAO 클래스 BoardDao.java 에서 update() 메소드 생성

	public int update(Board board) {
		return session.update("update", board);
	}

- Mapper 파일에서 id 가 "update" 인 SQL문을 불러옴, 전달하는 값은 수정할 데이터인 객체 board

- 수정 성공시 1 을 자동으로 돌려준다

 

- Mapper 파일 board.xml 에서 id 가 "update" 인 Update SQL문 작성

	<!-- 글 수정 -->
	<update id="update" parameterType="board">
		update myboard set writer=#{writer},subject=#{subject},
		content=#{content},register=sysdate where no=#{no}
	</update>

- Mapper 파일의 SQL문에선 SQL문 끝에 ; 를 찍으면 안된다

 

- View 페이지 updateresult.jsp 를 board 폴더 하위에 생성 및 작성

<%@ 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>Insert title here</title>
</head>
<body>

<c:if test="${result == 1}">
	<script>
		alert("글 수정 성공");
		location.href="boardlist.do?page=${page}"; // 목록 페이지
//		location.href="boardcontent.do?no=${board.no}&page=${page}"; // 상세 페이지
	</script>
</c:if>
<c:if test="${result != 1}">
	<script>
		alert("글 수정 실패");
		history.go(-1);
	</script>
</c:if>

</body>
</html>

- if 태그를 사용하기 위해 JSTL core 라이브러리를 불러온다

- 수정 성공 후 목록 페이지로 가기 위해서 페이지 번호 page 를 전달해야 한다

- 수정 성공 후 상세 페이지로 가기 위해서 글 번호 no, 페이지 번호 page 를 전달해야한다

- 수정 실패 시 result 는 -1 이므로 "글 수정 실패" 메세지 출력 후 다시 이전 페이지인 수정 폼으로 돌아간다

 


글 삭제폼

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

- 상세 페이지에서 '삭제'를 누르면 "boarddeleteform.do" 로 요청한다, 요청하면서 글 번호, 페이지 번호를 전달

 

- 글 번호는 객체 board 안에 있으므로 ${board.no} 로 가져오기

 

- Controller 클래스 BoardController.java 에서 "boarddeleteform.do" 요청 부분을 처리하자

 

- 앞에서 넘어온 글 번호, 페이지 번호의 변수명과 같은 이름을 써서 바로 값이 들어가게끔 한다, @RuequestParam 생략

- 글 삭제 폼에서는 여기서 글 번호, 페이지 번호 값을 받아서 Model 객체에 저장해서 가져가도 되지만, 글 삭제 폼에서 param 으로 바로 받아도 된다

- 그러므로 여기서는 아무값도 받아서 Model 객체에 저장해서 전달하지 않는다

- Model 2 에서도 View 에서 View 로 갈때 Controller 클래스를 거치지만 Contorller 에서 값을 중간 저장하고 전달하지 않았다, 바로 View 에서 전달한 값을 이동한 View 에서 getParameter() 로 받아왔었음, 여기서도 같은 방법을 사용 가능함

 

- View 페이지 boarddeleteform.jsp 를 생성 및 작성

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

<form method=post action="boarddelete.do">
<input type="hidden" name="no" value="${param.no}"/>
<input type="hidden" name="page" value="${param.page}"/>
<table border=1 width=400 align=center>
	<caption>글삭제</caption>
	<tr><th>비밀번호</th>
		<td><input type=password name="passwd" required="required"></td>
	</tr>
	<tr><td colspan=2 align=center>
			<input type=submit value="글삭제">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

- boardupdateform.jsp 파일 내용을 복붙 후 수정

 

상세 페이지에서 전달된 글 번호, 페이지 번호 받기

- 상세 페이지에서 GET 방식으로 넘어온 값인 글 번호, 페이지 번호를 여기 View 에서 바로 param 으로 받을 수 있다

- 중간에 Controller 클래스를 거치지만 Contorller 에서 중간 저장 후 다시 전달할 필요 없다

- request.getParameter("네임") 은 ${param.네임} 과 같은 의미

<input type="hidden" name="no" value="${param.no}"/>
<input type="hidden" name="page" value="${param.page}"/>

- 이렇게 param 으로 받은 값을 다시 hidden 객체로 전달한다

- 비밀번호를 입력 후 "글삭제" 클릭시 "boarddelete.do" 로 요청한다

- 넘어가는 값 : 글 번호, 페이지 번호, 비밀번호


글 삭제

- 글 삭제 폼 boarddeleteform.jsp 에서 비밀번호 입력 후 "글삭제" 클릭시 "boarddelete.do" 로 요청

 

- Controller 클래스 BoardController.java 에서 "boarddelete.do" 요청 부분을 처리하자

	// 글 삭제
	@RequestMapping("boarddelete.do")
	public String boarddelete(Board board, int page, Model model) {
		int result = 0;
		Board old = bs.getBoard(board.getNo()); // 상세 ㅈ어보 구하기
		
		// 비번 비교
		if(old.getPasswd().equals(board.getPasswd())) { // 비번 일치시
			result = bs.delete(board.getNo());	// 글 삭제
		} else {	// 비번 불일치시
			result = -1;
		}
		model.addAttribute("result", result);
		model.addAttribute("page", result);
		
		return "board/deleteresult";
	}

- 넘어온 값들은 DTO 객체에 저장해야한다, @ModelAttribute 를 써서 (생략) 넘어온 값들을 바로 DTO 객체 board 에 저장

- 페이지 번호 page 만 DTO 안에 들어갈 수 없으므로 @RequestParam("page") (생략) 을 써서 따로 받는다

- 삭제 성공 메세지를 뿌릴 것이므로 result 값을 Model 객체에 저장해서 넘겨줄 것, Model 객체도 선언함

글 삭제 요청에서 해야할 DB연동 2가지

1. 비번 비교 Select SQL

- 비번 비교를 위해 hidden 으로 넘어온 값인 글 번호 no 를 써서 DB에 저장된 해당 글의 상세 정보를 가져옴 

- 이때, 상세 정보를 가져오는 메소드인 getBoard() 를 호출해서 상세정보를 구함

2. 글 수정 Update SQL

- Service 클래스의 메소드 delete() 호출

- 전달하는 값은 삭제할 글 번호인 no

- 돌려받는 값은 글 수정 성공 개수, 즉 성공시 1 을 result 에 돌려받음

 

- 삭제 성공 이후 View deleteresult.jsp 에서 목록 페이지로 갈 것이므로 페이지 번호 page 를 View 로 전달줘야함

+ 상세 페이지로 갈때는 글 번호도 전달해야한다

 

- Service 클래스 BoardService.java 에서 delete() 메소드 생성

	public int delete(int no) {
		return dao.delete(no);
	}

 

- DAO 클래스 BoardDao.java 에서 update() 메소드 생성

	public int delete(int no) {
		return session.delete("delete", no);
	}

- Mapper 파일에서 id 가 "delete" 인 SQL문을 불러옴, 전달하는 값은 수정할 글의 글 번호 no

- 삭제 성공시 1 을 자동으로 돌려준다

 

- Mapper 파일 board.xml 에서 id 가 "delete" 인 DeleteSQL문 작성

	<!-- 글 삭제 -->
	<delete id="delete" parameterType="int">
		delete from myboard where no = #{no}
	</delete>

 

- View 페이지 deleteresult.jsp 를 board 폴더 하위에 생성 및 작성

<%@ 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>Insert title here</title>
</head>
<body>

<c:if test="${result == 1}">
	<script>
		alert("글 삭제 성공");
		location.href = "boardlist.do?page=${page}";
	</script>
</c:if>
<c:if test="${result != 1}">
	<script>
		alert("글 삭제 실패");
		history.go(-1);
	</script>
</c:if>

</body>
</html>

- if 태그를 사용하기 위해 JSTL core 라이브러리를 불러온다

- 삭제 성공 후엔 상세페이지로는 가지 못함, 목록페이지로 이동하자, 이동하면서 페이지 번호를 전달

- 삭제 실패 시 result 는 -1 이므로 "글 수정 실패" 메세지 출력 후 다시 이전 페이지인 수정 폼으로 돌아간다

 

- 삭제 성공시 "boardlist.do"로 요청하며 페이지번호를 전달하므로 원래 페이지로 돌아온다


메일 보내기

- 이메일 보내기 위한 메일 서버를 구축해야 이메일을 보낼 수 있다

- Window 계열에선 Exchange Server 서버 (유료) 사용, Linux 계열에선 Qmail 서버 사용

- Exchange Server 로 직접 서버를 구축(세팅)하지 않더라도 이메일을 제공해주는 서버들이 있다

ex) Naver 메일 또는 Gmail 서버를 사용하면 메일 보내고 받기가 가능하다

- 문자로 보내는 것은 유료가 많음, 메일 보내는 것은 Naver, Google 메일 활용시 무료로 ID, 비번 찾기 구현 가능

 

메일 서버 프로토콜

- Maile 송신(보내기) : STMP (Simple Mail Transfer Protocol) , 기본포트는 25번

- Maile 수신(받기) : POP3(Post Office Protocol 3) , 기본포트는 110번

 

- 네이버 메일 또는 Gmail 에서 이메일 서버를 사용하려면 환경설정이 필요하다

- 환경설정 후 Spring 을 이용해서 메일 서버를 사용해서 메일 보내기 가능

- 주로 회원관리 프로그램의 아이디 찾기, 비밀번호 찾기 시 메일을 보낸다

 

Email 보내기

1. Naver Mail Server 활용 : 네이버 메일에서 STMP 활성화

- 우리는 메일을 보내는 작업을 할 것이므로 네이버에서 SMTP 를 활성화 시켜야한다

- SMTP 기본포트 25번 대신 새로운 포트 465 을 사용하고 있다, 즉 기본 포트는 잘 사용하지 않음, 대부분 포트번호를 바꿔서 사용함

- IMAP(POP3 와 비슷한 기능, 암호화 된 것) 도 사용함으로 설정해준다

 

2. mailTest 프로젝트 가져오기

- 클라우드에서 프로젝트 mailTest 를 다운받아 압축을 풀고 import 한다

 

- 메일을 보내기 위해 pom.xml 부터 먼저 설정

 

3. pom.xml 에 메일 보내기 위한 의존 라이브러리 추가

- 메일을 보내기 위한 의존 라이브러리를 추가해야한다

- 여러가지 라이브러리가 있다, 지금은 그 중 commons-email 라이브러리 사용

- pom.xml 부분

		<!-- EMail -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-email</artifactId>
			<version>1.5</version>
		</dependency>

- 이 부분이 추가되어 있어야 메일 보내기 가능

 

+ web.xml 설정

- web.xml 에 servlet-mapping 의 url-pattern 가 *.do 로 설정되어있다, 확장자 do 로 요청해야한다

 

4. index 파일 실행

- 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>

<a href="send.do">메일 전송</a><br><br>

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

<%
//	response.sendRedirect("send.do");
%>

</body>
</html>

- "메일 전송" 클릭 시 "send.do" 로 요청한다

 

- Contorller 클래스를 보자

- servlet-context.xml 의 base-pacakge 가 controller 이므로 controller 폴더 아래에 Controller 클래스 MailTest.java 가 있다

 

package controller;

import java.util.Random;

import org.apache.commons.mail.HtmlEmail;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MailTest {

	@RequestMapping("/send.do")
	public String send(Model model) {

		Random random = new Random();
		int a = random.nextInt(100);

		// Mail Server 설정
		String charSet = "utf-8";
		String hostSMTP = "smtp.naver.com";
		String hostSMTPid = "내아이디@naver.com";
		String hostSMTPpwd = "내비번"; 	// 비밀번호 입력해야함

		// 보내는 사람 EMail, 제목, 내용
		String fromEmail = "내아이디@naver.com";
		String fromName = "친절한 Lay씨";
		String subject = "Overflow인증메일입니다.";

		// 받는 사람 E-Mail 주소
		String mail = "내아이디@naver.com";

		try {
			HtmlEmail email = new HtmlEmail();
			email.setDebug(true);
			email.setCharset(charSet);
			email.setSSL(true);
			email.setHostName(hostSMTP);
			email.setSmtpPort(587);

			email.setAuthentication(hostSMTPid, hostSMTPpwd);
			email.setTLS(true);
			email.addTo(mail, charSet);
			email.setFrom(fromEmail, fromName, charSet);
			email.setSubject(subject);
			email.setHtmlMsg("<p align = 'center'>Overflow에 오신것을 환영합니다.</p><br>" 
							 + "<div align='center'> 인증번호 : " + a + "</div>");
			email.send();
		} catch (Exception e) {
			System.out.println(e);
		}		
		model.addAttribute("result", "good~!!\n 등록된 E-Mail 확인");

		return "result";
	}
}

- 0 ~ 99 까지의 난수값을 메일로 보낼 것

- 난수를 발생시켜서 변수 a 에 저장했다

 

1. 메일 서버 설정

- 네이버 메일의 smtp 서버를 쓰므로 서버의 네임 hostSTMP 는 "smtp.naver.com" 이다

- hostSTMPid 에는 내 이메일, hostSTMPpwd 에는 내 비밀번호

- 반드시 네이버 이메일이어야함

 

2. 보내는 사람 Email, 제목, 내용 설정

- 보내는 사람 Email 도 내 이메일로 설정하자

- 반드시 네이버 이메일이어야함

- 메일 서버의 이메일과 같아야함

 

3. 받는 사람 Email 주소 설정

- 메일을 받을 곳의 이메일 주소를 쓰면 된다

- 반드시 네이버 이메일이 아니어도 된다

 

HtmlEmail 객체 생성 및 세팅

- commons-email 라이브러리에서 제공하는 HtmlEmail 클래스를 통해 여러가지 설정을 한다

+ setSSL(true) 를 통해 보안 연결을 사용한다

 

HtmlEmail 클래스의 메소드

- addTo() 메소드는 "받는 사람"의 메일 주소 설정하는 메소드

- setFrom() 메소드는 "보내는 사람" 의 메일 주소, 이름, charSet 등을 설정

- setSubject() 메소드로 제목을 세팅하고, setHtmlMsg() 메소드로 내용을 세팅, 여기에 변수 a ㄱ밧을 전달

 - send() 메소드로 이메일을 실제로 전송

- 메일을 전송한 후 전송 성공 메세지를 뿌리기 위해 Model 객체에 성공 메세지를 저장

 

- prefix 값이 /jsp/ 로 되어있으므로 webabb/jsp/ 안의 result.jsp 파일로 이동한다

- result.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>

결과 페이지: ${result}

</body>
</html>

- 메일을 보내보자

- 네이버 메일에 가서 확인

 

보낸 메일함 확인

 

받은 메일함 확인

- 현재는 보낸 메일과 받는 메일을 같게 설정했었다


회원 관리 프로그램

- 회원 가입시 이메일 보내기 기능을 활용해서 아이디 찾기, 비밀번호 찾기를 구현한 회원 관리 프로그램을 보자

- 클라우드의 springmember 프로젝트를 import


파일들 살펴보기 : pom.xml

- pom.xml 부분

		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.2</version>
		</dependency>

- 프로필 사진 (첨부파일) 을 업로드하기 위해 fileupload 라이브러리가 추가되어 있다

- 이메일 관련 라이브러리는 javax.mail 과 commons-email 등이 있다, 현재는 commons-email 라이브러리만 사용

		<!-- javax.mail -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>4.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>javax.mail</groupId>
			<artifactId>mail</artifactId>
			<version>1.4.7</version>
		</dependency>

		<!-- EMail -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-email</artifactId>
			<version>1.5</version>
		</dependency>

- 아래가 commons-email 라이브러리


파일들 살펴보기 : web.xml

- 들어가는 3가지 내용 중 DispatcherServlet 매핑 부분이 변경되었다

	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

- *.do 로 요청해야함

- 들어가는 3가지 내용 중 한글 인코딩 설정 코드가 들어가 있다

	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

- 들어가는 3가지 내용 중 servlet-context.xml , root-context.xml 파일을 불러오고 있다


파일들 살펴보기 : servlet-context.xml

servlet-context.xml 부분 중 base-package

	<context:component-scan base-package="myspring" />

- base-package 를 "myspring" 으로 설정해뒀다, src/main/java 하위에 myspring 디렉토리가 있음

- 이 base-package 안의 클래스들은 4가지 어노테이션 중 하나가 붙어있어야한다

* 4가지 어노테이션 : @Component, @Controller, @Service, @Repository

 

servlet-context.xml 부분 중 ViewResolver

	<!-- ViewResolver -->
	<beans:bean id="internalResourceViewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="jsp/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>

- View 페이지 저장 위치는 webapp 폴더가 기준이다

- 그러므로 prefix 의 value 에 "jsp/" 라고 썼다면 webapp 하위에 jsp 파일을 생성하고 그 안에 View 파일들이 있어야함

 

servlet-context.xml 부분 중 인터셉터 설정

	<!-- 인터셉터 설정 -->	
	<beans:bean id="sessionChk" class="myspring.controller.SessionCheckInter"/>	
	 <interceptors>
		<interceptor>
			<mapping path="/member_edit.do"/>
			<mapping path="/member_edit_ok.do"/>			
			<mapping path="/member_del.do"/>
			<mapping path="/member_del_ok.do"/>
			<mapping path="/member_logout.do"/>
			<beans:ref bean="sessionChk"/>
		</interceptor>
	</interceptors>

- 인터셉터 매핑을 잡고 있다, 로그인 해야만 쓸 수 있는 메뉴(요청) 들이다

ex) 수정, 삭제, 로그아웃관련 요청

- 로그인 해야만 사용할 수 있는 기능을 사용할때 인터셉터를 사용해서 Controller 로 가기 전에 인터셉터 클래스의 preHandler() 부분으로 감

- 인터셉터 클래스인 SessionCheckInter.java 를 보자

 

- SessionCheckInter.java

package myspring.controller;

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

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class SessionCheckInter extends HandlerInterceptorAdapter {
	
	// preHandle(request,response,handler)메소드
	// 1.Controller에서 요청(*.do)을 받기 전에  preHandle()가 호출되어 가로채는 역할로 사용
	// 2.로그인 하지않고(세션이 없으면) 요청하면 로그인 폼으로 이동 하도록 해주는 역할
	public boolean preHandle(HttpServletRequest request, 
			HttpServletResponse response, Object handler) throws Exception {
		HttpSession session = request.getSession();
		String id = (String)session.getAttribute("id");
		if (id == null || id.equals(""))  {		
			response.sendRedirect("member_login.do");	// 세션이 없으면 로그인 폼으로 이동
			return false;
		}
		return true;
	}
}

- 여기서는 HandlerInterceptor 클래스를 상속받아서 인터셉터 클래스를 구현했다

- preHandle() 메소드를 오버라이딩 하고, 그 안에서 

- 인터셉터 관련 설멍 : https://laker99.tistory.com/145

 

인터셉터 구현 방법

1. HandlerInterceptorAdapter 클래스 상속

2. HandlerInterceptor 인터페이스 상속

 

servlet-context.xml 부분 중 fileupload 설정

	<!-- 파일 업로드  설정 -->
	<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<beans:property name="maxUploadSize" value="10000000"/>	
	</beans:bean>

- fileupload 설정 bean 객체를 생성하면서 Setter DI 를 수행함

- 이(1MB) 이상의 크기인 첨부파일 업로드시 오류 발생하며 멈춤, 즉 너무 큰 첨부파일을 첨부할 수 없게 함

- 이 설정을 하지 않아도 상관 없음, 이 설정만으로는 막을 수 없다

- Controller 클래스에서 특정 크기가 넘는 파일을 첨부하지 못하도록 처리 가능

+ Controller 클래스에서 "jpg", "gif", "png" 가 아닌 파일은 업로드 되지 않는 등의 설정도 가능


파일들 살펴보기 : root-context.xml

root-context.xml 부분 1

	<!-- Data Source -->
	<!-- 
	<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> 
		<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" /> 
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" /> 
		<property name="username" value="spring" /> 
		<property name="password" value="spring123" /> 
	</bean>
 	-->	
 	
 	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="username" value="spring" />
		<property name="password" value="spring123" />
	</bean>

- 여러가지 클래스들이 있는데 그 중 DriverManagerDataSource 클래스를 사용하고 있다

- 각 클래스마다 프로퍼티가 다르므로 주의

ex) DriverManagerDataSource 는 "driverClassName" 이 프로퍼티명, SimpleDriverDataSource 는 "driverClass" 가 프로퍼티명

 

root-context.xml 부분 2

	<!-- 스프링으로 oracle 디비 연결 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation"	value="classpath:util/SqlMapConfig.xml" />
		<property name="mapperLocations" value="classpath:sql/*.xml" />
	</bean>

- 3줄은 순서대로 DB 연결하는 코드, MyBatis 환경설정 파일 Config 파일 불러오기, Mapper 파일 불러오기 코드이다

- 두 환경설정 파일은 resources 폴더 안에 있으므로 classpath: 를 붙여줌

- Setter DI 로 주입해서 sqlSEssionFactoryBean 객체를 생성하고 있다

 

root-context.xml 부분 3

	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="sqlSessionFactory" />
	</bean>

- 이 sqlSessionTemplate 객체에 sqlSessionFactoryBean 객체인 sqlSessioFactory 가 Constructor DI 로 주입되었다, 주입되면서 sqlSessionTemplate 객체 sqlSession 이 생성됨

- 주입이 되었으므로 Config 파일, Mapper 파일을 DAO 에서 불러오는게 가능해짐

 

+ SqlMapConfig.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>

	<typeAliases>
		<typeAlias alias="member" type="myspring.model.MemberBean" />		
	</typeAliases>

</configuration>

- 현재 스프링에서 DB 연동하므로 DTO alis 값 설정하는 역할만 하고 있다



DB 에 테이블 생성

- join_member.sql

--회원관리
select * from tab;
select * from seq;
select * from join_member;

drop table join_member purge;

create table join_member(
  	join_code number(38) unique not null 
  	/*unique 제약 조건은 중복값을 허용하지 않고 null은 허용*/  	
  , join_id varchar2(20) primary key /*회원아이디*/
  , join_pwd varchar2(20) not null /*회원비번*/
  , join_name varchar2(50) not null /*회원이름*/
  , join_zip1 varchar2(5) not null /*첫번째 우편번호 */
  , join_zip2 varchar2(5)           /*두번째 우편번호 */
  , join_addr1 varchar2(100) not null /*주소*/
  , join_addr2 varchar2(100) not null /*나머지 주소 */
  , join_tel varchar2(20) not null /*전번*/
  , join_phone varchar2(20) not null /*폰번호 */
  , join_email varchar2(100) not null /*전자우편 주소*/
  , join_profile varchar2(100)  /*이진파일명*/
  , join_regdate date /*가입 날짜*/
  , join_state number(10) /*가입회원 1, 탈퇴회원 2 */
  , join_delcont varchar2(4000) /*탈퇴 사유 */
  , join_deldate date /*탈퇴 날짜 */
);

/***** join_member 테이블의 join_code 시퀀스 생성 *****/
create sequence join_member_joincode_seq 
increment by 1 start with 1 nocache;

--drop sequence join_member_joincode_seq; 

insert into join_member (join_code,join_id,join_pwd,join_name,join_zip1,
join_zip2,join_addr1,join_addr2,join_tel,join_phone,join_email,join_regdate,
join_state)
values(join_member_joincode_seq.nextval,'aaaaa',
'77777','홍길동','745','850','서울시 마포구 대흥동','중앙정보 처리학원',
'02-7777-7777','010-9999-9999','hong@naver.com',sysdate,1);

select * from join_member;

--delete from join_member where join_code=21;

--update join_member set join_tel='032-999-9999' where join_id='bbbbb';

 

join_member 테이블 생성 부분만 (join_member.sql 부분)

create table join_member(
  	join_code number(38) unique not null 
  	/*unique 제약 조건은 중복값을 허용하지 않고 null은 허용*/  	
  , join_id varchar2(20) primary key /*회원아이디*/
  , join_pwd varchar2(20) not null /*회원비번*/
  , join_name varchar2(50) not null /*회원이름*/
  , join_zip1 varchar2(5) not null /*첫번째 우편번호 */
  , join_zip2 varchar2(5)           /*두번째 우편번호 */
  , join_addr1 varchar2(100) not null /*주소*/
  , join_addr2 varchar2(100) not null /*나머지 주소 */
  , join_tel varchar2(20) not null /*전번*/
  , join_phone varchar2(20) not null /*폰번호 */
  , join_email varchar2(100) not null /*전자우편 주소*/
  , join_profile varchar2(100)  /*이진파일명*/
  , join_regdate date /*가입 날짜*/
  , join_state number(10) /*가입회원 1, 탈퇴회원 2 */
  , join_delcont varchar2(4000) /*탈퇴 사유 */
  , join_deldate date /*탈퇴 날짜 */
);

1. join_code : 회원 번호가 저장되는 컬럼, unique 이자 not null 이므로 primary key 나 마찬가지

- sequence 로 넣는다

2. join_id : 회원 id 값이 저장되는 컬럼, primary key

3. join_tel : 1개의 컬럼에 전화번호 앞자리, 중간자리, 끝자리를 결합해서 저장해야한다

4. join_profile : 프로필 이미지 첨부파일명을 저장할 컬럼

5. join_state : 가입한 회원은 1 을 저장, 탈퇴를 하면 delete 시키는 대신 join_state 를 2 로 저장

- 회원 등록시에 insert 문 안에 1 을 넣어서 삽입, 회원 탈퇴시에 "update 문" 으로 join_state 만 2 로 수정

- 실무에선 회원정보를 함부로 삭제 하지 않는다, 회원 상태값만 변경함

- 이 부분을 실행시켜서 테이블 join_member 를 생성

 

join_member 시퀀스 생성 부분만 (join_member.sql 부분)

/***** join_member 테이블의 join_code 시퀀스 생성 *****/
create sequence join_member_joincode_seq 
increment by 1 start with 1 nocache;

- 이 부분을 실행시켜서 시퀀스 join_member_joincode_seq 를 생성

 

join_member 더미 데이터 삽입 부분만 (join_member.sql 부분)

insert into join_member (join_code,join_id,join_pwd,join_name,join_zip1,
join_zip2,join_addr1,join_addr2,join_tel,join_phone,join_email,join_regdate,
join_state)
values(join_member_joincode_seq.nextval,'aaaaa',
'77777','홍길동','745','850','서울시 마포구 대흥동','중앙정보 처리학원',
'02-7777-7777','010-9999-9999','hong@naver.com',sysdate,1);

- 강제로 더미 데이터들을 삽입

- 이 부분을 실행시켜서 더미 데이터 1개를 생성

 

 

기능 확인

- 이제 index 파일 실행시켜보자

- index.jsp 실행

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

<%
	response.sendRedirect("member_login.do");
%>

 

- 아이디 "aaaaa", 비번 77777 로 로그인 가능

- 정보 수정해서 프로필 사진을 등록해보자

- 회원 가입 또는 수정시 첨부파일 확장자가 이미지 확장자가 아니거나 100KB 가 넘는 경우 프로필 사진 등록 불가, 즉 회원가입 및 수정 불가

- 100KB 넘지 않는 작은 사진만 업로드 가능

 

- 100KB 가 넘는 큰 사진 업로드시 캡처

- 회원가입을 시켜보자

- 비밀번호 찾기를 할때 이메일 주소로 비밀번호를 알려줘야 하기 때문에 회원가입시 이메일 주소는 실제 이메일을 받을 수 있는 이메일 주소로 등록해야한다

 

- 콘솔창을 확인해서 path: 뒤에 적힌 경로를 확인

Path=C:\Users\admin\Documents\workspace-sts-3.9.11.RELEASE\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\springmember\upload

 

- 파일 탐색기를 통해 들어가면 실제 업로드가 되어있음을 확인 가능하고, "난수값 형태"로 파일명이 저장되어 있다

- 중복 파일명 문제를 직접 해결하는 방법으로서, 문자형태의 난수값을 발생시켜서 중복문제를 해결했다

 

- 가입한 계정으로 로그인을 하자

 

- 비번찾기 기능을 하기전에 Controller 클래스인 MemberAction.java 에서 Mail Server, 보내는 사람 Email 설정을 내 이메일로 바꿔줘야한다

- 받는 쪽 주소 설정은 회원가입시 등록했던 이메일 주소로 설정된다

 

- 설정하면 다시 실행해서 비번 찾기 기능을 해보자

 

- 회원가입시 등록했던 이메일 주소의 받은 메일함에서 확인

 

- SQL Developer 에서 spring 계정 연결 후 select 문으로 들어온 DB에 저장된 데이터(회원)를 확인해보자

 

 


코드 확인

- 기능을 봤으니 코드가 어떻게 되어있는지 확인

- 로그인 기능은 생략

- 회원가입 기능부터 보자

 

- 로그인 페이지에서 "회원가입" 클릭시 "member_join.do" 로 요청한다

    <input type="button" value="회원가입" class="input_button"
    		onclick="location='member_join.do'" />

- 이후 Controller -> member_join.jsp 로 온다

 

	/* 회원가입 폼 */
	@RequestMapping(value = "/member_join.do")
	public String member_join() {
		return "member/member_join";
		// member 폴더의 member_join.jsp 뷰 페이지 실행
	}

- member_join.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>
<link rel="stylesheet" type="text/css" href="./css/admin.css" />
<link rel="stylesheet" type="text/css" href="<%=request.getContextPath()%>/css/member.css" />
<!-- <script src="/springmember/js/jquery.js"></script> -->
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="<%=request.getContextPath()%>/js/member.js"></script>
<script src="http://dmaps.daum.net/map_js_init/postcode.v2.js"></script>
<script>
//우편번호, 주소 Daum API
function openDaumPostcode() {
	new daum.Postcode({
		oncomplete : function(data) {				
			// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
			// 우편번호와 주소 정보를 해당 필드에 넣고, 커서를 상세주소 필드로 이동한다.
			document.getElementById('join_zip1').value = data.zonecode;
			document.getElementById('join_addr1').value = data.address;				
		}
	}).open();
}
</script>

</head>
<body>
 <div id="join_wrap">
  <h2 class="join_title">회원가입</h2>
  <form name="f" method="post" action="member_join_ok.do"
  		onsubmit="return check()" enctype="multipart/form-data">
   <!-- 이진파일을 업로드 할려면 enctype 속성을 지정 -->
   <table id="join_t">
    <tr>
     <th>회원아이디</th>
     <td>
      <input name="join_id" id="join_id" size="14" class="input_box" />
      <input type="button" value="아이디 중복체크" class="input_button"
      	onclick="id_check()" />
      <div id="idcheck"></div>
     </td>
    </tr>
    
    <tr>
     <th>회원비번</th>
     <td>
      <input type="password" name="join_pwd" id="join_pwd1" size="14"
      class="input_box" />
     </td>
    </tr>
    
    <tr>
     <th>회원비번확인</th>
     <td>
      <input type="password" name="join_pwd2" id="join_pwd2" size="14"
      class="input_box" />
     </td>
    </tr>
    
    <tr>
     <th>회원이름</th>
     <td>
      <input name="join_name" id="join_name" size="14" class="input_box" />
     </td>
    </tr>
    
    <tr>
     <th>우편번호</th>
     <td>
      <input name="join_zip1" id="join_zip1" size="5" class="input_box"
      		readonly onclick="post_search()" />
      <!-- -<input name="join_zip2" id="join_zip2" size="3" class="input_box" readonly 
      		onclick="post_search()"/> -->
      <input type="button" value="우편번호검색" class="input_button"
      		onclick="openDaumPostcode()" />
     </td>
    </tr>
    
    <tr>
     <th>주소</th>
     <td>
      <input name="join_addr1" id="join_addr1" size="50" class="input_box"
      readonly onclick="post_search()" />
     </td>
    </tr>
    
    <tr>
     <th>나머지 주소</th>
     <td>
      <input name="join_addr2" id="join_addr2" size="37" class="input_box" />
     </td>
    </tr>
    
    <tr>
     <th>집전화번호</th>
     <td>
     <%@ include file="../../jsp/include/tel_number.jsp"%>    
      <select name="join_tel1" >      
      	<c:forEach var="t" items="${tel}" begin="0" end="16">
      		<option value="${t}">${t}</option>
      	</c:forEach>        
      </select>-<input name="join_tel2" id="join_tel2" size="4"
      maxlength="4" class="input_box" />-<input  name="join_tel3"
      id="join_tel3" size="4" maxlength="4" class="input_box" />
     </td>
    </tr>
    
    <tr>
     <th>휴대전화번호</th>
     <td>
     <%@ include file="../../jsp/include/phone_number.jsp" %>
     <select name="join_phone1">
      <c:forEach var="p" items="${phone}" begin="0" end="5">
       <option value="${p}">${p}</option>
      </c:forEach>
     </select>-<input name="join_phone2" id="join_phone2" size="4"
     maxlength="4" class="input_box" />-<input name="join_phone3"
     id="join_phone3" size="4" maxlength="4" class="input_box" />
     </td>
    </tr>
    
    <tr>
     <th>전자우편</th>
     <td>
      <input name="join_mailid" id="join_mailid" size="10" 
      class="input_box" />@<input name="join_maildomain" 
      id="join_maildomain" size="20" class="input_box" readonly />
      <!--readonly는 단지 쓰기,수정이 불가능하고 읽기만 가능하다 //-->
      <select name="mail_list" onchange="domain_list()">
      <option value="">=이메일선택=</option>
      <option value="daum.net">daum.net</option>
      <option value="nate.com">nate.com</option>
      <option value="naver.com">naver.com</option>
      <option value="hotmail.com">hotmail.com</option>
      <option value="gmail.com">gmail.com</option>
      <option value="0">직접입력</option>
     </select> 
     </td>
    </tr>
    
    <tr>
     <th>프로필사진</th>
     <td>
      <input type="file" name="join_profile1" />
     </td>
    </tr>
   </table>
   
   <div id="join_menu">
    <input type="submit" value="회원가입" class="input_button" />
    <input type="reset" value="가입취소" class="input_button" 
    onclick="$('#join_id').focus();" />
   </div>
  </form>
 </div>
</body>
</html>

- 첨부파일을 전송해야하므로 form 태그에 enctype="multipart/form-data" 속성을 반드시 추가해야한다

 

전화번호 앞자리 출력

1. 별도의 파일 tel_number.jsp 파일 안의 String 배열 tel 에 전화번호 앞자리들을 저장한다

2. 그 배열 tel 을 request 객체로 공유설정

- import/tel_numeber.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
   String[] tel={"02","031","032","033","041","042","043","044","051","052","053","054","055","061","062","063","064"};

//for(int i=0; i<tel.length;i++){
	 // out.println(tel[i]+"<hr/>");
//}

   request.setAttribute("tel",tel);
%>

 

3. member_join.jsp 파일에서 forEach 태그의 items 에 그 배열 tel 의 요소를 출력

      	<c:forEach var="t" items="${tel}" begin="0" end="16">
      		<option value="${t}">${t}</option>
      	</c:forEach>

- 회원가입 폼에 입력 후 "회원가입" 을 클릭시 "member_join_ok.do" 로 요청한다

 

- Controller 클래스 MemberAction.java 에서 "member_join_ok.do" 요청 부분 일부

- "member_join_ok.do" 처리 부분이 기므로 하나씩 잘라서 설명

- 여기서 넘어간 첨부파일은 @RequestParam 어노테이션을 통해서 MultipartFile 객체 nf 로 받는다

- 나머지 정보들은 @ModelAttribute (생략) 어노테이션을 통해 DTO MemberBean 객체 member 로 받는다

- 그 객체 nf 로 파일의 원래 이름과 크기를 구한다

- 이후 request 객체로 폴더 upload 의 실제 경로를 구함

난수 발생

- UUID 를 통해 난수를 발생시킨다 * 아래 설명

- 난수화된 파일명은 newfilename 에 저장되게 된다

파일 크기 제한 & 파일 확장자 제한

- 파일 크기가 100KB 초과시 result 에 1 을 저장해서 실패 처리를 uploadResult.jsp 에서 할 수 있게끔 함

- 파싱으로 원래 있던 파일명을 . 으로 파싱해서 파일명은 file[0]에, 확장자는 file[1] 에 저장한다

- 그 확장자가 "jpg", "gif", "png" 가 아닌 경우 업로드 불가, 이때 2 를 저장해서 실패 처리를 uploadResult.jsp 에서 할 수 있게끔 함

 

업로드 시키기 (MemberAction.java 에서 "member_join_ok.do" 요청 부분 일부)

		if (size > 0) { // 첨부파일이 전송된 경우

			mf.transferTo(new File(path + "/" + newfilename));

		}

- 실제 업로드 폴더에 업로드를 시켜주는 코드이다

- File 객체를 생성하고, 실제 업로드 폴더 경로인 path 에 newfilename 을 붙여서 업로드 시킴

 

전화번호, 이메일 등 결합

- 컬럼이 하나이므로 결합해서 저장

 

DB에 회원 등록(삽입)

		memberService.insertMember(member);

 

Mapper 파일에서 회원 등록 SQL문

    <!-- 회원저장 -->
    <insert id="member_join" parameterType="member">
     insert into join_member (join_code,join_id,join_pwd,join_name,
     join_zip1,join_addr1,join_addr2,join_tel,join_phone,join_email,join_profile,
     join_regdate,join_state) values(join_member_joincode_seq.nextval,
     #{join_id},#{join_pwd},#{join_name},
     #{join_zip1},#{join_addr1},#{join_addr2},#{join_tel},
     #{join_phone},#{join_email},#{join_profile, jdbcType=VARCHAR},sysdate,1)
    </insert>

- MyBatis 는 null 값을 허용하지 않으므로 첨부파일명을 저장하는 컬럼에는 null 값을 허용해주는 코드인 jdbcType=VARCHAR 를 추가해줘야한다

+ 첨부파일은 등록할 수도 있고 등록하지 않을수도 있기때문에

- 마지막 컬럼은 회원의 상태값을 저장하는 컬럼, 가입시이므로 1을 저장

 

return 문

- Controller 클래스에서 return 문에서 새로운 요청을 할때는 redirect: 를 붙여야한다

- 지금까지는 View 페이지로 바로 갔지만, 이번엔 회원가입 성공 후 회원가입 성공 메세지를 뿌리지 않고 바로 로그인 폼으로 가도록 요청, 이땐 redirect: 를 붙여서 새로운 요청을 해야한다


- 난수를 발생시키는 방법을 정리한 예제 RandomFile.java 를 보자

import java.util.UUID;

public class RandomFile {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		String filename = "clock.jpg";
		String extension = filename.substring(filename.lastIndexOf("."), filename.length());
		System.out.println("extension:"+extension);
		
		UUID uuid = UUID.randomUUID();
		System.out.println("uuid:"+uuid);
		
		String newfilename = uuid.toString() + extension;
		System.out.println("newfilename:"+newfilename);
		
	}

}

1. clock.jpg 라는 첨부파일을 저장한다고 하면,  확장자를 subsgring() 을 사용해서 분리한 후 extension 에 저장

2. UUID 클래스로 난수를 발생시킨다

3. 그 난수를 String 형으로 변환시킨 후 확장자를 붙이면 파일명을 문자형태 난수로 만들 수 있다

- 난수가 중복될 확률은 현저히 낮다


 

+ Recent posts