컴포넌트 컴파운드 패턴

몇 일 전 '디자인 시스템과 함수형 프로그래밍' 글에 saengmotmi님이 댓글을 달아주셨다. 그 글을 보고 '아... 이런 방법 말고 CCP(Compound Component Pattern)이 있구나' 생각이 들었다. 누구나 키오스크 프로젝트를 할 때 CCP 패턴이 어쩌구 저쩌구 했었는데 낫 놓고 기억자도 모른다는 속담이 떠올랐다. 뭐 어쨌든 깨달았으니까 회사 컴포넌트에 적용을 해보려고 했다. 마침 지금 들어간 프로젝트에서 만들고 있는 컴포넌트가 아주 자주 쓰이기 때문에 공용 컴포넌트로 사용할 수 있게 설계를 하면 좋겠다 싶었다.

하지만 간단하게 예제들 몇 개를 참고하면서 코딩을 하는데 내가 사용하고자 하는 모양이 잘 나오지 않았다. 내가 원하는 모양은 이런거다.

AdvanceSearchExample.tsx
1const handleMakeQueryParams = (e.React.FormEvent<HTMLFormElement>)=>{
2 const formData = new FormData(e.currentTarget);
3 const params = new Map()
4 for(const [params, value] of formData){
5 params.set(params, value)
6 }
7 // query params로 만드는 로직
8 return params
9}
10return <AdvanceSearch >
11 {/*이 안에는 AdvanceSearch Context에 있는 컴포넌트들이 순서와 값에 관계없이 들어올 수 있다.*/}
12 <AdvanceSearch.Form onSubmit={handleMakeQueryParams}>
13 <AdvanceSearch.Button>검색</AdvanceSearch.Button>
14 <AdvanceSearch.Select>
15 <AdvanceSearch.Option>하핫</AdvanceSearch.Option>
16 </AdvanceSearch.Select>
17 <AdvanceSearch.Group groupName="checkbox">
18 <AdvanceSearch.Checkbox name="check" id="check"/>
19 <AdvanceSearch.Checkbox name="radio" id="radio"/>
20 </AdvanceSearch.Group>
21 <AdvanceSearch.Radio />
22 </AdvanceSearch.Form>
23</AdvanceSearch>

하지만 생각처럼 AdvanceSearch Context에 무엇이 들어올지 모르는 상황에서 데이터 형을 어떻게 정해주어야할지 좀 난감했다. 특히 Select나 Checkbox는 여기서만 쓰이는 게 아니라 다른 곳에서도 쓰이기 때문에 부모가 가진 '어떤' 상태 값을 자녀가 받아서 '어떤' 상태를 가지고 UI를 변경해주어야한다.

생각처럼 잘 안되서 이틀간 머리 싸매고 끙끙 앓고 있는 중이다.(더 오랜 시간이 걸릴 것 같다.) 이번에 경험을 잘 쌓으면 어디가서 'CCP 그렇게 하는거 아닌데...'를 시전해 볼 수 도 있다 생각해서(뒷 감당은 알아서...) Chat GPT랑 씨름도 하고 Stack OverFlow도 열심히 뒤적거리고 Radix도 참조 해보고 기타 아티클도 읽어가면서 하고 있지만 잘 되지는 않는다.

재미있다는 말을 뱉어 버렸다면

날씨가 많이 쌀쌀해진 덕인지 예전 생각이 많이 난다. 요즘은 퇴근 후 집에가면서 예전에 네이버에서 진행했던 부스트 코스 썸머 챌린지 마지막날이 많이 생각난다. 그때 네이버 본사 건물에서 코스 완주한 사람들을 모아서 중소 기업 면접을 볼 수 있는 기회를 주었다. 나는 그때 프론트엔드가 뭔지 React가 뭔지 잘 모를 때였다. 왜냐하면 내가 들은 코스는 UI 과정 코스였고 나는 그때 디자인을 하고 싶었기 때문에 단순 html, css를 배워두면 뭔가 써먹을 때가 있겠지 싶어서 코스에 참여했기 때문이다. 그래서 면접을 본다라기 보다 그냥 개발자들에게 궁금한 것을 물어보러 다녔다. 그때 무슨 연구소(이름은 잘 기억 안난다.)라는 CTO에게 리엑트가 뭐에요. 리엑트 공부는 어떻게 해요. 프론트엔드 개발자로 취업하려면 어떻게 해야되요. 이런거 물어보다가 CTO가 나에게 되려 질문을 하셨다.

왜 개발자가 되려고 해요?

생각해본적 없는 질문이라 좀 고민해보다가 코스를 할 때 나름 즐겁게 했다고 생각해서 그냥 생각나는대로 "재미있어서요."라고 대답했었다. 그 말을 들은 CTO는 엄청난 썩쏘와 차가운 말투로 "아... 재.미.있.구.나...?"라고 했다. 그게 잊혀지지가 않는다.(아 물론 친절하게 잘 마무리해주셨다. 많이 만들어보라고...) 사실 그때는 그분이 왜 그렇게 반응했는지 이해할 수 없었다. 취업을 위해 개발 공부를 하면서도 가끔 그때가 생각 났다. 다시 대답 한다고 해도 '재미있어서요.'라는 대답 말고는 별다른 답이 떠오르지 않았다. 하지만 점점 난이도가 올라가고 듣도 보도 못한 개념들과 씨름하면서 '이거 언제 다 공부하지.'라는 생각과 '개발자 그냥 하지 말까.'하는 생각이 밀려오고 난 뒤부터 그분의 썩쏘와 차가운 말투가 다른 각도에서 느껴졌다. 하지만 이미 그땐 늦었다.

이걸 다 견뎌내고 개발자를 하고 있는거였구나.

지금 누군가 나에게 '프론트엔드 개발자'를 하고 싶다고 말하면 하지 말라고 할것이다. 일을 하면서 얻는 재미라던가 보람찬 순간이라던가 하는건 정말 악보위에 적혀있는 스타카토 한 음을 패달도 누르지 않고 연주하는 것처럼 지극히 짧다. '개발자는 문제를 해결하는 사람이다.'라는 말이 심심하지 않도록 정말 많은 문제가 쌓여있다. 프로젝트의 크기가 크면 클수록 코드 외의 신경 쓸것들이 넘쳐난다. 코드를 잘 작성하는 것은 기본기에 불과하다. 주말에는 습관처럼 공부를 해야한다. 자고 일어나면 새로운 기술이 생긴다는 말이 거짓이 아니라는 것을 증명이라도 하듯 구독한 뉴스 레터에서는 매주 새로운 것을 소개해준다. 그럼 배우면 끝인가? 아닌 것 같다. 마치 매일 매일이 타고 싶지 않은 롤러코스터를 타고 그냥 위에서 아래로 계속 떨어지는 기분이다.

그래서 쌀쌀해진 요즘 계속 생각나는 그분께(이름도 모르고 얼굴도 잘 기억 안나지만 감정으로 기억하고 있는 그분) 미안하다는 말을 전합니다.

재미있다는 말을 뱉어버렸다면 이제 후회해도 소용 없다. 전직이 아니라면 그냥 꾸준히 계속 하는 수밖에 없다. 아직 개발이 재미있다는 말을 하기 전이라면 도망가도 된다. 그건 부끄럽지만 도움이 된다. 도피처에서 잘 생각해본 뒤에 돌아오는 것이 좋다 생각한다. 그래도 개발자를 하겠다면 당신의 용기에 건배.(??? : 술이 식기 전에 돌아오겠습니다.)

그래도 동료가 있잖아(?)

야근 끝나고 동료한테 요즘 이런 생각하고 있다고 이야기를 하다가 글로 옮겨야겠다는 생각을 하게되었다. 첫 직장에 좋은 동료들를 만나서 그나마 버티고 있다. 회사는 왠지 모르게 '동료, 동료, 동료!'인 것 같다. 금융 치료가 답일 꺼라 생각했지만 실상 월급은 어차피 열받은 후라이펜 위 버터일 뿐이다. 좋은 동료가 없다면 내가 좋은 동료가 되는 것도 좋은 방법이다.

날이 점점 쌀쌀해지니까 별 생각이 다든다. 마지막으로 CCP를 하면서 참고하고 있는 자료들을 공유하면서 이만 글을 줄이려 한다. 비슷한 개념들이 반복되는데 React.Children((child)=>)가 반복된다. 하지만 Select 컴포넌트에서는 radix를 참고해서 Collection을 만들어서 React.Children이 가지고 있는 문제를 해결하려고 한다. 잘 이해가 안되면 CodeSandbox 예제들을 참고해서 간단하고 작은 것을 직접 만들어보고 감을 잡아 나갈 것을 추천한다.

컴파운드 컴포넌트 패턴을 공부하는 사람들을 위한 자료들

🇰🇷단단한 컴포넌트 부수기(feat.조합, IoC) 🇰🇷토스ㅣSLASH 22 - Effective Component 지속 가능한 성장과 컴포넌트 🇰🇷Select 컴포넌트 🇰🇷FECONF 2022 [B1] 디자인 시스템, 형태를 넘어서 🇺🇸React Hooks: Compound Components 📦위 아티클 CodeSandbox 예제 🇺🇸Inversion of Control 🇺🇸React Compound Components 📦위 아티클 CodeSandbox 예제 🇺🇸Compound Pattern 🇺🇸Command Pattern