코딩 스쿨 JavaScript

언어선택 : HTMLCSSJAVAJAVASCRIPTMYSQLSQL PHP
▶ JavaScript Tutorial
JavaScript HOME
JavaScript Introduction
JavaScript Where To
JavaScript Output
JavaScript Statements
JavaScript Syntax
JavaScript Comments
JavaScript Variables
JavaScript Let
JavaScript Const
JavaScript Operators
JavaScript Arithmetic
JavaScript Assignment
JavaScript Data Types
JavaScript Functions
JavaScript Objects
JavaScript Object Properties
JavaScript Object Methods
JavaScript Object Display
JavaScript Object Constructors
JavaScript Events
JavaScript Strings
JavaScript String Methods
JavaScript String Search
JavaScript String Templates
JavaScript Numbers
JavaScript BigInt
JavaScript Number Methods
JavaScript Number Properties
JavaScript Arrays
JavaScript Array Methods
JavaScript Array Search
JavaScript Array Sort
JavaScript Array Iteration
JavaScript Array Const
JavaScript Dates
JavaScript Date Formats
JavaScript Date Get Methods
JavaScript Date Set Methods
JavaScript Math
JavaScript Random
JavaScript Booleans
JavaScript Comparisons
JavaScript If Else
JavaScript Switch
JavaScript Loop For
JavaScript Loop For In
JavaScript Loop For Of
JavaScript Loop While
JavaScript Break
JavaScript Iterables
JavaScript Sets
JavaScript Set Methods
JavaScript Maps
JavaScript Map Methods
JavaScript Typeof
JavaScript Type Conversion
JavaScript Destructuring
JavaScript Bitwise
JavaScript RegExp
JavaScript Precedence
JavaScript Errors
JavaScript Scope
JavaScript Hoisting
JavaScript Strict Mode
JavaScript this Keyword
JavaScript Arrow Function
JavaScript Classes
JavaScript Modules
JavaScript JavaScriptON
JavaScript Debugging
JavaScript Style Guide
JavaScript Best Practices
JavaScript Mistakes
JavaScript Performance
JavaScript Reserved Words

JavaScript Mistakes

JavaScript Mistakes: 흔히 발생하는 오류와 해결 방법

JavaScript는 매우 유연하고 강력한 언어이지만, 그 특성상 실수를 저지르기 쉽습니다. 특히 비동기 작업, 변수 스코프, 타입 변환 등과 관련된 실수는 자주 발생합니다. 이러한 실수는 코드 오류예상치 못한 동작을 유발할 수 있습니다. 이 가이드는 JavaScript에서 자주 저지르는 실수들과 이를 방지하거나 수정하는 방법을 다룹니다.


1. var 대신 letconst를 사용하지 않음

  • *var*는 함수 스코프를 가지며, 호이스팅(변수 선언이 코드 상단으로 끌어올려지는 현상)으로 인해 예기치 못한 동작을 일으킬 수 있습니다. 이를 방지하기 위해 **let*과 **const*를 사용하는 것이 좋습니다.

1.1. 문제: var의 호이스팅

function example() {
    console.log(x);  // undefined
    var x = 10;
}
example();

var는 호이스팅 때문에 선언되기 전에 undefined 값으로 초기화됩니다.

1.2. 해결: letconst 사용

function example() {
    console.log(x);  // ReferenceError: x is not defined
    let x = 10;
}
example();

  • *let*과 **const*는 호이스팅되지만, 초기화되기 전에는 접근할 수 없습니다. 따라서 ReferenceError를 발생시켜 논리 오류를 방지할 수 있습니다.

2. 엄격한 비교(===)를 사용하지 않음

  • *==*는 느슨한 비교를 하여 타입을 자동으로 변환한 후 값을 비교합니다. 이로 인해 예상치 못한 결과가 발생할 수 있습니다. **===*를 사용하여 엄격한 타입 비교를 하는 것이 좋습니다.

2.1. 문제: 느슨한 비교(==)

console.log(0 == false);  // true
console.log(null == undefined);  // true

  • *==*는 타입을 자동으로 변환하여 비교하기 때문에, 논리적으로 다를 수 있는 값들도 같다고 판단할 수 있습니다.

2.2. 해결: 엄격한 비교(===) 사용

console.log(0 === false);  // false
console.log(null === undefined);  // false

  • *===*는 타입 변환을 하지 않고 값과 타입을 모두 비교하므로, 보다 명확한 결과를 제공합니다.

3. 비동기 작업에서 return을 사용하지 않음

비동기 작업에서 데이터를 반환할 때 **return**을 사용하지 않으면, Promise가 예상대로 동작하지 않을 수 있습니다. 비동기 함수는 항상 Promise를 반환하므로, 데이터를 반환할 때 return 키워드를 명시해야 합니다.

3.1. 문제: return을 빠뜨림

async function getData() {
    fetch('<https://api.example.com/data>');
}

const data = getData();
console.log(data);  // Promise {<pending>}

위 코드에서는 **fetch**의 반환 값을 반환하지 않아서 data는 항상 Promise 객체가 됩니다.

3.2. 해결: return 명시

async function getData() {
    return fetch('<https://api.example.com/data>');
}

getData().then(response => console.log(response));

  • *return*을 명시하면 getData() 함수가 Promise를 반환하고, **.then()*을 사용하여 비동기 결과를 처리할 수 있습니다.

4. this 바인딩 문제

JavaScript에서 **this**는 함수가 어떻게 호출되었는지에 따라 달라집니다. 특히 화살표 함수일반 함수에서 this가 다르게 동작할 수 있기 때문에, this를 사용할 때 주의가 필요합니다.

4.1. 문제: this가 전역 객체를 참조

const person = {
    name: 'Alice',
    greet: function() {
        setTimeout(function() {
            console.log(`Hello, ${this.name}`);  // undefined
        }, 1000);
    }
};
person.greet();

위 예제에서 일반 함수this전역 객체를 참조하므로, **this.name**은 undefined입니다.

4.2. 해결: 화살표 함수로 this를 상속

const person = {
    name: 'Alice',
    greet: function() {
        setTimeout(() => {
            console.log(`Hello, ${this.name}`);  // Hello, Alice
        }, 1000);
    }
};
person.greet();

화살표 함수는 상위 스코프의 this를 상속받으므로, **this.name**이 올바르게 **Alice**를 참조하게 됩니다.


5. for...infor...of의 혼동

  • *for...in*은 객체의 **열쇠(key)**를 순회하고, **for...of*는 **값(value)**를 순회합니다. 배열에서는 **for...in*을 사용하는 것이 바람직하지 않으며, 대신 **for...of*를 사용해야 합니다.

5.1. 문제: 배열에서 for...in 사용

const arr = [10, 20, 30];
for (let index in arr) {
    console.log(index);  // 0, 1, 2 (값이 아니라 인덱스가 출력됨)
}

  • *for...in*은 배열에서 인덱스를 순회하므로, 값이 아닌 **키(key)**가 출력됩니다.

5.2. 해결: 배열에서 for...of 사용

const arr = [10, 20, 30];
for (let value of arr) {
    console.log(value);  // 10, 20, 30 (값이 출력됨)
}

  • *for...of*는 배열의 을 순회하므로, 실제 값을 다룰 수 있습니다.

6. parseInt()를 사용할 때 기수(Radix)를 지정하지 않음

parseInt() 함수는 문자열을 숫자로 변환할 때 **기수(radix)**를 지정하지 않으면, 특정 상황에서 잘못된 변환이 발생할 수 있습니다. 항상 기수를 명시하는 것이 좋습니다.

6.1. 문제: 기수 미지정

const num = parseInt('08');  // 출력: 0 (8진수로 처리됨)

기수를 명시하지 않으면, 08 같은 숫자는 8진수로 해석될 수 있습니다.

6.2. 해결: 기수 지정

const num = parseInt('08', 10);  // 출력: 8 (10진수로 처리됨)

기수 10을 명시하여 10진수로 처리되도록 합니다.


7. 클로저로 인해 발생하는 메모리 누수

  • *클로저(closure)**는 함수가 외부 함수의 변수에 접근할 수 있는 구조입니다. 하지만 클로저가 불필요하게 많은 메모리를 참조하면 메모리 누수가 발생할 수 있습니다. 클로저를 사용한 후에는 명시적으로 해제해야 합니다.

7.1. 문제: 클로저로 인한 메모리 누수

function createCounter() {
    let count = 0;
    return function increment() {
        count++;
        console.log(count);
    };
}
const counter = createCounter();
counter();  // 1
counter();  // 2
// 하지만 count는 여전히 메모리에 남아 있음

클로저는 **count**를 참조하기 때문에, 필요하지 않다면 이를 해제해야 합니다.

7.2. 해결: 필요할 때 클로저 해제

function createCounter() {
    let count = 0;
    return function increment() {
        count++;
        console.log(count);
    };
}
const counter = createCounter();
counter();  // 1
counter = null;  // 클로저 해제


8. 정수 나눗셈에서 예상치 못한 결과 발생

JavaScript는 정수 나눗셈실수 나눗셈을 구분하지 않습니다. 소수점 이하의 값이 자동으로 처리되

기 때문에, 정수만 필요한 경우에는 주의해야 합니다.

8.1. 문제: 정수 나눗셈에서 소수점 처리

let result = 10 / 3;
console.log(result);  // 출력: 3.3333333333333335

정수 나눗셈에서도 소수점 이하 값이 출력됩니다.

8.2. 해결: Math.floor() 사용

let result = Math.floor(10 / 3);
console.log(result);  // 출력: 3

  • *Math.floor()*를 사용하여 소수점 이하 값을 제거하고 정수만 취합니다.

9. NaN을 잘못 처리

  • *NaN(Not-a-Number)**는 JavaScript에서 특별한 숫자 값입니다. 그러나 NaN과의 비교는 항상 **false*를 반환하기 때문에, isNaN() 함수로 처리하는 것이 안전합니다.

9.1. 문제: NaN 비교

console.log(NaN === NaN);  // false

  • *NaN*은 자기 자신과도 같지 않음을 나타냅니다.

9.2. 해결: isNaN() 사용

console.log(isNaN(NaN));  // true

isNaN() 함수를 사용하면 NaN 값을 안전하게 처리할 수 있습니다.


10. 함수 호출 시 this를 명시적으로 바인딩하지 않음

JavaScript에서 함수 호출 시 **this**가 의도하지 않게 바인딩될 수 있습니다. 특히, 이벤트 핸들러나 콜백 함수에서는 bind(), call(), 또는 **apply()**로 명시적으로 this를 설정해야 합니다.

10.1. 문제: this 바인딩 누락

const obj = {
    name: 'Alice',
    greet: function() {
        console.log(this.name);
    }
};
setTimeout(obj.greet, 1000);  // undefined

위 코드에서는 **this**가 전역 객체를 참조하므로, **undefined**가 출력됩니다.

10.2. 해결: bind()로 바인딩

setTimeout(obj.greet.bind(obj), 1000);  // Alice

  • *bind()*를 사용하여 **this*를 명시적으로 바인딩합니다.

요약

  • *letconst*를 사용하여 변수 스코프 문제를 해결하고, **엄격한 비교(===)**로 예상치 못한 타입 변환을 방지합니다.
  • 비동기 함수에서 **return*을 명시하여 Promise의 결과를 처리하고, 이벤트 핸들러콜백에서 this를 올바르게 바인딩합니다.
  • *for...of*와 **for...in*의 차이를 이해하고 적절하게 사용하며, **기수(radix)**를 지정하여 숫자를 정확하게 파싱합니다.
  • 클로저를 사용한 후에는 불필요한 참조를 해제하고, isNaN() 함수를 통해 NaN 값을 안전하게 처리합니다.

이러한 JavaScript 실수와 해결 방법을 이해하고 적용하면 코드 안정성유지보수성이 크게 향상됩니다.


copyright ⓒ 스타트코딩 all rights reserved.
이메일 : startcodingim@gamil.com