Home Js to Ts (passport)
Post
X

Js to Ts (passport)

디렉토리 구조

1
2
3
4
5
6
7
8
9
10
11
12
13
src/
├── types/
   └── model.d.ts
├── passports/
   ├── index.ts
   └── strategy/
       ├── local.strategy.ts
├── models/
   └── user.model.ts
├── utils/
   └── crypto.until.ts
├── tsconfig.json
└── app.ts

index.js

index.js파일을 index.ts 파일로 변환해보겠습니다.

확장자만 변경

1
2
3
4
5
6
7
import passport from "passport";
import localStrategy from "./strategy/local.strategy.js";

// 로컬 로그인
passport.use("local", localStrategy);

export default passport;

local.strategy.ts

local.strategy.js파일을 local.strategy.ts 파일로 변환해보겠습니다.

passport-local 라이브러리는 타입 정의 파일을 제공합니다.

  • 설치

    1
    
    npm i -D @types/passport-local
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// local.strategy.ts
import { User } from "types/model/user";
import { Strategy as LocalStrategy } from "passport-local";
import { userPasswordVerify } from "utils/crypto.util.js";
import userStorage from "models/user.model.js";

const localStrategy = new LocalStrategy(
  {
    usernameField: "id",
    passwordField: "password",
    session: false,
  },
  async (id, password, done) => {
    try {
      let user: User | null = await userStorage.getUserById(id);

      if (!user) {
        return done(null, false, {
          message: "가입되지 않은 회원입니다.",
        });
      }

      let checkedPassword = userPasswordVerify(password, user.password);

      if (checkedPassword) {
        return done(null, user.id);
      }

      done(null, false, {
        message: "비밀번호가 틀렸습니다",
      });
    } catch (err) {
      done(err);
    }
  }
);

export default localStrategy;

model.d.ts

1
2
3
4
5
6
7
8
9
10
11
12
// User 타입 정의
export interface User {
  user_id: number;
  id: string;
  password: string;
}

// UserInfo 타입 정의
export interface UserInfo {
  user_id: number;
  nickname: string; // 사용자 이름
}

crypto.util.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import crypto from "crypto";

// 암호화된 비밀번호 생성 함수
const createPasswordAndSalt = (userInputPassword: string): string => {
  try {
    const salt = crypto.randomBytes(32).toString("base64");

    // 매개변수 : 입력값, salt, 반복 횟수, Key의 길이, 해시알고리즘
    const key = crypto
      .pbkdf2Sync(userInputPassword, salt, 200, 8, "sha512")
      .toString("base64");

    return `${key}$${salt}`;
    // 암호화된 비밀번호와 임의로 생성된 salt를 '$'로 구분
  } catch (err) {
    throw err;
  }
};

// 비밀번호 검증 함수
// 사용자의 입력 값과 데이터베이스에 저장된 암호화된 값을 비교해 검증합니다.
const userPasswordVerify = (
  userInputPassword: string,
  storedPasswordAndSalt: string
): boolean => {
  try {
    const [encrypted, salt] = storedPasswordAndSalt.split("$");

    const userInputEncrypted = crypto
      .pbkdf2Sync(userInputPassword, salt, 200, 8, "sha512")
      .toString("base64");

    if (userInputEncrypted === encrypted) {
      return true;
    } else {
      return false;
    }
  } catch (err) {
    throw err;
  }
};

export { createPasswordAndSalt, userPasswordVerify };

user.model.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import pool from "config/dbPool.js";
import { RowDataPacket } from "mysql2";
import { User, UserInfo } from "types/model";

class userStorage {
  static async getUserById(id: string): Promise<User | null> {
    const conn = await pool.getConnection();

    try {
      const [rows] = await conn.query<RowDataPacket[]>(
        "SELECT * FROM user WHERE id = ?",
        [id]
      );

      console.log(rows);

      if (rows.length === 0) return null;

      return rows[0] as User;
    } catch (err) {
      throw err;
    } finally {
      conn.release();
    }
  }

  static async getUserInfo(userId: number): Promise<UserInfo | null> {
    const conn = await pool.getConnection();

    try {
      const [rows] = await conn.query<RowDataPacket[]>(
        "SELECT * FROM userInfo WHERE user_id = ?",
        [userId]
      );

      if (rows.length === 0) return null;

      return rows[0] as UserInfo;
    } catch (err) {
      throw err;
    } finally {
      conn.release();
    }
  }
  static async createUser(userInfo: {
    id: string;
    password: string;
    nickname: string;
  }): Promise<void> {
    const conn = await pool.getConnection();

    try {
      await conn.beginTransaction();

      const [rows] = await conn.query<RowDataPacket[]>(
        "INSERT INTO user (id, password) VALUES (?, ?)",
        [userInfo.id, userInfo.password]
      );

      await conn.query(
        "INSERT INTO userInfo (user_id, nickname) VALUES (?, ?)",
        [rows[0].insertId, userInfo.nickname]
      );

      await conn.commit();

      return;
    } catch (err) {
      await conn.rollback();

      throw err;
    } finally {
      conn.release();
    }
  }
}

export default userStorage;

파일 하나씩 ts 파일로 변환하면서 모듈에서 타입 정의 파일을 제공하지 않는다면 따로 정의합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.