React
2023. 2. 1. 10:58ㆍ개발/실무용 메모
1. React 소개
npm install -g create-react-app // 리액트 설치
create-react-app . // 디렉토리 생성
- Vue와 마찬가지로 VirtualDOM 을 사용하는 라이브러리.
- 기본적으로 CSR (Client Side Rendering) 로 동작.
- 리액트 핵심 모듈 2가지.
//1. 리액트 컴포넌트 => HTMLElement 연결하기
import ReactDOM from 'react-dom';
//2. 리액트 컴포넌트 만들기
import React from 'react';
- JS/JSX 를 이용한 리액트 컴포넌트 사용.
//main
ReactDOM.render(
<Vhows name = "GreenLabs"/>
);
//component
class Vhows extends React.Component {
render() {
return(
<div>
Hello {this.props.name}
</div>
)
}
}
Vue와 차이점
- 간단한 To-do list 앱으로 비교해보는 차이점
- 출처 : 링크
- 파일 구조 차이

- 컴포넌트 내부 구조 차이
//ToDoItem.vue => vue
<template>
<div class="ToDoItem">
<p class="ToDoItem-Text">{{todo.text}}</p>
<div class="ToDoItem-Delete"
@click="deleteItem(todo)">
</div>
</div>
</template>
<script>
export default {
name: "to-do-item",
props: ['todo'],
methods: {
deleteItem(todo) {
this.$emit('delete', todo)
}
}
}
</script>
<style>
.ToDoItem {
display: flex;
justify-content: center;
align-items: center;
}
.ToDoItem-Text {
width: 90%;
background-color: white;
border: 1px solid lightgrey;
box-shadow: 1px 1px 1px lightgrey;
padding: 12px;
margin-right: 10px;
}
</style>
//ToDoItem.js => react
//css 별도 파일 작성
import React, {Component} from 'react';
import './ToDoItem.css';
class ToDoItem extends Component {
render() {
return (
<div className="ToDoItem">
<p className="ToDoItem-Text">{this.props.item}</p>
<div className="ToDoItem-Delete"
onClick={this.props.deleteItem}>
</div>
</div>
);
}
}
export default ToDoItem;
- Vue 앱은 하나의 .vue 파일에 template , script , style 3가지 영역이 나뉘어 있고, 각 영역은 HTML , 자바스크립트 , CSS를 작성하는 패턴이다.
- React 앱은 .js 파일과 .css 파일이 나뉘어 있고, js 파일에서 jsx 형태로 코드를 작성한다.
2. React Component
- 컴포넌트란 기능 단위의 덩어리로, State 관리 및 Props 교환 등을 통해 DOM 변경을 JSX 문법으로 정의함 (CSS 정의는 포함되지 않음)
- 컴포넌트는 화면 렌더링이 마운트 / 업데이트 / 제거되는 LifeCycle을 지니기 때문에, 이를 알맞게 정의해줘야함
Component 생성
- 컴포넌트 정의 방법의 변천사 : 클래스형 → 함수형
- 클래스형 컴포넌트와 함수형 컴포넌트의 차이
- 클래스형
- class 키워드로 선언
- State 사용가능 / this.setState() 를 통해 변경
- 자식 컴포넌트로 this.props 를 통해 값 전달
- LiftCycle API를 통한 생명주기 관리
- 화면 렌더링 시, render()만 실행
// Class형 class Vhows extends React.Component { render() { return <div>Hello</div>; } }
- 함수형
- function() 혹은 화살표함수 형태로 선언
State 사용불가⇒ 리액트훅 (useState)을 사용하여 관리 및 변경- 매개변수를 통해 값 전달
LifeCycle API 사용불가⇒ 리액트훅(useEffect)을 사용하여 생명주기 관리- 화면 렌더링 시, 컴포넌트 함수 전체 실행
// Function형 function Vhows() { return <div>Hello</div>; } // Arrow Function형 const Vhows = () => { return <div>Hello</div>; }
- 클래스형
- React Hook이 나오기 전에는 클래스형과 함수형 구분하여 사용 하였으나, (상태 및 라이프사이클 관리 등) hook 개념이 나온 이후 최신 React는 함수형이 승리 ← 최신 ES의 지향점
- 버전마다 방향성이 왔다갔다해서 뒤늦게 합류하니 지랄맞음
- 클래스형 컴포넌트와 함수형 컴포넌트의 차이
- 컴포넌트에서 DOM 렌더링을 정의하는 방식의 변천사
- React.createElement() 사용하는 방식에서 render() JSX 및 Fragment 단축 문법 등으로 훨씬 간단하게 표현이 가능해짐
- React.createElement() 예제
- 이렇게 사용하면 태그안에 ...등 Depth가 늘어날 때 매우 복잡해짐 → 따라서 JSX 문법 등장
- render() JSX 및 Fragment 단축 문법 예제
- 이렇게 사용하면 태그안에 ...등 Depth가 늘어날 때 매우 복잡해짐 → 따라서 JSX 문법 등장
- React.createElement() 예제
class CustomComponent extends React.Component { render() { return ( <> <p>Vhows</p> </> ); } } // 또는 함수형 const CustomComponent = () => { return ( <> <p>Vhows</p> </> ); };
- React.createElement() 사용하는 방식에서 render() JSX 및 Fragment 단축 문법 등으로 훨씬 간단하게 표현이 가능해짐
State
- State = { 상태값 지정 }
- 화면에 동적으로 변경되는 상태 변수를 지정
- setState()
- 상태값을 변경할 때 반드시 setState() 사용
- setState 를 사용하지 않고 this.state.aaa = 'bbb'; 이런식으로 직접 명시해서 수정할 경우 값은 변경되어도 화면이 render 되지 않음
- 함수형 프로그래밍이 추구하는 불변성을 바탕으로 함
- 일반적으론 객체형으로 선언하고, 이전 State를 사용하는 경우 함수형 return 값으로 선언함
- setSate()가 실행될 때 마다 render()가 실행 → 자원 문제 고려해야 함
- 비동기로 동작함
- 사용 예시 (출처:https://velopert.com/3629)
- 상태값을 변경할 때 반드시 setState() 사용
import React, { Component } from 'react';
class Counter extends Component {
state = {
number: 0,
}
handleIncrease = () => {
// 일반적인 setState() 객체형 형태
this.setState({
number: this.state.number + 1
});
}
handleDecrease = () => {
// 이전 스테이트를 가져온 setState() 함수형 형태
this.setState((prevState) => {
return {
number: prevState.number - 1
};
});
}
render() {
return (
<div>
<h1>카운터</h1>
<div>값: {this.state.number}</div>
<button onClick={this.handleIncrease}>+</button>
<button onClick={this.handleDecrease}>-</button>
</div>
);
}
}
export default Counter;
Props
- Props - 부모 컴퍼넌트가 자식 컴퍼넌트에게 전달해주는 값
JSX
- JSX 문법으로 작성된 코드는 순수한 자바스크립트로 컴파일해서 사용 ⇒ Babel (참조:https://babeljs.io/)
- 문법
- 최상위 요소가 하나여야 함.
- 자식요소를 바로 렌더하고 싶을땐 <></> 사용 ⇒ Fragment (v16.2 에서 도입)
- 표현식은 중괄호 {} 사용.
- if 문 사용 불가. ⇒ 삼항 연산자 또는 && 사용. (return()내에서)
- style 을 이용해 인라인 스타일링 가능.
- class 대신 className 을 사용해 class 적용 가능
LifeCycle
- LiftCycle API 는 컴포넌트가 브라우저에서 나타날때, 사라질때, 업데이트 될때 호출 되는 API
- constructor
- 컴포넌트 생성자 함수. 컴포넌트가 새로 만들어 질때 최초로 이 함수가 호출됨.
-
componentWillMount- 컴포넌트가 화면에 그려지기 바로 직전에 호출되는 함수 → 현재는 사용되지 않음.
- componentDidMount
- 컴포넌트가 화면에 그려진후에 한번 호출되는 함수. → 해당 컴포넌트에서 필요로 하는 데이터를 요청하기 위해 fetch 등, ajax 요청을 하거나 속성값들을 변경하는 작업.
- componentDidUpdate
- 컴포넌트의 props 나 state 값이 변경 되면, 호출되는 함수.
- shouldComponentUpdate
- true인 경우 화면 렌더링 업데이트를 함.
- 기본적으로 true를 반환하며, 따로 작성한 조건에 따라 false 를 반환하게 되면 해당 조건에서는 render 함수를 호출하지 않음.
- componentWillUnmount
- 해당 컴포넌트가 더이상 사용되지 않을때 호출됨, 즉 Dom 에서 제거될때 → 예를 들면 라우터 이동등..
Component 기타
- Ref
- DOM에 직접 접근할 때 사용 (보통 input 포커스 줄 때 사용)
3. React Hooks
- 함수형 컴포넌트를 온전하게 사용할 수 있도록, State 관리나 LifeCycle 관리 등을 함수 형태로 사용하는 방법
- 상태 관리를 할 수 있게 해주는 useState , 렌더링 직후 작업을 설정하는 useEffect 등
- 기타 다른 내장된 Hooks : 링크 참조
- 보통 useXxx() 시작됨
- React는 함수형 컴포넌트 및 Hooks 지향 = 함수형 프로그래밍을 지향
useState
const [state, setState] = useState("");
- 개요
- State를 관리하는 Hook
- 하나의 상태를 관리하는 기본적인 방법
- 구조 분해 할당(Destructuring)를 통해 배열 첫번째로 상태 값을 담는 변수, 두번째로 상태 값을 변경해주는 함수를 할당
- useState 예제 1
import React, {useState} from 'react'; const ExampleCounter = () => { // 배열 첫번째 count 상태 값을 담는 변수 // 배열 두번째 setCount 상태 값을 변경해주는 함수 // 인자 0 은 count의 초기값 const [count, setCount] = useState(0); return ( <div> <p>{count}</p> <button onClick={()=>{ setCount(count + 1) }}> button </button> </div> ) }
- useState 예제 2 - buttonClick 함수를 중간에 추가
import React, {useState} from 'react';
const ExampleCounter = () => {
const [count, setCount] = useState(0);
const buttonClick = () => {
setCount(count + 1);
}
return (
<div>
<p>{count}</p>
<button onClick={buttonClick}>
button
</button>
</div>
)
}
- 여러 개의 상태를 관리 할 때는 용도에 따라 2가지 방법이 있음
- 각 상태 별로 useState 각각 정의
- 상태를 객체 등으로 구조화하고, 모든 상태 값에 대한 변경을 하나의 함수에서 정의
useEffect
useEffect(() => {
console.log('컴포넌트가 화면에 나타남');
return () => {
console.log('컴포넌트가 화면에서 사라짐');
};
}, []);
- 개요
- 클래스형 lifeCycle의 componentDidMount() 와 componentDidUpdate() 그리고 componentWillUnmount() 세가지가 합쳐진 형태로 보면 된다
- useEffect 의 매개변수는 ()⇒{} 익명함수와, [] 배열 두가지가 들어감
- 두번째 인자는 의존값이 들어있는 배열 (deps 배열)
- 빈 배열을 넣을경우 화면에 처음 렌더링 될 때 한번만 실행.
- return 을 이용하여 cleanup 함수 반환
- 컴포넌트가 사라질때(unmount 될때) 호출
- return 을 이용하여 cleanup 함수 반환
- 특정 값을 넣으면 처음 렌더링 될 때와 해당 값이 업데이트 될때 실행.
- cleanup 함수가 컴포넌트가 사라질때(unmount 될때) 뿐만 아니라, 값이 변경되기 직전에도 실행
- 생략할경우 렌더링이 다시 일어날때마다 실행.
- 빈 배열을 넣을경우 화면에 처음 렌더링 될 때 한번만 실행.
- 참고 사항
useRef
const nameInput = useRef(null);
<input
**ref={nameInput}**
name="name"
placeholder="이름"
onChange={onChange}
value={name}
/>
- 개요
- DOM 접근을 위한 Hook
- 자바스크립트에서 특정 DOM을 선택하고자 할 때, getElementById, querySelector 를 사용하는 것 처럼 리액트에서는 ref 를 사용
- useRef() 를 사용하여 Ref 객체를 만들고, 이 객체를 선택할 DOM 에 ref값으로 설정하면, Ref 객체의 .current값은 해당 DOM 을 가르키게 됨.
- DOM 접근을 위한 Hook
- 참고 사항
- state의 값과 ref가 가르키는 값의 렌더링시 차이
- state는 컴포넌트가 렌더링이 되는, 혹은 ‘렌더링 된 시점’의 상태값을 가리키고, ref는 컴포넌트의 ‘현재’ 상태값을 가리킨다.
- state의 값과 ref가 가르키는 값의 렌더링시 차이
useMemo
// 사용 전
const count = countActiveUsers(users)
// 사용 후
const count = useMemo(() => countActiveUsers(users), [users]);
- 개요
- 성능 최적화를 위해 연산된 값을 재사용 할 수 있도록 해주는 Hook
- 첫번째 파라미터는 어떻게 연산할지 정의하는 함수, 즉 실행할 함수를 담음.
- 두번째 파라미터는 deps 배열을 넣어준다.
import React, { useRef, useState, useMemo } from 'react'; import UserList from './UserList'; import CreateUser from './CreateUser'; function countActiveUsers(users) { console.log('활성 사용자 수를 세는중...'); return users.filter(user => user.active).length; } const UseMemo = () => { const [inputs, setInputs] = useState({ username: '', email: '' }); const { username, email } = inputs; const onChange = e => { const { name, value } = e.target; setInputs({ ...inputs, [name]: value }); }; const [users, setUsers] = useState([ { id: 1, username: 'velopert', email: 'public.velopert@gmail.com', active: true }, { id: 2, username: 'tester', email: 'tester@example.com', active: false }, { id: 3, username: 'liz', email: 'liz@example.com', active: false } ]); const nextId = useRef(4); const onCreate = () => { const user = { id: nextId.current, username, email }; setUsers(users.concat(user)); setInputs({ username: '', email: '' }); nextId.current += 1; }; const onRemove = id => { // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬 // = user.id 가 id 인 것을 제거함 setUsers(users.filter(user => user.id !== id)); }; const onToggle = id => { setUsers( users.map(user => user.id === id ? { ...user, active: !user.active } : user ) ); }; // const count = countActiveUsers(users); const count = useMemo(() => countActiveUsers(users), [users]); return ( <> <CreateUser username={username} email={email} onChange={onChange} onCreate={onCreate} /> <UserList users={users} onRemove={onRemove} onToggle={onToggle} /> <div>활성사용자 수 : {count}</div> </> ); } export default UseMemo;
useCallback
// 사용 전
const increament = () => {
setCount({ num: count.num + 1 });
};
// 사용 후
const increament = useCallback(() => {
setCount({ num: count.num + 1 });
},[count]);
- 개요
- useMemo 기반으로 만들어진, 함수를 위해 사용성을 증가시킨 Hook
- useMemo는 특정 결과값 (연산된 값)을 재사용 할 때 사용하는 반면, useCallback 은 특정 함수를 재사용 하고자 할 때 사용.
- useMemo 와의 차이점
- useMemo
- 함수의 값만 메모이제이션해서 반환
- 자식 컴포넌트에서 많은 계산량이나 복잡한 계산식의 값등, 특정 props값들을 최적화 하고자 할 때 사용
- useCallback
- 함수 자체를 메모이제이션해서 반환
- 부모 컴포넌트에서 많은 계산량이나 복잡한 계산식의 함수를 자식 컴포넌트에 props로 전달해줄 때 사용
- useMemo
- React.memo 와 함께 사용하여 최적화
- React.memo 란?
- 컴포넌트의 props가 바뀌지 않았다면, 리렌더링을 방지해서 성능 최적화를 해줄 수 있는 함수
- React.memo() 로 감싸서 사용.
- useCallback 을 사용하여, 자식 컴포넌트로 전달된 함수의 의존성 때문에 React.memo로 감싸진 자식 컴포넌트가 여러번 렌더링 되는 현상이 발생할 수 있음.
- 비동기적인 setState를 함수형 업데이트로 변경하여, 각각의 콜백함수에서 최신의 상태값을 바라볼수 있도록 해줌으로써 최적화&해결 가능.
import React, { useRef, useState, useMemo, useCallback } from 'react'; import UserList from './UserList'; import CreateUser from './CreateUser'; function countActiveUsers(users) { console.log('활성 사용자 수를 세는중...'); return users.filter(user => user.active).length; } const UseCallback = () => { const [inputs, setInputs] = useState({ username: '', email: '' }); const { username, email } = inputs; const onChange = useCallback( e => { const { name, value } = e.target; setInputs({ ...inputs, [name]: value }); }, [inputs] ); const [users, setUsers] = useState([ { id: 1, username: 'velopert', email: 'public.velopert@gmail.com', active: true }, { id: 2, username: 'tester', email: 'tester@example.com', active: false }, { id: 3, username: 'liz', email: 'liz@example.com', active: false } ]); const nextId = useRef(4); const onCreate = useCallback(() => { const user = { id: nextId.current, username, email }; setUsers(users => users.concat(user)); setInputs({ username: '', email: '' }); nextId.current += 1; }, [username, email]); const onRemove = useCallback(id => { // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬 // = user.id 가 id 인 것을 제거함 setUsers(users => users.filter(user => user.id !== id)); }, []); const onToggle = useCallback(id => { setUsers( users.map(user => user.id === id ? { ...user, active: !user.active } : user ) ); }, [users] ); // const onToggle = id => { // setUsers( // users.map(user => // user.id === id ? { ...user, active: !user.active } : user // ) // ); // }; const count = useMemo(() => countActiveUsers(users), [users]); return ( <> <CreateUser username={username} email={email} onChange={onChange} onCreate={onCreate} /> <UserList users={users} onRemove={onRemove} onToggle={onToggle} /> <div> 활성사용자 수 : {count} </div> </> ); } export default UseCallback;
- 비동기적인 setState를 함수형 업데이트로 변경하여, 각각의 콜백함수에서 최신의 상태값을 바라볼수 있도록 해줌으로써 최적화&해결 가능.
- React.memo 란?
useReducer
const [state, dispatch] = useReducer(reducer, initialState);
- 개요
- useState의 확장 개념
- 리덕스 (참고 : Redux) 를 알고있다면 쉽게 사용 가능
- 이 Hook 함수를 사용하면 컴포넌트의 상태 업데이트 로직을 컴포넌트에서 분리시킬 수 있음.
- 상태 업데이트 로직을 컴포넌트 바깥쪽, 혹은 다른 파일에 작성 후 불러와서 사용 가능.
- 다수의 하위값을 포함하는 복잡한 정적 로직을 만드는 경우,
- 다음 state 가 (변경될 state) 이전 state (변경되기전의 state) 에 의존적인 경우에 사용
- useState의 확장 개념
- 키워드 & 개념
- reducer
- state를 변경하는 로직이 담겨 있는 함수
- dispatch
- action 객체를 넣어서 실행
- action
- 객체이고 필수 프로퍼티로 type을 가짐
- reducer
Custom Hooks
- 자신만의 커스텀 hook을 만들면 컴포넌트 로직을 재사용 가능한 함수로 추출 할 수 있게 된다.
- 규칙 (필수는 아니나 권장) : 다른 Hook을 호출하며, 함수 이름이 'use'로 시작해야 한다.
4. 기타
- Hooks 사용한 함수형 컴포넌트의 문제점
- https://ui.toast.com/weekly-pick/ko_20200922
- Functional Component와 Function Programing은 완전 다른 것.
'개발 > 실무용 메모' 카테고리의 다른 글
ReScript (0) | 2023.02.01 |
---|---|
Redux (0) | 2023.02.01 |
Rendering (0) | 2023.02.01 |
SSR - Next.j (0) | 2023.02.01 |
Formatting & Linting (0) | 2023.01.31 |