# React Tutorial

# 개발환경 설정

맥은 무조건 homebrew 이용하세요.

# 1. node.js 설치하기

https://nodejs.org/ko/

# 2. 텍스트에디터 설치하기

vscode

vscode extensions - ESLint 설치

vscode settings.json 편집하기 - command+shift+p - open setting 타이핑 - 엔터

아래는 제 설정의 일부분입니다

{
  "editor.tabSize": 2,
  "editor.fontSize": 13,
  "editor.codeActionsOnSave": {
      "source.fixAll.eslint": true
  },
}

# 3. 프로젝트 생성

튜토리얼 이므로 create-react-app 를 활용합니다.

가이드를 따라하고, vscode에서 해당 디렉토리를 open해줍니다.


# 살펴보기

react앱을 개발하려면 최소한 자바스크립트 기본 문법을 알고 있어야합니다.

그리고 react나 컴포넌트 등의 개념을 알고있는게 시작하는게 중요한데,

그것은 여기서 모두 설명하고 있습니다.

시작부터 모두 이해하려고 하지말고, 컴포넌트까지만 간단하게 읽어보세요.

# index.js

vscode로 프로젝트를 열고, src를 열어보면 index.js가 보입니다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

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

// 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();

이 프로젝트 코드상에서 index.js가 시작점입니다. 프로젝트가 실행의 시작점을 엔트리 포인트라고 합니다.

우리가 개발한 앱이 실행되면, 이런 코드들이 실행되는거네요.

분명 자바스크립트 파일인데, <App /> 같은 특이한 문법이 보입니다.

저런 문법을 JSX라고 합니다.

# JSX

javscript extension의 약어입니다.

자바스크립트의 확장문법이라는 뜻이죠

jsx를 해석하기위해서는 babel이라는 트랜스파일링을 전문적으로 하는 친구의 도움을 받아야합니다.

돌아와서, jsx로 작성한 문법을 간단하게 먼저 봅시다.

<div onClick={() => alert('?')} className="some_class" />

는 아래와 같이 트랜스파일링됩니다.

React.createElement('div', { onClick: () => alert('?'), className: "some_class" }, null);

createElement 함수로 작성을 하면 가독성이 안 좋기 때문에, 보통 jsx 문법으로 ReactNode를 작성합니다.

# ReactDOM.render

이제 index.js로 돌아와서, ReactDOM.render 부분을 볼까요?

React와 ReactDOM은 import 구문을 보면 추측할 수 있듯이, 별도의 패키지(라이브러리)로 분리되어있습니다.

첫번째 파라미터로는 Jsx구문이 들어가있고, 두번째 파라미터는 querySelector가 들어가있죠?

jsx구문으로 생성한 React 가상DOM을 두번째 파라미터의 element에 그리겠다는 의미입니다.

# React를 배우기 전 최소한의 선행 학습

가장 중요한건 당연히 자바스크립트를 다루는 능력입니다

문법도 모르는 상태라면 아예 개발하기 어려운 수준이고요.

위에서 설명한것처럼 Jsx도 사실 자바스크립트입니다.

두번째는 querySelector같은 DOM API입니다.

바닐라(순수한) 자바스크립트로 DOM을 직접 제어할일은 없지만,

DOM 개념은 있어야 개발하기 수월합니다.

그래도 DOM 개념정도는 개발하면서 익혀도 아~주 큰 무리는 없습니다.

그리고 브라우저에 대해 많이 알수록 좋습니다.

# 실은..

리액트 개발은 이게 대부분입니다..

함수형 컴포넌트 + react hook API 로 개발하시면 됩니다.

클래스형 컴포넌트는 필요하면 그때가서 익혀도 늦지않습니다.

신규 개발을 클래스형 컴포넌트로 하는건 바보짓이라고 말씀드리고 싶네요.

hook API는 클래스형 컴포넌트의 문제점을 해결하기위해 등장했습니다.

기존의 라이프사이클 API의 문제를 아름답게 해결하는게 목표였지만,

다소 어렵습니다. 개념이 달라져서요.. 그래서 많은 질타를 받고있기도 하죠.

그래도 vue에서 새로 도입한 composition API도 hook API와 같은 컨셉을 갖고 있습니다.

그럼 답이 나왔죠? 선택은 여러분의 몫이지만, 저는 클래스형 컴포넌트를 절대 쓰고 싶지 않습니다.

# 첫번째 리액트 컴포넌트

그럼 간단하게라도 뭔가 개발해봐야겠죠?

튜토리얼에 가장 많이 활용하는 카운터를 개발해보겠습니다.

src/components/Counter.js 라는 이름으로 파일을 하나 생성하세요

const Counter = () => {
  const count = 0;
  return (
    <div>
      Count: {count}
    </div>
  );
}

export default Counter;

App.js에 Counter를 import 합니다.

필요없는 내용은 지워버리고 카운터만 남겨둡시다.

// ...
import Counter from './Counter';

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

npm start 명령어로 개발서버를 시작합니다.

브라우저에는 Count: 0 이라고 표시될 것 입니다.

이제 버튼을 클릭했을때, 숫자를 늘리고 줄여봅시다.

Counter.js를 수정합니다.

const Counter = () => {
  const [count, setCount] = useState(0);

  const countUp = () => {
    setCount(count + 1);
  }
  
  const countDown = () => {
    setCount(count - 1);
  }

  return (
    <div>
      Count: {count}
      <button onClick={countUp}>+</button>
      <button onClick={countDown}>-</button>
    </div>
  );
}

export default Counter;

변경사항을 저장하고, 브라우저에서 다시 확인해봅시다.

+ - 버튼들이 잘 동작함을 확인합니다.

이걸로 카운터 예제는 끝났다고 할 수도 있겠지만, 조금 더 살펴봅시다.

Counter.js를 수정합니다.


const Counter = () => {
  const [count, setCount] = useState(0);

  const countUp = () => {
    setCount(count + 1);
  }
  
  const countDown = () => {
    setCount(count - 1);
  }

  const countUp3 = () => {
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
  }

  return (
    <div>
      Count: {count}
      <button onClick={countUp}>+</button>
      <button onClick={countDown}>-</button>
      <button onClick={countUp3}>+3</button>
    </div>
  );
}

한번에 3을 더하는 로직과 버튼을 추가했습니다.

잘 동작하나요?

분명 count를 3번 더하는 의도인데.. 생각대로 동작하지않죠?

이유는 setCount 함수가 비동기로 동작하기때문입니다.

setCount 함수가 동작한다고해서, 내부의 count 변수가 바로 변하지않아요.

믿기지않을땐, 직접 확인해보세요.

로그를 마구마구 찍어봅시다

const countUp3 = () => {
  console.log(count);
  setCount(count + 1);
  console.log(count);
  setCount(count + 1);
  console.log(count);
  setCount(count + 1);
  console.log(count);
}

첫번째 클릭땐 0이 4번, 두번째 클릭땐 1이 4번 찍힐겁니다.

비동기라 함은 언제 동작할지 모릅니다.

우리 눈에는 바로바로 반영되겠지만, 사실 저 숫자 하나 바꾸기 위한 과정들이 꽤나 복잡합니다.

모든 과정을 지금 살펴보긴 어려우므로 우선 저걸 의대로한대로 동작할수있게 바꿔봅시다.

// setCount(count + 1);
// setCount(count + 1);
// setCount(count + 1);
setCount(prevState => prevState + 1);
setCount(prevState => prevState + 1);
setCount(prevState => prevState + 1);

이런식으로 변경해줍니다.

setter 파라미터에는 함수도 가능한데,

함수의 첫번째 파라미터로 변경 이전의 state가 넘어옵니다.

그것에 +1을 더하면 비로소 우리가 원하는 동작을 할 수 있죠.

이제 원하는대로 동작하죠?

# CSR, SSR의 개념?

간단하게 표현하면,

csr: 브라우저가 서버측에서 js파일을 다운받고, 그 자바스크립트를 실행하여 html 생성하여 렌더링

ssr: 서버측에서 html을 생성하고, 브라우저는 그것을 받아 렌더링

더 자세히는 여기서..

# props

자식 컴포넌트에 데이터를 전달할때 사용

const Hello = ({ name }) => {
  return (
    <div>hello~ {name}!</div>
  );
}
//App.js

const App = () => {
  return (
    <div>
      <div>App</div>
      <Hello name="world" />
    </div>
  );
}

Hello 컴포넌트는

hello~ world!
를 렌더링하겠죠?

예시에서는 문자열을 넘겼지만, 함수를 내려서 자식 컴포넌트에서 부모컴포넌트의

함수를 호출하게 만드는 것도 가능합니다.

부모의 setState 함수를 내려서, 자식에서 부모 컴포넌트의 상태를 바꿀 수 있겠죠?

그리고 props에는 children이라는 약간 특별해보이는 props도 있습니다.

const Hello2 = () => {
  return (
    <div className="hello-1">
      <div className="hello-2">
        <div className="hello-3">
          {children}
        </div>
      </div>
    </div>
  );
}

const App = () => {
  return (
    <div>
      <div>App</div>
      <Hello2>world</Hello2>
    </div>
  );
}

<Hello2>world</Hello2> 이 부분은...

<div className="hello-1">
  <div className="hello-2">
    <div className="hello-3">
      world
    </div>
  </div>
</div>

위처럼 렌더링됩니다. children을 어떻게 활용할 수 있을지 감이 오시나요?

이처럼 간편하게 데이터를 자식컴포넌트로 내려줄 수 있습니다.

하지만 반대로 자식이 부모 컴포넌트에게 데이터를 전달할수는 없습니다..

어플리케이션이 커질수록 데이터의 흐름을 알기 어렵게 하거든요.

이것을 단방향 데이터 플로우라고 합니다.

# 배열 렌더링하기

javascript array에는 map이라는 메소드가 있습니다.

(유사배열이나 iterable객체에 map메소드를 사용하고 싶다면, Array.from 을 활용하시면 됩니다.)

react에서는 {}에 expression을 작성하죠?

{렌더링하고싶은 배열.map}을 이용하여 배열을 렌더링 할 수 있겠죠.

const list = ['apple', 'banana', 'boseok'];

const Component1 = () => {
  return (
    <div>{list.map((item) => <div key={item}>{item}</div>)}</div>
  );
}

// Component1 result
<div>
  <div>apple</div>
  <div>banana</div>
  <div>boseok</div>
</div>

배열을 렌더링할땐 react에서는 key 라는 속성을 이용하여 배열의 변경을 감지합니다.

key에는 map 콜백의 두번째 인자인 index를 사용하지 않을 것을 권장합니다.

자세한 내용은 여기서..

# Context API

redux같은 구시대 유물은 이제 버리고, Context API를 쓰세요.

react팀에서도 Context를 권장하고 있습니다. 사실 react-redux도 Context 기반이기도 하고요.