Compression Streams API를 이용한 텍스트 데이터에 대한 압축 및 해제

Compression Streams API는 웹 표준 API로 데이터에 대한 압축 및 해제를 위한 API이다. 파일 단위가 아닌 데이터에 대한 압축으로 그 목적이 zip과는 다르지만 데이터 압축에 대한 알고리즘은 유사하다. 즉, zip은 여러개의 파일을 압축하면서 압축 해제시에는 파일 명과 디렉토리 구조까지 복원해주지만 Compression Sterams API는 데이터에 대한 압축으로 압축을 해제하면 해제된 데이터가 나오며 그 데이터를 하나의 파일로 저장한다.

Compression Streams API로 압축할 수 있는 데이터는 텍스트 든 바이너리든 모두 가능한데, 아래의 코드는 텍스트에 대한 압축과 압축된 데이터를 다시 원래의 텍스트 데이터로 복원(압축 해제)하는 코드이다.

async function compressAndDecompressString(text) {
  // 1. 텍스트를 Uint8Array로 변환
  const textEncoder = new TextEncoder();
  const encodedData = textEncoder.encode(text);

  console.log("원본 데이터:", text);
  console.log("원본 데이터 크기 (바이트):", encodedData.byteLength);

  // 2. 데이터를 압축 (gzip 형식)
  // ReadableStream.from()은 Node.js에서 사용 가능하며, 
  // 브라우저에서는 ArrayBuffer를 직접 ReadableStream으로 변환해야 함.
  // 여기서는 편의를 위해 Blob을 사용하여 ReadableStream을 생성합니다.
  const originalStream = new Blob([encodedData]).stream();
  const compressedStream = originalStream.pipeThrough(new CompressionStream("gzip"));

  // 3. 압축된 데이터를 ArrayBuffer로 읽기
  const compressedResponse = new Response(compressedStream);
  const compressedBuffer = await compressedResponse.arrayBuffer();

  console.log("압축된 데이터 크기 (바이트):", compressedBuffer.byteLength);
  console.log("압축률:", 
    ((1 - compressedBuffer.byteLength / encodedData.byteLength) * 100).toFixed(2) + "%");

  // 4. 압축된 데이터를 압축 해제
  const decompressedStream = new Blob([compressedBuffer]).stream()
    .pipeThrough(new DecompressionStream("gzip"));

  // 5. 압축 해제된 데이터를 ArrayBuffer로 읽기
  const decompressedResponse = new Response(decompressedStream);
  const decompressedBuffer = await decompressedResponse.arrayBuffer();

  // 6. 압축 해제된 Uint8Array를 다시 텍스트로 변환
  const textDecoder = new TextDecoder();
  const decompressedText = textDecoder.decode(decompressedBuffer);

  console.log("압축 해제된 데이터:", decompressedText);
  console.log("압축 해제된 데이터 크기 (바이트):", decompressedBuffer.byteLength);

  // 원본 데이터와 압축 해제된 데이터가 동일한지 확인
  console.log("원본과 압축 해제된 데이터 일치 여부:", text === decompressedText);
}

// 예제 실행
const longText = `The quick brown fox jumps over the lazy dog.`;

compressAndDecompressString(longText);

Modern JavaScript

구조를 분해해서 할당 (구조분해할당, Destructuring Assignment)

const user = { name: 'Alice', age: 30, city: 'Seoul' };

// 객체 구조 분해
const { name, age } = user;
console.log(name, age); // Alice 30

const { name: name2, ...sss } = user;
console.log(name2, sss); // Alice { age: 30, city: 'Seoul' }

// 다른 이름으로 할당하거나 기본값 설정도 가능
const { name: fullName, email = 'noemail@example.com' } = user;
console.log(fullName, email); // Alice noemail@example.com

// 배열 구조 분해
const colors = ['red', 'green', 'blue'];
const [first, second, third] = colors;
console.log(first, second, third); // red green blue

// 일부만 추출하거나 나머지 요소들을 배열로 받을 수도 있음
const [one, two, ...rest] = colors;
console.log(one, two, rest); // red green ['blue']

구성 요소를 펼쳐 사용하는 문법 (스프레드 문법, Spread Syntax)

/* 배열 결합 및 복사에서의 예 */

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// 배열 결합
const combinedArr = [...arr1, ...arr2];
console.log(combinedArr); // [1, 2, 3, 4, 5, 6]

// 배열 복사 (얕은 복사)
const copiedArr = [...arr1];
console.log(copiedArr); // [1, 2, 3]

/* 객체 병합 및 복사에서의 예 */

const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };

// 객체 병합
const combinedObj = { ...obj1, ...obj2 };
console.log(combinedObj); // { a: 1, b: 2, c: 3, d: 4 }

// 객체 복사 (얕은 복사)
const copiedObj = { ...obj1 };
console.log(copiedObj); // { a: 1, b: 2 }

/* 함수 인자 전달에서의 예 */

function sum(a, b, c) {
  return a + b + c;
}

const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6 (배열 요소를 개별 인자로 펼쳐서 전달)

null 또는 undefine이 아닐 경우에 대한 선택적 체이닝(선택적 체이닝, Optional Chaining)

const user = {
  name: 'Dave',
  address: {
    street: '123 Main St',
    city: 'Busan'
  },
  // phone: '010-1234-5678' // phone 속성이 없을 수 있음
};

// 기존 방식 (에러 방지)
let city = user.address && user.address.city;
console.log(city); // Busan

let zipCode;
if (user.address && user.address.zipCode) {
  zipCode = user.address.zipCode;
}
console.log(zipCode); // undefined

// 선택적 체이닝
const userCity = user.address?.city;
console.log(userCity); // Busan

const userPhone = user.phone?.number; // user.phone이 undefined이므로 userPhone은 undefined
console.log(userPhone); // undefined

const userStreet = user.address?.street;
console.log(userStreet); // 123 Main St

// 배열 요소에도 적용 가능
const arr = [{ value: 10 }];
const firstValue = arr?.[0]?.value;
console.log(firstValue); // 10

const emptyArr = [];
const nonExistentValue = emptyArr?.[0]?.value;
console.log(nonExistentValue); // undefined

진짜 null 또는 undefine에 대한 선택 연산자 (Nullish 병합 연산자, Nullish Coalescing Operator)

const userInput = null;
const defaultValue = 'default value';

// || 연산자 (Falsy 값도 걸러냄)
const resultOr = userInput || defaultValue;
console.log(resultOr); // default value

const zeroValue = 0;
const resultOrZero = zeroValue || defaultValue;
console.log(resultOrZero); // default value (0도 Falsy로 간주하여 default value가 할당됨)

// ?? 연산자 (null 또는 undefined만 걸러냄)
const resultNullish = userInput ?? defaultValue;
console.log(resultNullish); // default value

const zeroValueNullish = 0;
const resultNullishZero = zeroValueNullish ?? defaultValue;
console.log(resultNullishZero); // 0 (0은 유효한 값으로 간주됨)

const emptyString = '';
const resultNullishEmptyString = emptyString ?? defaultValue;
console.log(resultNullishEmptyString); // ''

이외에도 화살표 함수, 템플릿 리터럴, 클래스, 모듈, async/await 등이 있음.