98 컴포넌트 단위에 관하여..(vueJS 관점)

source: categories/study/vue-experiance/vue-experiance_9-98.md

98 컴포넌트 단위에 관하여..(vueJS 관점)

뷰 관련 개발을 하면서 항상 고민하던 부분이 있다.
바로 컴포넌트 단위에 관해서다.

컴포넌트 단위는 어느정도까지여야될까?
테이블이 있다면 셀 단위까지 컴포넌트로 나눠야될지, 아니면 하나의 테이블을 한 컴포넌트로 나눠야될지 항상 고민이었다.

이러한 고민을 하게된 이유는 크게 세가지가 있다.

  1. 랜더링 속도
  2. css overriding
  3. 컴포넌트간 통신

첫째, 랜더링 속도

컴포넌트를 잘개 쪼개면 쪼갤수록 페이지의 랜더링 속도는 크게 올라간다.
잘개 쪼갠 컴포넌트만 다시 재랜더링하면 되니까, 다른 컴포넌트에 영향을 주는일이 없기 때문이다.
성능면으로만 봤을 때 컴포넌트는 잘개 쪼개면 쪼갤수록 당연 이득이 크다.
하지만, 두번째, 세번째 이유 때문에 컴포넌트를 잘개 쪼갬에 있어 망설여지게된다.

두번째 CSS 오버라이딩

컴포넌트의 단위를 잘개 쪼갤 수록 파일이 많아지고 그만큼 비슷한 용도로 사용되는 컴포넌트 또한 많아진다.
그리고 클래스명에 대한 고민이 깊어진다.
파일이 많아지면 많아질수록 의도를 안했음에도 불구하고 CSS 오버라이딩 문제가 생기고 이는 나중에 어디서부터 어떻게 다시 스타일을 고쳐야될지 큰 혼란을 가져다준다.
즉, 작업자의 편의를 위해 컴포넌트 단위는 적당하게 쪼개는 것이 훨씬 좋다.

세번째, 컴포넌트간 통신

컴포넌트 단위를 잘개 쪼개면 쪼갤수록 컴포넌트간 통신을 하는데 있어서도 복잡해진다.
상위 컴포넌트에서 하위 컴포넌트로 props를 전달해줘야하고 뷰 같은 경우는 하위 컴포넌트에서 상위 컴포넌트로 event emit을 통해 통신을 해야된다.
컴포넌트의 깊이가 100이라면 100번 props를 전달하고 100번 event emit을 해야된다는 뜻이다.
이는 개발할 때 아주 불편하며 여간 복잡한 일이 아닐 수 없다.

위와 같은 이유 때문에 개발할 때 항상 컴포넌트 단위 관련해 고민이 클 수밖에 없었다.

하지만, 이는 내 프론트앤드 개발 지식이 부족함으로인해 발생된 고민이었을 뿐이었다.
다음과 같은 사실을 알게된 후로 위와 같은 고민을 말끔히 해소할 수 있었다.

  1. CSS Module
  2. this.$root, this.$parent (이건 vue에만 있는 개념으로 알고있다. 리액트나 앵귤러에선 없다고 알고 있다.)

첫번째, CSS Module

이는 뷰, 리액트 상관없이 CSS 클래스를 보다 더 쉽게 지을 수 있게 해주는 라이브러리이다.
이와 비슷하게 리액트에선 Styled Component, 뷰에선 scoped style이란 것이 존재한다.
(styled component 와 scoped style은 같은 거)
이는 각 요소에 ‘data-랜덤문자열' 속성을 주어 해당 속성이 해당 컴포넌트에만 (클래스명이 겹쳐도) 적용되도록 해주는 라이브러리이다.

CSS Module도 거의 비슷한 역할을 하는 라이브러리이다.
data-랜덤문자열 속성을 부여하는 scoped style, styled component와 다른점은 css module은 클래스명에 랜덤 문자열을 부여한다는 점이다.

예를 들어 txt라는 클래스를 줬다면 css module을 통해 해당 클래스명은 ‘컴포넌트명_txt_랜덤문자열'로 바뀌게된다.

styled component(=scoped style)과 css module이 어떤면에서 어떻게 다른지는 아직 찾아보진 않았지만, 일단 느낌상 더 깔끔한 방법은 css module인 것 같다.

여튼 styled component(=scoped style)이든 css module든 이 두개중 하나만 사용하더라도 두번째 고민은 해결할 수 있다.

그럼 남은 것은 세번째 고민이다. 컴포넌트간 데이터 통신.

컴포넌트의 깊이가 깊어질수록 상위 컴포넌트와의 통신이 너무 복잡해진다는 고민이다.
뷰에선 이를 위해 this.$root.$data, this.$parent.$data를 제공한다.
this.$root는 해당 컴포넌트의 제일 상위 컴포넌트를 가리키고
this.$parent는 한단계 위 컴포넌트를 가리킨다.
즉, 이를 활용해 data를 수단계거쳐서 props로 내리거나 event를 수단계 거쳐서 emit하지 않아도 컴포넌트 통신을 구현할 수 있다.

이러한 방법을 이제서야 알게되다니 너무 아쉽다.

여기서 궁금한건 리액트는 과연 이 세번째 고민을 어떤식으로 해결할 수 있을지이다.
리액트에선 뷰처럼 this.$root나 this.$parent를 제공하는게 없다고 알고 있기 때문이다.
다음번엔 리액트를 조사해봐야겠다.

추가 알게된 점

  1. this.$root, this.$parent를 사용하면 프로젝트 규모가 커질수록 코드파악이 복잡해진다.
  2. 이럴때를 대비해 vuex와 같은 중앙집중식 데이터 관리를 사용하는 것이 좋다.
  3. 하지만 EventBus를 활용해 이벤트를 중앙 집중식 관리하는 방식으로 해도 괜찮다.