한국어로는 리스코프 치환 원칙
이라고 부른다.
상위(부모) 타입 객체를 하위(자식) 타입 객체로 치환해도 정상 작동해야 한다는 원칙이다.
Custom Input 컴포넌트를 예시로 들겠다.
import cx from "clsx";
interface ISearchInputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
isLarge?: boolean;
}
export function SearchInput(props: ISearchInputProps) {
const { value, onChange, isLarge, ...restProps } = props;
return (
<div>
<div>
<svg
aria-hidden="true"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
></path>
</svg>
</div>
<input
type="search"
id="default-search"
className={cx(isLarge && "text-3xl")}
placeholder="Search for the right one..."
required
value={value}
onChange={onChange}
{...restProps}
/>
</div>
);
}
SearchInput
컴포넌트는 input
태그의 하위 타입 객체라고 볼 수 있다.
React에서 이런 식으로 HTML element를 상속해서 사용할 때는 LSP를 준수하기 위해 두 가지 사항을 지켜야 한다.
ISearchInputProps
와 같이 상위 element의 type을 extend한 인터페이스를 정의한다.restProps
와 같이 우리가 직접 사용하는 props를 제외한 나머지 props들 또한 전달해준다.LSP를 준수하게 되면 코드에서 상위 타입 객체와 하위 타입 객체를 서로 자유자재로 대체할 수 있기 때문에 이점이 있다.
하지만 React에선 종종 부모 타입 객체가 가지지 않은 새로운 기능 등을 부여하기 위해 자식 객체를 만들고, 이 과정에서 부모 객체의 인터페이스를 사용하지 못하게 될 수도 있다.
그렇기 때문에 LSP는 항상 반드시 지킬 수는 없고, 그래야만 하는 것도 아니다.
다만 LSP를 불필요하게 위반하는 상황은 피해야 한다.
LSP를 불필요하게 위반하는 상황은 크게 두 가지가 있다.
...restProps
에 해당하는 부분을 이유 없이 생략해 props를 모두 상속받지 않는 상황type Props = HTMLAttributes<HTMLInputElement> & {
onUpdate: (value: string) => void
}
const CustomInput = ({ onUpdate, ...props }: Props) => {
const onChange = (event) => {
/// ... some logic
onUpdate(event.target.value)
}
return <input {...props} onChange={onChange} />
}
위 코드에서는 CustomInput
내부의 onChange
라는 로컬 함수로 인해 props로 onChange
대신 onUpdate
라는 alias를 사용했다. 이런 상황을 방지하기 위해 아래와 같은 naming convention을 통해 LSP를 준수하도록 만든다.
type Props = HTMLAttributes<HTMLInputElement>
const CustomInput = ({ onChange, ...props }: Props) => {
const handleChange = (event) => {
/// ... some logic
onChange(event)
}
return <input {...props} onChange={handleChange} />
}
- LSP는 상위 타입 객체를 하위 타입 객체로 치환해도 정상 작동해야 한다는 원칙이다.
- 상위 타입 객체를 extend하는 인터페이스를 정의하고 props를 모두 상속받는다.
- 항상 반드시 지켜야만 하는 것은 아니지만 불필요하게 위반하지 않아야 한다.