티스토리 뷰

JavaScript

JavaScript 흔한 실수 모음

hjkang 2026. 2. 24. 13:42

JavaScript에서 흔하게 하는 실수를 정리해보자!

 

 

async/await 에러 처리 누락

에러 처리가 누락되면 에러가 조용히 사라지기 때문에 꼭 처리

// 에러가 나도 아무 일도 안 일어남
async function fetchUser() {
  const res = await fetch("/api/user");
  return res.json();
}

// try/catch로 명확하게
async function fetchUser() {
  try {
    const res = await fetch("/api/user");
    if (!res.ok) throw new Error(`HTTP error: ${res.status}`);
    return res.json();
  } catch (error) {
    console.error("유저 조회 실패:", error);
    throw error;
  }
}

 

참고로 Promise.all의 경우 하나만 실패해도 전체가 터지기 때문에 Promise.allSettled를 사용하자

// 하나 실패하면 전체 실패
const [user, post] = await Promise.all([fetchUser(), fetchPost()]);

// 각각의 성공/실패를 개별로 처리
const results = await Promise.allSettled([fetchUser(), fetchPost()]);
results.forEach(result => {
  if (result.status === "fulfilled") console.log(result.value);
  if (result.status === "rejected") console.error(result.reason);
});

 

 

객체/배열 직접 변경

원본을 직접 변경하면 어디서 변경되었는지 추적이 어렵고, 반응성이 깨지는 원인이 된다.

// 원본 배열을 직접 변경
const addItem = (items, newItem) => {
  items.push(newItem);
  return items;
};
const updateUser = (user, name) => {
  user.name = name;
  return user;
};

// 새 객체를 반환
const addItem = (items, newItem) => [...items, newItem];
const updateUser = (user, name) => ({ ...user, name });

 

 

== 사용

==는 타입을 자동으로 변환하기 때문에 예상하지 못한 결과를 만들어 낸다.

// == 사용 (암묵적 타입 변경)
0 == false        // true
"" == false       // true
null == undefined // true
0 == "0"          // true

// === 사용
0 === false        // false
null === undefined // false

 

다만 null 체크할 때는 ==를 쓰는 경우가 많다.

// null과 undefined를 동시에 체크하는 경우
if (value == null) { ... }

 

 

클로저에서 루프 변수 참조

var로 선언한 루프 변수를 클로저에서 참조하면 의도와 다르게 동작한다.

// 모두 3을 출력
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}

// let을 쓰면 블록 스코프로 의도대로 0, 1, 2 출력
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 0, 1, 2
}

 

 

부동소수점 연산 그대로 사용

JavaScript의 숫자는 IEEE 754 부동소수점이라 소수 계산이 정확하지 않아 금액 계산에서 틀어지는 경우가 많다.

// 예상과 다른 결과
0.1 + 0.2 === 0.3 // false
0.1 + 0.2         // 0.30000000000000004

// 정수로 변환해서 계산
const add = (a, b) => Math.round((a + b) * 100) / 100;
add(0.1, 0.2); // 0.3

 

 

this 바인딩 실수

콜백 함수 안에서 this를 참조하면 this가 window 또는 undefined이 되기 때문에 화살표 함수를 사용하는게 좋다.

// 콜백 안의 this는 컴포넌트를 가리키지 않음
export default {
  data() {
    return { count: 0 };
  },
  methods: {
    startTimer() {
      setTimeout(function () {
        this.count++; // this가 undefined 또는 window
      }, 1000);
    }
  }
}

// 화살표 함수를 쓰면 this가 유지됨
startTimer() {
  setTimeout(() => {
    this.count++; // 컴포넌트의 this
  }, 1000);
}

 

 

얕은 복사 vs 깊은 복사

{...obj} 로 복사해도 중첩 객체는 여전히 같은 참조를 가리키기 때문에 어디서 변경되었는지 추적이 어렵다.

const user = {
  name: "현지",
  address: { city: "서울" }
};

// 얕은 복사
const copy = { ...user };
copy.address.city = "부산";
console.log(user.address.city); // "부산" — 원본도 바뀜

// 깊은 복사
const deepCopy = JSON.parse(JSON.stringify(user));
deepCopy.address.city = "부산";
console.log(user.address.city); // "서울" — 원본 유지

 

복잡한 객체의 경우 최신 브라우저에서는 structuredClone() 을 사용하면 된다.

const deepCopy = structuredClone(user);

 

 

setTimeout / setInterval 정리 누락

컴포넌트가 사라져도 타이머가 계속 돌아가면 메모리 누수가 생기기 때문에 정리해야 한다.

import { onMounted, onUnmounted } from 'vue';

onMounted(() => {
  const timer = setInterval(fetchData, 3000);
  onUnmounted(() => clearInterval(timer));
});
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함