자바(JAVA)/JSP 웹 프로그래밍 공부 (성낙현의 JSP 자바 웹 프로그래밍 참고)
JAVA/JSP 18. 세션(Session) - 세션과 DB를 이용한 로그인 구현, 쿠키 vs 세션
개발학생
2023. 11. 30. 23:32
반응형
세션과 DB를 이용한 로그인 구현 (데이터베이스와 연동, 전에 만든 member 테이블의 회원 정보 활용)
1. 로그인 페이지 작성 - 간단한 로그인 폼
- LoginForm.jsp 파일 생성
// WebContent/Session/LoginForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>Session</title></head>
<body>
<h2>로그인 페이지</h2>
// 1. (span 태그 전체)
<span style="color: red; font-size: 1.2em;">
<%= request.getAttribute("LoginErrMsg") == null ?
"" : request.getAttribute("LoginErrMsg") %>
</span>
<%
if (session.getAttribute("UserId") == null) { // 2.로그인 상태 확인
// 로그아웃 상태
%>
<script>
// 3.(function 전체)
function validateForm(form) {
if (!form.user_id.value) {
alert("아이디를 입력하세요.");
return false;
}
if (form.user_pw.value == "") {
alert("패스워드를 입력하세요.");
return false;
}
}
</script>
// 4.(form 태그 전체)
<form action="LoginProcess.jsp" method="post" name="loginFrm"
onsubmit="return validateForm(this);"> // 5.
아이디 : <input type="text" name="user_id" /><br />
패스워드 : <input type="password" name="user_pw" /><br />
<input type="submit" value="로그인하기" />
</form>
<%
} else { // 로그인된 상태
%>
//6.(아래 코드 2줄)
<%= session.getAttribute("UserName") %> 회원님, 로그인하셨습니다.<br />
<a href="Logout.jsp">[로그아웃]</a>
<%
}
%>
</body>
</html>
- request 내장 객체 영역에 LoginErrMsg 속성이 있는지 확인하고, 그 내용을 출력 (회원 인증 실패 시 request 영역에 이 속성을 저장한 후 포워드 할 예정)
- session 영역에 사용자 아이디(UserId)가 저장되어 있는지 확인: 값이 null이면 사용자 아이디가 저장되지 않은 것이므로 ‘로그아웃 상태’
→ 이 코드의 결과에 따라 ‘로그아웃 상태’라면 3.과 4.가 실행되고, 로그인 상태라면 6.이 실행됨 - validateForm(): 자바스크립트로 작성한 유효성 검사 함수로, 아이디와 패스워드 중 빈 값이 있다면 경고창을 띄움
- form 태그와 input 태그로 작성한 로그인 폼
- 4.의 로그인 폼에서 폼값 전송 시, onsubmit 이벤트 핸들러가 validateForm()을 호출하도록 함
→ 유효성 검사 통과 시, 폼에 입력한 값이 post 방식으로 LoginProcess.jsp에 전송됨 (서버에서 이 값을 받아 로그인 처리를 하는데, 이때 DB 연동이 필요함) - 2.에서 session 영역에 사용자 아이디가 저장되어 있다고 판정되면(=로그인 상태라면) 실행됨
→ 로그인한 사용자의 이름, 로그아웃 버튼을 보여 줌
2. DB 연동
- 데이터를 주고받기 위한 DTO 클래스, 테이블에 접근하기 위한 DAO 클래스 생성
DTO (Data Transfer Object) = VO(Value Object, 값 객체) |
계층 사이에서 데이터를 교환하기 위해 생성하는 객체 | 별 다른 로직 없이, 속성(멤버 변수)과 그 속성에 접근하기 위한 Getter/Setter 메소드만 갖춤 |
DAO (Data Access Objcet) |
데이터베이스의 데이터에 접근하기 위한 객체 (JDBC를 통해 구현) | 하나의 테이블에서 수행할 수 있는 CRUD(Creste, Read, Update, Delete) 작업을 전담함 |
1) 회원 정보용 DTO 객체 준비
- 테이블 당 1개이고, 테이블의 컬럼과 동일한 멤버 변수를 가짐
- Java Resources/src에서 마우스 우클릭 → [New] → [Class] → ‘membership’ 패키지 및 MemberDTO.java 파일 생성
// Java Resources/src/membership/MemberDTO.java
package membership;
public class MemberDTO {
// 멤버 변수 선언
private String id;
private String pass;
private String name;
private String regidate;
// 멤버 변수별 게터와 세터
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRegidate() {
return regidate;
}
public void setRegidate(String regidate) {
this.regidate = regidate;
}
}
2) 회원 정보 테이블용 DAO 객체 준비
- DB에 연결한 후, CRUD에 해당하는 SQL 쿼리문을 실행하고, 그 결과를 DTO 객체에 담아 반환
// Java Resources/src/membership/MemberDAO.java
package membership;
import common.JDBConnect;
public class MemberDAO extends JDBConnect { //1.
// 명시한 데이터베이스로의 연결이 완료된 MemberDAO 객체를 생성합니다.
public MemberDAO(String drv, String url, String id, String pw) {
super(drv, url, id, pw); //2.
}
// 명시한 아이디/패스워드와 일치하는 회원 정보를 반환합니다.
public MemberDTO getMemberDTO(String uid, String upass) { //3.
//4.(아래 코드 2줄)
MemberDTO dto = new MemberDTO(); // 회원 정보 DTO 객체 생성
String query = "SELECT * FROM member WHERE id=? AND pass=?"; // 쿼리문 템플릿
try { //5.
// 쿼리 실행
psmt = con.prepareStatement(query); //6.(동적 쿼리문 준비)
//7.(아래 코드 2줄)
psmt.setString(1, uid); // 쿼리문의 첫 번째 인파라미터에 값 설정
psmt.setString(2, upass); // 쿼리문의 두 번째 인파라미터에 값 설정
rs = psmt.executeQuery(); //8.(쿼리문 실행)
// 결과 처리
if (rs.next()) { //9.
// 쿼리 결과로 얻은 회원 정보를 DTO 객체에 저장
//10.(아래 코드 4줄)
dto.setId(rs.getString("id"));
dto.setPass(rs.getString("pass"));
dto.setName(rs.getString(3));
dto.setRegidate(rs.getString(4));
}
}
catch (Exception e) {
e.printStackTrace();
}
return dto; //11.(DTO 객체 반환)
}
}
- MemberDAO가 상속한 JDBConnect는 ‘데이터베이스’ 파트에서 만들었던 클래스
: JDBC 연결 관리와 쿼리문 작성 등의 기능 제공 (DB 연결 시 필수) - 부모인 JDBConnect 클래스의 생성자 중 매개변수 4개짜리를 호출함.
super()는 부모 클래스의 생성자를 뜻하는데, 이 생성자가 호출되면 DB 연결 완료 - getMemberDTO() 메소드: 매개변수로 받은 아이디/패스워드와 일치하는 회원을 찾아 MemberDTO 형태로 반환
- DTO 객체와 쿼리문 템플릿 준비
(아이디와 패스워드가 일치하는 회원 찾음 - 인파라미터인 id=?, pass=?의 값은 7.에서 설정) - try/catch문으로 JDBC 프로그래밍에 기본적으로 필요한 예외처리를 함
- 쿼리문 템플릿 완성&실행 시작 단계
→ 인파라미터가 있는 동적 쿼리문을 실행하기 위한, PreparedStatement 객체 생성
(con은 JDBConnect에서 정의한 멤버 변수로, DB와의 연결 객체) - 쿼리문 템플릿에 ?로 처리된 인파라미터의 값 설정
→ setString() 메소드로, 인덱스 1에 아이디(uid) 설정 & 인덱스 2에 패스워드(upass) 설정 - 완성된 쿼리문 실행 (결과 레코드는 ResultSet 객체에 담겨 반환됨)
- 결과 레코드에 담긴 dto 객체 채우기의 첫 단계
→ next()메소드로 ResultSet 객체에 레코드가 들어 있는지 확인하고,
아이디와 패스워드가 일치하는 멤버 존재 시 레코드가 담겨 있으므로 true 반환 - 레코드의 값들을 getString() 메소드로 가져와서 dto 객체에 저장
(getString()의 인수로는, 컬럼명과 인덱스[=테이블 정의에서의 컬럼 순서] 둘 다 가능) - 완성된 DTO 객체를 반환
3) 로그인 처리 JSP 구현
- 사용자로부터 받은 폼값(아이디와 패스워드)으로 로그인을 처리할 JSP 파일
→ 폼값은 LoginForm.jsp 파일의 로그인 화면에서 전달받고, DB상의 정보는 DAO를 이용해 가져옴 - LoginProcess.jsp 파일 생성
// WebContent/Session/LoginProcess.jsp
<%@ page import="membership.MemberDTO"%>
<%@ page import="membership.MemberDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//1.로그인 폼으로부터 받은 아이디와 패스워드
String userId = request.getParameter("user_id");
String userPwd = request.getParameter("user_pw");
//2.web.xml에서 가져온 데이터베이스 연결 정보
String oracleDriver = application.getInitParameter("OracleDriver");
String oracleURL = application.getInitParameter("OracleURL");
String oracleId = application.getInitParameter("OracleId");
String oraclePwd = application.getInitParameter("OraclePwd");
//회원 테이블 DAO를 통해 회원 정보 DTO 획득
MemberDAO dao = new MemberDAO(oracleDriver, oracleURL, oracleId, oraclePwd); //3.
MemberDTO memberDTO = dao.getMemberDTO(userId, userPwd); //4.
dao.close(); //5.
// 로그인 성공 여부에 따른 처리
if (memberDTO.getId() != null) { //6.
//7.(아래 코드 2줄 - 로그인 성공)
session.setAttribute("UserId", memberDTO.getId());
session.setAttribute("UserName", memberDTO.getName());
//8.
response.sendRedirect("LoginForm.jsp");
}
else {
// 로그인 실패
request.setAttribute("LoginErrMsg", "로그인 오류입니다."); //9.
request.getRequestDispatcher("LoginForm.jsp").forward(request, response); //10.
}
%>
- 로그인 폼에서 전송된 아이디와 패스워드를, request 내장 객체를 통해 받아 둠
→ 로그인 실패 시, 어떤 오류가 발생했는지를 화면에 출력
→ 아이디와 패스워드에 해당하는 회원이 데이터베이스에 존재하면 로그인 성공,
존재하지 않으면 로그인 실패 (DB에 연결해야 확인 가능) - 데이터베이스 연결 정보는 web.xml에 컨텍스트 초기화 매개변수로 저장해뒀으니,
application 내장 객체를 통해 가져옴 - web.xml에서 가져온 데이터베이스 연결 정보를 건네서,
MemberDAO 객체 생성 (이 과정에서 오라클 데이터베이스 접속) - 사용자가 입력한 아이디와 패스워드를 인수로 getMemberDTO()를 호출
→ 아이디와 패스워드 일치 시 해당 회원의 정보가 저장된 DTO 객체 반환 - 데이터베이스 작업이 다 끝났으므로, 데이터베이스 연결 해제
- DTO 객체에 아이디가 담겨 있다면 로그인 성공
- 로그인 성공 시 이 session 영역에 아이디와 이름을 저장한 후
- 로그인 페이지로 이동 (7.에서 이어짐)
- 로그인 실패 시, 이 request 영역에 오류 메시지를 저장함
[request 영역에 저장된 속성값은 포워드된 페이지까지 공유됨] - 로그인 페이지로 포워드함 (9.에서 이어짐)
4) 동작 확인
- LoginForm.jsp를 [Run On Server]로 실행하고, ‘아이디’와 ‘패스워드’에 일부러 잘못된 값을 입력한 후 [로그인하기] 버튼 클릭
- 로그인에 실패했으므로, LoginProcess.jsp에서 LoginForm.jsp로 포워드됨
→ 주소표시줄에는 LoginProcess.jsp가 표시되지만 화면에는 LoginForm.jsp의 내용이 출력됨
(request 영역에 저장된 “로그인 오류입니다.” 출력됨)
- ‘아이디’, ‘패스워드’에, member 테이블에 입력했던 데이터인 ‘hello’, ‘1234’를 각각 입력 후 [로그인하기] 버튼 클릭
3. 로그아웃 처리
- session 영역에 저장된 로그인 관련 속성을 모두 지워주기만 하면 됨
1) 프로젝트의 [WebContent] → [Session]에서 Logout.jsp 파일 생성 후 코드 작성
// WebContent/Session/Logout.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
// 방법 1: 회원인증정보 속성 삭제
session.removeAttribute("UserId");
session.removeAttribute("UserName");
// 방법 2: 모든 속성 한꺼번에 삭제
session.invalidate();
// 속성 삭제 후 페이지 이동
response.sendRedirect("LoginForm.jsp");
%>
방법 1: removeAttribute()가 인수로 지정한 특정 속성을 선택해서 삭제하는 메소드
방법 2: invalidate()가 session 영역의 모든 속성을 한꺼번에 삭제하는 메소드 (세션 자체를 무효화 함 - 세션 정보를 더 이상 유지할 필요 없으므로 부담이 줄어듬)
속성 삭제 후 페이지 이동: 로그아웃 후 로그인 페이지로 이동
- 아이디, 패스워드에 ‘hello’, ‘1234’를 각각 입력 후 [로그인하기] 버튼 클릭 후 로그인에 성공한 상태에서,
[로그아웃] 버튼을 클릭
→ 처음의 로그인 페이지(LoginForm.jsp)로 이동
4. 공통 링크 추가
- 페이지 사이의 이동을 좀 더 편하게 하기 위해, 로그인 페이지 상단에 공통 링크 추가
1) 프로젝트의 [WebContent] → [Common]에 Link.jsp 파일 생성
// WebContent/Common/Link.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<table border="1" width="90%">
<tr>
<td align="center">
<!-- 로그인 여부에 따른 메뉴 변화 -->
<% if (session.getAttribute("UserId") == null) { %>
<a href="../Session/LoginForm.jsp">로그인</a> <!-- 1. -->
<% } else { %>
<a href="../Session/Logout.jsp">로그아웃</a> <!-- 2. -->
<% } %>
<!-- 회원제 게시판 프로젝트 진행 시 사용할 공통 링크 -->
<!-- 메뉴 사이의 공백(space) 확보용 특수 문자 -->
<a href="../Board/List.jsp">게시판(페이징 X)</a> <!-- 3. -->
<a href="..PagingBoard/List.jsp">게시판(페이징 O)</a> <!-- 4. -->
</td>
</tr>
</table>
- 로그아웃 상태면 [로그인] 링크를 출력
- 로그인 상태면 [로그아웃] 링크를 출력
- 페이징 X 게시판으로 갈 링크
- 페이징 O 게시판으로 갈 링크
2) 1)의 공통 링크를 로그인 폼(LoginForm.jsp) 상단에 추가
- LoginForm.jsp 파일 코드 추가
// WebContent/Session/LoginForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head><title>Session</title></head>
<body>
<jsp:include page="../Common/Link.jsp" /> <!--공통 링크 추가 -->
<h2>로그인 페이지</h2>
...생략...
</body>
</html>
3) 동작 확인
- LoginForm.jsp 파일을 [Run On Server]로 실행
→ ‘로그인 페이지’ 글자 위에 공통 링크가 생겼고, 공통 링크 중 ‘로그인’은 로그인을 했을 때 ‘로그아웃’으로 바뀜
쿠키 vs 세션
- 로그인은 쿠키보다는 세션을 이용해 구현하는 것이 좋음
쿠키 | 세션 | |
저장 위치/형식 | 클라이언트 PC에 text로 저장됨 | 웹 서버에 Object 타입으로 저장됨 |
보안 | 클라이언트에 저장되어 보안에 취약 | 서버에 저장되므로 보안에 안전함 |
자원/속도 | 서버 자원 사용 X/속도가 세션보다 빠름 | 서버 자원 사용 O/속도가 쿠키보다 느림 |
용량 | 용량 제한 O | 용량 제한 X (서버가 허용하는 한) |
유지 시간 | 쿠키 생성 시 설정 (설정 된 시간 경과 시 무조건 삭제됨) | 서버의 web.xml에서 설정 |
(설정 된 시간 경과 시에도 동작이 있으면 삭제되지 않고 유지됨) |
반응형