파일 작성
더보기








기본 시안
기본 페이지

회원 가입

글쓰기 화면입니다.

회원수정

로그인

제목자리

기본 머스태치 파일 시안 확인
더보기
<!DOCTYPE html>
<html lang="en">
<head>
<title>Blog</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<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>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body>
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Tencoding</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#collapsibleNavbar">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsibleNavbar">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/join-form">회원가입</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/login-form">로그인</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/board/save-form">글쓰기</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/user/update-form">회원정보보기</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/logout">로그아웃</a>
</li>
</ul>
</div>
</div>
</nav>
layout/footer.mustache
<div class="bg-light p-5 text-center">
<h4>Created by Tencoding</h4>
<h5>☎ 010-1234-1234</h5>
<button class="btn btn-outline-primary">고객센터</button>
<button class="btn btn-outline-primary">오시는길</button>
</div>
</body>
</html>
index.mustache
{{> layout/header}}
<div class="container p-5">
<div class="card mb-3">
<div class="card-body">
<h4 class="card-title mb-3">제목1</h4>
<a href="/board/1" class="btn btn-primary">상세보기</a>
</div>
</div>
<ul class="pagination d-flex justify-content-center">
<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
</div>
{{> layout/footer}}
user/join-form.mustache
{{> layout/header}}
<div class="container p-5">
<!-- 요청을 하면 localhost:8080/join POST로 요청됨
username=사용자입력값&password=사용자값&email=사용자입력값 -->
<div class="card">
<div class="card-header"><b>회원가입을 해주세요</b></div>
<div class="card-body">
<form action="/join" method="post" enctype="application/x-www-form-urlencoded">
<div class="mb-3">
<input type="text" class="form-control" placeholder="Enter username" name="username">
</div>
<div class="mb-3">
<input type="password" class="form-control" placeholder="Enter password" name="password">
</div>
<div class="mb-3">
<input type="email" class="form-control" placeholder="Enter email" name="email">
</div>
<button type="submit" class="btn btn-primary form-control">회원가입</button>
</form>
</div>
</div>
</div>
{{> layout/footer}}
user/login-form.mustache
{{> layout/header}}
<div class="container p-5">
<!-- 요청을 하면 localhost:8080/login POST로 요청됨
username=사용자입력값&password=사용자값 -->
<div class="card">
<div class="card-header"><b>로그인을 해주세요</b></div>
<div class="card-body">
<form action="/login" method="post" enctype="application/x-www-form-urlencoded">
<div class="mb-3">
<input type="text" class="form-control" placeholder="Enter username" name="username">
</div>
<div class="mb-3">
<input type="password" class="form-control" placeholder="Enter password" name="password">
</div>
<button type="submit" class="btn btn-primary form-control">로그인</button>
</form>
</div>
</div>
</div>
{{> layout/footer}}
- user/update-form.mustache
{{> layout/header}}
<div class="container p-5">
<!-- 요청을 하면 localhost:8080/join POST로 요청됨
username=사용자입력값&password=사용자값&email=사용자입력값 -->
<div class="card">
<div class="card-header"><b>회원수정을 해주세요</b></div>
<div class="card-body">
<form action="/user/update" method="post" enctype="application/x-www-form-urlencoded">
<div class="mb-3">
<input type="text" class="form-control" placeholder="Enter username" name="username" disabled>
</div>
<div class="mb-3">
<input type="password" class="form-control" placeholder="Enter password" name="password">
</div>
<div class="mb-3">
<input type="email" class="form-control" placeholder="Enter email" name="email" disabled>
</div>
<button type="submit" class="btn btn-primary form-control">회원가입수정</button>
</form>
</div>
</div>
</div>
{{> layout/footer}}
board/save-form.mustache
{{> layout/header}}
<div class="container p-5">
<div class="card">
<div class="card-header"><b>글쓰기 화면입니다</b></div>
<div class="card-body">
<!--
action="/board/save": 폼 제출시 /board/save 경로로 요청
method="post": HTTP POST 방식으로 데이터 전송
GET: URL에 데이터 노출, POST: 요청 본문에 데이터 숨김
-->
<form action="/board/save" method="post">
<!--
name 속성: 서버에서 받을 때 사용하는 변수명
name="username" → 컨트롤러에서 String username으로 받음
-->
<div class="mb-3">
<input type="text" class="form-control" placeholder="Enter username" name="username">
</div>
<div class="mb-3">
<input type="text" class="form-control" placeholder="Enter title" name="title">
</div>
<div class="mb-3">
<!-- textarea: 여러 줄 텍스트 입력 가능 -->
<textarea class="form-control" rows="5" name="content"></textarea>
</div>
<!-- 버튼 클릭시 폼이 제출됨 -->
<button class="btn btn-primary form-control">글쓰기완료</button>
</form>
</div>
</div>
</div>
{{> layout/footer}}
board/detail.mustache
{{> layout/header}}
<div class="container p-5">
<!-- 수정삭제버튼 -->
<div class="d-flex justify-content-end">
<button class="btn btn-warning me-1">수정</button>
<button class="btn btn-danger">삭제</button>
</div>
<div class="d-flex justify-content-end">
<b>작성자</b> : ssar
</div>
<!-- 게시글내용 -->
<div>
<h2><b>제목자리</b></h2>
<hr />
<div class="m-4 p-2">
내용입니다
</div>
</div>
<!-- 댓글 -->
<div class="card mt-3">
<!-- 댓글등록 -->
<div class="card-body">
<form action="/reply/save" method="post">
<textarea class="form-control" rows="2" name="comment"></textarea>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-outline-primary 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-primary text-white rounded">cos</div>
<div>댓글 내용입니다</div>
</div>
<form action="/reply/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-primary text-white rounded">ssar</div>
<div>댓글 내용입니다</div>
</div>
<form action="/reply/1/delete" method="post">
<button class="btn">🗑</button>
</form>
</div>
</div>
</div>
</div>
{{> layout/footer}}
개발 환경 설정 확인
더보기
application-dev.yml
server:
servlet:
encoding:
charset: utf-8
force: true
port: 8080
# 로그 레벨의 개념
# ERROR > WARN > INFO > DEBUG > TRACE
logging:
level:
root: INFO # 모든 라이브러리는 INFO 이상만 출력
com.tenco: DEBUG # 내 프로젝트는 DEBUG 이상 모두 출력
# spring 환경 설정
spring:
mustache:
# 템플릿 파일의 확장자를 .mustache로 지정하여 Mustache
# 템플릿 파일을 인식하도록 설정
suffix: .mustache
# 템플릿 파일이 위치한
# 기본 경로를 src/main/resources/templates/로 설정
prefix: classpath:/templates/
# 개발 중 템플릿 캐시를 비활성화하여 코드
# 수정 후 바로 반영되도록 함 (프로덕션에서는 true로 설정 권장)
cache: false
# 템플릿 파일을 UTF-8 인코딩으로 처리하여 한글 등 다양한 문자 지원
charset: UTF-8
servlet:
# 세션과 요청 속성을 템플릿에서 사용 가능하게 함
expose-session-attributes: true
expose-request-attributes: true
# 데이터 베이스 연결 설정 (H2 인메모리 데이터베이스)
datasource:
#driver-class-name: com.mysql.cj.jdbc.Driver
#url: jdbc:mysql://localhost:3306/blog?useSSl=false&serverTimezone=Asia/Seoul
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/blog?useSSl=false&serverTimezone=Asia/Seoul
username: root
password: asd1234
# H2 데이터베이스 웹 콘솔 활성화 (개발용)
# http://localhost:8080/h2-console
h2:
console:
enabled: true
sql:
init:
# 2.5 이상 버전부터 명시를 해야 insert 처리 됨
mode: always
data-locations:
- classpath:db/data.sql
# JPA 설정
jpa:
hibernate:
# create: 애플리케이션 시작시 테이블을 새로 생성
# 기존 데이터는 모두 삭제됨 (개발용)
ddl-auto: create
# SQL 쿼리를 콘솔에 출력 (개발용 디버깅)
show-sql: true
properties:
hibernate:
# SQL 쿼리를 보기 좋게 포맷팅
format_sql: true
# data.sql 파일을 Hibernate 초기화 이후에 실행
defer-datasource-initialization: true
# 기본값 : 엔티티 - 카멜케이스 -> db 생성될 스네이크 케이스로 자동으로 설정 됨
Board 엔티티 만들어 보기
model/Board.java
package com.tenco.blog.model;
import jakarta.persistence.*;
import lombok.Data;
import java.sql.Timestamp;
@Data
// @Table : 실제 데이터베이스 테이블 명을 지정할 때 사용
@Table(name = "board_tb")
// @Entity : JPA가 이 클래스를 테이터베이스 테이블과 매핑하는 객체(엔티티)로 인식
// 즉, @Entity 어노테이션이 있어야 JPA가 이 객체를 관리 한다.
@Entity
public class Board {
// @Id 이 필드가 기본키(Primary key) 임을 나타냄
@Id
// DENTITY 전략 : 데이터베이스의 기본 전략을 사용한다. -> Auto_Increment
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 별도 어노테이션이 없으면 필드명이 컬럼명이 됨
private String title;
private String content;
private String username;
private Timestamp createdAt; // created_at (스네이크 케이스로 자동 변환)
}
db/data.sql // 더미 파일
insert into board_tb(title, content, username, created_at) values('제목1','내용1','ssar',now());
insert into board_tb(title, content, username, created_at) values('제목2','내용2','ssar',now());
insert into board_tb(title, content, username, created_at) values('제목3','내용3','cos',now());
insert into board_tb(title, content, username, created_at) values('제목4','내용4','love',now());
화면 연결 설정
더보기
BoardController 코드 추가
package com.tenco.blog.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@Controller // IoC 대상 - 싱글톤 패턴으로 관리 됨
public class BoardController {
@GetMapping({"/", "/index"})
public String index() {
// prefix: /templates/
// return : index
// suffix: .mustache
// # 기본 경로를 src/main/resources/templates/index.mustache
return "index";
}
@GetMapping("/board/save-form")
public String saveFrom() {
// /templates//board
// /templates/board/
return "board/save-form";
}
//
/**
* 상세보기 화면 요청
* board/1
*/
@GetMapping("/board/{id}")
public String detail(@PathVariable(name = "id") Integer id) {
// URL 에서 받은 id 값을 사용해서 특정 게시글 상세보기 조회
// 실제로는 이 id값으로 데이터베이스에 있는 게시글 조회 하고
// 머스태치 파일로 데이터를 내려 주어야 함 (Model)
return "board/detail";
}
}
Usercontroller 코드 추가
package com.tenco.blog.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class UserController {
// 요청 되어 오는 주소 -> /join-form
@GetMapping("/join-form")
public String joinForm() {
return "user/join-form";
}
@GetMapping("/login-form")
public String loginForm() {
// 반환값이 뷰(파일) 이름이 됨 (뷰 리졸버가 실제 파일 경로를 찾아 감)
return "user/login-form";
}
// 주소 설계 : http://localhost:8080/user/update-form
@GetMapping("/user/update-form")
public String updateForm() {
return "user/update-form";
}
@GetMapping("/logout")
public String logout() {
// "redirect: " 스프링 에서 접두사를 사용하면 다른 URL 로 리다이렉트 됨
// 즉 리다렉트 한다는 것은 뷰를 렌더링 하지 않고 브라우저가 재 요청을
// 다시 하게끔 유도 한다.
return "redirect:/";
}
}
리다이렉트 추가 정리
1. 요청-응답, 재요청-응답 흐름
2. 클라이언트가 서버에 요청을 보냄(예:/ logout).
3.서버는 리다이렉트 응답(HTTP 302 상태 코드 + 새로운 URL)을 반환.
4.클라이언트는 새로운 URL(예:/)로 재요청
5. 서버는 재요청된 URL에 대한 응답(예: 홈페이지 HTML)을 반환.
728x90
'Spring boot' 카테고리의 다른 글
(스프링 부트 입문) 버전 1 - 스프링 부트 익명 블로그 만들어 보기(네이티브 쿼리만 사용) 2. 게시글 목록 보기 만들기 (1) | 2025.06.21 |
---|---|
(스프링 부트 입문) 버전 1 - 스프링 부트 익명 블로그 만들어 보기(네이티브 쿼리만 사용) 1. 게시글 작성 - 네이티브 쿼리 활용 (0) | 2025.06.21 |
(스프링 부트 입문)스프링 블로그 프로젝트 만들어보기 (0) | 2025.06.20 |
(스프링 부트 입문) 스프링 부트 익명 블로그 만들어 보기(네이티브 쿼리만사용) 1. 게시글 작성 - 네이티브 쿼리 활용 (0) | 2025.06.20 |
(스프링 부트)DELETE 방식에 이해 및 실습 (0) | 2025.06.20 |