Roen의 iOS 개발로그

React 기초3 : Event Listener & Handler, React와 DOM, state

by Steady On

📣 이 자료는 Udemy의 React 완벽 가이드 강의와 React 공식문서를 참고하여 작성되었습니다.

그리고 개인적으로 공부하고 이해한 내용을 바탕으로 정리하는 용도로 작성되었으므로 이해를 돕기위한 예시는 최소화하였습니다.

 

Event Listener - Event Handler

이벤트 리스너는 사용자가 어떤 이벤트를 일으켰을 때 그 이벤트를 캐치하는 것이고, 이벤트 핸들러는 그 이벤트가 일어났을 때 실행될 명령을 말한다. 

리액트는 모든 기본이벤트를 on으로 시작하는 props(ex. onClick)로 노출한다. 그리고 JSX에 그 on으로 시작하는 props를 추가하는 것으로 이벤트 리스너를 만들 수 있다. ex) <button onClick={}></button>

그리고 이 이벤트 리스너에 연결되는 것이 이벤트 핸들러이다. 모든 이벤트 핸들러 props는 값으로 함수가 필요하고, on props에 대한 값으로 전달된 함수는 이벤트가 발생했을 때 실행된다. 함수는 짧은 경우 익명의 화살표 함수로 바로 넣어줄 수도 있지만, 대부분의 경우 따로 작성해서 "함수명"을 써준다. 이때 주의할 점은 HTML에서는 onClick="clickHandler()"로 써주지만, 리액트에서는 onClick={clickHandler}으로 "함수의 이름만" 써준다는 점이다. 만약 여기서 괄호가 추가되면 어떻게 될까?

앞의 글에서 리액트는 코드들을 브라우저로 옮겨서 분석하고, 변환해서 브라우저에 render한다고 했다. 만약 괄호가 붙어있다면, 클릭했을 때 실행되는 것이 아니라 JSX코드가 분석되어 평가될 때 실행될 것이다. 그래서 이벤트 핸들러에 단순히 함수를 지정하기만 하는 것이고, 그렇게 해야 의도한 대로 이벤트가 일어났을 때 함수가 실행될 수 있다.

이벤트 핸들러는 보통 const clickHandler = (event) => {} 이렇게 정의하는데, 이때 매개변수로 받는 event는 우리가 따로 신경쓸 필요가 없다. 이벤트 리스너가 자동으로 onClick이면 클릭된 그 요소를, onChange면 어떤 변화가 있는 그 요소를 가져다준다. 

그럼 이벤트 리스너로 클릭 이벤트를 받아서 어떤 화면에 출력되는 값을 바꾼다고 생각해보자. 어떤 변수에 저장된 출력값을 이벤트 핸들러는 클릭 이벤트가 들어오면 어떤 임의의 값으로 재할당한다. 그런데 이벤트 리스너로 클릭 이벤트를 받아도 DOM에 표기된 출력값은 바뀌지는 않는다. 왜그럴까? 

 

React가 DOM을 그리는 방법(가상DOM)

*DOM : Document Object Model(문서객체 모델), html 단위 하나하나를 객체로 생각하는 모델, div라는 객체 아래에 하위의 어떤 객체들이 계속 가지를 뻗어 이어지기 때문에 트리구조를 가진다.

 

React는 React 기초1에서 말했듯 index.js 파일에서부터 모든 것을 시작한다. 실행을 한번 따라가보면, index.js에서 'index.html의 root라는 id를 가진 태그에 App.js에 있는 App 컴포넌트를 render하겠다.' 라고 되어있다. 그럼 리액트는 이제 App.js로 넘어간다. 그리고 App 컴포넌트가 return 하는 것을 분석하기 시작한다. 그렇게 컴포넌트들을 하나하나 따라가면서 실행하고 평가해서 DOM을 그려나간다. 근데 DOM의 렌더링을 드디어 끝냈는데 트리 중 하나가 수정되면 어떻게 될까? 수정이 될 때마다 모든 컴포넌트를 다시 재평가 하는 것은 너무 비효율적이다. 그래서 등장한 것이 가상DOM이다.

가상DOM은 메모리 상에서 돌아가는 가짜 DOM이다. 처음에  페이지가 모두 렌더링 되면, 그 상태를 메모리에 복사해서 저장해둔다. 이 복사본이 가상 DOM이다. 원본 DOM에서 '어떤 변화'가 일어나면, 리액트는 변화가 일어난 컴포넌트와 그 자식 컴포넌트들을 리렌더링을 해서 이 복사본을 수정한다. 그리고 수정된 복사본과 원본 DOM을 비교해서 뷰가 달라진 부분만 원본 DOM을 업데이트한다. 그럼 리액트는 '어떤 변화'가 있어야 DOM을 '새로' 렌더링 할까?

  1. props가 바뀔 때 (부모가 자식한테 주는 정보가 바뀌었을 때)
  2. state가 바뀔 때 (내가 가진 정보가 바뀌었을 때)
  3. 부모 컴포넌트가 업데이트 되었을 때(= 리렌더링 했을 때)
  4. 강제로 업데이트 했을 경우(forceUpdate())

이렇게 4가지 경우가 있다. 3번의 상황은 부모 컴포넌트의 어떤 state가 바뀌면, 그 컴포넌트 뿐만 아니라 그 하위에 있는 자식 컴포넌트들도 다함께 리렌더링 된다는 뜻이다.

 

그럼 위의 질문에 답을 해보자. 이벤트 리스너와 핸들러를 통해서 어떠한 값을 임의로 변수에 넣어서 바뀌도록 했는데 왜 화면에 반영되지 않을까? '화면에 출력되는 어떤 변수에 임의의 값을 재할당한다'는 것은 위의 업데이트가 일어나는 상황 4가지중 어디에도 해당되지 않는다. 만약에 업데이트가 일어난다고 하더라도 이 변수는 다시 다뤄져서 초기값으로 돌아갈 뿐이다. 그래서 알아야 하는 것이 State이다.

 

state

state는 일반적인 변수와는 다르게 메모리 어딘가에서 리액트로 관리되는 특별한 변수이다.

state 사용을 위해서는 일단, React 라이브러리에서 useState라는 함수를 import 해야한다.

import React, {useState} from 'react

이렇게 특정 함수를 따로 import 하는 것을 named import라고 하고, 추가할 라이브러리 이름 뒤에 ,와 중괄호를 사용한다.

❗️참고! useState는 리액트 훅이고, 모든 훅들은 이름이 use로 시작한다. 훅들은 일반적인 함수처럼 호출하는 것으로 사용을 하는데, 꼭! 반드시! 컴포넌트 함수 안에서 직접적으로 호출되어야 한다. 컴포넌트 함수 밖이나 컴포넌트 함수안의 중첩된 함수 안에서는 호출할 수 없다. 

❗️useState

type : 함수

사용form : const [변수명, set변수명] = useState(변수의 초기값)

💬 state를 정의할때 const(상수형)을 사용하는 이유는 state에 직접적으로 값을 재할당하지는 않기 때문이다.

매개변수 : 정의할 변수의 초기값

Return : [변수 자체, 변수를 업데이트할 함수]의 배열

특징 : 사용할 컴포넌트에서 꼭 named import 필요! 컴포넌트 함수안에서 호출 가능!(밖이나 중첩 함수안에서는 호출 불가능)

 

➕ set변수명

type : 함수

사용 form : set변수명(업데이트할 값)

매개변수 : 변수의 state를 업데이트할 값(재할당 하는 것이 아님. 실제로 변수를 console에 찍어보면 초기값이 찍힘)

Return : none

비고 : 중첩 함수 안에서 사용가능. 보통 이벤트 핸들러 내에서 사용됨

 

useState 함수로 생성된 state는 메모리 어딘가에서 리액트로 관리된다. 그리고 setState를 통해서 state를 업데이트하면, useState에 해당 정보가 전달되고, useState는 React에게 state가 등록된 컴포넌트의 재평가가 필요하다고 알린다. 그럼 React는 해당하는 컴포넌트 함수를 다시 실행하고, JSX를 재평가하여 감지된 변화를 화면에 렌더링 하는 것이다.

 

블로그의 정보

Roen의 iOS 개발로그

Steady On

활동하기