-
Notifications
You must be signed in to change notification settings - Fork 0
[BE] @nestjs swagger로 이미지 업로드 form을 어떻게 만들지?
박경미 edited this page Dec 14, 2023
·
5 revisions
- 이슈
- 삽질 과정
- 시도A @UploadedFile() 사용 ❌
- 시도B @UploadedFile() 사용 & dto에서 image 제거
- 시도C @UploadedFile() 사용 & dto에서 image 사용
- 최종 결론
이상 | 현실(문제) |
---|---|
- Swagger에서 왼쪽(이상)처럼 파일을 전송하는 칸 나와야 하지만, 실제론 텍스트 입력 칸이 나온다.
- 원래 Nest가 알아서 라우터 핸들러의 매개변수를 확인하고 자동으로 Swagger를 구성해준다.
- 예를 들어, @nestjs/swagger는 create 메서드의 매개변수인
CreateTimelineDto
를 자동으로 Swagger 문서에 반영해준다.
@Post() @UseInterceptors(FileInterceptor('image')) @ApiOperation({ summary: '타임라인 생성', description: '...'}) async create( @UploadedFile() image: Express.Multer.File, @Body() createTimelineDto: CreateTimelineDto )
- 예를 들어, @nestjs/swagger는 create 메서드의 매개변수인
- 그러나
@UploadedFile()
데코레이터로 받아들이는image
에 대한 입력칸은 Swagger에 자동으로 나오지 않는다.
-
컨트롤러
@Post() @UseInterceptors(FileInterceptor('image')) @ApiOperation({ summary: '타임라인 생성', description: '...'}) @ApiConsumes('multipart/form-data') async create( @UploadedFile() image: Express.Multer.File, @Body() createTimelineDto: CreateTimelineDto ): Promise<Timeline> { }
-
dto
export class CreateTimelineDto { @ApiProperty({ example: '서울역에서 출발~', maxLength: 14, minLength: 1 }) @IsString() @MinLength(1) @MaxLength(14) title: string; @ApiProperty({ required: false }) @IsOptional() @IsString() image: string; }
-
@ApiBody()
데코레이터를 사용하고, - schema의 properties에서
image: { type: 'string', format: 'binary' }
를 사용하면 된다고 함!
@Post()
@UseInterceptors(FileInterceptor('image'))
@ApiOperation({ summary: '타임라인 생성', description: '...'})
@ApiConsumes('multipart/form-data')
@ApiBody({
description: 'File upload with additional data',
schema: {
type: 'object',
properties: {
image: {
type: 'string',
format: 'binary',
},
},
},
})
async create(
@UploadedFile() image: Express.Multer.File,
@Body() createTimelineDto: CreateTimelineDto
): Promise<Timeline> { }
-
createTimelineDto
에 정의된 다른 프로퍼티는 모두 사라지고, - 오직 파일 업로드하는
image
프로퍼티만 덜렁 남겨짐,,
- 사라진 나머지 프로퍼티를 다시 Swagger에 출력되게 하려면,
-
@ApiBody
> schema > properties에 dto에 정의된 모든 프로퍼티를 작성하라고 함.
@ApiBody({
description: 'File upload with additional data',
schema: {
type: 'object',
properties: {
image: {
type: 'string',
format: 'binary',
},
createTimelineDto: {
type: 'object',
properties: {
title: { type: 'string', example: '노가다' },
day: { type: 'number' },
// Add other properties as needed
},
},
},
},
})
- 노가다이기도 하지만...
- CreateTimelineDto에서
@ApiProperty
데코레이터로 열심히 작성한 모든 내용이 사라진다.@ApiProperty({ example: '서울역에서 출발~', maxLength: 14, minLength: 1 }) @IsString() @MinLength(1) @MaxLength(14) title: string; @ApiProperty({ required: false, example: 'GS25 서울역점', description: '장소 이름', maxLength: 50, }) @IsOptional() @IsString() place: string;
- 따라서 dto 파일의
@ApiProperty
데코레이터에 적은 내용을 다시@ApiBody
에 작성해야 했다. - 내가 example도 넣고, length도 넣고, required 속성도 넣었는데, 이걸 다시 하라고? 이게 무슨 헛짓거리인가.
❗ 이건 아니라는 강한 의심 ⇒ 정말 방법이 없는 것일까?
이미지 파일 자체가 행방불명
-
컨트롤러
-
@UploadedFile
데코레이터 제거
export class TimelinesController { constructor(private readonly timelinesService: TimelinesService) {} @Post() @UseInterceptors(FileInterceptor('image')) // 이거 없애면 아래와 같은 일이.. @ApiOperation({ summary: '타임라인 생성', description: '사용자가 입력한 정보를 토대로 새로운 타임라인을 생성합니다.', }) @ApiConsumes('multipart/form-data') async create( @Body() createTimelineDto: CreateTimelineDto ): Promise<Timeline> { } }
-
⚠
@UploadedFile
사용 안 한다고@UserInterceptor
까지 제거하면, 모든 입력 데이터가 먹히지 않고 빈 dto가 전달된다.
-
-
dto
export class CreateTimelineDto { @ApiProperty({ required: false, type: 'file', description: '업로드하는 사진', }) @IsOptional() image: Express.Multer.File; }
image 미전송 | image 전송 |
---|---|
- 이미지 전송 시 dto에서 image 필드가 사라진다. 사진을 담은 image 프로퍼티는 dto에 정의된 image로 들어가는 게 아니니까~!
(아마 intercept 당한 듯) - 그럼 사진은 어디로 갔는가? 모릅니다. 그냥 사라져버렸어요.
swagger에서 파일 테스트 불가
-
컨트롤러
@Post() @UseInterceptors(FileInterceptor('image')) @ApiOperation({ summary: '타임라인 생성', description: '사용자가 입력한 정보를 토대로 새로운 타임라인을 생성합니다.', }) @ApiConsumes('multipart/form-data') async create( @UploadedFile() image: Express.Multer.File, @Body() createTimelineDto: CreateTimelineDto ): Promise<Timeline> { }
-
dto
- 아래 코드 제거
export class CreateTimelineDto { @ApiProperty({ required: false, type: 'file', description: '업로드하는 사진', }) @IsOptional() image: Express.Multer.File; }
- 사진을 첨부할 수 있는 image 필드 자체가 사라졌다.
image 첨부 | image 미첨부 |
---|---|
- dto의 image 필드 없음 - @UploadedFile()로 받은 image 존재 |
image가 있어야 한다는 에러 |
Swagger에서 image 프로퍼티가 존재하지 않으므로, 이미지 미첨부 상황만 테스트 가능
- 요청은 보내지지만,
@UploadedFile() image
에는 아무런 값이 들어 있지 않음
-
컨트롤러
@Post() @UseInterceptors(FileInterceptor('image')) @ApiOperation({ summary: '타임라인 생성', description: '사용자가 입력한 정보를 토대로 새로운 타임라인을 생성합니다.', }) @ApiConsumes('multipart/form-data') async create( @UploadedFile() image: Express.Multer.File, @Body() createTimelineDto: CreateTimelineDto ): Promise<Timeline> { }
-
dto
export class CreateTimelineDto { @ApiProperty({ required: false, type: 'file', description: '업로드하는 사진', }) @IsOptional() image: Express.Multer.File; }
image 미첨부 | image 첨부 |
---|---|
image 미첨부 | image 첨부 |
---|---|
-
controller
@Put(':id') @UseInterceptors(FileInterceptor('image')) @ApiOperation({ summary: 'id에 해당하는 타임라인 수정', description: 'id에 해당하는 타임라인을 수정합니다.', }) @ApiConsumes('multipart/form-data') @ApiOkResponse({ schema: { example: update_OK } }) async update( @Param('id', ParseUUIDPipe) id: string, @UploadedFile() image: Express.Multer.File, @Body() updateTimelineDto: UpdateTimelineDto ) { }
-
dto
export class CreateTimelineDto { @ApiProperty({ example: '서울역에서 출발~', maxLength: 14, minLength: 1 }) @IsString() @MinLength(1) @MaxLength(14) title: string; @ApiProperty({ required: false, type: 'file', description: '업로드하는 사진', }) @IsOptional() image: Express.Multer.File; }
-
@IsOptional
이 빠지면 안 됨! 그러면 이미지를 첨부하지 않았을 때property image should not exist
에러 발생 -
Express.Multer.File
타입의 프로퍼티 이름(image)는dto
와@UploadedFile()
에서 모두 동일해야 한다.
image 미전송 | image 전송 |
---|---|