반응형

이전까지 진행 상황

[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'로 저장됨

이미지가 추가된 게시글 보기 화면

반응형

↓ 클릭시 이동

복사했습니다!