๐ก DTO : ์ปจํธ๋กค๋ฌ์์ ์๋น์ค๋ก, ํน์ ์๋น์ค์์ ์ปจํธ๋กค๋ฌ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๋ ์ฌ์ฉ
VO : ์๋น์ค๋ DAO์ ๋ฐ์ดํฐ ์ฃผ๊ณ ๋ฐ์ ๋๋ VO๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์๋น์ค์์ DTO ↔ VO ์์ ์ด ํ์
- DTO : ์ปจํธ๋กค๋ฌ ↔ ์๋น์ค
- VO : ์๋น์ค ↔ DAO
1. ์ฝ์์ฐฝ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
CREATE TABLE `tbl_board` (
`no` int auto_increment primary key,
`title` varchar(100) not null, // ์ ๋ชฉ
`content` text not null , // ๋ด์ฉ
`writer` varchar(50) not null , // ์์ฑ์
`passwd` varchar(100) not null , // ๋น๋ฐ๋ฒํธ
`addDate` datetime, // ์์ฑ๋ ์ง
`hit` int default 0 // ์กฐํ์
);
2. BoardVO ์์ฑ
ํ ์ด๋ธ์ ์ ์ฅํ ๋ฐ์ดํฐ๋ฅผ ๋ด๊ฑฐ๋, ํ ์ด๋ธ์์ ๋ค๊ณ ์จ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ์ฉ๋
์ฃผ๋ก DAO (๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฒ๋ฆฌํ๋ ํํธ) ์์ ์ฌ์ฉ
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BoardVO {
private Integer no;
private String title; // ์ ๋ชฉ
private String content; // ๋ด์ฉ
private String writer; // ์์ฑ์
private String passwd; // ๋น๋ฐ๋ฒํธ
private LocalDate addDate; // ์์ฑ์ผ
private Integer hit; // ์กฐํ์
}
3. VO๋ฅผ ์ด์ฉํด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๋ ํ ์คํธ ์ฝ๋ ์์ฑ
์์ฑ ์์
1) BoardMapper ์ธํฐํ์ด์ค ์์ฑ - ๊ฐ์ฒด ๋ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ, ์์ , ์ญ์ ๋ฅผ ์คํํ๋ ๋ฉ์๋ ์์ฑ
2) BoardMapperxml ์์ฑ - ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ๋ฌธ ์์ฑ
3) BoardMapperTest ํด๋์ค ์์ฑ - ์คํ ํ ์คํธ
public interface BoardMapper {
void insert(BoardVO boardVO); // DB ์ ์ฅํ๋ ๋ฉ์๋
}
<mapper namespace="com.example.spring_ex_01_2404.mapper.BoardMapper">
<insert id="insert">
INSERT INTO tbl_board (title, content, writer, passwd, addDate)
VALUES (#{title}, #{content}, #{writer}, SHA2(#{passwd},256), now())
</insert>
</mapper>
โ mysql๋ก ๋น๋ฐ๋ฒํธ ์ํธํ ํ ๊ฒฝ์ฐ SHA2(์ ๋ ฅ๊ฐ, 256) ํจ์ ์ฌ์ฉ
โ addDate๋ ๊ธ ์์ฑ ์๊ฐ์ด ๋ค์ด๊ฐ๋ now() ํจ์๋ฅผ ์ฌ์ฉ
โ hit๋ ์์ฑ์์๋ 0์ด๋ ์ปฌ๋ผ์ ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉ
@Log4j2
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
class BoardMapperTest {
@Autowired(required = false)
private BoardMapper boardMapper;
@Test
public void testInsert() {
BoardVO boardVO = BoardVO.builder() // ๋น๋๋ฅผ ์ด์ฉํด์ BoardVO ๊ฐ์ฒด๋ฅผ ์์ฑ
.title("์คํ๋ง ํ
์คํธ")
.content("์คํ๋ง ํ
์คํธ ์ค...")
.writer("river")
.passwd("1234")
.addDate(now())
.build();
boardMapper.insert(boardVO);
}
}
02:34:06 DEBUG [com.example.spring_ex_01_2404.mapper.BoardMapper.insert] ==> Preparing: INSERT INTO tbl_board (title, content, writer, passwd, addDate) VALUES (?, ?, ?, SHA2(?,256), now())
02:34:06 DEBUG [com.example.spring_ex_01_2404.mapper.BoardMapper.insert] ==> Parameters: ์คํ๋ง ํ ์คํธ(String), ์คํ๋ง ํ ์คํธ ์ค...(String), river(String), 1234(String)
02:34:06 DEBUG [com.example.spring_ex_01_2404.mapper.BoardMapper.insert] <== Updates: 1
4. Service ์์
BoardDTO → BoardService → BoardServiceImpl → ํ ์คํธ ์ฝ๋
@ToString
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BoardDTO {
private Integer no;
private String title;
private String content;
private String writer;
private String passwd;
private LocalDate addDate;
private Integer hit;
}
public interface BoardService {
void register(BoardDTO boardDTO);
}
@Log4j2
@Service
@RequiredArgsConstructor // ์์ฑ์ ๊ฐ์ฒด ์ฃผ์
. private final๋ก ์ ์ธ๋ ์ฐธ์กฐ๋ณ์์ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๋ ์์ฑ์ ์์ฑ.
public class BoardServiceImpl implements BoardService{
private final BoardMapper boardMapper;
private final ModelMapper modelMapper;
@Override
public void register(BoardDTO boardDTO) {
log.info(boardDTO);
BoardVO boardVO = modelMapper.map(boardDTO, BoardVO.class);
log.info(boardVO);
boardMapper.insert(boardVO);
}
}
@Log4j2
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
class BoardServiceImplTest {
@Autowired
private BoardService boardService;
@Test
public void testRegister() {
BoardDTO boardDTO = BoardDTO.builder()
.title("service test")
.content("service test...")
.writer("user")
.passwd("12345")
.addDate(LocalDate.now())
.build();
boardService.register(boardDTO);
}
}
03:23:33 INFO [com.example.spring_ex_01_2404.service.BoardServiceImpl] BoardDTO(no=null, title=service test, content=service test..., writer=user, passwd=12345, addDate=2024-05-01, hit=null)
03:23:33 INFO [com.example.spring_ex_01_2404.service.BoardServiceImpl] BoardVO(no=null, title=service test, content=service test..., writer=user, passwd=12345, addDate=2024-05-01, hit=null)
03:23:33 DEBUG [com.example.spring_ex_01_2404.mapper.BoardMapper.insert] ==> Preparing: INSERT INTO tbl_board (title, content, writer, passwd, addDate) VALUES (?, ?, ?, SHA2(?,256), now())
03:23:33 DEBUG [com.example.spring_ex_01_2404.mapper.BoardMapper.insert] ==> Parameters: service test(String), service test...(String), user(String), 12345(String)
03:23:33 DEBUG [com.example.spring_ex_01_2404.mapper.BoardMapper.insert] <== Updates: 1
5. ๋ฑ๋กํ์ด์ง ์์ฑ
- /board/add๋ก ์ ๊ทผํ ๊ฒฝ์ฐ ๋ฑ๋ก ํ์ด์ง ์ถ๋ ฅ
- ๋ฑ๋กํ์ด์ง์์ ์ ๋ ฅ ํ submit์ ํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ธ์ด ๋ฑ๋ก
์์ ์์
- BoardController ์์ฑ
- get์ผ๋ก /board/add์ ์ ๊ทผํ ๊ฒฝ์ฐ ์คํํ ๋ฉ์๋ ์์ฑ
- /board/add๋ก ์ ์์ ์ผ๋ก ์ ๊ทผ๋๋์ง ํ์ธ
- webapp์ add.jsp ์ฝ๋ฉ
- controller์ post๋ก /board/add์ ์ ๊ทผํ ๊ฒฝ์ฐ ์คํํ ๋ฉ์๋ ์์ฑ
- ํ ์คํธ ํ ์ ๋ ฅํ ๋ฐ์ดํฐ๊ฐ ์ ๋๋ก ์ ๋ฌ๋๋์ง ํ์ธ
- addPost()๋ฅผ ์์ ํด์ ์๋น์ค๋ก ์ฐ๊ฒฐ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ ํ์ธ
- addPost()๋ฅผ ์์ ํด์ /board/list ์ด๋ ํ์ธ
@Log4j2
@Controller
@RequestMapping("/board")
@RequiredArgsConstructor
public class BoardController {
private final BoardService boardService;
@GetMapping("/add")
public void add() {
log.info("add board...");
}
@PostMapping("/add")
public String addPost(@Valid BoardDTO boardDTO, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
log.info("board addPost()...");
if (bindingResult.hasErrors()) {
log.info("has error...");
redirectAttributes.addFlashAttribute("errors", bindingResult.getAllErrors());
return "redirect:/board/add";
}
log.info(boardDTO);
boardService.register(boardDTO);
return "redirect:/board/list";
}
}
<form method="post">
<div class="input mb-3">
<span class="input-group-text">Title</span><br>
<input type="text" name="title" placeholder="Title"><br><br>
</div>
<div class="input mb-3">
<span class="input-group-text">Content</span><br>
<textarea name="content" cols="60" rows="18"></textarea><br><br>
</div>
<div class="input mb-3">
<span class="input-group-text">Writer</span><br>
<input type="text" name="writer" placeholder="Writer"><br><br>
</div>
<div class="input mb-3">
<span class="input-group-text">Password</span><br>
<input type="password" name="passwd" placeholder="๋น๋ฐ๋ฒํธ"><br><br>
</div>
<div class="float-end">
<button type="submit" name="submitBtn">Submit</button>
</div>
</form>
6. ๋ชฉ๋กํ์ด์ง
- DAO์ ๋ชฉ๋ก์ ๋ถ๋ฌ์ค๋ selectAll() ๋ฉ์๋ ์์ฑ
- ํ ์คํธ ์ฝ๋ ์์ฑ ํ ํ ์คํธ
- Service ์์
- ํ ์คํธ ์ฝ๋ ์์ฑ ํ ํ ์คํธ
- ์ปจํธ๋กค๋ฌ์ /board/list ๊ฒฝ๋ก์ ๋งคํ์ ํ list() ์์ฑ
- list.jsp ์์ฑ
public interface BoardMapper {
void insert(BoardVO boardVO); // DB ์ ์ฅํ๋ ๋ฉ์๋
List<BoardVO> selectAll(); // DB์ ์ ์ฅ๋์ด ์๋ ๋ฆฌ์คํธ ๋ชฉ๋ก
}
<select id="selectAll" resultType="com.example.spring_ex_01_2404.domain.BoardVO">
SELECT * FROM tbl_board ORDER BY no DESC
</select>
@Test
public void selectAll() {
List<BoardVO> boardVOList = boardMapper.selectAll();
for(BoardVO boardVO : boardVOList) {
log.info(boardVO);
}
}
public interface BoardService {
void register(BoardDTO boardDTO);
List<BoardDTO> getAll();
}
@Override
public List<BoardDTO> getAll() {
List<BoardVO> voList = boardMapper.selectAll(); // dao์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ค๊ณ ์จ VO๋ฆฌ์คํธ๋ฅผ ๋ฆฌํด
List<BoardDTO> dtoList = new ArrayList<>();
for (BoardVO boardVO: voList) {
// ๊ฐ๋ณ VO๋ฅผ DTO๋ก ๋ณํ.
BoardDTO boardDTO = modelMapper.map(boardVO, BoardDTO.class);
dtoList.add(boardDTO); // DTO๋ฆฌ์คํธ์ ์ ์ฅ.
}
return dtoList;
}
@Test
public void getAll() {
List<BoardDTO> boardDTOList = boardService.getAll();
for (BoardDTO boardDTO : boardDTOList) {
log.info(boardDTO);
}
}
@RequestMapping("/list")
public void list(Model model) {
log.info("todo list...");
model.addAttribute("dtoList", boardService.getAll());
}
<div class="card">
<div class="card-header">Board List</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th scope="col">No</th>
<th scope="col">Title</th>
<th scope="col">Writer</th>
<th scope="col">Date</th>
</tr>
</thead>
<tbody>
<c:forEach var="dto" items="${dtoList}">
<tr>
<th scope="row">${dto.no}</th>
<td>${dto.title}</td>
<td>${dto.writer}</td>
<td>${dto.addDate}</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
7. ์กฐํ
๋ฆฌ์คํธ์์ ํด๋ฆญ์ ํ๋ฉด ํด๋น ๊ธ์ ๋ณด์ฌ์ฃผ๋ ํ์ด์ง ๊ตฌํ
- DAO์ ๊ฒ์๋ฌผ์ ๋ถ๋ฌ์ค๋ selectOne() ๋ฉ์๋ ์์ฑ
- ํ ์คํธ ์ฝ๋ ์์ฑ ํ ํ ์คํธ
- Service ์์
- ์ปจํธ๋กค๋ฌ์ /board/read ๊ฒฝ๋ก์ ๋งคํ์ ํ read() ์์ฑ
- read.jsp ์์ฑ
- updatehit ์ถ๊ฐ (์กฐํ์ ์ฆ๊ฐ)
public interface BoardMapper {
void insert(BoardVO boardVO); // DB ์ ์ฅํ๋ ๋ฉ์๋
List<BoardVO> selectAll();
BoardVO selectOne(Integer no); // ์กฐํ
void updateHit(Integer no); // ์กฐํ์ ์ฆ๊ฐ
}
<select id="selectOne" resultType="com.example.spring_ex_01_2404.domain.BoardVO">
SELECT * FROM tbl_board WHERE no = #{no}
</select>
<update id="updateHit">
UPDATE tbl_board SET hit = hit + 1 where no = #{no}
</update>
@Test
public void selectOne() {
BoardVO boardVO = boardMapper.selectOne(1);
log.info(boardVO);
}
11:46:50 DEBUG [com.example.spring_ex_01_2404.mapper.BoardMapper.selectOne] ==> Preparing: SELECT * FROM tbl_board WHERE no = ?
11:46:50 DEBUG [com.example.spring_ex_01_2404.mapper.BoardMapper.selectOne] ==> Parameters: 1(Integer)
11:46:50 TRACE [com.example.spring_ex_01_2404.mapper.BoardMapper.selectOne] <== Columns: no, title, content, writer, passwd, addDate, hit
11:46:50 TRACE [com.example.spring_ex_01_2404.mapper.BoardMapper.selectOne] <== Row: 1, ์คํ๋ง ํ ์คํธ, <<BLOB>>, river, 1234, 2024-05-01 00:00:00, 1
11:46:50 DEBUG [com.example.spring_ex_01_2404.mapper.BoardMapper.selectOne] <== Total: 1
public interface BoardService {
void register(BoardDTO boardDTO);
List<BoardDTO> getAll();
BoardDTO getOne(Integer no);
}
@Override
public BoardDTO getOne(Integer no) {
boardMapper.updateHit(no);
BoardVO boardVO = boardMapper.selectOne(no);
BoardDTO boardDTO = modelMapper.map(boardVO, BoardDTO.class);
return boardDTO;
}
@GetMapping("/read")
public void read(Integer no, Model model) {
BoardDTO boardDTO = boardService.getOne(no);
log.info(boardDTO);
model.addAttribute("dto", boardDTO);
}
<div class="card-body">
<div class="input-group">
<span class="input-group-text">No</span>
<input type="text" name="tno" class="form-control" value="${dto.no}" readonly>
</div> <br>
<div class="input-group">
<span class="input-group-text">Title</span>
<input type="text" name="title" class="form-control" value="${dto.title}" readonly>
</div><br>
<div class="input-group">
<span class="input-group-text">Content</span>
<textarea name="title" class="form-control" readonly>${dto.content}</textarea>
</div><br>
<div class="input-group">
<span class="input-group-text">Writer</span>
<input type="text" name="writer" class="form-control" value="${dto.writer}" readonly>
</div><br>
<div class="input-group">
<span class="input-group-text">Date</span>
<input type="date" name="dueDate" class="form-control" value="${dto.addDate}" readonly>
</div><br>
<div class="input-group">
<span class="input-group-text">Hit</span>
<input type="text" name="writer" class="form-control" value="${dto.hit}" readonly>
</div><br>
<div class="my-4">
<div class="float-end">
<button type="submit" class="btn btn-primary">Modify</button>
<button type="reset" class="btn btn-secondary">List</button>
</div>
</div>
<script>
document.querySelector('.btn-primary').addEventListener('click', function (e) {
self.location = `/board/modify?no=${dto.no}&${boardDTO.link}`;
}, false);
document.querySelector('.btn-secondary').addEventListener('click', function (e) {
self.location = "/board/list?${boardDTO.link}";
}, false);
</script>
</div>
[ ๋ด์ฉ ์ฐธ๊ณ : IT ํ์ ๊ฐ์ ]
'Spring & Spring Boot' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring Boot] ์คํ๋ง ๋ถํธ ๊ฐ๋ ยท ํ๋ก์ ํธ ์คํ ยท ๊ธฐ๋ณธ ์ค์ (0) | 2024.05.09 |
---|---|
[Spring] ๊ฐ๋จํ ๊ฒ์ํ ๋ง๋ค๊ธฐ | ์์ ยท ์ญ์ ยท ๋น๋ฐ๋ฒํธ ํ์ธ (0) | 2024.05.03 |
[Spring] ๊ฒ์ ์กฐ๊ฑด์ ์ํ ํ๋ฉด ์ฒ๋ฆฌ (0) | 2024.04.30 |
[Spring] ๊ฒ์&ํํฐ๋ง (0) | 2024.04.29 |
[Spring] ๋ชฉ๋ก ๋ฐ์ดํฐ๋ฅผ ์ํ DTO์ ์๋น์ค ๊ณ์ธต (0) | 2024.04.29 |