Recent Posts
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
Tags
- kotlin
- 겨울 부산
- JanusWebRTC
- terminal
- OptimisticLock
- PessimisticLock
- k8s #kubernetes #쿠버네티스
- Kubernetes
- python
- vfr video
- 자원부족
- PersistenceContext
- tolerated
- preemption #
- 코루틴 빌더
- 티스토리챌린지
- 오블완
- pytest
- taint
- 깡돼후
- 달인막창
- 코루틴 컨텍스트
- 개성국밥
- JanusGateway
- Spring Batch
- JanusWebRTCGateway
- k8s
- 헥사고날아키텍처 #육각형아키텍처 #유스케이스
- JanusWebRTCServer
- mp4fpsmod
Archives
너와 나의 스토리
추가 배포 없이 API 케이스 통일하기: 카카오 사례 및 구현 가이드 본문
반응형
카카오 기술 블로그 “추가배포 없이 API의 case 통일시키기”를 토대로 작성한 글입니다.
문제 정의
- 다양한 케이스 혼용
- "Admin에서는 Camel case를 써요", "외부 연동처에서는 Snake case로 내려달라고 하네요" 등 의도치 않게 서비스별로 JSON 케이스가 혼용되어 코드베이스에 여러 Naming 전략이 섞이게 된다.
- DTO 중복
- 여러 대상 서버의 케이스 규칙에 맞추려면 DTO를 여러 벌 정의해야 하는 상황이 발생한다.
(Camel Case) -> SERVER -> (Snake Case)
- 동일한 값의 DTO지만 받을 때는 Camel case, 보낼 때는 Snake case가 필요한 상황이 있을 수 있다.
- 위와 같은 문제는 MSA 환경에서 더욱 빈번하게 발생할 수 있고, 무중단 서비스에서는 쉽게 고치기 어렵다.
추가 배포 없이 API 케이스 통일하기 3단계

- Case-Insensitive 파싱 모듈 도입
- 핵심 목표: 기존 API가 어떤 케이스로 값을 보내든 간에 정상 동작하도록 만들기
- 서버들이 camelCase 또는 snake_case JSON을 받아도 문제 없이 역직렬화할 수 있도록 Jackson의 기능을 활용.
- ObjectMapper 설정에 accept-case-insensitive-properties: true 옵션을 설정하면, 케이스 무관하게 필드를 바인딩할 수 있다.
spring:
jackson:
mapper:
accept-case-insensitive-properties: true
2. snake_case 응답/요청으로 일괄 전환
- 핵심 목표: API를 외부에 snake_case로 일원화
- 요청(Request)은 스프링에서 제공하는 RequestBodyAdvice를 활용하여 AOP 방식으로 camelCase -> snake_case로 변환된 JSON을 강제 파싱
- 응답(Response)은 공통 ApiResponse<T> 래퍼를 사용해 내부 데이터를 가공한 뒤 snake_case로 직렬화.
- 서버 응답 시 ObjectMapper에 PropertyNamingStarategies.SNAKE_CASE를 적용해 snake_case가 강제된다.
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
return mapper;
}
@ControllerAdvice
public class SnakeCaseRequestAdvice implements RequestBodyAdvice {
private final ObjectMapper snakeCaseMapper;
public SnakeCaseRequestAdvice(ObjectMapper defaultMapper) {
this.snakeCaseMapper = defaultMapper.copy();
this.snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
}
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// 모든 요청에 대해 Advice를 적용할지 여부
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
// body를 직접 수정할 게 아니라면 그대로 반환
return inputMessage;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// 여기서는 실제 바디 파싱이 끝난 이후라 바꾸는 작업은 거의 안 함
return body;
}
@Override
public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
}
3. 모듈 정리 및 경량화
- 핵심 목표: 호환 모듈을 제거하고 통일된 시스템으로 정리
- 1단계에서 도입했던 case-insentive 파싱 모듈(accept-case-insensitive-properties)을 제거 -> 더 이상 필요 없어서
- Jackson 관련 설정(RequestBodyAdvice)도 정리
이 접근 방식의 장점
- 점진적 적용 가능
- 기존 API 사용자가 인지하지 못한 채로 변화를 적용할 수 있어 위험이 낮음.
- 코드 수정 최소화
- DTO를 복제하거나 대규모 수정 없이도 명명 규칙을 바꿀 수 있음.
- 플랫폼 일관성 확보
- 외부 연동, 프론트엔드 API 호출 시 명확한 포맷 통일 가능.
- 테스트/배포 비용 절감
- 기존 API의 동작을 유지하면서 새로운 형식으로 전환 가능.
반응형
Comments