반응형
프론트엔드 개발을 하다 보면 컴포넌트가 점점 커지고, 재사용도 어려워지고, 유지보수는 지옥이 되어가는 경험... 한 번쯤 해보셨죠?
이럴 때 도움이 되는 게 바로 SOLID 원칙입니다.
SOLID는 객체 지향 설계 원칙이지만, 함수형과 컴포넌트 기반 개발에도 충분히 적용할 수 있어요.
오늘은 React 코드 예시를 통해 SOLID를 쉽게 풀어보겠습니다!
✅ 1. SRP - 단일 책임 원칙 (Single Responsibility Principle)
한 컴포넌트는 하나의 일만 해야 한다.
❌ 잘못된 예시
const UserProfile = ({ user }) => {
const [editing, setEditing] = useState(false);
const saveUser = async (userData) => {
// 서버 요청
};
return (
<div>
<img src={user.avatar} />
{editing ? (
<input value={user.name} />
) : (
<p>{user.name}</p>
)}
<button onClick={() => setEditing(!editing)}>Edit</button>
<button onClick={() => saveUser(user)}>Save</button>
</div>
);
};
✅ 개선된 예시
const UserAvatar = ({ avatar }) => <img src={avatar} alt="avatar" />;
const UserName = ({ name, editing, onChange }) =>
editing ? <input value={name} onChange={onChange} /> : <p>{name}</p>;
const UserActions = ({ onEdit, onSave }) => (
<>
<button onClick={onEdit}>Edit</button>
<button onClick={onSave}>Save</button>
</>
);
const UserProfile = ({ user }) => {
const [editing, setEditing] = useState(false);
const handleSave = () => {
// 서버 요청
};
return (
<div>
<UserAvatar avatar={user.avatar} />
<UserName name={user.name} editing={editing} onChange={() => {}} />
<UserActions
onEdit={() => setEditing(!editing)}
onSave={handleSave}
/>
</div>
);
};
✅ 2. OCP - 개방/폐쇄 원칙 (Open/Closed Principle)
확장에는 열려 있고, 수정에는 닫혀 있어야 한다.
예시: 버튼 스타일 확장 가능한 설계
const Button = ({ variant = 'default', children, ...props }) => {
const className = {
default: 'bg-gray-200 text-black',
primary: 'bg-blue-500 text-white',
danger: 'bg-red-500 text-white',
}[variant];
return (
<button className={className} {...props}>
{children}
</button>
);
};
새로운 스타일이 필요할 때 기존 컴포넌트를 수정하지 않고 variant만 확장하면 됩니다.
✅ 3. LSP - 리스코프 치환 원칙 (Liskov Substitution Principle)
상위 타입을 사용하는 곳에 하위 타입을 넣어도 잘 동작해야 한다.
예시
const Notification = ({ message }) => <div>{message}</div>;
const SuccessNotification = (props) => (
<Notification {...props} message={`✅ ${props.message}`} />
);
const ErrorNotification = (props) => (
<Notification {...props} message={`❌ ${props.message}`} />
);
SuccessNotification, ErrorNotification 모두 Notification 자리에 들어가도 문제 없이 작동합니다.
✅ 4. ISP - 인터페이스 분리 원칙 (Interface Segregation Principle)
필요한 기능만 가지는 작은 props 설계를 하자.
❌ 너무 많은 props를 가진 컴포넌트
const Form = ({ onSubmit, onReset, onCancel, onValidate, onSave }) => {
// ...
};
✅ 기능별로 나누기
const SubmitButton = ({ onSubmit }) => <button onClick={onSubmit}>Submit</button>;
const CancelButton = ({ onCancel }) => <button onClick={onCancel}>Cancel</button>;
필요한 기능만 사용하는 컴포넌트를 만들면 더 유연하고 재사용성도 높아져요.
✅ 5. DIP - 의존성 역전 원칙 (Dependency Inversion Principle)
상위 모듈이 하위 모듈에 직접 의존하지 말고, 추상화에 의존하자.
예시: API 요청을 추상화
const useUserService = (apiClient) => {
const getUser = async (id) => await apiClient.get(`/users/${id}`);
return { getUser };
};
const apiClient = {
get: (url) => fetch(url).then(res => res.json())
};
const UserContainer = () => {
const { getUser } = useUserService(apiClient);
useEffect(() => {
getUser(1).then(console.log);
}, []);
return <div>Loading user...</div>;
};
apiClient를 추상화하면 테스트나 교체가 매우 쉬워집니다!
✨ 마무리
React 컴포넌트 기반 개발에서도 SOLID 원칙은 강력한 가이드가 되어줍니다.
📌 정리하자면:
- SRP: 하나의 책임만!
- OCP: 확장 가능하게!
- LSP: 대체 가능하게!
- ISP: 필요한 것만!
- DIP: 추상화에 의존!
반응형
'Programming > React.js' 카테고리의 다른 글
Next.js 15 업데이트 및 변경점 (0) | 2024.12.08 |
---|---|
React.js 19 정식 버전 출시, 업데이트 내용 (1) | 2024.12.08 |
Next.js Static Rendering(정적 렌더링)과 Dynamic Rendering(동적 렌더링) (0) | 2024.12.08 |
React Hook Memoization(메모이제이션) useCallback/useMemo (0) | 2022.12.30 |