2022. 4. 26. 14:10ㆍ[Spring]_/[Spring]_포트폴리오 페이지 만들기
[환경]
개발툴 : IntelliJ
DB : oracle
프레임워크 : spring , mybatis
사용 언어 : ES6, Java , Html5 , CSS
[완성화면]
목록에서 번호를 클릭시
해당 게시글의 내용이 나오도록 한다.
개발 목표 : 기존에 만든 게시판에서 SPA 을 유지하며, 게시판 Row 클릭시 해당 목록 조회.
메뉴 1 소스코드]
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<style>
.board {
width: 100%;
}
</style>
</head>
<body>
<div id="content">
<div id="contentwrap">
<h2>CRUD</h2>
<button class="regi">등록</button>
<select id="selectview">
<option value="10" selected="selected">10개씩 보기</option>
<option value="15">15개씩 보기</option>
<option value="20">20개씩 보기</option>
</select>
<select class="searchoption">
<option value="titlename">제목</option>
<option value="wirtername">작성자</option>
</select>
<input type="text" class="searchtext" value =""/>
<button class="detailsearch">조회</button>
<table border="1" class="board">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>글쓴이</th>
<th>작성일자</th>
<th>조회수</th>
</tr>
</thead>
<tbody id="table">
</tbody>
<!-- forEach 문은 리스트 객체 타입을 꺼낼때 많이 활용된다. -->
</table>
<span id="nav"></span>
</div>
</div>
</body>
</html>
<script>
$(document).ready(async function () {
let regibtn = document.querySelector('.regi');
let tbody = document.querySelector('#table');
let viewcnt = document.querySelector('#selectview');
let nav = document.querySelector('#nav');
let $common = $commons.history
let searchoption = document.querySelector('.searchoption');
let searchtext = document.querySelector('.searchtext');
let detailsearchbtn = document.querySelector('.detailsearch');
searchtext.onkeyup = function(e){
if(e.key === "Enter" || e.keyCode === 13 ){
detailsearchbtn.click();
}
}
viewcnt.onchange = function(){
detailsearchbtn.click();
}
regibtn.onclick = function () {
let url = "/regi/loader.do"
let parentContent = document.querySelector('#contentwrap');
let id = _commons().util.random();
$common.sethistory(url,parentContent,id);
let test = $common.gethistory(url);
console.log(test);
$('#content').load(url,function(){
});
}
detailsearchbtn.onclick = function(){
let wr;
let ti;
if(searchoption.value == "titlename" ){
ti = searchtext.value;
wr = "";
}else{
wr = searchtext.value;
ti = "";
}
createBoard(1,wr,ti);
createPagenation(wr,ti);
}
detailsearchbtn.click();
async function createPagenation(wr,ti) {
let cnt;
while(nav.firstChild){
nav.removeChild(nav.firstChild);
}
await fetch('/board/boardcount.do', {
method: "POST",
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
writer : wr,
title : ti
})
}).then(res => res.json())
.then(data => {
cnt = data;
})
//총 페이지 수
let totalpagecnt = Math.ceil(cnt / viewcnt.value);
//페이지 숫자 생성.
for (let i = 1; i <= totalpagecnt; i++) {
//html 생성하기.
let node = document.createElement('a');
const template = `
<a class="navclick" href="javascript:void(0)">\${i}</a>
`
node.innerHTML = template;
node.querySelector('a').onclick = function () {
createBoard(i,wr,ti);
}
nav.appendChild(node);
}
}
//테이블 생성
async function createBoard(nowpage,wr,ti) {
while(tbody.firstChild){
tbody.removeChild(tbody.firstChild);
}
await fetch('/board/search.do', {
method: "POST",
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
view: viewcnt.value, // ~만큼 보기
pagenum: nowpage, // 페이지 숫자클릭넘버.
writer : wr,
title : ti
})
}).then(res => res.json())
.then(data => {
for (let i = 0; i < data.length; i++) {
let node = document.createElement('tr');
const template = `
<th><a href="javascript:void(0)" class="boardlink">\${data[i].NUM}</a></th>
<th>\${data[i].title}</th>
<th>\${data[i].writer}</th>
<th>\${data[i].regdate}</th>
<th>\${data[i].viewcnt}</th>
`
node.innerHTML = template;
tbody.appendChild(node);
let boardlink = document.getElementsByClassName('boardlink');
boardlink[i].onclick =function(){
selectBoard(data[i].bno);
}
}
});
}
//테이블 목록 클릭시 함수
function selectBoard(bno){
fetch('/board/selectboard.do',{
method:"POST",
headers: {
'content-type': 'application/json'
},
body :JSON.stringify({
bno: bno
})
}).then(res => res.json())
.then(data => {
console.log(data);
console.log(data[0].content);
let url = "/board/selectboarddetail.do"
let parentContent = document.querySelector('#contentwrap');
let id = _commons().util.random();
$common.sethistory(url,parentContent,id);
let jsondata = {
"title" : data[0].title,
"content" : data[0].content,
"writer" : data[0].writer,
"regdate" : data[0].regdate,
"bno" : data[0].bno
}
$('#content').load(url,jsondata,function(){
});
})
}
});
</script>
이전 포스팅에 비해 좀더 새분화 하여 나누었으며
기능은
테이블 생성, 목록 조회, 조회버튼 클릭, view 카운트 변경
의 4가지 기능으로 나누어 작성하였습니다.
코드설명]
1] 테이블 생성 부분
//테이블 생성
async function createBoard(nowpage,wr,ti) {
while(tbody.firstChild){
tbody.removeChild(tbody.firstChild);
}
await fetch('/board/search.do', {
method: "POST",
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
view: viewcnt.value, // ~만큼 보기
pagenum: nowpage, // 페이지 숫자클릭넘버.
writer : wr,
title : ti
})
}).then(res => res.json())
.then(data => {
for (let i = 0; i < data.length; i++) {
let node = document.createElement('tr');
const template = `
<th><a href="javascript:void(0)" class="boardlink">\${data[i].NUM}</a></th>
<th>\${data[i].title}</th>
<th>\${data[i].writer}</th>
<th>\${data[i].regdate}</th>
<th>\${data[i].viewcnt}</th>
`
node.innerHTML = template;
tbody.appendChild(node);
let boardlink = document.getElementsByClassName('boardlink');
boardlink[i].onclick =function(){
selectBoard(data[i].bno);
}
}
});
}
DB 를 조회하여 얻은 JSON 데이터를 for문을 돌면서 각 줄에 a 태그에 이벤트를 달아줍니다.
2] 테이블 조회 함수
//테이블 목록 클릭시 함수
function selectBoard(bno){
fetch('/board/selectboard.do',{
method:"POST",
headers: {
'content-type': 'application/json'
},
body :JSON.stringify({
bno: bno
})
}).then(res => res.json())
.then(data => {
console.log(data);
console.log(data[0].content);
let url = "/board/selectboarddetail.do"
let parentContent = document.querySelector('#contentwrap');
let id = _commons().util.random();
$common.sethistory(url,parentContent,id);
let jsondata = {
"title" : data[0].title,
"content" : data[0].content,
"writer" : data[0].writer,
"regdate" : data[0].regdate,
"bno" : data[0].bno
}
$('#content').load(url,jsondata,function(){
});
})
}
위에서 만든 함수가 클릭 되었을때 bno ( 테이블을 구별할 수 있는 고유 키) 를 기준으로 fetch 통신을 합니다.
2-1] Controller
@RequestMapping("/board/selectboard.do")
@ResponseBody
public String selectBoard(@RequestBody Map<String, String> map) throws Exception {
String data = map.get("bno");
System.out.println("data = " + data);
Integer bno = Integer.parseInt(data);
List result = boardService.selectBoard(bno);
//json 변환
String json = new Gson().toJson(result);
return json;
}
bno를 기준으로 데이터를 집어오고 json으로 반환해 줍니다.
※ service, serviceimpl 생략
2-2] BoardMapper
List<BoardVO> selectBoard(Integer bno);
해당 bno를 기준으로 vo 객체에 선언된 형식으로 데이터를 반환합니다.
2-3] Mapper.xml 쿼리
<select id="selectBoard" resultType="com.yoon.model.BoardVO">
select *
from board
where bno = #{bno}
</select>
간단하게 select 문으로 bno 번호가 일치하는 값을 받아옵니다.
3] fetch 통신 이후
.then(data => {
console.log(data);
console.log(data[0].content);
let url = "/board/selectboarddetail.do"
let parentContent = document.querySelector('#contentwrap');
let id = _commons().util.random();
$common.sethistory(url,parentContent,id);
let jsondata = {
"title" : data[0].title,
"content" : data[0].content,
"writer" : data[0].writer,
"regdate" : data[0].regdate,
"bno" : data[0].bno
}
$('#content').load(url,jsondata,function(){
});
})
해당 데이터는 json 데이터를 리스트로 받아왔습니다. 따라서 인덱스 번호로 조회가 가능하고, bno는 고유키 이기 때문에 0으로 지정해줬습니다.
그리고 상세페이지의 url을 지정하고,
이전 포스팅을 했던 history를 이용하여
다시 찾아갈 수 있게 해주는 url 를 키로 하여 content를 보관하는 객체를 만들어줍니다.
history 객체 만들기 다음포스트 참고
https://yn971106.tistory.com/93
그리고 상세페이지의 url 과 전달할 json 형식의 데이터를 jquery의 load() 함수를 이용하여 전달합니다.
4] 상세 페이지 연결 통신
let url = "/board/selectboarddetail.do" 의 url 매핑으로 controller를 호출합니다.
@RequestMapping("/board/selectboarddetail.do")
public ModelAndView detail(@RequestParam Map<String, String> map) {
System.out.println("상세조회 진입");
String title = map.get("title");
String content = map.get("content");
String writer = map.get("writer");
String regdate = map.get("regdate");
String bnostring = map.get("bno");
Integer bno = Integer.parseInt(bnostring);
ModelAndView mv = new ModelAndView("menu/boardselect");
mv.addObject("title", title);
mv.addObject("content", content);
mv.addObject("writer", writer);
mv.addObject("regdate", regdate);
mv.addObject("bno",bno);
return mv;
}
해당 Controller는 ModelAndView 를 사용합니다.
String 의 url 주소로 jsp 페이지를 전환을 하면서 데이터도 같이 전달하고 싶을 때 사용합니다.
우선 받은 json 데이터를 key를 기준으로 각각의 데이터로 바꾼 뒤.
ModelAndVIew 생성자를 만들고 바로 이동시킬 url 을 매핑합니다.
생성자에는 addObject를 이용해서 key 와 value의 순서로 기입합니다.
그리고 이를 return 합니다.
5] 상세페이지 jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div id="contentwrap2">
<h2> 제목 : <span class="title">${title}</span></h2>
<p> 작성자 : <span class="writer">${writer}</span> </p>
<p> 내용 : <span class="boardcontent">${content}</span> </p>
<p>작성일자 : <span class="date">${regdate}</span> </p>
<button class="backbtn">뒤로가기</button>
<button class="updatebtn" style="display: none">수정하기</button>
<button class="rmbtn" style="display: none">게시글 삭제</button>
</div>
</body>
</html>
<script src='/js/jquery-3.6.0.min.js?ver=1'></script>
<script>
$(document).ready(function(){
});
</script>
해당 jsp 가 열리게 되고
html 에서 데이터를 바로 뿌려주려면 %{key이름} 으로 바로 보여줄 수도 있습니다.
script 에서 핸들링을 하고 싶다면,
let writercheck = "${writer}";
이런식으로 사용 가능합니다.
이번포스팅에서는 row 별 이벤트 달기, row 클릭시 상세화면 이동 후 데이터 뿌려주기 까지 완료하였습니다.
다음에는 해당 게시글을 수정( update 하는 기능을 알아보겠습니다)
'[Spring]_ > [Spring]_포트폴리오 페이지 만들기' 카테고리의 다른 글
[포트폴리오 페이지]_17단계_CRUD 게시판 구현_(Delete) (0) | 2022.04.26 |
---|---|
[포트폴리오 페이지]_16단계_CRUD 게시판 구현_(Update 기능) (0) | 2022.04.26 |
[포트폴리오 페이지]_14단계_CRUD 게시판 구현_(검색 기능) (0) | 2022.04.26 |
[포트폴리오 페이지]_13단계_CRUD 게시판 구현_(신규 데이터 Create) (0) | 2022.04.26 |
[포트폴리오 페이지]_12단계_SPA 구현을 위한 History 객체 만들기 (0) | 2022.04.26 |