본문 바로가기

Sever

5. 로그인 기능 구현하기

로그인

사용자 인증 과정 중 하나로, 사용자가 입력한 아이디와 비밀번호가 데이터베이스에 저장된 회원정보와 일치하는지 확인하는 것이다. 일치하는 경우, 웹 어플리케이션은 해당 사용자의 정보를 Session 객체에 저장하여 유지한다. 이후, 사용자의 요청에 대해 해당 세션 정보를 참조하여 인증된 사용자임을 확인하고 적절한 서비스를 제공한다. 

 

* Session 객체

서버 측에서 클라이언트의 상태를 유지하기 위한 객체로, 사용자가 로그인을 하거나 어떤 정보를 제출하면 그 정보를 받아 처리한 후 , 사용자에게 할당된 고유한 세선 ID를 생성한다. 이 세선 ID는 쿠키나 URL 매개변수(parameter)를 통해 클라이언트에게 전달되어, 이후 클라이언트가 서버에 요청을 보낼 때마다 세선 ID를 함께 보낸다. 

 


 

1. Servlet 

package edu.kh.community.member.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import edu.kh.community.member.model.service.MemberService;
import edu.kh.community.member.model.vo.Member;

// --------------------------------------------

@WebServlet("/member/login")
public class LoginServlet extends HttpServlet {
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
			
		// 전달된 파라미터 변수에 저장 
		String inputEmail = req.getParameter("inputEmail");
		String inputPw = req.getParameter("inputPw");
		
		// 사용자로부터 입력받은 정보인 inputEmail, inputPw를 Member객체의 meberEmail과 memberPw에 세팅
		// (Mebmer객체는 VO클래스로 데이터베이스 테이블과 1:1 매핑)
		Member mem = new Member();
		mem.setMemberEmail(inputEmail);
		mem.setMemberPw(inputPw);
		
		try {
        
			// 서비스 객체 생성
			MemberService service = new MemberService();
            
			// 이메일, 비밀번호가 일치하는 회원을 조회하는 서비스 호출 후 결과 반환 받기 
			Member loginMember = service.login(mem);
			
			// Session 객체 얻어오기 
			HttpSession session = req.getSession();
			
			if(loginMember != null) { // 성공
				
				// 회원 정보를 Session에 세팅 
				session.setAttribute("loginMember", loginMember);
				
				// 세션 만료: 특정 시간동안 요청 없으면 세션 만료 
				session.setMaxInactiveInterval(3600); // 초 
				
				// 쿠키 객체 생성 
				Cookie c = new Cookie("saveId", inputEmail);
				
				// 아이디 저장이 체크된 경우 
				if(req.getParameter("saveId") != null){
					// 쿠키 파일을 30일간 유지
					c.setMaxAge(60*60*24*30);//초단위 
					
				} else { // 체크 안했을 때 ( 체크 O -> 체크해제 했을 때 ) 
					// 쿠키 파일을 0초 동안 유지
					// -> 기존에 존재하던 쿠키 파일의 유지 시간을 0초로 덮어 씌움 = 삭제하겠다. 
					c.setMaxAge(0);
				}
				
				// 해당 쿠키 파일이 적용될 주소를 지정 
				// 최상위 주소로 시작하는 주소에서만쿠키 적용
				c.setPath(req.getContextPath());
				
				// 응답 객체를 이용해서 클라이언트로 전달 
				resp.addCookie(c);
				
				
			} else { // 실패 
				session.setAttribute("message", "아이디 또는 비밀번호가 일치하지 않습니다.");

			}
			
			// sednRedirect(): 이전 요청(req)에서 보낸 HTTP 응답(resp)을 다시 보낼 때 사용 
			// 웹 브라우저는 새로운 요청을 보내고 새로운 URL로 이동
			// 이동한 URL은 반드시 절대경로 혹은 전체 URL 
			resp.sendRedirect(req.getContextPath()); 

		} catch (Exception e) {
			e.printStackTrace();
				
		}
	}

}

* HttpSession 객체 

 - 클라이언트와 서버 간의 상태 정보를 유지하기 위한 객체로, 일반적으로 사용자 인증 정보나 세션데이트 덩을 저장하고 관리함

 - 만약, 현재 요청에 대한 HttpSession객체가 이미 존재한다면, 해당 객체를 반환하고 없다면 새로운 HttpSession객체 생성하고, 이후 HttpSession 객체는 session 변수에 할당되어 사용될 수 있음

 - HttpSession 객체를 사용하면, 사용자가 로그인한 상태를 유지하거나, 쇼핑몰 장바구니 등 데이터를 관리하는 다양한 기능을 구현할 수 있으며, HttpSession 객체를 사용하는 것은 쿠키를 사용하는 것보다 보안적으로 안전하며 서버측에서 직접 관리하여 보안에 강함

 

* 아이디 저장(Cookie) 

 - Cookie: 클라이언트(브라우저)에서 관리하는 파일로, 특정 주소 요청 시마다 해당 주소와 연관된 쿠키 파일을 브라우저가 알아서 읽어오며, 읽어온 쿠키 파일의 내용을 서버에 같이 전달한다. 

<생성 및 사용 방법>

 ① 서버가 요청에 대한 응답을 할 때, 쿠키를 생성한 후 응답에 쿠키를 담아 클라이언트에게 전달

 ② 응답에 담긴 쿠키가 클라이언트에게 파일 형태로 저장

 ③ 이후 특정 주소 요청 시 쿠키 파일을 브라우저가 찾아 자동으로 요청에 실어서 보냄 

 ④ 서버는 요청에 실려온 쿠키파일을 전송

 

* 요청 

1. forward: 요청 위임(로그인의 경우 딱히 응답해야 할 페이지가 없음) 

 - Servlet으로 응답화면을 만들기가 불편하기 때문에 req, resp 객체를 위임 하여 요청에 대한 응답화면을 대신 만듦

2. redirect: 재요청

 - 현재 Servlet에서 응답 페이지를 만들지 않고 응답페이지를 만들 수 있는 다른 요청의 주소로 클라이언트를 이동시킴(재요청)
 - redirect 시 request 객체가 유치되지 않기 때문에 특정 데이터를 전달하거나 유지하고 싶으면 session 또는 application 범위에 세팅해야 함



 

2. Service

package edu.kh.community.member.model.service;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import static edu.kh.community.common.JDBCTemplate.*;
import edu.kh.community.member.model.dao.MemberDAO;
import edu.kh.community.member.model.vo.Member;

// ---------------------------------------------

public class MemberService {
	
	private MemberDAO dao = new MemberDAO();

	/** 로그인 서비스 
	 * @param mem
	 * @return
	 */
	public Member login(Member mem) throws Exception {
    
		// Connection 얻어오기 
		Connection conn = getConnection();
		
		
		// DAO 수행(mem: email, pw) 
		Member loginMember = dao.login(conn, mem);

		
		// Connection 반환 
		close(conn); 
				
		// 결과 반환 
		return loginMember;
		
	}

 

 

3. DAO 

package edu.kh.community.member.model.dao;

import java.io.FileInputStream;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import static edu.kh.community.common.JDBCTemplate.*;

import edu.kh.community.member.model.vo.Member;

//------------------------------------------------

public class MemberDAO {
	
	private Statement stmt;		    
	private PreparedStatement pstmt; 
	private ResultSet rs;
	private Properties prop;
	
	public MemberDAO() {
		try {
			prop = new Properties();
			
			String filePath = MemberDAO.class.getResource("/edu/kh/community/sql/member-sql.xml").getPath();
			
			prop.loadFromXML( new FileInputStream(filePath) );
			
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	

	/** 로그인 서비스 
	 * @param conn 
	 * @param mem
	 * @return
	 * @throws SQLException
	 */
	public Member login(Connection conn, Member mem) throws SQLException {
		
        // 결과 저장용 변수 선언 
		Member loginMember = null; 
		
		
		try {
			//SQL 얻어오기(entry key: login)
			String sql = prop.getProperty("login");
			
			//PreparedStatement 생성 및 SQL 적재 
			pstmt = conn.prepareStatement(sql);
			
			pstmt.setString(1, mem.getMemberEmail());
			pstmt.setString(2, mem.getMemberPw());
			
			// SQL 수행 (db로 보냈다 조회결과 가져와서 넣음) 
			rs = pstmt.executeQuery(); 
            
            
			/* 수행할 SQL 
			<entry key="login">
				SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICK, MEMBER_TEL,
				MEMBER_ADDR, PROFILE_IMG,
				TO_CHAR(ENROLL_DT, 'YYYY-MM-DD HH24:MI:SS') AS ENROLL_DT
				FROM MEMBER
				WHERE MEMBER_EMAIL = ?
				AND MEMBER_PW = ?
				AND SECESSION_FL = 'N'
			</entry>
			*/
            
            
			// 얻어온 결과가 하나, if문 사용 
			if(rs.next()) {
				loginMember = new Member();
				loginMember.setMemberNo(  rs.getInt("MEMBER_NO")  );
				loginMember.setMemberEmail( 	rs.getString("MEMBER_EMAIL") );
				loginMember.setMemberNickname( 	rs.getString("MEMBER_NICK")	 );
				loginMember.setMemberTel( 		rs.getString("MEMBER_TEL") 	 );
				loginMember.setMemberAddress( 	rs.getString("MEMBER_ADDR")  );
				loginMember.setProfileImage( 	rs.getString("PROFILE_IMG")  );
				loginMember.setEnrollDate( 		rs.getString("ENROLL_DT") 	 );
			}
			
		} finally {
			close(rs);
			close(pstmt);
		}

		return loginMember;
		// 결과: null or member 객체 주소 
	}