- Index 만들기




이번 프로젝트의 기간은 총 2주이기 때문에
원본 네이버 지식인 사이트를 모두 클론 코딩하기에는 시간이 부족했습니다
그래서 가장 중요한 기능이라고 생각되고 만들어보고 싶었던 기능만 추가하여
Index를 꾸몄습니다
- Index.jsp - script
중요하게 사용된 스크립트는 하단의 QnA를 보여주는 리스트입니다
제가 배운 페이징은 ajax를 사용하지 않으면 화면이 새로고침되며,
최상단으로 올라가는 문제가 있기 때문에 페이징을 ajax로 비동기 처리를 하였습니다
<script>
const main2ListNBBtn = (main2PagingPage, main2Search) => {
const qnaPage = main2PagingPage;
넘어갈 페이지를 담아줍니다
const q = main2Search;
사용자가 검색을 하는 상황을 대비해 검색어를 담아줍니다
const ListResult = document.getElementById('ajax-main2-contents-list-box');
DB에서 가져온 List를 양식에 맞춰 넣어 줄 부모 태그를 담아줍니다
const numResult = document.getElementById('numResult');
const backBtnResult = document.getElementById('backBtnResult');
const nextBtnResult = document.getElementById('nextBtnResult');
페이지를 이동하는 상황은 총 3가지로(이전 숫자 다음 )
해당 페이지에 따른 버튼 상황을 담아주기 위해 각각 버튼의 부모 태그를 담아줍니다
const target = document.getElementById('section-inner-section');
한페이지에 10개의 글을 보여주기 때문에
페이지 이동시 QnAList 시작점으로 보내기 위한 시작점 태그를 담아줍니다
$.ajax({ 본격적인 Ajax 시작점
type: "post",
url: "/index/UDPage",
data: {
"qnaPage": qnaPage,
"q": q
Controller로 보내주어야하는 파라미터는
넘어갈 페이지 그리고 검색어 입니다
둘중 하나가 null인경우에는 Controller에서 적용한 기본값으로
자동 적용됩니다
},
success: function (res) {
DB에서 정상적으로 값을 가져오는경우 success로 넘어오고 함께 넘어오는 값은
res에 담아줍니다
let outPut = "";
QnAList를 양식에 맞춰 담아줄 output 입니다.
let numPut = "";
하단의 number버튼을 양식에 맞춰 담아줄 numPut 입니다.
let downPut = "";
하단의 이전 버튼을 양식에 맞춰 담아줄 downPut 입니다
let upPut = "";
하단의 다음 버튼을 양식에 맞춰 담아줄 upPut 입니다
downPut += '<a class="QnA-back-bnt-on" style="cursor: pointer;" onclick="main2ListNBBtn(' + (res.qnaBoardPage.page - 1) + ',' + res.qnaBoardPage.q + ')">이전</a>';
upPut += '<a class="QnA-next-bnt-on" style="cursor: pointer;" onclick="main2ListNBBtn(' + (res.qnaBoardPage.page + 1) + ',' + res.qnaBoardPage.q + ')">다음</a>';
양식에 맞춰 이전 , 다음 버튼의 양식에 맞춰 각각의 변수에 문자열로 담아줍니다 당연히 +=를 사용합니다
for (let i in res.qnaBoardDTOList) {
페이징을위해 담아온 List를 for문을 사용하여
양식에 맞춰 outPut에 문자열로 이어서 담아줍니다
outPut += '<div class="main2-contents-list-box">';
outPut += '<div class="main2-contents-list-inner">';
outPut += '<div class="main2-contents-list-item">';
outPut += '<a href="/board/detail?BoardId=' + res.qnaBoardDTOList[i].id + '" target="_blank" class="main2-contents-item">';
if (res.qnaBoardDTOList[i].boardPoint != 0) {
outPut += '<span class="power_grade" title="내공 전시장">';
outPut += res.qnaBoardDTOList[i].boardPoint;
outPut += '</span>';
}
outPut += '<span class="main2-item-title">' + res.qnaBoardDTOList[i].boardTitle + '</span>';
outPut += '<p class="main2-item-contents">' + res.qnaBoardDTOList[i].boardContents + '</p>';
outPut += '</a>';
outPut += '</div>';
outPut += '<div class="main2-item-info">';
outPut += '<span class="item-info-answer">답변 ' + res.qnaBoardDTOList[i].boardAnswer + '</span>';
outPut += '<span class="item-info-type">조회수 ' + res.qnaBoardDTOList[i].boardHits + '</span>';
outPut += '<span class="item-info-time">';
분 전 , 시간 전 , 일 전 을 나타내기 위해 저장된 시간 값을 계산식에 적용하여
if문을 통해 조건에 맞는 text를 outPut에 담습니다
let nowTime = new Date().getTime(); // 현재 시간을 밀리초로 가져옴
let commentDate = new Date(res.qnaBoardDTOList[i].boardCreatedDate); // DTO의 boardCreatedDate를 JavaScript Date 객체로 변환
let timeDifference = (nowTime - commentDate) / (1000 * 60); // 분 단위로 시간 차이 계산
if (timeDifference <= 10) {
outPut += '방금 전';
} else if (timeDifference > 10 && timeDifference <= 60) {
outPut += moment(timeDifference) + '분 전';
} else if (timeDifference > 60 && timeDifference <= 60 * 24) {
timeDifference = (timeDifference / 60);
outPut += moment(timeDifference) + '시간 전';
} else if (timeDifference > 60 * 24 && timeDifference <= 60 * 24 * 30) {
timeDifference = (timeDifference / (60 * 24));
outPut += moment(timeDifference) + '일 전';
} else {
outPut += moment(res.qnaBoardDTOList[i].boardCreatedDate).format("YYYY-MM-DD HH:mm");
}
outPut += '</span>';
outPut += '</div>';
outPut += '</div>';
outPut += '</div>';
}
for (let i = res.qnaBoardPage.startPage; i <= res.qnaBoardPage.endPage; i++) {
if (res.qnaBoardPage.page == i) {
numPut += '<a class="QnA-paging-bnt-off">' + i + '</a>';
} else {
numPut += '<a class="QnA-paging-bnt-on" style="cursor: pointer;" onclick="main2ListNBBtn(' + i + ',' + res.qnaBoardPage.q + ')">' + i + '</a>';
}
}
숫자버튼의 경우 한페이지에 4개를 보여주게 설정했기 때문에
시작 page 부터 화면에서 보여주는 endPage까지의 수를 for문을 사용하여 numPut에 담아줍니다
for문 안에서 if문이 사용된 이유는 만약 시작페이지가 사용자가 선택한 페이지라면
해당 버튼을 비활성화하고 색을 변경하여 현재 페이지가 몇번째 페이지인지를 알려주기 위함입니다
이제 위에서 담아준 값들을 각각의 부모태그에 innerHTML을 사용하여 넘깁니다
if (res.qnaBoardPage.page <= 1) {
backBtnResult.innerHTML = "";
} else {
backBtnResult.innerHTML = downPut;
}
if (res.qnaBoardPage.page == res.qnaBoardPage.maxPage) {
nextBtnResult.innerHTML = "";
} else if (res.qnaBoardPage.page != res.qnaBoardPage.maxPage) {
nextBtnResult.innerHTML = upPut;
}
이전 / 다음 버튼의 경우 이전은 뒤로갈 페이지가 없다면,
다음은 현재 페이지가 마지막 페이지라면 보이지 않게 설정했기 때문에
IF문을 사용하여 각각의 조건에 따라 값을 할당합니다
numResult.innerHTML = numPut;
ListResult.innerHTML = outPut;
그외 List의 경우 조건없이 한페이지에 보여줄 갯수를 정하여 넘어왔고
numResult의 경우 조건에 맞는 값만 담게 하였기 떄문에
바로 부모태그의 innerHTML에 값을 넘겨 출력해줍니다
},
error: function () {
console.log("실패");
}
})
target.scrollIntoView({behavior: 'auto'});
이후 Ajax가 마무리되면 QnA리스트를 시작하는 태그로 스크롤을 이동시킵니다
}
</script>
- Controller
main화면 출력에 사용되는 GetMapping입니다
@GetMapping("/")
public String Index(@RequestParam(value = "bestPage", required = false, defaultValue = "1") int bestPage,
@RequestParam(value = "qnaPage", required = false,defaultValue = "1")int qnaPage,
@RequestParam(value = "q",required = false,defaultValue = "") String q,
메인화면에 사용된 페이징은 2가지로 나누어져있기 때문에
page를 받아오는 파리미터가 2개 사용되었습니다
둘중 하나라도 페이지를 이동하는 경우
사용하지 않는 페이지의 목록도 함께 출력해주어야하기 때문에
기본값을 설정해줍니다
Model model, HttpSession session) {
필요한 값을 담아갈 model 사용자의 정보가 담긴 session입니다
MemberDTO memberDTO = boardService.findById(session.getAttribute("memberId"));
사용자가 로그인을 했을 경우를 대비해 레이아웃에 사용자 정보를 출력하기 위해
사용자 정보가 담긴 DTO를 찾아옵니다
PageDTO bestPageDTO = new PageDTO();
1번째 페이징 정보를 담기위한 DTO객체를 선언합니다
bestPageDTO.setPage(bestPage);
파리미터로 받아온 PAGE정보를 담아줍니다
// qna게시물 페이징
PageDTO qnaPageDTO = new PageDTO();
2번째 페이징 정보를 담기위한 DTO객체를 선언합니다
qnaPageDTO.setPage(qnaPage);
파라미터로 받아온 PAGE정보를 담아줍니다
둘중 하나라도 사용되지 않았다면 기본값이 담기게 됩니다
Date Time = new Date();
int hours = Time.getHours();
String formattedHours = (hours < 10) ? "0" + hours : String.valueOf(hours);
String bestBoardTime = Time.getDate() + "일 " + formattedHours+"시 기준";
1번째 페이징을 진행한 공간에 몇시 기준으로 PAGELIST가 업데이트 되는지 알려주기위해
사용되는 현재 시간 정보를 일 , 시간 만 담아줍니다
String bestBoardCount = "6";
1번째 페이지의 경우 BEST게시물을 보여주는 PAGE이기 때문에
특이한 출력 방식으로인해 순위를 체크해주기 위한 Count변수를 따로 만들었습니다
model.addAttribute("firstBoard",boardService.findByFirst());
main화면의 오늘의 질문 공간에 담아줄 board의 정보를 model에 담아 넘겨줍니다
model.addAttribute("holyLand",boardService.findByBoard(commentService.findByHoly()));
main화면의 지식In성지 공간에 담아줄 board의 정보를 model에 담아줍니다 넘겨줍니다
1번째 페이징 정보가 담긴 값을 model에 담아 넘겨줍니다
model.addAttribute("bestBoardDTOList",boardService.bestBoardList(bestPageDTO));
한화면에 보여줄 List의 정보가 담긴 model입니다
model.addAttribute("bestPaging",boardService.bestPagingParam(bestPageDTO,q));
한화면에 보여줄 page버튼의 정보가 담긴 model 입니다
2번째 페이징 정보가 담긴 값을 model에 담아 넘겨줍니다
model.addAttribute("qnaBoardDTOList",boardService.qnaBoardList(qnaPageDTO,q));
한화면에 보여줄 List의 정보가 담긴 model입니다
model.addAttribute("qnaPaging",boardService.qnaPagingParam(qnaPageDTO,q));
한화면에 보여줄 page버튼의 정보가 담긴 model 입니다
model.addAttribute("bestBoardCount",bestBoardCount);
1번째 페이징에 사용되는 랭킹의 순위를 찍어주기위한 정보가 담긴 model입니다
model.addAttribute("bestBoardTime",bestBoardTime);
1번째 페이징에 사용되는 ListUpdate시간이 담긴 model입니다
model.addAttribute("memberDTO",memberDTO);
레이아웃에 사용되는 사용자 정보가 담긴 model입니다
return "index";
main화면인 index로 넘어갑니다
}
- PageDTO
이번 지식in 클론코딩을 진행하면서 페이징에 대한 고민이 많았습니다
어떻게하면 페이징을 편하게할 수 있을까 정말 많은 고민을하다가
DTO에 값을 넘기면 자동으로 계산하여 필드에 값을 담아주는 방법을 사용했습니다
코딩을 배우는 입장에서 이게 맞는지 아직 잘모르고, 어떤 문제가 있을지도 현재는 모르겠습니다
public class PageDTO {
int page; // 현재 페이지
int maxPage; // 전체페이지갯수
int startPage; // 하단에 보여지는 시작 페이지 번호
int endPage; // 하단에 보여지는 마지막 페이지 번호
int blockLimit; // 한화면에 보여줄 페이지 번호 갯수
int boardCount;
// 보드에 필요한 필드
Long boardId; // detail에서 사용
int pageLimit; // 한화면에 보여줄 게시글
int pageStart; // 게시글 시작번호
String q;// 검색어
public void setPage(int page) {
this.page = page;
}
public void setPageLimit(int pageLimit) {
this.pageLimit = pageLimit;
this.pageStart = (this.page -1) * this.pageLimit;
}
한화면에 보여주고 싶은 List의 갯수를 정하면
계산식을 적용하여 시작점까지 함께 저장합니다.
이러한 방식으로 처리하는 이유는 Mapper의 쿼리문에 사용하기 위해서입니다
public void setBlockLimit(int blockLimit) {
this.blockLimit = blockLimit;
this.startPage =(((int)(Math.ceil((double) this.page / blockLimit))) - 1) * blockLimit + 1;
}
한화면에 보여주고 싶은 숫자버튼의 갯수를 정하고
페이지가 넘어가는 상황을 대비해 버튼 시작점을 계산하여 필드에 저장합니다
public void setBoardCount(int boardCount) {
this.boardCount = boardCount;
this.maxPage = (int)(Math.ceil((double)boardCount / this.pageLimit));
this.endPage = this.startPage + this.blockLimit -1;
}
총 board의 count에 따라 숫자버튼의 마지막 번호,총번호의 갯수를 계산합니다
}
- Service
인덱스에 사용된 service의 메소드는 특별한 부분이 없습니다
그렇기 때문에 리펙토링을 진행했던 Page에 관련된 부분만 작성하겠습니다
한화면에 보여주고 싶은 List의 갯수
public List<BoardDTO> bestBoardList(PageDTO bestPageDTO) {
bestPageDTO.setPageLimit(6);
return boardRepository.bestBoardList(bestPageDTO);
}
한화면에 보여주고 싶은 하단의 page버튼 갯수
public PageDTO bestPagingParam(PageDTO bestPageDTO,String q) {
bestPageDTO.setBlockLimit(2);
bestPageDTO.setQ(q);
bestPageDTO.setBoardCount(boardRepository.BoardCount(bestPageDTO));
if (bestPageDTO.getEndPage() > bestPageDTO.getMaxPage()) {
bestPageDTO.setEndPage(bestPageDTO.getMaxPage());
}
한화면에 보여주는 숫자 버튼의 갯수는 2개이지만
List가 2개의 페이지에 못미치고 끝나게 되는 경우를 대비하여
endPage가 MaxPage 보다 높아지게 된다면
endPage를 maxPage와 같게 만들어줍니다.
return bestPageDTO;
}
한화면에 보여주고 싶은 List의 갯수
public Object qnaBoardList(PageDTO qnaPageDTO, String q) {
qnaPageDTO.setPageLimit(10);
qnaPageDTO.setQ(q);
2번째 페이징을 진행하는 부분에서는 검색도 가능하기 때문에
검색어 또한 dto에 담아줍니다
return boardRepository.qnaBoardDTOList(qnaPageDTO);
}
위의 설명과 마찬가지입니다.
public Object qnaPagingParam(PageDTO qnaPageDTO, String q) {
qnaPageDTO.setBlockLimit(10);
qnaPageDTO.setQ(q);
qnaPageDTO.setBoardCount(boardRepository.BoardCount(qnaPageDTO));
if (qnaPageDTO.getEndPage() > qnaPageDTO.getMaxPage()) {
qnaPageDTO.setEndPage(qnaPageDTO.getMaxPage());
}
return qnaPageDTO;
}
- Repository
1번째 페이징인 bestBoard에서는 검색기능을 사용하지 않기 때문에
바로 Mapper로 값을 전달하고 return받습니다 하지만
2번째 페이징인 QnaBoard의 경우에는 검색을 받고 있기 때문에
일반적인 페이지 이동 , 검색의 경우 리턴받은 값의 차이가 있기 때문에
검색어가 기본값인 " "인지에 따라 사용되는 Mapper의 쿼리문을 나눴습니다
public List<BoardDTO> bestBoardList(PageDTO bestPageDTO) {
return sql.selectList("naverBoard.bestBoardList", bestPageDTO);
}
public List<BoardDTO> qnaBoardDTOList(PageDTO qnaPageDTO) {
if (!(qnaPageDTO.getQ().equals(""))) {
return sql.selectList("naverBoard.qnaSearchBoardList", qnaPageDTO);
} else {
return sql.selectList("naverBoard.qnaBoardList", qnaPageDTO);
}
}
하단의 페이징 버튼의 경우 공통으로 사용하였습니다
public int BoardCount(PageDTO qnaPageDTO) {
if (!(qnaPageDTO.getQ().equals(""))) {
return sql.selectOne("naverBoard.BoardSearchCount", qnaPageDTO);
} else {
return sql.selectOne("naverBoard.BoardCount");
}
}
- Mapper
BestBoard의 페이징
<select id="bestBoardList" parameterType="page" resultType="board">
select * from board_table order by boardHits desc limit #{pageStart},#{pageLimit}
조회수가 높은순으로 검색하고 limit만큼 boardDTO에 담아 리스트로 return
</select>
QnaBoard의 페이징 일반적인 페이지 이동시에 사용되는 쿼리문
<select id="qnaBoardList" parameterType="page" resultType="board">
select * from board_table order by boardCreatedDate desc limit #{pageStart},#{pageLimit}
가장 최근에 작성된 게시글부터 차례로 BoardDTO에 담아 리스트를 리턴
</select>
QnaBoard의 페이징 검색을 통한 페이징을 위해 사용되는 쿼리문
<select id="qnaSearchBoardList" parameterType="page" resultType="board">
select * from board_table
where boardContents like concat('%', #{q}, '%') or boardTitle like concat('%', #{q}, '%')
order by id desc limit #{pageStart}, #{pageLimit}
스프링부트의 맵퍼에서는 concat이 잘먹지 않기 때문에 해당 양시으로 사용하였습니다
게시글의 내용과 제목에 검색어가 포함이되는경우 가장 최근에 작성된 게시글 부터
BoardDTO에 담아 List로 리턴합니다
</select>
'나의 수업일지' 카테고리의 다른 글
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (7) (0) | 2023.06.11 |
---|---|
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (6) (0) | 2023.06.09 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (4) (0) | 2023.06.07 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (3) (0) | 2023.06.07 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (2) (0) | 2023.06.07 |
- Index 만들기




이번 프로젝트의 기간은 총 2주이기 때문에
원본 네이버 지식인 사이트를 모두 클론 코딩하기에는 시간이 부족했습니다
그래서 가장 중요한 기능이라고 생각되고 만들어보고 싶었던 기능만 추가하여
Index를 꾸몄습니다
- Index.jsp - script
중요하게 사용된 스크립트는 하단의 QnA를 보여주는 리스트입니다
제가 배운 페이징은 ajax를 사용하지 않으면 화면이 새로고침되며,
최상단으로 올라가는 문제가 있기 때문에 페이징을 ajax로 비동기 처리를 하였습니다
<script>
const main2ListNBBtn = (main2PagingPage, main2Search) => {
const qnaPage = main2PagingPage;
넘어갈 페이지를 담아줍니다
const q = main2Search;
사용자가 검색을 하는 상황을 대비해 검색어를 담아줍니다
const ListResult = document.getElementById('ajax-main2-contents-list-box');
DB에서 가져온 List를 양식에 맞춰 넣어 줄 부모 태그를 담아줍니다
const numResult = document.getElementById('numResult');
const backBtnResult = document.getElementById('backBtnResult');
const nextBtnResult = document.getElementById('nextBtnResult');
페이지를 이동하는 상황은 총 3가지로(이전 숫자 다음 )
해당 페이지에 따른 버튼 상황을 담아주기 위해 각각 버튼의 부모 태그를 담아줍니다
const target = document.getElementById('section-inner-section');
한페이지에 10개의 글을 보여주기 때문에
페이지 이동시 QnAList 시작점으로 보내기 위한 시작점 태그를 담아줍니다
$.ajax({ 본격적인 Ajax 시작점
type: "post",
url: "/index/UDPage",
data: {
"qnaPage": qnaPage,
"q": q
Controller로 보내주어야하는 파라미터는
넘어갈 페이지 그리고 검색어 입니다
둘중 하나가 null인경우에는 Controller에서 적용한 기본값으로
자동 적용됩니다
},
success: function (res) {
DB에서 정상적으로 값을 가져오는경우 success로 넘어오고 함께 넘어오는 값은
res에 담아줍니다
let outPut = "";
QnAList를 양식에 맞춰 담아줄 output 입니다.
let numPut = "";
하단의 number버튼을 양식에 맞춰 담아줄 numPut 입니다.
let downPut = "";
하단의 이전 버튼을 양식에 맞춰 담아줄 downPut 입니다
let upPut = "";
하단의 다음 버튼을 양식에 맞춰 담아줄 upPut 입니다
downPut += '<a class="QnA-back-bnt-on" style="cursor: pointer;" onclick="main2ListNBBtn(' + (res.qnaBoardPage.page - 1) + ',' + res.qnaBoardPage.q + ')">이전</a>';
upPut += '<a class="QnA-next-bnt-on" style="cursor: pointer;" onclick="main2ListNBBtn(' + (res.qnaBoardPage.page + 1) + ',' + res.qnaBoardPage.q + ')">다음</a>';
양식에 맞춰 이전 , 다음 버튼의 양식에 맞춰 각각의 변수에 문자열로 담아줍니다 당연히 +=를 사용합니다
for (let i in res.qnaBoardDTOList) {
페이징을위해 담아온 List를 for문을 사용하여
양식에 맞춰 outPut에 문자열로 이어서 담아줍니다
outPut += '<div class="main2-contents-list-box">';
outPut += '<div class="main2-contents-list-inner">';
outPut += '<div class="main2-contents-list-item">';
outPut += '<a href="/board/detail?BoardId=' + res.qnaBoardDTOList[i].id + '" target="_blank" class="main2-contents-item">';
if (res.qnaBoardDTOList[i].boardPoint != 0) {
outPut += '<span class="power_grade" title="내공 전시장">';
outPut += res.qnaBoardDTOList[i].boardPoint;
outPut += '</span>';
}
outPut += '<span class="main2-item-title">' + res.qnaBoardDTOList[i].boardTitle + '</span>';
outPut += '<p class="main2-item-contents">' + res.qnaBoardDTOList[i].boardContents + '</p>';
outPut += '</a>';
outPut += '</div>';
outPut += '<div class="main2-item-info">';
outPut += '<span class="item-info-answer">답변 ' + res.qnaBoardDTOList[i].boardAnswer + '</span>';
outPut += '<span class="item-info-type">조회수 ' + res.qnaBoardDTOList[i].boardHits + '</span>';
outPut += '<span class="item-info-time">';
분 전 , 시간 전 , 일 전 을 나타내기 위해 저장된 시간 값을 계산식에 적용하여
if문을 통해 조건에 맞는 text를 outPut에 담습니다
let nowTime = new Date().getTime(); // 현재 시간을 밀리초로 가져옴
let commentDate = new Date(res.qnaBoardDTOList[i].boardCreatedDate); // DTO의 boardCreatedDate를 JavaScript Date 객체로 변환
let timeDifference = (nowTime - commentDate) / (1000 * 60); // 분 단위로 시간 차이 계산
if (timeDifference <= 10) {
outPut += '방금 전';
} else if (timeDifference > 10 && timeDifference <= 60) {
outPut += moment(timeDifference) + '분 전';
} else if (timeDifference > 60 && timeDifference <= 60 * 24) {
timeDifference = (timeDifference / 60);
outPut += moment(timeDifference) + '시간 전';
} else if (timeDifference > 60 * 24 && timeDifference <= 60 * 24 * 30) {
timeDifference = (timeDifference / (60 * 24));
outPut += moment(timeDifference) + '일 전';
} else {
outPut += moment(res.qnaBoardDTOList[i].boardCreatedDate).format("YYYY-MM-DD HH:mm");
}
outPut += '</span>';
outPut += '</div>';
outPut += '</div>';
outPut += '</div>';
}
for (let i = res.qnaBoardPage.startPage; i <= res.qnaBoardPage.endPage; i++) {
if (res.qnaBoardPage.page == i) {
numPut += '<a class="QnA-paging-bnt-off">' + i + '</a>';
} else {
numPut += '<a class="QnA-paging-bnt-on" style="cursor: pointer;" onclick="main2ListNBBtn(' + i + ',' + res.qnaBoardPage.q + ')">' + i + '</a>';
}
}
숫자버튼의 경우 한페이지에 4개를 보여주게 설정했기 때문에
시작 page 부터 화면에서 보여주는 endPage까지의 수를 for문을 사용하여 numPut에 담아줍니다
for문 안에서 if문이 사용된 이유는 만약 시작페이지가 사용자가 선택한 페이지라면
해당 버튼을 비활성화하고 색을 변경하여 현재 페이지가 몇번째 페이지인지를 알려주기 위함입니다
이제 위에서 담아준 값들을 각각의 부모태그에 innerHTML을 사용하여 넘깁니다
if (res.qnaBoardPage.page <= 1) {
backBtnResult.innerHTML = "";
} else {
backBtnResult.innerHTML = downPut;
}
if (res.qnaBoardPage.page == res.qnaBoardPage.maxPage) {
nextBtnResult.innerHTML = "";
} else if (res.qnaBoardPage.page != res.qnaBoardPage.maxPage) {
nextBtnResult.innerHTML = upPut;
}
이전 / 다음 버튼의 경우 이전은 뒤로갈 페이지가 없다면,
다음은 현재 페이지가 마지막 페이지라면 보이지 않게 설정했기 때문에
IF문을 사용하여 각각의 조건에 따라 값을 할당합니다
numResult.innerHTML = numPut;
ListResult.innerHTML = outPut;
그외 List의 경우 조건없이 한페이지에 보여줄 갯수를 정하여 넘어왔고
numResult의 경우 조건에 맞는 값만 담게 하였기 떄문에
바로 부모태그의 innerHTML에 값을 넘겨 출력해줍니다
},
error: function () {
console.log("실패");
}
})
target.scrollIntoView({behavior: 'auto'});
이후 Ajax가 마무리되면 QnA리스트를 시작하는 태그로 스크롤을 이동시킵니다
}
</script>
- Controller
main화면 출력에 사용되는 GetMapping입니다
@GetMapping("/")
public String Index(@RequestParam(value = "bestPage", required = false, defaultValue = "1") int bestPage,
@RequestParam(value = "qnaPage", required = false,defaultValue = "1")int qnaPage,
@RequestParam(value = "q",required = false,defaultValue = "") String q,
메인화면에 사용된 페이징은 2가지로 나누어져있기 때문에
page를 받아오는 파리미터가 2개 사용되었습니다
둘중 하나라도 페이지를 이동하는 경우
사용하지 않는 페이지의 목록도 함께 출력해주어야하기 때문에
기본값을 설정해줍니다
Model model, HttpSession session) {
필요한 값을 담아갈 model 사용자의 정보가 담긴 session입니다
MemberDTO memberDTO = boardService.findById(session.getAttribute("memberId"));
사용자가 로그인을 했을 경우를 대비해 레이아웃에 사용자 정보를 출력하기 위해
사용자 정보가 담긴 DTO를 찾아옵니다
PageDTO bestPageDTO = new PageDTO();
1번째 페이징 정보를 담기위한 DTO객체를 선언합니다
bestPageDTO.setPage(bestPage);
파리미터로 받아온 PAGE정보를 담아줍니다
// qna게시물 페이징
PageDTO qnaPageDTO = new PageDTO();
2번째 페이징 정보를 담기위한 DTO객체를 선언합니다
qnaPageDTO.setPage(qnaPage);
파라미터로 받아온 PAGE정보를 담아줍니다
둘중 하나라도 사용되지 않았다면 기본값이 담기게 됩니다
Date Time = new Date();
int hours = Time.getHours();
String formattedHours = (hours < 10) ? "0" + hours : String.valueOf(hours);
String bestBoardTime = Time.getDate() + "일 " + formattedHours+"시 기준";
1번째 페이징을 진행한 공간에 몇시 기준으로 PAGELIST가 업데이트 되는지 알려주기위해
사용되는 현재 시간 정보를 일 , 시간 만 담아줍니다
String bestBoardCount = "6";
1번째 페이지의 경우 BEST게시물을 보여주는 PAGE이기 때문에
특이한 출력 방식으로인해 순위를 체크해주기 위한 Count변수를 따로 만들었습니다
model.addAttribute("firstBoard",boardService.findByFirst());
main화면의 오늘의 질문 공간에 담아줄 board의 정보를 model에 담아 넘겨줍니다
model.addAttribute("holyLand",boardService.findByBoard(commentService.findByHoly()));
main화면의 지식In성지 공간에 담아줄 board의 정보를 model에 담아줍니다 넘겨줍니다
1번째 페이징 정보가 담긴 값을 model에 담아 넘겨줍니다
model.addAttribute("bestBoardDTOList",boardService.bestBoardList(bestPageDTO));
한화면에 보여줄 List의 정보가 담긴 model입니다
model.addAttribute("bestPaging",boardService.bestPagingParam(bestPageDTO,q));
한화면에 보여줄 page버튼의 정보가 담긴 model 입니다
2번째 페이징 정보가 담긴 값을 model에 담아 넘겨줍니다
model.addAttribute("qnaBoardDTOList",boardService.qnaBoardList(qnaPageDTO,q));
한화면에 보여줄 List의 정보가 담긴 model입니다
model.addAttribute("qnaPaging",boardService.qnaPagingParam(qnaPageDTO,q));
한화면에 보여줄 page버튼의 정보가 담긴 model 입니다
model.addAttribute("bestBoardCount",bestBoardCount);
1번째 페이징에 사용되는 랭킹의 순위를 찍어주기위한 정보가 담긴 model입니다
model.addAttribute("bestBoardTime",bestBoardTime);
1번째 페이징에 사용되는 ListUpdate시간이 담긴 model입니다
model.addAttribute("memberDTO",memberDTO);
레이아웃에 사용되는 사용자 정보가 담긴 model입니다
return "index";
main화면인 index로 넘어갑니다
}
- PageDTO
이번 지식in 클론코딩을 진행하면서 페이징에 대한 고민이 많았습니다
어떻게하면 페이징을 편하게할 수 있을까 정말 많은 고민을하다가
DTO에 값을 넘기면 자동으로 계산하여 필드에 값을 담아주는 방법을 사용했습니다
코딩을 배우는 입장에서 이게 맞는지 아직 잘모르고, 어떤 문제가 있을지도 현재는 모르겠습니다
public class PageDTO {
int page; // 현재 페이지
int maxPage; // 전체페이지갯수
int startPage; // 하단에 보여지는 시작 페이지 번호
int endPage; // 하단에 보여지는 마지막 페이지 번호
int blockLimit; // 한화면에 보여줄 페이지 번호 갯수
int boardCount;
// 보드에 필요한 필드
Long boardId; // detail에서 사용
int pageLimit; // 한화면에 보여줄 게시글
int pageStart; // 게시글 시작번호
String q;// 검색어
public void setPage(int page) {
this.page = page;
}
public void setPageLimit(int pageLimit) {
this.pageLimit = pageLimit;
this.pageStart = (this.page -1) * this.pageLimit;
}
한화면에 보여주고 싶은 List의 갯수를 정하면
계산식을 적용하여 시작점까지 함께 저장합니다.
이러한 방식으로 처리하는 이유는 Mapper의 쿼리문에 사용하기 위해서입니다
public void setBlockLimit(int blockLimit) {
this.blockLimit = blockLimit;
this.startPage =(((int)(Math.ceil((double) this.page / blockLimit))) - 1) * blockLimit + 1;
}
한화면에 보여주고 싶은 숫자버튼의 갯수를 정하고
페이지가 넘어가는 상황을 대비해 버튼 시작점을 계산하여 필드에 저장합니다
public void setBoardCount(int boardCount) {
this.boardCount = boardCount;
this.maxPage = (int)(Math.ceil((double)boardCount / this.pageLimit));
this.endPage = this.startPage + this.blockLimit -1;
}
총 board의 count에 따라 숫자버튼의 마지막 번호,총번호의 갯수를 계산합니다
}
- Service
인덱스에 사용된 service의 메소드는 특별한 부분이 없습니다
그렇기 때문에 리펙토링을 진행했던 Page에 관련된 부분만 작성하겠습니다
한화면에 보여주고 싶은 List의 갯수
public List<BoardDTO> bestBoardList(PageDTO bestPageDTO) {
bestPageDTO.setPageLimit(6);
return boardRepository.bestBoardList(bestPageDTO);
}
한화면에 보여주고 싶은 하단의 page버튼 갯수
public PageDTO bestPagingParam(PageDTO bestPageDTO,String q) {
bestPageDTO.setBlockLimit(2);
bestPageDTO.setQ(q);
bestPageDTO.setBoardCount(boardRepository.BoardCount(bestPageDTO));
if (bestPageDTO.getEndPage() > bestPageDTO.getMaxPage()) {
bestPageDTO.setEndPage(bestPageDTO.getMaxPage());
}
한화면에 보여주는 숫자 버튼의 갯수는 2개이지만
List가 2개의 페이지에 못미치고 끝나게 되는 경우를 대비하여
endPage가 MaxPage 보다 높아지게 된다면
endPage를 maxPage와 같게 만들어줍니다.
return bestPageDTO;
}
한화면에 보여주고 싶은 List의 갯수
public Object qnaBoardList(PageDTO qnaPageDTO, String q) {
qnaPageDTO.setPageLimit(10);
qnaPageDTO.setQ(q);
2번째 페이징을 진행하는 부분에서는 검색도 가능하기 때문에
검색어 또한 dto에 담아줍니다
return boardRepository.qnaBoardDTOList(qnaPageDTO);
}
위의 설명과 마찬가지입니다.
public Object qnaPagingParam(PageDTO qnaPageDTO, String q) {
qnaPageDTO.setBlockLimit(10);
qnaPageDTO.setQ(q);
qnaPageDTO.setBoardCount(boardRepository.BoardCount(qnaPageDTO));
if (qnaPageDTO.getEndPage() > qnaPageDTO.getMaxPage()) {
qnaPageDTO.setEndPage(qnaPageDTO.getMaxPage());
}
return qnaPageDTO;
}
- Repository
1번째 페이징인 bestBoard에서는 검색기능을 사용하지 않기 때문에
바로 Mapper로 값을 전달하고 return받습니다 하지만
2번째 페이징인 QnaBoard의 경우에는 검색을 받고 있기 때문에
일반적인 페이지 이동 , 검색의 경우 리턴받은 값의 차이가 있기 때문에
검색어가 기본값인 " "인지에 따라 사용되는 Mapper의 쿼리문을 나눴습니다
public List<BoardDTO> bestBoardList(PageDTO bestPageDTO) {
return sql.selectList("naverBoard.bestBoardList", bestPageDTO);
}
public List<BoardDTO> qnaBoardDTOList(PageDTO qnaPageDTO) {
if (!(qnaPageDTO.getQ().equals(""))) {
return sql.selectList("naverBoard.qnaSearchBoardList", qnaPageDTO);
} else {
return sql.selectList("naverBoard.qnaBoardList", qnaPageDTO);
}
}
하단의 페이징 버튼의 경우 공통으로 사용하였습니다
public int BoardCount(PageDTO qnaPageDTO) {
if (!(qnaPageDTO.getQ().equals(""))) {
return sql.selectOne("naverBoard.BoardSearchCount", qnaPageDTO);
} else {
return sql.selectOne("naverBoard.BoardCount");
}
}
- Mapper
BestBoard의 페이징
<select id="bestBoardList" parameterType="page" resultType="board">
select * from board_table order by boardHits desc limit #{pageStart},#{pageLimit}
조회수가 높은순으로 검색하고 limit만큼 boardDTO에 담아 리스트로 return
</select>
QnaBoard의 페이징 일반적인 페이지 이동시에 사용되는 쿼리문
<select id="qnaBoardList" parameterType="page" resultType="board">
select * from board_table order by boardCreatedDate desc limit #{pageStart},#{pageLimit}
가장 최근에 작성된 게시글부터 차례로 BoardDTO에 담아 리스트를 리턴
</select>
QnaBoard의 페이징 검색을 통한 페이징을 위해 사용되는 쿼리문
<select id="qnaSearchBoardList" parameterType="page" resultType="board">
select * from board_table
where boardContents like concat('%', #{q}, '%') or boardTitle like concat('%', #{q}, '%')
order by id desc limit #{pageStart}, #{pageLimit}
스프링부트의 맵퍼에서는 concat이 잘먹지 않기 때문에 해당 양시으로 사용하였습니다
게시글의 내용과 제목에 검색어가 포함이되는경우 가장 최근에 작성된 게시글 부터
BoardDTO에 담아 List로 리턴합니다
</select>
'나의 수업일지' 카테고리의 다른 글
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (7) (0) | 2023.06.11 |
---|---|
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (6) (0) | 2023.06.09 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (4) (0) | 2023.06.07 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (3) (0) | 2023.06.07 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (2) (0) | 2023.06.07 |