Contents
BoardControllerBoardServiceBoardBoardRepositorydata.sqlapplication.propertiesh2-consoleheader.mustachelist.mustachedetail.mustachesave-form.mustacheupdate-form.mustacheQ1. 테이블에 nickname 필드 추가, 게시글 쓰기할때 nickname 받기, 게시글 상세보기에서 nickname 확인BoardControllerBoardServiceBoardBoardRepositorydata.sqlapplication.propertieslist.mustachedetail.mustachesave-form.mustacheupdate-form.mustache게시글 추가하기게시글 삭제하기게시글 수정하기update-formBoardRepositoryBoardControllerBoardService


BoardController
package com.metacoding.blogv1.board;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
// 책임 : 요청 잘 받고, 응답 잘 하고
@Controller // 컴퍼넌트 스캔 --> DS가 활용
public class BoardController {
private BoardService boardService;
public BoardController(BoardService boardService) {
this.boardService = boardService;
}
@PostMapping("/board/{id}/update")
public String update(@PathVariable("id") int id, String title, String content) {
// update board_tb set title=?, content=? where id=?
// 주소로 받는 데이터는 전부 다 where에 걸린다 // 프라이머리키,유니크키 -> / 나머지는 -> ?쿼리스트링
System.out.println("id: " + id + " title: " + title + " content: " + content);
return "redirect:/board/" + id;
}
@PostMapping("/board/{id}/delete")
public String delete(@PathVariable("id") int id) {
System.out.println("id: " + id); // id로 db가서 삭제하면 됨
return "redirect:/";
}
@PostMapping("/board/save") // 수행이 끝나면 리다이렉션이 일어난다
public String save(String title, String content) { // input의 name값과 동일해야한다
boardService.게시글쓰기(title, content);
return "redirect:/"; // 해당 페이지로 가는 주소가 있으면 무조건 redirection
}
@GetMapping("/")
public String list() {
return "list";
}
@GetMapping("/board/{id}") // 패턴 매칭 /board/1,2,3 ...
public String detail(@PathVariable("id") int id) { // 주소에 들어오는 숫자값을 id에 받을 수 있는 annotation
return "detail";
}
@GetMapping("/board/save-form") // 주소 (하이픈(-) 사용)
public String saveForm() {
return "save-form"; // viewResolver를 타기 때문에 확장자를 적지 않아도 된다
}
@GetMapping("/board/{id}/update-form")
public String updateForm(@PathVariable("id") int id) {
return "update-form";
}
}
BoardService
package com.metacoding.blogv1.board;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
// 책임: 트랜잭션 처리
@Service // IoC
public class BoardService {
private BoardRepository boardRepository;
// DI: 의존성주입 -> IoC로 부터 들고옴
public BoardService(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
}
@Transactional // 트랜잭션이 시작 -> 함수 내부가 다 수행되면 commit, 실패하면 rollback //springframework
public void 게시글쓰기(String title, String content) {
boardRepository.insert(title, content);
}
}
Board
package com.metacoding.blogv1.board;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.sql.Timestamp;
@Getter
@AllArgsConstructor // 풀 생성자
@NoArgsConstructor // 디폴트 생성자
@Table(name = "board_tb") // table 명 설정
@Entity // jpa가 관리할 수 있게 설정
public class Board {
@Id // pk 설정
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increment 설정
private Integer id;
private String title;
private String content;
private Timestamp createdAt;
}
BoardRepository
package com.metacoding.blogv1.board;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import org.springframework.stereotype.Repository;
// 책임: DB와 소통하는 친구
@Repository // IoC(제어의역전) 컬렉션에 뜬다
public class BoardRepository {
private EntityManager em;
// DI -> 의존성 주입 IoC를 순회해서 타입으로 찾아서 전달해준다
public BoardRepository(EntityManager em) {
System.out.println("BoardRepository new 됨");
this.em = em;
}
public void insert(String title, String content) {
Query query = em.createNativeQuery("insert into board_tb(title, content,created_at) values(?,?,now())");
query.setParameter(1, title);
query.setParameter(2, content);
query.executeUpdate();
}
}
data.sql
insert into board_tb(title, content, created_at)
values ('제목1', '내용1', now());
insert into board_tb(title, content, created_at)
values ('제목2', '내용2', now());
insert into board_tb(title, content, created_at)
values ('제목3', '내용3', now());
insert into board_tb(title, content, created_at)
values ('제목4', '내용4', now());
insert into board_tb(title, content, created_at)
values ('제목5', '내용5', now());
application.properties
# utf-8 한글 인코딩
server.servlet.encoding.charset=utf-8
server.servlet.encoding.force=true
# DB 연결 코드 (EntityManager 만들어냄)
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
# JPA @Entity 스캔해서 테이블 생성
spring.jpa.hibernate.ddl-auto=create
# 콘솔에 쿼리 표시
spring.jpa.show-sql=true
# 더미데이터 sql문 실행
spring.sql.init.data-locations=classpath:db/data.sql
# ddl-auto가 실행된 후에 sql문 실행하는 법
spring.jpa.defer-datasource-initialization=true
# mustache에서 request 객체 접근하게 설정하는 법
spring.mustache.servlet.expose-request-attributes=true
h2-console

header.mustache
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>blog</title>
</head>
<body>
<nav>
<ul>
<li>
<a href="/">홈</a>
</li>
<li>
<a href="/save-form">글쓰기</a>
</li>
</ul>
</nav>
<hr>
list.mustache
{{> layout/header}}
<section>
<table border="1">
<tr>
<th>번호</th>
<th>제목</th>
<th></th>
</tr>
{{#models}}
<tr>
<td>{{id}}</td>
<td>{{title}}</td>
<td><a href="/board/{{id}}">상세보기</a></td>
</tr>
{{/models}}
</table>
</section>
</body>
</html>
detail.mustache
{{> layout/header}}
<section>
<a href="/board/{{model.id}}/update-form">수정화면가기</a>
<form action="/board/{{model.id}}/delete" method="post">
<button type="submit">삭제</button>
</form>
<div>
번호 : {{model.id}} <br>
제목 : {{model.title}} <br>
내용 : {{model.content}} <br>
작성일 : {{model.createdAt}} <br>
</div>
</section>
</body>
</html>
save-form.mustache
값을 적는 화면에는 주소에 form 붙이기 → 코드 컨벤션
{{> layout/header}}
<section>
<!-- http body : title=제목6&content=내용6
http header : application/x-www-form-urlencoded
key값은 input태그의 name, value값은 input태그에 사용자가 입력하는 값-->
<form action="/board/save" method="post" enctype="application/x-www-form-urlencoded">
<input type="text" name="title" placeholder="제목"><br>
<input type="text" name="content" placeholder="내용"><br>
<button type="submit">글쓰기</button>
</form>
</section>
</body>
</html>
update-form.mustache
{{> layout/header}}
<section>
<form action="/board/1/update" method="post" enctype="application/x-www-form-urlencoded">
<input type="text" name="title" value="제목1"><br>
<input type="text" name="content" value="내용1"><br>
<button type="submit">글수정</button>
</form>
</section>
</body>
</html>
Q1. 테이블에 nickname 필드 추가, 게시글 쓰기할때 nickname 받기, 게시글 상세보기에서 nickname 확인
BoardController
package com.metacoding.blogv1.board;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
// 책임 : 요청 잘 받고, 응답 잘 하고
@Controller // 컴퍼넌트 스캔 --> DS가 활용
public class BoardController {
private BoardService boardService;
public BoardController(BoardService boardService) {
this.boardService = boardService;
}
@PostMapping("/board/{id}/update")
public String update(@PathVariable("id") int id, String title, String content) {
// update board_tb set title=?, content=? where id=?
// 주소로 받는 데이터는 전부 다 where에 걸린다 // 프라이머리키,유니크키 -> / 나머지는 -> ?쿼리스트링
System.out.println("id: " + id + " title: " + title + " content: " + content);
return "redirect:/board/" + id;
}
@PostMapping("/board/{id}/delete")
public String delete(@PathVariable("id") int id) {
boardService.게시글삭제(id);
System.out.println("id: " + id); // id로 db가서 삭제하면 됨
return "redirect:/";
}
@PostMapping("/board/save") // 수행이 끝나면 리다이렉션이 일어난다
public String save(String title, String content, String nickname) { // input의 name값과 동일해야한다
boardService.게시글쓰기(title, content, nickname);
return "redirect:/"; // 해당 페이지로 가는 주소가 있으면 무조건 redirection
}
// C -> M -> V
@GetMapping("/")
public String list(HttpServletRequest request) {
List<Board> boardList = boardService.게시글목록();
request.setAttribute("models", boardList); // forward(request에 담기)
return "list";
}
@GetMapping("/board/{id}") // 패턴 매칭 /board/1,2,3 ...
public String detail(@PathVariable("id") int id, HttpServletRequest request) { // 주소에 들어오는 숫자값을 id에 받을 수 있는 annotation
Board board = boardService.게시글상세보기(id);
request.setAttribute("model", board);
return "detail";
}
@GetMapping("/board/save-form") // 주소 (하이픈(-) 사용)
public String saveForm() {
return "save-form"; // viewResolver를 타기 때문에 확장자를 적지 않아도 된다
}
@GetMapping("/board/{id}/update-form")
public String updateForm(@PathVariable("id") int id) {
return "update-form";
}
}
BoardService
package com.metacoding.blogv1.board;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
// 책임: 트랜잭션 처리 , 비즈니스 로직 처리 (ex. 송금 전 잔액검사)
@Service // IoC
public class BoardService {
private BoardRepository boardRepository;
// DI: 의존성주입 -> IoC로 부터 들고옴
public BoardService(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
}
@Transactional // 트랜잭션이 시작 -> 함수 내부가 다 수행되면 commit, 실패하면 rollback (write일때만) //springframework
public void 게시글쓰기(String title, String content, String nickname) {
boardRepository.insert(title, content, nickname);
}
public List<Board> 게시글목록() {
List<Board> boardList = boardRepository.findALl(); // boardRepository한테 위임
return boardList;
}
public Board 게시글상세보기(int id) {
return boardRepository.findById(id);
}
@Transactional
public void 게시글삭제(int id) {
// 확실할때만 삭제해야하기 때문에 로직이 필요하다
// 1. 게시글이 존재하는지 확인
Board board = boardRepository.findById(id);
// 2. 삭제
if (board == null) {
throw new RuntimeException("게시글이 없는데 왜 삭제?");
}
boardRepository.deleteById(id);
} // commit, 터지면 rollback
}
Board
package com.metacoding.blogv1.board;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.sql.Timestamp;
@Getter
@AllArgsConstructor // 풀 생성자
@NoArgsConstructor // 디폴트 생성자
@Table(name = "board_tb") // table 명 설정
@Entity // jpa가 관리할 수 있게 설정
public class Board {
@Id // pk 설정
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increment 설정
private Integer id;
private String title;
private String content;
private String nickname;
private Timestamp createdAt;
}
BoardRepository
package com.metacoding.blogv1.board;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
// 책임: DB와 소통하는 친구
@Repository // IoC(제어의역전) 컬렉션에 뜬다
public class BoardRepository {
private EntityManager em;
// DI -> 의존성 주입 IoC를 순회해서 타입으로 찾아서 전달해준다
public BoardRepository(EntityManager em) {
System.out.println("BoardRepository new 됨");
this.em = em;
}
public void insert(String title, String content, String nickname) {
Query query = em.createNativeQuery("insert into board_tb(title, content,nickname,created_at) values(?,?,?,now())");
query.setParameter(1, title);
query.setParameter(2, content);
query.setParameter(3, nickname);
query.executeUpdate(); // insert, update, delete 일때만
}
public List<Board> findALl() {
Query query = em.createNativeQuery("select * from board_tb order by id desc", Board.class); // Board.class 를 쓰면 while 돌면서 board 클래스로 자동 매핑
List<Board> boardList = query.getResultList();
return boardList; // service한테 return
}
public Board findById(int id) {
Query query = em.createNativeQuery("select * from board_tb where id = ?", Board.class);
query.setParameter(1, id);
try {
Board board = (Board) query.getSingleResult(); // 한건이면 query.getSingleResult()
return board;
} catch (Exception e) {
return null;
}
}
public void deleteById(int id) {
Query query = em.createNativeQuery("delete from board_tb where id = ?"); // 매핑할게 없음
query.setParameter(1, id);
query.executeUpdate();
}
}
data.sql
insert into board_tb(title, content, nickname, created_at)
values ('제목1', '내용1', '닉네임1', now());
insert into board_tb(title, content, nickname, created_at)
values ('제목2', '내용2', '닉네임2', now());
insert into board_tb(title, content, nickname, created_at)
values ('제목3', '내용3', '닉네임3', now());
insert into board_tb(title, content, nickname, created_at)
values ('제목4', '내용4', '닉네임4', now());
insert into board_tb(title, content, nickname, created_at)
values ('제목5', '내용5', '닉네임5', now());
application.properties
주석에 한글 작성하면 서버 실행하면서 깨지니까
서버 실행 전 백업 먼저 하기
# utf-8 한글 인코딩
server.servlet.encoding.charset=utf-8
server.servlet.encoding.force=true
# DB 연결 코드 (EntityManager 만들어냄)
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
# JPA @Entity 스캔해서 테이블 생성
spring.jpa.hibernate.ddl-auto=create
# 콘솔에 쿼리 표시
spring.jpa.show-sql=true
# 더미데이터 sql문 실행
spring.sql.init.data-locations=classpath:db/data.sql
# ddl-auto가 실행된 후에 sql문 실행하는 법
spring.jpa.defer-datasource-initialization=true
# mustache에서 request 객체 접근하게 설정하는 법
spring.mustache.servlet.expose-request-attributes=true
list.mustache
{{> layout/header}}
<section>
<table border="1">
<tr>
<th>번호</th>
<th>제목</th>
<th>닉네임</th>
<th>상세보기</th>
</tr>
{{#models}}
<tr>
<td>{{id}}</td>
<td>{{title}}</td>
<td>{{nickname}}</td>
<td><a href="/board/{{id}}">상세보기</a></td>
</tr>
{{/models}}
</table>
</section>
</body>
</html>
detail.mustache
{{> layout/header}}
<section>
<a href="/board/{{model.id}}/update-form">수정화면가기</a>
<form action="/board/{{model.id}}/delete" method="post">
<button type="submit">삭제</button>
</form>
<div>
번호 : {{model.id}} <br>
제목 : {{model.title}} <br>
내용 : {{model.content}} <br>
닉네임 : {{model.nickname}} <br>
작성일 : {{model.createdAt}} <br>
</div>
</section>
</body>
</html>
save-form.mustache
{{> layout/header}}
<section>
<!-- http body : title=제목6&content=내용6
http header : application/x-www-form-urlencoded
key값은 input태그의 name, value값은 input태그에 사용자가 입력하는 값-->
<form action="/board/save" method="post" enctype="application/x-www-form-urlencoded">
<input type="text" name="title" placeholder="제목"><br>
<input type="text" name="content" placeholder="내용"><br>
<input type="text" name="nickname" placeholder="닉네임"><br>
<button type="submit">글쓰기</button>
</form>
</section>
</body>
</html>
update-form.mustache
{{> layout/header}}
<section>
<form action="/board/1/update" method="post" enctype="application/x-www-form-urlencoded">
<input type="text" name="title" value="제목1"><br>
<input type="text" name="content" value="내용1"><br>
<input type="text" name="nickname" value="닉네임"><br>
<button type="submit">글수정</button>
</form>
</section>
</body>
</html>
list page

save-form page

detail page

update-form page

게시글 추가하기
➡ 6번 게시글 추가해보기

⬇
글쓰기
버튼을 누르면 list page로 redirection되면서 6번 게시글이 추가된 것을 확인할 수 있다
⬇ detail 페이지에서 제목, 내용, 닉네임 확인 가능

⬇
글쓰기
버튼을 누르면 DB에서도 동일하게 6번 게시글이 추가된 것을 확인할 수 있다
게시글 삭제하기
➡ 6번 게시글 삭제

⬇
삭제
버튼을 누르면 list page로 redirection되면서 6번 게시글이 사라진 것을 확인할 수 있다
⬇
삭제
버튼을 눌렀을 때 DB에서도 동일하게 삭제된 것을 확인할 수 있다
게시글 수정하기
update-form
{{> layout/header}}
<section>
<form action="/board/{{model.id}}/update" method="post" enctype="application/x-www-form-urlencoded">
<input type="text" name="title" value="{{model.title}}"><br>
<input type="text" name="content" value="{{model.content}}"><br>
<input type="text" name="nickname" value="{{model.nickname}}"><br>
<button type="submit">글수정</button>
</form>
</section>
</body>
</html>
BoardRepository
package com.metacoding.blogv1.board;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
// 책임: DB와 소통하는 친구
@Repository // IoC(제어의역전) 컬렉션에 뜬다
public class BoardRepository {
private EntityManager em;
// DI -> 의존성 주입 IoC를 순회해서 타입으로 찾아서 전달해준다
public BoardRepository(EntityManager em) {
System.out.println("BoardRepository new 됨");
this.em = em;
}
public void insert(String title, String content, String nickname) {
Query query = em.createNativeQuery("insert into board_tb(title, content,nickname,created_at) values(?,?,?,now())");
query.setParameter(1, title);
query.setParameter(2, content);
query.setParameter(3, nickname);
query.executeUpdate(); // insert, update, delete 일때만
}
public List<Board> findALl() {
Query query = em.createNativeQuery("select * from board_tb order by id desc", Board.class); // Board.class 를 쓰면 while 돌면서 board 클래스로 자동 매핑
List<Board> boardList = query.getResultList();
return boardList; // service한테 return
}
public Board findById(int id) {
Query query = em.createNativeQuery("select * from board_tb where id = ?", Board.class);
query.setParameter(1, id);
try {
Board board = (Board) query.getSingleResult(); // 한건이면 query.getSingleResult()
return board;
} catch (Exception e) {
return null;
}
}
public void deleteById(int id) {
Query query = em.createNativeQuery("delete from board_tb where id = ?"); // 매핑할게 없음
query.setParameter(1, id);
query.executeUpdate();
}
// 게시글수정
public void update(int id, String title, String content, String nickname) {
Query query = em.createNativeQuery("update board_tb set title = ?, content = ?,nickname = ? where id = ?");
query.setParameter(1, title);
query.setParameter(2, content);
query.setParameter(3, nickname);
query.setParameter(4, id);
query.executeUpdate();
}
}
BoardController
package com.metacoding.blogv1.board;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
// 책임 : 요청 잘 받고, 응답 잘 하고
@Controller // 컴퍼넌트 스캔 --> DS가 활용
public class BoardController {
private BoardService boardService;
public BoardController(BoardService boardService) {
this.boardService = boardService;
}
// 게시글수정
@PostMapping("/board/{id}/update")
public String update(@PathVariable("id") int id, String title, String content, String nickname) {
// update board_tb set title=?, content=?, nickname=? where id=?
// 주소로 받는 데이터는 전부 다 where에 걸린다 // 프라이머리키,유니크키 -> / 나머지는 -> ?쿼리스트링
System.out.println("id: " + id + " title: " + title + " content: " + content + " nickname: " + nickname);
boardService.게시글수정하기(id, title, content, nickname);
return "redirect:/board/" + id;
}
@PostMapping("/board/{id}/delete")
public String delete(@PathVariable("id") int id) {
boardService.게시글삭제(id);
System.out.println("id: " + id); // id로 db가서 삭제하면 됨
return "redirect:/";
}
@PostMapping("/board/save") // 수행이 끝나면 리다이렉션이 일어난다
public String save(String title, String content, String nickname) { // input의 name값과 동일해야한다
boardService.게시글쓰기(title, content, nickname);
return "redirect:/"; // 해당 페이지로 가는 주소가 있으면 무조건 redirection
}
// C -> M -> V
@GetMapping("/")
public String list(HttpServletRequest request) {
List<Board> boardList = boardService.게시글목록();
request.setAttribute("models", boardList); // forward(request에 담기) 여러건이면 models(컨벤션)
return "list";
}
@GetMapping("/board/{id}") // 패턴 매칭 /board/1,2,3 ...
public String detail(@PathVariable("id") int id, HttpServletRequest request) { // 주소에 들어오는 숫자값을 id에 받을 수 있는 annotation
Board board = boardService.게시글상세보기(id);
request.setAttribute("model", board); // 단건이면 model (컨벤션)
return "detail";
}
@GetMapping("/board/save-form") // 주소 (하이픈(-) 사용)
public String saveForm() {
return "save-form"; // viewResolver를 타기 때문에 확장자를 적지 않아도 된다
}
@GetMapping("/board/{id}/update-form")
public String updateForm(@PathVariable("id") int id, HttpServletRequest request) {
Board board = boardService.게시글상세보기(id);
request.setAttribute("model", board);
return "update-form";
}
}
BoardService
package com.metacoding.blogv1.board;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
// 책임: 트랜잭션 처리 , 비즈니스 로직 처리 (ex. 송금 전 잔액검사)
@Service // IoC
public class BoardService {
private BoardRepository boardRepository;
// DI: 의존성주입 -> IoC로 부터 들고옴
public BoardService(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
}
@Transactional // 트랜잭션이 시작 -> 함수 내부가 다 수행되면 commit, 실패하면 rollback (write일때만) //springframework
public void 게시글쓰기(String title, String content, String nickname) {
boardRepository.insert(title, content, nickname);
}
public List<Board> 게시글목록() {
List<Board> boardList = boardRepository.findALl(); // boardRepository한테 위임
return boardList;
}
public Board 게시글상세보기(int id) {
return boardRepository.findById(id);
}
@Transactional
public void 게시글삭제(int id) {
// 확실할때만 삭제해야하기 때문에 로직이 필요하다
// 1. 게시글이 존재하는지 확인
Board board = boardRepository.findById(id);
// 2. 삭제
if (board == null) {
throw new RuntimeException("게시글이 없는데 왜 삭제?");
}
boardRepository.deleteById(id);
} // commit, 터지면 rollback
@Transactional
public void 게시글수정하기(int id, String title, String content, String nickname) {
// 게시글이 있어야하기 때문에 로직이 필요하다
// 1. 게시글이 존재하는지 확인
Board board = boardRepository.findById(id);
// 2. 수정
if (board == null) {
throw new RuntimeException("게시글이 없는데 왜 수정?");
}
boardRepository.update(id, title, content, nickname);
}
}
➡ 5번 게시글 수정

⬇
삭제
버튼을 누르면 list page로 redirection되면서 6번 게시글이 사라진 것을 확인할 수 있다
⬇
삭제
버튼을 눌렀을 때 DB에서도 동일하게 삭제된 것을 확인할 수 있다
Share article