こんにちは。KOUKIです。
今回は、Go言語とJavaScriptでチャットアプリを作成したいと思います。
以前、gRPCでチャットアプリを作りましたが、ターミナル上でしか動作しないので、今回は、WebSocketsを使ってブラウザ上で動かしてみます。
WebSocket API(WebSockets) は、ユーザーのブラウザーとサーバー間で対話的な通信セッションを開くことができる先進技術です。この API によって、サーバーにメッセージを送信したり、応答をサーバーにポーリングすることなく、イベント駆動型のレスポンスを受信したりすることができます。
MDN Web Docsより
また、少し長くなるので、今回は環境準備編としました。
作るもの
チャット記事まとめ
Go言語テンプレート
Webアプリケーションを立ち上げるテンプレートを以下の記事に記載しているので、よかったら参考にしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# これをコピペすればOK mkdir chat-websockets cd chat-websockets go mod init chat-websockets mkdir -p cmd/web mkdir -p internal/handlers mkdir html touch cmd/web/main.go touch cmd/web/routes.go touch html/home.jet touch internal/handlers/handlers.go mkdir domain touch domain/connect.go mkdir static touch static/scripts.js touch static/style.css |
テンプレート記事で作成したワークスペースにプラスして、domainフォルダを作成しています。
追加モジュール
以下のモジュールをインストールしてください。
1 2 3 |
go get github.com/CloudyKit/jet/v6 go get github.com/bmizerany/pat go get github.com/gorilla/websocket |
Webサーバーの構築
まずは、さっとWebサーバーを立てます。ここでは、細かい説明はしません。
handlers.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package handlers import ( "log" "net/http" "github.com/CloudyKit/jet/v6" ) var views = jet.NewSet( jet.NewOSFileSystemLoader("./html"), jet.InDevelopmentMode(), ) func Home(w http.ResponseWriter, r *http.Request) { err := renderPage(w, "home.jet", nil) if err != nil { log.Println(err) } } func renderPage(w http.ResponseWriter, tmpl string, data jet.VarMap) error { view, err := views.GetTemplate(tmpl) if err != nil { log.Println(err) return err } err = view.Execute(w, data, nil) if err != nil { log.Println(err) return err } return nil } |
routes.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package main import ( "chat-websockets/internal/handlers" "net/http" "github.com/bmizerany/pat" ) func routes() http.Handler { mux := pat.New() mux.Get("/", http.HandlerFunc(handlers.Home)) fileServer := http.FileServer(http.Dir("./static/")) mux.Get("/static/", http.StripPrefix("/static", fileServer)) return mux } |
main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package main import ( "log" "net/http" ) func main() { mux := routes() log.Println("Starting web server on port 8080") _ = http.ListenAndServe(":8080", mux) } |
チャットUIの作成
チャットのUIを作成します.
home.jet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog==" crossorigin="anonymous" /> <title>Cheat App</title> <link rel="stylesheet" href="/static/style.css" /> </head> <body> <div class="chat-container"> <div class="chat-header"> <label for="username">YOUR NAME</label> <input type="text" id="username" class="username" autocomplete="off" placeholder=":) selfnote"> </div> <div class="chat-body"> <ul id="message-list"> <li class="me">Sample</li> <li class="other">Sample</li> </ul> <div class="send-area"> <input type="text" id="message" class="message" autocomplete="off" placeholder="message..."> <button id="submit" class="submit"> <i class="far fa-paper-plane"></i> </button> </div> </div> </div> <div class="oneline-user-container"> <ul id="online-users"> <li>XXXX</li> </ul> </div> <script src="/static/scripts.js"></script> </body> </html> |
サンプルデータを入れておきましたが、liの部分は消しておいてください。
1 2 3 4 5 6 7 8 9 10 11 |
<ul id="message-list"> <li class="me">Sample</li> <li class="other">Sample</li> </ul> <div class="oneline-user-container"> <ul id="online-users"> <li>XXXX</li> </ul> </div> |
style.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
/* フォント */ @import url("https://fonts.googleapis.com/css2?family=Indie+Flower&display=swap"); * { /* ボックスサイズを算出 paddingとborderをwidthとheightに含める */ box-sizing: border-box; } body { background-color: #e6f7ec; display: flex; align-items: center; justify-content: center; height: 100vh; overflow: hidden; margin: 0; padding: 0; } .chat-container { width: 40%; height: 98%; } .chat-header { background: #8688f5; box-shadow: 0px 2px 10px rgba(122, 121, 119, 0.966); display: flex; flex-direction: column; text-align: center; justify-content: center; padding: 30px; height: 15%; border-radius: 10px; } .chat-header label { margin-bottom: 10px; color: #fff; font-size: 1.3rem; } .chat-header input { border: none; font-size: 1.3rem; font-family: inherit; padding: 5px; letter-spacing: 1px; text-align: center; } .chat-header input:focus { outline: none; } .chat-body { background-color: ivory; box-shadow: 0px 2px 10px rgba(122, 121, 119, 0.966); height: 75%; border-radius: 10px; } .chat-body ul { font-family: "Indie Flower", cursive; font-size: 1.1rem; list-style: none; padding: 2px; height: 90%; display: flex; flex-direction: column; overflow: scroll; } .chat-body ul li { width: 40%; padding: 15px; border-radius: 240px 15px 100px 15px / 15px 200px 15px 185px; border: none; } .chat-body ul li.me { margin: 10px auto 10px 10px; background-color: orange; color: #fff; } .chat-body ul li.other { /* 右寄せ */ margin: 10px 10px 10px auto; background-color: powderblue; text-align: right; } .send-area { position: relative; } .message { position: absolute; top: 0; left: 10px; width: 80%; height: 30px; margin-bottom: 10px; letter-spacing: 1px; } .message:focus { outline: none; } .submit { position: absolute; top: 0; right: 25px; width: 10%; font-weight: bold; height: 30px; border: none; border-radius: 50%; box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.4); color: #fff; background: rgb(248, 121, 163); } .submit:focus { outline: none; } .oneline-user-container { height: 100vh; width: 200px; } .oneline-user-container ul { font-family: "Indie Flower", cursive; list-style: none; color: #111111; } .oneline-user-container li { margin-bottom: 20px; padding: 10px; border: 30px; background: ivory; border-radius: 10px; } @media (max-width: 1200px) { .chat-header label { margin-bottom: 10px; font-size: 1rem; } .chat-header input { padding: 8px; } } @media (max-width: 560px) { .chat-header label { font-size: 0.9rem; } .chat-header input { padding: 3px; } } |
Webサーバー起動
以下のコマンドで、Webサーバーを起動しましょう。
1 2 |
$ go run cmd/web/*.go 2021/04/01 06:45:30 Starting web server on port 8080 |
サーバーが立ち上がったら「http://localhost:8080」にアクセスします。

こんな感じですね。
次回
次回は、WebSocketsのコネクションを確立する処理を実装していきます。お楽しみに!
コメントを残す
コメントを投稿するにはログインしてください。