Spring/코드로 배우는 스프링부트 웹 프로젝트

코드로 배우는 스프링부트 웹 프로젝트 Day 15

한 면만 쓴 종이 2022. 9. 19. 09:56

/* 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);

    }
}

Board 엔티티에 데이터가 추가된 것을 확인

 

 

[ 게시물 목록 처리 ]

 

게시물의 목록 처리는 이전 장에서 만들었던 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엔티티, 댓글의 수를 가져온다.