React

[React] 리액트 개념 정리 #2

https.. 2025. 10. 26. 17:58

 3장

JSX(JavaScript XML) 주의 사항

 

1. 카멜표기법 

ex. className, onClick

 

2. 속성명이 DOM API 기반

실제 HTML 문자열이 아니라, React.createElement() 함수로 변환되어 DOM을 조작함

 

3. 보간법 { } 에는 표현식을 사용

{ } 내에 보간된 HTML 문자열은 인코딩 됨. JS 값을 넣을 때는 반드시 { }로 감싸야함.

const name = "홍경";
return <h1>Hello, {name}!</h1>

 

속성 (Props, 프롭스) == 데이터 전달

Props는 컴포넌트에 데이터를 전달하기 위해 사용되는 속성

즉, 상위 컴포넌트에서 하위 컴포넌트로 데이터 전달하는 방법

프롭스는 컴포넌트 외부에서 전달되는 값이며, 읽기 전용이기 때문에 자식 컴포넌트는 데이터 변경이 불가함.

리액트의 데이터 흐름은 부모-자식으로 단방향임

 

자식 컴포넌트에서 데이터가 변경되면, 데이터의 출처와 변경 과정 추적이 어려워지기 때문에

데이터 변경의 주체를 부모 컴포넌트의 상태(state)에 두고, 자식에게는 props로 전달하는 방식으로 관리함.

 

상태는 컴포넌트가 보유하고 관리하는 데이터, 속성은 컴포넌트에 데이터 전달하는 방법

 

자식 컴포넌트에서 props를 정의할 때는, TypeScripts의 타입(type) 또는 인터페이스(interface)를 이용해 속성 타입 정의.

#props 타입을 정의하고, 함수형 컴포넌트 사용하는 예시

type childProps = {
	name: string;
    age: number;
};

const Child = (props: childProps) => {
    return <p>{props.name} ({props.age})</p>
}

props는 함수형 컴포넌트의 인자(parameter)로 선언하여 사용

부모 컴포넌트에서 자식 컴포넌트로 값을 전달할 때는 JSX의 보간법 이용해 { } 안에 표현식 넣어서 전달함.

 

렌더링

렌더링은 사용자 화면에 View를 보여주기 위해 리액트 컴포넌트가 HTML을 그려내는 작업.

: JSX, HTML 코드를 화면에 실제로 표시하는(그리는) 방법이다.

 

리렌더링(Re-rendering)

: 한 번만 그리는 게 아니라, 데이터가 바뀌면 화면을 다시 갱신하는 과정

const [count, setCount] = useState(0);
return <button onClick=(()=> setCount(count + 1)}>Count: {count}</button>

처음 렌더링 시 Count: 0임

버튼 클릭 시 count값이 1로 바뀜

리액트가 다시 렌더링 하면 Count: 1이 됨

-> 상태나 프롭스가 바뀌면 자동으로 화면을 다시 그려주는 리렌더링을 수행함.

여기서 상태는, 시간이 지나면서 변할 수 있는 데이터, 화면(UI)에 영향을 주는 변하는 값임.

count는 상태, setCount는 상태를 변경하는 함수

 

상태

컴포넌트가 보유하고 관리하는 데이터

상태의 변경은 상태를 보유한 컴포넌트에서만 수행됨 (부모 컴포넌트)

=/= 무상태 컴포넌트: 자신의 상태가 없으므로 속성을 통해 부모 컴포넌트로부터 속성을 통해 전달받아야만 함

구분 상태 컴포넌트 무상태 컴포넌트
정의 컴포넌트가 자체적으로 상태(state)를 보유하고 관리 상태를 갖지 않고, 부모로부터 전달받은 props만 사용
데이터 관리 내부 상태를 직접 변경 가능 데이터 변경 불가, 화면은 props를 통해서만 업데이트
렌더링 상태 변경 시 리렌더링 발생, 화면 자동 갱신 props가 변경될 때만 리렌더링
역할 동적 데이터 관리 및 화면 업데이트 단순 화면 렌더링
예시 const [count, setCount] = useState(0); <Child name="홍경" age={30} />
장점 사용자 상호작용, UI 동적 변화 처리 가능 코드 단순, 재사용성 높음

 

4장

속성의 유효성 검증

PropTypes 

부모 컴포넌트에서 전달하는 props가 올바른 타입과 형식인지 확인하는 과정

 

리액트는 잘못된 프롭스가 전달되었을 때 경고를 표시해 주어, 런타임 에러를 방지하고 컴포넌트 사용을 안전하게 만들어줌

 

PropsType로 지정 가능한 유효성 검증 타입

1. PropsTypes.array: 배열 타입

2. PropsTypes.bool: 불리언 타입

3. PropsTypes.func: 속성 이용해 함수와 메서드 전달

4. PropsTypes.number: 숫자 타입

5. PropsTypes.object: 객체 타입

6. PropsTypes.string: 문자열 타입

 

복잡한 거

7. PropsTypes.instanceOf(Customer): Customer 클래스의 인스턴스인지 검증

8. PropsTypes.oneOf(['+', '*']: []에 포함된 값 중 하나인지 검증

9. PropsTypes.oneOfType([PropType.number, PropType.string]): []에 포함된 타입의 값인지 검증

10. PropsTypes.arrayOf(PropsTypes.object): 객체 배열인지 검증

 

리액트 이벤트

이벤트 처리 방법

HTML DOM 이벤트를 추상화하여 여러 브라우저에서 동일한 특성(attribute)을 이용할 수 있도록 이벤트 정규화함

 

리액트 이벤트 위임

성능 개선을 위해 모든 이벤트를 리액트 컴포넌트 트리가 렌더링 되는 루트 DOM 컨테이너 요소에 연결하고 이벤트를 위임 처리, 이벤트 발생하면 리액트는 루트 DOM 컨테이너에서 적절한 컴포넌트 요소를 연결하여 실행함

 

1. 리액트 앱이 브라우저에 렌더링 될 때

-> 루트 DOM 컨테이너 (<div id="root">)에 모든 이벤트를 연결

2. 사용자가 버튼, 입력창 등에서 이벤트를 발생시키면

-> 이벤트는 버블링을 통해 루트 컨테이너로 전달

3. 리액트는 내부적으로 이벤트가 어느 컴포넌트에서 발생했는지 확인

-> 연결된 이벤트 핸들러 호출

 

리액트 이벤트 설정 시 주의점

- 카멜 표기법 사용

- 함수나 메서드 연결 시 { } 보간법 사용.. 익명 함수를 { } 내부에 작성하여 호출 구문 작성

- DOM 요소가 아닌 컴포넌트에는 이벤트를 설정할 수 없음  

==> DOM 요소에만 이벤트 설정 가능. (소문자로 시작하는 HTML 태그들)

==> 컴포넌트는 props로 전달.

 

DOM 요소: button, input 등 HTML 태그와 1:1 대응되는 리액트 요소

컴포넌트 <Child />, <Header /> 같은 것.. 이벤트 설정 불가. 이벤트 전달이 필요하다면 props를 통해 핸들러를 전달해야 함

function Child({ onClick }) {
  return <button onClick={onClick}>버튼</button>;
}

<Child onClick={handleClick} />  // 올바른 사용

 

 

이벤트 적용 방법

1. 이벤트 핸들러 함수를 정의해서  { } 보간법 이용해 외부 함수 바인딩

const eventHandler = () => {
    //JSX 내부에서 외부 함수 바인딩
    <input type="text" ..., onChange={eventHandler} />
    
    // JSX 내부에서 익명 함수 바인딩
    <button onClick = { () => { ... } }>버튼</button>
}

 

2. 이벤트 핸들러 첫 번째 인자를 이용해 이벤트 아규먼트 값을 이용

import { useState } from "react";

function InputExample() {
  const [value, setValue] = useState(""); // 상태 선언

  const eventHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value); // 상태 업데이트
  };

  return <input type="text" value={value} onChange={eventHandler} />;
}

 

 

제어 컴포넌트

UI에서 입력 필드값이 상태(state)나 속성(props)에 의해 강하게 제어됨

속성이 변하지 않는 한 입력값은 변경 불가

입력값 변경하려면, 리액트 이벤트 핸들러 이용해 상태 변경해야 함.

 

비제어 컴포넌트

입력 필드의 값이 속성에 의해 제어되지 않음

사용자가 쉽게 변경할 수 있지만 입력한 값이 상태에 반영되지 않음.

사용자가 UI 입력 필드에 입력한 값을 알아내려면 브라우저의 HTML DOM에 직접 접근해야 하는 단점이 있음. p146

상태나 속성이 입력 필드를 제어하지 않지만 초기값을 부여할 수 있다. 

 

리액트에서 상태 변경하기 위해 세터 함수 사용.. 상태 변경하면 UI 갱신됨

세터함수의 기능

상태 변경 + 리액트에 상태가 변경되었음을 알려서 컴포넌트가 리렌더링 되도록 함

(리렌더링: 자식 컴포넌트들도 모두 다시 렌더링.. 가상 DOM에 DOM 요소를 쓰는 작업.

가상 DOM은 브라우저 DOM에 업데이트 작업을 줄임(렌더링 성능 개선을 위해 사용), 속상, 상태 변경 없으면 가상 DOM 쓰지 말자)

 

렌더링 최적화

자식 컴포넌트 중 속성이 변경된 것이 없다면 리렌더링 되지 않게 함.

속성 변경된 게 있으면 렌더링 한다.

 

컴포넌트

- 컨테이너 컴포넌트: 상태와 상태 변경, 비즈니스 로직을 처리하는 연산 기능이 있음.

- 표현 컴포넌트: 부모 컴포넌트로부터 props 전달받아 UI 렌더링함

 

 

5장

리액트 클래스 컴포넌트 

함수 컴포넌트보다 클래스 컴포넌트가 더 간결하고 렌더링 성능이 좋다.

 

생명 주기 메서드 (클래스 컴포넌트에서만 사용 가능)

생명 주기 메서드는 컴포넌트의 마운트, 업데이트, 언마운트의 경우 자동으로 호출되는 메서드를 말한다.

 

- 컴포넌트가 마운트 될 때

마운트란 컴포넌트를 처음으로 브라우저 화면(DOM)에 표시하는 과정. 컴포넌트가 처음 화면에 나타날 때 발생

 

1. constructor 생성자

this.state에 객체를 할당하여 컴포넌트의 상태를 초기화하기 위해 작성. 

생성자 내부에서는 상태를 초기화할 일이 없으면 생성자 작성하지 않아도 됨

 

타입스크립트에서는 constructor를 사용하면 this.state 속성의 타입을 지정할 수 없어서 constructor 를 사용하더라도 any 타입을 사용해야 하므로 권장하지 않는다.

대신에 클래스 내의 state 인스턴스 멤버를 지정하는 방법을 사용하는 경우가 더 많다.

아래 코드가 예시임

import { Component } from 'react'

type StateType = {
	name: string;
    age: number;
};

export default class Test extends Component<{ }, StateType> {
	state: StateType = { name: "홍길동", age: 20 };
    
    render() {
    	const { name, age } = this.state;
        return <div> {name}님의 나이 : {age}</div>;
    }
}

 

2. getDerivedStateFromProps 정적 메서드 (static 지정 필수)

컴포넌트 상태가 부모 컴포넌트로부터 전달받은 속성에 의해 달라지는 경우 사용

 

props는 전달받은 속성 객체이며 state는 컴포넌트의 기존 상태이다.

그리고 props를 이용해 새로운 상태를 만들어서 리턴한다. 아래 코드 예시

import { Component } from 'react'

type Props = { level: string };

type State = { discountRate: number; customerName: string };

export default class Child extends Component<Props, State> {
	state: State = { discountRate: 0, customerName: "홍길동" };
    
    static getDerivedStateFromProps(props: Props, state: State) {
    	let tempRate = 0;
        if (props.level === "GOLD") tempRate = 0.15;
        else if (props.level === "SILVER") tempRate = 0.1;
        else if (props.level === "BRONZE") tempRate = 0.05;
        else tempRate = 0.02;
        // 속성으로 전달받은 level 값에 따라서 discountRate 상태값이 달라진다.
        // 기존 상태는 변경하지 않아야 하므로 전개 연산자를 사용한다.
        return { ...state, discountRate: tempRate };
    }
    
    // render() 메서드
    render() {
    	return (
        	<div>
            	{this.state.customerName} 님의 할인율은 {this.state.discountRate * 100}% 입니다.
            </div>
        );
    }
}

 

- 정적 메서드: 클래스 자체가 호출. 객체 만들지 않아도 사용 가능

- 동적 메서드: 클래스로부터 만든 인스턴스(객체)가 호출

 

3. render 메서드

컴포넌트를 가상 DOM으로 렌더링하는 메서드

상태와 속성을 이용해 리액트 엘리먼트 등으로 렌더링하며, 함수 컴포넌트의 리턴문 부분에 해당한다.

상태와 속성이 변경되지 않았다면 같은 결과를 리턴해야 한다.

 

* 상태: 컴포넌트가 보유하고 관리하는 데이터

* 속성: 상위 컴포넌트에서 하위 컴포넌트로 데이터가 전달되는 방법

 

4. componentDidMount 메서드

컴포넌트의 마운트 완료 후, 브라우저 DOM의 트리까지 반영 후, 호출됨.

==> DOM이 완성된 후 실행해야 하는 초기화 작업에 적합하다.

 

- 컴포넌트가 업데이트될 때

1. getDerivedStateFromProps 정적 메서드 (컴포넌트가 마운트될 때와 같다)

컴포넌트 상태가 부모 컴포넌트로부터 전달받은 속성에 의해 달라지는 경우 사용

 

2. shouldComponentUpdate 메서드

렌더링 성능 최적화에 자주 사용함

- 전달되는 인자: 속성의 상태

- 리턴 값: 부울값(True, False) True이면 다음 단계의 생명주기 메서드가 호출됨, False이면 안 넘어감..

shouldComponentUpdate(nextProps: Props, nextState: State): boolean {

}

shouldComponentUpdate.. 메서드에서 깊은 비교를 하면 시스템 리소스 소모가 커서 렌더링 성능이 떨어짐. 

해결 방법: 불변성 유지, 라이브러리, 전개 연산자(spread operator) 사용

- 라이브러리 활용

* immer 라이브러리 -> 내부적으로 불변성 쉽게 유지 

* produce() 를 이용해 기존 데이터를 안전하게 변경하면서 새로운 객체 생성

produce란?

기존 상태를 직접 변경하지 않고, 새로운 상태 객체를 만들어 반환하는 함수

내부에서는 임시 드래프트(draft) 객체를 만들어서 자유롭게 수정 가능

import produce from 'immer'

const state = { count: 0 };

// 기존 상태를 직접 변경하지 않고 produce로 새 상태 생성
const nextState = produce(state, draft => {
	draft.count += 1;
}

console.log(state); // 기존 상태 그대로
console.log(nextState); // 새로운 객채 생성

 

- 전개 연산자 사용

const newArray = [...oldArray]; // 배열 복사
const newObj = {...oldObj};     // 객체 복사

 

3. getSnapshotBeforeUpdate 메서드

실행 시점: render() 메서드가 호출되어 가상 DOM으로의 쓰기 작업이 완료된 후, 브라우저 DOM 업데이트되기 전이다.

변경 전 DOM 상태 정보를 획득해서 스냅샷으로 리턴해서 componentDidUpdate() 메서드의 3번째 인자 받아낼 때 유용하다.

getSnapshotBeforeUpdate(prevProps, prevState) {
	// 이 메서드의 리턴값이 componentDidUpdate 메서드의 세 번째 인자로 전달된다.
}

 

4. componentDidUpdate()

실행 시점: 브라우저 DOM 까지 업데이트 완료 후, 컴포넌트가 업데이트되고 DOM 변경하고자 할 때 사용

현재의 속성과 상태를 이전의 속성, 상태와 비교해서 차이가 있다면 외부 API를 요청하는 등의 작업을 수행하도록 활용할 수 있음.

 

- 컴포넌트가 언마운트될 때

1. componentWillUnmount 메서드

컴포넌트 트리에서 삭제되기 직전에 실행.

주로 componentDidMount 와 짝을 이루어 사용한다. p185 p192

예를 들어, 

웹소켓을 이용해 서버에 연결하는 경우, 컴포넌트가 마운트될 때 componentDidMount 메서드에서 외부 서버나 리소스에 연결하면 컴포넌트가 언마운트될 때는 componentWillUnmount 메서드에서 서버와의 연결을 해제한다.

외부 리소스에 연결한 경우에는 반드시 componentWillUnmount 에서 깨끗하게 연결을 해제해야 한다. 그렇지 않으면 메모리 누수가 발생할 수 있고, 불필요한 외부 리소스 연결이 남아있게 된다.

 

 

생명 주기 메서드의  렌더링 에러 처리

componentDidCatch, getDerivedStateFromError 생명 주기 메서드는 평상 시 실행되지 않고 컴포넌트 자신이 포함하고 있는 하위 컴포넌트 트리에서 자바스크립트 에러가 발생했을 때 에러 화면 대신 fallback UI를 보여주고 에러를 잡아낼 수 있는 기능을 제공한다.

에러 처리를 수행하는 컴포넌트를 에러 경계 컴포넌트 라고 부름.

 

가상 DOM

브라우저 DOM의 트리구조를 그대로 브라우저 메모리 상에 구현한 DOM 트리 객체

사용 이유?

JS 언어로 화면을 브라우저에 다시 그려내는 작업(렌더링)이 느려서 렌더링 최적화를 위해 사용됨

 

(DOM은 웹 페이지를 구성하는 태그들을 자바스크립트 언어가 이용할 수 있게 만든 트리 구조의 객체 모델이다.)

 

브라우저 화면은 2단계를 거쳐서 렌더링 됨.

1. reflow(=relayout)

: 렌더링 할 DOM 트리를 새롭게 만들고, HTML 요소들의 위치와 크기를 계산해 배치하는 단계

2. repaint

: reflow가 완료되고 나면, HTML 요소에 스타일 요소를 입히고 UI를 그려내는 repaint 단계가 실행됨

 

브라우저 DOM을 조작할 때마다 reflow, repaint 단계가 실행되면서 브라우저 리소스가 소모되고 렌더링 성능이 저하되어서 브라우저 DOM의 조작은 바람직하지 않음.

=> 가상 DOM을 이용하자

 

가상 DOM을 이용하면 가상 DOM 수준에서 이전 버전과 현재 버전의 DOM 트리를 비교해서 차이가 나는 부분만 브라우저 DOM에 업데이트하기 때문에 브라우저 DOM 수준에서 불필요한 reflow, repaint 작업을 줄일 수 있음.. 이 과정을 '조정'이라고 함.

 

key의 특성

컴포넌트 내부에서 반복적으로 자식 컴포넌트와 요소를 렌더링 할 때 지정.

리스트의 항목 변경을 추적하기 이해 key 특성을 사용한다.

key의 특성은 배열 데이터(상태, 속성)를 이용해 반복적인 렌더링을 할 때 각 항목의 고유 키가 됨.

- key를 지정하지 않으면 배열 데이터의 어떤 값이 어느 요소에 렌더링 됐는지를 추적할 방법이 없으므로 리스트 전체를 다시 렌더링 한다.

- key 특성을 지정하면 배열 데이터의 어떤 값이 어느 요소에 렌더링 됐는지를 key를 비교해서 추적이 가능하다. => 리스트 전체를 다시 렌더링 하지 않아도 됨.

 

* 상태(state)란 컴포넌트가 자체적으로 보유하고 관리하는 데이터, 시간에 따라 변할 수 있음. 동적 데이터

* 속성(props)이란 컴포넌트 외부에서 전달되는 데이터. 읽기 전용으로 자식 컴포넌트는 직접 변경이 불가하다. 부모 컴포넌트에서 값을 전달하고 자식 컴포넌트가 이를 받아 화면에 그리는 역할 

 

렌더링 성능 최적화에 기본적으로 필요한 요구사항은 불변성을 가지도록 하는 것.

 

pureComponent

표현 컴포넌트에서 사용하기 적합함.

퓨어 컴포넌트는 shouldComponentUpdate 생명 주기 메서드가 상태와 속성을 얕은 비교를 하도록 이미 구현된 클래스이다.

따라서 명시적으로 shouldComponent() 메서드를 작성할 필요가 없다. 이런 특성 때문에 퓨어 컴포넌트는 표현 컴포넌트에서 사용하기 적합하다.

 

불변성이란? 

값이 한 번 정해지면 변경되지 않는다.

즉, 객체나 배열을 직접 수정하지 않고 새로운 객체나 배열을 만들어서 교체하는 방식.

... 전개 연산자(=스프레드 연산자) 사용

immer 라이브러리의 produce 사용: draft 객체..

 

불변성 중요한 이유

리액트는 state가 변경될 때 렌더링을 다시 수행한다.

"상태가 바뀌었는지" 얕은 비교로 판단함. ==> 참조가 바뀌었는지 본다.

'React' 카테고리의 다른 글

[React] 리액트 개념 정리 #6  (0) 2025.10.29
[React] 리액트 개념 정리 #5  (0) 2025.10.28
[React] 리액트 개념 정리 #4  (0) 2025.10.26
[React] 리액트 개념 정리 #3  (0) 2025.10.26
[React] 리액트 개념 정리 #1  (0) 2025.10.26