로케일 규칙에 따라 텍스트를 대문자 또는 소문자로 변환하는 방법
JavaScript를 사용하여 다양한 언어 및 문자 체계에 맞게 텍스트 대소문자를 올바르게 변경하기
소개
텍스트를 대문자와 소문자 사이에서 변환할 때, 이 작업이 모든 언어에서 동일하게 작동한다고 가정할 수 있습니다. 그러나 그렇지 않습니다. 다양한 문자 체계는 서로 다른 대소문자 변환 규칙을 따르며, 이러한 규칙을 고려하지 않으면 예상치 못한 결과가 발생할 수 있습니다.
자바스크립트는 표준 toUpperCase()와 toLowerCase() 메서드를 제공하는데, 이는 영어에서는 올바르게 작동하지만 다른 언어에서는 부정확한 결과를 생성할 수 있습니다. 로케일을 인식하는 메서드인 toLocaleUpperCase()와 toLocaleLowerCase()는 언어별 대소문자 변환 규칙을 적용하여 언어에 관계없이 텍스트가 올바르게 변환되도록 보장합니다.
이 강의에서는 대소문자 변환이 언어마다 다른 이유를 설명하고, 표준 메서드에서 발생하는 특정 문제를 보여주며, 국제 애플리케이션에서 대소문자 변환을 올바르게 처리하기 위해 로케일을 인식하는 메서드를 사용하는 방법을 보여줍니다.
로케일별로 대소문자 변환이 다른 이유
문자의 대문자와 소문자 버전은 모든 문자 체계에서 동일하게 작동하는 보편적인 개념이 아닙니다. 다양한 언어는 역사적인 쓰기 관행과 인쇄 관행에 기반하여 서로 다른 대소문자 변환 규칙을 발전시켰습니다.
영어에서 대소문자 변환은 간단합니다. 소문자 i는 대문자로 변환될 때 I가 되고, I는 소문자로 변환될 때 i가 됩니다. 이 관계는 영어 알파벳 전체에 적용됩니다.
다른 언어는 더 복잡한 규칙을 가지고 있습니다. 터키어는 두 개 대신 네 개의 서로 다른 i 문자를 가지고 있습니다. 독일어에는 ß(샤프 s) 문자가 있으며, 이는 대문자 변환에 특정 규칙이 있습니다. 그리스어는 단어 끝에 나타나는지 여부에 따라 시그마 문자의 다른 형태를 가지고 있습니다.
toUpperCase()와 toLowerCase()와 같은 표준 자바스크립트 메서드를 사용할 때, 변환은 영어 규칙을 따릅니다. 이는 다른 언어의 텍스트에서 부정확한 결과를 생성합니다. 로케일을 인식하는 메서드는 각 언어에 적합한 규칙을 적용하여 올바른 변환을 보장합니다.
터키어 i 문제
터키어는 로케일이 대소문자 변환에 왜 중요한지에 대한 가장 명확한 예시를 제공합니다. 영어와 달리, 터키어에는 i와 관련된 네 가지 구별되는 문자가 있습니다:
- 소문자 점 있는 i:
i(U+0069) - 대문자 점 있는 İ:
İ(U+0130) - 소문자 점 없는 ı:
ı(U+0131) - 대문자 점 없는 I:
I(U+0049)
터키어에서 소문자 점 있는 i는 대문자 점 있는 İ로 변환됩니다. 소문자 점 없는 ı는 대문자 점 없는 I로 변환됩니다. 이들은 서로 다른 발음과 의미를 가진 두 개의 별개 문자 쌍입니다.
표준 JavaScript 메서드는 영어 규칙을 따르며 점 있는 i를 점 없는 I로 변환합니다. 이는 터키어 단어의 의미를 변경하고 잘못된 텍스트를 생성합니다.
const turkish = "istanbul";
console.log(turkish.toUpperCase());
// 출력: "ISTANBUL" (잘못됨 - 점 없는 I 사용)
console.log(turkish.toLocaleUpperCase("tr"));
// 출력: "İSTANBUL" (올바름 - 점 있는 İ 사용)
도시 이름 Istanbul에는 점 있는 i 문자가 포함되어 있습니다. 터키어 규칙을 사용하여 대문자로 변환하면 점 있는 İ가 있는 İSTANBUL이 됩니다. 표준 toUpperCase()를 사용하면 점 없는 I가 있는 ISTANBUL이 생성되는데, 이는 터키어에서 올바르지 않습니다.
대문자 터키어 텍스트를 소문자로 변환할 때도 동일한 문제가 발생합니다.
const uppercase = "İSTANBUL";
console.log(uppercase.toLowerCase());
// 출력: "i̇stanbul" (잘못됨 - 결합 점 위 문자가 있는 i 생성)
console.log(uppercase.toLocaleLowerCase("tr"));
// 출력: "istanbul" (올바름 - 점 있는 i 생성)
터키어에서 점 있는 İ는 소문자로 변환될 때 점 있는 i가 되어야 합니다. 표준 toLowerCase()는 이를 올바르게 처리하지 못하고 결합 점 문자가 있는 소문자 i를 생성할 수 있는데, 이는 비슷해 보이지만 기술적으로 올바르지 않습니다.
기타 로케일별 대소문자 규칙
터키어만이 특별한 대소문자 변환 규칙을 가진 언어는 아닙니다. 다른 여러 언어도 로케일별 처리가 필요합니다.
독일어에는 ß(샤프 s) 문자가 있는데, 전통적으로 대문자 형태가 없었습니다. 2017년에 유니코드는 대문자 ẞ 문자를 추가했지만, 많은 시스템에서는 여전히 대문자로 변환할 때 ß를 SS로 변환합니다.
const german = "Straße";
console.log(german.toUpperCase());
// 출력: "STRASSE" (ß를 SS로 변환)
console.log(german.toLocaleUpperCase("de"));
// 출력: "STRASSE" (역시 ß를 SS로 변환)
대부분의 JavaScript 환경에서 두 메서드 모두 독일어 텍스트에 대해 동일한 결과를 생성합니다. 로케일 매개변수는 출력을 변경하지 않지만, 로케일 인식 메서드를 사용하면 향후 구현에서 유니코드 처리가 변경되더라도 코드가 올바르게 유지됩니다.
그리스어에는 세 가지 다른 형태의 시그마 문자가 있습니다. 소문자 형태는 단어 중간에 σ를 사용하고 단어 끝에 ς를 사용합니다. 두 형태 모두 동일한 대문자 Σ로 변환됩니다.
리투아니아어에는 점 있는 문자에 대한 특별한 규칙이 있습니다. i 문자는 특정 발음 구별 기호와 결합될 때 대문자로 변환되어도 점을 유지합니다. 이는 로케일 인식 메서드가 특정 문자 조합을 처리하는 방식에 영향을 미칩니다.
로케일 인식 대문자 변환을 위한 toLocaleUpperCase 사용
toLocaleUpperCase() 메서드는 로케일별 대소문자 매핑 규칙을 사용하여 문자열을 대문자로 변환합니다. 문자열에서 호출하고 선택적으로 로케일 식별자를 인수로 전달할 수 있습니다.
const text = "istanbul";
const result = text.toLocaleUpperCase("tr");
console.log(result);
// 출력: "İSTANBUL"
이것은 터키어 규칙을 사용하여 문자열을 대문자로 변환합니다. 점이 있는 i는 터키어에서 올바른 점이 있는 İ로 변환됩니다.
동일한 텍스트를 다른 로케일 규칙을 사용하여 변환할 수 있습니다.
const text = "istanbul";
console.log(text.toLocaleUpperCase("tr"));
// 출력: "İSTANBUL" (터키어 규칙 - 점이 있는 İ)
console.log(text.toLocaleUpperCase("en"));
// 출력: "ISTANBUL" (영어 규칙 - 점이 없는 I)
로케일 매개변수는 어떤 대소문자 변환 규칙이 적용되는지 결정합니다. 터키어 규칙은 i의 점을 유지하는 반면, 영어 규칙은 그렇지 않습니다.
인수 없이 toLocaleUpperCase()를 호출하면 JavaScript 런타임 환경에 의해 결정된 시스템 로케일을 사용합니다.
const text = "istanbul";
const result = text.toLocaleUpperCase();
console.log(result);
// 출력은 시스템 로케일에 따라 다름
출력은 JavaScript 환경의 기본 로케일에 따라 달라지며, 일반적으로 사용자의 운영 체제 설정과 일치합니다.
로케일 인식 소문자 변환을 위한 toLocaleLowerCase 사용
toLocaleLowerCase() 메서드는 로케일별 대소문자 매핑 규칙을 사용하여 문자열을 소문자로 변환합니다. toLocaleUpperCase()와 동일한 방식으로 작동하지만 대문자 대신 소문자로 변환합니다.
const text = "İSTANBUL";
const result = text.toLocaleLowerCase("tr");
console.log(result);
// 출력: "istanbul"
이것은 터키어 규칙을 사용하여 대문자 터키어 텍스트를 소문자로 변환합니다. 점이 있는 İ는 점이 있는 i로 변환되어 올바른 소문자 형태를 생성합니다.
로케일 매개변수 없이 표준 toLowerCase() 또는 기본 로케일 설정이 있는 toLocaleLowerCase()는 터키어 문자를 올바르게 처리하지 못할 수 있습니다.
const text = "İSTANBUL";
console.log(text.toLowerCase());
// 출력: "i̇stanbul" (잘못됨 - 위에 결합 점이 있는 i)
console.log(text.toLocaleLowerCase("tr"));
// 출력: "istanbul" (올바름 - 점이 있는 i)
터키어의 점이 있는 İ는 올바르게 변환하기 위해 터키어 대소문자 규칙이 필요합니다. tr 로케일을 사용한 로케일 인식 메서드를 사용하면 올바른 변환이 보장됩니다.
또한 터키어에서 점이 없는 I를 처리할 수 있으며, 소문자로 변환할 때 점이 없어야 합니다.
const text = "IRAK";
console.log(text.toLocaleLowerCase("tr"));
// 출력: "ırak" (터키어 규칙 - 점이 없는 ı)
console.log(text.toLocaleLowerCase("en"));
// 출력: "irak" (영어 규칙 - 점이 있는 i)
IRAK(터키어로 이라크) 단어는 점이 없는 I를 사용합니다. 터키어 대소문자 규칙은 이를 소문자 점이 없는 ı로 변환하는 반면, 영어 규칙은 점이 있는 i로 변환합니다.
로케일 식별자 지정하기
'toLocaleUpperCase()'와 'toLocaleLowerCase()' 모두 BCP 47 형식의 로케일 식별자를 허용합니다. 이는 Intl API와 다른 국제화 기능 전반에 사용되는 것과 동일한 언어 태그입니다.
const text = "Straße";
console.log(text.toLocaleUpperCase("de-DE"));
// 출력: "STRASSE"
console.log(text.toLocaleUpperCase("de-AT"));
// 출력: "STRASSE"
console.log(text.toLocaleUpperCase("de-CH"));
// 출력: "STRASSE"
이 예제들은 독일, 오스트리아, 스위스의 다양한 독일어 로케일을 사용합니다. 대소문자 변환 규칙은 일반적으로 동일 언어의 지역 변형 간에 일관성이 있으므로, 세 가지 모두 동일한 출력을 생성합니다.
로케일 식별자 배열을 전달할 수도 있습니다. 이 메서드는 배열의 첫 번째 로케일을 사용합니다.
const text = "istanbul";
const result = text.toLocaleUpperCase(["tr", "en"]);
console.log(result);
// 출력: "İSTANBUL"
'tr'이 배열의 첫 번째 로케일이므로 메서드는 터키어 규칙을 적용합니다. 런타임이 첫 번째 로케일을 지원하지 않는 경우, 배열의 후속 로케일로 대체됩니다.
브라우저의 로케일 환경설정 사용하기
웹 애플리케이션에서는 사용자의 브라우저 로케일 환경설정을 사용하여 어떤 대소문자 변환 규칙을 적용할지 결정할 수 있습니다. 'navigator.language' 속성은 사용자가 선호하는 언어를 반환합니다.
const userLocale = navigator.language;
const text = "istanbul";
const result = text.toLocaleUpperCase(userLocale);
console.log(result);
// 출력은 사용자의 로케일에 따라 다름
// 터키어 사용자: "İSTANBUL"
// 영어 사용자: "ISTANBUL"
이는 사용자의 언어 설정에 따라 올바른 대소문자 규칙을 자동으로 적용합니다. 터키어 사용자는 터키어 규칙을 사용하여 변환된 텍스트를 보고, 영어 사용자는 영어 규칙을 사용하여 변환된 텍스트를 보는 식입니다.
전체 로케일 환경설정 배열을 전달하여 대체 동작을 활성화할 수도 있습니다.
const text = "istanbul";
const result = text.toLocaleUpperCase(navigator.languages);
console.log(result);
이 메서드는 사용자 환경설정의 첫 번째 로케일을 사용하여 특정 로케일을 사용할 수 없을 때 더 나은 대체 처리를 제공합니다.
표준 메서드와 로케일 인식 메서드 비교
표준 toUpperCase() 및 toLowerCase() 메서드는 영어에서는 올바르게 작동하지만 다른 언어에서는 실패할 수 있습니다. 로케일 인식 메서드인 toLocaleUpperCase() 및 toLocaleLowerCase()는 로케일별 규칙을 적용하여 모든 언어를 올바르게 처리합니다.
const turkish = "Diyarbakır";
// 표준 메서드 (터키어에서 부정확)
console.log(turkish.toUpperCase());
// 출력: "DIYARBAKIR" (점 없는 I - 부정확)
console.log(turkish.toUpperCase().toLowerCase());
// 출력: "diyarbakir" (점 있는 i - 점 없는 ı를 잃어버림)
// 로케일 인식 메서드 (터키어에서 정확)
console.log(turkish.toLocaleUpperCase("tr"));
// 출력: "DİYARBAKIR" (점 있는 İ와 점 없는 I - 정확)
console.log(turkish.toLocaleUpperCase("tr").toLocaleLowerCase("tr"));
// 출력: "diyarbakır" (두 종류의 i를 모두 보존 - 정확)
터키 도시 이름 Diyarbakır에는 두 종류의 i가 포함되어 있습니다. 표준 메서드는 대소문자 간 변환 시 이러한 구분을 유지할 수 없습니다. 로케일 인식 메서드는 양방향으로 올바른 문자를 유지합니다.
단순한 대소문자 규칙을 가진 문자만 포함된 텍스트의 경우, 두 접근 방식 모두 동일한 결과를 생성합니다.
const english = "Hello World";
console.log(english.toUpperCase());
// 출력: "HELLO WORLD"
console.log(english.toLocaleUpperCase("en"));
// 출력: "HELLO WORLD"
영어 텍스트는 어떤 메서드를 사용해도 동일하게 변환됩니다. 영어 전용 텍스트에는 로케일 인식 버전이 필요하지 않지만, 이를 사용하면 텍스트에 다른 언어가 포함되어 있어도 코드가 올바르게 작동합니다.
로케일 인식 대소문자 변환을 사용해야 하는 경우
사용자 생성 콘텐츠나 여러 언어를 포함할 수 있는 텍스트를 처리할 때는 로케일 인식 메서드를 사용하세요. 이렇게 하면 텍스트에 어떤 언어가 포함되어 있든 올바른 대소문자 변환이 보장됩니다.
function normalizeUsername(username) {
return username.toLocaleLowerCase();
}
사용자 이름, 이메일 주소, 검색어 및 기타 사용자 입력에는 로케일 인식 변환을 사용해야 합니다. 이는 국제 문자를 올바르게 처리하고 터키어 및 기타 특수 사례와 관련된 문제를 방지합니다.
표준 메서드는 텍스트에 영어 문자만 포함되어 있다는 것을 알고 최대 성능이 필요한 경우에만 사용하세요. 표준 메서드는 로케일 규칙을 확인할 필요가 없기 때문에 약간 더 빠르게 실행됩니다.
const htmlTag = "<DIV>";
const normalized = htmlTag.toLowerCase();
// 출력: "<div>"
HTML 태그 이름, CSS 속성, 프로토콜 스키마 및 기타 기술적 식별자는 ASCII 문자를 사용하며 로케일 인식이 필요하지 않습니다. 이러한 콘텐츠에는 표준 메서드가 올바르게 작동합니다.
변환 후 문자 길이가 변경되는 방식
대소문자 변환은 항상 일대일 문자 매핑이 아닙니다. 일부 문자는 대문자로 변환될 때 여러 문자로 확장되어 문자열 길이에 영향을 줍니다.
const german = "groß";
console.log(german.length);
// Output: 4
const uppercase = german.toLocaleUpperCase("de");
console.log(uppercase);
// Output: "GROSS"
console.log(uppercase.length);
// Output: 5
독일어 단어 groß는 네 개의 문자를 가지고 있습니다. 대문자로 변환하면 ß가 SS로 바뀌어 다섯 개의 문자를 가진 GROSS가 됩니다. 변환 과정에서 문자열 길이가 한 문자 증가합니다.
이는 문자열 길이나 문자 위치에 의존하는 작업에 영향을 미칩니다. 대문자화되거나 소문자화된 문자열 버전이 원본과 동일한 길이를 가진다고 가정하지 마십시오.
const text = "Maße";
const positions = [0, 1, 2, 3];
const uppercase = text.toLocaleUpperCase("de");
// "MASSE" (5 characters)
// Original position mapping no longer valid
위치 2에 있는 ß는 대문자 버전에서 SS가 되어 이후의 모든 문자가 이동합니다. 원본 문자열의 문자 위치는 변환된 문자열의 위치와 일치하지 않습니다.
로케일 매개변수 재사용
동일한 로케일을 사용하여 여러 문자열을 변환해야 하는 경우, 로케일 식별자를 변수에 저장하고 재사용할 수 있습니다. 이렇게 하면 코드 유지 관리가 더 쉬워지고 일관된 로케일 처리를 보장합니다.
const userLocale = navigator.language;
const city = "istanbul";
const country = "türkiye";
console.log(city.toLocaleUpperCase(userLocale));
console.log(country.toLocaleUpperCase(userLocale));
이 접근 방식은 로케일 선택을 한 곳에 유지합니다. 사용하는 로케일을 변경해야 하는 경우 변수 정의만 업데이트하면 됩니다.
대량의 텍스트를 처리하는 애플리케이션의 경우, 이 방법은 성능상의 이점을 제공하지 않습니다. toLocaleUpperCase() 또는 toLocaleLowerCase()에 대한 각 호출은 독립적으로 변환을 수행합니다. Intl API 포매터와 달리, 재사용할 포매터 객체가 없습니다.