한국어로는 의존성 역전 원칙
이라고 부른다.
고수준 모듈
이 저수준 모듈
에 의존해서는 안 되고, 둘 다 추상화에 의존해야 한다는 원칙이다.
고수준 모듈
은 모듈의 본질적인 기능을 나타내고,
저수준 모듈
은 고수준 모듈의 기능을 수행하기 위한 각각의 동작들을 나타낸다.
고수준 모듈
보다는 저수준 모듈
에서 변화가 더 빈번하게 일어나기 때문에 고수준 모듈
이 저수준 모듈
에 의존한다면 결국 고수준 모듈
도 매번 저수준 모듈
의 변화에 맞춰 같이 변화해야만 하는 상황이 발생한다.
따라서 DIP는 '자신보다 변하기 쉬운 것에 의존하면 안 된다는 원칙'으로도 이해할 수 있다.
로그인을 예로 들어 설명하자면 고수준 모듈
은 로그인 기능 자체이고, 저수준 모듈
은 로그인 방식, 즉 '로그인 요청 api를 호출해 응답을 받아오는 것'이라고 볼 수 있다.
import api from '~/common/api'
const LoginForm = () => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const handleSubmit = async (evt) => {
evt.preventDefault()
await api.login(email, password)
}
return (
<form onSubmit={handleSubmit}>
<input type="email" value={email} onChange={e => setEmail(e.target.value)} />
<input type="password" value={password} onChange={e => setPassword(e.target.value)} />
<button type="submit">Log in</button>
</form>
)
}
위 코드에선 고수준 모듈
인 LoginForm
이 저수준 모듈
인 api
에 의존하고 있다.
따라서 api
에 변경이 생기거나 아예 다른 모듈을 사용하게 된다면 그에 맞춰 LoginForm
도 변경돼야 할 것이다.
이를 개선하려면 LoginForm
에서 직접적으로 api
를 사용하는 대신, 추상화 컴포넌트를 만들어 그 안에 모듈을 정의한 후 LoginForm
과 연결해주면 된다.
// ConnectedLoginForm.tsx
import api from '~/common/api'
const ConnectedLoginForm = () => {
const handleSubmit = async (email, password) => {
await api.login(email, password)
}
return (
<LoginForm onSubmit={handleSubmit} />
)
}
// LoginForm.tsx
type Props = {
onSubmit: (email: string, password: string) => Promise<void>
}
const LoginForm = ({ onSubmit }: Props) => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const handleSubmit = async (evt) => {
evt.preventDefault()
await onSubmit(email, password)
}
return (
<form onSubmit={handleSubmit}>
<input type="email" value={email} onChange={e => setEmail(e.target.value)} />
<input type="password" value={password} onChange={e => setPassword(e.target.value)} />
<button type="submit">Log in</button>
</form>
)
}
ConnectedLoginForm
이라는 추상화 컴포넌트로 인해 LoginForm
은 api
에 직접적으로 의존하는 대신 '로그인 요청 api를 호출해 응답을 받아오는 것'을 추상화한onSubmit
이라는 모듈에 의존하게 되면서 DIP를 준수하는 코드가 된다.
이는 결국 컴포넌트/모듈 간 결합도를 최소화하는 효과가 있어 OCP와도 밀접한 관계가 있다.
- DIP는 자신보다 변하기 쉬운 것에 의존하면 안 된다는 원칙이다.
- 추상화를 통해 고수준 모듈이 저수준 모듈에 의존하는 문제를 해결한다.
- DIP를 준수해 컴포넌트/모듈 간 결합도를 최소화한다.