혼자 하는 Spring 프로젝트 - 5 : TDD를 통한 코드 작성 (Controller) + chatGPT 외부 API 사용

꾸준하게 달리기~·2023년 7월 18일
1

솔로 프로젝트

목록 보기
5/11
post-thumbnail

이전 내용은 다음과 같다.
https://velog.io/@dlsrjsdl6505/%ED%98%BC%EC%9E%90-%ED%95%98%EB%8A%94-Spring-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-4-TDD%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-Service-Repository

Controller코드 + TDD 테스트코드

컨트롤러 코드는
각각 아래와 같이 작성했다.

해당 코드의 테스트코드는 아래와 같이 작성했다.
Mockito 라이브러리와 mock 객체를 사용했다.

<ProductControllerTest>

@SpringBootTest
@AutoConfigureMockMvc
public class ProductControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private Gson gson;

    @MockBean
    private ProductService productService;

    @Test
    void postProductTest() throws Exception {
        // given
        ProductDto.Create productCreateDto = ProductDto.Create.builder()
                .name("노트북")
                .sellPrice(120)
                .buyPrice(100)
                .quantity(1000)
                .build();

        Product createProduct = new Product();
        createProduct.setProductId(1L);


        given(productService.createProduct(Mockito.any(ProductDto.Create.class)))
                .willReturn(createProduct);

        String content = gson.toJson(productCreateDto);


        // when
        ResultActions actions =
                mockMvc.perform(
                        post("/products")
                                .accept(MediaType.APPLICATION_JSON)
                                .contentType(MediaType.APPLICATION_JSON)
                                .content(content)
                );

        // then
        actions
                .andExpect(status().isCreated())
                .andExpect(header().string("Location", is(startsWith("/products"))));
    }

    @Test
    public void sellProductTest() throws Exception {
        // Given
        long memberId = 1;
        long productId = 1;
        int quantity = 10;

        Product createProduct = new Product();
        createProduct.setProductId(1L); // 생성된 product의 ID 설정

        given(productService.sellProduct(Mockito.any(Long.class), Mockito.any(Long.class), Mockito.any(Integer.class)))
                .willReturn(createProduct); // 메서드의 동작 정의

        // When
        ResultActions actions =
                mockMvc.perform(patch("/products/sell/{product-id}/{member-id}/{quantity}", productId, memberId, quantity)
                .contentType(MediaType.APPLICATION_JSON));

        // Then
        actions
                .andExpect(status().isCreated())
                .andExpect(header().string("Location", startsWith("/products")));
    }

}

위와 비슷하게
MemberControllerTest
BalanceControllerTest 코드를 작성했다.

passed 16.. 나름 코드 작성하느라 힘들었다.
왜 DDD보다 TDD가 힘들다고 하는지 이해가 간다...
비즈니스로직이나 컨트롤러코드 짜는것보다, 테스트코드 짜는게 시간이 더 걸렸다..

컨트롤러 테스트코드를 작성하며 부딪힌 에러에서 느낀점은,

  • 컨트롤러 코드의 테스트는, 외부와의 API소통이 잘 이루어지는지를 테스트하므로, 서비스로직은 서비스테스트에서 작성할것

  • MockBean으로 만들어진 객체는, 가짜 객체이므로 실제 객체가 수행하는 로직을 제대로 수행할 수 없다.
    (테스트 코드에서 MockBean으로 생성된 객체의 메서드를 호출하면, 실제 객체의 동작이 실행되지 않고 Mock 객체가 설정된 동작을 수행하므로)



    이렇게 테스트코드는 다 작성했고, 코드를 실행시켜 잘 저장이 되는지 볼 차례이다.

코드 완성 후 DB에서의 결과

위 세 이미지를 보면 보시다시피 테이블별로 잘 저장이 되고,





이렇게 1번 member가, 1번 product를 10개 buy한다는 요청을 보내면,

  • 1번 member의 result는 1번 product를 10개 산 액수만큼 차감
  • 1번 product는 10개 산 갯수만큼 갯수 차감
  • balance의 amount(전체 잔액)은 사용한 액수(1번 product 10개)만큼 차감

위의 세개의 로직이 작동되어야 한다.
1번 member 초기 result 0
초기 amount 100000000
1번 product 초기 개수 1000, 구매가 100, 사들인 양 10 -> 총액1000 지출
이므로

1번 member 의 result 는 -1000
자산 총액 amount = 100000000 - 1000 = 99999000
1번 product의 quantity는 1000(초기) + 10(사들인 양) = 1010 이 되어야 한다.




amount = 99999000





result = 초기0 - 지출1000 = -1000





quantity = 1010

내가 생각한대로 로직이 잘 짜여진듯 하다.






chatGPT + Spring 프로젝트

비록 혼자 만든 재고관리 시스템이지만,
차세대 ERP 등
이제는 관리 시스템조차 AI와 연결된다.
그렇다면...

각각 사원이 실현해낸 이익인 result 필드 등을 기반으로
chatGPT에게 인사에 관해서, 혹은 재고 관련해서 전략을 물어볼 수 있도록
외부 API를 통해 chatGPT와 Spring을 사용하여 연결하려고 한다.


일단, 당연히 외부 API를 이용하기 위해서는 key를 발급받아야 한다.
https://platform.openai.com/account/api-keys
에서

다음과 같이 발급받았다.

발급받은 후에는, 사용해야 하므로
의존성 추가(gradle),

implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4'

그다음 받급받은 키를 어플리케이션의 설정파일에 추가해준다.
(application.yml)

chatgpt:
  api-key: 발급받은 키를 집어 넣으세용

위의 의존성을 추가해주면서 생긴, ChatgptService 인터페이스를 이용해서,

@Service
@RequiredArgsConstructor
public class ChatService {

    private final ChatgptService chatgptService;

    public String getChatting (String question) {
        return chatgptService.sendMessage(question);
    }
}

위와 같이 코드를 짰다.
(ChatServe 클래스가 내가 만든것, ChatgptService가 의존성으로 생긴 인터페이스)

그다음 Controller 클래스는 아래와 같이 만들었다.

@RestController
@Slf4j
@Validated
@Transactional
@RequiredArgsConstructor
@RequestMapping("/chatGPT")
public class ChatController {
    private final static String GPT_DEFAULT_URL = "/chatGPT";

    private final ChatService chatService;

    @PostMapping
    public String chatting (@RequestBody String question) {
        return chatService.getChatting(question);
    }
}

외부 API는 에러나오면 골치아픈 일이 많아서,
제발 돼라돼라 하면서 코드를 실행시켰다.

컨트롤러의 url로 String을 보내면,

보기좋게 위와 같이 성공했다.
재고시스템, 혹은 인사시스템에 도움을 받을 수 있는 인공지능 대화를
chatGpt 의 API를 받아와 추가해줬다!




다음 할 내용은..?

Java Restdocs 으로 문서화하거나 or AWS 배포를 위해 VM들의 설정을 체크 or redis 설정을 추가해줄 예정이다.

profile
반갑습니다~! 좋은하루 보내세요 :)

4개의 댓글

comment-user-thumbnail
2023년 7월 18일

뛰어난 글이네요, 감사합니다.

답글 달기
comment-user-thumbnail
2023년 7월 18일

글이 많은 도움이 되었습니다, 감사합니다.

답글 달기
comment-user-thumbnail
2023년 7월 18일

사실 요새는 API를 쓰지 않고 작업을 하기에는 퀄리티적으로 다른 서비스들과 차이가 커서 API는 필수로 사용해야 하는 것 같아요. 최근 백엔드에 작업을 하는 학생으로서 가벼운 컨트롤러와 MYSQL DB를 이용하는 점이 같아서 자주 읽고 있네요. 특히나 요새 가장 큰 이슈인 AI 플랫폼인 ChatGPT API를 사용해서 작업을 진행하신다니 굉장히 트렌디하신 것 같아요. 항상 화이팅입니다. 좋은 프로젝트 기대할게요!

1개의 답글

관련 채용 정보