diff --git a/backend/src/storage/dto/PreSignedPostRequest.ts b/backend/src/storage/dto/PreSignedPostRequest.ts index e7982d77..b336bea7 100644 --- a/backend/src/storage/dto/PreSignedPostRequest.ts +++ b/backend/src/storage/dto/PreSignedPostRequest.ts @@ -1,4 +1,5 @@ import { IsNotEmpty, IsString } from 'class-validator'; +import { IsImageFile } from '../storage.validator'; export class PreSignedPostRequest { @IsString() @@ -7,5 +8,6 @@ export class PreSignedPostRequest { @IsString() @IsNotEmpty() + @IsImageFile() extension: string; } diff --git a/backend/src/storage/storage.constants.ts b/backend/src/storage/storage.constants.ts new file mode 100644 index 00000000..9cd1cf9c --- /dev/null +++ b/backend/src/storage/storage.constants.ts @@ -0,0 +1,18 @@ +export const IMAGE_EXTENSIONS = new Set([ + 'jpg', + 'jpeg', + 'png', + 'gif', + 'bmp', + 'tiff', + 'tif', + 'webp', + 'svg', + 'heic', + 'raw', + 'cr2', + 'nef', + 'arw', + 'dng', + 'ico', +]); diff --git a/backend/src/storage/storage.controller.ts b/backend/src/storage/storage.controller.ts index c5340a20..8ea2f7de 100644 --- a/backend/src/storage/storage.controller.ts +++ b/backend/src/storage/storage.controller.ts @@ -11,9 +11,7 @@ export class StorageController { @Throttle({ default: { limit: 10, ttl: 60000 } }) @Post('/preSignedPost') @UseGuards(JwtAuthGuard) - async getPreSignedPostUrl( - @Body() preSignedPostRequest: PreSignedPostRequest, - ) { + async getPreSignedPost(@Body() preSignedPostRequest: PreSignedPostRequest) { const { dirName, extension } = preSignedPostRequest; return await this.storageService.generatePreSignedPost(dirName, extension); } diff --git a/backend/src/storage/storage.validator.ts b/backend/src/storage/storage.validator.ts new file mode 100644 index 00000000..0473c2bf --- /dev/null +++ b/backend/src/storage/storage.validator.ts @@ -0,0 +1,25 @@ +import { + registerDecorator, + ValidationArguments, + ValidatorOptions, +} from 'class-validator'; +import { IMAGE_EXTENSIONS } from './storage.constants'; + +export function IsImageFile(validationOptions?: ValidatorOptions) { + return function (object: object, propertyName: string) { + registerDecorator({ + name: 'IsImageFile', + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + validator: { + validate(value: any) { + return IMAGE_EXTENSIONS.has(value); + }, + defaultMessage(validationArguments?: ValidationArguments): string { + return `${validationArguments.value} 는 이미지 확장자가 아닙니다.`; + }, + }, + }); + }; +}