이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.
이 게시물의 소스코드는 게시판 만들기(고급) / 게시판 - 파일첨부 기능 만들기 2 (다운로드)에서 이어집니다.
board.git 을 clone 한 적이 있는 경우: 터미널에서 해당 폴더로 이동 후 아래 명령어들을 붙여넣기합니다. 폴더 내 모든 코드가 이 게시물의 코드로 교체됩니다. 이를 원치 않으시면 이 방법을 선택하지 마세요.
board.git 을 clone 한 적이 없는 경우: 터미널에서 코드를 다운 받을 폴더로 이동한 후 아래 명령어들을 붙여넣기하여 board.git 을 clone 합니다.
- Github에서 소스코드 보기: https://github.com/a-mean-blogger/board/tree/d57656674da8fc91dbee23342092e8578b4b5cdd
게시물에 첨부파일이 있는 경우 post index view에 아이콘을 추가해 봅시다. 이번 강의는 좀 짧지만 다음 강의와 합치자니 너무 복잡해지는것 같아서 분리했습니다.
일단 어떤식으로 이번 강의의 코드를 만들 수 있을지 생각해 봅시다. 게시판 - 댓글 기능 만들기 4 (댓글 수 표시) 강의와 비슷할 것이라고 추측할 수 있는데 맞습니다.
다만, 댓글에서는 isDeleted가 true
이면 댓글의 내용을 숨기기만 했는데, 첨부파일은 isDeleted가 true
이면 아예 해당 데이터가 없는것처럼 만들었습니다.
// routes/posts.js ... // Index ... posts = await Post.aggregate([ ... { $lookup: { from: 'comments', ... } }, { $lookup: { // 1 from: 'files', localField: 'attachment', foreignField: '_id', as: 'attachment' } }, { $unwind: { // 2 path: '$attachment', preserveNullAndEmptyArrays: true } }, { $project: { title: 1, author: { username: 1, }, views: 1, numId: 1, attachment: { $cond: [{$and: ['$attachment', {$not: '$attachment.isDeleted'}]}, true, false] }, // 3 createdAt: 1, commentCount: { $size: '$comments'} } }, ]).exec(); ...
1. post에 file을 $lookup을 통해 post.attachment로 연결합니다. 게시판 - 댓글 기능 만들기 4 (댓글 수 표시)강의에서 $lookup에 대해 설명했으므로 이 설명은 생략합니다.
2. $lookup은 항상 배열로 해당 조건을 만족하는 모든 데이터를 배열로 연결하는 특징이 있고, 이 배열을 풀어주려면 $unwind를 사용하는 것도 게시판 - 댓글 기능 만들기 4 (댓글 수 표시)강의에서 설명했었습니다. author와 마찬가지로 $unwind를 사용했는데, 그 모양이 조금 다릅니다.
author는 { $unwind: '$author' }
와 $unwind에 같이 문자열('$author')을 바로 넣어주었고, 여기서는 $unwind에 path
와 preserveNullAndEmptyArrays
항목이 들어갔습니다. { $unwind: '$author' }
는 { $unwind: { path:'$author' } }
의 축약형으로 다들 예상하셨을 것이고, preserveNullAndEmptyArrays
에 대해 알아봅시다.
unwind는 배열을 flat하게 풀어주는 대신에 배열의 수만큼 오브젝트를 생성합니다. author는 모든 post에 반드시 하나 존재하므로 단순히 unwind하면 되지만, 첨부파일은 없을수도 있습니다. 첨부파일이 없는 post는 unwind하게 되면 $lookup으로 생성된 첨부파일 배열의 길이가 0이므로 해당 post가 사라집니다. 배열의 길이가 0이거나, 배열이 없는 항목을 unwind하는 경우 기존 오브젝트를 삭제하지 않도록 하는 설정이 preserveNullAndEmptyArrays: true
입니다.
3. $cond(https://docs.mongodb.com/manual/reference/operator/aggregation/cond)에 대해 알아봅시다!
{ $cond: [조건, 조건이_참인_경우_값, 조건이_거짓인_경우_값] }
3. aggregation에서 $cond은 길이가 3인 배열을 받으며, 첫번째로는 조건, 두번째로는 조건이 참인 경우에 사용될 값, 세번째로 조건이 거짓인 경우에 사용될 값을 받습니다. 현재 코드는 조건이_참인_경우_값이 true
, 조건이_거짓인_경우_값이 false
로 조건에 따라 attachment의 값을 true
나 false
로 만드는 코드입니다.
다음으로 이 강의에서 조건에 사용된 코드를 살펴봅시다.
{$and: ['$attachment', {$not: '$attachment.isDeleted'}]
$and(https://docs.mongodb.com/manual/reference/operator/aggregation/and)와 $not(https://docs.mongodb.com/manual/reference/operator/aggregation/not)을 사용하고 있는데, $and는 배열을 받아 해당배열의 모든 값이 true
이면 true
를 return합니다. $not은 true
인 값은 false
로, false
인 값은 true
로 바꿔버립니다.
{ $cond: [{$and: ['$attachment', {$not: '$attachment.isDeleted'}]}, true, false] }
즉 위의 조건을 자바스크립트로 나타내면
$attachment && !$attachment.isDeleted ? true : false
으로, post.attachment의 값이 있고, post.attachment.isDeleted가 true
가 아닌 경우 post.attachment에 true
를, 아니면 false
를 대입합니다.
<!-- views/posts/index.ejs --> ... <a href="/posts/<%= post._id %><%= getPostQueryString() %>" class="title-container"> ... <% if(post.commentCount){ %> <small class="title-comments d-inline-block pl-2">(<%= post.commentCount %>)</small> <% } %> <!-- 1 --> <% if(post.attachment){ %> <!-- 1 --> <small class="title-attachment d-inline-block pl-2">📁</small> <!-- 1 --> <% } %> </a>
1. post index route에 의해 게시물에 첨부파일이 있는 경우 post.attachment의 값이 true
설정되므로 이 값을 이용해 첨부파일이 있는지 없는지를 표시해 줄 수 있습니다.
/* public/css/master.css */ ... .board-table .title-container:hover .title-comments, .board-table .title-container:hover .title-text, .board-table .title-container:hover .title-attachment { text-decoration: underline; } ...
기존의 .title-comments, .title-text만 있었는데, .title-attachment를 추가해주었습니다.
4번 게시물에 첨부파일이 있으므로 폴더 모양 아이콘이 표시되었습니다.
다음으로 게시물에 첨부파일을 삭제/수정하는 기능을 추가합니다.
댓글
이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기