본문 바로가기
개발자 도전기/[STUDY] JS || TS

ts-node | NodeBird | passport 설정

by 답수 2022. 4. 22.
728x90
SMALL

 

 

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

 

 

Passport

이제 로그인 인증 기능을 구현하기 위한 passport 미들웨어를 작성할 것이다. 먼저 back 폴더 내에 passport 폴더를 생성하고, 폴더 내에 index.ts를 만든다. 코드는 다음과 같이 작성한다.

// passport/index.ts

import * as passport from 'passport';
import User from '../models/user';      // 클래스는 그 자체로 타입으로 정의할 수도 있음
import local from './local';

export default () => {
    passport.serializeUser<User | number>((user, done) => {      // 로그인할 때 한 번 실행됨
        done(null, user.id);    // user의 id 정보를 메모리에 저장(user로 저장하면 용량 크기 때문)
    });

    passport.deserializeUser<User | number>(async(id, done) => {    // 모든 요청이 있을 때마다 실행됨
        try {
            const user = await User.findOne({
                where: { id },
            });
            if (!user) return done(new Error('No user!'));
            return done(null, user);    // req.user
        } catch (err) {
            console.error(err);
            return done(err);
        }
    });
}

 

serializeUser()

passport 모듈의 serializeUser메소드는 req.user에 담겨진 사용자의 정보를 세션에 저장하는 역할을 한다.

콜백함수의 두 매개변수 user와 done을 이용해서 사용자 정보를 세션에 저장하는데, 두 번째 매개변수는 done(null, user.id)과 같이 넘기면 된다. 즉 유저의 아이디만 세션에 저장하도록 적었다.

 

deserializeUser() 

이 메소드는 serializeUser에서 세션에 저장한 값을 이용해서 사용자 데이터를 일치하는지 확인하고 사용자 데이터를 HTTP Request에 "req.user"값으로 반환한다. deserializeUser는 브라우저에서 매번 요청이 있을때마다 실행되기 때문에 사용자 정보가 크다면 메모리에도 부담이 되고 성능면에서도 비효율적일 수 있다. 그래서 애초에 세션에 저장할 때 user.id 처럼 사용자의 아이디 값만 저장하는 것을 추천한다고 한다.

 

* passport의 흐름을 이해하는데 도움이 되었던 글

 

 

그런데, 문제는 serializeUser와 deserializeUser의 메소드 타입을 정의하는데 에러가 발생한다. 강사님이 커뮤니티를 통해 해결 방법을 올려주셨다.

https://www.inflearn.com/news/154101

 

back폴더 내 types 폴더를 생성 후, 아래와 같은 코드를 추가한다.

// back/types/index.d.ts

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

declare global {
    namespace Express {
        export interface User extends IUser {}
    }
}

 

declare 키워드는 컴파일러에게 해당 변수나 함수가 이미 존재한다는 것을 알리는 역할이다. 즉 이 파일을 만들어서 선언을 확장하는 것인데, 익스프레스 네임스페이스의 User 타입이 User 모델을 확장하도록 하면 문제가 해결된다.

 

이 부분이 잘 이해가 되지 않아서 더 구글링을 해봤다. 여러 글들을 참조해보면서 이 문제의 흐름을 더 이해하려고 해봤다. 먼저 passport 모듈의 타입이 선언되어 있는 파일을 확인해봤다.

경로는 node_modules/@types/passport/index.d.ts 이다.

import { IncomingMessage } from 'http';

declare global {
    namespace Express {
        // tslint:disable-next-line:no-empty-interface
        interface AuthInfo {}
        // tslint:disable-next-line:no-empty-interface
        interface User {}	// <-- req.user를 받으면 빈 인터페이스를 반환함

        interface Request {
            authInfo?: AuthInfo | undefined;
            user?: User | undefined;
            
            // ... 생략

 

즉, 우리가 models에서 작성한 User 클래스를 passport 모듈에 있는 User에 extends를 통해 확장하여 타입 형식 문제를 해결한 것이다!

 

 

여기까지 하고 나면 다음과 같이 실행이 된다.

 

 

솔직히 아직까지는 따라하면서 모르는 것들을 찾아보고 하나씩 이해하고 있는 수준이긴 하지만... 근래 공부하던 것들 중에서 제일 재밌다. 배우는 것도 많고 유익하고!! 이대로 더더 공부해서 빨리 나만의 프로젝트도 진행해봐야지. 얼른 강의 끝내고 많이 배우자!!!

728x90
LIST

댓글