-
[React] React redux-saga 프로젝트 시작하기 #4Front-End/React 2023. 12. 20.
전 글에는 대략적인 화면 Grid를 구성하였고 이를 화면에 렌더링 시켜 볼 것이다.
그 전에 주소에 따라 페이지를 이동할 수 있는 React Router를 사용할 것이다.
React는 Single Page Application이므로 기존의 html처럼 경로를 붙여 페이지를 이동하는 방식이 아니다.
React Router는 사전에 정의한 URL에 맞는 컴포넌트를 하나의 페이지에서 렌더링 해 준다
npm install react-router-dom
설치가 되었으면 렌더링이 시작되는 최상단 파일에 Router를 적용해준다. 해당 프로젝트에서는 index.js 파일이 최상단이므로 index.js 파일에 BrowserRouter를 설정해준다.
import React from 'react'; import { createRoot } from 'react-dom/client'; import { Provider } from 'react-redux'; import App from './App'; import 'bootstrap/dist/css/bootstrap.min.css'; import { BrowserRouter } from 'react-router-dom'; const container = document.getElementById('root'); const root = createRoot(container); const render = () => { root.render( <BrowserRouter> {/* BrowserRouter 설정 */} <Provider> <App /> </Provider> </BrowserRouter> ); } render();
설정이 되었으면 App.js 파일에 실제 Route를 적용해준다.
import React from 'react'; import { Route, Routes } from 'react-router-dom'; import CounterPage from 'pages/test1/counter/container/CounterPage'; import SamplePage from 'pages/test1/sample/container/SamplePage'; import Home from 'pages/components/Home'; import Header from 'pages/components/Header'; import PageNotFound from 'pages/components/PageNotFound'; function App() { return ( <div> <Header /> <Routes> {/* exact={true}는 router가 경로값이 정확히 URL의 경로값과 일치할 때만 렌더링되도록 함 */} <Route exact={true} path="/" element={<Home />} /> <Route path="/counter" element={<CounterPage />} /> <Route path="/sample" element={<SamplePage />} /> <Route path="/*" element={<PageNotFound />} /> </Routes> </div> ); } export default App;
path는 서버 주소 뒤에 오는 경로이며, 해당 주소가 없을 경우 PageNotFound 페이지를 불러 올 예정이다.
Router 기능에 많이 사용되는 Bootstrap의 NavBar를 이용해 상단에 페이지 이동을 가능하게 하는 네비게이션 바를 적용해준다
Header.js
import React from 'react'; import { Container, Nav, Navbar, NavDropdown }from 'react-bootstrap'; const Header = () => { return ( <Navbar expand="lg" className="bg-body-tertiary"> <Container> <Navbar.Brand>React-Bootstrap</Navbar.Brand> <Navbar.Toggle aria-controls="basic-navbar-nav" /> <Navbar.Collapse id="basic-navbar-nav"> <Nav className="me-auto"> <Nav.Link href="/">Home</Nav.Link> <Nav.Link href="/counter">Counter</Nav.Link> <Nav.Link href="/sample">Sample</Nav.Link> <NavDropdown title="Dropdown" id="basic-nav-dropdown"> <NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item> <NavDropdown.Item href="#action/3.2"> Another action </NavDropdown.Item> <NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item> <NavDropdown.Divider /> <NavDropdown.Item href="#action/3.4"> Separated link </NavDropdown.Item> </NavDropdown> </Nav> </Navbar.Collapse> </Container> </Navbar> ) } export default Header;
헤더 페이지를 적용하면 상단에
위와 같이 네비게이션 바가 생성된다.
NavLink를 통해 해당 버튼을 누르면 주소 링크로 이동하게 되는데 Router에서 이를 감지해 해당되는 element에 적용된 컴포넌트들을 띄워준다
화면을 띄우고 Counter를 들어가게 되면 아래와 같이 뜬다. 필자는 Table태그에 dark모드를 적용해 검정색으로 보인다.
이제 값 입력을 통해 입력한 값이 더하고 빼지는 기능을 만들어 볼 껀데, 값을 변경하려면 state를 사용해야 하고 reducer를 통해 해당 값을 관리해야 한다. 또한 reducer뿐만 아니라 redux-saga를 이용해 액션 타입을 정의하고 해당 액션을 실행하면 특정 함수가 수행 될 수 있도록 할 것이다.
npm install redux-saga
redux-saga는 Action을 모니터링 하고 있다가 해당 Action이 발생하면 이에 따라 특정 작업을 수행한다
redux saga를 이용하려면 액션 함수와 제너레이트 함수를 정의해 사용해야 한다
자세한 내용은 (https://react.vlpt.us/redux-middleware/) 에서 확인이 가능하다
counter/state 폴더에 index.js와 saga.js가 만들어져 있을 것이다.
index.js는 state관리와 saga action 타입을 정의화고 reducer함수를 정의한다.
saga.js는 실제 사가 액션이 발생하면 실행될 함수들을 정의한다
index.js
import { createSlice, createAction } from '@reduxjs/toolkit'; // Action 명은 중복이 되지 않아야 하니 root 폴더와 하위 폴더 명으로 정의한다 const ROOT_SLICE_NAME = 'test1'; const SLICE_NAME = 'counter'; // 해당 state에 사용될 초기 값을 정의한다 const initialState = { onload: false, number: 0, } // saga action을 호출하기 위한 Action들을 정의한다 const sagaAction = { fetchInitialInfo: createAction(`${SLICE_NAME}/fetchInitialInfo`), getList: createAction(`${SLICE_NAME}/getList`), }; // 리듀서 함수들을 작성한다 const reducers = { initState: () => initialState, setInitialInfo: (state) => { state.onload = true; state.number = 1; }, setValue: { reducer: (state, { payload : { key, value }}) => { state[key] = value; }, prepare: (key, value) => { return { payload : {key, value} }; } }, increase: (state, { payload : { data } }) => { state.number += data; }, decrease: (state, { payload : { data } }) => { state.number -= data; } } // createSlice를 통해 해당 페이지에서 사용될 reducer들을 세팅한다 const slice = createSlice({ name: SLICE_NAME, initialState, reducers, }); // 현재 state tree에 가지고 있는 값들을 가져올 수 있게 한다 export const getState = (state) => state[ROOT_SLICE_NAME][SLICE_NAME]; // 외부에서 action를 가져올 수 있게 한다 export const actions = { ...slice.actions, ...sagaAction, } export default slice.reducer;
counter페이지에서 사용되는 reducer가 지정되었으니 test1폴더에서 관리할 state인 rootReducer.js를 생성한다.
pages/test1/state/rootReducer.js
import { combineReducers } from 'redux'; import counter_Reducer from 'pages/test1/counter/state/index'; const rootReducerTest1 = combineReducers({ counter: counter_Reducer, }) export default rootReducerTest1; /* 실제 combineReducers가 구성되면 아래와 같은 구조처럼 생성된다 combineReducers({ counter: { createSlice({ name: SLICE_NAME, initialState, reducers, }); } }) */
'Front-End > React' 카테고리의 다른 글
[React] React redux-saga 프로젝트 시작하기 #3 (0) 2023.12.20 [React] React redux-saga 프로젝트 시작하기 #2 (0) 2023.12.20 [React] React redux-saga 프로젝트 시작하기 #1 (0) 2023.12.20