본문 바로가기
Front-End, CS 스터디/항해99 - React 스터디

[리액트 React] 기초반 강의 2주차 정리

by 제니운 2022. 7. 24.
728x90

✅ 라이프 사이클이란?

 

1️⃣ 가상돔이란?

 

- DOM 트리 중 하나가 수정될 때마다 모든 DOM을 뒤지고 수정한 걸 찾고 싹 수정을 한다면, 필요없는 연산이 너무 많이 일어난다. 그래서 등장한 게 가상돔이다.

 

- 가상돔은 메모리 상에서 돌아가는 가짜 DOM이다.

 

- 가상돔의 동작 방식 : 기존  DOM과 어떤 행동 후 새로 그린 DOM(가상 돔에 올라갔다고 표현한다)을 비교해서 정말 바뀐 부분만 갈아끼워 준다. 돔 업데이트 처리가 정말 간결해진다.

 

 

2️⃣ 라이프 사이클이란?

 

컴포넌트의 라이트 사이클(컴포넌트의 생명주기)은 정말 중요한 개념.

 

👌 라이프 사이클

 

컴포넌트가 렌더링을 준비하는 순간부터, 페이지가 사라질 때까지

 

 

- 컴포넌트는 생성(Mount)되고 수정(업데이트)되고 사라진다(Unmount).

 

- 생성은 처음으로 컴포넌트를 불러오는 단계이다.

 

- 수정(업데이트)은 사용자의 행동(클릭, 데이터 입력 등)으로 데이터가 바뀌거나, 부모 컴포넌트가 렌더링할 때 업데이트 된다.

 

🔹 props가 바뀔 때

🔹 state가 바뀔 때

🔹 부모 컴포넌트가 업데이트 되었을 때(=리렌더링했을 때)

🔹 또는, 강제로 업데이트 했을 경우!(forceUpdate())를 통해 강제로 컴포넌트를 업데이트할 수 있다.

 

- 제거는 페이지를 이동하거나, 사용자의 행동(삭제 버튼 클릭 등)으로 인해 컴포넌트가 화면에서 사라지는 단계이다.

 

 

✅ 라이프 사이클 함수로 보는 라이프 사이클

 

라이프 사이클 함수는 클래스형 컴포넌트에서만 사용할 수 있다.

우리는 클래스형 컴포넌트보다 함수형 컴포넌트를 많이 쓰는데, 리액트 공식 매뉴얼에서 함수형 컴포넌트를 더 권장하기 때문이다.

// 리액트 16.8버전부터 등장한 React Hooks로 라이프 사이클 함수를 대체할 수 있다.

 

 

👌 클래스형 컴포넌트 모양

 

Class LifecycleEx extends React.Component{
}

 

👌 생성자 함수 모양

 

  constructor(props) {
    super(props);
    
    this.state = {
      cat_name: '나비',
    };

    console.log('in constructor!');
  }

 

 

1️⃣ constructor()

 

생성자 함수라고도 부른다. 컴포넌트가 생성되면 가장 처음 호출되는 함수

 

 

2️⃣ render()

 

컴포넌트의 모양을 정의하는 함수

여기에서도 state, props에 접근해서 데이터를 보여줄 수 있다.

 

리액트 요소를 return에 넣어 반환해줬었다.

render()안에 들어갈 내용은, 컴포넌트의 모양에만 관여하는 것이 가장 좋다.

즉, state나 props를 건드려 데이터를 수정하려고 하면 안 된다.

 

 

3️⃣ componentDidMount()

 

컴포넌트가 화면에 나타나는 것을 마운트(Mount)한다고 표현한다.

didMount()는 마운트가 완료되었다는 뜻이다.

이 함수는 첫번째 렌더링을 마친 후에만 딱 한 번 실행되며, 컴포넌트가 리렌더링할 때는 실행되지 않는다.

보통은 이 안에서 ajax 요청, 이벤트 등록, 함수 호출 등 작업을 처리한다.

또, 이미 가상돔으로 올라간 후니까 DOM 관련 처리를 해도 된다.

 

 

4️⃣ componentDidUpdate(prevProps, prevState, snapshot)

 

DidMount()가 첫 렌더링 후에 호출되는 함수라면, DidUpdate()는 리렌더링을 완료한 후 실행되는 함수이다.

 

이 함수에 중요한 파라미터가 2개 있는데, prevProps와 prevState이다.

각각 업데이트 되기 전 props, state이며 이전 데이터와 비교할 일이 있다면 가져다 쓰면 된다.

DidUpdate()가 실행될 때도 가상돔이 실제돔으로 올라간 후니까 DOM 관련 처리를 해도 된다.

 

 

5️⃣ componentWillUnmount()

 

componentWillUnmount()가 호출되는 걸 보려면, app.js에서 <LivecycleEX/>를 없애봐야 한다.

삼항 연산자를 사용해서 컴포넌트를 보여주거나, 없애는 것을 조건부 렌더링이라고 부른다.

 

✅ Component(1)

 

Component는 클래스형과 함수형이 있다.

이제 클래스형 컴포넌트는 잘 쓰지 않지만 두 가지 모두 살펴봐야 한다.

 

 

🔹 리액트는 레고, 컴포넌트는 블록 같은 것

 

Component는 웹 사이트의 조각이고 우리는 이 조각을 모아서 웹사이트에 뿌려준다.

 

 

1️⃣ state

 

Component가 가지고 있는 데이터

 

 

- sparta 헤더에 들어갈 데이터는 로고 이미지 경로, 메뉴 이름 등이 있을 것이다.

- 이 데이터는 <container/>나 <footer/> 컴포넌트에서는 쓰지 않을 것이다.

- 즉, <header/> 컴포넌트에서만 쓰는 정보

 

state는 한 컴포넌트에서만 사용하는 정보를 주로 넣어놓고 생성, 수정하는 데이터이다.

생성도 수정도 오직 해당 컴포넌트 내에서만 이뤄진다. 내꺼니까 생성도 수정도 자유롭다

 

 

2️⃣ props

 

props는 Component가 부모 Component로부터 받아온 데이터이다.

 

스파르타 홈페이지 가운데 container 부분

 

<container/> 컴포넌트는 두 개의 자식 컴포넌트를 가지고 있다.

 

- <container/> 컴포넌트만 <imagebanner/> 컴포넌트한테 필요한 이미지 경로를 state로 가지고 있다고 가정한다.

- 이 때 <imagebanner/> 컴포넌트는 자신의 부모인 <container/> 컴포넌트로부터 이미지 경로를 전달받아서 사용해야 한다.

- <container/>가 가지고 있는 이미지 경로를 <imagebanner/>에게 전달해주면, 이 이미지 경로가 <imagebanner/> 컴포넌트의 props가 된다.

 

🔹 props : 부모 컴포넌트로부터 전달받은 데이터

 

 

👌 그럼 부모 컴포넌트가 가지고 있는 데이터를 <imagebanner/> 컴포넌트가 추가하거나 수정할 수 있을까?

 

Props로 받은 데이터는 수정할 수 없다. 남의 것이므로.

 

 

✅ Component(2)

 

1️⃣ 함수형 Component

 

👌 코딩 룰

 

🔹 폴더는 소문자로 시작하는 카멜케이스를 사용

🔹 JS파일, 컴포넌트 이름은 대문자로 시작하는 카멜케이스를 사용

 

 

👌 리액트 패키지를 불러온다.

 

import React from 'react'; 

 

👌 함수형 컴포넌트(1) 

function Bucketlist(props){
     return (
         
버킷 리스트

     );
 }

 

👌 함수형 컴포넌트(2) - 화살표 함수

const BucketList = (props) => {

    // 컴포넌트가 뿌려줄 ui 요소(리엑트 엘리먼트라고 불러요.)를 반환해줍니다.
    return (
        <div>
            버킷 리스트
        </div>
    );
}

 

🔹 (props) : 부모 컴포넌트에게 받아온 데이터

js 함수가 값을 받아오는 것과 똑같이 받아온다.

 

 

👌 export

 

만든 함수형 컴포넌트를 export 해준다.

export 해주면 다른 컴포넌트에서 BucketList 컴포넌트를 불러다 쓸 수 있다.

 

export default BucketList;

 

 

👌 App.js 로 돌아가서 BucketList 컴포넌트를 불러와 본다.

 

import BucketList from './BucketList';

BucketList 컴포넌트를 import 해온다.

 

 

🔹 import [컴포넌트명] from [컴포넌트가 있는 파일경로];

 


👌 컴포넌트를 <div className=""></div> 사이에 넣어준다.

 

function App() {

  return (
    <div className="App">
      <h1>내 버킷리스트</h1>
      {/* 컴포넌트를 넣어줍니다. */}
      <BucketList/>
    </div>
  );
}

export default App;

 

✅ Component(3)

 

1️⃣ 클래스형 Component

 

함수형 컴포넌트를 클래스형 컴포넌트로 바꾸려고 한다.

App.js를 클래스형으로 바꾸고, BucketList 컴포넌트에 데이터를 넘겨줘 본다.

 

 

👌 App.js를 클래스형으로 바꾸기

 

import React from 'react';
import logo from './logo.svg';
import './App.css';
// BucketList 컴포넌트를 import 해온다.
// import [컴포넌트 명] from [컴포넌트가 있는 파일경로];
import BucketList from './BucketList';

// 클래스형 컴포넌트
class App extends React.Component {

  constructor(props){
    super(props);
    // App 컴포넌트의 state를 정의해줍니다.
    this.state = {
      list: ['영화관 가기', '매일 책읽기', '수영 배우기'],
    };
  }

  // 랜더 함수 안에 리액트 엘리먼트를 넣어줍니다!
  render() {
      return (
      
      </div classname="app">
        <h1>내 버킷리스트</h1>

        {/* 컴포넌트를 넣어줍니다. */}
        <BucketList/>
      
    );
  }
}

export default App;

 

 

2️⃣ Component에서 Component로 데이터를 넘겨주기

 

👌 state 값 사용하기

 

render() {
    console.log(this.state);
    ...
}

 

👌 App 컴포넌트에서 Bucket 컴포넌트로 state를 넘겨주기

 

render() {
    // this 키워드를 통해 state에 접근할 수 있다.
    console.log(this.state);

      return (
      <div className="App">
        <h1>내 버킷리스트</h1>
        {/* 컴포넌트를 넣어준다. */}
        {/* <컴포넌트 명 [props 명]={넘겨줄 것(리스트, 문자열, 숫자, ...)}/> */}
        <BucketList list={this.state.list}/>
      </div>
    );
  }

 

🔹 BucketList.js 파일을 열고 입력

 

const BucketList = (props) => {
    
    console.log(props);

    // 컴포넌트가 뿌려줄 ui 요소(리엑트 엘리먼트라고 불러요.)를 반환해준다.
    return (
        <div>
            버킷 리스트
        </div>
    );
}

 

✅ 리액트에서 돔요소 가져오기

 

1️⃣ Ref

 

어떤 인풋박스에서 텍스트를 가져오고 싶을때, 리액트에선 리액트 요소에서 가져와야한다.

 

 

👌 React 요소를 가지고 오는 방법1. : React.createRef()

 

import React from "react";
import logo from "./logo.svg";
// BucketList 컴포넌트를 import 해옵니다.
// import [컴포넌트 명] from [컴포넌트가 있는 파일경로];
import BucketList from "./BucketList";
import styled from "styled-components";

// 클래스형 컴포넌트는 이렇게 생겼다.

class App extends React.Component {
  constructor(props) {
    super(props);

    // App 컴포넌트의 state를 정의해준다.

    this.state = {
      list: ["영화관 가기", "매일 책읽기", "수영 배우기"],
    };

    // ref는 이렇게 선언한다! 
    this.text = React.createRef();
  }

  componentDidMount(){
// 콘솔에서 확인해보자!
    console.log(this.text);
console.log(this.text.current);
  }

  // 랜더 함수 안에 리액트 엘리먼트를 넣어준다.
  render() {
    
    return (
      <div className="App">
        <Container>
          <Title>내 버킷리스트</Title>
          <Line />
          {/* 컴포넌트를 넣어준다. */}
          {/* <컴포넌트 명 [props 명]={넘겨줄 것(리스트, 문자열, 숫자, ...)}/> */}
          <BucketList list={this.state.list} />
        </Container>

        <div>
          <input type="text" ref={this.text}/>
        </div>
      </div>
    );
  }
}

const Container = styled.div`
  max-width: 350px;
  min-height: 80vh;
  background-color: #fff;
  padding: 16px;
  margin: 20px auto;
  border-radius: 5px;
  border: 1px solid #ddd;
`;

const Title = styled.h1`
  color: slateblue;
  text-align: center;
`;

const Line = styled.hr`
  margin: 16px 0px;
  border: 1px dotted #ddd;
`;

export default App;

 

👌 React 요소를 가지고 오는 방법2 : React.useRef()

 

useRef()는 리액트 훅 중 하나이다.

 

import React from "react";
import styled from "styled-components";

const BucketList = ({ list }) => {
  const my_lists = list;
  const my_wrap = React.useRef(null);

  console.log(my_wrap); // 콘솔로 확인해보기

  window.setTimeout(() => { 
// 1초 뒤에는?!
    console.log(my_wrap);
  }, 1000);
  return (
    <div ref={my_wrap}>
      {my_lists.map((list, index) => {
        return <ItemStyle key={index}>{list}</ItemStyle>;
      })}
    </div>
  );
};

const ItemStyle = styled.div`
  padding: 16px;
  margin: 8px;
  background-color: aliceblue;
`;

export default BucketList;

 

✅ State 관리(1)

 

1️⃣ 단방향 데이터 흐름이란?

 

데이터는 위에서 아래로, 부모에서 자식으로 넘겨줘야 한다는 뜻

 

👌 왜 단방향으로 써야하는지

 

🔹 라이프 사이클과 함께 생각해보기

 

부모 컴포넌트의 state가 업데이트 되면 자식 컴포넌트도 리렌더링이 일어난다.

만약 자식 컴포넌트의 state가 바뀐 걸 부모 컴포넌트가 props로 받는다고 생각해보면, 자식 컴포넌트가 업데이트 될 때 부모 컴포넌트도 업데이트 된다. 그럼 또 자식 컴포넌트가 리렌더링 되므로..

 

 

👌 클래스형 컴포넌트에서 state 관리 - setState()

 

🔹 새 CRA 만들기

 

yarn create react-app nemo

 

🔹 App.js를 class형 컴포넌트로 바꾸고 시작

 

// App component를 class형으로!
import React from 'react';

class App extends React.Component {

  constructor(props){
    super(props);

    this.state = {}
  }

  componentDidMount(){

  }
  
  render(){

    return (
      <div className="App">
        
      </div>
    );
  }
}

export default App;

 

🔹 state에 count라는 변수를 추가하고 count 숫자만큼 네모칸을 화면에 띄우기

 

import React from "react";

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      count: 3, // 숫자넣기!
    };
  }

  componentDidMount() {}

  render() {
    // 배열을 만든다.
    // Array.from()은 배열을 만들고 초기화까지 해주는 내장 함수
    // Array.from()의 첫번째 파라미터로 {length: 원하는 길이} 객체를,
    // 두번째 파라미터로 원하는 값을 반환하는 콜백함수를 넘겨주면 끝!
    // array의 내장함수 대부분은 콜백 함수에서 (현재값, index넘버)를 인자로 씁니다.
    const nemo_count = Array.from({ length: this.state.count }, (v, i) => i);

    // 콘솔로 만들어진 배열을 확인해봅니다. 숫자가 0부터 순서대로 잘 들어갔나요?
    console.log(nemo_count);

    return (
      <div className="App">
        {nemo_count.map((num, idx) => {
          return (
            <div key={idx}
              style={{
                width: "150px",
                height: "150px",
                backgroundColor: "#ddd",
                margin: "10px",
              }}
            >
              nemo
            </div>
          );
        })}
      </div>
    );
  }
}

export default App;

 

🔹 더하기, 빼기 버튼 만들기

 

return (
      <div className="App">
        {nemo_count.map((num, idx) => {
          return (
            <div key={idx}
              style={{
                width: "150px",
                height: "150px",
                backgroundColor: "#ddd",
                margin: "10px",
              }}
            >
              nemo
            </div>
          );
        })}

        <div>
          <button>하나 추가</button>
          <button>하나 빼기</button>
        </div>
      </div>
    );

 

🔹 함수 만들기

 

addNemo = () => {
    // this.setState로 count를 하나 더해준다.
    this.setState({ count: this.state.count + 1 });
  };

  removeNemo = () => {
    // 네모 갯수가 0보다 작을 순 없겠죠! if문으로 조건을 걸어준다.
    if (this.state.count > 0) {
      // this.setState로 count를 하나 빼줍니다!
      this.setState({ count: this.state.count - 1 });
    }else{
      window.alert('네모가 없어요!');
    }
  };

 

🔹 연결하기

 

return (
      <div className="App">
        {nemo_count.map((num, idx) => {
          return (
            <div
              key={idx}
              style={{
                width: "150px",
                height: "150px",
                backgroundColor: "#ddd",
                margin: "10px",
              }}
            >
              nemo
            </div>
          );
        })}

        <div>
          {/* 함수를 호출한다. 이 클래스 안의 addNemo 함수를 불러오기 때문에 this.addNemo로 표기. */}
          <button onClick={this.addNemo}>하나 추가</button>
          <button onClick={this.removeNemo}>하나 빼기</button>
        </div>
      </div>
    );

 

✅ State 관리(2)

 

1️⃣ 함수형 컴포넌트에서 state 관리 - useState()

 

함수형 컴포넌트는 클래스형처럼 자체적으로 state를 가지고 있지 않지만, react hooks를 사용하면 state를 가질 수 있다.

 

 

👌 새 컴포넌트 만들어서 시작하기

 

🔹 Nemo 컴포넌트 만들기

 

import React from "react";

const Nemo = (props) => {

    // 반환할 리액트 요소가 없을 때는 null을 넘겨준다. 처음 껍데기 잡으실때도 null을 넘겨주면 굳!
    return null;
}

export default Nemo;

 

🔹 App에서 <Nemo/> 불러오기

 

// import 먼저 하고
import Nemo from "./Nemo";
...
      <div className="App">
{/*컴포넌트 불러다 쓰기*/}
        <Nemo/>
     ...

...

 

🔹 useState()로 count를 state로 등록하자

 

// count에는 state 값이, setCount는 count라는 state 값을 수정하는 함수가 된다.
  // useState(초기값): () 안에 초기값을 넣어줍니다.

  const [count, setCount] = React.useState(3);

 

🔹 뷰를 만든다 = 반환할 리액트 요소를 만든다.

 

const nemo_count = Array.from({ length: count }, (v, i) => i);


  return (
    <div className="App">
      {nemo_count.map((num, idx) => {
        return (
          <div
            key={idx}
            style={{
              width: "150px",
              height: "150px",
              backgroundColor: "#ddd",
              margin: "10px",
            }}
          >
            nemo
          </div>
        );
      })}

      <div>
        <button>하나 추가</button>
        <button>하나 빼기</button>
      </div>
    </div>
  );

 

🔹 함수를 만든다.

 

const addNemo = () => {
    // setCount를 통해 count에 저장된 값을 + 1 해준다.
    setCount(count + 1);
  };

  const removeNemo = () => {
    // setCount를 통해 count에 저장된 값을 - 1 해준다.
    // 이번엔 if문 대신 삼항 연산자로 해보기
    setCount(count > 0 ? count - 1 : 0);
  };

 

🔹 연결하기

 

<div>
        {/* 함수를 호출합니다. */}
        <button onClick={addNemo}>하나 추가</button>
        <button onClick={removeNemo}>하나 빼기</button>
      </div>

 

 

👌 최종 순서

 

컴포넌트 만들기 -> state를 쓰는 순서! 뷰 먼저 -> state 만들기(기본값 잡아주기) -> state를 조작하는 무언가를 만들기 -> 연결하기

728x90