React

SOLID 하게 만들기

노엠디엔 2023. 12. 21. 17:02

결론적으로 소프트웨어 설계와 유지보수에 중점을 두려면 이론이 아닌
실무에 초점을 맞추는 것이 효과적이다.
- 조영호 <오브젝트>, 7~8p

 

SOLID 원칙에 대해 배우면서 이론에 그치지 않고
코드에 적용해 보고 기록하려고 한다
내가 작성한 코드가 맞는지 틀린 지는 모르겠지만
항상 의심하며 계속해서 수정해 나갈 예정이다.

 

MainContainer는 UI만 처리할 것

더보기

밑에 MainContainer는 메인페이지에서 사이드별 컴포넌트들을 보여주는
컴포넌트이다 하지만 nickname이라는 유저의 정보를 가져오는
데이터 로직 비즈니스 로직이 포함되어 있고

해당 정보를 하위컴포넌들에게 props로 전달해주고 있다 

SRP 단일 책임 원칙에도 어긋나고 RightSdie와 NaviSide가
결국 MainContainer에  props에 의존하고 있다는 생각도 들었다

처음에 이렇게 구현한 이유는 MainContainer에서 필요한 상태들을
다 관리하자는 의미였는데

지금 생각하니 MainContainer는 UI만을 return 하는 컴포넌트로써
유저 데이터를 분리하는 게 좋아 보였다.

     *변경 전

MainContainer 로직

    *변경 후

데이터를 가져오는 로직과 props를 없앤 mainContainer 컴포넌트

 데이터를 가져오는 로직과 props를 넘겨주는 부분을 제거하였다.

 RightSide 안에 ServiceRogo라는 컴포넌트에서 해당 데이터를 사용 중인데 

사용하는 곳에서 가져오도록 수정하였다 (NaviSdie도 동일).

하지만 한번 더 ServiceRogo 컴포넌트에서도 해당 유저 정보를 가져오는

로직이 필요할까?라는 생각이 들었다. 해당 유저정보의 Nickname의
사용하는 곳이 있으므로 Nickname 만 가져오는 역할을 수행하는 Provider를
만들어서 Children으로 해당 컴포넌트를 넘겨주는 방법을 사용해보고 싶었는데
props로 어떻게 넘겨줘야 할지 몰라서 실험에 실패했다..

뭐 대충 이런 느낌인데 아 뭔가 다 왔는데? 뭔가 까먹고 놓치고 있다..

이 부분은 MainContainer에서 많이 넘어와서 다른 파트로 기록해야겠다.

ServiceRogo 컴포넌트가 정말 ServiceRogo 컴포넌트인가?

더보기

거의 귀찮아서 컴포넌트를 분리안 한 느낌에 ServiceRogo 컴포넌트이다.

StyeldGreetings부분에 아예 분리해서
ServiceWelecomeMsg 컴포넌트로 만들었다.

   *변경 전

    *변경 후 

이제 ServiceLogo 컴포넌트의 역할은 Logo 부분의
text 부분을 보여 줄 UI로직 밖에 없다.
문제는 ServiceWelcomeMsg 컴포넌트이다. 이 컴포넌트에서는
유저 정보에 따라 UI가 조건부 렌더링된다 nickname 가져오는
비지니스 로직도 포함되어 있다.

   *변경 전

   *변경 후

먼저 ServiceWelecomeMsg 컴포넌트를 생성 후 해당 컴포넌트는
WeclomeMsgContainer컴포넌트를 return 한다.
ServiceWelecomeMsg  컴포넌트는 UI 로직만 담당하게 된다.

WeclomeMsgContainer 컴포넌트는 nickname 가져오는
비즈니스 로직을 담당하게 되고 해당 데이터를  캡슐화 하여
WelcomeMsg 컴포넌트에 props로 보내게 된다.

 

이제 중요한 WelcomeMsg 컴포넌트는 해당 nickname 데이터를
담아서 데이터에 따라 nickname이 있을 때의 text를 보여줄
PersonalizeMsg 컴포넌트와 GenericMsg 컴포넌트를 

변수 MessageComponet에 담는다.
그 후에 MessageComponent를 jsx 부분에서
props에 nickname을 담아 return 한다.

 이렇게 되면 WelcomeMsg에서 nickname 데이터의 유무에 따라
컴포넌트를 return 역할을 담당하며  정확히 SOLID 한 지는 잘 모르겠지만
어느 정도 원칙을 따르는 흐름이 완성되었다.

제일 신기했던 건  액션, 계산을 분리한다면 반은 간다고 들었는데 
조건부 렌더링인 부분을 계산적인 로직으로 빼낸 것? 같아서 나름 뿌듯했다.

    const MessageComponent = nickname !== undefined ? PersonalizedMsg : GenericMsg;
 

 text를 수정하게 되면 PersonalizeMsg 컴포넌트나,
GenericMsg 컴포넌트만 수정하면 되고
컴포넌트 간의 의존성이 줄어들어서 확장성이 좋아진 것 같다!

 

요구 사항에 맞게 NaviSidie 를 SOLID 하게

더보기

MainContainer에서 요구사항이 추가되어 현재 유저가 보고 있는 창이 크기가
1100px 이상이라면 NaviSide가 렌더링 되도록 현재 창의 크기를 확인하는
커스텀훅과 조건부 렌더 로직이 추가되었다.

그런데 해당 부분들을 NaviSdie 안에서 구현해야 겠다는 생각이 들었다.

그래서 NaviSdie 컴포넌트 안에 로직을 옮기고 layouChangeSizeCheck라는
함수로  현재 windowWidth 정보와 제한할 px 크기의를 전달하여
true 또는 false 값을 받아isNavisideRender값에 따른 NaviSdie를 렌더링 해준다.

 

이렇게 해서 요구사항이 추가된 상태에서 아직 정리되지 않은
아주 거친 상태에 컴포넌트가 완성되었다.
 NaviSdie 컴포넌트는 nickname 받아 조건부 렌더링에서 사용되며

wundowWidth는 isNavisideRender 값을 만들기 위해 사용된다.

routeBtnData라는 외부파일에 선언되어 있는 배열 데이터값을 돌며
UI를 보여준다.

 

많이 복잡하니 isNavisideRender 값에 따른 컴포넌트 렌더링 기능에만
집중해 보았다.

    *변경  후

 

이제 NaviSdie 컴포넌트는 현재 사용자의 창 with값에 따른 NaviSideContainer
컴포넌트를 렌더링 할지 말지만 수행하면 된다.

 

* 변경 후

NaviSdieContainer는 nickname과 routeBtnData에
의존하여 UI로직을 담당하고 있다.

routeBtnData의 데이터를 알 수 없게끔 하고자 NaviSdieItem에 props 전달 시
{... props}와 같이 작성하였다. authCheckRenderItem 함수를 만들어서
nickname과 withAuth데이터를 받아 boolean값을 return 하도록 처리하였다.
(autCheckRenderItem 함수에서 withAuth라는 데이터를 받는
부분이 마음에 들지 않지만 그 이상 방법이 떠오르지 않았다..)

하지만 여기서 문제점을 느낀 건 조건부 렌더링에 관한 상황 많아서
계산 처리하는 상황이 계속해서 발생한다.
이 부분은  좀 더 윗 단계에서 다른 방안을 고안하거나
다른 방법이 있지 않을까? 한다..
지금 내 머리로는 여기까지가 한계인 것 같다..

 

이제 NaviSdieItem 컴포넌트는 props만을 받아서 UI로직만을 담당하게 된다.