반이중을 stateful로… 쿠키(Cookie)
반이중을 stateful로 만들 수 있으나... 그렇게 되면 클라이언트의 request를 모두 다 저장하게 되는 것이기에 용량과 과부화 문제가 발생할 수 있다. 원래 만들던대로 stateless로 만들면 용량과 과부화 문제는 해결되나 클라이언트의 상태 정보를 유지하지 않기 때문에, 클라이언트의 이전 상태를 참조하거나 변경할 수 없는 제약이 있다. 이걸 해결하기 위해 나온게 바로 '쿠키(Cookie)' 쿠키를 사용하면 클라이언트의 이전 상태를 참조하거나 변경할 수 있다!
서버가 Stateless
서버는 헬스장. 엄청나게 큰 공터에 헬스장이 2개가 있다고 가정하자. 클라이언트가 헬스장(서버)에 갈 때 수건, 샴푸, 바다워시, 운동복 등등을 다 들고 간다. 이 물건들을 '바디데이터' 라고 정의하며, '헤더'에 [세면도구(=MIME타입]라고 적어서 간다. a헬스장에 도착. 물건들을 왕창 들고 가니까, 다루기가 힘들 것 같아 헬스장에서 가방(리퀘스트 객체)을 준다. 그 가방(리퀘스트 객체)에다가 내가 들고 온 도구들(바디데이터)을 다 담는다. 그럼 헬스장은 그 가방만 들고 다니면 되겠지? 그런데 가지고 싶은 헬스 도구(데이터)를 발견했다! 그래서 리퀘스트 가방에 막 담겠지? 그럼 리퀘스트 가방이 더 두꺼워지지? 응답을 하잖아?(집에 가잖아) 리스폰스가 되잖아? 그럼 가방을 버리고 집에 간다. 근데 가방에 있는 내용물만 다 버리고 감. 이런 가방을 한 20~30개 들고 있음. 상태 저장 안하죠? stateless
매 요청마다 클라이언트가 필요한 데이터를 요청하고, 서버는 해당 요청을 처리한 후 응답을 보내고 클라이언트와의 연결을 끊는다. 이후에 다시 같은 클라이언트가 요청을 보낼 때는 이전 요청과는 상관없이 새로운 요청으로 처리된다.
장점 : 서버의 과부화나 메모리 사용량 감소
단점 : 클라이언트의 요청에 따라 ‘상태를 유지’ 해야 하는 경우가 있는데… 그걸 못함!
서버가 Stateful
마음에 드는 헬스 도구(데이터)를 봐서 헬스 가방에 담았다. 그런데 가방의 내용물을 버리지 않고 어디다가 보관한 후 내일도 찾고 싶다. 그래서 락카(세션) 등장! 내 가방에 있는 신발을 꺼내서 락카에 보관했다. 이게 바로 세션 락카에는 번호가 많기 때문에 내가 이용한 락카를 알기 위해서 1번 락카 key를 받는다. 이 key를 들고 집에 간다. (서버가 클라이언트에게 응답을 함) 내일 다시 헬스장에 와서 가방(리퀘스트 객체)을 받고, 어제 받았던 key를 사용해 락카를 열어 신발을 꺼낼 수 있다. 이 락카(세션 저장소)는 계속 유지되고 있죠? stateful
[ 다른 예를 들어보자 ] 클라이언트가 로그인을 한 후에 상품을 선택하고 결제를 진행하는 경우라고 가정하자. 클라이언트가 로그인을 하면 서버는 해당 클라이언트에 대한 세션을 생성하고 세션 ID를 클라이언트에게 전달한다. 그리고 클라이언트가 상품 선택과 결제를 진행하는 동안에는 클라이언트의 세션 ID를 통해 서버는 클라이언트의 상태를 유지한다. (계속 똑같은 애가 요청 보내는거다~ 하고?) 이후 클라이언트가 추가적인 요청을 보낼 때마다 서버는 세션 ID를 확인하여 해당 클라이언트의 상태를 파악하고, 이에 맞는 응답을 제공한다 즉, 서버는 클라이언트의 상태를 유지하며 클라이언트가 여러 번의 요청을 보낼 때 이전 상태에 따라 다른 응답을 제공할 수 있다. 스테이트풀한 방식에서는 클라이언트를 식별하기 위해 세션 ID(락카 key)를 사용 (세션 ID = 클라이언트와 서버 간의 연결을 유지하고, 클라이언트가 다시 요청을 보낼 때 이전 상태를 유지하기 위해 사용)
클라이언트의 상태를 유지하기 때문에, 서버에 상태 정보를 저장하고 관리할 수 있다
스프링은 모두 포워드로 작동 ✓
클라이언트가 데이터를 요청할 때, 리퀘스트 객체에 바디 데이터를 담아서 준다. 그럼 서버는 클라이언트의 요청에 따라 DB에 조회된 데이터를 리스폰스 객체에 담아서 보낸다. 그런데 요청을 2번 해야하는 경우가 있다. (ex, join(컨트롤러)-main(뷰) 처럼. 302...) 즉, 리스폰스 객체를 사용할때, 컨트롤러에서 뷰로 '포워드'하는 경우가 있다. (내부요청 302) 이때, 리스폰스 객체에 담긴 데이터를 유지하려면 포워드를 사용하여 이동해야겠지. (sendDirect는 외부 요청, stateless니까 갔다오면 데이터가 사라지고 없다) 로그인 창에서 로그인을 하면 로그인 된 정보를 가지고 main 페이지로 302 하잖아 이걸 stateless로 짜버리면 main페이지로 302 했을 때, 로그인 정보가 끊어져버림. 그걸 방지하기 위해서 stateful로 만들어 고객의 상태를 유지하는 걸 세션이라고 함 이걸 내부 요청(포워드)로 한다는 말인 듯.
포워드 (내부요청) : 웹 애플리케이션에서 클라이언트의 요청을 다른 리소스(서블릿, JSP, HTML 등)로 전달하는 기능
세션, 세션 키 (세션이 있다 = stateful 이다) ✓
세션은 서버의 메모리나 데이터베이스와 같은 저장소에 저장되며, 이를 세션 저장소라고 한다. (락카가 바로 세션 저장소) 클라이언트의 정보는 세션 키 또는 세션 ID로 식별 (HashCode로 사용)되며, 이는 서버 측에서 세션 저장소에서 해당 세션을 찾아 클라이언트의 정보를 읽고 쓰는 용도로 사용된다. 클라이언트의 정보는 최소한의 필요한 정보만 세션에 저장한 후, 이 세션에 접근할 수 있는 카드 (세션 키 or 세션 ID)만 주는 것! 그러나 세션 키가 데이터는 아니기 때문에 서버는 response를 줄 때, Set-Cookie 헤더에 세션 ID(세션 키 값)를 담아서 전달하고, 브라우저는 헤더를 보고 이 key를 쿠키라는 세션 저장소에 담는다. 그 다음에 요청할 때, 브라우저가 이 key를 Reqeust 헤더에 암아서 서버에 보내면 서버는 헤더를 보고, 쿠키가 있다는 걸 알아챈 후, 키를 꺼내서 서랍의 키랑 비교해보고 '아까 왔던 애구나' 하고 알 수 있다. 기억을 한다는 것! Stateful! 헤더에 쿠키가 없으면 서버는 처음 온 애로 인식하는 것. 클라이언트는 헤더를 보고, Set-Cookie가 있네? 하면서 브라우저가 프로토콜을 알기 때문에 이 세션 ID를 이용하여 자기가 알아서 서버의 세션 저장소(쿠키)에 접근하여 정보를 사용한다.
* 응답(response)하는 키 값 Set-Cookie (서버 > 클라이언트)
* 요청(request)하는 키 값 Cookie (클라이언트 > 서버)
* 세션 저장소는 서버와 클라이언트 모두 가지고 있다
세션은 클라이언트의 상태를 유지하고 저장하기 위한 서버 측의 저장소
DB에서는 프라이머리 키로 클라이언트를 식별한다
그림으로 보는 세션과 쿠키 설명
근데 앱은 .. 브라우저가 해주는 프로토콜도 다 직접 만들어야함. 브라우저는 프로토콜이 있기 때문에 알아서 저장하고 알아서 키를 들고 가는데 앱은 내가 다 해줘야함...
IoC는 싱글톤이라 하나의 타입만 저장한다.
(SET타입이라 동일한 타입을 넣어도 하나만 저장됨)
HTTPServletRequest 객체는 IoC에 저장 못 한다.
SET은 중복값을 저장하지 않으니까, Request가 100개가 와도 1개만 저장 되기 때문!
대신 세션은! IoC에 저장 가능!!
세션은 클라이언트와 서버 간의 상태를 유지하기 위한 객체로,
사용자의 세션 정보를 담고 있다.
세션은 요청마다 새로운 객체가 생성되는 것이 아니라, 클라이언트의 세션 ID에 해당하는 세션 객체를 찾아 사용하게 되기에 세션은 IoC 컨테이너에 저장되어 다른 객체에서 참조하고 활용할 수 있다.
Share article