dh_0e

[Node.js] 강의 내용 개념 정리(7) (로그 미들웨어, Transaction) 본문

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

[Node.js] 강의 내용 개념 정리(7) (로그 미들웨어, Transaction)

dh_0e 2024. 5. 28. 19:58

로그 미들웨어(Log Middleware)

  • 클라이언트의 모든 요청 사항을 기록하여 서버의 상태를 모니터링하기 위한 미들에어
  • 어플리케이션을 모니터링하고 문제가 발생할 때 빠르게 진단할 수 있음
  • 로그 데이터는 사용자의 행동을 분석하는 등 데이터 분석 작업에도 활용 가능함
  • 모든 로그를 일일이 확인하는 것은 불가능하며 morgan, winston 등의 라이브러리나 DATADOG, Grafana, AWS CloudWatch 등을 사용하여 로그를 수집하거나 관리할 수 있음

 

winston 사용 방법

  • 라이브러리 설치
yarn add winston

 

  • winston 라이브러리의 format 메소드를 사용하여 로그 포맷
  • winston 라이브러리의 transports 메소드를 사용하여 로그 콘솔에 출력
// src/middlewares/log.middleware.js

import winston from "winston";

const logger = winston.createLogger({
  level: "info", // 로그 레벨을 'info'로 설정합니다.
  format: winston.format.json(), // 로그 포맷을 JSON 형식으로 설정합니다.
  transports: [
    new winston.transports.Console(), // 로그를 콘솔에 출력합니다.
  ],
});

export default function (req, res, next) {
  // 클라이언트의 요청이 시작된 시간을 기록합니다.
  const start = new Date().getTime();

  // 응답이 완료되면 로그를 기록합니다.
  res.on("finish", () => {
    const duration = new Date().getTime() - start;
    logger.info(
      `Method: ${req.method}, URL: ${req.url}, Status: ${res.statusCode}, Duration: ${duration}ms`
    );
  });

  next();
}

 

  • log.middleware.js 미들웨어 등록
// src/app.js
import logMiddleware from "./middleware/log.middleware.js";

app.use(logMiddleware);

 

 

트랜잭션(Transaction)

  • 특정한 작업을 전부 처리하거나, 전부 실패하게 만들어 데이터의 일관성을 보장해주는 기능
  • 작업의 완전성을 보장해주기 위해 사용되는 개념
  • 작업의 단위를 하나의 쿼리에 종속하는 것이 아닌, 여러 개의 작업(쿼리)을 묶어 하나의 작업 단위로 그룹화하여 처리하는 작업을 뜻함
  • MySQL, AWS DynamoDB, MongoDB, CockroachDB 등 다양한 데이터베이스에서도 확인할 수 있음

 

트랜잭션을 사용해야하는 이유

ex)

더보기

여러분들은 트랜잭션을 이용해 다양한 문제 상황들을 해결할 수 있게됩니다.

예를들어 은행에서 계좌이체를 하게 될 경우 아래와 같은 상황이 발생하게 됩니다.

1️⃣  A 고객의 계좌에서 1,000원을 차감합니다.

2️⃣  B 고객의 계좌에 1,000원을 추가합니다.

여기서 1️⃣ 번 작업 이후 2️⃣ 번 작업을 수행하던 중 에러가 발생하게 될 경우 A 고객의 계좌에서 1,000원만 차감되기만 하는 문제점이 발생하게됩니다. 만약 순서가 반대로 되었다면, B 고객의 계좌에 1,000원이 증가되기만 하는 문제가 발생하겠죠? 🙂

이런 부분 업데이트(Partial Update)와 같은 상황을 방지하기 위해 트랜잭션(Transaction)이라는 개념을 도입하게 되었습니다.

  • 위와 같은 상황 외에도 로깅 작업, 영화 예매 시스템, 비행기 좌석 예매데이터의 일관성을 유지해야하는 상황에서 사용됨
  • 사용자가 항상 어플리케이션 실행을 완료하도록 구성할 수 있게되고, 치명적인 오류가 발생하더라도 DB에 피해가 가지 않아 안전하게 어플리케이션을 구성할 수 있음

 

트랜잭션의 특징(ACID)

  • 데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 특징 4가지를 나열한 개념

출처: https://www.databricks.com/kr/glossary/acid-transactions

 

원자성(Atomicity)

  • 나눠질 수 없는 단일 작업을 의미
  • 트랜잭션 내에서 실행되는 명령들을 하나의 묶음으로 처리하여, 내부에서 실행된 명령들이 전부 성공하거나 전부 실패해야한다는 특징
  • 가장 대표적인 트랜잭션의 특징으로 여러개의 작업들을 묶어 하나의 작업단위로 보게하는 특징

 

일관성(Consistency)

  • 트랜잭션 내부에서 처리되는 데이터의 일관성을 유지해야한다는 특징
  • 작업이 실패하더라도 실패한 상태로 데이터를 방치하지 않고 ROLLBACK이 실행되어 트랜잭션 시작 전 상태로 복구됨
  • 트랜잭션 내의 데이터는 일관되어야 하며, 에러가 발생하더라도 데이터의 상태가 일관성을 유지하게 해줌

 

격리성(Isolation)

  • 트랜잭션이 실행 중인 경우 다른 트랜잭션에 의해 데이터가 변경되는 것을 방지하는 특징
  • 트랜잭션의 중간 과정이나 중간 결과외부에서 참조할 수 없도록 하는 특징
  • MySQL의 경우 사용중인 DB 오브젝트에 락(Lock)을 걸어 격리성을 구현
  • 락(Lock) 상태는 또 다른 클라이언트가 해당 DB 오브젝트를 읽거나, 사용할 수 없도록 방지하여 데이터 무결성을 보장함
  • 동시성 격리 수준이라는 두 가지 중요한 개념이 나타남
동시성 - 여러 클라이언트가 동시에 하나의 데이터를 사용 및 공유하는 것으로 다수의 사용자가 동일한 시스템을 공유하면서 발생하는 동시 접근 문제를 해결해야 함

동시성 문제 - 여러 클라이언트가 동시에 같은 데이터를 접근하려고 할 때 발생하는 문제

 

 

지속성(Durability)

  • 트랜잭션이 성공적으로 커밋된 후, 해당 트랜잭션에 의해 생성 or 수정된 데이터가 어떠한 상황에서도 보존되는 특징
  • 트랜잭션이 완료되면 데이터는 데이터베이스에 영구적으로 저장하며 시스템에 어떠한 문제가 생기더라도 손상되지 않음
  • 트랜잭션의 안정성을 보장하며, 데이터 손실 없이 시스템의 안정성을 유지하는데 중요한 역할을 담당함 

 

락(Lock)

  • 동시성을 제어하기 위해 사용하는 기능
  • 해당하는 데이터를 점유하여 다른 트랜잭션의 접근을 막아 동시성일관성의 균형을 맞춤

 

락(lock)의 종류

 

공유락(Shared Locks) | 읽기 락(READ Locks)

  • 다른 트랜잭션이 읽는 것은 허용하지만 수정하는 것을 금지
  • READ 전용 락이라 불리기도 하며, 해당 락을 사용하는 트랜잭션이 끝나면 공유 락이 해제
# 트랜잭션을 시작합니다.
START TRANSACTION;

# SPARTA 테이블을 조회할 때, 해당 데이터들에 공유 락을 설정합니다.
SELECT * FROM SPARTA LOCK IN SHARE MODE;

 

배타 락(Exclusive Locks) | 쓰기 락(WRITE Locks)

  • 다른 트랜잭션이 데이터를 읽거나 수정하는 것을 금지
  • WRITE 전용 락이라 불리며, 트랜잭션이 해당하는 데이터를 점유한 후 다른 트랜잭션이 데이터에 접근 할 수 없음
# 트랜잭션을 시작합니다.
START TRANSACTION;

# SPARTA 테이블을 조회할 때, 해당 데이터들에 배타 락을 설정합니다.
SELECT * FROM SPARTA FOR UPDATE;

 

 

락킹 수준(Locking Level)

 

글로벌 락(Global Locks) | 데이터베이스 락(Database Locks)

  • 데이터베이스의 모든 테이블에 락을 걸어 현재 트랜잭션을 제외한 나머지 트랜잭션들이 모든 테이블을 사용하지 못하게 만듬
  • 가장 높은 수준의 락을 가지며, 가장 큰 범위를 가짐
# 글로벌 락을 획득합니다.
# MySQL 서버에 존재하는 모든 테이블에 락을 겁니다.
FLUSH TABLES WITH READ LOCK;

 

테이블 락(Table Locks)

  • 다른 사용자가 작업중인 테이블을 동시에 수정하지 못하도록 함, 잘 사용하지 않음
# SPARTA 테이블에 테이블 락을 설정합니다.
LOCK TABLES SPARTA READ;

 

네임드 락(Named Locks)

  • 테이블, 테이블의 행 등의 DB 오브젝트가 아닌, 특정한 문자열을 점유
# sparta_name 문자열을 획득합니다.
# 만약, 10초 동안 획득 하지 못한다면, NULL을 반환합니다.
SELECT GET_LOCK('sparta_name', 10);

 

메타데이터 락(Meatadata Locks)

  • 작업중인 테이블의 동일한 행동일한 데이터베이스의 객체동시에 수정하지 못하도록 점유함
  • 가장 많이 사용하는 락(Lock) 종류
# 테이블 구조를 변경할 때, MySQL은 내부적으로 메타데이터 락을 설정합니다.
ALTER TABLE SPARTA ADD COLUMN Age Int;

 

  • 잘못된 락 설정을 하게 될 경우 모든 API가 동작하지 않는 교착 상태(Dead Lock)이 발생하여 프로그램이 멈춤
교착 상태(Dead Lock)
- 여러 테이블에 락(Lock)을 적용하여, 다른 작업이 처리되지 못하게 점유하고 있을 때, 다른 작업이 끝나는 것을 무한정 기다리는 것을 나타냄

 

출처: https://www.sqlshack.com/understanding-the-deadlock-definition-in-sql-server/

 

 

트랜잭션의 격리 수준(Isolation Level)

  • 다른 트랜잭션에서 변경 및 조회하는 데이터를 읽을 수 있도록 허용하거나 거부하는 것을 결정하기 위해 사용
  • '데이터의 일관성'과 '동시성 처리 성능' 사이에서 균형을 잘 잡는게 중요함
READ UNCOMMITTED
커밋 되지 않은 읽기(Uncommitted Read)를 허용하는 격리 수준
가장 낮은 수준의 격리수준이며, 락을 걸지 않아 동시성이 높지만 일관성이 쉽게 깨질 수 있음

READ COMMITTED
커밋 된 읽기(Committed Read)만을 허용하고, SELECT 문을 실행할 때 공유락을 거는 격리 수준
다른 트랜잭션이 데이터를 수정하고 있는 중에는 데이터를 읽을 수 없어 커밋되지 않은 읽기현상이 발생하지 않음

REPEATABLE READ
읽기를 마치더라도 공유락을 풀지 않으며, 트랜잭션이 완전히 종료될 때까지 락을 유지
공유락이 걸린 상태에서 데이터를 수정하는 것은 불가능하지만, 데이터를 삽입하는 것이 가능해지므로 팬텀 읽기가 발생할 수 있는 문제점이 있음

SERIALIZABLE
데이터를 읽는 동안 다른 트랜잭션이 해당 데이터를 읽거나 삽입할 수 없고, 새로운 데이터를 추가하는 것 또한 불가능한 격리 수준
가장 높은 수준의 격리 수준이므로, 동시성이 떨어지는 문제점이 존재함

 

커밋 되지 않은 읽기(Uncommitted Read)

  • 다른 트랜잭션에 의해 작업중인 데이터를 읽게 되는 것으로 변경 전의 의도치 않은 데이터를 참조하게 되어 데이터의 일관성이 깨지게 되는 상황이 발생하게 됨

팬텀 읽기

  • 트랜잭션 수행 중 다른 트랜잭션에 의해 삭제된 데이터를 팬텀행(Phantom Rows)라 부르며 이에 해당하는 데이터를 읽는 것이 팬텀 읽기(Phantom Read)라고 부름