본문 바로가기
React

[React] redux 를 이용하여 게시판 만들기(1) - 떽떽대는 개발공부

by 떽이 2021. 1. 25.

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

 

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

오늘은 그동안 공부했던 것을 바탕으로 react-router-dom 과 redux 를 이용한 페이지 구현하는 부분을 포스팅 해보겠다. 원레는 게시판을 만들어보려고 했는데 오늘은 이 부분 구현하는 데 시간을 많

ddeck.tistory.com

 

 

 

오늘은 전 글에 이어 게시판을 만들어볼 것이다.

전 글에서 ui 는 거의 다 작성해 두었고, 오늘은 boardReducer를 만들고, 글을 등록해서 store 에 저장하면

store를 구독하고 있는 list 에서 게시글 목록을 받아오는 부분을 할 것이다.

 

src/modules/boardReducer.js

const _SAVE = 'DATA_SAVE';

export const dataSave = (inputData) => ({
    type: _SAVE,
    inputData: {
        id: inputData.id,
        title: inputData.title,
        content: inputData.content
    }
})

const initialState = {
    lastId: 0,
    inputData: [
        {
            id: '',
            title: '',
            content: ''
        }
    ]
}

export default function boardReducer(state = initialState, action){
    switch(action.type) {
        case _SAVE:
            console.log(state.inputData)
            return {
                lastId: state.lastId + 1,
                inputData: state.inputData.concat({
                    ...action.inputData,
                    id: state.lastId + 1,
                })
            }
        default:
            return state
    }
}

 

먼저, 타입을 지정 해주고 글 작성시 호출될 datasave 함수를 작성하였다.

글 작성 페이지에서 submit 버튼을 클릭하게 되면 datasave 함수에 dispatch 하여 저장된 데이터를 받아오고, export default 설정된 boardReducer 에서 action 으로 받아온 데이터를 전달 받을 수 있다.

중요한 점은 리듀서에서는 순수 함수만을 사용해야 함으로 state의 값을 직접 수정할 수 없다는 것이다.

그래서 나는 concat 함수를 사용하였는데, 이 부분에서 괜히 삽질을 했다ㅠ

concat 함수로 배열을 추가 해주기 위해 initialData에서 대괄호 처리를 해주어야 중괄호로 묶이는 배열들을 계속 concat 처리할 수 있다. 아니면 에러가 발생될 것이다.

 

src/components/BoardNew.js

import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { dataSave } from '@modules/boardReducer';
import { uriSave } from '@modules/uriReducer'
import { useHistory } from 'react-router-dom';

function BoardNew() {
	// title, content 를 제어하기 위해 선언
    const [title, setTitle] = useState('')
    const [content, setContent] = useState('')

	// 함수형 컴포넌트에서 useDispatch 사용을 위해 선언
    const dispatch = useDispatch();
    
    //data dispatch 후 list 페이지로 이동하기 위해 선언
    const history = useHistory();

    const onSave = () => {
    	// reducer 에 선언된 초기값과 동일한 타입으로 data 값 설정
        const _inputData = {
            id: '',
            title: title,
            content: content
        }
        // 리듀서의 dataSave 함수에 dispatch
        dispatch(dataSave(_inputData))
        // input 값 reset
        setTitle('')
        setContent('')
        // data dispatch 후 페이지 이동
        history.push('/')
        // 페이지 이동 시 footer 의 button 설정을 위해 dispatch
        dispatch(uriSave('/'))
    }

	// input 값이 onChange 될 때마다 호출되어 setTitle, setContent 에 값을 넣어 제어한다.
    const handleTitle = (e) => {
        setTitle(e.target.value)
    }

    const handleContent = (e) => {
        setContent(e.target.value)
    }

    return(
        <div>
            <h2>글 작성</h2>
            <div>
                <div>
                    <input type='text' className='inputTitle' placeholder='제목을 입력하세요' onChange={handleTitle} value={title} />
                </div>
                <div>
                    <textarea className='inputContent' placeholder='내용을 입력하세요' onChange={handleContent} value={content} />
                </div>
                <div>
                    <button type='button' onClick={onSave}>submit</button>
                </div>
            </div>
        </div>
    )
}

export default BoardNew;

 

BoardNew.js 에서는 먼저 input 란에 value 값이 입력 될 때마다 입력되는 값을 계속 저장을 해야한다.

이를 위해 onChange 로 handleContent로 값을 계속 setContent 에 넣어 주었다.

그리고 함수형 컴포넌트에서 useDispatch 를 사용하기 위해 dispatch 라는 함수를 먼저 선언 해주었다.

글 저장 버튼이 클릭되면 onSave 함수가 호출되고, reducer 에서 초기화한 data 값과 동일한 타입으로 맞춰준다.

먼저 선언한 dispatch 함수를 통해 reducer 에 선언되어 있는 saveData 함수에 넘겨주고 input 란은 reset 해준다.

 

추가로 submit 버튼 클릭 후 dispatch 후 list 페이지로 이동할 때 새로고침 되면 store 에 저장되어 있는 state 값이 초기화 되기 때문에 useHistory 를 이용하여 페이지 이동하고 uriReducer 의 uriSave 를 호출하여 footer 부분의 버튼이 제대로 동작하도록 한다.

 

src/components/BoardList.js

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

function BoardList() {
	// useSelector 로 boardReducer 에 있는 inputData 값을 가져온다.
    const {inputData} = useSelector(state => state.boardReducer)
    // useSelector 로 boardReducer 에 있는 lastId 값을 가져온다.
    const {lastId} = useSelector(state => state.boardReducer)

    return(
        <div>
            <h2>게시판</h2>
            <div>
                <table className='listTable'>
                    <tbody>
                        <tr>
                            <td className='listTableIndex th'>index</td>
                            <td className='listTableTitle th'>title</td>
                        </tr>
                        {lastId !== 0 ? // lastId 가 0이 아닐때만 목록을 보여준다.
                            inputData.map(rowData => (
                                rowData.id !== '' && // rowData의 id 가 ''이 아닐때 목록을 보여준다.
                                <tr>
                                    <td className='listTableIndex'>{rowData.id}</td>
                                    <td className='listTableTitle'>{rowData.title}</td>
                                </tr>
                            )) :  // 작성된 목록이 없을 때 보여줄 내용
                            <tr>
                                <td className='listTableIndex'></td>
                                <td className='listTableTitle noData'>작성된 글이 없습니다.</td>
                            </tr>
                        }
                    </tbody>
                </table>
            </div>
        </div>
    )
}

export default BoardList;

 

boardList.js 에서는 현재 store 의 state 값을 가져오고, 그 값에 따라 어떤 내용을 보여줄 지 정한다.

먼저 useSelector 를 통해 현재 state 를 받아오고, 그 값에 따라 list 를 보일 지, 내용이 없다고 나타낼 지 알려준다.

이제 글 작성에 대한 부분은 완성 되었다. 

다음 글은 리스트에서 글 제목을 클릭하면 해당 글로 이동하는 부분을 구현해볼 것이다.

 

 

댓글