ES6(ECMAScript6)

- http://web-front-end.tistory.com/21

- https://cimfalab.github.io/deepscan/2016/07/ecmascript-6

- https://www.w3schools.com/js/js_es6.asp

- https://www.w3schools.com/js/js_es5.asp


React & Redux

- https://velopert.com/3613

- https://www.inflearn.com/course/react-%ea%b0%95%ec%a2%8c-velopert/

- https://www.inflearn.com/course/reactjs-web/

- https://reactjs.org/

- React + Redux 플로우의 이해


Router (특히 로그인 상태에 따른 처리)

https://reacttraining.com/react-router/web/guides/quick-start


Firebase

- https://firebase.google.com/docs/?hl=ko


React, Redux & Firebase

- https://chanspark.github.io/2017/12/06/Firebase-%EA%B3%B5%EB%B6%80.html

- https://www.youtube.com/watch?v=mwNATxfUsgI

- https://www.youtube.com/watch?v=z0QpTl5-0Bk

- https://www.youtube.com/channel/UCW5YeuERMmlnqo4oq8vwUpg


이상의 기초를 가지고 만든 CRUD 게시판

   1. React 게시판(CRUD) 만들기

   2. React + Redux 게시판(CRUD) 만들기

   3. React(Redux) + Firebase 게시판(CRUD) 만들기


디자인

- https://material-ui.com/ or https://material-ui.com/premium-themes/

- material-ui로 만든 템플릿 https://github.com/gujc71/project9_react

- http://react-toolbox.io/#/


이상의 기초를 가지고 만든 응용 웹

- Firebase 기반 메신저 앱 - DirectTalk9 (Web과 PC 버전)


분석해 보면 좋을 예제

- https://github.com/firebase/friendlyeats-web

- https://github.com/web-pal/react-trello-board

- https://react.rocks/


라이브러리

- https://github.com/brillout/awesome-react-components

- http://khan.github.io/react-components/


'Node.js > React' 카테고리의 다른 글

React(Redux) + Firebase 게시판(CRUD) 만들기  (2) 2018.11.04
React + Redux 게시판(CRUD) 만들기  (1) 2018.11.04
React 게시판(CRUD) 만들기 1  (4) 2018.10.28
React 게시판(CRUD) 만들기 2  (11) 2018.10.28

React로 작성한 이전 예제는 게시판 데이터를 배열에 저장하기 때문에 다른 사람은 볼 수 없고, 웹 브라우저를 갱신(F5)하면 초기화 되었다.

이번에는 Firebase를 이용하여 실제 게시판처럼 데이터를 서버에 저장해서 데이터가 사라지지 않고 다른 사람과 공유할 수 있도록 구현한다.


여기서 정리한 내용은 React 게시판 만들기 시리즈의 세번째 내용이다.

   1. React 게시판(CRUD) 만들기

   2. React + Redux 게시판(CRUD) 만들기

   3. React(Redux) + Firebase 게시판(CRUD) 만들기


Firebase는 구글에서 제공하는 서버 관련 기능들로 gmail계정으로 간편하게 사용할 수 있다.

NodeJS에서 Firebase를 사용하는 방법은 이 문서에 정리되어 있다.

Firebase의 콘솔 사용법도 같이 정리 되었으니 이전 문서를 확인하고,

여기서는 Firebase의 여러 서비스 중 데이터를 저장하는 Cloud FireStore를 이용한다.

여기서 만드는 게시판은 로그인기능이 없기 때문에 아무나 글을 쓰고 저장할 수 있도록 Cloud FireStore를 테스트 모드로 설정하고, 관련 설정은 이 문서를 참고하면 된다.


배열이 아닌 Cloud FireStore에 사용자가 작성한 게시물을 저장한다.

이외에 Firebase에 대한 사용법은 Firebase 문서를 참고하고, 이 문서 중에서 Cloud FireStore 관련 문서(데이터 가져오기, 데이터 저장등)를 읽어 보는 것이 좋다.


React로 작성한 이전 게시판 예제Nodejs 기반으로 작성한 Firebase 게시판 예제를 합치는 것으로,

다음과 같이 App_reduce.js 파일의 내용만 Firebase에 맞추어 조금만 수정하면 작성할 수 있다.

그전에 다음과 같이 Firebase와 미들웨어를 설치 한다.

npm install --save firebase

npm install --save redux-thunk

redux-thunk를 설치하고, redux-thunk를 사용할 수 있도록 다음과 같이 index.js 파일을 수정한다.

import thunk from 'redux-thunk';

import App from './App';
import board_reducer from './App_reducer';

let store = createStore(board_reducer
                , window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
               , applyMiddleware(thunk) );

index.js

그리고, Firestore.js 파일을 생성하여 다음과 같이 작성한다.

import firebase from 'firebase';

var config = {
    apiKey: "AIzaSyDbsU_",
    authDomain: "fir-example-.firebaseapp.com",
    databaseURL: "https://fir-example-.firebaseio.com",
    projectId: "fir-example-",
    storageBucket: "fir-example-.appspot.com",
    messagingSenderId: ""
};
firebase.initializeApp(config);
var firestore = firebase.firestore();

const settings = {timestampsInSnapshots: true};
firestore.settings(settings);

export default firestore;


Firestore.js

React에서 Firestore에 접속하기 위한 정보로 config의 내용을 채워야 한다.

config의 내용은 Firebase 콘솔에서 가져올 수 있고, 가져오는 방법은 이전 문서를 참고하면 된다.

Firestore에 접속한 연결은 firestore로 외부로 보내지고 (export ),

다음 코드에서처럼 firestore로 사용된다 [라인 2, 7, 23, 32, 40].


다음과 같이 App_reduce.js에 firebase_board_list, firebase_board_remove, firebase_board_save 3개의 함수를 추가한다.

3 개의 파일은 Firebase(FireStore)의 게시판 데이터를 가지고 오는 리스트, 선택한 게시물을 삭제하는 삭제, 게시글을 수정하거나 새로 등록하는 글 저장 함수이다.

먼저, firebase_board_list()는 FireStore에서 데이터를 가지고 와서 기존의 state.boards에 넣어주는 기능을 한다 [라인 5].

작성된 날짜(brddate)를 기준으로 최근 데이터가 먼저 오게(desc) 데이터들을 가지고 와서 (get) [라인7 ]

데이터의 개수 만큼 반복(forEach)해서 별도의 배열(rows)에 넣어주고 [라인 10],

화면에 출력하기 위해 board_list() 함수를 호출해서 Redux로 보관한다 [라인 15].

데이터의 개수 만큼 반복하는 것은 전송 받은 데이터 리스트 구조가 JSon 배열이라 필요한 정보만 추출(doc.data() – 게시물 하나)해서 보관하기 위한 것이다 [라인 11].

전송 받은 데이터를 그대로 배열에 넣어서 사용해도 된다 (대신 관련한 코드를 좀더 수정해야 한다.).

이전 예제에서는 board_list()함수를 사용하지 않았는데, 여기서는 state.boards에 데이터를 저장하는 역할을 한다.

개념상 Redux로 저장된 state.boards의 데이터를 가지고 오는 것이 되어야 할 것 같지만 반대로 저장하는 역할을 한다.

즉, firebase_board_list()로 서버에서 데이터를 가지고 오고, board_list()로 state에 저장하는 구조이다.


firebase_board_remove()는 주어진 글번호로(brdno) FireStore에 있는 데이터를 삭제(delete)하는 명령어를 실행한다 [라인 20].

삭제하고 나면(then) [라인 23],

화면에 출력하기 위해 데이터를 가지고 있는 state.boards에서 제거하기 위해 board_remove() 함수를 호출한다 [라인 24].

board_remove()는 기존 코드와 동일하다.


firebase_board_save()함수는 수정인지 신규 등록인지 구분해서 [라인 31]

수정이면 update [라인 10],

신규 등록이면 set으로 데이터를 저장한다 [라인 35].

수정이든 신규등록이든 저장하고 나면(then), board_save() 함수를 호출해서 state.boards의 값을 수정하여 화면에 반영되게 한다 [라인 37, 41].

board_save()의 코드도 기존 코드와 동일하다.


사용자가 행을 선택하면 호출하는 board_read()는 데이터 변경이 없기 때문에 수정없이 그대로 사용한다.


initialState 에서 선언했던 최대 글번호(maxNo)를 제거한다 [라인 47].

Firestore에 저장하기 때문에 Firestore에서 제공하는 문서 번호(id)를 글번호로 사용한다 [라인 33].


이상으로 App_reducer.js 파일을 이렇게 수정한 뒤,

기존에 각 컴포넌트에서 사용한 board_remove(), board_save() 함수를 사용한 각 컴포넌트에서 firebase_ 을 앞에 붙여 추가한 3개의 함수를 사용하도록 하고,

글 리스트의 경우에는 state.boards 를 사용하기 전에 firebase_board_list()를 호출하도록 수정한다.


글리스트(BoardList) 먼저 수정하면 다음과 같다.

App.js에 작성했던 리스트 코드를 분리하여 별도의 컴포넌트로 제작하였다.

이해를 위해 App.js에 그대로 작성했지만, React에서는 기능별로 세분화해서 컴포넌트로 사용하는 것이 권장된다.


글 리스트를 구현 할 것이기 때문에 Reducer에 작성해 놓은 firebase_board_list()를 사용한다고 선언한다(import) [라인 2].

컴포넌트가 생성되는 시점에 발생하는 이벤트인 componentDidMount에서 firebase_board_list()를 실행한다 [라인 6].

firebase_board_list()를 호출하면 Reducer의 state.boards에 게시글들이 채워지고

이것을 바라보고 있는 this.props.boards에 전달되면서 화면에 출력된다 [라인 9].

this.props.boards는 앞서 정리한 데로 mapStateToProps으로 연결하였다 [라인 16~23].


각 게시물을 출력하는 BoardItem은 데이터 삭제를 위해 board_remove() 대신에 firebase_board_remove()를 호출한다.

import { board_read, firebase_board_remove } from './App_reducer'

const BoardItem = ({row, inx, board_read, firebase_board_remove}) => (
    <tr>
        <td>{inx}</td>
        <td><a onClick={() => { board_read(row.brdno) } }>{row.brdtitle}</a></td>
        <td>{row.brdwriter}</td>
        <td>{row.brddate}</td>
        <td><a onClick={() => { firebase_board_remove(row.brdno) }}>X</a></td>
    </tr>
);

const mapDispatchToProps = dispatch => ({
  board_read: brdno => dispatch(board_read(brdno)),
  firebase_board_remove: brdno => dispatch(firebase_board_remove(brdno))
})

export default connect(null, mapDispatchToProps)(BoardItem)

BoardItem.js

코드의 다양성을 보여 주기 위해 BoardItem에서는 기존과 다른 방법으로 App_reducer의 함수를 호출하도록 했다.

BoardList 컴포넌트에서는 import문에서 사용할 함수를 선언하고 다음과 같이 사용했다.

         this.props.dispatch(firebase_board_list());

firebase_board_remove도 이렇게 사용해도 되지만 mapDispatchToProps 를 사용해서

App_reducer에서 사용할 함수를 다시 선언하고 BoardItem에 연결(connect)할 때 파라미터를 넘기는 방식으로 사용한다.

이렇게 사용하면 this.props.dispatch를 생략하고 firebase_board_remove() 함수만 호출해서 사용할 수 있다.


[ 참고 ] connect에서 첫번째 파라미터는 mapStateToProps로 컴포넌트에서 사용할 변수들을 선언한다. BoardItem에서는 사용할 변수가 없어서 null로 지정하였다. mapDispatchToProps 는 사용할 함수를 선언하는 것이다.


글 쓰기 폼(BoardForm)에서는 board_save() 대신에 firebase_board_save로 바꾸어 주면 된다.

import { firebase_board_save } from './App_reducer'

class BoardForm extends Component {
   
    handleSave = () => {
        this.props.dispatch(firebase_board_save(this.state));
        this.setState (this.initialSelectedBoard);
    }
}

BoardForm.js

BoardForm에서는 this.props.dispatch로 호출했다.

mapDispatchToProps와 this.props.dispatch 중 적절한 방식으로 작성하면 되지만 mapDispatchToProps가 많이 사용된다.

(개인적으로 귀찮아서...)


이상으로 간단하게 Redux 예제를 Firebase Cloud Firestore로 구현하였다.

기존 코드에 Firestore의 CRUD를 처리하는 함수를 추가하는 것으로 간단하게 구현하였다.

즉, React(Redux)로 사용자의 PC에서 화면에 데이터를 출력하고 관리하는 기능을 하도록 하고,

Firestore로 서버에 데이터를 저장하고 관리하는 기능을 하도록 서로의 역할을 나눠서 사용한다.


다른 브라우저로 접속해서 데이터가 동일하게 출력되는 것을 확인 할 수 있다.


Firestore(Cloud Firestore)는 Realtime database와 같이 실시간 데이터베이스 이다.

서버의 데이터에 변경이 생기면 현재 접속한 클라이언트에 실시간으로 데이터가 보내진다.

실시간으로 동기화 된다는 것으로, 이 기능을 이용하여 많은 예제들이 Fireabse 기반 메신저를 제작하였다.

상세한 설명은 Firebase 문서에서 확인할 수 있다.


이번에는 앞서 작성한 예제를 실시간 데이터 방식으로 작성한다.

소스는 Github의 step2 브랜치에서 받을 수 있다.

변경 방법을 간단하게 정리하면, 데이터 변경 사항을 글 리스트에서 받아서 state.boards에 반영하여 보여주는 식으로 작성한다.

코드에서 보듯이 글 리스트(firebase_board_list)에서 거의 모든 기능이 구현된다.


이전에는 get() 메소드를 사용했지만, onSnapshot()을 사용하고 [라인 4],

전송 받은 데이터의 개수(forEach) 만큼 반복했다.

이전과 가장 큰 차이는 전송된 데이터의 종류(change.type)이다 [라인 7, 11, 14].

현재 데이터(행)가 신규 추가인지(add), 수정 인지(modified), 삭제 인지를(removed) 나타낸다.

이 종류에 따라 board_save()나 [라인 9, 12] board_remove()를 호출해서[라인 15] state.boards의 데이터를 관리한다.


그리고, firebase_board_remove(), firebase_board_save()에서

처리 후 콜백(then)으로 board_save()나 board_remove()를 호출할 필요가 없어져 제거한다 [라인 25, 35, 37].

이 코드들을 모두 리스트에 작성하였다 [라인 9, 12, 15] .


그리고 onSnapshot으로 호출된 콜백 함수는 함수가 종료 되어도 계속 실행되기 때문에 서버의 데이터 변경 정보를 계속 수신해서 반영한다.

다른 사람이 작성한 것도 모두 실시간으로 제공된다.


 주의  onSnapshot으로 호출된 콜백 함수는 계속 실행되기(Listening) 때문에 필요하지 않을 경우에는 사용을 중지해야 한다.

중지 방법은 Firestore 문서에서 [리스너 분리]항목을 참조하면 된다.


이상으로 React, Redux, Firebase 순으로 기초적인 게시판 만들기를 정리하였다.

이해를 위해 Table 테그로 디자인 없이 작성하였는데,

그림과 같이 Material-ui와 같은 React용 디자인 라이브러리를 사용하면 제법 쓸만한 SPA(Single Page Application) 웹사이트를 만들 수 있다.

게시판에 대한 개념이 이해가 되었으면,

Google's Material Design을 준수하는 Material-ui등의 디자인 라이브러리를 적용하여 다음과 같이 사용하면 된다.


이상의 게시판에 기능을 보강하고

Fireabse 인증(로그인) 기능, Material-ui 등을 추가하여 Project9 템플릿을 제작하였다.

실제 프로젝트를 쉽게 할 수 있도록 만든 템플릿으로 (작성된 코드를 Copy & Paste 하여 개발하기 위한 예제들)

이상의 개념을 가지고 응용하는 방법을 이해하는데 도움을 줄 수 있으니 확인해보면 도움이 될 것이다.


Project9 템플릿을 기반으로 DirectTalk9 이라는 실시간 메신저를 웹 버전으로 제작하였고,

다시 이것을 Electron을 이용하여 PC 버전으로 제작하였다.

React Native를 쓰면 모바일까지….



'Node.js > React' 카테고리의 다른 글

React 학습자료  (0) 2019.01.23
React + Redux 게시판(CRUD) 만들기  (1) 2018.11.04
React 게시판(CRUD) 만들기 1  (4) 2018.10.28
React 게시판(CRUD) 만들기 2  (11) 2018.10.28

앞서서 정리한 React 기반 게시판을 Redux로 구현한다.

React 기반 게시판에서 글을 수정할 경우,

사용자가 선택한 행을 부모에게 알리고, 부모는 이것을 받아서 입력 폼으로 전송하는 방식으로 구현하였다.

사용자가 입력을 완료하고 저장하면, 다시 입력한 내용을 부모에게 전송해서 부모의 state 변수에 저장한다.

정리하면, 데이터 저장소(state)가 부모(App.js)에게 있기 때문에 항상 부모를 거쳐서 모든 기능이 구현되어야 한다.


여기서 정리한 내용은 React 게시판 만들기 시리즈의 두번째 내용이다.

   1. React 게시판(CRUD) 만들기

   2. React + Redux 게시판(CRUD) 만들기

   3. React(Redux) + Firebase 게시판(CRUD) 만들기


데이터 저장소와 데이터를 관리하는(CRUD) 기능(함수)등을 한 곳에 두고

각 컴포넌트들은 각자의 기능에 맞추어, 이 함수만 호출하도록 작성한다면 깔끔하고 쉬운 프로그램을 개발 할 수 있을 것이다.

이러한 기능을 제공하는 라이브러리 중에 많이 사용하는 Redux로 앞서 제작한 게시판을 간단하게 구현한다.

Redux 기본 문법과 이해는 자료가 많으니 검색해 보길 바라고, 여기서는 Redux로 구현하면서 사용법을 간단하게 정리한다.

소스는 Github에서 받을 수 있다.


먼저, create-react-app로 새로운 프로젝트를(redux_board) 생성하거나

앞서 정리한 예제를 수정하면서 따라한다.

create-react-app redux_board

cd redux_board


Redux를 설치한다.

npm install --save redux react-redux


create-react-app로 생성된 index.js 파일을 열어서 다음과 같이 문장을 추가한다.

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

import { createStore } from 'redux';
import { Provider } from 'react-redux';

import App from './App';
import board_reducer from './App_reducer';

let store = createStore(board_reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

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

index.js

데이터 입출력과 관련된 모든 기능은 App_reducer.js 파일에 구현되어 있고,

이상의 코드는 이 App_reducer.js 파일을 Redux의 문법에 맞추어 App 전체에서 사용할 수 있도록 등록하는 것이다.


App_reducer.js 파일은 Redux의 reducer로 다음과 같이 데이터 관리와 관련된 기능을 구현해 둔다.

Reducer에는 지켜야 하는 형식이 있어서 복잡해 보이는데,

중요한 것은 데이터를 저장하는 state와 이를 관리하는 board_reducer 함수 이다 [라인 42].

state는 별도로 선언하지 않고,

board_reducer 함수 파라미터에서 initialState[라인 23]로 초기값을 지정하면서 사용한다 [라인 42].

initialState는 Json으로 구성되어 앞서의 React 게시판에서 데이터를 저장하는 state 구조와 동일하다.

최대 글 번호를 가지는 maxNo, 게시물 데이터를 보관하는 boards, 데이터를 수정하기 위해 현재 선택한 글 정보를 가지는 selectedBoard로 구성하였다.


board_reducer 함수에 대해서 정리하면

board_reducer 함수에서 모든 처리가 이루어지고,

파라미터로 제공되는 action의 종류(type)에 따라 어떤 처리(CRUD)를 할 것 인지를 구현한다.

action 종류(type)는 4가지의 상수로(const)로 구현되어 있다 [라인 1~4].

글 리스트를 제공하는 BOARD_LIST [라인 4],

사용자가 신규로 작성하거나 수정한 내용을 저장하는 BOARD_SAVE [라인 1],

수정하기 위해 글을 선택하는 BOARD_READ [라인 3],

글을 삭제하는 BOARD_REMOVE로 CRUD를 구성하였다 [라인 2].


App_reducer.js 파일 외부에서는 board_reducer 함수를 호출하는 것이 아니고,

액션 종류에 따른 각각의 함수 board_list, board_save, board_read, board_remove를 호출해서 사용한다.

이 함수를 호출하면 지정된 action type이 같이 파라미터와 같이 제공된다.


board_list는 그냥 전체 글 리스트를 반환 하기 때문에 파라미터 없이 action type만 지정한다 [라인 21].
(의미상 선언한 것으로 사용하지 않는다.)

board_save은 파라미터로 저장할 게시글 정보(data)가 필요하고 [라인 6~9],

board_read와 board_remove은 수정하거나 삭제할 글번호(brdno)가 필요하다 [라인 11~14, 16~19].

따라서 각각 라인처럼 필요한 정보를 파라미터로 지정하고

board_reducer에서는 action.data, action.brdno로 파라미터 값을 가지고 와서 사용한다.


board_reducer에서 구현한 각 액션의 기능은 모두 각 컴포넌트에서 사용한 코드들을 모은 것이다.

게시물과 관련된 CRUD 코드들을 board_reducer에 모아서 사용하는 것이다.


좀더 상세하게 정리하면,

글 리스트는 CRUD 개념상 정의한 것으로 여기서는 사용하지 않으니 넘어간다.

글 리스트 구현은 함수로 뭔가를 처리하는 것이 아니고,

state의 boards 변수의 전체 값을 가지고 와서 출력하는 것이기 때문에 구현하지 않는다.


글 저장은(BOARD_SAVE) 글 번호(brdno)의 값이 있으면 수정이니 

boards의 모든 행을 검사해서(map), 글 번호가 같은 게시물이면 새로운 게시물(data)를 반환하고 그렇지 않으면 기존 게시물(row)을 반환해서 새로운 배열을 생성한다 [라인 52] .

선택한 행은(selectedBoard) {}로 초기화 하고, 기존 state 값(…state)과 같이 반환한다 [라인 52] .

state에 변수가 3개 있으니, maxNo를 같이 반환하는 것과 같다 [라인 52] .

이 코드는 다음과 같이 작성해도 된다.

        return {maxno: state.maxno, boards: boards.map(생략), selectedBoard: {} };

변수가 많을 경우 이와 같이 모든 변수를 나열하는 것 보다 ...state로 작성하는 것이 좋다 (버그 방지).


글 번호(brdno)의 값이 없으면 [라인 49],

신규라 기존 게시물 데이터(boards)에 새로운 게시물을(data) 추가(concat)해주고, 글 번호(maxNo)를 1 증가 시켜 놓는다 [라인 50] .


글 삭제는(BOARD_REMOVE)는 게시물 데이터 (boards)에서 삭제할 글 번호에 해당하는 행을 찾아서 지우는 방식이 아니고,

삭제할 게시글이 아닌 게시물만 모아서(fiter) 배열로 다시 생성하는 방식으로 구현한다 [라인 54] .

조건에 부합하는 데이터(fiter)만 모아서 다시 배열을 만드는 방식이다.

성능 등의 여러가지 이유로 이 방식이 권장되고 있다.


글 수정을 위한 선택(BOARD_READ)은 주어진 글번호(brdno)에 맞는 게시글을 찾아서(find) selectedBoard로 지정하면 된다 [라인 58] .

나머지(maxNo, boards)는 ...state로 지정해서 반환한다.


React로 작성된 코드를 Redux로 바꾸는 것은

지금까지 정리한 App_reducer.js의 내용이 핵심이고, 다른 컴포넌트에서는 호출해서 사용만 하면 된다.

App_reducer.js에서는 저장할 데이터(state)의 초기값 지정(initialState)과 역할별 기능을 board_reducer에 구현하면 된다.


데이터와 데이터를 관리하는 기능들을 모두 리듀서 파일에 작성하기 때문에 App.js 파일에서는 다음과 같이 깔끔하게 정리된 코드를 볼 수 있다.

데이터를 저장하고, 삭제하고, 선택하는 기능들이 모두 리듀서 파일로 이동하고,

App.js에서는 게시글 리스트를 적절하게 출력하고, 주요 컴포넌트를 호출한다 [라인 14, 25].


코드 하단에 있는 mapStateToProps에서는 App 컴포넌트에서 사용할 변수 (데이터)를 선언한다 [라인 35~39].

여기서는 게시물 데이터만 가져와서 출력하기 때문에

Reducer의 state.boards를 boards로 받아서[라인 37] App 컴포넌트로 넘겨 준다(connect) [라인 41].

Reducerr에 있는 변수(boards)를 가지고 올 때에는 이런식으로 선언해서 가시고 오고 [라인 35~41]

this.props(.boards)로 해당 변수를 이용한다 [라인 9].


다음으로 글 항목(BoardItem)을 살펴보면, 코드는 다음과 같다.

부모로부터 받은 게시물 하나(this.props.row)를 TR 테그를 생성해서 적절하게 값을 출력한다.

글 항목(BoardItem)은 게시글을 출력하고,

사용자가 행을 선택하면(Click) 선택된 행 정보를 알려주는 역할만 하기 때문에 기존에도 코드가 별로 없었고, Redux에서도 큰 변경이 없다.


수정사항이 별로 없어서, 자바스크립트 문법을 다양하게 적용해 봤다.

제목을 클릭하면 글 수정, 삭제(X) 버튼을 클릭하면 삭제하도록 이벤트 핸들러를 작성한다.

기존에는 각각의 onClick에 [this.함수]방식으로 작성해서 Click 이벤트에 이벤트 핸들러를 연결했다.

이번에는 이벤트 핸들러를 화살표(=>) 함수로 작성하고,

화살표 함수에서 다른 함수를 호출하는 방식으로 구현했다 [라인 17, 20].

HTML에서 자바스크립트 함수를 직접 작성하는 것으로,

제목을 클릭하면 화살표 함수로 작성된 이벤트 핸들러에서 글번호(brdno)를 파리미터로 handleUpdateForm 함수를 호출한다 [라인 17].

handleUpdateForm 함수에서 실제로 데이터를 저장하는 리듀스(App_reducer)에 있는 board_read를 호출한다 [라인 7].

글 삭제는 이러한 과정을 생략하고 리듀스(App_reducer)에 있는 board_remove를 호출해서 데이터를 삭제한다 [라인 20].

하나는 함수를 거쳐서 실행한 것이고 하나는 직접적으로 실행한 것이다.

코드를 다양하게 구현할 수 있다는 걸 보여주기 위해 작성한 코드이다.


이상에서 주의해야 할 것은 기존의 기능들이 사라지고,

리듀스(App_reducer)에 구현해 놓은 함수 중에서 필요한 것을(board_read, board_remove) 선택해서 선언하고 [라인 4],

필요에 따라 호출하면 된다는 것이고 [라인 8, 20],

그냥 함수를 호출하는 것이 아니고, 부모에게서 받았다는 의미로 this.props.dispatch를 이용해서 호출한다는 것이다.


BoardForm에서는 글쓰기와 글 수정 기능이 구현되었다.

앞서의 예제에서 하나로 정리하지 않고 2가지 방식으로 정리한 글쓰기 코드(App5, App6)를 여기에서 정리하였다.

따라서 글쓰기 코드와 설명이 앞서 예제의 내용과 조금 더 차이가 있다 (둘을 겹합).


먼저, 사용자가 입력한 글 제목(brdtitle)과 작성자 이름(brdwriter)은 onChange 이벤트를 이용하여BoardForm 컴포넌트의 자체 state에 저장한다 [라인 15~17].

사용자가 저장 버튼을 클릭하면 [라인 33], 리듀서(App_reducer)에 구현해 놓은 board_save()함수를 호출하여 저장한다 [라인 19].

호출할때, 사용자가 입력한 값을 가지고 있는 내부 state 변수를 파라미터로 넘기고,

리듀서의 board_save()에서 boards 배열에 이 파라미터를 새로운 행으로 추가하면 관련 컴포넌트가 갱신되면서 행(TR-BoardItem)이 추가되어 화면에 출력된다.

입력 상자(input)가 내부 state 변수의[라인 7] 값을 바라보고 있기 때문에 [라인 31, 32]

setState로 각 변수의 빈 값을 가진 initialSelectedBoard를 지정해서 초기화 시켰다.

입력 상자의 값이 지워진다.


글 수정은 사용자가 수정할 행을 선택하면 board_read 함수가 실행되면서 selectedBoard에 선택된 행의 정보가 저장된다 [라인 39~43].

BoardForm에서는 mapStateToProps를 이용해서 리듀서(App_reducer)의 selectedBoard를 가지고 와서 화면에 출력한다.

BoardItem에서와 동일하게 mapStateToProps에서는 컴포넌트에서 사용할 리듀서의 변수들을 선언해서 사용한다.

사용할 때에는 this.props.selectedBoard로 사용하면 되고,

여기서는 props가 바뀔 때 발생하는 이벤트인 componentWillReceiveProps 를 이용해서 작성했다 [라인 24].

사용자가 선택할 때 마다 selectedBoard의 내용이 바뀌고,

이때마다 selectedBoard를 바라보고 있는 BoardForm의 componentWillReceiveProps의 이벤트가 실행된다 [라인 24].

componentWillReceiveProps에서 현재 선택된 행을(selectedBoard) 내부 state에 넣어주면 [라인 25],

state를 바라보고 있는 입력상자에 값들이 출력되어 사용자가 수정하게 된다 [라인 31, 32 ].

그리고, 내부 state에 있는 글번호(brdno) 값의 유무로 글 수정과 새 글쓰기가 구분되어 처리 되도록 리듀서에 작성하였다.

수정일 경우에는 selectedBoard의 글번호를 받아서 내부 state에 글번호가 있게 되고 [라인 25],

신규일 경우에는 initialSelectedBoard로 초기화 되어 글번호가 없다 [라인 21].


이상으로 Redex 사용 예제를 정리하였다.

앞서서 React만으로 구현한 예제 코드와 비교하면서 살펴보면 Redex가 얼마나 좋은지 쉽게 이해 될 것이다.

한마디로 다시 정리하면, 코드 복잡도가 줄어들어 버그나 유지 보수에 많은 이점이 있다.


그리고, 이번에는 이 편리한 것을 더 편리하게 사용하게 해주는 redux-actions을 적용한다.

리듀스에서 함수 선언시 역할(type)과 사용할 파라미터를 지정하는 것은 불편한 작업이고,

board_reducer함수에서 switch문의 사용은 여러가지 문제로 사용이 추천되지 않고 있다.


이것을 수정하는 것으로, 먼저 다음과 같이 redux-actions를 설치한다.

소스는 Github의 step2 브랜치(branch)를 다운로드 받으면 된다.


npm install --save redux-actions


설치한 redux-actions에서 사용할 createAction, handleActions 함수를 가지고 온다.

import { createAction, handleActions } from 'redux-actions'; const BOARD_SAVE = 'SAVE'; const BOARD_REMOVE = 'REMOVE'; const BOARD_READ = 'READ'; const BOARD_LIST = 'LIST'; export const board_save = createAction(BOARD_SAVE); export const board_remove = createAction(BOARD_REMOVE, brdno => brdno); export const board_read = createAction(BOARD_READ); export const board_list = createAction(BOARD_LIST); const initialState = { ~~생략 ~~ }; export default handleActions({ [BOARD_SAVE]: (state, { payload: data }) => { let boards = state.boards; ~~생략 ~~ }, [BOARD_REMOVE]: (state, { payload: brdno }) => { let boards = state.boards; return {...state, boards: boards.filter(row => row.brdno !== brdno), selectedBoard: {} }; }, [BOARD_READ]: (state, { payload: brdno }) => { let boards = state.boards; return {...state, selectedBoard: boards.find(row => row.brdno === brdno) }; } }, initialState);

App_reducer.js

createAction은 이상의 코드에서 작성된 것과 같이 그냥 역할(type)만 지정해서 호출하면 해당 역할을 하는 함수가 생성된다.

글번호(brdno)를 파라미터로 받는 경우 board_remove에서 사용한 것처럼 표시해도 되고, 다른 함수들처럼 생략해도 된다.


board_reducer 함수는 handleActions으로 바꾸고

Switch 문 대신에 Json 형식으로 각 역할에 따라 화살표(=>) 함수를 지정해 주면 된다.

데이터가 저장된 state는 파라미터로 제공받고,

글 저장을 위해 필요한 게시글 정보(data),

삭제와 선택을 위해 필요한 게시글 번호(brdno)와 같은 파라미터는 payload 변수의 멤버로 받아서 사용한다.


기존에는 하나의 함수에서 SWITCH문으로 각각의 기능을 처리해서

하나의 boards만 선언하면 되었지만,

handleActions에서는 각각의 함수로 구현하기 때문에 let boards = state.boards가 모든 함수에 사용되었다.

그 외의 코드는 모두 동일하다.


전체 코드(App_reducer.js)를 보면 훨씬 간단하게 작성된 것을 알 수 있다.







'Node.js > React' 카테고리의 다른 글

React 학습자료  (0) 2019.01.23
React(Redux) + Firebase 게시판(CRUD) 만들기  (2) 2018.11.04
React 게시판(CRUD) 만들기 1  (4) 2018.10.28
React 게시판(CRUD) 만들기 2  (11) 2018.10.28

React는 페이스북에서 제안된 사용자 인터페이스 제작을 위한 자바스크립트 라이브러리의 하나로,

싱글 페이지(SPA-Sigle Page Applications)나 모바일 애플리케이션의 개발에 유용하다.

이 React를 익히기 위해서

React를 이용하여 데이터 입출력(CRUD) 기능을 가진 게시판을 구현한다.

데이터베이스에 연결해서 구현하지 않고, 배열에 데이터를 저장하고 출력하는 방식으로 게시판을 구현한다.

React 기초 문법은 따로 정리하지 않으니, 관련 자료를 읽어보는 것을 좋다.

여기서 구현한 소스는 Github에서 다운로드 받을 수 있다.

여기서 정리한 내용은 React 게시판 만들기 시리즈의 첫번째 내용이다.

   1. React 게시판(CRUD) 만들기

   2. React + Redux 게시판(CRUD) 만들기

   3. React(Redux) + Firebase 게시판(CRUD) 만들기


React 게시판(CRUD) 만들기는 React 설치와 Github 예제를 실행해 보는 방법(만들기 1)과

실제로 이 게시판(CRUD) 예제를 만드는 과정(만들기 2)으로 정리한다.

       React 게시판(CRUD) 만들기 1

       React 게시판(CRUD) 만들기 2


먼저, NodeJS가 설치되어 있어야 한다.

yarn이나 npm중 편리한 것을 사용하면 되고, 여기에서는 npm으로 작성한다.

NodeJS와 npm 설치는 관련 자료가 많기 때문에 찾아보면 되고

여기서는 React 설치부터 시작한다.

운영체제 콘솔창(여기서는 윈도우 CMD)에서 다음 명령어로 create-react-app를 설치한다.

npm install -g create-react-app

create-react-app는 React 프로젝트를 쉽게 시작할 수 있도록 필요한 설정을 해주는 도구이다.

create-react-app를 설치한 뒤에 다음과 같이 create-react-app로 제작할 프로젝트를 생성한다.

여기에서는 react_board라는 이름으로 프로젝트를 생성하였다.


create-react-app react_board


create-react-app으로 React 앱 프로젝트를 생성하면

다음 그림과 같이 간단한 사용법이 출력된다.

yarn을 설치한 경우 npm이란 단어 대신 yarn으로 출력된다.

각 사용법은 따로 익혀두고 그림 하단의 2가지 명령어를 실행한다.

먼저, 생성한 프로젝트 폴더로 이동해서 (cd react_board)

웹 서버를 가동하면(npm start) 다음과 같이 웹 페이지가 실행된다.

웹 페이지에 설명되어 있듯이

src 폴더의 App.js 파일을 편집기로 열어서 다음 코드를 확인한다.

(Edit src/App.js and save to reload.)

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
}

export default App;

HTML 태그와 Javascript, React로 작성된 코드를 볼 수 있다.

Edit src/App.js and save to reload. 문장에서 글자를 원하는데로 수정하고 저장한 뒤에

웹브라우저로 확인하면 수정된 내용이 반영된 것을 확인할 수 있다.


여기까지 React를 실행하는 방법을 간단하게 정리하였다.

기초 문법은 따로 정리하지 않으니, 관련 자료를 읽어보는 것을 좋다.


구현할 게시판 예제는 다음과 같이 단계별로 제작한다.

    1. 데이터 출력 (글 리스트)

    2. 형식에 맞춰서 출력 (글 리스트)

    3. 데이터 입력 (새 글 작성)

    4. 데이터 수정과 삭제 (글 수정 / 삭제)

    5. 기능(컴포넌트)별 파일 구성


이상의 내용을 App.js 파일에 작성하면 되지만

작성 과정을 각각의 예제 파일로 남기기 위해 App1.js ~ App6.js 파일로 구성하고

react-router-dom을 이용하여 별도의 url로 제작하였다.


import React, { Component } from 'react';
import { Route, BrowserRouter as Router } from 'react-router-dom';

import App1 from './App1';
import App2 from './App2';
import App3 from './App3';
import App4 from './App4';
import App5 from './App5';
import App6 from './App6';

// simple list
class App extends Component {
render() {
return (
<Router>
<div>
<Route exact path="/" component={App1}/>
<Route exact path="/App1" component={App1}/>
<Route exact path="/App2" component={App2}/>
<Route exact path="/App3" component={App3}/>
<Route exact path="/App4" component={App4}/>
<Route exact path="/App5" component={App5}/>
<Route exact path="/App6" component={App6}/>
</div>
</Router>
);
}
}

export default App;

App.js

각 컴포넌트는 웹브라우저에서 다음과 같이 컴포넌트 이름으로 접속하여 실행할 수 있다.

       http://localhost:3000/App1

       ...

       http://localhost:3000/App6



이상으로 React 설치와 Github 예제를 실행해 보는 방법을 정리하였고,

다음으로 실제로 이 게시판(CRUD) 예제를 만드는 과정을 정리한다.

       React 게시판(CRUD) 만들기 1

       React 게시판(CRUD) 만들기 2




'Node.js > React' 카테고리의 다른 글

React 학습자료  (0) 2019.01.23
React(Redux) + Firebase 게시판(CRUD) 만들기  (2) 2018.11.04
React + Redux 게시판(CRUD) 만들기  (1) 2018.11.04
React 게시판(CRUD) 만들기 2  (11) 2018.10.28

React를 쉽게 익히기 위해서 데이터 입출력(CRUD) 기능을 가진 게시판을 구현한다.

React 만으로 구현하기 위해 배열에 데이터를 저장하고 출력하는 방식으로 게시판을 구현한다.


여기서 정리한 내용은 React 게시판 만들기 시리즈의 첫번째 내용이다.

   1. React 게시판(CRUD) 만들기

   2. React + Redux 게시판(CRUD) 만들기

   3. React(Redux) + Firebase 게시판(CRUD) 만들기


앞서 간단한 설치와 사용법을 정리하였고, 여기서는 다음과 같이 단계별로 제작한다.

소스는 Github에서 받을 수 있다.


    1. 데이터 출력 (글 리스트)

    2. 형식에 맞춰서 출력 (글 리스트)

    3. 데이터 입력 (새 글 작성)

    4. 데이터 수정과 삭제 (글 수정 / 삭제)

    5. 기능(컴포넌트)별 파일 구성


개인적으로 React는 아주 쉽게 (?) 배울 수 있는 라이브러리 이지만

자바스크립트(JavaScript), 특히 ES5 (ECMAScript 5) 이상(ES6)의 문법을 모른다면 배우기 아주 어려운 라이브러리라고 생각한다.

ES5와 ES6의 특징 정도는 알고 있는 것이 좋으니 웹 문서들을 찾아보길 바란다.

여기에서는 별도로 정리하지 않지만 쉽게 React를 익히기 위해 단계별로 가장 쉬운 것 부터 시작한다.


먼저, 주어진 배열의 내용을 출력하는 글 리스트부터 작성한다 (App1.js, App2.js).

앞서서 create-react-app로 생성하고 정리한 예제에서 App.js 파일의 내용을 지우고 다음과 같이 작성한다.

Github에서 다운받은 파일에서는 App1.js로 작성되어 있다.

작성 과정을 각각의 예제 파일로 남기기 위해 단계별로 진행한 내용을 App1.js ~ App6.js 파일로 작성하였지만,

따라 할 때에는 App.js에 코드를 추가하는 방식으로 작성하면 된다.

코드를 살펴보면 state 변수를 가장 먼저 선언하였다 [라인 4].

state 변수는 게시판 데이터를 배열로 가지는 boards 배열을 구성원(Json)으로 가지고 있다.

boards는 글번호(brdno), 작성자(brdwriter), 글제목(brdtitle), 작성일자(brddate)로 구성되고 2개의 데이터가 등록되어 있다 [라인 7~16].

데이터 베이스로 보면 4개의 필드와 2개의 행이 있는 것이다.


이 데이터(state)를 render()에서 출력한다 [라인 21~33].

render()는 React에서 화면을 생성하기 위해 실행하는 이벤트이다.

App 컴포넌트(Component)에 있는 state [라인 4]을 render()에서 사용하기 위해 this.state로 지정한다 [라인 22].

this는 자바스크립트에서 자기 자신(Component)을 의미한다.

this.state에 있는 것 중에서 하나를 가지고 올 때 사용하는 것이 대괄호({})로state에 있는 데이터(boards)를 boards에 저장한다 [라인 22].

대괄호({})는 state에 변수가 많을 때 편하게 사용하는 코드로 다음과 같이 사용해도 된다.

           const { boards } = this.state;

           const boards = this.state. boards;


 주의  const, let, var의 차이는 찾아보길……


가지고 온 데이터(boards)를 map() 메서드를 이용하여 2개 행의 글번호와 작성자를 묶어서 하나의 문자열(list)로 작성하였다 [라인 23~25].

이 값을 화면에 출력한다 [라인 29].


 주의  값을 출력할 때 대괄호({})가 사용되었다. 대괄호는 자바스크립트에서 함수의 범위가 되기도 하고 Json이기도 하지만, 변수내의 수 많은 변수 중 일부를 빼내서 사용하거나 [라인 22], React의 문법으로 태그(div) 사이에서 값을 출력할 때에도 사용된다. 여러 가지 의미로 사용되니 잘 기억해야 한다.

실행 결과는 다음과 같다.

정리하면,

render() 시작부터 return 이전(라인 22~6)까지는 자바스크립트의 영역이고

return 내부는 HTML (React 정의라 조금 다름) 영역으로 볼 수 있다.

HTML영역에서 자바스크립트를 사용할 때에는 대괄호({})를 사용한다.


이상의 코드는 다음과 같이 작성 할 수 있다.

    render() {
        const { boards } = this.state;

        return (
            <div>
            {
                boards.map(function(row){
                    return row.brdno + row.brdwriter ;
                })
            }
            </div>
        );
    }

App2.js

이전과 동일하게 글 번호와 작성자를 묶어서 출력하는 코드이다.

자바스크립트 코드 위치가 바뀌었고 대괄호({})를 사용한다.

데이터를 출력하는 코드는 render와 return 사이보다 return의 HTML 사이에서 많이 사용한다.


두번째 단계로 테이블(table) 테그로 게시판 리스트처럼 출력한다 (App3.js).

DIV 테그를 사용하는 것이 좋지만 CSS 설정 등으로 불필요한 코드 작성이 필요해, 다음과 같이 테이블 테그로 간단하게 작성하였다.

기존 코드에 테이블 테그를 사용하여 리스트 헤드 [라인 7~14]를 작성한다.

그리고, boards에 있는 데이터를 행(tr)으로 출력하도록 작성하였다 [라인 16~18].

다만, 각 행을 BoardItem이라는 컴포넌트를 이용하여 출력하게 작성한다 [라인 27].


BoardItem 컴포넌트에 row라는 변수로 boards의 행(row)을 하나씩 지정해서 넘겨주고 [라인 17]

BoardItem 컴포넌트에서는 이 row를 this.props로 받아서 사용한다.


 주의  컴포넌트 자신이 사용하는 것은 state이고, 부모로부터 받은 것은 props이다. 이 개념을 잘 이해하면 React의 주요 개념 절반을 이해한 것이다.

BoardItem 컴포넌트를 사용하는 것은 React의 특징으로 React에서는 모든 기능을 컴포넌트로 구현하여 사용한다.


이상으로 글 리스트 기능을 구현하면서 React의 컴포넌트 개념을 사용하였다.

React는 기능을 세분화해서 컴포넌트로 구현하는 특징이 있다.

그리고, 부모(호출하는) 컴포넌트가 자식(호출 받는) 컴포넌트에 값을 넘겨주고 받는 방법을 정리하였다.

세번째로 값을 입력받아서 저장하는 글 쓰기 기능을 구현한다 (App4.js).

글쓰기에서는 그림과 같이 글 제목과 작성자를 입력하고 저장(save) 버튼을 누르면 입력한 내용이 baords 배열에 저장되도록 구현한다.

글쓰기 기능을 구현하기 위해서는 HTML 컨트롤과 이벤트가 React와 연동되는 방법을 알아야 한다.

React(특히 이 예제)에서 가장 어려운 부분이라고 생각하니 코드의 의미에 대해서 제대로 이해하고 넘어가야 한다.


글쓰기 기능을 구현할 BoardForm 컴포넌트를 생성하고 [라인 21],

글쓰기에서 사용할 state 변수와 handleChange, handleSubmit 이벤트 핸들러를 작성하였다.

1 라인에 선언된 state는 App 컴포넌트에서 사용하는 state 이고, 22 라인에서 선언한 state는 BoardForm내부에서 사용하는 state이다.

state는 컴포넌트 내부에서 사용할 변수로 이름이 고정되어 있다.


두 이벤트 핸들러는 화살표(=>) 함수로 작성하였다.

화살표 함수가 아닌 전통적인 함수로 작성하면 bind등의 제법 복잡한 처리를 해야 한다.

handleChange는 사용자가 값을 입력할 때 마다(onChange 이벤트) 입력하는 값을 받아서 state 변수에 각 컨트롤의 이름(brdtitle, brdwriter)으로 저장한다 [라인 24].

handleChange핸들러의 e는 자바스크립트의 change 이벤트에서 파라미터로 넘어오는 Event를 의미하고 e.target은 현재 이벤트가 발생한 개체,

즉 값을 입력하는 입력상자를 의미한다.

두 개의 입력상자가 각각 brdtitle, brdwriter로 지정되어 있기 때문에 [라인 39, 40] 각각의 이름으로 변수가 생성되어 사용자가 입력한 값이 저장된다.

입력 받는 값이 글 제목(brdtitle)과 작성자(brdwriter)이므로, brdtitle, brdwriter로 저장된다.

즉,
    state = {
        brdtitle: 값,
        brdwriter: 값
    }
으로 저장된다.

저장시 "this.state.brdwriter=값" 이나 "this.state[brdwriter]=값" 으로 저장하지 않고 setState 함수를 사용하여 저장한다 [라인 25].

React의 규칙이니 준수해야 하고, 이렇게 하지 않으면 웹 브라우저 콘솔 등에 경고가 출력된다 (다음 그림과 전후 설명 참조).


라인 39 와 40 에서 입력 상자와 handleChange를 연결하였다.

연결시 handleChange에 this를 붙여 사용하는데, 컴포넌트 내의 변수나 함수(이벤트 핸들러)를 참조할 때에는 this를 붙여야 한다.


handleSubmit은 Form 태그가 값을 서버로 전송할 때 발생하는 이벤트를 처리하기 위한 핸들러이다 [라인 38].

즉, 사용자가 값을 입력하고 저장할 때 발생한다.

실제로 서버로 보낼 것이 아니기 때문에 preventDefault로 이벤트를 중지한다 [라인 22].

그리고 onSaveData 함수를 호출하여 데이터를 저장한다.


onSaveData()는 BoardForm 컴포넌트에 있지 않고 [라인 32],

부모인 App 컴포넌트에 있기 때문에 this.props.onSaveData()로 사용한다 [라인5, 15].

onSaveData()는 부모로 부터 파라미터(this.props)로 받았다 [라인 15].

부모로부터 받은 것은 값이든 함수이든 항상 props를 사용해야 한다 [라인 32].

그리고 저장할 값은 stae에 있으니 함수를 호출하면서 this.state를 넘겨준다 [라인 32].


부모 (호출하는) App 컴포넌트에서는 값을 입력 받을 적당한 위치에 BoardForm 컴포넌트를 생성한다 [라인 15].

컴포넌트의 생성은 HTML 태그처럼 <BoardForm/>로 작성하면 된다.

BoardForm를 생성하면서 파라미터로 handleSaveData() 함수를 onSaveData()라는 이름으로 넘겨준다.

이것을 자식(호출받는) BoardForm에서는 this.props.onSaveData()로 호출한다 [라인 32].

호출 받은 부모의 handleSaveData()에서는 setState를 이용하여 [라인 6] state에 있는 baords배열에 값을 추가(concat)한다 [라인 7].

baords배열에 concat으로 추가하고 이것을 baords라는 이름으로 저장하는 방식으로 작성한다.


저장시 글 번호(brdno)와 작성일자(brddate)을 생성한다[라인 7].

작성일자는 자바스크립트 Date 클래스로 현재 날짜를 입력하고, 글 번호는 state에 추가한 변수 maxNo의[라인 2] 값을 사용한다.

기본적으로 baords에 데이터 2 건이 있으므로 maxNo은 3의 값을 가지고 있고, 글을 추가한 후에 1 증가(++) 한 값(다음 글번호)을 저장한다.


 주의  웹 브라우저에서 이상의 예제인 App4를 실행하면 다음과 같은 경고를 볼 수 있다.



이 경고는 state에 값을 바로 대입한 코드를[라인 7] setState 함수로 수정하라는 것으로 다음과 같이 작성 한다.

    this.setState({
        maxNo: this.state.maxNo+1,
        boards: boards.concat({brdno: this.state.maxNo, brddate: new Date(), ...data })
    });

게시물(boards)을 추가하듯이 [라인 7],

maxNo의 값을 증가시키고, maxNo라는 이름으로 setState함수를 이용하여 저장한다.


실행해서 새로운 글이 잘 작성되는지 확인한다.


React의 값을 주고 받는 방식도 잘 기억해야 하지만,

자식(BoardForm)에서 작성한 값을 부모에게 보내어 저장하는 구조도 잘 기억해야 한다.

부모(App)의 state에 있는 boards에 모든 값을 저장하기 때문에 부모에게 사용자가 입력한 값을 전송한다.

부모의 state에 값을 저장하고,

이 state에 변경이 생기면

state(boards)의 값을 참조하는 또 다른 자식인 BoardItem에 값들이 자동으로 보내어져 추가된 글이 화면에 출력된다.

이 것이 React의 특징 중 하나이다.

일반적인 프로그래밍에서는 데이터를 추가하거나 수정하면 화면 갱신을 하도록 별도의 처리를 해야 하지만

React에서는 state에 변경사항이 생기면 관련 내용이 자동으로 반영된다.

익숙하지 않은 사람에게는 아주 헷갈리는 기능일 수 있다.


네 번째로 데이터를 수정하고 삭제하는 기능을 구현한다 (App5.js).


삭제 기능은 글을 추가하는 것과 같은 원리로 구현되니 상세한 설명은 생략하고 개념만 정리한다.

(직접 해보는 것이 실력 향상에 도움이 된다.)

삭제는 다음 그림처럼 글 리스트의 각 항목(BoardItem)에 삭제 버튼을 두고 [라인 37],

사용자가 삭제 버튼을 클릭하면 부모에 저장된 boards에서 해당 글을 삭제한다 [라인 3].

삭제 할 때는, 사용자가 선택한 글 번호(brdno)에 해당하는 글을 찾아서 삭제한다.

배열에서 값을 삭제하는 것은 fiter 사용이 권장된다 [라인 5].

다음 코드에서 handleRemove를 따라 다니면 보다 쉽게 코드를 이해 할 수 있을 것이다. (라인 37 > 24 > 26 > 17 > 3 > 5 순)


글 수정은 글 항목(행)들 중에 하나를 선택하면(handleSelectRow)

선택된 행의 값들을 사용자가 수정할 수 있도록 입력상자(BoardForm)에 뿌려주고,

사용자가 수정 후 저장 버튼을 클릭하면, 글번호의 값이 있으면 수정, 없으면 신규 등록으로 구현한다.

개념은 새글 작성과 동일하지만 글 수정은 제법 많은 변경과 추가가 이루어져 모든 코드를 글로 정리하기 어렵다. (실제로는 가장 어려운 부분이 여기다)

정리하더라도 읽으면서 혼란해지기 때문에 주요 코드와 개념만 정리한다.

나머지는 … (이 부분은 이해되지 않는 경우 넘어가도 된다. Redux에서 좀더 쉽고 간단하게 구현한다.)

handleSelectRow함수를 중심으로 살펴 보면, 그나마 보다 쉽게(?) 코드를 이해 할 수 있을 것이다.

사용자가 글 항목(행)들 중에 하나를 선택하면(handleSelectRow) [라인 58 => 50 => 38 => 25]

선택된 행의 값들을 사용자가 수정할 수 있도록 입력상자에 뿌려주어야 한다.

입력상자는 BoardForm에 있기 때문에 BoardForm을 부모(App) 컴포넌트가 알고 있어야 한다.

즉, 자식(BoardForm)의 핸들러를 가지고 있어야 한다.

각 컴포넌트의 핸들을 가지고 오는 속성 ref를 this.child에 보관하는 방법을 사용한다 [라인 34].


부모는 선택한 행의 데이터를 자식 (this.child)의 handleSelectRow를 호출하면서 파라미터로 넘겨준다 [라인 26].


입력 폼(BoardForm)에서는 부모로부터 받은 값을 그대로 state 변수에 넣어준다 [라인 76].

파라미터로 받은 값의 구조가 Json이기 때문에 이전처럼[라인 83~85] “변수: 값”의 구조로 지정하지 않고 한번에 넣어 준다 [라인 76].

이 state의 변수를 입력상자가 바로 보고 있도록 수정해서 [라인 92, 93] state 값이 변경되면 자동으로 바뀌도록 작성했다.

BoardItem과 같은 원리이다.


사용자가 수정 후 저장 버튼을 클릭하면 [라인 79]

부모로 데이터를 보내서 [라인 81]

글 번호(brdno)가 있으면 [라인 11] 수정, 없으면 신규 등록으로 구현한다.

신규 등록은 앞서 정리했고,

수정은 글 번호가 같은 행을 찾아서 (data.brdno === row.brdno) 행을 바꾸는 방식으로 구현한다 [라인 18].


마지막으로 사용자가 입력한 값은 지워 주는데

BoardForm에서 state 변수내의 값 변수들 값을 지워주면 화면에서도 지워진다.

BoardItem과 같은 원리로 입력 상자들이 state를 바라보고 있기 때문에 가능한 것이다.

React에서는 HTML 태그들이 자바스크립트에 바인드(Bind) 된 것처럼 작동한다.


이상으로 React의 기본적인 사용법을 게시판 예제로 정리하였다.

React 자체는 state, props만 알면 그렇게 어렵지 않지만

모든 기능을 세분화해서 컴포넌트로 작성하고, 값이 컴포넌트를 오가면서 코드가 아주 복잡해 지고 개념이 어려워 진다.

(App 컴포넌트의 리스트 부분도 BoardList로 별도로 제작해야 한다. 직접 해보길…)


값을 한 곳에 넣고 처리할 수 있도록 함수를 작성해 두고

이 함수만 각 컴포넌트에서 호출한다면 아주 쉽고 깔끔하게 코드를 작성 할 수 있을 것이다.

이러한 기능을 제공하는 라이브러리 중에 하나가 ReduxRedux예제는 별도로 작성하였기 때문에 여기에 정리하지 않는다.


마지막 예제는 각 컴포넌트를 별도의 파일로 작성하는 것이다 (App6.js).

코드 양이 많기 때문에 컴포넌트는 각각의 파일로 작성하고 export 를 이용하여 다른 파일에서 사용할 수 있도록 한다.

각 컴포넌트를 사용할 컴포넌트에서는 import로 가져와서 사용한다.


메인 컴포넌트인 App을 포함해서, BoardItem(BoardRow)과 BoardForm의 총 3개의 컴포넌트가 사용되었다.

따라서, 다음과 같이 각각의 파일을 작성하였다.

import React, { Component } from 'react';

class BoardRow extends Component {


}

export default BoardRow;

App6_BoardItem.js


import React, { Component } from 'react';


class BoardForm extends Component {

}

export default BoardForm;

App6_BoardForm.js

import React, { Component } from 'react';
import BoardForm from './App6_BoardForm';
import BoardItem from './App6_BoardItem';

class App6 extends Component {

    render() {
        const { boards, selectedBoard } = this.state;

        return (
            <div>
                <BoardForm selectedBoard={selectedBoard} onSaveData={this.handleSaveData}/>
                <table border="1">

                    {
                        boards.map(row =>
                            (<BoardItem key={row.brdno} row={row} onRemove={this.handleRemove} onSelectRow={this.handleSelectRow} />)
                        )
                    }
                    </tbody>
                </table>
            </div>
        );
    }
}

export default App6;

App6.js

주의 - BoardItem은 파일에서는 BoardRow로 작성하고, import 할 때에는 BoardItem로 이름 붙여 사용했다. 이렇게 사용해도 된다는 의미로 사용한 것이다.


마지막 예제에는 컴포넌트 별로 각각의 파일을 작성한 것 외에 몇 가지 React 기능을 더 추가해서 작성했다.

네 번째 예제(App5)는 React의 특징을 제대로 표현하지 못하였고, 값이 여러 컴포넌트를 오고 가면서 상당히 헷갈리는 예제다.

이 예제를 state를 이용하여 구현하였다.


부모(App6)에서 수정된 코드는 다음과 같다.

class App6 extends Component {

    state = {
        maxNo: 3,
        boards: [        ],
         selectedBoard:{}
    }
   
    handleSelectRow = (row) => {
        this.setState({selectedBoard:row});
    }
    ~~ 생략 ~~
    render() {
        const { boards, selectedBoard } = this.state;

        return (
            <div>
                <BoardForm selectedBoard={selectedBoard} onSaveData={this.handleSaveData}/>
                ~~ 생략 ~~
            </div>
        );
    }
}

행이 선택되면 handleSelectRow에서 선택된 행의 값을 모두 state의 selectedBoard에 저장한다.

이 selectedBoard을 입력폼(BoardForm)으로 전달한다.

선택된 행(selectedBoard)을 입력폼(BoardForm)의 shouldComponentUpdate 이벤트에서 처리하는 방식으로 구현한다.


이전 방식은 React의 특징인 state를 이용하여 컨트롤에 값을 보여주고,

사용자가 입력한 값을 state에 저장해서 구현했고,

이번에는 다음과 같이 shouldComponentUpdate 이벤트와 입력 상자에 대한 컨트롤(ref)을 이용해서 구현한다.

입력폼(BoardForm)에서는 컴포넌트가 업데이트 될때 발생하는 shouldComponentUpdate 이벤트에서 전달 받은 selectedBoard의 값을 화면에 출력한다 [라인 5].

shouldComponentUpdate와 같은 React의 이벤트는 이 문서를 참조하면 된다.


shouldComponentUpdate 이벤트에서 글 번호의 값이 있으면 [라인 7]

글 수정 상황이니 selectedBoard의 값을 입력상자에 넣어주고 [라인 13, 14]

값이 없는 것은 초기화 상태이니 입력 상자에 빈 문자열을 넣어준다 [라인 8, 9].


사용자가 값을 입력하면 이전에는 Chang 이벤트에서 값을 state에 보관해서 처리했지만 이번에는 ref를 사용하여 컨트롤을 참조하는 방식으로 구현했다 [라인 35, 36].

 주의  이 방식은 추천하는 방식이 아니지만 React의 이해(ref)를 위해 사용한 코드이다.


저장 버튼을 누르면 사용자가 입력한 값을 저장할 구조에 맞추어 Json 형식으로 생성한다 [라인 21~28].

글 번호(selectedBoard.brdno)가 있으면 수정이니 기존 데이터(selectedBoard)에서 글 번호와 작성일자를 가져오고 [라인 26, 27],

없으면 신규이니 사용자만 입력한 값만 Json으로 저장하면 된다.


이상으로 React를 이용한 게시판 제작을 5 단계로 구현하였다.

React는 특이한 방식으로 개발하여 익히는데 혼란을 주지만 개념을 잘 잡으면 쉽게 웹 개발을 할 수 있다.

더욱이 멀티 OS 개발을 쉽게 하는 React Native의 기본 문법이라 더욱 매력적이다.





'Node.js > React' 카테고리의 다른 글

React 학습자료  (0) 2019.01.23
React(Redux) + Firebase 게시판(CRUD) 만들기  (2) 2018.11.04
React + Redux 게시판(CRUD) 만들기  (1) 2018.11.04
React 게시판(CRUD) 만들기 1  (4) 2018.10.28

+ Recent posts