게시판 - 파일첨부 기능 만들기 4 (수정/삭제)

소스코드

이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.

이 게시물의 소스코드는 게시판 만들기(고급) / 게시판 - 파일첨부 기능 만들기 3 (리스트에 아이콘 추가)에서 이어집니다.

board.git 을 clone 한 적이 있는 경우: 터미널에서 해당 폴더로 이동 후 아래 명령어들을 붙여넣기합니다. 폴더 내 모든 코드가 이 게시물의 코드로 교체됩니다. 이를 원치 않으시면 이 방법을 선택하지 마세요.

git reset --hard
git pull
git reset --hard 8a25436
git reset --soft d576566
npm install
atom .

board.git 을 clone 한 적이 없는 경우: 터미널에서 코드를 다운 받을 폴더로 이동한 후 아래 명령어들을 붙여넣기하여 board.git 을 clone 합니다.

git clone https://github.com/a-mean-blogger/board.git
cd board
git reset --hard 8a25436
git reset --soft d576566
npm install
atom .

- Github에서 소스코드 보기: https://github.com/a-mean-blogger/board/tree/8a25436059c830c61c0f851acad240349d80af90


첨부파일을 수정하거나 삭제하는 기능을 추가하여 첨부파일 기능을 완성해봅시다.

폴더 구조

코드 - ejs

<!-- views/posts/edit.ejs -->

      <form action="/posts/<%= post._id %>?_method=put<%= getPostQueryString(true) %>" enctype="multipart/form-data" method="post"> <!-- 1 -->

        <div class="form-group">
          ...
        </div>

        <div class="form-group"> <!-- 2 -->
          <label for="title">Attachment</label>

          <input type="hidden" id="attachment" name="attachment" value="<%= post.attachment?post.attachment._id:'' %>">  <!-- 2-1 --> 
          <% if(post.attachment){ %> <!-- 2-2 --> 
            <div class="input-group mb-3" id="currentAttachemnt">
 <!-- 2-3 --> <input type="text" class="form-control" value="<%= post.attachment.originalFileName %>" readonly />
              <div class="input-group-append">
   <!-- 2-4 --> <button class="btn btn-outline-secondary" type="button" onclick="$('#attachment').val('');$('#currentAttachemnt').hide();$('#newAttachment').removeClass('d-none');">Delete</button>
              </div>
            </div>
          <% } %>
          <input type="file" id="newAttachment" class="form-control-file <%= post.attachment?'d-none':'' %>" name="newAttachment" id="uploadImage"> <!-- 2-5 -->
        </div>

        <div class="form-group">
          <label for="body">Body</label>
          <textarea id="body" name="body" rows="5" class="form-control <%= (errors.body)?'is-invalid':'' %>"><%= post.body %></textarea>
          <% if(errors.body){ %>
            <span class="invalid-feedback"><%= errors.body.message %></span>
          <% } %>
        </div>

1. 게시판 - 파일첨부 기능 만들기 1 (업로드) 강의의 post new view의 form과 마찬가지로 enctype="multipart/form-data"를 form 태그에 추가해줍니다.

2. Attachment 필드가 추가됩니다. 첨부파일이 있는 경우, 첨부파일의 이름과 삭제 버튼을 보여주고, 첨부파일이 없는 경우 첨부파일을 업로드할 수 있는 버튼(input type="file")을 보여줍니다.

2-1. 현재 게시물의 첨부파일 정보를 담고 있는 input name="attatchment"를 hidden type으로 form에 추가해줍니다. 물론 첨부파일이 없다면 value는 ''입니다.

2-2. 첨부파일의 이름과 삭제버튼은 post의 attachment가 있는 경우에만 표시합니다.

2-3. 현재 첨부된 파일의 이름을 표시하는 부분입니다.

2-4. 첨부파일 삭제버튼입니다. 이 버튼을 클릭하면 2-1번의 attachment의 값을 지우고(''로 변경), 2-2번의 태그들을 숨기고, 2-5의 태그를 보여줍니다.

2-5. 파일 업로드 버튼입니다. 첨부파일이 있는 경우 'd-none' css class가 추가되어 숨겨져 있다가, 2-4번의 버튼을 누르면 보여집니다. 첨부된 파일은 'newAttachment'로 서버에 전달됩니다.

코드 - js

// routes/posts.js

...

// edit
    ...
    Post.findOne({_id:req.params.id})                           // 1
      .populate({path:'attachment',match:{isDeleted:false}})    // 1
      .exec(function(err, post){                                // 1
        if(err) return res.json(err);
        res.render('posts/edit', { post:post, errors:errors });
      });
  ...

// update
router.put('/:id', util.isLoggedin, checkPermission, upload.single('newAttachment'), async function(req, res){ // 2
  var post = await Post.findOne({_id:req.params.id}).populate({path:'attachment',match:{isDeleted:false}}); // 2-1
  if(post.attachment && (req.file || !req.body.attachment)){ // 2-2
    post.attachment.processDelete();
  }
  req.body.attachment = req.file?await File.createNewInstance(req.file, req.user._id, req.params.id):post.attachment; // 2-3
  req.body.updatedAt = Date.now();
  ...

1. 게시판 - 파일첨부 기능 만들기 1 (업로드) 강의의 show route과 마찬가지로 populate을 설정하여 attachment.isDeleted가 false인 경우에만 attachment를 populate합니다.

2. multer를 사용한 upload.single('newAttachment') 미들웨어가 추가되었고, callback함수에 async 키워드가 추가되었습니다.

2-1. 1번과 같은 방식으로 post에 attachment를 populate합니다. 첨부파일 비교를 위해 기존의 post를 불러오는 과정입니다.

2-2. 수정 전의 post에 attachment가 존재했었지만, 현재 multer를 통해 req.file이 생성되었거나 form body의 attachment가 없다면 file 인스턴스의 processDelete함수를 호출합니다.

2-3. req.file이 존재하면 file 모델의 createNewInstance함수로 attachment를 만들어 넣습니다.

실행 결과

첨부파일이 있는 게시물의 수정 버튼을 누릅니다.

현재 첨부되어 있는 파일 이름이 표시됩니다. delete을 눌러봅시다.

첨부된 파일 이름이 사라지고 새로운 파일을 첨부할 수 있는 버튼이 생겼습니다. 이상태로 게시물을 저장하면 게시물에서 첨부파일이 지워지게 됩니다. 새로운 파일을 첨부하고 저장해봅시다.

첨부파일이 변경되었습니다.

마치며...

이것으로 첨부파일 기능에 관련된 강의는 끝입니다. 하지만 파일첨부 강의는 끝이 아닙니다. 다음 강의에서는 파일을 서버에 저장하지 않고, 서드파티 사이트의 API를 통해 저장하는 방법에 대해 알아보겠습니다.

댓글

강민규1 2020.09.09
안녕하세요 덕분에 노드를 잘 공부하고 있는 사람입니다.
강의를 보면서 항상 감사한 마음을 가지고 있습니다 ㅎㅎ
다름이 아니라
오탈자(?)가 있어서 제보 드립니다. 첨부파일 수정 소스에서 
routes/posts.js 스크린샷(?)에는 아래와 같이 나와 있습니다.
req.body.attachment = req.file?await createNewAttachment(req.file, req.user._id, req.params.id):post.attachment; // 2-3
해당 소스에 대한 설명은
2-3. req.file이 존재하면 file 모델의 createNewFile함수로 attachment를 만들어 넣습니다. 로 되어 있구요
아무리 찾아도 createNewAttachment 이 함수나 createNewFile 이 함수를 찾을 수가 없어서 깃에서 파일을 내려 받아서 확인해 봤더니
req.body.attachment = req.file?await File.createNewInstance(req.file, req.user._id, req.params.id):post.attachment;
이런 소스로 되어 있었습니다.
해당 부분에서 참고해 주시기 바랍니다 ^^
I
Ian H 2020.09.09
@강민규1,
제보주셔서 감사합니다. 해당부분은 수정하였습니다. 감사합니다!
댓글쓰기

이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기

UP