인트로
나는 사실 클래스 컴포넌트를 써본 적도 배워본 적도 없다.
그렇지만 백엔드 입장에서 봤을 때 클래스를 사용하면 기존에 많이 나온 패턴들을 통해서 더 정형화된 코드, 정리된 코드를 만들 수 있을 텐데. 굳이 함수형을 쓰는 이유가 있을까?
라는 질문을 받았고 그에 대해서 궁금해서 찾아본 결과를 정리하고자 한다.
클래스 컴포넌트? 난 써본 적도 없다. 어떻게 생겼을까;;;
React에 hooks
가 생긴 16.8 버전 이후 함수형이 대세를 잡았다고 보여진다.
그렇기 때문에 17버전의 후반기, 지금의 18버전만 봐본 나는 써본 적이 없는... 방식이었다.
자, 본격적으로 들어가기 앞서 Class, Function 컴포넌트의 형태는 어떻게 생겼을까?
// 클래스형
import React, { Component } from 'react';
class ClassHello extends Component {
constructor(props) {
super(props);
// state 지정
this.state = { counter: 0 }
};
this.clickHandler = () => {
// state 변경
this.setState({
counter: this.state.counter - 1
});
};
render() {
const { props들 } = this.props;
return (<tag prop1={props들에서 꺼냄}>컴포넌트들</tag>);
};
};
export default ClassHello;
// 함수형
export default function FunctionHello() {
// state 등등 hooks
const [index, setIndex] = useState(0);
// state 관리할 함수
function handleClick() {
setIndex(index + 1);
}
return (<tag prop1={index}>컴포넌트들</tag>);
}
클래스 컴포넌트와 함수형 컴포넌트는 위에 예시처럼 작성한 코드처럼 작성한다.
이 때 state
와 handler
를 클래스 기반에서는 this
문법을 통해 사용하게 되고, 컴포넌트 기반의 경우 useState
훅과 함수
로 관리를 하게 된다.
랜더링을 해야하는 태그(들)은 함수형에서는 render()
함수를 통해 호출을 하고, 함수형에서는 return
을 통해 반환하는 방식을 사용한다.
클래스 기반 컴포넌트를 안써본 나와 비슷한 상황의 사람들에게 추가 설명을 하자면
- 컴포넌트가 처음 실행(mount)되었을 때
- (props에 의해서, state에 의해서) 업데이트(update)를 할 때
- 컴포넌트와 이벤트를 지울 때 (unmount)
이 밖에 여러 상황이 컴포넌트 라이프 사이클에는 존재한다.
그리고 이러한 라이프사이클의 하나하나를 컨트롤 가능하다는 점에서 클래스 컴포넌트는 매력이 있었고,
함수기반에선 라이프사이클 제어가 너무너무 힘들었다는 단점이 있었다.
useState 말고 let으로 상태를 정의하면 안될까?
아래 코드는 리액트 공식문서에서 나온 코드를 약간만 변형한 함수형 컴포넌트다.sculptureList
라는 데이터는 12개의 조각상에 대한 데이터를 갖고 있는 배열이다.
next라고 적혀있는 버튼을 클릭하면 index
가 1씩 올라가고 올라간 것을 확인하기 위해서 console.log(index)
로 확인한다.
이후엔 인덱스의 조각상 정보 sculpture
에 저장하고 조각상 정보를 return
의 태그에 끼워넣는다.
import { sculptureList } from './data.js';
export default function Gallery() {
let index = 0;
function handleClick() {
index += 1;
console.log(index);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
</>
);
}
자 이제 열심히 next 버튼을 눌러보자.
왜 페이지가 안바뀌지?
re-render을 해야한다는 트리거가 없기 때문이다.
열심히 index
를 늘렸으나 re-render를 하라고 LifeCycle 관련 함수를 불러올 수 없는 슬픈 상황이다.
이러한 단점을 함수형 컴포넌트는 16.8 버전 이후에 나온 hooks
로 해결을 한 것이다.
위에 예시 코드에서 useState
없이 함수형에서 상태관리를 할 수 있을까?
방법을 찾으면 있겠지만... 나는 저 상황이라면 클래스 컴포넌트 쓸 것 같다.
Hooks의 등장
함수형 컴포넌트의 해결사. (LifeCycle을 해결)ComponenDidMount
, componentWillUnmount
, componentDidUpdate
이라는 라이프 사이클 관리 함수를
함수형 컴포넌트에서hooks(useEffect, useState 등등)
& return
실행으로 대체
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
function handleClick() {
setIndex(index + 1);
console.log(index);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
</>
);
}
그런데 왜? 함수형 컴포넌트를 굳이 쓰고 싶었을까?
의문이 든다. 굳이. 왜. 함수형 컴포넌트를 hooks까지 만들면서 쓸까? 클래스 기반이 이미 잘 되있는데?
생산성 측면
- class 문법, this에 대한 이해 없이 개발이 가능하다.
- 직관적으로 이해할 수 있는 코드.
- 라이프사이클을 이해해야하는 클래스 컴포넌트는 러닝커브가 높고, 갈수록 코드의 양이 증가한다.
- props가 바뀌었으면 props 관련 함수를 불러서 사용해서 적용해야함.
- state가 바뀌면 관련 함수를 불러서 만들어놔야함.
(ex) ComponenDidMount, componentWillUnmount, componentDidUpdate
생산성 측면에서 봤을 때. 처음 JS로 개발을 하는 사람에게 class
, this
는 엄청 무시무시한 문법이다.
그런 의미에서 안 쓰고도 개발이 가능하다면 러닝커브를 확실히 줄여줄 수 있다.
그리고 useState
로 하는 상태관리, 컴포넌트 함수의 결과로 랜더링이 필요한 태그는 return
으로 알 수 있기에 직관적이다.
성능 측면
나름대로 찾아봤을 때 성능에 대해서는 이야기는 많고 확실하게 옳다라고 느껴지지 않는다.
'이러한 주장이 있다' 라고만 생각하면 된다.
1. Babel 변환 과정에서 코드의 양
실제 작성하는 코드의 양이 많아지는 상황에, 바벨로 브라우저가 읽을 수 있게 바꾸면 코드의 양은 당연히 늘어난다.
함수형 컴포넌트보다 클래스기반 컴포넌트를 변환했을 때 코드의 양이 더 많게 변환되고
JS 파일을 브라우저로 보내서 랜더링을 시켜야하는데, 용량이 적을수록 더 빨리 브라우저는 코드를 열어볼 수 있다.
2. 메모리 관리
함수형보다 클래스 컴포넌트가 메모리 사용량이 많다는 주장이 있다.
- 인스턴스 생성
클래스 컴포넌트: 클래스 컴포넌트는 인스턴스화될 때마다 새로운 인스턴스를 생성. 이 인스턴스는 메서드, 생명주기 메서드, 상태 등을 포함하며, 이로 인해 추가 메모리가 필요하다.
함수형 컴포넌트: 함수형 컴포넌트는 인스턴스화되지 않으며, 렌더링 시 단순히 함수가 호출. 따라서 인스턴스 관련 메모리 오버헤드가 없다. - 메서드 바인딩
클래스 컴포넌트: 클래스 컴포넌트에서는 메서드를 this에 바인딩해야 할 수 있다. 이러한 바인딩은 메모리를 추가로 사용할 수 있으며, 특히 많은 메서드와 인스턴스가 있는 경우 더욱 그렇다.
함수형 컴포넌트: 함수형 컴포넌트에서는 이러한 바인딩이 필요하지 않으므로, 관련 메모리 오버헤드가 없다. - 최적화 기법
함수형 컴포넌트:useMemo
와 같은 최적화 기법을 사용하면, 불필요한 계산과 렌더링을 줄일 수 있다. 이로 인해 메모리 사용량과 성능에 긍정적인 영향을 미칠 수 있다.
HOC (Higher Order Component, 고차 컴포넌트)
고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수.
state에 따라서 props를 올리고 내리는 과정이 우리가 이미 많이 써본 함수형과 다르게 부자연스럽다.
(나는 사실 함수형에서도 많이 사용하고 있다,,, 정답은 없지 않을까)
HOC를 활용한 클래스 기반 Counter App과 state를 활용한 Counter App
HOC를 활용한 counter App
import React, { Component } from 'react';
// HOC
function withCounter(WrappedComponent) {
return class extends Component {
state = {
count: 0
}
incrementCount = () => {
this.setState(prevState => {
return { count: prevState.count + 1 }
})
}
render() {
return <WrappedComponent
count={this.state.count}
incrementCount={this.incrementCount}
{...this.props}
/>
}
}
}
// Component
class Counter extends Component {
render() {
return <button onClick={this.props.incrementCount}>{this.props.count}</button>
}
}
export default withCounter(Counter);
useState를 활용한 counter App
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(prevCount => prevCount + 1);
}
return <button onClick={incrementCount}>{count}</button>
}
export default Counter;
위에 예시는 좀 억지같은데?
맞다. 글에 쓰려고 억지를 조금 부렸다. 극적으로 보여지는 예시인 것이다.
그러나 HOC Wrapper의 늪이 있다면? 상태관리를 해버리고 싶을 것이다.
상태 관리를 위해서
만약 유저에 대한 정보, 각종 인증, 인가에 대한 함수를 담는 wrapper를 만들어야한다면…
style에 대한 컴포넌트로 테마를 관리할 wrapper로 필요하다면…
만약 영어, 한국어 페이지에 대한 처리가 필요하다면…
치킨 만들기에 관련해서 관리하는 wrapper가 필요하다면…
스크롤에 관련한 wrapper가 필요하다면?!!
기타 등등이 필요하다면?
<UserAuth something="something">
<CSStheme mode="night">
<LanguageWrapper lang="kor">
<MakeChicken>
<스크롤관리컴포넌트>
<기타등등1>
<기타등등2>
아들 컴포넌트
</기타등등2>
</기타등등1>
</스크롤관리컴포넌트>
</MakeChicken>
</LanguageWrapper>
</CSStheme>
</UserAuth>
useState와 전역 상태관리를... 하고 싶지 않나요?
마무리
인트로에서 이야기했던 질문을 듣고 나만의 생각으로 먼저 노션으로 정리하고 이 내용에 대해서 알려드린 적이 있다.
그 내용을 블로그에 올리려고 더 정리를 하고자 했다. 추가된 부분도 있고, 남이 만들어놓은 것을 캡쳐한 부분은 제거했다.
개발자는 블로그를 하는 덕목이 있던데, 보통 노력은 아닌 것 같다.
'FrontEnd > React' 카테고리의 다른 글
개발자 도구 모르는 기능 써보기 (0) | 2024.02.25 |
---|
인트로
나는 사실 클래스 컴포넌트를 써본 적도 배워본 적도 없다.
그렇지만 백엔드 입장에서 봤을 때 클래스를 사용하면 기존에 많이 나온 패턴들을 통해서 더 정형화된 코드, 정리된 코드를 만들 수 있을 텐데. 굳이 함수형을 쓰는 이유가 있을까?
라는 질문을 받았고 그에 대해서 궁금해서 찾아본 결과를 정리하고자 한다.
클래스 컴포넌트? 난 써본 적도 없다. 어떻게 생겼을까;;;
React에 hooks
가 생긴 16.8 버전 이후 함수형이 대세를 잡았다고 보여진다.
그렇기 때문에 17버전의 후반기, 지금의 18버전만 봐본 나는 써본 적이 없는... 방식이었다.
자, 본격적으로 들어가기 앞서 Class, Function 컴포넌트의 형태는 어떻게 생겼을까?
// 클래스형
import React, { Component } from 'react';
class ClassHello extends Component {
constructor(props) {
super(props);
// state 지정
this.state = { counter: 0 }
};
this.clickHandler = () => {
// state 변경
this.setState({
counter: this.state.counter - 1
});
};
render() {
const { props들 } = this.props;
return (<tag prop1={props들에서 꺼냄}>컴포넌트들</tag>);
};
};
export default ClassHello;
// 함수형
export default function FunctionHello() {
// state 등등 hooks
const [index, setIndex] = useState(0);
// state 관리할 함수
function handleClick() {
setIndex(index + 1);
}
return (<tag prop1={index}>컴포넌트들</tag>);
}
클래스 컴포넌트와 함수형 컴포넌트는 위에 예시처럼 작성한 코드처럼 작성한다.
이 때 state
와 handler
를 클래스 기반에서는 this
문법을 통해 사용하게 되고, 컴포넌트 기반의 경우 useState
훅과 함수
로 관리를 하게 된다.
랜더링을 해야하는 태그(들)은 함수형에서는 render()
함수를 통해 호출을 하고, 함수형에서는 return
을 통해 반환하는 방식을 사용한다.
클래스 기반 컴포넌트를 안써본 나와 비슷한 상황의 사람들에게 추가 설명을 하자면
- 컴포넌트가 처음 실행(mount)되었을 때
- (props에 의해서, state에 의해서) 업데이트(update)를 할 때
- 컴포넌트와 이벤트를 지울 때 (unmount)
이 밖에 여러 상황이 컴포넌트 라이프 사이클에는 존재한다.
그리고 이러한 라이프사이클의 하나하나를 컨트롤 가능하다는 점에서 클래스 컴포넌트는 매력이 있었고,
함수기반에선 라이프사이클 제어가 너무너무 힘들었다는 단점이 있었다.
useState 말고 let으로 상태를 정의하면 안될까?
아래 코드는 리액트 공식문서에서 나온 코드를 약간만 변형한 함수형 컴포넌트다.sculptureList
라는 데이터는 12개의 조각상에 대한 데이터를 갖고 있는 배열이다.
next라고 적혀있는 버튼을 클릭하면 index
가 1씩 올라가고 올라간 것을 확인하기 위해서 console.log(index)
로 확인한다.
이후엔 인덱스의 조각상 정보 sculpture
에 저장하고 조각상 정보를 return
의 태그에 끼워넣는다.
import { sculptureList } from './data.js';
export default function Gallery() {
let index = 0;
function handleClick() {
index += 1;
console.log(index);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
</>
);
}
자 이제 열심히 next 버튼을 눌러보자.
왜 페이지가 안바뀌지?
re-render을 해야한다는 트리거가 없기 때문이다.
열심히 index
를 늘렸으나 re-render를 하라고 LifeCycle 관련 함수를 불러올 수 없는 슬픈 상황이다.
이러한 단점을 함수형 컴포넌트는 16.8 버전 이후에 나온 hooks
로 해결을 한 것이다.
위에 예시 코드에서 useState
없이 함수형에서 상태관리를 할 수 있을까?
방법을 찾으면 있겠지만... 나는 저 상황이라면 클래스 컴포넌트 쓸 것 같다.
Hooks의 등장
함수형 컴포넌트의 해결사. (LifeCycle을 해결)ComponenDidMount
, componentWillUnmount
, componentDidUpdate
이라는 라이프 사이클 관리 함수를
함수형 컴포넌트에서hooks(useEffect, useState 등등)
& return
실행으로 대체
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
function handleClick() {
setIndex(index + 1);
console.log(index);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
</>
);
}
그런데 왜? 함수형 컴포넌트를 굳이 쓰고 싶었을까?
의문이 든다. 굳이. 왜. 함수형 컴포넌트를 hooks까지 만들면서 쓸까? 클래스 기반이 이미 잘 되있는데?
생산성 측면
- class 문법, this에 대한 이해 없이 개발이 가능하다.
- 직관적으로 이해할 수 있는 코드.
- 라이프사이클을 이해해야하는 클래스 컴포넌트는 러닝커브가 높고, 갈수록 코드의 양이 증가한다.
- props가 바뀌었으면 props 관련 함수를 불러서 사용해서 적용해야함.
- state가 바뀌면 관련 함수를 불러서 만들어놔야함.
(ex) ComponenDidMount, componentWillUnmount, componentDidUpdate
생산성 측면에서 봤을 때. 처음 JS로 개발을 하는 사람에게 class
, this
는 엄청 무시무시한 문법이다.
그런 의미에서 안 쓰고도 개발이 가능하다면 러닝커브를 확실히 줄여줄 수 있다.
그리고 useState
로 하는 상태관리, 컴포넌트 함수의 결과로 랜더링이 필요한 태그는 return
으로 알 수 있기에 직관적이다.
성능 측면
나름대로 찾아봤을 때 성능에 대해서는 이야기는 많고 확실하게 옳다라고 느껴지지 않는다.
'이러한 주장이 있다' 라고만 생각하면 된다.
1. Babel 변환 과정에서 코드의 양
실제 작성하는 코드의 양이 많아지는 상황에, 바벨로 브라우저가 읽을 수 있게 바꾸면 코드의 양은 당연히 늘어난다.
함수형 컴포넌트보다 클래스기반 컴포넌트를 변환했을 때 코드의 양이 더 많게 변환되고
JS 파일을 브라우저로 보내서 랜더링을 시켜야하는데, 용량이 적을수록 더 빨리 브라우저는 코드를 열어볼 수 있다.
2. 메모리 관리
함수형보다 클래스 컴포넌트가 메모리 사용량이 많다는 주장이 있다.
- 인스턴스 생성
클래스 컴포넌트: 클래스 컴포넌트는 인스턴스화될 때마다 새로운 인스턴스를 생성. 이 인스턴스는 메서드, 생명주기 메서드, 상태 등을 포함하며, 이로 인해 추가 메모리가 필요하다.
함수형 컴포넌트: 함수형 컴포넌트는 인스턴스화되지 않으며, 렌더링 시 단순히 함수가 호출. 따라서 인스턴스 관련 메모리 오버헤드가 없다. - 메서드 바인딩
클래스 컴포넌트: 클래스 컴포넌트에서는 메서드를 this에 바인딩해야 할 수 있다. 이러한 바인딩은 메모리를 추가로 사용할 수 있으며, 특히 많은 메서드와 인스턴스가 있는 경우 더욱 그렇다.
함수형 컴포넌트: 함수형 컴포넌트에서는 이러한 바인딩이 필요하지 않으므로, 관련 메모리 오버헤드가 없다. - 최적화 기법
함수형 컴포넌트:useMemo
와 같은 최적화 기법을 사용하면, 불필요한 계산과 렌더링을 줄일 수 있다. 이로 인해 메모리 사용량과 성능에 긍정적인 영향을 미칠 수 있다.
HOC (Higher Order Component, 고차 컴포넌트)
고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수.
state에 따라서 props를 올리고 내리는 과정이 우리가 이미 많이 써본 함수형과 다르게 부자연스럽다.
(나는 사실 함수형에서도 많이 사용하고 있다,,, 정답은 없지 않을까)
HOC를 활용한 클래스 기반 Counter App과 state를 활용한 Counter App
HOC를 활용한 counter App
import React, { Component } from 'react';
// HOC
function withCounter(WrappedComponent) {
return class extends Component {
state = {
count: 0
}
incrementCount = () => {
this.setState(prevState => {
return { count: prevState.count + 1 }
})
}
render() {
return <WrappedComponent
count={this.state.count}
incrementCount={this.incrementCount}
{...this.props}
/>
}
}
}
// Component
class Counter extends Component {
render() {
return <button onClick={this.props.incrementCount}>{this.props.count}</button>
}
}
export default withCounter(Counter);
useState를 활용한 counter App
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(prevCount => prevCount + 1);
}
return <button onClick={incrementCount}>{count}</button>
}
export default Counter;
위에 예시는 좀 억지같은데?
맞다. 글에 쓰려고 억지를 조금 부렸다. 극적으로 보여지는 예시인 것이다.
그러나 HOC Wrapper의 늪이 있다면? 상태관리를 해버리고 싶을 것이다.
상태 관리를 위해서
만약 유저에 대한 정보, 각종 인증, 인가에 대한 함수를 담는 wrapper를 만들어야한다면…
style에 대한 컴포넌트로 테마를 관리할 wrapper로 필요하다면…
만약 영어, 한국어 페이지에 대한 처리가 필요하다면…
치킨 만들기에 관련해서 관리하는 wrapper가 필요하다면…
스크롤에 관련한 wrapper가 필요하다면?!!
기타 등등이 필요하다면?
<UserAuth something="something">
<CSStheme mode="night">
<LanguageWrapper lang="kor">
<MakeChicken>
<스크롤관리컴포넌트>
<기타등등1>
<기타등등2>
아들 컴포넌트
</기타등등2>
</기타등등1>
</스크롤관리컴포넌트>
</MakeChicken>
</LanguageWrapper>
</CSStheme>
</UserAuth>
useState와 전역 상태관리를... 하고 싶지 않나요?
마무리
인트로에서 이야기했던 질문을 듣고 나만의 생각으로 먼저 노션으로 정리하고 이 내용에 대해서 알려드린 적이 있다.
그 내용을 블로그에 올리려고 더 정리를 하고자 했다. 추가된 부분도 있고, 남이 만들어놓은 것을 캡쳐한 부분은 제거했다.
개발자는 블로그를 하는 덕목이 있던데, 보통 노력은 아닌 것 같다.
'FrontEnd > React' 카테고리의 다른 글
개발자 도구 모르는 기능 써보기 (0) | 2024.02.25 |
---|