선택 과제) 본인이 작성했던 코드 중에서 SOLID 원칙과 IoC를 적용할만한 부분을 찾아보고 개선한 뒤 느낀 점 블로그에 정리하기
지난 과제에서 비즈니스 로직을 분리하면서 어떠한 ‘답답함’을 느끼셨을 수 있다고 말씀드렸습니다. 이번 강의에서 학습한 SOLID와 IoC의 개념을 가지고 코드를 개선해 보세요.
SOLID 원칙과 CCP 적용을 위해 전에 만들었던 작은 프로젝트를 리펙터링했다.
비교적 상하위 컴포넌트 구분이 쉬워 적절해 보였다.
https://github.com/yeonhub/PP-doosan_bears
GitHub - yeonhub/PP-doosan_bears: [개인 프로젝트] 두산 베어스 선수 정보
[개인 프로젝트] 두산 베어스 선수 정보. Contribute to yeonhub/PP-doosan_bears development by creating an account on GitHub.
github.com
https://github.com/yeonhub/PP-doosan_bears_node
GitHub - yeonhub/PP-doosan_bears_node: [개인 프로젝트] 두산 베어스 선수 정보 (Node.js)
[개인 프로젝트] 두산 베어스 선수 정보 (Node.js). Contribute to yeonhub/PP-doosan_bears_node development by creating an account on GitHub.
github.com

1. 폴더구조 다시 하기
📦src
┣ 📂assets
┃ ┣ 📂api
┃ ┃ ┣ 📜DSPlayerData.js
┃ ┃ ┗ 📜DSStatsData.js
┗ 📂doosan
┣ 📂DSContentComponet
┃ ┣ 📜DSClubUI.jsx
┃ ┣ 📜DSNewsUI.jsx
┃ ┣ 📜DSProfileUI.jsx
┃ ┣ 📜DSStatsUI.jsx
┃ ┣ 📜DSYoutubeUI.jsx
┃ ┗ 📜newsService.js
┣ 📜DSContent.jsx
┣ 📜DSContentContainer.jsx
┣ 📜DSImages.jsx
┣ 📜DSImagesUI.jsx
┣ 📜DSInfo.jsx
┣ 📜DSMenuUI.jsx
┣ 📜DSPlayers.jsx
┣ 📜DSPlayersUI.jsx
┣ 📜Header.jsx
┣ 📜Main.jsx
┗ 📜Main.scss

content를 담당하는 컴포넌트들을 폴더에 모아두었다.
2. 비즈니스 로직 UI 로직 분리
SOLID 중 SRP(단일 책임 원칙)를 준수하기 위해 비즈니스 로직과 UI 로직이 같이 들어있는 컴포넌트를 분리했다.
DSPlayers / DSPlayersUI
DSImages / DSImagesUI
newsService / DSNewsUI
// DSPlayer.jsx
const DSPlayers = () => {
const { selectPlayer } = useContext(HeaderContext);
const onSelectPlayer = (nameno) => {
selectPlayer(nameno);
};
return <DSPlayersUI data={DSPlayerData} onSelectPlayer={onSelectPlayer} />;
};
// DSPlayersUI.jsx
const DSPlayersUI = ({ data, onSelectPlayer }) => {
return (
<ul className='list'>
{data.map(item => (
<li key={item.id} data-id={item.id} onClick={() => onSelectPlayer(item.nameno)}>
<img src={item.imgurl} alt={item.name} />
<p>
<em>{item.no}</em>
<strong>{item.position}</strong>
</p>
<span>{item.name}</span>
</li>
))}
</ul>
);
};
네이버 API를 이용한 비즈니스 로직도 분리해 주었다.
// newsService.js
const fetchNewsItems = async (name) => {
try {
const response = await axios.get('http://localhost:5000/news', {
params: {
query: `${name}`,
},
});
return response.data.items.map((item) => ({
title: item.title.replace(/<\/?b>/g, '').replace(/'/g, '').replace(/&/g, '').replace(/"/g, ''),
description: item.description ? item.description.replace(/<\/?b>/g, '').replace(/'/g, '').replace(/&/g, '').replace(/"/g, '') : "(포토기사입니다)",
link: item.link,
}));
} catch (error) {
console.error(error);
return [];
}
};
// DSNewsUI.jsx
const DSNewsUI = () => {
const { currentPlayer } = useContext(HeaderContext);
const { no, name } = currentPlayer;
const [newsItems, setNewsItems] = useState([]);
useEffect(() => {
const fetchData = async () => {
const items = await fetchNewsItems(name);
setNewsItems(items);
};
fetchData();
}, [name]);
return (
<div className='news'>
<p className='noth'>두산베어스 NO.{no}</p>
<p className='nameth'>{name}</p>
<p className='naversports'><a href="https://sports.news.naver.com/index"><img src="./images/naversports.png" alt="naversports" /></a></p>
{
newsItems.map((item, index) => (
<ul className='newsUl' key={index}>
<li className='newsLi'>
<a className='newsA' href={item.link}>
<p className='title'>{item.title}</p>
<p className='desc'>{item.description}</p>
</a>
</li>
</ul>
))
}
</div>
);
};
3. 컴파운드 컴포넌트 패턴(CCP)
작은 컴포넌트를 모아 하나의 컴포넌트로 만들어 각각 컴포넌트들이 어떤 역할을 하는지 쉽게 알 수 있고 컴포넌트 간의 결합성을 낮추었다.
const Main = () => {
return (
<>
<Header>
<Header.Logo />
<DSPlayers />
<DSInfo>
<DSImages />
<DSContentContainer>
<DSMenuUI />
<DSContent />
</DSContentContainer>
</DSInfo>
</Header>
</>
);
};
Header 컴포넌트에선 Context를 사용해 Header 컴포넌트 안에 있는 작은 컴포넌트들에서 데이터를 주고받을 수 있게 했다.
DSInfo 컴포넌트에도 선택한 선수의 정보를 다른 컴포넌트에도 전달해 주기 위해 Context를 추가해 주었다.

4. 결과
새로운 기능을 추가하더라도 쉽게 할 수 있고 불필요한 prop를 받는 컴포넌트들이 없어졌다. 그리고 변수를 필요한 컴포넌트들에서만 사용하고 관리할 수 있게 되어 다른 컴포넌트에 영향이 없게 되었다.
제일 중요한 SRP 원칙에 맞게 컴포넌트들이 간단해졌다. 그렇기에 코드 가독성도 높아지고 수정할 부분이 생긴다면 쉽게 할 수 있을 것 같다.
https://www.youtube.com/watch?v=VM6YRrUsnUY
5. 후기
볼륨도 크지 않아서 금방, 쉽게 리펙터링 할 수 있을 거라고 생각했지만... 생각처럼 쉽지 않았다.
기존 코드를 오류와 함께 몇 번이고 수정하다 결국 새 폴더를 만들어서 리펙터링했다.
개념을 익히고 실제로 적용해 보니 이전 코드가 얼마나 비효율적이고 알아보기 힘든지 느꼈다. 다시 한번 이번 챌린지에서 배우고 공부한 것들을 앞으로 프로그래밍하며 잘 적용시켜 나가야 할 것을 다짐하게 된다.
https://fe-developers.kakaoent.com/2022/220731-composition-component/
합성 컴포넌트로 재사용성 극대화하기 | 카카오엔터테인먼트 FE 기술블로그
방경민(Kai) 사용자들에게 보이는 부분을 개발한다는 데서 프론트엔드 개발자의 매력을 듬뿍 느끼고 있습니다.
fe-developers.kakaoent.com
https://brunch.co.kr/@finda/556
단단한 컴포넌트 부수기(feat. 조합, IoC)
핀다 개발자들은 이렇게 일하곤 하지 Vol.02 | 안녕하세요. 핀다에서 열심히 프론트엔드 개발을 하고 있는 신권철입니다. 핀다는 웹서비스를 개발할 때 리액트를 적극적으로 활용하고 있습니다.
brunch.co.kr