inblog logo
|
DailyCoding
    Spring Boot

    7. (실습) 스프링 V1 (기본 - CRUD) (1)

    박은서's avatar
    박은서
    Feb 05, 2026
    7. (실습) 스프링 V1 (기본 - CRUD) (1)
    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 (삭제): 데이터를 삭제하거나 제거하는 기능
    프로젝트명 : boardv1
    프로젝트명 : boardv1

    [참고] 참고 코드

    metacoding-06-springboot-v1
    metacoding-06-springboot-v1 has 5 repositories available. Follow their code on GitHub.
    metacoding-06-springboot-v1
    https://github.com/orgs/metacoding-06-springboot-v1/repositories
    metacoding-06-springboot-v1
    notion image

    1. mustache 파일 생성

    압축 폴더 다운 받기
    압축 폴더 다운 받기
    7-zip으로 압축 풀기
    7-zip으로 압축 풀기
    파일 드래그해서 복사
    파일 드래그해서 복사
    templates 폴더 선택해서 ctrl+V
    templates 폴더 선택해서 ctrl+V

    [참고] HTML <head>에서 외부 CSS 로딩

    [참고] HTML <head>에서 외부 CSS 로딩
    [참고] HTML <head>에서 외부 CSS 로딩 - DailyCoding
    Spring Boot
    [참고] HTML <head>에서 외부 CSS 로딩 - DailyCoding
    https://dailycoding.inblog.io/%EC%B0%B8%EA%B3%A0-html-head%EC%97%90%EC%84%9C-%EC%99%B8%EB%B6%80-css-%EB%A1%9C%EB%94%A9-100235
    [참고] HTML <head>에서 외부 CSS 로딩 - DailyCoding

    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 설정

    💡

    application.properties의 용도

    • Spring Framework / Spring Boot 애플리케이션의 전역 설정(configuration)을 정의하는 핵심 설정 파일
    • 애플리케이션의 동작 방식, 실행 환경, 외부 리소스 연결 정보 등을 코드와 분리해서 관리

    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=true
    💡
    H2 Datasource는 새로 열 때마다 초기화되고 Board.java 대로 테이블 생성
    테스트할 때는 좋지만 실제로는 사용하지 않음!

    2) 테스트

    notion image
    notion image
    notion image
    notion image
    notion image
    notion image

    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) 테스트 (에러)

    notion image
    notion image
    ⛔
    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) 테스트 (성공)

    notion image
    notion image
     
    notion image

    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=true
    💡
    Hibernate(JPA)가 테이블을 생성한 뒤에 INSERT SQL을 실행

    3) 테스트

    notion image

    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
    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건)

    DailyCoding

    RSS·Powered by Inblog