Golang+Reactでアプリを作成してみた【Realtime Chat App】

## Technology used
golang
goroutines
websockets
webserver
channels

Frontend
react


## backendを作成

①backendディレクトリとmain.jsを作成
```
# バックエンド用のディレクトリを作成
mkdir backend
touch main.go
cd backend
go mod init chatapplication
go get github.com/gorilla/websocket
go run main.go
```
②main.goを作成する

```backend/main.js
package main

import (
	customwebsocket "chatapplication/websocket"
	"log"
	"net/http"
)

func serverWs(pool *customwebsocket.Pool,w http.ResponseWriter, r *http.Request) {
    log.Println("This is working")
    conn, err := customwebsocket.Upgrade(w,r)
    if err != nil {
        log.Println(err)
        return
    }

    client := &customwebsocket.Client{
        Conn: conn,
        Pool: pool,
    }
    pool.Register <- client
    client.Read()
}

func setupRoutes(){
    log.Println("This is working")
    pool := customwebsocket.NewPool()
    go pool.Start()

    http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
        serverWs(pool, w, r)
    })
}

func main() {
    setupRoutes()
    http.ListenAndServe(":9000", nil)
};
```
③backendにwebsocketフォルダとwebsocket.goを作成する

```
mkdir websocket
touch websocket.go
```
```backend/websocket/websocket.go
package customwebsocket

import (
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    ReadBufferSize: 1024,
    WriteBufferSize: 1024,
}

func Upgrade(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error){
    upgrader.CheckOrigin = func(r *http.Request) bool { return true }

    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("websocket connection error:", err)
		return nil, err
    }
	return conn, nil
}
```
```
cd backend
go mod tidy
```
④backendにwebsocket/client.goを作成する

```backend/websocket/client.go
package customwebsocket

import (
	"fmt"
	"sync"

	"github.com/gorilla/websocket"
)

type Client struct{
	Conn * websocket.Conn
	Pool *Pool
	mu sync.Mutex
}

type Message struct {
    Type int `json:"type"`
    Body string `json:"body"`
}

func(c *Client) Read(){
	defer func ()  {
		c.Pool.Unregister <- c
		c.Conn.Close()
	}()

	for {
		msgType, msg, err := c.Conn.ReadMessage()
		if err != nil {
			fmt.Println(err)
					return
		}
		m := Message{Type: msgType, Body: string(msg)}
		c.Pool.Broadcast <- m
		fmt.Println("msg recived===>>>\n", m)
	}
}
```

⑤backendにwebsocket/pool.goを作成する

```backend/websocket/pool.go
package customwebsocket

import "fmt"

type Pool struct{
	Register chan *Client
	Unregister chan *Client
	Clients map [*Client] bool
	Broadcast chan Message
}

func NewPool() *Pool{return &Pool{
	Register: make(chan *Client),
	Unregister:  make(chan *Client),
	Clients: make(map [*Client] bool),
	Broadcast: make(chan Message),
}}

func(pool *Pool) Start(){
	for {
		select{
		case client := <-pool.Register:
			pool.Clients[client] = true
			fmt.Println("total connection ppol:- ", len(pool.Clients))
			for k, _ := range pool.Clients{
				fmt.Println(k)
				k.Conn.WriteJSON(Message{Type: 1, Body: "New User Joined"})
			}
		case client := <-pool.Unregister:
			delete(pool.Clients,client)
			fmt.Println("total connection ppol:- ", len(pool.Clients))
			for k, _ := range pool.Clients{
				fmt.Println(k)
				k.Conn.WriteJSON(Message{Type: 1, Body: "User Disconnected"})
			}
		case msg := <-pool.Broadcast:
			fmt.Println("broadcasting a message")
			for k, _ := range pool.Clients {
				if err := k.Conn.WriteJSON(msg); err != nil {
					fmt.Println(err)
					return
				}
			}
		}
	}
}
```

## Frontendを作成する

①frontendディレクトリ
```
mkdir frontend
cd frontend
npx create-react-app <プロジェクト>
```

②srcにapiフォルダとindex.jsを作成する

```src/api/index.js
var socket = new WebSocket('ws://localhost:9000/ws');

let connect = (cb) => {
  console.log('connecting');

  socket.onopen = () => {
    console.log('websocket connected successfully');
  };

  socket.onmessage = (msg) => {
    console.log('message from socket', msg);
    cb(msg);
  };
  socket.onclose = (event) => {
    console.log('websocket connected closed', event);
  };
  socket.onerror = (error) => {
    console.log('websocket error', error);
  };
};
let sendMsg = (msg) => {
  console.log('msg send:-', msg);
  socket.send(msg);
};

export { connect, sendMsg };

```

```
mkdir api
touch index.js
```

③srcにComponents/Header/Header.jsxを作成する

```src/Components/Header/Header.jsx
import React from 'react';
import './Header.scss';

const Header = () => {
  return (
    <div>
      <h2>Go chat application in react</h2>
    </div>
  );
};

export default Header;
```

④srcにComponents/Header/Header.scssを作成する

```src/Components/Header/Header.scss
.header {
  background-color: gray;
  color: #ffeda3;
  h2 {
    font-size: 5em;
    text-align: center;
  }
}
```

⑤srcにComponents/Header/index.jsを作成する

```src/Components/Header/index.js
import Header from './Header';

const index = () => {
  return <div>index</div>;
};

export default Header;
```

```
cd frontend
cd chat
npm i sass --save-dev
```

⑥srcにComponents/ChatInput/ChatInput.jsxを作成する

```src/Components/ChatInput/ChatInput.jsx
import React, { Component } from 'react';
import './ChatInput.scss';

class ChatInput extends Component {
  render() {
    return (
      <div className='ChatInput'>
        <input onKeyDown={this.props.send} placeholder='Enter a message...' />
      </div>
    );
  }
}

export default ChatInput;
```


⑦srcにComponents/ChatInput/ChatInput.scssを作成する

```
.ChatInput {
  width: 90%;
  display: block;
  margin: auto;

  input {
    padding: 10px;
    margin: 0;
    background-color: white;
    font-size: 15px;
    border: none;
    border-radius: 5px;
    border: 1px solid black;
    width: 98%;
  }
}
```

⑧srcにComponents/ChatInput/index.jsを作成する

```src/Components/ChatInput/index.js
import ChatInput from './ChatInput';

const index = () => {
  return <div>index</div>;
};

export default ChatInput;

```

⑨srcにComponents/Message/Message.jsxを作成する

```src/Components/Message/Message.jsx
import React, { Component } from 'react';
import './Message.scss';

class Message extends Component {
  constructor(props) {
    super(props);
    let temp = JSON.parse(this.props.message);
    this.state = { message: temp };
  }
  render() {
    return <div className='Message'>{this.state.message.body}</div>;
  }
}

export default Message;
```

⑩srcにComponents/Message/Message.scssを作成する

```src/Components/Message/Message.scss
.Message {
  display: block;
  background-color: lightblue;
  margin: 10px auto;
  padding: 10px 20px;
  border-radius: 5px;
  box-shadow: 0 5px 15px -5px black;
  clear: both;

  &.me {
    color: rgb(138, 131, 245);
    float: right;
  }
}
```

⑪srcにComponents/Message/index.jsを作成する

```src/Components/Message/index.js
import Message from './Message';

export default Message;
```

⑫srcにComponents/ChatHistory/ChatHistory.jsxを作成する

```src/Components/ChatHistory/ChatHistory.jsx
import React, { Component } from 'react';
import './ChatHistory.scss';
import Message from '../Message';

class ChatHistory extends Component {
  render() {
    const messages = this.props.ChatHistory.map((msg) => (
      <Message key={msg.timeStamp} message={msg.data} />
    ));
    console.log(messages);
    return (
      <div className='ChatHistory'>
        <h2>Chat History</h2>
        {messages}
      </div>
    );
  }
}

export default ChatHistory;
```

⑬srcにComponents/ChatHistory/ChatHistory.scssを作成する

```src/Components/ChatHistory/ChatHistory.scss
.ChatHistory {
  background-color: lightcoral;
  margin: 0;
  padding: 20px;
  h2 {
    margin: 0;
    padding: 0;
    color: white;
  }
}
```

⑭srcにComponents/ChatHistory/index.jsを作成する

```src/Components/ChatHistory/index.js
import ChatHistory from './ChatHistory';

export default ChatHistory;
```

⑮src/App.jsを編集する

```src/App.js
import Header from './Components/Header';
import ChatInput from './components/ChatInput';
import './App.css';
import React, { Component } from 'react';
import { connect, sendMsg } from './api';
import ChatHistory from './components/ChatHistory';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      chatHistory: [],
    };
  }

  componentDidMount() {
    connect((msg) => {
      console.log();
      this.setState((prevState) => ({
        chatHistory: [...prevState.chatHistory, msg],
      }));
      console.log(this.state);
    });
  }

  send(event) {
    if (event.keyCode === 30) {
      sendMsg(event.target.value);
      event.target.value = '';
    }
  }

  render() {
    return (
      <div className='App'>
        <Header />
        <ChatHistory chatHistory={this.state.chatHistory} />
        <ChatInput send={this.send} />
      </div>
    );
  }
}
export default App;
```




## 参考サイト
[GO + REACT Fullstack App - Chat Application PART- 1 IN HINDI](https://www.youtube.com/watch?v=pIP8YNgLvhs)
[GO + REACT Fullstack App - Chat Application PART- 2 IN HINDI](https://www.youtube.com/watch?v=eiu_91QdYNA)
[GO + REACT Fullstack App - Chat Application PART- 3 IN HINDI](https://www.youtube.com/watch?v=83MGCT7JdNI)
[GO + REACT Fullstack App - Chat Application PART- 4 IN HINDI](https://www.youtube.com/watch?v=Z7u4UtS9i5k)
[GO + REACT Fullstack App - Chat Application PART- 5 IN HINDI](https://www.youtube.com/watch?v=KeY1Yvpjs4o)
[GO + REACT Fullstack App - Chat Application PART- 6 IN HINDI](https://www.youtube.com/watch?v=3unRPGVrC1Q)
[GO + REACT Fullstack App - Chat Application PART- 7 IN HINDI](https://www.youtube.com/watch?v=oNZdfOWKpkY)
[GO + REACT Fullstack App - Chat Application PART- 8 IN HINDI](https://www.youtube.com/watch?v=mLLBGQ-jHss)
[GO + REACT Fullstack App - Chat Application PART- 9 IN HINDI](https://www.youtube.com/watch?v=2FWpJzxjujQ)