## Django에서 gettext_lazy를 JSON Key로 쓸 때 생기는 뒷목 잡는 상황 {#sec-66ae94ff1145} > 결론의 한줄 정리 : **JSON Key는 반드시 '진짜' 문자열이어야 한다** , 하지만 Django의 `gettext_lazy`가 반환하는 것은 사실 문자열이 아니라 `__proxy__`라는 객체이다. ## 1. 잘 돌아가던 코드가 왜 갑자기? {#sec-4a4b34e6a73c} [[Django]] 개발 중 다국어 처리를 위해 `gettext_lazy`를 쓰는 건 일상적인 일이죠. 그런데 평소처럼 잘 쓰다가 어느 날 갑자기 JSON 응답(JsonResponse) 과정에서 직렬화(Serialization) 에러가 터질 때가 있습니다. 보통은 에러 로그를 봐도 `__proxy__` 어쩌구 하는 알 수 없는 메시지만 뜨니 당황하기 마련인데요. 원인을 뜯어보면 의외로 단순한 곳에 범인이 있습니다. ![infographic about gettext_lazy and JSON key problem](/media/whitedec/blog_img/gettext_lazy_json_error.webp) ## 2. 범인은 "Key"에 있었다 {#sec-361c46af305d} 결론부터 말하면, `gettext_lazy` 객체는 딕셔너리의 **Value**로 들어갈 땐 조용히 넘어가 주지만, **Key**로 들어가는 순간 문제를 일으킵니다. | 구분 | Value에 썼을 때 | Key에 썼을 때 | | :--- | :--- | :--- | | **작동 여부** | 정상 (자동 str 변환됨) | **에러 발생** | | **이유** | JSON 직렬화 시 자동 형변환 지원 | JSON Key는 반드시 '진짜' 문자열이어야 함 | ## 3. 왜 이런 차이가 생길까? {#sec-80787a3802c2} `gettext_lazy`가 반환하는 건 진짜 String이 아니라 `__proxy__`라는 객체입니다. 이름 그대로 "나중에 필요할 때 번역해서 줄게"라고 약속만 해둔 상태죠. Python의 `json.dumps()`나 Django의 `JsonResponse`는 딕셔너리의 값을 훑으면서 이 '약속'을 알아서 문자열로 풀어줍니다(Evaluation). 하지만 **딕셔너리의 Key**는 이야기가 다릅니다. [[JSON]] 표준상 Key는 무조건 문자열이어야 하는데, 이 과정에서 `__proxy__` 객체가 자동으로 변환되지 않고 그대로 부딪히니 에러가 나는 겁니다. ```python # ✅ 문제 없음: Value는 알아서 str로 변환됩니다. json.dumps({'language': _('Korean')}) # ❌ 에러: Key는 스스로 변환되지 못하고 거절당합니다. json.dumps({_('Korean'): 'language'}) ``` ## 4. 어떻게 해결할까? {#sec-87b95d49caed} ### 방법 1: 그냥 `gettext` 쓰기 (가장 깔끔) {#sec-d736d0465485} Lazy 방식이 꼭 필요한 상황이 아니라면, 호출 즉시 문자열을 반환하는 `gettext`를 사용하는 게 가장 속 편합니다. ```python from django.utils.translation import gettext as _ LANGUAGE_MAP = { "en": _("English"), "ko": _("Korean"), } ``` * **참고:** 다만 이 방식은 앱이 로드되는 시점에 번역이 결정되므로, 동적인 번역 환경이 중요하다면 주의가 필요합니다. ### 방법 2: 직렬화 직전에 `str()` 끼워넣기 {#sec-fc5762a0f995} 이미 Lazy 객체를 광범위하게 쓰고 있다면, [[JSON]]으로 넘기기 바로 직전에 강제로 문자열 변환을 해줍니다. ```python # Key로 활용하기 전에 str()로 한 번 감싸서 '진짜 문자열'로 만듭니다. lang_key = str(LANGUAGE_MAP.get(code)) ``` ### 방법 3: 번역의 주체를 클라이언트로 넘기기 {#sec-f61be7ca95b2} 만약 프론트를 Django의 템플릿을 사용하지 않고 별도의 프론트 클라이언트를 사용하는 방식이라면 (요즘은 이런 방식이 트렌드인지도 모르겠습니다만.), Django(혹은 DRF) 서버는 `en`, `ko` 같은 코드(Code)만 던져주고, 실제 화면에 뿌리는 텍스트는 프론트엔드(React, Vue 등)의 i18n 라이브러리에서 처리하는 방식입니다. 서버 로직이 가벼워진다는 장점이 있죠. --- ## 마치며 {#sec-0d2251a657d4} json.dumps() 와 `gettext_lazy()`와 함께 사용하던 중, "어제는 됐는데 오늘은 왜 안 되지?" 싶은 상황이 생겼다면, 아마 어제는 운 좋게 Value에만 썼고 오늘은 Key에 썼을 가능성이 높습니다. `gettext_lazy`는 편리하지만, 결국 '진짜 문자열'이 아니라는 점을 늘 염두에 두어야 합니다. 특히 JSON을 다룰 때는 더더욱요. 비슷한 에러로 삽질하고 계신 분들께 도움이 되었길 바랍니다.