[React] React Virtual Dom & useDef

김유진·2022년 7월 6일
1

React

목록 보기
9/64

1. DOM과 Tree에 대하여


Real Dom의 사진이다
위의 과정은 우리가 일반적으로 웹을 보게 되는 과정을 나타낸 과정입니다. html, css를 사용하여 만든 파일이 브라우저를 통하여 보여지는 과정을 이렇게 나타내는 것이죠.

그런데 문제가 있어요. Render Tree가 바뀔 때마다 전부, 모든 트리를 다시 생성해야 합니다. 좋아요를 누르면, 좋아요 부분만 바뀌어야 하는데 모두 랜더링을 해야 한다?? 너무 무식하다!!

그리고 요즘에는 SPA(Single Page Application) 하나의 페이지에서 많은 컨텐츠들을 사용하다 보니 이러한 랜더링 문제가 중요하게 되었습니다. 효율적인 DOM 변화의 필요성이 부각이 된 것이지요.

그러한 대안으로 Virtual Dom이 등장하게 되었고, 이것을 가장 잘 사용하고 있는 것이 React이다! Virtual Dom은 변화가 발생하게 되면, Diffing 알고리즘을 통하여 어떤 부분이 변화하였는지 알아채고, 그 전과 비교하였을 때 달라진 부분만을 바꿔 리랜더링 하는 방식으로 진행한다.

2. Virtual DOM이 어떻게 작동할까?

우리가 작성하게 되는 jsx 파일은 사진과 같이 마치 자바스크립트 객체로 저장된다. 이러한 부분은 메모리로 저장이 되고, 곧바로 랜더링되지 않습니다. 만약 Virtual DOM이 바뀌게 된다면, 이것도 마찬가지로 자바스크립트 객체 형식으로 children에 추가되고, 랜더링 되지 않고 메모리에서 동작하고, 이후 한번에 달라진 부분을 적용합니다.

그럼 React에서 어떻게 자세히 동작하는데?

  1. Props를 변화 시키거나
  2. setState를 사용한다.

Component는 Re-render를 하고, 메모리에서 변화된 부분을 감지하여, 달라진 부분만 적용하게 된다.

3. Virtual Dom을 효율적으로 컨트롤 하는 useDef

우리가 javascript에서 DOM을 컨트롤 할때는 어떤 class를 가져올 것인지, 어떤 id를 가져올 것인지..일일이 생각하여 getElementById, querySelector과 같은 선택자를 사용하였다. 그러나, React에서는 Real Dom 생성 시에, class name, id를 확실히 가져올 거라는 보장이 없다. (그렇다고 아예 사용 안되는 건 아님) 그리고 아이디를 통하여 가져오게 된다면, 해당 컴포넌트 외의 다른 것을 가져오게 되어 React Flow에 어긋나는 상황이 발생할 수 있다. 그래서 React에서는 useDef를 이용하게 된다.

아니 이렇게 자기 맘대로 class 내용이 저장되는데 어떻게 직접 가져오겠어!

useRef를 사용하기 위해서는 import를 해 주어야 한다.

import React, {useRef} from 'react;

이렇게 하고, useRef를 할당해보자.

function InputPost({ onChange, title, contents }) {
  const titleInput = useRef();
  const contentsInput = useRef();
}; 

위 코드는 useRef를 할당하는 코드이다.

<TitleInput
  ref = {titleInput}
/>

이렇게 연결을 해 주고,

const onKeyUp = (e) => {
  if (e.key ==='Enter'){
    contentsInput.current.focus();
  }
};
useEffect(() => {
  titleInput.current.focus();
}, []);

useRef를 사용하는 모습이다. 현재의 포커스를 달라는 말이라고 바로 해석할 수 있다.

4. UseRef를 통하여, 바로 키보드에 입력을 하여도 제목이 입력되는 focus를 설정해보자.

원래 프로젝트에서 내가 글을 입력하기 위해서는 마우스를 가져다 대서 입력하는 부분을 클릭해야 했었다. 그런데 글을 쓰러 들어가자마자, 자동으로 입력 커서가 깜빡이고 있으면 얼마나 좋을까!! 이때 UseRef를 이용하는 것이다.
기존의 WritePost를 분리하여, InputPost부분만 따로 만들어보자.

import React, { useState, useRef, useEffect} from 'react';
import { ContentsInput, TitleInput} from './styledComponent';


function InputPost({onChange, contents, title}) {
    const titleInput = useRef();
    const contentsInput = useRef();
    useEffect(() =>{
        titleInput.current.focus();
    },[]);
    const onKeyUpplz = (e) => {
        if(e.key === 'Enter'){
            contentsInput.current.focus();
        }
    }
    return (
        <>
            <TitleInput 
                name = "title"
                type = "text"
                value = {title}
                placeholder="제목을 입력해주세요. (15자 이내)"
                onChange = {onChange}
                ref = {titleInput}    
                onKeyUp = {onKeyUpplz}
            />
            <ContentsInput 
                name = "contents"
                value = {contents}
                cols="30" 
                rows="10"
                onChange = {onChange}
                ref = {contentsInput}
            ></ContentsInput>
        </>
    );
};

export default InputPost;

전체 코드는 다음과 같으나, 하나하나 뜯어서 꼭꼭 씹어 먹자.
일단 onChange, contents, title은 props 값으로 새로 받아왔다. (필요하기 때문)
먼저, useRef를 import한다.

import React, { useState, useRef, useEffect} from 'react';

그리고, useRef를 할당한다.

const titleInput = useRef();
const contentsInput = useRef();

여기서 titleInput은 제목 입력 부분, contentsInputs은 글상자 입력 부분이다. 각 컴포넌트로 이동해서 useRef를 사용한다고 이야기해두자~

return (
        <>
            <TitleInput 
                name = "title"
                type = "text"
                value = {title}
                placeholder="제목을 입력해주세요. (15자 이내)"
                onChange = {onChange}
                ref = {titleInput}    
                onKeyUp = {onKeyUpplz}
            />
            <ContentsInput 
                name = "contents"
                value = {contents}
                cols="30" 
                rows="10"
                onChange = {onChange}
                ref = {contentsInput}
            ></ContentsInput>
        </>
    );

그리고 useEffect에서 진짜로 사용해보자. 여기서 onKeyUp을 활용하여, 해당 키가 발현되었을때 실행할 함수를 명시하고 있다. 아래에서 onKeyUpplz함수를 작성해보자.

useEffect(() =>{
        titleInput.current.focus();
    },[]);

현재 들어오자마자 커서가 제목창에서 깜빡깜빡한다.
그런데, 나는 엔터를 누르게 되면 글상자로 넘어가고 싶은데, 이건 어떻게 하나요?

const onKeyUpplz = (e) => {
     if(e.key === 'Enter'){
         contentsInput.current.focus();
     }
 }

이렇게 사용하면 되지~ 해당 키를 누르고 떼면, 이벤트가 발생하는데, 그 키가 엔터키라면 현재 커서 깜빡이라는 거 아녀~~~ 이제 댓글창도 똑같이 만들어 볼 수 있었다.

ShowPost.jsx

...
const replInput = useRef();
useEffect(() => {
 setTimeout(() => {
     setPost(postData);
     setPostLoading(false);
 }, 1000);
 replInput.current.focus();});
...
const onChange = (e) => {
  setRepl(e.target.value);
}
...
 <WriterDiv>
          <ReplInput onChange = {onChange} value = {repl} ref = {replInput}></ReplInput>
          <ReplSubmitDiv>
            <span>입력</span>
          </ReplSubmitDiv>
        </WriterDiv>
      </PostSection>
...

0개의 댓글