본문 바로가기
교재 실습/자바 웹 개발 워크북

47. 데이터 보관소 (3)

by Jint 2022. 6. 18.

2. HttpSession의 활용 - 로그인

HttpSession 객체는 클라이언트 당 한개가 생성된다. 웹 브라우저로부터 요청이 들어오면, 그 웹 브라우저를 위한 HttpSession 객체가 있는지 검사하고, 없다면 새로 HttpSession 객체를 만든다. 이렇게 생성된 HttpSession 객체는 그 웹 브라우저로부터 일정 시간 동안 Timeout 요청이 없으면, 삭제된다. 따라서 로그인되어 있는 동안 지속적으로 사용할 데이터를 HttpSession 객체에 저장한다.

HttpSession 객체를 활용하는 상황을 실습한다. 다음 그림은 로그인을 수행하는 시나리오다(그림 1).

그림 1 (로그인 시나리오)

① 웹 브라우저에서 '/auth/login' 서블릿을 요청한다.

② LogInServlet 클래스는 LogInForm.jsp로 화면 출력 작업을 위임한다.

③ LogInForm.jsp는 로그인 입력폼을 만들어 출력한다.

④ 사용자가 입력한 정보를 가지고 다시 '/auth/login' 서블릿을 요청한다. 단 이번에는 POST 요청이다.

⑤ LogInServlet 클래스는 이메일과 암호가 일치하는 회원 정보를 데이터베이스에서 찾아서 값 객체 'Member'에 담는다. 또한, 다른 서블릿들도 참조할 수 있도록 HttpSession 객체에 보관한다. 만약 이메일과 암호가 일치하는 회원을 찾지 못한다면, LogInFail.jsp로 작업을 위임한다.

⑥ 로그인 성공일 때, 회원 목록 페이지로 리다이렉트 한다. 로그인 실패일 때, 로그인 실패 메시지를 출력한 후 다시 로그인 입력폼으로 리프래시 한다.

 

- 로그인 컨트롤러 만들기

로그인 입력 화면을 만들고 로그인 정보를 처리할 서블릿을 만든다. 지금까지 배운 MVC 구조를 일부 적용한다.

spms.servlets 패키지에 LogInServlet 클래스를 생성한다.

package spms.servlets;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
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 spms.vo.Member;

@WebServlet("/auth/login")
public class LogInServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//로그인 페이지로 포워딩
		RequestDispatcher rd = request.getRequestDispatcher("/auth/LogInForm.jsp");
		rd.forward(request, response);
	}
	
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			ServletContext sc = this.getServletContext();
			conn = (Connection)sc.getAttribute("conn");  
			stmt = conn.prepareStatement(
					"SELECT MNAME,EMAIL FROM MEMBERS"
					+ " WHERE EMAIL=? AND PWD=?");
			stmt.setString(1, request.getParameter("email"));
			stmt.setString(2, request.getParameter("password"));
			rs = stmt.executeQuery();
			if(rs.next()) {//이메일, 암호 일치하는 회원 있을때 - 로그인 성공
				Member member = new Member().setEmail(rs.getString("EMAIL"))
											.setName(rs.getString("MNAME"));
				HttpSession session = request.getSession();
				session.setAttribute("member", member);
				//회원 목록 페이지로 리다이렉트
				response.sendRedirect("../member/list");
			}else {//이메일, 암호 일치하는 회원 없을때 - 로그인 실패
				//로그인 실패 페이지로 포워딩
				RequestDispatcher rd = request.getRequestDispatcher("/auth/LogInFail.jsp");
				rd.forward(request, response);
			}
		}catch(Exception e) {
			throw new ServletException(e);
		}finally {
			try {if (rs != null) rs.close();} catch (Exception e) {}
			try {if (stmt != null) stmt.close();} catch (Exception e) {}
		}
	}
}

웹 브라우저로부터 GET 요청이 들어오면 doGet()이 호출되어 LogInForm.jsp로 포워딩한다.

//로그인 페이지로 포워딩
RequestDispatcher rd = request.getRequestDispatcher("/auth/LogInForm.jsp");
rd.forward(request, response);

JSP에서 다시 서블릿으로 돌아올 필요가 없기 때문에 인클루딩 대신 포워딩으로 처리한다.

사용자가 이메일과 암호를 입력한 후 POST 요청을 하면 doPost()가 호출된다. doPost()에서는 데이터베이스로부터 회원 정보를 조회한다. 만약 이메일과 암호가 일치하는 회원을 찾는다면 값 객체 Member에 회원 정보를 담는다.

Member member = new Member().setEmail(rs.getString("EMAIL"))
                            .setName(rs.getString("MNAME"));

그리고 Member 객체를 HttpSession 에 보관한다.

HttpSession session = request.getSession();
session.setAttribute("member", member);

로그인 성공이면, /member/list로 리다이렉트 한다.

//회원 목록 페이지로 리다이렉트
response.sendRedirect("../member/list");

여기서 '../'는 현재 위치의 상단폴더라는 뜻이다.

'/'는 루트를 의미하면 './'는 현재 위치, '../'는 현재 위치의 상단 폴더를 의미한다.

예시) File.xxx가 /web05/member/list에 위치하면 '/'는 web05, './'는 list, '../'는 member 이다.

즉, '/'는 가장 최상의 폴더로 이동하고(웹 애플리케이션 루트), './'는 파일의 현재 폴더, '../'는 파일이 존재하는 폴더의 상위 폴더로 이동한다. '../../'는 두 단계 상위 폴더로 이동한다.

출처 : https://88240.tistory.com/122

 

절대경로와 상대경로

참말로... 소스코드 짜다보면 절대경로와 상대경로. 너무헷갈린다! 학교나 학원이나 또는 혼자 공부로 경로에 대해서 수없이 공부해도 매번 쓸때마다 헷갈리는 경로. 개념은 숙지했어도 실천에

88240.tistory.com

로그인에 실패하면, /auth/LogInFail.jsp로 포워딩 한다.

//로그인 실패 페이지로 포워딩
RequestDispatcher rd = request.getRequestDispatcher("/auth/LogInFail.jsp");
rd.forward(request, response);

 

- 로그인 입력폼 만들기

web05 프로젝트의 webapp 폴더에 auth 폴더를 생성하고, LogInForm.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>로그인</title>
</head>
<body>
<h2>사용자 로그인</h2>
<form action="login" method="post">
이메일: <input type="text" name="email"><br>
암호: <input type="password" name="password"><br>
<input type="submit" value="로그인">
</form>
</body>
</html>

로그인 입력폼의 값을 서버에 전달할 때 POST 요청을 하도록 <form> 태그를 설정했다. 요청 URL은 입력폼 요청할 때의 URL과 같다. 이렇게 로그인 입력폼의 URL과 로그인 처리를 하는 URL이 같더라도, 서버에서는 GET 요청과 POST 요청을 구분하여 처리하기 때문에 상관없다.

<form action="login" method="post">
...
</form>

 

- 로그인 실패 시 출력작업을 수행할 JSP 만들기

web05 프로젝트의 webapp 폴더의 auth 폴더에 LogInFail.jsp를 작성한다. 이 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">
<meta http-equiv="Refresh" content="1;url=login">
<title>로그인 실패</title>
</head>
<body>
<p>로그인 실패입니다. 이메일 또는 암호가 맞지 않습니다.!<br>
잠시 후에 다시 로그인 화면으로 갑니다.</p>
</body>
</html>

여기서 중요한 것은, 로그인 실패에 대한 안내 문구를 출력한 후, 1초가 지난 다음에 다시 서버에 로그인 입력폼을 요청하도록 설정했다는 것이다.

<meta http-equiv="Refresh" content="1;url=login">

<head> 태그를 보면 리프래시를 설정한 <meta> 태그를 볼 수 있는데, 1초 후에 /auth/login을 다시 요청하라고 웹 브라우저에게 내리는 명령이다.

 

톰캣 서버를 시작한 후, 웹 브라우저에서 http://localhost:9999/web05/auth/login을 요청한다(그림 2).

그림 2 (로그인 입력폼)

로그인 실패를 먼저 테스트한다. 데이터베이스에 등록되지 않은 임의의 이메일과 암호를 입력하여 '로그인' 버튼을 클릭한다(그림 3).

그림 3 (로그인 실패 안내 문구 출력 화면)

그림 3과 같은 화면을 출력한 후 1초 후에 다시 로그인 입력폼이 출력될 것이다.

로그인 성공을 테스트한다. 데이터베이스에 등록된 회원의 이메일과 암호를 사용하여 로그인한다(그림 4).

그림 4 (로그인 성공 후 회원 목록 화면)

회원 목록이 정상적으로 출력되는 것을 확인할 수 있다. 하지만, 화면 상단에 로그인한 회원의 이름이 출력되지 않았다. 이 부분을 처리해 보겠다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr

'교재 실습 > 자바 웹 개발 워크북' 카테고리의 다른 글

49. 데이터 보관소 (5)  (0) 2022.06.20
48. 데이터 보관소 (4)  (0) 2022.06.19
46. 데이터 보관소 (2)  (0) 2022.06.16
45. 데이터 보관소 (1)  (0) 2022.06.15
44. 포워딩과 인클루딩 (2)  (0) 2022.06.14

댓글