책 읽다가 코딩하다 죽을래
프론트엔드의 꽃, 리액트 - 3주차 개발일지( Router, Redux ) 본문
GD프로젝트 리액트 기초
프론트엔드의 꽃, 리액트 3주 차도 무난히 끝을 냈다.
이번 3주차에 있었던 일을 바로 기술해보겠다.
목차
1. 새롭게 배운내용 (redux, router)
2. 결과물
3. 배운 내용 적용
1. 새롭게 배운 내용
1 - 1 Router
라우팅이란 페이지를 나눠주는 것이다.
우리가 React로 작업을 할 때
create-react-app을 이용하든 웹팩을 이용하든
html 파일은 index.html 하나뿐이라는 것을 알 것이다.
이렇게 어플리케이션 안에 html 파일이 하나밖에 존재하지 않는 경우
우리는 이것을 SPA(Single Page Application)이라 부른다.
이렇게 html파일이 하나가 있으면 좋은 점은 사용성이다.
페이지를 이동하거나 웹사이트 내에 정보가 달라지게 되면 리다이렉트(새로고침)가 일어나게 되는데
만약에 페이지가 여러개이면 html 파일이 수시로 교체가 되고 서버에서 주는 데이터도 바뀌다 보니까
상태 유지도 어렵고 바뀌지 않은 부분까지 새로 불러오게 되니 비효율적입니다.
(ex : facebook에서 좋아요 버튼 눌렀다고 페이지가 자동으로 새로고침이 되면 불편하겠죠?)
그래서 React는 Router를 이용해서 SPA를 유지한 채 여러 개의 페이지를 제어하게 만드는 겁니다.
사용방법은 간단합니다
router의 모듈을 터미널에 입력한 후
npm install --save react-router-dom
yarn add react-router-dom
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom"
import App from './route/App';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
먼저 index.js 파일에 App을 브라우저 라우터로 감싸줍니다.
브라우저 라우터는 웹 브라우저가 가지고 있는 주소 관련 정보를 props로 넘겨줍니다.
import React, { Component } from "react";
import { render } from "react-dom";
import {
Route,
} from "react-router-dom";
import Cat from "./Cat";
import Dog from "./Dog";
import Home from "./Home";
class App extends Component{
constructor(props){
super(props);
this.state = {};
}
componentDidMount(){
console.log(this.props);
}
render(){
return (
<div className="App">
<Route exact path="/" component={Home}/>
<Route path="/cat" component={Cat}/>
<Route path="/dog" component={Dog}/>
</div>
)
}
}
export default App;
이렇게 각각 컴포넌트 별로 path를 주면 된다.
그렇지만 사용자는 일일이 url을 입력해서 페이지를 이동하지 않는다.
그래서 a태그처럼 click 하면 페이지가 이동되는 Link함수나
아까 브라우저 라우터로 인해 props로 받았던 것 중에 history.push()를 사용하여
페이지 이동 로직을 함수처럼 사용할 수 있다.
history 객체를 props로 받아오려면 withRouter를 설정하는 것을 잊지 말자
import React, { Component} from "react";
import { render } from "react-dom";
import {
BrowserRouter as Router,
Route,
Link
} from "react-router-dom";
import { withRouter } from "react-router";
import Cat from "./Cat";
import Dog from "./Dog";
import Home from "./Home";
class App extends Component{
constructor(props){
super(props);
this.state = {};
}
componentDidMount(){
console.log(this.props);
}
render(){
console.log(this.props);
return (
<div className="App">
<div>
<Link to="/">Home으로 가기</Link>
<Link to="/cat">Cat으로 가기</Link>
<Link to="/dog">Dog으로 가기</Link>
</div>
<Route exact path="/" component={Home}/>
<Route path="/cat" component={Cat}/>
<Route path="/dog" component={Dog}/>
<button onClick={() => {
this.props.history.push('/cat');
}}>/cat으로 가기</button>
<button onClick={() => {
this.props.history.goBack();
}}>뒤로가기</button>
</div>
)
}
}
export default withRouter(App);
이렇게 Router를 설정하면 주소가 바뀌어도 보여줄 컴포넌트가 달라져도
페이지가 리다이렉트 되지 않는다.
최대한 리다이렉트를 방지하고 바뀌는 부분만 바꿔주는 것이
React를 쓰는 중요한 이유이므로 확실하게 알아야 하는 개념이다.
또한 다행히도 react나 hooks나 router문법은 똑같다.
1 - 2 Redux
React에서 컴포넌트 내에서의 데이터는 state로 해결된다.
그럼 컴포넌트 간에서는? props가 있다.
그렇지만 되게 제약사항이 많다.
props는 양방향이 아닌 일방향이다.
부모가 자식에게 props를 줄 수 있지만
부모에게 받은 props를 수정할 수 없을뿐더러
애초에 부모에게 데이터를 줄 수 있는 수단이 없다.
게다가 만약 컴포넌트 관계가 A->B->C->D와 같고
A의 데이터를 D에게 전달하려면
B와 C를 거쳐야 하고 또한 데이터 변화가 없는 B와 C도 필요 없는 리랜더링 일어난다.
이렇게 자식이 부모에게 데이터를 줘야 할 상황이나
데이터 변화가 있는 컴포넌트만 랜더링이 일어나게 만들려면
redux를 사용해야 한다.
redux가 무엇이냐면은 react안의 전역 변수라고 생각하면 된다.
redux의 구조는 다음과 같다
redux는 스토어, 리듀서, 액션, 디스패치, 구독이 있는데 각각 설명하자면
스토어(store) : 전역 변수들이 담겨있는 곳이다.
사실상 이해하기 쉽게 전역 변수라고 표현했지만 모든 컴포넌트에서 접근할 수 있는 변수 가 더 정확한 표현이다.
액션 (action) : 사용자가 웹사이트에 들어와서 할 수 있는 모든 행동들을 추상화한 것이다.
또한 액션을 통해서 다른 컴포넌트들에게도 데이터가 변경되었다는 것을 알려주기 위한 통보이기도 하다.
예로 들면 회원가입 폼에서 회원정보를 입력하고 가입 버튼을 누르는 과정에서 액션은 '가입하기'가 있다.
디스패치 (dispatch) : 사용자가 액션을 취함으로써 웹사이트 내에서 실행되는 로직이다.
예로 들면 사용자가 가입하기를 누르면 회원이 입력한 정보를 서버에 저장하는 로직이 dispatch(회원정보저장()); 로 표현된다.
리듀서 (reducer) : 사용자의 액션과 액션을 통해 동작해야 하는 로직들이 기술되어 있는 곳이다.
보통 액션과 액션 크리에이터, 초깃값, reducer가 있고 reducer는 switch구문으로 이루어져 있다.
리듀서의 내용을 보면 공통적으로 ...state가 있는데 이 부분은
리듀서는 불변성을 유지하기 위함이다.
redux는 어찌 보면 전역 변수이다.
소프트웨어 공학을 배운 사람이면 알겠지만 전역 변수는
설계하는 데에 있어 치명적이라는 것을 알 수 있다.
전역 변수는 모듈 간 결합도 중 위에서 2번째인 공통 결합도이니
redux를 쓰면 컴포넌트 간의 결합도가 매우 높아지는데
이를 막기 위해 자신이 바꾸는 데이터를 제외한 나머지 데이터는 바뀌지 말라는 뜻으로 ...state를 사용함으로써
리듀서를 순수한 함수(동일한 입력값엔 동일한 반환 값이 나오는 함수)로 만드는 것이다.
또한 바꾸는 그 과정도 action과 action 크리에이터라는 절차를 따르지 않고는 바뀔 수 없다는 무결성도 안 보이게 지켜져 있는 것이다.
그리고 마지막으로
구독 : 리듀서를 통해 바뀌어진 데이터를 바라보는 컴포넌트들을 리랜더링 시켜주는 과정이다.
데이터를 바라보지 않는 컴포넌트들은 아무 변화가 없다.
react를 처음 공부하는 사람이 제일 힘들어하는 구간이기도 하다.
특히 dispatch는 비동기처리이다 보니 비동기에 잘 모르시는 분들은 제어하기 힘든 부분도 있을 것이다.
2. 결과물
3주 차 과제는 2주 차 과제보다 훨씬 어려워졌다.
거의 하루 종일 작업을 하여 과제를 해결했는데
어려운 만큼 router, redux의 기능을 더욱 심도 있게 실습할 수 있었다.
또한 hooks-redux의 dispatch가 비동기 함수였다는 것을 알게 되었다.
그래서 dispatch 비동기 처리에 관해서 튜터님에게 질문을 해보았다.
useEffect가 있었다는 걸 깜빡했다...ㅎ
store에 저장되어있는 값을 dispatch이후에 바로 읽고 싶으면
useEffect 2번째인자의 읽고 싶은 값을 넣으면 된다.
그럼 변하자마자 그 값이 log에 출력된다.
또한 dispatch 사용 시 주의해야할 점이 react 공식 문서에 나와있다.
3. 배운 내용 적용
router의 history를 이용해 뒤로 가기 홈으로가기 버튼을 만들었다.
props.history.push('/') : 홈으로가기
props.history.goBack() : 뒤로가기
여기서 js의 href.location 하고 route의 props.history.push와 무슨 차이냐고 팀원과 의논한 바가 있는데
쉽게 말해 location은 새로고침이 되며 route는 앞에서 말한 대로 새로고침이 되지 않는다.
새로고침을 피하는 React에선 Route를 지양하자.
'GD프로젝트 > 개발일지' 카테고리의 다른 글
리액트 심화반 - 1주차 개발일지(scope, Object, Function) (0) | 2021.07.22 |
---|---|
프론트엔드의 꽃, 리액트 - 마지막주차 개발일지( middleware, AWS ) (0) | 2021.07.19 |
프론트엔드의 꽃, 리액트 - 4주차 개발일지( firebase ) (0) | 2021.07.18 |
프론트엔드의 꽃, 리액트 - 2주차 개발일지(SCSS, Styled Component,가상DOM) (0) | 2021.07.12 |
프론트엔드의 꽃, 리액트 - 1주차 개발일지(Scope, 웹 동작 방식, 서버리스) (0) | 2021.07.07 |