리액트
- 웹 프론트엔드 및 앱 Native, VR 등에서 활용되는 UI 라이브러리로, UI 데이터를 관리하는 방법을 제공한다.
- 부모 컴포넌트로 내려받는 값을 속성값, 컴포넌트 내부에서 생성/관리되는 값을 상태값이라고 한다.
- 리액트는 UI에 연결된 속성값과 상태값이 변경되면, 해당 컴포넌트의 render() 함수가 호출되어 화면을 자동으로 갱신한다.
- 클래스형 컴포넌트에서는 render() 함수 호출
- 함수형 컴포넌트에선느 해당 함수가 매번 호출되며, 컴포넌트에서 유지해야 할 값들은 Hook을 통해 관리
- 또한 Virtual DOM을 통해 이전 UI 상태를 메모리에 유지하고 변경될 UI의 최소 집합을 계산하므로, UI를 빠르게 갱신한다.
선언적 UI
- UI에 변화가 있을 때마다 일일이 코드를 수행하는 것이 아니라, 데이터(속성값, 상태값)에 맞춰 보여질 UI를 미리 선언해두고 데이터가 변경되면 그 즉시 데이터에 맞춰 UI가 그려진다.
function App() {
const [postList, setPostList] = useState([]);
const addPost = () => {
const post_title = "New Post";
setPostList([
...postList,
{ title: post_title },
]);
};
return (
<div>
{postList.map(post =>
<div className="post">{post.title}</div>
)}
<button onClick={addPost}>Add Post</button>
</div>
);
}
- 즉, UI에 보여질 값들을 효율적으로 관리하고, 그 값들의 변경에 맞춰서 업데이트되게 함으로써 UI가 불필요하게 업데이트되지 않도록 한다.
React Element
- React 앱의 가장 작은 단위로, 리액트 컴포넌트에서 화면을 담당한다.
// jsx 문법
const reactElement1 = <h1>Hello, React!</h1>;
// js 문법
const reactElement2 = React.createElement('h1', null, 'Hello, React!');
리액트 컴포넌트
- 리액트는 컴포넌트를 통해 UI를 재사용 가능한 개별적인 여러 조각으로 나누는데, 하단의 export default를 통해 컴포넌트를 다른 컴포넌트에서 사용할 수 있다.
- 컴포넌트는 자바스크립트 함수 또는 자바스크립트 클래스 형태로 작성할 수 있으며, 속성값을 전달받아 Element를 반환한다.
- 클래스형 컴포넌트가 먼저 지원되었으며, 최근에는 함수로 구현하는 컴포넌트를 지원한다.
- 자바스크립트 파일 내에 HTML 코드를 사용하고 있는데, 이는 React가 한 파일에서 HTML과 자바스크립트를 함께 사용하려고 확장한 자바스크립트 문법으로 JSX라고 부른다.
- 리액트 컴포넌트는 렌더링 부분인 HTML과, 로직 부분인 자바스크립트를 포함하는 JSX를 리턴한다.
- JSX 문법은 Babel이라는 라이브러리가 빌드 시 자바스크립트로 번역해준다.
ReactDOM.render()
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// 최상위 컴포넌트를 특정 DOM에 렌더링
ReactDOM.render(
<App />,
document.getElementById('root'));
)
- ReactDOM.reader() 함수의 매개변수로 <App />을 주면, ReactDOM App 컴포넌트를 렌더링한다.
- 즉, ReactDOM은 매개변수로 넘겨받은 <App /> 컴포넌트를 이용해 DOM 트리를 만드는데, 이때 컴포넌트의 render() 함수가 반환한 JSX를 렌더링한다.
- ReactDOM.render()는 첫 번째 매개변수로 리액트 컴포넌트를 받고, 두 번재 매개변수로 root 엘리먼트를 받는다.
- root 엘리먼트는 index.html에 정의되어 있으며, root 엘리먼트에 첫 번째 매개변수로 넘겨진 리액트 컴포넌트를 추가한다.
- React로 만든 모든 컴포넌트는 이 root 엘리먼트 하위에 추가된다.
상태값 (state)
- 각 컴포넌트가 개별로 생성하고 유지하는 값들로, 주로 컴포넌트 단위로 UI에 반영할 값들을 저장할 목적
- 상태가 변경되면 변경된 state 값을 참조하는 UI를 자동으로 업데이트 한다.
- 불변 객체로 처리향 하며, 제공되는 상태값 setter 함수를 통해서만 변경해야 한다.
클래스형 컴포넌트에서의 상태값
- 클래스형 컴포넌트에서는 항상 this.state 객체를 통해 상태값에 접근하고, this.setState 함수를 통해 상태값을 변경해야 한다.
import React from 'react';
class Counter extends React.Component {
state = {
counter: 0,
};
onClick = () => {
const counter = this.state.counter + 1; // getter
this.setState({ counter }); // setter
}
render() {
const { counter } = this.state;
return (
<p onClick={this.onClick}>
Counter : {counter}
</p>
);
}
}
export default Counter;
setState
- 클래스형 컴포넌트에서 제공되는 유일한 상태값 setter
- 비동기로 동작하며, 변경할 특정 state 값들이 담긴 객체를 지정하거나, 함수를 지정할 수 있다.
- 함수를 지정할 경우 매개변수로 호출되기 직전의 상태값을 받을 수 있다.
onClick = () => {
this.setState(prevState => ({count: prevState.count + 1}));
this.setState(prevState => ({count: prevState.count + 1}));
}
- 만약 setState를 통하지 않고 state 값을 직접 변경하면 UI에 자동으로 반영되지 않으므로 주의해야 한다.
- 또한 setState 호출은 컴포넌트가 마운트된 이후에만 유효하기 때문에, 클래스형 컴포넌트 생성자에서의 setState 호출은 무시된다.
- 만약 데이터를 가져오기 위해 API를 호출하고 그 응답을 state에 반영하고자 할 경우에는 componentDidMount 메서들 이용한다.
componentDidMount
- 리액트는 브라우저에 보이는 HTML DOM 트리의 다른 버전인 ReactDOM을 가지고 있으며, 컴포넌트의 상태가 변하면 ReactDOM은 이를 감지하고 변경된 부분의 HTML을 바꿔준다.
- 렌더링이 처음 일어나는 순간, 즉 ReactDOM 트리가 존재하지 않는 상태에서 리액트가 처음으로 각 컴포넌트의 render() 함수를 콜해 자신의 DOM 트리를 구성하는 과정을 마운팅이라고 한다.
- 마운팅 과정에서 생성자와 render() 함수를 호출하고, 마운팅을 마친 후 componentDidMount 함수를 호출한다.
- 컴포넌트가 렌더링되자마자 API 콜을 하고, 그 결과를 리스트로 보여주려면 componentMount 함수에 백엔드 API 콜 부분을 구현해야 한다.
상태값 저장과 관리
- 각 컴포넌트 내에서만 사용되는 값들은 컴포넌트 안에서 생성/갱신한다. (리액트 기본 동작 방식)
- 여러 컴포넌트에서 사용되는 값들은 별도 공간에서 생성/갱신한다. (Redux, Context API 등을 활용)
1. 컴포넌트 내부에 저장
- 각 컴포넌트 객체 단위로 상태값을 유지하고, 하위 컴포넌트에 속성값으로 전달한다.
- 부모 컴포넌트를 거쳐야만 값을 전달받을 수 있으며, 상태값을 변경할 때는 상태값 setter 함수를 이용한다.
- 단점: 컴포넌트 계층이 복잡할 때, 상태값과 속성값 전파가 번거롭다.
2. 컴포넌트 외부에서 전역 상태 관리
- 컴포넌트 외부에 별도의 상태값 저장소를 두고, 여러 컴포넌트들에 의해 공유될 상태값들을 관리한다.
- 상태값에 대한 직접적인 변화를 가하는 setter를 제공하지 않는 대신, 하나의 함수에서 임의의 객체를 전달받아 이를 상태값에 반영한다. (dispatch 함수, action 객체)
function dispatch(action, state) {
const { type, payload } = action;
if ( type === 'INCREMENT' ) {
const { value } = payload;
return {
...state,
value: state.value + value,
}
} else {
return state;
}
}
const action = {type: 'INCREMENT', payload: {value: 1}};
dispatch(action);
dispatch({
type: 'INCREMENT',
payload: { value: 3 },
});
속성값 (props)
- 컴포넌트 생성 시 넘겨지는 값의 목록으로, 자식 컴포넌트 입장에서 데이터와 함수를 전달받는 통로
- 읽기 전용인 불변 객체로 만들어야 하며, 부모가 props를 변경하면,변경된 props 값을 참조하는 UI가 자동으로 업데이트 된다.
- 값 지정 시에 중괄호를 통해 다양한 타입의 값과 표현식을 지정할 수 있으며, 중괄호를 생략하면 문자열 타입만 지정 가능
속성값 받기
constructor(props) {
super(props);
this.state = { item: props.item };
}
- 컴포넌트에 매개변수를 넘기려면 생성자를 사용한다.
- super를 이용해 props 오브젝트를 초기화시키고, state를 item 변수와 props.item으로 초기화 한다.
- state는 리액트가 관리하는 오브젝트로, 리액트에서는 추후에 변경할 수 있는 변수를 state 오브젝트에서 관리한다.
- 자바스크립트 내에서 변경한 변수의 값을 HTML에 다시 렌더링하기 위해서
상태값 넘기기
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
item: { id: 0, data: "hello" },
};
}
render() {
return (
<div className="App">
<컴포넌트 이름 item={this.state.item} />
</div>
)
}
}
- App.js도 컴포넌트이므로 생성자에서 props를 넘겨받고, thist.state에서 item을 초기화한다.
- 이후 this.state.item을 이용해 item 오브젝트에 접근할 수 있고, item={<변수>}를 이용해 props로 매개변수를 넘길 수 있다.
속성값을 받아서 상태값으로 저장
class Counter extends React.Component {
state = {
value: this.props.initialValue,
};
render() {
return (
<div>
{this.state.value}
</div>
);
}
}
클래스 컴포넌트 생명주기
getDerivedStateFromProps
- render 호출 직전에 호출되며, 속성값으로부터 상태값을 계산하여 반영한다.
- 상태값으로 반영되기 때문에 필요시에 상태값을 변경할 수 있으며, 반영할 상태값이 없으면 null을 반환한다.
state = {
messageLength: 0
}
// render 메서드 호출 직전에 호출
static getDerivedStateFromProps(props, state) {
return {
messageLength: props.message.length
};
}
componentDidUpdate
- 속성값이 변경될 때 API를 호출하기 위해 사용한다.
- getDerivedStateFromProps는 정적 메서드이기 때문에 this 객체에 접근할 수 없기 때문에 componentDidUpdate에서 처리하는 것
- 함수형 컴포넌트에서는 useEffect 훅을 활용하여, 속성값/상태값이 변경될 때 원하는 함수를 호출할 수 있다.
componentDidUpdate(prevProps) {
const { postId } = this.props;
if ( prevProps.postId !== postId ) {
this.requestPost(postId);
}
}
'Frontend' 카테고리의 다른 글
순수 함수와 커링 기법 (0) | 2022.03.17 |
---|---|
Promise와 async/await (0) | 2022.03.17 |
ES6+ 문법 정리 (0) | 2022.03.17 |
react-router-dom과 로그인 인증 (0) | 2022.01.22 |
백엔드 통합과 CORS (0) | 2022.01.20 |