Today's AntipatternAll Posts
ν…Œλ§ˆ
GitHubToday's AntipatternAll Posts

μ•ˆν‹°νŒ¨ν„΄μ„ 톡해 더 λ‚˜μ€ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” 방법을 λ°°μ›Œλ³΄μ„Έμš”. κ°œλ°œμžλ“€μ΄ μ‹€μˆ˜ν•˜λŠ” νŒ¨ν„΄λ“€μ„ λΆ„μ„ν•˜κ³  κ°œμ„ λ°©μ•ˆμ„ μ œμ‹œν•©λ‹ˆλ‹€.

μ—°κ²°ν•˜κΈ°

Β© 2025 Smelly.dev All rights reserved.

July 20, 2025

πŸ§™β€β™‚οΈ λ§ˆλ²•μ˜ λ¬Έμžμ—΄/숫자 λ‚¨μš©: 예츑 λΆˆκ°€λŠ₯ν•œ μ½”λ“œμ™€ μœ μ§€λ³΄μˆ˜μ˜ 덫

넀이밍
μ•„ν‚€ν…μ²˜
TypeScript
Lint/Formatter

Summary

μ½”λ“œ λ‚΄ 의미 μ—†λŠ” λ¬Έμžμ—΄/숫자(λ§ˆλ²•μ˜ λ¦¬ν„°λŸ΄)λŠ” 가독성, μœ μ§€λ³΄μˆ˜μ„±μ„ μ €ν•΄ν•˜κ³  버그λ₯Ό μœ λ°œν•©λ‹ˆλ‹€. μƒμˆ˜λ₯Ό μ •μ˜ν•˜κ³ , μ—΄κ±°ν˜• λ˜λŠ” 객체둜 κ΄€λ¦¬ν•˜μ—¬ μ½”λ“œμ˜ μ˜λ„λ₯Ό λͺ…ν™•νžˆ ν•˜κ³  μž¬μ‚¬μš©μ„±μ„ λ†’μ—¬μ•Ό ν•©λ‹ˆλ‹€.

Why Wrong?

μ½”λ“œ 내에 μ˜λ―Έκ°€ λΆˆλΆ„λͺ…ν•œ λ¬Έμžμ—΄("active", "pending", "admin")μ΄λ‚˜ 숫자(예: 1, 0, 999 같은 μƒνƒœ μ½”λ“œ, νŠΉμ • 간격 κ°’)λ₯Ό 직접 μ‚¬μš©ν•˜λŠ” 것은 'λ§ˆλ²•μ˜ λ¦¬ν„°λŸ΄(Magic Literals)'이라고 λΆˆλ¦¬λŠ” λŒ€ν‘œμ μΈ μ•ˆν‹°νŒ¨ν„΄μž…λ‹ˆλ‹€. μ΄λŸ¬ν•œ λ¦¬ν„°λŸ΄λ“€μ€ λ‹€μŒκ³Ό 같은 문제λ₯Ό μ•ΌκΈ°ν•©λ‹ˆλ‹€:

  • 가독성 μ €ν•˜: κ°œλ°œμžκ°€ μ½”λ“œμ˜ μ˜λ„λ₯Ό νŒŒμ•…ν•˜κΈ° μ–΄λ ΅κ²Œ λ§Œλ“­λ‹ˆλ‹€. if (status === 1)μ—μ„œ 1이 μ •ν™•νžˆ 무엇을 μ˜λ―Έν•˜λŠ”μ§€ λ¬Έλ§₯ μ—†μ΄λŠ” μ•ŒκΈ° μ–΄λ ΅μŠ΅λ‹ˆλ‹€.
  • μœ μ§€λ³΄μˆ˜μ„± μ•…ν™”: νŠΉμ • 값이 λ³€κ²½λ˜μ–΄μ•Ό ν•  λ•Œ, μ½”λ“œλ² μ΄μŠ€ 전체λ₯Ό κ²€μƒ‰ν•˜μ—¬ λͺ¨λ“  μΈμŠ€ν„΄μŠ€λ₯Ό μˆ˜λ™μœΌλ‘œ λ³€κ²½ν•΄μ•Ό ν•©λ‹ˆλ‹€. μ΄λŠ” μ‹œκ°„μ΄ 많이 μ†Œμš”λ˜λ©°, λˆ„λ½μ΄λ‚˜ μ˜€νƒ€λ‘œ μΈν•œ 버그λ₯Ό μœ λ°œν•˜κΈ° μ‰½μŠ΅λ‹ˆλ‹€. (DRY 원칙 μœ„λ°°)
  • 잠재적 버그 λ°œμƒ: μ˜€νƒ€("acive" λŒ€μ‹  "active")κ°€ λ°œμƒν•˜λ”λΌλ„ λŸ°νƒ€μž„μ—λ§Œ λ¬Έμ œκ°€ λ“œλŸ¬λ‚˜κ±°λ‚˜, μ•„μ˜ˆ λ°œκ²¬λ˜μ§€ μ•Šμ•„ 예츑 λΆˆκ°€λŠ₯ν•œ λ™μž‘μ„ μœ λ°œν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ¬Έμžμ—΄ λΉ„κ΅λŠ” 특히 μ΄λŸ¬ν•œ λ¬Έμ œμ— μ·¨μ•½ν•©λ‹ˆλ‹€.
  • λ¦¬νŒ©ν† λ§μ˜ 어렀움: κ΄€λ ¨ 둜직이 μ—¬λŸ¬ 곳에 흩어져 있으면 λ¦¬νŒ©ν† λ§ μ‹œ λ³€κ²½ λ²”μœ„ νŒŒμ•…μ΄ μ–΄λ ΅κ³  였λ₯˜ λ°œμƒ ν™•λ₯ μ΄ λ†’μ•„μ§‘λ‹ˆλ‹€.
  • νƒ€μž… μ•ˆμ „μ„± μ €ν•΄: 특히 TypeScript와 같은 정적 νƒ€μž… μ–Έμ–΄μ—μ„œ λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄μ„ μ‚¬μš©ν•˜λŠ” 것은 컴파일 μ‹œμ μ— νƒ€μž…μ„ κ°•μ œν•˜κΈ° μ–΄λ ΅κ²Œ λ§Œλ“€μ–΄ νƒ€μž… μ•ˆμ •μ„±μ„ μ•½ν™”μ‹œν‚΅λ‹ˆλ‹€.

How to Fix?

λͺ¨λ“  'λ§ˆλ²•μ˜ λ¦¬ν„°λŸ΄'을 의미 μžˆλŠ” μ΄λ¦„μ˜ μƒμˆ˜λ‘œ μ •μ˜ν•˜κ³  ν•œ κ³³μ—μ„œ 관리해야 ν•©λ‹ˆλ‹€. μ΄λŠ” μ½”λ“œμ˜ 가독성을 높이고, ν•œ κ³³μ—μ„œ 값을 관리할 수 있게 ν•˜μ—¬ μœ μ§€λ³΄μˆ˜λ₯Ό μš©μ΄ν•˜κ²Œ ν•©λ‹ˆλ‹€.

  • μƒμˆ˜(Constants) μ‚¬μš©: μž¬μ‚¬μš©λ˜κ±°λ‚˜ μ˜λ―Έκ°€ μ€‘μš”ν•œ λͺ¨λ“  λ¦¬ν„°λŸ΄ 값을 λͺ…λͺ…λœ μƒμˆ˜λ‘œ μΆ”μΆœν•˜μ—¬ μ‚¬μš©ν•©λ‹ˆλ‹€. μ΄λŠ” μ½”λ“œμ˜ μ˜λ„λ₯Ό λͺ…ν™•νžˆ ν•©λ‹ˆλ‹€.
  • 객체(Object) λ˜λŠ” μ—΄κ±°ν˜•(Enum) μ‚¬μš©: κ΄€λ ¨λœ μƒμˆ˜λ“€μ„ κ·Έλ£Ήν™”ν•˜μ—¬ κ°μ²΄λ‚˜ μ—΄κ±°ν˜•μœΌλ‘œ κ΄€λ¦¬ν•©λ‹ˆλ‹€. 특히 TypeScriptμ—μ„œλŠ” enumμ΄λ‚˜ union type을 ν™œμš©ν•˜μ—¬ νŠΉμ • κ°’λ“€μ˜ 집합을 λͺ…μ‹œμ μœΌλ‘œ ν‘œν˜„ν•˜κ³  νƒ€μž… μ•ˆμ •μ„±κ³Ό μžλ™ μ™„μ„± κΈ°λŠ₯을 μ œκ³΅ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ§€ν•‘ 객체(Mapping Object) μ‚¬μš©: μ„œλ²„ 응닡 μ½”λ“œλ‚˜ νŠΉμ • μƒνƒœ 값에 따라 λ‹€λ₯Έ λ™μž‘μ„ μˆ˜ν–‰ν•΄μ•Ό ν•  경우, λ§€ν•‘ 객체λ₯Ό μ‚¬μš©ν•˜μ—¬ 가독성을 높이고 쑰건문의 λ³΅μž‘μ„±μ„ 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ¦°ν„°(Linter) κ·œμΉ™ ν™œμš©: ESLintλ‚˜ TSLint와 같은 λ¦°ν„° λ„κ΅¬μ—μ„œ 'no-magic-numbers'λ‚˜ μ»€μŠ€ν…€ κ·œμΉ™μ„ μ„€μ •ν•˜μ—¬ μ½”λ“œμ— λ§ˆλ²•μ˜ λ¦¬ν„°λŸ΄μ΄ μ‚¬μš©λ˜μ§€ μ•Šλ„λ‘ κ°•μ œν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ 방법을 톡해 μ½”λ“œμ˜ μ˜λ„λ₯Ό λͺ…ν™•νžˆ ν•˜κ³ , 잠재적인 버그λ₯Ό 쀄이며, μž₯기적인 κ΄€μ μ—μ„œ μ½”λ“œμ˜ 응집도와 μœ μ§€λ³΄μˆ˜μ„±μ„ 크게 ν–₯μƒμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

Before Code (Bad)

// components/UserProfile.jsx
import React from 'react';

function UserProfile({ status, userType }) {
  let statusMessage = '';
  if (status === 1) { // 1이 뭘까? 'active'λ₯Ό μ˜λ―Έν•˜λŠ” 건가?
    statusMessage = 'μ‚¬μš© 쀑';
  } else if (status === 0) { // 0은? 'inactive'?
    statusMessage = 'λΉ„ν™œμ„±';
  } else {
    statusMessage = 'μ•Œ 수 μ—†μŒ';
  }

  const getProfileStyle = () => {
    // ν•˜λ“œμ½”λ”©λœ κ°’λ“€
    if (userType === 'admin') {
      return { border: '2px solid red', padding: '10px' };
    } else if (userType === 'guest') {
      return { border: '1px solid gray', opacity: 0.7 }; // 0.7의 μ˜λ―ΈλŠ”?
    }
    return {};
  };

  return (
    <div className={`user-profile ${userType}`}>
      <h3>μ‚¬μš©μž ν”„λ‘œν•„</h3>
      <p>μƒνƒœ: {statusMessage}</p>
      <div style={getProfileStyle()}>
        <p>νƒ€μž…: {userType}</p>
      </div>
      {/* λ‹€λ₯Έ ν”„λ‘œν•„ 정보듀 */}
    </div>
  );
}

export default UserProfile;

After Code (Good)

// constants/user.js
export const USER_STATUS = {
  ACTIVE: 1,
  INACTIVE: 0,
  UNKNOWN: -1, // λͺ…ν™•ν•œ κΈ°λ³Έκ°’ μΆ”κ°€
};

export const USER_TYPE = {
  ADMIN: 'admin',
  GUEST: 'guest',
  REGULAR: 'regular',
};

// components/UserProfile.jsx
import React from 'react';
import { USER_STATUS, USER_TYPE } from '../constants/user'; // μƒμˆ˜ μž„ν¬νŠΈ

function UserProfile({ status, userType }) {
  let statusMessage = '';
  switch (status) {
    case USER_STATUS.ACTIVE:
      statusMessage = 'μ‚¬μš© 쀑';
      break;
    case USER_STATUS.INACTIVE:
      statusMessage = 'λΉ„ν™œμ„±';
      break;
    default:
      statusMessage = 'μ•Œ 수 μ—†μŒ';
  }

  const getProfileStyle = () => {
    switch (userType) {
      case USER_TYPE.ADMIN:
        return { border: '2px solid red', padding: '10px' };
      case USER_TYPE.GUEST:
        return { border: '1px solid gray', opacity: 0.7 }; // 0.7은 μ—¬μ „νžˆ λ§ˆλ²•μ˜ 숫자일 수 μžˆμœΌλ―€λ‘œ ν•„μš”μ‹œ μƒμˆ˜λ‘œ μΆ”μΆœ
      default:
        return {};
    }
  };

  return (
    <div className={`user-profile ${userType}`}>
      <h3>μ‚¬μš©μž ν”„λ‘œν•„</h3>
      <p>μƒνƒœ: {statusMessage}</p>
      <div style={getProfileStyle()}>
        <p>νƒ€μž…: {userType}</p>
      </div>
      {/* λ‹€λ₯Έ ν”„λ‘œν•„ 정보듀 */}
    </div>
  );
}

export default UserProfile;

// TypeScript ν™˜κ²½μ΄λΌλ©΄ λ‹€μŒκ³Ό 같이 νƒ€μž… μ•ˆμ •μ„±μ„ 더 높일 수 μžˆμŠ΅λ‹ˆλ‹€.
/*
// types/user.ts
export enum UserStatus {
  ACTIVE = 1,
  INACTIVE = 0,
  UNKNOWN = -1,
}

export type UserType = 'admin' | 'guest' | 'regular';

// components/UserProfile.tsx
import React from 'react';
import { UserStatus, UserType } from '../types/user';

interface UserProfileProps {
  status: UserStatus;
  userType: UserType;
}

function UserProfile({ status, userType }: UserProfileProps) {
  let statusMessage: string;
  switch (status) {
    case UserStatus.ACTIVE:
      statusMessage = 'μ‚¬μš© 쀑';
      break;
    case UserStatus.INACTIVE:
      statusMessage = 'λΉ„ν™œμ„±';
      break;
    default:
      statusMessage = 'μ•Œ 수 μ—†μŒ';
  }

  const getProfileStyle = () => {
    switch (userType) {
      case 'admin': // TypeScript의 λ¦¬ν„°λŸ΄ νƒ€μž… 좔둠을 ν™œμš©
        return { border: '2px solid red', padding: '10px' };
      case 'guest':
        return { border: '1px solid gray', opacity: 0.7 };
      default:
        return {};
    }
  };

  return (
    <div className={`user-profile ${userType}`}>
      <h3>μ‚¬μš©μž ν”„λ‘œν•„</h3>
      <p>μƒνƒœ: {statusMessage}</p>
      <div style={getProfileStyle()}>
        <p>νƒ€μž…: {userType}</p>
      </div>
    </div>
  );
}

export default UserProfile;
*/

You Might Also Like

λ§ˆλ²•μ˜ 숫자λ₯Ό μƒμˆ˜λ‘œ μ •μ˜ν•˜μ—¬ μ½”λ“œλ₯Ό 더 읽기 쉽고 μœ μ§€λ³΄μˆ˜ν•˜κΈ° μ’‹κ²Œ λ§Œλ“œλŠ” 것에 λŒ€ν•œ λͺ¨λ²” 사둀λ₯Ό μ„€λͺ…ν•©λ‹ˆλ‹€.
https://refactoring.com/smells/magic-number.html
TypeScriptμ—μ„œ `enum`을 μ‚¬μš©ν•˜μ—¬ κ΄€λ ¨λœ μƒμˆ˜ 집합을 μ •μ˜ν•˜κ³  νƒ€μž… μ•ˆμ •μ„±μ„ ν™•λ³΄ν•˜λŠ” 방법을 μ„€λͺ…ν•©λ‹ˆλ‹€.
https://www.typescriptlang.org/docs/handbook/enums.html
ESLint `no-magic-numbers` κ·œμΉ™μ„ 톡해 μ½”λ“œ λ‚΄ ν•˜λ“œμ½”λ”©λœ 숫자 λ¦¬ν„°λŸ΄ μ‚¬μš©μ„ λ°©μ§€ν•˜μ—¬ 가독성과 μœ μ§€λ³΄μˆ˜μ„±μ„ λ†’μ΄λŠ” 방법을 μ„€λͺ…ν•©λ‹ˆλ‹€.
https://eslint.org/docs/latest/rules/no-magic-numbers