2 분 소요

6.2 세션 요구사항 및 실습

http는 클라이언트와 서버가 연결된 후 상태를 유지할 수 없기에 무상태 프로토콜이라고 부른다.

하지만 웹 어플리케이션은 로그인과 같이 상태를 유지할 필요가 있는 요구 사항이 발생한다.

이와 같은 경우에 사용할 수 있는 방법이 쿠키 헤더를 사용하는 방법이다.

쿠키 헤더

일단 set-cookie 헤더를 통해 쿠키를 생성하면 이후 발생하는 모든 요청에 set cookie 로 추가한 값을 Cookie 헤더로 전달하는 방식이다.

보안에 취약하다는 단점이 있다.

세션

서버에 상태값을 저장한 후 클라이언트에 고유한 아이디를 발급해 이 아이디를 set cookie 에 전달하는 방식

세션이 상태를 웹서버에서 관리하는 점만 다르고 http 상태값을 유지하기 위한 값을 전달할 때는 쿠키를 사용

세션은 http 쿠키를 기반으로 동작

6.2.1. 요구사항 구현

실습은 5장에서 구현한 http 웹 서버에서 시작할 수 있다.

혹은 https://github.com/slipp/web-application-server 저장소의

was-step3-controller-refactoring 브런치에서 시작할 수 있다.

구현 메서드

  • String getId()

    : 현재 세션에 할당되어있는 고유한 세션 아이디를 반환

  • void setAttribute(String name, Object value)

    : 현재 세션에 value 인자로 전달되는 객체를 name 인자 이름으로 저장

  • Object getAttribute(String name)

    : 현재 세션에 name 인자로 저장되어있는 객체 값을 찾아 반환

  • void removeAttribute(String name)

    : 현재 세션에 name 인자로 저장되어있는 객체 값을 삭제

  • void invalidate(): 현재 세션에 저장되어있는 모든 값을 삭제

요구사항 분리 및 힌트

  • 예측할 수 어려운 고유 아이디 생성
    • 힌트

        UUID uuid = UUID.randomUUID();
      
  • 생성한 고유한 아이디를 쿠키를 통해 전달
    • 힌트

        쿠키는 set cookie 헤더를 통해 전달되며 name1=value1, name2=value2 형태로 전달된다,.
        세션 아이디를 전달하는 이름으로 JSESSIONID를 사용한다.
      
  • 서버 측에서 모든 클라이언트의 세션 값을 관리하는 저장소 클래스를 추가한다.
    • 힌트

        HttpSessions 와 같은 이름을 가지는 클래스를 추가한다.
        이 클래스는 Map<String HttpSession>와 같은 저장소를 통해 모든 클라이언트별 세션을
        관리해야한다. 이 저장소의 키는 앞에서 UUID로 생성한 고유한 아이디이다.
      
  • 클라이언트 별 세션 데이터를 관리할 수 있는 클래스를 추가한다.
    • 힌트

        HttpSession 클래스는 요구사항에 있는 5개의 메서드를 구현해야하며, 
        상태 데이터를 저장할 Map<String, Object>가 필요하다.
      

6.3 세션 구현

6.3.1 고유한 아이디 생성

세션에서 사용할 고유한 아이디를 생성한다.

public class UUIDTest{
	@Test
	public void uuid(){
		System.out.printLn(UUID.randomUUID());
	}
}

테스트 실행 시 UUID 의 형태로 임의의 값이 생성된다.

public class RequestHandler extends Thread {
...
	public void run(){
		...
		if(getSessionId(request.getHeader("Cookie")) == null){
			response.addHeader("Set-Cookie", "JSESSION=" + UUID.randomUUID());
		...	
	}

	private String getSessionId(String cookieValue){
		Map<String, String> cookies = 
			HttpRequestUtils.parseCookies(CookieValue);
		return cookies.get("JSESSIONID");
	}
}

구현을 완료한 후 리팩토링할 부분을 찾아본다. ListUserController 코드에서 로그인 여부를 확인하기 위해 쿠키 헤더 값을 활용하는 부분이 점점 증가하고 있다.

쿠키 헤더값을 관련하는 HttpCookie 를 추가하는 것이 좋겠다.

public class HttpCookie {
	private Map<String, String> cookies;
	HttpCookie(String cookieValue){
		cookies = HttpRequestUtils.parseCookies(cookieValue);
	}
	public String getCookie(String name){
		return cookies.get(name);
	}

위와 같이 HttpCookie 를 추가한 후 HttpsRequest 에서 HttpsCookie에 접근할 수 있는 메소드를 추가한다.

public class HttpRequest{
	...
	public HttpCookie getCookies(){
		return new HttpsCookie(getHeader("Cookie"));
	}
}