본문 바로가기

개발/문서화

MSA에서의 swagger 사용

문제 정의

  • MSA에서의 swagger + restdocs적용

해결과정

  • 다행스럽게도 튜터님께서 MSA에 swagger + restdocs 적용하는 방법을 깔끔하게 알려주셔서 그대로 적용한 결과 잘 되었습니다. 문제는 restdocs 적용을 위한 테스트 코드 작성이었는데, 테스트 코드 작성이 거의 전무한 저에게는 MSA에서 다른 서버로 요청을 보내 값을 받아오는 로직이 포함된 경우 어떻게 할지 고민이 되었죠. 구글링과 챗gpt를 이용한 결과 mock 객체를 만들어서 사용하면 된다고 하더군요. 이 부분을 적용하여 수정에 수정을 거친 결과 정상적으로 테스트가 실행되는 것을 확인했습니다.

결과

  • 코드
    • user서버에서 다른 서버들로 요청을 보낼 때에 사용한 mock객체에는 @MokitoBean을 붙여서 테스트에 사용하였습니다.
    @SpringBootTest
    @AutoConfigureRestDocs
    @AutoConfigureMockMvc
    @ActiveProfiles("local")
    class UserControllerApiTest {
    
        @Autowired
        private MockMvc mockMvc;
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Autowired
        private UserRepository userRepository;
    
        @MockitoBean
        private DeliveryService deliveryService; // FeignClient Mocking
    
        @MockitoBean
        private AuthService authService;
        
        @BeforeEach
        @Transactional
        @Rollback
        void setUp() {
            User user = User.builder()
                    .username("master001")
                    .password(passwordEncoder.encode("aA123123!"))
                    .role(UserRoleEnum.MASTER)
                    .slackId("slackMaster01")
                    .createdBy("SYSTEM")
                    .updatedBy("SYSTEM")
                    .build();
            userRepository.save(user);
        }
        
        
        @Transactional
        @Rollback
        @Test
        public void testDeleteSoftDeleteUser() throws Exception {
            //given
            User user = userRepository.findByUsername("master001").orElseThrow();
    
            GetUUIDDto.UUIDListDto mockDeliveryManager = GetUUIDDto.UUIDListDto.builder()
                    .deliveryManagerId(1L)
                    .build();
    
            //when
            when(authService.verifyUser(any(VerifyUserDto.class))).thenReturn(true);
    
            ApiResponseDto<GetUUIDDto> mockResponse = ApiResponseDto.response(HttpStatus.OK.value(), "조회 성공", new GetUUIDDto(List.of(mockDeliveryManager)));
            when(deliveryService.getDeliveryManagerByUserId(any(Long.class), any(String.class), any(String.class), eq(UserRoleEnum.MASTER.toString())))
                    .thenReturn(ResponseEntity.status(HttpStatus.OK).body(mockResponse));
    
            // Mocking the response from softDeleteDeliveryManager
            List<Long> deliveryManagerIds = mockResponse.getData().getContent().stream()
                    .map(GetUUIDDto.UUIDListDto::getDeliveryManagerId)
                    .toList();
    
            for (Long deliveryManagerId : deliveryManagerIds) {
                when(deliveryService.softDeleteDeliveryManager(eq(deliveryManagerId), any(String.class), any(String.class), eq(UserRoleEnum.MASTER.toString())))
                        .thenReturn(ResponseEntity.ok(ApiResponseDto.response(HttpStatus.OK.value(), "삭제 성공", null)));
            }
    
            mockMvc.perform(
                            RestDocumentationRequestBuilders.delete("/api/users/{username}", user.getUsername())
                                    .header("X-User_Id", user.getUserId().toString())
                                    .header("X-Username", user.getUsername())
                                    .header("X-Role", user.getRole().toString())
                    )
                    .andExpectAll(
                            MockMvcResultMatchers.status().isOk()
                    )
                    //then
                    .andDo(
                            MockMvcRestDocumentationWrapper.document(
                                    "삭제 성공",
                                    Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
                                    Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
                                    ResourceDocumentation.resource(
                                            ResourceSnippetParameters.builder()
                                                    .tag("USER-SERVICE V1")
                                                    .summary("회원 삭제")
                                                    .description(
                                                            """
                                                                    ## User 서비스 회원 소프트 삭제 엔드포인트입니다.
                                                                    
                                                                    ---
                                                                    
                                                                    - 소프트 삭제는 MASTER 권한만 가능합니다.
                                                                    - delivery 서버로 delivery manager 삭제도 함께 요청합니다.
                                                                    """
                                                    )
    
                                                    .build()
                                    )
                            )
                    );
    
        }
    
    }
    
    @MokitoBean을 이용하여 mock객체를 통해 다른 서버에서 온 응답 객체를 미리 만들어두고, 비즈니스 로직에서 해당 메서드가 호출이 되면 만들어 놓은 응답 객체를 넣어줌으로써 다른 서버와 직접적인 통신이 없어도 local에서 테스트가 진행되게 하였습니다.

꺠달은 내용

  • MSA에서의 swagger는 어떻게 적용해야 될까 고민이 많았습니다. 문서화가 중요하다는 건 지금도 느끼고 있고 앞으로도 더 느낄 것 같기에 꼭 해보고 싶은 부분 중 하나였습니다. 구글링을 해봐도 MSA에서 실행까지 완벽하게 되는 건 잘 찾지 못하였는데, 너무 감사하네요 ㅎㅎ.