티스토리 뷰

javascript/react

개발 원칙 With SOLID - 1

oraclian 2024. 10. 14. 19:39

SOLID란?

SOLID 원칙은 객체 지향 프로그래밍에서 소프트웨어 설계를 개선하기 위한 다섯 가지 원칙을 의미.

주로 객체 지향 언어에 많이 사용되지만 언어에 상관 없이 적용할 수 있으며, 이러한 원칙을 적용하면 코드의 유지보수성과 확장성을 높일 수 있음.

 

1.  단일 책임 원칙 (SRP, Single Responsibility Principle)

각 모듈은 하나의 책임만을 가져야 하며, 그 책임은 완전히 캡슐화되어야 함을 의미합니다.

여기서 "책임"이란 해당 모듈이 수행해야 하는 기능이나 역할을 의미하며, 그 기능이 변경될 경우에만 해당 클래스나 모듈을 수정해야 함을 의미합니다.

 

책임의 예시는 다음과 같습니다.

- UI 책임: 사용자 인터페이스 렌더링.

- 비즈니스 로직 책임: 데이터 처리나 비즈니스 규칙을 적용.

- 데이터 접근 책임: 데이터베이스나 외부 API와의 상호작용.

 

단일 책임 원칙이 중요한 이유는 각 모듈의 구성 요소가 명확한 역할을 가지게 하여 커플링을 줄이고 효율적으로 개발할 수 있습니다.

 

전통적인 Container/Presentational 패턴을 이용해 설명하자면 아래와 같습니다.

// Presentational Component
function Image({ src }) {
	return <img src={src} />
}

// Container Component
function ImageList() {
    const { imageList, error } = useImageList();

    if (error) {
        return <div>Error: {error}</div>;
    }

    return (
        <div>
            {imageList.map((imageSource, index) => (
                <Image key={index} src={imageSource} />
            ))}
        </div>
    );
}

// Custom Hook
function useImageList() {
    const { data: imageList, error } = useFetch('https://dog.ceo/api/breed/labrador/images/random/6');

    return imageList;
}

Image 컴포넌트는 오직 이미지를 렌더링하는 책임만을 가집니다.

ImageList 컴포넌트는 데이터를 관리하고 Image 컴포넌트들을 렌더링하는 책임을 가집니다.

useImageList 훅은 이미지 데이터를 fetch하는 책임을 가집니다.

 

2.  개방 폐쇄 원칙 (OCP, Open-Closed Principle)

소프트웨어 구성 요소(컴포넌트, 클래스, 모듈, 함수)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 함을 의미합니다.

이 말은 기존 코드를 변경하지 않고 기능을 수정하거나 추가할 수 있어야함을 의미합니다.

 

개방 폐쇄 원칙의 중요성은 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있어 확장성과 재사용성을 높인다는 점에 있습니다.

 

이 원칙은 HOC와 Compound Component Pattern을 이용해 설명할 수 있습니다.

아래는 HOC를 이용한 예시입니다.

// 기본 Input 컴포넌트
const Input = ({ error, ...props }) => {
  return (
    <div>
      <input {...props} />
      {error && <span style={{ color: 'red' }}>{error}</span>}
    </div>
  );
};

// 유효성 검사를 위한 HOC
const withValidation = (WrappedComponent, validationFunc) => {
  return (props) => {
    const [error, setError] = useState('');
    const [value, setValue] = useState('');

    const handleChange = (e) => {
      const newValue = e.target.value;
      setValue(newValue);
      const validationError = validationFunc(newValue);
      setError(validationError);
      if (props.onChange) {
        props.onChange(e);
      }
    };

    return (
      <WrappedComponent
        {...props}
        value={value}
        onChange={handleChange}
        error={error}
      />
    );
  };
};


const validateEmail = (value) => {
  if (!value) return '이메일을 입력해주세요.';
  if (!/\S+@\S+\.\S+/.test(value)) return '유효한 이메일 주소를 입력해주세요.';
  return '';
};

// Input과 HOC를 이용해 확장된 EmailInput
const EmailInput = withValidation(Input, validateEmail);


const validatePassword = (value) => {
  if (!value) return '비밀번호를 입력해주세요.';
  if (value.length < 8) return '비밀번호는 8자 이상이어야 합니다.';
  return '';
};

// Input과 HOC를 이용해 확장된 PasswordInput
const PasswordInput = withValidation(Input, validatePassword);

아래는 Compound Component Pattern을 이용한 예시입니다.

// Compound Component Pattern으로 작성된 Card 컴포넌트
const Root = ({ children, className }) => {
  return <div className={cn("card", className)}>{children}</div>;
};

const Header = ({ children, className }) => {
  return <div className={cn("card-header", className)}>{children}</div>;
};

const Body = ({ children, className }) => {
  return <div className={cn("card-body", className)}>{children}</div>;
};

const Footer = ({ children, className }) => {
  return <div className={cn("card-footer", className)}>{children}</div>;
};

const Card = {
  Root,
  Header,
  Body,
  Footer,
}


// Card 컴포넌트를 이용해 확장한 ProductCard 컴포넌트
const ProductCard = ({ product }) => {
  return (
    <Card.Root className={styles.productCard}>
      <Card.Header>
        <h2>{product.name}</h2>
      </Card.Header>
      <Card.Body>
        <img src={product.image} alt={product.name} />
        <p>{product.description}</p>
        <p>가격: {product.price}원</p>
      </Card.Body>
      <Card.Footer>
        <button>장바구니에 추가</button>
      </Card.Footer>
    </Card>
  );
};

 

너무 길어져 다음 게시글에서 이어서 진행하도록 하겠습니다.

 

다음글: https://orashelter.tistory.com/94

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함