SpringBoot with JPA 프로젝트(N:1) 4.DTO,Service,게시물등록,페이지처리

mingki·2022년 2월 16일
0

SpringBoot & JPA

목록 보기
14/26
post-thumbnail

📚 공부한 책 : 코드로배우는 스프링 부트 웹프로젝트
❤️ github 주소 : https://github.com/qkralswl689/LearnFromCode/tree/main/board2022

1.DTO 작성

DTO를 구성하는 기준은 화면에 전달하는 데이터이거나 화면쪽에서 전달되는 데이터를 기준으로 하기때문에 Entity클래스의 구성과 일치하지 않는 경우가 많다.

1-1.BoardDTO 작성

BoardDTO의 경우 Member에 대한 참조는 구성하지 않고 작성한다

  • BoardDTO 클래스와 Board 엔티티 클래스와 다른점 : Member를 참조하는 대신 화면에서 필요한 작성자의 이메일과 이름으로 처리하고 있는 점
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BoardDTO {

    private Long bno;

    private String titel;

    private String content;

    private String writerEmail; // 작성자의 이메일(id)

    private String writerName; // 작성자의 이름

    private LocalDateTime regDate;

    private LocalDateTime modDate;

    private int replyCount; // 해당 게시글의 댓글 수

}

2.게시물등록 구현

BoardDTO타입을 파라미터로 전달받고 생성된 게시물의 버호를 반환하도록 작성한다

2-1.BoardService Interface 작성

BoardDTO를 Board 엔티티 타입으로 변환을 위해 dtoToEntity()를 작성한다

  • dtoToEntity()는 DTO가 연관관계를 가진 Board 엔티티 객체와 Member 엔티티 객체를 구성해야 하므로 내부적으로 Member 엔티티를 처리하는 과정을 거쳐야 한다
import com.example.board2022.dto.BoardDTO;
import com.example.board2022.entity.Board;
import com.example.board2022.entity.Member;

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.getTitel())
                .content(dto.getContent())
                .writer(member)
                .build();

        return board;
    }
    
}

2-2.BoardServiceImple 작성

import com.example.board2022.dto.BoardDTO
import com.example.board2022.entity.Board;
import com.example.board2022.entity.Member;
import com.example.board2022.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
@RequiredArgsConstructor
public class BoardServiceImpl implements BoardService{

    @Autowired
    private final BoardRepository repository; //자동주입 final

    @Override
    public Long register(BoardDTO dto) {

        Board board = dtoToEntity(dto);

        repository.save(board);

        return board.getBno();
    }
}

2-3.register() 테스트코드 작성

import com.example.board2022.dto.BoardDTO;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class BoardServiceTests {

    @Autowired
    private BoardService boardService;

    @Test
    public void testRegister(){

        BoardDTO dto = BoardDTO.builder()
                .titel("Test...")
                .content("Test....")
                .writerEmail("user55@aaa.com") // 현재 DB에 존재하는 회원이메일
                .build();

        Long bno = boardService.register(dto);
    }
}
  • 실행 쿼리
Hibernate: 
    select
        member_.email,
        member_.moddate as moddate2_1_,
        member_.name as name4_1_,
        member_.password as password5_1_ 
    from
        member member_ 
    where
        member_.email=?
Hibernate: 
    insert 
    into
        board
        (moddate, regdate, content, title, writer_email) 
    values
        (?, ?, ?, ?, ?)
  • 실행결과

3.게시물 목록(페이지) 처리

3-1.PageRequestDTO 작성

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;

// 화면에서 전달되는 목록 관련된 데이터에 대한 DTO를 PageRequestDTO 라는 이름으로 생성
// 목록 페이지를 요청할 때 사용하는 데이터를 재사용하기 쉽게 만든다
// -> 파라미터를 DTO로 선언하고 나중에 재사용하는 용도
@Builder
@AllArgsConstructor
@Data
public class PageRequestDTO { // 목적 : JPA쪽에서 사용하는 Pageable 타입의 객체를 생성하는 것
    // 화면에서 절달되는 page,size 라는 파라미터를 수집한다
    private int page;
    private int size;

    // 검색 처리를 위해 추가
    private String type;
    private String keyword;


    public PageRequestDTO(){
        // 페이비 번호 등은 기본값을 가지는것이 좋기때문에 1과10이라는 값을 지정한다
        this.page = 1;
        this.size = 10;
    }

    public Pageable getPageable(Sort sort){
                            // 페이지 번호가 0부터 시작한다는 점을 감안해 1페이지의 경우 0이 될수 있도록 page -1로 작성해준다
                            // 정렬은 다양한 상황에서 쓰기위해 별도의 파라미터로 받도록 설계
        return PageRequest.of(page -1, size,sort);
    }
}

3-2.PageResultDTO 작성

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;

// 화면에서 필요한 결과는 PageResultDTO라는 이름으로 생성한다
@Data
            // 다양한 곳에서 사용할 수 있도록 제네릭 타입을 이용해 DTO 와 EN(entity) 이라는 타입을 지정한다
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;

                                           // Function<EN,DTO> : 엔티티 객체들을 DTO로 변환해주는 기능
    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());
    }

}

4.Object[]를 DTO로 변환하기

PageResultDTO의 핵심은 JPQL의 결과로 나오는 Object[]를 DTO 타입으로 변화하는 기능이다.

  • Object[]의 내용은 Board와 Member, 댓글수는 Long 타입으로 나오게 되므로 이것을 파라미터로 전달받아 BoardDTO를 구성하도록 작성해야 한다.

4-1.Service Interface 작성

entityToDTO()는 총 3개의 파라미터를 처리할 수 있도록 구성한다
-> Board 엔티티 객체와 Member 엔티티 객체, 댓글의 수를 파라미터로 전달받도록 구성하고, 이것들을 이용해 BoardDTO 객체를 생성할 수 있도록 처리한다

import com.example.board2022.dto.BoardDTO;
import com.example.board2022.dto.PageRequestDTO;
import com.example.board2022.dto.PageResultDTO;
import com.example.board2022.entity.Board;
import com.example.board2022.entity.Member;

public interface BoardService {

    PageResultDTO<BoardDTO, Object[]> getList(PageRequestDTO pageRequestDTO); // 목록처리
    
    default BoardDTO entityToDTO(Board board,Member member,Long replyCount){

        BoardDTO boardDTO = BoardDTO.builder()
                .bno(board.getBno())
                .titel(board.getTitle())
                .content(board.getContent())
                .regDate(board.getRegDate())
                .modDate(board.getModDate())
                .writerEmail(member.getEmail())
                .writerName(member.getName())
                .replyCount(replyCount.intValue()) // long으로 나오므로 int로 처리
                .build();

        return boardDTO;
    }
}

4-2.ServiceImpl 작성

getList()의 핵심 : entityToDTO()를 이용해 PageResultDTO 객체를 구성하는 부분이다


import com.example.board2022.dto.BoardDTO;
import com.example.board2022.dto.PageRequestDTO;
import com.example.board2022.dto.PageResultDTO;
import com.example.board2022.entity.Board;
import com.example.board2022.entity.Member;
import com.example.board2022.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.function.Function;

@Service
@RequiredArgsConstructor
public class BoardServiceImpl implements BoardService{

    @Autowired
    private final BoardRepository repository; //자동주입 final

    @Override
    public PageResultDTO<BoardDTO, Object[]> getList(PageRequestDTO 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);
    }
}

4-3.getList() 테스트

BoardDTO 객체 내에 목록 화면에 필요한 10개의 BoardDTO 객체가 만들어지고 필요한 모든 내용이 담겨져 있는것을 확인할 수 있다

import com.example.board2022.dto.BoardDTO;
import com.example.board2022.dto.PageRequestDTO;
import com.example.board2022.dto.PageResultDTO;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class BoardServiceTests {

    @Autowired
    private BoardService boardService;

    @Test
    public void testList(){

        PageRequestDTO pageRequestDTO = new PageRequestDTO();

        PageResultDTO<BoardDTO, Object[]> result = boardService.getList(pageRequestDTO);

        for (BoardDTO boardDTO : result.getDtoList()) {
            System.out.println(boardDTO);
        }
    }
}
  • 실행 쿼리
Hibernate: 
    select
        board0_.bno as col_0_0_,
        member1_.email as col_1_0_,
        count(reply2_.rno) as col_2_0_,
        board0_.bno as bno1_0_0_,
        member1_.email as email1_1_1_,
        board0_.moddate as moddate2_0_0_,
        board0_.regdate as regdate3_0_0_,
        board0_.content as content4_0_0_,
        board0_.title as title5_0_0_,
        board0_.writer_email as writer_e6_0_0_,
        member1_.moddate as moddate2_1_1_,
        member1_.regdate as regdate3_1_1_,
        member1_.name as name4_1_1_,
        member1_.password as password5_1_1_ 
    from
        board board0_ 
    left outer join
        member member1_ 
            on board0_.writer_email=member1_.email 
    left outer join
        reply reply2_ 
            on (
                reply2_.board_bno=board0_.bno
            ) 
    group by
        board0_.bno 
    order by
        board0_.bno desc limit ?
Hibernate: 
    select
        count(board0_.bno) as col_0_0_ 
    from
        board board0_
BoardDTO(bno=101, titel=Test..., content=Test...., writerEmail=user55@aaa.com, writerName=USER55, regDate=2022-02-15T19:17:31.876659, modDate=2022-02-15T19:17:31.876659, replyCount=0)
BoardDTO(bno=100, titel=Title...100, content=Content...100, writerEmail=user100@aaa.com, writerName=USER100, regDate=2022-02-03T22:59:09.783053, modDate=2022-02-03T22:59:09.783053, replyCount=4)
BoardDTO(bno=99, titel=Title...99, content=Content...99, writerEmail=user99@aaa.com, writerName=USER99, regDate=2022-02-03T22:59:09.767433, modDate=2022-02-03T22:59:09.767433, replyCount=3)
BoardDTO(bno=98, titel=Title...98, content=Content...98, writerEmail=user98@aaa.com, writerName=USER98, regDate=2022-02-03T22:59:09.761206, modDate=2022-02-03T22:59:09.761206, replyCount=3)
BoardDTO(bno=97, titel=Title...97, content=Content...97, writerEmail=user97@aaa.com, writerName=USER97, regDate=2022-02-03T22:59:09.754006, modDate=2022-02-03T22:59:09.754006, replyCount=5)
BoardDTO(bno=96, titel=Title...96, content=Content...96, writerEmail=user96@aaa.com, writerName=USER96, regDate=2022-02-03T22:59:09.749046, modDate=2022-02-03T22:59:09.749046, replyCount=2)
BoardDTO(bno=95, titel=Title...95, content=Content...95, writerEmail=user95@aaa.com, writerName=USER95, regDate=2022-02-03T22:59:09.740988, modDate=2022-02-03T22:59:09.740988, replyCount=2)
BoardDTO(bno=94, titel=Title...94, content=Content...94, writerEmail=user94@aaa.com, writerName=USER94, regDate=2022-02-03T22:59:09.733527, modDate=2022-02-03T22:59:09.733527, replyCount=4)
BoardDTO(bno=93, titel=Title...93, content=Content...93, writerEmail=user93@aaa.com, writerName=USER93, regDate=2022-02-03T22:59:09.728620, modDate=2022-02-03T22:59:09.728620, replyCount=3)
BoardDTO(bno=92, titel=Title...92, content=Content...92, writerEmail=user92@aaa.com, writerName=USER92, regDate=2022-02-03T22:59:09.725467, modDate=2022-02-03T22:59:09.725467, replyCount=3)

5.게시물 조회

게시물 조회는 파라미터로 게시물의 번호(bno)를 파라미터로 받아 처리한다

5-1.Service Interface 작성

import com.example.board2022.dto.BoardDTO;

public interface BoardService {

	// 생략..
    
    BoardDTO get(Long bno);
    
    // 생략..

}

5-2.ServiceImpl 작성

BoardRepository의 getBoardByBno()를 이용해 처리한다

  • getBoardByBno() : Board , Member 엔티티, 댓글의 수를 가져온다

import com.example.board2022.dto.BoardDTO;
import com.example.board2022.entity.Board;
import com.example.board2022.entity.Member;
import com.example.board2022.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;

@Service
@RequiredArgsConstructor
public class BoardServiceImpl implements BoardService{

    @Autowired
    private final BoardRepository repository; //자동주입 final


    @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]);
    }
}

5-3.get() 테스트

import com.example.board2022.dto.BoardDTO;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class BoardServiceTests {

    @Autowired
    private BoardService boardService;
 
    @Test
    public void testGet(){

        Long bno = 100L;

        BoardDTO boardDTO = boardService.get(bno);

        System.out.println(boardDTO);
    }
}
  • 실행 쿼리
Hibernate: 
    select
        board0_.bno as col_0_0_,
        member1_.email as col_1_0_,
        count(reply2_.rno) as col_2_0_,
        board0_.bno as bno1_0_0_,
        member1_.email as email1_1_1_,
        board0_.moddate as moddate2_0_0_,
        board0_.regdate as regdate3_0_0_,
        board0_.content as content4_0_0_,
        board0_.title as title5_0_0_,
        board0_.writer_email as writer_e6_0_0_,
        member1_.moddate as moddate2_1_1_,
        member1_.regdate as regdate3_1_1_,
        member1_.name as name4_1_1_,
        member1_.password as password5_1_1_ 
    from
        board board0_ 
    left outer join
        member member1_ 
            on board0_.writer_email=member1_.email 
    left outer join
        reply reply2_ 
            on (
                reply2_.board_bno=board0_.bno
            ) 
    where
        board0_.bno=?
BoardDTO(bno=100, titel=Title...100, content=Content...100, writerEmail=user100@aaa.com, writerName=USER100, regDate=2022-02-03T22:59:09.783053, modDate=2022-02-03T22:59:09.783053, replyCount=4)
profile
비전공초보개발자

0개의 댓글