JavaScript 앱을 위한 예측 가능한 state 컨테이너이다.
애플리케이션이 일관되게 동작하는 데 도움을 주며, 여러 환경(client, server, native 등)에서 쉽게 테스트해볼 수 있다. React 외에도 Angula, Vue와 같이 여러 프레임워크에서 사용되고 있다.
→ 쉽게 말하면 Redux를 사용하면 하나의 저장소에서 state를 공유하고 관리할 수 있다. 이를 통해 서로 다른 컴포넌트에서 공통된 객체의 상태를 쉽게 조회할 수 있다는 장점이 있다.
공식 문서에서는 다음과 같은 경우에 사용하는 것을 추천한다.
지금까지 사용해본 경험으로는, 정말 state 관리하기에는 최고의 라이브러리인 것 같다. 어디에서든지 편하게 state를 가져올 수 있기 때문에 개발하는 로직이 많이 단순해진다. 단점은 초기 진입 장벽이 비교적 높은 편인 것 같고, 공식 문서에서도 Vanilla JS와 React에 완전히 익숙해진 후 사용하는 것을 권한다고 한다. 개인적으로는 프로젝트 진행을 위해 가져온 보일러플레이트에 있는 바람에 사용하게 되었다.
처음에는 React Hook처럼 함수형 컴포넌트에서만 사용 가능한 기능인 줄 알았다. 그런데 개발 과정에서 클래스형 컴포넌트에서 꼭! Redux Store에 저장되어있는 state를 조회해야하는 일이 생겼고 열심히 구글링을 해보니 클래스형 컴포넌트에서도 state 조회가 가능했다!! 😆
대략적인 사용 방법은 다음과 같다.
import { connect } from 'react-redux';
class BoardDetail extends Component {
...
const {storeUser} = this.props;
...
render() {
...
}
}
const mapStateToProps = (state) => ({
storeUser: state.user.userData
});
export default connect(mapStateToProps)(BoardDetail)
1) 먼저 connect
함수를 import
해준다. 이 함수를 통해 React 컴포넌트와 Redux store를 연결해줄 수 있다.
2) 그 후 store에서 원하는 데이터를 가져올 수 있다. 이때 state를 다루기 위한 부분이 mapStateToProps
라는 함수이다. 말 그대로 원하는 state
를 props
로 넘겨준다. 이때 반환형은 객체이다.
3) 이렇게 받아온 props
를 this.props
로 조회하여 원하는 객체에 저장할 수 있다.
4) 추가적으로, 만약 store에 저장된 state
에 action을 주고 싶다면 mapDispatchToProps
라는 함수를 사용해주면 된다!
import React, {Component} from 'react';
import { Table, Button } from "react-bootstrap";
import {NavLink} from 'react-router-dom';
import { connect } from 'react-redux'
import axios from 'axios';
import './css/BoardDetail.css'
class BoardDetail extends Component {
constructor(props) {
super(props);
this.state = {board: []};
this.state = {writer: ""};
this.state = {isSame: false};
}
componentDidMount() {
if (this.props.location.query !== undefined) {
this.getDetail();
} else {
window.location.href="/board";
}
}
deleteBoard = _id => {
const send_param = {
_id
};
if (window.confirm("정말 삭제하시겠습니까?")) {
axios
.post("/api/board/delete", send_param)
//정상!
.then(returnData => {
if(returnData.data.success){
alert("삭제되었습니다.");
window.location.href="/";
} else {
alert("삭제에 실패했습니다.")
}
})
//에러
.catch(err => {
console.log(err);
});
}
};
getDetail = () => {
const send_param = {
_id: this.props.location.query._id //게시물 고유번호
};
const marginBottom = {
marginBottom: 5
};
axios
.post("/api/board/detail", send_param)
//정상!
.then(returnData => {
if (returnData.data.board[0]) { //해당 게시물이 있으면
// 게시물 writer에게만 수정/삭제 버튼 보여주기
const {storeUser} = this.props; //현재 로그인한 유저 _id
this.setState({writer: returnData.data.board[0].writer}) //게시물 쓴 사람 _id
// 로그인 된 상태이고 && 로그인한 유저 === 게시물 쓴 사람이면
if(storeUser.hasOwnProperty('_id') && storeUser._id === this.state.writer){
this.setState({isSame: true});
} else {
this.setState({isSame: false});
}
const board = (
<div style={{width:'60%', margin:'auto'}}>
<Table striped bordered hover>
<thead>
<tr>
<th id='title' className='content'>{returnData.data.board[0].title}</th>
</tr>
<tr>
<td id='writer' className='content'>
<span>{`Writer `}</span>
{returnData.data.board[0].name}
</td>
</tr>
<tr>
<td id='date' className='content'>
<span>{`Date `}</span>
{returnData.data.board[0].createdAt.substring(0, 10)}
</td>
</tr>
</thead>
<tbody>
<tr>
<td className='content' colSpan="2" dangerouslySetInnerHTML={{
__html: returnData.data.board[0].content
}}/>
</tr>
</tbody>
</Table>
<div>
<br/>
<NavLink
to={{
pathname: "/board/write",
query: {
title: returnData.data.board[0].title,
content: returnData.data.board[0].content,
_id: this.props.location.query._id
}
}}
>
<Button block style={marginBottom, { display : `${this.state.isSame ? 'inline-block' : 'none'}`}}>
글 수정
</Button>
</NavLink>
<Button
style={{ display : `${this.state.isSame ? 'inline-block' : 'none'}`}}
block
onClick={this.deleteBoard.bind(
null, this.props.location.query._id
)}>
글 삭제
</Button>
</div>
</div>
);
this.setState({board: board});
}
})
//에러
.catch(err => {
console.log(err);
});
};
render() {
const divStyle = {
margin: 50
};
return <div style={divStyle}>{this.state.board}</div>;
}
}
const mapStateToProps = (state) => ({
storeUser: state.user.userData
});
export default connect(mapStateToProps)(BoardDetail)
https://react-redux.js.org/introduction/getting-started
https://redux.js.org/faq/general#when-should-i-use-redux
https://react-redux.js.org/api/connect