코드로 배우는 스프링부트 웹 프로젝트 Day 15
/* Annotation들은 스프링부트 프로젝트의 Annotation 정리 페이지에 따로 정리해두었습니다. */
[ 게시물 등록 ]
게시물 등록은 BoardDTO 타입을 파라미터로 전달받고, 생성된 게시물의 번호를 반환하도록 작성
실제 처리 과정에서 DTO를 엔티티 타입으로 변환할 필요가 있음 => BoardService 인터페이스에 dtoToEntity()를 작성해서 처리
BoardService 인터페이스
public interface BoardService {
Long register(BoardDTO dto);
default Board dtoToEntity(BoardDTO dto) {
Member member = Member.builder().email(dto.getWriterEmail()).build();
Board board = Board.builder()
.bno(dto.getBno())
.title(dto.getTitle())
.content(dto.getContent())
.writer(member)
.build();
return board;
}
}
dtoToEntity() 는 DTO가 연관관계를 가진 Board엔티티 객체와 Member 엔티티 객체를 구성해야하기 때문에 내부적으로 Member 엔티티 객체를 처리하는 과정을 거쳐야 함
BoardServiceImpl 클래스
package org.zerock.board2.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.zerock.board2.dto.BoardDTO;
import org.zerock.board2.entity.Board;
import org.zerock.board2.repository.BoardRepository;
@Service
@RequiredArgsConstructor
@Log4j2
public class BoardServiceImpl implements BoardService{
private final BoardRepository repository; // 자동 주입 final
@Override
public Long register(BoardDTO dto) {
log.info(dto);
Board board = dtoToEntity(dto);
repository.save(board);
return board.getBno();
}
}
BoardDTO를 dtoToEntity()로 Board 엔티티 객체로 만들어 BoardRepository에 save() 한다.
test 폴더에 service 패키지를 생성하고 BoardServiceTests 클래스 생성
BoardServiceTests 클래스
package org.zerock.board2.service;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.zerock.board2.dto.BoardDTO;
@SpringBootTest
public class BoardServiceTests {
@Autowired
private BoardService boardService;
@Test
public void testRegister() {
BoardDTO dto = BoardDTO.builder()
.title("Test.")
.content("Test...")
.writerEmail("user55@aaa.com") // 현재 데이터베이스에 존재하는 회원 이메일
.build();
Long bno = boardService.register(dto);
}
}
[ 게시물 목록 처리 ]
게시물의 목록 처리는 이전 장에서 만들었던 PageRequestDTO와 PageResultDTO를 그대로 사용함
[ Object[] 을 DTO로 변환하기 ]
PageRequestDTO 클래스
package org.zerock.board2.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@Builder
@AllArgsConstructor
@Data
public class PageRequestDTO {
private int page;
private int size;
private String type;
private String keyword;
public PageRequestDTO() {
this.page = 1;
this.size = 10;
}
public Pageable getPageable(Sort sort) {
return PageRequest.of(page -1, size, sort);
}
}
PageResultDTO 클래스
package org.zerock.board2.dto;
import lombok.Data;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Data
public class PageResultDTO<DTO, EN> {
//DTO리스트
private List<DTO> dtoList;
//총 페이지 번호
private int totalPage;
//현재 페이지 번호
private int page;
//목록 사이즈
private int size;
//시작 페이지 번호, 끝 페이지 번호
private int start, end;
//이전, 다음
private boolean prev, next;
//페이지 번호 목록
private List<Integer> pageList;
public PageResultDTO(Page<EN> result, Function<EN, DTO> fn) {
dtoList = result.stream().map(fn).collect(Collectors.toList());
totalPage = result.getTotalPages();
makePageList(result.getPageable());
}
private void makePageList(Pageable pageable) {
this.page = pageable.getPageNumber() + 1; // 0 부터 시작하므로 1 추가
this.size = pageable.getPageSize();
//temp end page
int tempEnd = (int)(Math.ceil(page/10.0)) * 10;
start = tempEnd - 9;
prev = start > 1;
end = totalPage > tempEnd ? tempEnd: totalPage;
next = totalPage > tempEnd;
pageList = IntStream.rangeClosed(start, end).boxed().collect(Collectors.toList());
}
}
PageResultDTO의 핵심은 JPQL의 결과로 나오는 Object[]을 DTO타입으로 변환하는 기능
이 기능은 java.util.Function을 이용해서 작성함
이때, JPQL의 실행 결과로 나오는 Object[]을 BoardDTO로 처리해주어야 함
*java.util.function.Function
function패키지의 Function 인터페이스
Function<T, R>으로 정의되어 있어, Generic 타입을 두 개 갖고있음
T는 입력 타입, R은 리턴 타입을 의미
즉, 타입 변환을 할 때 주로 사용
Object[]의 내용은 Board와 Member, 댓글의 수는 Long 타입으로 나오게 되므로 이를 파라미터로 전달 받아서 BoardDTO를 구성하도록 작성해야 함
이 기능은 BoardService 인터페이스에 entityToDTO()라는 메서드를 작성해서 처리
BoardService 인터페이스 추가
//BoardService 인터페이스에 추가하는 entityToDTO()
default BoardDTO entityToDTO(Board board, Member member, Long replyCount) {
BoardDTO boardDTO = BoardDTO.builder()
.bno(board.getBno())
.title(board.getTitle())
.content(board.getContent())
.regDate(board.getRegDate())
.modDate(board.getModDate())
.writerEmail(member.getEmail())
.replyCount(replyCount.intValue()) //long으로 나오므로 int로 처리하도록 함
.build();
return boardDTO;
}
default 접근 제어자를 사용하여 동일한 패키지 내에서만 접근이 가능한 entityToDTO 메소드를 만듦
BookService 인터페이스에 목록 처리를 하는 getList() 메소드 선언
PageResultDTO<BoardDTO, Object[]> getList(PageRequestDTO pageRequestDTO); //목록 처리
BookServiceImpl 클래스 추가
@Override
public PageResultDTO<BoardDTO, Object[]> getList(PageRequestDTO pageRequestDTO) {
log.info(pageRequestDTO);
Function<Object[], BoardDTO> fn = (en -> entityToDTO((Board) en[0], (Member) en[1], (Long) en[2]));
Page<Object[]> result = repository.getBoardWithReplyCount(
pageRequestDTO.getPageable(Sort.by("bno").descending()));
return new PageResultDTO<>(result, fn);
}
getList() 메소드를 만들어 entityToDTO()를 이용하여 PageResultDTO 객체를 만들어 리턴하도록 했음
[ 목록 처리 테스트 ]
getList() 가 정상적으로 작동하는지 확인하는 test 코드를 만들 것이다.
BoardSericeTests 클래스 추가
@Test
public void testList() {
PageRequestDTO pageRequestDTO = new PageRequestDTO();
PageResultDTO<BoardDTO, Object[]> result = boardService.getList(pageRequestDTO);
for (BoardDTO boardDTO : result.getDtoList()) {
System.out.println(boardDTO);
}
}
위 테스트 코드를 실행시킨 결과, 목록의 한 화면에 필요한 10개의 BoardDTO 객체가 정상적으로 만들어진 것을 확인할 수 있음
[ 게시물 조회 처리 ]
게시물의 조회는 파라미터로 게시물의 번호(bno)를 파라미터로 받아서 처리하도록 BoardService와 BoardServiceImpl 클래스에 get() 메서드를 추가해서 작성
BoardService 인터페이스 추가
public interface BoardService {
Long register(BoardDTO dto);
PageResultDTO<BoardDTO, Object[]> getList(PageRequestDTO pageRequestDTO); //목록 처리
BoardDTO get(Long bno);
BoardServiceImpl 클래스 추가
@Override
public BoardDTO get(Long bno) {
Object result = repository.getBoardByBno(bno);
Object[] arr = (Object[])result;
return entityToDTO((Board) arr[0], (Member) arr[1], (Long) arr[2]);
}
getBoardByBno()를 이용해서 Board 엔티티, Member엔티티, 댓글의 수를 가져온다.