1. 디자인 시스템의 컴포넌트는 어디까지 Primitves 해야할까?
디자인 시스템 컴포넌트의 어려움은 광범위하게 참조된다는 것에서 기인하는 것 같다는 생각이 많이 듭니다.
너무나도 광범위하게 사용되다보니 작은 css 수정은 물론 컴포넌트의 인터페이스를 교체하는것이 굉장히 큰 이펙트로 영향을 끼치게 된다는 점이 공포입니다.
저는 주로 사용성을 위해 디자인 시스템 컴포넌트에 특정 기능이나 css를 기본적으로 박아두는 행위 (w값을 기본값으로 준다거나.. padding에 대한 기본값이 존재한다거나) 같은 짓을 했다가 나중에 특정 사용처에서는 기본값이 존재하면 오히려 안되는 상황에서 진퇴양난에 빠지게되는 경우가 많았던 것 같습니다.
그런데 그렇다고해서 Primitves한 컴포넌트의 스타일을 극단적으로 Primitves하게 가져가자니 사용성이 망가지고, 컴포넌트 사용 방법이 파편화되는 문제가 발생합니다. 어디에선 어떤 식으로 쓰고 저기에선 저떤 식으로 쓰고 이런 상황이 발생하더라구요
2. 내가 하고있는 건 애초에 디자인 시스템이라고 부를 수도 없지않나?
어느새 대 디자인 시스템의 시대가 된 것 같습니다. 누구나 디자인 시스템을 만들었다고 하며 (사실 저도 그랬긴했습니다.) 심지어는 시간이 극단적으로 한정된 해커톤에서조차 "우리는 디자인 시스템을 만들었어요"라고 하는 팀들도 여럿 보일 정도이고 디자인 시스템이 필요하지 않을만한 팀들도 약속이라도 한듯이 전부 디자인 시스템을 만듭니다.
디자인 시스템의 원래 의미가 어떻고 너희가 하고있는건 그냥 스타일 가이드, 패턴 가이드에 불과할 뿐이라고 하기에는 언어의 사회성이란게 존재하다보니 뭐 그냥 대충 알아들으면 되지 않을까 싶긴합니다만 가끔 마음의 여유가 없을때는 대충 넘어가고 싶지 않아지는 그런 생각이 들기도하는데요 아무튼 그렇습니다...
3. 클린아키텍처 , 디자인 패턴 적용할 필요가 있을까?
개발자라면 흥분하지 않을 수 없는 클린 아키텍처, 클린 코드, 디자인 패턴은 필연적으로 학습곡선이 존재합니다. 그런데 일반적으로 클린아키텍처가 국제룰처럼 자리잡힌 앱개발, 백엔드개발과 달리 웹 프론트엔드는 그런 추세에서 약간 벗어나있는 듯 합니다.
물론 웹 프론트엔드와 클린아키텍처를 접목하려는 시도 자체는 많이 이루어지고있고 실제로 잘 도입해서 쓰고있는 팀들도 있겠지만.. 주변에서 쉽게 찾아보기는 어려운 것 같습니다.
요새 드는 생각은 이런 현상이 단순히 웹 프론트엔드 개발자들이 좋은 코드에 관심이 없고, 좋은 아키텍처에 관심이 없어서 발생하는 일이라기보다는 웹 프론트엔드 개발의 특수성때문이 아닐까 싶습니다. 그렇다기에는 프론트엔드 개발자들도 좋은 코드에 관심이 많은 것 같거든요
인정하든말든 웹프론트엔드 === 리액트 가 된 것은 이미 거스를 수 없는 추세인 지금 어떤 디자인 패턴을 적용하든 "그래서 그거 리액트랑 같이 쓸 때도 괜찮음?" 이란 질문을 피해갈 수가 없는 것 같아요
요부분에 있어서는 대표적으로 백엔드 개발에서 주로 이용되는 레포지토리, 서비스 계층, 클래스와 상속을 이용하는 패턴이 생각납니다 물론 유용한 패턴이지만 실제로 적용해보면 여러가지 애로사항이 발생합니다.
1. 내가 저 패턴이 익숙하더라도 내 동료들도 똑같이 익숙하리란 보장은 없다. (동료와 같은 관점과 같은 코드로 소통이 안된다.)
2. 구조를 이해하기 위한 학습 곡선이 생겨난다. (견고한 구조를 위해 추상화가 입혀지는 만큼 팀원들이 그 추상화를 이해하기 전까진 코드 작성이 딜레이된다)
3. 그냥 작성한 코드에 비해 코드양이 많다.
결국에는 내 동료와 같은 시선으로 코드를 바라볼 수 있어야하는데 이를 위한 팀전체의 학습비용보다 도입의 이점이 더 큰가? 에 대해서는 대부분이 물음표가 띄워질 수 밖에 없는 것 같습니다.
4. 최대한 쉬운 구조로 쉬운 코드를 만드는 게 오히려 더 좋지않을까?
웹 프론트엔드 개발은 그 특성상 코드의 생명주기가 짧습니다. 3시간뒤에 없어질 코드를 작성할일도 많고 A,B 테스트 등에서는 패배한 코드는 어차피 버릴코드가 됩니다.
또 그와 동시에 요구사항의 변화도 빈번합니다. 기획의 변경, 디자인의 변경, 백엔드의 변경 등 서비스에서 발생할 수 있는 모든 변경사항을 직격으로 때려맞습니다.
또 이건 어느 포지션이나 같겠지만 항상 내가 작성한 코드만 수정한다는 법은 없습니다. 이러한 특성들 탓에 프론트엔드 개발에서 가장 크게 요구되는 미덕은 "쉬운 변경" , "쉬운 이해" , "쉬운 읽힘" 인 것 같습니다.
코드가 쉽게 읽혀야 쉽게 이해하고 쉽게 이해해야 빠르게 내가 원하는 변경을 할 수 있습니다. 또 쉽게 변경하기 위해서는 관심사가 잘 분리되어야 하는 것은 당연합니다.
그런 관점에서 프론트엔드 개발은 깔끔하게 관심사를 발라내고 누구나 쉽게 읽을 수 있는 코드를 쓰는 것이 중요해보입니다. 그런데 그렇다보니 나혼자 디자인 패턴을 사용해버리고 나혼자 클린아키텍처를 해버리면 팀내의 코드 통일성이 깨져버리게되는 문제가 발생합니다.
5. 아토믹 디자인, FSD , 다음은 뭘까?
앞에서 어찌보면 디자인 패턴 무용론에 가까워 보이는 소리를 많이 했지만 그럼에도 불구하고 웹 프론트엔드 진영도 웹 프론트엔드라는 특수성을 수용할 수 있는 디자인패턴들을 꽤 많이 제안했고 많은 팀들이 도입을 시도했던 것 같습니다.
대표적으로 비교적 과거에는 아토믹 디자인, 그리고 지금은 FSD가 그 중심에 있을 것 같은데요 아토믹 디자인은 컴포넌트 설계의 방법론을 다루고 FSD는 폴더구조를 다루지만 저는 둘 다 비슷한 문제점을 공유한다고 느껴집니다.
계층을 나누는 과정에서 애매함이 발생한다는 점이 가장 큰 문제점이라고 생각하는데 사실 이건 웹 프론트엔드 개발의 특성이 아닌가 싶습니다. 어떻게 계층을 나눠보려고해도 여기도 아니고 저기도 아니거나 아니면 여기도 될 거 같은데 저기도 될 거 같은 회색지대에 존재하는 코드들이 너무 많이 발생합니다.
이런 애매함까지 다 해결해줄 수 있는 패러다임이나 관점이 등장할 수 있을까요? 일단 제 머리로는 힘들긴합니다.
6. 백엔드의 변경이 프론트엔드에 큰 영향을 미치지 않았으면 좋겠는데 어떡하지?
프론트엔드는 사실상 백엔드 API를 Entity로 삼게됩니다. 서비스에서 가장 변하지 않아야하고 중심이 되어주어야하는 엔티티가 외부 데이터에 의존한다는 이 모순 , 이런 모순을 견뎌내기 위해 FSD는 entities 계층에서 api 통신 코드가 포함됩니다.
또 한편으로는 이 질문의 답 또한 간단합니다. 엔티티의 변화가 다른 계층에 영향을 끼치지 않길 원한다면 포트와 어댑터 패턴을 사용해주면 그만입니다. 엔티티를 한번 변환해주면 자연스럽게 내부 시스템은 외부 API가 바뀌든 말든 크게 변경되지 않습니다.
그러나 이것 역시 쉬운 코드의 문제가 발생해버립니다. 매핑계층을 두는 순간 "매핑을 위한 대충 긴 코드"가 발생하게되고 이런 패턴에대한 내성이 없는 개발자는 "그래서 이게 뭔데"라는 반응이 나옵니다.
요 문제를 고민하다가 지금은 화면을 그리는 UI 컴포넌트는 완전히 엔티티를 모르게 (멍청하게) 작성하고 엔티티와 UI 컴포넌트를 이어주는 어댑터 비슷한 함수를 만드는 형태로 코드를 작성하고 있는데요
복잡한 UI의 경우 멍청한 UI 컴포넌트에게 적절한 값들을 멍청한 형태로 전달해주기위해 어댑터의 구조를 설계해야하는 오버헤드가 발생하는 문제가 있긴하지만.. 나름 괜찮은 것 같습니다.
<대충멍청한컴포넌트
onClick={() => handleClick(엔티티)}
{...create대충멍청한컴포넌트를위한Props(엔티티)}
/>
const create대충멍청한컴포넌트를위한Props = () => {
return 대충멍청한컴포넌트의Props
}
대략 이런식으로 작성을 하고있는데 어떻게보면 VAC 패턴 비슷한 형태가 되는 것 같기도합니다.
프론트엔드는 백엔드의 값을 그대로 화면에 뿌려주기보다는 한번 사용자에게 보기 좋은 형태로 가공해주는 일을 많이 수행하는데 (날짜는 2024.01.12 형태로 해주세요 , type 필드의 값이 ZZANG 이라면 유저에겐 짱으로 보여주세요 같은)
그런 로직들의 위치가 매우 명확해지다보니 수정이 용이한 효과가 있었습니다. (유저에게 표시되는 내용이 바뀌어야할때는 함수를 수정, 유저에게 보이는 디자인이 바뀔때는 멍청컴포넌트를 수정)
7. 직관적인 상태 구조 vs 성능 최적화
성능을 고려하지 않아도 된다면 가장 이해하기 직관적인 상태구조는 그냥 상단에서 useState로 상태를 들고 컴포넌트들에게 적당히 상태를 뿌려주는 형태일 것 같아요 쉬운 것도 장점이고 테스트가 쉬운 건 덤이고.. 참 좋습니다
그런데 이런식으로 짜면 상태하나 바뀔때마다 렌더링은 와장창 일어나고 요구사항이 복잡해질수록 상태를 들고있는 쪽의 코드가 복잡해진다는 문제가 있습니다.
당장은 쉽게 짜되 언젠가 해결해야한다면 어떻게 해결해야하는가? 가 참 어려운 지점 중 하나인데 개인적으로는 zustand의 많은 점들이 마음에 안들긴 하지만.. 요 부분에 있어서 상태관리와 성능 최적화를 가장 쉬운 문법으로 해결해주는 라이브러리는 zustand인 것 같긴 합니다.
8. 프론트엔드 개발은 테스트가 지옥인게 맞다
프론트엔드 개발에서 테스트가 어려운 이유는 필연적으로 프론트엔드 개발이 다양한 부수효과와 외부 환경에 의존성이 있다는 점이 크게 기인한다고 생각을 합니다.
이 부분에서 가장 눈물이 주르륵 흐르곤하는 부분은 백엔드 API에 의존성이 컴포넌트에 대한 테스트를 작성할때인 것 같은데요 백엔드 api가 변경되는 순간 mockData도 전부 수정해주어야하기 때문에 수정포인트가 늘어나는것은 물론 이런 테스트의 어려움을 회피하기 위해서 계층을 두고 외부에서 값을 주입해주겠다는 식으로 컴포넌트를 설계하자니 렌더링 최적화, 보일러플레이트 등의 측면에서 문제가 발생하곤 합니다.
next.js를 쓴다면 그냥 테스트에 거지같은 부분이 너무 많아서 할말이 없을 정도입니다.
거기에 많은 부분이 개선되긴했지만 nodejs 환경에서 돌리는 테스트 환경의 한계로 인한 어려움도 상당히 존재하고 그렇다고 e2e 테스트를 붙이자니 다양한 의존성이 존재하는 실제 서비스에서 유의미한 e2e 테스트를 수행하기 위해서는 꽤 많은 고민과 노력이 들어가기도 하는 것 같아요
9. next.js는 더이상 쓰고싶지않아요
요즘들어서는 왜 해외 개발자들이 next.js를 싫어하는지 어느정도 이해가 되는 것 같습니다. 서버사이드 렌더링이 강제되다보니 (use client는 진짜 클라이언트에서만 렌더링한다는 의미가 아닙니다) 항상 서버사이드렌더링을 고려한 코드를 작성해주어야하는 점
위에서도 살짝 언급했지만 테스트가 거지같은 점 , 대놓고 느린 빌드, 대놓고 폐쇄적이어서 지원안되는 생태계가 많은 점 (Module Federation, vite.. 등등) SSR을 선택함에서 기인하는 다양한 엔지니어링 문제 (서버와 클라이언트간의 인증 세션공유, 하이드레이션 미스매치, 리액트쿼리와의 통합 등등..)을 모두 극복하면서까지 SSR 성능이 필요한 서비스가 몇이나 될까..?
또 인터랙션이 풍부한 모바일 서비스는 어차피 대부분의 컴포넌트가 client로 이루어져야하다보니 next.js는 진짜 ssg 발사대로 이용하게되는 경우도 많은 것 같고요..
딱 블로그 같은거 만들기에는 참 좋은데 그 외에는 득보다 실이 많은 경우가 더 잦은 것 같이 느껴지는 것 같습니다.
마치며
그냥 제 생각이었습니다 🥲
'frontend' 카테고리의 다른 글
pnpm monorepo에서 node-linker=hoisted 없이 react native 하기 (2) | 2024.12.24 |
---|---|
storybook, chromatic 대신 vercel로 배포하기 (1) | 2024.09.29 |
모노레포에서 tailwindcss를 쓰는 경우엔 설정을 어떻게 해야할까? (1) | 2024.06.20 |
모노레포에서 Internal Packages를 관리하는 3가지 방법 (0) | 2024.06.13 |
Feature-Sliced Design을 직접 사용하면서 느낀 장점과 단점 (4) | 2024.05.29 |