ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 코드로 배우는 스프링부트 웹 프로젝트 Day 6
    Spring/코드로 배우는 스프링부트 웹 프로젝트 2022. 8. 18. 16:44

    /* Annotation들은 스프링부트 프로젝트의 Annotation 정리 페이지에 따로 정리해두었습니다. */

     

    [서비스계층의 테스트]

    Day 5에서 만든 서비스계층을 테스트 작업을 통해 검사하고 이후에 컨트롤러와 연동시키는 것이 바람직함

     

    service/GuestbookServiceTests

    package org.zerock.guestbook.service;
    
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.zerock.guestbook.dto.GuestbookDTO;
    @SpringBootTest
    public class GuestbookServiceTests {
        
        @Autowired
        private GuestbookService service;
        
        @Test
        public void testRegister() {
    
            GuestbookDTO guestbookDTO = GuestbookDTO.builder()
                    .title("Sample Title...")
                    .content("Sample Content...")
                    .writer("user0")
                    .build();
            System.out.println(service.register(guestbookDTO));
        }
    }

    GuestbookDTO를 Guestbook엔티티로 변환한 결과 확인

     

    GuestbookServiceImpl 클래스

    @Service
    @Log4j2
    @RequiredArgsConstructor // 의존성 자동 주입
    public class GuestbookServiceImpl implements GuestbookService {
        
        private final GuestbookRepository repository;
    
        @Override
        public Long register(GuestbookDTO dto) {
    
            log.info("DTO----------------------");
            log.info(dto);
    
            Guestbook entity = dtoToEntity(dto);
    
            log.info(entity);
            
            repository.save(entity);
    
            return entity.getGno();
        }
    }

    GuestbookServiceImpl 클래스는 JPA를 처리하기 위해 GuestbookRepository를 주입하는 것 (@RequiredArgsConstructor)

    entity를 save()로 저장

    해당 entity의 gno값 반환

     

    testRegister() 테스트 실행

    실행 결과
    추가된 데이터 확인

     

    [목록 처리]

    목록 처리 작업시 고려할 상황

        • 화면에서 필요한 목록 데이터에 대한 DTO생성

        • DTO를 Pageable 타입으로 전환

        • Page<Entity>를 화면에서 사용하기 쉬운 DTO의 리스트 등으로 변환

        • 화면에 필요한 페이지 번호 처리

     

    [목록 처리를 위한 DTO]

    목록은 거의 모든 게시판의 기능으로 사용되기 때문에 재사용성이 높은 구조로 생성하는 것이 좋음

     

    [페이징 요청 처리 DTO(PageRequestDTO)]

    이제 만들 PageRequestDTO는 목록 페이지를 요청할 때 데이터를 재사용하기 쉽게 만드는 클래스임

    '페이지 번호', '페이지 내 목록 수', '검색 조건' 등의 파라미터가 될 부분을 DTO로 선언하고 후에 재사용하는 용도로 사용함

     

    PageRequestDTO: 화면에서 전달되는 목록 관련 데이터에 대한 DTO

    PageResultDTO: 화면에서 필요한 결과

     

    클래스 생성

    PageRequestDTO 클래스

    package org.zerock.guestbook.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;
        
        public PageRequestDTO() {
            this.page = 1;
            this.size = 10;
        }
        
        public Pageable getPageable(Sort sort) {
            
            return PageRequest.of(page -1, size, sort);
        }
    }

    화면에서 전달되는 page, size 파라미터를 수집하는 역할

    JPA쪽에서 사용하는 Pageable타입의 객체를 생성하는 것이 목적

    정렬 방법은 파라미터로 받음

     

    [페이지 결과 처리 DTO(PageResultDTO)]

    Repository는 JPA를 이용하므로 페이지의 결과를 Page<Entity> 타입으로 반환함

    => 서비스 계층에서 이를 처리하기 위해 별도의 클래스를 만들어서 처리해야 함

    해당 클래스(PageResultDTO) 내용

        • Page<Entity>의 엔티티 객체들을 DTO 객체로 변환해서 자료구조로 담아 주어야 함

        • 화면 출력에 필요한 페이지 정보들을 구성해 주어야 함

    PageResultDTO 클래스

    package org.zerock.guestbook.dto;
    
    import lombok.Data;
    import org.springframework.data.domain.Page;
    
    import java.util.List;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    
    @Data
    public class PageResultDTO<DTO, EN> {
        
        private List<DTO> dtoList;
        
        public PageResultDTO(Page<EN> result, Function<EN, DTO> fn) {
            
            dtoList = result.stream().map(fn).collect(Collectors.toList());
        }
    }

    재사용하기 쉽도록 제네릭 타입을 이용해서 DTO와 EN이라는 타입을 지정함

    Function<EN, DTO>는 엔티티 객체들을 DTO로 변환해주는 기능

     

    [서비스 계층에서는 목록 처리]

    엔티티 객체의 DTO변환을 서비스 인터페이스에서 정의한 entityToDto()와 별도로 Function객체로 만들어 처리할 예정

     

    서비스 계층에서 PageRequestDTO를 파라미터로, PageResultDTO를 리턴 타입으로 사용하는 getList()를 설계하고, 엔티티 객체를 DTO 객체로 변환하는 entityToDto()를 정의

     

    GuestbookService 인터페이스

    package org.zerock.guestbook.service;
    
    import org.zerock.guestbook.dto.GuestbookDTO;
    import org.zerock.guestbook.dto.PageRequestDTO;
    import org.zerock.guestbook.dto.PageResultDTO;
    import org.zerock.guestbook.entity.Guestbook;
    
    public interface GuestbookService {
        Long register(GuestbookDTO dto);
    
        PageResultDTO<GuestbookDTO, Guestbook> getList(PageRequestDTO requestDTO);
    
        default Guestbook dtoToEntity(GuestbookDTO dto) {
            Guestbook entity = Guestbook.builder()
                    .gno(dto.getGno())
                    .title(dto.getTitle())
                    .content(dto.getContent())
                    .writer(dto.getWriter())
                    .build();
            return entity;
        }
        
        default GuestbookDTO entityToDto(Guestbook entity) {
            
            GuestbookDTO dto = GuestbookDTO.builder()
                    .gno(entity.getGno())
                    .title(entity.getTitle())
                    .content(entity.getContent())
                    .writer(entity.getWriter())
                    .regDate(entity.getRegDate())
                    .modDate(entity.getModDate())
                    .build();
            return dto;
        }
    }

     

    GuestbookServiceImpl 클래스

    package org.zerock.guestbook.service;
    
    import lombok.RequiredArgsConstructor;
    import lombok.extern.log4j.Log4j2;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.domain.Sort;
    import org.springframework.stereotype.Service;
    import org.zerock.guestbook.dto.GuestbookDTO;
    import org.zerock.guestbook.dto.PageRequestDTO;
    import org.zerock.guestbook.dto.PageResultDTO;
    import org.zerock.guestbook.entity.Guestbook;
    import org.zerock.guestbook.repository.GuestbookRepository;
    
    import java.util.function.Function;
    
    @Service
    @Log4j2
    @RequiredArgsConstructor // 의존성 자동 주입
    public class GuestbookServiceImpl implements GuestbookService {
    
        private final GuestbookRepository repository;
    
        @Override
        public Long register(GuestbookDTO dto) {
        ```
        }
    
        @Override
        public PageResultDTO<GuestbookDTO, Guestbook> getList(PageRequestDTO requestDTO) {
            Pageable pageable = requestDTO.getPageable(Sort.by("gno").descending());
    
            Page<Guestbook> result = repository.findAll(pageable);
    
            Function<Guestbook, GuestbookDTO> fn = (entity -> entityToDto(entity));
    
            return new PageResultDTO<>(result, fn);
        }
    }

    getList()에서 entityToDto(entity)로 Function을 생성함

    JPA의 처리 결과인 Page<Entity>와 Function을 전달해서 엔티티 객체들을 DTO의 리스트로 변환하고, 화면에 페이지 처리와 필요한 값들을 생성

     

    [목록 처리 테스트]

    엔티티 객체들이 DTO객체들로 변환되었는지 확인

     

    @Test
    public void testList() {
    
        PageRequestDTO pageRequestDTO = PageRequestDTO.builder().page(1).size(10).build();
    
        PageResultDTO<GuestbookDTO, Guestbook> resultDTO = service.getList(pageRequestDTO);
    
        for (GuestbookDTO guestbookDTO : resultDTO.getDtoList()) {
            System.out.println(guestbookDTO);
        }
    }
    Hibernate: 
        select
            guestbook0_.gno as gno1_0_,
            guestbook0_.moddate as moddate2_0_,
            guestbook0_.regdate as regdate3_0_,
            guestbook0_.content as content4_0_,
            guestbook0_.title as title5_0_,
            guestbook0_.writer as writer6_0_ 
        from
            guestbook guestbook0_ 
        order by
            guestbook0_.gno desc limit ?
    Hibernate: 
        select
            count(guestbook0_.gno) as col_0_0_ 
        from
            guestbook guestbook0_

    GuestbookDTO(gno=301, title=Sample Title..., content=Sample Content..., writer=user0, regDate=2022-08-18T15:04:06.972053, modDate=2022-08-18T15:04:06.972053)
    GuestbookDTO(gno=300, title=Change Title...., content=Change Content..., writer=user0, regDate=2022-08-17T11:18:30.937445, modDate=2022-08-17T11:49:36.334030)
    GuestbookDTO(gno=299, title=Title....299, content=Content...299, writer=user9, regDate=2022-08-17T11:18:30.930463, modDate=2022-08-17T11:18:30.930463)

    테스트 실행 결과 Page<Guestbook>이 List<GuestBookDTO> 로 정상적으로 DTO객체로 변환된 것을 확인 가능함

     

    [목록 데이터 페이지 처리]

    화면까지 전달되는 데이터는 PageResultDTO이고, 이를 이용해서 화면에서 페이지 처리를 진행함

    PageResultDTO 타입으로 처리된 결과에 사용할 정보(시작 페이지, 끝 페이지 등)를 담아서 화면에서는 필요한 내용을 찾기만 하면 구성 가능하도록 구현

     

    페이지 처리에 필요한 정보

        • 화면에서 시작 페이지 번호(start)

        • 화면에서 끝 페이지 번호(end)

        • 이전/다음 이동 링크 여부(prev, next)

        • 현재 페이지 번호(page)

     

    package org.zerock.guestbook.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());
        }
    }
    

     

     

Designed by Tistory.