리액트 기초들을 공부하고 정리한 글
추가 할 것 : props, routing,비동기처리, import/export,상태관리프레임워크(redux,mobx)
CRA는 리액트로 웹 어플리케이션을 만들기 위한 환경을 편하게 제공한다.
주요 제공 하는 것들로는 바벨과 웹펙, 그리고 테스트환경, HMR, ES6+ , CSS 후처리 등 여러가지의 개발환경을 구축해 준다.
이러한 개발 환경을 직접 구축하려면 시간이 오래 걸릴 뿐 아니라 직접 유지보수도 해야한다.
CRA의 경우는 패키지 버전만 올리면 자동 업데이트가 되서 편리하고 초기 난이도도 쉬워진다.
//터미널에서
npx create-react-app (프로젝트이름)
을 입력하면 된다.
저 문구만 입력하면 리액트가 동작하는데 필요한 폴더와 리소스들이 자동으로 생성되있다.
- public 폴더에는 html과 정적 리소스들을 넣는다
- public 폴더에는 index.html에서 참조하는 파일들이 있어야 한다.
- src 폴더에는 모든 스크립트를 넣는다
- 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를 하게되면 터미널에서 4가지 명령어들을 사용 할 수 있다.
숨겨져있던 CRA의 내부 설정 파일들이 생성된다.
웹펙, 바벨의 설정을 변경 할 수 있다.
장점) 내 맘대로 설정을 커스텀 할 수 있다.
단점) CRA에서 개선된 기능을 직접 유지보수해 업데이트 해야한다
단점) 한번 eject 시키면 다시 되돌릴 수 없다.
CRA의 설정을 변경하는 다른 방법이 있다.
1) react-script 프로젝트를 포크(forks)해서 나만의 스크립트를 만든다.
2) react-app-rewired 패키지를 사용
-> 자유도는 낮지만 비교적 쉽게 설정을 변경 할 수 있다. 그러나 마찬가지로 CRA이후 버전에 변경된 내용을 쉽게 적용하기는 어렵다.
jsx는 리액트에서 사용하는 자바스크립트와 HTML 이 섞여있는 문법이다.
리액트에서는 컴파일 했을 때 최종적으로 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}`
)
)
}
function example(){
<div>
<div>이렇게 큰 하나의 태그로 감싸고 있어야 한다. </div>
<div>2개 이상이면 에러가 나온다</div>
</div>
}
//리액트 프래그먼트라 칭한다.
function example2(){
<>
<div>이렇게 선언하면 </div>
<div>하나의 큰 HTML을 반환하지 않는다. </div>
<div>div 의 depth 깊어질때 사용하면 깔끔해짐 </div>
</>
}
<h1 className="testing" style={bb ? {color:"red"} : {color:"green"}}>
안녕하세요{aa}
</h1>
//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;
}
요약하자면 Hook은 함수형 컴포넌트가 클래스형 컴포넌트의 기능을 사용할 수 있도록 해주는 기능이다.
아주 쉽게 클래스형 컴포넌트 / 함수형 컴포넌트(Hook)으로 구분된다고 할 수 있다.
컴포넌트 작성을 클래스로 하느냐 함수로 하느냐의 차이다.
컴포넌트란?
HTML 덩어리를 하나의 태그로 줄여서 쓸 수 있는 하나의 단위.
<규칙>
1. 첫 이름은 대문자
2. return 안에 태그 하나로 묶어야 함
3. 다른 파일에서 컴포넌트 만들때 상단에
import React,{useState} from 'react'; 필수? 왜?
기존의 개발방식은 일반적으로 함수형 컴포넌트를 주로 사용하되 state이나 Life Cycle method를 사용해야 할 때에만 클래스형 컴포넌트를 사용하는 방식이었다.
클래스형 컴포넌트는 함수형에 비해서 단점들을 가지고 있었다.
1. 코드가 길고 복잡하다.
이러한 단점이 있음에도 클래스형 컴포넌트를 사용했던 이유는 state관리와 Life Cycle method 때문이었는데 이제 Hooks의 등장으로 인해 함수형 컴포넌트에서도 클래스형 컴포넌트에서 할 수 있었던 작업들을 할 수 있게 되었다.
State Hook - useState
Effect Hook - useEffect
이 두가지의 Hook이 존재한다.
함수형 컴포넌트 안에 이런 hook을 사용함으로서 클래스형 컴포넌트의 장점(state관리, Life Cycle method) 들을 살리면서 코드도 쉽고 직관적으로 사용 할 수 있기에 리액트 팀에서도 훅과 관련된 기능 개발에 많은 시간을 투자하고 있다.
(함수형 컴포넌트를 사용하는게 좋다)
State는 리액트에서 변수 대신 사용할 수 있는 데이터 저장공간이다. (useState함수로 선언한다) , 상태값 추가하기
주의! useState import 필수!!
리액트에서 변수 대신 state를 사용하는 이유는 state가 변경되면 HTMl이 자동으로 랜더링을 해 실시간으로 변해 웹앱처럼 사용할 수 있기 때문이다.
변하지 않거나 중요하지 않은 값들은 state로 안해도 상관없다.
useState는 기본적으로 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입니다.');
이렇게 쉽게 변경 가능하다.
useeffect훅은 컴포넌트가 생길때(mount), update됬을 때 특정코드를 실행 할 수 있음
컴포넌트는
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 컴포넌트 업데이트 시 실행 안된다. => 페이지 로드시에만 한번 실행
하위 컴포넌트에서 상위 컴포넌트에 있는 데이터를 사용 하고 싶을 때 사용한다.
참고
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};
라우터는 페이지 이동을 위해 쓰인다.
라우팅이란?
//index.js
import { BrowserRouter } from 'react-router-dom'; // 라우터 관련
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App/>
</BrowserRouter>
</React.StrictMode>
document.getElementById('root')
);
App.js에서 Router 사용 -> 페이지 이동하는 것처럼 보이게 가능
실제로는 하나의 HTML 파일이다.
기본적으로 Router는 매치되는 모든 url을 전부 다 보여준다.
모든 Router 들을 로 감싸면,여러개의 Router를 찾아도 맨위의 Router 하나만보여준다. ( 중복 처리 가능 - exact 필요없음)
/detail/:id => 아무 문자나 받겠나는 URL 작명법이다.
//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;
useEffect(()=> {
console.log('실행');
let Timer = setTimeout(() => {
console.log('4초 후');
set_alert(false);
}, 4000);
return clearTimeout(Timer)// 여기에는 컴포넌트가 종료될떄(페이지 이동할 떄) 실행시킬 콜백함수 작성 가능
},[]);
/*eslint-disable 이라고 치면 터미널에 노란색 warning 안보임*/