HTTP 프로토콜은 비연결지향과 상태정보 유지 안함 이라는 특징을 가지고 있어, 서버와 클라이언트가 통신시 연속적으로 이어지지 않고 한 번 통신 되면 끊어지게 되며, 상태정보가 유지되지 않게됩니다. 이러한 문제를 해결하기 위해 사용하는 것이 cookie와 session입니다.
쿠키는 사용자의 웹 브라우저에 저장되는 작은 데이터 조각입니다. 클라이언트(브라우저)와 서버 간의 상태를 유지하는 데 사용되며, HTTP는 기본적으로 상태를 유지하지 않기 때문에 이를 보완하기 위해 사용합니다. 쿠키는 사용자 정보를 저장하고 이를 서버에 전달하여 사용자를 식별하거나 사이트의 기본 설정(언어, 테마 등)을 유지하는 데 사용됩니다.
Set-Cookie
헤더 사용) 이후 요청마다 브라우저는 해당 쿠키를 서버에 보내게 됩니다.1 ) 클라이언트 측 저장
쿠키는 클라이언트(사용자의 웹 브라우저)에 저장됩니다. 서버는 Set-Cookie
라는 HTTP 헤더를 사용해 쿠키를 클라이언트에 설정할 수 있습니다. 이렇게 설정된 쿠키는 사용자가 웹 페이지를 다시 방문할 때마다 서버에 자동으로 전송됩니다.
2 ) 작은 크기
쿠키의 크기는 일반적으로 하나당 약 4KB로 제한됩니다. 때문에 너무 큰 데이터를 쿠키에 저장하기는 어렵고, 주로 사용자 식별 정보와 같은 간단한 데이터를 저장하는 데 사용됩니다.
3 ) 만료 기간 설정
쿠키는 만료 기간(유효기간)을 설정할 수 있습니다. 만료 기간은 Expires
나 Max-Age
속성으로 설정하며, 이 속성에 따라 쿠키가 자동으로 삭제됩니다.
4 ) 도메인 및 경로 제한
쿠키는 특정 도메인과 경로에서만 사용할 수 있도록 설정할 수 있습니다. 이를 통해 쿠키를 어느 서버에서 사용할 수 있는지 제어할 수 있습니다.
/user
로 설정하면 /user
경로 하위의 모든 URL에서만 해당 쿠키가 유효하게 됩니다.5 ) 보안 설정
Strict
, Lax
, None
과 같은 값을 가지며, CSRF(Cross-Site Request Forgery)와 같은 공격에 대한 방어에 도움이 됩니다.6 ) 자동 전송
사용자가 설정한 쿠키는 이후 해당 도메인의 모든 HTTP 요청마다 자동으로 포함되어 서버로 전송됩니다. 이를 통해 서버는 사용자를 식별하고, 맞춤형 정보를 제공할 수 있습니다.
7 ) 사용자 제어 가능
사용자는 브라우저 설정을 통해 쿠키를 볼 수 있고, 삭제하거나 차단할 수 있습니다. 따라서 웹 애플리케이션이 쿠키에 의존해 중요한 기능을 수행할 경우, 사용자가 쿠키를 삭제하거나 차단하면 해당 기능이 제한될 수 있습니다.
const http = require('http');
// 서버 생성
const server = http.createServer((req, res) => {
if (req.url === '/') {
// 'Set-Cookie' 헤더를 설정하여 쿠키 생성
res.setHeader('Set-Cookie', 'user=tester; Max-Age=900; HttpOnly; Path=/');
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Cookie has been set directly using http module');
} else if (req.url === '/get-cookie') {
// 클라이언트가 보낸 쿠키를 가져오기
const cookies = req.headers.cookie;
if (cookies) {
// 간단히 쿠키 문자열을 '; '로 분리하여 각 쿠키를 배열로 나누기
const cookieArray = cookies.split('; ');
let userCookie;
cookieArray.forEach(cookie => {
const [name, value] = cookie.split('=');
if (name === 'user') {
userCookie = value;
}
});
res.writeHead(200, { 'Content-Type': 'text/plain' });
if (userCookie) {
res.end(`User cookie value is: ${userCookie}`);
} else {
res.end('No user cookie found');
}
} else {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('No cookies sent by client');
}
} else {
// 기본 응답 처리
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
});
// 서버 시작
server.listen(8080, () => {
console.log('Server is running on port 8080');
});
쿠키 설정하기
res.setHeader('Set-Cookie', ...)
를 사용하여 직접 쿠키를 설정합니다.user=tester
라는 이름과 값을 가진 쿠키를 설정하고, 다음과 같은 속성을 지정했습니다:Max-Age=900
: 쿠키의 수명이 900초(15분)입니다.HttpOnly
: 이 속성을 통해 클라이언트 측 JavaScript에서 접근하지 못하게 합니다.Path=/
: 이 쿠키가 애플리케이션 내의 모든 경로에서 유효하도록 설정합니다.개발자 도구의 네트워크 탭에 헤더 부분을 확인 하면 Set-Cookie
헤더에 설정한 쿠키값이 들어간 것을 확인할 수 있습니다.
쿠키 가져오기
req.headers.cookie
를 사용해 클라이언트가 보낸 모든 쿠키를 하나의 문자열로 가져옵니다.;
로 구분된 여러 쿠키가 포함될 수 있으므로, split('; ')
을 통해 각 쿠키를 개별적으로 분리하고 원하는 값을 찾습니다.개발자 도구 애플리케이션 탭 쿠키를 확인하면 설정한 쿠키를 확인할 수 있습니다.
const http = require('http');
const url = require('url');
const querystring = require('querystring');
// HTML 코드: 로그인 화면과 메인 화면을 위한 HTML 문자열 준비
const loginPage = `
<html>
<body>
<h2>Login</h2>
<form method="GET" action="/login">
<label for="id">ID: </label>
<input type="text" id="id" name="id" />
<button type="submit">Login</button>
</form>
</body>
</html>
`;
const createMainPage = (userId) => `
<html>
<body>
<h2>${userId}님 안녕하세요!</h2>
<a href="/logout">Logout</a>
</body>
</html>
`;
// 서버 생성
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url);
// 쿠키 가져오기
const cookies = req.headers.cookie;
let userId = null;
if (cookies) {
const cookieArray = cookies.split('; ');
cookieArray.forEach(cookie => {
const [name, value] = cookie.split('=');
if (name === 'userId') {
userId = decodeURIComponent(value);
}
});
}
// 메인 화면
if (parsedUrl.pathname === '/' && userId) {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(createMainPage(userId));
// 로그인 화면
} else if (parsedUrl.pathname === '/' && !userId) {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(loginPage);
// 로그인 처리
} else if (parsedUrl.pathname === '/login') {
const query = querystring.parse(parsedUrl.query);
if (query.id) {
// ID 값을 URL 인코딩 후 쿠키에 저장 (특수 문자 처리)
const encodedId = encodeURIComponent(query.id);
res.setHeader('Set-Cookie', `userId=${encodedId}; Max-Age=3600; HttpOnly; Path=/`);
res.writeHead(302, { 'Location': '/' });
res.end();
} else {
res.writeHead(400, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('ID를 입력해주세요.');
}
// 로그아웃 처리
} else if (parsedUrl.pathname === '/logout') {
// 쿠키 삭제 (Max-Age를 0으로 설정하여 삭제)
res.setHeader('Set-Cookie', 'userId=; Max-Age=0; Path=/');
res.writeHead(302, { 'Location': '/' });
res.end();
// 404 Not Found
} else {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Not Found');
}
});
// 서버 시작
server.listen(8080, () => {
console.log('Server is running on port 8080');
});
세션은 서버 측에서 사용자의 상태나 데이터를 유지하기 위한 방법입니다. 사용자가 서버에 접속할 때 서버는 세션을 생성하고 이를 통해 각 사용자의 정보를 서버에 저장합니다. 세션은 로그인 정보와 같은 데이터를 저장하여 사용자가 웹사이트를 이동할 때마다 다시 로그인할 필요가 없도록 합니다.
1 ) 서버 측 저장
2 ) 보안성
3 ) 세션 ID와 쿠키의 관계
4 ) 유효 기간
5 ) 서버의 자원 사용
6 ) 사용자 별 고유성
const http = require('http');
// 메모리에 세션 데이터를 저장할 객체
const sessionStore = {};
// 세션 기간 (15분)
const sessionTimeout = 900000;
// 쿠키 파싱 함수
const parseCookies = (cookieHeader) => {
const cookies = {};
if (cookieHeader) {
cookieHeader.split(';').forEach(cookie => {
const [name, value] = cookie.trim().split('=');
cookies[name] = value;
});
}
return cookies;
};
// 서버 생성
const server = http.createServer((req, res) => {
const cookies = parseCookies(req.headers.cookie);
let sessionId = cookies['sessionId'];
// 세션이 존재하지 않으면 새로운 세션 생성
if (!sessionId || !sessionStore[sessionId]) {
sessionId = new Date().getTime(); // 새로운 세션 ID로 현재 시간 설정
sessionStore[sessionId] = {
user: 'tester',
createdAt: Date.now()
};
// Set-Cookie 헤더로 클라이언트에게 세션 ID 전달
res.setHeader('Set-Cookie', `sessionId=${sessionId}; Max-Age=${sessionTimeout / 1000}; HttpOnly`);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('New session has been created');
} else {
const session = sessionStore[sessionId];
// 세션이 만료되었는지 확인
if (Date.now() - session.createdAt > sessionTimeout) {
delete sessionStore[sessionId]; // 만료된 세션 삭제
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Session expired, please refresh to create a new session');
} else {
// 세션이 유효하면 사용자 데이터를 반환
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`User session value is: ${session.user}`);
}
}
});
// 서버 시작
server.listen(8080, () => {
console.log('Server is running on port 8080');
});
new Date()
을 사용하여 현재 시간을 세션 ID로 사용합니다.Set-Cookie
헤더를 통해 전달된 세션 ID를 쿠키로 저장하게 됩니다.sessionStore
에서 해당 세션이 유효한지, 만료되었는지 확인합니다.개발자 도구의 네트워크 탭 헤더의 Set-cookie
에설정한 sessionId를 확인할 수 있습니다.
개발자 도구 애플리케이션의 쿠키 탭에 생성한 sessionId 쿠키를 확인할 수 있습니다.
const http = require('http');
const url = require('url');
const querystring = require('querystring');
// 세션 저장소
const sessions = {};
// HTML 페이지
const loginPage = `
<html>
<body>
<h2>Login</h2>
<form method="GET" action="/login">
<label for="id">ID: </label>
<input type="text" id="id" name="id" />
<button type="submit">Login</button>
</form>
</body>
</html>
`;
const createMainPage = (userId) => `
<html>
<body>
<h2>${userId}님 안녕하세요!</h2>
<a href="/logout">Logout</a>
</body>
</html>
`;
// 서버 생성
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url);
// 쿠키 가져오기
const cookies = req.headers.cookie;
let sessionId = null;
if (cookies) {
const cookieArray = cookies.split('; ');
cookieArray.forEach(cookie => {
const [name, value] = cookie.split('=');
if (name === 'sessionId') {
sessionId = value;
}
});
}
// 세션 ID를 통해 사용자 확인
const session = sessionId ? sessions[sessionId] : null;
// 메인 화면
if (parsedUrl.pathname === '/' && session) {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(createMainPage(session.userId));
// 로그인 화면
} else if (parsedUrl.pathname === '/' && !session) {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(loginPage);
// 로그인 처리
} else if (parsedUrl.pathname === '/login') {
const query = querystring.parse(parsedUrl.query);
if (query.id) {
// 새로운 세션 생성
const newSessionId = new Date().getTime().toString(); // 고유한 세션 ID 생성 (UNIX 타임스탬프 사용)
sessions[newSessionId] = { userId: query.id }; // 세션 저장소에 세션 정보 저장
// 세션 쿠키 설정
res.setHeader('Set-Cookie', `sessionId=${newSessionId}; Max-Age=3600; HttpOnly; Path=/`);
res.writeHead(302, { 'Location': '/' });
res.end();
} else {
res.writeHead(400, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('ID를 입력해주세요.');
}
// 로그아웃 처리
} else if (parsedUrl.pathname === '/logout') {
if (sessionId && sessions[sessionId]) {
delete sessions[sessionId]; // 세션 삭제
}
// 세션 쿠키 삭제
res.setHeader('Set-Cookie', 'sessionId=; Max-Age=0; Path=/');
res.writeHead(302, { 'Location': '/' });
res.end();
// 404 Not Found
} else {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Not Found');
}
});
// 서버 시작
server.listen(8080, () => {
console.log('Server is running on port 8080');
});
HttpOnly
속성을 설정해 JavaScript로 접근할 수 없게 해야 합니다.Max-Age
또는 Expires
속성에 따라 만료됩니다. 이 속성에 따라 쿠키는 브라우저를 닫아도 유지되거나, 정해진 시간 후에 만료될 수 있습니다.구분 | 쿠키 | 세션 |
---|---|---|
저장 위치 | 클라이언트 측(브라우저) | 서버 측 |
보안성 | 보안에 취약(암호화 필요), 사용자가 직접 수정 가능 | 상대적으로 안전(클라이언트는 세션 ID만 보관) |
수명 | 브라우저 닫아도 유지 가능(지속적 저장 가능) | 브라우저를 닫으면 보통 만료(일정 시간 후 만료) |
용량 제한 | 4KB로 제한(브라우저별 개수 제한 있음) | 서버에 저장, 용량 제한 없음 |
사용 예시 | 로그인 상태 유지, 사용자 설정 저장 | 로그인 인증, 중요한 상태 정보 유지 |