일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Variabla
- 부적합한열
- WHEREIN
- springboot
- git revase
- Java
- PathVariable
- SQL
- CRUD
- assertequals
- git amend
- BCryptPasswordEncoder
- Thymeleaf
- mybatis
- oracle
- JDBC
- localStorage
- MVC
- passwordencoder
- git reset
- 이딴게개발자
- 배열
- JavaScript
- useContext
- ResultType
- react
- HTML
- content-box
- Spring
- git
- Today
- Total
개발새발
[SpringBoot] 3. 게시판으로 CRUD 기능 구현 - (2) 본문
이 글은 '게시판으로 CRUD 기능 구현 -(1)'과 이어집니다.
[SpringBoot] 3. 게시판으로 CRUD 기능 구현 - (1)
안녕하세요 오늘로 세 숟가락 째 뜨게되는 글이군요 아직 배는 부르지 않지만, 알듯말듯 아리송 할 정도는 온 것같아요. 알듯말듯 아리송 알리송 ㅋㅋ 역시 새벽 블로그는 제정신이 아닌 맛이죠
im2ho.tistory.com
죽지도 않고 돌아왔습니다.
당연하긴해요. 하루밖에 안 지났거든요 ㅎㅋㅋ
그리고 여전히 틀린정보 속출 가능 주의 바라구요
레지고
.
.
.
아, 들어가기에 앞서 진짜 틀린(?)정보가 하나 나와버리고야 말았습니다.
제가 그동안 '왜 이렇게 코드 쓰는게 힘들지?' 생각해봤는데, 이런.. 개발순서가 살짝 틀렸더라구요.
뭐 사람 취향 따라 아무렇게나 쓰면 되는것도 맞긴한데 그래도 흐름이라는게 있으니까용
아무튼 그런 의미로 지금은 컨트롤러를 잠시 제쳐두고 서비스를 먼저 만들겠습니다.
< BoardService.java >
com.example.springdb.serice 패키지에 BoardService.java 생성
여기서는 할 일이 굉장히 많습니다.. 무려 CRUD 기능을 4개나 만들겠다고해서 그렇긴한데요
쪼금 막막하긴 하지만, 전 어른이니까 괜찮습니다.
package com.example.springdb.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.springdb.mapper.BoardMapper;
import com.example.springdb.model.Board;
@Service
public class BoardService {
@Autowired //bean 불러오기
private BoardMapper boardMapper;
//전체 게시글 조회
public List<Board> getAllBoards(){
return boardMapper.getAllBoards();
}
//전체 게시판에서 게시물 1개 선택 > 상세보기 서비스 by board_id
public Board getBoardById(int board_id) {
return boardMapper.getBoardById(board_id);
}
//게시판에서 게시글 작성하기
public void insertBoard(Board board) {
boardMapper.insertBoard(board);
}
//게시판에서 게시글 수정하기
public void updateBoard(Board board) {
boardMapper.updateBoard(board);
}
//게시판에서 게시글 삭제 by board_id
public void deleteBoard(int board_id) {
boardMapper.deleteBoard(board_id);
}
}
일단 완성된 코드입니다.
"왜 컨트롤러 말고 서비스를 먼저 만들었냐?" 할 수 있는데
앞서 말했다시피 제가 이 프로젝트 개발 흐름을 다소 잘못 알고 있었더라구요.
myBatis를 활용한 Java객체와 관계형데이터베이스 연결 및 작동 순서는 다음과 같습니다.
어쩌다보니 JPA 순서까지 나와있는데 여기서 말하고자 하는 데에는 큰 차이가 없습니다.
전 그동안 Service가 그냥 무슨 Controller의 하청업체 정도 된다고만 생각했는데 그게 아니었어요.
SQL구문을 통해 Mapper.java에서 받아온 데이터를 어떻게 처리할지는 이 Service가 결정하는 것이었습니다.
그것도 모르고... 그동안 냅다 애꿎은 Controller만...
아무튼 그래서 @Autowired를 통해 BoardMapper 객체를 불러오고,
각각의 데이터 및 사용법에 맞게 메서드를 작성해준 모습을 볼 수 있습니다~
< BoardController.java >
com.example.springdb.controller 패키지에 BoardController.java 생성
네, 그리고 드디어 이 Service가 Java형식에 맞게끔 처리한 데이터를 Controller가 받아서~
사용자에게 보일 페이지(View)에 어떻게 보이게 할지!를 작성하는 것입니다 드디어 보이는 흐름
package com.example.springdb.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.springdb.model.Board;
import com.example.springdb.service.BoardService;
@Controller
@RequestMapping("/boards") //url을 한 번에 합쳐준다
public class BoardController {
@Autowired
private BoardService boardService;
/****************** 게시글 조회 ******************/
@GetMapping //("/boards") 생략된 상태 by @RequestMapping
public String getAllBoards(Model model) {
//전체 게시물 보는 Controller (일단 model에 값을 전달해주궛어요)
List<Board> boards = boardService.getAllBoards();
model.addAttribute("boards", boards); //model.addAttribute(이름, 리스트명)
return "board-list"; //board-list.html로 이동
}
@GetMapping("/{board_id}") //마찬가지로 ("/boards")가 내장되어 생략된 상태
//@GetMapping("/boards/{board_id}")
public String getBoardById(@PathVariable int board_id, Model model) {
//board_id를 통해 게시글 상세보기 Controller
Board board = boardService.getBoardById(board_id);
model.addAttribute("board", board);
return "board-detail";
}
/****************** 게시판 글 작성 ******************/
//1. GetMapping을 사용해서 게시글 작성하는 html로 이동한 후
@GetMapping("/create") //이동할 공간
//http GET 요청이 /create라는 경로로 들어올 때 호출됩니달라
public String displayCreateForm(Model model) { //Model 객체를 매개변수로 받아서 templates(view)으로 데이터를 전달 가능
//new Board로 새로운 Board 객체를 생성해서 모델에 추가하겟습니달라
model.addAttribute("board",new Board());
return "board-form"; //board-form.html 템플릿에서 해당 view를 보여주겟습니다
}
//2. PostMapping을 사용해서 작성해놓은 insert HTML form 가져온다!
@PostMapping("create")
public String createBoard(@ModelAttribute Board board) {
boardService.insertBoard(board);
return "redirect:/boards"; //글이 작성된 후 돌아갈 템플릿 (boards로 돌아갈랭)
}
/****************** 게시글 수정 ******************/
@PostMapping("/update/{board_id}")
public String updateForm(@PathVariable int board_id, @ModelAttribute Board board) {
//URL에서 가져온 board_id 값을 Board 객체에 저장 (수정한 내용을 setBoard_id를 통해 저장)
board.setBoard_id(board_id);
boardService.updateBoard(board);
//수정이 완료된 후 게시글 목록 페이지로 돌아가기
return "redirect:/boards";
}
@GetMapping("/update/{board_id}")
public String displayUpdateForm(@PathVariable int board_id, Model model) {
Board board = boardService.getBoardById(board_id);
model.addAttribute("board", board);
return "board-form";
}
/****************** 게시글 삭제 ******************/
//게시글 삭제 by board_id
@GetMapping("/delete/{board_id}")
public String deleteBoard(@PathVariable int board_id) {
boardService.deleteBoard(board_id);
return "redirect:/boards";
}
}
코드에서 사용하는
Model model 의 경우, 처음에는 Board board도 아니고 이게 무슨 소리람? 하고 의아해 했던 부분이 있어 짚어보겠습니다.
< Model model >
Controller에서 View로 데이터를 전달할 때 사용하는 인터페이스로,
Controller에 있는 메서드에서 매개변수로 Model을 선언하면 Get에 추가한 데이터는 자동으로 View에 전달됨
아, 생각보다 별거 아니었군요.
그저 컨트롤러와 뷰를 연결하는 데에 있어 필요한 녀석 그 이상도 이하도 아니었습니다.
그리고 또 짚고 넘어갈 부분이라면 @RequestMapping과 @GetMapping 및 @PostMapping의 관계정도?
의미만 간략하게 말을 하자면 @RequestMapping에서 설정한 url이 디폴트가 되어, @GetMapping이 적용된 메서드는 그 이후의 url을 이어 받는 느낌입니다.
< View >
와 드디어 마지막! View가 뭡니까. 바로 사용자의 눈에 보이는 것이잖아요?
사용자는 보통 브라우저에 나타나는 화면을 통해서 코드의 결과를 보게 될 것이므로 저희는 view를 나타내기 위해 상황에 맞는 html을 작성해야 합니다.
파일명은 Controller에서 나타낸 return값을 참고해서 작성했습니다.
- board-list.html 전체 게시물 목록을 나타내는 페이지
- board-detail.html 게시물 상세보기 페이지 by board_id
- board-form.html 새로운 게시물을 작성하는 페이지
html 파일은 동적인 요소를 지녔으니 scr/main/resources/templates 폴더에 생성하겠습니다.
1. board-list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>게시물 목록</title>
<link rel="stylesheet" href="/css/styles.css" th:href="@{/css/styles.css}">
</head>
<body>
<h2>게시물 목록보기</h2>
<table border="1">
<thead>
<tr>
<th>아이디</th>
<th>제목</th>
<th>작성자</th>
<th>↓↓↓ click ↓↓↓</th>
</tr>
</thead>
<tbody>
<tr th:each="board : ${boards}">
<td th:text="${board.board_id}"></td>
<td th:text="${board.title}"></td>
<td th:text="${board.author}"></td>
<td>
<!-- 게시물 상세보기 버튼 추가 -->
<a th:href="@{/boards/{id}(id=${board.board_id})}">View</a>
<!-- 게시물 추가하기 버튼 추가 -->
<a th:href="@{/boards/update/{id}(id=${board.board_id})}">Update</a>
<!-- 게시물 삭제하기 버튼 추가 -->
<a th:href="@{/boards/delete/{id}(id=${board.board_id})}">Delete</a>
</td>
</tr>
</tbody>
</table><br>
<a th:href="@{/boards/create}">게시글 작성하기</a><br>
</body>
</html>
css파일은 디자인을 위해 별도 작성한 것으로 없어도 무방합니달라..
borad-list 파일의 경우에는, 게시판의 메인 역할을 하므로 이 페이지를 통해 게시글 상세보기, 게시글 추가 작성 및 삭제를 위해 페이지를 이동할 수 있게끔 구현했습니다.
2. board-detail.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>게시물 상세보기</title>
<link rel="stylesheet" href="/css/styles.css" th:href="@{/css/styles.css}">
</head>
<body>
<h2>게시물 상세보기</h2>
<table border="1">
<tr>
<td>아이디</td>
<td th:text="${board.board_id}"></td>
</tr>
<tr>
<td>제목</td>
<td th:text="${board.title}"></td>
</tr>
<tr>
<td>내용</td>
<td th:text="${board.content}"></td>
</tr>
<tr>
<td>작성자</td>
<td th:text="${board.author}"></td>
</tr>
</table>
<a th:href="@{/boards}">목록으로 돌아가기</a>
</body>
</html>
각 게시물마다 존재하는 고유번호인 board_id를 통해(컨트롤러에서 @PathVariable 활용) 상세 페이지를 확인 가능합니다.
3. board-detail.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>게시글 작성</title>
<link rel="stylesheet" href="/css/styles.css" th:href="@{/css/styles.css}">
</head>
<body>
<h2>게시글 작성하기</h2>
<!--boardList 주소로 제출한다-->
<form th:object="${board}"
th:action="${board_id != null ? '/boards/update/' + board_id:'/boards/create'}"
method="post">
<input type="hidden" th:field="*{board_id}"/>
<label for="title">제목 : </label>
<input type="text" id="title" name="title" th:field="*{title}" required>
<br>
<label for="content">내용 : </label>
<input type="text" id="content" name="content" th:field="*{content}" required>
<br>
<label for="author">작성자 : </label>
<input type="text" id="author" name="author" th:field="*{author}" required>
<br>
<button type="submit">저장</button>
</form>
</body>
</html>
새로운 게시글을 작성할 수 있는 화면입니다.
<form>태그 내 작성된 내용은 하단의 저장버튼을 통해 게시글의 목록이 보이는 /board 주소로 전송됩니다.
th:action="${board_id != null ? '/boards/update/' + board_id:'/boards/create'}
이 친구의 경우는, board_id값이 이미 존재하는 경우 /update 주소로 보내져, 존재하는 게시물을 수정할 수 있게끔 합니다.
board_id값이 존재하지 않을시, 새 게시물 작성 가능 UPDATE와 INSERT를 한 번에! 와아
그럼 이제 VIEW까지 모두 완성 되었으니 실행을 해봅시다!
게시물 목록 화면 정상 출력되었네요.
하지만 아직 테이블에 존재하는 데이터가 없어 비어있는 상태입니다.
게시글 작성도 무사히 가능할까요?
저희 블로그 배너 사진 만들어준 어텀로그 샤라웃 함 갈겨줬습니다.
아무튼 게시글 작성 페이지로도 잘 이동하네요.
이제 작성한 글이 과연 잘 저장될지?
redirect를 통해 /boards로 바로 돌아옴과 동시에,
게시글이 무사히 저장된 것을 확인 가능합니다.
내친김에 게시글 수정도 한번 해보죠.
이미 존재하는 board_id 값이 있을 경우에는
/boards/update/{board_id}로 이동하는 것을 확인 가능합니다
마지막으로 삭제 기능도 테스트 하겠습니다
뭐라 할 새도 없이 Delete버튼을 누름과 동시에 가차없이 삭제돼버렸네요;;ㅋㅋ
추후 alert와 confirm을 통해 삭제 의사를 묻고 진행하는 방법을 택해도 되겠습니다.
아무튼 본래 목적인 CRUD 구현은 무사히 마쳤네요!! 짝짝
와. 진짜 막막했는데 이게 되네요.
역시 코드는 많이 써보는 놈이 장땡이라는 말이 맞는 것같습니다.
아무리 개인공부를 위해 마구잡이로 쓰기 시작한 글 일지라도,
그래도 최소한이라도 정제된 글을 쓰고싶어서 시간을 조금 들인 결과..
이번 글을 쓰면서 많이 배우고 깨쳤습니다.
역시 밥을 세 숟가락을 뜨니 배부르진 않아도 요기는 되네요ㅋㅋ
암튼 오늘은 후련하게 잠들어도 되겠습니다 히히
'Spring > Spring Boot' 카테고리의 다른 글
[SpringBoot] 4. JPA를 활용한 DB 연결(feat.@RestController) - 이미 존재하는 데이터를 가져오기 (1) | 2023.12.04 |
---|---|
[SpringBoot] 3. 게시판으로 CRUD 기능 구현 - (1) (1) | 2023.11.28 |
[SpringBoot] 2. 스프링부트 프로젝트로 oracle DB 연결하기 (3) | 2023.11.27 |
[SpringBoot] 1. eclipse로 maven 프로젝트 만들어보기 (6) | 2023.11.26 |