React 의 특장점, 렌더 라이프사이클 및 Hook(7) - immer(리렌더링 이슈 해결)

ASAC 웹 풀스택
2024. 9. 22. 17:02
목차
  1. immer: 불변성 객체로 인한 리렌더링 이슈 해결
  2. immer 동작 과정
728x90

immer: 불변성 객체로 인한 리렌더링 이슈 해결

 

객체 State 업데이트하기 – React

The library for web and native user interfaces

ko.react.dev

 

패스워트 컴포넌트 내 useRef 통한 input 태그 타입 변경을 렌더링 없이 적용 한 후 유효성 검증 상태들을 객체로 단일화 하였더니 아래와 같은 문제 발생

import { useRef, useState } from 'react'
import '@/App.css'
function UsernameInput() {
return (
<div>
Username : <input />
</div>
)
}
function PasswordInput() {
const [valid, setValid] = useState({
maximum: false,
minimum: false,
required: false,
})
const reference = useRef(null)
function changeMode(e) {
if (reference.current.type === 'password') {
reference.current.type = 'text'
e.currentTarget.innerText = '🔒 감추기'
} else if (reference.current.type === 'text') {
reference.current.type = 'password'
e.currentTarget.innerText = '🔓 보이기'
}
}
console.log('- 매번 입력할때마다 rerender 발생. 상태를 객체로 단일화하자 useRef 사용 이유를 잃음')
return (
<div>
Password :{' '}
<input
type='password'
ref={reference}
onChange={(e) => {
const input = e.currentTarget.value
setValid({
maximum: input.length <= 10,
minimum: input.length > 5,
required: input.length > 0,
})
}}
/>
<button onClick={changeMode}>🔓 보이기</button>
{valid.maximum || <div style={{ color: 'red' }}>비밀번호는 10글자를 넘을 수 없습니다.</div>}
{valid.minimum || <div style={{ color: 'red' }}>비밀번호는 5글자를 넘어야합니다.</div>}
{valid.required || <div style={{ color: 'red' }}>비밀번호를 입력해주세요.</div>}
</div>
)
}
function App() {
function registration() {}
return (
<section style={{ textAlign: 'start', width: 400 }}>
<UsernameInput />
<PasswordInput />
<button onClick={registration}>회원가입 완료</button>
</section>
)
}
export default App

valid라는 상태를 만들어서 유효성 검증항목을 객체로 감싸서 상태를 변경하고 있다.

 

[문제]

input값이 바뀔때마다 새로운 객체를 생성해서 setValid해주므로 input값 변경할 때마다 리렌더링 발생

  • ref 사용한 의미가 없다.

[해결]

immer를 사용해서 불변성 객체로 인한 리렌더링 이슈를 해결할 수 있다.

import { useRef, useState } from 'react'
import '@/App.css'
import { produce } from 'immer'
function UsernameInput() {
return (
<div>
Username : <input />
</div>
)
}
function PasswordInput() {
const [valid, setValid] = useState({
maximum: false,
minimum: false,
required: false,
})
const reference = useRef(null)
function changeMode(e) {
if (reference.current.type === 'password') {
reference.current.type = 'text'
e.currentTarget.innerText = '🔒 감추기'
} else if (reference.current.type === 'text') {
reference.current.type = 'password'
e.currentTarget.innerText = '🔓 보이기'
}
}
console.log('- 매번 입력해도 rerender 미발생. 객체 프로퍼티 단위 불변성을 유지하며 리렌더 방지')
return (
<div>
Password :{' '}
<input
type='password'
ref={reference}
onChange={(e) => {
const input = e.currentTarget.value
const changed = produce(valid, (draft) => {
if (valid.maximum !== input.length <= 10) draft.maximum = input.length <= 10
if (valid.minimum !== input.length > 5) draft.minimum = input.length > 5
if (valid.required !== input.length > 0) draft.required = input.length > 0
})
setValid(changed)
}}
/>
<button onClick={changeMode}>🔓 보이기</button>
{valid.maximum || <div style={{ color: 'red' }}>비밀번호는 10글자를 넘을 수 없습니다.</div>}
{valid.minimum || <div style={{ color: 'red' }}>비밀번호는 5글자를 넘어야합니다.</div>}
{valid.required || <div style={{ color: 'red' }}>비밀번호를 입력해주세요.</div>}
</div>
)
}
function App() {
function registration() {}
return (
<section style={{ textAlign: 'start', width: 400 }}>
<UsernameInput />
<PasswordInput />
<button onClick={registration}>회원가입 완료</button>
</section>
)
}
export default App

immer 동작 과정

리액트 공식 문서에서 immer의 작동을 아래와 같이 설명하고 있다.

Immer가 제공하는 draft는 Proxy라고 하는 아주 특별한 객체 타입으로, 당신이 하는 일을 “기록” 합니다. 객체를 원하는 만큼 자유롭게 변경할 수 있는 이유죠!
Immer는 내부적으로 draft의 어느 부분이 변경되었는지 알아내어, 변경사항을 포함한 완전히 새로운 객체를 생성합니다.

 

위 설명에 따르면 아래 코드를 적용했을 때 객체의 프로퍼티 값이 바뀔때만 리렌더링 되는 이유를 알 것이다.

const changed = produce(valid, (draft) => {
if (valid.maximum !== input.length <= 10) draft.maximum = input.length <= 10
if (valid.minimum !== input.length > 5) draft.minimum = input.length > 5
if (valid.required !== input.length > 0) draft.required = input.length > 0
})

console.log(changed)를 해보면 아래 사진과 같이 객체가 반환되는 걸 볼 수 있다.(maximum, required가 true로 변경된 경우이다)

draft는 valid라는 state를 base로 삼고 있고, draft는 valid라는 객체가 변경되면 변경 사항을 포함해 완전히 새로운 객체를 생성하게 된다.

baseState(valid)가 변경되는 조건이 if문으로 작성되어 있고, 해당 조건이 참일 경우에 draft라는 프록시 객체가 이전 객체와 비교하여 변경 되었다면 해당 프로퍼티 값을 변경해서 새로운 draft객체를 생성하여 반환한다. 따라서 새로운 객체가 반화되었으므로 valid의 상태가 변경되면서 리렌더링 된다? -> 질문 필요

immer를 사용하기 위해서는

프로젝트 폴더로 이동 한 후 터미널에 아래 명령어 입력

npm install immer

 

package.json에 immer가 생겼다면 immer를 사용할 수 있다.

 

 

immer는 produce 함수를 제공한다. 구조는 아래와 같다 (immper가 제공하는 produce함수에 대한 자세한 설명)

 

Using produce | Immer

<div

immerjs.github.io

produce(baseState, recipe: (draftState) => void): nextState
  • baseState :produce함수에 변경 불가능한 state를 넘겨 받는 인자.
  • recipe: baseState를 변경해야하는 방식은 포착
  • draft: recipe의 첫번째 인자로, baseState를 안전하게 변경하는 프록시이다.
  • producer: (baseState, ...arguments) => resultState 형식인 함수이다.
recipe 의 첫번째 인자인 draft에 이름을 붙일 필요는 없다. 본인이 원하는 변수 이름으로 설정할 수 있음.
draft 라는 변수 이름을 사용하는 것은 draft객체에 대해서는 변경을 해도 괜찮다고 명시하는 것 뿐이다.
produce는 baseState와 draf에 전달되는 모든 돌연변이를 수행하는 데 사용할 수 있는 recipe를 제공한다.
baseState를 손상시키지 않고, nextState에 적용된 모든 변경사항이 반영된다.

 

immer를 사용하기 전에는 매번 새로운 객체가 생성되서 주소값이 바뀌니까 state가 계속 변했는데, immer를 사용하면 새로운 객체를 생성하지 않으면서 프로퍼티 값만 변경할 수 있다.

recipe 내부에서는 draf객체에 대해서 필드 할당, delete 연산, 배열 변형, Map 및 Set 연산(예 push: pop, splice, set, sort, remove, 등) 을 포함하여 모든 표준 JavaScript API를 객체에서 사용할 수 있습니다.
recipe 함수는 void를 반환한다는 점을 명시해라. 하지만 draft 객체를 새로운 객체로 대체할 경우 새로운 draft 객체를 반환할 수 있다. 자세한 내용은 새 데이터 반환 을 참조하세요
728x90

'ASAC 웹 풀스택' 카테고리의 다른 글

Java 기본 문법 및 JVM 구성(2) - 자바 개발 환경 설정(Intellij, Gradle, Lombok)  (2) 2024.09.25
Java 기본 문법 및 JVM 구성(1) - Java동작 원리  (0) 2024.09.25
React 의 특장점, 렌더 라이프사이클 및 Hook(6) - Ref  (0) 2024.09.22
React 의 특장점, 렌더 라이프사이클 및 Hook(4) - State  (1) 2024.09.22
React 의 특장점, 렌더 라이프사이클 및 Hook(5) - Props  (0) 2024.09.22
  1. immer: 불변성 객체로 인한 리렌더링 이슈 해결
  2. immer 동작 과정
'ASAC 웹 풀스택' 카테고리의 다른 글
  • Java 기본 문법 및 JVM 구성(2) - 자바 개발 환경 설정(Intellij, Gradle, Lombok)
  • Java 기본 문법 및 JVM 구성(1) - Java동작 원리
  • React 의 특장점, 렌더 라이프사이클 및 Hook(6) - Ref
  • React 의 특장점, 렌더 라이프사이클 및 Hook(4) - State
hapBday
hapBday
hapBday
개발자로 성장하기 위한 기록들
hapBday
전체
오늘
어제
  • 분류 전체보기 (203)
    • CS (12)
      • 컴퓨터네트워크 (11)
      • 운영체제 (0)
      • 분산 시스템 (0)
      • 데이터베이스 (1)
    • Spring (47)
      • Spring 핵심 원리 (13)
      • Spring MVC (15)
      • Spring DB (12)
      • Spring Security (6)
    • JPA (14)
    • 알고리즘 (30)
      • 프로그래머스 (6)
      • 백준 (20)
    • Design Pattern (0)
    • 언어 (5)
      • JAVA (5)
    • ASAC 웹 풀스택 (38)
      • Spring Boot (21)
      • React (0)
      • DevOps (8)
    • 트러블슈팅 (15)
    • DevOps (5)
      • Docker (5)
    • ETC (2)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • github

공지사항

  • 블로그 이전

인기 글

태그

  • 트랜잭션
  • spring boot
  • docker
  • 프로그래머스
  • Spring
  • 티스토리챌린지
  • docker workflow
  • aws lambda
  • CORS
  • cookie
  • docker best practices
  • 인프런
  • 오블완
  • multi-stage
  • currency control
  • basicerrorcontroller
  • s-lock
  • MVC
  • 김영한
  • spring security
  • S3
  • Session
  • 백준
  • 3-layerd 아키텍쳐 패턴
  • Java
  • x-lock
  • jwt
  • CSRF
  • JPA
  • 구현

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.3.0
hapBday
React 의 특장점, 렌더 라이프사이클 및 Hook(7) - immer(리렌더링 이슈 해결)
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.