반응형
이전까지 진행 상황
[Node.js + Vue.js] 게시판 만들기 1. 설계 & 결과
[Node.js + Vue.js] 게시판 만들기 2. Vue.js를 사용한 Front Server 구현
[Node.js + Vue.js] 게시판 만들기 3. Node.js를 사용한 Back Server 구현
[Node.js + Vue.js] 게시판 만들기 4. 검색 기능 추가
추가 기능 정리
- Vue의 FormData와 Node.js의 multer을 사용한 이미지 업로드 기능
- 글 작성 시 이미지 추가 할 수 있게함 (여러개 가능)
- 추가한 이미지 미리보기
- 게시판에서 글 선택시 글 내용과 함께 업로드한 이미지들 나오게 함
- 업로드한 이미지는 로컬 파일에 저장하고 불러옴
추가 코드
Front -> Write.vue template 부분 추가코드
<v-file-input class="input" type="file" counter show-size label="이미지 제출(여러개 가능)"
outlined dense multiple prepend-icon="mdi-camera" style="width: 400px; margin-left: 100px;"
@change="onImageChange"/>
<v-img v-for="(item,i) in uploadimageurl" :key="i" :src="item.url"
contain height="150px" width="200px" style="border: 2px solid black; margin-left:100px;"/>
- <v-file-input> 태그의 type="file"을 통해 파일 업로드 가능하게 함
- multiple 속성을 통해 파일 여러개 업로드 가능하게 함
- @change 이벤트를 통해 이미지가 선택되면 onImageChange 메소드 호출
- <v-img> 태그는 업로드한 이미지 미리보기를 위해 사용
Front -> Write.vue script 부분 추가코드
// data속성에서 추가되는 변수들
uploadimageurl: [], // 업로드한 이미지의 미리보기 기능을 위해 url 저장하는 객체
imagecnt: 0, // 업로드한 이미지 개수 => 제출버튼 클릭시 back서버와 axios 통신하게 되는데, 이 때 이 값도 넘겨줌
// 수정된 onSubmitForm => imagecnt도 같이 전송해줘야 함
onSubmitForm(){
if(this.$refs.form.validate()) { // 위에 써준 rules를 만족하면 실행
axios({
url: "http://127.0.0.1:52273/content/write/",
method: "POST",
data: {
boardnum: this.$route.params.id,
writer: this.writer,
title: this.title,
text: this.text,
imagecnt: this.imagecnt
},
}).then(res => {
alert(res.data.message);
window.history.back();
}).catch(err => {
alert(err);
});
}
},
// 추가된 method
onImageChange(file) { // v-file-input 변경시
if (!file) {
return;
}
const formData = new FormData(); // 파일을 전송할때는 FormData 형식으로 전송
this.uploadimageurl = []; // uploadimageurl은 미리보기용으로 사용
file.forEach((item) => {
formData.append('filelist', item) // formData의 key: 'filelist', value: 이미지
const reader = new FileReader();
reader.onload = (e) => {
this.uploadimageurl.push({url: e.target.result});
// e.target.result를 통해 이미지 url을 가져와서 uploadimageurl에 저장
};
reader.readAsDataURL(item);
});
axios({
url: "http://127.0.0.1:52273/content/imagesave/", // 이미지 저장을 위해 back서버와 통신
method: "POST",
headers: {'Content-Type': 'multipart/form-data'}, // 이걸 써줘야 formdata 형식 전송가능
data: formData,
}).then(res => {
console.log(res.data.message);
this.imagecnt = file.length; // 이미지 개수 저장
}).catch(err => {
alert(err);
});
},
Front -> Content.vue template 부분 추가코드
<v-img v-for="(item, i) in imagelist" :key="i" :src="require(`../../../node-back/uploads/${item}`)"
contain height="150px" width="200px" style="border: 2px solid black; margin-left:100px;"/>
- 나중에 back 서버에 이미지 전송 시 'node-back/uploads/이미지명' 이런식으로 저장하게 해서 다음과 같이 이미지 불러옴 => 본인이 이미지를 저장할 경로를 지정해 주면 됨
- node-back(back project)에 uploads 디렉토리 생성 필요
Front -> Content.vue script 부분 추가코드
// data속성에서 추가되는 변수들
imagelist: [], // 불러온 이미지들의 url을 저장하는 객체
imagecnt: 0, // 불러올 이미지 개수 (db에서 받아옴)
// 이미지 수정, 삭제 기능은 없어서 처음에 불러오는 부분만 수정
mounted() {
axios({
url: "http://127.0.0.1:52273/content/content/",
method: "POST",
data: {
id: this.$route.query.id
},
}).then(res => {
this.writer = res.data.writer;
this.title = res.data.title;
this.createdAt = res.data.createdAt.split('T')[0] ;
this.updatedAt = res.data.updatedAt.split('T')[0];
this.text = res.data.text;
this.imagecnt = res.data.imagecnt; // db에서 새로운 field인 imagecnt 값도 받아옴
for(var i = 1; i <= res.data.imagecnt; i++){
this.imagelist.push(this.$route.query.id + '-' + i + '.png');
// 이미지를 저장할 때, '글id - 1.png', '글id - 2.png', ... 이런식으로 저장할 것임
}
}).catch(err => {
alert(err);
});
},
Back
- DB에 'imagecnt' 라는 field 추가 -> '/write' 에서 imagecnt 값 전송
- 위에서 언급한 것처럼 이미지를 저장할 때, '글id - 1.png', '글id - 2.png' 이런식으로 저장
- fs모둘, multer 모듈 설치 필요
- npm i fs multer
Back -> routes/content.js 추가코드
var multer = require('multer');
var fs = require('fs');
// multer을 이용해 파일 업로드 기능 구현
var storage = multer.diskStorage({
destination: function (req, file, cb) { // 경로 => uploads 폴더
cb(null, 'uploads/')
},
filename: function (req, file, cb) { // 파일명 => 이미지 업로드시 원본 이름 그대로
cb(null, file.originalname);
}
})
var upload = multer({ storage: storage });
// 글 작성 수정 부분
// imagecnt도 같이 받아줘야 함
router.post('/write', function(req, res){ // 글 작성
db.content.create({ // 게시판번호와 게시글정보를 req.body로 받아와 db에 삽입
boardnum: req.body.boardnum,
writer: req.body.writer,
title: req.body.title,
text: req.body.text,
imagecnt: req.body.imagecnt
}).then(function(){
return res.status(200).json({
message: '글 작성 완료!',
})
}).catch(function(err){
console.log(err);
return res.status(404).json({message: '에러뜸'});
})
});
// 글 작성 페이지에서 이미지를 올리면 실행되게 되는 부분
router.post('/imagesave', upload.array('filelist'), function(req, res) {
// 전송된 formdata의 filelist에 해당하는 value 값들을 multer을 통해 저장
var i, newname;
db.content.findOne({ // 새 글 작성시 기존에 있던 가장 큰 id+1로 자동 저장되므로 가장 큰 id를 찾아줌
limit: 1,
order: [['id', 'DESC']],
raw:true,
}).then(result => {
try {
newname = result.id;
} catch (e) {
// 등록된 글이 하나도 없으면 id가 Null이여서 에러 발생
// 이 상황에서는 newname을 0으로 설정해줌
newname = 0;
}
for(i=0;i<req.files.length;i++) {
fs.renameSync(req.files[i].path, 'uploads/'+(newname+1)+'-'+(i+1)+'.png');
// 위에서 이미지이름을 원본으로 저장해줬었는데 file system을 통해 이름을 바꿔주는 작업
}
return res.status(200).json({message:'이미지업로드완료!'});
}).catch(err => {
console.log(err);
return res.status(404).json({message: '에러뜸'});
})
});
결과
이미지 업로드 기능이 추가된 글쓰기 화면
글쓰기 화면에서 이미지를 추가한 모습
제출하면 다음과 같이 로컬 디렉토리에 '게시글id-사진 번호.png'로 저장됨
이미지가 추가된 게시글 보기 화면
반응형
'Node.js' 카테고리의 다른 글
[Node.js] Swagger 사용하기 2 - 코드 분리, components 사용 (0) | 2021.12.22 |
---|---|
[Node.js] Swagger 사용하기 (0) | 2021.12.14 |
[Node.js + Vue.js] 게시판 만들기 4. 검색 기능 추가 (0) | 2021.12.11 |
[Node.js + Vue.js] 게시판 만들기 3. Node.js를 사용한 Back Server 구현 (0) | 2021.12.11 |
[Node.js + Vue.js] 게시판 만들기 2. Vue.js를 사용한 Front Server 구현 (0) | 2021.12.11 |