1. 폼 유효성 검사
console.log(form.name.checkValidity())
어떤 요소를 선택해서 checkValidity()매서드를 사용하면 작성되었는지 않았는지 확인가능
<!-- 이름, 성별(라디오버튼)을 정보를 입력받는 폼 -->
<form name="register">
<label for="name">이름</label>
<input type="text" id="name" name="name" required />
<br />
성별
<input type="radio" name="gender" value="m" id="male" required />
<!-- radio에서 상위 하나에만 required넣으면 하위항목은 자동으로... -->
<label for="male">남</label>
<input type="radio" name="gender" value="f" id="female" />
<label for="female">여</label>
<br />
<br />
async function axiosGet() {
console.log('axiosGet 함수 호출!');
const form = document.forms['register'];
const data = {
name: form.name.value,
gender: form.gender.value,
};
console.log(form.name.checkValidity());
//같은 name으로 묶인 radio button은 하나만 선택되더라도 true를 반환
console.log(form.gender[0].checkValidity());
console.log(form.gender[1].checkValidity());
try {
//폼 유효성 검사
//name input에 입력된 값이 없다면, '이름을 입력해주세요!'
//gender radio button을 선택하지 않았다면, '성별을 선택해주세요!'
//둘다 입력이 잘 되었다면, axios로 back에 요청 날리기
const response = await axios({
method: 'get',
url: '/axios',
params: data,
}); // response: 서버의 응답 결과
const { name, gender } = response.data; // 객체 구조분해 할당
if (!form.name.checkValidity()) {
resultBox.textContent = '이름을 입력해주세요!';
} else if (!form.gender[0].checkValidity()) {//gender배열형태로 넣어야함 안그러면 에러뜸
resultBox.textContent = '성별을 선택해주세요!';
} else {
resultBox.textContent = `GET /axios 요청 완료!! ${name}님은 ${gender}이시죠?ㅎㅎ`;
}
} catch (error) {
resultBox.textContent = `알 수 없는 에러 발생! 다시 시도해주세요~`;
console.log('Error!', error);
}
}
if문과 checkValidity()매서드를 사용해서 폼 유효성검사를 해보았습니다!
정리해보자면 폼 유효성 검사란 checkValidity()매서드를 이용해서 input안에 내용이 입력되었는지 확인하는 것이고, checkValidity()는 입력된 값이 있으면 true를 없다면 false를 반환하는 매서드라 볼 수 있겠네요!
일반 폼 전송 vs 동적 폼 전송
일반 폼 전송
form 태그 안에 button type="submit"
제출 되었을 때 다른페이지로 이동
동적 폼 전송
form태그 필요가 없음. button type="button"
제출 되었을 때 현재경로에서 요청에 대한 결과를 받음(버튼 타입이 submit이 아닌 이유)
-->물론 자바스크립트 코드를 이용해 결과를 받은 후에 다른 페이지로 이동 가능
2. 파일 전송하기
1) 정적 파일 업로드
body-parser
멀티파트 데이터(이미지 동영상 파일 등)을 처리하지 못한다==> multer를 이용한다!!
지금까지는 텍스트만 사용해서 괜찮았지만...
"npm install multer"을 터미널에 입력해서 multer를 설치하고...
"dependencies": {
"ejs": "^3.1.9",
"express": "^4.18.2",
"multer": "^1.4.5-lts.1"//추가된것!
}
<!-- multer는 multipart가 아닌 폼에서는 동작하지 않음! 따라서 enctype="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>
//multer관련 설정
const multer = require('multer');
const upload=multer({
dest:'uploads/',//dest: 클라이언트가 업로드한 파일을 저장할 서버측 경로
})
//1. single():하나의 파일을 업로드
app.post('/upload',upload.single('userfile'),(req,res)=>{
res.send('파일 업로드 완료!')
})
multer는 미들웨어! 서버와 클라이언트의 중간에서 파일을 업로드해야할 위치
dest만 설정할 경우 파일형식이 마음대로 저장되어서 파일이 열리지 않음
--> multer세부설정을 통해서 경로뿐 아니라 파일명, 파일 크기 등을 직접 지정, 제어 할 수 있다...
path 내장 모듈
// path 내장 모듈
const path = require('path');
const ext = path.extname('hello.txt'); // 파일명에서 확장자만 추출
const base = path.basename('hello.txt', ext); // basename: 원본 파일에서 확장자를 제외한 파일 이름만 추출
const result = base + Date.now() + ext; // 파일이름 + 날짜 + 확장자
console.log(ext); // .txt
console.log(base); // hello
console.log(result);
// 파일명 결과에 왜 Date.now()를 추가해서 저장할까?
// 1. 파일 이름 중복을 막기위해
// 2. 파일 이름만 보고 파일이 저장된 시점 유추 가능
다시 multer세부 설정으로 돌아와서
const path = require('path'); //경로에 관한 내장모듈
//multer 세부 설정
const uploadDetail = multer({
//storage:저장할 공간에 대한 정보
storage: multer.diskStorage({
destination(req, file, done) {
//done: callback함수
//done(null,xx)여기에서 null은 error를 의미하는 매개변수!
//에러가 없으므로 "null"이라고 전달하여 콜백함수를 호출!
done(null, 'uploads/'); //파일을 업로드 할 경로를 설정
},
filename(req, file, done) {//두번째 인자로 파일!!!!!
const ext = path.extname(file.originalname); //extname():파일"확장자"를 추출
done(null, path.basename(file.originalname, ext) + Date.now() + ext); //basename():원본 파일에서 확장자를 제외한 파일 이름만 추출
//파일이름+날짜+확장자
//날짜를 추가해서(Date.now()) 파일이름을 저장하는 이유는??
//1. 파일 이름 중복을 막기 위해서
//2. 파일 이름만 보고 파일이 저장된 시점 유추 가능
},
}),
//limits:파일 제한 정보
limits: { fileSize: 5 * 1024 * 1024 }, //5MB로 파일 크기를 제한,
});
path를 사용할거기 때문에 require('path')로 path 내장모듈을 불러와야 사용이 가능합니다.
또한
<h2>Multi file upload ver1</h2>
<p>하나의 인풋에 여러개의 파일을 업로드</p>
<form action="/upload/array" method="POST" enctype="multipart/form-data">
<input type="file" name="userfile" multiple /><br />
<!-- input 속성으로 "multiple"추가해야함! -->
<input type="text" name="title" /><br /><br />
<button type="submit">업로드</button>
</form>
여러개의 파일을 업로드 하기 위해서는 input 속성에 multiple을 추가해야합니다!
// 2. array(): 여러 파일을 한 번에 업로드
// uploadDetail.array('userfiles'): 클라이언트 요청이 들어오면
// multer 설정(uploadDetail 변수)에 따라 파일을 업로드한 후, req.files 객체 생성
app.post('/upload/array', uploadDetail.array('userfiles'), (req, res) => {
console.log(req.files); // [ { 파일1_정보 }, { 파일2_정보 }, .. ] : 배열 형태로 각 파일 정보를 출력
console.log(req.body);
res.send('하나의 인풋에 여러 파일 업로드 완료!');
});
서버에서는 이렇게 작성되어야 하고 미들웨어에는 위에서 작성했던 uploadDetail을 사용해서 파일 이름을 저장하도록 할게요!
<h2>Multi file upload ver2</h2>
<p>여러 인풋에 여러개의 파일을 업로드</p>
<form action="/upload/fields" method="POST" enctype="multipart/form-data">
<input type="file" name="userfile1" multiple /><br />
<input type="text" name="title1" /><br /><br />
<input type="file" name="userfile2" multiple /><br />
<input type="text" name="title2" /><br /><br />
<button type="submit">업로드</button>
</form>
//3.fields():여러 파일을 각각 인풋에 업로드
//req.files에서 파일 정보를 확인
//fields()매게 변수로 input 태그의 name을 각각 넣기
app.post(
'/upload/fields',
uploadDetail.fields([{ name: 'userfile1' }, { name: 'userfile2' }]),
(req, res) => {
console.log(req.files); // {userfile1:[{파일_정보}],userfile2:[{파일_정보}]}객체 안에 배열형태로 각파일 정보
console.log(req.body);
res.send('하나의 인풋에 여러 파일 업로드 완료!');
}
);
특정 패키지의 특정 버전에서 발생하는 이슈!
multer 1.4.5 버전을 이용할 때 한글 파일명 깨짐 이슈
해결방법
1. multer 설치 한 것을 삭제 -> 1.4.4 버전으로 지정해서 다시 설치
npm uninstall multer # 패키지 삭제
npm install multer@1.4.4
2. package.json에서 multer의 버전을 1.4.4 로 지정하고, node_modules 폴더 삭제 후, npm i 로 dependencies 항목 전체 재설치
이러한 방법으로 해결할 수 있습니다.
2) 동적 파일 업로드
<h2>동적 파일 업로드</h2>
<!-- 동적폼전송은 form태그 필요 없음!!-->
<input type="file" name="dynamicUserfile" id="dynamic-file" /><br />
<button type="submit" onClick="fileUpload()">업로드</button>
<br />
<!-- 업로드한 이미즈를 보여줄 img 태그 요소 -->
<img src="" alt="" width="200" />
<script>
function fileUpload() {
console.log('동적 파일 업로드');
//js 만으로 폼을 전송(파일 데이터를 서버로 전송해야하는 케이스)
//FormData 객체를 활용하면 쉽게 전송 가능!
const formData = new FormData(); //formData():formData에 대한 여러가지 일을 할 수 있는 js 내장 객체
const file = document.querySelector('#dynamic-file');
console.dir(file); //업로드한 파일 객체
console.dir(file.files); //업로드한 첫 파일
console.dir(file.files[0]);
//append(key,value)
formData.append('dynamicUserfile', file.files[0]);
axios({
method: 'post',
url: '/dynamicFile',
data: formData,
header: { 'Content-Type': 'multipart/form-data' }, //enctype="multipart/form-data"
}).then((res) => {
console.log(res);
const { data } = res; //구조분해로 data에 꺼내옴
//서버에 있는 이미지 경로-->app.js에서 static미들웨어를 등록해야한다.
console.log(data);
console.log(data.path);
document.querySelector('img').src = '/' + data.path;
// '/'을 붙이는 이유는 절대경로로 하기 위해서임
//data.path는 const {path}=data를 해서 객체 구조분해 후 그냥 path로 작성할 수도 있다.
});
}
</script>
app.post('/dynamicFile', uploadDetail.single('dynamicUserfile'), (req, res) => {
console.log(req.file);
res.send(req.file);
});
여기에서 보면
const formData = new FormData(); //formData():formData에 대한 여러가지 일을 할 수 있는 js 내장 객체
라고 되어 있는데 FormData 내장 객체에 대해 좀 더 알아 보았습니다!
🌐 FormData 사용법 & 응용 총정리 (+ fetch 전송) (tistory.com)
이렇게 오늘은 checkValidity를 통한 폼유효성검사, 파일 업로드에 대해서 배워보았습니다!