아직 늦지 않았다. 이 글에서는 개발자가 무심코 지나쳤을지도 모를 ES11(ECMAScript 11 또는 ECMAScript 2020이라고도 함) 기능을 정리했다. 편의성을 위한 개선과 기타 현대적 기능 개선이 포함된다.
선택적 체이닝
선택적 체이닝은 일상을 조금 더 쉽게 만드는, 단순하지만 효과적인 기능이다. null 또는 undefined 값을 처리하기 위해 객체와 함수 체인을 간편하게 탐색할 수 있다.선택적 체이닝은 중첩된 객체 데이터, 존재하지 않는 값을 반환하는 함수, 그리고 객체의 멤버 메서드로 존재하지 않는 함수 같은 간단한 사례에 사용된다. 따라서 예시 1과 같은 작업이 가능하다.
예시 1. 선택적 체이닝 예제
box = {
innerBox: {},
nullFunction: function() { return null; },
// non-existent method foo() and members bar
}
// with optional chaining:
if (box?.innerBox?.foo){ }// navigate object graph safely
// old style without optional chaining:
if (box.innerBox && box.innerBox.foo){ }
//also works for functions:
box.nullFunction()?.foo
// and nonexistent methods and members:
box?.foo() && box?.bar
이 코드는 원하는 것을 간결하게 표현한다. 원하는 것이 존재하면 그것으로 이동하고, 없으면 undefined를 반환한다.
globalThis
코어 언어에 추가된 또 다른 실용적인 요소인 globalThis는 현재 컨텍스트에서 사용할 수 있는 전역 객체의 추상화다. 가장 잘 알려지고 오래된 것은 브라우저에서 실행되는 자바스크립트의 창 객체다. Node.js, 웹 작업자와 같은 환경에는 이와 동일한 용도로 사용되는 자체 루트 객체인 global과 self가 있다.globalThis는 코드가 어느 환경에서 실행되든 전역 루트 객체로 귀결되는 하나의 식별자에 모든 루트 객체를 래핑해 더 이식성이 높은 코드를 만들고 존재 여부 확인(existence check) 절차를 없앤다.
BigInt
ES11 이전에는 자바스크립트에서 안전하게 참조 가능한 가장 큰 정수가 9007199254740991(또는 2^53 - 1)이 되는 Number.MAX_SAFE_INTEGER였다. 별다른 문제가 되지 않는 경우도 있지만, 많은 애플리케이션에서 이 수는 너무 작아서 프로그래머가 big-integer와 같은 래퍼를 사용해야 했다(big-integer 라이브러리는 polyfill로 여전히 유용함).전통적인 Number 형식을 사용해서 이 같은 큰 수를 표현하는 경우 예기치 못한 끝수처리가 발생하게 된다. 예를 들어 -2^53 -1과 같은 아주 작은 수에도 마찬가지로 적용된다.
ES11에는 이런 시나리오를 위해 BigInt 형식이 도입됐다. mySafeBigNumber = 9007199254740992n과 같이 숫자 끝에 n을 추가하면 이 형식을 정의할 수 있다.
BigInt는 기존 Number 형식에 기교를 부린 것이 아닌 새로운 형식이다. typeof 12로는 number를 얻으며, typeof mySafeBigNumber로는 bigint를 얻는 것이다. 또한 mySafeBigNumber – 12와 같이 어떤 잘못을 저지르려고 하면 “BigInt와 다른 형식을 함께 사용할 수 없습니다. 명시적 변환을 사용하십시오.(Cannot mix BigInt and other types, use explicit conversions.)”라는 오류가 표시된다.
이 둘 사이에서 변환하려면 생성자를 사용해야 한다. 예를 들어 let myUnsfeBigNumber = Number(mySafeBigNumber)를 사용하는 방법이 있는데, 이 경우에는 안전하지 않은 큰 Number 객체가 생성되므로 이렇게 하면 안 된다. 따라서 BigInt가 MAX_SAFE_INTEGER 값보다 작게 축소됐을 경우에만 변환해야 한다.
비슷한 맥락에서, Number와 BigInt의 동일성을 비교하는 경우 형식이 다르므로 항상 false로 반환된다.
BigInt는 2진수, 10진수 및 16진수 표기법도 지원하며, 값을 Number로 변환하기 위한 용도의 단항 더하기 연산자를 제외한 모든 일반적인 수학 연산자를 지원한다. 이렇게 만들어진 이유는 명확하지 않지만, JS가 아닌 기존 asm 코드의 작동 중단을 유발하는 변화를 피하는 데 있다.
마지막으로, 이름 탓에 헷갈리기 쉽지만 BigInt는 정수를 나타낸다. x = 3n; x = x / 2n; 코드를 실행하면 x의 값 1n을 결과로 얻는다. BigInts가 숫자의 소수 부분을 버리기 때문이다.
null 병합
null 병합(nullish coalescing)은 ES11에서 가장 미묘한 이름을 가진 기능으로, 선택적 체이닝과 마찬가지로 null 값 처리에 사용된다. null 병합은 새로운 기호인 이중 물음표 ??이기도 하다. 논리적 OR 연산자(이중 파이프 기호 ||)와 비슷하게 동작하는 논리적 연산자다.??와 ||의 차이점은 연산자가 null 값을 처리하느냐, false 값을 처리하느냐에 있다. 자바스크립트 개발자 대부분은 true/false 테스트에서 부울이 아닌 값을 처리하는 자바스크립트 언어 특유의 방식에 익숙하다. (짧게 정리하면 false, 0, null, 빈 문자열, undefined는 false로 간주되며 그 외의 모든 것은 true가 된다. 더 자세한 내용은 MDN 웹 문서에서 확인할 수 있다.) 이런 부분을 활용해서 개발자는 어떤 것의 존재 여부를 테스트하고 존재하지 않는 경우 다른 것을 사용하는 경우가 많다.
let meaningOfLife = answer || 42;
이렇게 하면 answer에서 어떤 것의 존재 여부를 신속하게 테스트하면서 일종의 기본값을 설정할 수 있다. answer에 아무런 false 값이 설정되지 않은 경우 meaningOfLife의 기본값이 42가 되도록 설정할 때는 문제가 없다. 그러나 실제 null에 해당하는 값(null 또는 undefined)이 있을 때만 42가 되도록 하려면 어떻게 해야 할까?
다음과 같이 ??를 사용하면 간단하다.
let meaningOfLife = answer ?? 42;
명확한 이해를 돕기 위해 answer에 값으로 0을 설정하고, || 또는 ??를 사용해서 meaningOfLife에 값을 할당하는 방법에 대해 생각해 보자(예시 2 참조). 값이 설정되면 0을 유지하고, answer가 실제로 비어 있는 경우 42를 사용하고자 하는 경우다.
예시 2. null 병합의 실제 사용
let answer = 0;
let meaningOfLife = answer ?? 42; // meaningOfLife === 0 - what we want
let meaningOfLife = answer || 42; // meaningOfLife === 42 - not what we want
let answer = undefined;
let meaningOfLife = answer ?? 42; // meaningOfLife === 42 - what we want
let meaningOfLife = answer || 42; // meaningOfLife === 42 - also what we want
String.prototype.matchAll
ES11 사양에서는 String 프로토타입에 새 메서드인 matchAll이 추가됐다. 이 메서드는 String 인스턴스에 정규식을 적용하고 조건에 맞는 모든 반복자를 반환한다. 예를 들어 문자열에서 t 또는 T로 시작하는 모든 단어를 스캔하려는 경우 예시 3과 같이 해서 원하는 결과를 얻을 수 있다.예시 3. matchAll 사용
let text = "The best time to plant a tree was 20 years ago. The second best time is now.";
let regex = /(?:^|\s)(t[a-z0-9]\w*)/gi; // matches words starting with t, case insensitive
let result = text.matchAll(regex);
for (match of result) {
console.log(match[1]);
}
regex 구문의 본질적인 복잡함은 잠시 접어두고, 예시 3에 정의된 정규식이 t 또는 T로 시작하는 단어를 찾는다는 점에 주목하자. matchAll()은 이 정규식을 문자열에 적용해서 손쉽게 결과를 살펴보면서 일치 그룹에 액세스할 수 있게 해주는 반복자를 반환한다.
동적 가져오기
ES11에서 모듈을 가져오는 방법이 더 발전해 이제 비동기적으로 로드되는 가져오기를 임의로 배치할 수 있다. 이를 코드 분할이라고도 하는데, 사실 개발자가 빌드 툴을 통해 오래전부터 해오던 것이다. 동적 가져오기는 사양이 실제 관행을 따라가는 사례에 해당한다.이 구문의 간단한 예는 예시 4에서 확인할 수 있다.
예시 4. 비동기 모듈 가져오기
let asyncModule = await import('/lib/my-module.ts');
이 같은 종류의 가져오기는 사용자 이벤트에 대한 응답을 포함해 JS 코드 어디에나 나타날 수 있다. 실제로 필요해질 때 손쉽게 모듈을 지연 로드(lazy-load)할 수 있다.
Promise.allSettled()
promise.allSettled() 메서드를 사용하면 프로미스의 결과(이행 또는 거부)를 관찰할 수 있다. 거부된 프로미스 또는 오류 비 프로미스(non-promise)로 종료되는 promise.all()과는 대비된다. promise.allSettled()는 각 프로미스의 결과를 기술하는 객체 배열을 반환한다.이 기능은 서로 무관한 프로미스 그룹을 관찰할 때, 즉 일부가 중간에 실패하더라도 모든 프로미스의 결과를 알고자 할 때 유용하다. 예시 5를 확인해 보자.
예시 5. promise.allSettled()의 예
let promise1 = Promise.resolve("OK");
let promise2 = Promise.reject("Not OK");
let promise3 = Promise.resolve("After not ok");
Promise.allSettled([promise1, promise2, promise3])
.then((results) => console.log(results))
.catch((err) => console.log("error: " + err));
이 경우 catch 람다는 실행되지 않는다. (promise.all을 사용했다면 실행됨) 대신 then 절이 실행되고, 예시 6의 내용이 담긴 배열이 반환된다. 여기서 핵심은 세 번째 프로미스가 실행됐고, 그 전에 promise2가 실패했음에도 불구하고 결과를 볼 수 있다는 것이다.
예시 6. promise.allSettled() 결과
[
{"status":"fulfilled","value":"OK"},
{"status":"rejected","reason":"Not OK"},
{"status":"fulfilled","value":"After not ok"}
]
* 구문 내보내기
이 기능은 모듈에서 export * 기능을 추가한다. 다른 모듈에서의 import *는 이미 가능했지만 이제 예시 7과 같이 동일한 구문으로 내보내기도 가능하다.예시 7. export *의 예
Export * from '/dir/another-module.js'
일종의 모듈 체이닝으로, 현재 모듈 내에서 다른 모듈의 모든 것을 내보낼 수 있게 해준다. 예를 들면 다음과 같이 다른 모듈을 API로 통합하는 모듈을 구축할 때 유용하다.
for-in 순서의 표준화
자바스크립트에서 for-in 루프의 컬렉션에 대한 열거 순서는 사실상 모든 자바스크립트 환경(브라우저, Node.js 등)이 보장했는데, 공식적인 것은 아니었다. 이제 ‘사실상의 표준’이 자바스크립트의 기본 사양에 흡수됐다. 이론이 관행을 뒤따른 사례다.
진화하는 사양
소프트웨어 분야에서 가장 재미있는 부분은 다양한 진화를 지켜보는 것이다. 프로그래밍 언어는 모든 것의 가장 기본적인 표현으로, 컴퓨터과학의 철학이 일상적인 코딩 현실과 만나는 지점이다. 자바스크립트는 연간 릴리스 일정에 따라 다른 현존하는 언어와 마찬가지로 계속해서 성장하고 있다.editor@itworld.co.kr
함께 보면 좋은 콘텐츠
Sponsored
Surfshark
“유료 VPN, 분명한 가치 있다” VPN 선택 가이드
ⓒ Surfshark VPN(가상 사설 네트워크, Virtual Private Network)은 인터넷 사용자에게 개인 정보 보호와 보안을 제공하는 중요한 도구로 널리 인정받고 있다. VPN은 공공 와이파이 환경에서도 데이터를 안전하게 전송할 수 있고, 개인 정보를 보호하는 데 도움을 준다. VPN 서비스의 수요가 증가하는 것도 같은 이유에서다. 동시에 유료와 무료 중 어떤 VPN을 선택해야 할지 많은 관심을 가지고 살펴보는 사용자가 많다. 가장 먼저 사용자의 관심을 끄는 것은 별도의 예산 부담이 없는 무료 VPN이지만, 그만큼의 한계도 있다. 무료 VPN, 정말 괜찮을까? 무료 VPN 서비스는 편리하고 경제적 부담도 없지만 고려할 점이 아예 없는 것은 아니다. 보안 우려 대부분의 무료 VPN 서비스는 유료 서비스에 비해 보안 수준이 낮을 수 있다. 일부 무료 VPN은 사용자 데이터를 수집해 광고주나 서드파티 업체에 판매하는 경우도 있다. 이러한 상황에서 개인 정보가 유출될 우려가 있다. 속도와 대역폭 제한 무료 VPN 서비스는 종종 속도와 대역폭에 제한을 생긴다. 따라서 사용자는 느린 인터넷 속도를 경험할 수 있으며, 높은 대역폭이 필요한 작업을 수행하는 데 제약을 받을 수 있다. 서비스 제한 무료 VPN 서비스는 종종 서버 위치가 적거나 특정 서비스 또는 웹사이트에 액세스하지 못하는 경우가 생긴다. 또한 사용자 수가 늘어나 서버 부하가 증가하면 서비스의 안정성이 저하될 수 있다. 광고 및 추적 위험 일부 무료 VPN은 광고를 삽입하거나 사용자의 온라인 활동을 추적하여 광고주에게 판매할 수 있다. 이 경우 사용자가 광고를 보아야 하거나 개인 정보를 노출해야 할 수도 있다. 제한된 기능 무료 VPN은 유료 버전에 비해 기능이 제한될 수 있다. 예를 들어, 특정 프로토콜이나 고급 보안 기능을 지원하지 않는 경우가 그렇다. 유료 VPN의 필요성 최근 유행하는 로맨스 스캠은 인터넷 사기의 일종으로, 온라인 데이트나 소셜 미디어를 통해 가짜 프로필을 만들어 상대를 속이는 행위다. 이러한 상황에서 VPN은 사용자가 안전한 연결을 유지하고 사기 행위를 방지하는 데 도움이 된다. VPN을 통해 사용자는 상대방의 신원을 확인하고 의심스러운 활동을 감지할 수 있다. 서프샤크 VPN은 구독 요금제 가입 후 7일간의 무료 체험을 제공하고 있다. ⓒ Surfshark 그 외에도 유료 VPN만의 강점을 적극 이용해야 하는 이유는 다음 3가지로 요약할 수 있다. 보안 강화 해외 여행객이 증가함에 따라 공공 와이파이를 사용하는 경우가 늘어나고 있다. 그러나 공공 와이파이는 보안이 취약해 개인 정보를 노출할 위험이 있다. 따라서 VPN을 사용하여 데이터를 암호화하고 개인 정보를 보호하는 것이 중요하다. 서프샤크 VPN은 사용자의 개인 정보를 안전하게 유지하고 해킹을 방지하는 데 유용하다. 개인정보 보호 인터넷 사용자의 검색 기록과 콘텐츠 소비 패턴은 플랫폼에 의해 추적될 수 있다. VPN을 사용하면 사용자의 IP 주소와 로그를 숨길 수 있으며, 개인 정보를 보호할 수 있다. 또한 VPN은 사용자의 위치를 숨기고 인터넷 활동을 익명으로 유지하는 데 도움이 된다. 지역 제한 해제 해외 여행 중에도 한국에서 송금이 필요한 경우가 생길 수 있다. 그러나 IP가 해외 주소이므로 은행 앱에 접근하는 것이 제한될 수 있다. VPN을 사용하면 지역 제한을 해제해 해외에서도 한국 인터넷 서비스를 이용할 수 있다. 따라서 해외에서도 안전하고 편리하게 인터넷을 이용할 수 있다. 빠르고 안전한 유료 VPN, 서프샤크 VPN ⓒ Surfshark 뛰어난 보안 서프샤크 VPN은 강력한 암호화 기술을 사용하여 사용자의 인터넷 연결을 안전하게 보호한다. 이는 사용자의 개인 정보와 데이터를 보호하고 외부 공격으로부터 사용자를 보호하는 데 도움이 된다. 다양한 서버 위치 서프샤크 VPN은 전 세계 곳곳에 여러 서버가 위치하고 있어, 사용자가 지역 제한된 콘텐츠에 액세스할 수 있다. 해외에서도 로컬 콘텐츠에 손쉽게 접근할 수 있음은 물론이다. 속도와 대역폭 서프샤크 VPN은 빠른 속도와 무제한 대역폭을 제공하여 사용자가 원활한 인터넷 경험을 누릴 수 있도록 지원한다. 온라인 게임, 스트리밍, 다운로드 등 대역폭이 필요한 활동에 이상적이다. 다양한 플랫폼 지원 서프샤크 VPN은 다양한 플랫폼 및 디바이스에서 사용할 수 있다. 윈도우, 맥OS, iOS, 안드로이드 등 다양한 운영체제 및 디바이스에서 호환되어 사용자가 어디서나 안전한 인터넷을 즐길 수 있다. 디바이스 무제한 연결 서프샤크 VPN은 무제한 연결을 제공하여 사용자가 필요할 때 언제든지 디바이스의 갯수에 상관없이 VPN을 사용할 수 있다.