현재 오즈코딩스쿨 강의를 통해 프론트엔드를 학습하고 있습니다.
본 포스트는 해당 강의에 대한 내용 정리를 목적으로 합니다.

웹에서 말하는 Cookie(쿠키)는 웹사이트가 사용자의 브라우저에 저장하는 작은 데이터 조각이다.


로그인 상태를 기억하게 해줘요. 예: 로그인하면 쿠키에 정보가 저장되어, 새로고침하거나 페이지를 이동해도 로그인이 유지.
예: 다크 모드 설정, 언어 설정 등을 저장해서 다음 방문 시 자동으로 적용
방문 기록, 클릭 패턴 등을 분석해서 마케팅, 광고 등에 사용

document.cookie = "username=Danaka; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/";
이 코드는 username=Danaka라는 쿠키를 저장하고, 2025년 12월 31일까지 유지되도록 설정
npm init -y
npm i express
npm i express cors cookie-parser
cookie.js
// HTML 문서에서 ID가 'set_cookie'인 요소를 찾아서 setCookieButton 변수에 할당합니다.
// 이 요소는 아마도 "쿠키 설정" 버튼
const setCookieButton = document.querySelector('#set_cookie');
// HTML 문서에서 ID가 'delete_cookie'인 요소를 찾아서 deleteCookieButton 변수에 할당합니다.
// 이 요소는 아마도 "쿠키 삭제" 버튼
const deleteCookieButton = document.querySelector('#delete_cookie');
// axios의 기본 설정에 withCredentials를 true로 설정합니다.
// 이 설정은 브라우저가 교차 출처 요청(cross-origin requests)을 보낼 때
// 자동으로 쿠키, 인증 헤더 등의 자격 증명을 포함하도록 지시합니다.
// 쿠키를 사용한 세션 관리 등에서 필수적인 설정입니다.
axios.defaults.withCredentials = true;
// setCookieButton을 클릭했을 때 실행될 이벤트 핸들러를 정의합니다.
setCookieButton.onclick = () => {
// http://localhost:3000으로 GET 요청을 보냅니다.
// 이 요청은 서버(http://localhost:3000)에게 쿠키를 설정하라는 신호 역할을 할 것으로 예상됩니다.
// 서버는 이 GET 요청에 대한 응답으로 'Set-Cookie' 헤더를 포함하여 쿠키를 브라우저에 설정하게 될 것입니다.
axios.get('http://localhost:3000')
.then(res => {
// 요청이 성공적으로 완료되면 서버로부터 받은 응답(res) 객체를 콘솔에 출력합니다.
// 응답 상태 코드나 데이터 등을 확인할 수 있습니다.
console.log(res);
})
// .catch(...)를 추가하여 오류 처리 로직을 구현하는 것이 좋습니다.
// 예: .catch(error => console.error('쿠키 설정 실패:', error));
}
// deleteCookieButton을 클릭했을 때 실행될 이벤트 핸들러를 정의합니다.
deleteCookieButton.onclick = () => {
// http://localhost:3000으로 DELETE 요청을 보냅니다.
// 이 요청은 서버(http://localhost:3000)에게 쿠키를 삭제하라는 신호 역할을 할 것으로 예상됩니다.
// 서버는 이 DELETE 요청을 받고 해당 쿠키를 만료시키거나 삭제하는 로직을 수행해야 합니다.
axios.delete('http://localhost:3000')
.then(res => {
// 요청이 성공적으로 완료되면 서버로부터 받은 응답(res) 객체를 콘솔에 출력합니다.
// 응답 상태 코드 등을 확인할 수 있습니다.
console.log(res);
})
// .catch(...)를 추가하여 오류 처리 로직을 구현하는 것이 좋습니다.
// 예: .catch(error => console.error('쿠키 삭제 실패:', error));
}
server.js
// express 모듈을 불러와 웹 서버를 구성하는 데 사용합니다.
const express = require('express');
// cors 모듈을 불러와 교차 출처 요청(Cross-Origin Requests)을 처리합니다.
// 클라이언트(예: localhost:9000)와 서버(localhost:3000)의 출처가 다르므로 필요합니다.
const cors = require('cors');
// cookie-parser 모듈을 불러와 요청 헤더의 쿠키를 파싱하고 응답에 쿠키를 설정하는 데 도움을 줍니다.
const cookieParser = require('cookie-parser');
// express 애플리케이션 인스턴스를 생성합니다.
const app = express();
// CORS 미들웨어를 애플리케이션에 적용합니다.
app.use(cors({
// origin: 허용할 클라이언트 출처(Origin) 목록입니다. 클라이언트 코드가 실행되는 주소와 포트를 명시합니다.
// 여기서는 127.0.0.1:9000 또는 localhost:9000에서 오는 요청만 허용합니다.
origin: ['http://127.0.0.1:9000', 'http://localhost:9000'],
// methods: 허용할 HTTP 메소드 목록입니다. 클라이언트에서 사용하는 GET, DELETE 메소드와 CORS preflight 요청을 위한 OPTIONS를 허용합니다.
methods: ['GET', 'DELETE', 'OPTIONS'],
// credentials: true 설정은 브라우저가 요청 시 쿠키나 인증 헤더 같은 자격 증명을 포함하도록 허용합니다.
// 클라이언트와 서버 모두에서 이 설정을 true로 해야 쿠키 기반 통신이 정상적으로 동작합니다.
credentials: true
}));
// cookie-parser 미들웨어를 적용합니다.
// 이 미들웨어는 들어오는 요청의 Cookie 헤더를 파싱하여 req.cookies 객체로 만들어주고,
// res.cookie(), res.clearCookie() 등의 메서드를 사용할 수 있게 해줍니다.
app.use(cookieParser());
// 루트 경로('/')에 대한 GET 요청을 처리하는 라우트를 정의합니다.
// 클라이언트의 '쿠키 설정' 요청을 여기서 처리합니다.
app.get('/', (req, res) => {
// 'test-cookie'라는 이름으로 쿠키를 설정합니다.
// 값은 'my cookie'입니다.
// 옵션 설명:
// maxAge: 쿠키의 최대 수명(밀리초)입니다. 100000ms(100초)로 설정됩니다.
// httpOnly: true로 설정하면 클라이언트 측 JavaScript에서 document.cookie 등으로 쿠키에 접근할 수 없습니다.
// XSS 공격 방지에 도움이 되는 중요한 보안 설정입니다.
// secure: true로 설정하면 HTTPS 연결에서만 쿠키가 전송됩니다.
// 로컬 환경(http://localhost)에서 테스트 시 secure: true이면 쿠키가 설정되지 않거나 전송되지 않을 수 있으니 주의하세요.
//로컬 테스트 시에는 이 옵션을 제거하거나 false로 설정할 필요가 있습니다.
res.cookie('test-cookie', 'my cookie', { maxAge: 100000, httpOnly: true, secure: true });
// 클라이언트에 '쿠키 생성 완료' 메시지를 응답으로 보냅니다.
res.send('쿠키 생성 완료');
});
// 루트 경로('/')에 대한 DELETE 요청을 처리하는 라우트를 정의합니다.
// 클라이언트의 '쿠키 삭제' 요청을 여기서 처리합니다.
app.delete('/', (req, res) => {
// 'test-cookie'라는 이름의 쿠키를 삭제(무효화)합니다.
// 실제로는 해당 쿠키의 만료일을 현재보다 과거로 설정하여 브라우저가 즉시 삭제하도록 지시합니다.
res.clearCookie('test-cookie');
// 클라이언트에 '쿠키 삭제 완료' 메시지를 응답으로 보냅니다.
res.send('쿠키 삭제 완료');
});
// 3000번 포트에서 서버를 시작합니다.
app.listen(3000, () => {
// 서버가 성공적으로 시작되면 콘솔에 메시지를 출력합니다.
console.log('서버 실행!');
});


Session은 서버가 사용자(클라이언트)의 정보를 일정 시간 동안 유지하기 위해 사용하는 저장 방식
즉, 사용자와 서버 간의 "연결된 상태"를 유지하기 위한 기술이다.



즉, 세션 ID는 쿠키에 저장되지만, 실제 데이터(예: 로그인한 유저 정보)는 서버에 저장
쿠키는 클라이언트에 저장, 세션은 서버에 저장
쿠키보다 보안이 뛰어남
로그인 등 보안이 필요한 경우에 자주 사용
HttpOnly, Secure, SameSite 옵션을 쿠키에 적용const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
cookie: { maxAge: 60000 } // 1분
}));
app.get('/', (req, res) => {
if (req.session.user) {
res.send(`환영합니다, ${req.session.user.name}님`);
} else {
res.send('로그인이 필요합니다.');
}
});
app.get('/login', (req, res) => {
req.session.user = { name: '홍길동' };
res.send('로그인 성공');
});
app.get('/logout', (req, res) => {
req.session.destroy(() => {
res.send('로그아웃 완료');
});
});
npm init -y
npm i express cors cookie-parser express-session
login.js
// DOM(Document Object Model) 요소를 선택합니다.
// 폼(form) 관련 요소들
const form = document.querySelector('form'); // HTML 문서의 첫 번째 form 요소를 선택합니다.
const idInput = document.querySelector('#user_id'); // ID가 'user_id'인 요소를 선택합니다 (사용자 ID 입력 필드).
const passwordInput = document.querySelector('#user_password'); // ID가 'user_password'인 요소를 선택합니다 (비밀번호 입력 필드).
const loginButton = document.querySelector('#login_button'); // ID가 'login_button'인 요소를 선택합니다 (로그인 버튼).
// 메인 콘텐츠 및 사용자 정보 관련 요소들
const main = document.querySelector('main'); // HTML 문서의 첫 번째 main 요소를 선택합니다 (사용자 정보 등을 표시할 영역).
const userName = document.querySelector('#user_name'); // ID가 'user_name'인 요소를 선택합니다 (사용자 이름을 표시할 요소).
const userInfo = document.querySelector('#user_info'); // ID가 'user_info'인 요소를 선택합니다 (추가 사용자 정보를 표시할 요소).
const logoutButton = document.querySelector('#logout_button'); // ID가 'logout_button'인 요소를 선택합니다 (로그아웃 버튼).
// Axios 설정
// 모든 Axios 요청에 자격 증명(예: 쿠키, 인증 헤더)을 포함하도록 설정합니다.
// 이는 서버와의 세션 관리에 중요하며, 특히 CORS 환경에서 필요할 수 있습니다.
axios.defaults.withCredentials = true;
// 이벤트 리스너: 폼 제출
// 폼의 'submit' 이벤트가 발생했을 때 기본 동작(페이지 새로고침)을 막습니다.
// 이렇게 하면 JavaScript를 통해 비동기적으로 로그인 처리를 할 수 있습니다.
form.addEventListener('submit', (e) => e.preventDefault());
// 로그인 요청 함수
// 사용자 ID와 비밀번호를 입력 필드에서 가져와 Axios POST 요청으로 서버에 전송합니다.
// 서버 주소는 'http://localhost:3000'으로 하드코딩되어 있습니다.
function login() {
const userId = idInput.value; // ID 입력 필드의 현재 값을 가져옵니다.
const userPassword = passwordInput.value; // 비밀번호 입력 필드의 현재 값을 가져옵니다.
// 'http://localhost:3000' 엔드포인트로 POST 요청을 보냅니다.
// 요청 본문에는 userId와 userPassword 객체가 JSON 형태로 포함됩니다.
return axios.post('http://localhost:3000', { userId, userPassword });
}
// 로그아웃 요청 함수
// 서버에 로그아웃 요청을 보냅니다. DELETE 메서드를 사용하고 있습니다.
// 서버 주소는 'http://localhost:3000'으로 하드코딩되어 있습니다.
function logout() {
// 'http://localhost:3000' 엔드포인트로 DELETE 요청을 보냅니다.
return axios.delete('http://localhost:3000');
}
// 사용자 정보 가져오기 함수
// 서버로부터 현재 로그인된 사용자의 정보를 가져옵니다. GET 메서드를 사용하고 있습니다.
// 서버 주소는 'http://localhost:3000'으로 하드코딩되어 있습니다.
function getUserInfo() {
// 'http://localhost:3000' 엔드포인트로 GET 요청을 보냅니다.
return axios.get('http://localhost:3000');
}
// 사용자 정보 표시 함수
// 전달받은 사용자 정보를 UI에 렌더링하고, 폼을 숨기고 메인 영역을 표시합니다.
function renderUserInfo(user) {
main.style.display = 'block'; // main 영역을 보이게 합니다.
form.style.display = 'none'; // 폼 영역을 숨깁니다.
userName.textContent = user.user_name; // 사용자 이름을 표시합니다.
userInfo.textContent = user.user_info; // 추가 사용자 정보를 표시합니다.
}
// 로그인 폼 표시 함수
// 사용자 정보 영역을 숨기고 로그인 폼을 표시합니다. 사용자 정보 필드는 초기화합니다.
function renderLoginForm() {
main.style.display = 'none'; // main 영역을 숨깁니다.
form.style.display = 'block'; // 폼 영역을 보이게 합니다.
userName.textContent = ''; // 사용자 이름 필드를 비웁니다.
userInfo.textContent = ''; // 추가 사용자 정보 필드를 비웁니다.
}
// 이벤트 핸들러: 로그인 버튼 클릭
// 로그인 버튼 클릭 시 실행되는 함수입니다.
loginButton.onclick = () => {
// login() 함수를 호출하여 로그인 요청을 보냅니다. 이는 Promise를 반환합니다.
login()
// 로그인 요청이 성공하면 (Promise resolve) getUserInfo() 함수를 호출하여 사용자 정보를 가져옵니다.
.then(() => getUserInfo())
// 사용자 정보 가져오기 요청이 성공하면 (Promise resolve) 응답 데이터를 받아 renderUserInfo 함수에 전달하여 UI를 업데이트합니다.
.then(res => renderUserInfo(res.data))
// TODO: 에러 처리가 누락되어 있습니다. 로그인 또는 사용자 정보 가져오기 실패 시 사용자에게 알리거나 UI를 변경하는 로직이 필요합니다.
// .catch(error => {
// console.error('로그인 또는 사용자 정보 가져오기 실패:', error);
// // 예: 사용자에게 에러 메시지 표시
// });
}
// 이벤트 핸들러: 로그아웃 버튼 클릭
// 로그아웃 버튼 클릭 시 실행되는 함수입니다.
logoutButton.onclick = () => {
// logout() 함수를 호출하여 로그아웃 요청을 보냅니다. 이는 Promise를 반환합니다.
logout()
// 로그아웃 요청이 성공하면 (Promise resolve) 응답 데이터를 받아 처리합니다.
.then(res => {
console.log(res); // 콘솔에 응답을 출력합니다 (디버깅용).
renderLoginForm(); // 로그인 폼을 다시 표시합니다.
})
// TODO: 에러 처리가 누락되어 있습니다. 로그아웃 실패 시 처리 로직이 필요합니다.
// .catch(error => {
// console.error('로그아웃 실패:', error);
// // 예: 사용자에게 에러 메시지 표시
// });
}
server.js
// 필요한 모듈들을 가져옵니다.
const express = require('express') // 웹 서버 프레임워크 Express
const cors = require('cors') // CORS (Cross-Origin Resource Sharing) 미들웨어
const session = require('express-session') // 세션 관리 미들웨어
const cookieParser = require('cookie-parser') // 쿠키 파싱 미들웨어
// 사용자 데이터 (임시로 메모리에 저장)
// 실제 서비스에서는 데이터베이스를 사용해야 합니다.
// 보안 경고: 비밀번호를 평문으로 저장하는 것은 매우 위험합니다. 반드시 해싱하여 저장해야 합니다.
const users = [
{
user_id: 'test',
user_password: '1234', // 보안 경고: 평문 비밀번호! 해싱 필요!
user_name: '테스트 유저',
user_info: '테스트 유저입니다'
}
]
// Express 애플리케이션 인스턴스를 생성합니다.
const app = express()
// 미들웨어 설정
// CORS 설정: 클라이언트의 특정 Origin(주소)에서의 요청을 허용합니다.
// credentials: true 설정은 클라이언트에서 인증 정보(쿠키 등)를 함께 보내도록 허용합니다.
app.use(cors({
origin: ['http://127.0.0.1:9000', 'http://localhost:9000'], // 허용할 클라이언트 주소 목록
methods: ['OPTIONS', 'POST', "GET", "DELETE"], // 허용할 HTTP 메서드 목록
credentials: true // 쿠키 등 자격 증명 정보 교환 허용
}))
// cookie-parser 미들웨어: 요청의 쿠키를 파싱하여 req.cookies 객체로 만듭니다.
app.use(cookieParser())
// express.json 미들웨어: 요청의 본문(body)이 JSON 형식일 경우 파싱하여 req.body 객체로 만듭니다.
app.use(express.json())
// express-session 미들웨어: 세션을 관리합니다.
app.use(session({
secret: 'session secret', // 세션 ID 쿠키를 서명하는 데 사용되는 비밀 키. 보안을 위해 무작위의 복잡한 문자열을 사용하고 환경 변수로 관리하는 것이 좋습니다.
resave: false, // 세션 데이터가 변경되지 않아도 세션을 저장할지 여부 (일반적으로 false 권장)
saveUninitialized: false, // 초기화되지 않은(새로 생성되었지만 수정되지 않은) 세션을 저장소에 저장할지 여부 (일반적으로 false 권장)
name: 'session_id' // 클라이언트에게 전송될 세션 쿠키의 이름
}))
// 라우트 정의
// POST / : 로그인 처리
app.post('/', (req, res) => {
// 요청 본문(req.body)에서 userId와 userPassword를 가져옵니다. (express.json 미들웨어 덕분)
const { userId, userPassword } = req.body
// users 배열에서 제출된 ID와 비밀번호가 일치하는 사용자를 찾습니다.
const userInfo = users.find(el => el.user_id === userId && el.user_password === userPassword)
// 사용자가 찾아지지 않으면 (로그인 실패)
if (!userInfo) {
res.status(401).send('로그인 실패') // 401 Unauthorized 상태 코드와 메시지를 응답합니다.
} else {
// 사용자를 찾았으면 (로그인 성공)
// 세션에 사용자 ID를 저장합니다. 이로써 사용자가 로그인 상태임을 세션을 통해 관리합니다.
req.session.userId = userInfo.user_id
res.send("세션 생성 완료!") // 성공 메시지를 응답합니다.
}
})
// GET / : 사용자 정보 가져오기
app.get('/', (req, res) => {
// 현재 세션의 userId를 사용하여 users 배열에서 해당 사용자를 찾습니다.
// req.session.userId가 존재하지 않으면 (로그인되지 않은 상태) userInfo는 undefined가 됩니다.
const userInfo = users.find(el => el.user_id === req.session.userId)
// TODO: 로그인되지 않은 상태(userInfo가 undefined인 경우)에 대한 명시적인 처리가 필요합니다.
// 현재는 userInfo가 undefined여도 res.json(undefined)를 보내게 되며, 이는 HTTP 200 OK와 본문 null로 응답됩니다.
// 일반적으로는 로그인되지 않은 경우 401 Unauthorized 상태 코드를 응답하는 것이 좋습니다.
// if (!userInfo) {
// return res.status(401).send('로그인 정보 없음');
// }
// 찾은 사용자 정보를 JSON 형식으로 응답합니다.
return res.json(userInfo)
})
// DELETE / : 로그아웃 처리
app.delete('/', (req, res) => {
// 세션을 파기(삭제)합니다. 서버 메모리/저장소에서 세션 데이터가 제거됩니다.
req.session.destroy()
// 클라이언트의 세션 쿠키를 삭제하도록 지시합니다.
// express-session이 destroy() 시 쿠키를 만료시키기도 하지만 명시적으로 지우는 코드를 두는 경우도 있습니다.
res.clearCookie('session_id')
res.send("세션 삭제 완료!") // 성공 메시지를 응답합니다.
})
// 서버를 3000번 포트에서 실행합니다.
app.listen(3000, () => console.log('서버 실행!'))