JPA 게시판 등록 - JPA gesipan deunglog

Spring Data Jpa와 H2 Database를 사용하여 게시글을 조회, 등록, 수정, 삭제하는 API를 구현하겠습니다.

1.Dependency 설정

pom.xml

2.Properties 설정

application.properties

3.Entity 구현

JPA에서 제공하는 어노테이션

@Entity

 클래스와 테이블과 매핑한다고 JPA에게 전달하며, @Entity 어노테이션이 선언된 클래스를 엔티티 클래스라고 합니다.

@Table

 엔티티 클래스에 매핑할 테이블 정보를 전달하며, 생략 시 클래스명을 테이블명으로 매핑합니다.

@Id

 엔티티 클래스의 필드를 테이블에 기본 키(PK, Primary key)로 매핑합니다.

@GeneratedValue

 생성 전략(strategy)에 따라 기본 키를 지정합니다.

AUTO(default) - JPA가 자동으로 생성 전략을 결정합니다.

IDENTIT - 기본키 생성을 데이터베이스에 위임한다. 예를 들어 MySQL의 경우 AUTO_INCREMENT를 사용하여 기본키를 생성합니다.

SEQUENCE - 데이터베이스의 특별한 오브젝트 시퀀스를 사용하여 기본키를 생성합니다.

TABLE - 데이터베이스에 키 생성 전용 테이블을 하나 만들고 이를 사용하여 기본키를 생성합니다.

@Column

 엔티티 클래스의 필드를 컬럼에 매핑하며, 생략 시 필드명을 컬럼에 매핑합니다.

@Lob

 엔티티 클래스의 필드를 데이터베이스 BLOB, CLOB, TEXT 타입과 맵핑합니다.

Lombok에서 제공하는 어노테이션

@NoArgsConstructor

기본 생성자를 자동으로 추가합니다.

@Getter

클래스 내 모든 필드의 Getter 메소드를 자동 생성합니다.

@Builder

해당 클래스의 빌더 패턴 클래스를 생성합니다.

생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함합니다.

Board.java

4.Dto 구현

 Entity 클래스를 변경하는 경우 DB Layer에 영향을 주기 때문에, View Layer에서 사용할 Request/Response Dto를 추가하세요.

BoardSaveRequestDto.java

BoardUpdateRequestDto.java

BoardResponseDto.java

5.Repository 구현

 Spring Data JPA는 Repository 계층의 반복되는 작업을 피하기 위해 JpaRepository 인터페이스를 제공합니다. JpaRepository 인터페이스에는 자주 사용되는 기본적인 CRUD 메서드가 선언되어 있는데, 개발자는 Repository 클래스를 생성하지 않고 인터페이스를 생성하여 JpaRepository 인터페이스를 상속받으면 JpaRepository에서 선언한 메서드들을 사용 할 수 있게 됩니다. 또한 JpaRepository에서 제공하는 메서드 외에 필요한 쿼리를 수행하기 위한 메서드를 직접 정의할 수도 있습니다.

BoardRepository.java

6.Service 구현

BoardService.java

7.Controller 구현

BoardController.java

8.SQL Script

 src/main/resources에 import.sql 파일이 있으면 서버 실행 시에 해당 스크립트를 자동 실행합니다.

import.sql

9.프로젝트 구조

JPA 게시판 등록 - JPA gesipan deunglog

Github Repository - https://github.com/tychejin1218/jpa-sample

springboot는 jsp 사용을 권장하지 않는다.
이유 : 독립적으로 실행가능한 빠른 개발을 목표로 하기 때문.
        (웹어플리케이션은 WAR 배포가 아닌 JAR 배포를 권장)

jsp를 사용하면 JAR 배포가 아닌 WAR 배포를 해야 하고(jar 배포 시 jsp파일은 배포에 포함되지 않는다..), WAR는 복잡한 폴더 구조를 가져야 하므로 springboot가 목표로 하는 독립실행,빠른개발에 어울리지 않는다나... 뭐라나...

하지만 실제 현업에서는 아직도 jsp를 많이 사용하고 있다.

jar 배포, war 배포 는 나중에 다시 정리 해보자.

H2DB를 사용할 예정이므로 아래 게시물을 참고하여 로컬에 H2DB 설치가 필요함.

2021.08.01 - [개발/database] - [H2 DB] 설치 및 실행

프로젝트 생성 시 선택 한 Dependencies

JPA 게시판 등록 - JPA gesipan deunglog

jsp 사용을 위한 jstl과 tomcat-embed-jasper를 pom.xml 에 추가

...
<!--jsp-->
<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>jstl</artifactId>
</dependency>
<dependency>
	<groupId>org.apache.tomcat.embed</groupId>
	<artifactId>tomcat-embed-jasper</artifactId>
</dependency>

application.peoperties에 ViewResolver 세팅

# ViewResolver 세팅
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

src/main/webapp/WEB-INF/jsp 폴더 생성

(spring boot는 jsp를 기본적으로 지원하지 않기 때문에 폴더를 생성해야 한다)

JPA 게시판 등록 - JPA gesipan deunglog

게시판 목록 조회 만들기

1. Board 도메인 만들기

package com.tistory.hitomis.springbootjspboard.domain;

import lombok.Data;
import java.util.Date;

@Data
public class Board {
    private Long seq;           //pk
    private String title;       //제목
    private String writer;      //작성자
    private String content;     //내용
    private Date createDate;    //생성일자
    private Long cnt;           //조회수
}

2. Board Controller 만들기 - jsp에 잘 나오는지 테스트용 목록 화면 컨트롤러

package com.tistory.hitomis.springbootjspboard.controller;

import com.tistory.hitomis.springbootjspboard.domain.Board;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

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

@Controller
public class BoardController {

    @RequestMapping("/testBoardList")
    public String testBoardList(Model model) {
        List<Board> boardList = new ArrayList<Board>();

        // 임시로 게시물 10개를 만들자
        for (int i = 0; i < 9; i++) {
            Board board = new Board();
            board.setSeq(new Long(i));
            board.setTitle("제목   " + i);
            board.setWriter("작성자 " + i);
            board.setContent("글내용  " + i);
            board.setCreateDate(new Date());
            board.setCnt(0L);
            boardList.add(board);
        }
        model.addAttribute("boardList", boardList);
        return "testBoardList"; // jsp 파일 이름
    }
}

3. src/main/webapp/WEB-INF/jsp 폴더 아래 testBoardList.jsp 파일 만들기

<%@page contentType="text/html; charset=UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>게시판 목록</title>
    <style>
        table {
            width: 100%;
            border: 1px solid #444444;
            border-collapse: collapse;
        }
        table th {
            border: 1px solid #444444;
            text-align: center;
            height: 40px;
            background-color: dodgerblue;
            color: cornsilk;
        }
        table td {
            border: 1px solid #444444;
            text-align: center;
        }
    </style>
</head>
<body>
<div style="text-align: center;">
    <h2>게시판 목록</h2>
    <table style="width: 700px; margin: auto">
        <tr>
            <th style="width: 10%">번호</th>
            <th style="width: 50%">제목</th>
            <th style="width: 15%">작성자</th>
            <th style="width: 15%">등록일</th>
            <th style="width: 10%">조회수</th>
        </tr>
        <c:forEach var="board" items="${boardList}">
            <tr>
                <td>${board.seq}</td>
                <td style="text-align: left"><a href="getBoard?seq=${board.seq}">${board.title}</a></td>
                <td>${board.writer}</td>
                <td><fmt:formatDate value="${board.createDate}" pattern="yyyy-MM-dd"></fmt:formatDate> </td>
                <td>${board.cnt}</td>
            </tr>
        </c:forEach>
    </table>
    <a href="insertBoard">새글 등록</a>
</div>
</body>
</html>

4. 브라우저로 확인 (localhost:8080/testBoardList)

JPA 게시판 등록 - JPA gesipan deunglog

H2 데이터 베이스, JPA 연동

5. Board.java 엔티티 클래스로 변경

package com.tistory.hitomis.springbootjspboard.domain;

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Date;

@Data
@Entity
public class Board {
    @Id @GeneratedValue
    private Long seq;           //pk
    
    private String title;       //제목
    
    @Column(updatable = false)
    private String writer;      //작성자
    
    private String content;     //내용
    
    @Column(insertable = false, updatable = false, columnDefinition = "date default sysdate")
    private Date createDate;    //생성일자
    
    @Column(insertable = false, columnDefinition = "number default 0")
    private Long cnt;           //조회수
}

6. board 테이블 생성을 위한 application.properties 수정

(spring.jpa.hibernate.ddl-auto=create : 프로그램 실행 시 ddl을 생성한다. (ex. 테이블..))

(실행시마다 데이터가 삭제되는 걸 방지 하려면 최초 create 로 실행 후 이후에는 update로 변경 해야함.)

# Database 기본 세팅
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.username=sa
spring.datasource.password=

# JPA 하이버네이트 로그 레벨 설정
logging.level.org.hibernate=info
logging.level.org.springframework=info

# ViewResolver 세팅
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

#JPA 세팅
spring.jpa.hibernate.ddl-auto=create
# spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=false
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.format_sql=true

7. Repository 인터페이스 생성 - Board 엔티티를 이용한 데이터베이스에 조회/생성/수정/삭제 담당

package com.tistory.hitomis.springbootjspboard.persistence;

import com.tistory.hitomis.springbootjspboard.domain.Board;
import org.springframework.data.repository.CrudRepository;

public interface BoardRepository extends CrudRepository<Board, Long> {
}

8. 비즈니스 컴포넌트 만들기 (Class, Interface)

package com.tistory.hitomis.springbootjspboard.service;

import com.tistory.hitomis.springbootjspboard.domain.Board;

import java.util.List;

public interface BoardService {
    List<Board> getBoardList(Board board);

    void insertBoard(Board board);

    Board getBoard(Board board);

    void updateBoard(Board board);

    void deleteBoard(Board board);
}
package com.tistory.hitomis.springbootjspboard.service;

import com.tistory.hitomis.springbootjspboard.domain.Board;
import com.tistory.hitomis.springbootjspboard.persistence.BoardRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BoardServiceImpl implements BoardService {

    @Autowired
    private BoardRepository boardRepository;

    /**
     * 글목록 조회
     * @param board
     * @return
     */
    @Override
    public List<Board> getBoardList(Board board) {
        return (List<Board>) boardRepository.findAll();
    }

    /**
     * 글쓰기 처리
     * @param board
     */
    @Override
    public void insertBoard(Board board) {

    }

    @Override
    public Board getBoard(Board board) {
        return null;
    }

    @Override
    public void updateBoard(Board board) {

    }

    @Override
    public void deleteBoard(Board board) {

    }

}

9. BoardController에서 비즈니스 컴포넌트 사용

기존 BoardController 에서 BoardService를 autowired하고 하단에 getBoardList 메소드를 추가

....
    @Autowired
    private BoardService boardService;
....

    @RequestMapping("/getBoardList")
    public String getBoardList(Model model, Board board) {
        List<Board> boardList = boardService.getBoardList(board);
        model.addAttribute("boardList", boardList);
        return "getBoardList";

    }

10. 테스트로 만든 testBoardList.jsp를 복사하여 getBoardList.jsp를 만들자

JPA 게시판 등록 - JPA gesipan deunglog

11. 재부팅 시 하이버네이트 로그 확인 (테이블 생성 스크립트 출력)

Hibernate: 
    
    drop table if exists board CASCADE 
Hibernate: 
    
    drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: 
    
    create table board (
       seq bigint not null,
        cnt number default 0,
        content varchar(255),
        create_date date default sysdate,
        title varchar(255),
        writer varchar(255),
        primary key (seq)
    )

12. 브라우저로 확인 (localhost:8080/getBoardList) - 실제 테이블에 데이터가 없어서 글이 조회되지 않는다.

JPA 게시판 등록 - JPA gesipan deunglog

board 테이블이 조회되는 쿼리가 로그에서 확인 가능

Hibernate: 
    select
        board0_.seq as seq1_0_,
        board0_.cnt as cnt2_0_,
        board0_.content as content3_0_,
        board0_.create_date as create_d4_0_,
        board0_.title as title5_0_,
        board0_.writer as writer6_0_ 
    from
        board board0_
JPA 게시판 등록 - JPA gesipan deunglog
실제 콘솔 표시 화면

13. 새글쓰기 컨트롤러, 화면, 서비스 만들기

BoardController.java

BoardController.java

...
    /**
     * 글쓰기 화면
     * @return
     */
    @RequestMapping("/insertBoardView")
    public String insertBoardView() {
        return "insertBoard";
    }

    /**
     * 글쓰기 처리
     * @param board
     * @return
     */
    @RequestMapping("/insertBoard")
    public String insertBoard(Board board) {
        boardService.insertBoard(board);
        return "redirect:getBoardList";
    }
...

insertBoard.jsp

<%@page contentType="text/html; charset=UTF-8" %>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>글쓰기</title>
    <style>
        table {
            width: 100%;
            border: 1px solid #444444;
            border-collapse: collapse;
        }
        table th {
            border: 1px solid #444444;
            text-align: center;
            height: 40px;
            background-color: dodgerblue;
            color: cornsilk;
        }
        table td {
            border: 1px solid #444444;
            text-align: left;
        }
    </style>
</head>
<body>
<div style="text-align: center;">
<h3>글쓰기</h3>
<hr>
<form action="insertBoard" method="post">
    <table style="width: 700px; margin: auto">
        <tr>
            <td width="70" style="background-color: dodgerblue; color: cornsilk">제목</td>
            <td><input type="text" name="title"/></td>
        </tr>
        <tr>
            <td style="background-color: dodgerblue; color: cornsilk">작성자</td>
            <td><input type="text" name="writer"/></td>
        </tr>
        <tr>
            <td style="background-color: dodgerblue; color: cornsilk">내용</td>
            <td><textarea name="content" cols="40" rows="10"></textarea></td>
        </tr>
        <tr>
            <td colspan="2" align="center">
                <input type="submit" value="등록"/>
            </td>
        </tr>
    </table>
</form>
</div>
</body>
</html>

BoardServiceImpl.java

BoardServiceImpl.java

...
    /**
     * 글쓰기 처리
     * @param board
     */
    @Override
    public void insertBoard(Board board) {
        // jpa 사용시 별도 쿼리 작성 필요없이..
        // 아래 한줄 추가로 DB에 데이터가 저장되었다.
        boardRepository.save(board);
    }
...

14. 글쓰기 화면/기능 확인

JPA 게시판 등록 - JPA gesipan deunglog
http://localhost:8080/getBoardList
JPA 게시판 등록 - JPA gesipan deunglog
http://localhost:8080/insertBoardView

"등록" 버튼 클릭 시 글이 등록되며, 목록화면으로 redirect 된다.

JPA 게시판 등록 - JPA gesipan deunglog
http://localhost:8080/getBoardList

15. 글 상세 조회

BoardController.java

...

    /**
     * 상세 글 보기
     * @param board
     * @param model
     * @return
     */
    @RequestMapping("/getBoard")
    public String getBoard(Board board, Model model) {
        model.addAttribute("board", boardService.getBoard((board)));
        return "getBoard";
    }

...

BoardServiceImpl.java

 ....
 
     /**
     * 상세글 조회
     * @param board
     * @return
     */
    @Override
    public Board getBoard(Board board) {
    
        // 조회수 처리
        Board findBoard = boardRepository.findById(board.getSeq()).get();
        findBoard.setCnt(findBoard.getCnt()+1);
        boardRepository.save(findBoard);

        return findBoard;
    }

....

getBoard.jsp

<%@page contentType="text/html; charset=UTF-8" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>글상세</title>
    <style>
        table {
            width: 100%;
            border: 1px solid #444444;
            border-collapse: collapse;
        }
        table th {
            border: 1px solid #444444;
            text-align: center;
            height: 40px;
            background-color: dodgerblue;
            color: cornsilk;
        }
        table td {
            border: 1px solid #444444;
            text-align: left;
        }
    </style>
</head>
<body>
<div style="text-align: center;">
    <h3>글쓰기</h3>
    <hr>
    <form action="updateBoard" method="post">
        <input type="hidden" name="seq" value="${board.seq}"/>
        <table style="width: 700px; margin: auto">
            <tr>
                <td width="70" style="background-color: dodgerblue; color: cornsilk">제목</td>
                <td><input type="text" name="title" value="${board.title}"/></td>
            </tr>
            <tr>
                <td style="background-color: dodgerblue; color: cornsilk">작성자</td>
                <td>${board.writer}</td>
            </tr>
            <tr>
                <td style="background-color: dodgerblue; color: cornsilk">내용</td>
                <td><textarea name="content" cols="40" rows="10">${board.content}</textarea></td>
            </tr>
            <tr>
                <td style="background-color: dodgerblue; color: cornsilk">등록일</td>
                <td><fmt:formatDate value="${board.createDate}" pattern="yyyy-MM-dd"></fmt:formatDate> </td>
            </tr>
            <tr>
                <td style="background-color: dodgerblue; color: cornsilk">조회수</td>
                <td>${board.cnt}</td>
            </tr>
            <tr>
                <td colspan="2">
                    <div style="text-align: center;">
                        <input type="submit" value="수정"/>
                    </div>
                </td>
            </tr>
        </table>
    </form>
    <hr>
    <a href="insertBoardView">글등록</a>
    <a href="deleteBoard?seq=${board.seq}">글삭제</a>
    <a href="getBoardList">글목록</a>
</div>
</body>
</html>

15. 상세글 조회 화면/기능 확인

JPA 게시판 등록 - JPA gesipan deunglog
http://localhost:8080/getBoard?seq=3

16. 글 수정

BoardController.java

....

    /**
     * 글 수정 처리 후 목록으로 이동
     * @param board
     * @return
     */
    @RequestMapping("/updateBoard")
    public String updateBoard(Board board) {
        boardService.updateBoard(board);
        return "forward:getBoardList";
    }
    
....

BoardServiceImpl.java

....

    /**
     * 글 수정
     * @param board
     */
    @Override
    public void updateBoard(Board board) {
        // 수정 대상 글을 가져온다.
        Board findBoard = boardRepository.findById(board.getSeq()).get();
        
        // 가져온 글에 수정한 내용을 세팅한다.
        findBoard.setTitle(board.getTitle());
        findBoard.setContent(board.getContent());

        // DB에 저장
        boardRepository.save(findBoard);
    }
    
....

17. 글 삭제 처리

BoardController.java

....

    /**
     * 글 삭제 처리 후 목록으로 이동
     * @param board
     * @return
     */
    @RequestMapping("/deleteBoard")
    public String deleteBoard(Board board) {
        boardService.deleteBoard(board);
        return "forward:getBoardList";
    }
    
....

BoardServiceImpl.java

....

    /**
     * 글 삭제 처리
     * @param board
     */
    @Override
    public void deleteBoard(Board board) {
        boardRepository.deleteById(board.getSeq());
    }

....

18. 전체 소스 구조

JPA 게시판 등록 - JPA gesipan deunglog

19. 파일별 전체 소스

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.tistory.hitomis</groupId>
    <artifactId>springbootjspboard</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springbootjspboard</name>
    <description>Spring Boot JSP Board</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--jsp-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

BoardController.java

package com.tistory.hitomis.springbootjspboard.controller;

import com.tistory.hitomis.springbootjspboard.domain.Board;
import com.tistory.hitomis.springbootjspboard.service.BoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

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

@Controller
public class BoardController {

    @Autowired
    private BoardService boardService;

    /**
     * 게시판 목록 테스트용 샘플
     *
     * @param model
     * @return
     */
    @RequestMapping("/testBoardList")
    public String testBoardList(Model model) {
        List<Board> boardList = new ArrayList<Board>();

        // 임시로 게시물 10개를 만들자
        for (int i = 0; i < 9; i++) {
            Board board = new Board();
            board.setSeq(new Long(i));
            board.setTitle("제목   " + i);
            board.setWriter("작성자 " + i);
            board.setContent("글내용  " + i);
            board.setCreateDate(new Date());
            board.setCnt(0L);
            boardList.add(board);
        }
        model.addAttribute("boardList", boardList);
        return "testBoardList"; // jsp 파일 이름
    }

    /**
     * 게시판 목록
     *
     * @param model
     * @param board
     * @return
     */
    @RequestMapping("/getBoardList")
    public String getBoardList(Model model, Board board) {
        List<Board> boardList = boardService.getBoardList(board);
        model.addAttribute("boardList", boardList);
        return "getBoardList";

    }

    /**
     * 글쓰기 화면
     *
     * @return
     */
    @RequestMapping("/insertBoardView")
    public String insertBoardView() {
        return "insertBoard";
    }

    /**
     * 글쓰기 처리
     *
     * @param board
     * @return
     */
    @RequestMapping("/insertBoard")
    public String insertBoard(Board board) {
        boardService.insertBoard(board);
        return "redirect:getBoardList";
    }

    /**
     * 상세 글 화면/처리
     *
     * @param board
     * @param model
     * @return
     */
    @RequestMapping("/getBoard")
    public String getBoard(Board board, Model model) {
        model.addAttribute("board", boardService.getBoard((board)));
        return "getBoard";
    }

    /**
     * 글 수정 처리 후 목록으로 이동
     *
     * @param board
     * @return
     */
    @RequestMapping("/updateBoard")
    public String updateBoard(Board board) {
        boardService.updateBoard(board);
        return "forward:getBoardList";
    }

    /**
     * 글 삭제 처리 후 목록으로 이동
     * @param board
     * @return
     */
    @RequestMapping("/deleteBoard")
    public String deleteBoard(Board board) {
        boardService.deleteBoard(board);
        return "forward:getBoardList";
    }

}

Board.java

package com.tistory.hitomis.springbootjspboard.domain;

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Date;

@Data
@Entity
public class Board {
    @Id @GeneratedValue
    private Long seq;           //pk

    private String title;       //제목

    @Column(updatable = false)
    private String writer;      //작성자

    private String content;     //내용

    @Column(insertable = false, updatable = false, columnDefinition = "date default sysdate")
    private Date createDate;    //생성일자

    @Column(insertable = false, updatable = false, columnDefinition = "number default 0")
    private Long cnt;           //조회수
}

BoardRepository.java

package com.tistory.hitomis.springbootjspboard.persistence;

import com.tistory.hitomis.springbootjspboard.domain.Board;
import org.springframework.data.repository.CrudRepository;

public interface BoardRepository extends CrudRepository<Board, Long> {
}

BoardService.java

package com.tistory.hitomis.springbootjspboard.service;

import com.tistory.hitomis.springbootjspboard.domain.Board;

import java.util.List;

public interface BoardService {
    List<Board> getBoardList(Board board);

    void insertBoard(Board board);

    Board getBoard(Board board);

    void updateBoard(Board board);

    void deleteBoard(Board board);
}

BoardServiceImpl.java

package com.tistory.hitomis.springbootjspboard.service;

import com.tistory.hitomis.springbootjspboard.domain.Board;
import com.tistory.hitomis.springbootjspboard.persistence.BoardRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BoardServiceImpl implements BoardService {

    @Autowired
    private BoardRepository boardRepository;

    /**
     * 글목록 조회
     * @param board
     * @return
     */
    @Override
    public List<Board> getBoardList(Board board) {
        return (List<Board>) boardRepository.findAll();
    }

    /**
     * 글쓰기 처리
     * @param board
     */
    @Override
    public void insertBoard(Board board) {
        boardRepository.save(board);

    }

    /**
     * 상세글 조회
     * @param board
     * @return
     */
    @Override
    public Board getBoard(Board board) {

        // 조회수 처리
        Board findBoard = boardRepository.findById(board.getSeq()).get();
        findBoard.setCnt(findBoard.getCnt()+1);
        boardRepository.save(findBoard);

        return findBoard;
    }

    /**
     * 글 수정
     * @param board
     */
    @Override
    public void updateBoard(Board board) {
        // 수정 대상 글을 가져온다.
        Board findBoard = boardRepository.findById(board.getSeq()).get();

        // 가져온 글에 수정한 내용을 세팅한다.
        findBoard.setTitle(board.getTitle());
        findBoard.setContent(board.getContent());

        // DB에 저장
        boardRepository.save(findBoard);
    }

    /**
     * 글 삭제 처리
     * @param board
     */
    @Override
    public void deleteBoard(Board board) {
        boardRepository.deleteById(board.getSeq());
    }

}

application.properties

# Database 기본 세팅
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.username=sa
spring.datasource.password=

# JPA 하이버네이트 로그 레벨 설정
logging.level.org.hibernate=info
logging.level.org.springframework=info

# ViewResolver 세팅
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

#JPA 세팅
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=false
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.format_sql=true

getBoard.jsp

<%@page contentType="text/html; charset=UTF-8" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>글상세</title>
    <style>
        table {
            width: 100%;
            border: 1px solid #444444;
            border-collapse: collapse;
        }
        table th {
            border: 1px solid #444444;
            text-align: center;
            height: 40px;
            background-color: dodgerblue;
            color: cornsilk;
        }
        table td {
            border: 1px solid #444444;
            text-align: left;
        }
    </style>
</head>
<body>
<div style="text-align: center;">
    <h3>글쓰기</h3>
    <hr>
    <form action="updateBoard" method="post">
        <input type="hidden" name="seq" value="${board.seq}"/>
        <table style="width: 700px; margin: auto">
            <tr>
                <td width="70" style="background-color: dodgerblue; color: cornsilk">제목</td>
                <td><input type="text" name="title" value="${board.title}"/></td>
            </tr>
            <tr>
                <td style="background-color: dodgerblue; color: cornsilk">작성자</td>
                <td>${board.writer}</td>
            </tr>
            <tr>
                <td style="background-color: dodgerblue; color: cornsilk">내용</td>
                <td><textarea name="content" cols="40" rows="10">${board.content}</textarea></td>
            </tr>
            <tr>
                <td style="background-color: dodgerblue; color: cornsilk">등록일</td>
                <td><fmt:formatDate value="${board.createDate}" pattern="yyyy-MM-dd"></fmt:formatDate> </td>
            </tr>
            <tr>
                <td style="background-color: dodgerblue; color: cornsilk">조회수</td>
                <td>${board.cnt}</td>
            </tr>
            <tr>
                <td colspan="2">
                    <div style="text-align: center;">
                        <input type="submit" value="수정"/>
                    </div>
                </td>
            </tr>
        </table>
    </form>
    <hr>
    <a href="insertBoardView">글등록</a>
    <a href="deleteBoard?seq=${board.seq}">글삭제</a>
    <a href="getBoardList">글목록</a>
</div>
</body>
</html>

getBoardList.jsp

<%@page contentType="text/html; charset=UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>게시판 목록</title>
    <style>
        table {
            width: 100%;
            border: 1px solid #444444;
            border-collapse: collapse;
        }
        table th {
            border: 1px solid #444444;
            text-align: center;
            height: 40px;
            background-color: dodgerblue;
            color: cornsilk;
        }
        table td {
            border: 1px solid #444444;
            text-align: center;
        }
    </style>
</head>
<body>
<div style="text-align: center;">
    <h2>게시판 목록</h2>
    <table style="width: 700px; margin: auto">
        <tr>
            <th style="width: 10%">번호</th>
            <th style="width: 50%">제목</th>
            <th style="width: 15%">작성자</th>
            <th style="width: 15%">등록일</th>
            <th style="width: 10%">조회수</th>
        </tr>
        <c:forEach var="board" items="${boardList}">
            <tr>
                <td>${board.seq}</td>
                <td style="text-align: left"><a href="getBoard?seq=${board.seq}">${board.title}</a></td>
                <td>${board.writer}</td>
                <td><fmt:formatDate value="${board.createDate}" pattern="yyyy-MM-dd"></fmt:formatDate> </td>
                <td>${board.cnt}</td>
            </tr>
        </c:forEach>
    </table>
    <a href="insertBoardView">새글 등록</a>
</div>
</body>
</html>

insertBoard.jsp

<%@page contentType="text/html; charset=UTF-8" %>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>글쓰기</title>
    <style>
        table {
            width: 100%;
            border: 1px solid #444444;
            border-collapse: collapse;
        }
        table th {
            border: 1px solid #444444;
            text-align: center;
            height: 40px;
            background-color: dodgerblue;
            color: cornsilk;
        }
        table td {
            border: 1px solid #444444;
            text-align: left;
        }
    </style>
</head>
<body>
<div style="text-align: center;">
<h3>글쓰기</h3>
<hr>
<form action="insertBoard" method="post">
    <table style="width: 700px; margin: auto">
        <tr>
            <td width="70" style="background-color: dodgerblue; color: cornsilk">제목</td>
            <td><input type="text" name="title"/></td>
        </tr>
        <tr>
            <td style="background-color: dodgerblue; color: cornsilk">작성자</td>
            <td><input type="text" name="writer"/></td>
        </tr>
        <tr>
            <td style="background-color: dodgerblue; color: cornsilk">내용</td>
            <td><textarea name="content" cols="40" rows="10"></textarea></td>
        </tr>
        <tr>
            <td colspan="2">
                <div style="text-align: center;">
                    <input type="submit" value="등록"/>
                </div>
            </td>
        </tr>
    </table>
</form>
</div>
</body>
</html>

testBoardList.jsp

<%@page contentType="text/html; charset=UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>게시판 목록</title>
    <style>
        table {
            width: 100%;
            border: 1px solid #444444;
            border-collapse: collapse;
        }
        table th {
            border: 1px solid #444444;
            text-align: center;
            height: 40px;
            background-color: dodgerblue;
            color: cornsilk;
        }
        table td {
            border: 1px solid #444444;
            text-align: center;
        }
    </style>
</head>
<body>
<div style="text-align: center;">
    <h2>게시판 목록</h2>
    <table style="width: 700px; margin: auto">
        <tr>
            <th style="width: 10%">번호</th>
            <th style="width: 50%">제목</th>
            <th style="width: 15%">작성자</th>
            <th style="width: 15%">등록일</th>
            <th style="width: 10%">조회수</th>
        </tr>
        <c:forEach var="board" items="${boardList}">
            <tr>
                <td>${board.seq}</td>
                <td style="text-align: left"><a href="getBoard?seq=${board.seq}">${board.title}</a></td>
                <td>${board.writer}</td>
                <td><fmt:formatDate value="${board.createDate}" pattern="yyyy-MM-dd"></fmt:formatDate> </td>
                <td>${board.cnt}</td>
            </tr>
        </c:forEach>
    </table>
    <a href="insertBoard">새글 등록</a>
</div>
</body>
</html>