매 코드 리뷰에서 보는 8가지 JavaScript 실수 (그리고 해결 방법)

발행: (2026년 3월 29일 AM 06:51 GMT+9)
5 분 소요
원문: Dev.to

Source: Dev.to

수백 개의 PR을 검토한 결과, 계속해서 나타나는 패턴들이 있습니다. 한 번에 모두 해결해 봅시다.

1. == 대신 === 사용하기

// 🚫 Wrong – type coercion is unpredictable
if (user.age == "18") ...
if (count == null) ...
if (0 == false) ...   // true!
if ("" == false) ...  // true!

// ✅ Correct – strict equality, no surprises
if (user.age === 18) ...
if (count === null || count === undefined) ...
// Or better:
if (count == null) ...  // Only acceptable for null/undefined check

예외: == nullnull undefined 를 동시에 확인할 때는 괜찮습니다.


2. 함수 매개변수 변형

// 🚫 Wrong – mutates the caller's object
function addTimestamp(user) {
  user.createdAt = new Date();
  return user;
}

const admin = { name: 'Alice', role: 'admin' };
const timestamped = addTimestamp(admin);
console.log(admin.createdAt); // Oops! admin was mutated too
// ✅ Correct – return a new object
function addTimestamp(user) {
  return { ...user, createdAt: new Date() };
}

3. 비동기 오류 처리 안 함

// 🚫 Wrong – crashes the whole app on network error
async function fetchUser(id) {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}
// ✅ Correct – handle errors where they happen
async function fetchUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return { data: await response.json(), error: null };
  } catch (err) {
    return { data: null, error: err.message };
  }
}

const { data, error } = await fetchUser(42);
if (error) { /* handle */ }

4. 루프 안에서 함수 만들기

// 🚫 잘못된 예 – N개의 클로저를 만들고 모두 같은 `i`를 참조함
const handlers = [];
for (var i = 0; i < 5; i++) {
  handlers.push(() => console.log(i));
}
handlers.forEach(h => h()); // 5 5 5 5 5 — 원하는 결과가 아님!
// ✅ 올바른 옵션 1 – `let` 사용 (블록 스코프)
const handlersLet = [];
for (let i = 0; i < 5; i++) {
  handlersLet.push(() => console.log(i));
}
handlersLet.forEach(h => h()); // 0 1 2 3 4 ✓
// ✅ 올바른 옵션 2 – `map`만 사용
const handlersMap = [0, 1, 2, 3, 4].map(i => () => console.log(i));
handlersMap.forEach(h => h()); // 0 1 2 3 4

5. 부수 효과 정리를 잊음

// 🚫 Wrong – memory leak and stale state
function SearchComponent() {
  const [results, setResults] = useState([]);

  useEffect(() => {
    // If component unmounts before fetch completes, setState on unmounted component
    fetchSearch(query).then(data => setResults(data));
  }, [query]);
}
// ✅ Correct – clean up with `AbortController`
function SearchComponent() {
  const [results, setResults] = useState([]);

  useEffect(() => {
    const controller = new AbortController();

    fetchSearch(query, { signal: controller.signal })
      .then(data => setResults(data))
      .catch(err => {
        if (err.name !== 'AbortError') console.error(err);
      });

    return () => controller.abort(); // Cleanup!
  }, [query]);
}

6. 조기 반환을 사용하지 않음

// 🚫 Wrong – deeply nested, hard to read
function processOrder(order) {
  if (order) {
    if (order.items.length > 0) {
      if (order.user.isVerified) {
        if (order.total > 0) {
          return submitOrder(order);
        }
      }
    }
  }
  return null;
}
// ✅ Correct – flat, readable, each failure is explicit
function processOrder(order) {
  if (!order) return null;
  if (order.items.length === 0) return null;
  if (!order.user.isVerified) return null;
  if (order.total <= 0) return null;
  return submitOrder(order);
}

7. 적절한 배열 메서드 사용

// 🚫 Also wrong – mixing concerns
const results = [];
users.forEach(user => {
  if (user.active) {
    results.push({ ...user, displayName: user.name.trim() });
  }
});
// ✅ Correct – use the right method
const doubled = [1, 2, 3].map(n => n * 2);

const results = users
  .filter(user => user.active)
  .map(user => ({ ...user, displayName: user.name.trim() }));

8. 환경 검사 사용을 제대로 하지 않음

// 🚫 Wrong – exposes sensitive info, breaks across environments
const API_URL = "https://api.production.com"; // Hardcoded!
const DEBUG = true; // Always enabled!

// 🚫 Also wrong – string comparison with typo risk
if (process.env.NODE_ENV == "producton") { /* ... */ } // Typo!
// ✅ Correct – centralized config (config.js)
export const config = {
  apiUrl: process.env.REACT_APP_API_URL || 'http://localhost:3000',
  isDev: process.env.NODE_ENV === 'development',
  isProd: process.env.NODE_ENV === 'production',
};

// Use everywhere
if (config.isDev) console.log('Debug info:', data);

빠른 참고

안티패턴해결책
== 느슨한 동등성=== 엄격한 동등성
파라미터 변형Spread: { ...obj }
처리되지 않은 비동기 오류try/catch + error state
루프에서 varlet or map
useEffect에서 정리 없음Return a cleanup function
깊은 중첩Early returns
결과가 필요할 때 forEachmap / filter / reduce
하드코딩된 설정Environment variables / config file

자주 보는 패턴을 놓쳤나요? 댓글에 남겨 주세요!

0 조회
Back to Blog

관련 글

더 보기 »

'Vibe Coding' 수치의 벽

기사 URL: https://crackr.dev/vibe-coding-failures 댓글 URL: https://news.ycombinator.com/item?id=47566491 점수: 9 댓글: 3