본문 바로가기

프로그래밍/JAVA

[Spring-Boot] JPA를 활용하여 간단한 CRUD 게시판 만들기(3)

728x90
반응형

이번부터는 본격적으로 JPA를 활용하여 데이터 조작을 해볼 것이다

우선 위와 같이 패키지와 클래스들을 만들어 놓은 다음 하나하나씩 작성해 나갈 것이다

 

그리고 build.gradle 에 spring-boot-devtools를 추가시켜 줬다

developmentOnly 'org.springframework.boot:spring-boot-devtools'

https://velog.io/@bread_dd/Spring-Boot-Devtools

 

Spring Boot Devtools 알아보기

Spring Devtools에 대해 알아보았습니다.

velog.io

 

IntelliJ는 Devtools를 따로 설정해줘야 하는데 위에 블로그에 설명이 잘 되어 있다

eclipse는 따로 설정을 안 해줘도 아마 동작을 하는 걸로 알고 있다

 

그리고 설정 파일인 application.yml (application.properties) 파일에 jpa 설정을 추가해 준다

spring:
  h2:
    console:
      enabled: true
  thymeleaf:
    cache: false
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true

위 설정을 보면 show-sql 은 sql문을 보여주라는 것이고 format_sql은 sql문을 가독성 있게 해 준다는 뜻이다

여기에는 설정이 안 되어 있지만 현재는 서버가 재시작될 때마다 테이블을 지웠다 새로 만드는데 그게 싫다면

ddl-auto 설정을 하면 원하는 데로 바꿀 수 있다

 

이제 최종 완성된 소스를 보여주면서 설명을 할 것이다

파일 구조는 위에 파일 트리를 보면서 참고하면 된다

 

Board.java

import lombok.*;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.time.LocalDateTime;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Entity
public class Board {

    @Id
    @GeneratedValue
    private int idx;

    private String title;
    private String content;
    private String writer;

    @CreationTimestamp
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @UpdateTimestamp
    private LocalDateTime updatedAt;
}

테이블과 매핑될 Board 클래스이다

lombok으로 getter, setter, constructor를 생성하는데 JPA에서는 NoArgsConstructor는 필수로 들어가 있어야 한다

lombok을 사용하지 않는 다면 아무것도 없는 빈 생성자를 하나 만들어 주면 된다

 

@Entity 애노테이션은 이 클래스가 Entity라는 걸 알려주기 위해 필수로 달아 줘야 한다

그럼 JPA는 DB에 Board라는 테이블을 생성하는데 우리가 선언해준 저 변수들이 속성이 되는 것이다

@Id 애노테이션은 idx가 기본키라는 걸 명시하는 것이다

 

BoardRepository.java

import com.example.jpaboard.domain.Board;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BoardRepository extends JpaRepository<Board, Integer> {

}

이 인터페이스는 별거 없이 JpaReoisitory를 상속받아서 객체가 되는 클래스인 Board와 Board 테이블에 기본키인 idx의 자료형을 래퍼 클래스 형태로 넣어 주면 된다

 

이 인터페이스를 이용해서 JPA를 custom 도 할 수 있지만 여기서는 기본적인 CRUD만 할 거기 때문에 이상태로만 생성해 준다

 

BoardService.java

import com.example.jpaboard.domain.Board;
import com.example.jpaboard.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class BoardService {

    private final BoardRepository boardRepository;

    public void register(Board board) {
        boardRepository.save(board);
    }

    public List<Board> list() {
        return boardRepository.findAll(Sort.by(Sort.Direction.DESC, "idx"));
    }

    public Board detail(int idx) {
        return boardRepository.findById(idx).orElse(null);
    }

    public void update(Board board) {
        boardRepository.save(board);
    }

    public void delete(int idx) {
        boardRepository.deleteById(idx);
    }
}

service 단에서는 BoardRepository를 이용해 데이터를 컨트롤해주면 되는데

등록, 목록, 상세보기, 수정, 삭제 이렇게 간단하게 구현 해 놨다

 

list에서 findAll을 할 때 idx를 기준으로 내림차순 정렬을 준 것이고 detail에서는 findById가 optional로 감싸져 있는 것을 Board로 바로 쓰기 위해 orElse를 준 것이다

 

update는 등록과 마찬가지로 save 메서드를 활용하는데 데이터가 없다면 insert가 되고 데이터가 있으면 update가 된다

 

BoardController.java

import com.example.jpaboard.domain.Board;
import com.example.jpaboard.service.BoardService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

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

    private final BoardService boardService;

    @GetMapping("/list")
    public String list(Model model) {

        model.addAttribute("board", boardService.list());
        return "list";
    }

    @GetMapping("/detail/{idx}")
    public String detail(@PathVariable int idx, Model model) {

        model.addAttribute("board", boardService.detail(idx));
        return "detail";
    }

    @GetMapping("/register")
    public String registerGet() {

        return "register";
    }

    @PostMapping("/register")
    public String registerPost(Board board) {

        boardService.register(board);
        return "redirect:/board/list";
    }

    @GetMapping("/update/{idx}")
    public String updateGet(@PathVariable int idx, Model model) {

        model.addAttribute("board", boardService.detail(idx));
        return "update";
    }

    @PostMapping("/update")
    public String updatePost(Board board) {

        boardService.update(board);
        return "redirect:/board/list";
    }

    @GetMapping("/delete/{idx}")
    public String delete(@PathVariable int idx) {

        boardService.delete(idx);
        return "redirect:/board/list";
    }
}

컨트롤러는 사실 딱히 설명할 만한게 없다

 

list.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        .main {
            width: 700px;
            height: 700px;
            margin: auto;
            margin-top: 100px;
        }

        table {
            text-align: center;
        }

        .register {
            text-align: center;
            display: inline-block;
            border: 1px solid;
            width: 80px;
            height: 30px;
            background-color: mistyrose;
            text-decoration: none;
            line-height: 30px;
        }

    </style>
</head>
<body>
  <div class="main">
      <a href="/board/register" class="register">글 쓰기</a>
      <table>
          <colgroup>
              <col width="50">
              <col width="300">
              <col width="150">
              <col width="200">
          </colgroup>
          <thead>
            <tr>
                <th>번호</th>
                <th>제목</th>
                <th>작성자</th>
                <th>등록일</th>
            </tr>
          </thead>
          <tbody>
            <tr th:each="board : ${board}">
                <td th:text="${board.idx}"></td>
                <td><a th:href="@{/board/detail/{idx}(idx=${board.idx})}" th:text="${board.title}"></a></td>
                <td th:text="${board.writer}"></td>
                <td th:text="${#temporals.format(board.createdAt, 'yyyy-MM-dd HH:mm')}"></td>
            </tr>
          </tbody>
      </table>
  </div>
</body>
</html>

 

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        .main {
            width: 500px;
            height: 700px;
            margin: auto;
        }

        label {
            display: inline-block;
            width: 60px;
        }

        input {
            display: inline-block;
            width: 400px;
            margin-bottom: 10px;
        }

        textarea {
            width: 470px;
            height: 400px;
        }

        button {
            margin-top: 10px;
            width: 70px;
            background-color: cyan;
            border: 1px solid;
        }
    </style>
</head>
<body>
    <div class="main">
        <form method="post">
            <label for="writer">작성자 :</label>
            <input type="text" name="writer" id="writer">
            <label for="title">제목 :</label>
            <input type="text" name="title" id="title">
            <label for="content">내용 :</label>
            <textarea name="content" id="content"></textarea>
            <button type="submit">등록</button>
        </form>
    </div>
</body>
</html>

 

detail.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        .main {
            width: 500px;
            height: 700px;
            margin: auto;
        }

        label {
            display: inline-block;
            width: 60px;
        }

        input {
            display: inline-block;
            width: 400px;
            margin-bottom: 10px;
        }

        textarea {
            width: 470px;
            height: 400px;
        }

        #update {
            text-align: center;
            display: inline-block;
            border: 1px solid;
            width: 80px;
            height: 30px;
            background-color: moccasin;
            text-decoration: none;
            line-height: 30px;
        }

        #delete {
            text-align: center;
            display: inline-block;
            border: 1px solid;
            width: 80px;
            height: 30px;
            background-color: lightpink;
            text-decoration: none;
            line-height: 30px;
        }
    </style>
</head>
<body>
    <div class="main">
        <label for="writer">작성자 :</label>
        <input type="text" name="writer" id="writer" th:value="${board.writer}" disabled>
        <label for="title">제목 :</label>
        <input type="text" name="title" id="title" th:value="${board.title}" disabled>
        <label for="content">내용 :</label>
        <textarea name="content" id="content" th:text="${board.content}" disabled></textarea>
        <a th:href="@{/board/update/{idx}(idx=${board.idx})}" id="update">수정</a>
        <a th:href="@{/board/delete/{idx}(idx=${board.idx})}" id="delete">삭제</a>
    </div>
</body>
</html>

 

update.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        .main {
            width: 500px;
            height: 700px;
            margin: auto;
        }

        label {
            display: inline-block;
            width: 60px;
        }

        input {
            display: inline-block;
            width: 400px;
            margin-bottom: 10px;
        }

        textarea {
            width: 470px;
            height: 400px;
        }

        button {
            margin-top: 10px;
            width: 70px;
            background-color: cyan;
            border: 1px solid;
        }
    </style>
</head>
<body>
<div class="main">
    <form method="post" th:action="@{/board/update}">
        <input type="hidden" name="idx" id="idx" th:value="${board.idx}">
        <label for="writer">작성자 :</label>
        <input type="text" name="writer" id="writer" th:value="${board.writer}">
        <label for="title">제목 :</label>
        <input type="text" name="title" id="title" th:value="${board.title}">
        <label for="content">내용 :</label>
        <textarea name="content" id="content" th:text="${board.content}"></textarea>
        <button type="submit">수정</button>
    </form>
</div>
</body>
</html>

 

 

 

아무래도 화면 그리는게 제일 힘든 거 같다

디자인 쪽으로는 아무래도 재능이 영 없는 것 같다...ㅋ

 

이걸로 정말 간단한 게시판을 만들어 보았다

사실 이건 JPA 맛만 봤을 뿐 더 어렵고 많은 기능들이 있는데 그건 차차 공부하면서 정리할 생각이다

 

728x90
반응형