[react] 게시글 작성 기능 만들기(텍스트와 이미지 함께 업로드)

·2022년 11월 13일
0

개발 기록

목록 보기
40/68

게시글 작성 기능을 구현하는데 텍스트만 보내는건 일반 POST로 하면 되겠는데, 이미지를 첨부할 수 있다보니 파일을 서버로 전송하는 방법을 찾아헤맸다. 처음에는 이미지 업로드를 찾아봤더니 이미지만 보내는 방법처럼 보여서 텍스트랑 같이 보내는 방법을 찾는다고 헤맸는데 알고보니 똑같았다..그냥 formData에 넣어주면 되는 것을..

formData

폼 요소 내부 필드들의 값들이 FormData 객체에 반영이 되고, 바디에 FormData 객체가 들어가 서버로 전달된다.

<form id="formElem">
  <input type="text" name="name" value="Bora">
  <input type="text" name="surname" value="Lee">
  <input type="submit">
</form>

이 경우 name 속성의 내용을 key로 사용자 입력을 value로 가지게 된다.

{
  name: 사용자 입력,
  surname: 사용자 입력,
}

submit 버튼을 누를 경우 자동 생성되지만 직접 생성하고 값을 넣을 수 있다.

let formData = new FormData();
formData.append('key1', 'value1');
formData.append('key1', 'value2');
formData.append('key2', 'value3');

append

name 속성은 id 값 처럼 고유한 값이 아니어도 된다. 그래서 같은 name을 가질 수 있다. append 메서드를 사용하면 같은 name(키)을 가진 필드에 값을 여러개 넣을 수 있다.

append를 할 땐 배열을 만들어 한번에 넣지말고 반복문을 돌면서 하나하나 넣어줘야 한다고 한다. 듣기로는 한번에 넣으면 multer가 인식을 못한다고..

set

set 메서드도 append처럼 필드 추가를 할 수 있다. 하지만 append와 달리 이어 붙이는 것이 아니라 덮어써서 이전에 추가된 값이 없어지게 된다.

참고자료

multipart

제일 감이 안왔던 부분이 헤더에 넣는 Content-type인데 텍스트를 보낼 때와 formData를 보낼 때가 달랐다. 그래서 어떻게 같이 보내지? 이 의문 때문에 헤맸었다. api를 두 개로 나눠야하나? 텍스트랑 formData를 body 객체에 서로 다른 프로퍼티로 보내야하나? 고민을 계속하다 아래 참고자료의 글을 보고 모든 의문이 해결되었다. 같이 보낼 수 없기 때문에 multipart타입이 생긴 것이다.

참고자료

클라이언트에서 보내기

const formData = new FormData();

const onChange = ({ target }) => {
    const files = target.files;

    const images = Object.entries(files).reduce((images, [i, file]) => {
      formData.append("images", file);
      images[i] = file;
      return images;
    }, []);
  }; // 반복문으로 넣어주기

const registerPost = async () => {
  formData.set("topic", postTags.topic); // 파일 외 다른 값들 넣어주기
  formData.set("pet", postTags.pet);
  formData.set("text", postText);
  conosole.log(formData);
  const response = await callRegisterPostApi(formData); //post 요청
};

//이 코드는 돌아가지 X

방법을 찾아서 POST 요청을 보내고 요청을 콘솔에 찍어봤는데

formData는 콘솔로 확인할 수 없다. 브라우저 차원에서 막는다. 그래서 네트워크 탭에 들어가서 페이로드를 봤는데 이걸 보고서는 이렇게 보내면 서버에서 사용할 수 있는게 맞는지 알 수가 없었다.

서버에서 받기

그래서 직접 노드로 받아보기로 했다. 백엔드를 공부하려는게 아니고 프론트에서 보낸 데이터가 잘 받아오는지 확인을 위한 것이기 때문에 디테일하게 적지 않고 나중에 다시 시도해 볼 수 있게만 적을 것이다.

multer가 있어야 파일을 읽을 수 있다. 사용하지 않으면 아래 사진처럼 출력된다.

확인을 위해 서버에서 사용한 코드

import express from 'express';
import axios from 'axios';
import cors from 'cors';
import multer from 'multer';
import moment from 'moment';

const app = express();
const port = process.env.port || 8080;

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.set('port', port);

const storage = multer.diskStorage({
  destination: './test',
  filename(req, file, cb) {
    cb(null, `${moment().format('YYYYMMDDHHmmss')}_${file.originalname}`);
  },
});

const upload = multer({ storage });

app.post(
  '/upload',
  upload.array('images', 5),
  (req, res) => {
    console.log(1, req.body);
    console.log(2, req.files);
    res.status(200).send({
      status: 'OK',
      result: 1,
    });
  },
);

app.listen(app.get('port'), () => {
  console.log(`listen to ${app.get('port')} port`);
});

참고자료

0개의 댓글