좋은 컴포넌트를 설계하기 위해 고려해야 할 6가지(feat. chatGPT)

jellyjw·2023년 4월 26일
122
post-thumbnail

공부 겸 개인프로젝트를 진행하면서, 간단한 컴포넌트를 만들더라도 좋은 컴포넌트를 만들고 싶었다.

혼자 진행하는 프로젝트는 특히나 다른 개발자나 멘토님의 코드리뷰도 받을 수 없기 때문에 스스로 공부하고 찾아서 만들어야했다.

그래서 좋은 컴포넌트를 설계하려면 어떤 사항들을 고려해서 만들어야 하는지 핵심적인 내용만 정리해 보려고 한다.


1. 단일 책임 원칙(Single Responsibility Principle, SRP)

객체 지향 프로그래밍(OOP)에서 SRP란 모든 클래스는 하나의 책임만 가지며, 클래스는 그 책임을 완전히 캡슐화 해야 함을 일컫는다.

단일 책임 원칙은 하나의 컴포넌트는 한 가지 일을 하는게 이상적이라는 원칙입니다. 하나의 컴포넌트가 커지게 된다면 이는 보다 작은 하위 컴포넌트로 분리되어야 합니다.
출처 : React 공식문서 - UI를 컴포넌트 계층 구조로 나누기

SRP를 고려하지 않고 컴포넌트를 개발하게 된다면 다음과 같은 문제가 발생한다.

  • 컴포넌트의 크기카 커져 가독성이 떨어지고, 유지보수가 어려워짐
  • 의존성이 생기게 됨으로, 확장성이 떨어짐
  • side effect가 발생할 가능성 높아짐

예를 들어보자. 우리는 컴포넌트 설계시 UI와 로직을 분리해야 된다는 것을 알고 있지만, 지켜지지 않는 경우가 있다. 이는 SRP를 위반하는것! (물론 SRP가 항상 정답은 아니다.)

Good


UserList 컴포넌트는 데이터를 props 로 전달받아 단순히 UI를 렌더링하는 역할만 수행한다. 이는 SRP를 잘 지킨 예시이다.

Bad

UserList 컴포넌트에서 UI 렌더링과 데이터를 받아오는 로직이 함께 이루어지고 있다. 부끄럽지만 지금까지 이런식의 컴포넌트를 더 많이 만들어 본 것 같다.

SRP를 잘 지켜서 개발하면, 코드의 응집성을 높이고 가독성이 향상되며 유지보수가 쉬워지는 등의 장점이 있다.


2. 재사용 가능성(Reusability)

컴포넌트를 사용하는 가장 큰 이유중 하나는 재사용을 하기 위해서일 것이다. 유사한 기능을 가진 컴포넌트들을 추상화하여 공통으로 사용할 수 있는 컴포넌트를 만들 경우, 코드의 중복을 피하고 유지보수를 간편하게 만들 수 있다.

Good

Button 컴포넌트를 만든다고 가정해보자. SRP 예시와 비슷하게 UI를 렌더링하는 역할만 수행하고 있고, labelonClickprops 로 전달받아 사용하므로 재사용이 가능하다.

Bad


위 예시에서 Page 컴포넌트는 페이지의 제목, 내용, 저장 버튼을 모두 포함하고 있다.
이 경우 UI와 페이지 이동 로직이 함께 존재하므로 다른 페이지에서 해당 컴포넌트를 사용하기 어렵다는 단점이 있다.

컴포넌트의 목적을 잊지 말고, 재사용이 가능하도록 컴포넌트나 모듈은 최소 단위로 분리하자.

UI와 로직을 분리할 경우

  • 재사용성, 유지보수성이 향상된다.
  • UI와 로직을 독립적으로 변경할 수 있다.

3. 성능 최적화(Performance Optimization)

컴포넌트의 성능은 UX에 중요한 영향을 미친다. React에서 성능을 최적화 할 수 있는 방법은 다양하지만, 상황에 맞게 사용하는것이 중요하다.

  • 불필요한 렌더링을 방지하고, 리렌더링 최소화시키기
  • 메모이제이션 활용하여 중복되는 계산을 피하기
  • 최적화된 이벤트 핸들러나 상태관리 라이브러리 사용 등

예를 들어 리스트의 어떤 요소를 삭제할 때 보통 이런식으로 구현하는것을 자주 볼 수 있다.

그런데 얼마 전, 여러 요소를 삭제하는 것이 아닌 하나씩만 삭제하는 경우, 굳이 array의 모든 요소를 순회하는 filter 보다 splice 를 사용하는게 성능상 더 낫다는 이야기를 듣게 되었다.

추가
댓글로 달아주신 것처럼 state 를 업데이트 해야 할 경우는 불변성을 위해 filter 를 사용하는게 원본 배열을 직접 수정하는 splice 보다 적합할 것 같습니다. 의견 감사합니다!

삭제 로직을 구현할땐 주로 filter 를 사용했었지만, 성능 최적화를 고려한다면 단순한 기능을 구현하더라도 이런 부분들을 고려해서 개발해야 겠다고 느꼈던 순간이었다.


4. 테스트 가능성(Testability)

테스트를 고려한 컴포넌트 설계는 코드의 품질을 높이고 버그를 줄이는데 도움이 된다.

  • 로직과 UI를 분리
  • 컴포넌트의 상태나 프로퍼티를 조작할 수 있는 인터페이스를 제공

이를 통해 단위 테스트, 통합 테스트 등을 수행하여 컴포넌트의 동작을 검증할 수 있다.

  • props 를 이용한 데이터 전달로, 독립성을 높이고 테스트시에도 간편하게 데이터 주입 가능
  • 콜백함수를 props로 전달하여 테스트 시 모의(mock) 콜백함수 전달로 컴포넌트 동작을 테스트 가능
  • 렌더링 결과를 검증 가능한 형태로 반환
    • 테스트 라이브러리를 이용할 시, 컴포넌트 반환값이 JsxHTML 형태로 반환될 경우 테스트가 용이하다.
  • 외부 종속성을 주입 가능한 형태로 설계할 것

5. 가독성과 유지보수성

컴포넌트 개발에만 해당되는것은 아니지만, 가독성과 유지보수성을 고려하여 개발하는것은 기본이다.
코드는 개발자들의 문서이기도 하다. 내 코드를 다른 개발자들이 이해하고 유지보수할 수 있도록 노력해야 한다.

변수와 함수명을 직관적으로 짓고, 컨벤션을 지켜야 하며 필요에 따라 주석을 활용할 수도 있다.

Good

Bad

같은 결과를 도출해내는 코드이지만 가독성이 확연히 차이나는것을 볼 수 있다.
나만 이해할 수 있는 코드는 절대 좋은 코드가 아니라는것을 명심하자!


6. 디버깅 및 에러 핸들링

우리는 개발하면서 예상치 못한 부분에서 정말 다양한 에러를 만나게 된다.
컴포넌트 내부에서 발생하는 에러를 적절하게 처리하고, 에러 메세지를 유저에게 명확하게 전달하도록 설계하는것이 좋다.

예를 들어 로그인 컴포넌트를 구현한다고 했을 때,

이런식으로 아이디가 입력되지 않았을 경우
"아이디는 필수 입력 사항입니다." 라는 명확한 메세지를 띄워주는 것과,

로그인 버튼 클릭시 로그인 정보가 잘못되었습니다. 라고 에러메세지를 띄워주는 경우 어느 케이스가 더 명확하게 유저에게 전달될까? 당연히 전자일 것이다.

다양한 유효성 검사가 실행되는 회원가입시에는 특히 더 어떤 정보의 입력값이 잘못되었는지 명확하게 표시해주는게 중요하다.


정리하다보니 좋은 컴포넌트 설계의 핵심은 재사용성, 유지보수 를 고려한 개발이라는걸 다시한번 느꼈다.
위 6가지 사항들을 고려하여 좋은 컴포넌트를 설계해보자!

글에 잘못된 내용이 있다면 댓글로 남겨주시면 감사하겠습니다. :)


Reference

https://www.stevy.dev/react-design-guide/
React 공식문서 - UI를 컴포넌트 계층 구조로 나누기
위키백과 : 단일 책임 원칙

profile
남는건 기록뿐👩🏻‍💻

18개의 댓글

comment-user-thumbnail
2023년 4월 27일

👍👍

1개의 답글
comment-user-thumbnail
2023년 4월 27일

참고하고 갑니다!!^^

1개의 답글
comment-user-thumbnail
2023년 4월 27일

멋쪄용!!

1개의 답글
comment-user-thumbnail
2023년 4월 27일

filter와 splice의 퍼포먼스 차이는 어떻게 나는건가요? 자세하게 알려주실수있나요?

1개의 답글
comment-user-thumbnail
2023년 4월 27일

너무 좋은 글 이네여 잘 읽고갑니다!

1개의 답글
comment-user-thumbnail
2023년 4월 27일

아주 멋진 글입니다~~!

1개의 답글
comment-user-thumbnail
2023년 5월 5일
답글 달기
comment-user-thumbnail
2023년 5월 5일

잘 읽고 갑니당

1개의 답글