인프런 커뮤니티 질문&답변

G General님의 프로필 이미지
G General

작성한 질문수

탄탄한 백엔드 NestJS, 기초부터 심화까지

Multer와 미디어 파일 서비스 (mp3, mp4, img 등)

CurrentUser관련 질문 드립니다

작성

·

334

1

- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요!
- 먼저 유사한 질문이 있었는지 검색해보세요.
- 서로 예의를 지키며 존중하는 문화를 만들어가요.
- 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.

uploadCatImg(@CurrentUser() cat: Cat)

관련해서 궁금한게 있는데, 여기 Cat은 Schema에서 임포트 하는데요.

1) jwt.strategy.ts에서 작성한 validate 함수에서 findCatByIdWithoutPassword의 리턴값인 cat을 request.user에 삽입, 즉 @CurrentUser는 실질적으로 {email: string, name: string} 타입 상태.

2) @CurrentUser() cat: Cat, 여기서 Cat은 schema에서 정의한 잡다한 값들이 들어간 상태

일단 오류가 안 나는 걸 보면 Cat이 {email:string, name: string}보다 범위가 더 크기 때문인 것 같은데, ParamDecorator의 타이핑 표준이 어떤것인지 헷갈립니다.

그냥 스키마 가져와서 넓게 타이핑하는 것인지, 아니면 jwt의 payload에 타입에 딱 맞춰서 타이핑해야하는지 잘 모르겠습니다

답변 1

0

윤상석님의 프로필 이미지
윤상석
지식공유자

안녕하세요. 

저희가 validate에서 return을 한 cat의 타입은 Cat입니다. 

(기존에 findCatByWithoutPassword의 리턴값의 타입을 CatRequestDto 타입이라고 했었는데 Cat 타입이 맞습니다. 혼란스럽게 해서 죄송합니다.)

CatRequestDto에서 PickType을 사용한 이유는 회원가입을 할 때 부분적으로 유효성 검사 및 swagger에 적용하기 위함이었습니다. 

select로 필드를 부분적으로 가져오더라도 타입은 Cat 타입입니다.

따라서 @CurrentUser() cat : Cat으로 타이팡하는 것이 맞습니다.

궁금하신 것 있으시면 답글 달아주세요.

좋은 질문 감사합니다. :)

G General님의 프로필 이미지
G General
질문자

아하 제가 궁금했던 것은 필드를 부분적으로 가져온 것도 Cat타입에 속하는 것인지였습니다! 답변 감사드립니다

근데 제가 임의적으로 type CatMyDto {email:string, name: string} 이런식으로 설정한 뒤, @CurrentUser() cat: CatMyDto 이렇게 임의적으로 설정해줘도 되는 건가요?

예를 들어서 큰 규모의 어플리케이션에서, 컨트롤러에서 각자 다른 Cat의 PickType을 리턴하는 서비스 여러개를 호출해서 비즈니스 로직의 변수로 사용된다고 가정했을 때, 실제 데이터 필드가 다른데도 불구하고 전부 Cat으로 타이핑 하면, 실제로 컴파일 에러가 발생해야 할 로직에서(ex 예를들어 스키마에 '수량'을 추가하고, A 변수는 수량을 포함한 Cat타입, B 변수는 수량을 select로 뺀  Cat타입일 때 A와 B의 수량 비교 로직 등) 컴파일 에러가 발생하지않고 런타임에서 터지는 경우가 있을 것 같아서 다시 질문드립니다!

윤상석님의 프로필 이미지
윤상석
지식공유자

안녕하세요!

아 무슨 말씀인지 이해했습니다.

사실 @CurrentUser() cat은 값은 데코레이터에 의해 첨가가 되어 request.user 이 부여가 되지만 타이핑은 어떤 타입이든지 타이핑을 할 수 있습니다. 따라서 string이라고 타이핑해도 임의로 부여가 됩니다. (의도치 않은 타이핑인 것이지요.)

따라서 데코레이팅된 값과 동일한 타입(request.user의 타입)으로 타이핑을 해주어야 합니다.

저희가 의도한 request.user는 select로 인해 password 필드가 undefined인 Cat 타입입니다. 따라서 @CurrentUser() cat : Cat으로 타이핑을 해도 됩니다.  이때 cat.password는 undefined 값입니다.

하지만 이 경우 말씀해주신 것처럼 의도치 않게 개발자가 cat.password = "1205" 으로 접근할 수 있습니다. (컴파일 에러가 발생하지 않지만 안전하지는 않습니다. 런타임 에러가 발생할 확율이 높습니다.)

따라서 필요에 따라 더 강력하게 타이핑을 하는 것이 좋습니다. ( 더 권장드립니다.)

1. 먼저 CatCurrentDto를 설계합니다. (여기서 OmitType은 PickType과 반대되는 빼는 것입니다.)

import { OmitType } from '@nestjs/swagger';
import { Cat } from '../cats.schema';

export class CatCurrentDto extends OmitType(Cat, ['password'] as const) {}

2. "cats.repository.ts"에서 광범위하게 잡은 Cat 타이핑 대신 CatCurrentDto를 사용합니다.

async findCatByIdWithoutPassword(
catId: string,
): Promise<CatCurrentDto | null> {
const cat = await this.catModel.findById(catId).select('-password');
return cat;
}

3. "user.decorator.ts"에서 request.user의 타입을 CatCurrentDto으로 캐스팅합니다. 

일반적으로 request.user 는 any 타입입니다. express에서는 처음에는 어떤 user 타입인지 모르기 때문이죠.

export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request: Request = ctx.switchToHttp().getRequest();
return request.user as CatCurrentDto;
},
);

(이 방법 외에 아예 처음부터 node_modules의 express의 User 타입 자체를 정의할 수도 있습니다.)

4. "cats.controller.ts"에서 @CurrentUser() cat : CatCurrentDto로 타이핑합니다.

@Get()
getCurrentCat(@CurrentUser() cat: CatCurrentDto) {
return cat.readOnlyData;
}

추가적으로 이해안되는 것이 있으시면 말씀해주세요! 🙂

감사합니다 :)

G General님의 프로필 이미지
G General

작성한 질문수

질문하기