리액트 스터디 3
SPA
SPA는 Single page Application
의 약자로 서버에서 단 한장의 HTML
을 제공하는 방식을 의미한다. SPA 이전의 웹사이트는 화면의 UI에 따라 여러장의 HTML을 가졌는데 사용성이 떨어지는 단점이 있었다.
-
장점
단순한 요청에서도 화면이 새로 그려지므로 데이터의 상태를 일관되게 유지하는데 많은 코드가 필요하다. 또한, 화면의 일부만 변경하는 경우도 전체를 다시 그려서 자원이 낭비된다. 동기 방식의 통신에서 페이지가 가지고 있던 데이터가 전부 사라진다. 이러한 경우를 사용성이 떨어진다고 표현하는데 SPA는 한장의 HTML안에서 컴포넌트를 조립하여 화면 전체에 대한 갱신을 방지하여 이러한 부분을 보완해준다. -
단점
한번에 정적자원을 내려받기 때문에 대량의 컴포넌트 즉, 많은 화면을 표현해야 하는 웹사이트에 경우 첫 로딩이 느려질 수 있다. 따라서 웹 사이트의 성격에 맞게 SPA 채용 여부를 고려해야 한다.
router
리액트에서는 일반적으로 페이지의 이동을 위해 router를 사용한다. 대표적인 라이브러리 중 하나인 react-route-dom
를 사용해보자.
yarn add react-route-dom
위와 같이 bash
를 통해 설치한다.
다음은 공식 문서에서 제공하는 quik start 가이드의 코드이다. App.js
에 작성하면 간단한 router
구현이 가능하다.
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
export default function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
</nav>
{/* A <Switch> looks through its children <Route>s and
renders the first one that matches the current URL. */}
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function Users() {
return <h2>Users</h2>;
}
위와 같이 생성된 화면을 통해 기존의 네비게이션을 대체하는 라우터를 이해할 수 있다.
parameter 전달
index.js
의 <App />
을 불러온 <BrowserRouter>
로 감싸준다.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
reportWebVitals();
import { Route, Link } from 'react-router-dom';
<Route exact path="/" component={Home} />
<Route path="/user/:user_name" component={User} />
<Route path="/about" component={About} />
페이지 이동 뿐만 아니라 값도 전달할 수 있다. 첫 줄의 /
에 할당된 exact
옵션은 path
가 정확히 일치할때만 컴포넌트를 불러오는 옵션으로 /user
나 /about
등 /
가 포함된 path
를 가진 화면을 호출 되었을 때 /
의 반응을 방지한다.
localhost:3000/user/johndoe
로 호출 한다고 가정했을 때 :user_name
의 자리에 johndoe
가 전달된다.
return (
<div>
my name is {props.match.params.user_name}
</div>
)
User.js
에서는 위와 같이 꺼내서 사용할 수 있다.
history
브라우저가 제공하는 history
를 사용할 수도 있다. 뒤로 가기 역시 간단하게 구현 할 수 있다.
<button onClick={() => {
this.props.history.push('/user/johndoe');
}}>user</button>
<button onClick={() => {
this.props.history.goBack();
}}>back</button>
404
Not Found
일명 404
를 간단하게 처리 할 수 있다. NotFound.js
를 만들었다고 가정하고 react-router-dom
에서 Switch
를 가져온다.
import { Route, Switch } from "react-router-dom";
그리고 다른 <Route>
들을 <Switch>
로 감싸고 path
가 없는 Route
를 추가하면 path
가 정의 되지 않은 주소를 요청했을 때 지정된 컴포넌트인 NotFound.js
로 이동된다.
<Switch>
<Route component={NotFound} />
</Switch>
전술한 history
를 이용해서 뒤로 가기 역시 구현할 수도 있다.
<Route render={(props) => (<NotFound history={props.history} />)} />
Redux
데이터의 전역적인 상태 관리를 위해, state
와 props
를 보완하기 위해 redux
(공식 문서)를 사용해보자.
yarn add redux react-redux
스탠드 얼론인redux
와 리액트에서 리덕스를 쓰기 위한 react-redux
를 함께 설치하자.
- State : 딕셔너리 형태로 데이터를 보관한다.
{[key]:value}
- Action : 데이터에 변경이 발생할 때 발생하는 객체로 다음과 같은 형태이다. 스토어 내부의 데이터는 일관성과 불변성을 유지하기 위해 오직 액션을 통해서만 변경하는 것이 원칙이다.
{type: 'CHANGE_STATE', data: {...}}
- ActionCreator : 액션 생성 함수는 액션을 생성하여 반환해준다.
const changeState = (new_data) => {
return {
type: 'CHANGE_STATE',
data: new_data
}
}
-
reducer : 리덕스에 저장된 데이터를 변경하는 함수이며 흐름은 다음과 같다.
액션 생성 함수 호출
→액션 생성
→리듀서(데이터, 액션 객체)
→새로운 데이터 반환
리듀서는 순수 함수로 인수부를 제외하고 외부와의 의존관계를 맺지 않는다. 외부의 상태와 상관없이 항상 같은 동작을 보장해야 한다. -
store : 리덕스를 적용하기 위해 사용하는 저장소 개념으로 리듀서, 상태, 액션 호출 등을 위한 내장 함수가 포함되어 있다. 단일 스토어 규칙에 따라 하나의 스토어만 사용한다.
-
dispatch : 액션 생성 함수를 호출하는 함수이다.
ducks
일반적으로 리덕스의 파일 구조는 확장성을 위해 action
, action creator
reducer
끼리 역할을 기준으로 모아 작성한다. 반면 ducks 구조는 기능을 기준으로 그 기능 해당하는 action
, action creator
reducer
를 한 파일안에 작성하는 방식이다. 작은 규모의 프로젝트나 학습을 위해 이러한 구조를 많이 사용하는 편이고 본 포스트의 목적에 부합한다.
src
폴더 하위에 redux/modules
폴더를 추가하고 다음과 같이 src/redux/configStore.js
와 src/redux/modules/bucket.js
를 만든다.
ducks
구조에 따라 bucket.js
에 Actions
, Action Creators
, Reducer
를 작성한다.
// bucket.js
// Actions
const LOAD = "bucket/LOAD";
const CREATE = "bucket/CREATE";
const initialState = {
list: ['영화관 가기', '매일 책읽기', '수영 배우기'],
};
// Action Creators
export const loadBucket = (bucket) => {
return { type: LOAD, bucket };
}
export const createBucket = (bucket) => {
return { type: CREATE, bucket };
}
// Reducer
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
// do reducer stuff
case "bucket/LOAD": {
return state;
}
case "bucket/CREATE": {
const new_bucket_list = [...state.list, action.bucket];
return { list: new_bucket_list };
}
default: return state;
}
}
store
역시 작성해준다.
import { createStore, combineReducers } from "redux";
import bucket from "./modules/bucket";
import { createBrowserHistory } from "history";
export const history = createBrowserHistory();
const rootReducer = combineReducers({ bucket });
const store = createStore(rootReducer);
export default store;
index.js
에 Provider
를 추가하고 라우터를 감싸준 뒤 store
를 연결해준다.
import { Provider } from "react-redux";
import store from "./redux/configStore";
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
connect
이제 스토어와 어플리케이션을 연결해보자. App.js
에서 connect
와 bucket
을 불러오고 mapStateToProps
, mapDispachToProps
를 만든다.
import { connect } from "react-redux";
import { loadBucket, createBucket } from "./redux/modules/bucket";
const mapStateToProps = (state) => {
return { bucket_list: state.bucket.list };
}
const mapDispachToProps = (dispatch) => {
return {
load: () => {
dispatch(loadBucket());
},
create: (bucket) => {
dispatch(createBucket(bucket));
}
};
}
export
부분을 수정해서 connect
로 감싸준다.
export default connect(mapStateToProps, mapDispachToProps)(withRouter(App));
이제 componentDidMount
에서 bucket_list
를 확인해 볼 수 있다.
componentDidMount() {
console.log(this.props);
}
이제 기존의 setState
를 주석하고 create
를 사용해보자.
this.props.create(new_item);
//this.setState({ list: [...list, new_item] });
useSeletor
을 이용해서 Hook
도 사용 할 수 있다.
import { useSelector, useDispatch } from "react-redux";
const bucket_list = useSelector(state => state.bucket.list);
Author And Source
이 문제에 관하여(리액트 스터디 3), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@likerdo/리액트-스터디-3저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)