[React] 인프런강의(섹션1,2)

김수현·2022년 5월 24일
0

> 섹션1 - 1

  • 리액트란?
    자동으로 업데이트 되는 UI
    render함수는 순수 함수로 작성
    state는 불변 변수로 관리

> 섹션1 - 3,4

  • 라이브러리
    => 웹팩(WebPack) VS 바벨(Babel)
    WebPack: 의존성을 분석해 여러 모듈을 하나의 파일로 묶어주는 역할 / 모듈 시스템(ESM,CommonJS)을 사용하기 위해서
    Babel: 코드 변환기. 최신 js문법을 구형 브라우저에서도 쓸 수 있도록 코드 자체를 변환시킨다.

> 섹션1 - 5

  • 리액트 환경 설정(create-react-app/CRA)
    => 리액트 개발환경을 직접 구축하려면 많은 지식과 시간이 필요..
    => 페이스북에서 관리하는 공식 툴
    => npx create-react-app {app 이름} 을 통해 react web app을 setup할 수 있음
    => cra는 서버사이드 렌더링을 지원하지 않음
    => Next.js는 서버사이드 렌더링 지원

> 섹션1 - 7

  • CSS작성방법
  1. CSS파일
    : 상단에 import ./파일명.css
    : 이름충돌 유의

  2. CSS모듈
    : 상단에 import ./파일명.module.css 객체형식으로 받아서 속성으로 입력하면 됌
    : 이름충돌 문제 없음

  3. Sass
    : npm install node-sass 설치
    : 파일형식은 .scss

  4. css-in-js
    : js파일 안에서 css를 사용하는 방법
    : npm install styled-components 설치

> 섹션1 - 8

  • 단일 페이지 애플리케이션(SPA)
    : 클라이언트가 초기요청 서버가 html을 내려주고 페이지 전환이 있을땐, 필요할 때마다 데이터를 요청해서 받아오고 라우팅 처리로 가능

  • SPA 브라우저 API
    : pushState, replaceState함수
    : popState함수


- SPA 브라우저 만들기
=> App.js

function App(){
  const [pageName, setPageName] = useState('');
  useEffect(()=>{
    window.onpopstate = function (event) {
      console.log(`location: ${document.location}, state: ${event.state}`);
      setPageName(event.state);
    };
  }, []);
  function onClick1(){
    const pageName = 'page1';
    window.history.pushState(pageName,'','/page1');
    setPageName(pageName);
  }
  function onClick2(){
    const pageName = 'page2';
    window.history.pushState(pageName,'','/page2');
    setPageName(pageName);
  }
  return (
    <div>
      <button onClick={onClick1}>
        page1
      </button>
      <button onClick={onClick2}>
        page1
      </button>
      {!pageName && <Home/>}
      {pageName === 'page1' && <Page1/>}
      {pageName === 'page2' && <Page2/>}
    </div>
  );
}

function Home(){
  return <h2> 여기는 홈페이지입니다. </h2>
}
function Page1(){
  return <h2> 여기는 Page1입니다.</h2>
}
function Page2(){
  return <h2> 여기는 Page2입니다.</h2>
}

- SPA로 동작하는 웹사이트 만들기
: npm install react-router-dom 설치
: route태그를 통해서 3가지 component가 렌더링됌
: exact부분은 url형식을 지정해줌

=> App.js

import logo from './logo.svg';
import './App.css';
import React, {useState , useEffect} from 'react';
import reactDom from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Route, Link } from 'react-router-dom';
import Rooms from './Rooms'

function App(){
  return (
    <BrowserRouter>
      <div style={{padding: 20, border: '5px solid gray '}}>
        <Link to="/"></Link>
        <br/>
        <Link to="/photo">사진</Link>
        <br/>
        <Link to="/rooms">방 소개</Link>
        <br/>
        <Route exact path="/" component={Home}/>
        <Route exact path="/photo" component={Photo}/>
        <Route exact path="/rooms" component={Rooms}/>
      </div>
    </BrowserRouter>
  );
}

function Home(){
  return <h2> 여기는 홈페이지입니다. </h2>
}
function Photo(){
  return <h2> 여기서 사진을 감상해보세요.</h2>
}

export default App;

=> Rooms.js

import React from 'react';
import { Route, Link } from 'react-router-dom';

export default function Rooms({match}){
    return(
        <div>
            <h2> 여기는 방을 소개하는 페이지. </h2>
            <Link to={`${match.url}/bludRoom`}>파란 방입니다.</Link>
            <br/>
            <Link to={`${match.url}/greenRoom`}>초록 방입니다.</Link>
            <br/>
            <Route path={`${match.url}/:roomId`} component={Room}/>
            <Route
                exact
                path={match.url}
                render={()=><h3>방을 선택해 주세요.</h3>}
            />
            <br/>
        </div>
    );
}

function Room({match}){
    return <h2>{`${match.params.roomId} 방을 선택했습니다.`}</h2>
}

> 섹션2 - 1

: [...arr]은 array의 모든 요소를 나열해주는 것과 같음
: 리액트는 데이터를 기반으로 UI가 어떤 모습인지를 기술해줌
: UI의 모습이 한눈에 그려짐(추상화 단계가 높아 비즈니스 로직에 더 집중할 수 있음)

  • TodoList 만들기
    => App.js
import logo from './logo.svg';
import './App.css';
import React, {useState , useEffect} from 'react';
import reactDom from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Route, Link } from 'react-router-dom';
import Rooms from './Rooms'
import { current } from 'immer';

export default function App(){
  const [todoList, setTodoList] = useState([]);
  const [currentId, setCurrentId] = useState(1);
  const [desc, setDesc] = useState('');
  const [showOdd, setShowOdd] = useState(false);
  function onAdd(){
    const todo = { id: currentId, desc };
    setCurrentId(currentId+1);
    setTodoList([...todoList, todo]);
  }
  function onDelete(e){
    const id = Number(e.target.dataset.id);
    const newTodoList = todoList.filter(todo => todo.id !== id)
    setTodoList(newTodoList);
  }
  function onSaveToServer(){

  }
  return (
    <div>
      <h3>할 일 목록</h3>
      <ul>
        {todoList.filter((_,index)=> showOdd ? index%2 == 0: true ).map(todo => (
          <li key={todo.id}>
            <span>{todo.desc}</span>
            <button data-id={todo.id} onClick={onDelete}>
              삭제
            </button>
          </li>
        ))}
        <input type="text" value={desc} onChange={e=>{setDesc(e.target.value)}}/>
        <button onClick={onAdd}>추가</button>
        <button onClick={()=> setShowOdd(!showOdd)}>
          홀수 아이템만 보기 on/off  
        </button>
        <button onClick={onSaveToServer}>서버에 저장</button>
      </ul>
    </div>
  );
}

> 섹션2 - 2

UI컴포넌트에서 데이터 다루는 법

  • UI데이터를 속성이나 상태값으로 관리함
  • 리액트가 값의 변경을 알려면 상태값으로 관리해야 함
  • 상태값: useState
  • 속성값: 부모컴포넌트가 렌더링될 때마다 자식컴포넌트도 렌더링
  • 속성값이 변경될 때만 렌더링 하기 위해서 React.memo를 사용하면 됌
  • 속성값은 불변 변수
  • 상태값도 불변 변수로 관리하기 위해 객체 형식으로
  • 객체를 불변 변수로 하기 위해서는 전개연산자를 이용

=>App.js

import logo from './logo.svg';
import './App.css';
import React, {useState , useEffect} from 'react';
import reactDom from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Route, Link } from 'react-router-dom';
import Rooms from './Rooms'
import { current } from 'immer';
import Counter from './Counter';

export default function App(){
  const [color, setColor] = useState('red');
  function onClick(){
    setColor('blue');
  }
  return(
    <div>
      <Counter/>
      <Counter/>
      <button style={{backgroundColor:color}} onClick={onClick}>
      좋아요
      </button>
    </div>
    
  );
}

=>Title.js

import React, { memo } from 'react';

function Title({title}){
    return <p> {title} </p>
}

export default React.memo(Title);

=>Counter.js

import React, {useState} from 'react';
import Title from './Title';

export default function Counter() {
    const [count, setCount] = useState({value:0,value1:1,value2:2});
    function onClick(){
        setCount({...count, value:count.value+1});
    }
    return (
        <div>
            <Title title={`현재 카운트: ${count.value}`}/>
            <button onClick={onClick}>증가</button>
        </div>
    );
}

> 섹션2 - 3

  • 컴포넌트가 반환할 수 있는 값
  • 배열로 반환할 때는 리액트 요소가 항상 key를 갖고 있어야 함
  • fragment: 여러개 요소를 반환할 때 유용함 (= </>, <> )
return(
    <React.Fragment>
      <p>안녕</p>
      <p>하세요.</p>
    </React.Fragment>
  );
  • react portal: react portal을 사용하기 위해서 react-dom에 있는 함수를 사용해야 함 => reactDom.createPortal()

> 섹션2 - 4

  • 리액트 요소와 가상돔
  • 컴포넌트가 삭제: unmount / 컴포넌트가 생성: mount

> 섹션2 - 5

하나의 화면을 표현하기 위해서 여러 리액트 요소가 트리구조로 구성
화면 구성은 "랜더단계 -> 커밋단계" 를 거침
(렌더단계: 가상돔 / 커밋단계: 실제돔)

> 섹션2 - 6

리액트 훅(Hook)은 컴포넌트에 기능을 추가할 때 사용하는 함수
컴포넌트에 상태값을 추가, 자식요소에 접근과 같은 기능을 사용하고 싶을 때 훅을 사용
훅이 나오면서 클래스형 컴포넌트는 더이상 사용안해도 되는 상황

  • 대표 훅
  1. useState: 상태값 추가
  2. useEffect: 부수효과 처리(외부의 상태를 변경하는 것:서버API를 등록,이벤트 헨들러 등록)
  • useState
    비동기, 배치로 처리
    여러상태값을 관리할 때는 useState보다는 useReducer가 더 적합

  • useEffect
    렌더링 결과가 실제 돔에 반영되고 비동기로 호출이 됌
    *외부함수는 의존성 배열에 입력할 필요없지만, 지역함수는 부수효과 내부에서 사용했다면 의존성 배열에 입력해줘야 됌.

=> App.js

import logo from './logo.svg';
import './App.css';
import React, {useState , useEffect} from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Route, Link } from 'react-router-dom';
import Rooms from './Rooms'
import { current } from 'immer';
import Counter from './Counter';

console.log(
  <a key="key1" style={{width:100}} href="http://google.com">
        click here
  </a>,
)
export default function App(){
  const [count, setCount ] = useState(0);
  function onClick() {
    ReactDOM.unstable_batchedUpdates(()=>{
      setCount(v => v+1);
      setCount(v => v+1);
    });
  }
  useEffect(()=>{
    window.addEventListener('click',onClick);
    return ()=>window.removeEventListener('click',onClick);
  });
  console.log('render canceled');
  return(
    <div>
      <h2>{count}</h2>
      <button onClick={onClick}>증가</button>
    </div>
  );
}

> 섹션2 - 7

훅은 재사용성이 좋음
커스텀 훅 만들어보기!

=> App.js

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

export default function App(){
  return <Profile/>;
}

=> Profile.js

import React,{useState, useEffect} from 'react';
import useUser from './useUser';

export default function Profile({userId}){
    const user = useUser(userId);
    return(
        <div>
            {!user&&<p>사용자 정보를 가져오는 중...</p>}
            {user&&(
                <>
                    <p>{`name is ${user.name}`}</p>
                    <p>{`age is ${user.age}`}</p>
                </>
            )}
        </div>
    );
}

=> useUser.js

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

export default function useUser(userId){
    const [user, setUser] = useState(null);
    useEffect(()=>{
        getUserApi(userId).then(data=>setUser(data));
    }, [userId]);
    return user;
}

const USER1 = { name: "mike", age: 23 };
const USER2 = { name: "jane", age: 41 };
function getUserApi(userId){
    return new Promise(res => {
        setTimeout(()=>{
            res(userId % 2 ? USER1 : USER2);
            }, 500);
        }
    );
}

  • 현직에서 사용할 훅은 ???
    useMounted 훅을 만들어보자
import React,{useState, useEffect} from 'react';

export default function useWindowWidth(){
    const [mounted, setMounted] = useState(false);
    useEffect(()=>{
        setMounted(true);
    }, []);
    return mounted
}

> 섹션2 - 8

  • 훅 사용시 규칙
  1. 하나의 컴포넌트에서 훅을 호출하는 순서는 항상 같아야 한다.
  2. 훅은 함수형 컴포넌트 또는 커스텀 훅 안에서만 호출되어야 한다.
  3. if문/for문/함수 안에서 훅을 사용하면 안된다.
  4. 훅이 사용된 위치정보를 기반으로 훅은 데이터를 관리한다.

0개의 댓글