multer 를 통한 이미지 파일 업로드 / axios 사용

타다닥·2023년 11월 22일
0
post-thumbnail

body-parser는?

  • 데이터를 쉽게 처리할 수 있도록 도와주는 라이브러리이다.
    app.use(express.urlencoded({ extended: true }));
    app.use(express.json());
  • req.body 를 읽으려면 얘가 필요하다. (post전송 시)
  • 별도의 설치는 필요 없다.
  • 하지만 얘는 멀티파트 데이터를 처리하지 못한다. 이미지, 파일, 동영상 등!
  • 그럼 다른걸 사용해야하는데 ? multer를 사용하면 된다.

multer

  • 파일 업로드를 위해 사용되는 Node.js의 미들웨어이다. 설치가 필요하다 !
  • 기본적으로 파일을 업로드 하는 과정은 이렇게 된다. client가 server에 데이터를 전송한다. 그리고 그 데이터에 대한 저장이나 처리는 백엔드쪽에서 한다. 이 과정에서 필요한 것이 multer!
  • express로 서버를 구축할 때 가장 많이 사용되는 미들웨어이다.

▶️ multer 설치

  • npm install multer
  • package.json 에 multer가 설치 된 것 확인.
  • 그리고 이렇게 multer를 불러와서 사용해주면 된다.
  • npm install multer@^1.4.4
const multer = require("multer"); // multer 불러오기

▶️ client의 파일 업로드

  • client가 파일을 업로드 할 수 있는 상황을 만들어주자.
  • enctype 을 꼭 multipart/form-data 로 설정해주어야 한다!
    • multer는 multipart/form-data가 아닌 폼에서는 동작하지 않는다!
<form action="/upload" method="post" enctype="multipart/form-data">
   <input type="file" name="userfile" /><br />
   <input type="text" name="title" /><br />
   <button type="submit">업로드</button>
</form>

▶️ 파일 업로드 경로 설정

  • server에서는 파일을 받을 준비를 해야 한다. 이걸 multer로 한다.
const multer = require("multer"); // multer 불러오기
const path = require("path"); //내장 모듈이다. 경로를 쉽게 구하기 위한.

const upload = multer({
  dest: "uploads/", //파일을 업로드하고, 파일이 저장 될 경로를 지정하는 속성이다.destiny.
});
  • multer로 특정 객체를 만들면 얘는 미들웨어로 사용이 가능하다!
    • upload라는 객체 안에는 미들웨어 함수가 존재한다. single(), array(), fields()
    • 파일을 multer의 설정대로 저장해서, req.file or req.files 이라는 객체를 생성해서 다음 함수에 넘겨주게 된다.

▶️ 파일 업로드 - 하나의 파일 업로드 하기

  • upload.single() 을 사용한다. 얘는 req.file 을 생성하게 된다

// upload라는 부분의 post요청에 single() 미들웨어를 걸고 싶은 것.
//single("")미들웨어 안에는 client가 파일 객체를 넘겨주는 식별자가 들어간다.
//미들웨어로 넘겨주는 값은 input type="" name="" 의 name부분을 적어준다.
//single()은 미들웨어이다. 얘는  cilent가 보낸 요청 중에 userfile 이라는 name의 파일이 있다면?
//파일을 저장해서 multer의 조건에 맞게 저장을 한다. (client의 파일 업로드에서 지정한 const upload = multer)
//저장 후 req.file 이라는 객체를 만든다. 그리고 다음에 위치한 함수에 넘겨준다.
//여기서는 function (req, res)에 넘겨주게 되는 것.
//그럼 파일은 req.file에서 볼 수 있다.

app.post("/upload", upload.single("userfile"), function (req, res) {
  console.log("file:", req.file);
  console.log("body:", req.body);
  res.send("파일 업로드");
});
  • 그리고 파일을 업로드 하게 되면?
    • 터미널에서 req.file 객체와 req.body 객체를 확인할 수 있다.
    • 주소창도 바뀌게 되고

    • uploads 라는 폴더가 생기게 된다!

▶️ 파일 업로드 - 하나의 파일 업로드 하기 (multer storage 사용)

  • ☑️ 근데 파일명이 지 맘대로 저장이 된다? 그럼 파일명, 파일크기, 경로 등을 제어해주자!
    • multer가 임의의 문자열을 생성해서 파일 이름을 만들어주기 때문.
//path는 특정 파일 경로를 받았을 때, 그에 대한 관리나 조작을 도와준다.
//예를 들어 확장자를 추출하든지, 파일이름을 알아온다든지!
const path = require("path");

//extname 메소드로 확장자를 알 수 있고
//basename 메소드로 파일명을 알 수 있다.
console.log("hi.txt의 확장자:", path.extname("hi.txt"));
console.log("hi.txt의 이름:", path.basename("hi.txt", path.extname("hi.txt")));
//기본 구조
const uploadDetail = multer({
    //diskStorage() .디스크에 저장소를 만들어 둘 때 사용하면된다. 객체가 들어간다.
    storage: multer.diskStorage({
        destination: function(){

        },
        filename: function(){
            
        }
    })
})
//-------------------------------------이렇게 세부 설정!

//---------multer 설정 상세 구조에는 storage와 limits 두 객체가 들어간다. 아래 구조.
//storage : 파일을 저장할 정보.
//--diskStorage : 파일을 디스크에 저장하기 위한 기능을 제공하는 메소드. 그걸 어케 할거냐. 아래 두개로
//-----destination : 파일이 저장 될 경로
//-----filename : 파일이 저장 될 이름

//diskStorage() .디스크에 저장소를 만들어 둘 때 사용하면된다. 객체가 들어간다.
//여기에는 3개의 인자가 들어간다. done이라는 콜백함수도 들어간다. 여기에 파일경로를 넣어준다.
//설정을 끝마치면 done이 실행된다. 얘를 이용해서 경로를 지정하는 것. done뒤에 오는 것이 경로다.

//그리고 파일 업로드를 계속 하게 되면 덮어쓰기가 잘못 될 수 있으니
//Date.now() 함수를 사용해 파일명에 활용해보자.

const upload = multer({
  dest: "uploads/",
});

const uploadDetail = multer({
  storage: multer.diskStorage({
    destination: function (req, file, done) {
      done(null, "uploads/");
    },
    filename: function (req, file, done) {
      console.log("uploadDetail filename", req.body);
      console.log(file); // file.originalname: 쿼카.png 라면?
      const ext = path.extname(file.originalname); // .png 출력.
      const basename = path.basename(file.originalname, ext); // 쿼카 출력.
      const fileName = basename + "_" + Date.now() + ext; // 쿼카_123453156.png 출력.

      done(null, fileName); //이렇게 파일명 내맘대로 설정하기 완료.
    },
  }),
  limits: { fileSize: 5 * 1024 * 1024 }, //5MB 제한. 
	//limits:파일을 제한하겠다.
	//filesize : 파일의 최대 크기 지정가능. 
});

//-------------------------------------그리고 불러오자
app.post(
  "/upload/detail",
  uploadDetail.single("userfile"),
  function (req, res) {
    console.log("file detail:", req.file);
    console.log("body detail:", req.body);

    res.render("result", {
      src: req.file.path,
      title: req.body.title,
    });
  }
);
//[index.ejs파일]

<h2>multer storage를 이용한 설정</h2>
    <form action="/upload/detail" method="post" enctype="multipart/form-data">
      <input type="file" name="userfile" /><br />
      <input type="text" name="title" /><br />
      <button type="submit">업로드</button>
</form>
  • 파일명이 이쁘게 잘 들어갔다.

▶️ 파일 업로드 - 파일 여러 개 업로드 하기 array()

  • array() 메소드를 이용한다. 하나의 input을 이용한다. name 하나로 여러개의 파일을 받는 방법
    • 즉 하나의 요청에 여러 파일이 존재 할 때!
    • req.files 를 생성하게 된다.
//[index.js파일]

app.post("/upload/array", uploadDetail.array("userfile"), function (req, res) {
  console.log("file 여러개(ver1):", req.files);
  res.send("여러개 업로드 성공!");
});
//[index.ejs파일]

<h2>파일 여러개 업로드 (하나의 input 이용)</h2>
    <form action="/upload/array" method="post" enctype="multipart/form-data">
      <!-- input type file의 multiple 속성은 여러개의 파일을 선택할 수 있게 함. -->
      <input type="file" name="userfile" multiple /><br />
      <input type="text" name="title" /><br />
      <button type="submit">업로드</button>
    </form>
  • console.log(req.files); 를 확인해보면 정보도 잘 들어와 있다!

▶️ 파일 업로드 - 파일 여러 개 업로드 하기 fields()

  • fields() 메소드를 이용한다. input이 2개 이상 일 때 이용한다. (name이 2개 이상)
    • 즉 여러개의 input을 이용해 각각 파일을 받을 때!
    • req.files 를 생성하게 된다.
//[index.js파일]

app.post("/upload/fields",uploadDetail.fields([{ name: "userfile1" }, { name: "userfile2" }]),
  function (req, res) {
    console.log("file 여러개(ver2):", req.files);
    console.log("req.body", req.body);

    res.send("여러개 업로드 성공(ver2)");
  }
);
//[index.ejs파일]

<h2>파일 여러개 업로드 (여러 개의 input 이용)</h2>
    <form action="/upload/fields" method="post" enctype="multipart/form-data">
      <input type="file" name="userfile1" multiple /><br />
      <input type="text" name="title1" /><br />
      <input type="file" name="userfile2" /><br />
      <input type="text" name="title2" /><br />
      <button type="submit">업로드</button>
    </form>

☑️ 헷갈리지 말자

지금까지 한 건 일반 폼 전송! 동기 http 통신!

그리고 이제 할 건 동적 폼 전송! 비동기 http 통신!


동적 폼 전송, 파일 업로드(Axios 이용)

☑️ 간단히 복습하는 비동기 http 통신에서의 동적 폼 전송

  • 비동기 방식에서는 <button *type*="button" *onclick*="axiosGet()">axios get 전송</button> 이런식으로 함수를 걸어서 js로 폼 전송을 적용시켜준다.
    • 따라서 js코드 내에서 폼 유효성 검사를 하는 코드를 작성해야한다!
  • 비동기 방식의 HTTP 통신
    • 서버에 데이터를 보내고 응답을 기다리는 동안 다른 처리가 가능하다.
    • 폼의 데이터를 서버와 dynamic하게 송수신 하는 것이다!
    • 웹 문서가 정적으로 멈춰있는 것이 아니라 일부 내용이 실시간으로 변경되는 것!
  • 즉 무언가를 제출 할 때마다 페이지를 이동하는 것이 아니라 한 페이지에서 동작하게 하고 싶다면?
    • 비동기 방식을 이용하면 된다.

☑️ 간단히 복습하는 Axios 요청, 응답 문법


axios({
  method: "GET",
  url: "/axios",
  params: data 
 }).then((response) => {
	//서버가 제공한 응답(데이터)
	console.log(response.data) 
	//서버 응답의 HTTP 상태 코드. 성공이면 200
	console.log(response.status)
	//서버가 응답한 헤더
	console.log(response.headers)
});
  • url : 통신하고자 하는 주소를 넣어준다.
    • form에서의 action에 해당한다. 내가 데이터를 보내고자 하는 주소!
  • method : 통신하고자 하는 방식을 넣어준다.
    • get, post, patch, delete 등
  • data : json형태의 보내고자 하는 데이터를 {}안에 넣어준다.
    • { key: value, key: value} 의 형태로 만들어 준다.
    • put, post, patch 일 때 사용한다.
    • req.body로 데이터를 보낸다.
  • params : get 요청으로 데이터를 보낼 때 url의 ? 뒤에 객체로 보내지는 부분을 넣어준다.
    • { key: value, key: value} 의 형태로 작성한다.
    • req.query에 데이터가 담긴다.
    • params 값을 안보낼거면 URL에 쿼리스트링을 작성해서 보내도 된다.

  • 비동기 방식이기 때문에 js에서 함수를 걸어 폼 전송을 해준다.
    • 폼의 데이터를 서버와 dynamic하게 송수신 하는 것이다!
//[index.js파일]
//비동기 http 통신(동적 폼 전송) axios
app.post("/upload/dynamic",uploadDetail.single("userfile"),function (req, res) {
    //응답으로 잘 나오는지보려고 보낸거.
    res.send({ src: req.file.path });
  }
);
//[index.ejs파일]

<h2>동적 폼 전송(axios)를 이용한 파일 업로드</h2>
   <form name="dynamic-upload">
      <input type="file" name="userfile" /><br />
      <input type="text" name="title" /><br />
      <button type="button" onclick="dynamicUpload()">업로드</button>
   </form>
	    <div id="dynamic-upload-result"></div>
//[index.ejs파일 - script 부분]

<script>
      function dynamicUpload() {
        const form = document.forms["dynamic-upload"]

        const formData = new FormData();
        //FormData객체의 append메소드는 데이터를 추가할 때 사용한다.
        formData.append("title", form.title.value) //얘는 값을 받아 올 수 있다.
        //type이 file인 input태그를 선택해서 files에 접근해보면 파일이있다?
        //아래 얘네 2줄로는 처리못한다. 이미지데이터는 files로 접근해야한다!
        console.log("value", form.userfile.value)
        console.log("files", form.userfile.files)
        //파일이라는 정보 자체를 넘겨주는 것.
        formData.append("userfile", form.userfile.files[0])
        //아래 방식은 이미지 데이터를 정상적으로 처리할 수 없음
        // const data = {
        //   title: form.title.value,
        //   userfile: form.userfile.value
        // }

        axios({
          method: "post",
          url: "/upload/dynamic",
          data: formData, //폼데이터를 여기로 옮긴다.
          headers: {
            "Content-type": "multipart/form-data"
          }
        }).then((res) => {
          console.log(res.data)
          const result = document.getElementById("dynamic-upload-result")
          result.innerHTML = `<img src="/${res.data.src}" width="150"/>`
        })
      }
    </script>

이렇게 복잡하고 헷갈리는 multer 끝!
많이 쓰일 것 같은 multer. 하지만 나중에 사용할 때 마다 찾아보고 사용할 것 같다. 꽤나 헷갈린다!

profile
프론트엔드 ㄱH발ㅈr ㅎH보ㅈr - ☆

0개의 댓글