일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 0.75px border
- angular
- TypeScript
- jwt
- Websocket
- 문서번호
- Props
- Strict
- 클론코딩
- &연산
- 0.5px border
- 당근마켓
- 타입스크립트
- npm
- font-size
- readonly
- 컴포넌튼
- ZOOM
- es6
- TS
- ES5
- github
- 데이터베이스 #try #이중
- 10px
- 0.25px border
- 서버리스 #
- 1px border
- literal
- 전역변수
- entity
- Today
- Total
복잡한뇌구조마냥
RRR( React, Redux, Route ) 본문
단순 개념 정리
1. Redux
- 공식 홈페이지에서는 리덕스는 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너라고 정의하고있다.
- 자식 컴포넌트들 간의 다이렉트 데이터 전달은 불가능 하다.
- 자식 컴포넌트 간의 데이터를 주고 받을 때는 상태 관리를 담당하는 부모컴포넌트를 통해야 한다.
- 자식이 많이 진다면 관리가 매우 복잡하기때문에 상태 관리의 복잡성을 라이브러리로 해결하는 것이다.
- 부모 컴포넌트로부터 내려받던 데이터를 리덕스를 통해서 나눠갖게 된다.
- 전역 상태 저장소를 제공받고 *Props Drilling 문제를 해결한다.
*Props Drilling이란?
Props Drilling 은 props를 오로지 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트들을 거치면서
React Component 트리의 한 부분에서 다른 부분으로 데이터를 전달하는 과정이다.
따라서, Props의 비효율 발생, 컴포넌트가 많아질 수록 추적이 어렵고 컴포넌트 분리의 어려움과 props수정이 번거롭다.
2. Route
- 서로 다른 컴포넌트의 주소에 따라 컴포넌트 배치를 변경하는 작업을 Routing이라고 한다.
- 사용자가 입력한 주소를 감지하는 역할을 한다.
- SPA라 하더라도 다수의 컴포넌트 페이지주소를 다르게 구성할 필요가 있다.
- React에서는 자체적 Router 기능이 포함되지 않아 라이브러리를 설치해야한다. (React-Router-Dom)
- 가장 많이 사용되는 라우터 컴포넌트는 BrowserRouter와 HashRouter 이다.
BrowserRouter - HashRouter
Browser Router
- HTML5의 history API를 활용하여 UI를 업데이트 한다.
- 동적인 페이지에 적합하다 ※ 서버에 있는 데이터들을 스크립트에 의해 가공처리 한 후 생성되어 전달되는 웹페이지
- 새로 고침하면 경로를 찾지 못해서 에러가 난다. ※ 주소를 사용하여 페이지를 찾아갈 때에도 에러발생
- 서버에 추가적 세팅 필요
- 페이지의 유무를 서버에 알려줘야 하며, 서버 세팅시 검색엔진에 신경써야함
- github pages에서 설정하기 복잡하다. (배포가 복잡함)
Hash Router
- URL의 hash를 활용한 라우터이다.
- 주소에 #이 붙는다.
- 정적인 페이지에 적합하다. (미리 저장된 페이지가 그대로 보이는 웹페이지)
- 검색엔진으로 읽지 못한다. (거의 사용하지 않는 이유) ※ #값 때문에 서버가 읽지 못하고 서버가 페이지 유무를 알지 못하기 때문
- 새로고침해도 에러가 나지 않는다.
- github pages에서 설정하기 간편하다. (배포가 간편함)
사용 방법 정리
1. Router
1)설치
//npm
npm install react-router-dom
//yarn
yarn add react-router-dom
2) packge.json 설치 확인 (라이브러리 설치 시 동일하게 확인)
3) <Browser Router>로 index.js 수정 작성
import {BrowserRouter} from "react-router-dom"
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
- App.js에서 return값을 묶어주거나 외부파일로 묶어줘도 동일 기능 동작
4) App.js 경로 설정
<구버전 방식>
import { Route, Switch } from "react-router-dom";
function App() {
return (
<Switch>
<Route path="/" exact>
<TodoList />
</Route>
<Route path="/login" exact component={LoginPage}></Route>
<Route path="/join" exact component={JoinPage}></Route>
<Route path="/:id" exact component={DetailTodo}>
{/* <DetailTodo/> */}
</Route>
<Route>
<NotFound/>
</Route>
</Switch>
)
}
방법 1. <Route path="경로" component = {컴포넌트 이름}/>
방법 2. <Route path="경로"> <컴포넌트/> </Route>
옵션1. exact - 경로 중복 제외
옵션2. /:id (다른이름 가능) 콜론(:)을 통한 경로 동적 할당
옵션3. <Route> 경로 없는 Route로 빈페이지 할당
<다른 문법>
import { Route, Routes } from "react-router-dom";
function App() {
return (
<Routes>
<Route path="/" exact element={<TodoList />}/>
<Route path="/login" exact element={<LoginPage/>}/>
<Route path="/join" exact element={<JoinPage/>}/>
<Route path="/:id" exact element={<DetailTodo/>}>
<Route path="1" element{<div>추가페이지</div>}/>
</Route>
<Route path="*" exact element={<NotFound/>}>
</Routes>
)
}
방법 1. <Route path="경로" element = {내부 작성 내용}/>
방법 2. <Route path="경로"> <컴포넌트/> </Route>
방법 3. nested routes기법 내부에 경로를 작성함으로서 경로 이어서 작성 가능 ex) localhost/id/1
옵션1. exact - 경로 중복 제외
옵션2. /:id (다른이름 가능) 콜론(:)을 통한 경로 동적 할당
옵션3. <Route path="*"> 나머지 경로 빈페이지 할당
1-2. 부가기능
1) Link
- 클릭하면 다른 주소로 이동시키는 컴포넌트
<a href="..."> .... </a>
- 리액트 라우터를 사용할 땐 일반 .... 를 사용하시면 안됩니다.
import { Link } from "react-router-dom";
<Link to="/">메인화면</Link>
- 그 대신 Link 라는 컴포넌트를 사용해야 합니다.
이유 : a태그의 기본적인 속성은 페이지를 이동시키면서, 페이지를 아예 새로 불러오게 됩니다.
=> 리액트앱이 지니고 있는 상태 초기화 => 렌더링된 컴포넌트 제거 후 재 랜더링
따라서, Link컴포넌트를 사용하면 HTML History API를 사용하여 주소만 바꾸고 페이지를 새로 불러오지 않습니다.
2) useHistory
- 리액트에서 URL주소를 변경할 때 사용하는 Hook
- URL주소를 변경시켜 URL 주소와 일치하는 Route컴포넌트를 렌더링하기 위해 사용한다.
- 리액트 특성상, URL변경 없이 내부 컴포넌트만 변경시켜 화면을 바꿔줄 수 있다.
- URL을 바꿔주면 현제 페이지의 위치를 대략적으로 알 수 있고, URL주소만으로 결과값을 불러올 수 있기 때문에 사용
import { useHistory } from 'react-router-dom';
const App = () => {
const history = useHistory();
return (
<>
<button onClick={(()=>{history.push("/")})}>메인화면</button>
</>
)
}
- useHistory에는 메서드가 존재하는데 가장 많이 사용하는 메서드로는 go, back, replace가 있다.
- go : 해당 URL을 추가한다. (Stack의 개념이기에 하나씩, 차곡차곡 쌓인다.)
- back : 현재 URL 이전으로 변경한다.
- replace : URL을 추가하는 것이 아니라 현재 URL을 변경한다.
3) useNavigate
- 훅을 실행하면 페이지 이동을 할 수 있게 해주는 함수를 반환합니다.
- 리액트 라우터 v6에서는 기존의 useHistory 대신 useNavigate를 사용해야합니다.
- useNavigate로 기존에 useHistory의 기능을 전부 대체 가능
- useHistory의 history는 객체였지만 useNavigate의 navigate는 함수다.
기존 코드
import { useHistory } from 'react-router-dom';
const App = () => {
const history = useHistory();
return (
<>
<button onClick={(()=>{history.push("/")})}>메인화면</button>
</>
)
}
history.push('/');
history.goback();
history.go(-2);
history.push(`/user/${user._id}`);
v6 코드
import { useNavigate } from 'react-router-dom';
const App = () => {
let navigate = useNavigate();
return (
<>
<button onClick={(()=>{navigate("/")}}>메인화면</button>
</>
)
}
navigate('/');
navigate(-1);
navigate(-2);
navigate(`/user/${user._id}`);
4) Outlet
- 중첩 라우팅의 구성이 되면 Outlet을 통해서 상위의 컴포넌트를 레이아웃화 할 수 있다.
- <Outlet> 을 사용하면 {children} 을 사용하는 것과 같은 효과가 난다
import { Route, Routes, Outlet } from "react-router-dom";
function App() {
return (
<Routes>
<Route path="/:id" exact element={<Event/>}>
<Route path="1" element{<div>생일 기념 이벤트</div>}/>
</Route>
<Route path="*" exact element={<NotFound/>}>
</Routes>
)
}
const Event = () =>{
return (
<div>
<h4>오늘의 이벤트</h4>
<Outlet></Outlet>
</div>
)
}
- 이런식으로 작성하면 상위 컴포넌트의 Outlet에 중첩 라우터의 내용을 작성할 수 있다.
2. Redux
2-1. Redux
1) 설치
//npm
npm install redux react-redux
//yarn
yarn add redux react-redux
2) 폴더 구조 생성하기
상측의 이미지와 같이 폴더 구조를 생성하세요.
- src 폴더 안에 redux 폴더를 생성
- redux 폴더 안에 config, modules 폴더를 생성
- config 폴더 안에 configStore.js파일을 생성합니다.
각각의 폴더와 파일은 역할이 있습니다.
- redux : 리덕스와 관련된 코드를 모두 모아 놓을 폴더 입니다.
- config : 리덕스 설정과 관련된 파일들을 놓을 폴더 입니다.
- configStore : “중앙 state 관리소" 인 Store를 만드는 설정 코드들이 있는 파일 입니다.
- modules : 우리가 만들 State들의 그룹이라고 생각하면 됩니다. 예를 들어 투두리스트를 만든다고 한다면, 투두리스트에 필요한 state들이 모두 모여있을 todos.js를 생성하게 되텐데요, 이 todos.js 파일이 곧 하나의 모듈이 됩니다.
3) <Provider>로 index.js 수정 작성 (index.js에서 App을 감싸서도 사용 가능)
import {Provider} from "react-redux"
//store 경로 지정
import store from "./redux/configStore"
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
- App으로 Provider을 통한 store(저장소) 제공
4) Store 기능 저장소 세팅 redux/configStore (경로지정은 필수가 아님)
import { createStore, combineReducers } from "redux";
import 데이터명 from "받아올 경로"
const rootReducer = combineReducers({데이터명});
const store = createStore(rootReducer);
export default store;
5) module 작성 redux/modules/Todo.js (예제 경로)
// Actions
const CREATE = 'todo/CREATE';
const UPDATE = 'todo/UPDATE';
const REMOVE = 'todo/REMOVE';
// initialState
const initialState ={
todo : [
{
id: 1,
title: "리액트 공부하기",
body: "리액트 기초를 공부해봅시다.",
isDone: false,
},
{
id: 2,
title: "리액트 공부하기",
body: "리액트 기초를 공부해봅시다.",
isDone: true,
},
]
}
// Reducer
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case "todo/CREATE":{
const new_todo_list = [...state.todo, action.todo];
return {todo:new_todo_list};
}
case "todo/UPDATE":{
const new_todo_list = [...state.todo].map(todo =>{
if(todo.id === action.id){
return{
...todo,
isDone:!todo.isDone,
};
}else{
return {...todo};
}
});
return {todo:new_todo_list};
}
case "todo/REMOVE":{
const new_todo_list = [...state.todo].filter(todo=> {
return todo.id !== action.id;
});
return {todo:new_todo_list};
}
// do reducer stuff
default: return state;
}
}
// Action Creators
export function createTodo(todo){
return {type: CREATE, todo: todo}
}
export function chagneTodo(id){
return { type: UPDATE, id: id };
}
export function removeTodo(id){
return {type: REMOVE, id: id}
}
- Action을 통해서 Action type의 변수 지정
- Action Creator 를 통한 reducer action의 type과 파라미터 지정
- initialState를 통한 초기값 설정
- reducer에 state 및 action 지정 ( switch문을 통한 Action 설정 및 함수 실행 )
6) 사용처에서 데이터 불러오기 및 함수 사용
import { useSelector } from "react-redux";
import Todo from "../todo/Todo";
function List() {
const todos = useSelector((state) => state);
console.log(todos);
return (
<div className="list-wrapper">
{todos.todo.todo.map((todo) => {
if (!todo.isDone) {
return (
<Todo
todo={todo}
key={todo.id}
/>
);
} else {
return null;
}
})}
</div>
)
- useSelector를 통한 Store의 데이터를 불러와 state 상태 값 사용
const todos = useSelector((state) => state.todos.todos);
- 상위 코드 처럼 작성하여 state의 상태값을 세부적으로 받아올 수 있음. store지정 값, state 지정 값 사용
import { useDispatch } from "react-redux";
import {removeTodo, chagneTodo} from "../../redux/modules/Todo";
function Todo({todo}) {
const dispatch = useDispatch();
return(
<div>
<button
className="todo-delete-button button"
onClick={() => dispatch(removeTodo(todo.id))}
>
삭제하기
</button>
</div>
)
- useDispatch를 통한 Store의 데이터를 불러서 Reducer 사용 (ActionCreator도 따로 불러와서 사용)
2-1. Redux-Toolkit
장점
- 초기 설정이 간편해짐 ( 리덕스 스토어 구성이 좀 더 간편화)
- 다양한 패키지 설치를 제외 (redux devtool, immer, thunk등 라이브러리 내장)
- 반복되는 코드가 너무 많아 코드가 복잡해지고 실수를 많이 유발했지만 이 부분 개선
- 툴킷에서는 더이상 불변성을 신경쓰지 않아도 됩니다.
사용방법
1) 설치
// npm
npm install @reduxjs/toolkit react-redux
// yarn
yarn add @reduxjs/toolkit react-redux
2) <Provider>로 index.js 수정 작성 (index.js에서 App을 감싸서도 사용 가능)
import { Provider } from "react-redux";
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
export default App;
3) store 생성
import { configureStore, createSlice } from "@reduxjs/toolkit";
let todo = createSlice({
// 이름 선언(예제라.. 다른거 쓰셔도 됩니다.)
name: "todo"
// 초기 값 설정
initialState:[
{id: 0, title: "리액트 공부", body: "힘내자", isDone:false}
{id: 1, title: "다들", body: "화이팅", isDone:true}
]
//함수 생성
reducers: {
addTodo(state, action){
//state = initialState부터 시작한 상태값
//action 사용처에서 추가로 작성해준 데이터 ex)todo, id
//받아온 데이터 사용하려면 action.payload 를 통해 받아오기(console에 찍어보세요)
//만들어진 todo값을 받아온다고 가정
state.push(action.payload);
}
// id값을 받아온다고 가정
chageTodo(state, action){
let index = state.findIndex(todo => todo.id === action.payload);
state[index].isDone = !state[index].isDone;
}
// id 값을 받아 온다고 가정
removeTodo(state, action){
let index = state.findIndex(todo=> todo.id === action.payload);
state.splice(index,1);
}
})
// 사용하는 함수 외부 export
export let {changeTodo, addTodo, removeTodo} = todo.actions;
// configureStore 저장소가 가지는 reducer 값 선언
// 추가 reducer시 reducer 내부에 이어서 작성
export default configureStore({
reducer: {
counter: todo.reducer,
}
});
4) 사용처에서 데이터 불러오기 및 함수 사용
import { useSelector, useDispatch } from 'react-redux/';
import { addTodo, changeTodo, RemoveTodo } from {store경로}
const Todo = () =>{
// todo = state의 todo값 제공
let todo = useSelector((state)=>{return state.todo});
// 함수 사용을 위해서 작성
let dispatch = useDispatch();
return (
<div>
// todo에서 각각의 값을 map함수로 받아 사용
{todo.map((todo)=>{
<div>
//데이터 불러오기
<h1>todo.title</h1>
<p>todo.body</p>
<button onClick={()=>{
// import해온 함수 사용 방법
dispatch(chageTodo(todo.id));
}}></button>
</div>
})
</div>
)
}
자료 참조:
https://phsun102.tistory.com/91
'이노베이션 캠프 > 회고록' 카테고리의 다른 글
리액트 키워드 정리 (리덕스, 미들웨어, 프로미스, axios 등) (0) | 2022.08.29 |
---|---|
React 첫 도전기 (0) | 2022.08.21 |
2주차 프로그래밍 기초 협업 내용 정리 (0) | 2022.08.15 |
알고리즘 테스트를 마치며... (0) | 2022.08.14 |
알고리즘 진행 과정 중간 결산 (0) | 2022.08.10 |