고급 조건문 패턴 (Advanced Conditional Patterns)

고급 조건문 패턴 (Advanced Conditional Patterns)

자바스크립트에서 조건문은 프로그램 흐름을 제어하는 데 핵심적인 역할을 한다. 단순한 if-else 구조를 넘어 고급 패턴을 사용하면 코드의 가독성과 유지보수성을 높이고 성능을 개선할 수 있다. 고급 조건문 패턴의 개념과 다양한 접근법을 코드와 함께 단계적으로 알아보자.


조건문을 잘 활용하면 복잡한 로직도 간결하고 명확하게 정리된다. 기본부터 심화된 기법까지 차근차근 다뤄보자.


조건문의 기본 구조

자바스크립트에서 조건문은 주로 if, else if, else로 구성된다. 다음 코드를 보자:

const score = 85;
if (score >= 90) {
 console.log("A");
} else if (score >= 80) {
 console.log("B");
} else {
 console.log("C");
} // "B"

이 방식은 직관적이지만 조건이 늘어나면 복잡해질 수 있다. 더 나은 방법을 찾아보자.


1. Early Return 패턴

Early Return은 조건이 충족되면 즉시 함수를 끝내며 불필요한 중첩을 줄이는 방식이다. 두 가지 접근을 비교해보자:

// 중첩된 구조
function checkUser(user) {
 if (user) {
 if (user.age >= 18) {
 console.log("성인입니다.");
 } else {
 console.log("미성년자입니다.");
 }
 } else {
 console.log("사용자가 없습니다.");
 }
}

// Early Return 사용
function checkUserOptimized(user) {
 if (!user) return console.log("사용자가 없습니다.");
 if (user.age < 18) return console.log("미성년자입니다.");
 console.log("성인입니다.");
}

checkUserOptimized({ "age": 20 }); // "성인입니다."

Early Return은 코드의 깊이를 줄여 이해하기 쉽게 만들고 불필요한 검사를 피한다.


2. 객체 맵(Object Map) 패턴

조건이 많을 때 if-else 대신 객체를 사용하면 로직이 간단해진다. 다음 코드를 보자:

// 긴 조건 분기
function getGrade(score) {
 if (score >= 90) return "A";
 else if (score >= 80) return "B";
 else if (score >= 70) return "C";
 else if (score >= 60) return "D";
 else return "F";
}

// 객체 맵 적용
const gradeMap = {
 90: "A",
 80: "B",
 70: "C",
 60: "D",
 0: "F"
};
function getGradeOptimized(score) {
 for (const threshold in gradeMap) {
 if (score >= threshold) return gradeMap[threshold];
 }
}

console.log(getGradeOptimized(85)); // "B"

객체 맵은 조건을 데이터로 바꿔 관리하기 편리하게 만든다.


3. 삼항 연산자와 간소화

간단한 조건은 삼항 연산자(?:)로 줄일 수 있다. 다음 코드를 보자:

// 일반적인 분기
let message;
if (age >= 18) {
 message = "성인";
} else {
 message = "미성년자";
}

// 삼항 연산자 사용
const age = 20;
const messageOptimized = age >= 18 ? "성인" : "미성년자";
console.log(messageOptimized); // "성인"

삼항 연산자는 코드 길이를 줄이지만, 지나치게 중첩하면 읽기 어려울 수 있으니 적절히 사용하자.


4. 논리 연산자 활용

&&||를 사용하면 조건문을 더 간결하게 표현할 수 있다. 다음 코드를 보자:

// 일반적인 조건
function greet(name) {
 if (name) {
 console.log(`안녕, ${name}!`);
 } else {
 console.log("안녕, 손님!");
 }
}

// 논리 연산자 적용
function greetOptimized(name) {
 console.log(`안녕, ${name || "손님"}!`);
}

greetOptimized("철수"); // "안녕, 철수!"
greetOptimized(); // "안녕, 손님!"

||는 기본값을 설정하는 데 유용하고, &&는 조건에 따른 동작을 간소화한다:

const user = { "active": true };
user.active && console.log("활성 사용자"); // "활성 사용자"

5. Switch 대체 패턴

switch 대신 객체 또는 함수 맵 으로 로직을 구성할 수 있다. 다음 코드를 보자:

// switch 사용
function getAction(command) {
 switch (command) {
 case "start":
 return "시작";
 case "stop":
 return "정지";
 default:
 return "알 수 없음";
 }
}

// 함수 맵 사용
const actions = {
 "start": () => "시작",
 "stop": () => "정지",
 default: () => "알 수 없음"
};
function getActionOptimized(command) {
 return (actions[command] || actions.default)();
}

console.log(getActionOptimized("start")); // "시작"

함수 맵은 동적인 로직을 추가하기 쉬워 유연성이 뛰어나다.


6. 패턴 매칭 스타일

자바스크립트는 공식 패턴 매칭을 지원하지 않지만, 객체 분해로 비슷한 효과를 낼 수 있다:

function describeUser({ age, name } = {}) {
 return age >= 18 && name
 ? `성인 ${name}`
 : age < 18 && name
 ? `미성년자 ${name}`
 : "정보 없음";
}

console.log(describeUser({ age: 20, name: "영희" })); // "성인 영희"

구조 분해를 활용하면 조건과 데이터를 깔끔하게 연결할 수 있다.


7. 상태 기반 조건문

상태 객체를 사용해 조건문을 체계적으로 관리할 수 있다:

const state = {
 "isLoggedIn": true,
 "isAdmin": false,
 "hasPermission": true
};

function checkAccess(state) {
 if (!state.isLoggedIn) return "로그인 필요";
 if (state.isAdmin) return "관리자 접근 허용";
 if (state.hasPermission) return "일반 접근 허용";
 return "접근 불가";
}

console.log(checkAccess(state)); // "일반 접근 허용"

상태 기반 접근은 복잡한 조건을 구조적으로 정리한다.


8. 다양한 상황에서의 활용

고급 조건문 패턴을 여러 상황에 적용한 코드를 살펴보자.


1. 사용자 권한 확인

const roles = {
 "admin": () => "모든 권한",
 "editor": () => "편집 권한",
 "viewer": () => "보기 권한",
 default: () => "권한 없음"
};

function getPermission(user) {
 if (!user || !user.role) return roles.default();
 return (roles[user.role] || roles.default)();
}

console.log(getPermission({ role: "editor" })); // "편집 권한"

2. 상태에 따른 출력

const renderContent = (status) => {
 const contentMap = {
 "loading": "로딩 중...",
 "success": "데이터 로드 완료",
 "error": "오류 발생"
 };
 return contentMap[status] || "알 수 없는 상태";
};

console.log(renderContent("success")); // "데이터 로드 완료"

성능과 가독성에 미치는 영향

고급 패턴은 코드의 성능과 가독성에 영향을 준다:

- 성능: 객체 맵은 O(1) 조회 속도를 제공하며, 긴 if-elseswitch보다 효율적일 수 있다.

- 가독성: 중첩을 줄이고 로직을 데이터로 변환하면 코드를 더 쉽게 파악할 수 있다.

Early Return객체 맵은 특히 자주 활용되는 패턴으로 주목할 만하다.


마무리

고급 조건문 패턴은 자바스크립트에서 코드 품질을 높이는 데 중요한 도구다. Early Return, 객체 맵, 논리 연산자 등 다양한 방법을 코드와 함께 다뤘으니, 이를 활용해 더 나은 코드를 작성해보자.


배열 성능 최적화 (Array Performance Optimization)

배열 성능 최적화 (Array Performance Optimization)

자바스크립트에서 배열(Array)은 데이터를 다루는 핵심 자료구조다. 하지만 배열을 잘못 사용하면 성능 저하가 발생할 수 있다. 배열 성능 최적화의 개념, 주요 기법, 실제 예제를 통해 이해할 수 있도록 다뤄보자.


성능 최적화는 코드 실행 속도를 높이고 메모리 사용을 줄이는 데 초점을 맞춘다. 자바스크립트 엔진의 동작 방식을 이해하며 시작해보자.


배열과 자바스크립트 엔진의 동작

자바스크립트의 배열은 동적이며, 내부적으로 객체처럼 동작한다. 하지만 일반 객체와 달리 배열은 연속적인 메모리 공간을 활용해 빠른 인덱스 접근을 제공한다. 기본 예제를 보자:

const arr = [1, 2, 3];
console.log(arr[0]); // 1 (O(1) 시간 복잡도)
arr.push(4); // 동적 크기 조정
console.log(arr); // [1, 2, 3, 4]

배열의 인덱스 접근은 O(1)로 매우 빠르지만, 삽입이나 삭제 같은 작업은 상황에 따라 느려질 수 있다. 최적화를 위해선 이런 특성을 이해해야 한다.


배열 최적화의 기본 원칙

배열 성능을 개선하려면 다음 원칙을 따르는 것이 중요하다:

- 불필요한 연산 줄이기: 반복문에서 중복 계산을 피한다.

- 메모리 사용 최소화: 불필요한 배열 복사를 줄인다.

- 엔진 최적화 활용: V8 같은 엔진이 이해하기 쉬운 코드를 작성한다.


이제 구체적인 기법과 예제를 살펴보자.


1. 배열 생성 최적화

배열을 생성할 때 초기 크기를 설정하면 메모리 재할당을 줄일 수 있다. 기본 생성과 최적화된 생성을 비교해보자:

// 비효율적인 생성
const arr1 = [];
for (let i = 0; i < 1000000; i++) {
    arr1.push(i); // 크기 조정 빈번 발생
}

// 최적화된 생성
const arr2 = new Array(1000000);
for (let i = 0; i < 1000000; i++) {
    arr2[i] = i; // 메모리 재할당 없음
}

new Array(size)로 초기 크기를 설정하면 동적 크기 조정으로 인한 오버헤드가 줄어든다.


2. 반복문 최적화

배열을 순회할 때 반복문의 성능은 매우 중요하다. 일반적인 for 루프와 최적화된 방법을 비교해보자:

const arr = new Array(1000000).fill(0);

// 비효율적인 반복
for (let i = 0; i < arr.length; i++) {
    arr[i] += 1; // length를 매번 계산
}

// 최적화된 반복
const len = arr.length;
for (let i = 0; i < len; i++) {
    arr[i] += 1; // length 캐싱
}

length를 변수에 저장하면 속도가 빨라진다. V8 엔진은 이를 더 잘 최적화한다.


forEach vs for: 함수형 메서드와 기본 루프의 차이도 보자:

arr.forEach((item, i) => {
    arr[i] += 1;
}); // 콜백 호출 오버헤드 발생

forEach는 가독성이 좋지만 성능은 for보다 느리다.


3. 배열 복사와 메모리 관리

배열을 복사할 때 얕은 복사와 깊은 복사의 차이를 이해해야 한다. 예제를 보자:

const original = [1, 2, 3];

// 비효율적인 복사
const copy1 = [];
for (let i = 0; i < original.length; i++) {
    copy1[i] = original[i];
}

// 최적화된 복사
const copy2 = [...original]; // 스프레드 연산자
const copy3 = original.slice(); // slice 사용

slice나 스프레드 연산자는 간결하고 빠르다. 대규모 배열에서는 slice가 약간 더 효율적일 수 있다.


4. 배열 조작 최적화

삽입, 삭제 같은 조작은 배열의 연속성을 깨뜨릴 수 있다. 예제를 통해 비교해보자:

const arr = [1, 2, 3, 4];

// 비효율적인 삭제 (앞부분)
arr.shift(); // O(n) - 모든 요소 이동
console.log(arr); // [2, 3, 4]

// 효율적인 삭제 (뒷부분)
arr.pop(); // O(1) - 끝 요소 제거
console.log(arr); // [2, 3]

pushpop은 끝에서 동작하므로 O(1)로 빠르다. 반면 shiftunshiftO(n)으로 느리다.


중간 삽입 최적화: 중간에 삽입해야 한다면 splice 대신 대안 자료구조를 고려할 수 있다:

arr.splice(1, 0, 2.5); // O(n) - 요소 이동
console.log(arr); // [2, 2.5, 3]

5. 필터링과 매핑 최적화

filter, map 같은 메서드는 편리하지만 성능에 영향을 줄 수 있다. 예제를 보자:

const numbers = new Array(1000000).fill().map((_, i) => i);

// 비효율적인 연속 사용
const result1 = numbers.filter(n => n % 2 === 0)
                        .map(n => n * 2);

// 최적화된 단일 순회
const result2 = [];
for (let i = 0, len = numbers.length; i < len; i++) {
    if (numbers[i] % 2 === 0) {
        result2.push(numbers[i] * 2);
    }
}

연속적인 메서드 체인은 배열을 여러 번 순회하므로 단일 순회로 통합하면 성능이 향상된다.


6. 희소 배열(Sparse Array) 피하기

희소 배열은 메모리를 낭비하고 성능을 저하시킨다. 예제를 보자:

// 희소 배열 생성
const sparse = [];
sparse[1000] = 1;
console.log(sparse.length); // 1001
console.log(sparse[0]); // undefined

// 밀집 배열 사용
const dense = new Array(1);
dense[0] = 1;

희소 배열은 엔진 최적화를 어렵게 하므로 밀집 배열을 사용하는 것이 좋다.


7. Typed Array 사용

고정된 타입의 데이터를 다룰 때는 TypedArray를 활용하면 성능이 향상된다:

const regularArr = new Array(1000000).fill(1);
const typedArr = new Int32Array(1000000);
typedArr.fill(1);

for (let i = 0; i < 1000000; i++) {
    regularArr[i] += 2;
    typedArr[i] += 2;
}

Int32Array는 메모리 사용과 계산 속도에서 일반 배열을 능가한다.


8. 실제 활용 사례

배열 최적화를 실무에서 어떻게 적용할지 예제로 확인해보자.


1. 대규모 데이터 필터링

const data = new Array(1000000).fill().map((_, i) => ({
    "id": i,
    "active": i % 2 === 0
}));

const activeItems = [];
for (let i = 0, len = data.length; i < len; i++) {
    if (data[i].active) {
        activeItems.push(data[i]);
    }
}

2. 게임 데이터 처리

const positions = new Float32Array(100000);
for (let i = 0; i < 100000; i++) {
    positions[i] = Math.random() * 100;
}

성능 측정과 비교

performance.now()로 최적화 전후를 측정해보자:

const arr = new Array(1000000).fill(0);
let start = performance.now();
for (let i = 0; i < arr.length; i++) {
    arr[i] += 1;
}
console.log("비최적화:", performance.now() - start);

start = performance.now();
const len = arr.length;
for (let i = 0; i < len; i++) {
    arr[i] += 1;
}
console.log("최적화:", performance.now() - start);

최적화된 코드가 일반적으로 더 빠른 것을 확인할 수 있다.


마무리

배열 성능 최적화는 자바스크립트 개발에서 필수적인 기술이다. 초기화, 반복, 복사, 조작 등 다양한 상황에서 최적화 기법을 적용하면 성능을 크게 개선할 수 있다.


WeakSet과 WeakMap (WeakSet and WeakMap)

WeakSet과 WeakMap (WeakSet and WeakMap)

자바스크립트에서 WeakSetWeakMap은 ES6에서 도입된 데이터 구조로, 객체 참조를 약하게 유지한다. 메모리 관리를 개선하며 Set과 Map의 특수한 변형이다. WeakSet과 WeakMap의 개념, 사용 방법, 활용 사례를 예제로 확인해보자.


이들은 가비지 컬렉션(Garbage Collection)과 연계되어 동작한다.


WeakSet이란 무엇인가?

WeakSet은 객체만 저장할 수 있는 집합으로, 참조가 약하다. 객체가 더 이상 참조되지 않으면 자동으로 제거된다. 기본적인 예제를 보자:

const weakSet = new WeakSet();
let obj = { "id": 1 };
weakSet.add(obj);
console.log(weakSet.has(obj)); // true
obj = null; // 참조 제거
// 가비지 컬렉션 후 weakSet에서 obj 사라짐

WeakSet은 값으로 객체만 허용하며, 크기나 내용을 직접 확인할 수 없다.


WeakMap이란 무엇인가?

WeakMap은 키가 객체만 가능한 키-값 쌍 컬렉션이다. 키가 더 이상 참조되지 않으면 해당 항목이 제거된다. 예제를 보자:

const weakMap = new WeakMap();
let key = { "id": 2 };
weakMap.set(key, "철수");
console.log(weakMap.get(key)); // "철수"
key = null; // 참조 제거
// 가비지 컬렉션 후 weakMap에서 key 사라짐

WeakMap은 키로 객체만 사용할 수 있다.


WeakSet과 WeakMap의 주요 메서드

WeakSet과 WeakMap의 메서드는 제한적이다. 예제로 확인해보자.


1. WeakSet 메서드

- add: 객체 추가

- has: 객체 존재 여부 확인

- delete: 객체 삭제

const ws = new WeakSet();
const obj1 = {};
ws.add(obj1);
console.log(ws.has(obj1)); // true
ws.delete(obj1);
console.log(ws.has(obj1)); // false

2. WeakMap 메서드

- set: 키-값 쌍 추가

- get: 값 조회

- has: 키 존재 여부 확인

const wm = new WeakMap();
const key2 = {};
wm.set(key2, "영희");
console.log(wm.get(key2)); // "영희"
console.log(wm.has(key2)); // true

WeakSet과 WeakMap의 동작 방식

WeakSet과 WeakMap은 약한 참조를 통해 메모리를 관리한다. 예제로 확인해보자.


1. 약한 참조

참조가 끊어지면 가비지 컬렉션이 동작한다:

let weakSet2 = new WeakSet();
let temp = {};
weakSet2.add(temp);
temp = null;
// weakSet2에서 temp 자동 제거 (검사 불가)

2. 비반복성

내용을 순회할 수 없다:

const weakMap2 = new WeakMap();
const key3 = {};
weakMap2.set(key3, 42);
// weakMap2.forEach 같은 메서드 없음

사용 사례

WeakSet과 WeakMap을 활용한 몇 가지 예제를 확인해보자.


1. 객체 추적 (WeakSet)

특정 객체가 처리되었는지 확인한다:

const processed = new WeakSet();
const item = { "data": 1 };
if (!processed.has(item)) {
    processed.add(item);
    console.log("처리됨");
} // "처리됨"

2. 메타데이터 저장 (WeakMap)

객체에 추가 정보를 붙인다:

const metadata = new WeakMap();
const user = { "id": 3 };
metadata.set(user, { "role": "admin" });
console.log(metadata.get(user)); // {role: "admin"}

3. DOM 요소 관리

DOM 객체를 약하게 참조한다:

const domRefs = new WeakMap();
const element = document.createElement("div");
domRefs.set(element, "메인 요소");
console.log(domRefs.get(element)); // "메인 요소"
// element 제거 시 domRefs에서도 자동 정리

성능과 한계

장점

- 메모리 관리: 약한 참조로 메모리 누수를 방지한다.

- 간결함: 불필요한 참조를 유지하지 않는다.


한계

- 제한된 기능: 반복, 크기 확인 불가.

- 객체 전용: WeakSet과 WeakMap은 객체만 다룬다.

SetMap은 강한 참조를 유지하며 더 많은 메서드를 제공한다.


마무리

WeakSet과 WeakMap은 자바스크립트에서 메모리 효율적인 데이터 관리를 가능하게 한다. 약한 참조의 특성과 활용 사례를 예제로 다뤘다.


Set과 Map (Set and Map)

Set과 Map (Set and Map)

자바스크립트에서 SetMap은 ES6에서 도입된 새로운 데이터 구조다. 중복 없는 값과 키-값 쌍을 효율적으로 관리한다. Set과 Map의 개념, 사용 방법, 활용 사례를 예제로 확인해보자.


이들은 배열이나 객체와 다른 독특한 특징을 제공한다.


Set이란 무엇인가?

Set은 중복되지 않는 값의 집합이다. 배열과 비슷하지만 값의 유일성을 보장한다. 기본적인 예제를 보자:

const set = new Set();
set.add(1);
set.add(2);
set.add(1); // 중복 무시
console.log(set); // Set {1, 2}

Set은 값만 저장하며 순서를 유지한다.


Map이란 무엇인가?

Map은 키-값 쌍을 저장하는 컬렉션이다. 객체와 비슷하지만 키로 모든 타입을 사용할 수 있다. 예제를 보자:

const map = new Map();
map.set("name", "철수");
map.set(42, "숫자");
console.log(map); // Map {"name" => "철수", 42 => "숫자"}

Map은 객체와 달리 키로 객체도 사용할 수 있다.


Set과 Map의 주요 메서드

Set과 Map의 주요 메서드를 예제로 확인해보자.


1. Set 메서드

- add: 값 추가

- has: 값 존재 여부 확인

- delete: 값 삭제

const set2 = new Set([1, 2, 3]);
set2.add(4);
console.log(set2.has(2)); // true
set2.delete(3);
console.log(set2); // Set {1, 2, 4}

2. Map 메서드

- set: 키-값 쌍 추가

- get: 값 조회

- has: 키 존재 여부 확인

const map2 = new Map();
const keyObj = {};
map2.set(keyObj, "객체 키");
console.log(map2.get(keyObj)); // "객체 키"
console.log(map2.has(keyObj)); // true

Set과 Map의 동작 방식

Set과 Map은 일반 배열이나 객체와 다른 동작을 보인다. 예제로 확인해보자.


1. Set의 중복 제거

중복 값을 자동으로 제거한다:

const array = [1, 2, 2, 3];
const unique = new Set(array);
console.log([...unique]); // [1, 2, 3]

2. Map의 키 타입 유연성

객체와 달리 다양한 키를 허용한다:

const map3 = new Map();
const funcKey = function() {};
map3.set(funcKey, "함수 키");
console.log(map3.get(funcKey)); // "함수 키"

사용 사례

Set과 Map을 활용한 몇 가지 예제를 확인해보자.


1. 중복 제거 (Set)

배열에서 고유한 값만 추출한다:

const duplicates = ["a", "b", "a", "c"];
const uniqueSet = new Set(duplicates);
console.log([...uniqueSet]); // ["a", "b", "c"]

2. 객체 속성 관리 (Map)

객체를 키로 사용해 데이터를 관리한다:

const userMap = new Map();
const user1 = { "id": 1 };
const user2 = { "id": 2 };
userMap.set(user1, "철수");
userMap.set(user2, "영희");
console.log(userMap.get(user1)); // "철수"

3. 반복 처리

Set과 Map은 반복이 용이하다:

const set3 = new Set([1, 2, 3]);
for (let value of set3) {
    console.log(value); // 1, 2, 3
}
const map4 = new Map([["a", 1], ["b", 2]]);
for (let [key, value] of map4) {
    console.log(`${key}: ${value}`); // "a: 1", "b: 2"
}

성능과 한계

장점

- 효율성: 중복 제거(Set)와 키-값 관리(Map)가 빠르다.

- 유연성: Map은 객체 키를 허용한다.


한계

- 메모리: 일반 객체보다 더 많은 메모리를 사용할 수 있다.

- 단순성 부족: 간단한 경우 배열이나 객체가 더 적합할 수 있다.

WeakSetWeakMap은 메모리 관리를 개선한 대안이다.


마무리

Set과 Map은 자바스크립트에서 데이터를 관리하는 새로운 방법이다. 주요 메서드와 활용 사례를 예제로 다뤘다.


타입드 배열 (Typed Arrays)

타입드 배열 (Typed Arrays)

자바스크립트에서 타입드 배열(Typed Arrays)은 바이너리 데이터를 효율적으로 다루기 위한 도구다. 숫자 데이터를 고정된 타입으로 저장하며, 일반 배열과 다른 특성을 가진다. 타입드 배열의 개념, 사용 방법, 활용 사례를 예제로 확인해보자.


타입드 배열은 주로 성능이 중요한 상황에서 사용된다.


타입드 배열(Typed Arrays)이란 무엇인가?

타입드 배열은 고정된 크기와 데이터 타입을 가진 배열로, ArrayBuffer를 기반으로 동작한다. 일반 배열과 달리 동적 크기 조정이 불가능하다. 기본적인 예제를 보자:

const buffer = new ArrayBuffer(8); // 8바이트 버퍼
const array = new Int32Array(buffer);
array[0] = 42;
console.log(array[0]); // 42

Int32Array는 32비트 정수 타입으로 데이터를 저장한다.


타입드 배열의 종류와 생성

타입드 배열은 다양한 데이터 타입을 지원한다. 주요 타입과 생성 방법을 예제로 확인해보자.


1. 주요 타입드 배열

- Int8Array: 8비트 정수 (-128 ~ 127)

- Uint8Array: 8비트 부호 없는 정수 (0 ~ 255)

- Int32Array: 32비트 정수

- Float64Array: 64비트 부동 소수점

const int8 = new Int8Array(2);
int8[0] = 127;
console.log(int8[0]); // 127

2. ArrayBuffer와 뷰

ArrayBuffer에 여러 뷰를 적용할 수 있다:

const buffer2 = new ArrayBuffer(4);
const uint8 = new Uint8Array(buffer2);
const int16 = new Int16Array(buffer2);
uint8[0] = 255;
console.log(int16[0]); // 메모리 공유로 값 변동 가능

타입드 배열의 동작 방식

타입드 배열은 메모리를 직접 조작하며, 일반 배열과 다른 특징을 가진다. 예제로 확인해보자.


1. 고정 길이

길이를 변경할 수 없다:

const typed = new Uint16Array(3);
typed[0] = 1;
console.log(typed.length); // 3
typed[3] = 4; // 무시됨
console.log(typed); // Uint16Array [1, 0, 0]

2. 메모리 효율성

불필요한 타입 변환이 없다:

const regular = [1.5, 2.7];
const float32 = new Float32Array([1.5, 2.7]);
console.log(float32); // Float32Array [1.5, 2.7]

사용 사례

타입드 배열을 활용한 몇 가지 예제를 확인해보자.


1. 바이너리 데이터 처리

파일 데이터를 다룰 때 유용하다:

const buffer3 = new ArrayBuffer(4);
const dataView = new DataView(buffer3);
dataView.setInt16(0, 1234);
console.log(dataView.getInt16(0)); // 1234

2. 그래픽 데이터

WebGL에서 버텍스 데이터를 저장한다:

const vertices = new Float32Array([0.0, 1.0, 0.0, -1.0]);
console.log(vertices); // Float32Array [0, 1, 0, -1]

3. 성능 최적화

대량의 숫자 연산에 효율적이다:

const numbers = new Int32Array(1000);
for (let i = 0; i < numbers.length; i++) {
    numbers[i] = i;
}
console.log(numbers[999]); // 999

성능과 한계

장점

- 속도: 메모리 접근이 빠르고 타입 변환이 없다.

- 효율성: 바이너리 데이터를 직접 조작 가능.


한계

- 유연성 부족: 동적 크기 조정 불가.

- 복잡성: ArrayBuffer와 뷰를 이해해야 한다.

DataView는 바이트 단위로 세밀한 제어가 필요할 때 유용하다.


마무리

타입드 배열(Typed Arrays)은 자바스크립트에서 바이너리 데이터를 다루는 도구다. 주요 타입과 활용 사례를 예제로 다뤘다.


프로토타입 (Prototype)

프로토타입 (Prototype)

자바스크립트(JavaScript)에서 객체는 상속과 속성 공유를 위해 프로토타입(Prototype)이라는 메커니즘을 사용한다. 객체가 다른 객체의 속성과 메서드를 물려받을 때 유용하다. 프로토타입의 개념, 동작 방식, 활용 사례를 예제로 확인해보자.


프로토타입은 자바스크립트의 객체 지향 프로그래밍을 가능하게 하는 핵심 요소다.


프로토타입(Prototype)이란 무엇인가?

프로토타입은 객체가 다른 객체로부터 속성과 메서드를 상속받을 수 있게 하는 연결고리다. 모든 객체는 내부적으로 프로토타입을 참조한다. 기본적인 예제를 보자:

const obj = {};
console.log(obj.toString()); // "[object Object]"

objtoString 메서드를 직접 정의하지 않았지만, Object.prototype에서 상속받아 사용한다.


프로토타입 체인(Prototype Chain)

프로토타입 체인은 객체가 속성을 찾을 때 따라가는 참조 경로다. 객체에 속성이 없으면 프로토타입을 확인하고, 없으면 그 다음 프로토타입을 확인한다. 예제를 통해 확인해보자:

const parent = { "x": 1 };
const child = Object.create(parent);
console.log(child."x"); // 1 (parent에서 상속)
child."x" = 2;
console.log(child."x"); // 2 (child 자체 속성)
console.log(parent."x"); // 1 (원본 유지)

프로토타입 정의와 사용

프로토타입을 정의하고 사용하는 몇 가지 방법을 예제로 확인해보자.


1. Object.create

특정 객체를 프로토타입으로 하는 새 객체를 생성한다:

const proto = { "say": function() { return "안녕"; } };
const obj2 = Object.create(proto);
console.log(obj2.say()); // "안녕"

2. 생성자 함수와 prototype

생성자 함수의 prototype 속성을 통해 공유한다:

function Person(name) {
    this."name" = name;
}
Person.prototype.greet = function() {
    return `안녕, ${this.name}`;
};
const p1 = new Person("철수");
console.log(p1.greet()); // "안녕, 철수"

3. 클래스 문법 (ES6)

클래스를 사용하면 프로토타입이 내부적으로 설정된다:

class Animal {
    constructor(type) {
        this."type" = type;
    }
    speak() {
        return `${this.type} 소리`;
    }
}
const dog = new Animal("개");
console.log(dog.speak()); // "개 소리"

사용 사례

프로토타입을 활용한 몇 가지 예제를 확인해보자.


1. 메서드 공유

여러 객체가 동일한 메서드를 공유한다:

const proto2 = { log: function() { console.log(this."value"); } };
const obj3 = Object.create(proto2);
obj3."value" = 10;
const obj4 = Object.create(proto2);
obj4."value" = 20;
obj3.log(); // 10
obj4.log(); // 20

2. 상속 구현

객체 간 상속 관계를 만든다:

const vehicle = { "wheels": 4 };
const car = Object.create(vehicle);
car."type" = "승용차";
console.log(car."wheels"); // 4
console.log(car."type"); // "승용차"

3. 프로토타입 확장

기존 객체의 프로토타입을 수정한다:

Array.prototype.double = function() {
    return this.map(x => x * 2);
};
const arr = [1, 2, 3];
console.log(arr.double()); // [2, 4, 6]

성능과 한계

장점

- 메모리 효율: 메서드를 공유해 메모리를 절약한다.

- 유연성: 동적으로 프로토타입을 수정할 수 있다.


한계

- 복잡성: 프로토타입 체인이 길어지면 디버깅이 어려울 수 있다.

- 속도: 깊은 체인에서 속성 검색이 느려질 수 있다.

__proto__ 대신 Object.getPrototypeOf를 사용하는 것이 권장된다.


마무리

프로토타입(Prototype)은 자바스크립트에서 객체 상속을 구현하는 기반이다. 프로토타입 체인과 활용 사례를 예제로 다뤘다.


ES6 객체 확장 (ES6 Object Extensions)

ES6 객체 확장 (ES6 Object Extensions)

자바스크립트(JavaScript)의 ES6(ECMAScript 2015)에서 객체를 다루는 기능이 크게 확장되었다. ES6 객체 확장은 객체 리터럴 문법 개선, 새로운 메서드 추가 등을 포함한다. ES6에서 도입된 객체 관련 기능과 그 사용법을 예제로 확인해보자.


기존 방식보다 간결하고 효율적인 객체 작업이 가능해졌다.


ES6 객체 확장이란 무엇인가?

ES6 객체 확장은 객체 정의와 조작을 더 편리하게 만드는 문법과 메서드를 제공한다. 대표적인 기능을 예제로 보자:

const name = "철수";
const obj = { name }; // 속성 축약
console.log(obj."name"); // "철수"

ES6 객체 확장 기능

ES6에서 추가된 주요 객체 기능을 예제로 확인해보자.


1. 속성 이름 축약

변수 이름과 속성 이름이 같을 때 축약 가능하다:

const age = 25;
const user = { age, "name": "영희" };
console.log(user); // { age: 25, name: "영희" }

2. 메서드 정의 축약

객체 내 함수 정의에서 function 키워드를 생략한다:

const obj2 = {
    sayHello() {
        return "안녕!";
    }
};
console.log(obj2.sayHello()); // "안녕!"

3. 계산된 속성 이름

속성 이름을 동적으로 정의한다:

const key = "id";
const obj3 = { [key]: 1 };
console.log(obj3."id"); // 1

4. Object.assign

여러 객체를 하나로 병합한다:

const obj4 = { "a": 1 };
const obj5 = { "b": 2 };
const merged = Object.assign({}, obj4, obj5);
console.log(merged); // { a: 1, b: 2 }

5. Object.is

두 값의 엄격한 비교를 수행한다:

console.log(Object.is(0, -0)); // false
console.log(Object.is(NaN, NaN)); // true

사용 사례

ES6 객체 확장을 활용한 예제를 확인해보자.


1. 동적 속성 생성

const prefix = "user";
const data = {
    [prefix + "Name"]: "철수",
    [prefix + "Age"]: 25
};
console.log(data); // { userName: "철수", userAge: 25 }

2. 객체 병합

const defaults = { "theme": "light" };
const settings = { "font": "Arial" };
const config = Object.assign({}, defaults, settings);
console.log(config); // { theme: "light", font: "Arial" }

3. 메서드 정의 간소화

const calculator = {
    value: 0,
    add(x) { this.value += x; },
    getValue() { return this.value; }
};
calculator.add(5);
console.log(calculator.getValue()); // 5

성능과 한계

장점

- 간결함: 코드가 더 짧고 읽기 쉽다.

- 유연성: 동적 속성 이름과 메서드 정의가 편리하다.


한계

- 호환성: ES6 미지원 환경에서는 변환(Babel 등)이 필요하다.

- 얕은 복사: Object.assign은 깊은 복사를 지원하지 않는다.

깊은 복사가 필요한 경우 JSON 방식이나 커스텀 함수를 고려할 수 있다.


마무리

ES6 객체 확장은 자바스크립트에서 객체를 다루는 방식을 개선한다. 새로운 문법과 메서드를 예제로 다뤘다.


객체 복사 (Object Cloning)

객체 복사 (Object Cloning)

자바스크립트(JavaScript)에서 객체를 다룰 때, 원본 객체를 유지하면서 동일한 데이터를 가진 새 객체를 만들고 싶을 때가 있다. 이런 경우에 객체 복사(Object Cloning)를 사용한다. 객체 복사의 개념, 얕은 복사와 깊은 복사의 차이, 구현 방법을 예제로 확인한다.


객체 복사는 원본 데이터를 안전하게 보존하거나 수정 가능한 복제본을 생성하는 데 사용된다. 다양한 방법이 존재한다.


객체 복사(Object Cloning)란 무엇인가?

객체 복사는 기존 객체의 속성을 새 객체로 옮기는 과정이다. 기본적으로 객체는 참조 타입이므로 단순 할당은 복사가 아닌 참조를 공유한다:

const obj = { "name": "철수" };
const ref = obj;
ref."name" = "영희";
console.log(obj."name"); // "영희" (원본 변경됨)

진정한 복사는 원본과 독립적인 새 객체를 만든다:

const obj2 = { "name": "철수" };
const copy = Object.assign({}, obj2);
copy."name" = "영희";
console.log(obj2."name"); // "철수" (원본 유지)

얕은 복사와 깊은 복사의 차이

객체 복사는 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)로 나뉜다.


1. 얕은 복사

최상위 속성만 복사하며, 중첩 객체는 참조를 공유한다:

const original = { "info": { "age": 25 } };
const shallow = Object.assign({}, original);
shallow."info"."age" = 30;
console.log(original."info"."age"); // 30 (원본 변경됨)

2. 깊은 복사

모든 수준의 속성을 복사해 완전히 독립적인 객체를 만든다:

const original2 = { "info": { "age": 25 } };
const deep = JSON.parse(JSON.stringify(original2));
deep."info"."age" = 30;
console.log(original2."info"."age"); // 25 (원본 유지)

객체 복사 방법

객체 복사를 구현하는 여러 방법을 예제로 확인한다.


1. Object.assign

얕은 복사를 수행한다:

const obj3 = { "x": 1, "y": 2 };
const copy1 = Object.assign({}, obj3);
copy1."x" = 10;
console.log(obj3."x"); // 1

2. 스프레드 연산자(...)

ES6에서 도입된 문법으로 얕은 복사를 한다:

const obj4 = { "a": 1, "b": { "c": 2 } };
const copy2 = { ...obj4 };
copy2."b"."c" = 3;
console.log(obj4."b"."c"); // 3 (중첩 객체 참조 공유)

3. JSON 방식

깊은 복사를 위해 JSON 변환을 사용한다:

const obj5 = { "data": { "value": 100 } };
const copy3 = JSON.parse(JSON.stringify(obj5));
copy3."data"."value" = 200;
console.log(obj5."data"."value"); // 100

4. 커스텀 깊은 복사 함수

재귀적으로 깊은 복사를 구현한다:

function deepClone(obj) {
    if (obj === null || typeof obj !== "object") return obj;
    const copy = Array.isArray(obj) ? [] : {};
    for (let key in obj) {
        copy[key] = deepClone(obj[key]);
    }
    return copy;
}
const obj6 = { "nested": { "x": 1 } };
const copy4 = deepClone(obj6);
copy4."nested"."x" = 2;
console.log(obj6."nested"."x"); // 1

사용 사례

객체 복사를 활용한 예제를 확인한다.


1. 원본 데이터 보존

const config = { "setting": 1 };
const workingCopy = { ...config };
workingCopy."setting" = 2;
console.log(config."setting"); // 1

2. 중첩 객체 수정

const data = { "user": { "name": "철수" } };
const deepCopy = JSON.parse(JSON.stringify(data));
deepCopy."user"."name" = "영희";
console.log(data."user"."name"); // "철수"

성능과 한계

장점

- 독립성: 원본과 분리된 객체를 생성한다.

- 간편함: 얕은 복사는 간단한 코드로 가능하다.


한계

- JSON 방식: 함수나 undefined는 복사되지 않는다.

- 성능: 깊은 복사는 큰 객체에서 느릴 수 있다.

복사 대상에 따라 적절한 방법을 선택하는 것이 중요하다.


마무리

객체 복사(Object Cloning)는 자바스크립트에서 데이터를 다루는 방법 중 하나다. 얕은 복사와 깊은 복사의 차이, 구현 방법을 예제로 다뤘다.


+ Recent posts