express-mysql -> user && 기본 세팅

smart Seo (Seo smart)·2023년 12월 8일
0

템플릿 프로젝트

목록 보기
14/18

Request 인터페이스를 확장하여 user 필드를 추가하는 역할

import {} from "../../config";

// Express의 Request 객체를 글로벌 네임스페이스에서 확장
declare global {
  namespace Express {
    // Express의 Request 인터페이스 확장
    export interface Request {
      user?: User; // Request 객체에 user 필드 추가 (선택적)
    }
  }
}

초기 config 설정

import dotenv from "dotenv";
// 기본적으로 NODE_ENV 환경변수를 'development'로 설정
process.env.NODE_ENV = process.env.NODE_ENV || "development";

// .env 파일에서 환경변수 로드
const envFound = dotenv.config();
// .env 파일을 찾지 못할 경우 에러 발생
if (envFound.error) throw new Error("⚠️  Couldn't find .env file  ⚠️");

export default {
  port: parseInt(process.env.PORT!, 10), // 서버 포트 번호 (환경변수에서 불러옴)
  jwtSecret: process.env.JWT_SECRET!, // JWT 비밀키 (환경변수에서 불러옴)
  logs: {
    level: process.env.LOG_LEVEL || "silly", // 로깅 레벨 (환경변수에서 불러옴, 기본값 'silly')
  },
  api: {
    prefix: "/api", // API 경로 접두사
  },
};

jwt config 설정

// jsonwebtoken 모듈을 import 합니다.
import jwt from "jsonwebtoken";

// 비밀 키를 설정합니다. 이 키는 토큰을 생성하고 검증할 때 사용됩니다.
const secretKey = "your-secret-key";

// 페이로드를 받아서 JWT 토큰을 생성하는 함수입니다.
export const generateToken = (payload: any): string => {
    // jwt.sign 메소드를 사용하여 토큰을 생성합니다.
    const token = jwt.sign(payload, secretKey);
    // 생성된 토큰을 반환합니다.
    return token;
};

// 토큰을 받아서 검증하고, 페이로드를 반환하는 함수입니다.
export const verifyToken = (token: string): any => {
    // jwt.verify 메소드를 사용하여 토큰을 검증하고, 페이로드를 디코딩합니다.
    const decoded = jwt.verify(token, secretKey);
    // 디코딩된 페이로드를 반환합니다.
    return decoded;
};

passport config 설정

import passport from "passport";
import User from "../models/userModel";
import passportLocal from "passport-local";
import passportJwt from "passport-jwt";
import argon2 from "argon2";
import dotenv from "dotenv";
dotenv.config();

export default () => {
  // JWT 전략과 Local 전략을 Passport에 설정
  const JwtStrategy = passportJwt.Strategy;
  const ExtractJwt = passportJwt.ExtractJwt;
  const LocalStrategy = passportLocal.Strategy;

  // 로컬 인증 전략 사용 설정
  passport.use(
    new LocalStrategy(
      {
        usernameField: "email", // 이메일 필드 명시
        passwordField: "password", // 비밀번호 필드 명시
        session: false, // 세션 사용 안함
        passReqToCallback: true, // 콜백 함수에 req 객체 전달
      },
      async (req, email, password, done) => {
        try {
          // 이메일을 사용하여 사용자 검색
          const user = await User.findOne({ where: { email } });
          if (!user)
            return done(null, false, { message: "존재하지 않는 이메일입니다" });
          // 비밀번호 검증
          if (!(await argon2.verify(user.password, password)))
            return done(null, false, { message: "비밀번호가 틀립니다" });
          done(null, user); // 검증 성공 시 사용자 정보 전달
        } catch (err) {
          done(err);
        }
      }
    )
  );

  // JWT 인증 전략 사용 설정
  passport.use(
    new JwtStrategy(
      {
        jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), // Bearer 토큰으로부터 JWT 추출
        secretOrKey: process.env.JWT_SECRET!, // JWT 비밀키
      },
      async (payload, done) => {
        try {
          // JWT 페이로드를 사용하여 사용자 검색
          const user = await User.findOne({ where: { id: payload.id } });
          if (!user) return done(null, false);
          done(null, user); // 검증 성공 시 사용자 정보 전달
        } catch (err) {
          done(err, false);
        }
      }
    )
  );

  // 세션을 사용할 때만 필요한 코드 (현재 주석 처리됨)
  // passport.serializeUser<User, any>((user, done) => {
  //     done(null, user.email);
  // });
  // passport.deserializeUser<User, any>(async (email, done) => {
  //     try {
  //         const user = await User.findOne({ where: { email } });
  //         if (!user) return done(new Error('is not exists'));
  //         done(null, user!);
  //     } catch (err) {
  //         done(err);
  //     }
  // });
};

model 추가

import { Model, DataTypes } from "sequelize";
import { sequelize } from "./sequelize";

// User 모델 클래스 정의
class User extends Model {
  public readonly id!: number; // 사용자 ID (자동 생성)
  public email!: string; // 사용자 이메일
  public password!: string; // 사용자 비밀번호
  public name!: string; // 사용자 이름
  public nickname!: string; // 사용자 닉네임
  public phone?: string; // 사용자 전화번호 (선택적)
  // createdAt과 updatedAt은 Sequelize에서 자동으로 관리됨
}

// User 모델 초기화 및 테이블 구조 정의
User.init(
  {
    email: { type: DataTypes.STRING, allowNull: false, unique: true }, // 이메일 필드 (유일하며, 필수)
    name: { type: DataTypes.STRING(20), allowNull: false }, // 이름 필드 (20자 제한, 필수)
    nickname: { type: DataTypes.STRING(20), allowNull: false, unique: true }, // 닉네임 필드 (20자 제한, 유일하며, 필수)
    password: { type: DataTypes.STRING, allowNull: false }, // 비밀번호 필드 (필수)
    phone: { type: DataTypes.STRING, allowNull: true }, // 전화번호 필드 (선택적)
  },
  {
    sequelize, // sequelize 인스턴스
    modelName: "User", // 모델 이름
    tableName: "user", // 테이블 이름
    charset: "utf8", // 문자셋 설정
    collate: "utf8_general_ci", // 정렬 규칙 설정
  }
);

// 관계 설정 (추후 정의할 수 있음)
// export const associate = (db)=>{}
export default User;
import { Sequelize, Options } from "sequelize";
import databaseConfig from "../config/databaseConfig";
import dotenv from "dotenv";

dotenv.config(); // .env 파일에서 환경변수를 불러옴

// 현재 환경(NODE_ENV)에 따라 데이터베이스 환경 설정 선택
const env =
  (process.env.NODE_ENV as "production" | "test" | "development") ||
  "development";
const { database, username, password } = databaseConfig[env];
console.log(username, password, database, 1111);
console.log(databaseConfig[env]);
// console.log(process.env);
// Sequelize 인스턴스 생성
const sequelize = new Sequelize(database, username, password, {
  ...(databaseConfig[env] as Options),
});

// 순환 참조를 방지하기 위해 sequelize 인스턴스 내보내기
export { sequelize };
export default sequelize;
import User from "./userModel"; // User 모델 가져오기
export * from "./sequelize"; // sequelize 관련 설정 및 유틸리티 내보내기

// db 객체에 User 모델 포함
const db = { User };

// db 객체의 타입을 정의하는 dbType 타입
export type dbType = typeof db;
import { sequelize } from '../models';

// 데이터베이스 연결을 시도하는 함수
async function waitForDatabase() {
    try {
        await sequelize.authenticate();
        logger.info(
            '🤩 Database connection has been established successfully.'
        );
        return true;
    } catch (error) {
        logger.error('데이터베이스 연결 실패, 재시도 중...');
        return false;
    }
}

// Express 애플리케이션을 초기화하는 함수
export default async ({ expressApp }: { expressApp: Application }) => {
    try {
        // 데이터베이스가 준비될 때까지 기다림
        while (!(await waitForDatabase())) {
            await new Promise(resolve => setTimeout(resolve, 5000)); // 5초 후 재시도
        }

        // Express 로더를 통해 필요한 미들웨어 및 라우트 설정
        await expressLoader({ app: expressApp });
        logger.info('🤩 express loaded'); // Express 로딩 성공 로그


profile
꾸준히 발전하는 풀스택 개발자!!

0개의 댓글