Spring/Spring Boot

Spring Rest Docs Enum class 문서화 하기

민돌v 2024. 3. 19. 01:15
개발환경 : Java 17, Spring 3

현재 사이드 프로젝트에서 Rest Docs + Swagger UI 를 사용 중 입니다. (참고 → restdocs + swagger ui 같이사용하기)

 

보통 상태나 선택 특정 목록같은 딱딱 선택지가 정해져있는 상태값은 Enum 으로 관리하는 걸 선호하는 편인데,
이게 RestDocs 를 사용하지만, Open API 를 사용하여 바로 Swagger UI 에 적용되는 json 파일을 자동적으로 만들어주다보니, Enum 클래스에 대한 문서 지원이 되지않아 (찾지못해) 신경이 좀 쓰였습니다.

 

그래서 이번 글에서는 Rest Docs 와 Open API를 이용한 Swagger UI 적용환경에서 ENUM 클래스를 어느정도 문서화하기 편한 상태로 만든 방법에 대해 공유하고자 합니다.

 


1. 기존 상태 설명

  • 이제 저희는 위와같은 ENUM 클래스를 테이블의 상태값으로 사용한다고 가정합니다.
  • Enum 을 사용한다면 String 으로 관리하는 것 보다 유지보수에 상당히 많은 장점을 가집니다.
public enum Gender {

    MALE("MALE"),
    FEMALE("FEMALE"),
    BISEXUAL("BISEXUAL");
 }

 

이제 이 Enum 을 상태변수로 가지는 User 라는 도메인있고 이 값을 Response 로 보내고, 이를 Rest Docs 로 문서화 한다면 아래와 같은 테스트코드가 작성될 것입니다.

@WebMvcTest(UserController.class)
class UserJoinDocumentation {

    //생략
    
    @Test
    @DisplayName("유저 회원가입 docs")
    void userJoin() throws Exception {

        //생략
        
        //then
        ResultActions resultActions = mockMvc.perform(
            RestDocumentationRequestBuilders.post(DEFAULT_URL + "/signup")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON)
                .content(requestBody)
        ).andDo(
            document("유저 회원가입 docs",
                preprocessRequest(prettyPrint()),
                preprocessResponse(prettyPrint()),
                resource(
                    ResourceSnippetParameters.builder()
                        .tag("유저 - 회원가입")
                        .description("유저 일반 회원가입")
                        .requestFields(
                            fieldWithPath("username").description("닉네임"),
                            fieldWithPath("email").description("이메일"),
                            fieldWithPath("birthDay").description("생일"),
                            fieldWithPath("gender").description("성별"),
                        )
                        .responseFields(
                        )
                        .build()
                ))
        );

        resultActions.andExpect(MockMvcResultMatchers.status().isCreated());
    }
}

이후 확인해보면 이렇게 잘! 문서화 된 걸 볼 수 있습니다! 비록 문자열로 나오지만!


 

2. 개선 방안

  • 이제 저 schema 의 문서에 Enum 의 상태값을 모두 적어두어클라이언트 사용자가 조금 더 용이하게 개발할 수 있게 바꿔보고자 합니다!
fieldWithPath("gender").description("성별 -[MALE, FEMALE]")

단순하게 생각한다면 이렇게 모두 직접 적어두어 수정하여도 원하는 결과를 뽑아냅니다.

📌 하지만 이 방법은 Enum 의 상태값이 추가되거나 변경된다면 컴파일시 잡을 수 없기 때문에 문서가 틀어질 가능성이 몹시 높습니다.

 


3. 자동화 하기

  • 자동화라는 단어가 적합한지는 모르겠으나, 저는 인터페이스를 사용하여 Enum 클래스를 한단계 추상화하기로 하였습니다.
  • 먼저 단순하게 Enum 클래스의 모든 변수값을 뽑아올 메소드를 추상화 해주었습니다.

Interface

public interface EnumModel {

    String getKey();

    String getValue();

}

Enum

@Getter
@AllArgsConstructor
@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum Gender implements EnumModel{

    MALE("MALE"),
    FEMALE("FEMALE"),
    BISEXUAL("BISEXUAL");

    private final String value;

    @Override
    public String getKey() {
        return name();
    }

    @Override
    public String getValue() {
        return value;
    }
}

 

Util Class

  • 이후 Test 에서만 사용할 Util 클래스를 만듭니다.
  • Enum class 에 상위 interface를 두었기 때문에 제네릭을 사용하여 하위 타입을 받아올 수 있습니다.
  • enums.getEnumConstans() 메소드는 선언된 enum 클래스의 구성된 모든 값을 선언된 순서대로 포함하는 배열을 반환합니다.
public abstract class EnumDocsUtils {

    public static String getTypesFieldList(Class<? extends EnumModel> enums) {

        return Arrays.stream(enums.getEnumConstants())
                .map(EnumModel::getValue)
                .toList()
                .toString();
    }

}

Test 에서만 사용할 util 클래스이기에 Test 패키지 내부에 만들었습니다.

Rest Docs

  • 이후 util 클래스를 이용해 원하는 타입을 매개변수로 주입하면 모든 상태값을 출력해주도록 합니다.
  • 이렇게 하여 열거형 값이 추가, 변경, 삭제되어도 기존의 코드를 수정하지 않아도 되는 구조를 만들 수 있었습니다.
fieldWithPath("gender").description(String.format("성별 - %s", EnumDocsUtils.getTypesFieldList(Gender.class))),

 


Test 를 위해 프로덕션 코드에 interface 를 추가하였지만,,, 문서화와 관련된 작업이고, 유지보수에 대한 공수를 굉장히 많이 줄여줄 수 있는 구조를 완성하였고,

추상화된 인터페이스에 key 와 value 로의 관리를 강제함으로써 Enum 클래스 자체의 값에 대해서도 변경을 용이하게 하는것 같아 나쁘지 않은 선택같았습니다.

테스트를 위해 프로덕션 코드를 변경한다는 거부감이 있긴하지만 더 좋은 방법이 떠오르지가 않네요..

 

코멘트는 언제나 환영입니다!

끝!