[EXT-018][실무] 여러 항목을 자연스러운 문장으로 묶기
페이지 정보

본문
[이번 수업]
이름을 쉼표로만 이어 붙이면 마지막 접속사와 문장부호가 언어에 맞지 않을 수 있습니다. 이번에는 목록 형식화(list formatting, 여러 항목을 언어 규칙에 맞게 조합하는 기능)를 Intl.ListFormat으로 구현합니다.
[선수지식]
JavaScript 배열·객체와 Node.js 실행 방법을 알아야 합니다. 표시 언어 선택은 EXT-016에서 연습할 수 있습니다.
[학습목표]
1. conjunction, disjunction, unit 목록의 차이를 설명한다.
2. 같은 배열을 로케일별 문장으로 형식화한다.
3. formatToParts로 항목과 연결 문구를 구분한다.
[핵심개념]
Intl.ListFormat은 문자열 배열과 로케일을 받아 자연스러운 목록을 만듭니다. type의 conjunction은 모두를 잇는 ‘그리고’, disjunction은 대안을 잇는 ‘또는’에 해당합니다. unit은 단위처럼 접속사 없이 묶는 짧은 목록에 사용합니다. style은 long, short, narrow 중 하나이며 짧아질수록 표시가 압축될 수 있습니다. 정확한 문구와 문장부호는 로케일 데이터에 따라 정해집니다.
format은 완성 문자열을 반환합니다. formatToParts는 원래 항목을 element, 쉼표·공백·접속사 같은 연결 부분을 literal로 나눕니다. 항목만 굵게 표시하는 등 구조가 필요할 때 문자열을 쉼표로 다시 쪼개지 말고 parts를 사용하세요. 입력 항목은 미리 문자열로 준비하며, 빈 목록은 빈 문자열이 되고 항목이 하나면 그 항목만 표시됩니다.
[따라하기]
list_format.js 파일에 다음 코드를 저장합니다.
```javascript
const items = ["테스트", "검토", "배포"];
const korean = new Intl.ListFormat("ko-KR", {
style: "long",
type: "conjunction",
});
const english = new Intl.ListFormat("en-US", {
style: "long",
type: "conjunction",
});
const choice = new Intl.ListFormat("ko-KR", {
style: "long",
type: "disjunction",
});
console.log("한국어:", korean.format(items));
console.log("영어:", english.format(items));
console.log("선택:", choice.format(items));
const parts = korean.formatToParts(items);
console.log(parts.map(({ type, value }) => `${type}:${value}`).join("|"));
```
Windows PowerShell·명령 프롬프트, macOS 터미널, Linux 셸에서 같은 명령을 실행합니다.
```text
node list_format.js
```
확인한 실행 환경의 결과입니다.
```text
한국어: 테스트, 검토 및 배포
영어: 테스트, 검토, and 배포
선택: 테스트, 검토 또는 배포
element:테스트|literal:, |element:검토|literal: 및 |element:배포
```
Node.js의 Intl은 ICU와 CLDR 데이터를 사용합니다. 데이터 버전에 따라 일부 문장부호가 달라질 수 있습니다. 출력 전체를 고정한 테스트가 필요하다면 운영과 테스트에서 같은 지원 버전을 사용하고, 로케일을 항상 명시하세요.
[흔한 실수]
첫째, 모든 언어에서 배열을 join(", ")만으로 묶습니다. 둘째, conjunction과 disjunction을 구분하지 않아 ‘그리고’와 ‘또는’의 의미가 바뀝니다. 셋째, 완성 문자열을 쉼표로 나눠 원래 항목을 복원하려 합니다. 항목 자체에도 쉼표가 있을 수 있습니다. 넷째, narrow 스타일을 긴 문장에 써 의미가 불분명해집니다. 다섯째, 번역된 접속사를 코드에 직접 붙여 로케일 규칙을 중복 구현합니다.
[보안 주의]
ListFormat은 입력 문자열을 안전한 HTML로 바꾸지 않습니다. 사용자 입력이 들어간 항목은 textContent처럼 텍스트 문맥으로 출력하거나 올바르게 이스케이프하세요. formatToParts의 literal과 element를 구분해도 element가 신뢰된 값이 되는 것은 아닙니다. 서버에서는 항목 개수와 각 길이를 제한해 지나치게 큰 응답이나 로그를 막고, 목록에 보인다는 이유로 접근 권한이 있다고 판단하지 않습니다. 실습은 본인 소유의 로컬 파일에서만 진행합니다.
[직접 해볼 과제]
items를 두 항목과 한 항목으로 줄여 결과를 비교하세요. type을 disjunction, style을 short와 narrow로 바꿔 차이를 기록합니다. 마지막으로 항목 중 하나를 "검토, 승인"으로 바꾸고 formatToParts를 사용해야 하는 이유를 설명해 보세요.
[확인문제]
1. conjunction과 disjunction은 의미가 어떻게 다른가요?
2. 완성된 목록 문자열을 쉼표로 다시 나누면 왜 위험한가요?
3. formatToParts를 사용해도 element를 출력 이스케이프해야 하는 이유는 무엇인가요?
[다음 학습]
다음에는 ‘3일 전’, ‘2시간 후’ 같은 상대 시간을 Intl.RelativeTimeFormat으로 표시합니다.
[공식 참고 자료]
ECMA-402 ListFormat 객체
https://tc39.es/ecma402/#listformat-objects
Unicode LDML 목록 패턴
https://www.unicode.org/reports/tr35/tr35-general.html#ListPatterns
Unicode CLDR 안정 릴리스 안내
https://cldr.unicode.org/index/downloads
Node.js 국제화 지원과 ICU
https://nodejs.org/api/intl.html
이름을 쉼표로만 이어 붙이면 마지막 접속사와 문장부호가 언어에 맞지 않을 수 있습니다. 이번에는 목록 형식화(list formatting, 여러 항목을 언어 규칙에 맞게 조합하는 기능)를 Intl.ListFormat으로 구현합니다.
[선수지식]
JavaScript 배열·객체와 Node.js 실행 방법을 알아야 합니다. 표시 언어 선택은 EXT-016에서 연습할 수 있습니다.
[학습목표]
1. conjunction, disjunction, unit 목록의 차이를 설명한다.
2. 같은 배열을 로케일별 문장으로 형식화한다.
3. formatToParts로 항목과 연결 문구를 구분한다.
[핵심개념]
Intl.ListFormat은 문자열 배열과 로케일을 받아 자연스러운 목록을 만듭니다. type의 conjunction은 모두를 잇는 ‘그리고’, disjunction은 대안을 잇는 ‘또는’에 해당합니다. unit은 단위처럼 접속사 없이 묶는 짧은 목록에 사용합니다. style은 long, short, narrow 중 하나이며 짧아질수록 표시가 압축될 수 있습니다. 정확한 문구와 문장부호는 로케일 데이터에 따라 정해집니다.
format은 완성 문자열을 반환합니다. formatToParts는 원래 항목을 element, 쉼표·공백·접속사 같은 연결 부분을 literal로 나눕니다. 항목만 굵게 표시하는 등 구조가 필요할 때 문자열을 쉼표로 다시 쪼개지 말고 parts를 사용하세요. 입력 항목은 미리 문자열로 준비하며, 빈 목록은 빈 문자열이 되고 항목이 하나면 그 항목만 표시됩니다.
[따라하기]
list_format.js 파일에 다음 코드를 저장합니다.
```javascript
const items = ["테스트", "검토", "배포"];
const korean = new Intl.ListFormat("ko-KR", {
style: "long",
type: "conjunction",
});
const english = new Intl.ListFormat("en-US", {
style: "long",
type: "conjunction",
});
const choice = new Intl.ListFormat("ko-KR", {
style: "long",
type: "disjunction",
});
console.log("한국어:", korean.format(items));
console.log("영어:", english.format(items));
console.log("선택:", choice.format(items));
const parts = korean.formatToParts(items);
console.log(parts.map(({ type, value }) => `${type}:${value}`).join("|"));
```
Windows PowerShell·명령 프롬프트, macOS 터미널, Linux 셸에서 같은 명령을 실행합니다.
```text
node list_format.js
```
확인한 실행 환경의 결과입니다.
```text
한국어: 테스트, 검토 및 배포
영어: 테스트, 검토, and 배포
선택: 테스트, 검토 또는 배포
element:테스트|literal:, |element:검토|literal: 및 |element:배포
```
Node.js의 Intl은 ICU와 CLDR 데이터를 사용합니다. 데이터 버전에 따라 일부 문장부호가 달라질 수 있습니다. 출력 전체를 고정한 테스트가 필요하다면 운영과 테스트에서 같은 지원 버전을 사용하고, 로케일을 항상 명시하세요.
[흔한 실수]
첫째, 모든 언어에서 배열을 join(", ")만으로 묶습니다. 둘째, conjunction과 disjunction을 구분하지 않아 ‘그리고’와 ‘또는’의 의미가 바뀝니다. 셋째, 완성 문자열을 쉼표로 나눠 원래 항목을 복원하려 합니다. 항목 자체에도 쉼표가 있을 수 있습니다. 넷째, narrow 스타일을 긴 문장에 써 의미가 불분명해집니다. 다섯째, 번역된 접속사를 코드에 직접 붙여 로케일 규칙을 중복 구현합니다.
[보안 주의]
ListFormat은 입력 문자열을 안전한 HTML로 바꾸지 않습니다. 사용자 입력이 들어간 항목은 textContent처럼 텍스트 문맥으로 출력하거나 올바르게 이스케이프하세요. formatToParts의 literal과 element를 구분해도 element가 신뢰된 값이 되는 것은 아닙니다. 서버에서는 항목 개수와 각 길이를 제한해 지나치게 큰 응답이나 로그를 막고, 목록에 보인다는 이유로 접근 권한이 있다고 판단하지 않습니다. 실습은 본인 소유의 로컬 파일에서만 진행합니다.
[직접 해볼 과제]
items를 두 항목과 한 항목으로 줄여 결과를 비교하세요. type을 disjunction, style을 short와 narrow로 바꿔 차이를 기록합니다. 마지막으로 항목 중 하나를 "검토, 승인"으로 바꾸고 formatToParts를 사용해야 하는 이유를 설명해 보세요.
[확인문제]
1. conjunction과 disjunction은 의미가 어떻게 다른가요?
2. 완성된 목록 문자열을 쉼표로 다시 나누면 왜 위험한가요?
3. formatToParts를 사용해도 element를 출력 이스케이프해야 하는 이유는 무엇인가요?
[다음 학습]
다음에는 ‘3일 전’, ‘2시간 후’ 같은 상대 시간을 Intl.RelativeTimeFormat으로 표시합니다.
[공식 참고 자료]
ECMA-402 ListFormat 객체
https://tc39.es/ecma402/#listformat-objects
Unicode LDML 목록 패턴
https://www.unicode.org/reports/tr35/tr35-general.html#ListPatterns
Unicode CLDR 안정 릴리스 안내
https://cldr.unicode.org/index/downloads
Node.js 국제화 지원과 ICU
https://nodejs.org/api/intl.html
- 이전글[EXT-019][실무] 지난 시간과 남은 시간을 자연스럽게 표시하기 26.09.08
- 다음글[EXT-017][실무] 수량에 맞는 문구 고르기 26.09.08
댓글목록
등록된 댓글이 없습니다.
