WAS/WAS수업

#50. JSP/Servlet 회원(로그인)

열하나요 2023. 9. 11. 09:34

0. 환경설정

Prefences > Java > Code Templates > Edit 후 주석 제거할 주석 지워주기 : 주석제거

Team > Ignored Resources > Add paterns (/classes)   : 나중에 할것

 

1. properties 생성

properties란...

문자열형식 / key와 value값 / 공백x / 개행x

username=SERVER
password=SERVER
url=jdbc:oracle:thin:@localhost:1521:xe
driver=oracle.jdbc.driver.OracleDriver

 

2. index.jsp 메인화면

<title>메인화면</title>
</head>
<body>
	<%@ include file="views/common/menubar.jsp" %>
</body>

 

3. menubar.jsp

Home 화면

href="#" 딱히 지정한 경로가 없을 때!

float : right해준뒤에 clear="both" 해주기

display : inline역할  table-cell이란 속성값도 있음

 

form태그 action값 : 

절대경로 : action="/jsp/test1.do"   => /로 시작해서 Context Root 붙여지면서 요청

상대경로 : action="test1.do" => 마지막 / 뒤에 action값이 붙여지면서 요청

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="com.kh.jsp.member.model.vo.Member" %>
<%
	
	Member loginUser = (Member)session.getAttribute("loginUser");
	// 로그인 전 : member.jsp가 로딩될 때 null
	// 로그인 성공 : menubar.jsp가 로딩될 때 로그인한 회원의 정보가 담겨있는 Member객체의 주소값

	// 성공 / 경고메시지 뽑기
	String alertMsg = (String)session.getAttribute("alertMsg");
	// 서비스 요청 전 : alertMsg == null
	// 서비스 요청 후 성공 시 : alertMsg == 메시지 문구
	
	String contextPath = request.getContextPath();
	
%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
	<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.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.2/dist/js/bootstrap.bundle.min.js"></script>


	<style>
		.login-area{
			float : right;
		}
		
		#user-info a {
			text-decoration : none;
			color : lightgray;
			font-size : 12px;
		}
		
		.nav-area {background-color : palegreen;}
		
		.menu{
			display : table-cell;
			height : 50px;
			width : 150px;
		}
		
		.menu a{
			text-decoration : none;
			width : 100%;
			height : 100%;
			display : block;
			line-height : 50px;
			color : white;
			font-weight : bold;
			font-size : 20px;
		}
		
		.menu a:hover{background-color : lightgreen;}

		.outer{
        width : 1000px;
        margin : auto;
        background-color : palegreen;
        margin-top: 5px;
        color : white;
    }
		
		
	</style>
</head>
<body>

	<script>
		// script 태그 안에서도 스크립틀릿 같은 JSP요소 사용가능 == **********권장하지 않는 방법**********
		var msg = '<%= alertMsg %>'; // null / 메시지문구
		
		if(msg != 'null'){
			alert(msg);
			
			// menubar.jsp가 로딩될 때마다 alert이 계속 수행됨
			// session에 들어있는 alertMsg키값에 해당하는 밸류값을 지워줄 것!
			// => XX.removeAttribute("키값");
			<% session.removeAttribute("alertMsg"); %>
		}
		
	
	</script>

	<h1 align="center">취업뽀개기</h1>
	
	<div class="login-area">
	
		<!-- 사용자가 로그인 전 보게 될 화면(아이디 입력란 / 비밀번호 입력란 / 로그인버튼 / 회원가입버튼) -->
		
		<!-- 
		
			=> http://localhost:8001/jsp/test1.do(서블릿 매핑값)
			form태그 안에 있는 제출버튼(submit) 클릭 시 form태그가 가지고 있는 속성 중
			action에 작성된 url로 요청이 감(제출)
			
			==> Controller(Servlet)을 호출한다고 생각하면 됨
			
			* 경로 지정 방식
			절대 경로 방식(/) : /Context Root/요청할 url
							localhost:8001 뒤에 action에 작성한 값이 붙어지면서 요청
			
			상대 경로 방식(test1.do) : 요청할 url문구로 시작하는 경우
								  현재 이 페이지가 보여질 때 url경로 중에서
								  마지막 /로부터 뒤에 action에 작성한 값이 붙어지면서 요청			
		 -->

 

 

4. LoginController.java

00.00.00 : 대격변버전.기능추가등.에러수정등

1) POST방식일 경우 인코딩 작업

2) 요청 시 전달값을 꺼내서 변수에 기록 => request의 Parameter

3) 해당 요청을 처리해주는 서비스클래스의 메소드를 호출

package com.kh.jsp.member.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 javax.servlet.http.HttpSession;

import com.kh.jsp.member.model.service.MemberService;
import com.kh.jsp.member.model.vo.Member;

/**
 * Servlet implementation class LoginController
 */
@WebServlet("/login.me")
public class LoginController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public LoginController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		/*
		 * <HttpServletRequest, HttpServletResponse>
		 * 
		 * - request : 서버로 요청할 때 정보(요청 시 전달값, 요청 전송방식, 요청한 사용한 정보 등등)
		 * - response : 요청에 대해 응답하고자 할 때 사용하는 객체
		 */
		
		// 1) POST방식일 경우 인코딩 작업
		request.setCharacterEncoding("UTF-8");
		
		// 2) 요청 시 전달값을 꺼내서 변수에 기록 => request의 Parameter
		// request.getParameter("키값") : String
		// request.getParameterValues("키값") : String[] => checkbox일 경우 사용
		
		// System.out.println(request.getParameter("userId"));
		// System.out.println(request.getParameter("userPwd"));
		
		String userId = request.getParameter("userId");
		String userPwd = request.getParameter("userPwd");
		
		// 3) 해당 요청을 처리해주는 서비스클래스의 메소드를 호출
		Member loginUser = new MemberService().loginMember(userId, userPwd);
		// SELECT * FROM MEMBER WHERE USER_ID = '입력한아아디' AND USER_PWD = '입력한비밀번호' AND STATUS = 'Y'
		// 모든 조건을 만족하는 행이 존재한다면 반환된 값에는 필드값이 회원정보로 꽉찬 주소값
		// 하나의 조건이라도 만족하지 못했다면 null이 돌아올 것
		
		// System.out.println(loginUser);
			
		}
		
	}

	/**
	 * @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);
	}

}

 

5. MemberService.java

1) Connection객체 생성

2) Controller에서 넘어온 전달값과 Connection객체를 DAO메소드를 호출하면서 전달

public Member loginMember(String userId, String userPwd) {

    // Service => Connection 객체 생성

    // 1) Connection객체 생성
    Connection conn = JDBCTemplate.getConnection();

    // 2) Controller에서 넘어온 전달값과 Connection객체를 DAO메소드를 호출하면서 전달
    Member m = new MemberDao().loginMember(conn, userId, userPwd);
}

 

6. MemberDao.java

1) 생성자를 통해서 xml파일 읽어오기(SQL읽음)

public class MemberDao {

private Properties prop = new Properties();

public MemberDao() {

    String file = MemberDao.class.getResource("/sql/member/member-mapper.xml").getPath();
    //System.out.println(file);

    try {
        prop.loadFromXML(new FileInputStream(file));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

 

7. member-mapper.xml 생성

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	
	<comment>회원 관련 SQL문</comment>
	
</properties>

 

8. Member.java

1) Member 객체 생성

public class Member {
	
	private int userNo;
	private String userId;
	private String userPwd;
	private String userName;
	private String phone;
	private String email;
	private String address;
	private String interest;
	private Date enrollDate;
	private Date modifyDate;
	private String status;
	
	// 기본생성자, 모든 필드를 매개변수로 가지고 있는 생성자, 각각 setter/getter, toString
}

 

9. MemberDao.java

1) 필요한 변수 세팅

    // 필요한 변수 세팅
    PreparedStatement pstmt = null;
    ResultSet rset = null;
    Member m = null;

 

10. member-mapper.xml 

1) SQL key값으로 저장

STATUS = 'Y' (회원탈퇴해도 정보는 남겨놓는다 / 탈퇴하지 않은 회원 Y)

<entry key="loginMember">
    SELECT
           USER_NO,
           USER_ID,
           USER_PWD,
           USER_NAME,
           PHONE,
           EMAIL,
           ADDRESS,
           INTEREST,
           ENROLL_DATE,
           MODIFY_DATE,
           STATUS
      FROM 
           MEMBER
     WHERE 
           USER_ID = ?
       AND 
           USER_PWD = ?
       AND 
           STATUS = 'Y'
</entry>

 

11. MemberDao.java

1) PreparedStatement 객체생성

2) 위치홀더 채우기

3) 쿼리문 실행 후 결과받기

4) rset으로부터 각가의 컬럼의 값을 뽑아서 Member객체에 담는다.

조회결과가 한 행일 때 => if(rset.next())
조회결과가 여러 행일 때   => while(rset.next())

생성자의 인자값으로 담을 수도 있고 set을 이용하여 담을수도 있다~

5) 자원반납

6) Service에 결과 넘기기

public Member loginMember(Connection conn, String userId, String userPwd) {
    // SELECT문 => ResultSet객체(unique key 제약조건에 의해 한 행만 조회됨) => Member

    // 필요한 변수 세팅
    PreparedStatement pstmt = null;
    ResultSet rset = null;
    Member m = null;

    String sql = prop.getProperty("loginMember");

    try {
        // pstmt객체 생성
        pstmt = conn.prepareStatement(sql);

        // 위치홀더(?) 채우기
        pstmt.setString(1, userId);
        pstmt.setString(2, userPwd);

        // 쿼리문 실행 후 결과 받기
        // 쿼리문 실행 메소드
        // pstmt.executeQuery(); => ResultSet : SELECT
        // pstmt.executeUpdate(); => int / 0 : INSERT / UPDATE / DELETE

        rset = pstmt.executeQuery();

        // rset으로부터 각가의 컬럼의 값을 뽑아서 Member객체에 담는다.
        // 조회결과가 한 행일 때	=> if(rset.next())
        // 조회결과가 여러 행일 때   => while(rset.next())

        if(rset.next()) {

            // 각 컬럼의 값 뽑기
            // rset.getInt / rset.getString / rset.getDate(뽑아올 컬럼명 또는 컬럼의 순번)
            m = new Member(rset.getInt("USER_NO"), 
                           rset.getString("USER_ID"),
                           rset.getString("USER_PWD"),
                           rset.getString("USER_NAME"),
                           rset.getString("PHONE"),
                           rset.getString("EMAIL"),
                           rset.getString("ADDRESS"),
                           rset.getString("INTEREST"),
                           rset.getDate("ENROLL_DATE"),
                           rset.getDate("MODIFY_DATE"),
                           rset.getString("STATUS"));
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        // 자원반납 -> 생성된 순서의 역순으로
        JDBCTemplate.close(rset);
        JDBCTemplate.close(pstmt);
    }
    // Service에 결과(Member) 넘기기
    return m;
}

 

12. MemberService.java

3) Connection 객체 반납

4) Controller에 결과 반환

public Member loginMember(String userId, String userPwd) {

    // 3) Connection객체 반납
    JDBCTemplate.close(conn);

    // 4) Controller에 결과 반환
    return m;
}

 

13. LoginController.java(Servlet)

4) 처리된 결과를 가지고 사용자가 보게 될 응답화면 지정

원래는,

스텝1. request객체에 응답화면에 보여질 값 담기 => request의 attribute영역!!!
스텝2. RequestDispatcher객체 생성 => 응답할 뷰 화면 지정
스텝3. RequestDispatcher객체로부터 forward() 호출

 

request객체에 담게 되면 첫 jsp(로그인성공 화면)만 로그인이 유지됨(로그인 유지된 상태로 다른 페이지에 넘어가지 않음)

그래서,
스텝1. 어딘가에 응답화면에 보여질 값 담기(request, session, application, page)
응답페이지에 전달할 값이 있을 경우 값을 어딘가에 담아야 함 => 어딘가에 Attribute영역에 담아서 보낸다. 
(담아줄 수 있는 객체가 4종류 => Scope 내장 객체)

크다
1) application : 웹 어플리케이션 전역에서 언제나 꺼내 쓸 수 있음(자바클래스에서 쓸 수 있음)
2) session : 모든 JSP와 Servlet에서 꺼내 쓸 수 있음 => 로그인
단, 내가 직접적으로 session객체에 담은 값을 지우기 전까지만 꺼내 쓸 수 있음
세션이 끊기는 경우 : 브라우저가 종료될 때, 서버가 멈춘 경우 
3) request : 해당 request가 포워딩한 응답 JSP에서만 쓸 수 있음 => 장바구니
요청 페이지부터 응답페이지까지에서만 쓸 수 있음
4) page : 담은 값을 해당 JSP페이지에서만 쓸 수 있음
작다

 

=> session, request가 가장 많이 쓰인다.

=> 공통적으로 데이터를 담고자할 때 : XXX.setAttribute(키, 밸류);
데이터를 뽑고자 한다면 : XXX.getAttribute(키);
데이터를 지우고자 한다면 : XXX.removeAttribute(키);
예시)
로그인 시 : session.setAttribute("userInfo", loginUser);
로그아웃 시 : session.removeAttribute("userInfo"); 또는 무효화시키는 메소드 사용

스텝2. RequestDispatcher객체 생성(응답할 뷰 화면 지정) => forward();

로그인 실패 시,

에러메시지 남기기
스텝 1. request의 Attribute영역에 메시지 담기

스텝 2. RequestDispatcher 객체 생성

스텝 3. forwarding

 


// 4) 처리된 결과를 가지고 사용자가 보게 될 응답화면 지정
// 스텝 1. request객체에 응답화면에 보여질 값 담기 => request의 attribute영역!!!
// 스텝 2. RequestDispatcher객체 생성 => 응답할 뷰 화면 지정
// 스텝 3. RequestDispatcher객체로부터 forward() 호출

// 스텝 1. 어딘가에 응답화면에 보여질 값 담기(request, session, application, page)

/*
 * 응답페이지에 전달할 값이 있을 경우 값을 어딘가에 담아야 함 => 어딘가에 Attribute영역에 담아서 보낸다. 
 * 						(담아줄 수 있는 객체가 4종류 => Scope 내장 객체)
 * 
 * 크다
 * 
 * 1) application : 웹 어플리케이션 전역에서 언제나 꺼내 쓸 수 있음(자바클래스에서 쓸 수 있음)
 * 
 * 2) session : 모든 JSP와 Servlet에서 꺼내 쓸 수 있음
 * 				단, 내가 직접적으로 session객체에 담은 값을 지우기 전까지만 꺼내 쓸 수 있음
 * 				세션이 끊기는 경우 : 브라우저가 종료될 때, 서버가 멈춘 경우 
 * 
 * 3) request : 해당 request가 포워딩한 응답 JSP에서만 쓸 수 있음
 * 				요청 페이지부터 응답페이지까지에서만 쓸 수 있음
 * 
 * 4) page : 담은 값을 해당 JSP페이지에서만 쓸 수 있음
 * 
 * 작다
 * 
 * => session, request가 가장 많이 쓰인다.
 * 
 * => 공통적으로 데이터를 담고자할 때 : XXX.setAttribute(키, 밸류);
 *    데이터를 뽑고자 한다면 : XXX.getAttribute(키);
 *    데이터를 지우고자 한다면 : XXX.removeAttribute(키);
 * 
 * 
 * 예시)
 * 로그인 시 : session.setAttribute("userInfo", loginUser);
 * 로그아웃 시 : session.removeAttribute("userInfo"); 또는 무효화시키는 메소드 사용
 */

// 스텝 2. RequestDispatcher객체 생성(응답할 뷰 화면 지정) => forward();

// 로그인 실패
if(loginUser == null) {

    // 에러메시지 남기기
    // 스텝 1. request의 Attribute영역에 메시지 담기
    request.setAttribute("errorMsg", "로그인에 실패했습니다.");

    // 스텝 2. RequestDispatcher 객체 생성
    RequestDispatcher view = request.getRequestDispatcher("views/common/errorPage.jsp");

    // 스텝 3. forwarding
    view.forward(request, response);

 

14. error.jsp 생성

errorMsg호출

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

<%
	String errorMsg = (String)request.getAttribute("errorMsg");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>나는 문제가 생겼을 때 오는 곳이야... ㅠ^ㅠ...</title>
	<style>
	h1{
		color : red;
		margin-top : 100px;
		text-align : center;
		font-size : 33px;
		font-weight : bold;
	}
	</style>
</head>
<body>

	<h1><%= errorMsg %></h1>
</body>
</html>

 

15. LoginController.java

로그인 성공 시, 

index.jsp 데이터 응답
사용자의 정보 넘기기
로그인한 회원의 정보를 로그아웃하거나 브라우저를 종료하기 전까지 계속 사용할 것이기 때문에 session에 담기

스텝 1. session의 attribute영역에 사용자 정보 담기
session 객체의 Type : HttpSession (Session은 서버측에서 관리하는 저장소)
=> HttpSession을 선언하는 방법 : request.getSession()
HttpSession session = request.getSession();

* 포워딩 방식
스텝 2. RequestDispatcher 객체 생성

스텝 3. forward()



} else { // 로그인 성공 => index.jsp 데이터 응답

    // 사용자의 정보 넘기기

    // 로그인한 회원의 정보를 로그아웃하거나 브라우저를 종료하기 전까지 계속 사용할 것이기 때문에 session에 담기

    // 스텝 1. session의 attribute영역에 사용자 정보 담기
    // session 객체의 Type : HttpSession
    // => HttpSession을 선언하는 방법 : request.getSession()
    HttpSession session = request.getSession();

    session.setAttribute("loginUser", loginUser);

    session.setAttribute("alertMsg", "로그인에 성공했다 ~~ 축하한다 ~~~");

    // * 포워딩 방식
    /*
    // 스텝 2. RequestDispatcher 객체 생성
    RequestDispatcher view = request.getRequestDispatcher("index.jsp");

    // 스텝 3. forward()
    view.forward(request, response);
    */

 

16. menubar.jsp

로그인한 유저이름을 보이게 하려면,

로그인이 먼저 되어있어야 함(if 문 안에 userName != null 일 때 )

아니면 nullPointException이 뜸 !!!

=>

<% String userName = loginUser.getUserName(); %> 하고

해당 위치에 <%= userName %> 하려고 봤더니 500error가 뜸

 

menubar.jsp가 로딩될 때마다 alert이 계속 수행됨
session에 들어있는 alertMsg키값에 해당하는 밸류값을 지워줄 것!
=> XX.removeAttribute("키값");
<% session.removeAttribute("alertMsg"); %>

<% if(loginUser == null) { %>

    <!-- <form action="/jsp/login.me" method="post"> -->
    <form action="<%= contextPath %>/login.me" method="post">
        <table>
            <tr>
                <th>아이디</th>
                <td><input type="text" name="userId" required></td>
            </tr>
            <tr>
                <th>비밀번호</th>
                <td><input type="password" name="userPwd" required></td>
            </tr>
            <tr>
                <th colspan="2">
                    <button type="submit" class="btn btn-sm btn-primary">로그인</button>
                    <button type="button" class="btn btn-sm btn-secondary" onclick="enrollPage();">회원가입</button>
                </th>
            </tr>
        </table>
    </form>

 

17. LoginController.java

포워딩방식은 request값이 자꾸 넘어간다. (

url재요청 방식(sendRedirect방식) : Client에게 url을 다시 요청하게 해주면서 응답데이터를 받게 함 
response.sendRedirect("/재요청경로");

response.sendRedirect("/abcdefg");

    // 포워딩방식은 request값이 자꾸 넘어가서 주석처리하고 

    // url재요청 방식(sendRedirect방식) : Client에게 url을 다시 요청하게 해주면서 응답데이터를 받게 함 
    // response.sendRedirect("/재요청경로");

    response.sendRedirect("/jsp");

}

}

/**
* @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);
}

}