
이번 글은 Backend / Java(JSON 직렬화·역직렬화) 영역에서 자주 쓰는 JSON 변환을 정리한다. Java에서 API 응답(JSON)을 다룰 때 핵심은 두 가지다.
여기서는 Gson을 사용해서 Gson 생성 방식 차이, Pretty Printing, Map ↔ JSON, DTO(User) ↔ JSON까지 흐름을 잡는다.
Gson은 Google에서 만든 JSON 라이브러리로, 자바 객체를 JSON으로 직렬화(serialize)하고 JSON을 다시 자바 객체로 역직렬화(deserialize)할 수 있다.
JSON을 다루는 흐름은 보통 아래처럼 연결된다.
Gson을 만드는 방법은 크게 2가지다.
즉, GsonBuilder는 “설정 가능한 생성기”고, 마지막에 create()로 Gson 인스턴스를 만든다.
Map은 JSON의 Key-Value 구조와 1:1로 잘 맞는다. 그래서 구조가 유동적인 데이터(옵션/설정/간단한 임시 JSON)에서는 Map이 편하다.
다만 JSON → Map으로 받을 때는 제네릭 타입 정보가 사라지기 때문에 값이 Object로 떨어지고, 숫자도 Double로 들어오는 등 타입이 깔끔하지 않을 수 있다. 이건 Gson의 일반적인 특성이라서 “빠르게 찍어보기/디버깅”에는 Map이 좋고, “정확한 타입”은 DTO가 좋다.
User처럼 필드 구조가 고정된 “도메인 데이터”는 DTO 클래스로 다루는 게 정석이다.
이 방식은 Spring Boot에서 API 응답을 DTO로 받고, 다시 DTO로 응답하는 구조와 완전히 동일하다.
아래 코드는 네가 준 예제를 “Gson 생성 → Map JSON → DTO JSON → JSON에서 Map 변환” 흐름으로 정리한 것이다.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.HashMap;
import java.util.Map;
public class GsonExample {
public static void main(String[] args) {
// 1) Gson() 생성자를 통해 생성 (기본 설정)
Gson gson1 = new Gson();
System.out.println(gson1.toString());
// 2) GsonBuilder() 생성자를 통해 생성 (옵션 설정 가능)
Gson gson2 = new GsonBuilder().create();
// Pretty Printing(가독성 좋은 JSON 출력)
Gson gson3 = new GsonBuilder().setPrettyPrinting().create();
// Map -> JSON
Map<String, String> map = new HashMap<>();
map.put("username", "gildong");
map.put("password", "1234");
map.put("name", "길동");
map.put("email", "gildong@gmail.com");
String json2 = gson2.toJson(map);
System.out.println(json2);
System.out.println();
String json3 = gson3.toJson(map);
System.out.println(json3);
// DTO(User) 생성
User user = UserService.createUser();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
// 객체 -> Json
String userJson = gson.toJson(user);
System.out.println(userJson);
// Json -> 객체
User user2 = gson.fromJson(userJson, User.class);
System.out.println(user2);
System.out.println(user2.getAddress());
// Json -> Map (디버깅/동적 구조 확인 용도)
Map<String, Object> mapObj = gson.fromJson(userJson, Map.class);
System.out.println(mapObj);
}
}
DTO는 “데이터 구조”를 표현하는 클래스고, JSON 파싱/응답에서 가장 깔끔한 타입 안정성을 제공한다.
import java.util.List;
import lombok.Builder;
import lombok.Data;
@Builder
@Data
public class User {
private int userCode;
private String username;
private String password;
private String name;
private String email;
private String address;
private String phone;
private List<String> hobby;
}
실무에서도 “테스트 데이터”를 만들 때 Service에서 DTO를 만들어 반환하는 방식은 매우 자주 쓴다. 여기서는 Builder 패턴으로 User 객체를 깔끔하게 생성하고 있다.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class UserService {
public static User createUser() {
List<String> hobby = new ArrayList<>();
hobby.addAll(Arrays.asList(new String[]{"축구", "농구", "야구", "골프"}));
User user = User.builder()
.userCode(1)
.username("gildong")
.password("1234")
.name("길동")
.email("gildong@gmail.com")
.address("부산 진구")
.phone("010-1234-5678")
.hobby(hobby)
.build();
return user;
}
}
JSON 변환은 “문법”보다 “구조 선택(Map vs DTO)”가 더 중요하다. 동적 데이터는 Map으로 빨리 처리하고, 핵심 도메인 데이터는 DTO로 안정적으로 관리하는 습관이 실서비스 유지보수에서 큰 차이를 만든다.