Book/스프링부트 핵심가이드

Service, Controller 클래스 생성 및 메서드 구현

블로그 주인장 2023. 11. 3.

Service, controller 클래스 생성 및 메서드 구현

애플리케이션에서 핵심 기능을 제공하는 서비스, 비즈니스 로직과 클라이언트 요청을 연결하는 컨트롤러를 생성해보겠습니다.


 

서비스 클래스 만들기


  • 서비스 레이어에서는 도메인 모델(Domain Model)을 활용해 애플리케이션에서 제공하는 핵심 기능을 제공한다.
  • 핵심 기능을 구현하려면 세부 기능을 정의해야한다.
  • 아키텍처의 한계를 극복하기 위해 아키텍처를 서비스 로직과 비즈니스 로직으로 분리하기도 한다.
  • 도메인을 활용한 세부 기능들은 비즈니스 레이어 로직에 구현하고, 서비스 레이어에서는 기능 등을 종합해서 핵심기능을 전달하도록 구성하는 경우가 대표적이다.

 

DTO 클래스 생성


ProductDto

 

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ProductDto {
    private String name;
    private int price;
    private int stock;
}

 

ProductResponseDto

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ProductResponseDto {
    private Long number;
    private String name;
    private int price;
    private int stock;
}

 

 

Service 클래스 생성


ProductService

public interface ProductService {
    ProductResponseDto getProduct(Long number);

    ProductResponseDto saveProduct(ProductDto productDto);

    ProductResponseDto changeProductName(Long number, String name) throws Exception;
    
    void deleteProduct(Long number);
}
  • DAO에서 구현한 기능을 서비스 인터페이스에서 호출해 결과값을 가져오는 작업을 수행하도록 설계했다.
  • 서비스에서는 클라이언트가 요청한 데이터를 적절하게 가공해서 컨트롤러에게 넘기는 역할을 한다.
  • 서비스에서는 리턴 타입이 DTO 객체이다. DAO 객체에서는 엔티티 타입을 사용하는 것을 고려하면 서비스 레이어에서 DTO 객체와 엔티티 객체를 각 레이어에 변환해서 전달하는 역할을 수행한다.
  • 회사나 개발 그룹 내 규정에 따라 DTO를 사용하기도 한다.

 

ProductService 인터페이스 구현 클래스

@Service
@RequiredArgsConstructor
public class ProductServiceImpl implements ProductService {

    private final ProductDao productDao;

    @Override
    public ProductResponseDto getProduct(Long number) {
        return null;
    }

    @Override
    public ProductResponseDto saveProduct(ProductDto productDto) {
        return null;
    }

    @Override
    public ProductResponseDto changeProductName(Long number, String name) throws Exception {
        return null;
    }

    @Override
    public void deleteProduct(Long number) {

    }
}

 

getProduct() 메서드 구현

@Override
public ProductResponseDto getProduct(Long number) {
    Product product = productDao.selectProduct(number);

    ProductResponseDto productResponseDto = ProductResponseDto.builder()
            .number(product.getNumber())
            .name(product.getName())
            .price(product.getPrice())
            .stock(product.getStock())
            .build();
    
    return productResponseDto;
}
  • lombok의 builder()를 이용하여 DTO 객체를 생성하고 엔티티 객체 변환 작업 진행

 

saveProduct() 메서드 구현

@Override
public ProductResponseDto saveProduct(ProductDto productDto) {
    Product product = Product.builder()
            .name(productDto.getName())
            .price(productDto.getPrice())
            .stock(productDto.getStock())
            .createdAt(LocalDateTime.now())
            .updatedAt(LocalDateTime.now())
            .build();

    Product savedProduct = this.productDao.insertProduct(product);

    ProductResponseDto productResponseDto = ProductResponseDto.builder()
            .number(savedProduct.getNumber())
            .name(savedProduct.getName())
            .price(savedProduct.getPrice())
            .stock(savedProduct.getStock())
            .build();
            
    return productResponseDto;
}
  • 전달받은 DTO 객체를 통해 엔티티 객체를 생성해서 초기화 후 DAO 객체로 전달한다.
  • 일반적으로 저장 메서드는 void 타입으로 작성하거나 성공 여부를 나타내는 boolean 타입으로 지정하는 경우가 많다.
  • void로 저장 메서드를 구현하는 경우 클라이언트가 저장한 데이터의 인덱스 값을 알 방법이 없기 때문에, 데이터를 저장하면서 가져온 인덱스를 DTO에 담아 결과값으로 클라이언트에 전달한다.

 

changeProductName() 메서드 구현

@Override
public ProductResponseDto changeProductName(Long number, String name) throws Exception {
    Product changedProduct = this.productDao.updateProductName(number, name);
    
    ProductResponseDto productResponseDto = ProductResponseDto.builder()
            .number(changedProduct.getNumber())
            .name(changedProduct.getName())
            .price(changedProduct.getPrice())
            .stock(changedProduct.getStock())
            .build();

    return productResponseDto;       
}
  • 이름을 변경하기 위해 클라이언트로부터 대상을 식별할 수 있는 인덱스 값과 변경하려는 이름 값을 받아온다.
  • 실제 레코드 값을 변경하는 작업은 DAO에서 진행하기 때문에 서비스 레이어에서는 해당 메서드를 호출해서 결과값만 받아온다.

 

deleteProduct() 메서드 구현

@Override
public void deleteProduct(Long number) throws Exception{
    this.productDao.deleteProduct(number);
}
  • 삭제하는 메서드는 리포지토리에서 제공하는 delete() 메서드를 사용할 경우 리턴타입이 지정되지 않기에 리턴 타입을 void 로 지정하여 메서드를 구현한다.

 

컨트롤러 클래스 생성


ProductController 클래스

@RestController
@RequestMapping("/product")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService productService;

    @GetMapping()
    public ResponseEntity<ProductResponseDto> getProduct(Long number) {
        ProductResponseDto responseDto = this.productService.getProduct(number);

        return ResponseEntity.status(HttpStatus.OK).body(responseDto);
    }

    @PostMapping()
    public ResponseEntity<ProductResponseDto> createProduct(@RequestBody ProductDto productDto) {
        ProductResponseDto responseDto = this.productService.saveProduct(productDto);

        return ResponseEntity.status(HttpStatus.OK).body(responseDto);
    }

    @PutMapping()
    public ResponseEntity<ProductResponseDto> changeProductName(
            @RequestBody ChangeProductNameDto changeProductNameDto) throws Exception {

        ProductResponseDto responseDto = this.productService.changeProductName(
                changeProductNameDto.getNumber(),
                changeProductNameDto.getName());

        return ResponseEntity.status(HttpStatus.OK).body(responseDto);
    }
}

 

 

Summary


📌 일반적으로 DAO의 리턴타입은 Entity, 서비스에서의 리턴타입은 DTO이다.

 

📌 Service 메서드 구현 방법

  1. Service 생성
    • 엔티티 객체의 값을 받아와서 DTO 객체를 생성하고 변환한다.
  2. Service 저장
    • 일반적으로 저장 메서드는 void 또는 boolean 타입으로 구현된다.
    • void 타입인 경우, 데이터의 인덱스 값을 알 수 없기 때문에 DTO 타입으로 변환하여 값을 리턴한다.
  3. Service 수정
    • 실제 변경하는 값은 DAO에서 진행하고 있기 때문에 해당 메서드를 호출하여 결과값만 받아온다.
  4. Service 삭제
    • 리포지토리에서 제공하는 delete() 메서드 사용
    • 리턴 타입이 지정되지 않기에 void로 지정하여 구현한다.

 

반응형

댓글