[3차] 프리온보딩 챌린지(FE) - 과제 (SOLID한 컴포넌트)

2023. 12. 19. 23:36·💡 원티드 프리온보딩 챌린지 💡/PRE-ONBOARDING_FE (12월)

선택 과제) 본인이 작성했던 코드 중에서 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(/&apos;/g, '').replace(/&amp;/g, '').replace(/&quot;/g, ''),
            description: item.description ? item.description.replace(/<\/?b>/g, '').replace(/&apos;/g, '').replace(/&amp;/g, '').replace(/&quot;/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

 

 

'💡 원티드 프리온보딩 챌린지 💡/PRE-ONBOARDING_FE (12월)' 카테고리의 다른 글
  • [3차] 프리온보딩 챌린지(FE) - 백엔드 코드를 수정할 수 없을 때 프론트엔드가 할 수 있는 최적화 방법
  • [4차] 프리온보딩 챌린지(FE) - Logic First, React Later
  • [3차] 프리온보딩 챌린지(FE) - SOLID한 컴포넌트 만들기
  • [2차] 프리온보딩 챌린지(FE) - 과제 (비즈니스 로직 분리)
Yeonhub
Yeonhub
✨ https://github.com/yeonhub 📧 lsy3237@gmail.com
  • Yeonhub
    비 전공자의 Be developer
    Yeonhub
  • 전체
    오늘
    어제
    • 전체보기 (169)
      • 🔍 Tech 🔍 (19)
        • Front-End (11)
        • Back-End (4)
        • AI (1)
        • Server (1)
        • Etc (2)
      • 💡 원티드 프리온보딩 챌린지 💡 (14)
        • PRE-ONBOARDING_AI (11월) (1)
        • PRE-ONBOARDING_FE (2월) (2)
        • PRE-ONBOARDING_FE (1월) (2)
        • PRE-ONBOARDING_FE (12월) (9)
      • 🔥 부트캠프-웹 개발 🔥 (118)
        • HTML5 (7)
        • CSS3 (21)
        • JavaScript (27)
        • JavaScript_advanced (9)
        • React (24)
        • Next (1)
        • MYSql (5)
        • Node (5)
        • 오늘하날(개인프로젝트) (12)
        • 이젠제주투어(팀프로젝트) (7)
      • 💻 CS 💻 (1)
        • 알고리즘 (1)
      • ⚡ 코딩테스트 ⚡ (11)
        • JavaScript (11)
      • 📚 Books 📚 (6)
        • 클린 아키텍처 (2)
        • 인사이드 자바스크립트 (4)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Github
  • 공지사항

  • 인기 글

  • 태그

    프론트엔드 테스트코드
    bottom sheet
    expo map
    react native expo fcm
    react vite
    php node
    expo google map
    react native analytics
    expo admob
    react native admob
    expo 길찾기
    rn admob
    expo fcm push
    rn bottom sheet
    컴파운드 컴포넌트 패턴
    라스콘4
    Node
    expo deep linking
    node cron
    node.js fcm
    라스콘
    python node
    node crontab
    react native firebase analytics
    expo node fcm
    node fcm
    react native bottom sheet
    javascript fcm
    expo fcm
    expo 지도
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Yeonhub
[3차] 프리온보딩 챌린지(FE) - 과제 (SOLID한 컴포넌트)
상단으로

티스토리툴바