next.js formData으로 보내고 nest.js 백엔드 처리하기

lemon·2024년 7월 18일
1

개발 공부

목록 보기
18/18

문제

  • 패키지 상품을 판매하기 위해 텍스트, 숫자, 이미지 2개, 패키지에 포함되는 item 배열 등등 데이터를 저장이 필요했다.
  • 텍스트, 이미지를 저장하는건 어렵지 않았는데 Array Object형식으로 저장하는 부분이 자꾸 백엔드에서 타입 에러나 값을 인식하지 못했다.

프론트(next.js)

  • 데이터를 formData에 담아 보냈다.
const formData = new FormData();

for (const key in values) {
  formData.append(key, values[key]);
}

// items 필드를 JSON 문자열로 변환하여 추가
if (values.items) {
  formData.append('items', JSON.stringify(values.items));
}
  • items는 [{}, {}] array object 타입인데, 이 부분을 문자열로 변환하였다.

백엔드(nest.js)


// controller
  @GenerateSwaggerApiDoc({
    summary: '마켓 상품 등록',
    description: '상품을 마켓에 등록합니다.',
  })
  @Post('products')
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(
    FileFieldsInterceptor([
      { name: 'img', maxCount: 1 },
      { name: 'imgDetail', maxCount: 1 },
    ]),
  )
  async createMarketProduct(
    @Body() request: RequestCreateProductsDto,
    @UploadedFiles() files: { img: Express.Multer.File[]; imgDetail: Express.Multer.File[] },
  ): Promise<unknown> {
    if (files.img) request.img = files.img[0];
    if (files.imgDetail) request.imgDetail = files.imgDetail[0];
    await this.marketsService.createMarketProduct(request);
    return response({}, '상품이 등록되었습니다.');
  }
  • 백엔드 쪽에 헤더 설정을 multipart/form-data 하였기 때문에 프론트 호출부에 하지 않았다. 근데 찾아보니 하는것이 좋다고 하네..
  • @UploadedFiles() 데코레이터를 사용하여 이미지를 받았다.

어려웠던 점

  • 위에서도 말했지만 이미지/텍스트는 저장하는 것이 어렵지 않았는데 items[]를 저장하는건 어려웠다.
  • 계속 items[]안에 값들을 찾을 수 없다는 에러가 발생하였다.

해결

// dto 수정 전
  @ApiProperty({ description: '아이템 리스트', type: [Item], required: true })
  @ValidateNested({ each: true })
  @IsArray()
  @IsNotEmpty()
  @Type(() => Item)
  @Transform(({ value }) => {
    if (typeof value === 'string') {
      try {
        return JSON.parse(value);
      } catch (error) {
        throw new Error('Invalid JSON string');
      }
    }
    return value;
  })
  items: Item[];
  • 분명 Transform으로 데이터를 변환하는데도 에러가 발생했다.
  • @ValidateNested({ each: true }) 코드를 삭제하면 에러가 발생하지 않았지만 데이터 검사가 필요 했기 때문에 적절한 해결 방법은 아니었다.
  • 데코레이터 순서가 문제가 있는건가? 라는 생각도 들었다.
// dto 수정 후
  @ApiProperty({ description: '아이템 리스트', type: [Item], required: true })
  @ValidateNested({ each: true })
  @IsArray()
  @IsNotEmpty()
  @Type(() => Item)
  @Transform(({ value }) => {
    if (typeof value === 'string') {
      try {
        return JSON.parse(value).map((item: Item) => plainToInstance(Item, item));
      } catch (error) {
        throw new Error('Invalid JSON string');
      }
    }
    return value;
  })
  items: Item[];
  • 데이터를 정확히 instance로 변경하니 에러가 발생하지 않았다!!

이거 말고도 프론트의 File과 타입 이슈.. 등등 때문에 기존 input에서 File로 변경하는 작업만 2.5일정도 소요됐다.. ㅜㅜ
crud에 화면 만드는데는 5일이 소요됐는데..

profile
시키면 다 하는 사람

0개의 댓글