실행 컨텍스트는 소스 코드를 실행하는데 필요한 환경을 제공하고, 코드의 실행결과를 실제로 관리하는 영역입니다.
이 실행 컨텍스트를 통해서 스코프, 클로저등의 동작과 코드의 실행 순서 관리가 구현됩니다. 실행 컨텍스트는 소스코드의 타입에 따라서 실행 컨텍스트를 생성하는 과정과 컨텍스트 내부에서 관리하는 내용이 달라집니다.
또한, 실행 컨텍스트와 렉시컬 환경은 별도의 객체입니다. 실행 컨텍스트에서 렉시컬 환경을 참조하고는 있지만, 실행 컨텍스트가 종료된 후에도 해당 렉시컬 환경이 어딘가에서 참조되고 있다면 렉시컬 환경은 가비지 컬렉팅 대상에서 제외됩니다. 이러한 동작으로 인해 클로저라는 개념이 성립하게 됩니다.
클로저는 자신이 생성될 때의 환경을 기억하고, 그를 사용하는 함수다. (다시 제대로 정리한 번 해야 함.. 아직 명확하게 이해하고 있지 않음..)
일단 이럴때는 항상 AI에게 먼저 빠르게 정리를 요청한다. 그리고 나서 블로그, 공식문서를 토대로 내가 궁금한 걸 하나씩 해결해 나가면서 내 언어로 조금씩 정제하여 마무리를 한다.
고차 컴포넌트(Higher-Order Component, HOC)는 리액트에서 재사용성을 높이기 위해 사용되는 패턴으로,
함수로 컴포넌트를 감싸 새로운 컴포넌트를 반환하는 방식입니다.
이를 통해 로직의 재사용과 추상화를 도모하며, 공통 작업을 한 번에 처리하거나 상태 관리와 라이프사이클 메서드
등을 추가할 수 있습니다.
예를 들어, 인증 기능을 갖춘 컴포넌트를 만들거나, 데이터 로딩과 에러 처리를 공통으로 처리하는 래핑 컴포넌트를
생성할 수 있습니다. 이를 통해 코드 중복을 줄이고 유지 보수성을 향상시킬 수 있습니다.
이후에 이걸 하나씩 검증해 나간다는 생각으로 블로그와 공식문서를 토대로 하나씩 이해해 나갔다.
고차 컴포넌트는 컴포넌트 로직을 재사용하기 위해 사용되고 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수다.
고차 컴포넌트(HOC, Higher Order Component)는 컴포넌트 로직을 재사용하기 위한 React의 고급 기술입니다. 고차 컴포넌트(HOC)는 React API의 일부가 아니며, React의 구성적 특성에서 나오는 패턴입니다.
구체적으로, 고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수입니다.
컴포넌트는 props를 UI로 변환하는 반면에, 고차 컴포넌트는 컴포넌트를 새로운 컴포넌트로 변환합니다.
고차 컴포넌트(HOC)는 Redux의 [connect](https://react-redux.js.org/api/connect)
와 Relay의 [createFragmentContainer](https://relay.dev/docs/v10.1.3/fragment-container/#createfragmentcontainer)
와 같은 서드 파티 React 라이브러리에서 흔하게 볼 수 있습니다.
여기까지 보면 공통적인 건, 아무튼 새 컴포넌트를 반환한다는 것 같다 인 것 같았고, 그걸 인자로 기존에 있던 컴포넌트를 가져와서 새로운 컴포넌트로 변환하는 것이라고 이해했다.
const CommentListWithSubscription = withSubscription(
CommentList,
(DataSource) => DataSource.getComments()
);
const BlogPostWithSubscription = withSubscription(
BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id)
);
그리고 관심사의 분리를 위해서 고차 컴포넌트를 사용하는 예시가 나오는데, 위의 코드를 보면 withSubscription이라는 함수에서 첫 번째 인자로는 컴포넌트를 전달하고 두 번째 인자에 비슷한 로직을 처리할 수 있는 데이터를 전달한다. 이 함수는 컴포넌로 받은 매개변수를 래핑하고 전달된 data를 통해서 새로운 컴포넌트를 반환한다.
// 이 함수는 컴포넌트를 매개변수로 받고..
function withSubscription(WrappedComponent, selectData) {
// ...다른 컴포넌트를 반환하는데...
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
componentDidMount() {
// ... 구독을 담당하고...
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
}
render() {
// ... 래핑된 컴포넌트를 새로운 데이터로 렌더링 합니다!
// 컴포넌트에 추가로 props를 내려주는 것에 주목하세요.
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
그리고 이렇게 반환된 새로운 컴포넌트는 기존의 컴포넌트를 변경하지 않기 때문에 불변성을 유지할 수 있다 까지 이해했다. 하지만 굳이 왜 이렇게 해야하지? 관심사를 분리할거면 커스텀 훅을 사용하면 되지 않나라는 생각이 들었는데, 아마 고차 컴포넌트는 클래스 컴포넌트에서 자주 사용하던 개념이 아니였을까 하는 게 내 추측이다. 그렇다고 지금 함수 컴포넌트에서 고차 컴포넌트가 아예 쓰이지 않는지는 아직 조사해보지 않았다.
그 과정에서 발생했던 질문들(궁금했던 질문들..)
const CommentListWithSubscription = withSubscription(
CommentList,
(DataSource) => DataSource.getComments()
);
const BlogPostWithSubscription = withSubscription(
BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id)
);
withSubscription이라는 함수를 사용해서 CommentList와 BlogPost를 래핑하고 prop으로 추가적인 data를 받아서 그걸 새로운 컴포넌트로 반환한다. 그리고 반환하는 이 컴포넌트가 바로 고차컴포넌트고 이걸 CommentListWithSubscription, BlogPostWithSubscription 할당한다.
그러니까 한 마디로 래핑된 컴포넌트와 동일하게 반복되는 로직을 prop으로 받아서 그걸 함수로 정의하게 해서 반환한다. 이때 래핑된 컴포넌트와 prop은 기존 컴포넌트에 영향을 끼치지 않기 때문에 불변성을 유지하면서 새로운 컴포넌트를 반환한다. 이때 반환되는 컴포넌트가 고차컴포넌트다.
고차 컴포넌트(Higher-Order Component, HOC)는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수입니다.
React API의 일부가 아니며, React의 구성적 특성에서 나오는 패턴입니다.
즉, 컴포넌트를 인자로 받거나 반환하는 함수입니다.
고차 컴포넌트는 원본 컴포넌트를 컨테이너 컴포넌트로 포장(Wrapping)하여 조합(compose)합니다.
프로젝트 내에서 동일한 로직이 반복된다고 했을때 이 로직을 추상화해서 사용할 때 고차 컴포넌트를 사용하면
좋습니다.
새로운 함수에 인자로 래핑된 컴포넌트와 반복되는 로직을 prop으로 받아서 정의하고 그걸 토대로 새로운 컴포넌트
를 반환합니다. 이때 반환된 컴포넌트를 고차 컴포넌트라고 할 수 있습니다.
고차 컴포넌트는 prop으로 받은 데이터가 사용되는 이유 및 방법과 연관이 없으며 래핑된 컴포넌트는 데이터가
어디서부터 왔는지와 관련이 없습니다.
하지만 Hooks가 개발되고 나서부터는 중복되는 로직은 커스텀 hook을 통해서 외부에서 주입해서 사용할 수 있게
되었기 때문에 고차 컴포넌트를 사용할 필요성이 많이 줄어들었다고 생각합니다.