본문 바로가기

웹 개발/Spring

[Web_Spring] 15

실습(동적쿼리_검색)

- [Web_Spring] 14 이어서

 

 

1. src/main/java/com.example.board/domain/dao/BoardDAO.java​

package com.example.board.domain.dao;

import com.example.board.domain.vo.BoardVO;
import com.example.board.domain.vo.Criteria;
import com.example.board.mapper.BoardMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
// RDB에 가깝던 mapper 인터페이스를
// 객체에 가깝게 변경시켜주는 역할
@RequiredArgsConstructor
public class BoardDAO {
    private final BoardMapper boardMapper;

    //    게시글 목록
    public List<BoardVO> getList(Criteria criteria){
        return boardMapper.getList(criteria);
    }
    //    게시글 추가
    public void register(BoardVO boardVO){
        boardMapper.insert(boardVO);
    }
    //    게시글 한 개 가져오기
    public BoardVO findByBoardNumber(Long boardNumber){
        return boardMapper.select(boardNumber);
    }
    //    게시글 수정
    public boolean modify(BoardVO boardVO){
        return boardMapper.update(boardVO) == 1;
    }
    //    게시글 삭제
    public boolean remove(Long boardNumber){
        return boardMapper.delete(boardNumber) == 1;
    }
    //    게시글 전체 개수
    public int getTotal(Criteria criteria){
        return boardMapper.getTotal(criteria);
    }

}

 

 

 

2. src/test/java/com.example.board/domain/dao/BoardDaoTests.java

package com.example.board.domain.dao;

import com.example.board.domain.vo.BoardVO;
import com.example.board.domain.vo.Criteria;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@Slf4j
public class BoardDaoTests {
    @Autowired
    private BoardDAO boardDAO;

//    @Test
//    public void getListTest(){
//        boardDAO.getList().stream().map(BoardVO::toString).forEach(log::info);
//    }

//    @Test
//    public void insertTest(){
//        BoardVO boardVO = new BoardVO();
//        boardVO.setBoardTitle("새롭게 추가된 게시글 제목1");
//        boardVO.setBoardContent("새롭게 추가된 게시글 내용1");
//        boardVO.setBoardWriter("new1");
//        boardDAO.register(boardVO);
//        log.info("추가된 게시글 번호 : " + boardVO.getBoardNumber());
//    }

//    @Test
//    public void selectTest(){
//        log.info(boardDAO.findByBoardNumber(2582L).toString());
//    }

//    @Test
//    public void updateTest(){
//        BoardVO boardVO = boardDAO.findByBoardNumber(2582L);
//        boardVO.setBoardTitle("수정된 게시글 제목");
//        boardVO.setBoardContent("수정된 게시글 내용");
//
//        log.info("UPDATE : " + boardDAO.modify(boardVO));
//    }

//    @Test
//    public void deleteTest(){
//        log.info("DELETE : " + boardDAO.remove(2582L));
//    }

    @Test
    public void getTotalTest(){
        log.info("총 게시글 : " + boardDAO.getTotal(new Criteria()));
    }
}

 

 

3. src/main/java/com.example.board/service/BoardService.java​

package com.example.board.service;

import com.example.board.domain.vo.BoardVO;
import com.example.board.domain.vo.Criteria;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public interface BoardService {
    public List<BoardVO> getList(Criteria criteria);
    public void register(BoardVO boardVO);
    public BoardVO get(Long boardNumber);
    public boolean modify(BoardVO boardVO);
    public boolean remove(Long boardNumber);
    public int getTotal(Criteria criteria);
}

 

 

 

4. src/main/java/com.example.board/service/BoardServiceImpl.java

package com.example.board.service;

import com.example.board.domain.dao.BoardDAO;
import com.example.board.domain.vo.BoardVO;
import com.example.board.domain.vo.Criteria;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import java.util.List; 

//    Service
//    여러 개의 DML을 하나의 서비스로 묶어준다.
@Service
@RequiredArgsConstructor
@Qualifier("board") @Primary
public class BoardServiceImpl implements BoardService{
    private final BoardDAO boardDAO;

    @Override
    public List<BoardVO> getList(Criteria criteria) {return boardDAO.getList(criteria);}

    @Override
    public void register(BoardVO boardVO) {boardDAO.register(boardVO);}

    @Override
    public BoardVO get(Long boardNumber) {
        return boardDAO.findByBoardNumber(boardNumber);
    }

    @Override
    public boolean modify(BoardVO boardVO) {
        return boardDAO.modify(boardVO);
    }

    @Override
    public boolean remove(Long boardNumber) {
        return boardDAO.remove(boardNumber);
    }

    @Override
    public int getTotal(Criteria criteria) {
        return boardDAO.getTotal(criteria);
    }
}

 

 

 

5. src/main/resource/templates/board/list.html

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Board</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
    <meta name="description" content="" />
    <meta name="keywords" content="" />
    <link rel="stylesheet" href="/css/main.css" type="text/css"/>
    <style>
        .small-width {display: none;}
        .big-width {display: block;}
        div#searchWrap {
            text-align: center;
            display: flex;
        }
        select{
            width:30%;
        }
        #keyword{
            width:60%;
        }
        form#searchForm {
            overflow: hidden;
        }

        @media(max-width: 918px){
            .boardRegisterDate {display: none;}
            .boardUpdateDate {display: none;}
            .small-width {display: block;}
            .big-width {display: none;}
            div.small-width {display: block;}
            div.big-width {display: none;}

            div#searchWrap { display: block;}
            select{
                width:100%;
            }
            #keyword{
                width:100%;
            }
            .search{
                width:100%;
            }
        }

    </style>
</head>
<body class="is-preload">
<!-- Main -->
<div id="main">
    <div class="wrapper">
        <div class="inner">

            <!-- Elements -->
            <header class="major">
                <h1>Board</h1>
                <p>게시판 목록</p>
            </header>
            <!-- Table -->
            <h3><a href="/board/register" class="button small">글 등록</a></h3>
            <div class="table-wrapper">
                <table>
                    <thead>
                    <tr class="tHead">
                        <th class="boardNumber">번호</th>
                        <th class="boardTitle">제목</th>
                        <th class="boardWriter">작성자</th>
                        <th class="boardRegisterDate">작성일</th>
                        <th class="boardUpdateDate">수정일</th>
                    </tr>
                    </thead>
                    <tbody>
                    <th:block th:each="board:${boardList}">
                        <tr class="tBody" th:object="${board}">
                            <td class="boardNumber" th:text="*{boardNumber}"></td>
                            <td class="boardTitle"><a id="goRead" th:text="*{boardTitle}" th:href="*{boardNumber}"></a></td>
                            <td class="boardWriter" th:text="*{boardWriter}"></td>
                            <td class="boardRegisterDate" th:text="*{boardRegisterDate}"></td>
                            <td class="boardUpdateDate" th:text="*{boardUpdateDate}"></td>
                        </tr>
                    </th:block>
                    </tbody>
                </table>
                <form method="get" action="/board/list" id="searchForm" th:object="${pageDTO}">
                    <div class="fields">
                        <div class="field">
                            <div id="searchWrap" th:object="${criteria}">
                                <select name="type">
                                    <option value="">검색 기준</option>
                                    <option value="T" th:selected="*{type == 'T'}">제목</option>
                                    <option value="C" th:selected="*{type == 'C'}">내용</option>
                                    <option value="W" th:selected="*{type == 'W'}">작성자</option>
                                    <option value="TC" th:selected="*{type == 'TC'}">제목 또는 내용</option>
                                    <option value="TW" th:selected="*{type == 'TW'}">제목 또는 작성자</option>
                                    <option value="TCW" th:selected="*{type == 'TCW'}">제목 또는 내용 또는 작성자</option>
                                </select>
                                <input id="keyword" type="text" th:field="*{keyword}"/>
                                <a href="#" class="search button primary icon solid fa-search">검색</a>
                            </div>
                        </div>
                    </div>
                </form>
                <div class="big-width" style="text-align: center">
                    <a class="changePage" th:if="${pageDTO.prev}" th:href="${pageDTO.startPage - 1}"><code>&lt;</code></a>
                    <th:block th:each="num:${#numbers.sequence(pageDTO.startPage, pageDTO.endPage)}">
                        <code th:text="${num}" th:if="${pageDTO.criteria.pageNum == num}"></code>
                        <a class="changePage" th:unless="${pageDTO.criteria.pageNum == num}" th:href="${num}"><code th:text="${num}"></code></a>
                    </th:block>
                    <a class="changePage" th:if="${pageDTO.next}" th:href="${pageDTO.endPage + 1}"><code>&gt;</code></a>
                </div>
                <!--이전버튼 <code>현재페이지</code> 다음버튼-->
                <!--이전버튼 : 현재페이지 - 1-->
                <!--다음버튼 : 현재페이지 + 1-->
                <div class="small-width" style="text-align: center;" th:object="${pageDTO}">
                    <a class="changePage" th:if="*{criteria.pageNum > 1}" th:href="${criteria.pageNum - 1}"><code>&lt;</code></a>
                    <code th:text="${criteria.pageNum}"></code>
                    <a class="changePage" th:if="*{criteria.pageNum < realEnd}" th:href="${criteria.pageNum + 1}"><code>&gt;</code></a>
                </div>
                <form action="/board/list" th:object="${criteria}" name="pageForm">
                    <input type="hidden" th:field="*{pageNum}">
                    <input type="hidden" th:field="*{amount}">
                    <input type="hidden" th:field="*{type}">
                    <input type="hidden" th:field="*{keyword}">
                </form>
            </div>
        </div>
    </div>
</div>
</body>
<!-- Scripts -->
<script src="/js/jquery.min.js"></script>
<script src="/js/jquery.dropotron.min.js"></script>
<script src="/js/browser.min.js"></script>
<script src="/js/breakpoints.min.js"></script>
<script src="/js/util.js"></script>
<script src="/js/main.js"></script>
<script th:inline="javascript">;
let boardNumber = [[${boardNumber}]];
let $pageForm = $(pageForm);
let $serachForm = $(searchForm);
let params = [[${pageDTO.criteria.listLink}]];

$("a#goRead").on("click", function(e){
    e.preventDefault();
    location.href = "/board/read" + params + "&boardNumber=" + $(this).attr("href");
})

$("#searchForm a").on("click", function(e){
    e.preventDefault();

    if(!$serachForm.find("option:selected").val()){
        alert("검색종류를 선택하세요");
        return false;
    }

    if(!$serachForm.find("input[name='keyword']").val()){
        alert("키워드를 입력하세요");
        return false;
    }

    $serachForm.submit();
});

if(boardNumber != null){
    alert(boardNumber + "번 게시글이 등록되었습니다.");
}

$("a.changePage").on("click", function(e){
    e.preventDefault();
    $pageForm.find("input[name='pageNum']").val($(this).attr("href"));
    $pageForm.submit();
});
</script>
</html>

 

 

 

6. src/main/java/com.example.board/controller/BoardController.java​

package com.example.board.controller;

import com.example.board.domain.vo.BoardVO;
import com.example.board.domain.vo.Criteria; 
import com.example.board.domain.vo.PageDTO;
import com.example.board.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;

import javax.servlet.http.HttpServletRequest;

/*
 *   TASK        URL                 METHOD      PARAMETER       FORM        URL이동
 *
 *   전체목록    /board/list          GET         없음             없음
 *   등록       /board/register      POST         모든 항목        필요          /board/list
 *   조회       /board/read          GET          boardNumber     없음
 *   삭제       /board/remove        POST         boardNumber     없음          /board/list
 *   수정       /board/modify        POST         모든 항목        필요          /board/read
 *
 * */
@Controller
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/board/*")
public class BoardController {
    private final BoardService boardService;

    @GetMapping("register")
    public void register(){}

    @GetMapping("list")
    public String getList(Criteria criteria, Model model){
        log.info("----------------------------");
        log.info("list............. : " + criteria);
        log.info("----------------------------");

        model.addAttribute("boardList", boardService.getList(criteria));
        model.addAttribute("pageDTO", new PageDTO(criteria, boardService.getTotal(criteria)));
        return "/board/list";
    }

    @PostMapping("register")
//    매개변수에 선언된 객체는 자동으로 화면에 전달되므로,
//    이를 막기 위해서는 redirect 방식의 전송이 필요하다.
    public RedirectView register(BoardVO boardVO, RedirectAttributes rttr){
        log.info("----------------------------");
        log.info("register............. : " + boardVO);
        log.info("----------------------------");

        boardService.register(boardVO);
//        1. Flash 사용
//         세션에 파라미터를 저장하고, request 객체가 초기화된 후 다시 request에 담아준다.
        rttr.addFlashAttribute("boardNumber", boardVO.getBoardNumber());

//        2. 쿼리 스트링
//        rttr.addAttribute("boardNumber", boardVO.getBoardNumber());
        return new RedirectView("/board/list");
    }

    //    게시글 상세보기
    @GetMapping({"read", "modify"})
    public void read(Long boardNumber, HttpServletRequest req, Model model){
        log.info("----------------------------");
        log.info(req.getRequestURI() + "............. : " + boardNumber);
        log.info("----------------------------");
        model.addAttribute("board", boardService.get(boardNumber));
    }

    //    수정
    @PostMapping("modify")
    public RedirectView modify(BoardVO boardVO, RedirectAttributes rttr){
        log.info("----------------------------");
        log.info("modify............. : " + boardVO);
        log.info("----------------------------");
        boardService.modify(boardVO);
//        컨트롤러에서 다른 컨트롤러의 매개변수로 파라미터를 전달할 때에는
//        addAttribute(), 쿼리스트링 방식으로 전달해야 받을 수 있다.
//        Flash방식은 최종 응답 화면에서 사용될 파라미터를 전달할 때에만 사용하도록 한다.
        rttr.addAttribute("boardNumber", boardVO.getBoardNumber());
        return new RedirectView("/board/read");
    }

    //    삭제
    @PostMapping("remove")
    public String remove(Long boardNumber, Criteria criteria, Model model){
        log.info("----------------------------");
        log.info("remove............. : " + boardNumber);
        log.info("----------------------------");

        boardService.remove(boardNumber);
        return getList(criteria, model);
    }
}

 

 

 

 

'웹 개발 > Spring' 카테고리의 다른 글

[Web_Spring] 17  (0) 2022.06.29
[Web_Spring] 16  (0) 2022.06.28
[Web_Spring] 14  (0) 2022.06.26
[Web_Spring] 13  (0) 2022.06.25
[Web_Spring] 12  (0) 2022.06.24