SpringBoot with JPA 프로젝트 4.DTO,SERVICE,목록처리

mingki·2022년 1월 31일
0

SpringBoot & JPA

목록 보기
2/26


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

1.service AND DTO

DTO의 목적 : 데이터의 전달 => 읽고,쓰는 것이 모두 허용되는 점이 가능하고 일회성으로 사용되는 성격이 강하다
DTO사용의 장점 : 엔티티 객체의 범위를 한정 지을수 있기 때문에 좀 더 안전한 코드를 작성할 수 있으며 화면과 데이터를 분리하려는 취지에 부합한다
DTO사용의 단점 : Entity와 유사한 코드를 중복으로 개발, Entity객체를 DTO로 변환하거나 DTO객체를 Entity 객체로 변환하는 과정이 필요하다

☆ 1-1.DTO 클래스 작성

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
// Entity클래스인 Guestbook과 거의 동일한 필드를 가지고 있으며 getter/setter를 통해 자유롭게 값을 변경할 수 있도록 구성한다
public class GuestbookDTO {

    private Long gno;
    private String title;
    private String content;
    private String writer;
    private LocalDateTime regDate, modDate;
}

☆ 1-2.service 클래스 작성

import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.dto.PageRequestDTO;
import com.example.guestbook2022.dto.PageResultDTO;
import com.example.guestbook2022.entity.Guestbook;

// service계층에서는 DTO를 이용해 필요한 내용을 전달받고 반환하도록 처리한다

public interface GuestbookService {

    Long register(GuestbookDTO dto);
}

☆ 1-3.service 구현클래스 작성

import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.entity.Guestbook;
import com.example.guestbook2022.repository.GuestbookRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor // 의존성 자동 주입
public class GuestbookServiceImpl implements GuestbookService{

    private final GuestbookRepository repository;

    @Override
    public Long register(GuestbookDTO dto) {

        // service interface에 구현된 dtoToEntity 활용
        Guestbook entity = dtoToEntity(dto);

        repository.save(entity);

        return entity.getGno();
    }

2.등록,DTO를 Entity로 변환하기

service에서는 파라미터를 DTO타입으로 받기 때문에 JPA로 처리하기 위해서는 Entity타입의 객체로 변환해야 하는 작업이 반드시 필요하다

  • 코드
package com.example.guestbook2022.service;

import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.dto.PageRequestDTO;
import com.example.guestbook2022.dto.PageResultDTO;
import com.example.guestbook2022.entity.Guestbook;

// service계층에서는 DTO를 이용해 필요한 내용을 전달받고 반환하도록 처리한다

public interface GuestbookService {

    Long register(GuestbookDTO dto);

    // service 에서는 파라미터를 DTO타입으로 받기 때문에 JPA로 처리하기 위해서는 Entity 타입의 객체로 변환해야 하는 작업이 반드시 필요하다
    // java 8 버전부터 인터페이스의 실제 내용을 가지는 코드는 default라는 키워드로 생성할 수 있다 -> 실제 코드를 인터페이스에 선언할 수 있다
    // => 추상클래스를 생략하는것이 가능해 졌다
    default Guestbook dtoToEntity(GuestbookDTO dto){
        Guestbook entity = Guestbook.builder()
                .gno(dto.getGno())
                .title(dto.getTitle())
                .content(dto.getContent())
                .writer(dto.getWriter())
                .build();
        return entity;
    }

}
  • 구현 코드
import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.entity.Guestbook;
import com.example.guestbook2022.repository.GuestbookRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.function.Function;

@Service
@RequiredArgsConstructor // 의존성 자동 주입
public class GuestbookServiceImpl implements GuestbookService{

    private final GuestbookRepository repository;

    @Override
    public Long register(GuestbookDTO dto) {

        // service interface에 구현된 dtoToEntity 활용
        Guestbook entity = dtoToEntity(dto);

        repository.save(entity);

        return entity.getGno();
    }

}

☆ 2-1.등록 및 DTO -> Entity 변환테스트

  • 테스트 코드
import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.entity.Guestbook;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

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

}
  • 실행시 쿼리
Hibernate: 
    insert 
    into
        guestbook
        (moddate, regdate, content, title, writer) 
    values
        (?, ?, ?, ?, ?)
301
  • 결과

3.목록처리

목록 처리시 고려사항

  • 화면에서 필요한 목록 데이터에 대한 DTO 생성
  • DTO 를 Pageable 타입으로 전환
  • Page를 화면에서 사용하기 쉬운 DTO의 리스트 등으로 변환
  • 화면에 필요한 페이지 번호 처리

4.DTO클래스 생성

재사용이 가능한 구조를 생성하기 위해 DTO 클래스를 작성한다

☆ 4-1. 페이지 요청 처리 DTO(PageRequestDTO)

PageRequestDTO : 목록 페이지를 요청할 때 사용하는 데이터를 재사용하기 쉽게 만드는 클래스
=> 파라미터를 DTO로 선언하고 나중에 재사용하는 용도로 사용한다
목적 : JPA쪽에서 사용하는 Pageable 타입의 객체를 생성하는 것

  • 코드
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;


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

☆ 4-2. 페이지 결과 처리 DTO(PageResultDTO)

PageResultDTO : JPA Repository에서 페이지 처리 결과를 Page 타입으로 반환해 주면 service에서 이를 처리하기 위해 만들어진 클래스 이다

  • Page의 객체들을 DTO 객체로 변환해 자료구조로 담아준다
  • 화면 출력에 필요한 페이지 정보들을 구성해 준다
  • 코드
import lombok.Data;
import org.springframework.data.domain.Page;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

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

    private List<DTO> dtoList;
                                           // Function<EN,DTO> : 엔티티 객체들을 DTO로 변환해주는 기능
    public PageResultDTO(Page<EN> result, Function<EN,DTO> fn){
        dtoList = result.stream().map(fn).collect(Collectors.toList());
    }

}

5.service 에서의 목록 처리

  • PageRequestDTO를 파라미터로, PageResultDTO를 리턴타입으로 사용하는getList()를 설계
  • Entoty 객체를 DTO 객체로 변환하는 entityToDto()를 정의한다
  • 코드
import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.dto.PageRequestDTO;
import com.example.guestbook2022.dto.PageResultDTO;
import com.example.guestbook2022.entity.Guestbook;

// service계층에서는 DTO를 이용해 필요한 내용을 전달받고 반환하도록 처리한다

public interface GuestbookService {

    Long register(GuestbookDTO dto);

    PageResultDTO<GuestbookDTO,Guestbook> getList(PageRequestDTO requestDTO);

    // service 에서는 파라미터를 DTO타입으로 받기 때문에 JPA로 처리하기 위해서는 Entity 타입의 객체로 변환해야 하는 작업이 반드시 필요하다
    // java 8 버전부터 인터페이스의 실제 내용을 가지는 코드는 default라는 키워드로 생성할 수 있다 -> 실제 코드를 인터페이스에 선언할 수 있다
    // => 추상클래스를 생략하는것이 가능해 졌다
    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;
    }
}

☆ 5-1. service 구현

getList() 구현

  • 코드
import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.dto.PageRequestDTO;
import com.example.guestbook2022.dto.PageResultDTO;
import com.example.guestbook2022.entity.Guestbook;
import com.example.guestbook2022.repository.GuestbookRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.function.Function;

@Service
@RequiredArgsConstructor // 의존성 자동 주입
public class GuestbookServiceImpl implements GuestbookService{

    private final GuestbookRepository repository;

    @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));
                    // JPA의 처리결과인 Page<Entity>와 Function을 전달해 엔티티 객체들을 DTO의 리스트로 변환하고 화면에 페이지 처리와 필요한 값들을 생성한다
        return new PageResultDTO<>(result,fn);
    }
}

6. 목록처리 테스트

Entity 객체들을 DTO 객체로 변환 테스트

  • 테스트 코드
package com.example.guestbook2022.service;

import com.example.guestbook2022.dto.GuestbookDTO;
import com.example.guestbook2022.dto.PageRequestDTO;
import com.example.guestbook2022.dto.PageResultDTO;
import com.example.guestbook2022.entity.Guestbook;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class GuestbookServiceTests {

    @Autowired
    private GuestbookService service;

    @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-01-31T19:43:17.140328, modDate=2022-01-31T19:43:17.140328)
GuestbookDTO(gno=300, title=Changed Title..., content=Changed Content..., writer=user0, regDate=2022-01-31T14:16:14.908530, modDate=2022-01-31T14:24:46.650907)
GuestbookDTO(gno=299, title=Title...299, content=Content...299, writer=user9, regDate=2022-01-31T14:16:14.904784, modDate=2022-01-31T14:16:14.904784)
GuestbookDTO(gno=298, title=Title...298, content=Content...298, writer=user8, regDate=2022-01-31T14:16:14.900567, modDate=2022-01-31T14:16:14.900567)
GuestbookDTO(gno=297, title=Title...297, content=Content...297, writer=user7, regDate=2022-01-31T14:16:14.892919, modDate=2022-01-31T14:16:14.892919)
GuestbookDTO(gno=296, title=Title...296, content=Content...296, writer=user6, regDate=2022-01-31T14:16:14.890396, modDate=2022-01-31T14:16:14.890396)
GuestbookDTO(gno=295, title=Title...295, content=Content...295, writer=user5, regDate=2022-01-31T14:16:14.887866, modDate=2022-01-31T14:16:14.887866)
GuestbookDTO(gno=294, title=Title...294, content=Content...294, writer=user4, regDate=2022-01-31T14:16:14.885068, modDate=2022-01-31T14:16:14.885068)
GuestbookDTO(gno=293, title=Title...293, content=Content...293, writer=user3, regDate=2022-01-31T14:16:14.882828, modDate=2022-01-31T14:16:14.882828)
GuestbookDTO(gno=292, title=Title...292, content=Content...292, writer=user2, regDate=2022-01-31T14:16:14.881012, modDate=2022-01-31T14:16:14.881012)
profile
비전공초보개발자

0개의 댓글