본문 바로가기
React

[React] react-router-dom, redux 를 이용한 페이지 이동 - 떽떽대는 개발공부

by 떽이 2021. 1. 24.

 

 

오늘은 그동안 공부했던 것을 바탕으로 react-router-dom 과 redux 를 이용한 페이지 구현하는 부분을 포스팅 해보겠다.

 

원레는 게시판을 만들어보려고 했는데 오늘은 이 부분 구현하는 데 시간을 많이 소요했다.ㅎ.ㅎ

위와 같이 Write 버튼 클릭 시 글 작성 페이지로 이동되며, footer 부분의 버튼 중 Write 버튼이 사라지게 되는 부분이다.

먼저 디렉토리는 아래와 같다.

 

먼저, redux 를 사용하기 위해 index.js 에서 store 를 생성하고, Provider 로 App.js 를 감싸준다.

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';

import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from '@modules/rootReducer';
import App from '@src/App';

const store = createStore(rootReducer);

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>
    , document.getElementById('app')
);

 

App.js 에서는 react-router-dom 을 사용하기 위해 Router로 감싸주고, 호출되는 path 주소에 따라 나타낼 component 를 지정해 주었다.

가장 아랫부분은 Footer 컴포넌트를 넣어 따로 관리해 준다.

src/App.js

import React  from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import '@css/style.css';

import BoardList from '@components/BoardList';
import BoardNew from '@components/BoardNew';
import BoardContent from '@components/BoardContent';
import Footer from '@components/Footer';

function App () {
  return (
    <div className="App">
      <Router>
        <div>
          <Switch>
            <Route path='/' component={BoardList} exact />
            <Route path='/BoardNew' component={BoardNew} exact />
            <Route path='/BoardContent' component={BoardContent} exact />
          </Switch>
        </div>
        <div>
          <Footer />
        </div>
      </Router>
    </div>
  )
}

export default App;

 

path 마다 열릴 컴포넌트들을 각각 생성하여 작성해준다.

src/components/BoardList.js

import React from 'react';

function BoardList() {
    return(
        <div>
            <h2>게시판</h2>
            <div>
                <table className='listTable'>
                    <tbody>
                        <tr>
                            <td className='listTableIndex th'>index</td>
                            <td className='listTableTitle th'>title</td>
                        </tr>
                        <tr>
                            <td className='listTableIndex'></td>
                            <td className='listTableTitle noData'>작성된 글이 없습니다.</td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    )
}

export default BoardList;

 

src/components/BoardNew.js

import React from 'react';

function BoardNew() {

    return(
        <div>
            <h2>글 작성</h2>
            <div>
                <form>
                    <div>
                        <input type='text' className='inputTitle' placeholder='제목을 입력하세요' />
                    </div>
                    <div>
                        <textarea className='inputContent' placeholder='내용을 입력하세요' />
                    </div>
                    <div>
                        <button type='submit'>submit</button>
                    </div>
                </form>
            </div>
        </div>
    )
}

export default BoardNew;

 

src/components/BoardContent.js

import React from 'react';

function BoardContent() {
    return(
        <div>
            <h2>상세보기</h2>
            <div>
                
            </div>
        </div>
    )
}

export default BoardContent;

 

BoardContent 는 컴포넌트만 만들어 두었다.ㅎㅎ

CSS 는 아래와 같다.

public/css/style.css

ul {
    list-style-type: none;
}
ul li {
    display:inline;
    margin: 15px;
}
table {
    font-family: Arial, Helvetica, sans-serif;
}
.listTable{
    border-collapse: collapse;
}
.listTable tbody tr td{
    border: 1px solid rgb(50, 50, 50);
    padding: 5px;
}
.listTableIndex {
    width: 100px;
}
.listTableTitle {
    width: 350px;
}
.th {
    text-align: center;
}
.noData {
    color: rgb(150, 150, 150);
    text-align: center;
}
.inputTitle {
    width: 350px;
    font-size: 16px;
    margin: 5px;
}
.inputContent {
    width: 350px;
    margin: 5px;
    font-size: 16px;
    height: 200px;
}

 

이제 Footer 컴포넌트와 버튼 컴포넌트들도 만들어준다.

src/components/Footer.js

import React from 'react';

import ButtonHome from '@components/ButtonHome';
import ButtonWrite from '@components/ButtonWrite';

function Footer() {
    return(
        <div>
            <hr />
            <nav>
                <ul>
                    <li><ButtonHome /></li>
                    <li><ButtonWrite /></li>
                </ul>
            </nav>
        </div>
    );
}

export default Footer;

 

src/components/ButtonHome.js

import React from 'react';
import { Link } from 'react-router-dom';

function ButtonHome() {
    return(
        <Link to='/'>
            <button>
                Home
            </button>
        </Link>
    );
}

export default ButtonHome;

 

src/components/ButtonWrite.js

import React from 'react';
import { Link } from 'react-router-dom';

function ButtonHome() {
    return(
        <Link to='/BoardNew'>
            <button>
                Write
            </button>
        </Link>
    );
}

export default ButtonHome;

 

ButtonHome.js 와 ButtonWrite 에서 버튼을 Link 로 감싸고, to= 에 path 를 넣어 주었다. 이 path 는 App.js 에서 컴포넌트를 호출할 수 있도록 한다.

/BoardContent 는 BoardList 에서 글 제목 클릭 시 구현될 상세보기 페이지 이므로 굳이 하지 않아도 상관없다.

 

이제 Footer.js 에서 uri 별로 Write버튼이 보일지 말지를 결정하기 위해 버튼이 클릭될 때마다 reducer를 호출하여 변경 될 uri 를 store 에 저장할 수 있도록 할 것이다.

index.js 에서 생성한 store(rootReducer) 를 생성해준다.

src/modules/rootReducer.js

import { combineReducers } from 'redux';
import uriReducer from '@modules/uriReducer';

const rootReducer = combineReducers({
    uriReducer
});

export default rootReducer;

 

rootReducer 생성 없이 uriReducer 로 바로 연결 해줄 수도 있지만, 나는 boardReducer 도 추가할 예정이기 때문에 rootReducer 로 combineReducers 를 사용하여 합쳐주었다.

이제 uriReducer.js 를 생성하고 작성해준다.

src/modules/uriReducer.js

// action type 설정
const URI_SAVE = 'URI_SAVE';

// 버튼 클릭 시 호출될 함수
export const uriSave = (inputData) => ({
    type: URI_SAVE,
    inputData: inputData
})

// data 초기화
const initialState = {
    inputData: '/'
}

// store 에 실질적으로 저장해주는 reducer
export default function uriReducer(state = initialState, action){
    switch(action.type) {
        case URI_SAVE:
            return Object.assign({}, state, {
                inputData: action.inputData
            })

        default:
            return state
    }
}

 

위와 같이 간단하게 uriReducer 를 작성했다.

위에서 작성한 uriSave() 함수를 호출해줄 버튼 컴포넌트들에 dispatch를 추가해준다.

src/components/ButtonHome.js

import React from 'react';
import { Link } from 'react-router-dom';

import { useDispatch } from 'react-redux';
import { uriSave } from '@modules/uriReducer'

function ButtonHome() {
	// 함수형 컴포넌트에서 dispatch 하기 위해 사용
    const dispatch = useDispatch();

	// reducer의 uriSave 함수를 호출
    function onClick() {
        dispatch(uriSave('/'))
    }

    return(
        <Link to='/'>
            <button onClick={onClick}>
                Home
            </button>
        </Link>
    );
}

export default ButtonHome;

 

write 버튼도 동일하게 작성한다.

src/components/ButtonWrite.js

import React from 'react';
import { Link } from 'react-router-dom';

import { useDispatch } from 'react-redux';
import { uriSave } from '@modules/uriReducer';

function ButtonHome() {
    const dispatch = useDispatch();

    function onClick() {
        dispatch(uriSave('/BoardNew'))
    }

    return(
        <Link to='/BoardNew'>
            <button onClick={onClick}>
                Write
            </button>
        </Link>
    );
}

export default ButtonHome;

 

이제 버튼이 클릭 될때마가 reducer의 함수를 호출하고 store 에 상태가 저장될 것이다.

Footer 컴포넌트에서 store 의 상태가 변경될 때마다 state 를 전달받도록 구독하고, state 에 따라 write 버튼을 보여줄 지 말지 결정할 수 있다.

src/components/Footer.js

import React from 'react';
import { useSelector } from 'react-redux';

import ButtonHome from '@components/ButtonHome';
import ButtonWrite from '@components/ButtonWrite';

function Footer() {
	// store 의 상태가 바뀔 때마다 상태를 받아온다.
    const uri = useSelector(state => state.uriReducer.inputData)

    return(
        <div>
            <hr />
            <nav>
                <ul>
                    <li><ButtonHome /></li>
                    // 받아온 상태가 '/BoardNew' 가 아닐때만 버튼을 보여준다.
                    {uri !== '/BoardNew' ?
                        <li><ButtonWrite /></li> : 
                        <li></li>
                    }
                </ul>
            </nav>
        </div>
    );
}

export default Footer;

 

이제 npm start 로 프로젝트를 실행하면 아래와 같이 구동 될 것이다.

다음 게시물은 게시글을 추가하고 삭제하는 기능을 추가 해보도록 하겠다.

 

 

댓글