서버가 어떻게 클라이언트를 식별하는지에 대해 작성한 글입니다.
우리가 익히 알고 있던 쿠키, 세션, 토큰은 결국엔 HTTP의 특성으로부터 파생되어 생겨난 것이란 걸 알게 되었습니다. HTTP의 어떤 특성이 쿠키를 만들어냈는지, 세션과 토큰은 어떤 이유로 생겨났는지를 중심으로 글을 작성했습니다.
틀린 부분이 있다면 댓글로 알려주시면 감사하겠습니다!
클라이언트는 서버에게 Request를 보내고, 서버는 클라이언트에게 Response를 보낸다.
각기 다른 클라이언트들이 하나의 서버에 요청을 보낼 수 있는데, 모두 다른 방식으로 요청을 보내면 서버가 일일이 대응할 수가 없다. 따라서 HTTP라는 통신규약을 지정하여 모든 요청과 응답은 HTTP 양식에 맞게 보내야 한다.
HTTP 통신을 하기 위해서는 클라이언트와 서버가 연결이 되어있어야 한다. 이 커넥션을 유지하는 것 자체도 부담이고, 클라이언트가 늘어나서 커넥션의 개수가 많아지면 서버에 많은 부담이 된다.
따라서 HTTP는 통신을 한번 할 때 클라이언트와 서버를 연결하고, 통신이 끝나면 바로 연결을 끊는다. 이를 통해 서버 자원을 효율적으로 관리하고 여러 클라이언트의 요청에 대응한다.
한 클라이언트가 여러번의 요청을 보내도 커넥션은 계속 끊어졌다, 연결됐다를 반복한다. 이러한 HTTP의 특성을 Connectionless라고 한다.
(HTTP 1.1부터 일정 시간동안 계속 요청을 보내면 한번의 커넥션 연결로 처리하며 이를 지속 커넥션이라 한다.)
장점
단점
HTTP는 이전 요청의 상태를 기록하지 않기 때문에 서버는 클라이언트를 식별할 수 없다.
커넥션이 계속 연결되어 있으면 요청을 보낸 클라이언트가 누구인지 지속적으로 파악할 수 있지만, 커넥션은 끊어지기 때문에 다음번 요청이 이전에 요청을 보낸 클라이언트인지 새로 요청을 보낸 클라이언트인지 알 수 없다.
또한 "서버는 클라이언트의 상태를 보존하지 않는다." 즉, 이전 요청이 어떻게 끝났느냐에 따라 다음 요청이 영향을 받지 않고 모든 요청이 독립적이다. 따라서 클라이언트를 식별할 수 없다.
예시를 통해 Stateless를 살펴보자. 아래는 아이스 아메리카노 2잔을 주문하는 상황이다.
Stateful의 경우, 클라이언트가 여러번에 걸쳐 주문을 진행해도, 카페 직원은 여태까지의 주문 사항을 기억하고 있으므로 정상적으로 주문을 처리할 수 있다.
Stateless의 경우, 카페 직원은 클라이언트가 한 말을 바로 잊어버린다. 따라서 클라이언트가 아메리카노를 주문하고 "차가운거요"라고 주문하면, 카페 직원은 "어떤걸 차갑게 달라는거죠?"라고 응답하게 된다.
이전 상태를 보존한다면 이전 요청의 내용을 기억하여 다음 요청에 반영할 수 있다. 그러나 Stateless는 이전 요청이 무엇이였는지 기억하지 않기 때문에 한번의 요청에 모든 내용을 다 전달해야 한다.
서버는 여러대의 컴퓨터로 구성될 수도 있다. 마찬가지로 카페 직원도 여러명일 수 있다.
Stateful인 경우, 카페 직원이 인수인계 없이 중간에 교체되면 주문을 정상적으로 처리할 수 없다.
마찬가지로 서버 컴퓨터끼리 요청 사항이 공유되지 않은 상태에서, 다른 서버 컴퓨터가 요청을 받으면 제대로 된 응답을 처리할 수 없다.
Stateless의 경우, 모든 주문 사항을 한번에 말하므로 어느 카페 직원이 주문을 받더라도 처리할 수 있다.
마찬가지로 요청 한번에 모든 요구사항이 들어있기 때문에 어떤 서버 컴퓨터가 요청을 받아도 제대로 응답을 처리할 수 있다.
장점
단점
웹이 발전함에 따라 각 클라이언트에 맞게 개인화된 웹 사이트를 제공하고자 하는 욕구가 발생했다.
개인화를 위해서 여러 클라이언트들을 서버가 식별할 수 있어야 했다. HTTP는 Connectionless, Stateless였으므로 클라이언트를 식별할 수가 없었고, 초기 웹 개발자들은 클라이언트를 식별하기 위한 방법들을 각기 개발했다.
HTTP 헤더의 정보를 분석하여 사용자를 어느정도 구분할 수 있었다.
클라이언트로부터 요청이 들어왔을때, 위 정보들을 사용하여 이 클라이언트가 이전에 방문한 어떤 클라이언트와 같은지 식별하였다. 그러나 확실하게 식별하기에는 정보가 부족했다.
초기 웹 개발자들은 IP 주소를 이용해 클라이언트를 식별하려고도 했다.
그러나 IP는 컴퓨터를 식별하는 것이지 실제 사용자를 식별할 수는 없었다. 여러 사람이 하나의 IP로 접근할 수도 있고, 한 사람이 여러 개의 IP로 접근할 수도 있었다.
웹 서버가 클라이언트에게 인증 정보를 요청하는 방식인 로그인을 통해 클라이언트를 식별할 수 있었다.
그러나 매 요청마다 인증 정보를 요구하는 것은 사용자 입장에서 너무 귀찮은 일이다. 개인화된 웹을 위해 모든 페이지 전환, 요청마다 아이디와 비밀번호를 달라고 요구하면 사용자는 차라리 개인화를 하지 말라고 할 것이다.
URL에 고유 ID를 넣어서 클라이언트를 식별하는 방법
클라이언트가 웹 사이트에 방문한 첫 요청에 서버는 고유 ID를 발행하고, 그 값을 URL에 계속 넣어둔다. 사이트 내에서 페이지 이동이 일어나도 해당 아이디를 꼭 URL에 포함시켜서 클라이언트 추적을 놓치지 않도록 한다.
아마존에서는 Fat URL 방식으로 사용자의 행동을 추적하여 개인화된 웹 사이트를 제공했다고 한다.
Fat URL 단점
URL이 못 생겨진다.
URL을 공유할 수 없다.
특정 사용자의 상태 정보가 누적된 URL이기 때문에 공유하면 개인 정보도 같이 공유하게 된다.
URL을 유지하지 못 하고 이탈할 가능성이 크다.
사용자가 다른 사이트를 방문하고 다시 돌아오거나, 고유 ID가 누락된 링크를 클릭하면 해당 클라이언트에 대한 식별은 끝이 난다.
위 방식들의 한계로 새로운 클라이언트 식별법이 필요했고, 넷스케이프에서 쿠키를 개발하였고 현재 모든 브라우저에서 쿠키를 지원한다.
쿠키는 왜 쿠키일까?
UNIX OS에서 프로그램 사이에 전송되는 작은 데이터 패킷을 "Magic Cookie"라고 불렀는데,
클라이언트에서 서버로 전송되는 작은 데이터라는 개념이 비슷하여 여기서 가져온 것이라 한다.
매직 쿠키라는 용어는 쿠키를 열면 메세지가 나오는 포춘 쿠키에서 파생된 용어라고 한다.
HTTP 쿠키는 서버가 클라이언트에게 준 정보를 브라우저에 저장하고, 클라이언트가 요청을 보낼 때 HTTP 헤더에 정보를 담아서 서버에 전달한다.
클라이언트가 웹 사이트를 첫 방문하면 서버는 해당 클라이언트를 식별하기 위한 Key-Value 객체를 만든다.
(자바스크립트 객체로 표현했지만, 실제로 이렇게 생겼다는 것은 아니다. 실제로는 String이다.)
{
Name: "status",
Value: "lawyer",
Domain: "www.lawandgood.com",
Path: "/",
Expires: "2021-09-30T23:59:59.000Z",
.
.
}
HTTP 헤더의 Set-Cookie 항목에 쿠키를 담아서 전달한다. 클라이언트는 Set-Cookie 헤더에 있는 쿠키를 브라우저에 저장한다.
꼭 Set-Cookie 헤더를 통해 쿠키를 세팅할 수 있는 것은 아니다. 서버에서 렌더링하는 HTML에 정보를 전달하고 script 태그 내에서 쿠키를 세팅할 수도 있고, 사용자가 어떤 버튼을 눌렀을 때 자바스크립트를 통해 쿠키를 서버 개입 없이 세팅해놓을 수도 있다.
<script>
setCookie({
name: 'status',
value: '{{ user.status }}',
expires: new Date('2021-09-30 23:59:59'),
.
.
})
</script>
쿠키의 저장위치는 브라우저가 어떻게 쿠키 스토리지를 구현했느냐에 따라 다르다.
크롬의 경우, 데이터를 SQLite에 저장하며 쿠키는 Cookies라는 항목에 쿠키를 습득한 사이트 별로 저장된다.
클라이언트의 첫 요청 이후, 다음 요청부터는 쿠키가 HTTP 헤더에 포함된다.
브라우저가 가지고 있는 쿠키의 개수는 몇백개가 되는데, 모든 사이트를 방문하거나 API 요청을 보낼 때마다 몇백개의 쿠키를 HTTP 헤더에 담아서 보내지는 않는다.
쿠키에는 Domain과 Path라는 속성이 있다.
이렇게 쿠키를 서버로 보내서, 서버가 클라이언트를 식별할 수 있도록 한다.
쿠키의 기본 발상은 브라우저가 서버의 정보를 저장하고, 사용자가 해당 서버에 접근할 때마다 그 정보를 함께 전송하는 것이다.
저장되는 서버 정보가 쿠키이고, 브라우저는 쿠키를 저장할 책임이 있다. 이러한 시스템을 "클라이언트 측 상태", "HTTP 상태 관리 체계" 라고 한다.
쿠키는 2종류가 있고, 둘의 차이점은 파기 시점뿐이다.
만료일을 설정하지 않기 때문에 브라우저를 종료하면 파기된다. (만료일은 Expires, Max-Age 로 설정한다.)
사용자가 사이트를 탐색할 때 선호 사항, 임시 설정 등을 저장하는 쿠키
디스크에 저장되어서 만료일까지는 브라우저를 끄거나 재부팅하여도 남아있는다.
다음 방문에도 영향을 주는 설정 정보나 로그인 정보 등을 유지할 때 사용하는 쿠키
쿠키가 탈취당할 수 있다는 것을 인지하여, 민감한 정보는 서버에만 저장하고 쿠키에는 서버에 저장된 정보를 찾을 수 있는 키만 전달하는 방식.
쿠키를 사용하는 것에는 변함이 없고, 쿠키 값으로 어떤 것을 주느냐가 다르다.
쿠키의 동작방식과 같이 클라이언트가 첫 요청을 보내면 서버는 이 클라이언트를 식별하기 위한 객체를 만든다.
쿠키에서는 이 객체를 바로 HTTP 헤더에 담아서 보내지만, 세션에서는 이를 서버에 저장한다. 데이터베이스에 저장할 수도 있고, 서버의 파일 시스템에 저장할 수도 있다. 이렇게 세션이 저장되는 공간을 세션 스토리지라고 한다.
서버는 세션 스토리지에 저장된 정보를 식별할 수 있는 세션 ID를 쿠키에 담아서 클라이언트에게 전달한다.
세션 스토리지가 데이터베이스라고 한다면, 쿠키가 담고 있는 값은 단지 데이터베이스의 어떤 로우를 가리키는 PK일뿐이다. 따라서 쿠키가 탈취당해도 쿠키로부터 얻을 수 있는 정보는 없다.
클라이언트의 두번째 요청부터는 쿠키가 함께 들어온다.
서버는 쿠키를 받아서 세션 스토리지에서 세션 객체를 찾는다. 세션 객체로부터 사용자정보를 얻고 개인화된 응답을 보내준다.
세션의 핵심은 HTTP 통신 상에서는 어떠한 민감한 정보도 오가지 않는다는 것이다. 민감한 정보는 오직 서버와 세션 스토리지에서만 다뤄지고 외부로 노출되지 않는다.
서버는 인증에 필요한 정보들을 암호화시켜 토큰을 발행하고, 클라이언트는 발행받은 토큰을 HTTP 헤더에 계속 넣어서 요청을 보내는 방식
클라이언트는 아이디, 패스워드 등의 인증정보를 HTTP 요청에 담아서 서버에 전달
서버는 클라이언트로부터 받은 인증 정보가 유효한지 확인하고, 토큰을 HTTP 응답에 담아서 전달
토큰은 Secret Key로 암호화되어 있고, 유효기간이 설정되어 있다.
클라이언트는 서버로부터 발급받은 토큰을 HTTP 헤더에 담아서 요청을 보내고, 서버는 토큰이 유효한지 검사하여 인증 여부를 판별한다.
토큰은 Secret Key로 복호화하여 유효한지 아닌지 판별할 수 있기 때문에 별도의 저장공간이 필요하지 않다. 따라서 세션 방식의 단점인 서버 과부하를 줄여줄 수 있다.
Why are internet cookies called cookies? - inLIFE
Why Are Internet Cookies Called Cookies?
https://bediroglunakliye.com/gebze-evden-eve-nakliyat/
Gebze Evden Eve Nakliyat Fiyatları konusunda sizleri endişeye ve zora sokacak hiçbir durum söz konusu değildir. Nakliyat fiyatlandırma konusunda bir sorununuz olmaması için sizinleyiz.
You have really explained this topic in great detail, your article is quite long, thank you for that. motox3m
정리가 진짜 깔끔합니다ㅠㅠ 잘 보고 갑니다!