💡 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 |