`프론트엔드 개발자` 개형이의 벽돌집

프론트엔드 학습 일지 Day 1 - 리액트 Best Practices, Design Pattern 본문

학습일지

프론트엔드 학습 일지 Day 1 - 리액트 Best Practices, Design Pattern

개형이 2023. 8. 1. 21:16

프론트엔드 지식에 대한 인사이트를 해보고 기록해보는 학습을 시작해보기로 했다.

죽은 내 블로그를 살려보고자...!! 🤡

그럼 지금부터 스탓뚜! (시작이 반이니까 반 했다 ㅎㅎ)

 

 

 

리액트의 Best Practices 살펴보기


 

1. 파일 유형별로 디렉토리를 구성하지 말고 기능별로 그룹화 하라.

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

위 이미지처럼 구성 요소의 js 파일이나 css 파일을 더 간단하게 찾을 수 있다고 한다.

이 것에 대해선 전적으로 동의한다. css를 잔뜩 묶어 놓은 프로젝트를 만난 적이 있는데 찾는게 꽤나 힘들었다!

 

 

2. 컴포넌트를 작은 단위로 쪼개어 유지, 이해, 테스트가 쉽게 만들자.

 

예를 들어 UserProfile 컴포넌트가 작업 중에 커진다고 생각했을 때 ProfilePicture.tsx, UserName.tsx 과 같은 식으로 쪼개어 합쳐서 사용하는 것이 더 간단하며 재사용에 용이하다는 것이다.

올해 초에 클린코드 책에서 본 내용과 상당히 비슷하다. 책에서는 늘 작은 단위로 쪼갤 것을 강조했다. 이는 분명 코드 이해도를 높이는데 도움이 된다고 생각한다.

비슷한 맥락으로 관심사를 분리하는 습관을 가져서 더 깔끔한 React 앱을 만들기 위해 노력해야 겠다.

 

 

3. 명명 규칙

 

- 컴포넌트: PascalCase

- 변수, 함수: camelCase

- 상수: UPPERCASE_SNAKE_CASE

 

위처럼 코드를 작성했었기 때문에 빠르게 넘어가겠다.

 

 

4. Page, Presentational 컴포넌트

 

페이지 컴포넌트 (컨테이너 컴포넌트)는 API로 부터 데이터 fetching 작업 수행, state나 로직 관리, presentational 컴포넌트로 props를 전달하는 코드가 들어가는 것이 좋다고 하고,

Presentational 컴포넌트는 UI와 데이터를 보여주는 코드가 들어가는 것이 좋다. 

컴포넌트 유형에 따라 책임을 분리하여 재사용성도 높이고 모듈화도 시킬 수 있는 좋은 방법론인 것 같다.

 

 

5. Do not DRY

 

우선 난 DRY라는 표현을 처음 알았다. DRY - Don’t Repeat Yourself ! 결국 반복하지 말라는 뜻이다. 반복될 것 같은 코드에는 Array를 매핑하여 반복을 줄일 수 있고 동적으로 렌더링도 가능해진다.

 

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

리액트를 사용하는 프론트엔드 개발자라면 위 코드에 대한 이해도는 높을 것 같다. 반복된 코드를 줄여보자!

 

6. Custom Hook 커스텀 훅 사용을 해보자

 

커스텀 훅은 컴포넌트에서 사용되는 stateful logic을 가져와서 분리한 것으로, 여러 컴포넌트에서 재사용할 수 있다.

예제로는 useForm을 만드는 것이 있다. (양식 처리 입력에 사용)

 

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

모든 Form의 입력 값을 저장하는 state를 가진 Hook을 만들고 이를 재사용하는 코드이다.

모양이 약간 react-hook-form 과 비슷하다. 여튼 위처럼 Hook을 정의하고 재사용한다면 더 깔끔한 코드를 만들 수 있을 것 같다.

Form과 같은 양식 입력 뿐만 아니라 API 호출과 같은 상황에도 Custom Hook을 사용하는 것이 일반적이다. 여러 리액트 프로젝트를 보면 이러한 코드가 일반적이라는 것도 알 수 있을 것 같다.

 

 

7. Services 서비스

 

리액트 앱을 더 모듈화시키고 싶다면 서비스 Services를 사용하는 것이 좋다. 서비스는 무엇인가 하면... API 호출과 같은 비지니스 로직을 다루는 함수나 클래스를 말한다. 이렇게 만든 Services를 커스텀 훅이나 컴포넌트에 넣을 수 있고 이는 로직을 분리하고 조직화하기에 용이하다.

 

코드의 예시도 있다.

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

위 코드 예제에서 보면 CRUD 동작을 수행하는 RESTful API를 더 간단하게 사용하기 위해 서비스로 구성한 것이다.

 

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

서비스로 만들어두면 위처럼 간단하게 원하는 비지니스 로직을 호출할 수 있다.

위처럼 비지니스 로직 뿐만 아니라, 우리가 흔히 사용하는 유틸 함수 (날짜 포맷팅, 계산 함수)도 서비스라고 볼 수 있다. 서비스로 로직을 분리해서 효율적으로 재사용하는 습관을 가지자!

 

🛑 자, 그렇다면 커스텀 훅과 서비스를 어떤 차이점을 두고 사용하면 좋을까?

1) 커스텀 훅 Custom Hook: State, Context를 사용하는 코드가 들어있을 때는 커스텀 훅이 적절하다.

2) 서비스 Services: State, Context에는 의존되지 않는 재사용 가능한 로직은 서비스가 적절하다.

 

 

 

 

리액트의 Design Patterns 살펴보기


 

1. Higher-Order Components (HOCs)

 

리액트에서 계정에 관한 작업을 할 때 HOCs를 사용하는 프로젝트를 본 적이 있다. 이번 기회에 HOCs에 대해 확실히 알아둬야지.

그렇다면 HOCs는 무엇일까?

 

HOCs는 컴포넌트를 가지고 더 많은 기능을 넣어 새로운 컴포넌트로 만드는 것이다. 덧붙인다고 보면 될까?

위에서 언급했던 것처럼 권한의 관한 예제가 있었다. 유저가 권한이 있는지 없는지 체크하고 있으면 아래 감싸져 있는 컴포넌트를 보여줄 것이고 권한이 없다면 로그인 페이지로 튕겨내는 것이다. 코드로 한번 보자.

 

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

위 예제는 입력으로 컴포넌트를 넣고 로그인 유무에 따라 페이지 이동이 달라지게 하는 HOCs이다.

 

권한 외에도 HOCs를 사용한 예제는 많다. 흔히 우리가 최적화에 사용하는 Memo도 위와 같이 사용했던 걸 기억할 수 있다. 그렇다, Memo도 HOCs인 것이였다. 

HOCs 역시 컴포넌트 로직을 재사용하고 앱 전체에 사용할 수 있기 때문에 매우 좋은 디자인 패턴이라고 볼 수 있다.

 

 

2. Render Props

 

Props는 하위 컴포넌트로 전달하는 것일텐데, Render Props는 무슨 역할일까?

Render Props는 하위 컴포넌트로 함수를 전달한다. 경우에 따라서는 HOCs보다 더 유연할 수 있다고 하는데...

"기본적으로 호출될 때 React Element (JSX)를 return한다. 자식 컴포넌트는 필요한 기능을 제공하는 것에 집중하도록 하고 부모 컴포넌트는 content가 어떻게 보일 지를 결정한다."

이게 Render Props Design Pattern의 기본 규칙인 것 같다. 예제를 안보면 와닿지 않을 것 같으니 예제를 봐야 겠다.

 

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

위 예제를 살펴보면, ProductFetcher 컴포넌트에 render props를 보내줘 호출한 데이터를 보내준 render props에 맞게 렌더링할 것이다. 우선 네이밍만 보면 ProductFetcher 컴포넌트에는 product 데이터를 fetch하는 기능이 들어가있을 것이고 이를 props로 받은 render 컴포넌트에 넣어줄 것으로 예상이 된다...!

render props를 보낼 때 각각의 render component (위 예제에서는 ProductGrid, ProductList)가 보내지는데 여기에는 어떻게 render를 할지에 대한 JSX가 명시되었을 것으로 예상된다!

 

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

역시 언급한대로 ProductFetcher 컴포넌트는 Loading 여부를 체크하는 기능, 데이터를 fetch하는 기능이 포함되었다. 그리고 호출한 product를 props로 받은 render 함수에 넣어 보여주게 될텐데! 그러면 render를 어떻게 하는지 명시해둔 컴포넌트를 살펴보자.

 

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

위 예제에서 products가 ProductFetcher에서 호출한 데이터일 것이다. 데이터를 화면에 어떻게 표시할 것인지 내용이 들어가있다.

 

 

따라서!! 데이터를 fetch하는 공통된 기능을 가진 컴포넌트를 하나 정해두고, 동일한 데이터로 다른 render를 해야할 때 각각의 render props를 다르게 보내주어 같은 데이터라도 다른 UI를 보여줄 수 있게 되었다. 사실 난 처음 보는 Design Pattern이였다.

 

Render Props를 사용해서 얻을 수 있는 이점으로는 역시나 비슷한 이점일 수는 있겠지만,,

관심사 분리, 재사용성 용이, 유연성 확보가 될 것이다.

 

 

3. Compound Components Pattern

 

합성 컴포넌트?! 이는 무엇일까.

우선 요약하자면 관련 컴포넌트를 함께 그룹화하고 상위 컴포넌트에서 동작을 제어할 수 있도록 하는 패턴 이다.

 

예시를 한번 살펴보자

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

위 예제에서는 Tabs 안에서 React.cloneElement를 사용하여 Tab, TabContent 컴포넌트의 동작을 제어하기 위한 추가 props를 제공하고 있다. 말그대로 관심사의 연관도가 높은 컴포넌트들을 한 곳에 두고 부모에서 합쳐서 사용하고 있는 모습이다. 사용법은 아래 예제 코드에 있다.

 

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

위처럼 합성 컴포넌트를 사용하면 별도의 컴포넌트를 묶는 방법 대신 상위 컴포넌트에서 함께 그룹화하여 보다 직관적이게 표현될 수 있다. 기존에 패턴 없이 Tabs, Tab, TabContent를 다 따로 컴포넌트로 두는 것 보다 해당 디자인 패턴처럼 한 곳에 묶어 사용하면 개발자가 사용 용도를 파악하기에 더 쉽게 느껴질 거라는 생각이 든다.

 

 

4. React.memo

 

최적화에 관심을 두고 리액트 앱을 개발해봤다면 React.memo는 한번쯤 접해봤을 것이다.

React.memo는 props가 변경될 때만 컴포넌트가 리렌더링되도록 해준다. 이는 성능 향상에 크게 도움이 될 수 있는데, 예제를 한번 보자.

 

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

위 예제 컴포넌트에서 props의 name이 변경될 때만 UserProfileName 컴포넌트가 리렌더링될 것이기 때문에 불필요한 업데이트를 줄일 수 있다.

 

 

5. React.lazy

 

React.lazy도 최적화하면 꼭 나오는 멋진 녀석이다. lazy는 게을러서(?) 컴포넌트를 분할하여 필요할 때만 필요한 친구를 로드할 수 있게 해준다. 나머지 친구들은 게으르게 두는 것이다. 이 기법은 분명 메모리를 덜 사용하게 하기 때문에 일반적으로 훨씬 더 효율적일 것이다.

 

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

사용은 위와 같이 할 수 있다. 위처럼 사용하면 필요한 경우에만 ProductDetails 컴포넌트를 로드하게 된다. 이는 Suspence 컴포넌트와 함께 사용이 가능한데,

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

Suspense 컴포넌트가 ProductDetails의 lazy loading을 기다리면서 fallback으로 로딩중 표시를 보여준다. 이건 정말 효율적일 수 밖에 없다고 생각한다!

 

 

6. Context, React.createContext 사용으로 상태 관리를 해보자

 

리액트하면 상태 관리를 빼놓을 수 없다. 상태 관리를 하면 장점 중 하나로 Props Drilling을 방지해준다는 것이다. Props Drilling은 props를 하위 컴포넌트로 넘겨주고 또 하위로 넘겨주고.. 가 반복되며 지저분한 형태로 넘겨주는 것을 의미하는데, 상태관리를 하면 계층에 관계 없이 컴포넌트간 state를 공유하기 때문에 쉽게 관리가 된다!

 

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

 

 

보통 Material UI, Styled Component와 같은걸 사용할 때 위와 같이 테마를 지정하는 것을 볼 수 있다.

위 코드 안 children에 있는 모든 컴포넌트는 useTheme을 편하게 사용할 수 있고 상태를 업데이트할 수도 있다. 모든 컴포넌트에서 테마 업데이트가 가능하게 된 것이다!

 

 

6. 목록 렌더링의 필수인 키 Key

 

리액트 개발자는 목록을 map으로 렌더링할 때 key가 중복된다 혹은 없다라고 콘솔에 에러가 뜨는 것을 확인해봤을 것이다.

꼭 목록 렌더링할 때 key를 고유하게 부여하는 습관을 가지자! 이는 DOM을 엉망으로 만들고 불필요한 DOM를 방지해준다고 한다.

 

 

 

 

 

이로써 DAY1 학습을 마쳤다. 좋은 학습 자료를 작성해준

https://medium.com/@obrm770/best-practices-and-design-patterns-in-react-js-for-high-quality-applications-6b203be747fb

위 저자에게 감사드린다.

 

아는 부분도 있고 모르는 부분도 있었다. 무지성으로 개발함에도 습관적으로 적용했던 내용도 있었고 처음보는 내용도 있었다.

리액트 고급 개발자가 되기 위해 리액트의 Design Pattern 파악이 필요할 것 같다는 인식이 생겨 귀중한 시간이었다.

Comments