Gson — Google에서 제공하는 Java 객체를 JSON으로 직렬화하고 다시 변환하는 라이브러리로, Android 개발에서 널리 사용됩니다. 파서를 수동으로 작성하지 않고도 복잡한 객체 그래프를 간결한 JSON 문자열로 변환할 수 있습니다. Google Gson, 2024에 따르면, 이 라이브러리는 GitHub에서 23,000개 이상의 별을 보유하고 있으며 Java 및 Kotlin 생태계에서 JSON 작업을 위한 가장 인기 있는 솔루션 중 하나로 남아 있습니다.
핵심 사항
Gson은 객체를 JSON 표현으로 변환하기 위해 Google이 개발한 Java 라이브러리입니다. 리플렉션을 사용하여 클래스 구조를 분석하므로 사전 구성 없이도 작동할 수 있습니다. Gson은 임의의 Java 객체, 컬렉션, 배열, 제네릭 및 중첩 클래스를 지원합니다. 라이브러리는 기본 사용에 어노테이션이 필요하지 않지만 미세 조정을 위해 제공합니다. 리플렉션의 주요 단점은 초기화 중 성능 저하와 컴파일 시간 최적화 불가능이며, 이는 Android 애플리케이션의 콜드 스타트에서 수백 개의 모델을 역직렬화할 때 특히 두드러집니다. 그럼에도 불구하고 Gson은 안정성과 방대한 문서 덕분에 대부분의 프로젝트에서 신뢰할 수 있는 선택으로 남아 있습니다.
Gson은 2008년 Google에 의해 출시되었으며 빠르게 Android 애플리케이션에서 JSON의 사실상 표준이 되었습니다. Moshi와 kotlinx.serialization이 등장하기 전까지 Gson은 Kotlin 프로젝트에서 유일하게 인기 있는 선택이었습니다. 통합의 용이성 — build.gradle에 단일 종속성 추가 — 과 필수 어노테이션이 없어 Gson은 모든 수준의 개발자 사이에서 인기를 얻었습니다.
// build.gradle에 Gson 추가
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
}
// 기본 사용법
data class User(
val id: Int,
val name: String,
val email: String
)
val gson = Gson()
val user = User(1, "John", "john@test.com")
val json = gson.toJson(user)
println(json) // {"id":1,"name":"John","email":"john@test.com"}
기본 직렬화 외에도 Gson은 동작 구성을 위한 GsonBuilder를 제공합니다: 날짜 형식 지정, HTML 이스케이프 비활성화, 키 케이스 및 사용자 정의 인스턴스. GsonBuilder는 라이브러리가 자동으로 처리할 수 없는 유형에 대한 사용자 정의 JsonSerializer 및 JsonDeserializer를 등록할 수도 있습니다. 구성 유연성 덕분에 GsonBuilder는 최신 Android 개발에서 특정 프로젝트 요구 사항에 라이브러리를 적용할 때 없어서는 안 될 유용한 도구입니다.
toJson은 리플렉션을 통해 필드를 분석하여 Java 객체를 JSON 문자열로 변환합니다. 기본적으로 Gson은 transient 및 static을 제외한 모든 필드를 포함합니다. 이 메서드는 기본형, 객체, 컬렉션 및 배열 등 모든 유형을 지원합니다. fromJson은 역연산을 수행하여 JSON 문자열과 대상 객체 클래스를 받아 필드가 채워진 인스턴스를 반환합니다.
직렬화 중에 Gson은 중첩된 필드를 포함한 모든 객체 필드를 재귀적으로 순회합니다. 순환 참조는 StackOverflowError를 유발하므로 @Expose 어노테이션이나 사용자 정의 어댑터를 통해 제외해야 합니다. 컬렉션의 경우 Gson은 요소 유형을 유지하지만 제네릭이 있는 목록을 역직렬화할 때는 유형 정보를 유지하기 위해 TypeToken이 필요합니다.
// 중첩 객체가 있는 data class
data class Address(
val city: String,
val street: String
)
data class Employee(
val id: Int,
val name: String,
val address: Address
)
val gson = Gson()
val employee = Employee(1, "Alice",
Address("New York", "5th Ave"))
// JSON으로 직렬화
val json = gson.toJson(employee)
// JSON에서 역직렬화
val jsonString = """
{"id":2,"name":"Bob","address":{"city":"London","street":"Baker St"}}
"""
val parsed = gson.fromJson(jsonString, Employee::class.java)
Gson은 직렬화 프로세스를 관리하기 위한 어노테이션 세트를 제공합니다. @SerializedName은 필드 이름과 다른 JSON 키 이름을 지정합니다. @Expose는 필드가 직렬화에 포함되는지 여부를 제어합니다: GsonBuilder.excludeFieldsWithoutExposeAnnotation()을 통해 생성된 Gson은 @Expose가 있는 필드만 처리합니다. @Since 및 @Until은 필드 버전 관리를 제어합니다.
@SerializedName 어노테이션은 이름 불일치 문제를 해결합니다: 서버가 snake_case를 사용하는 반면 코드에서는 camelCase를 사용할 수 있습니다. 어노테이션은 값과 이전 버전과의 호환성을 위한 선택적 대안을 허용합니다. @Expose는 민감한 필드(비밀번호, 토큰)를 @Expose(serialize = false)로 표시하여 직렬화에서 숨길 수 있습니다. 포함 및 제외 외에도 @Expose는 GsonBuilder.excludeFieldsWithoutExposeAnnotation과 결합하여 필드의 화이트리스트를 생성할 수 있으며, 이는 많은 필드가 있는 객체를 직렬화할 때 공격 표면을 제어하는 데 도움이 됩니다.
// Gson 어노테이션이 있는 모델
data class UserResponse(
@SerializedName("user_id")
val userId: Int,
@SerializedName("full_name",
alternate = [Alternative("name")])
val fullName: String,
@Expose(serialize = false)
val password: String
)
// @Expose 필터링이 있는 Gson
val gson = GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting()
.create()
val user = UserResponse(1, "John", "secret123")
println(gson.toJson(user))
// {"user_id":1,"full_name":"John"} — 비밀번호 제외됨
제네릭 문제는 Java 및 Kotlin에서 컴파일 시간의 유형 소거입니다. Gson이 List<User>를 역직렬화할 때 요소 유형을 알 수 없어 List<Map<String, Any>>를 반환합니다. 유형 정보를 유지하기 위해 Gson은 TypeToken을 제공합니다 — 익명 클래스를 통해 유형 매개변수를 캡처하는 추상 클래스입니다. TypeToken이 없으면 개발자가 각 요소를 Map에서 대상 유형으로 수동 변환해야 하므로 코드가 번거로워지고 성능이 저하됩니다.
TypeToken은 유형 소거 문제를 해결합니다. 개발자는 필요한 유형 매개변수로 TypeToken의 익명 하위 클래스를 만들고, Gson은 클래스 서명의 정보를 사용하여 올바르게 역직렬화합니다. TypeToken은 Map, Set 및 중첩 제네릭을 포함한 모든 매개변수화된 유형과도 작동합니다. 특히 Map<String, List<User>>의 경우 완전한 중첩 유형 서명이 있는 TypeToken이 필요합니다. 그렇지 않으면 Gson은 List<User> 대신 값을 List<Map<String, Any>>로 역직렬화합니다.
// 목록 역직렬화를 위한 TypeToken
data class Product(
val id: Int,
val title: String,
val price: Double
)
val jsonArray = """
[
{"id":1,"title":"Phone","price":599.0},
{"id":2,"title":"Laptop","price":1299.0}
]
"""
val gson = Gson()
val listType = object : TypeToken<List<Product>>() {}
val products: List<Product> =
gson.fromJson(jsonArray, listType.type)
// 사용자 정의 역직렬화기
class LocalDateAdapter :
JsonDeserializer<LocalDate> {
override fun deserialize(
json: JsonElement,
typeOfT: java.lang.reflect.Type,
context: JsonDeserializationContext
): LocalDate {
return LocalDate.parse(json.asString)
}
}
사용자 정의 직렬화 로직을 위해 Gson은 JsonSerializer 및 JsonDeserializer 인터페이스를 지원합니다. 이들은 GsonBuilder.registerTypeAdapter()를 통해 등록되며 라이브러리가 자동으로 직렬화할 수 없는 유형(Java 8 날짜, 비표준 값이 있는 Enum, 소스 코드에 액세스할 수 없는 타사 클래스)을 처리할 수 있습니다. 어댑터를 구현할 때 성능 모니터링이 중요합니다: 사용자 정의 어댑터 내에서 리플렉션을 호출하면 수동 제어의 이점이 무효화되므로 직접 메서드 및 필드 호출이 선호됩니다. Gson 생태계에는 UUID, Optional 및 Joda-Time 날짜 휠과 같은 일반적인 유형에 대한 어댑터를 제공하는 gson-extras 모듈도 있습니다.
GsonBuilder는 직렬화 미세 조정을 위한 수십 가지 메서드를 제공합니다. setPrettyPrinting은 가독성을 위해 출력 JSON에 들여쓰기와 줄 바꿈을 추가합니다. disableHtmlEscaping은 문자열에서 HTML 문자 이스케이프를 비활성화합니다. setDateFormat은 날짜 형식을 지정하며, 비표준 시간 표현을 사용하는 서버와 작업할 때 중요합니다. setLenient는 특정 JSON 형식 오류를 무시하는 관대한 구문 분석 모드를 활성화합니다. addDeserializationExclusionStrategy는 사용자 정의 전략에 따라 프로그래밍 방식으로 필드를 역직렬화에서 제외할 수 있습니다. 디버깅의 경우 setPrettyPrinting과 로깅을 결합하면 유용합니다 — JSON 응답을 로그에서 읽을 수 있게 만들고 불일치 찾기를 단순화합니다.
GsonBuilder의 중요한 기능은 @Since 및 @Until 어노테이션을 통한 필드 버전 관리입니다. 개발자는 setVersion을 통해 객체 버전을 지정하고 Gson은 버전 어노테이션에 따라 필드를 자동으로 포함하거나 제외합니다. 이는 동일한 모델이 서버 프로토콜의 다른 버전에 사용되는 API 진화 중에 유용합니다. GsonBuilder는 패밀리 유형의 전역 처리를 위한 TypeAdapterFactory 등록과 복잡한 Map 키의 올바른 작업을 위한 complexMapKeySerialization도 지원합니다.
자주 묻는 질문
Gson은 Java 객체를 JSON으로 변환하기 위한 Google 라이브러리입니다. 서버 응답 구문 분석, 요청 직렬화 및 로컬 저장소에 데이터 저장을 위해 Android 애플리케이션에서 널리 사용됩니다.
기본적으로 Gson은 직렬화 중에 null 필드를 건너뜁니다. null 값을 포함하려면 GsonBuilder.serializeNulls()를 사용하세요. 역직렬화 중에 JSON에 없는 필드는 null로 남거나 해당 유형의 기본값을 취합니다.
Moshi는 Kotlin 클래스에 리플렉션을 사용하지 않아 더 높은 성능과 예측 가능한 동작을 제공합니다. Moshi는 Kotlin의 null 안전성을 올바르게 처리하는 반면, Gson은 null을 non-null 필드로 역직렬화하여 예외를 발생시킬 수 있습니다.
@SerializedName은 이름이 일치하지 않을 때 JSON 키를 클래스 필드에 바인딩합니다. 예를 들어 kotlinName 필드와 "kotlin_name" JSON 키의 경우 @SerializedName("kotlin_name") 어노테이션이 올바른 변환을 보장합니다.
TypeToken은 익명 클래스를 통해 유형 매개변수를 캡처하는 추상 클래스입니다. 유형 소거로 인해 Gson이 런타임에 요소 유형을 복구할 수 없기 때문에 컬렉션 및 기타 매개변수화된 유형을 역직렬화하는 데 필요합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.