React 기초 개념들

고은찬·2021년 8월 30일
0

React.js

목록 보기
1/4

리액트 기초들을 공부하고 정리한 글

추가 할 것 : props, routing,비동기처리, import/export,상태관리프레임워크(redux,mobx)

▶CRA (Create React App)

CRA는 리액트로 웹 어플리케이션을 만들기 위한 환경을 편하게 제공한다.
주요 제공 하는 것들로는 바벨과 웹펙, 그리고 테스트환경, HMR, ES6+ , CSS 후처리 등 여러가지의 개발환경을 구축해 준다.
이러한 개발 환경을 직접 구축하려면 시간이 오래 걸릴 뿐 아니라 직접 유지보수도 해야한다.
CRA의 경우는 패키지 버전만 올리면 자동 업데이트가 되서 편리하고 초기 난이도도 쉬워진다.

//터미널에서
npx create-react-app (프로젝트이름) 

을 입력하면 된다.
저 문구만 입력하면 리액트가 동작하는데 필요한 폴더와 리소스들이 자동으로 생성되있다.

  1. public 폴더에는 html과 정적 리소스들을 넣는다
  2. public 폴더에는 index.html에서 참조하는 파일들이 있어야 한다.
  3. src 폴더에는 모든 스크립트를 넣는다
  4. index.js로부터 연결된 모든 js,css파일은 src폴더 밑에 넣는다.
    -> src폴더밑에서 import를 해야 빌드시 js,css 파일들이 자동 압축된다(이미지,폰트 파일도 import하는게 좋다.

<주요 폴더 설명>
1. index.js : public/index.html 에 App.js의 내용들을 다 집어넣는 파일
- Strict 모드는 UI에서 활성화되지 않고 개발모드에서 여러 검사나 경고, 알림 등을 표시해주는역할을 한다. 그래서 지워도 작동은 한다.
2. index.html : 사용자가 처음보이는 파일
- 이 페이지 안의

안에 js로 모든 내용들을 동적으로 추가한다.
3. package.json : 이 프로젝트가 사용하고 있는, 필요한 모듈들 기록
4. src 폴더 : 이 폴더 안에 모든 스크립트가 들어간다.
5. public 폴더 : 이미지들 여기다 배치
6. node_modules : 필요한 모든 모듈들을 싹 정리해놓은 것. (크기가 큼)

CRA 주요 명령어

CRA를 하게되면 터미널에서 4가지 명령어들을 사용 할 수 있다.

  1. npm start
  • 개발 모드로 프로그램을 실행하는 명령어
  • https 로 실행할 경우(API 호출 등) "set HTTPS=true && npm start" 를 입력
  1. npm run build
  • 배포 환경에서 사용할 파일을 만들어 주는 명령어 -> build 폴더 생성
  • 배포 시 이파일을 웹 서버에 등록하면 된다.
  1. npm test
  • 테스트 코드 실행하는 명령어
  • jest라는 테스트 프레임워크를 기반으로 한 테스트 시스템이 실행된다.
    -CRA에서는 아래 조건들을 만족하면 테스트파일로 인식
    1) tests 폴더 밑에 있는 모든 js파일
    2) 파일 이름이 .test.js로 끝나는 파일
    3) 파일 이름이 .spec.js로 끝나는 파일
  • 자세한 테스트 내용은 책 참고
  1. npm run eject
  • 숨겨져있던 CRA의 내부 설정 파일들이 생성된다.

  • 웹펙, 바벨의 설정을 변경 할 수 있다.

  • 장점) 내 맘대로 설정을 커스텀 할 수 있다.

  • 단점) CRA에서 개선된 기능을 직접 유지보수해 업데이트 해야한다

  • 단점) 한번 eject 시키면 다시 되돌릴 수 없다.

  • CRA의 설정을 변경하는 다른 방법이 있다.
    1) react-script 프로젝트를 포크(forks)해서 나만의 스크립트를 만든다.
    2) react-app-rewired 패키지를 사용
    -> 자유도는 낮지만 비교적 쉽게 설정을 변경 할 수 있다. 그러나 마찬가지로 CRA이후 버전에 변경된 내용을 쉽게 적용하기는 어렵다.




▶JSX

jsx는 리액트에서 사용하는 자바스크립트와 HTML 이 섞여있는 문법이다.

왜 JSX를 사용할까?

리액트에서는 컴파일 했을 때 최종적으로 JS문법으로 컴파일 해 랜더링한다.
아래 예제 코드의 "React.createElement" 로 시작하는 문법으로 변환하여 컴파일하는데 이 문법은 읽기도, 쓰기도 불편하다.
그래서 저 코드를 편하게 치기 위해 JSX가 나오게 되었다.
바벨이 JSX코드를 JS문법으로 변환해주는 역할을 맡는다.

import React from "react";

const aa = "은찬";
const bb = false


function Content(){
    
    return ( 
     	// 변수 bb가 true면 red, false면 green 을 나타내는 문법
        <h1 className="testing" style={bb ? {color:"red"} : {color:"green"}}>
            안녕하세요{aa}
        </h1>
    
    
        // 아래의 JSX코드를 바벨이 이렇게 JS문법으로 변환해서 컴파일한다.
        // JSX가 생겨난 이유가 이 이유다. 아래 코드는 읽기도, 쓰기도 불편해서
        
         React.createElement(
             'h1',
             {className:"testing", style:{backgroundColor:"red"}},
             `안녕하세요 ${aa}`
         )

    )
} 

JSX 주요 문법

  1. return() 안에는 하나의 div를 반환해야 한다.
function example(){
    <div>
    	<div>이렇게 큰 하나의 태그로 감싸고 있어야 한다. </div>
        <div>2개 이상이면 에러가 나온다</div>
    </div>
}

//리액트 프래그먼트라 칭한다. 
function example2(){
    <>
    <div>이렇게 선언하면 </div>
    <div>하나의 큰 HTML을 반환하지 않는다. </div>
    <div>div 의 depth 깊어질때 사용하면 깔끔해짐 </div>
    </>
}
  1. HTML의 클래스는 className = "name" 으로 선언해야 한다.
  2. CSS할때 {}로 감싸고, padding-left가 아닌 paddingLeft로 작성해야 한다.
            <h1 className="testing" style={bb ? {color:"red"} : {color:"green"}}>
            안녕하세요{aa}
        </h1>
  3. return 안에 {} 안에는 함수(),변수만 들어가고 if문,for문은 못들어간다.
//if문을 이렇게
 {
          modal_switch === true 
          ? <Modal /> // ? == if
          : null // 텅 비었다. // : else

 }
 
 
 //for 문은 이렇게
 {
 // 방법 1. map() 사용
 //array 내의 모든 데이터에 똑같은 작업을 시켜 주고 싶을 때 map()을 사용한다.
 // map = 유사 반복문
 let array = [2,3,4];
 
 let newarray = array.map(function(a){
  return a*2
  
   {
        //리스트의 갯수만큼 for문 실행
        title.map(function(titles,i){
          return(
            <div className="list" key = {i}>
            {/* onClick = {()=>{ 콜백함수 실행 내용 } } */}
            <div onClick = { ()=>setCliickidx(i) }>{titles} <span onClick = {()=>{setcount(count+1)}}>👍</span> {count} </div>
            <p>내용</p>
            <hr/>
          </div>

          )
        })
      }
   
   
  // newarray = 4,6,8
 )}
 
 -------------
   { 반복할데이터.map( ()=>{ return<HTML>} )}
                          
 }     

   
  // 방법 2. for문 함수 따로 생성
   
  function formoon() {
    let array =[];
    for (let i=0; i<title.length; i++ ){
      array.push(
        <div>안녕</div>
      );
    }
    return array;
  }
  1. 함수를 사용할떄는 ()를 빼고 쓴다. ()가 있으면 무한 렌더링 에러



▶Hooks

요약하자면 Hook은 함수형 컴포넌트가 클래스형 컴포넌트의 기능을 사용할 수 있도록 해주는 기능이다.

아주 쉽게 클래스형 컴포넌트 / 함수형 컴포넌트(Hook)으로 구분된다고 할 수 있다.
컴포넌트 작성을 클래스로 하느냐 함수로 하느냐의 차이다.

컴포넌트란?

컴포넌트란?
HTML 덩어리를 하나의 태그로 줄여서 쓸 수 있는 하나의 단위.

<규칙>
1. 첫 이름은 대문자
2. return 안에 태그 하나로 묶어야 함
3. 다른 파일에서 컴포넌트 만들때 상단에

import React,{useState} from 'react'; 필수? 왜?

컴포넌트를 만드는 기준?

  1. 반복적으로 나오는 HTMl
  2. 사이트에서 자주 변경되는 UI(재랜더링 많이 일어나는)-> 성능 좋아짐
  3. 하나의 페이지 컴포넌트화

컴포넌트를 많이 만들면 단점

  1. 컴포넌트 안에서 state를 사용 할때 복잡해진다
    -> 다른 컴포넌트 안에서 만든 state를 하위 컴포넌트에서 사용하고 싶다 => Props

클래스형 컴포넌트

기존의 개발방식은 일반적으로 함수형 컴포넌트를 주로 사용하되 state이나 Life Cycle method를 사용해야 할 때에만 클래스형 컴포넌트를 사용하는 방식이었다.
클래스형 컴포넌트는 함수형에 비해서 단점들을 가지고 있었다.
1. 코드가 길고 복잡하다.

  • 생성자,this,bindding등 지켜야할 규칙이 많아 코드가 길어진다.
  • Life Cycle method로 인해 기본적으로 코드가 뚱뚱하다.
  1. 로직의 재사용이 어렵다.
  • 부분적인 DOM 관련 처리나 API사용 및 state를 다루는 로직에서는 같은 로직을 2개 이상의 Life Cycle method 에 넣어야 하는 등 재사용에 제약이 따른다.

이러한 단점이 있음에도 클래스형 컴포넌트를 사용했던 이유는 state관리와 Life Cycle method 때문이었는데 이제 Hooks의 등장으로 인해 함수형 컴포넌트에서도 클래스형 컴포넌트에서 할 수 있었던 작업들을 할 수 있게 되었다.


함수형 컴포넌트

State Hook - useState
Effect Hook - useEffect
이 두가지의 Hook이 존재한다.

함수형 컴포넌트 안에 이런 hook을 사용함으로서 클래스형 컴포넌트의 장점(state관리, Life Cycle method) 들을 살리면서 코드도 쉽고 직관적으로 사용 할 수 있기에 리액트 팀에서도 훅과 관련된 기능 개발에 많은 시간을 투자하고 있다.
(함수형 컴포넌트를 사용하는게 좋다)


useState

State는 리액트에서 변수 대신 사용할 수 있는 데이터 저장공간이다. (useState함수로 선언한다) , 상태값 추가하기

주의! useState import 필수!!



리액트에서 데이터를 관리하는 방법으로는
1. 변수에 넣는다
2. state에 넣는다
2가지 방법이 있다.

리액트에서 변수 대신 state를 사용하는 이유는 state가 변경되면 HTMl이 자동으로 랜더링을 해 실시간으로 변해 웹앱처럼 사용할 수 있기 때문이다.

변하지 않거나 중요하지 않은 값들은 state로 안해도 상관없다.

useState는 기본적으로 getter와 setter로 이루어져 있다.

  • getter : 변수의 값을 가져오는데 사용
  • setter : 변수의 값을 변경하는데 사용
//ES6 destructuring 문법으로 인해 이렇게 선언할 수 있다.

//title에 글제목이라는 값 초기화
let title,setTitle = useState('글제목');  // [ state데이터, state 데이터 변경 함수]



//배열도 담을 수 있다. 
let [title,setTitle] = useState(['title1','title2','title3']); //[state데이터, state 데이터 변경 함수]


//실제 사용은 아래와 같은 형식
<div>{title[0]}</div>

문자, 숫자, array, object 다 담을 수 있다.

만약 값을 변경하고 싶다면

setTitle('제목1입니다.'); 

이렇게 쉽게 변경 가능하다.

  • useState()의 호출순서는 책 참고
  • 여러 상탯값을 하나의 객체로 관리할때는 ...state를 쓰는 것보다 useReducer를 사용하는게 낫다.

useEffect

useeffect훅은 컴포넌트가 생길때(mount), update됬을 때 특정코드를 실행 할 수 있음

Lifecycle

컴포넌트는
1. 생성이 될 수도 있고
2. 삭제가 될 수 있고
3. 관련된 state가 변경되면 재렌더링(업데이트)가 일어날 수있음

ex) 컴포넌트가 생성/삭제되기 전에 Hook을 걸어 "이것좀 해주세요!" 할수 있다.

//이전 class 형 
class Detail2 extends React.Component {
  componentDidMount(){
    //Detail2 컴포넌트가 Mount 되고나서 실행할 코드
  }
  componentWillUnmount(){
    //Detail2 컴포넌트가 Unmount 되기전에 실행할 코드
  }
}
//함수형 
import React, {useState, useEffect} from 'react';

function Detail(){

  useEffect(()=>{
    //코드를 적습니다 여기
  });
  
  return (
    <HTML많은곳/>
  )
}

<함수형- Hook(useEffect) 사용법>
1. 미리 페이지 상단에서 useEffect를 import 해오신 후
2. useEffect() 를 사용
3. 그리고 안에 콜백함수
4. 콜백함수 안에는 Detail 컴포넌트가 첫 등장하고나서 실행하고싶은 코드가 있으면 적어주면 됨
5. useEffect() return 에는 컴포넌트가 사라질 때(unmount) 실행할 코드(페이지 이동시)
6. useEffect()는 여러개 정의할 수 있다. (순서대로 진행)
7. mount될때, update될 때 2번 실행되기 때문에 update될때는 막아줘야 한다.

function Detail(props) {
  const [alert,set_alert] = useState(true);

    //Detail 컴포넌트가 생길때(로딩이 될 떄), 컴포넌트가 update될때 useEffect()함수 안의 내용이 실행된다.
    useEffect(()=> {
        console.log('실행');
            
        let Timer = setTimeout(() => {
            console.log('4초 후');
            
            set_alert(false);
            
        }, 4000);
        
        return // 여기에는 컴포넌트가 종료될떄(페이지 이동할 떄) 실행시킬 콜백함수 작성 가능
    },[clearTimeout(Timer)]); //update될때 실행안시키기 위해, 이 []안에는 state를 넣을 수있다. [state] => state함수가 변경 될 때만 실행 ( 조건 )
    //아무 값도 안넣고 그냥 []만 사용하면 => Detail 컴포넌트 업데이트 시 실행 안된다. => 페이지 로드시에만 한번 실행
    
    
    



▶props

하위 컴포넌트에서 상위 컴포넌트에 있는 데이터를 사용 하고 싶을 때 사용한다.

참고
1. 하위 컴포넌트에서 사용할때 prop.state .. 앞에 "props" 를 붙힌 뒤 사용해야 한다.
2. <.Card shoes={data} i={i} key={i}/> 식으로 여러개의 props를 보낼 수 있다.
3. 중요한 데이터는 App.js에서 가지고 있는게 암묵적인 규칙이다. 데이터를 최상위 컴포넌트에서 관리하는게 쉽기 떄문 (리액트는 단방향)

import { Button } from 'react-bootstrap';
import '../css/mainpage.css';
import React,{useState} from 'react';
import dataArray from '../data/data.js';
import { func } from 'prop-types';
function Mainpage() {
    // data.js에서 json 형식의 신발 데이터를 가져와 state에 담는다. 
    const [shoes,set_shoes] = useState(dataArray); //array

    //(확인)하나의 신발 데이터에 id,img,price,content가 포함된다.
    console.log(shoes);


    // 신발상품 카드에 담길 컴포넌트 -> 후에 반복해서 보여질 예정
    // props : shoes 배열
    function Card(props) {
        return(
            <div className="col-md-4">
            {/* 이미지 둘중에 아무거나 써도 값 동일함 */}
            {/* <img src={'https://codingapple1.github.io/shop/shoes'+(props.i+1) +'.jpg'} width="100%" /> */}
            <img src={props.shoes.img} width="100%" />
            <h4>{props.shoes.title}</h4>
            <p>{props.shoes.content} / {props.shoes.price}원</p>
        </div>
        )
    }

    return(
        <>
        <div className="mainpage">
            <div className="intro">
                <h2>쇼핑몰 메인 페이지</h2>
                <Button variant="primary">시작하기</Button>{' '} {/* 부트스트랩 버튼*/}
            </div>
        </div>

        {/* 부트스트랩 사용 -> app.js 상단에 bootstrap <link>연결 필요*/}
        <div className="container"> 
        <div className="row" >
            {
                // data.js에서 가져온 shoes의 배열크기만큼 반복문으로 컴포넌트를 그려준다.
                // data : shoes 데이터 , i : index 
                shoes.map( (data,i) => { 

                    //(확인) 데이터 array + index
                    console.log("shoes: "+shoes+" / "+i);

                    return(
                        // 컴포넌트의 이름은 대문자로 시작해야 된다^^
                        // <Card shoes={shoes[i]}/> 와 아래 코드는 동일하다.
                        // map()사용시엔 key가 필수인데 key는 여기 적어도 상관없다.
                       <Card shoes={data} i={i} key={i}/>

                    )
                })
            }
        </div>
      </div>
      </>
    )
}


export {Mainpage};



▶router

라우터는 페이지 이동을 위해 쓰인다.
라우팅이란?

  1. npm install react-router-dom 라우터를 설치한다.
  2. index.js 파일에서 추가로 설정해준다.
//index.js

import { BrowserRouter } from 'react-router-dom'; // 라우터 관련 

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App/>
    </BrowserRouter>
  </React.StrictMode>
  document.getElementById('root')
);
  1. App.js에서 Router 사용 -> 페이지 이동하는 것처럼 보이게 가능
    실제로는 하나의 HTML 파일이다.

  2. 기본적으로 Router는 매치되는 모든 url을 전부 다 보여준다.

  3. 모든 Router 들을 로 감싸면,여러개의 Router를 찾아도 맨위의 Router 하나만보여준다. ( 중복 처리 가능 - exact 필요없음)

  4. /detail/:id => 아무 문자나 받겠나는 URL 작명법이다.

  • 글번호,상품번호 등으로 쓰일 수 있다.
  • 콜론 뒤에 맘대로 작명가능
  • 여러개 사용가능 (/:id/:idx...)
  
//App.js
import './App.css';
import {Navibar} from './components/Navibar';
import {Mainpage} from './components/Mainpage'; 
import {Detail} from './components/Detail';
import { Link, Route, Switch } from 'react-router-dom'; // 라우터 관련 import

function App() {
  return (
    <div className="App">
        {/* 아래의 경우 "/"가 모든 경로에 포함되므로 중복되어 다 보여진다
        이 경우 exact를 추가하여 경로가 정확하게 일치할때만 라우팅되도록 한다. */}
        <Switch>
            <Route exact path="/">
                <Navibar/>
                <Mainpage/>
            </Route>
            <Route path="/detail/:id"> <Detail /> </Route>
            {/* <Route path="/example" component={example}></Route> */}
            <Route path="/:id"> 
                <div>아무거나 적었을때 이거 보여주셈</div>
            </Route>
        </Switch>

    </div>
  );
}

export default App;
  



HashRouter vs browerRouter

  1. HashRouter
  • 좀 더 라우팅을 안전하게 할 수있게 도와줌
  • url에 # 이 붙음 ( #뒤에 적는 것은 서버에 전송이 되지 않기에 더 안전함 )
  1. BrowserRouter
  • 라우팅을 서버에 요청할 수 도 있어서 위험
  • 서버에서 라우팅을 방지하는 API를 작성해둬야함 ( 서버 : 그런 페이지 없는데요? )



▶기타 꿀팁

  1. settimeout() 이후 제대로 종료해주어야 오류 없음
    useEffect(()=> {
           console.log('실행');
               
           let Timer = setTimeout(() => {
               console.log('4초 후');
               
               set_alert(false);
               
           }, 4000);
           
           return clearTimeout(Timer)// 여기에는 컴포넌트가 종료될떄(페이지 이동할 떄) 실행시킬 콜백함수 작성 가능
       },[]);
     
/*eslint-disable 이라고 치면 터미널에 노란색 warning 안보임*/
profile
연애하는 개발자

0개의 댓글