[2024.05.24 TIL] 내일배움캠프 28일차 (express-session)

My_Code·2024년 5월 26일
0

TIL

목록 보기
37/112
post-thumbnail

본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.


💻 TIL(Today I Learned)

📌 Today I Done

✏️ express-session이란?

  • express-session은 Express에서 세션 기능을 쉽게 구현하기 위한 미들웨어

  • 세션 ID를 클라이언트에게 발급하고, 이 세션 ID를 통해 서버는 클라이언트의 상태를 추적함

  • 즉, 클라이언트가 세션 ID를 발급받은 후에는 모든 서버 요청마다 세션 ID가 포함된 쿠키를 전달하게 되며. 이로 인해 서버는 클라이언트를 쉽게 식별할 수 있음


✏️ express-session 미들웨어 구성요소

  • express-session은 아래와 같이 전역 미들웨어로 등록함
import expressSession from 'express-session';

app.use(
    expressSession({
        secret: process.env.CUSTOMIZED_SECRET_KEY,  // 세션 암호화 비밀 키
        resave: false,  // 클라이언트 요청이 올 때마다 세션을 새롭게 저장할 지 설정, 변경 사항이 없어도 다시 저장함
        saveUninitialized: false,  // 세션이 초기화되지 않았을 때 세션을 저장할 지 설정
        cookie: {
          	// 세션 쿠키 설정
            maxAge: 1000 * 60 * 60 * 24,
        },
    }),
);

✏️ express-session API

  • 세션에 userId 저장하는 API
/** 세션 등록 API **/
app.post('/sessions', (req, res, next) => {
  	const { userId } = req.body;

  	// 클라이언트에게 전달받은 userId를 세션에 저장
  	req.session.userId = userId;

  	return res.status(200).json({ message: '세션을 설정했습니다.' });
});
  • 세션에 저장된 userId 조회하는 API
/** 세션 조회 API **/
app.get('/sessions', (req, res, next) => {
  	return res.status(200).json({
    	message: '세션을 조회했습니다.',
    	session: req.session.userId ?? null, // 세션에 저장된 usrId를 조회
  	});
});

✏️ express-session을 활용한 사용자 인증 미들웨어

  • express-session 적용 전
// import JWT from 'jsonwebtoken';
import { prisma } from '../utils/prisma/index.js';

export default async (req, res, next) => {
    try {
        // 클라이언트로부터 쿠키를 전달 받음
        const { authorization } = req.cookies;

        // 쿠키가 Bearer 토큰 형식인지 확인
        // 배열 구조 분해 할당
        const [tokenType, token] = authorization.split(' ');
        if (tokenType !== 'Bearer') throw new Error('토큰 타입이 일치하지 않습니다.');

        // 서버에서 발급한 JWT가 맞는지 검증
        const decodedToken = JWT.verify(token, process.env.JWT_SECRET_KEY);
        const userId = decodedToken.userId;

        // JWT를 통해 얻은 userId를 통해 실제로 사용자가 있는지 확인 (타입 주의)
        const user = await prisma.users.findFirst({
            where: { userId: +userId },
        });

        if (!user) {
            // 특정 쿠기를 삭제 (초기화)
            // 쿠키에서 가져온 토큰이 잘못된 토큰이기 때문에 그냥 쿠키를 비워버림
            res.clearCookie('authorization');
            
            throw new Error('토큰 사용자가 존재하지 않습니다.');
        }

        // req.user에 조회된 사용자 정보 할당
        req.user = user;

        // 다음 미들웨어를 실행
        next();
    } catch (err) {
        res.clearCookie('authorization'); // 특정 쿠기를 삭제 (초기화)

        switch (err.name) {
            case 'TokenExpiredError':
                return res.status(401).json({ message: '토큰이 만료되었습니다.' });
            case 'JsonWebTokenError':
                return res.status(401).json({ message: '토큰 인증에 실패했습니다.' });
            default:
                return res.status(401).json({ message: err.message ?? '비정상적인 요청입니다.' });
        }
    }
};
  • express-session 적용 후
// import JWT from 'jsonwebtoken';
import { prisma } from '../utils/prisma/index.js';

export default async (req, res, next) => {
    try {
        // express-session을 통해 위 과정을 간략화
        const { userId } = req.session;
        if (!userId) throw new Error('로그인이 필요합니다.');

        // JWT를 통해 얻은 userId를 통해 실제로 사용자가 있는지 확인 (타입 주의)
        const user = await prisma.users.findFirst({
            where: { userId: +userId },
        });

        if (!user) throw new Error('토큰 사용자가 존재하지 않습니다.');

        // req.user에 조회된 사용자 정보 할당
        req.user = user;

        // 다음 미들웨어를 실행
        next();
    } catch (err) {
        switch (err.name) {
            default:
                return res.status(401).json({ message: err.message ?? '비정상적인 요청입니다.' });
        }
    }
};

✏️ express-mysql-session이란?

  • express-mysql-session 모듈은 express-session의 정보를 MySQL에 저장할 수 있도록 도와주는 모듈

  • 기존의 express-session은 서버를 재실행할 때마다 세션 정보가 초기화 되었음

  • 이는 express-session이 기본적으로 세션 정보를 인 메모리에 저장하기 때문에 발생함

  • 이를 해결하기 위해서 외부 세션 스토리지를 사용해서 서버가 재실행되어도 세션 정보가 남아있도록 함

  • express-mysql-session 모듈을 설치해서 MySQL를 외부 세션 스토리지로 사용하여, 세션 정보를 MySQL에 저장하고 관리할 수 있도록 할 수 있음


✏️ express-mysql-session 적용하기

import express from 'express';
import cookieParser from 'cookie-parser';
import expressSession from 'express-session';
import expressMySQLSession from 'express-mysql-session';
import dotenv from 'dotenv';
import UsersRouter from './routes/users.router.js';
import PostsRouter from './routes/posts.router.js';
import CommentsRouter from './routes/comments.router.js';
import logMiddleware from './middlewares/log.middleware.js';
import errorHandingMiddleware from './middlewares/error-handing.middleware.js';

dotenv.config();

const app = express();
const SERVER_PORT = process.env.SERVER_PORT;

// 외부 세션 스토리지를 사용하기 위한 설정
const MySQLStorage = expressMySQLSession(expressSession);
const sessionStore = new MySQLStorage({
    // 어떤 MySQL을 사용할지 설정
    user: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
    host: process.env.MYSQL_HOST,
    port: process.env.MYSQL_PORT,
    database: process.env.MYSQL_NAME,
    expiration: 1000 * 60 * 60 * 24,
    createDatabaseTable: true,
});

app.use(logMiddleware);
app.use(express.json());
app.use(cookieParser());
app.use(
    expressSession({
        secret: process.env.CUSTOMIZED_SECRET_KEY,
        resave: false,
        saveUninitialized: false,
        store: sessionStore,
        cookie: {
            maxAge: 1000 * 60 * 60 * 24,
        },
    }),
);

app.use('/api', [UsersRouter, PostsRouter, CommentsRouter]);
app.use(errorHandingMiddleware);

app.listen(SERVER_PORT, () => {
    console.log(SERVER_PORT, '포트로 서버가 열렸어요!');
});


📌 Tomorrow's Goal

✏️ 개인 과제 진행

  • 강의 시청 및 정리가 끝났기에 개인 과제 진행

  • 강의에서 진행한 프로젝트 기반으로 각종 미들웨어 및 API 구현하기



📌 Today's Goal I Done

✔️ 노드 숙련 강의 시청하기

  • 예비군으로 밀린 강의를 시청함

  • 원래는 2주차를 오늘 안에 다 시청하려고 했으나, 생각보다 내용이 어려웠음

  • 완전히 막힌다기 보다는 내용이 어려워서 검색하면서 이해하느라 진행이 느렸음

  • 주말을 이용해서 결국 강의 시청 완료함


profile
조금씩 정리하자!!!

0개의 댓글