はじめに:Goを学び始めたけれど、この先が見えないあなたへ
「Goの基本文法は一通り理解した。簡単なCLIツールも作れるようになった。でも、そこから先どう進めばいいんだろう?」
Go言語を学習しているエンジニアの多くが、この壁にぶつかります。特に、Goの真骨頂とも言える 並行処理(Concurrency) と、実務で頻出する REST API開発 については、「なんとなくわかるけど、自信を持って書けない」という方が非常に多い領域です。
-
goroutineを起動したはいいけれど、いつ終わるのか制御できない -
channelの使い方がイマイチ腑に落ちない - REST APIを書いてみたけれど、エラーハンドリングやテストが雑になってしまう
この記事では、こうした悩みを解消するために Goの並行処理の基礎 と REST API開発の実践的なコツ を、コード例を交えながら解説していきます。読み終わる頃には、「なるほど、Goらしい書き方ってこういうことか」と一段深く理解できているはずです。
1. Goの並行処理:goroutineとchannelを正しく使う
1-1. goroutineは「軽量スレッド」
Goの並行処理の主役は goroutine です。go キーワードを関数呼び出しの前に付けるだけで、その関数を並行に実行できます。
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 関数終了時にカウンタを減らす
fmt.Printf("Worker %d 開始\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d 完了\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // カウンタを増やす
go worker(i, &wg) // goroutineを起動
}
wg.Wait() // すべてのgoroutineが終わるまで待つ
fmt.Println("すべての処理が完了しました")
}
ここでのポイントは sync.WaitGroup です。goroutine は起動したそばから独立して動くため、明示的に「終わるのを待つ」仕組みが必要になります。これを忘れると、main 関数が先に終了してしまい、goroutine の処理が途中で切れてしまいます。
1-2. channelでデータをやり取りする
Goの哲学に「Don't communicate by sharing memory; share memory by communicating.(メモリを共有して通信するのではなく、通信によってメモリを共有せよ)」という有名な言葉があります。この哲学を体現するのが channel です。
func main() {
ch := make(chan int, 3) // バッファ付きチャネル
go func() {
for i := 1; i <= 3; i++ {
ch <- i * 10 // チャネルに送信
}
close(ch) // 送信終了を通知
}()
for v := range ch { // チャネルから受信
fmt.Println("受信:", v)
}
}
1-3. context でキャンセル制御を行う
実務では「タイムアウトしたら処理を打ち切る」「親の処理が中断されたら子も止める」といった キャンセル制御 が不可欠です。ここで登場するのが context パッケージです。
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case <-time.After(3 * time.Second):
fmt.Println("処理完了")
case <-ctx.Done():
fmt.Println("タイムアウト:", ctx.Err())
}
context は特にREST APIやDB接続で頻繁に使う機能なので、必ず押さえておきましょう。
2. REST API開発のコツ:標準ライブラリで十分書ける
2-1. まずは net/http を理解する
GoにはGinやEchoなどの有名なWebフレームワークがありますが、まずは標準ライブラリ net/http を使いこなせるようになる ことが上達の近道です。
package main
import (
"encoding/json"
"log"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func getUser(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
user := User{ID: 1, Name: "Taro"}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(user); err != nil {
http.Error(w, "内部エラー", http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/user", getUser)
log.Println("Server started at :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
2-2. ハンドラー設計で意識したい3つのポイント
REST APIを書くとき、以下の3点を意識するとコード品質が一気に上がります。
- 単一責任にする:1つのハンドラーは1つのユースケースに絞る
- エラーは早期リターンで処理:ネストを深くしない
-
contextをハンドラーの引数として引き回す:r.Context()を使い、DB処理などに渡す
2-3. 並行処理を活かした外部API呼び出し
REST APIの中で複数の外部APIを叩く場面はよくあります。ここでこそ goroutine の出番です。
func fetchAll(ctx context.Context) (Result, error) {
var wg sync.WaitGroup
var user User
var orders []Order
var errUser, errOrders error
wg.Add(2)
go func() {
defer wg.Done()
user, errUser = fetchUser(ctx)
}()
go func() {
defer wg.Done()
orders, errOrders = fetchOrders(ctx)
}()
wg.Wait()
if errUser != nil { return Result{}, errUser }
if errOrders != nil { return Result{}, errOrders }
return Result{User: user, Orders: orders}, nil
}
順次実行なら合計 A + B 秒かかる処理も、並行化すれば max(A, B) 秒で済みます。レスポンス速度が劇的に改善する のは、Goが実務で選ばれる大きな理由の一つです。
より高度なパターンとしては、errgroup.Group を使うとエラーハンドリングとキャンセル制御をよりスマートに書けます。
3. まとめ:Goの中級への道筋
ここまで解説した内容を振り返っておきましょう。
- goroutine と WaitGroup で並行処理の基本を制御する
- channel でgoroutine間の安全なデータ受け渡しを行う
- context でタイムアウト・キャンセルを扱う
-
REST APIは
net/httpの理解が土台 になる - 外部API呼び出しなどI/O待ちの処理は並行化 することで大きな価値を生む
Goの並行処理とREST API開発は、この2つが組み合わさって初めて真価を発揮します。「文法はわかる」から「実務で使える」へ、そのステップアップの鍵はまさにここにあるのです。
次のステップ:手を動かしながら体系的に学びたい方へ
とはいえ、記事を読むだけでは「わかったつもり」で終わってしまうのも事実です。並行処理やREST APIは、実際に自分で書いて、動かして、失敗してみる ことで初めて身につきます。
もし「もっとドリル形式で数をこなしながら実力を固めたい」「テストコードまで含めて実践レベルに引き上げたい」という方には、次の講座が学習リソースの一つとして役立つかもしれません。
📘 Go言語中級:30問ドリルで学ぶ並行処理・REST API・テスト実践講座
- 30問のドリル形式 で並行処理・REST API・テストを段階的に習得
- 実務でつまずきやすいポイントを、手を動かしながら克服できる
- 中級者に必要な「テストを書く習慣」まで一気にキャッチアップ
書籍や公式ドキュメントで基礎を押さえたあとの アウトプット教材 としてちょうどよい構成になっています。「読むだけでは物足りない」というフェーズに来ている方は、ぜひ選択肢の一つとして覗いてみてください。