개발 공부한 것들/SeSSAC 웹 풀스텍 과정 회고록

[SeSSAC] 웹 풀스택 영등포 5기 과정_폼유효성검사, 웹 파일 업로드

geograp 2023. 8. 22. 01:32

이번 시간에는 폼 유효성 검사와 웹 파일 업로드에 대해 배웠습니다.

요즈음 먼가 알 것 같으면서 모르겠는 수업시간들이 나날이 이어져가고 있어요....

변수와 함수가 많고 파일과 파일로 데이터들이 왔다갔다하니까

등장인물 많은 외국 영화를 보는 느낌..?이랄까...

저는 외국인 배우들을 잘 기억도 못하고 구분도 못하는 편인데,

비슷하게 생긴 등장이 많아서 그런 것 같아요 ㅋㅋㅋㅋㅋ

 

아무튼, 먼저 폼 유효성에 대해 먼저 배웠습니다!.

 

checkValidity()

HTML5에서 제공하는 폼 유효성 검사 메소드 중 하나입니다. 

 

폼 요소의 데이터 유효성을 검사하는데, 여기서 검사하는 것들은 대충.....

필수 태그에 value가 채워져있는지, 데이터 형식이 올바른지, 최솟값과 최댓값이 있다면 만족하는 지 등이 있습니다.

 

이 검사를 통해 사용자에게 피드백을 제공할 수 있어요

        async function axiosGet() {
            console.log('axiosGet 함수 호출!');
            const form = document.forms['register'];
            const data = {
                name: form.name.value,
                gender: form.gender.value,
            };
            try{
                if (!form.name.checkValidity()){ // name value의 checValidity() = false라면
                    resultBox.textContent = `이름을 입력해주세요!`;
                } else if (!form.gender[0].checkValidity()){
                    resultBox.textContent = `성별을 선택해주세요!`;
                } else {
                    const response = await axios({
                    method: 'get', 
                    url: '/axios', 
                    params: data,
                    });
                    const {name, gender} = response.data;
                    resultBox.textContent = `GET /axios 요청 완료!! ${name}님은 ${gender}이시군요!`;
                }
            } catch (error) {
                console.log('Error!', error);
                resultBox.textContent = `알 수 없는 에러 발생!`;
            };

 

위의 코드는 axiosGet() 함수에서 axios를 동기적으로 처리한 함수입니다.

다른 것보다 try 구문 내부의 if, else if, else문을 보면 됩니다.

 

checkValidity()함수는 결과 값으로 데이터가 유효하다면 true를, 유효하지 않다면 false를 반환하므로

그 점을 이용하여 이렇게 if문을 걸어서 유효성에 따라 사용자에게 경고를 할 수 있습니다.


그 다음은 제가 바로 등장인물 많은 외국영화를 보는 것 같다고 했던 웹 페이지 파일 업로드 부분입니다.

일단 제일 중요한 단어가 등장하죠.

 

0. multer

multer는 node.js에서 파일 업로드를 처리하기 위한 미들웨어입니다.

웹 사용자가 웹에 파일 데이터를 등록하면, 서버에 파일을 넘겨주는 과정을 쉽게 할 수 있도록 도와줍니다.

 

multer에 대한 간단한 예시 코드를 보도록 하겠습니다.

const multer = require('multer');
const upload = multer({
  dest: 'uploads/', // dest: 클라이언트가 업로드한 파일을 저장할 서버측 경로
                    // => uploads/라는 파일에 사용자가 업로드한 파일을 저장하겠다.
});

 

require 내부 함수를 이용하여 multer 미들웨어를 불러와서 multer변수에 넣어줍니다.

그리고 설명에 적혀있듯이,

dest라는 multer내 모듈 설정 옵션을 선택하여 서버측 경로를 설정해줍니다.

이렇게 서버 측 경로를 정해주면,

uploads라는 디렉토리가 저절로 생겨서 그 안에 등록 파일들이 쌓이게 됩니다.

 

이는 multer를 알아보기 위한 정말 간단한 예제로,

보통 multer에 대한 세부 설정을 해서 쓴다고 합니다.


하여 저희도 multer에 대한 커스터마이징을 해보겠습니다.

const uploadDetail = multer({
    storage: multer.diskStorage({
        destination(req, file, done) { // done: callback 함수이다.
            done(null,'uploads/'); // 파일을 업로드할 경로 설정
        },
        filename(req, file, done) {
            const ext = path.extname(file.originalname); // 파일 "확장자"를 추출 (.txt, .png 등)
            done(null, path.basename(file.originalname, ext) + Date.now() + ext); // 파일이름 + 날짜 + 확정자
        },
    }),
    limits: {fileSize: 5* 1024 * 1024}, // 5MB를 의미
});

uploadDetail이라는 변수에 multer를 커스터마이징하여 넣어서 활용할 겁니다.

 

multer내 storage옵션으로 저장할 공간에 대한 정보를 지정해줄게요

multer.diskStorage(): 업로드 된 파일을 디스크에 저장할 때 방식을 세밀하게 지정할 수 있는 옵션입니다.

이 multer.diskStorage()를 사용하려면, destinationfilename 두 개의 인자를 받아서 객체를 생성합니다.

storage: multer.diskStorage({
        destination(req, file, callback) { // 세 개의 인자를 매개변수로 받음
            callback(null,'uploads/'); // 파일을 업로드할 경로 설정
        },
        filename(req, file, callback) {
            const ext = path.extname(file.originalname); // 파일 "확장자"를 추출 (.txt, .png 등)
            // basename: 원본 파일에서 확장자를 제외한 파일 이름만 추출
            callback(null, path.basename(file.originalname, ext) + Date.now() + ext); // 파일이름 + 날짜 + 확정자
        },

 destinationreq (요청 객체), file (업로드 된 파일 객체), callback (함수) 라는 세 개의 매개변수를 받고,

 

callback함수의 매개변수로 null (에러 처리 객체) , 'uploads/' (파일의 저장 경로 or 파일명)을 받습니다.

이러한 세밀하나 지정을 통해 저장 방식 및 어디에 저장할 것인지 결정할 수 있습니다.

 

filename도 마찬가지로 req (요청 객체), file (업로드 된 파일 객체), callback (함수) 라는 세 개의 매개변수를 받습니다.

callback함수도 마찬가지로 매개변수로 null(에러 처리 객체), (저장할 파일명에 대한 지정) 을 받습니다.

 

limits: {fileSize: 5* 1024 * 1024}

limits라는 옵션으로 fileSize에 대한 제한을 해줄겁니다!

현재 제가 수업에서 지정한 limit은 5MB를 의미합니다 : D


multer에 대한 세부적인 설정이 끝났다면, 파일을 본격적으로 업로드 했을 때 어떻게 서버로 가져올 수 있을까요?

방법은 3가지가 있습니다.

1. single()
2. array()
3. fields()

 

1. single( fieldname)

 

single은 하나의 파일만 업로드할 때 사용합니다. 

app.post('/upload', uploadDetail.single('userFile'), (req, res) => {
    res.send('파일 업로드 완료!');
    console.log(req.file);
});

지난 번에 배운 post와 형식이 똑같은데, 경로와 함수 사이에 미들웨어를 등록해주면

post로 보낼 때 미들웨어를 사용할 수 있습니다. ( uploadDetail.single('userFile') 부분을 지칭한다. )

 

저희가 앞서 설정한 uploadDetail를 활용하여 single('fieldname') 을 넣어주게 되면

<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="userFile" /><br />

위와 같은 HTML 파일에서 input type= "file"이고  name이 앞선 single에 넣었던

fieldname(이 코드에서는 "userFile" 부분 )와 동일한 부분을 찾아가게 됩니다.

 

( * 이 HTML 코드에서 enctype="multipart/form-data"는 multer를 사용하겠다는 선언입니다. )

 

그래서 이 파일에 대한 정보들은 하기 사진과 같이 req.file에 객체 형태로 담게 됩니다.

cat.jpeg를 single에 업로드한 console결과


2. array(fieldname[, maxCount])

 

파일 여러 개를 한꺼번에 처리하는 메서드입니다.

수업 시간에는 안나왔는데 여기서 maxCount는 최대로 올릴 수 있는 파일의 갯수 limit을 지정할 수 있습니다.

app.post('/upload/array', uploadDetail.array('userFiles'), (req,res)  => {
	console.log(req.files);
	res.send('하나의 인풋에 여러 파일 업로드 완료!');
});

같은 미들웨어라서, post내 url과 함수 중간에 미들웨어에 대해 적어주는 것은 같습니다.

 

이 array는 하나의 파일 등록 버튼에 여러 개의 파일을 동시에 올릴 수 있어서

console.log로 req.files를 출력해본다면 한 객체에 값이 여러 개 인것을 알 수 있습니다.

정확히는 대괄호 묶여서 배열 안에 여러 개의 객체가 들어가는 형태가 됩니다.

여기서 유의할 것은 single과 달리 req.file이 아닌 req.files에 담긴다는 점!


 

3. fields(fields)

 

여러 개의 파일을 각각의 인풋에 업로드 하여 하나의 객체로 묶어 처리할 수 있는 메서드 입니다.

app.post('/upload/fields',
	uploadDetail.fields([{ name: 'userFile1' }, { name: 'userFile2' }]),
	(req, res) => {
	  console.log(req.files);
	  res.send('하나의 인풋에 여러 파일 업로드 완료!');
	}
  );

이때 post내 매개변수로 두번째에 미들웨어에 대한 정보가 들어가는 것은 똑같은데

fields에 대한 구체적인 값을 전달하고 있습니다.

 

하기 연결된 HTML을 본다면,  file 인풋 태그에 각각 name이 있는 것을 볼 수 있습니다.

 <form action="/upload/fields" method="POST" enctype="multipart/form-data">
      <input type="file" name="userFile1"/><br />
      <input type="text" name="title1" /><br />
      <input type="file" name="userFile2"/><br />
      <input type="text" name="title2" /><br /><br />
      <button type="submit">업로드</button>
    </form>

이 name값을 넘겨주고 매칭을 해주고 req.files를 console에 출력해주면 하기와 같습니다.

아까 array와 비슷하지만, 객체 안에 배열이 있고, 배열에 이름이 붙었으며

배열안에 객체가 생성되어있는 것을 볼 수 있습니다.


이처럼 3가지의 매서드는 각각 서버에 넘기는 req.file(s)의 형태가 다르고, 방식이 약간씩 달라서

웹 어플리케이션이 맞는 함수로 구현하여 사용하면 좋을 것같습니다 :)