왓츠뉴? With ES2024

제이밍·2024년 2월 18일
2

자바스크립트 : ECMAScript의 스펙을 준수하는 언어

ECMA International

1961년부터 현재까지 국제 정보통신기술(ICT, Information and Communications Technology)과 전자제품(CE, Consumer Electronics)의 표준 규격(standards)을 개발하고 발표하는 곳으로, 핸드폰 혹은 컴퓨터 등의 전자기기로 사용하는 대부분의 규격을 관리하는 곳이다.

ECMAScript

특히 ECMAScript는 이 기관의 수많은 규격중에 ECMA-262ECMAScript를 따른다.

특히 ECMAScript 중에서도ES6는 많이 언급된 버전중 하나인데, 이유는 ES6에서 추가된 문법들이 기존의 문제들을 매우 깔끔하게 해결했으며, 가독성 및 유지 보수성을 보강하는 문법이 대거 추가된 버전이기 때문이다.

ES6에서 추가된 대표적인 기능으론, Promise, Class, Arrow Function 등이 있다.

매년 새로운 버전이 배포되고 있으며 오늘은 따끈따끈 최신 문법을 소개해보기로 한다.

ECMAScript의 표준이 되기 위한 여정

TC39

TC39의 전체 이름은 "Technical Committee 39"이며, 이 위원회는 ECMA International의 하위 그룹 중 하나입니다.
TC39JavaScript 언어의 개발 및 표준화를 책임지며, 새로운 기능과 문법을 제안하고 검토합니다.

TC39의 제안 단계는 다음과 같습니다:

  • Stage 0: 제안 (Proposal)
    아이디어가 제안되고 초기 구상이 작성됩니다.
  • Stage 1: 초안 (Draft)
    초안이 작성되고 구현 가능성이 논의됩니다.
  • Stage 2: 구체적인 제안 (Candidate)
    세부 사항이 명확화되고 구현이 진행됩니다.
  • Stage 3: 표준 제안 (Draft)
    표준화를 위한 최종 제안으로 성숙해집니다.
  • Stage 4: 최종 승인 (Finished)
    표준으로 승인되고 ECMAScript 사양에 포함됩니다.

ES2024 WHAT'S NEW

Promise.withResolvers

Promise 확장 방법 중 하나로 새로운 Promise 인스턴스와 해당 Promise의 해결(resolve) 및 거부(reject) 함수를 반환합니다.

마치 tanstack-query 와 비슷해 보이는군요

const { promise, resolve, reject } = Promise.withResolvers();

코드를 더 간결하게 작성할 수 있고, 비동기 작업을 보다 효율적으로 제어할 수 있을것 같습니다.

proposal-array-grouping

특정 속성을 기준으로 객체 배열을 그룹화하는 함수입니다.

const array = [1, 2, 3, 4, 5];

// `Object.groupBy` groups items by arbitrary key.
// In this case, we're grouping by even/odd keys
Object.groupBy(array, (num, index) => {
  return num % 2 === 0 ? 'even': 'odd';
});
// =>  { odd: [1, 3, 5], even: [2, 4] }

// `Map.groupBy` returns items in a Map, and is useful for grouping
// using an object key.
const odd  = { odd: true };
const even = { even: true };
Map.groupBy(array, (num, index) => {
  return num % 2 === 0 ? even: odd;
});
// =>  Map { {odd: true}: [1, 3, 5], {even: true}: [2, 4] }

이를 통해 데이터를 더 쉽게 분석하고 처리할 수 있습니다.

올바른 형식의 유니코드 문자열 toWellFormed()

toWellFormed()

현재 문자열의 서러게이트(surrogate)가"\uFFFD"로 대체된 새로운 문자열을 반환한다.

WHAT'S SURROGATE ?
유니코드에서 surrogate는 기본 다국어 평면(Basic Multilingual Plane, BMP)에 속하지 않는 문자를 나타내기 위해 사용되는 코드 포인트입니다.

BMP에는 가장 일반적으로 사용되는 문자가 포함되어 있으며, 이는 U+0000부터 U+FFFF의 코드 포인트로 나타납니다.

high surrogate (범위: U+D800부터 U+DBFF)
low surrogate (범위: U+DC00부터 U+DFFF)

이 두 개의 단위가 함께 하나의 문자를 형성합니다.

예를 들어, BMP를 넘어서는 문자는 하나의 높은 서로게이트와 하나의 낮은 서로게이트의 조합으로 UTF-16으로 표시됩니다.
이 접근 방식은 UTF-16이 BMP의 16비트 구조와 호환되면서도 많은 수의 문자를 나타낼 수 있도록 합니다.

const sampleStrings = [
  // Examples with lone surrogates
  "igor\uD800", // Leading surrogate
  "igor\uD800komolov", // Leading surrogate followed by text
  "\uDC00yourfuse",    // Trailing surrogate
  "your\uDC00fuse",    // Trailing surrogate followed by text
  
  // Well-formed examples
  "yourFuse",       // Regular string without surrogates
  "emoji\uD83D\uDE00", // String with a complete surrogate pair (emoji)
];

sampleStrings.forEach(str => {
  console.log(`Processed String: ${str.toWellFormed()}`);
});

// Expected output:
// "Processed String: igor�"
// "Processed String: igor�komolov"
// "Processed String: �yourfuse"
// "Processed String: your�fuse"
// "Processed String: yourFuse"
// "Processed String: emoji😀"  

surrogate 는 항상 leadingtrailing이 pair로 이루어져야 한다.
하지만 각각의 단위가 따로 사용된다면 위처럼 오류가 발생됩니다.

위의 예시 에서 toWellFormed()서드는 단일 서로게이트가 있는 일부 문자열과 올바른 형식의 문자열을 포함하여 문자열 배열에 적용됩니다.

const problematicURL = "https://yourfuse.com/query=\uDC00data";

try {
  encodeURI(problematicURL);
} catch (e) {
  console.log('Error:', e.message); // Expected: URIError: URI malformed
}

// Using toWellFormed() to prevent the error
console.log('Well Formed URI:', encodeURI(problematicURL.toWellFormed())); 
// Expected output: "https://yourfuse.com/query=%EF%BF%BDdata"

예를들어 encodeURL() 에서 형식에 맞지않는 문자열을 전달할시 오류가 발생하게 되는데
toWellFormed()로 먼저 올바른 형식의 문자열로 변환하면 문제를 피할 수 있게 된다.

Atomic waitSync

Atomics.waitAsync() 정적 메서드는 공유 메모리 위치에서 비동기적으로 대기하고 Promise를 반환합니다.

참고로 위 메소드는 Int32Array 혹은 BigInt64Array에서만 동작합니다.

const arrayBuffer = new SharedArrayBuffer(1024);
const arr = new Int32Array(arrayBuffer);

// 이 코드는 int32 배열의 위치 0에서 0을 기대하고, 500ms 동안 대기합니다.
Atomics.waitAsync(arr, 0 , 0 , 500);
// { async: true, value: Promise {<pending>}}

// notify 메소드는 배열을 깨우고 Promise가 이행됩니다.
Atomics.notify(arr, 0);
// { async: true, value: Promise {<fulfilled>: 'ok'} }

위의 코드에서 Atomics.waitAsync()는 배열의 위치 0에서 0을 기대하며, 500ms 동안 대기하는 비동기 메소드입니다.
반환값은 { async: true, value: Promise {<pending>}} 와 같이 비동기적으로 실행되는 Promise입니다.

이후 Atomics.notify() 메소드를 호출하여 배열을 깨우면, Promise가 이행되어
{ async: true, value: Promise {<fulfilled>: 'ok'}}와 같이 이행된 상태로 반환됩니다.

정규식 v 플래그

const str = 'hello world hello';

// 정규식을 설정합니다. 'v' 플래그를 사용하여 시작부터 일치하는 것을 강제합니다.
const regex = /hello/gv;

// 정규식에 따라 문자열을 검색하고 일치하는 부분을 배열로 반환합니다.
const matches = str.match(regex);

// 일치하는 부분을 출력합니다.
console.log(matches); // ['hello']

Top-level await

await 키워드를 비동기 함수 바깥에서 사용할 수 있게 해줍니다.

// 다른 모듈에서 fetchData 함수를 가져옵니다.
// 모듈 컨텍스트에서만 지원 
import { fetchData } from './dataFetcher.js';

// Top-level await를 사용하여 데이터를 비동기적으로 가져옵니다.
const data = await fetchData();

// 가져온 데이터를 로그에 출력합니다.
console.log(data);
  • 모듈의 최상위 수준에서 await를 사용하여 fetchData 함수를 통해 데이터를 비동기적으로 가져옵니다.
  • await 호출을 async 함수 안에 감싸는 필요성이 사라집니다.
  • 가져온 데이터는 data 변수에 저장되고 콘솔에 로그로 출력됩니다.

Pipeline Operator (TC39 2단계)

// 파이프라인 연산자 없이
const calculatedValue = Math.ceil(Math.pow(Math.max(0, -10), 1/3));

// 파이프라인 연산자 사용
const calculatedValue = -10
  |> (n => Math.max(0, n)) // Math.max를 대체
  |> (n => Math.pow(n, 1/3)) // Math.pow를 대체
  |> Math.ceil; // Math.ceil을 사용`

파이프라인 연산자 (|>)는 이러한 연산들을 체이닝하여
간편하게 사용할 수 있게 해주어 코드의 가독성을 향상시킵니다.

보자마자 값이 있으면 비교하는 건가? 라고 생각했는데,
단순히 가독성을 향상시키기 위한 연산자 인것 같다.

가독성을 위해 임시변수를 붙혀주는 단계는 건너 뛸 수 있을 것 같아 기다려지는 문법!

immutable data structures Records and Tuples (TC39 2단계)

레코드(Records)와 튜플(Tuples)은 객체와 배열에 유사한 불변(immutable) 데이터 구조입니다.
이들은 생성된 후에 수정할 수 없습니다.

예를 들어, 레코드나 튜플을 업데이트하면 새로운 인스턴스가 생성됩니다.

// 불변 레코드 생성
const userProfile = #{
  username: "IgorKomolov",
  age: 39,
};

// 불변 튜플 생성
const numberSequence = #[10, 20, 30];

// 이러한 구조를 업데이트하면 새로운 인스턴스가 생성됩니다
const updatedProfile = userProfile.with({ age: 40});
console.log(updatedProfile); // #{ username: "IgorKomolov", age: 40 }
console.log(userProfile); // #{ username: "IgorKomolov", age: 39 } (변경되지 않음)

const newNumberSequence = numberSequence.with(1, 25);
console.log(newNumberSequence); // #[10, 25, 30]
console.log(numberSequence); // #[10, 20, 30] (변경되지 않음)

레코드는 객체와 유사하게 동작하며, 튜플은 배열과 유사합니다. 그러나 이들의 주요 특징은 변경 불가능성입니다.

레코드와 튜플은 특정 상황에서 성능을 향상시키고 코드베이스에서 변경 불가능성을 강제할 수 있습니다.

REFERENCE

JavaScript — What’s new with ECMAScript® 2024 (ES15) — In Depth Guide

Finished-proposals

profile
모르는것은 그때그때 기록하기

0개의 댓글