[새싹x코딩온] 웹 개발자 부트캠프 과정 | SoundSquad - 3) TypeScript 적응하기, 로깅 미들웨어 구현하기

고민하는 무민·2024년 9월 13일
post-thumbnail

SoundSquad-back Github : SoundSquad - back Repository
Development Document Notion : Development Document


TypeScript의 정적 타입 시스템

 이전 프로젝트에서 동적 타입 시스템에 의한 문제가 있었고, 다른 언어를 사용하는 경험을 해보고자 TypeScript를 활용하기로 했었다.

TypeScript 를 활용하면서 있었던 일 중에서 TypeScript 는 타입 안정성을 확보하기 위해서 각 API에 대응하는 라우트 핸들러 함수에서 DB와 작업하는 과정에서 생성되는 객체들의 타입을 지정해줘야 한다. 데이터를 입력하는 경우에는 누락된 데이터가 있는지를 확인할 수 있어 편리하지만, 데이터의 일부를 수정하는 경우 입력하는 객체에 대해서는 객체의 형식을 일일이 만들어줘야 했다.

그 객체의 형식을 따로 라우트마다 따로 관리했는데 그 중 사이트의 게시판 기능에 관한 부분을 예로 들어보면


import { Optional } from 'sequelize';

export interface CommunityAttributes {
  article_num: number;
  user_num: number;
  category: string;
  article_title: string;
  article_content: string;
  activate: boolean;
}

export interface CommunityCreationAttributes extends Optional<CommunityAttributes, 'article_num' > {}

export interface UpdateTargetComment { // 추후 분리 예정
  comment_content : string;
}

export interface DeleteTarget { // 추후 분리 예정
  activate:boolean;
}

export interface UpdateTargetCommunity { // 추후 분리 예정
  category: string;
  article_title: string;
  article_content: string;
}
  • 인터페이스 정의:
    CommunityAttributes, UpdateTargetComment, DeleteTarget, UpdateTargetCommunity 인터페이스를 정의하여 객체의 구조를 명시적으로 선언하고 있다.
  • 타입 확장:
    CommunityCreationAttributes는 CommunityAttributes를 확장하여 새로운 인터페이스를 만들어 사용한다.
  • 유틸리티 타입 사용:
    Sequelize의 Optional 유틸리티 타입을 사용하여 CommunityCreationAttributes를 정의하고 CommunityAttributes의 'article_num' 필드를 선택적(optional)으로 활용할 수 있게 한다.
  • 부분적 타입 정의 (Partial Type Definition):
    UpdateTargetComment, DeleteTarget, UpdateTargetCommunity는 일부 속성만을 포함하는 부분적 타입을 정의해 특정 작업에 필요한 필드만을 명시한다.

로깅 미들웨어 구현하기

console.time("Performance Time"); // 시작시간
{
	/// 함수 내용 
}    
console.timeEnd('Performance Time'); // 해당 부분에서 콘솔로 값을 리턴 받는다.

이전 리팩토링 프로젝트에서는 성능 측정을 위해서 함수마다 삽입해서 측정했는데, 더 편리한 방법을 사용해 보고자 한다. 이번에는 동료분들도 테스트를 하며 사용 데이터를 누적하고, 사이트 이용에 있어 어느 요청이 많이 사용되고, 시간이 오래걸리는지 찾아내어 개선작업 시 우선순위를 세워볼 계획이다.

express-winston

express-winston 은 HTTP 요청에 대해서 요청과 응답에 대한 상세 정보를 자동으로 기록할 수 있고 로그 형식 로그 레벨(error, info 와 같은) 출력 대상(콘솔, 파일 등) 등을 사용자가 원하는 대로 설정하기 편하다.

import express from 'express';
import expressWinston from 'express-winston';
import winston from 'winston';
import path from 'path';

const app = express();
const logDir = path.join(__dirname, '..', 'logs');

const formatTimestamp = () => {
  return new Date().toLocaleString('ko-KR', {
    timeZone: 'Asia/Seoul',
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false
  }).replace(/[/]/g, '-');
};

const jsonFileTransport = new winston.transports.File({
  filename: path.join(logDir, 'requests.log'),
  format: winston.format.combine(
    winston.format.timestamp({
      format: formatTimestamp
    }),
    winston.format.json()
  )
});

const consoleTransport = new winston.transports.Console({
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.timestamp({
      format: formatTimestamp
    }),
    winston.format.printf(({ timestamp, level, message, meta }) => {
      return `${timestamp} ${level}: ${message} ${JSON.stringify(meta)}`;
    })
  )
});

app.use(expressWinston.logger({
  transports: [
    jsonFileTransport,
    ...(process.env.NODE_ENV !== 'production' ? [consoleTransport] : [])
  ],
  meta: true,
  msg: "HTTP {{req.method}} {{req.url}}",
  expressFormat: true,
  colorize: false,
  ignoreRoute: function (req, res) { return false; },
  dynamicMeta: (req, res) => {
    return {
      timestamp: formatTimestamp()
    };
  }
}));

export default app;

formatTimestamp

로그에 표시되는 시간 형식을 우리나라에서 사용하는 시간 형식으로 바꾸는 함수이다.

jsonFileTransport

로그를 파일 형식으로 저장할 때, 사용한다.

consoleTransport

개발환경에서 로그를 콘솔에 표시할 필요가 있을때 사용한다. 배포 상황에서는 사용하지 않을 예정

profile
점심 뭐먹지

0개의 댓글