개발 중에 흥미로운 문제에 부딪쳤다. Supabase를 사용해 연락처 목록을 가나다순으로 정렬하려 했는데, 예상과 다른 결과가 나왔다.
문제 상황
처음에는 Supabase의 기본 정렬 기능을 사용했다:
const { data, error } = await supabase
.from('contacts')
.select('contacts_id, name, relationship_level, contacts_profile_img, is_pinned')
.eq('user_id', userId)
.order('name', { ascending: true });
하지만 결과는 예상과 달랐다. 예를 들어 '감자', '나비', '감나무', '날치'라는 이름이 있다면, 한글 자모음 순서로는 '감나무', '감자', '나비', '날치' 순으로 정렬되어야 하는데, 실제로는 '감자', '나비', '날치', '감나무' 같은 순서로 정렬되었다.
원인을 조사해보니 Supabase의 기본 정렬은 단순 문자코드 비교 방식을 사용하기 때문에 한글의 자모 구조와 글자 수에 영향을 받아 제대로 된 가나다순 정렬이 되지 않는 것이었다.
해결 방법
해결책은 서버에서의 정렬을 포기하고 클라이언트에서 JavaScript의 localeCompare 메서드를 사용하는 것이었다:
export const getContacts = async (userId: string): Promise<ContactItemType[]> => {
try {
const { data, error } = await supabase
.from('contacts')
.select('contacts_id, name, relationship_level, contacts_profile_img, is_pinned')
.eq('user_id', userId);
// 서버 정렬은 사용하지 않음
if (error) {
throw error;
}
// 한국어 로케일을 사용한 정렬
const sortedData = [...(data || [])].sort((a, b) =>
a.name.localeCompare(b.name, 'ko-KR')
);
return sortedData;
} catch (error) {
console.error('연락처를 불러오는 중 오류가 발생했습니다:', error);
throw error;
}
};
localeCompare 메서드는 문자열을 비교할 때 특정 언어와 지역의 규칙을 따르도록 해주는 JavaScript의 내장 메서드다. 두 번째 인자로 로케일을 지정할 수 있는데, 'ko-KR'은 한국어(대한민국) 로케일을 의미한다.
예를 들면
a.name.localeCompare(b.name, 'ko-KR')
이 코드가 작동하는 방식은 다음과 같다:
- 두 문자열 a.name과 b.name을 비교한다.
- 'ko-KR' 로케일 규칙에 따라 비교하므로 한글의 자모음 순서와 발음 규칙이 정확히 반영된다.
- 반환값은 세 가지 중 하나다:
- 음수: a.name이 b.name보다 사전순으로 앞에 온다.
- 0: 두 문자열이 같다.
- 양수: a.name이 b.name보다 사전순으로 뒤에 온다.
- 이 반환값을 기준으로 JavaScript의 sort 메서드가 배열을 정렬한다.
이 방법의 장점은 단순히 문자 코드 값만 비교하는 것이 아니라, 한글의 특성을 고려한 정렬이 가능하다는 점이다. '가나다' 순서대로 올바르게 정렬되며, 글자 수나 초,중,종성을 갖고 있는 한글 구조에 영향을 받지 않는다.
결론
데이터베이스 쿼리 결과를 정렬할 때 언어별 특성을 고려해야 한다는 중요한 교훈을 얻었다. 특히 한글과 같은 복잡한 문자 체계를 다룰 때는 더욱 주의가 필요다고 생각한다. Supabase나 다른 데이터베이스 시스템을 사용할 때, 언어 특성에 맞는 정렬이 필요하다면 클라이언트 측에서의 2차 정렬을 고려해보는 것이 좋겠다.