이번에는 이전글에 생성하였던 상품을 조회하는 기능을 만들어보자! 

 

먼저 Service Test 파일을 생성해 준다. (경로 src > test > java > com.example.productorderservice > product )

 

ProductServiceTest.java

package com.example.productorderservice.product;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

@SpringBootTest
class ProductServiceTest {

    @Autowired
    private ProductService productService;

    @Test
    void 상품조회(){
        // 상품 등록
        productService.addProduct(ProductSteps.상품등록요청_생성());
        final long productId = 1L;
        // 상품을 조회
        final GetProductResponse response = productService.getProduct(productId);

        // 상품의 응답을 검증
        assertThat(response).isNotNull();
    }

}

 

그리고 상품을 조회할 때 호출하는 ProductService 클래스의 getProduct 메소드를 생성해 준다.

 


ProductService.java ( getProductResponse 메소드 추가)

package com.example.productorderservice.product;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/products")
class ProductService {
    private final ProductPort productPort;

    ProductService(final ProductPort productPort) {
        this.productPort = productPort;
    }

    @PostMapping
    @Transactional
    public ResponseEntity<Void> addProduct(@RequestBody final AddProductRequest request){
        final Product product = new Product(request.name(), request.price(), request.discountPolicy());

        productPort.save(product);

        return ResponseEntity.status(HttpStatus.CREATED).build();
    }

    public GetProductResponse getProduct(final long productId) {
        final Product product = productPort.getProduct(productId);

        return new GetProductResponse(
                product.getId(),
                product.getName(),
                product.getPrice(),
                product.getDiscountPolicy());
    }
}

 

 위에 ProductService 에서 참조하는 GetProductResponse record를 생성해준다.

(경로 src > main> java > com.example.productorderservice > product )

 

GetProductResponse .java

package com.example.productorderservice.product;

import org.springframework.util.Assert;

record GetProductResponse(long id, String name, int price, DiscountPolicy discountPolicy) {
    GetProductResponse {
        Assert.notNull(id, "상품 ID는 필수입니다.");
        Assert.hasText(name, "상품명은 필수입니다.");
        Assert.notNull(discountPolicy, "할인 정책은 필수입니다.");
    }
}

이제 ProductService 에서 호출하는 getProduct에 대하여 Adapter와 Port에 데이터를 처리하기 위한 로직을 작성해 준다. 

 

ProductAdapter.java

package com.example.productorderservice.product;

import org.springframework.stereotype.Component;

@Component
class ProductAdapter implements ProductPort {
    private final ProductRepository productRepository;

    ProductAdapter(final ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @Override
    public void save(final Product product) {
        productRepository.save(product);
    }

    @Override
    public Product getProduct(long productId) {
        return productRepository.findById(productId)
                .orElseThrow(() -> new IllegalArgumentException("상품이 존재하지 않습니다."));
    }
}

 

ProductPort.java

package com.example.productorderservice.product;

interface ProductPort {
    void save(final Product product);

    Product getProduct(long productId);
}

그리고 ProductApiTest.java 안에 private 로 선언되어 있던 상품등록()과 상품등록요청_생성()메소드를 따로 빼내어 별도의 클래스로 만들어 준다. (상품 처리에 대한 로직을 한곳에 모아서 관리하기 용이)

 

경로(경로 src > test > java > com.example.productorderservice > product)

 

ProductSteps.java

package com.example.productorderservice.product;

import io.restassured.RestAssured;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import org.springframework.http.MediaType;

public class ProductSteps {
    public static ExtractableResponse<Response> 상품등록요청(final AddProductRequest request) {
        return RestAssured.given().log().all()
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .body(request)
                .when()
                .post("/products")
                .then()
                .log().all().extract();
    }

    public static AddProductRequest 상품등록요청_생성() {
        final String name = "상품명";
        final int price = 1000;
        final DiscountPolicy discountPolicy = DiscountPolicy.NONE;
        return new AddProductRequest(name, price, discountPolicy);
    }
}

 

 

그러면 ProductApiTest.java  클래스 안의 내용은 다음과 같이 바뀌게 된다.

 

ProductApiTest.java

package com.example.productorderservice.product;

import com.example.productorderservice.ApiTest;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;


class ProductApiTest extends ApiTest {

    @Test
    void 상품등록() {
        final var request = ProductSteps.상품등록요청_생성();
        // API 요청
        final var response = ProductSteps.상품등록요청(request);

        assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value());
    }


}

ProductServiceTest.java 를 실행해보면 다음과 같이 테이블 생성 후 상품정보를 생성한다.

그리고 나서 상품정보를 조회하는 순서의 로그가 보여진다.

 

 

 

다음글에는 사용자가 HTTP GET요청을 보냈을 때 오늘 구현한 response를 응답에 담아 보내주는 것을 구현해보자

 

 

Git : https://github.com/ShinHenry/product-order-service.git
Branch :  product_search

 

출처 : 실전! 스프링부트 상품-주문 API 개발로 알아보는 TDD

+ Recent posts