TypeScript 에서의 req.user

passport.jsnode.js에서 인증처리를 보다 편하기 하기위한 middleware입니다. 다양한 strategy중에 passport-local을 이용하면 쿠키-세션 방식으로 usernamepassword를 통해 인증을 할 수 있습니다.

WorkFlow

workflow

위 WorkFlow의 과정으로 req.user를 통해 인증된 사용자의 객체를 사용할 수 있게 됩니다. 하지만 TypeScript에서는 문제가 발생합니다.

TypeScript에서의 문제

TypeScript에서는 req.user객체의 property에 접근하고자 할때 TypeScript는 타입문제를 제기합니다. 따라서 일련의 타입을 정의하는 과정을 필요로 합니다.

문제 발생 이유

@types/passport 타입 정의

index.d.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { IncomingMessage } from 'http';

declare global {
namespace Express {
interface AuthInfo {}
interface User {} // <- User 타입 정의 부분 (Empty)

interface Request {
​ authInfo?: AuthInfo;
​ user?: User; // <- user의 타입 = User

login(user: User, done: (err: any) => void): void;

​ ...

logout(): void;
logOut(): void;
isAuthenticated(): boolean;
isUnauthenticated(): boolean;
​ }
}
}

이것은 @types/passport에 정의된 타입 선언 입니다. 우리가 위에서처럼 req.user객체의 property에 접근하려 할때 TypeScript가 타입문제가 있다고 주장하는 이유는 여기에 있습니다. Request에서 req.user는 빈 User 인터페이스로 정의되어 있기 때문에 TypeScriptreq.user는 비어있다고 여기게 됩니다. 이러한 이유때문에 문제가 생기는 것입니다.

문제 해결 방법

User 타입 재정의

이 문제의 솔루션은 간단히 User타입을 재정의 해주는 것입니다. @types/index.d.ts를 생성한후 다음과 같이 작성합니다. sequelize를 사용하던 TypeOrm을 사용하던 상관 없습니다. 실제 사용하고자 하는 UserModel클래스를 불러와 상속시켜주면 끝입니다. 물론 typeRoots설정을 해주어야 합니다. 이 설정은 검색하면 관련 자료가 많이 나오니 생략하도록 하겠습니다.

@types/index.d.ts
1
2
3
4
5
6
7
import UserModel from '../models/user'; // <- User class

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

req.user의 타입은 User임으로, 위와 같이 정의하면 Uesr가 상속받은 classproperty에 접근할 수 있게 됩니다.