Developer_hong

2. React 기초, 설명 본문

개인 프로잭트/react

2. React 기초, 설명

Developer_hong 2022. 10. 17. 19:38
반응형

[ npx create-react-app test ]

-> npx : 라이브러리 설치 도와주는 명령어 (node.js 설치 돼 있어야 사용가능)

-> create-react-app : 라이브러리 이름 (리액트 셋팅 다 된 bilerplate)

https://create-react-app.dev/

 

Create React App

Set up a modern web app by running one command.

create-react-app.dev

-> test : 프로젝트 이름


[ 프로젝트 실행 ]

npm start

-> nodejs 설치하면 npm이라는 툴 사용 가능


[ react 프로젝트의 index 파일은 public 폴더에 있다 ]

App.tsx : 메인 페이지에 들어갈 HTML 코드 구성하는 곳

즉, index.tsx에서 App.tsx 안에 있는 내용을 public 폴더의 index.html 구성하도록 작동원리
(App.tsx (index.tsx) index.html)


[ 프로젝트 폴더 설명 ] 

 

node_modules : 

리액트 프로젝트에서 사용하는 라이브러리 모아둔 폴더

-> 예를 들어서 create-react-app 을 구동하기 위해서 수 많은 라이브러리들이 필요하고, 그 라이브러리들은 node_modules에 보관

 

public : 

동적으로 바뀌지 않는 static 파일들을 보관한다 ex) 이미지 파일, robots.txt, favicon.ico

-> 리액트 프로젝트를 다 만들고 나면 코드들이 압축이 되는데 public 안에 파일들은 건드리지 않는다

 

src : 

소스 코드 보관함, 실질적으로 코딩은 모두 여기서 


[ JSX ]

리액트에서는 HTML 코드 대신에 JSX를 사용한다

일종의 JavaScript라고 생각하면 좀 더 이해가 쉽다

HTML : <div class="test-class">

JSX : <div className="test-class"> //Camel case 사용한다

 

Angluar, Vue, React 는 데이터 바인딩이  쉽다는 장점이 있어서 사용한다

전통적인 자바스크립트 데이터 바인딩을 사용하는 경우, HTML에서 서버에서 가져온 데이터를 document.getElementById().innerHTML = ... 를 사용하여서 하나 하나 셋팅 해줘야 하는데

JSX에서는 중괄호를 사용하여 {변수명} 을 통해 쉽게 데이터 바인딩을 할 수 있다

 

App.tsx

let 변수를 이용한 데이터 바인딩
함수를 이용한 데이터 바인딩
import 이미지를 데이터 바인딩
let 변수를 이용한 className 데이터 바인딩

Style 세팅
HTML : <div style="font-size : 16px">

JSX : <div style = { { color : 'blue', fontSize : '30px' } }

let 변수를 이용한 style 데이터 바인딩


[ 터미널에 뜨는 warning ]

compiling...

compiling with warnings

 

./src/App.js

 Line 2:8: 'test_1' is defined but never used  no-unused-vars

 Line 6:13: 'test_2' is assigned a vlue but never used    no-unused-vars

eslint가 잡아주는 문법 체크 필요 없다면 코드 최상단에 다음과 같이 입력

/* eslint-disable */


[ 클릭 리스너 ]

HTML : <button onclick="select()">

JSX : <button onClick={ select() }> 또는 <button onClick={ ()=> { } }>

 

// 콜백 함수

addEventListener('click', function (){

});

 

// ES6 콜백 함수는 function 대신에 arrow 사용가능

addEventListener('click', ()=>{

});

 

// 클릭했을 때 증가하도록

let [clickCnt, clickCntUp] = useState(0);

<span onClick={ () => { clickCntUp(clickCnt + 1) } }></span>


[ event ]

preventDefault를 사용하여 해당 태그의 기본동작을 방지한다 (a 태그에 사용한 경우 클릭했을 때 페이지 이동을 막는다)

props로 전달된 속성을 사용하여 onChangeMode 함수를 호출하여 alert(); 가능

import React from "react";
import "./App.css";

function List(props: any) {
  const list = [];
  for (let i = 0; i < props.items.length; i++) {
    let t = props.items[i];
    list.push(
      <li key={t.id}>
        <a
          id={t.id}
          href={"/item/" + t.id}
          onClick={(event) => {
            event.preventDefault();
            props.onChangeMode((event.target as HTMLInputElement).id);
            //event.target은 event를 유발시킨 target(a태그)를 가르킨다
          }}
        >
          {t.title}
        </a>
      </li>
    );
  }
  return (
    <div>
      <ol>{list}</ol>
    </div>
  );
}

function App() {
  const items = [
    { id: 1, title: "title1", body: "body1" },
    { id: 2, title: "title2", body: "body2" },
    { id: 3, title: "title3", body: "body3" },
  ];

  return (
    <div className="App">
      <List
        items={items}
        onChangeMode={(id: String) => {
          alert(id); //해당 태그의 id 값을 출력
        }}
      ></List>
      this is new project
    </div>
  );
}

export default App;

[ react return에는 하나의 태그만 생성 가능 ]

App.tsx

function App() {
  return (
    <div></div> // 다음과 같이 여러개의 div 태그 사용 불가능

    <div></div>

    <div></div>

  );

 

function App() {
  return (

<div>                   //다음과 같이 큰 div로 감싸주기
    <div></div>

    <div></div>

    <div></div>

</div>

  );

 

function App() {
  return (

< >                      //다음과 같이 fragment 문법 사용
    <div></div>

    <div></div>

    <div></div>

< / >

  );


[ component ]

component 관습

- 이름은 대문자

- return() 안에는 여러개의 태그 사용 불가능, 하나의 태그로 묶어서 사용해야 함

- component 만드는 기준 : 너무 많은 component는 관리가 더 어려워질 수 있음

 반복적인 HTML 태그

 자주 변경되는 HTML UI (재랜더링이 빨라진다)

 다른 페이지로 나눌 때 컴포넌트로 나눈다

- component 단점 : 

 useState 사용할 때 다른 컴포넌트에서 만든 변수를 사용할 때 불편 ( 상위 컴포넌트에서 만든 건 props로 사용가능 )


[  Redux (비슷한 기능 : Mobx, Overmid.js, Recoil) ]

props를 내부로 한번에 전송하는 것 불가능, 이렇게 컴포넌트 이동해서 전달해 줘야한다

redux를&nbsp; 이용하면&nbsp;props drilling 현상을 방지할 수 있음

Redux를 설치하면 state를 보관하는 공간을 생성할 수 있음

예를 들어서 storage.js 를 만들어서 각각의 component에서 꺼내서 쓸 수 있음

상태 관리가 용이하다(=state 관리가 용이하다) : 다른 component에서 변경을 위해서 모두 수정 방법을 정해둔다

-> redux에 저장한 변수가 어느순간 숫자였는데 문자로 바껴서 에러가 발생할 경우 어떻게 찾아야할까?

만약 component가 100개 이상인 경우 어디서 어떻게 바뀌는지 찾기가 어려울 것이다

따라서 storage.js에 수정 방법을 정해두고 component에서는 수정 요청만 가능하도록 설계되어 있음

 

 

index.tsx

 

import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react";
import { createStore } from 'redux';
import "./index.css";
import App from "./App";


function reducer (state = reduxData, action){
  if (action.type === '증가'){
      state ++;
      return state;
  } else if (action.type === '감소'){
      state —;
      return state;
  } else {
      return state;
  }
}


const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    <Provider store = {store}>
      <App />
    </Provider>
  </React.StrictMode>
);

 

App.tsx

 

import React from "react";
import { useSelector } from 'react-redux';
import "./App.css";

function App() {
  const reduxData = useSelector( (state) => state );

  const dispatch = dispatch()


  return (
    <div className="App">
      <div> { reduxData } </div>

      <button onClick={ () => { dispatch( { type : '증가' } ) } } > 증가</button>
    </div>
  );
}
export default App;


[ props ]

*Prop과 State의 차이점

prop은 컴포넌트를 사용하는 외부자를 위한 데이터

state는 컴포넌트를 만드는 내부자를 위한 데이터

 

HTML에는 width, height 와 같은 속성이 있다  <p with="300" height=100"></p>

이러한 속성을 react에서는 props라고 부른다

Div function의 parameter로 props를 선언한다 (이름은 아무거나 해도 되지만 대부분 props로 사용)

 

import React from "react";
import "./App.css";

function Test(props: any) {
  const list = [];
  for (let i = 0; i < props.items.length; i++) {
    let t = props.items[i];
    list.push(
      <li key={t.id}>
        <a href={"/item/" + t.id}>{t.title}</a>
      </li>
    );
  }
  return (
    <div>
      <ol>{list}</ol>
    </div>
  );
}

function App() {
  const items = [
    { id: 1, title: "title1", body: "body1" },
    { id: 2, title: "title2", body: "body2" },
    { id: 3, title: "title3", body: "body3" },
  ];

  return (
    <div className="App">
      <Test items={items}></Test>
      this is new project
    </div>
  );
}

export default App;

react에서 자동으로 생성하는 태그의 경우에는 태그들을 추적해야되는데 key라는 prop 고유 값을 준다

 


[ router ]

설치 : npm install react-router-dom

리액트에서 가장 보편적으로 사용되는 BrowserRouter import하고 <BrowserRouter>로 감싸준다

 

Index.tsx

App.tsx


[ 리액트에서 타이머 ]

react에서 카운트다운, 시간에 따른 버튼 활성화 등을 구현하려면

렌더링이 너무 자주 일어나는데 어떻게 해야할까?

useInterval을 사용하면 불필요한 렌더링을 피할 수 있고, hook을 만들어서 사용 가능하다

 

setInterval 사용

1] import React, { useState, useEffect, useRef } from 'react';

2] function Counter() {
3]   let [count, setCount] = useState(0);
4]  setInterveal(() => {

5]     // custom logic here
6]     setCount(count + 1);
7]   }, 1000);
8]   return <h1> {count} </h1>;
9] }

 

setInterval을 사용하면 1초 마다 새로운 타이머가 계속 렌더링됨 (모든 타이머는 계속 존재)

첫번째 렌더링의 클로저에서 count : 0 -> 1  // setCount(count + 1)

두번째 렌더링의 클로저에서 count : 1 -> 2  // setCount(count + 1)

. . .

열번째 렌더링의 클로저에서 count : 9 -> 10  // setCount(count + 1)

 

위 4~6 라인의 코드는 출력 return <h1> { count } </h1> 을 바꾸는게 아니라

입력을 바꾸는 사이드 이펙트이며 위 컴포넌트 입장에서 입력은 바깥에서 오고있음

입력이 바뀌면 출력을 위해서 계속 렌더링됨 (리액트의 특징)

다음과 같이 useEffect 사용하여 변경하면 동작할 수는 있지만 너무 신경써줄 내용이 많다

 

[useInterval 사용

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

function Counter() {
  let [count, setCount] = useState(0);
  useInterveal(() => {
    // custom logic here
    setCount(count + 1);
  }, 1000);
  return <h1> {count} </h1>;
}


[ input ]

App.tsx

import React, { useState } from "react";
import "./App.css";

function App() {
  const [inputs, setInputs] = useState({ name: "", phone: "", address: "" });

  const onChange = (e: any) => {
    const value = (e.target as HTMLInputElement).value;
    const id = (e.target as HTMLInputElement).id;
    setInputs({
      ...inputs,
      [id]: value,
    });
  };

  return (
    <div className="App">
      <div>
        <label>이름</label>
        <input type="text" id="name" value={inputs.name} onChange={onChange} />
      </div>
      <div>
        <label>전화번호</label>
        <input
          type="text"
          id="phone"
          value={inputs.phone}
          onChange={onChange}
        />
      </div>
      <div>
        <label>주소</label>
        <input
          type="text"
          id="address"
          value={inputs.address}
          onChange={onChange}
        />
      </div>
      <p>이름 : {inputs.name}</p>
      <p>전화번호 : {inputs.phone}</p>
      <p>주소 : {inputs.address}</p>
    </div>
  );
}

export default App;

[ map ]

App.tsx

import React from "react";
import "./App.css";

const Itemcomponent = ({ itemData }: any) => {
  console.log(itemData);
  return (
    <tr key={itemData.id}>
      <td>{itemData.name}</td>
      <td>{itemData.price}</td>
    </tr>
  );
};

function App() {
  const items = [
    { id: 1, name: "HP포션", price: "50원" },
    { id: 2, name: "MP포션", price: "100원" },
    { id: 3, name: "갑옷", price: "300원" },
    { id: 4, name: "수정석", price: "400원" },
  ];

  return (
    <div className="App">
      <table>
        <thead>
          <tr>
            <th>name</th>
            <th>price</th>
          </tr>
        </thead>
        <tbody>
          { items.map( (item) => (<Itemcomponent itemData={item} />) ) }
        </tbody>
      </table>
    </div>
  );
}

export default App;

 

반응형

'개인 프로잭트 > react' 카테고리의 다른 글

4. React useEffect  (0) 2022.11.23
1. 개발환경 세팅 (React + TypeScript)  (0) 2022.08.23