[포트폴리오 페이지]_15단계_CRUD 게시판 구현_(목록 Read 기능)

2022. 4. 26. 14:10[Spring]_/[Spring]_포트폴리오 페이지 만들기

728x90
반응형

[환경]

 

개발툴 : 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

 

[포트폴리오 페이지]_12단계_SPA 구현을 위한 History 객체 만들기

[환경] 개발툴 : IntelliJ DB : oracle 프레임워크 : spring , mybatis 개발 목표 : 기존에 만든 게시판에서 SPA 을 유지하기 위해 common 파일 생성 common.js 파일을 사용할 위치를 지정해 줍니다. ( 자유 ) le..

yn971106.tistory.com

 

그리고 상세페이지의 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 하는 기능을 알아보겠습니다)

728x90
반응형