본문 바로가기

Backend/Node.js

Node.js v14 - Optional Chaining / Nullish / Intl.*

※ 지난번 Node.js v14 발표에 대한 글 작성시 주요 특징들로 언급되었던 Optional Chaining / Nullish / Intl.DIsplayNames /

Intl.DateTimeFormat 에 대해 알아보겠습니다.

 

Optional chaining

MDN docs 에 나와있는 옵셔널 체이닝에 대한 설명입니다.

- 옵셔널 체이닝 연산자인  ?.  는 체인의 각 참조가 유효한지 명시적으로 검증할 필요 없이 연결된 객체의 체인 내에 있는 속성 값을 읽을 수 있도록 합니다.  ?.  연산자 함수는  체이닝 연산자와 유사하지만, 참조가 nullish (null 혹은 undefined) 일때 에러를 발생시키는 대신 표현식은 undefined 값을 반환합니다. 또한 함수 호출과 함께 사용할 때, 지정한 함수가 존재하지 않는다면 undefined 를 반환합니다. 이로 인해 참조가 누락될 수 있는 가능성이 있는 경우 체인 속성에 접근할 때 더 짧고 간단한 표현식을 만들어냅니다. 또한 어떤 속성이 필요한지에 대한 보장이 없는 경우 객체의 내용을 탐색하는데 도움이 됩니다.

 

예시를 보겠습니다.

 

/* v12 */
const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah'
  }
};

const dogName = adventurer.dog.name;
console.log(dogName);
// TypeError: Cannot read property 'name' of undefined

console.log(adventurer.someNonExistentMethod());
// TypeError: adventurer.someNonExistentMethod is not a function

 

/* v14 */
const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah'
  }
};

const dogName = adventurer.dog?.name;
console.log(dogName);
// expected output: undefined

console.log(adventurer.someNonExistentMethod?.());
// expected output: undefined

 

위처럼 v12 와 v14 에서 명확히 차이점을 볼 수 있습니다. adventurer 객체엔 name 과 cat 프로퍼티가 있고 name 은 'Alice' , cat 은

{ name : 'Dinah' } 라는 객체를 갖고 있습니다. v12 의 경우 adventurer 에 존재하지 않는 dog 프로퍼티의 name 에 접근시 타입에러가 발생합니다. someNonExistentMethod 또한 존재하지 않기에 호출시 마찬가지로 타입에러가 발생합니다.

v14 에서도 객체의 프로퍼티 접근에  연산자를 사용했다면 같은 에러가 발생하겠지만,  ?.  연산자를 사용함으로써 이러한 에러 없이 객체 프로퍼티에 접근할 수 있습니다. 위와 동일한 호출에서 v14 의  ?.  연산자를 통해 undefined 로 반환받는 부분을 확인할 수 있습니다.

 

옵셔널 체이닝에 대한 더 많은 정보

 

Nullish coalescing operator

MDN docs 에 나와있는 설명입니다.

- null 병합 연산자 ( ?? ) 는 왼쪽 피연산자가 null 이거나 undefined 일 때는 오른쪽 피연산자를 반환하고, 그 외의 경우엔 왼쪽 피연산자를 반환하는 논리 연산자입니다. OR 논리 연산자 ( || ) 와 반대로, 왼쪽 피연산자가 null 혹은 undefined 가 아닌 허위의 값이면 그 값이 반환됩니다. 다시말해, || 를 사용하여 foo 변수에 디폴트 값을 할당할 때 잘못된 값을 사용 가능한것 (eg. '' 또는 0) 으로 간주하여 기대치않은 동작이 발생할 수 있습니다.

 

예시를 보겠습니다.

 

const foo = null ?? 'default string';
console.log(foo);
// expected output: "default string"

const topaz = null || 'default string';
console.log(topaz);
// expected output: "default string"

const baz = 0 ?? 42;
console.log(baz);
// expected output: 0

const pearl = 0 || 42;
console.log(pearl);
// expected output: 42

 

변수 foo 와 topaz 의 경우, 왼쪽 피연산자가 null 이기에  ??  연산자나  ||  연산자 어느것을 사용하더라도 논리적 결과에 따라 오른쪽 피연산자가 반환됩니다. 하지만 baz 와 pearl 변수 할당시 왼쪽 피연산자가 0 (null 혹은 undefined 가 아닌 허위의 값) 이므로  ??  연산자를 사용했을 때는 0 이 반환되지만,  ||  사용시 42 가 반환되는 차이점이 있습니다. 설명에는 null 혹은 undefined 가 아닌 허위의 값을 falsy value 라고 하는데, 위의 예시처럼 0 외에도 '' , false 와 같은 값이 있습니다.

 

const gold = { description: '' };
const gold_description = gold.description || 'non existence';

console.log(gold_description);
// output : 'non existence'

const new_gold_description = gold.description ?? 'non existence';
// output : ''

 

억지로 예시를 만들어보자면, gold 라는 변수에 description 프로퍼티를 가진 객체를 할당하고 gold_description 변수에  ||  연산자를 사용해서 gold 객체에 description 프로퍼티가 존재하지 않는 경우 'non existence' 를 기본값으로 세팅하려는 의도를 갖고 위와같이 작성했습니다. 하지만 위의 경우 gold 의 description 값이 '' 로 분명히 존재하지만, '' 는 falsy value 이므로  ||  연산자를 거쳐 우측 피연산자인 'non existence' 값이 gold_description 변수에 할당됩니다. 이러한 경우  ??  연산자를 사용해 기존 의도대로 코드를 작성할 수 있습니다.

 

null 병합 연산자에 대한 더 많은 정보

 

Intl.DisplayNames

MDN docs 에 나와있는 설명입니다.

- Intl.DisplayNames 객체는 언어, 지역 및 스크립트 표시 이름을 일관되게 번역 가능하게 하는 객체의 생성자입니다.

 

간단히 예시를 보겠습니다.

 

// Get display names of region in English
var regionNames = new Intl.DisplayNames(['en'], {type: 'region'});
console.log(regionNames.of('419')); // "Latin America"
console.log(regionNames.of('BZ')); // "Belize"
console.log(regionNames.of('US')); // "United States"
console.log(regionNames.of('BA')); // "Bosnia & Herzegovina"
console.log(regionNames.of('MM')); // "Myanmar (Burma)"

// Get display names of region in Traditional Chinese
regionNames = new Intl.DisplayNames(['zh-Hant'], {type: 'region'});
console.log(regionNames.of('419')); // "拉丁美洲"
console.log(regionNames.of('BZ')); // "貝里斯"
console.log(regionNames.of('US')); // "美國"
console.log(regionNames.of('BA')); // "波士尼亞與赫塞哥維納"
console.log(regionNames.of('MM')); // "緬甸"

 

Intl.DisplayNames 생성자를 통한 객체 생성시 지역을 지정하면, 위처럼 지역 코드에 대한 표시 이름을 지정한 지역 언어로 가져올 수 있습니다. (en : 영어, zh-Hant : 중국어)

 

Intl.DisplayNames 에 대한 더 많은 정보

 

Intl.DateTimeFormat

MDN docs 에 나와있는 설명입니다.

- Intl.DateTimeFormat 객체는 언어에 따라 날짜 및 시간 형식을 사용 가능하게 하는 객체의 생성자입니다.

 

간단히 예시를 보겠습니다.

 

const date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
// Results below assume UTC timezone - your results may vary

console.log(new Intl.DateTimeFormat('en-US').format(date));
// expected output: "12/20/2012"

console.log(new Intl.DateTimeFormat('en-GB').format(date));
// expected output: "20/12/2012"

console.log(new Intl.DateTimeFormat('ko-KR').format(date));
// expected output: "2012. 12. 20."

// Include a fallback language, in this case Indonesian
console.log(new Intl.DateTimeFormat(['ban', 'id']).format(date));
// expected output: "20/12/2012"

 

선언된 date 변수를 생성자 Intl.DateTimeFormat 에 의해 만들어진 객체를 사용해 포맷을 출력해보면, 객체 생성시 사용된 지역에서 사용되는 날짜 형식에 따라 출력 결과가 변하는 것을 볼 수 있습니다. en-US (미국) : 월-일-년 / en-GB (영국) : 일-월-년 / 한국 : 년-월-일 순서입니다. 마지막 예시처럼 생성자의 매개 변수에 배열이 사용된 경우는 대체 언어를 지정한 경우이며 ban (발리어) 가 지원되지 않기 때문에 id (인도네시아) 로 대체되어 해당 포맷에 맞게 출력되었습니다.

 

Intl.DateTimeFormat 에 대한 더 많은 정보