게시판 회원가입 구현

jungnoeun·2022년 7월 15일
0

jsp

목록 보기
8/14

메인화면 (기본화면) 생성

게시판을 실행했을때 처음 들어가지는 메인화면을 생성한다.
메인화면 구현 - list.jsp

2) 부트스트랩을 이용한 화면 구성

w3school사이트의 BS4 Navbar 에 가서 코드를 list.jsp에 넣는다.

3) navbar의 헤더 분리

navbar는 모든 페이지에 있어야 하므로 BS4 Navbar의 전체코드 중 헤더부분을 header.jsp에 넣는다.
그리고 list.jsp를 포함한 모든 파일에서 header.jsp를 include하여 사용하게 한다. webapp/userloginForm.jspjoinForm.jsp에서도 header.jsp를 호출하는 코드를 넣는다.

header.jsp를 호출하는 코드는 아래와 같다.
<% @ include file = "../layout/header.jsp"%>

4) 프로젝트 실행시 메인화면 실행

프로젝트 실행 시 index.jsp가 실행되므로 index.jsp에서 메인화면(list.jsp)가 실행되게 한다.

list.jsp를 호출하는 코드는 아래와 같다.
response.sendRedirect("board/list.jsp");

5) 정보를 입력하는 칸(form) 코드를 넣는다.

다시 w3s 사이트에 가서 BS4 Form 코드를 가져온다. 헤더부분은 header.jsp에 따로 만들었기 때문에 헤더 부분의 코드는 안가져와도 된다.
BS4 Form 코드는 joinForm.jsp에 넣는다.






회원가입 정보를 DB에 전달

1) 회원가입 폼에서 컨트롤러 로 데이터 전달

joinForm.jsp에 action값을 넣는다.
경로: 회원가입 페이지(joinForm.jsp)에서 컨트롤러(UserController.java)에 값을 전달한다. 이때 보내는 값들은 회원가입 페이지(joinForm.jsp가 실행된 웹페이지)에 작성된 값들이다.

joinForm.jsp에 추가된 코드
<form action="blog/user?cmd=join" method="post">
: 컨트롤러에서 cmd가 join일때 실행되는 함수를 실행하고 값들은 post로 보낸다.

jsp에서 컨트롤러에서 데이터 null 전달?

jsp의 <input> 에 name값을 넣어줘야 key&value 형식으로 값이 제대로 넘어갈 수 있다.

회원가입 폼에서 null값은 전달이 안되게 하려면?

값이 반드시 있게 하려면,
required 속성을 jsp에 추가한다.

2) 컨트롤러에서 호출한 서비스의 함수

회원가입() 함수를 작성한다.
요청&응답의 기능들은 컨트롤러에서 처리했으므로 DAO에 요청하는 함수를 작성한다.
UserDao.java를 호출하면서 dto를 넘겨준다.

3) DB연결과 sql명령어 실행

UserDao.javasave() 함수를 작성한다.
database연결과 연결한 database에 원하는 sql명령어를 적용시킨다.

4) close()함수 작성

코드의 깔끔함을 위해 UserDao.java에서 사용한 것들을 close해준다.







navbar의 버튼 클릭 이벤트

목적 : navbar의 회원가입 또는 로그인 목록버튼을 누르면 해당되는 회원가입 또는 로그인 페이지로 이동하게 한다.
예) 회원가입 목록버튼을 누르면 회원가입창(joinForm.jsp)페이지로 이동한다.

header.jsp 파일에 각각 아래의 코드를 추가한다.

href = "<%=response.getContextPath()%>/user?cmd=joinForm"
href = "<%=response.getContextPath()%>/user?cmd=joinForm"

이때 response.getContextPath()/blog의 경로를 의미한다
즉, 각각 blog/user?cmd=joinForm"blog/user?cmd=loginForm"
의 경로를 의미하는 것이다.







회원가입 실패시 이벤트 발생

회원가입에 실패하면 Script.javaback()함수가 실행하게 하여 회원가입 실패시 회원가입 실패라는 알림 창이 뜨게 한다.
예) 회원가입이 실패하는 경우: 아이디의 길이가 100보다 클때,,







주소 API 이용

목적: 회원가입 폼 작성시 주소 입력은 직접하는 것이 아니라 검색을 통해서만 작성하게 한다.
1) juso.go.kr 페이지에 접속하여 API를 신청하여 승인키를 얻는다.
-> 승인키가 있어야 juso.go.kr에서 request.getParameter("승인키") 로 키값을 확인해서 맞으면 주소를 준다.
2) joinForm.jsp의 주소 입력 기능의 버튼을 생성한다. (부트스트랩 사용)
3) Sample.jspjusoPopup.jsp 파일을 다운받아 test패키지에 넣는다.
4) 두 파일중 jusoPopup.jsp파일은 user패키지로 그대로 옮기고, Sample.jsp 파일의 내용중 goPopup() 함수와 jusoCallBack() 함수를 joinForm.jsp 파일로 옮긴다.
5) joinForm.jsp 파일에 onClick="goPopup()"과 주소 검색 버튼에 id="address"를 추가한다. 그리고 jusoCallBack() 함수를 id를 이용해 폼을 검색해 값을 넣는 방식으로 바꾼다.

실행 과정

청록색의 주소검색 버튼을 누르면 두번째 이미지와 같은 창이 뜬다.





주소 API test

api 문서 확인

호출 방식이 POST와 GET방식이 둘다 가능하다는 것을 보아 보내는 데이터 형식은 "key&value" 이어야 한다.
-> 이유: GET으로 데이터를 보내는 방식은 쿼리스트링 밖에 없기 때문이다.

원하는 정보만 얻기

도로명 주소 API-팝업API를 입력해서 "도로명주소 전체(roadFullAddr)"만 얻는 방법
-> 아래의 코드를 사용한다.
request.getParameter("roadFullAddr")







파일 구조








코드

webapp/board/list.jsp

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

<%@ include file = "../layout/header.jsp" %>

</body>
</html>
    

webapp/layout/header.jsp

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

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Cos 블로그</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>

<nav class="navbar navbar-expand-md bg-dark navbar-dark">
  <a class="navbar-brand" href="#">블로그</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
    <span class="navbar-toggler-icon"></span>
  </button>
  <div class="collapse navbar-collapse" id="collapsibleNavbar">
    <ul class="navbar-nav">
      <li class="nav-item">
        <a class="nav-link" href="<%=request.getContextPath() %>/user?cmd=joinForm">회원가입</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="<%=request.getContextPath() %>/user?cmd=loginForm">로그인</a>
      </li>   
    </ul>
  </div>  
</nav>
<br>

헤더부분은 다른 jsp에서 호출하므로 </body></html>을 써줄 필요가 없다.



webapp/user/loginForm.jsp

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

<%@ include file = "../layout/header.jsp" %>

</body>
</html>

webapp/user/joinForm.jsp

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

<%@ include file = "../layout/header.jsp" %>

<div class = "container">
<form action="/blog/user?cmd=join"  method="post">

  <div class="form-group">
    <input type="text" name="username" class="form-control" placeholder="Enter Username"  required/>
  </div>
  
  <div class="form-group">
	<input type="password" name="password" class="form-control" placeholder="Enter Password"  required/>	
  </div>

  <div class="form-group">
	<input type="email" name="email" class="form-control" placeholder="Enter Email"  required/>
  </div>
  
  <div class="d-flex justify-content-end">
  <button type="button" class="btn btn-info" onClick="goPopup();">주소검색</button>
  </div>
	<input type="text" name="address"  id="address"  class="form-control" placeholder="Enter Aderess"  required readonly/>
	<br/>

  <button type="submit" class="btn btn-primary">회원가입완료</button>
</form>
 </div>
 
 
 <script>
// opener관련 오류가 발생하는 경우 아래 주석을 해지하고, 사용자의 도메인정보를 입력합니다. ("팝업API 호출 소스"도 동일하게 적용시켜야 합니다.)
//document.domain = "abc.go.kr";

function goPopup(){
	// 주소검색을 수행할 팝업 페이지를 호출합니다.
	// 호출된 페이지(jusopopup.jsp)에서 실제 주소검색URL(https://www.juso.go.kr/addrlink/addrLinkUrl.do)를 호출하게 됩니다.
	var pop = window.open("/blog/user/jusoPopup.jsp","pop","width=570,height=420, scrollbars=yes, resizable=yes"); 
	// window.open이면 브라우저가 하나 더 열린다는 의미이다. 
	
	// 모바일 웹인 경우, 호출된 페이지(jusopopup.jsp)에서 실제 주소검색URL(https://www.juso.go.kr/addrlink/addrMobileLinkUrl.do)를 호출하게 됩니다.
    //var pop = window.open("/popup/jusoPopup.jsp","pop","scrollbars=yes, resizable=yes"); 
}


function jusoCallBack(roadFullAddr){
		// 팝업페이지에서 주소입력한 정보를 받아서, 현 페이지에 정보를 등록합니다.
//		document.form.roadFullAddr.value = roadFullAddr; //이렇게 name값으로 찾는 방법은 옛날 방식이다.
	var addressEl = document.querySelector("#address");
	addressEl.value = roadFullAddr;
	 
}

</script>
</body>
</html>

UserService.jsp

public class UserService {
	// 회원 가입, 회원 수정, 로그인, 로그아웃, 아이디중복체크
	// 로그아웃은 세션 invalidate만 하면 된다. 로그아웃 함수안에서 세션을 찾을 수없다.-> request와 response가 없기 때문. 
	// 로그아웃은 DB관련 기능이 하나도 없다. 그냥 세션만 날리면 된다. 그래서 서비스로 만들지 않고 컨트롤러에서 처리한다. 왜냐하면 서비스에까지 request를 끌고 오면 안되기 때문이다. 컨트롤러가 request관련된 일을 다 하게해야 한다.
	// 서비스에는 request로 다 처리된 결과만 가져와서 처리해야 한다.
	
	// DB에 select가 아닌 insert 요청 (select인지 insert인지에 따라 응답형태가 달라진다.)
	// select: 리턴하는값이 데이터, insert: 리턴하는 값이 성공 또는 실패를 의미하는 응답값
	
	private UserDao userDao;
	
	public UserService() {
		userDao = new UserDao();
	}
	
	
	public int 회원가입(JoinReqDto dto) {	
		// 회원가입의 경우 insert 요청 
		
		// Data access object(dao)만 실행
//		UserDao userDao = new UserDao();
		int result = userDao.save(dto);
		return result;
	}
	
	public User 로그인(LoginReqDto dto) {
		//로그인은 select 요청 그리고 나서 세션 만들것
		//로그인하고 select하고 그 행을 찾아 리턴할 것임
		//select * from user where username = ? and password = ? >> * 들은 Model에 매핑이 된다.
		//join하는 경우:  select * from user inner join board where username = ? and password = ? >> * 들은 dto로 받아야 한다.
		
		// 리턴한 값을 세션에 담을 것이다. 그래서 리턴값: User 오브젝트이다.
		return null;
	}
	
	public int 회원수정(UpdateReqDto dto) {
		// insert하는 기능
		
		return -1;
	}
	
	public int 아이디중복체크(String username) {
		//사용자로부터 아이디만 받으면 되서 dto를 만들 필요가 없다.
		
		return -1;
	}
	
	
}

UserDao.java

public class UserDao {
	
	public int save(JoinReqDto dto) { // 회원 가입
		String sql = "INSERT INTO user(username, password, email, address, userRole, createDate) VALUES (?,?,?,?,'USER',now())";
		// DB 연결
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, dto.getUsername());
			pstmt.setString(2, dto.getPassword());
			pstmt.setString(3, dto.getEmail());
			pstmt.setString(4, dto.getAddress());
			int result = pstmt.executeUpdate(); // executeUpdate() 가 성공하면 1, 실패하면 0 또는 -1
			// select 하는 게 아니므로 executeUpdate()이다. executeQuery()는 resultSet을 반환한다.
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		}finally { //무조건 실행
			
			DB.close(conn, pstmt);
			//resultSet도 있으면 resultSet도 닫아줘야 한다.
			//가비지 컬렉션에 걸리겠지만 즉각적으로 일어나지 않으므로, 최대한 즉각적으로 닫아준다. -> db연결을 최소화시키기 위해
		}
		
		return -1;
	}
	
	public void update() { // 회원 수정
		
	}
	
	public void usernameCheck() { // 아이디 중복 체크
		
	}
	
	public void findById() { //회원 정보 보기
		
	}
}

DB.java

public class DB {
	// DB 연결
	public static Connection getConnection() {
		
		try {
			Context initContext = new InitialContext();
			Context envContext  = (Context)initContext.lookup("java:/comp/env");
			DataSource ds = (DataSource)envContext.lookup("jdbc/TestDB");
			Connection conn = ds.getConnection();	 // pulling 기술이라서 conn이라는 connection은 데이터베이스를 100개 가지고 있다.(근거: context.xml의 maxTotal="100")
			//그래서 필요한거마다 connection 객체를 가져와서 그걸로 데이터를 주거나 받으면 된다. 
			return conn;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	// DB 연결 끊기
	public static void close(Connection conn, PreparedStatement pstmt) {
		try {
			conn.close();
			pstmt.close();
		} catch (Exception e) { 
			e.printStackTrace();
		}
	}
}

Script.java

public class Script {
	
	public static void back(HttpServletResponse response, String msg) {
	
		PrintWriter out;

		try {
			out = response.getWriter();
			out.println("<script>");
			out.println("alert('" + msg + "');");
			out.println("history.back();");
			out.println("</script>");
			out.flush(); // 버퍼 비우기
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	
}

jusoPopup.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<% 
	request.setCharacterEncoding("UTF-8");  //한글깨지면 주석제거
	String inputYn = request.getParameter("inputYn"); 
	String roadFullAddr = request.getParameter("roadFullAddr"); 

%>
</head>
<script language="javascript">
// opener관련 오류가 발생하는 경우 아래 주석을 해지하고, 사용자의 도메인정보를 입력합니다. ("주소입력화면 소스"도 동일하게 적용시켜야 합니다.)




function init(){
	var url = location.href; // 자신(jusoPopup.jsp)에게 다시 돌아오라는 뜻
	var confmKey = "U01TX0FVVEgyMDIyMDcxNzAxNDk0MzExMjc5NjU=";
	var resultType = "4"; // 도로명주소 검색결과 화면 출력내용, 1 : 도로명, 2 : 도로명+지번+상세보기(관련지번, 관할주민센터), 3 : 도로명+상세보기(상세건물명), 4 : 도로명+지번+상세보기(관련지번, 관할주민센터, 상세건물명)
	var inputYn= "<%=inputYn%>";
	if(inputYn != "Y"){ //inputYn의 값은 이동한 주소에서 주는 것이다
		document.form.confmKey.value = confmKey;
		document.form.returnUrl.value = url;
		document.form.resultType.value = resultType;
		document.form.action="https://www.juso.go.kr/addrlink/addrLinkUrl.do"; //인터넷망 -> 이 주소를 요청한다. 이주소로 이동
		//document.form.action="https://www.juso.go.kr/addrlink/addrMobileLinkUrl.do"; //모바일 웹인 경우, 인터넷망
		document.form.submit();
	}else{
		opener.jusoCallBack("<%=roadFullAddr%>");
		window.close();
		}
}
//body의 onload의 의미는 바디에 그림이 다 그려지면 init을 호출한다는 의미이다.

</script>
<body onload="init();">
	<form id="form" name="form" method="post">
		<input type="hidden" id="confmKey" name="confmKey" value=""/>
		<input type="hidden" id="returnUrl" name="returnUrl" value=""/>
		<input type="hidden" id="resultType" name="resultType" value=""/>
		<!-- 해당시스템의 인코딩타입이 EUC-KR일경우에만 추가 START-->
		<!-- 
		<input type="hidden" id="encodingType" name="encodingType" value="EUC-KR"/>
		 -->
		<!-- 해당시스템의 인코딩타입이 EUC-KR일경우에만 추가 END-->
	</form>
</body>
</html>
profile
개발자

0개의 댓글