원지의 개발
article thumbnail
728x90

MVC2

 

//web.xml
<display-name>JSPWeb</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  • 웹 애플리케이션의 루트 URL에 접근했을 때 서버가 제공하는 기본 파일을 지정

공통 부분 처리 (header, footer)

//include 사용 예시
<%@ include file= "include/header.jsp" %> 
<%@ include file = "include/footer.jsp" %>
  • header와 footer를 jsp 파일로 만들고, include 사용하여 코드를 간편화
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
  • header.jsp에 사용할 JSTL(JavaServer Pages Standard Tag Library) 코어 태그 라이브러리를 선언

header.jsp 에 포함되어있는 메뉴들은 모두 링크가 걸려있는데 이를 a태그를 통해서 작성할 수도 있지만
MVC Model2에 부합하도록 모든 요청이 하나의 서블릿을 통해 전달되고
서블릿에서 요청을 처리하여 그에 걸맞는 jsp 페이지를 포워딩하도록 만들어야 함
  • jsp에서 ${pageContext.request.contextPath}: 내 현재 위치 (EL 사용) = JSPWeb
  • 웹 애플리케이션의 컨텍스트 경로를 기준으로 이동
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                	
                    <li>
                        <a href="${pageContext.request.contextPath}/index.main">HOME</a>
                    </li>
                    <li>
                        <a href="${pageContext.request.contextPath}/member/member.main">Member</a>
                    </li>
                    <li>
                        <a href="${pageContext.request.contextPath}/board/board_list.board">BOARD</a>
                    </li>
                    
                    <!-- 특정 조건일 때 html부분이 보였다, 안보였다 해줌, if대신 choose 사용해도 됨 -->
                    <c:if test="${sessionScope.user_id == null }"> <!-- 로그인 안되어 있으면 login, join만 -->
                    <li>
                        <a href="${pageContext.request.contextPath}/user/user_login.user">LOGIN</a>
                    </li>
                    <li>
                        <a href="${pageContext.request.contextPath}/user/user_join.user" style="color:red">JOIN</a>
                    </li>
					</c:if>
					
					<c:if test="${sessionScope.user_id != null }"> <!-- 로그인 되어 있으면 logout, mypage만 -->                   
                     <li>
                        <a href="${pageContext.request.contextPath}/user/user_logout.user">LOGOUT</a>
                    </li>
                    <li>
                        <a href="${pageContext.request.contextPath}/user/user_mypage.user" style="color:red">MYPAGE</a>
                    </li>
                    </c:if>
                    
                </ul>
            </div>
  • 헤더의 login, logout은 jstl 사용하여 동적으로 표현

servlet 생성

  • 모든 요청을 받을 수 있는 servlet을 생성
  • 서블릿은 get과 post로 나뉘지만 이를 구분하지 않고 통합해서 작동될 수 있도록 doAction() 이라는 메서드를 만들어서 get과 post를 통합
package com.example.controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("*.main")
public class MainController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response);
	}
	
	protected void doAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		
		String uri = request.getRequestURI(); //프로젝트명 + 파일경로
		String path = request.getContextPath(); //파일경로
		String command = uri.substring(path.length());
		
		System.out.println(command);
		
		switch (command) {
		case "/index.main":
				request.getRequestDispatcher("index.jsp").forward(request, response);
			break;

		case "/member/member.main" :
			request.getRequestDispatcher("member.jsp").forward(request, response);
			break;
		default:
			break;
		}
		
	}
}
  • 통합인 doAction은 post가 들어올 경우를 대비해 한글처리 필요
  • uri는 프로젝트명까지 포함하므로 이를 제거해서 파일 경로만 나오게 만듦
  • mainController에서는 /index.main, /member/member.main만 연결
  • 나머지 userController, boardController에서도 화면 포워딩
URI : /JSPWeb/user/user_login.user
path : /JSPWeb
요청경로 : /user/user_login.user

회원가입

화면 요청

@WebServlet("*.user")
public class UserController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response); //do, post 통합
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doAction(request, response); //do, post 통합
	}

	//get, post 하나로 묶는 메서드
	protected void doAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//한글처리
		request.setCharacterEncoding("utf-8");
		//요청분기
		String uri = request.getRequestURI();
		String path = request.getContextPath();
		String command = uri.substring(path.length());
		
		System.out.println("요청경로: " + command);

		UserService service = new UserServiceImpl();
		HttpSession session = null;
		
		//요청분기
		switch (command) {
        case "/user/user_join.user":
			request.getRequestDispatcher("user_join.jsp").forward(request,response);
			break;
        
        ...
            
        default:
			break;
		}
		
	} //end doAction
	
}
  • userController 만들고, doAction 메서드 안에서 분기 처리 하면 됨
  • service 인터페이스 - serviceImpl 클래스로 비즈니스 로직 실행
  • join(회원가입)의 요청이 들어왔을 경우 그에 맞는 jsp 페이지를 forward
    (MVC Model2에서 다른 페이지로 넘어가는 것은 forward를 사용, Redirect는 MVC Model2 위배)

form을 통한 요청

태그에서 제공하는 input 유효성 검사 중요!!!!

pattern = JS정규표현식
required = "required" 정규표현식에 일치하지 않으면 submint 불가 공백을 허용하지 않음
라디오 or 체크박스 미리 선택 checked = "checked"
셀렉트박스의 미리 선택 selected = "selected"
input 태그를 읽기 속성으로 하는 거 readonly = "readonly"
input 태그 사용안하기 = 태그가 작동이 안함
disabled = "disableds"
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ include file= "../include/header.jsp" %> 

<section>
	<div align = "center" >
		<h3> 회원가입 </h3>
		
		<form action="joinForm.user" method="post">
			<span style = "color" : red>${msg }</span>
			
			<table>
				<tr>
					<td>아이디</td>
					<td><input type="text" name="id" placeholder="4~8글자 영문자숫자" pattern = "\w{4,8}" required = "required"></td>
				</tr>
				<tr>
					<td>비밀번호</td>
					<td><input type="password" name="pw" placeholder="4~8글자 영문자숫자" pattern = "\w{4,8}" required = "required"></td>
				</tr>
				<tr>
					<td>이름</td>
					<td><input type="text" name="name" pattern = "[가-힣]{3,}"></td>
				</tr>
				<tr>
					<td>이메일</td>
					<td><input type="email" name="email"></td>
				</tr>
				<tr>
					<td>성별</td>
					<td>
					<input type="radio" name="gender" value = "M" checked = "checked">남자
					<input type="radio" name="gender" value = "F"> 여자
					</td>
				</tr>
							
			</table>
			
			<input type="submit" value="가입">
			<!-- button은 JS로 기능을 추가한다. -->
			<input type="button" value="로그인하기" onclick="location.href = 'user_login.user'">
		</form>
	
	</div>
</section>

<script>
	var msg = '${msg}';
	if(msg != ''){
		alert(msg);
	}
</script>

<%@include file = "../include/footer.jsp"  %>
  • 회원가입을 담당하는 user_join.jsp 에서 필요한 값들을 form 태그를 통해 입력받고 이를 다시 input submit 태그를 통해서 값들을 전달해주어야 함
  • form 태그 action 에 적힌 링크로 값들이 전달되는데 요청이므로 서블릿으로 전달해야 함

요청 사항 처리

  • 서블릿에 너무 많은 코드가 작성되면 가독성이 떨어지므로 서블릿의 역할을 분담하는 메서드 작성을 위해 새로운 클래스를 생성 (UserServiceImpl)
  • 그전에 service Interface를 만들어 먼저 값들을 받아와야 하므로 매개변수로 HttpServletRequest request, HttpServletResponse response를 받음
  • 이후 request.getParameter 메서드를 통해 user_join.jsp에서 입력되었던 값들을 받아옴

UserServiceImpl

  • 여기서 입력되었던 값들 중 중복이 발생되서는 안되는 값(id, email...)이 있을 경우
    예외 처리를 해주어야 하는데 중복을 확인하기 위해서는 회원의 정보가 담겨있는 database가 활용되어야 함.
  • sql 쿼리문을 java에서 작업해야하므로 JDBC활용을 위한 DAO, VO 클래스가 새로 만들어져야 함

DAO 생성

public class UserDAO {
	
	//UserDAO는 불필요하게 여러 개 만들어질 필요가 없기 때문에
	//한 개의 객체만 만들어지도록 Singleton 형식으로 설계
	
	//1. 나 자신의 객체를 생성해서 1개로 제한
	private static UserDAO instance = new UserDAO();
	
	
	//2. 직접 객체를 생성할 수 없도록 생성자에 private
	private UserDAO() {
		//드라이버 클래스는 어차피 생성되어야 하므로 생성자에 포함
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
		} catch (Exception e) {
			System.out.println("드라이버클래스 로드에러");
		}
	}
	
	
	//3. 외부에서 객체 생성을 요구할 때 getter 메서드를 통해 1번의 객체를 반환
	public static UserDAO getInstance() {
		return instance;
	}

	
	//4. 필요한 데이터베이스 변수 선언
	public String url = "jdbc:oracle:thin:@localhost:1521:xe";
	public String uid = "JSP";
	public String upw = "JSP";
	Connection conn;
	PreparedStatement pstmt;
	ResultSet rs;
	
	
	//5. 메서드
	public int idCheck(String id, String email) {
		int result = 0;
		
		String sql = "select count(*) as total from users where id = ? or email = ?";
		
		try {
			conn = DriverManager.getConnection(url, uid, upw);
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setString(1, id);
			pstmt.setString(2, email);
			
			rs = pstmt.executeQuery(); 
			if(rs.next()) {
				result = rs.getInt("total");
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(conn, pstmt, rs);
		}
		
		return result;
	}
}
  • DAO의 객체는 여러 개 생성될 필요가 없기 때문에 Singleton 형식으로 설계
  • sql 쿼리문을 통해 id 중복확인을 하는 메서드를 작성
    쿼리문에서 중복되는 값의 개수를 구하는데 중복값이 없다는 것은 0으로 중복을 확인할 수 있고 결과를 int로 반환

DB 저장 후 리다이렉트

//회원가입
	public void join(UserVO vo) {
	
		String sql = "insert into users values(?, ?, ?, ?, ?)";
		
		try	{
			conn = DriverManager.getConnection(url, uid, upw);
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setString(1, vo.getId());
			pstmt.setString(2, vo.getPw());
			pstmt.setString(3, vo.getName());
			pstmt.setString(4, vo.getEmail());
			pstmt.setString(5, vo.getGender());
			
			pstmt.executeUpdate(); // 실행 후 성공 시 1 반환 실패 시 0 반환
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(conn, pstmt, rs);
		}
		
	}
  • 다시 서블릿의 역할을 분담하기 위한 회원가입 메서드가 있는 UserServiceImpl로 돌아와서 중복값을 확인하는 DAO클래스의 idcheck메서드를 호출하고 1이 리턴되면(중복O) 메시지를 띄우고 다시 가입하도록
  • 0이 리턴하면 (중복x) UserVO 클래스에 값들을 저장하고 DAO클래스의 insert 쿼리가 담긴 메서드를 실행하여 db에 데이터를 저장하여 회원가입을 마침
  • 회원가입이 끝난 이후 로그인페이지로 돌아가야는데 여기서는 Redirect가 허용
    왜? 페이지에서 다른페이지로 전환이 아닌 컨트롤러 내부에서 다른 요청을 하는 것이기 때문에 MVC Model 2에 위배되지 않음
    • request.getRequestDispatcher("user_join.jsp").forward(request, response);는 서블릿에서 JSP 페이지로의 내부 포워딩, 서블릿에서 설정한 속성은 JSP 페이지에서도 접근 가능
  • response.sendRedirect("user_login.user");는 클라이언트에게 새로운 URL로의 리다이렉션을 보내는 것 (컨트롤러나 서블릿)
내부 포워딩은 서버 내부에서의 자원 이동으로, 클라이언트는 이를 인지하지 못하고 이전 요청을 유지
반면에 리다이렉션은 클라이언트에게 새로운 요청을 생성하도록 지시하여 서버가 다른 리소스를 제공하고 있는 것으로 인식

로그인

<li>
  <a href="${pageContext.request.contextPath}/user/user_login.user">LOGIN</a>
</li>
  • header에서 로그인 페이지 클릭 > 서블릿에서 login 요청 처리> switch문에서 로그인 요청을 처리할 수 있도록 코드 작성

  • 이번에도 서블릿의 역할 분담을 위해 UserServiceImpl 클래스에서 login 메서드 작성
  • id, pw를 사용자로부터 입력받은 후 입력받은 값이 db에 저장된 값과 동일한지 확인
//로그인
	public UserVO login(String id, String pw) {
		
		UserVO vo = null;
		
		String sql = "select * from users where id = ? and pw = ?";
		
		try {
			conn = DriverManager.getConnection(url,uid,upw);
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setString(1, id);
			pstmt.setString(2, pw);
			
			rs = pstmt.executeQuery();
			
			if(rs.next()) { //로그인 성공 시 데이터가 조회되서 나타났을 것이다. 이를 vo에 담는다.

				String id2 = rs.getString("id");
				String name = rs.getString("name");
				String email = rs.getString("email");
				String gender = rs.getString("gender");
				
				vo = new UserVO(id2, null, name, email, gender);
			}
			
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(conn, pstmt, rs);
		}
		
	//////////////////////////////////////////////////////////////////////
	
		return vo; //vo가 null이면 로그인 실패
	}
  • DAO 클래스에서 id, pw 가 같은 데이터 있는지 확인하는 쿼리 실행
  • 동일한 id,pw 있을 경우 해당 데이터 값을 담은 VO 객체 반환, 없을 경우 null 반환

  • 컨트롤러에 세션 만들고
  • 로그인 성공 시 이름과 아이디로 환영 문구 작성하기 위해 아이디, 이름을 session에 담고 mypage로 이동해야하므로 역시 서블릿에게 요청
  • 서블릿에서 mypage 요청 들어올 경우 mypage.jsp로 이동시킴

로그인에 따라 navbar 변경

  • 로그인시 세션에 아이디랑 이름을 저장했기 때문에 가져와서 사용

마이페이지

<section>
	<div align = "center">
		${sessionScope.user_id}
		<b>(${sessionScope.user_name})</b>님 환영합니다.
		
		<div>	
		<a href="user_logout.user">[로그아웃]</a>	
		<a href="user_modify.user">[정보수정]</a>	
		<a href="user_delete.user">[회원탈퇴]</a>	
		</div>
	</div>
</section>
case "/user/user_mypage.user": //마이페이지
	request.getRequestDispatcher("user_mypage.jsp").forward(request, response);
break;
  • 마이페이지 화면 요청 - 처리

로그아웃

String path = request.getContextPath();

	case "/user/user_logout.user": //로그아웃
			session = request.getSession();
			session.invalidate(); //세션무효화 (아예 싹다 소멸, remove는 그건 아님)
			
			//main 컨트롤러 사용
			//response.sendRedirect("/JSBasic/index.main"); //메인으로
			response.sendRedirect(path + "/index.main"); //위와 같은 표현
			
			break;
  • 로그아웃시 세션 무효화 후 메인으로 보내기

회원정보 수정

  • interface - dao - UserServiceImpl - userController - user_modify

회원 데이터 가지고 나오기

회원데이터를 가지고 나오는 작업
1. service와 dao에 getInfo() 메서드를 선언
2. service메서드 세션에서 아이디를 얻음
3. dao에서는 id를 전달받아 회원 데이터를 조회하여 vo에 저장
4. controller에서는 조회한 vo를 저장하고 화면으로 가지고 나감
5. 화면에서는 input태그에 값을 출력

  • serviceImpl에서 세션을 받아옴 (로그인 당시 user_id라는 키로 저장했음)
  • dao 객체 생성해서 id에 맞는 회원정보 가져와 vo에 담기
public UserVO getInfo(String result_id) { //id가 파라미터로(화면에서) 안넘어오니까 세션에 저장되어 있는 것을 꺼내서 사용
		
		String sql = "select * from users where id = ? "; //아이디가 맞는 정보

		UserVO vo = null; //데이터 조회한 결과를 생성해서 vo에 담아줌, 미리 생성해서 담아줘도 됨(받아오는 파라미터값이 이미 있는 아이디이므로 null값 아니니까)
		
		try {
			conn = DriverManager.getConnection(url,uid,upw);
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setString(1, result_id);
			
			rs = pstmt.executeQuery();
			
			if(rs.next()) { //로그인 성공 시 데이터가 조회되서 나타났을 것이다. 이를 vo에 담는다.
				  			//select 같은 경우 DB에서 읽어온 데이터를 자바에서 쓰기 위해서
							//변수에 담아서 UseVO객체에 담음, insert, delete 에서는 필요 없음
				String id = rs.getString("id"); //id
				String name = rs.getString("name"); //name
				String email = rs.getString("email"); //email
				String gender = rs.getString("gender"); //gender
				
				vo = new UserVO(id, null, name, email, gender);
			}
			
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(conn, pstmt, rs);
		}
		
		
		return vo;
		
	}
  • 컨트롤러에서 vo라는 이름으로 담아주고, 회원정보 수정 화면으로 포워딩
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

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

<section>

	<div align="center">
	<h3>회원정보 수정 연습</h3>
	
		<form action="updateform.user" method="post">
			
			<table>
				<tr>
					<td>아이디</td>
					<td><input type="text" name="id" value="${vo.id}" pattern = "\w{4,8}" required readonly ></td>
		
				</tr>
				<tr>
					<td>비밀번호</td>
					<td><input type="password" name="pw" placeholder="4~8글자 영문자숫자" pattern = "\w{4,8}" required = "required"></td>
				</tr>
				<tr>
					<td>이름</td>
					<td><input type="text" name="name" value="${vo.name}"  pattern = "[가-힣]{3,}"></td>
				</tr>
				<tr>
					<td>이메일</td>
					<td><input type="email" name="email" value="${vo.email}" readonly ></td>
				</tr>
				<tr>
					<td>성별</td>
					<td>
					
					<c:if test="${vo.gender == 'M' }">
					<input type="radio" name="gender" value = "M" checked = "checked">남자
					<input type="radio" name="gender" value = "F"> 여자
					</c:if>
					
					<c:if test="${vo.gender == 'F' }">
					<input type="radio" name="gender" value = "M" >남자
					<input type="radio" name="gender" value = "F" checked = "checked"> 여자
					</c:if>
					
					<%-- 3항 연산자 사용, checked라는 문자열에 태그 안에 출력
					<input type="radio" name="gender" value = "M" ${vo.gender == 'M' ? 'checked' : '' } >남자
					<input type="radio" name="gender" value = "F" ${vo.gender == 'F' ? 'checked' : '' } > 여자
					--%>
					
					</td>
				</tr>
							
			</table>
			
			<input type="submit" value="정보수정">
			<!-- button은 JS로 기능을 추가한다. -->
			<input type="button" value="마이페이지" onclick=" location.href = 'user_mypage.user' ">
		</form>
	
	</div>

</section>

<%@ include file="../include/footer.jsp" %>
  • 화면에서는 vo. 으로 사용 가능

회원 정보 업데이트

  • readonly - getParameter로 데이터 넘김
  • disabled - id 태그 사용X
회원정보를 업데이트 하는 작업
1. service와 dao에 update() 메서드를 생성
2. service의 필요한 파라미터 값을 받음 (pw, name, gender) 조건절 (id)
3. dao에서 데이터를 전달받아서 업데이트를 실행
4. 업데이트 이후에는 컨트롤러를 태워서 mypage로 리다이렉트

  • 회원정보 수정에서는 폼태그로 updateform.user로 보냄

  • UserController - UserService - UserServiceImpl (오버라이딩) - UserDAO에서 업데이트 결과 확인
  • 업데이트 성공시 화면에 alert 띄우고 마이페이지로 보내기
  • 실패시 리다이렉트로 수정화면으로 보내기

회원 탈퇴

  • UserController - UserService - UserServiceImpl (오버라이딩), 파라미터로 값 받아서 DAO넘겨줌 - DAO - UserServiceImpl에서 DAO 객체생성 하고 반환 - UserController 돌아와서 리다이렉트

  • UserServiceImpl에서는 세션 전부 삭제
  • 성공시 메인화면, 실패시 마이페이지로 리다이렉트

오늘 하루

더보기

기억에 남는 부분

 

어려운 부분

 

문제 해결 부분

 

728x90
profile

원지의 개발

@원지다

250x250