본문 바로가기
  • soldonii's devlog
Javascript 공부/React

리액트 1

by soldonii 2019. 11. 20.

*Udemy의"Complete React Developer in 2020"강의에서 학습한 내용을 정리한 포스팅입니다.

*자바스크립트와 리액트를 배우는 단계라 오류가 있을 수 있습니다. 틀린 내용은 댓글로 말씀해주시면 수정하겠습니다. 감사합니다. :)


1. create-react-app

create-react-app은 개발자가 다른 것들은 신경쓰지 않고, 오로지 개발에만 집중할 수 있도록 기본적인 모듈들이 포함된 패키지이다. 이를 통해 새로운 프로젝트 폴더를 생성하면, 여러가지 폴더가 생성된다. 

 

# src 폴더 : react app과 관련된 모든 코드들이 들어있는 공간이다. 앱을 개발하면서 필요한 파일들을 저장하는 곳이라 보면 된다.

# public 폴더 : create-react-app 패키지에는 bable, webpack 등의 기능이 내장되어 있다. JSX로 작성된 코드를 브라우저가 이해할 수 있도록 변환하는 과정이 필요한데, yarn build를 하면 브라우저가 이해할 수 있는 코드로 변환한 후 변환 및 minified된 버전의 자바스크립트 파일이 생성되어 public 폴더에 들어간다.

 

# index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

또한 가장 기본이 되는 index.js 파일을 보면 위와 같은 코드들이 존재한다.

1) 첫번째 줄은 React 라이브러리 를 불러오겠다는 의미이고,

2) 두번째 줄은 가상 DOM과 실제 DOM을 비교하는 ReactDOM 라이브러리를 불러오는 것이다.

3) 세번째 줄은 기본적인 css 파일을 불러오는 것이고,

4) 네번째 줄은 App.js 파일에서 export 시킨 App 컴포넌트를 불러오는 것이다.

 

마지막 render가 포함된 코드는 ReactDOM 라이브러리에게 어떤 것을 rendering할지를 알려주는 코드이다. root라는 ID를 가진 element를 document에서 선택한 후, 이를 App 컴포넌트로 대체시키는 것이다.

 

# index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="logo192.png" />

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>

  </body>
</html>

위에서 말한 root라는 id를 가진 tag는 단지 div 태그일 뿐이다. 실제로 index.html 파일에는 딱 이 div 태그 하나만 존재한다.(물론 이 아래에 html 태그를 이용해서 추가로 작업을 하는 것도 가능하다.) 따라서 결국 단순한 div 태그를 React DOM이 index.html파일과 가상DOM과의 비교를 통해 변경된 부분을 App 컴포넌트로 대체시키는 것이다.

 

# App.js

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

App.js 파일에서 App 컴포넌트는 단지 JSX 문법으로 작성된 함수에 불과하다. 리액트에서 컴포넌트는 위처럼 함수로 구현할 수도 있지만, Class를 이용해서 구현하는 것도 가능하다. 일반적으로 Class를 사용하는 것이 더 많은 일을 할 수 있다고 한다. 차이를 살펴보자.

 

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  constructor() {
    super();
    this.state = {
      string: 'Hello Hyunsol'
    }
  }
  render() {
    return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>{ this.state.string }</p>
        <button onclick={() => this.setState({ string: 'Hello Hyewon'})}>Change Text</button>
      </header>
    </div>
  );
  }
}

export default App;

class를 사용하는 이유는 state에 접근하고 이용할 수 있기 때문이다. 동적으로 데이터를 변경하면서 제어하기를 원할 경우에는 class를 사용해야 한다.

 

super()는 하나의 클래스가 다른 클래스를 상속받을 때 기본적으로 작성해야 하는 키워드이다. super()를 선언함으로써 Component 객체에 내장된 프로퍼티를 사용할 수 있게 된다.(super 관련 자세한 내용은 이 글을 참고) 이제 App 컴포넌트 내에 state를 생성할 수 있다.

 

우선 초기값으로 state에 string: 'Hello Hyunsol' 값을 주었다. 그리고 버튼을 클릭했을 때, 기존의 텍스트를 'Hello Hyunsol'로 변경하고 싶다면, 위 코드처럼 { this.state.string }을 작성하면 된다. 여기서 {}는 JSX 문법 중의 일부로, {} 중괄호 안의 내용은 javascript임을 표시해주는 것이다. 

 

2. React Component

# setState()

버튼 태그를 보면 setState라는 프로퍼티가 부여되어 있다. 맨 처음 위에서 class App extends Component는 리액트 라이브러리에 내장된 Component 객체의 프로퍼티를 상속받는 행위라고 했다. setState는 이 Component 객체에 존재하는 수 많은 메소드 중 하나이다. 하는 역할은 말 그대로 state의 값을 변경시킨다. 버튼을 클릭하면 state의 string을 'Hello Hyewon'으로 변경하도록 했다. 

 

그리고 이 setState는 이전 글에서 말했던 리액트의 중요한 특성, One-way Data Flow와 깊은 관련이 있는 메소드이다. 리액트에서는 setState 메소드를 이용하지 않고서는 state를 변경하는 것이 허용되지 않는다.

위 코드에서처럼 버튼이 클릭되면 ⇒ setState 메소드가 실행되면서 state의 값을 변경되고 ⇒ 변경된 사실을 React가 감지한 후 ReactDOM에게 변경 사실을 알려준다. 그러면 ReactDOM은 변경된 가상 DOM과 실제 현재 DOM을 비교해서 차이가 발생한 부분만을 실제 DOM에 변경을 가해 re-rendering한다.

일방향적인 리액트

결국 위 사진처럼 state로부터 시작하여 가상DOM을 거친 후 변경된 부분을 DOM에 표시하는 일방향적인 데이터 흐름을 가능토록하는 것이 바로 setState 메소드이다.

 

# componentDidMount()

component 객체에는 위에서 설명한 render, setState 메소드 외에 수 많은 프로퍼티들이 존재한다. 이 중 componentDidMount()에 대해서 간단히 짚고 넘어가보자.

 

이 메소드는 말 그대로 컴포넌트가 mount될 때, 즉 현재 작업하고 있는 컴포넌트가 DOM에 최초로 rendering될 때, 작성한 코드를 실행시켜주는 메소드이다.

 

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor() {
    super();

    this.state = {
      monsters: []
    }
  }

  componentDidMount() {
    fetch('http://jsonplaceholder.typicode.com/users') // api로 요청을 보내는 작업
    .then(response => response.json()) // 전달받은 응답을 자바스크립트가 이해할 수 있도록 json 형태로 변환
    .then(users => this.setState({ monsters: users })); // state의 monster 데이터를 응답받은 user 데이터로 바꿔주기
  }

  render() {
    return (
      <div className='App'>
        {
          this.state.monsters.map(monster => <h1 key={monster.id}> { monster.name }</h1>)
        }
      </div>
    )
  }
}

이 강의에서 실습하는 내용 중 일부이다. App 컴포넌트가 React Component를 상속받도록 한 후, state에 monster를 빈 배열로 초기화했다. 그리고 이 App 컴포넌트가 DOM에 렌더링될 때, componentDidMount() 내부의 코드가 실행된다.

위 경우에는 jsonplaceholder 주소에서 json 데이터를 불러오도록 http 요청을 보낸 후, 성공적으로 response를 받으면 전달받은 데이터를 자바스크립트가 이해할 수 있도록 변환하고, 변환에 성공하면 해당 데이터를 state의 값인 monster의 빈 객체 대신 전달받은 데이터를 할당하도록 하고 있다. 

그리고 최종적으로는 변경된 monsters의 값을 h1 태그로 둘러서 DOM에 rendering하는 코드라고 할 수 있다.

댓글