ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Chat App with ReactJS, Socket.io #1
    2018~2019/React.js 2018. 10. 7. 17:31

    ReactJS로 Chat App 만들기 #1

    (출처 : https://www.youtube.com/watch?v=84GXJANOYFw)


    1. create-react-app react-chat


    2. npm install socket.io uuid concurrently nodemon


    1) socket.io : 웹 소켓을 활용한 라이브러리.

    2) uuid : 범용 고유 식별자 (고유 식별 번호), RFC 4122스펙으로 정해져 있으며, 생성할 때 특별한 알고리즘을 이용하여 생성함.

    3) concurrently : command 여러 개 실행.

    4) nodemon : node.js에서 파일이 수정될 경우 자동으로 서버를 재시동함.


    3. package.json 수정


    "scripts": {
    "start": "concurrently 'npm run react' 'npm run server'",
    "react": "react-scripts start",
    "server": "nodemon src/server/index.js",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
    },


    4. react-chat/src/server/index.js 생성

    - 서버 생성 및 소켓 연결


    var app = require('http').createServer();
    var io = module.exports.io = require('socket.io')(app);

    const PORT = process.env.PORT || 8080;
    const SocketManager = require('./SocketManager');

    io.on('connection', SocketManager);

    app.listen(PORT, () => {
    console.log("Connected to port:"+PORT);
    });


    5. react-chat/src/Events.js 생성

    - 웹 소켓 이벤트 정의


    module.exports = {
    COMMUNITY_CHAT: "COMMUNITY_CHAT",
    USER_CONNECTED: "USER_CONNECTED",
    MESSAGE_RECIEVED: "MESSAGE_RECIEVED",
    MESSAGE_SENT: "MESSAGE_SENT",
    USER_DISCONNECTED: "USER_DISCONNECTED",
    TYPING: "TYPING",
    VERIFY_USER: "VERIFY_USER",
    LOGOUT: "LOGOUT",
    }


    6. react-chat/src/server/SocketManager.js 생성

    - 웹 소켓 이벤트 연결


    1) VERIFY_USER : 닉네임 중복 체크

    2) USER_CONNECTED : 유저 연결

    3) LOGOUT : 유저 연결 해제


    const io = require('.').io;
    const { VERIFY_USER, USER_CONNECTED, LOGOUT } = require('../Events');
    const { createUser, createMessage, createChat } = require('../Factories');
    const connectedUsers = { }

    module.exports = function(socket) {
    console.log("Socket ID :"+socket.id);

    socket.on(VERIFY_USER, (nickname, callback) => {
    if(isUser(connectedUsers, nickname)) {
    callback({isUser: true, user: null})
    }
    else {
    callback({isUser: false, user: createUser({name: nickname})})
    }
    });

    socket.on(USER_CONNECTED, (user) => {
    connectedUsers = addUser(connectedUsers, user);
    socket.user = user;
    console.log(connectedUsers);
    });
    }

    function addUser(userList, user) {
    let newList = Object.assign({}, userList);
    newList[user.name] = user;
    return newList;
    }

    function removeUser(userList, username) {
    let newList = Object.assign({}, userList);
    delete newList[username];
    return newList;
    }

    function isUser(userList, username) {
    return username in userList;
    }


    7. react-chat/src/server/Factories.js 생성

    - 객체 정의


    const uuidv4 = require('uuid/v4');

    const createUser = ({name = ""} = {}) => (
    {
    id: uuidv4(),
    name
    }
    )

    const createMessage = ({message = "", sender = ""} = {}) => (
    {
    id: uuidv4(),
    time: getTime(new Date(Date.now())),
    message,
    sender,
    }
    )

    const createChat = ({messages = [], name = "Community", users = []} = {}) => (
    {
    id: uuidv4(),
    name,
    messages,
    users,
    typingUsers: [],
    }
    )

    const getTime = (date) => {
    return `${date.getHours()}:${("0"+date.getMinutes()).slice(-2)}`;
    }

    module.exports = {
    createMessage,
    createChat,
    createUser
    }


    7. react-chat/src/App.js 수정

    - Chat App Layout 구현


    import React, { Component } from 'react';
    import Layout from './components/Layout';
    import './App.css';

    class App extends Component {
    render() {
    return (
    <Layout title="React Chat App"/>
    );
    }
    }

    export default App;


    7. react-chat/src/components/Layout.js 수정

    - 웹 소켓 연결 및 유저 등록


    import React, { Component } from 'react';
    import io from 'socket.io-client';
    import { USER_CONNECTED, LOGOUT } from '../Events';
    import LoginForm from './LoginForm';
    const socketURL = "http://localhost:8080";

    class Layout extends Component {

    constructor(props) {
    super(props);
    this.state = {
    socket: null,
    user: null,
    };
    }

    componentWillMount() {
    this.initSocket();
    }

    initSocket = () => {
    const socket = io(socketURL);
    socket.on('connect', () => {
    console.log('connected...');
    });
    this.setState({socket});
    }

    setUser = (user) => {
    const { socket } = this.state;
    socket.emit(USER_CONNECTED, user);
    this.setState({user});
    }

    logout = () => {
    const { socket } = this.state;
    socket.emit(LOGOUT);
    this.setState({
    user: null,
    });

    }

    render() {
    const { socket } = this.state;
    const { title } = this.props;
    return (
    <div className="container">
    {title}
    <LoginForm socket={socket} setUser={this.setUser} />
    </div>
    );
    }
    }

    export default Layout;


    8. react-chat/src/components/LoginForm.js 수정

    - 부모 컴포넌트(Layout.js)에서 전달받은 props(socket, setUser)를 통해 닉네임 중복 체크 후, 유저 등록


    import React, { Component } from 'react';
    import { VERIFY_USER } from '../Events';

    class LoginForm extends Component {

    constructor(props) {
    super(props);
    this.state = {
    nickname: "",
    error: "",
    };
    }

    setUser = ({user, isUser}) => {
    console.log(user, isUser);
    if(isUser) {
    this.setError("User name taken");
    }
    else {
    this.props.setUser(user);
    this.setError("");
    }
    }

    setError = (error) => {
    this.setState({
    error
    });
    }

    handleSubmit = (e) => {
    e.preventDefault();
    const { socket } = this.props;
    const { nickname } = this.state;

    socket.emit(VERIFY_USER, nickname, this.setUser);
    }

    handleChange = (e) => {
    this.setState({
    nickname: e.target.value,
    });
    }

    render() {
    const { nickname, error } = this.state;
    return (
    <div className="login">
    <form onSubmit={this.handleSubmit} className="login-form">
    <label htmlFor="nickname">
    <h2>Got a nickname?</h2>
    </label>
    <input type="text" ref={(input) => {
    this.textInput = input
    }} id="nickname" value={nickname} onChange={this.handleChange} placeholder={'UserName'}/>
    <div className="error">{error ? error : null}</div>
    </form>
    </div>
    );
    }
    }

    export default LoginForm;




Designed by Tistory.