[스프링부트] 15. 블로그v1

백하림's avatar
Mar 20, 2025
[스프링부트] 15. 블로그v1
notion image
notion image

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 // Component Scan -> DS가 활용 public class BoardController { private BoardService boardService; public BoardController(BoardService boardService) { this.boardService = boardService; } @PostMapping("/board/save") public String save(String title, String content, String nickname) { boardService.게시글쓰기(title, content, nickname); return "redirect:/"; // 주소가 이미 만들어져 있으면 리다이렉션 } @PostMapping("/board/{id}/delete") public String delete(@PathVariable("id") int id) { boardService.게시글삭제(id); return "redirect:/"; } @PostMapping("/board/{id}/update") public String update(@PathVariable("id") int id, String title, String content) { // update board_tb set title=?, content=? where id=? // 주소로 받는 값은 where에 걸린다 boardService.게시글수정(id, title, content); return "redirect:/board/" + id; } @GetMapping("/") public String list(HttpServletRequest request) { List<Board> boardList = boardService.게시글목록(); request.setAttribute("models", boardList); // request 담기 return "list"; // forward } @GetMapping("/board/{id}") // Pattern Matching (/board/1,2,3,...) public String detail(@PathVariable("id") int id, HttpServletRequest request) { Board board = boardService.게시글상세보기(id); request.setAttribute("board", board); return "detail"; } @GetMapping("/board/save-form") // 주소는 하이픈 사용 (-) public String saveForm() { return "save-form"; } @GetMapping("/board/{id}/update-form") public String updateForm(@PathVariable("id") int id, HttpServletRequest request) { Board board = boardService.게시글수정화면(id); request.setAttribute("board", 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; // 책임 : 트랜잭션 처리, Business Logic 처리 @Service // IoC public class BoardService { private BoardRepository boardRepository; // DI : 의존성 주입 -> IoC로 부터 들고옴 public BoardService(BoardRepository boardRepository) { this.boardRepository = boardRepository; } @Transactional // 트랜잭션 시작 -> 함수 내부 전부 완료 시 commit, 실패 시 rollback public void 게시글쓰기(String title, String content, String nickname) { boardRepository.insert(title, content, nickname); } public List<Board> 게시글목록() { List<Board> boardList = boardRepository.findAll(); return boardList; } public Board 게시글상세보기(int id) { Board board = boardRepository.find(id); return board; } @Transactional public void 게시글삭제(int id) { // 1. 게시글 존재 확인 Board board = boardRepository.find(id); // 2. 삭제 if (board == null) { throw new RuntimeException("게시글이 존재하지 않습니다."); } else boardRepository.delete(id); } @Transactional public void 게시글수정(int id, String title, String content) { boardRepository.update(id, title, content); } public Board 게시글수정화면(int id) { return boardRepository.find(id); } }

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 Container public class BoardRepository { private EntityManager em; // DI -> IoC 순회해서 타입으로 찾아서 전달 public BoardRepository(EntityManager em) { System.out.println("new BoardRepository constructor"); 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(); } public List<Board> findAll() { Query query = em.createNativeQuery("select * from board_tb order by id desc", Board.class); List<Board> boardList = query.getResultList(); return boardList; } public Board find(int id) { Query query = em.createNativeQuery("select * from board_tb where id = ?", Board.class); query.setParameter(1, id); Board board = (Board) query.getSingleResult(); return board; } public void delete(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) { Query query = em.createNativeQuery("update board_tb set title = ?, content = ? where id = ?"); query.setParameter(1, title); query.setParameter(2, content); query.setParameter(3, id); query.executeUpdate(); } }

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; private String nickname; }

Application.properties

# UTF-8 ?? (?? ???) server.servlet.encoding.charset=UTF-8 server.servlet.encoding.force=true # DB ?? 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 ?? spring.jpa.hibernate.ddl-auto=create spring.jpa.show-sql=true # ?? ?? spring.sql.init.data-locations=classpath:db/data.sql spring.jpa.defer-datasource-initialization=true # request spring.mustache.servlet.expose-request-attributes=true

data.sql

insert into board_tb(title, content, nickname, created_at) values ('제목1', '내용1', 'Lee', now()); insert into board_tb(title, content, nickname, created_at) values ('제목2', '내용2', 'Park', now()); insert into board_tb(title, content, nickname, created_at) values ('제목3', '내용3', 'Choi', now()); insert into board_tb(title, content, nickname, created_at) values ('제목4', '내용4', 'Kim', now()); insert into board_tb(title, content, nickname, created_at) values ('제목5', '내용5', 'ssar', now());

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="/board/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/{{board.id}}/update-form">수정화면 가기</a> <form action="/board/{{board.id}}/delete" method="post"> <button type="submit">삭제</button> </form> <div> 번호 : {{board.id}} <br> 제목 : {{board.title}} <br> 내용 : {{board.content}} <br> 작성일 : {{board.createdAt}} <br> 작성자 : {{board.nickname}} <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.mustache

<!-- 글 수정 화면 --> {{> layout/header}} <section> <form action="/board/{{board.id}}/update" method="post" enctype="application/x-www-form-urlencoded"> <input type="text" name="title" value={{board.title}}><br> <input type="text" name="content" value={{board.content}}><br> <button type="submit">글수정</button> </form> </section> </body> </html>

메인 페이지

notion image

글쓰기 페이지

notion image
 

글쓰기 버튼 눌렀을 때의 페이지

notion image

글 수정 페이지

notion image

수정이 완료된 모습

notion image

삭제를 눌렀을 때 적용이 된 모습

notion image
Share article

harimmon