직전 포스팅에서 만든 게시판에 기능을 몇가지 추가합니다
list 페이지를 화면에 그릴 때, reverse
메서드를 써서
최신글이 가장 위에 올라오게끔 리스트를 역순으로 정렬합니다
다만 정렬 메서드를 쓰기 전에 먼저 스프레드 연산자로 배열을 복제할 필요가 있습니다
원본 배열에 정렬 메서드를 적용하면 아이템이 추가될 때마다 정렬이 뒤집혀버려서요
[server]
app.get("/list", (req, res) => {
const reversedList = [...boardList].reverse();
res.render("list.html", { boardList: reversedList });
});
그리고 목록을 역순으로 정렬하면 목록에서 <a>
태그를 타고 글로 이동할 때
최신글의 인덱스가 0으로(/view?index=0
) 잡히는 문제가 생기기 때문에
html의 인덱스 배열도 같이 뒤집어줘야 합니다
[list.html]
<tbody>
{% for boardItem in boardList %}
<tr>
<td>{{ loop.length - loop.index0 }}</td>
<td><a href='./view?index={{ loop.length - loop.index0 - 1 }}'>{{ boardItem.subject }}</a></td>
<td>{{ boardItem.writer }}</td>
<td class="timeLine">{{ boardItem.date }}</td>
<td>{{ boardItem.hit }}</td>
</tr>
{% endfor %}
</tbody>
페이징 기능은 전부 클라이언트 측에서 구현했습니다
배열 아이템이 11개, 21개, 31개...를 넘어갈 때마다
리스트 페이지의 인덱스가 추가됩니다
[list.js]
//paging
let lastPage = 0;
if (totalNumber % 10 == 0) {
lastPage = parseInt(totalNumber / 10);
} else if (totalNumber % 10 != 0) {
lastPage = parseInt(totalNumber / 10 + 1);
}
function pageTemplate() {
let pageArr = [];
for (let i = 0; i < lastPage; i++) {
pageArr.push(
`<a href="http://127.0.0.1:3000/list?index=${i}" class="pageMove">${
i + 1
}</a>`
);
}
return pageArr;
}
paging.innerHTML += pageTemplate().join("");
function prevHandler(e) {
if (pageIndex > 0) {
location.href =
"http://127.0.0.1:3000/list?index=" + (Number(pageIndex) - 1);
} else return alert("최신 페이지입니다.");
}
function nextHandler(e) {
if (pageIndex < lastPage - 1) {
location.href =
"http://127.0.0.1:3000/list?index=" + (Number(pageIndex) + 1);
} else return alert("마지막 페이지입니다.");
}
prev.addEventListener("click", prevHandler);
next.addEventListener("click", nextHandler);
let startList = Number(pageIndex) * 10;
let lastList = startList + 9;
for (let i = 0; i < items.length; i++) {
if (i >= startList && i <= lastList) {
items[i].style.display = "";
} else {
items[i].style.display = "none";
}
}
서버에 저장된 글 작성시점과 클라이언트 측에서
페이지를 로드한 시간의 차이를 구하는 방식으로 DOM을 조작합니다
연월일은 '-'로, 시분초는 '.'으로 연결해서 차이 계산이 끝나면
split을 써서 각각을 계산하고 제거하는 방식입니다
어설프긴 해도 애착이 가는 아이디어네요
[server]
const writeHandler = (req, res) => {
const boardItem = req.body;
boardList.push(boardItem);
const getTimeNow = (date) => {
let mm = date.getMonth() + 1; // 0 ~ 11
mm = (mm > 9 ? "" : "0") + mm; // 01 02...09 10 11
let dd = date.getDate(); // 1 ~ 31
dd = (dd > 9 ? "" : "0") + dd;
let yy = date.getFullYear();
let hr = date.getHours()
let min = date.getMinutes()
let sec = date.getSeconds()
return [yy, mm, dd].join("-")+"."+[hr, min, sec].join(".")
};
boardItem.date = getTimeNow(new Date());
boardItem.hit = -1;
boardItem.idx = boardList.length - 1;
res.redirect(`/view?index=${boardItem.idx}`);
};
[list.js]
const getTimeNow = (date) => {
let mm = date.getMonth() + 1; // 0 ~ 11
mm = (mm > 9 ? "" : "0") + mm; // 01 02...09 10 11
let dd = date.getDate(); // 1 ~ 31
dd = (dd > 9 ? "" : "0") + dd;
let yy = date.getFullYear();
let hr = date.getHours();
let min = date.getMinutes();
let sec = date.getSeconds();
return [yy, mm, dd].join("-") + "." + [hr, min, sec].join(".");
};
// 서버와 같은 방식으로 시간을 표시할 배열을 만들었습니다
// 시, 분, 초에 해당하는 부분은 점(.)으로 구분합니다
const timeLines = document.querySelectorAll(".timeLine");
const timeLine = document.querySelector(".timeLine");
const thisTime = getTimeNow(new Date());
timeLines.forEach((timeLine) => {
const uploadedTime = timeLine.textContent;
// console.log("timeA : " + thisTime.split('.'))
// console.log("timeB : " + uploadedTime.split('.'))
if (
thisTime.split(".")[0] === uploadedTime.split(".")[0] &&
thisTime.split(".")[1] === uploadedTime.split(".")[1] &&
thisTime.split(".")[2] === uploadedTime.split(".")[2]
) {
timeLine.innerHTML = "방금전";
} else if (
thisTime.split(".")[0] === uploadedTime.split(".")[0] &&
thisTime.split(".")[1] === uploadedTime.split(".")[1] &&
thisTime.split(".")[2] !== uploadedTime.split(".")[2]
) {
timeLine.innerHTML = `${
thisTime.split(".")[2] - uploadedTime.split(".")[2]
}분전`;
} else if (
thisTime.split(".")[0] === uploadedTime.split(".")[0] &&
thisTime.split(".")[1] - uploadedTime.split(".")[1] < 7
) {
timeLine.innerHTML = `${
thisTime.split(".")[1] - uploadedTime.split(".")[1]
}시간전`;
} else timeLine.innerHTML = uploadedTime.split(".")[0];
});
검색 전
검색 후
항목별 텍스트를 검색하는 기능입니다
searchType이라는 이름으로 검색 항목을 분류하고
인풋태그(search)에 검색한 내용이 get 요청을 타고 서버측으로
전달되면 filter
메서드를 사용해서 검색 결과를 화면에 다시 그립니다
리스트 페이지를 그리는것과 검색결과를 그리는 것
각각의 get 요청에 대해 어떤 응답을 할 것인지는 if문을 써서 구분지었기 때문에
searchType이 undefined일 때의 예외처리가 중요합니다
+) 검색결과라는 텍스트를 화면에 띄우려면 이벤트 리스너가 아닌
다른 방식으로 검색 여부를 감지해야 합니다
[html]
<div id="searchBox">
<p>현재 <span class="total_article">{{ boardList.length }}</span>개의 글이 있습니다</p>
<p style="display: none;"><span class="search_result">{{ boardList.length }}개의 검색결과</span></p>
<form action="/list" method="get">
<select name="searchType" id="searchType">
<option value="writer">작성자</option>
<option value="subject">제목</option>
<option value="content">내용</option>
<input name="search" type="text">
</select>
<button type="submit">검색</button>
</form>
</div>
[server]
app.get("/list", (req, res) => {
//검색 기능 구현
const searchType = req.query.searchType || 'writer';
const search = req.query.search
const result = req.query.searchType === undefined
? boardList
: boardList.filter(obj => obj[searchType].includes(search));
});
[list.js]
const searchBtn = document.querySelector("#searchBox > form > button");
const searchResult = document.querySelectorAll("#searchBox > p");
const queryString = window.location.search;
if (queryString.includes('&search')) {
// 쿼리스트링이 &search를 포함하고 있으면 스타일 변경하기
searchResult[0].style.display = 'none';
searchResult[1].style.display = 'block';
}