본문 바로가기

Node.js

[Node.js] 계정관리 서버 프로젝트 3

 

오늘의 할 일

- 로그 관리

- 예외 처리

 

1. 로그관리

외부 로깅 라이브러리인 winston을 사용하기 위해 winston 설치

 

logger.js  작성

const winston = require("winston");

const logger = winston.createLogger({
  // 출력 형식 정의
  transports: [
    new winston.transports.Console(), // 콘솔 출력: 개발 중 디버깅에 사용됨
    new winston.transports.File({ filename: "app.log" }), // 파일 출력: 실제 운영 환경에서 로그 관리
  ],
  // 로그 형식 지정
  format: winston.format.combine(
    // combine을 사용하여 여러 형식을 조합할 것을 배열로 정의
    winston.format.timestamp(), //  시간 출력
    winston.format.colorize(), // 색상 출력
    winston.format.json(), // JSON 형식으로 출력
    winston.format.simple() // ${info.level}: ${info.message} 형식으로 출력
  ),
});

module.exports = logger;

 

로그파일 사용(세션모듈)

var UserRealm = require("../db/realm");
var logging = require("../util/logger"); // 로그 모듈

function session(req, res, next) {
  logging.info("사용자 인증 요청");

  // 헤더에서 이메일과 토큰 추출
  const email = req.headers.email;
  const token = req.headers.token;

  if (!email || !token) {
    logging.error("이메일과 토큰을 확인해주세요.");
    res.status(401).send({ message: "이메일과 토큰을 확인해주세요." });
    return 0;
  }

  try {
    // 이메일 && 토큰이 true인 사용자 찾기
    const user = UserRealm.objects("User").filtered(
      `email = "${email}" AND token = "${token}"`
    )[0];


    if (user && new Date() > user.tokenExp) {
      logging.error("토큰 만료");
      return 0;
    } else {
      logging.info("사용자 인증 성공", { user: user });
      // 토큰 만료시간
      const tokenExp = new Date();
      tokenExp.setDate(tokenExp.getDate() + 30);

      // realm에 토큰 만료시간 갱신
      UserRealm.write(() => {
        user.tokenExp = tokenExp;
      });
      return user;
    }
  } catch (error) {
    logging.error("사용자 인증 실패", error);
    return 0;
  }
}

module.exports = session;

 

로그 확인

 

winston에서 제공하는 timestamp 가 대한민국 기준이 아니라서 moment-timezone 라이브러리를 설치하여 형식을 바꿔줘야함

moment-timezone 설치

 

logger.js 파일 수정

const winston = require("winston");
const moment = require("moment-timezone");

moment.tz.setDefault("Asia/Seoul"); // 한국 시간으로 설정

const logger = winston.createLogger({
  // 출력 형식 정의
  transports: [
    new winston.transports.Console(), // 콘솔 출력: 개발 중 디버깅에 사용됨
    new winston.transports.File({ filename: "app.log" }), // 파일 출력: 실제 운영 환경에서 로그 관리
  ],
  // 로그 형식 지정
  format: winston.format.combine(
    // combine을 사용하여 여러 형식을 조합할 것을 배열로 정의
    winston.format.timestamp({
      format: moment().format("YYYY-MM-DD HH:mm:ss"), // 시간 형식 지정
    }), //  시간 출력
    winston.format.colorize(), // 색상 출력
    winston.format.json(), // JSON 형식으로 출력
    winston.format.simple() // ${info.level}: ${info.message} 형식으로 출력
  ),
});

module.exports = logger;

 

로그 확인

 

로그 Level

error 에러 메시지
warn 경고 메시지
info 정보 메시지
debug 디버그 메시지
verbose 자세한 메시지

 

 

2. 예외처리

try ~ catch 로 에러가 발생했을 때 error 그 자체를 로그에 나오게 되어 있었음

 

이 경우 가독성이 떨어지며 구체적인 에러 파악을 위한 시간을 많이 써야할 수 있음

아래와 같이 구체적인 상황마다 에러 발생 이유를 남겨주는 형태로 수정

/* POST 계정 추가. */
// todo: 이메일 중복체크
router.post("/", function (req, res, next) {
  logging.info("POST/ 계정 추가", { body: req.body });

  var email = req.body.email;
  var password = req.body.password;
  var name = req.body.name;
  var tel = req.body.tel;

  try {
    // 이메일 중복체크
    const existUser = account.findUserByEmail(email);
    if (existUser) {
      logging.error("이메일 중복", { email: email });
      res.status(409).send(response.fail(409, "이메일 중복")); // 409: Conflict
    } else {
      UserRealm.write(() => {
        UserRealm.create("User", {
          email: email,
          password: password,
          name: name,
          tel: tel,
          toeken: "default",
          date: new Date(),
          tokenExp: new Date(),
        });
      });
      res.status(201).send(response.success(201, "계정 추가 성공"));
    }
  } catch (error) {
    logging.error("계정 추가 실패", { error: error });
    res.status(500).send(response.fail(500, "계정 추가 실패"));
  }
});

 

그 결과 로그확인 시 가독성이 좋고, 어디서 에러가 발생했는지 한번에 파악할 수 있음