Java Focus
JSON → Java Class: 실무용 불변 DTO 만들기
이 가이드는 JSON 응답을 빠르게 Java DTO로 변환하고, Spring 프로젝트에서 즉시 쓸 수 있도록 Validation과 컬렉션, 중첩 객체를 정돈하는 방법을 담았습니다. 아래 예제를 따라 한 번의 변환으로 타입 안정성을 확보해 보세요.
언제 JSON을 Java 클래스로 변환할까?
대부분의 백엔드 API는 JSON으로 응답하지만, 서비스 코드는 정적 타입을 요구합니다. DTO를 미리 정의하면 컨트롤러 단에서 바로 검증을 거쳐 도메인 로직에 들어갈 수 있고, 계약이 깨졌을 때 즉시 컴파일 에러로 발견됩니다. JSON2Class는 브라우저에서 JSON을 붙여 넣는 순간 다음을 자동으로 처리합니다.
- 필수/선택 필드를 감지하고 `@NotNull` 등 Validation 어노테이션을 제안합니다.
- 숫자, 문자열, 불리언, 배열, 중첩 객체를 불변 필드(`private final`)와 생성자로 변환합니다.
- 필드 경로를 Markdown 표로 내보내어 API 문서를 동시에 정리합니다.
이 흐름은 백엔드-프런트엔드 계약을 맞출 때 유용합니다. 프런트엔드 팀이 기대하는 JSON 구조를 DTO로 고정하면 브레이킹 체인지가 줄어들고, 테스트 픽스처를 만들 때도 동일한 JSON을 재사용할 수 있습니다.
대표적인 사용 사례
Java 개발자가 JSON2Class를 활용하는 전형적인 장면은 다음과 같습니다.
- API 온보딩: 파트너사의 샘플 JSON을 붙여 넣어 DTO와 검증 규칙을 즉시 정의합니다.
- 테스트 픽스처: 컨트롤러/서비스 테스트에서 재사용할 객체를 안정적으로 생성합니다.
- 계약 문서화: 생성된 Markdown 테이블을 위키나 PR 설명에 붙여 커뮤니케이션 비용을 낮춥니다.
- 리팩터링: 레거시 Map 기반 코드를 타입 안전한 DTO로 교체할 때 구조를 빠르게 확인합니다.
JSON → Java 예시와 타입 매핑
아래 JSON을 붙여 넣으면 Lombok과 Jakarta Validation을 적용한 클래스를 얻을 수 있습니다.
숫자 타입은 소수 여부에 따라 `Integer` 또는 `Double`로, 배열은 `List
{
"userId": "u-1029",
"active": true,
"score": 88.5,
"roles": ["USER", "ADMIN"],
"profile": {
"name": "Lee",
"timezone": "Asia/Seoul"
}
}
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public class UserProfile {
@NotNull private final String userId;
private final Boolean active;
private final Double score;
private final List roles;
private final Profile profile;
@Getter
@AllArgsConstructor
public static class Profile {
private final String name;
private final String timezone;
}
}
타입 매핑 규칙 요약: 문자열은 `String`, 소수점이 있는 숫자는 `Double`, 정수는 `Integer`,
불리언은 `Boolean`, 배열은 `List
널 처리와 Validation 전략
JSON 필드의 존재 여부는 계약 안정성을 결정합니다. 변환기에서 Nullable 체크박스를 끄면 모든 필드를 필수로 취급하고, 켜면 배열/객체의 Optional 상황을 고려합니다. Jakarta/`javax` Validation 중 하나를 선택하면 필수 필드에는 `@NotNull`, 문자열 길이 제약을 JSON 지시어(`#max:10` 등)로 표현할 수도 있습니다.
컨트롤러에서 DTO를 그대로 사용한다면 `@Valid`로 검증을 통과시키고, 서비스 내부에서는 도메인 규칙에 맞는 값 객체로 매핑해 주면 됩니다. 이 과정을 코드 리뷰에 남기면 팀 규칙을 표준화하는 데 큰 도움이 됩니다.
네이밍과 패키지 구성 팁
- 루트 클래스명을 API 리소스 단위로 지정하세요. 예: `OrderResponse`, `UserProfileResult`.
- 중첩 클래스는 static으로 두어 직렬화/역직렬화가 명확하도록 합니다.
- 패키지는 `api.dto` 또는 `presentation.response`처럼 호출 계층을 드러내는 이름이 관리에 유리합니다.
- 필드명이 JSON 스네이크 케이스라면 Jackson의 `@JsonProperty`를 추가해 매핑을 고정할 수 있습니다.
자주 겪는 문제 해결
Nullability: 스키마가 자주 변한다면 nullable 옵션을 켜고, 서비스 내부에서 `Objects.requireNonNull`이나\n 커스텀 검증기로 다시 한 번 단언하는 방식을 추천합니다. 반대로 계약이 단단하다면 모든 필드를 필수로 고정해 배포 전에 오류를 잡아냅니다.
숫자와 통화: 금액, 세금처럼 정밀도가 중요한 필드는 `BigDecimal`을 선호하는 팀도 있습니다. 변환기로 초안을 만든 뒤\n 해당 필드만 `BigDecimal`로 수동 교체하면 됩니다. JSON의 소수 여부는 샘플에 따라 결정되므로, 가장 큰/작은 값 사례를 함께 넣어 테스트하세요.
날짜/시간: 문자열로 추론된 필드가 날짜라면 `OffsetDateTime`이나 `LocalDate`로 교체하고,\n Jackson `@JsonFormat`을 붙여 직렬화 포맷을 명확히 합니다. 팀에서 epoch millisecond를 사용한다면 `Long`으로 유지해도 됩니다.
맵 형태의 동적 키: 키 이름이 유동적인 JSON(`\"metrics\": {\"t1\": 1.2, \"t2\": 0.9}`)은 `Map
대형 페이로드: 수천 라인의 JSON이라면 필요한 부분만 잘라내어 변환하거나, 샘플을 여러 개로 나눠 테스트하세요.\n 생성된 클래스는 가이드라인 역할을 하므로, 실제 도메인에 맞게 소형화해 관리하는 편이 낫습니다.\n
변환기와 함께 쓰는 베스트 프랙티스
변환된 코드는 시작점입니다. 팀의 스타일 가이드를 더해 완성도를 높이세요.
- 롬복 대신 record를 쓰는 프로젝트라면 생성된 필드/타입을 참고해 직접 record를 작성합니다.
- 테스트 더블을 만들 때는 JSON 샘플을 그대로 픽스처로 저장하고, DTO를 통해 역직렬화하는 테스트를 추가하세요.
- OpenAPI/Swagger 스펙이 있다면, 생성된 DTO와 비교해 차이를 문서에 반영하면 계약 검증이 쉬워집니다.
- 프런트엔드가 TypeScript를 사용할 경우, 같은 JSON을 TypeScript 탭에도 붙여 인터페이스를 맞춰 봅니다.
- 컨트롤러/서비스/리포지토리 계층 중 어느 곳에서 DTO를 도메인 모델로 전환할지 팀 합의를 남기고, 주석 대신 README에 흐름을 기록합니다.
- 배치 작업이나 메시지 소비자처럼 입력이 비동기인 경우, 예외 처리 정책(드롭/리큐/데드레터)을 DTO 주석이나 팀 위키에 함께 명시합니다.
마지막으로, 변환된 클래스 파일을 PR에 포함하고 JSON 샘플을 함께 올리면 리뷰어가 설계 의도를 이해하기 쉽습니다. JSON2Class는 초안을 빠르게 만드는 도구이며, 팀의 도메인 언어와 규칙을 더해 완성본으로 다듬는 과정을 잊지 마세요.
지금 JSON 붙여넣기