저번에 이어, React
와 Redux
의 lifescycle
을 공부하고자 한다.
이번에는 React Hooks
에 대해 알고자하는데, 앞서 Class Component
의 lifecycle
을 알아본 봐 있고, 이는 크게 hooks
에서도 다르지 않다.
우선 정의는 다음과 같다
Hook
이란 함수형Component
에서React state
와Lifecycle
을연동(Hook)
하는 것
결국 Hook
에서의 Lifecycle
이란 말 보다, Lifecycle
과 state
를 이어주는 다리 라고 생각하는 것이 더 직관적인 것 같다.
그렇다면 가장 중요한 useEffect
에 대해 알아보자.
useEffect
는ComponentDidMount
,ComponentDidUpdate
,ComponentWillUnmount
같은 목적 제공, 하나의API
로 통합한 것
즉, state
와 Lifecycle
을 이어주는 Hook
중 useEffect
는, 다양한 Lifecycle
을 하나로 묶어줌으로써, state
와 연동하여 사용하게끔 도와준다.
이로써 각 단계에 따라 정해주듯 주체가 Lifecycle
이 아닌, state
가 되도록 하는 것이다.
가장 어려웠던 부분 중 하나 인 부분이었다. 객체 지향 언어와 절차 지향 언어가 아닌 함수형 프로그래밍에서, 프로그래밍 하는 방식을 이해하고 100퍼센트 활용하기에는 React
3개월차인 나에게는 아직 버겁기도 하였다.
이는 결국 state
의 남발, useEffect
의 남용, 꼬여버린 스파게티코드와 함수 그리고 critical bug를 남기기도 하였다.
과거에 존재하였던 Class Component
, 왜 Hook
과 함께 Functional Component
로 넘어왔고, Class Component
의 유지보수는 틀리지 않지만, 새로운 코드들은 Functional Component
로 적극 권유할까?
이유는 다음과 같다.
Class components
have downsides
- Confusing (both human and machines, especially at binding and thiskeyword)
- Lifecycle methods, logic spread over different lifecycle methods
- Hard to test compared to functional components
Compiled code size and compile time
Class component
는 this
와 같이 기계와 사람 모두에게 혼동을 주고, Lifecycle
메소드와 로직은 각기 다른 Lifecycle
에 퍼지며, 테스트에 용이하지 않다. 한마디로 훨씬 복잡하다 라고 할 수 있을 것이다.
우선 거시적인 관점에서, 코드를 짜기전, 대 선배님들이 정리해주는 올바른 길, 디자인 패턴을 보고자한다.
UI가 어떻게 보여지는데에 대한 컴포넌트들은 존재한다. 이들은 어플리케이션과 어떠한 의존성도 없고, 데이터를 보여주는 것에만 사용된다.
const ItemsList = (props) => {
return (
<ul>
{props.items.map((item) => (
<li key={item.id}>
<a href={item.url}>{item.name}</a>
</li>
))}
</ul>
);
};
앞선
presentational components
와 다르게,Container components
는 더욱 작동 방식에 책임을 갖는다. 일반적으로Life Cycle
과 구성 요소를 포함하는 클래스 구성 요소이다.
class TvShowsContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
shows: [],
loading: false,
error: ""
};
}
componentDidMount() {
this.setState({ loading: true, error: "" });
fetch("https://api.tvmaze.com/schedule/web?date=2020-05-29")
.then((res) => res.json())
.then((data) => this.setState({ loading: false, shows: data }))
.catch((error) =>
this.setState({ loading: false, error: error.message || error })
);
}
render() {
const { loading, error, shows } = this.state;
return (
<div>
<h1> Tv Shows </h1>
{loading && <p>Loading...</p>}
{!loading && shows && <ItemsList items={shows} />}
{!loading && error && <p>{error}</p>}
</div>
);
}
}
하지만 이의 제작자는 더 이상 다음 패턴을 호옹하지 않는다... 따라서 의도만 이해하고 넘어가면 될 것 같다.
prop drilling
이라는 현상은 리엑트 개발자들이 가장 많이 맞서는 문제중 하나이다. 데이터(prop
)가 원하는 컴포넌트까지 오기 전까지 전달하는것에서 발생한다. 이는Central Store
에 저장함으로 (eg. Redux Store) 원하는 어떠한 컴포넌트에도 전달함으로써 해결 할 수 있다.
실제로 마지막으로 진행했던 프로젝트는 다음과 같은 사항들을 염두해, css
는 styled-component
로 data
는 React-Redux toolkit
으로 저장한 바 있다. 이는 더욱 프로젝트를 관리하는데 윤택하고 편리하게 한다.
메인 기능 (Route
)별로 central store
를 만들고 관리하였다. 또한 css
는 styled component
로 각 컴포넌트 당 할당해주었다.
데이터
는 공통적으로 쓰이는 곳이 많아, 재사용성이 높고, 활용도가 높아 가장 큰 단위에서 위치 할 수 있도록 하였고, 디자인에 쓰이는 css
의 경우는 styled component
의 특성 상, 각 태그명에 쓰였는데, 최소한의 단위로 쪼개야 가독성과 locating 하는 것이 매우 쉬워졌다.
위와 같이 한개의 컴포넌트의 css
정보를 담고 있는 styles.js
는 100줄이 넘어갔고, 이는 평이한 수준 이었다. 비슷, 동일한 css
가 반복적으로 쓰인다면 (eg. Button
) 최상단에서 정의하여 주는것이 용이 할 수 있으나, 디자인과가 아닌 이상, 지속적인 수정이 이루어져야 했고 (당장만 봐도 px로 위치와 크기를 정해주 었다... 리팩토링 시 반응형을 고려하여 모두 수정해줘야 할 것 같다....) 이는 개발자가 이해 할 수 있는 최소 단위로 연결 해주는 것이 훨 씬 용이하다 생각이 들었다.
다음 글에서는 최신 5가지 패턴들을 소개하고, 또한 그 패턴 중에서 가장 호평을 받는 패턴을 소개 및 적용해보고자 한다.