개발환경 : 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 클래스 자체의 값에 대해서도 변경을 용이하게 하는것 같아 나쁘지 않은 선택같았습니다.
테스트를 위해 프로덕션 코드를 변경한다는 거부감이 있긴하지만 더 좋은 방법이 떠오르지가 않네요..
코멘트는 언제나 환영입니다!
끝!
'Spring > Spring Boot' 카테고리의 다른 글
Spring 에서 Redis 사용하기 (설정, In-memory DB, Transaction) (0) | 2024.07.01 |
---|---|
@Transactional 동작과정 살펴보기 (with. Spring AOP) (1) | 2024.06.16 |
[Spring] FCM 푸시 알림 연동하기 (AOS, IOS) (2) | 2024.01.07 |
[Spring Security] 존재하지 않는 API 호출 시 404 대신 401 or 403 을 반환할 때 (2) | 2023.07.15 |
Spring Security Exception Handling - Filter 단 예외 처리하기 (5) | 2023.07.06 |