Interpace Segregation Principle (ISP)
ISP
란 구현에 의존하고 있는 인터페이스가 Compact
해야 한다는 것을 의미한다.
React
에서 사용되는 ISP
에 대한 원칙을 하기 전 , 객체 지향에서 이야기 하는 ISP
에 대해 먼저 이야기 한 후 React
에서 사용되는 ISP
원칙에 대해이야기 해보자
ISP
class Calculator {
introduce() {
console.log('안녕하세요 저는 계산기입니다');
}
add(num1, num2) {
return num1 + num2;
}
subtract(num1, num2) {
return num1 - num2;
}
multiply(num1, num2) {
return num1 * num2;
}
devide(num1, num2) {
return num1 / num2;
}
}
const adder = new Calculator();
const subtracter = new Calculator();
const multiplier = new Calculator();
const devider = new Calculator();
다음과 같이 Calculator
라는 추상화 된 인터페이스를 사용하는 adder , subtracter , mutiplier , devider
객체들이 존재한다고 이야기 해보자
각 객체들은 객체명에서 알 수 있듯이 한 가지 기능만을 하는 객체이다.
현재 아키텍쳐의 관계는 객체의 기능은 제한적임에도 불가하고 다양한 기능이 있는 Calculator
인터페이스에 의존성을 가지고 있는 모습을 볼 수 있다.
이렇게 사용하는 인터페이스가 무거워질 수록 (교재에서는 fat
이라는 표현을 한다.) 아키텍쳐의 구조를 명확하게 아는 것이 힘들다.
뚱뚱한 인터페이스를 수정하던 과정에서 예기치 못한 다른 메소드에도 영항을 미쳐 오류들이 범잡을 수 없이 늘어나기도 하고 말이다.
또한 우리는 현재 런타임 시 컴파일 되는 자바스크립트 언어를 이용하고 있지만 만약 컴파일을 사용하는 언어를 사용 할 시 뚱뚱한 인터페이스인 Calculator
에 새 기능이 추가되거나 객체와 상관없는 메소드가 수정되게 되면
수정된 부분과 상관 없는 객체들 마저도 다시 컴파일 해줘야 한다는 단점이 존재한다.
ISP
추상화된 클래스와 해당 클래스를 이용해 만든 객체의 관계가 1:1로 잘 대응 될 수록 아키텍쳐의 구조를 명확하게 보는 것이 쉽고
객체와 클래스를 관리하는 것이 더 용이해진다.
class Calculator {
introduce() {
console.log('안녕하세요 저는 계산기입니다');
}
}
class Adder extends Calculator {
add(num1, num2) {
return num1 + num2;
}
}
class Subtracter extends Calculator {
subtract(num1, num2) {
return num1 - num2;
}
}
...
class Devider extends Calculator {
devide(num1, num2) {
return num1 / num2;
}
}
const adder = new Adder();
const subtracter = new Subtracter();
const multiplier = new Multiplier();
const devider = new Devider();
다음처럼 인터페이스와 객체의 관계가 1:1로 잘 대응 될 수록 하여 클래스와 객체 간의 관계를 명확하게 이해하는 것이 가능하다.
항상 기억하자 , 뚱뚱한 인터페이스는 관리하기가 매우 힘들다.
React
에서의 ISP
React
에서의 ISP
는 객체지향에서의 ISP
와 깊은 연관은 없지만
뚱뚱한 요소들과 의존성을 갖고 있는 경우는 자주 볼 수 있다. 예를 들어
props
가 뚱뚱한 경우const users = {
lastName: 'dongdong',
age: 20,
};
export default function GreetingName ({users}) {
return <h1>안녕하세요 {users.lastName} 님 </h1>
}
export default function GreetingAge ({users}) {
return <h1>안녕하세요 {users.age} 살이시군요 </h1>
}
GreetingName , GreetingAge
컴포넌트의 경우는 props
로 뚱뚱한 users
객체를 받는다.
실제 필요한 기능은 users
객체의 일부 정보인데 말이다.
이 때 users
에 새로운 값이 추가되거나 , 기존의 값이 수정이 되게 된다면
본인이 사용하지 않는 property
일지라도 객체가 변경됨에 따라 각 컴포넌트는 불필요한 리렌더링을 겪게 된다.
const users = {
lastName: 'dongdong',
age: 20,
};
export default function GreetingName ({lastName}) {
return <h1>안녕하세요 {lastName} 님 </h1>
}
export default function GreetingAge ({age}) {
return <h1>안녕하세요 {age} 살이시군요 </h1>
}
뚱뚱한 props
를 받는 것이 아니라 본인에게 필요한 내용만 props
를 받음으로서 의존성의 관계를
뚱뚱한 props
가 아닌 필요한 부분에만 의존성을 갖도록 변경해줄 수 있다.
추가적으로 불필요한
state
문이나context
를 컴포넌트 내부에서 불러오는 등의 경우도ISP
를 지키지 못한 경우로 생각 할 수 있을 것이다.