Seung Hun

이번에 Frontend Fundamentals 모의고사에 참여했습니다. 왜 참여했고, 무엇을 얻었는지 간단하게 후기를 공유해 보겠습니다.

왜 참여했나요?

1. 토스 과제에서는 무슨 기준으로 코드를 판단하는지 보고 싶었습니다.

저는 이전에 토스 채용 과제에 응시했다가 떨어진 경험이 있습니다. 그때를 돌이켜보면 보여주고 싶은 것은 정말 많은데, 시간이 턱없이 부족하다고 느꼈습니다. 시간에 쫓겨 불필요한 곳에 시간을 너무 많이 투자했고, 결국 가장 중요한 기능 구현도 미흡한 상태로 제출했습니다.

그래서 토스에서 과제를 평가할 때, 이런 제한된 시간 안에서 어떤 기준으로 코드를 평가하고 무엇을 신경 써야 하는지가 궁금했습니다. 그리고 토스에서 말하는 좋은 코드, 추상화, 재사용성, 확장성을 어떻게 코드로 녹여내야 하는지도 알고 싶었습니다.

2. 내가 생각하는 좋은 코드에 대해 검증해 보고 싶었습니다.

저는 지금까지 3곳의 회사에서 3년이 조금 넘는 시간 동안 현업에서 일하고 있습니다. 제가 지금까지 개발하면서 배웠던 것들, 몸소 느꼈던 것들에 대해 저 나름의 기준이 생겼습니다. 이 기준이 괜찮은 기준인지 확인하고 검증해 보고 싶었습니다.

3. 그냥… 재밌어 보였습니다.

이분은 제가 좋아하는 개발자인 토스의 프론트엔드 헤드 박서진 님인데요. 서진 님께서 링크드인에 글을 올리시면 한 번씩 관심을 갖고 읽고 있습니다. 얼마 전 이런 글을 읽고, 오랜만에 채용 과제를 해보면 재밌겠다는 생각이 들어 바로 지원해 봤습니다.

무엇을 얻었나요?

이 과정을 통해 제가 지금까지 과제를 대하며 잘못 생각했던 점, 컴포넌트의 props 설계에 대해 배울 수 있었습니다.

지금까지 과제를 응시하면서 잘못 생각했던 점

지금까지 제가 응시했던 과제들은 시간이 늘 촉박했습니다. 그리고 그 짧은 시간 안에서 저는 최대한 많은 것을 보여주고 싶은 욕심이 있었습니다. 현업에서도, 과제에서도 가장 중요한 것은 **“요구사항 달성”**입니다. 하지만 저는 제가 보여주고 싶은 것들에 매몰되어, 정작 요구사항도 다 구현하지 못한 채 다른 디테일을 먼저 챙기려다 보니 시간이 많이 부족했다고 생각합니다. 과제 해설을 보면서 요구사항을 충족하기 위한 설계를 먼저 하고, 이를 바탕으로 어떻게 좋은 코드를 작성할지 설명하는 내용이 저에게는 무척 인상 깊었습니다.

앞으로는 과제에 응시할 때 요구사항 달성을 먼저 하고, 남은 시간에(만약 시간이 남는다면?) 디테일을 챙기는 식으로 전략을 수정해야겠습니다.

컴포넌트의 props 설계

컴포넌트에서 ui를 그릴때 조건에 따라 그리는 것이 달라지는 부분이 있습니다.

예를 들자면

function ProductList ({ valueA, valueB }: {valueA: number, valueB: number}) { const data = useProductData() const filteredData = useMemo(() => { return data.filter(item => item.a > valueA && item.b < valueB); }, [data, valueA, valueB]); return ( ... ) }

이런식으로 valueA, valueB의 조건으로 data를 수정합니다. 하지만 이런식으로 컴포넌트의 props을 설계하면 사용하는 곳에서

<ProductList valueA={1000000} valueB={2000000} />

해당 props를 보고 이게 어떤 기능인지 알기 어렵습니다. 그래서 이런식으로 props을 구성하지말고

function ProductList ({ filter }: {filter: (item: Product) => boolean}) { const data = useProductData() const filteredData = useMemo(() => { return data.filter(filter); }, [data, filter]); return ( ... ) } //사용하는 곳 <ProductList filter={(item) => item.a > 1000000 && item.b < 2000000} />

이렇게 설계를 하면 좀 더 사용하는 곳에서도 어떤 기능인지 파악하기도 쉽고 다른 조건들도 받을 수 있으니 재사용성이 높아지겠죠?

추가적으로 고민 했던 점

저는 특정 페이지에서만 쓰이는 기능을 따로 커스텀 훅으로 분리하는 것을 별로 선호하지 않습니다.

type Tab = 'TabA' | 'TabB' function Page () { const [tab, setTab] = useState<Tab>('TabA') const handleTabChange = (tab: Tab) => { setTab(tab) } return ( ... ) }

예를 들어 이런 식으로, 그냥 사용하는 곳에서 직접 구현해 쓰는 것을 선호합니다. 하지만 이번 모의고사 해설에서는

type Tab = 'TabA' | 'TabB'; const useTab = () => { const [tab, setTab] = useState<Tab>('TabA'); const handleTabChange = (tab: Tab) => { setTab(tab); }; return [tab, handleTabChange] as const; }; function Page () {

이런 식으로 사용하는 컴포넌트 바로 위에(파일 분리 없이) 커스텀 훅을 하나 만들어서 사용하였습니다.

그 이유는 다음과 같았습니다.

Tab의 동작을 추상화하여 내부 구현은 숨기고누구나 알 만한 인터페이스를 제공함으로써 사용하는 곳에서 내부 구현을 알 필요 없이 사용할 수 있다는 점입니다.

React를 사용하면서 “상태”로 사용할 수 있는 것들은 여러 개가 있는데요. Local 상태(useState), 전역 상태, Query String, Local Storage 등이 있습니다. 이런 식으로 커스텀 훅으로 분리하면 상태의 종류가 바뀔 때 수정해야 하는 코드의 범위가 줄어든다는 장점이 있다고 들었습니다.

특정 페이지에서만 쓰이는 훅은 따로 빼지 않고, 나중에 재사용 필요성이 느껴질 때 추상화하여 분리하는 방법을 택했던 저에게는 나름 신선한 내용이었습니다. 그래서 제가 원래 택했던 방법과 이 방법을 비교해 보고자 합니다.

내부 동작의 추상화와 그 이면

커스텀 훅으로 로직을 분리하면, 사용하는 쪽에서는 내부의 복잡한 동작을 신경 쓸 필요 없이 인터페이스만 보고 동작을 유추하여 개발할 수 있습니다. 이는 메인 로직의 코드 양을 줄여주고, '구현을 숨긴다'는 추상화의 장점이 잘 드러나는 부분이라고 생각합니다. 하지만 반대로 단점도 분명 존재합니다. 개발 중 문제가 발생해 코드를 하나씩 타고 올라가야 할 때, 추상화된 훅 내부까지 확인해야 한다면 시선이 위아래로 분산됩니다. 코드를 위에서 아래로 자연스럽게 읽어 내려가는 흐름(Reading Flow)이 끊기고, 확인해야 할 컨텍스트가 늘어나는 불편함이 생깁니다. 또한, 커스텀 훅을 여러 개 만드는 것은 일종의 '레이어(Layer)'를 쌓는 것과 같습니다. 이러한 레이어 구조가 유효하려면 하위 레이어에 대한 '신뢰'가 기본적으로 전제되어야 합니다. 예를 들어, 우리는 React의 useState를 사용할 때 "setState를 실행하면 리렌더링이 제대로 발생할까?"라고 의심하며 라이브러리 소스 코드를 열어보지 않습니다. 이미 검증된 레이어이기 때문입니다. 하지만 우리가 직접 만든 커스텀 훅은 다릅니다. 잘 설계되면 문제가 없지만, 이 레이어에 결함이 발생하면 디버깅 복잡도가 급격히 올라갑니다. 게다가 특정 훅을 수정할 때, 그 훅을 의존하고 있는 다른 곳들에서 발생할 사이드 이펙트(Side Effect)까지 꼼꼼히 고려해야 합니다. 결론적으로 구현 세부 사항을 숨기고 인터페이스를 통해 기능을 제공하는 추상화의 철학에는 깊이 공감합니다. 다만, 앞서 언급한 디버깅의 복잡성과 유지보수 비용을 고려했을 때, 단순히 코드를 분리하는 것을 넘어 '신뢰할 수 있는 레이어를 잘 만드는 것'이 무엇보다 중요하다고 느꼈습니다.

마지막으로 개발자로서 성장할 수 있는 기회를 마련해 준 토스 팀과, 해설 방송을 통해 귀한 인사이트를 공유해 주신 한재엽 님, 오종택 님께 깊은 감사를 드립니다. 아울러 이번 프로그램에 열정적으로 임하시는 다른 참가자분들의 모습을 보며, 저 또한 긍정적인 자극과 동기부여를 얻을 수 있어 무척 뜻깊은 시간이었습니다.