TIL_20221124_세션/쿠키/필터/리스너

창고·2022년 11월 24일
0

서적 : http://www.yes24.com/product/goods/112373280
Spring + MyBatis + JSP 실습 겸 리마인드용으로 공부 중입니다.

1. 세션과 필터

(1) 무상태에서 과거를 기억하는 법

  • HTTP는 기본적으로 무상태(stateless)이므로 과거의 요청 기록을 알 수 없음
    -> 과거의 방문 기록을 추적하는 기법인 세션 트래킹이 필요
  • 쿠키와 세션 트래킹
    • 쿠키 : 문자열로 만들어진 데이터의 조각, 서버 - 브라우저 간 요청, 응답 시에 주고 받는 형태로 사용
    • 이름(name)과 값(value)의 구조가 기본적인 형태
  • 쿠키를 주고 받는 기본적인 시나리오
    • 브라우저에서 최초로 서버 호출 시 : 해당 서버에서 발행한 쿠키가 없다면 브라우저는 아무것도 전송하지 않음
    • 서버에서는 응답 메시지를 보낼 때 브라우저에게 쿠키를 보냄, 'Set-Cookie' 라는 HTTP 헤더를 이용
    • 브라우저는 쿠키를 받은 후 이에 대한 정보를 읽고 파일 형태로 보관할 것인지 메모리상에서만 처리할 것인지를 결정. (판단은 쿠키에 있는 유효기간(만료기간)으로 판단)
    • 브라우저가 보관하는 쿠키는 다음에 다시 브라우저가 서버에 요청할 때 HTTP 헤더에 'Cookie' 라는 헤더 이름과 함께 전달 (쿠키에는 경로 path를 지정할 수 있어 해당 경로에 맞는 쿠키 전송)
    • 서버에서는 필요에 따라 브라우저가 보낸 쿠키를 읽고 이를 사용
  • 쿠키를 생성하는 방법
    • 서버에서 자동으로 생성하는 쿠키
      • 응답 메시지 작성 시 정해진 쿠키가 없는 경우 자동으로 발행 WAS에서 발행 (WAS마다 고유한 이름으로 발행)
      • 기본적으로 브라우저의 메모리상에 보관 -> 브라우저 종료 시 발행한 쿠키 삭제됨
      • 서버에서 발행한 쿠키의 경로는 "/"로 지정됨
    • 개발자가 생성하는 쿠키
      • 이름을 원하는대로 지정 가능
      • 유효기간을 지정할 수 있음 (유효기간 지정 시 브라우저가 파일 형태로 보관)
      • 반드시 직접 응답에 추가해주어야 함
      • 경로나 도메인을 지정할 수 있음 (특정한 서버의 경로를 호출하는 경우에만 쿠키 사용)

(2) 서블릿 컨텍스트와 세션 저장소

  • 하나의 WAS는 여러 개의 웹 애플리케이션을 실행할 수 있음
  • 서블릿 컨텍스트
    • 각각의 웹 애플리케이션은 자신만이 사용하는 고유의 메모리 영역을 하나 생성하여 해당 공간에 서블릿 / JSP 등을 인스턴스로 만들어 서비스를 제공
    • 이를 서블릿 컨텍스트라고 함
  • 세션 저장소
    • 각각의 웹 애플리케이션 생성 시 WAS가 발행하는 쿠키(세션 쿠키)들을 관리하기 위한 메모리 영역이 하나 더 생성
    • 이를 세션 저장소라고 함
    • 키(key)와 값을 보관하는 구조. 이 때 키 역할을 하는 것이 WAS가 발행하는 쿠키의 값이 됨 (톰캣은 JSESSIONID라는 이름의 쿠키)
  • 세션을 통한 상태 유지 메커니즘
    • 코드 상에서 HttpServletRequest의 getSession() 실행 시 WAS에서는 WAS가 발행한 쿠키(JSESSIONID)가 요청할 때 있었는지 확인하고 없다면 새로운 값을 만들어 세선 저장소에 보관
    • 세션 저장소에서는 쿠키의 값마다 고유한 공간을 가지게 되며 이 공간은 다시 키와 값으로 데이터를 보관할 수 있음. 이를 통해 서블릿 / JSP 등은 원하는 객체를 보관할 수 있음
    • 서블릿 API에서는 HttpServletRequest의 getSession() 이라는 메소드로 각 JSESSIONID의 공간에 접근 가능
  • HttpServletRequest의 getSession()
    • 브라우저가 보내는 정보를 이용해 다음과 같은 작업을 수행
    • JSESSIONID가 없는 경우 : 세션 저장소에 새로운 번호로 공간을 만들고 해당 공간에 접근할 수 있는 객체를 반환. 새로운 번호는 브라우저에 JSESSIONID의 값으로 전송 (세션 쿠키)
    • JSESSIONID가 있는 경우 : 세션 저장소에서 JSESSIONID 값을 이용, 할당된 공간을 찾고 이 공간에 접근할 수 있는 객체를 반환
    • 해당 메소드의 결과물 : 세션 저장소 내의 공간이며 이를 의미하는 타입은 HttpSession 타입이라고 부르며, 공간은 세션 컨텍스트(Session Context) 혹은 세션(Session) 이라고 함
    • HttpSession 타입의 객체를 이용하여 현재 사용자만의 공간에 원하는 객체를 저장하거나 수정/삭제할 수 있음
    • isNew() 와 같은 메소드로 새롭게 공간을 만들어낼 것인지, 기존의 공간을 재사용하는지 구분

(3) 로그인 체크 - 세션 이용

  • 세션을 이용하는 로그인 체크는 다음과 같은 시나리오로 구성
    • 사용자가 로그인 성공 시 HttpSession을 이용, 해당 사용자의 공간(세션 컨텍스트)에 특정한 객체를 이름(key)과 함께 저장
    • 로그인 체크가 필요한 컨트롤러에서는 현재 사용자의 공간에 지정된 이름(key)으로 객체가 저장되어 있는지 확인 -> 객체 존재 시 해당 사용자는 로그인된 사용자로 간주, 그렇지 않다면 로그인 페이지로 이동
  • 로그인 체크 기능 추가 (doGet() 수정)
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        log.info("/todo/register GET .......");


        HttpSession session = req.getSession();

        if(session.isNew()) { //기존에 JSESSIONID가 없는 새로운 사용자
            log.info("JSESSIONID 쿠키가 새로 만들어진 사용자");
            resp.sendRedirect("/login");
            return;
        }

        //JSESSIONID는 있지만 해당 세션 컨텍스트에 loginInfo라는 이름으로 저장된
        //객체가 없는 경우
        if(session.getAttribute("loginInfo") == null) {
            log.info("로그인한 정보가 없는 사용자.");
            resp.sendRedirect("/login");
            return;
        }

        //정상적인 경우라면 입력 화면으로
        req.getRequestDispatcher("/WEB-INF/todo/register.jsp").forward(req,resp);

    }
  • 로그인 페이지 JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<c:if test="${param.result == 'error'}">
    <h1>로그인 에러</h1>
</c:if>

<form action="/login" method="post">
    <input type="text" name="mid">
    <input type="text" name="mpw">
    <input type="checkbox" name="auto">
    <button type="submit">LOGIN</button>
</form>
</body>
</html>

(4) 로그인 체크 - 필터 이용

  • 로그인 여부를 체크해야 하는 컨트롤러마다 동일하게 체크하는 로직 작성 시 같은 코드를 계속 작성해야 하는 문제가 있음 -> 대부분 필터(Servlet Filter)를 사용하여 처리
  • 필터 (Servlet Filter)
    • 특정한 서블릿이나 JSP 등에 도달하는 과정에서 필터링하는 역할을 담당하는 서블릿 API의 객체
    • @WebFilter 어노테이션을 이용, 필터가 동작하도록 설계
    • 한 개 이상, 여러 개를 적용할 수 있음
    • Filter 인터페이스를 import한 후 구현
    • 특정 경로를 지정, 해당 경로의 요청에 대해 doFilter()를 실행하는 구조
    • 다음 필터나 목적지로 갈 수 있도록 FilterChain의 doFilter()를 실행
      (문제가 생겨서 진행이 안될 경우 다른 방식으로 리다이렉트 처리도 가능)

(5) 로그아웃 - 세션 이용

  • 로그아웃의 경우 다음과 같이 처리
    • HttpSession을 이용하는 경우 로그인 확인 시에 사용했던 정보를 삭제하는 방식으로 구현하거나
    • 현재의 HttpSession이 더 이상 유효하지 않다고 invalidate() 시키는 방식 사용

(6) 로그인 페이지 관련

  • EL에서 쿼리 스트링 처리
<c:if test="${param.result == 'error'}">
  <h1>로그인 에러</h1>
</c:if>
  • EL의 Scope와 HttpSession 접근하기
    • EL은 특별하게도 HttpServletRequest에 저장(setAttribute())된 객체를 찾을 없을 경우 자동으로 HttpSession에서 저장된 객체를 찾아내는 방식으로 동작 -> Scope (변수의 범위)
    • EL의 Scope는 HttpServletRequest나 HttpSession 등에서 setAttribute()로 된 데이터를 찾을 때 사용
    • Page Scope : JSP에서 EL을 이용해 <c:set>으로 저장한 변수
    • Request Scope : HttpServletRequest에 setAttribute()로 저장한 변수
    • Session Scope : HttpSession을 이용해서 setAttribute()로 저장한 변수
    • Application Scope : ServletContext를 이용해서 setAttribute()로 저장한 변수
    • EL로 {$obj}라고 하면 순차적으로 page -> request -> session -> application 의 순서대로 'obj' 라는 이름으로 저장된 객체를 찾는 방식으로 동작

(1) 쿠키의 생성 / 전송

  • 사용자 정의 쿠키와 세션 쿠키(WAS에서 발행하는 쿠키) 비교
    • 생성 : 개발자가 직접 newCookie()로 생성, 경로 지정 가능 / 자동
    • 전송 : 반드시 HttpServletResponse에 addCookie()를 통해서만 전송
    • 유효기간 : 쿠키 생성 시 초 단위로 지정 가능 / 불가
    • 브라우저의 보관 방식 : 유효기간 없을 시 메모리상에서만, 유효기간 있을 경우 파일이나 기타 방식으로 보관 / 메모리상에서만 보관
  • newCookie()를 통해 직접 쿠키 생성 -> 문자열로 된 이름과 값 필요, 값은 일반적인 문자열로 저장이 불가하여 URLEncoding된 문자열로 저장(한글 저장 불가)
  • 쿠키를 사용하는 경우
    • 쿠키는 서버와 브라우저 사이를 오감 -> 보안에 취약한 단점이 있음
    • 이로 인해 쿠키의 용도는 상당히 제한적일 수 밖에 없음
    • 오랜 시간 보관해야 하는 데이터 -> 항상 서버에 보관
    • 약간의 편의를 제공하기 위한 데이터 -> 쿠키로 보관 (오늘 하루 이 창 열지 않기 등)
    • 모바일에서 시작된 '자동 로그인' 방식 -> 쿠키의 유효기간을 지정해서 파일로 보관 -> 모바일에서 매번 사용자가 로그인하지 않아도 됨

(2) 쿠키와 세션을 같이 활용하기

  • 로그인한 사용자의 정보를 쿠키에 보관, 이를 이용해 사용자의 정보를 HttpSession에 담는 방식
  • 사실 제대로 된 구현은 Spring Boot, Spring Security에서 더 상세하게 다뤄야 함
  • 자동 로그인 구현 방식
    • 사용자가 로그인할 때 임의의 문자열 생성, DB에 보관
    • 쿠키에 생성된 문자열을 값으로 삼고 유효기간 지정
  • 로그인 체크
    • 현재 사용자의 HttpSession에 로그인 정보가 없는 경우에만 쿠키 확인
    • 쿠키의 값과 DB의 값을 비교, 같다면 사용자의 정보를 읽어와서 HttpSession에 사용자 정보 추가

3. 리스너(Listener)

(1) 리스너의 개념과 용도

  • 이벤트(Event)라는 특정 데이터 발생 시 자동으로 실행되는 특징
  • 리스너 이용 시 어떤 정보가 발생했을 때 미리 약속해둔 동작을 수행, 기존의 코드를 변경하지 않고도 추가적인 기능을 수행
  • Spring MVC가 리스너를 통해 동작
  • 서블릿 API는 여러 이벤트에 맞는 리스너들을 인터페이스들로 저장해둠
    • 해당 웹 애플리케이션이 시작되거나 종료될 때 특정 작업 수행
    • HttpSession에 특정한 작업에 대한 감시 및 처리
    • HttpServletRequest에 특정한 작업에 대한 감시와 처리
  • ServletContextEvent와 ServletContext
    • ServletContetextListener 인터페이스의 contextInitialized()와 contextDestroyed()에 파라미터로 ServletContextEvent라는 객체가 전달
    • 해당 객체는 현재 애플리케이션이 실행되는 공간은 ServletContext에 접근 가능
    • ServletContext : 현재의 웹 애플리케이션 내 모든 자원들을 같이 사용하는 공간, 이 공간에 무언가를 저장할 경우 모든 컨트롤러나 JSP 등에서 활용이 가능해짐
  • ServletContextListener와 Spring Framework
    • ServletContextListener와 ServletContext를 이용할 경우 프로젝트가 실행될 때 필요한 객체들을 준비하는 작업을 처리할 수 있음
    • 예시) 커넥션 풀 초기화, Service 객체 미리 생성 등
    • Spring Framework를 웹 프로젝트에서 미리 로딩하는 작업 처리 시 ServletContextListener를 이용
  • 결과
profile
공부했던 내용들을 모아둔 창고입니다.

0개의 댓글