protobuf를 알아보다가 Uint8Array를 알게되고, 그러다가 TypedArray을 알게 되었다.
자바스크립트 쓰면서 타입이 없어 비효율이 많다는 점을 내면속에 알고만 있었는데, 이런 부분을 보완한 Array가 있다고 해서 알아봤다.
당연히 이런것들이 존재했을 법 한데, 새삼 이제야 알게된다.
TypedArray는 단순히 타입이 있는것 뿐만 아니라 많은 용도들이 있는데 신기해서 가져와 봄.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
TypedArray - JavaScript | MDN
TypedArray 객체는 이진 데이터 버퍼에 기초하여 배열과 같은 보기를 만들어냅니다. 하지만 TypedArray라는 전역 속성은 존재하지 않으며, 직접 볼 수 있는 TypedArray 생성자도 존재하지 않습니다. 대신
developer.mozilla.org
TypedArray가 필요한 이유
일반 배열의 문제점
JavaScript의 일반 배열은 매우 유연하지만, 성능면에서 한계가 있음.
// 일반 배열: 타입이 섞여있고 메모리 비효율적
const normalArray = [1, "hello", 3.14, true, null];
// 메모리에서는 각 요소가 다른 타입으로 저장됨
// 숫자도 내부적으로는 64비트 부동소수점으로 저장
일반 배열의 한계:
- 모든 숫자가 64비트 부동소수점으로 저장 (메모리 낭비)
- 타입 체크 오버헤드
- 메모리가 연속적이지 않을 수 있음
- 바이너리 데이터 처리에 부적합
TypedArray의 장점
// TypedArray: 타입이 고정되고 메모리 효율적
const uint8Array = new Uint8Array([1, 2, 3, 255]);
// 각 요소가 정확히 1바이트씩 연속된 메모리에 저장
// 0~255 범위의 정수만 저장 가능
TypedArray 종류와 특징
정수형 TypedArray
타입 | 크기 | 범위 | 용도 |
---|---|---|---|
Int8Array |
1바이트 | -128 ~ 127 | 부호있는 8비트 정수 |
Uint8Array |
1바이트 | 0 ~ 255 | 부호없는 8비트 정수 |
Uint8ClampedArray |
1바이트 | 0 ~ 255 (고정) | 이미지 픽셀 데이터 |
Int16Array |
2바이트 | -32,768 ~ 32,767 | 부호있는 16비트 정수 |
Uint16Array |
2바이트 | 0 ~ 65,535 | 부호없는 16비트 정수 |
Int32Array |
4바이트 | -2^31 ~ 2^31-1 | 부호있는 32비트 정수 |
Uint32Array |
4바이트 | 0 ~ 2^32-1 | 부호없는 32비트 정수 |
부동소수점 TypedArray
타입 | 크기 | 정밀도 | 용도 |
---|---|---|---|
Float32Array |
4바이트 | 단정밀도 | 3D 그래픽, 게임 |
Float64Array |
8바이트 | 배정밀도 | 과학 계산 |
특수한 Uint8ClampedArray
const normal = new Uint8Array([300, -10, 50]);
console.log(normal); // [44, 246, 50] - 오버플로우 발생
const clamped = new Uint8ClampedArray([300, -10, 50]);
console.log(clamped); // [255, 0, 50] - 범위에 고정됨
TypedArray 생성 방법
1. 길이로 생성
const buffer = new Uint8Array(4); // 4바이트 배열
console.log(buffer); // [0, 0, 0, 0]
2. 배열로 생성
const buffer = new Uint8Array([1, 2, 3, 4]);
console.log(buffer); // [1, 2, 3, 4]
3. ArrayBuffer로 생성
const arrayBuffer = new ArrayBuffer(4); // 4바이트 메모리
const uint8View = new Uint8Array(arrayBuffer);
const uint16View = new Uint16Array(arrayBuffer);
uint8View[0] = 255;
uint8View[1] = 255;
console.log(uint16View[0]); // 65535 (같은 메모리를 다른 방식으로 해석)
4. 다른 TypedArray로부터 생성
const original = new Uint32Array([1, 2, 3, 4]);
const copy = new Uint8Array(original.buffer); // 같은 메모리 공유
TypedArray vs 일반 배열 성능 비교
// 성능 테스트: 1백만 개 숫자 처리
const size = 1000000;
// 일반 배열
console.time('일반 배열');
const normalArray = new Array(size);
for (let i = 0; i < size; i++) {
normalArray[i] = i % 256;
}
console.timeEnd('일반 배열'); // ~50ms
// TypedArray
console.time('Uint8Array');
const typedArray = new Uint8Array(size);
for (let i = 0; i < size; i++) {
typedArray[i] = i % 256;
}
console.timeEnd('Uint8Array'); // ~20ms
// 메모리 사용량
console.log('일반 배열 메모리:', normalArray.length * 8, '바이트'); // 8MB
console.log('TypedArray 메모리:', typedArray.byteLength, '바이트'); // 1MB
실제 사용 사례
여기는 알아보다가 안쓸것 같지만 그렇구나 싶었다. 바이너리를 이해하면 많은걸 할 수 있구나 싶더라.
1. 파일 처리
// 파일을 바이너리로 읽기
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async (event) => {
const file = event.target.files[0];
const arrayBuffer = await file.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
console.log('파일 크기:', uint8Array.length, '바이트');
console.log('첫 4바이트:', uint8Array.slice(0, 4));
});
2. 이미지 픽셀 조작
// Canvas에서 이미지 데이터 가져오기
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// Uint8ClampedArray로 픽셀 데이터 접근
const pixels = imageData.data; // [R, G, B, A, R, G, B, A, ...]
// 이미지를 흑백으로 변환
for (let i = 0; i < pixels.length; i += 4) {
const avg = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;
pixels[i] = avg; // Red
pixels[i + 1] = avg; // Green
pixels[i + 2] = avg; // Blue
// pixels[i + 3]은 Alpha (투명도)
}
ctx.putImageData(imageData, 0, 0);
3. 네트워크 통신
// 바이너리 데이터 전송
const data = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"의 ASCII
fetch('/api/binary', {
method: 'POST',
body: data,
headers: {
'Content-Type': 'application/octet-stream'
}
});
4. WebGL과 3D 그래픽
// 3D 좌표 데이터
const vertices = new Float32Array([
-1.0, -1.0, 0.0, // 점 1
1.0, -1.0, 0.0, // 점 2
0.0, 1.0, 0.0 // 점 3
]);
// WebGL 버퍼에 데이터 전송
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
주의사항과 팁
1. 범위 오버플로우
const uint8 = new Uint8Array([300]); // 300은 255를 초과
console.log(uint8[0]); // 44 (300 % 256 = 44)
// 안전한 값 할당
function safeAssign(array, index, value) {
array[index] = Math.max(0, Math.min(255, value));
}
2. 메모리 효율적인 복사
// 비효율적인 방법
const copy1 = new Uint8Array([...original]);
// 효율적인 방법
const copy2 = new Uint8Array(original.length);
copy2.set(original);
// 가장 빠른 방법 (같은 타입일 때)
const copy3 = original.slice();
3. 기타.
엔디안이란 개념도 보였는데 이건 잘 이해를 못해서 패스..
말로는 멀티바이트 데이터를 메모리에 저장하는 순서를 말한다고 함.
언제 TypedArray를 사용해야 할까?
사용하면 좋은 경우
- 바이너리 데이터 처리: 파일, 이미지, 오디오
- 대용량 숫자 배열: 과학 계산, 데이터 분석
- 메모리 효율성이 중요한 경우: 게임, 실시간 애플리케이션
- WebGL/Canvas 작업: 3D 그래픽, 이미지 처리
- 네트워크 통신: Protocol Buffers, 바이너리 API
일반 배열을 사용하는 경우
- 다양한 타입이 섞인 데이터
- 문자열과 숫자가 함께 있는 경우
- 작은 크기의 배열 (수십 개 이하)
- 복잡한 객체를 저장하는 경우
결론
TypedArray는 JavaScript에서 바이너리 데이터와 대용량 숫자 배열을 효율적으로 처리할 수 있다. 특히 웹에서 파일 처리, 이미지 조작, 3D 그래픽, 실시간 데이터 처리 등의 작업을 할 때 필수.
- 메모리 효율성: 일반 배열보다 8배 적은 메모리 사용
- 성능 향상: 2-3배 빠른 처리 속도
- 타입 안정성: 정해진 범위의 값만 저장
- 바이너리 호환성: C/C++ 등 다른 언어와 데이터 교환 용이
'개발 아카이브 > Javascript' 카테고리의 다른 글
Uint8Array.from()이 반복문보다 빠른 이유 (1) | 2025.08.02 |
---|---|
protobufjs로 JSON보다 10배 빠른 데이터 통신하기 (0) | 2025.08.02 |
크롬 AI 번역 API - Translator API (7) | 2025.07.25 |
Google Apps Script에 ChatGPT 연동하기 (3) | 2024.07.24 |
[Sveltekit] 버튼 hover시 +page.server.js 실행을 막는 법 (0) | 2024.06.17 |