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

wave·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개의 댓글