hmk run dev

지속가능한 컴포넌트 Effective Component 본문

Front-End

지속가능한 컴포넌트 Effective Component

hmk run dev 2024. 3. 10. 12:58

지속 가능한 프런트엔드 컴포넌트를 개발하는 과정에서 중요한 측면 중 하나는 기획이 변경되었을 때 코드 수정이 필요한 상황에 대한 대비책을 마련하는 것입니다. 

 

코드의 수정이 필요한 경우가 생겼을 때, 심지어 모든 코드가 필요 없어질 수도 있는 상황이 발생할 수 있습니다. 그러나 이는 제품이 변화하고 성장하는 과정에서 피할 수 없는 현상이며, 고객이 원하는 사용자 경험을 제공하기 위해는 빠르게 대응할 필요가 있습니다.

여기서 강조해야 할 중요한 이유 중 하나는 모든 사용자가 제품을 원활하게 사용하고 있는 경우에도 제품이 변경되어야 하는 이유가 있다는 점입니다. 

 

제품은 사용자의 니즈와 시장 동향에 부응하기 위해 지속적으로 발전하고 있으며, 기획이나 디자인이 언제든지 변할 수 있습니다. 이러한 유연성은 사용자가 원하는 것을 빠르게 찾고, 그에 맞게 제품을 개선하는 데 중요한 역할을 합니다.

 

고객의 니즈는 기회로 여겨져야 합니다.


 빠르게 변화하는 환경에서는 사용자의 니즈를 정확히 예측하기 어렵습니다. 따라서 변경을 예측하지 않고 유연하게 대응하는 것이 필요합니다. 이를 통해 제품은 사용자 경험을 향상하고, 고객들의 니즈에 빠르게 대응하여 가치를 지속적으로 창출할 수 있습니다.

 


변경을 예측하지 않고 대응하기

 

제품은 보통 아래의 흐름과 같이 만들어집니다.

작은 컴포넌트나 모듈이 합쳐지면서 기능이 만들어지고

기능이 만들어지면 "적당히" 분리해야 합니다.

 

이때 적당히의 기준이 명확해야 하는데요

 

 

아래의 Item이라는 컴포넌트는 어떤 컴포넌트일까요?

 

어떤 데이터를 다루는지

데이터를 어떻게 보여주는지 예상할 수 없습니다.

 

아래 코드를 변경해야 한다면 어떻게 해야 할까요?

const { filtered, value, index, fetchResponseType } = repoes;

const Results = (
	<ItemLayout id="fix_scroll">
		{filtered.map((repo: ItmeType, i: number) => (
        	<Item 
            	key={`${item_${i}}`}
                index={i}
                curIndex={index}
            	item={repo}
                onClickItem={onClickItem}
            />
            {currentTargetValue && (
            	<Item 
                    key={`${item_${filtered.length}}`}
                    index={filtered.length}
                    curIndex={index}
                    item={{
						id: `search_in_google`,
                        name: `github: ${currentTargetValue}`
                    }}
                    onClickItem={onClickItem}
                />
            )}	
        ))}
    </ItemLayout>    
)

 

 

변경에 유연하도록 컴포넌트 잘 만들기

아래 세 가지 기준점을 기반으로 컴포넌트를 잘 만드는 법에 대해 예시를 들어보겠습니다.

 

 

1. Headless 기반의 추상화하기

변하는 것 vs 상대적으로 변하지 않는 것

 

 

2. 한 가지 역할만 하기

또는 한 가지 역할만 하는 컴포넌트의 조합으로 구성하기

 

 

3. 도메인 분리하기

도메인을 포함하는 컴포넌트와 그렇지 않은 컴포넌트 분리하기

 

 

 

1. Headless UI 기반의 추상화하기

 

프런트엔드의 컴포넌트의 역할은 아래와 같습니다.

 

 

어떻게 보여줄지는 디자인에 의존합니다.

디자인에 의존하는 UI를 컴포넌트가 관리하는 '데이터'와 분리해 보면 어떨까요?

 

데이터와 디자인 나누기

 

1. 데이터 나누기

 

- 달력 데이터에 관련된 것들을 useCalandar 훅으로 나누기

- 디자인 부분은 따로 jsx 형태로 마크업

 

 

데이터를 관심사에서 제외하고

UI가 변경되더라도 데이터만 다루는 useCalandar 훅은 변경할 필요가 없어짐

 

 

2. 디자인 나누기

 

아래 코드를 보면 컴포넌트 내부에 여러 가지 로직이 들어가 있습니다.

데이터 부분을 추상화한 것처럼 상호작용하는 부분도 추상화해 봅시다.

 

상호작용하는 이벤트 핸들러들을 useLongPress라는 훅으로 분리

hooks로부터 반환하는 값을 longPressProps를 주입하는 형태로 변경

 

상호작용하는 부분의 관심사가 분리되고, 다른 컴포넌트에서도 longPress 동작을 정의할 때 재사용도 용이해짐

 

 

2. 한 가지 역할만 하기(Composition)

 

아래는 흔하게 볼 수 있는 Select 컴포넌트인데요

이 UI를 만들다 보면 자연스럽게 작은 컴포넌트로 구성해서 만들게 됩니다.

 

코드를 살펴보면 변경이 발생했을 때

변경에 유연하게 대처하기 어렵게 보이는데요

 

다른 곳에서 사용한다고 가정해 보면 어떨까요?

 

당장 label 디자인만 변경되더라도 재사용하기 어렵습니다.

 

 

개선해 보기

 

담당하고 있는 데이터와 역할을 기준으로 분리

 

1. 메뉴의 노출 여부를 관리하는 상태인 isOpen을 분리

2. isOpen의 상태를 바꾸기 위한 상호작용을 Dropdown.Trigger로 분리

3. 옵션영역을 Dropdown.menu로 구성

4. 메뉴를 구성하는 각각 Item을 분리

 

 

 

재구성한 코드는 다음과 같습니다.

 

- 변경에 유연

Select 컴포넌트와 trigger로 전달한 InputButton 컴포넌트는 서로의 존재에 대해 알지 못한다.

서로의 변경이 서로에게 영향을 끼치지 않게 됨

 

합성가능한 컴포넌트는 유지보수 및 재사용의 용이성을 증가시켜 줍니다.

 

 

 

좀 더 복잡한 경우를 볼까요?

 

1. 선택하기 버튼을 클릭하면 모달이 열립니다.

2. 모달의 안의 체크박스를 여러 가지를 선택이 가능함

3. 체크 후 적용하기 버튼을 누르면 선택한 값들이 버튼에 노출됩니다.

 

 

UI만 봐서는 꽤나 복잡한 컴포넌트라는 생각이 드는데요,

이 컴포넌트가 관리하고 있는 데이터를 기준으로 바라보면 그렇게 복잡한 컴포넌트는 아닐 수 있습니다.

 

 

컴포넌트의 기능을 나눠본다면

 

1. 옵션을 제공하고

2. 선택한 값을 보여즘

 

 

 

Trigger 부분만 수정하고 싶으면 분리된 Trigger만 수정하면 됩니다.

 

 

3. 도메인 분리하기

컴포넌트를 주입받은 것처럼 데이터도 주입받으면 어떨까요?

언제 데이터를 주입받고, 언제 스스로 가져와야 할까요?

 

 

특정 도메인 맥락을 제거하고 일반적인 인터페이스로 분리하기

 

- 컴포넌트 인터페이스는 일반적일수록 이해하기 좋다.

- 컴포넌트의 이름과 props 네이밍은 일반적인 컨벤션으로 작성하자

 

프론트엔드 개발자라면 Select가 어떻게 동작하는지 알고 있고,

아래처럼 도메인 로직을 걷어내면 사용하는 입장에서 동작을 쉽게 예측할 수 있게 된다.

 

즉, 비즈니스 로직은 스스로 처리하되 

UI 로직만 위임하는 방식으로 볼 수 있습니다.

 

컴포넌트를 설계하는 순서

 

1. 인터페이스를 먼저 고민하기

구현해야하는 기능이 이미 만들어져 있다고 가정하고 그것을 사용하듯이 작성해보자

 

- 의도가 무엇인가?

- 이 컴포넌트의 기능은 무엇인가?

- 어떻게 표현되어야 하는가?

 

 

 

2. 컴포넌트를 나누는 이유 생각해보기

 

- 컴포넌트로 분리하면 실제로 복잡도를 낮추는가?

- 컴포넌트로 분리하면 재사용 가능한 컴포넌트인가?

 

 


Reference

 

https://www.youtube.com/watch?v=fR8tsJ2r7Eg

 

Comments