[ KOSTA 28일차 교육 ] 서블릿 - 세션, 토큰 | HttpSession class | Tomcat의 두가지 설정 파일 : web.xml vs server.xml | 한글 Encoding 변경 | 세션 + JSP 실습 | setAttribute | pagescope | 동일 서블릿 & 동일 메서드에 대한 구분 : pagecode | JSP에서 서블릿 호출 시 a href을 통해 Query String 직접 사용할 수 있음.

junjun·2024년 5월 23일

KOSTA

목록 보기
25/48

[ 전날 Summary ]

1) Login / Signup JSP + Servlet 로직 개발 완료

2) <form> 태그는 한 HTML 안에 여러 개가 올 수 있음.
하지만, <form> 태그 안에 <form> 태그가 하나 더 있는건 동작하지 않음.
(화면 상에 문제가 없음)

3) 서블릿 내에서 여러 개의 요청 매핑을 가지기 위해
input type="hidden"pageCode와 같은 값으로 요청을 받아 페이지를 구분했지만,
Spring에서는 이를 개선하여 메소드마다 주소와 요청을 받을 수 있도록 개선함.

4) JDBC의 PreparedStatementexecuteQuery()ResultSet을 반환하고, executeUpdate()는 int값으로 메소드로 인해 영향받은 Row의 개수를 반환함.

5) Front-End의 <form> 태그나 API Request로 받은 Request Parameter의 이름을 내가 원하는 이름의 파라미터로 바인딩할 수 있다. ( String myParam = request.getParameter("요청 스펙에서의 param 이름");


세션, 쿠키

  • 세션과 쿠키는 왜 필요한가?
    - HTTP Protocol무상태성(Stateless)의 특징을 가지기 때문
    • 요청-응답의 단순성을 가지고, 확장이 용이하지만, 클라이언트의 요청 정보를 기억해야하는 경우가 있다.
    • 내 머리속의 지우개 / 남자애가 집에 결혼 액자도 걸어놓고.. 심장에 이름표도 박아둠 / 클라이언트의 정보를 담고있는 역할을 한다. ( 그 친구가 누구인지 기억하기 위해! )

HttpSession Class

  • Java Servlet 단에서 제공하는 Session

  • javax.servlet.http.HttpSession

String getId()

  • 세션의 ID를 리턴한다.

String[] getValueNames()

  • 설정된 세션 객체들의 이름을 리턴한다.

Object getAttribute(String name)

  • 세션 객체에 저장된 속성 리턴
  • Object 타입으로 리턴받기에, 이를 캐스팅해주어야한다.

void setAttribute(String name, Object value)

  • 세션 객체에 속성 저장
  • page scope = "session" 이면, 유저가 로그인한 시간동안 이렇게 담아놓은 데이터가 유지됩니다.

`void removeAttribute(String name)

  • 세션 객체에 저장된 특정 속성 제거 ( Key값 전달해줌 )
  • 마치 map과 비슷하게 동작합니다.

void invalidate()

  • 현재 모든 세션 속성을 제거합니다.
  • 로그아웃 처리 시, 해당 브라우저에 대한 세션 로그 파일의 내용을 전부 삭제합니다.

void setMaxInactiveInterval(int interval)

  • 세션 유지 시간을 설정합니다. ( 단위 : 초 )
  • interval을 0으로 설정함으로써 invalidate()와 동일한 효과를 낼 수 있습니다.

int getMaxInactiveInterval()

  • 세션 유지 유효 시간 리턴

long getCreationTime()

  • 세션이 생성된 시간 리턴 (단위 : ms)

long getLastAccessedTime()

  • 마지막 요청 시간 리턴

JSP 에서는 내장객체로써 session을 가지고 있습니다. 이는 추후 Tomcat에 의해 Servlet으로 변환될 때 HttpSession 타입의 인스턴스로 변환됩니다.

로그아웃 시, 유저 브라우저에 할당된 세션을 다 지워야합니다.

세션 지우는 방법 ( 로그아웃 시 )

  • void removeAttribute(String name)
    - 세션 객체가 담고있는 속성들중 지정한 속성에 해당하는 Key-Value값을 제거합니다.
    • 세션은 결국 브라우저 아이디에 대한 파일이기에, 파일에서의 내용 삭제가 이루어집니다.
  • invalidate()
    - 세션 객체 자체에 저장된 모든 속성을 전부 제거합니다.
  • setMaxInactiveInterval(0)
    - 세션 유지 시간을 0초로 설정합니다. ( = 세션이 지워지는 효과를 냅니다. )

Servlet API를 이용한 세션 설정

1) Session 객체 생성

  • HttpSession session = request.getSession();

2) Session에 Client 정보 저장

  • session.setAttribute(String key, Object value);

3) Session 정보 얻어오기

  • Object obj = session.getAttribute(String key);
  • 실제로 사용하기 위해 다운캐스팅 필요

4) Session 종료하기

  • session.invalidate();
  • session.removeAttribute(String key); // 해당 session 종료

5) Session 시간 설정

  • session.setMaxInactiveInterval(초);
  • 만약, 세션 유지 시간을 변경해야하는 요구사항이 추가되면?
    - 이를 위해 설정 파일이 존재
    • Tomcatweb.xml or 특정 어플리케이션의 web.xml
<session-config>
  <session-timeout></session-timeout>
</session-config>

Tomcat의 설정파일 : [web.xml]

  • Tomcat의 web 관련 설정이 모두 들어있다.
  • Tomcat이 구동을 시작할 때, 처음 이 파일(설정)을 보고 프로그램을 시작합니다.
  • 누가 이걸 설정하나?
    - 인프라 아키텍트
    • 개발자는 10%정도?
    • 하지만 무슨 설정이 있고 어떻게 동작하는지는 알아야한다 생각한다. 추후 스프링의applciation.properties 등의 설정과도 연관이 있다.

web.xml 예시 1 서블릿 등록, 초기화

  • <servlet> : 서블릿 등록
  • <servlet-class> : 서블릿이 존재하는 패키지포함 클래스 이름
  • <init-param> : 서블릿이 초기화(init())될 때 전달할 파라미터
    - 위의 그림에서는 debug 모드는 켜지말라는 뜻(0)
  • <load-on-startup> : 톰캣 구동 시, 등록된 서블릿 중 구동되는 순서

  • JspServlet 이다.
  • 톰캣 구동 시 3번째로 구동된다.

web.xml 예시 2 : default 서블릿과 서블릿 매핑


  • <servlet-name> 을 보면 이름이 default 인 서블릿이 등록된다는 것을 알 수 있다.

  • <servlet-mapping>을 보면, / , 즉 모든 url에 대해 매핑된다는 것을 알 수 있다.

  • 톰캣은 전달받은 요청에 대해 URL을 기준으로 실행시킨다.
    톰캣에서 URL-Pattern을 이렇게 등록해놓으면, /로 오는 서블릿을 실행시킨다.

  • 톰캣은, <servlet-mapping>으로 url pattern에 매핑된 Servlet을 확인하고, <servlet-name>을 통해 서블릿을 init() 한 이후 service()를 호출해준다.

Tomcat의 설정 파일 : server.xml

  • 톰켓 서버 자체의 설정
  • Connection을 어떻게 관리할지, Port 번호를 뭐 쓸지, 프로토콜을 어떤 것을 사용할지 등등을 지정한다.
<!-- Tomcat server.xml 설정 중 일부 -->
<Connector connectionTimeout="20000" 
maxParameterCount="1000" port="8081" 
URIEncoding="UTF-8" protocol="HTTP/1.1" redirectPort="8443"/>
  • 최대 전달받을 수 있는 파라미터 개수, 서버가 바인딩받는 포트번호, 인코딩 방식, 프로토콜 등을 정의한다.

Tomcat의 설정 파일 : server.xml - UserDatabase

  • UserDatabase
    - 톰켓에서 제공하는 일종의 데이터소스이다.
    • DAO에서 DB연결을 위해 JDBC의 DataSource를 썼지만
    • Tomcat(WAS)에서도 DB 소스를 꺼내쓸 수 있다
      - Tomcat 자체에서도 데이터베이스를 제공한다 생각하면 된다.

Tomcat의 최상위 Context-Path

  • webapps

우리 프로젝트의 최상위 path : web_prj ( 톰켓 위에서 동작하는 어플리케이션의 이름의 예시로, 현재 web_prj라는 패키지 명으로 되어있기에 이렇게 적었습니다. )
  • 프로젝트 4개를 등록했다면?
    - <Context docBase =”web_prj” …> <Context docBase = “my_prj” … > <Context docBase = “user_prj” … >
    이와 같이 추가해서 적어주면 됩니다.

  • 즉, 하나의 톰캣 위에서 여러 개의 어플리케이션을 띄울 수 있습니다.
    ( 스프링 부트에서는 내장 Tomcat으로 하나의 어플리케이션에 하나의 WAS가 붙습니다. )

  • 톰캣은 실행 시작 시, web.xml / server.xml 을 비롯한 모든 xml 설정 파일을 읽어서
    이렇게 설정된 내용을 기반으로 동작합니다.

  • webapp - WEB/INF 는 우리 프로젝트 자체의 설정!

    • 우리 프로젝트를 위한 web.xml이 또 있음!
    • **Tomcat****web.xml** & 우리 프로젝트를 위한 **web.xml**
      • **Tomcat****web.xml** 에서 세션 최대 시간을 50초로 해놓으면
        그 안의 프로젝트들은 공통적으로 세션 지속 시간이 50초
      • 근데 그 중 일부 프로젝트 (**web_prj**) 에서 세션 지속 시간을 5분으로 하면
        우리 프로젝트만! 세션 지속 시간이 5분으로 설정된다.
      • 각 프로젝트 입장에서는 내가 설정한 내용만, 내 설정 내용이 우선시된다!
      • **Tomcat****web.xml**은 왜 하는건가?
        • 일괄로 공통적으로 설정하고싶은 것은 Tomcat**web.xml** 을 참조하고
        • 우리 프로젝트에만 적용될 이야기는 **web.xml**을 참조해야한다.\
  • **web.xml**을 통해 세션 지속 시간 / 한글 처리 등의 설정을 지정해줄 수 있다.

즉 정리하자면, web.xml 은 톰캣에 있고 처음 시작 할 때 구동됩니다. 또한, web.xml은 톰켓이 관리하는 각 프로젝트에도 있는데, 프로젝트의 설정은 이 web.xml을 통해 할 수 있습니다.
  • 어플리케이션이 처음 구동되면 Tomcat의 web.xml이 시작됩니다.
    • 톰캣 안에 여러개의 프로젝트에 공통 설정으로서 적용된.
  • 그 이후, 각각 프로젝트마다의 web.xml 의 설정에 맞게 재빌드됩니다.
    • 프로젝트 입장에선 본인에게 있는 설정(web.xml)이 최우선이다.
    • 여러 프로젝트에 공통 설정
      • DB는 한 곳에 쓰고있다면 ( 공통부 )
      • 하나는 일본어 하나는 한국어
        • 일본어 인코딩, 한국어 인코딩 ⇒ 이런 설정은 각자 프로젝트에 하자!

우리 실습 프로젝트 ( web_prj )의 web.xml 구경

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>web_prj</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    <welcome-file>default.htm</welcome-file>
  </welcome-file-list>
</web-app>
  • 맨 처음으로 만약, URL 매핑을 주지 않고 사용자가 그냥 도메인으로만 접근했다면
    index.html을 찾습니다.. 찾다가 없으면 index.jsp 찾습니다

한글 설정 방식 4가지

  • 한글 설정

    • POST로 보낼 때와 GET으로 보낼 때 차이가 있다.
    • POST로 보낼 때 한글 깨짐 → 프로그래밍 시 인코딩 방식 설정
      • 응답 헤더의 contentType 설정

        • <%@page contentType=”text/html;charset=euc-kr” pageEncoding=”euc-kr” %>
      • 매 서블릿마다 아래의 코드를 추가해준다.

        request.setCharacterEncoding("euc-kr");
        response.setContentType("text/html;charset=euc-kr");
      • 이걸 모든 서블릿에 해주는 것이 불편하니
        설정 파일을 건든다. ( **web.xml** )

    URL vs URI

  • URL : 주소 ( 프로토콜 포함 )

  • URI : 모든 접근가능한 자원의 식별자

세션(Session) 정의

!!! 세션은 파일이다 !!!

  • HTTP 한계를 극복할 수 있도록 ( 필요한 클라이언트 정보 )
    웹 사이트의 방문기록을 서버에 저장, 사용자와 웹사이트 사이를 매개해주는 정보
  • 브라우저의 요청이 오면, 브라우저 아이디를 통해 세션 로그 파일 작성 ( 브라우저 아이디가 File의 제목이 됨 → 해당 File에다가 사용자 정보를 set 해줌 → 이것은 File에 저장한다. )
  • 쿠키보다 보안에 유리하다
    • 세션의 파일을 열어보려면, 서버를 뚫어야하는데.
      서버를 뚫을 정도면 그보다 더한 것을 할 수도 있다.
  • Tomcat의 유효시간 1800초 ( 30분 ) 가 Default
    - 저장 유효시간은 설정파일 ( web.xml or server.xml 을 통해 바꿀 수 있다 )

세션 설정 & 꺼내서 쓰는 코드 부분

UserServlet.java

HttpSession session = request.getSession();
session.setAttribute("KEY_SESS_USERID", uuvo.getUserId());
session.setAttribute("KEY_SESS_UNAME", uuvo.getUname());
session.setAttribute("KEY_SESS_GRADE", uuvo.getGrade());


// 서블릿에서 session 꺼내서 표시할 때.
// JSP에서 하는게 낫다! 왜냐하면 JSP는 내장객체로 session이 있기 때문이다.
<%
String uid = (String) session.getAttribute("KEY_SESS_USERID");
String unam = (String) session.getAttribute("KEY_SESS_UNAME");
String grade = (String) session.getAttribute("KEY_SESS_GRADE");
%>
<%= uid %>
<%= out.println(unam + "님 환영합니다 "); %>

// 근데 만약 로직이 들어가야한다면, <%= %> 를 쓰는 것이 아니다.
// <%= %> 은 out.println의 약자로 동작하기 때문이다!

index.jsp

<div class="container-fluid px-4">
<h1 class="mt-4">Dashboard</h1>

<%= session.getAttribute("KEY_SESS_USERID")%>님 환영합니다.

<!-- 세션 보이기 -->
<%
	String userId = (String) session.getAttribute("KEY_SESS_USERID");
	String uname = (String) session.getAttribute("KEY_SESS_UNAME");
	String grade = (String) session.getAttribute("KEY_SESS_GRADE");
	
	if(grade.equals("u")){
		out.println("사용자접속");
	} else if(grade.equals("a")){
		out.println("관리자 접속");
	} else {
		response.sendRedirect("500.html");
	}
%>

로그아웃 시 주의해야할 점

클라이언트가 서버에게 세션을 지워달라 요청해도,
서버가 지우는 것이기에 즉각적으로(Right Now) 지우지 못한다.
(서버가 처리해야할 시간이 필요하다.)
가장 확실한 방법은 브라우저를 껐다가 키는 것.
브라우저가 종료되니 브라우저 세션 키가 바뀜!!
그래서 서버는 기존의 브라우저의 키에 해당하는 세션로그파일을 설정된 시간만큼 관리하다 폐기함.

JSP - Servlet - DAO 구조

  • DAO와 통신하는 주체는 Servlet
  • JSP에서 DAO를 바로 호출하면 절대 X
  • JSP에서 DB의 데이터가 필요하면?
    • Serlvet을 호출해야한다.

문제점 : 로그인, 로그아웃 페이지가 세션의 유무에 따라 나타내게 하는 것은 index에만 적용되어있고, 다른 페이지들은 적용되어있지 않다.

근데 만약 이런 JSP가 30개면??
공통적으로 다 불러들이는 문법인 include 를 통해 이 문제를 해결할 수 있다.

JSP와 HTML을 혼합해서 사용하기 ( 로그아웃을 세션, JSP로 구현하기 )

<%
	String grade = (String) session.getAttribute("KEY_SESS_GRADE");
%>
          
<%
	if(grade != null) {
%>
	<a class="nav-link" href="<%=request.getContextPath()%>/user_servlet">Logout</a>
<% } else { %>
	<a class="nav-link" href="<%=request.getContextPath()%>login.jsp">Login</a>
<% } %>
  • grade 관련 session이 있다면,
    로그인이 된 것으로 판단해서 로그아웃 창이 뜬다.
  • session이 없다면,
    로그아웃 된 것으로 판단해서 로그인 창이 뜬다.

Servlet -> JSP로 데이터 전송 방식

  • 글자 전송 ( Query String )
  • 객체 전송 ( request.setAttribute / RequestDispatcher / forward 사용 )
		/**
		 * response.sendRedirect("xx.jsp"); : 글자 전송에 사용
		 * response.sendRedirect("tables.jsp?UID=kim"); : Query String 전달 가능
		 */

		// 객체 전송에 사용
		request.setAttribute("bvos", bvos);
		RequestDispatcher rd = request.getRequestDispatcher("xx.jsp");
		rd.forward(request, response);

		// 세션에 담으면 어떤 일이 일어날까?
		// session.setAttribute("bvos", bvos);
		// 사용자 로그인 이후 세션 삭제될 때까지 board같은 경우
		// 기존의 값이 유지됨. ( 새로운 Board가 추가되어도 반영되지 않음 )
  • RequestDispatcher를 통해 request / response 안의 값에 데이터를 추가해서 보내줄 수 있다.
  • JSP에게 Forward 해준다.

request.setAttribute와 session.setAttribute의 차이점 [ Trouble-Shooting ]

  • request.setAttribute는 해당 요청에 대해서만 유효, 끝

  • session.setAttribute는, 로그아웃 할 때까지 해당 데이터가 고정됨

    • 그렇게 써서 좋은 정보 ( 1분 1초 바뀌는게 아닌 **유저 등급** 같은 경우는 session으로 담는다.
    • 실시간으로 변하는 정보같은 경우에는 **request.setAttribute**에 담는다.
  • session이 request 보다 데이터가 유지되는 범위가 크다.

  • **application.setAttribute()** 라는 것도 있다.

    • application은 유지되는 범위가 session 보다 크다.
    • application도 내장객체 ( = Server Config )
    • request : JSP - Servlet 한번
    • session : JSP - Servlet 이후 오만데에서 사용
    • application : Tomcat Server를 껐다 킬때까지.
  • page.setAttribute는?

    • JSP 안에서만 쓰는 것

page scope

page ( JSP 안에서 ) → requet (JSP + Servlet) → session (로그인, 로그아웃 하기 전 모든 페이지?) → application (서버에 연결된 모든 페이지)

  • 가져온 값을 어느 범위까지 공유해서 사용할 것인가
  • 담는 방법이 다 똑같다.
    • **setAttribute**를 통해 담음.
      • request냐, session이냐.. 이런 것들이 다 다르다.

JSTL 없이 for문으로 객체 컬렉션 JSP에서 반복하기

<table id="datatablesSimple">
      <thead>
          <tr>
              <th>글번호</th>
              <th>제목</th>
              <th>글쓴이</th>
              <th>작성일</th>
          </tr>
      </thead>
      <tfoot>
      
          <tr>
              <th>글번호</th>
              <th>제목</th>
              <th>글쓴이</th>
              <th>작성일</th>
          </tr>
      </tfoot>
      <tbody>
      <%
  if(request.getAttribute("bvos") != null){
  	ArrayList<BoardVO> list = (ArrayList<BoardVO>) request.getAttribute("bvos");
  	out.println("총 : " + list.size());
  	for(BoardVO bvo : list){
  %>
          <tr>
              <td><%=bvo.getSeq()%></td>
              <td><%=bvo.getTitle()%></td>
              <td><%=bvo.getRegId()%></td>
              <td><%=bvo.getRegDate()%></td>
          </tr>
        <% }} %>
      </tbody>
  </table>

페이징(Paging)의 필요성

만약 DB에 데이터가 12억개가 있고 해당 데이터를 다 가져온다면?

  • 로딩바 돌다가 터진다.
  • 1000개의 데이터만 가져옴.
    • 해당 페이지 분량에 해당하는 만큼만 가져온다.
    • 페이지 누를 때마다 서블릿을 호출한다면.. 페이지 로딩을 새로해야해서 페이지가 번쩍거림.
    • REST 방식 ( 페이지가 번쩍거리지 않게.. )
      • 커서 기반 페이지네이션
      • 페이지 하나 바뀔때마다..?
      • 페이징 해야한다!

만약, 유저가 어떤 페이지를 거쳐 들어오는 상황에서는 JSP에서 참조할 attribute가 추가되어오고, 처음 해당 페이지 URL을 쳐서 들어오거나 다른 경로로 들어올 때 attribute가 없다면, JSP에서 attribute가 null인지 체크해주어야 한다.

<% if(session.getAttribute("KEY_SESS_USERID") != null) {
     out.println(session.getAttribute("KEY_SESS_USERID") + "님 환영합니다.");
}
%>

<!-- tables.jsp -->         
<!-- 세션 보이기 -->
<%
      if(grade != null){
	      if(grade.equals("u")){
	           out.println("사용자접속");
	      } else if(grade.equals("a")){
	           out.println("관리자 접속");
	      } else {
	          response.sendRedirect("500.html");
	     }
     }
%>
                        
 <!-- tables_detail.jsp 내용 -->
 
 <%
BoardVO bvo = null;
if(request.getAttribute("bvo") != null){
	bvo = (BoardVO) request.getAttribute("bvo");
}
%>

JSP의 <a href> 태그에서 직접 ? 를 통해 Query String을 넣어줄 수 있다.

<%
if(request.getAttribute("bvos") != null){
	ArrayList<BoardVO> list = (ArrayList<BoardVO>) request.getAttribute("bvos");
    out.println("총 : " + list.size());
    for(BoardVO bvo : list){
%>
<tr>
   <td><%=bvo.getSeq()%></td>
   <td><a href="<%=request.getContextPath()%>/board_servlet?seq=<%=bvo.getSeq()%>&pagecode=B002"><%=bvo.getTitle()%></a></td>
   <td><%=bvo.getRegId()%></td>0
   <td><%=bvo.getRegDate()%></td>
</tr>
<% }} %>

동일한 서블릿 매핑을 동일한 HTTP Method로 사용하는 경우 Query String으로 pagecode를 넘겨주어 구분할 수 있다.

<td>
  <a href="<%=request.getContextPath()%>/board_servlet?seq=<%=bvo.getSeq()%>&pagecode=B002"><%=bvo.getTitle()%></a>
</td>

0개의 댓글