업데이트:

태그: ,

카테고리:

리액트가 없다면

public이라는 디렉토리는 npm run start 할때 파일을 찾는 곳이다.

만약, 리액트가 없다면 index.html a태그로 연결된 모든 페이지를 각각 만들어하므로 html의 구조를 한눈에 보기가 어렵지만,

리액트는 필요한 기능을 가진 컴포넌트를 만들고,
그것을 논리적으로 삽입하고, 삭제하는 등의 과정을 거치기때문에 유지보수,개발에 훨씬 용이하다.

리액트는 html파일에 직접적으로 태그를 작성하는게 아닌, 태그를 넣는 컴포넌트들을 만들고, 이것들을 javascript로 렌더링할 수 있게 한다.



리액트 컴포넌트


💡 기본 구조

App.js에 아래 코드를 삽입

import React, { Component } from 'react';

class Subject extends Component {
    render() {
        return (
            <header>
                <h1>Web<h1/>
            <header/>
        )
    };
}

컴포넌트 내부의 리턴해서 렌더링할 최소단위는 태그이다.
문장만 리턴하면 안된다는 의미.


ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

index.js는 index.html에서 태그를 가져와 리액트 컴포넌트를 넣고있고,


class App extends Component {
  render() {
    return (
      <div className="App">
        Hello, React!
        <Subject />
      </div>
    );
  }
}

이 컴포넌트가 App.js에서 만들어진 App이라는 컴포넌트를 넣고있으므로,

를 App 컴포넌트에 넣어준다.

html에 중구난방으로 덕지덕지 코드를 작성하는게 아니라,
기능별로, 역할별로 각각의 독립적인 컴포넌트를 만들 수 있고,
복잡한 웹사이트 구현에 유용하다.



Props

Subject 컴포넌트를 리팩토링 해보자.

class Subject extends Component {
  render() {
    return (
      <header>
        <h2>Web</h2>
        world wide web!
      </header>
    );
  }
}

class App extends Component {
  render() {
    return (
      <div className="App">
        <Content />
        Hello, React!
        <Subject />
        <Toc />
      </div>
    );
  }
}

Subject라는 컴포넌트는 항상 아래와 같은 태그를 만든다.

<header>
  <h2>Web</h2>
  world wide web!
</header>

만약 함수처럼 매개변수를 입력받을 수 있어서 입력받는 내용에 따라 다른 내용을 만들 수 있다면?

이를 가능케 하는 것이 props이다.

props는 리액트컴포넌트가 만들어질때 가지는 속성(attribute)를 매개변수로 컴포넌트 내부에서 사용할 수 있게한다.

컴포넌트에서 props를 사용하기 위해서는
{this.props.attributeName} 으로 만들 수 있다.

class Subject extends Component {
  render() {
    return (
      <header>
        <h2>{this.props.title}</h2>
        {this.props.sub}
      </header>
    );
  }
}
class App extends Component {
  render() {
    return (
      <div className="App">
        <Content />
        Hello, React!
        <Subject title="WEB" sub="world wide web!" />
        <Subject title="React" sub="js library" />
        <Toc />
      </div>
    );
  }
}



Component 파일로 분리하기

App.js에 덕지덕지 모여있는 컴포넌트들을 분리하자.

import React, { Component } from "react";

class Content extends Component {
  render() {
    return (
      <article>
        <h1>WEB</h1>
      </article>
    );
  }
}

export default Content;
import React, { Component } from 'react';

class Subject extends Component {
    render() {
      return (
        <header>
          <h2>{this.props.title}</h2>
          {this.props.sub}
      </header>
      )
    }
  }

export default Subject;
import React, { Component } from 'react';

class Toc extends Component {
    render() {
      return (
        <nav>
          <ul>
            <li>react</li>
            <li>vue</li>
            <li>angular</li>
          </ul>
        </nav>
      )
    }
  }

export default Toc;

이제 App.js에 이 파일들의 경로를 import하기만 하면 컴포넌트들을 사용할 수 있다.

import React, { Component } from "react";
import "./App.css";
import Content from "./components/content";
import Subject from "./components/subject";
import Toc from "./components/toc";

class App extends Component {
  render() {
    return (
      <div className="App">
        <Content />
        Hello, React!
        <Subject title="WEB" sub="world wide web!" />
        <Subject title="React" sub="js library" />
        <Toc />
      </div>
    );
  }
}

export default App;



state

state는 부모컴포넌트가 자식컴포넌트에게 주는 값이다.

자식컴포넌트는 부모컴포넌트에게서 props를 받아오기만 할 수 있지만(읽기전용)
state는 컴포넌트 내부에서 선언하며, 직접 값을 변경할수도 있다(읽고,쓰기 가능).



💡 state값을 초기하는 방법

어떤 컴포넌트가 실행될때, render보다 이전에 실행되고, 컴포넌트를 초기화하기 위해 만든다.
constuctor는 render함수보다 이전에 실행된다.

파이썬의 클래스가 여러가지 객체(인스턴스)를 만들 수 있는 틀이라면,

리액트에서는 컴포넌트가 여러가지 객체를 찍어 만들어 낼 수 있는 틀이다.

따라서 리액트의 constructor는
python class의 `__init__(self)`메서드와 같다고 생각하면된다.

파이썬의 클래스에서 __init__메서드는
붕어빵 'A', 붕어빵 'B'같이 각 붕어빵에 이름을 붙여주기 위해서
각각의 특성을 다르게 만드는 것이 __init__이었다.
__init__메서드가 있음으로서 클래스에 따른 각각 다른 인스턴스가 생성될 수 있다.


import React, { Component } from "react";
import "./App.css";
import Content from "./components/content";
import Subject from "./components/subject";
import Toc from "./components/toc";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      subject: { title: "WEB", sub: "World wide web!" },
    };
  }
  render() {
    return (
      <div className="App">
        <Content />
        Hello, React!
        <Subject
          title={this.state.subject.title}
          sub={this.state.subject.sub}
        />
        <Toc />
      </div>
    );
  }
}

export default App;



💡 state의 의의

state는 부모 컴포넌트의 state값이 자식 컴포넌트의 props값으로 전달될 수 있다는 의미가 있다.

✏️ 1. index.js가 App.js를 호출한다.

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

✏️ 2. App.js가 호출된다.

import React, { Component } from "react";
import "./App.css";
import Content from "./components/content";
import Subject from "./components/subject";
import Toc from "./components/toc";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      subject: { title: "WEB", sub: "World wide web!" },
    };
  }
  render() {
    return (
      <div className="App">
        <Content />
        Hello, React!
        <Subject
          title={this.state.subject.title}
          sub={this.state.subject.sub}
        />
        <Toc />
      </div>
    );
  }
}

export default App;

동시에 생성자를 초기화하면서, Subject 컴포넌트에 state의 subject프로퍼티값을 속성값으로 전달한다.



✏️ 3. Subject 컴포넌트가 호출되며 속성값에 접근해 props로 전달받는다.

import React, { Component } from "react";

class Subject extends Component {
  render() {
    return (
      <header>
        <h2>{this.props.title}</h2>
        {this.props.sub}
      </header>
    );
  }
}

export default Subject;

부모 컴포넌트 App은 state의 subject 프로퍼티 값을 속성값으로 전달했는데,
자식 컴포넌트 Subject는 props로 이 값을 감지하고, 사용할 수 있다.



toc.js에 state 응용하기

toc.js의 리스트를 하드코딩하지말고, App 컴포넌트의 state가 변경될때마다 바뀌도록 수정해보자.

import React, { Component } from "react";
import Content from "./components/content";
import Subject from "./components/subject";
import Toc from "./components/toc";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      subject: { title: "WEB", sub: "World wide web!" },
      contents: [
        { id: 1, title: "REACT", desc: "HTML is a programming laguage ^^" },
        { id: 2, title: "Vue", desc: "vue is vue" },
        { id: 3, title: "angular", desc: "hello angular" },
      ],
    };
  }
  render() {
    return (
      <div className="App">
        <Content />
        Hello, React!
        <Subject
          title={this.state.subject.title}
          sub={this.state.subject.sub}
        />
        <Toc data={this.state.contents} />
      </div>
    );
  }
}

export default App;

state의 프로퍼티로 contents를 리스트로 만들었다.

리스트의 인덱스로 각 프로퍼티에 접근이 가능하다.

이를 토대로 toc.js가 부모컴포넌트인 App의 state를 인식하기 위해서

이 state가 Toc 컴포넌트의 속성(data)로 주어졌다.

import React, { Component } from "react";

class Toc extends Component {
  render() {
    var lst = [];
    var data = this.props.data;
    let i = 0;
    while (i < data.length) {
      lst.push(<li id={data[i].id}>{data[i].title}</li>);
      i += 1;
    }
    return (
      <nav>
        <ul>{lst}</ul>
      </nav>
    );
  }
}

배열 lst를 만들어 li태그들을 이 리스트에 push한다.
그리고 return에서 {lst}로 표현하면, 배열 lst가 태그로 표현되게된다.



Key

스크린샷 2021-11-16 오후 8 51 52

list 내부의 각각의 child는 unique key prop을 가져야한다는 오류가 발생한다.

리액트가 내부적으로 필요해서 요청하는 데이터인데, 각각 값이 달라야한다.

lst.push(<li id={data[i].id}>{data[i].title}</li>);

여기서 id를 key로 바꿔주자.

댓글남기기