반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • prerendering
  • 자바 디자인패턴
  • 이펙티브자바
  • 팩터리 메서드 패턴
  • 서버리스 컴퓨팅
  • Pre-rendering
  • 빈 검증
  • 느린 쿼리
  • serverless computing
  • 외래키 제약조건
  • 객체복사
  • 도커공식문서
  • item9
  • 스프링 검증
  • 메이븐 라이프사이클
  • item7
  • 자바스크립트 면접
  • Javadoc 자바독 자바주석 주석 Comment
  • 슬로우 쿼리
  • try-with-resources
  • NEXT JS
  • 메이븐 골
  • rust
  • 싱글톤
  • 자바스크립트
  • 싱글톤 패턴
  • bean Validation
  • 알고리즘
  • Java
  • 자바 검증
  • 토비의 스프링
  • 메이븐 페이즈
  • next js app
  • 자바스크립트 인터뷰
  • item8
  • 프로그래머의 뇌
  • Next.js
  • 이펙티브 자바 item9
  • 작업기억공간
  • 자바
  • 참조 해제
  • 플라이웨이트패턴
  • 자료구조
  • MSSQL
  • 디자인패턴
  • 싱글턴
  • pnpm
  • 러스트
  • 추상 팩터리 패턴
  • 이펙티브 자바

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

자바스크립트/모던 자바스크립트

모던 자바스크립트, TypedArray (타입이 있는 배열)

2023. 3. 19. 00:51

자바스크립트 기본 배열의 문제점

  • 자바스크립트의 배열은 컴퓨터 과학에서 정의하는 일반적 배열이 아니다.
  • 자바스크립트의 배열은 특수처리가 된 객체이다.
    • 배열 인덱스, length, Array.prototype 에서 상속한 메서드들을 가진 객체이다.

타입이 있는 배열 (TypedArray)

  • ES2015 에 생겨났다.
  • 파일 읽기/쓰기, 그래픽 작업, 수학 API 등에 쓰인다.

TypedArray 와 기존 배열과의 차이

  • 값이 항상 프리미티브 숫자 값이다.
    • 8비트 정수 혹은 32비트 부동 소수점 등이다.
  • 배열 내부의 모든 원소는 동일한 타입이다.
  • 배열을 한번 만들면 길이를 변경할 수 없다.
  • 지정된 바이너리 양식으로 연속 메모리 버퍼에 저장된다.
  • 중간에 공백이 있을 수 없다.
    • 성긴 배열 (sparse array) 이 아니다.
  • 다른 타입의 배열과 메모리를 공유할 수 있다.
  • 스레드 간에 전송되거나 공유될 수 있다.
    • 브라우저의 웹 워커나 워커 스레드에서 공유될 수 있다.
  • 타입이 있는 배열을 가져올 때 Float64 타입이 아니라면, 형변환이 수반된다.
onst a = [];
a[9] = "nine";
console.log(a);

성긴 배열을 만드는 간단한 예제 코드.

TypedArray 에 대해 오해하면 안되는 점

  • 기존 배열과 마찬가지로 객체이다.
    • 포인터가 있는 미가공 데이터 블록이 아니다.
    • 기존 자바스크립트 배열처럼 엔트리가 아닌 속성을 넣을 수 있다.

TypedArray 의 종류

총 11 가지가 존재한다.

  • 이름, 값타입, 엔트리 사이즈, 변환 작업 순이다.
  • Int8Array Int8 1 ToInt8
  • Uint8Array Uint8 1 ToUint8
  • Uint8ClampedArray Uint8Clamped 1 ToUint8Clamped
  • Int16Array Int16 2 ToInt16
  • Uint16Array Uint16 2 ToUint16
  • Int32Array Int32 4 ToInt32
  • Uint32Array Uint32 4 ToUint32
  • Float32Array Float32 4 반올림 모드 (IEEE-754-2008)
  • Float64Array Float64 8 변환이 필요하지 않다.
  • BigInt32Array BigInt32 8 ToBigInt32
  • BigUInt64Array BigUInt64 8 ToBigUInt64

Clamped array 에서 Clamp 의 의미

TypedArray 사용법

  • 생성자 혹은 생성자의 of 혹은 from 메서드를 통해 생성한다.
    • 배열 리터럴로 생성할 수 없다.
    • TypedArray 의 타입이 달라도 생성법은 같다.

생성자를 통한 생성 방법

  • new TypedArray(): 길이가 0으로 설정된 배열을 생성한다.
  • new TypedArray(length): 길이 만큼의 엔트리가 있는 배열을 생성한다.
  • new TypedArray(object): array-like 객체나 iterable 객체만 인자로 올 수 있다.
    • TypedArray.from() 메서드와 동일하다.
  • new TypedArray(typedArray): TypedArray 의 메모리를 복사하여 생성한다. 이터레이터를 거치지 않는다.
  • new TypedArray(buffer[, start[, length]]): ArrayBuffer 혹은 SharedArrayBuffer 를 이용하여 생성한다.

TypedArray 에 다른 타입을 할당했을 때

  • 다른 타입의 값을 할당하면, 자동으로 변환 함수를 수행한다.
  • 평가된 값이 undefined 일 때는 0 이 할당된다.
const a1 = new Int8Array(3);
a1[0] = 1;
a1[1] = "2";
a1[2] = 3;
console.log(a1); // Int8Array(3): [1, 2, 3]

const a2 = Int8Array.of(1, 2, "3");
console.log(a2); // Int8Array(3): [1, 2, 3]

const a3 = Int8Array.from({ length: 3, 0: 1, 1: "2" });
console.log(a3); // Int8Array(3): [1, 2, 0]

const a4 = Int8Array.from([1, 2, 3]);
console.log(a4); // Int8Array(3): [1, 2, 3]

TypedArray 에 범위를 벗어나는 숫자를 입력했을 때

  • 범위를 초과하는 숫자를 입력했을 때 반응은 타입마다 조금씩 다르다.

부동 소수점 배열에 할당하는 경우

  • Float64 의 경우엔 자바스크립트 Number 와 동일하기 때문에 변화가 없다.
  • Float32 의 경우엔 IEEE-754-2008 사양의 "가장 가까운 값으로 반올림, 짝수에 연결" 모드를 사용하여 Float32 로 변환된다.

일반 정수 배열에 할당하는 경우 1: 범위를 초과하는 경우

  • 최대 값이나 최소 값 범위를 넘어서는 경우
  • 래핑이 일어나서 Int8 범위의 값으로 변경된다.
const i1 = Int8Array.of(127, -128, 128, -129);
console.log(i1); // Int8Array(4) [127, -128, -128, 127]
  • 127, -128 은 범위 내에 있는 값이므로 그대로 표현된다.
  • 128 은 1000 0000 이고, 부호 비트가 1(음수) 이라서 1의 보수를 취하면 0111 1111 이 되고 2의 보수를 취하면 1000 0000 이 되어 -128 인 것을 확인할 수 있다.

1의 보수와 2의 보수를 쉽게 이해할 수 있는 관련 포스팅

const i2 = Int8Array.of(500, -500);
console.log(i2); // Int8Array(2) [-12, 12]
  • 500 은 2진수로 바꾸면 0001 1111 0100 이 된다.
    • 우측 끝에서 8비트만 남기면 1111 0100 이 된다.
    • 1111 0100 에서 1의 보수를 취하면 0000 1011 이 된다.
    • 0000 1011 에서 2의 보수로 만들면 0000 1100 이 된다.
    • 즉 -12 가 되는 것이다.
  • -500 은 2진수로 바꾸면 0010 0000 1100 이 된다.
    • 8비트만 남기면 0000 1100 이 된다.
    • 즉 12 가 되는 것이다.

일반 정수 배열에 할당하는 경우 2: 소수점을 넣는 경우

const a = new Uint8Array(1);
a[0] = -25.4;
console.log(a[0]); // 231
  • -25.4 를 넣으면 먼저 소수점에 대한 부분이 날아간다.
  • -25 는 8bit 로 표현하면 128 - 25 = 103 이라 -128 + 103 을 해주면 될 것이다.
  • 1110 0111 이 -25 일 것이다.
  • 그런데 이번엔 Int8Array 가 아니라 Uint8Array 여서 1110 0111 은 231 이 된다.

ArrayBuffer: TypedArray 에 사용되는 저장소

  • ArrayBuffer 는 모든 타입의 TypedArray 를 품을 수 있다.
  • 비트를 저장해둘 수 있고, 몇비트씩 짤라서 볼 것이냐에 따라 TypedArray 가 보는 관점이 되는 것이다.
  • 단, 데이터에 직접 접근할 수는 없고 TypedArray 혹은 DataView 를 통해서만 접근 가능하다.

ArrayBuffer 를 이용해 TypedArray 공간 할당해보기

const arrayBuffer = new ArrayBuffer(20);
const intArray = new Int32Array(arrayBuffer);
console.log(arrayBuffer.byteLength); // 20
console.log(intArray.length); // 5
  • new ArrayBuffer(20) 는 20바이트의 ArrayBuffer 를 생성한다.
  • new Int32Array(arrayBuffer) 는 20바이트의 ArrayBuffer 를 이용해 생성된다.
    • 32 비트는 4 바이트고, 총 5개의 공간이 있는 Int32Array 배열이 된다.
const intArray = new Int32Array(5);
console.log(intArray.length); // 5
  • 위의 코드와 같다.

적합하지 않은 공간을 할당한다면?

  • 적합하지 않은 공간이 1개의 공간에 4바이트가 드는데 총 공간이 7바이트로 떨어지거나 하는 경우이다.
  • 배열 1개도 2개도 될 수 없다.
const arrayBuffer = new ArrayBuffer(7);
const intArray = new Int32Array(arrayBuffer);

/*
Uncaught RangeError: byte length of Int32Array should be a multiple of 4
    at new Int32Array (<anonymous>)
    at <anonymous>:2:18
*/
  • 에러가 발생한다.
  • 이러한 경우를 방지하기 위해 TypedArray 에는 BYTES_PER_ELEMENT 라는 속성을 제공한다.
const arrayBuffer = new ArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 5);
const intArray = new Int32Array(arrayBuffer);
  • 보통은 이렇게 코드를 작성할 일은 거의 없고 어떠한 사정에 의해 ArrayBuffer 를 따로 작성하는 경우에 필요하다.

PNG 확장자인지 체크하는 로직 만들어보기

  • 읽은 파일의 데이터를 8바이트씩 읽어 PNG_HEADER 패턴인지 확인한다.
  • FileReader 는 범용적이어서 몇비트 단위로 접근할지 미리 정해두지 않는다.
const PNG_HEADER = Uint8Array.of(
  0x89,
  0x50,
  0x4e,
  0x47,
  0x0d,
  0x0a,
  0x1a,
  0x0a
);

const isPNG = (byteData) =>
  byteData.length >= PNG_HEADER.length &&
  PNG_HEADER.every((b, i) => b === byteData[i]);

const show = (msg) => {
  const p = document.createElement("p");
  p.appendChild(document.createTextNode(msg));
  document.body.appendChild(p);
};

document
  .getElementById("file-input")
  .addEventListener("change", ({ target }) => {
    const file = target.files[0];

    if (!file) {
      return;
    }

    const fr = new FileReader();
    fr.readAsArrayBuffer(file);
    fr.onload = () => {
      const byteData = new Uint8Array(fr.result);
      show(`${file.name} ${isPNG(byteData) ? "is" : "is not"} a PNG file.`);
    };
    fr.onerror = (error) => {
      show(`File read failed: ${error}`);
    };
  });

DataView 로 ArrayBuffer 접근하기

  • ArrayBuffer 에 접근하는 여러 방식을 제공한다.
  • 빅 엔디언 혹은 리틀 엔디언 방식을 지정하여 읽어낼 수도 있다.
    • dv.getUint32(0, true) 처럼 두번째 인자가 true 면 littleEndian 으로 읽는 것이다.
const PNG_HEADER1 = 0x89504e47;
const PNG_HEADER2 = 0x0d0a1a0a;
const TYPE_IHDR = 0x49484452;

const isPNG = (byteData) =>
  byteData.length >= PNG_HEADER.length &&
  PNG_HEADER.every((b, i) => b === byteData[i]);

const show = (msg) => {
  const p = document.createElement("p");
  p.appendChild(document.createTextNode(msg));
  document.body.appendChild(p);
};

document
  .getElementById("file-input")
  .addEventListener("change", ({ target }) => {
    const file = target.files[0];

    if (!file) {
      return;
    }

    const fr = new FileReader();
    fr.readAsArrayBuffer(file);
    fr.onload = () => {
      const dv = new DataView(fr.result);

      if (
        dv.byteLength >= 24 &&
        dv.getUint32(0) === PNG_HEADER_1 &&
        dv.getUint32(4) === PNG_HEADER_2 &&
        dv.getUint32(12) === TYPE_IHDR
      ) {
        const width = dv.getUint32(16);
        const height = dv.getUint32(20);
        show(`${file.name}은 ${width} x ${height} 픽셀`);
      } else {
        show(`${file.name}은 PNG file 이 아니다.`);
      }
    };
    fr.onerror = (error) => {
      show(`File read failed: ${error}`);
    };
  });

ArrayBuffer 의 공유

  • ArrayBuffer 는 모든 TypedArray 에서 공통으로 사용 가능하기 때문에 공유도 가능하다.

겹침 없는 공유

  • 겹침 없는 공유는 별다른 문제가 없다.
const buf = new ArrayBuffer(20);
const bytes = new Uint8Array(buf, 0, 8);
const words = new Uint16Array(buf, 8);

console.log(buf.byteLength); // 20 바이트
console.log(bytes.length); // 8 바이트
console.log(words.length); // (16 bit * 6) -> 12 바이트

겹침 있는 공유

  • 겹침 있는 공유는 서로를 침범하므로 주의해야 한다.
  • 아래 코드는 Uint8Array 의 배열이 각각 리틀 엔디언의 상위 바이트 하위 바이트를 가리키고 있음을 인지해야 한다.
const buf = new ArrayBuffer(12);
const bytes = new Uint8Array(buf);
const words = new Uint16Array(buf);
console.log(words[0]); // 0
bytes[0] = 1;
bytes[1] = 2;
console.log(bytes[0]); // 1
console.log(bytes[1]); // 1
console.log(words[0]); // 513 -> 리틀 엔디언 사용

TypedArray 의 메서드

  • 배열의 길이가 고정이라 배열의 길이를 변화시키는 메서드는 없다.
    • pop(), push(), shift(), unshift(), splice()
  • 중첩된 배열이 포함되지 않으므로 아래 메서드도 없다.
    • flat(), flatMap(), concat()

map(), slice(), filter() 메서드

  • map(), slice(), filter() 는 지원하지만 같은 타입의 배열만 반환한다.
const a1 = Uint8Array.of(50, 100, 150, 200);
const a2 = a1.map((v) => v * 2);
console.log(a2); // Uint8Array(4) [50, 100, 150, 200]

TypedArray.prototype.set() 메서드

  • setter 와 비슷한 역할을 한다.
  • 배열 내용과 길이를 주면 set() 된다.
const source = new Uint8Array([1, 2, 3, 4, 5]);
const dest = new Uint8Array(5);
dest.set(source);

console.log(dest); // Output: Uint8Array [ 1, 2, 3, 4, 5 ]

TypedArray.prototype.subarray() 메서드

  • 메모리를 공유하는 하위 TypedArray 를 생성한다.
  • begin 과 end 를 인자로 받아 범위를 지정 가능하다.
  • 아래 예제에서 값을 바꾸니 공유하는 메모리 값이 같이 바뀐 것을 알 수 있다.
const wholeArray = Uint8Array.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
const firstHalf = wholeArray.subarray(0, 5);
console.log(wholeArray); // Uint8Array(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(firstHalf); // Uint8Array(5) [0, 1, 2, 3, 4]
firstHalf[0] = 100;
console.log(wholeArray); // Uint8Array(10) [100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(firstHalf); // Uint8Array(5) [100, 1, 2, 3, 4]
반응형
저작자표시 비영리 (새창열림)

'자바스크립트 > 모던 자바스크립트' 카테고리의 다른 글

모던 자바스크립트, 4가지 동등성 비교 알고리즘  (0) 2023.03.22
모던 자바스크립트, Reflect (리플렉트) 객체란?  (0) 2023.03.20
모던 자바스크립트, ES2019 의 stable 내장 정렬 (Array.prototype.sort)  (0) 2023.03.15
모던 자바스크립트, 편의 유틸 배열 메서드  (0) 2023.03.13
모던 자바스크립트, 편의 유틸 문자열 메서드  (0) 2023.03.12
    '자바스크립트/모던 자바스크립트' 카테고리의 다른 글
    • 모던 자바스크립트, 4가지 동등성 비교 알고리즘
    • 모던 자바스크립트, Reflect (리플렉트) 객체란?
    • 모던 자바스크립트, ES2019 의 stable 내장 정렬 (Array.prototype.sort)
    • 모던 자바스크립트, 편의 유틸 배열 메서드
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바