こんにちは。KOUKIです。
とある企業で、Go言語を使ったWeb開発業務に従事しています。
本記事では、ハンズオン形式で掲示板アプリを実装していきます!
<目次>
作るもの
まとめ
ワークスペース
ワークスペースを準備してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
mkdir bulletin-board cd bulletin-board/ go mod init bulletin-board 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 mkdir -p internal/handlers mkdir html touch cmd/web/main.go touch cmd/web/routes.go touch Makefile |
参考記事
事前準備
Makefile
アプリケーション起動用のコマンドをMakefileに記述しておきます。Windowsの方は、コマンドプロンプト上で「go run cmd/web/*.go」コマンドを実行してください。
1 2 3 4 |
.PHONY: run run: go run cmd/web/*.go |
追加モジュール
go modファイルが存在するディレクトリ上で、以下のコマンドを実行してください。
1 2 3 |
go get github.com/CloudyKit/jet/v6 go get github.com/bmizerany/pat go get github.com/gorilla/websocket |
実装
掲示板アプリケーションは、サーバーサイドをGo言語で、フロントエンドをHTML & CSS & JavaScriptで実装していきます。
今回は、Webサーバーを構築し、掲示板のUIを作成するところまで作業を進めましょう。
Webサーバーの構築
Go言語で、Webサーバーを構築します。結構、簡単です。
handlerの作成
handelr.goには、HTTPリクエストのハンドリング処理を実装します。
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 |
// handlers.go package handlers import ( "log" "net/http" "github.com/CloudyKit/jet/v6" ) // Jetをセットアップ var views = jet.NewSet( // htmlフォルダを読み込む 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 } |
前頭に記述してある「jet.NewSet」は、CloudyKit/jetのテンプレートエンジンのメソッドです。
今回のようにHTMLファイルを読み込んで、ユーザーにページ情報を返したい場合は、このようなテンプレートエンジンが必要になります。
Go言語にはもともとtext/templateというテンプレートエンジンが組み込まれていますが、jetの方が使いやすいです。
HTTPリクエストを受け取り後、「home.jet」を読み取り、「view.Execute(w, data, nil)」処理にて呼び出し元にページ情報を返します。
ルートの設置
routes.goには、HTTPリクエストを受け取ったとき、どのhandlerに処理を渡すかを指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// routes.go package main import ( "bulletin-board/internal/handlers" "net/http" "github.com/bmizerany/pat" ) func routes() http.Handler { mux := pat.New() mux.Get("/", http.HandlerFunc(handlers.Home)) // staticファイルの配信設定 fileServer := http.FileServer(http.Dir("./static/")) mux.Get("/static/", http.StripPrefix("/static", fileServer)) return mux } |
handlerへのマッチャーには、bmizerany/patを使いました。
これで、ユーザーがルート(/)にアクセスした時、Home handlerが呼ばれるようになります。
また、JavaScriptやCSSなどのstaticファイルを配信できるように、「http.FileServer」の設定をしました。これにより、「/static」にアクセスした際、staticフォルダ配下のファイルが読み込まれるようになりました。
main関数の作成
続いて、main関数を作成します。ここには、Webサーバーの起動処理を実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// main.go package main import ( "log" "net/http" ) func main() { mux := routes() log.Println("Starting web server on port 8080") log.Fatal(http.ListenAndServe(":8080", mux)) } |
サンプルページ
テストのため、サンプルページをhome.jetファイルに書き込んでおきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<!-- home.jet --> <!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>Bulletin Board App</title> <link rel="stylesheet" href="/static/style.css" /> </head> <body> <h1>Welcome to our bulletin-board</h1> <script src="/static/scripts.js"></script> </body> </html> |
ここで注目していただきたいのが、cssとjavascriptの読み込み設定です。「/static/」と指定しているので、staticフォルダ配下のファイルを読み込めるます。
※staticフォルダは、routes.goにて配信元として指定したフォルダ
起動確認
以下のコマンドで、Webサーバーを起動しましょう。
1 2 3 4 5 |
# Mac/Linux make run # windows go run cmd/web/*.go |
「http://localhost:8080/」にアクセスすると、以下のページが表示されます。

掲示板UIの作成
サクッと掲示板UIを作成しましょう。
home.jetと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 |
<!-- home.jet --> <!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>Bulletin Board App</title> <link rel="stylesheet" href="/static/style.css" /> </head> <body> <div id="post-container" class="post-container"> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> <div class="post"> <p>XXXXXX</p> <small class="name">selfnote</small> </div> </div> <div class="form-area"> <input type="text" class="username" id="username" autocomplete="off" placeholder="Your Name"> <textarea name="post" id="post" cols="30" rows="10" autocomplete="off"></textarea> <button id="submit" class="submit">POST</button> </div> <script src="/static/scripts.js"></script> </body> </html> |
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 |
/* style.css */ @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap"); * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: "Roboto", sans-serif; background: url("https://images.unsplash.com/photo-1508768787810-6adc1f613514?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=e27f6661df21ed17ab5355b28af8df4e&auto=format&fit=crop&w=1350&q=80") no-repeat center center/cover; height: 100vh; overflow: hidden; position: relative; display: flex; align-items: center; justify-content: space-evenly; } body::before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.4); } .post-container { height: 100vh; width: 80vw; max-width: 560px; background-color: rgba(41, 39, 39, 0.6); overflow-y: scroll; z-index: 100; } .post { display: flex; flex-direction: column; justify-content: center; margin: 10px; background-color: rgba(41, 39, 39, 1); padding: 10px; } .post:nth-child(even) { align-items: flex-end; } .post p { color: #fff; } .post small { color: rgb(248, 197, 31); } .form-area { display: flex; flex-direction: column; z-index: 2; } .form-area input { display: inline-block; margin-bottom: 10px; font-size: 1.5rem; letter-spacing: 1.5px; text-align: center; border-radius: 5px; border: 0; box-shadow: 1px 2px 4px 2px rgba(247, 7, 7, 0.4); padding: 5px; width: 25vw; max-width: 500px; } .form-area input:focus { outline: none; } .form-area textarea { display: inline-block; margin-bottom: 10px; border-radius: 5px; border: 0; padding: 10px; letter-spacing: 1.3px; box-shadow: 1px 2px 4px 2px rgba(247, 7, 7, 0.4); } .form-area textarea:focus { outline: none; } .submit { border-radius: 5px; border: 0; font-size: 1rem; box-shadow: 1px 2px 4px 2px rgba(247, 7, 7, 0.4); padding: 10px; cursor: pointer; letter-spacing: 1.5px; background-color: rgba(41, 39, 39, 1); color: #fff; } .submit:focus { outline: none; } .submit:active { transform: scale(0.98); } |
画面を表示してみましょう。

テストデータが入ってますが、これらは使わないので削除しておいてください。
次回
次回は、WebSocketsのコネクション確立処理を実装したいと思います。
コメントを残す
コメントを投稿するにはログインしてください。