カテゴリ: Go言語 更新日: 2025/12/12

Go言語のエラーハンドリング設計パターン集!現場で役立つ実践例

Go言語のエラーハンドリング設計パターン集!現場で役立つ実践例
Go言語のエラーハンドリング設計パターン集!現場で役立つ実践例

先生と生徒の会話形式で理解しよう

生徒

「Goでエラーをどう整理したらきれいに書けますか?」

先生

「Goではエラーを返す仕組みを使って、わかりやすく、再利用しやすく設計するのがポイントです。」

生徒

「具体的にはどんなパターンがありますか?」

先生

「エラー統一、ラップ(error wrapping)、共通処理への委譲などが定番になります。それぞれ見ていきましょう!」

1. エラー返却の統一パターン

1. エラー返却の統一パターン
1. エラー返却の統一パターン

Goでは、エラーは値として返す仕組みがあります。関数が失敗する可能性があるときは、戻り値の最後にerrorを置き、正常・異常で分けます。


func readConfig(path string) (string, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return "", err
    }
    return string(data), nil
}

このように、エラー処理が一貫していて読みやすい構造になります。慣れないうちは「value, err := …」の形を覚えると良いでしょう。

2. error wrapping(エラーを包む)パターン

2. error wrapping(エラーを包む)パターン
2. error wrapping(エラーを包む)パターン

処理の流れが複数段階の場合、どの段階でエラーが発生したか分かりやすくするには、エラーをラップ(包む)と良いです。Go 1.13以降はfmt.Errorf%wを使います。


func loadUser(id int) (*User, error) {
    user, err := db.Find(id)
    if err != nil {
        return nil, fmt.Errorf("ユーザー取得失敗 id=%d: %w", id, err)
    }
    return user, nil
}

このようにすると、エラーを伝えながら原因を保持でき、ログやデバッグ時に便利です。

3. 共通エラー処理を関数にまとめる

3. 共通エラー処理を関数にまとめる
3. 共通エラー処理を関数にまとめる

エラーのログ出力や把握を共通化したいときは、関数でまとめて呼ぶとコードがスッキリします。


func handleError(err error, context string) {
    log.Printf("エラー [%s]: %v", context, err)
}

func run() {
    data, err := readConfig("config.json")
    if err != nil {
        handleError(err, "設定読み込み")
        return
    }
    fmt.Println(data)
}

このパターンを使うと、どこでエラーがあっても同じ処理・ログ形式で管理できます。

4. カスタムエラー型による詳細情報の付加

4. カスタムエラー型による詳細情報の付加
4. カスタムエラー型による詳細情報の付加

より詳しい情報を伝えたいときは、自分専用のエラー型を作成し、必要情報を付加しましょう。


type MyError struct {
    Code int
    Msg  string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("[%d] %s", e.Code, e.Msg)
}

func doTask() error {
    return &MyError{Code: 42, Msg: "処理に失敗しました"}
}

起動時にif err, ok := err.(*MyError)のように型アサーションすれば、エラーの詳細な分岐処理も可能になります。

5. パターンを組み合わせた実践例

5. パターンを組み合わせた実践例
5. パターンを組み合わせた実践例

実際の現場では、上記パターンを組み合わせて使います。たとえば:


func process(id int) error {
    user, err := loadUser(id)
    if err != nil {
        return fmt.Errorf("process内 loadUserエラー: %w", err)
    }
    // 処理続行…
    return nil
}

func main() {
    if err := process(7); err != nil {
        handleError(err, "main process")
    }
}

この例では、ラップ、共通処理利用、呼び出し元での表示、すべてがまとまり、現場でよく使われる形です。

6. 次に進むためのポイント

6. 次に進むためのポイント
6. 次に進むためのポイント
  • 戻り値は「値, error」の形式で返す
  • エラー発生時は早期リターンして処理を止める
  • fmt.Errorf + %w でエラーをラップして流れを残す
  • handleError のように共通処理にまとめてログや通知を整理する
  • 必要に応じてカスタムエラー型で制御分岐を実装する

これらの基本パターンが揃っていれば、多くの実践環境で対応しやすくなります。

まとめ

まとめ
まとめ

Go言語のエラーハンドリングは、一見すると単純に「エラーを返すだけ」に見えますが、実際にはとても奥深い仕組みになっています。特に現場の開発では、コードの読みやすさや再利用性、そして障害時の原因追跡のしやすさが重要になるため、エラーの扱い方には一定の設計が求められます。本記事で紹介した「エラー返却の統一」「error wrapping」「共通処理化」「カスタムエラー型」「複合パターン活用」は、Go言語の開発現場で広く使われている考え方です。どれも特別なテクニックというより、Goが備えている仕組みを丁寧に組み合わせて活かす方法といえます。

特に、Goのエラーは「例外ではなく値として返す」という特徴があるため、開発者自身が「どう扱うべきか」を決める余地が大きく、その分、設計の工夫が品質に直結します。エラーの統一的な返し方を決めるだけでも読みやすさが大きく向上し、ラップを使えば処理の流れを追いやすくなり、共通処理を関数化すればメンテナンスが容易になります。また、複雑なエラーを扱う場合にはカスタムエラー型が非常に有効で、運用時のログ解析やユーザーへのメッセージ出し分けにも役立ちます。

さらに、Go言語ではエラー処理が明確にコードに表れるため、トラブルシューティングがしやすく、コードレビューでも意図を読み取りやすいメリットがあります。初心者のうちは「毎回 err を書くのが大変」と感じるかもしれませんが、慣れてくるとこの明示的なスタイルの良さがわかるはずです。実践的なプロジェクトでは、これらのエラーパターンを組み合わせて、より安全で安定した処理に仕上げていくことが求められます。

最後に、エラーハンドリングの理解を深めるための簡単なサンプルをもう一つ紹介します。これはラップと共通処理を組み合わせた小さな例ですが、日常的に使われる形です。


func readFile(path string) (string, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return "", fmt.Errorf("ファイル読み込み失敗: %w", err)
    }
    return string(data), nil
}

func main() {
    content, err := readFile("sample.txt")
    if err != nil {
        handleError(err, "main")
        return
    }
    fmt.Println("読み込み成功:", content)
}

このように、Goのエラーハンドリングはさまざまな設計パターンを組み合わせることで、より実践的で安全なコードを実装できます。エラーを恐れるのではなく、「どう設計すれば扱いやすくなるか」を考えながら進めるのがポイントです。

先生と生徒の振り返り会話

生徒

「先生、今日の内容でGoのエラーハンドリングがだいぶ理解できました!特に、エラーをラップすると原因が追いやすくなるのが印象に残っています。」

先生

「そうですね。ラップは現場でもとてもよく使われる考え方ですし、ログから原因を特定するのにも役立ちます。あと共通処理化も大事なポイントです。」

生徒

「共通化するとコードがスッキリして読みやすくなりますね。カスタムエラーも便利そうですけど、どんな場面で使うんですか?」

先生

「例えば、エラーごとにエラーコードを使い分けたいときや、ユーザーに分かりやすく伝えたいときですね。運用時のログ分析にもとても有効なんですよ。」

生徒

「なるほど…!今日は具体例が多かったので、現場でどんな風に使えるのかイメージしやすかったです。これから自分で書くときも気をつけてみます!」

先生

「はい、その調子です。エラー処理を丁寧に書けると開発者としてのレベルがぐっと上がりますから、ぜひ実践しながら身につけていきましょう。」

関連記事:
カテゴリの一覧へ
新着記事
New1
Kotlin
Kotlinのforループの基本!範囲・配列・コレクションの繰り返し処理
New2
Go言語
Go言語のスライスでappend時のメモリの仕組みを理解しよう!初心者でもわかる基礎解説
New3
Go言語
Go言語のスコープとは?ローカル変数・グローバル変数の違いと使い分け
New4
Go言語
Go言語のhttp.ResponseWriterとhttp.Requestの仕組みを理解しよう
人気記事
No.1
Java&Spring記事人気No1
Go言語
Go言語の関数パラメータ!値渡しと参照渡しの違いを理解しよう
No.2
Java&Spring記事人気No2
Swift
Swift Playgroundの使い方を完全解説!初心者に最適な学習環境の始め方
No.3
Java&Spring記事人気No3
Kotlin
Gradleファイル(build.gradle.kts)の書き方と役割をやさしく解説!Kotlin初心者向け完全ガイド
No.4
Java&Spring記事人気No4
Swift
Swiftで数値と文字列を相互変換!NumberFormatterで桁区切りや通貨表示をわかりやすく解説
No.5
Java&Spring記事人気No5
Kotlin
Kotlinの演算子一覧と使い方!算術・比較・論理演算子の基本を解説
No.6
Java&Spring記事人気No6
Swift
Swift開発環境の構築方法を徹底解説!Xcode・Windows・Linux対応
No.7
Java&Spring記事人気No7
Kotlin
Kotlinで画面遷移を実装しよう!初心者でもわかるIntentの使い方完全ガイド
No.8
Java&Spring記事人気No8
Swift
SwiftPMでのバージョン指定と互換性の考え方を初心者向けに徹底解説