티스토리 뷰

1. properties 부분

board.pageSize=10					// 한 페이지당 10개
board.linkSize=2					// 페이지 표시 수 2개
board.uploadPath=c:/java/upload				// 파일 저장 경로

 

2. HTML 부분

<!DOCTYPE html>
<html lang="en" xmlns:sec="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>게시판</title>
    <!-- CSS 파일 링크 -->
    <link rel="stylesheet" th:href="@{/base/css/list.css}">
    <!-- jQuery 파일 링크 -->
    <script th:src="@{/base/jq/jquery-3.7.1.min.js}"></script>
    <!-- JavaScript 파일 링크 -->
    <script th:src="@{/base/js/listJS.js}"></script>
    <!-- 페이지 이동 스크립트 -->
    <script>
        function pagingFormSubmit(currentPage) {
            $('#page').val(currentPage);
            $('#pagingForm').submit();
        }
    </script>
</head>
<body>
<h1>[ 게시판 ]</h1>
<!-- 글 목록 출력 영역 -->
<table>
    <tr>
        <!-- 전체 글 수 출력 -->
        <td>
            전체글 수: <span th:text="${boardPage.totalElements}"></span>
        </td>
        <!-- 현재 페이지 및 전체 페이지 수 출력 -->
        <td colspan="2">페이지 <span th:text="${page}"></span> of <span th:text="${boardPage.getTotalPages()}"></span></td>
        <!-- 글쓰기 링크, 인증된 사용자만 볼 수 있음 -->
        <td><a sec:authorize="isAuthenticated()" th:href="@{/board/write}">글쓰기</a></td>
        <!-- 홈으로 돌아가는 링크 -->
        <td><a th:href="@{/}">HOME</a></td>
    </tr>
    <tr>
        <!-- 테이블 헤더 -->
        <th>번호</th>
        <th style="width:300px;">제목</th>
        <th>작성자</th>
        <th>조회수</th>
        <th>작성일</th>
    </tr>
    <!-- 게시글 목록을 반복하며 출력 -->
    <tr th:each="board, status : ${boardPage}">
        <!-- 게시글 번호 -->
        <td th:text="${board.boardNum}" class="center"></td>
        <!-- 게시글 제목 및 링크 -->
        <td>
            <a th:text="${board.title}" th:href="@{/board/read(boardNum=${board.boardNum})}"></a>
        </td>
        <!-- 작성자 이름 -->
        <td th:text="${board.memberName}" class="center"></td>
        <!-- 조회수 -->
        <td th:text="${board.viewCount}" class="center"></td>
        <!-- 작성일 -->
        <td th:text="${#temporals.format(board.createDate, 'yy.MM.dd HH:mm')}"></td>
    </tr>
</table>

<!-- 페이지 네비게이션 영역 -->
<div class="pagination">
    <!-- 앞 뒤로 2페이지 씩 표기하기 -->
    <span
            th:if="${boardPage.getTotalPages() > 0}"
            th:each="counter : ${#numbers.sequence((page - linkSize < 1 ? 1 : page - linkSize),
             (page + linkSize > boardPage.getTotalPages() ? boardPage.getTotalPages() : page + linkSize))}">
        <!-- 현재 페이지 -->
        <th:block th:if="${counter == page}"></th:block>
            <a th:text="${counter}" th:href="|javascript:pagingFormSubmit(${counter})|"></a>&nbsp;
        <th:block th:if="${counter == page}"></th:block>
    </span>
    <br><br>
    <!-- 검색 폼 -->
    <form id="pagingForm" method="get" th:action="@{/board/list}">
        <!-- 현재 페이지를 담는 히든 필드 -->
        <input type="hidden" name="page" id="page" />
        <!-- 검색 타입 선택 -->
        <select id="type" name="searchType">
            <option value="title" th:selected="${searchType == 'title'}">제목</option>
            <option value="contents" th:selected="${searchType == 'contents'}">본문</option>
            <option value="memberName" th:selected="${searchType == 'memberName'}">작성자</option>
        </select>
        <!-- 검색어 입력 필드 -->
        <input type="text" name="searchWord" th:value="${searchWord}">
        <!-- 검색 버튼 -->
        <input type="submit" onclick="pagingFormSubmit(1)" value="검색">
    </form>
</div>
</body>
</html>

 

3. Controller 부분

package net.datasa.web5.controller;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.datasa.web5.domain.dto.BoardDTO;
import net.datasa.web5.security.AuthenticatedUser;
import net.datasa.web5.service.BoardService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Controller
@Slf4j
@RequestMapping("board")
@RequiredArgsConstructor
public class BoardController {

    private final BoardService boardService;

    /*  게시물을 10개씩 보여주기
        2번이상 쓰는 상수는 선언해놓는게 좋다
     */
    //  int pageSize = 10; → 타임리프와 @Value 안쓰는 경우
    @Value("${board.pageSize}") // 타임리프를 통하여 괄호안에 한 페이지당 게시글 갯수가 정해짐. 위에 변수를 직접 선언한 것과 같은 효과
            int pageSize;

    @Value("${board.linkSize}") // 페이지 이동 링크 수
    int linkSize;

    @Value("${board.uploadPath}") // 첨부파일 저장 경로
    String uploadPath;

    /**
     * 글 목록 보기
     * @param model
     * @return 글 목록 출력 HTML 파일
     */
     
// 게시글을 조회하는 기능 + 게시글 검색 기능
    @GetMapping("list")
    public String list(Model model,
                       @RequestParam(name="page", defaultValue = "1") int page,
                       @RequestParam(name="searchType", defaultValue = "") String searchType,
                       @RequestParam(name="searchWord", defaultValue = "") String searchWord) {
        log.debug("properties 값 : pageSize={} linkSize={} uploadPath={}", pageSize, linkSize, uploadPath);
        log.debug("요청 파라미터 : page={} searchType={} searchWord={}", page, searchType, searchWord);
        // 현재페이지, 페이지당 글수, 검색대상, 검색어
        Page<BoardDTO> boardPage = boardService.getList(page, pageSize, searchType, searchWord);

        log.debug("목록정보 getContent() : {}", boardPage.getContent());
        log.debug("현재페이지 getNumber() : {}", boardPage.getNumber());
        log.debug("전체 글 개수 getTotalElements() : {}", boardPage.getTotalElements());
        log.debug("전체 페이지수 getTotalPages() : {}", boardPage.getTotalPages());
        log.debug("한 페이지당 글 수 getSize() : {}", boardPage.getSize());
        log.debug("이전페이지 존재 여부 hasPrevious() : {}", boardPage.hasPrevious());
        log.debug("다음페이지 존재 여부 hasNext() : {}", boardPage.hasNext());

        // 검색부분 보여줄 것들
        model.addAttribute("page", page);               // 현재 페이지
        model.addAttribute("linkSize", linkSize);       // 페이지 이동링크 수
        model.addAttribute("searchType", searchType);   // 검색기준
        model.addAttribute("searchWord", searchWord);   // 검색어
        // 글 목록부분 보여줄 것
        model.addAttribute("boardPage", boardPage);     // 출력할 글정보
        return "boardView/list";
    }

    /**
     * 글쓰기 폼으로 이동
     * @return 글쓰기폼을 출력하는 HTML파일
     */
    @GetMapping("write")
    public String write() {
        return "boardView/writeForm";
    }

    /**
     *  게시글 저장
     */
    @PostMapping("write")
    public String writesave(@ModelAttribute BoardDTO boardDTO, @AuthenticationPrincipal AuthenticatedUser user) {
        boardDTO.setMemberId(user.getId());
        log.debug("저장할 정보 {}", boardDTO);
        boardService.write(boardDTO);
        return "redirect:/";
    }
}
더보기

[글 목록 요청 예]

/board/list : 게시글 전체 목록

/board/list?searchType=title&searchWord=테스트 : 전체 내용중 제목에 '테스트'가 들어간 게시글 목록

/board/list?page=5 : 게시글 5페이지 

/board/list?searchType=title&searchWord=&page=3 : 전체 내용 중 3페이지

/board/list?searchType=title&searchWord=테스트&page=1 : 제목에 '테스트'가 들어간 게시글 중 1페이지

 

[글 본문 읽기]

/board/read?boardNum=1

 

4. Service 부분

package net.datasa.web5.service;

import jakarta.persistence.EntityNotFoundException;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.datasa.web5.domain.dto.BoardDTO;
import net.datasa.web5.domain.entity.BoardEntity;
import net.datasa.web5.domain.entity.MemberEntity;
import net.datasa.web5.repository.BoardRepository;
import net.datasa.web5.repository.MemberRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.ArrayList;
import java.util.List;

@Service
@Transactional
@RequiredArgsConstructor
@Slf4j
public class BoardService {

    private final BoardRepository boardRepository;
    private final MemberRepository memberRepository;

    /**
     * 게시판 글 저장
     *
     * @param dto 저장할 글 정보
     */
    public void write(BoardDTO dto) {
        log.info("Writing a new board entry: {}", dto);
        // 글 작성자 정보 조회
        MemberEntity memberEntity = memberRepository.findById(dto.getMemberId())
                .orElseThrow(() -> new EntityNotFoundException("Member not found"));

        BoardEntity entity = BoardEntity.builder()
                .member(memberEntity)   
                .title(dto.getTitle())
                .contents(dto.getContents())
                .originalName(dto.getOriginalName())
                .fileName(dto.getFileName())
                .build();

        boardRepository.save(entity);
        log.debug("저장되는 엔티티 : {}", entity);
//      todo : 첨부파일 처리할 것
    }

    /**
     * 게시글 목록 조회
     *
     * @param page       현재 페이지
     * @param pageSize   한 페이지당 글 수
     * @param searchType 검색 대상 (제목 : titel, 내용 : contents, 작성자 : memberName)
     * @param searchWord 검색어
     * @return 글정보가 한 페이지 분량 저장된 Page객체
     */
    public Page<BoardDTO> getList(int page, int pageSize, String searchType, String searchWord) {
        // Pageable : 한페이지를 끊어오기 위한 정보
        // Sort.Direction.DESC, "boardNum" : 정렬 객체
        // Spring에서는 첫 페이지가 '0'이므로, 가독성을 위해 page-1해줘야 '1' 페이지부터 시작
        // p : 한 페이지당 10개씩 boardNum기준으로 내림차순하여 덩어리로 만들어 놓음
        Pageable p = PageRequest.of(page - 1, pageSize, Sort.Direction.DESC, "boardNum");
        // Pageable 객체로 정의된 변수 p에 담긴 정보를 findAll(오버로딩)로 가져오면
        // Page<BoardEntity>타입(객체)의 변수 entityPage에 담음
        Page<BoardEntity> entityPage = null;
        if (searchType.equals("title")) {
            entityPage = boardRepository.findByTitleContaining(searchWord, p);
        } else if (searchType.equals("contents")) {
            entityPage = boardRepository.findByContentsContaining(searchWord, p);
        } else if (searchType.equals("memberName")) {
            entityPage = boardRepository.findByMember_MemberName(searchWord, p);
        } else {
            entityPage = boardRepository.findAll(p);
        }

        // 리턴받은 entityPage의 정보를 바탕으로 출력할 정보만 가져갈 것이다.
        // 그래서 entityPage에 담겨있는 정보를 바탕으로 BoardDTO가 저장된 Page객체를 생성해서 리턴
        // → BoardDTO가 여러개 들어있는 dtoPage 완성
        // map 메서드 : 안에 있는 요소들을 순회하는 반복문 역할
        Page<BoardDTO> dtoPage = entityPage.map(this::convertToDTO);
        return dtoPage;
    }

    /**
     * BoardEntity 객체를 전달받아 BoardDTO객체로 변환하여 리턴
     * @param entity DB에서 읽은 정보를 담은 엔티티 객체
     * @return       출력용 정보를 담은 DTO객체
     */
    private BoardDTO convertToDTO(BoardEntity entity) {
        return BoardDTO.builder()                                   // 다른 곳에서 쓰기 위해 만든 것
                .boardNum(entity.getBoardNum())
                .memberId(entity.getMember().getMemberId())
                .memberName(entity.getMember().getMemberName())
                .title(entity.getTitle())
                .contents(entity.getContents())
                .viewCount(entity.getViewCount())
                .likeCount(entity.getLikeCount())
                .dislikeCount(entity.getDislikeCount())
                .originalName(entity.getOriginalName())
                .fileName(entity.getFileName())
                .createDate(entity.getCreateDate())
                .updateDate(entity.getUpdateDate())
                .build();
    }
}

 

5. Repository 부분

package net.datasa.web5.repository;

import net.datasa.web5.domain.entity.BoardEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface BoardRepository extends JpaRepository<BoardEntity, Integer> {

    // 메서드 이름에 find를 붙이면 DB의 select 구문 기능을 레포지토리(JPA)가 알아서 만들어줌
    // → 결국, DB에서 원하는 조건을 메서드 이름에 다 집어넣으면 JPA가 자동으로 생성
//    List<BoardEntity> findByTitleContaining(String s, Sort sort);

    // JPA에 정의되어 있는 쿼리 메서드 이름들 찾는 법
    // Spring.io 접속→ 화면 상단 Projects → Spring Data → Spring Data JPA → LEARN 블럭 →
    // 버전(3.3.2)에 맞는 Reference Doc. 클릭 → 회면 왼쪽 JPA 클릭 → JPA Query Methods

    // 전달된 문자열을 제목에서 검색한 후 지정한 한페이지 분량 리턴
    Page<BoardEntity> findByTitleContaining(String s, Pageable pageable);

    // 전달된 문자열을 작성자 이름에서 검색 후 지정한 페이지 분량 리턴
    // Member : entity
    // MemberName : Member 엔티티 안에 있는 작성자 이름
    // Member와 MemberName사이에 '_' 필요
    Page<BoardEntity> findByMember_MemberName(String s, Pageable pageable);

    // 전달된 문자열을 내용에서 검색 후 지정한 페이지 분량 리턴
    Page<BoardEntity> findByContentsContaining(String s, Pageable pageable);
}
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함