아직 완성은 아니지만 정리하기 위해 작성해봅니다.
+) 참고
0. create-react-app을 이용해 기본 뼈대 만들기
2020/01/02 - [STUDY/React.js] - React | Create React App을 이용해 React 시작하기
그 후, server폴더 생성
1. socket.io 설치
socket.io는 브라우저와 서버간 실시간 통신을 가능하게 해주는 라이브러리입니다.
$ npm install socket.io
2. 서버 생성
http서버로 생성하는 것도 가능하나 저는 express를 이용하여 서버를 생성해주었습니다.
앞서 생성해둔 server폴더 안에 작성
//chat_server.js
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const port = process.env.PORT || 3005 ;
server.listen(port, () => { console.log(`Listening on port ${port}`) });
3. client 작성
우선 socket.io-client를 설치해줍니다.
$ npm install socket.io-client
3-1. redux를 사용하기 위해 react-redux설치
$ npm install react-redux
4. Action작성
4-1. action types
굳이 나누지 않아도 되지만 앱이 커질경우 이렇게 type들만 별도 모듈로 관리하는게 좋다고 합니다...!
// src/redux/constants/actionTypes.js
export const SEND_CHAT = "SEND_CHAT";
export const RECEIVE_CHAT = "RECEIVE_CHAT";
4-2. action 생산자 작성
// src/redux/actions/actions.js
import * as type from '../constants/actionTypes';
export const sendChat = () => {
return {
type: type.SEND_CHAT
}
}
export const receiveChat = (data) => {
return {
type: type.RECEIVE_CHAT,
data
}
}
5. Reducer 작성
Reducer에서는 상태(state)를 관리하게 됩니다.
여러가지 리듀서를 작성한 후 combineReducers로 리듀서들을 합쳐주면 상태를 좀 더 나눠서 관리할 수 있게됩니다.
앞으로 리듀서를 더 추가할 예정이라 미리 combineReducers를 사용해주었습니다.
// src/redux/reducers/reducers.js
import {combineReducers} from 'redux';
import * as type from '../constants/actionTypes';
const chatStates = {
chatList: [],
socketId: null
};
const chatReducer = (state = chatStates, action) => {
switch(action.type){
case type.MY_SOCKET_ID:
return { ...state, socketId: action.socketId };
case type.RECEIVE_CHAT:
let newChatList = state.chatList.slice();
newChatList.push(action.data);
return { ...state, chatList: newChatList };
case type.CLEAR_CHAT:
return {...state, chatList: []}
default:
return state;
}
}
const rootReducer = combineReducers({chatReducer});
export default rootReducer;
{...state, ...newState} == Object.assign(state, { newState })
...state를 앞에 쓰는 것이 이전의 state값을 복제한 후 state를 변화시키는 것
6. Store작성
리듀서는 여러 개를 작성할 수 있지만 스토어는 하나로 작성해야 합니다. (분리할 수 있다고는 함)
logger는 액션이 실행되고 state값이 바뀌는 부분을 콘솔창에 보여주는 편리한 미들웨어입니다. 사용하세요!
// src/reducer/store.js
import {createStore, applyMiddleware} from 'redux';
import logger from 'redux-logger';
import rootReducer from './reduecers/reducers';
const store = createStore(rootReducer, applyMiddleware(logger));
export default store;
5-1. 스토어 연결
//src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './redux/store';
import App from '../src/containers/App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root')
);
serviceWorker.unregister();
7. socket 통신해보기!
드디어!
//server/chat_server.js
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const port = process.env.PORT || 3005 ;
server.listen(port, () => { console.log(`Listening on port ${port}`) });
io.on('connection', socket => {
console.log("연결된 socketID : ", socket.id);
io.to(socket.id).emit('my socket id',{socketId: socket.id});
socket.on('enter chatroom', () => {
console.log("누군가가 입장함");
socket.broadcast.emit('receive chat', {type: "alert", chat: "누군가가 입장하였습니다.", regDate: Date.now()});
})
socket.on('send chat', data => {
console.log(`${socket.id} : ${data.chat}`);
io.emit('receive chat', data);
})
socket.on('leave chatroom', data => {
console.log('leave chatroom ', data);
socket.broadcast.emit('receive chat', {type: "alert", chat: "누군가가 퇴장하였습니다.", regDate: Date.now()});
})
})
//containers/App.js
import { connect } from 'react-redux';
import * as action from '../redux/actions/actions';
import io from 'socket.io-client';
import App from '../../src/App';
const socket = io.connect("http://localhost:3005");
const socketSubscribe = dispatch => {
socket.on('my socket id', (data) => {
console.log('mySocketID : ' , data);
dispatch(action.mySocketId(data.socketId));
});
socket.on('receive chat', (data) => {
console.log("App.js Socket(receive chat) ", data);
dispatch(action.receiveChat(data));
});
}
const mapStateToProps = state => {
console.log("containers/App.js mapStateToProps ", state);
return state;
};
const mapDispatchToProps = dispatch => {
socketSubscribe(dispatch);
return {
enterChatroom: () => {
socket.emit('enter chatroom');
},
leaveChatroom: () => {
socket.emit('leave chatroom');
},
sendChat: (chat) => {
socket.emit('send chat', {type: "msg", socketId: socket.id, chat: chat, regDate: Date.now()});
},
clearChat: () => {
dispatch(action.clearChat());
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
//App.js
import React from 'react';
import {HashRouter as Router, Route} from 'react-router-dom';
import Home from './components/Home';
import Chat from './components/chat/Chat';
function App({chatReducer, mySocketId, enterChatroom, leaveChatroom, sendChat, clearChat}) {
console.log("src/App.js ", chatReducer);
return (
<Router>
<Route path="/" exact component={Home}></Route>
<Route path="/chat/:id"
render={props => <Chat chatReducer={chatReducer}
mySocketId={mySocketId}
leaveChatroom={leaveChatroom} enterChatroom={enterChatroom}
sendChat={sendChat}
clearChat={clearChat} />} />
</Router>
);
}
export default App;
'STUDY > React' 카테고리의 다른 글
React | 로그인 정보 없을 경우 로그인 페이지로 redirect하기 ( react-router, PrivateRoute, 로그인 권한 ) (6) | 2020.03.30 |
---|---|
React | React-Redux 비동기 통신(fetch / redux-thunk) (0) | 2020.03.19 |
React | 이미지 업로드 후 미리보기 (on Change / image preview) (0) | 2020.01.21 |
React | Node.js연동 파일 업로드 (+ multer) (2) | 2020.01.19 |
React | LifeCycle API (생명주기 메서드) (0) | 2020.01.15 |