질문의 배경:
Go에는 기본적으로 제네릭 맵과 같은 범용 컨테이너가 없으며, Go 1.18부터 제네릭이 도입되었지만, 동적 구조체에는 여전히 map[string]interface{}가 많이 사용됩니다. 이 타입은 문자열 키를 사용하여 모든 유형의 값을 저장할 수 있게 해줍니다.
문제:
이 패턴은 딕셔너리/JSON 객체와 유사하며, 직렬화, 미들웨어 작업, 구조가 없는 데이터(예: encoding/json을 통한 JSON 파서)에서 자주 사용됩니다. 하지만 값에 접근할 때는 수동으로 타입을 변환해야 하며, 암시적인 값과 키가 없는 경우에 주의해야 합니다.
해결책:
입력 데이터의 구조가 알려지지 않았을 때 map[string]interface{}를 사용하세요. 키의 존재를 조심스럽게 확인하고, exists-idiom 후에만 타입 변환(type assertion)을 수행하는 것이 좋습니다. 이러한 맵을 비즈니스 로직에서 "깊게" 유지하는 것보다 시스템 경계에서 어댑터로 사용하는 것이 좋습니다.
코드 예시:
obj := map[string]interface{}{ "int": 42, "str": "안녕하세요", "flag": true, } if v, ok := obj["int"]; ok { n, success := v.(int) if success { fmt.Println(n) } }
핵심 특징:
map[string]interface{}에서 키가 없을 때 키로 값을 에러 없이 접근할 수 있는가?
아니요, 이는 interface{}에 대한 "zero value"(nil)를 반환하며, 특정 타입으로의 변환은 패닉을 발생시킵니다.
중첩 슬라이스나 다른 맵이 있는 map[string]interface{}를 직렬화할 때 무슨 일이 발생하는가?
JSON 직렬화는 구조를 올바르게 처리하지만, 기본적으로 지원되지 않는 타입(예: 지도, 채널, 함수)이 포함되어 있으면 직렬화 오류가 발생합니다.
map[string]interface{}에서 두 값을 ==로 비교할 수 있는가?
아니요, interface{}는 기본 값이 비교 가능한 경우에만 비교할 수 있습니다. 만약 맵이나 슬라이스가 포함되면 비교 시 패닉이 발생합니다.
애플리케이션의 모든 로직이 map[string]interface{} 객체에 기반하며, 각 컨트롤러/서비스가 이를 깊게 호출합니다.
장점:
단점:
map[string]interface{}는 외부 인터페이스, 입력/출력 데이터 작업에만 사용하고, 나중에 정상 구조로 변환합니다.
장점:
단점: