π§ββοΈ λ§λ²μ λ¬Έμμ΄/μ«μ λ¨μ©: μμΈ‘ λΆκ°λ₯ν μ½λμ μ μ§λ³΄μμ λ«
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;
*/