리액트 프로젝트의 결합도와 응집도를 관리하는 방법에 대해 알아봅니다.
결합도 분석 단위는 모듈(폴더, 패키지)이라 볼 수 있습니다.
응집도 분석 단위는 라인 입니다.
같이 보면 좋은 글 !
https://itchallenger.tistory.com/644
https://ui.toast.com/weekly-pick/ko_20150522
원문 : https://betterprogramming.pub/coupling-cohesion-552b022492b2
서로 분리되어있지만 각각은 응집력 있는 방식으로 시스템 요소를 구성합니다. 즉, 낮은 결합도과 높은 응집도를 선호합니다.
TLDR
- 응집도를 논하는 각 모듈(디렉터리) 범위에서 요소들은 같이 변해도 무관합니다.
- 결합도는 의존대상의 변경이 의존중인 모듈의 변경을 유발함을 의미합니다.
- 의존하는 모듈의 변경이 의존하고 있는 모듈의 변경을 유발하지 않도록 결합도를 관리해야 합니다.
- 양뱡향 결합은 피해야 합니다.
- 결합도를 낮추기 위해선 먼저 모듈 간의 응집도를 높여야 합니다.
- 응집도를 높이기 위해선 같이 사용하는 것들, 변하는 것들을 하나의 모듈로 묶습니다.
- Place code as close to where it's relevant as possible
- Things that change together should be located as close as reasonable.
결합도 (coupling)
사실, 연결 수를 계산하는 쉬운 방법이 있는지 확실하지 않습니다.
또한 나는 이 숫자를 계산하는 것이 유용하다고 생각하지도 않습니다.
단 하나의 가장 적절한 방법은 이후 논의할 기술을 따르는 것입니다.
응집도 (cohesion)
- module
- class
- React component (both class and functional)
- namespace
- service
- 등등,,,
- 응집력 있는 시스템에서는 요소 간의 연결이 훨씬 적기 때문입니다.
- "결합"과 "응집"이라는 용어는 항상 함께 사용됩니다.
- 높은 응집도는 연결 수를 줄이고 시스템을 보다 쉽게 관리하고 확장할 수 있도록 합니다.
응집도와 결합도
다양한 사례를 살펴보겠습니다.
아래에서 "결합"과 "응집" 사이의 연결을 설명하는 다이어그램을 찾을 수 있습니다.
이는 응용 프로그램을 분석하고 개선 계획을 만드는 데 유용할 수 있습니다.
1. 이상적(Ideal) : 낮은 결합도, 높은 응집도
응집도를 논하는 범위에서 요소들은 같이 변해도 무관합니다.
결합도는 의존대상의 변경이 의존중인 모듈의 변경을 유발하는 것을 의미힙니다.
의존하는 모듈의 변경이 의존하고 있는 모듈의 변경을 유발하지 않도록 결합도를 관리해야 합니다.
또한 양뱡향 결합은 피해야 합니다.
- 같이 쓰이는 요소
- 같이 변하는 요소
프로젝트 구조는 아래와 같이 보일 것입니다.
이와 관련하여 참고할 문서가 있습니다.
https://kentcdodds.com/blog/colocation
https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster
같이 변하는 것들을 같은 모듈로 관리합니다.
같이 사용하는 것들을 같은 모듈로 관리합니다.
app
├── components
│ ├── dropdown
│ . ├── container.js
│ ├── component.js
│ ├── utils.js
│ ├── use-options-search.js
│ └── index.js
├── utils
│ ├── find-tree-items
│ . ├── find-tree-items.js
│ ├── find-tree-items.test.js
│ └── index.js
├── hooks
│ ├── use-previous
│ . ├── use-previous.js
│ ├── use-previous.test.js
│ └── index.js
├── index.js
.
// app/index.js
import usePrevious from 'hooks/use-previous';
import Dropdown from 'components/dropdown';
const App = () => {
const [value, setValue] = useState('');
const previousValue = usePrevious(provalue);
const handleChange = useCallback((event) => {
setValue(event.value);
}, []);
return (
<>
<span>Pevious value: {previousValue}</span>
<Dropdown
onChange={handleChange}
options={[/*...*/]} />
</>
);
};
// ...
2. God Module (갓 모듈) : 높은 결합도, 높은 응집도
app
├── components
│ ├── dropdown
│ . ├── container.js
│ ├── component.js
│ ├── utils.js
│ ├── use-options-search.js
│ ├── use-user-options-data.js
│ ├── use-language-options-data.js
│ └── index.js
├── index.js
.
코드 예제는 다음과 같습니다.
// app/component/dropdown/container
import useOptionsSearch from './use-options-search';
import useUserOptionsData from './use-user-options-data';
import useLanguageOptionsData from './use-language-options-data';
import * as utils from './utils';
import Dropdown from './component';
const DropdownContainer = (props) => {
const optionsSearch = useOptionsSearch(/*...*/);
const userOptionsData = useUserOptionsData(/*...*/);
const langageOptionsData = useLanguageOptionsData(/*...*/);
const optionsData = useMemo(() => {
switch (props.dataType) {
case 'user':
return userOptionsData;
case 'language':
return languageOptionsData;
default:
return [];
}
}, [props.dataType]);
return (
<Dropdown {/*...*/} />
);
};
DropdownContainer.propTypes = {
onChange: PropTypes.func.isRequired,
dataType: PropTypes.oneOf(['user', 'language']).isRequired,
};
export default DropdownContainer;
주 : 렌더링 (반복, 조건부 렌더링, 스타일) 관련 로직만 View Asset Component에 넣음
3. Wrong Boundaries (잘못된 경계) : 높은 결합도, 낮은 응집도
가장 흔하게 볼 수 있는 프로젝트 구조입니다.
추가 설명 없이 다이어그램과 예제를 살펴보겠습니다.
프로젝트 구조입니다.
app
├── components
│ ├── dropdown
│ . ├── container.js
│ ├── component.js
│ └── index.js
├── utils
│ ├── find-tree-items
│ │ ├── find-tree-items.js
│ │ ├── find-tree-items.test.js
│ │ └── index.js
│ ├── dropdown-utils
│ . ├── ...
│ .
├── hooks
│ ├── use-previous
│ │ ├── use-previous.js
│ │ ├── use-previous.test.js
│ │ └── index.js
│ ├── use-dropdown-options-search
│ . ├── use-dropdown-options-search.js
│ ├── use-dropdown-options-search.test.js
│ └── index.js
├── index.js
.
경계가 잘못되었음을 분명히 이해하시기 바랍니다. 이러한 앱은 첫 번째 예와 같이 재설계되어야 합니다.
4. Destructive Decoupling (파괴적 결합 분해): 낮은 결합도, 낮은 응집도
이번에도 자세한 설명은 생략하고 그림을 보겠습니다.
프로젝트 구조는 다음과 같습니다.
app
├── dropdown.js
├── find-tree-items.js
├── find-tree-items.test.js
├── dropdown-utils.js
├── use-previous.js
├── use-previous.test.js
├── use-dropdown-options-search.js
├── use-dropdown-options-search.test.js
├── index.js
.
// app/index.js
import usePrevious from './use-previous';
import Dropdown from './dropdown';
const App = () => {
const [value, setValue] = useState('');
const previousValue = usePrevious(provalue);
const handleChange = useCallback((event) => {
setValue(event.value);
}, []);
return (
<>
<span>Pevious value: {previousValue}</span>
<Dropdown
onChange={handleChange}
options={[/*...*/]} />
</>
);
};
// ...
- 모듈(파일)의 사용 범위를 파악하기가 다소 어렵습니다.
- 같은 파일에 위치한 정보가 너무 많습니다.
이 콘텐츠가 마음에 드셨기를 바라며 강력한 애플리케이션을 설계하는 데 도움이 되기를 바랍니다!
좀 더 디테일하게 코드 레벨에서 결합도 살펴보기
https://itchallenger.tistory.com/651?category=0
'FrontEnd' 카테고리의 다른 글
React 18의 useSyncExternalStore, Tearing 현상은 무엇인가? (0) | 2022.08.01 |
---|---|
리액트의 의존성 주입 with Context API (dependency injection) (0) | 2022.08.01 |
리액트 컴포넌트의 응집도를 관리하는 방법 (0) | 2022.07.30 |
리액트 Concurrent UI Pattern - Scheduling in React (0) | 2022.07.30 |
리액트 점진적으로(일부만) 도입하기 (제이쿼리와 함께 쓸수 있을까?): ReactDOMServer (0) | 2022.07.25 |