dh_0e

[Node.js] 강의 내용 개념 정리(8) (Prisma Transaction, express-session) 본문

내일배움캠프/Node.js[숙련]

[Node.js] 강의 내용 개념 정리(8) (Prisma Transaction, express-session)

dh_0e 2024. 5. 28. 20:50

Prisma Transaction

  • 여러 개의 쿼리를 하나의 트랜잭션으로 수행할 수 있는 Sequential 트랜잭션,
  • Prisma가 자체적으로 트랜잭션의 성공과 실패를 관리하는 Interactive 트랜잭션이 존재함

 

Sequential 트랜잭션

  • Prisma의 여러 쿼리를 배열([ ])로 전달받아, 각 쿼리들을 순서대로 실행하는 특징이 있어 여러 작업이 순차적으로 실행되어야 할 때 사용 가능
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// Sequential 트랜잭션은 순차적으로 실행됩니다.
// 결과값은 각 쿼리의 순서대로 배열에 담겨 반환됩니다.
const [posts, comments] = await prisma.$transaction([
  prisma.posts.findMany(),
  prisma.comments.findMany(),
]);

 

  • Raw Query에서도 사용 가능
더보기

Sequential Transaction, Raw Query

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// Sequential 트랜잭션은 순차적으로 실행됩니다.
// Raw Quyery를 이용하여, 트랜잭션을 실행할 수 있습니다.
const [users, userInfos] = await prisma.$transaction([
  prisma.$queryRaw`SELECT * FROM Users`,
  prisma.$queryRaw`SELECT * FROM UserInfos`,
]);

 

 

Interactive 트랜잭션

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// Prisma의 Interactive 트랜잭션을 실행합니다.
const result = await prisma.$transaction(async (tx) => { // tx를 prisma처럼 사용
  // 트랜잭션 내에서 사용자를 생성합니다.
  const user = await tx.users.create({
    data: {
      email: 'testuser@gmail.com',
      password: 'aaaa4321',
    },
  });

  // 에러가 발생하여, 트랜잭션 내에서 실행된 모든 쿼리가 롤백됩니다.
  throw new Error('트랜잭션 실패!');
  return user;
});
  • 비즈니스 로직이 성공적으로 완료되면 자체적으로 COMMIT 실행
  • 에러가 발생했을 때 자체적으로 ROLLBACK을 실행하여 트랜잭션을 관리
  • 트랜잭션 진행 중에도 비즈니스 로직을 처리할 수 있어 복잡한 쿼리 시나리오를 효과적으로 구현할 수 있음
  • (자유도가 높음)

 

Prisma에서 격리 수준을 설정하는 방법

  • isolationLevel 옵션을 정의함으로써 설정할 수 있음
import { Prisma } from '@prisma/client';

await prisma.$transaction(
  async (tx) => { ... },
  {
    isolationLevel: Prisma.TransactionIsolationLevel.ReadCommitted,
  },
);

 

 

UUID(범용 공유 식별자)

UUID(Universally Unique Identifier, 범용 고유 식별자)는 총 4개의 정보를 하이픈(-)으로 구분하여 순차적으로 저장한 데이터 타입이며 시간 정보를 포함하고 있어 생성된 순서대로 정렬이 되는 특징을 가지고 있음

 

 

express-session

  • Express.js에서 세션(Session) 기능을 쉽게 구현하기 위한 미들웨어

Cookie-Session 흐름 출처: https://www.wisecleaner.com/think-tank

  • 위와 같은 복잡한 Cookie-Session 흐름미들웨어 하나로 간단하게 구현할 수 있게 해줌

 

express-session 미들웨어의 구성 요소

  • express-session은 아래와 같이 전역 미들웨어로 등록됨
app.use(
  expressSession({
    secret: 'express-session-secret-key.', // 세션을 암호화하는 비밀 키를 설정
    resave: false, // 클라이언트의 요청이 올 때마다 세션을 새롭게 저장할 지 설정, 변경사항이 없어도 다시 저장
    saveUninitialized: false, // 세션이 초기화되지 않았을 때 세션을 저장할 지 설정
    cookie: {
      // 세션 쿠키 설정
      maxAge: 1000 * 60 * 60 * 24, // 쿠키의 만료 기간을 1일로 설정합니다.
    },
  }),
);
  • secret
    • 세션 ID를 암호화하기 위한 비밀 키
    • 클라이언트에게 발급할 세션 ID를 암호화하기 위한 비밀 키 정보
  • resave
    • 클라이언트의 요청(Request)이 들어올 때마다 세션 정보를 다시 저장할 지 설정함
    • 변경사항이 없더라도 true 로 설정하면, 매번 새로운 세션에 저장됨
  • saveUninitialized
    • req.session에 아무런 정보가 저장이 되지 않더라도 사용자에게 세션 ID를 발급할 지 설정함
    • true 로 설정하면, 서버에 접속하는 모든 사용자에게 세션 ID가 발급됨
  • cookie.maxAge
    • 세션 ID가 저장된 클라이언트의 쿠키 만료 기간을 설정

 

POST /sessions API 만들기

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

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

  return res.status(200).json({ message: '세션을 설정했습니다.' });
});
  • req.session클라이언트의 세션 정보를 관리하는데 사용되는 객체
  • 클라이언트의 요청이 들어오면 req.session.<property 명>에 원하는 정보 저장

 

GET /sessions API 만들기

/** 세션 조회 API **/
app.get('/sessions', (req, res, next) => {
  return res.status(200).json({
    message: '세션을 조회했습니다.',
    session: req.session.userId ?? null, // 세션에 저장된 usrId를 조회합니다.
  });
});
  • express-session은 클라이언트가 전달한 쿠키의 세션 ID를 바탕으로 req.session에서 정보를 조회
  • 클라이언트가 제공한 세션 ID가 없을 경우 Null 반환
- 기존 JWT를 이용한 쿠키를 클라이언트에게 전달하는 것보다 더욱 편리하게 구현할 수 있다는 장점이 있음
- 서버가 종료되면 express-session의 정보 또한 사라지는 인 메모리(In-Memory)방식이라는 단점이 있음

 

express-mysql-session

  • express-session의 세션 정보를 MySQL에 저장할 수 있도록 도와주는 모듈
  • express-session의 인 메모리 방식으로 인한 단점을 해결하기 위해 사용됨
import expressSession from "express-session";
import expressMySQLSession from "express-mysql-session";

const MySQLStorage = expressMySQLSession(expressSession);
const sessionStore = new MySQLStorage({
  // 우리가 어떤 MySQL을 사용할 건지
  user: "root",
  password: "PW 입력",
  host: "DB의 엔드 포인트 입력",
  port: 3306,
  database: "notice_board",
  expiration: 1000 * 60 * 60 * 24,
  createDatabaseTable: true,
});

app.use(
  expressSession({
    secret: "customized_secret_key",
    resave: false,
    saveUninitialized: false,
    store: sessionStore,
    cookie: {
      maxAge: 1000 * 60 * 60 * 24, // 1일 동안 사용 가능
    },
  })
);
  • express-mysql-session 또한 세션 ID로 정보를 조회할 때마다 MySQL의 조회 쿼리를 매번 실행한다는 문제점이 존재
  • 이에 대한 해결방법은 다시 JWT 쿠키를 이용하는 것 or MySQL 대신 외부 세션 스토리지를 캐시 메모리 데이터베이스인 Redis로 변경하는 것

 

dotenv

Node.js를 위한 환경 변수 관리 모듈로 .env파일을 통해 환경 변수를 process.env에 추가하는 역할을 함
  • 라이브러리 설치
yarn add -D dotenv

 

  • 라이브러리 적용
import dotenv from 'dotenv';

// .env 파일을 읽어서 process.env에 추가
dotenv.config();
  • 이후 process.env.KEY로 사용 (.env 파일엔 KEY="VALUE" 형식으로 저장)