Contents
[참고] 참고 코드1. mustache 파일 생성[참고] HTML <head>에서 외부 CSS 로딩1) header.mustache2) index.mustache3) detail.mustache4) save-form.mustache5) update-form.mustache2. application.properties 설정1) application.properties2) 테스트3. 테이블 세팅1) Board.java2) 테스트 (에러)3) Board.java - 수정4) 테스트 (성공)4. 더미데이터 세팅1) data.sql2) application.properties 에 추가3) 테스트4) data.sql - 수정 (더미데이터 6건)CRUD
- Create (생성): 데이터베이스나 파일 시스템에 새로운 데이터를 생성하고 추가하는 기능
- Read (읽기/조회): 저장된 데이터를 검색하거나 조회하는 기능
- Update (갱신/수정): 기존 데이터를 수정하거나 변경하는 기능
- Delete (삭제): 데이터를 삭제하거나 제거하는 기능

[참고] 참고 코드

1. mustache 파일 생성




[참고] HTML <head>에서 외부 CSS 로딩
[참고] HTML <head>에서 외부 CSS 로딩1) header.mustache
C:\workspace\spring_lab\boardv1\src\main\resources\templates\header.mustache
<!DOCTYPE html>
<html lang="en">
<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.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-sm" style="background-color: grey;">
<div class="container-fluid">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/" style="font-weight: bold; color: white">Metacoding</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/boards/save-form" style="color: white">글쓰기</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/logout" style="color: white">로그아웃</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/join-form" style="color: white">회원가입</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/login-form" style="color: white">로그인</a>
</li>
</ul>
</div>
</nav>
</div>2) index.mustache
C:\workspace\spring_lab\boardv1\src\main\resources\templates\index.mustache
{{> header}}
<div class="container mt-3">
<table class="table table-hover">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>내용</th>
</tr>
</thead>
<tbody>
<tr>
<td>3</td>
<td>
<a href="/boards/1" style="color: inherit; text-decoration: none;">
제목3
</a>
</td>
<td>내용3</td>
</tr>
<tr>
<td>2</td>
<td>
<a href="/boards/1" style="color: inherit; text-decoration: none;">
제목2
</a>
</td>
<td>내용2</td>
</tr>
<tr>
<td>1</td>
<td>
<a href="/boards/1" style="color: inherit; text-decoration: none;">
제목1
</a>
</td>
<td>내용1</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>3) detail.mustache
C:\workspace\spring_lab\boardv1\src\main\resources\templates\board\detail.mustache
{{> header}}
<div class="container p-5">
<!-- 수정삭제버튼 -->
<div class="d-flex justify-content-end">
<a href = "/boards/1/update-form" class="btn btn-secondary me-1">수정</a>
<button class="btn btn-outline-secondary">삭제</button>
</div>
<!-- 게시글내용 -->
<div>
<h2><b>제목</b></h2>
<hr />
<div class="d-flex justify-content-end">
작성자 : 익명
</div>
<div class="m-4 p-2">
내용
</div>
</div>
<!-- 댓글 -->
<div class="card mt-3">
<!-- 댓글등록 -->
<div class="card-body">
<form action="/replies/save" method="post">
<textarea class="form-control" rows="2" name="comment"></textarea>
<div class="d-flex justify-content-end">
<button class="btn btn-secondary mt-1">
댓글등록
</button>
</div>
</form>
</div>
<!-- 댓글목록 -->
<div class="card-footer">
<b>댓글리스트</b>
</div>
<div class="list-group">
<!-- 댓글아이템 -->
<div class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex">
<div class="px-1 me-1 bg-secondary text-white rounded">익명</div>
<div>댓글 내용2</div>
</div>
<form action="/replies/1/delete" method="post">
<button class="btn">🗑</button>
</form>
</div>
<!-- 댓글아이템 -->
<div class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex">
<div class="px-1 me-1 bg-secondary text-white rounded">익명</div>
<div>댓글 내용1</div>
</div>
<form action="/replies/1/delete" method="post">
<button class="btn">🗑</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>4) save-form.mustache
C:\workspace\spring_lab\boardv1\src\main\resources\templates\board\save-form.mustache
{{> header}}
<div class="container p-5">
<div class="card">
<div class="card-header"><b>게시글 작성</b></div>
<div class="card-body">
<form action="/boards/save" method="post" enctype="application/x-www-form-urlencoded">
<div class="mb-3">
<input type="text" class="form-control" placeholder="Enter title" name="title">
</div>
<div class="mb-3">
<textarea class="form-control" rows="5" name="content"></textarea>
</div>
<button class="btn btn-secondary form-control">글쓰기</button>
</form>
</div>
</div>
</div>
</body>
</html>5) update-form.mustache
C:\workspace\spring_lab\boardv1\src\main\resources\templates\board\update-form.mustache
{{> header}}
<div class="container p-5">
<div class="card">
<div class="card-header"><b>게시글 수정</b></div>
<div class="card-body">
<form action="/boards/1/update" method="post" enctype="application/x-www-form-urlencoded">
<div class="mb-3">
<input type="text" class="form-control" placeholder="Enter title" name="title">
</div>
<div class="mb-3">
<textarea class="form-control" rows="5" name="content"></textarea>
</div>
<button class="btn btn-secondary form-control">글수정하기</button>
</form>
</div>
</div>
</div>
</body>
</html>2. application.properties 설정
1) application.properties
C:\workspace\spring_lab\boardv1\src\main\resources\application.properties
# ===== Server =====
server.port=8080
spring.servlet.encoding.charset=UTF-8
spring.servlet.encoding.enabled=true
spring.servlet.encoding.force=true
# ===== Mustache =====
spring.mustache.servlet.expose-session-attributes=true
spring.mustache.servlet.expose-request-attributes=true
# ===== ansi =====
spring.output.ansi.enabled=always
# ===== H2 Datasource =====
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=trueH2 Datasource는 새로 열 때마다 초기화되고 Board.java 대로 테이블 생성
테스트할 때는 좋지만 실제로는 사용하지 않음!
2) 테스트






3. 테이블 세팅
1) Board.java
C:\workspace\spring_lab\boardv1\src\main\java\com\example\boardv1\board\Board.java
package com.example.boardv1.board;
import java.sql.Timestamp;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 데이터베이스 세상의 테이블을 자바 세상에 모델링한 결과 = 엔티티
*/
@NoArgsConstructor // 디폴트 생성자
@Data // getter, setter, toString
@Entity // 해당 어노테이션을 보고 컴퍼넌트 스캔 후 데이터베이스에 테이블을 생성
@Table(name = "board_tb") // 테이블명 설정
public class Board {
private Integer id;
private String title;
private String content;
private Timestamp createdAt; // import 주의!! (java.sql)
}2) 테스트 (에러)


org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/hibernate/autoconfigure/HibernateJpaConfiguration.class]: Entity 'com.example.boardv1.board.Board' has no identifier (every '@Entity' class must declare or inherit at least one '@Id' or '@EmbeddedId' property)
➡️ 빈을 생성하는 과정에서
Board 엔티티가 @Id(또는 @EmbeddedId)로 지정된 Primary Key를 가지고 있지 않아, JPA 규칙을 위반하여 빈 생성이 실패한 오류➡️
Board.java에서 적어도 하나는 @Id를 붙여야 함※ Bean = new된 객체
3) Board.java - 수정
package com.example.boardv1.board;
import java.sql.Timestamp;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 데이터베이스 세상의 테이블을 자바 세상에 모델링한 결과 = 엔티티
*/
@NoArgsConstructor // 디폴트 생성자
@Data // getter, setter, toString
@Entity // 해당 어노테이션을 보고 컴퍼넌트 스캔 후 데이터베이스에 테이블을 생성
@Table(name = "board_tb") // 테이블명 설정
public class Board {
@Id // id를 primary key로 설정
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto increment 설정
private Integer id;
private String title;
private String content;
private Timestamp createdAt; // import 주의!! (java.sql)
}
@Entity, @Table 안 붙이면 @Id도 안 붙여도 됨!
@Table 을 안 붙이면 클래스명이 테이블명이 됨
단, 테이블명으로 만들 수 없는 이름들이 있기 때문에 그럴 경우 테이블 안 만들어짐!
그래서 @Table로 테이블명 설정해줘야 함!
4) 테스트 (성공)



4. 더미데이터 세팅
1) data.sql
C:\workspace\spring_lab\boardv1\src\main\resources\db\data.sql
insert into board_tb (title, content, created_at) values ('title1', 'content1', now());
insert into board_tb (title, content, created_at) values ('title2', 'content2', now()); Table.java에는 카멜 표기법으로 적었지만, 실제 화면에는 언더 스코어
더미 데이터 세팅 시에도 언더 스코어로 적어야 함! 카멜 표기법으로 적으면 더미 데이터 안 들어감!
2) application.properties 에 추가
# ===== SQL Init (data.sql) =====
spring.sql.init.data-locations=classpath:db/data.sql
spring.jpa.defer-datasource-initialization=trueHibernate(JPA)가 테이블을 생성한 뒤에 INSERT SQL을 실행
3) 테스트

4) data.sql - 수정 (더미데이터 6건)
insert into board_tb (title, content, created_at) values ('title1', 'content1', now());
insert into board_tb (title, content, created_at) values ('title2', 'content2', now());
insert into board_tb (title, content, created_at) values ('title3', 'content3', now());
insert into board_tb (title, content, created_at) values ('title4', 'content4', now());
insert into board_tb (title, content, created_at) values ('title5', 'content5', now());
insert into board_tb (title, content, created_at) values ('title6', 'content6', now());Share article