이미지 파일 업로드 (feat. multer)

Franklee·2023년 1월 29일
0

Typescript

목록 보기
2/2
post-thumbnail

Reason

개인 프로젝트 진행 중에 백엔드를 기초적인것만 사용하고 있기에 이미지 데이터 및 모든 데이터를 DB에 저장하고 있었다.
배포를 하기전이라 이미지파일을 로드하면 "localhost ... /'이미지파일이름'"이렇게 된 url(?)이 데이터로 저장이된다.
저장된 데이터를 DB에서 불러오면 당연하게도 정상적인 이미지가 로드되지는 않는다.

따라서, 이미지를 어떻게 효율적으로 로드시킬까 정보를 찾다가 이미지를 백엔드 파일에 저장시키고 DB에는 저장된 경로를 저장하는 방식에 대해 찾게되었다.(원래는 이미지를 웹에 올리고 해당이미지의 URL을 저장할까 했지만...너무 단순무식한 방식)

해당 방식은 백엔드에 multer를 설치 해야한다.


Use

기본중의 기본만 설명..

Nodejs

  • multer를 설치해준다.
npm install --save multer
  • multer 이미지 저장 경로 설정. (백엔드 server.js)
const multer = require("multer");

const upload = multer({ dest: "이미지 저장 경로" }); // ex) "./images/" 원하는 경로

app.post("/upload", upload.single("formdata의 key"), (req, res) => {
  // formdata 객체를 보내게 될것인데 해당 key가 일치해야한다. 밑에 설명
  //upload.single에서 single은 단 하나의 이미지만 가능하다. 여러개를 위해서는 field가 있다.
  console.log(req.file);
  //body가 아닌 file에 존재.
});

React + TypeScript

  • 이미지를 가져오기 위한 input element (프론트엔드 js/tsx)
<input type='file' accept="img/*" multiple onChange={()=>{
 setFileQ(fileQ=>e.target.files[0]) 
  //대체로 배열에 담겨 오는데 이부분은 상황에 알맞게 처리해주면된다.(저는 state에 저장)
  //e.target.files의 타입은 FileList
}}></input>
{/** 파일을 가져오는 input, 이미지 형식만 가능, 여러개가능 */}
<button></button>
  • 이미지 파일을 FormData 객체를 생성하여 할당.
const postImage=()=>{
  let foo = fileQ //위에서 받은 파일 타입은 File
  let formData = new FormData();
  formData.append(`img`, awe, `img$.png`);
  //formData에 데이터를 넣는 방식 .append( key , value , name(선택적) )
}
  • axios를 통한 데이터통신
const request = await axios.post('url', foo, { 
  							header: { 
  							"Content-Type": `multipart/form-data`
								}
});
//요청하는 url , 보내는 데이터 foo (:FormData) , post에서 formdata형식을 보내면 
//자동으로 "Content-Type": `multipart/form-data`가 된다고 하는데 안될시에 넣어주자. 

이렇게 요청을 보내면 nodejs에서 console.log(req.file)을 하면 확인 할 수 있다.
정상적으로 작업이 진행되었다면 req.file 존재 및 설정한 경로에 이미지파일이 생성되있을 것이다.


Error

  • 진행 중 겪은 에러 및 문제 해결
  1. formdata를 console.log를 해도 빈 객체(배열)만 뜹니다.

    • 원래 그렇습니다. XMLHttpRequest 전송을 위해 설계된 특수한 객체라고 하여 일반적으로는 불가능합니다.
    • 물론 조회할수 있는 방법이 있습니다.
    for (let key in 폼데이터이름.keys()) {
      console.log(key);//콘솔로 key 확인
    }
    
    // FormData의 value 확인
    for (let value in 폼데이터이름.values()) {
      console.log(value);//콘솔로 values 확인 
    }
    • 다만 Typescript에서는 폼데이터이름.keys() 부분에서 오류가 뜰텐데 tconfig.json(프로젝트 폴더 하단쪽에 존재)에서 "compilerOptions""downlevelIteration": true,를 추가하고
      for in이 아닌 for of 를 사용. 해당정보
    for (let values of formData.keys()) { //for in이 아닌 for of 사용
      console.log(values);
    }
  2. Uncaught SyntaxError: Unexpected token u in JSON at position 0

    • 해당 문제는 JSON.parse(undefined) 를 사용했을 때와 같은 상황이다.
    • 보내는 정보 혹은 받는 메소드내부에서 잘못된 JSON파일을 보내거나 만든 상황이다.
    • 대체로 실수로 나온 오류인듯하다.. 작성한 코드 및 데이터를 확인하자.
  3. Content-Type

    • 이녀석 때문에 시간좀 잡아 먹었는데, axios interceptor를 사용한다면 공부할 때 보낸 요청을 가로채 console.log로 어떻게 진행되나 체크해 볼때가 있다.
    • "Content-Type": "multipart/form-data"를 넣고 요청을 콘솔에 띄우면 "Content-Type"이 사라져 있다.
      하지만, 넣지 않으면 Content-Type은 "application ..." 설정 되어있음
    • 이때 서버에서 받은 req를 콘솔에 띄워보고 control+f(컨트롤+f)를 하여 'Content'를 찾아보면 정상적으로 설정되어 요청이 보내진것을 볼 수 있다.

End

하루 + 반나절을 보냈다.
어디서부터 꼬인건지 돌고돌아 해결했지만, 중간에 포기하고 작성 중이던 README나 velog 게시물이 있었지만, 차마 포기 했다고 하고싶진 않았다.
그런데 이상한건 맨처음 코드랑 별다를것이 없는데 왜 이제서야 되는걸까...
그래도 진행하면서 공부하는 오류 및 여러 설정 등에 대한 것을 덤(?)으로 학습하게 되었다.


profile
복잡한 문제를 쉬운 코드로 해결해 나가는 개발자

0개의 댓글