React 구조는 어떻게 잡는 것이 좋을까

기운찬곰·2021년 11월 4일
0

TIL

목록 보기
4/10

React 구조는 어떻게 잡는 것이 좋을까?

음... 이번에 새로 하게 될 파일럿 프로젝트에서 React 첫 시작을 어떻게 가져가는 것이 좋을지 곰곰히 생각해봤다. 사실 뭐가 맞고 틀리다는 정답은 없겠지만 나름 좋은 구조가 있다면 따라해보고 싶었기 때문이다.

전역 상태 관리 라이브러리에 따라 달라질거 같다

전역 상태 관리 라이브러리 종류는 선택지가 여러가지가 있는 편이다. 그 중에서 역시 리덕스가 아직 대세인거 같고, 최근에는 리코일, 조타이 등이 그 뒤를 바짝 뒤쫓고 있는거 같다. 또한 간단하게만 사용할 거라면 Context API도 좋은 선택지가 될 것이다.

어쨌거나 선택지를 고를때 가장 중요한 점은 프로젝트 규모에 따라서도 물론 중요하지만 무엇보다도 내가 편한게 가장 좋지 않을까 생각한다. 그런점에서 리코일도 한번 써보니까 편했고 리덕스 툴킷도 나쁘지 않았다.

Presentational & Container 패턴?

리액트를 다루는 기술을 보면 해당 Presentational & Container 패턴에 대해서 소개되어있다(요즘 개정이 되었나 모르겠네). 어쨌거나 처음 이 패턴을 소개한 사람이 현재는 이 패턴을 사용하지 말라고 언급하고 있다.

해당 패턴이 나타난 이유에 대해서 한번 살펴보자. 리액트는 이전에는 Hook이라는게 존재하지 않았다. 그랬기 때문에 컴포넌트 내에서 상태 로직과 UI 로직을 좀 분리시키고자 하는 시도가 있었가 그 결과로 등장한 패턴이 Presentational & Container 패턴이다.

Hooks 패턴이라고 해야하나?

하지만 현재는 Hook이 탄생하여 로직과 표현의 분리가 가능해졌기에 이 문제는 해결이 가능해졌고 따라서 더이상 해당 패턴을 사용할 필요는 없다. 아래는 예시이다. 이런식으로 hooks라는 폴더 아래다가 상태를 처리하는 로직을 분리시켜놓을 수 있다.

import { useEffect } from 'react'
import { useRecoilState } from 'recoil'
import { initialAmountState } from '../atoms/labSettingState'
import useFormattedNumber from './useFormattedNumber'

export default function useInitialAmount() {
  const [initialAmount, setInitialAmount] = useRecoilState(initialAmountState)
  const [value, onChange, setValue] = useFormattedNumber(initialAmount)

  useEffect(() => {
    setInitialAmount(parseInt(value.replace(/[^\d]+/g, ''), 10))
  }, [value])

  useEffect(() => {
    setValue(initialAmount.toLocaleString())
  }, [initialAmount])

  return [value, onChange] as const
}

// 예시 : https://github.com/velopert/velofolio/blob/main/packages/webapp/src/hooks/useInitialAmount.ts

요즘에는 그래서 이전에는 containers / components 를 구분해서 컴포넌트들을 작성해 왔던 방식에서 벗어나 이를 따로 구분하지 않고 모두 components 라는 디렉터리에 저장하고 있으며 상태를 사용할 때는 Custom Hook을 만들어서 사용하고 있는 추세인듯합니다.

redux-toolkit과 관련해서...

나는 처음에 리덕스가 굉장히 어렵게 느껴졌다. 뭐 하나 하려면 step이 좀 여러 단계가 있었기 때문에, 그리고 생소했기 때문에 어려웠던거 같다. 근데 이제는 redux-tookit으로 인해 확실히 간단해진 편이라고 느낀다.

redux를 그냥 사용해봤다면 아래와 같은 트리 구조로 구분하는 방법을 많이 사용할 것이다. 내가 가장 초창기에 사용했던 프로젝트에서는 modules 안에 기능별로 액션타입과 리듀서, 초기상태를 종합한 파일을 개별로 만들었던거 같다.

.
├── api (API 모듈)
├── components (일반 켬포넌트)
├── constant (상수)
├── models? modules? (데이터 모델 변수, 타입) - 개별 액션타입? 리듀서?
├── pages (페이지 컴포넌트)
├── store  (Redux 관련 코드) - root 리듀서? 스토어? 
├── styles (스타일시트)
└── utils (유틸리티 모듈)

근데 redux-toolkit 개발팀에서 공개한 공식 템플릿에서는 독특한 방식의 폴더구조를 제안하고 있다. 그니까 features 라는 폴더 안에 기능별로 폴더를 만든 후 그 안에 React 컴포넌트와 스타일시트, Redux 구현 등 관련 소스를 모두 같은 곳에 위치시키는 방법이라고 보면 된다.

.
├── api
│   └── githubAPI.js
├── components
│   └── Heading.js
├── features
│   ├── counter
│   │   ├── Counter.css
│   │   ├── Counter.js
│   │   └── counterReducer.js
│   ├── github
│   │   ├── RepoDetail.js
│   │   └── repoDetailSlice.js
│   └── todos
│       ├── Todos.js
│       └── todosSlice.js

뭔가 공용컴포넌트나 글로벌 스타일, 유틸리티 성격인 것들은 밖으로 모듈로 빼고, 나머지 실제 기능과 관련된 소스는 모두 features 폴더 안에 주제별로 모은다음 관리하는 형태인듯하다.

내가 이해한 바로는 features안에 컴포넌트는 초기에 fetch해서 데이터를 초기화해주고 그런 데이터를 각 컴포넌트 폴더에 있는 컴포넌트로 넘겨주는 형태? 그니까 위에서 말했던 Presentational & Container 패턴과 비슷할 수도 있겠다.

그렇지만 각 컴포넌트 폴더에 있는 컴포넌트에서도 필요한 부분이 있다면 dispatch할 수 있거나 useSelector로 조회할 수 있는 유동성도 있는 거 같다.

프로젝트 예시 참고 : https://github.com/eunha0ne/netflix-clone-coding/tree/develop/src

✍️ 어쨌거나 여러 프로젝트를 살펴보고 선택의 폭을 넓힐 수 있었으면 좋겠다. 그럼 ㅂㅇ~

참고 사이트

profile
배움을 좋아합니다. 새로운 것을 좋아합니다.

0개의 댓글