[Next.js] BFF에서 formData로 Spring 서버로 요청 보내기

Rachaen·2023년 5월 14일
0

BFF 패턴으로 개발을 하려던 중에 multipart/form-data 형식으로 요청을 보내야 했었다.
우선 클라이언트에서 보낸 파일을 pages/api에 구현된 BFF에서 처리를 해야했다.

클라이언트에서 보낸 데이터 처리하기

Multer에서 Fromidable로의 전환

클라이언트에서 보내는 데이터를 처리하기 위해 처음에는 Multer로 열심히 시도를 해보았다. 하지만 왜 안되는 거지? 그것은 바로 Multer은 Express 미들웨어이기 때문... 아무생각 없이 사용한 나 자신 반성.. 내가 사용하고 있는 건 Next.js로 Express를 사용하지 않는다.
next-connect 라이브러리를 사용하면 multer를 사용할 수 있다고 하지만 Express에 종속적이지 않은 Formidable을 사용하기로 생각했다.

node.js에서는 formData를 처리할 수 없다?!

  • FormData 객체는 웹 브라우저 환경에서 HTML 폼 데이터를 쉽게 처리하기 위해 제공되는 API라한다...(당연히 안되는 것)

하지만 난 Spring 서버에 formData로 보내야한다... 그래서 외부 라이브러리를 사용하기로 했다.
제일 유명한 건 multer과 formidable이였다.

formidable 사용하기

import formidable from 'formidable';

export const config = {
  api: {
    bodyParser: false,
  },
};

export default async function handler(req, res) {
   if (req.method !== 'POST') {
    res.status(400).json({ error: 'Only POST method allowed' });
    return;
  }
  const form = new IncomingForm();
  form.keepExtensions = true;
  form.multiples = true;
  form.parse(req, async (err, fields, files) => {
    if (err) {
      res.status(500).json({ error: 'Failed to parse the request' });
      return;
    }
    if (response.status === 200) {
        res.status(200).json({ success: true });
    } else {
      res.status(500).json({ error: 'Failed to upload files' });
    }
}
  • HTTP POST 요청을 받아 멀티파트 데이터를 처리하고, 처리 결과를 JSON 형태로 응답한다.
  • bodyParser: false : Next.js에서는 기본적으로 모든 요청에 body parser가 적용된다. 파일 업로드 같은 멀티파트 형식의 데이터를 파싱하는데는 문제가 될 수 있기 때문에 body parser 비활성화
  • form.keepExtensions = true : 파일 확장자 유지
  • form.multiples = true; : 여러 파일을 한 번에 업로드

BFF 서버에서 Spring 서버로 데이터 보내기위한 formData

  • node는 formData 객체를 생성할 수 없다... 그래서 form-data를 이용하기로 했다.

form-data 라이브러리 활용하기

npm install --save form-data

import fs from 'fs';
import path from 'path';
import FormData from 'form-data';
import { IncomingForm } from 'formidable';

export const config = {
  api: {
    bodyParser: false,
  },
};

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    res.status(400).json({ error: 'Only POST method allowed' });
    return;
  }

  const form = new IncomingForm();
  form.keepExtensions = true;
  form.multiples = true;

  form.parse(req, async (err, fields, files) => {
    if (err) {
      res.status(500).json({ error: 'Failed to parse the request' });
      return;
    }

    const formData = new FormData();
    formData.append('registerStoreRequestDto', fields.data);

    const images = Array.isArray(files.images) ? files.images : [files.images];
    images.forEach(file => {
      const ext = path.extname(file.name);
      formData.append('images', fs.createReadStream(file.path), file.name + ext);
    });

  try {
      const httpInstance = createHttpInstance(req);
      const response = await httpInstance.post('/store', formData, {
        headers: formData.getHeaders(),
      });

      if (response.status === 200) {
        res.status(200).json({ success: true });
      } else {
        res.status(500).json({ error: 'Failed to upload files' });
      }
    } catch (error) {
      console.error(error);
      res.status(500).json({ error: 'Failed to upload files' });
    }
  });
}
  • FormData 객체를 만들 때는 'fs'모듈을 사용해서 파일을 읽어온 후 이 데이터를 FormData 객체에 추가한다.
  • 이미지 확장자는 path.extname(file.originalFilename)를 사용하여 파일 확장자를 얻었다. => 확장자가 안들어가서 당황스러웠다.. 나중에 따로 글을 작성해야겠다.
  • 후에 formData를 담아서 BFF 서버에서 Spring 서버로 데이터를 보내주었다.
  • formidableparse 함수는 filedsfiles 두 개의 객체를 전달하는데 이름에서 예상할 수 있지만 fields는 필드정보를 담고있고 files 객체는 업로드한 파일에 대한 정보를 담고 있다.
profile
개발을 잘하자!

0개의 댓글