개발자 도전기/[STUDY] JS || TS

ts-node | NodeBird | 다양한 케이스를 위한 오버로딩

답수 2022. 5. 6. 15:59
728x90
반응형

 

 

※ 인프런 - Node.js에 TypeScript 적용하기(feat. NodeBire) by 조현영 강의를 기반으로 정리한 내용입니다.

 

오버로딩? 같은 매개변수를 여러 방법으로 설정하는 것

 

이전 글에서 local.ts 파일에 해당하는 타입 정의를 위해 직접 타이핑을 했었다. 그런데 만약 local 내의 코드를 수정해야 하는 경우라면 어떻게 해야 할까?

import * as passport from 'passport';
import * as bcrypt from 'bcrypt';
import { Strategy } from 'passport-local';

import User from '../models/user';

export default () => {
    passport.use('local', new Strategy({
        usernameField: 'userId',
        passwordField: 'password',
        session: false,     // 세션에 로그인 정보 저장 안할 때 false
        passReqToCallback: true,	// req 객체를 passport 인증 시 활용
    }, async (req, userId, password, done) => {
        try {
            const user = await User.findOne({ where: { userId } });
            if (!user) return done(null, false, { message: '존재하지 않는 사용자입니다!' });
            const result = await bcrypt.compare(password, user.password);
            if (result) return done(null, user);
            return done(null, false, { message: '비밀번호가 틀립니다.' });
        } catch (err) {
            console.error(err);
            return done(err);
        }
    }))
};

 

기존 코드에서 strategy 안에 session과 passReqToCallback 속성을 추가했다. passReqToCallback 속성을 true로 설정하게 되면 req객체를 passport 인증시 활용해야 하기 때문에 콜백함수에 req 매개변수도 추가되어야 한다. 여기서 문제가 발생한다.

 

    export interface IStrategyOptions {
        usernameField: string;
        passwordField: string;
        session?: boolean;
        passReqToCallback?: false;
    }

우리가 사용하는 인터페이스에서는 passReqToCallback 속성을 false로 했었다. 이를 true로 바꾸게 된다면 이로 인해 수정되는 모든 코드들을 다 수정해야 한다.

 

즉 속성의 옵션을 그때그때 다르게 사용하게 되는 경우 그때마다 작업해야 하는 요소가 생기게 되는 것이고, 이는 매우 비효율적인 방법이다. 이렇게 모듈들은 가변적인 매개변수를 받을 수 있고, 이런 다양한 케이스에 맞는 타이핑을 한다.

/**
 * 남들에게 배포하는 용은 아님. 이건 개발용
 * 배포할 때는 리덕스 따라가면서 알려드림..?
 * 모든 부분을 타이핑할 필요 전혀 없음. 내가 사용하는 코드 부분만 작성
 * local.ts 코드 보면서 적합한 타입 직접 정의하기
 */
declare module "passport-local" {       // delcare module은 실제 모듈의 이름과 같게 해야 에러나지 않음
    import { Request } from 'express';
    import { Strategy as PassportStrategy } from 'passport';

    // 인터페이스 이름은 실제 모듈과 이름을 같게 만듦
    export interface IVerifyOptions {       // export로 확장성 고려
        [key: string]: any;
    }
    export interface IStrategyOptions {
        usernameField: string;
        passwordField: string;
        session?: boolean;
        passReqToCallback?: false;
    }
    export interface IStrategyOptionsWithRequest {
        usernameField: string;
        passwordField: string;
        session?: boolean;
        passReqToCallback: true;
    }
    export interface Done {
        (error: any, user?: any, options?: IVerifyOptions): void;
    }
    export interface VerifyFunction {
        (username: string, password: string, done: Done): void | Promise<any>;
    }
    // req를 가장 앞 매개변수에 사용해야 하는 경우가 필요할 때, 오버로딩할 수 있는 인터페이스 생성
    export interface VerifyFunctionWithRequest {
        (req: Request, username: string, password: string, done: Done): void | Promise<any>;
    }

    export class Strategy extends PassportStrategy {
        // constructor(options: IStrategyOptions | IStrategyOptionsWithRequest, verify: VerifyFunction | VerifyFunctionWithRequest) // | 연산자 사용하면 에러 발생 가능함
        constructor(options: IStrategyOptions, verify: VerifyFunction)
        constructor(options: IStrategyOptionsWithRequest, verify: VerifyFunctionWithRequest)
    }
}

 

728x90
반응형
LIST