7 분 소요

2022. 08. 11. 수업내용 정리

react 개요 및 기본개념


강의에 들어가기 앞서 설치나 환경설정은 Node.JS 강좌 class07에서 다뤘으므로 생략하겠습니다.



  • react

    사용자 인터페이스를 구축하기 위한 선언적이고 효율적이며 유연한 javascript 라이브러리.

    컴포넌트라고 불리는 작고 고립된 코드의 파편을 이용하여 복잡한 UI를 구성하도록 돕습니다.

    3가지의 대표적인 특징이 있습니다.

    1. JSX

      자바스크립트 안에서 HTML 문법을 사용해서 view를 구성할 수 있게 도와주는 자바스크립트 문법입니다.

      • 예시
      class HelloMessage extends React.Component {
        render() {
          return (
            <div>
              Hello {this.props.name}
            </div>
          );
        }
      }
      



    2. Component 기반

      리액트는 컴포넌트기반 라이브러리로 여러 부분을 분할해서 코드의 재사용성과 유지보수성을 증가시켜 줍니다.

      예로 기존의 HTML코드로 작업할 경우 긴 코드에서 수정할 부분을 찾아야하지만, 컴폰너트를 사용하는 리액트의 경우 잘못된 부분만 찾아 바로 수정할 수 있으므로, 다른 코드에 영향을 줄 염려가 없으며 수정이 용이합니다.



    3. Virtual DOM

      가상 돔은 기존 DOM의 한계를 탈피하기 위해서 나온 대안입니다.

      react


      DOM의 구조는 트리구조로 HTML과 같습니다.

      만약 어떤 DOM의 요소를 하나 수정하는 함수를 만들고 실행 시킬 때, 랜더트리를 재생성하고(그러면 모든 요소들의 스타일이 다시 계산 됩니다.)

      레이아웃을 만들고 페인팅을 하는 과정이 다시 반복됩니다.

      여기서 문제점은 복잡한 SPA(Single Page Application) 개발을 통해서 DOM의 요소를 많이 수정한다고 할 때, 불필요한 연산이 매번 일어나는 문제가 발생하게 되는 것입니다.

      렌더트리가 매번 재생성되는 것보다는 한 번에 작업이 진행되는 것을 더 선호할 것입니다.

      이 문제를 해결하기 위해 가상 DOM이라는 개념이 등장합니다.


      가상 DOM은 변화를 가상 DOM에서 미리 인지해 변화시킵니다.

      이 작업은 실제 DOM이 아니기 때문에 렌더링도 되지 않고 연산 비용이 비교적 실제 DOM보다는 적습니다.

      그 가상 DOM의 변화를 마지막에 실제 DOM에게 던져주어, 모든 변화를 한번에 랜더링 되게 합니다.

      실제 DOM을 직접 변경할 수 있지만, 그 작업이 굉장히 복잡한 작업이므로, 가상 돔에서 미리 최적화를 한 번 해주는 것이 효율적이기에 가상 DOM을 사용하는 것으로 이해할 수 있습니다.



  • CRA 파일 분석

    Nodejs 설치 후 CMD 창에서 ‘npx create-react-app {프로젝트 폴더 명}’을 작성 후 실행하면 작업에 필요한 모든 모듈 및 여러 라이브러리로 구성된 리액트 작업 폴더가 생성되고 VSC에 들어가면 아래의 구성을 확인할 수 있습니다.

    react


    이번 정리에서는 public, src 폴더에 대해 알아보겠습니다.


    1. public

      public은 가상 DOM을 사용하는 리액트는 실제 DOM(가상 DOM이 들어갈 빈 껍데기 HTML)이 필요하기에 실제 DOM이 위치한 폴더입니다.

      • favicon.ico : 페이지 아이콘 이미지 파일입니다.

      • index.htm : 가상 DOM이 들어가기 위한 빈 껍데기 HTML 파일입니다.

      • manifest.json : 웹 앤 매니페스트는 사용자가 앱을 볼 ㅇ것으로 예상되는 개발자가 제어하고, 사용자가 시작할 수 있는 항목을 지시하고, 시작 시의 모습을 정의할 수 있는 JSON파일입니다.

      • robots.txt : 웹 사이트에 웹 크롤러같은 로봇드르이 접근을 제어하기 위한 규약입니다.



    2. src 폴더

      src는 리액트 개발이 이루어지는 메인 폴더입니다.

      여러 파일이 있지만, 여기에서는 index.js, App.js 위주로 알아보겠습니다.

      • index.js

        App.js에서 생성된 리액트 코드를 index.js에서 불러온 후, public에 있는 index.html의 id가 root인 곳에다가 넣는 코드입니다.

        import React from 'react';
        import ReactDOM from 'react-dom/client';
        import './index.css';
        import App from './App';
        import reportWebVitals from './reportWebVitals';
               
        const root = ReactDOM.createRoot(document.getElementById('root'));
        root.render(
          <React.StrictMode>
            <App />
          </React.StrictMode>
        );
               
        // If you want to start measuring performance in your app, pass a function
        // to log results (for example: reportWebVitals(console.log))
        // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
        reportWebVitals();
        


      • App.js

        리액트 코드를 생성하는 부분입니다.

        import { Component } from 'react'
        import './App.css'
               
        class App extends Component {
          render() {
            return (
              <div>
                       
              </div>
            )
          }
        }
               
        export default App
        


        App이라는 클래스 생성 후, 리액트 컴포넌트를 상속받습니다.(extends Component)

        그렇게 되면 리액트 컴포넌트 매서드를 사용할 수 있게 됩니다.

        render() 메소드는 리액트 컴포넌트인데, 화면에 html view를 생성해주는 역할을 합니다.

        return으로 받는 값들은, 나중에 html코드로 바뀌게 됩니다.

        그렇게 생성된 App 클래스를 export 문법을 이용해서 내보냅니다.

        결과적으로 리액트를 사용하면 html코드를 여러 개의 리액트 파일로 분할해서 작업을 할 수 있으므로 협업과 유지보수의 측면에서 매우 유용합니다.


  • props & state

    데이터를 다룰 때 사용하는 개념입니다.

    props, state 모두 하위 컴포넌트에 상속이 가능합니다.


    • state

      하나의 컴포넌트가 가질 수 있는 변경 가능한 데이터입니다.

      컴폰넌트를 렌더링 할 때 새로운 데이터를 생성해야 한다던지, 기존의 데이터를 참고해서 새로운 데이터를 만들어야 할 때 사용할 수 있습니다.

      import {Component} from 'react';
          
      class App extends Component {
          state = {
              hello: 'hello app js!'
          }
              
          render() {
              return<div className='App'>{this.state.hello}</div>; // 변수를 넣을  {} 사용!
          }                                                        // {this.state.hello} 'hello app js' 문자열이 들어있는 state 변수
      }
          
      export default App;
      

      웹페이지 화면 ▼

      react


      이번엔 state에 변화를 주고 이벤트를 적용시킨 예제를 보겠습니다.

      // count 예제
      import {Component} from 'react';
          
      class App extends Component {
          state = {
              count: 0
          };
              
          countUp = () => {
              this.setState({
                  count: this.state.count + 1
              });
          };
              
          render() {
              return(
                  <div className='App'>
                  	<div>
                  		{this.state.count}
                  	</div>
      				<button onClick={this.countUp}>
                          count up!
                      </button>
          		</div>
      		)    
      	} 
      }
          
      export default App;
      


      웹페이지 화면 ▼(3번 ‘count up!’버튼을 누른 상태)

      react


      button을 클릭하면, ‘countUp’이라는 메소드가 실행되고, this.setState 메소드를 통해 state인 ‘count’의 데이터가 1씩 증가합니다.

      HTML에서처럼 onclick이벤트를 사용할 수 있지만 한 가지 다른 점은 이벤트들의 이름을 camelCase 형식으로 사용해야 하는 점입니다.

      추가적으로 JSX 파일이므로 HTML 파일로 바뀌는 과정에서 함수가 실행될 수 있습니다.

      기존의 onclick이벤트에서 처럼 ‘this.functionName()’형식을 작성하는게 아니라 ‘this.functionName’ 형식으로 작성해야 합니다.


    • props

      상위 컴포넌트가 하위 컴포넌트에 값을 전달할 때 사용합니다.(단방향)

      props는 현재 컴포넌트 내에서 변경이 불가능합니다.(읽기 전용)

      프로퍼티에 문자열을 전달할 때는 큰 따옴표(““)를, 문자열 외의 값을 전달할 때는 중괄호({})를 사용합니다.


      새로운 컴포넌트 myName을 생성했습니다.

      import { Component } from 'react';
          
      class MyName extends Component {
        render() {
          return (
            <div>
              안녕하세요!  이름은 <b>{this.props.name}</b> 입니다.
            </div>
          );
        }
      }
          
      export default MyName;
      

      상위 컴포넌트 App.js에서 상속받은 props 값은 this. 키워드를 통해 참조할 수 있습니다.

      name이라는 props를 보여주도록 설정했습니다. 이제 이 컴포넌트를 사용해보겠습니다.


      import { Component } from 'react';
      import MyName from './MyName';
          
      class App extends Component {
        render() {
          return (
            <MyName name="리액트" />
          );
        }
      }
          
      export default App;
      

      import를 통해 컴포넌트를 불러온 후 props 값은 불러온 컴포넌트 내부에 name="react" 형식으로 작성해줍니다.

      그러면 아래와 같은 결과를 볼 수 있습니다.

      웹페이지 화면 ▼

      react


      가끔씩 실수로 props를 빠뜨린 경우 defaultProps를 사용해 props의 기본값을 설정할 수 있습니다.

      import React, { Component } from 'react';
          
      class MyName extends Component {
        static defaultProps = {
          name: '기본이름'
        }
        render() {
          return (
            <div>
              안녕하세요!  이름은 <b>{this.props.name}</b> 입니다. //  안녕하세요!  이름은 기본이름입니다. 
            </div>
          );
        }
      }
          
      export default MyName;
      

      아래의 형태로도 설정할 수 있습니다.

      import React, { Component } from 'react';
          
      class MyName extends Component {
        render() {
          return (
            <div>
              안녕하세요!  이름은 <b>{this.props.name}</b> 입니다. //  안녕하세요!  이름은 기본이름입니다. 
            </div>
          );
        }
      }
          
      MyName.defaultProps = {
        name: '기본이름'
      };
          
      export default MyName;
      



  • life cycle

    리액트는 컴포넌트 기반의 View를 중심으로 한 라이브러리입니다.

    그렇기에 각각의 컴포넌트에는 라이프사이클 즉, 컴포넌트의 수명 주기가 존재합니다.

    컴포넌트의 수면은 보통 페이지에서 렌더링되기 전인 준비과정에서 시작해 페이지에서 사라질 때 끝납니다.

    react


    라이프 사이클은 위 그림과 같이 총 9개가 존재합니다.

    크게 세가지 유형으로 나눌 수 있는데, 생성될 때/ 제거될 때/ 업데이트할 때로 나눌 수 있습니다.

    이를 리액트에서는 마운트, 언마운트, 업데이트라고 표현합니다.

    마운트는 DOM이 생성되고 웹 브라우저 상에서 나타나는 것을 뜻하고, 언마운트는 DOM에서 제거되는 것을 뜻합니다.

    업데이트는 4가지 상황에서 발생합니다.

    1. props가 바뀔 때
    2. state가 바뀔 때
    3. 부모 컴포넌트가 리렌더링 될 때
    4. this.forceUpdate로 강제로 렌더링을 트리거할 때



    • 라이프 사이클 메서드

      1. constructor

        자바와 마찬가지로 컴포넌트를 생성할 때 처음으로 실행됩니다.

        이 메서드에서는 초기 state를 정할 수 있습니다.

        // Class
        class Example extends React.Component {
            constructor(props) {
                super(props);
                this.state = { count: 0 };
        }
        

        클래스형에서는 초기 state를 정할 때 constructor를 사용해야 합니다.

      2. getDerivedStateFromProps

      이 메서드는 리액트 16.3버전 이후에 생긴 메서드이다.

      props로 받아 온 값을 state에 동기화시키는 용도로 사용하며, 컴포넌트가 마운트될 때와 업데이트 될 때 호출됩니다.

         // Class
         class Example extends React.Component {
           static getDerivedStateFromProps(nextProps, prevState) {
             if (nextProps.value !== prevState.value) {
               return { value: nextProps.value }
             }
             return null
           }
         }
      



      1. shouldComponentUpdate

        이 메서드는 props나 state를 변경했을 때, 리렌더링을 할지 말지 결정하는 메서드입니다.

        이 메서드에서는 반드시 true나 false를 반환해야합니다.

        이 메서드는 오직 성능 최적화만을 위한 것이며 렌더링 목적을 방지하는 목적으로 사용하게된다면 버그로 이어질 수 있습니다.

        // Class
        class Example extends React.Component {
          shouldComponentUpdate(nextProps) {
            return nextProps.value !== this.props.value
          }
        }
        



      2. render

        이는 가장 기초적인 메서드이기도하고 가장 중요한 메서드이기도 합니다.

        컴포넌트를 렌더링할 때 필요한 메서드로 유일한 필수 메서드이기도 합니다.

        함수형 컴포넌트에서는 render를 안쓰고 컴포넌트를 렌더링할 수 있습니다.

        class Example extends React.Component {
          render() {
            return <div>컴포넌트</div>
          }
        }
        



      3. getSnapshotBeforeUpdate

        이 메서드는 render에서 만들어진 결과가 브라우저에 실제로 반영되기 직전에 호출됩니다.

        공식문서의 말을 따보자면 이 메서드에 대한 사용 예는 흔하지 않지만, 채팅 화면처럼 스크롤 위치를 따로 처리하는 작업이 필요한 UI 등을 생각해볼 수 있다고합니다.

        class Example extends React.Component {
          getSnapshotBeforeUpdate(prevProps, prevState) {
            if (prevProps.list.length < this.props.list.length) {
              const list = this.listRef.current
              return list.scrollHeight - list.scrollTop
            }
            return null
          }
        }
        



      4. componentDidMount

        이 메서드는 컴포넌트를 만들고 첫 렌더링을 마친 후 실행합니다.

        class Example extends React.Component {
            componentDidMount() {
                ...
            }
        }
        



      5. ComponentDidUpdate

        이것은 리렌더링을 완료한 후 실행합니다.

        업데이트가 끝난 직후이므로, DOM관련 처리를 해도 무방합니다.

        // Class
        class Example extends React.Component {
            componentDidUpdate(prevProps, prevState) {
                ...
            }
        }
        



      6. componentWillUnmount

        이 메서드는 컴포넌트를 DOM에서 제거할 때 실행합니다.

        componentDidMount에서 등록한 이벤트가 있다면 여기서 제거 작업을 해야합니다.

        // Class
        class Example extends React.Component {
            coomponentWillUnmount() {
                ...
            }
        }
        



      7. componentDidCatch

        마지막으로 맨 위의 사진에는 보이지 않지만 componentDidCatch라는 메서드가 존재합니다.

        이 메서드는 컴포넌트 렌더링 도중에 에러가 발생 했을 때 애플리케이션이 멈추지 않고 오류 UI를 보여줄 수 있게 해줍니다.

        // Class
        class Example extends React.Component {
          componentDidCatch(error, info) {
            console.log('에러가 발생했습니다.')
          }
        }
        

태그:

카테고리:

업데이트:

댓글남기기