-
[Troubleshooting] 기하학 데이터(Point)를 JSON으로 반환하면 안되는 이유프로그램 개발일지 2025. 11. 22. 17:26반응형
1. 문제 상황
스포츠 바우처 시설 정보를 제공하는 API를 개발하던 중이었다. DB에는 위치 기반 검색(내 주변 헬스장 찾기 등)을 염두에 두고 좌표 데이터를 Point로 저장해 둔 상태였다.
"뭐, 그냥 조회해서 내보내면 되겠지?" 하고 findAll()을 호출했는데... Postman에 뜬 건 아름다운 데이터가 아니라 빨간맛 500 에러였다.
{ "timestamp": "2025-11-22T...", "status": 500, "error": "Internal Server Error", "message": "Type definition error: [simple type, class org.locationtech.jts.geom.Point]..." }서버 로그를 보니 Jackson 라이브러리가 비명을 지르고 있었다. InvalidDefinitionException: No serializer found for class org.locationtech.jts.geom.Point ...
2. 원인 분석
범인은 List<Facility> 엔티티를 그대로 반환하려고 했던 나의 안일함이었다.
Spring Boot의 기본 JSON 변환기인 Jackson은 자바 객체를 JSON으로 직렬화할 때, 기본적으로 Getter를 찾아 값을 꺼낸다. String이나 Integer는 쉽다. 하지만 Point 객체는 달랐다.
Point 객체 내부를 뜯어보면 대충 이렇게 생겼다.
// Point 객체의 대략적인 내부 (의사 코드) public class Point { private GeometryFactory factory; private CoordinateSequence coordinates; // <- 여기서부터 Jackson이 "이게 뭐야?" 시전 private double x; private double y; private int SRID; // ... 엄청 복잡함 }Jackson 입장에서는 "이 복잡한 객체를 { } 안에 어떻게 구겨 넣으라는 거야?" 라며 파업을 선언한 것이다. 이게 바로 순환 참조나 직렬화 불가능 객체를 만났을 때 발생하는 전형적인 500 에러였다.
3. 해결 과정
해결책은 간단했다. "Jackson이 모르는 건 안 주면 된다."
프론트엔드(혹은 지도 API)가 필요한 건 복잡한 Point 객체가 아니라, 단순히 좌표를 찍을 숫자 2개(위도, 경도)일 뿐이다.
그래서 응답 전용 DTO (FacilityResponseDto)를 도입했다. 여기서 Point 객체를 분해해서 Jackson이 아주 좋아하는 Double 타입으로 변환했다.
[수정된 DTO 코드]
@Getter public class FacilityResponseDto { private String name; private Double latitude; // 위도 (y) private Double longitude; // 경도 (x) public FacilityResponseDto(Facility facility) { this.name = facility.getName(); if (facility.getLocation() != null) { this.latitude = facility.getLocation().getY(); // 위도 this.longitude = facility.getLocation().getX(); // 경도 } } }[결과] 이제 API는 500 에러 대신 깔끔한 JSON을 뱉어낸다.
{ "name": "서울 체육관", "latitude": 37.5665, "longitude": 126.9780 }
4. 그럼 왜 DB에는 Point로 저장했나?
여기서 의문이 들 수 있다. "어차피 Double로 줄 거면, 처음부터 DB에 Double로 저장하면 되는 거 아냐? 왜 굳이 귀찮게 Point를 써?"
- Double로 저장했다면? (비효율의 극치)
- "내 주변 1km 헬스장 찾아줘" 기능을 만들 때, DB에 있는 25,000개 데이터 하나하나마다 피타고라스 정리나 하버사인 공식을 돌려야 한다. (Full Table Scan)
- 데이터가 늘어날수록 서버는 느려진다.
- Point로 저장했다면? (공간 인덱스의 마법)
- MySQL/MariaDB는 공간 데이터에 특화된 R-Tree를 지원한다.
- ST_Distance_Sphere 같은 함수를 쓰면, 인덱스를 타고 바로 내 주변 시설을 찾아낸다.
결론:
- DB(저장소): 계산과 검색 성능을 위해 Point를 쓴다. (Expert 영역)
- DTO(반환값): 통신 안정성과 프론트엔드 편의성을 위해 Double을 쓴다. (Delivery 영역)
반응형'프로그램 개발일지' 카테고리의 다른 글
AI가 막차시간이 언제인지 알려주는 막차지킴이 (Feat: Play MCP) (0) 2026.02.11 귀멸의 칼날 프리미어 좌석 오픈 알리미 만들기 (다른 영화도 적용 가능) (5) 2025.08.01 깃허브 코드리뷰 gemini에게 시키기!!! (무료, 간단) (10) 2025.07.31 AWS x 코드트리 "절대 포기 못하는 코딩 기초 챌린지" 체험기 (1) 2025.05.13 [SKADA] SK AI Data Academy (sk mysuni) 초급반 후기 (0) 2024.08.20 - Double로 저장했다면? (비효율의 극치)