1. SPR 단일 책임 원칙 (Single Responsibility Principle)
설명:
- 단일 책임 원칙은 클래스나 컴포넌트가 한 가지 책임만 가져야 한다는 원칙입니다.
- 변경 요청을 하는 사람(사용자나 이해관계자)에 따라 책임을 분리합니다.
- 비즈니스 관점에서 책임을 나누어 한 가지 책임에 대한 변경사항이 있을 때만 코드가 수정되도록 합니다.
예시: 리액트 컴포넌트에서 사용자 프로필을 표시하는 부분을 예로 들겠습니다.
import React from 'react';
import UserInfo from './UserInfo';
import UserAvatar from './UserAvatar';
const UserProfile = ({ user }) => (
<div>
<UserAvatar avatarUrl={user.avatarUrl} />
<UserInfo name={user.name} email={user.email} />
</div>
);
export default UserProfile;
// UserInfo.js
const UserInfo = ({ name, email }) => (
<div>
<h2>{name}</h2>
<p>{email}</p>
</div>
);
export default UserInfo;
// UserAvatar.js
const UserAvatar = ({ avatarUrl }) => (
<img src={avatarUrl} alt="User Avatar" />
);
export default UserAvatar;
- UserProfile 컴포넌트는 사용자 정보와 아바타를 보여주는 책임만 가집니다.
- 변경 요청이 있을 경우, 해당 책임에 맞는 컴포넌트만 수정하면 됩니다.
2. OCP 개방 폐쇄 원칙 (Open-Closed Principle)
설명:
- 소프트웨어 구성 요소는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 합니다.
- 기존 코드를 수정하지 않고 새로운 기능을 추가하는 방법을 지향합니다.
예시: CardItem 컴포넌트를 확장 가능한 구조로 만듭니다.
import React from 'react';
const CardItem = ({ Thumbnail, Body, Text }) => (
<div className="card-item">
{Thumbnail && <Thumbnail />}
{Body && <Body />}
{Text && <Text />}
</div>
);
export default CardItem;
// Thumbnail.js
const Thumbnail = ({ imageUrl }) => <img src={imageUrl} alt="Thumbnail" />;
export default Thumbnail;
// Body.js
const Body = ({ content }) => <div>{content}</div>;
export default Body;
// Text.js
const Text = ({ text }) => <p>{text}</p>;
export default Text;
// Usage example
import CardItem from './CardItem';
import Thumbnail from './Thumbnail';
import Body from './Body';
import Text from './Text';
const App = () => (
<CardItem
Thumbnail={() => <Thumbnail imageUrl="thumbnail.png" />}
Body={() => <Body content="Card content" />}
Text={() => <Text text="Some text" />}
/>
);
- CardItem 컴포넌트는 확장이 가능하지만, 기존 코드는 변경하지 않습니다.
- 추가적인 섹션을 확장하여 추가할 수 있습니다.
3. LSP 리스코프 치환 원칙 (Liskov Substitution Principle)
설명:
- 하위 타입 객체는 상위 타입 객체로 대체 가능해야 합니다.
- 리액트에서는 상속보다는 합성을 사용하여 컴포넌트를 재사용하는 것이 권장됩니다.
예시: 상위 컴포넌트를 대체 가능한 하위 컴포넌트로 합성합니다.
const Button = ({ onClick, children }) => (
<button onClick={onClick}>{children}</button>
);
export default Button;
// PrimaryButton.js
import Button from './Button';
const PrimaryButton = (props) => (
<Button {...props} style={{ backgroundColor: 'blue', color: 'white' }} />
);
export default PrimaryButton;
// Usage example
import Button from './Button';
import PrimaryButton from './PrimaryButton';
const App = () => (
<div>
<Button onClick={() => alert('Button clicked')}>Click me</Button>
<PrimaryButton onClick={() => alert('Primary button clicked')}>Primary</PrimaryButton>
</div>
);
- PrimaryButton은 Button을 대체하여 사용 가능합니다.
- 컴포넌트 합성을 통해 코드를 재사용합니다.
4. ISP 인터페이스 분리 원칙 (Interface Segregation Principle)
설명:
- 클라이언트(사용자)가 실제로 사용하는 인터페이스를 만들어야 합니다.
- 불필요한 의존성을 줄이고, 인터페이스를 사용하는 용도에 맞게 분리합니다.
예시: 사용자 정보와 인증 인터페이스를 분리합니다.
export const getUserInfo = (userId) => {
// Fetch user info from an API
};
// authService.js
export const signIn = (email, password) => {
// Authenticate user
};
export const signOut = () => {
// Sign out user
};
// Usage example
import { getUserInfo } from './userService';
import { signIn, signOut } from './authService';
const App = () => {
// Use getUserInfo, signIn, and signOut as needed
};
- userService와 authService를 분리하여 필요에 따라 사용할 수 있습니다.
- 각 서비스는 사용 용도에 맞게 분리된 인터페이스를 제공합니다.
5. DIP 의존 역전 원칙 (Dependency Inversion Principle)
설명:
- 고수준 모듈은 저수준 모듈의 구현에 의존하지 말아야 합니다.
- 추상화된 레이어에 의존하여 의존성을 줄입니다.
예시: 데이터를 가져오는 로직을 추상화하여 컴포넌트에서 사용할 수 있도록 합니다.
// dataService.js
export const fetchData = async (url) => {
const response = await fetch(url);
const data = await response.json();
return data;
};
// DataComponent.js
import React, { useEffect, useState } from 'react';
import { fetchData } from './dataService';
const DataComponent = ({ dataUrl }) => {
const [data, setData] = useState(null);
useEffect(() => {
const loadData = async () => {
const fetchedData = await fetchData(dataUrl);
setData(fetchedData);
};
loadData();
}, [dataUrl]);
return (
<div>
{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : <p>Loading...</p>}
</div>
);
};
export default DataComponent;
// Usage example
const App = () => <DataComponent dataUrl="https://api.example.com/data" />;
- fetchData 함수는 데이터를 가져오는 로직을 추상화합니다.
- DataComponent는 데이터 로딩에 대한 로직을 알 필요 없이 fetchData에 의존하여 데이터를 가져옵니다.
이렇게 각 원칙에 따라 리액트 코드에서 어떻게 적용할 수 있는지 예시와 함께 설명드렸습니다. 각 원칙을 코드에 반영하면, 유지보수가 용이하고 재사용성이 높은 코드를 작성할 수 있습니다.
'TIL' 카테고리의 다른 글
TIL - React 훅 소개 (0) | 2024.06.19 |
---|---|
TIL - 팀 프로젝트에서 SOLID 원칙 적용 경험 공유 (0) | 2024.06.18 |
TIL - 상태 관리 도구 비교: Redux, Zustand, React Query (0) | 2024.06.14 |
TIL - React 컴포넌트의 생애주기 (0) | 2024.06.13 |
TIL - 트러블 슈팅 with tanstack/react-query (0) | 2024.06.12 |