본문 바로가기
Framework/Spring

[Spring Boot] 게시판 목록 페이지

by 코딩하는 랄로 2024. 1. 8.
728x90

Spring MVC에 대해서 알아보았으니 이를 활용하여 게시판의 목록 페이지를 만들면서 직접 Spring Boot를 사용해보겠다.  IDE로는 Intellij ultimate을 사용하였고 View를 생성하기 위한 Template Engine으로는 Thymeleaf를 사용하였다.

 

 

 

DB 생성

간단한 게시판을 만들기 위해서 필요한 테이블은 하나만 있어도 된다. 후에 파일 첨부, 로그인 등 부가 기능이 추가된다면 테이블이 여러개 필요하지만 간단한 게시판을 만들기 위해서는 Board Table 하나만 생성하겠다.

CREATE TABLE board (
    id int PRIMARY KEY AUTO_INCREMENT,
    title varchar(100) NOT NULL,
    content text NOT NULL,
    viewCnt int DEFAULT 0,
    regDate datetime DEFAULT now()
);

 

Board table을 만들기 위한 DDL이다. 제목과 내용은 NOT NULL 조건을 걸어주었고, viewCnt는 조회수를 위한 attribute이다. 추가로 목록 페이지에 보여주기 위해 미리 샘플 데이터를 추가하자.

INSERT INTO board (title, content)
VALUES ('title1', 'content1'),
       ('title2', 'content2'),
       ('title3', 'content3'),
       ('title4', 'content4'),
       ('title5', 'content5');

 

추가적으로. Spring Boot를 사용하여 DB에 접근하여 데이터를 다루기 위해서는 이번 프로젝트에서는 SQL Mapper 방식을 사용을 하고 이를 위해 Spring에서 제공하는 MyBatis 를 Dependency 에 추가해주어야 한다.

 

추가로 mapper 는 spring project의 resources에 mapper directory를 추가하여 해당 폴더를 mapper 파일을 저장해주겠다.

 

추가로 mapper directory 하위의 mapper 파일을 mybatis mapper로 사용하겠다는 설정을 해주어야 하기 때문에 application.yml에 다음과 같이 설정을 추가해주어야 한다.

>> yaml 파일
mybatis:
  mapper-locations: classpath:mapper/**/*.xml

>> properies 파일
mybatis.mapper-locations=classpath:mapper/**/*.xml

 

 

 

 

게시판 목록 페이지 설계

이제 코드를 작성하기 위한 준비는 끝났다. 본격적으로 코드를 작성하기 전에 먼저 게시판 목록을 보여줄 때, 어떤 요청이 들어왔을 때 해당 목록을 보여줄 것인지, 그리고 목록에는 Board 테이블에서 어떠한 정보들을 가져와서 보여줄 것인지 등을 정해야 한다.

 

이번 게시판에서는 기본적으로 "/" 로 들어온 요청에 대해서 목록을 보여주기로 하고, 목록에 표시될 board의 정보는 id, title, viewCnt만 아래와 같이 보여주도록 하겠다. 

 

 

 

Repository & Mapper 생성

페이지 설계까지 끝마쳤으니 이제 코드를 작성을 해보자!! 목록 페이지에 어떠한 정보를 보여줄지를 정하였으니 해당 정보를 가지고 오는 Repository 와 Mapper 를 생성하여야 한다.

 

MVC 구조에서 Model은 DB에 접근하여 데이터를 가져와 비즈니스 로직을 처리하는데, 이 때 Repository와 Mapper는 바로 DB에서 데이터를 다루기 위한 단계라고 생각하면 된다.

 

Repository 계층에서 데이터를 다루기 위한 CRUD를 어떻게 정의할 것인가 데이터를 Collection을 사용하여 여러개를 받아올 것인가 데이터를 뽑아오기 위해 어떠한 정보들이 필요한 가를 정의해주는 interface이고, Mapper는 이렇게 정의된 것을 토대로 데이터를 직접 DB에서 다루게 되는 것이다.

 

이를 위해 위와 같이 각각의 해당하는 파일을 생성해주었다. ( BoardRepostiry는 interface class로 생성해주어야 한다. ) 목록 페이지에서는 board 테이블의 있는 정보들을 모두 간략하게 보여주기 때문에 List를 사용하여 여러 데이터를 받을 수 있도록 하고 추가로 데이터를 가져오기 위한 정보는 없기 때문에 파라미터는 없어도 된다.

package com.spring.study.repository;

import com.spring.study.domain.Board;
import java.util.List;

public interface BoardRepository {
    List<Board> findAll();
}

 

mapper 파일에서는 mapping 된 repository의 메소드 이름을 id로 가지는 sql문을 생성해주면 된다.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.spring.study.repository.BoardRepository">
    <select id="findAll" resultType="com.spring.study.domain.Board">
        SELECT
            id,
            title,
            viewCnt
        FROM
            board
    </select>
</mapper>

 

Mapper에서 반환하는 Data를 자바의 Object로 변환하기 위한 DTO 객체인 Board는 위의 project 구조에서 Domain directory 에 선언하여 준다.

package com.spring.study.domain;

import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;

@Data   // Getter & Setter 생성
@NoArgsConstructor  // 기본 생성자 생성
public class Board {
    private Long id;
    private String title;
    private String content;
    private long viewCnt;

    private LocalDateTime regDate;
}

 

Board 객체를 반환받아야 하기 때문에, mapper에서 resultType을 꼭 지정해주어야 한다. mapper에서 데이터를 반환할 때, 형성되는 컬럼 네임으로 Board 객체의 setter를 찾아서 프로퍼티값을 설정해주기 때문에 만약 테이블의 컬럼명과 Board 객체의 property 명이 다르다면 SELECT문에서 alias를 사용하여 같게 해주어야 한다!! ( <= 매우 중요) 예를 들어, board_title이라는 컬럼명을 가지고 있다면 board_title "title"로 alias를 지정해주어야 Board의 title property에 값이 담긴다. ( alias를 안 해주면 setBoardTitle이라는 setter를 찾음...)

 

 

 

Service 생성

Repository와 Mapper는 데이터를 가져오는 방식을 정의하고 실제로 가져오는 단계를 담당하고 Model의 남은 작업인 비즈니스 로직에 관한 부분은 바로 Service 단계에서 처리한다.

 

Reposioty를 통해 받아온 데이터를 처리하고 이를 Controller에 넘겨주는 것이다. Spring에서는 @Service annotation을 이용하여 Service layer를 지정해줄 수 있다. 

 

위와 같이 interface를 생성해준 뒤, BoardServiceImpl class에서 이를 구현하도록 하자. 사실 목록 페이지를 위해서 Service 단에서는 딱히 처리해야 할 비즈니스 로직이 없기 때문에, repository를 통해 받은 데이터를 바로 반환해 주어도 된다.

public interface BoardService {
    List<Board> findAll();
}

@Service
public class BoardServiceImpl implements BoardService{

    private BoardRepository boardRepository;

    // 생성자의 파라미터가 하나일 때는 @Autowired 생략 가능
    public BoardServiceImpl(SqlSession sqlSession) {
        this.boardRepository = sqlSession.getMapper(BoardRepository.class);
    }

    @Override
    public List<Board> findAll() {
        return boardRepository.findAll();
    }
}

 

 

 

Controller 생성

이제 마지막으로 요청을 받고 Model에게 비즈니스 로직에 대한 작업을 위임하고 반환받은 결과와  View에 관한 정보를 반환하는 Controller를 생성하여 보자. "/" 에 대한 요청에서 list를 보여주기로 하였기 때문에 다음과 같이 코드를 작성을 하면 된다.

@Controller
public class BoardController {

    private BoardService boardService;

    public BoardController(BoardService boardService) {
        this.boardService = boardService;
    }

    @RequestMapping("/")
    public String list(Model model) {
        model.addAttribute("list", boardService.findAll());
        return "list";
    }

}

 

먼저 @Controller annotation을 통해 해당 클래스가 Controller임을 지정해주고 Model 계층에 작업을 위임하기 위해 Service 클래스를 주입받은 뒤에 @RequestMapping을 사용하여 "/" 들어오는 어떤 요청에도 list라는 view를 리턴하는 코드이다.

 

Model계층이 반환한 데이터 또는 그 외에 view를 생성할 때 필요한 데이터를 ModelAndView 에 담아야 하는데 이를 위한 객체가 바로 Model 객체이다. Model 객체의 addAttribute 메소드를 사용하여 Service 단에서 반환한 List<Board> 를 list라는 이름으로 담아서 보내는 것이다.

 

 

 

View 생성

view는 html로 작성하였고 랜더링하기 위한 template engine으로는 thymeleaf를 사용하였다. 또한 view 는 다음과 같이 template/views directory 에 생성하여 주었다.

 

추가로, 아래와 같이 설정을 해주어야 한다.

>> yaml 파일
spring: 
  thymeleaf:
    prefix: classpath:templates/views/

>> properties 파일
spring.thymeleaf.prefix=classpath:templates/views/

 

설정을 해주었으니 thymeleaf를 사용하여 list.html 파일을 다음과 같이 작성을 해주면 된다. (thymeleaf 문법에 대해서는 다른 글에서 자세히 다루겠다. )

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>

    <title>목록</title>
</head>
<body>
    <div class="container mt-3 w-50">
        <h2>목록</h2>
        <hr>

        <!-- 목록 -->
        <table class="table table-hover">
            <thead class="table-success">
            <tr>
                <th class="col-3">id</th>
                <th class="col-6">제목</th>
                <th class="col-3">조회수</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="board : ${list}">
                <th><span th:text="${board.id}"></span></th>
                <th><span><a th:href="@{'/detail/' + ${board.id}}" th:text="${board.title}"></a></span></th>
                <th><span th:text="${board.viewCnt}"></span></th>
            </tr>
            </tbody>
        </table>
        <!-- 목록 -->

        <div class="row">
            <div class="col-12">
                <a class="btn btn-outline-dark" th:href="@{/board/write}">작성</a>
            </div>
        </div>
    </div>
</body>
</html>

 

이제 코드를 돌려보면, Board 테이블에 있는 데이터를 토대로 목록을 잘 보여주는 것을 확인할 수 있을 것이다.

728x90