Go
Goでembedを利用してファイルを取得する方法を学ぼう
Goではファイルを取得する方法はさまざまあります。 os.ReadFile()を使用してファイルを読み込む方法もあれば、embedパッケージを使用してファイルを取り込む方法もあります。 用途は少し異なりますが、embedパッケージを使用するとビルド時にファイルを含めることができるため、配布時に非常に便利です。 簡単な例として、次のようなtxtファイルを作成してみましょう。 hello.txtHello, World! それでは、embedパッケージを使用してこのファイルを取り込んでみましょう。 main.gopackage main import ( "fmt" _ "embed" ) //go:embed hello.txt var content string func main() { fmt.Println(content) } 上記のコードを実行すると、次のような結果が得られます。
2025年4月16日
Golangでフォームデータをボディに含めて送信する方法
作業をしていると、外部APIを使用しなければならなくなった。 一般的なJSONやXMLをやり取りするAPIとは異なり、フォームデータをやり取りする必要があった。著者の場合、JSPで扱ったことがあるが、現場ではほとんど扱ったことがなかったので、フォームデータ自体を使用したのは非常に長い時間前のことだった。また、Goではどのようにするのかわからなかった。 今回はこれらを調べながら整理してみようと思う。 フォームデータとは? フォームデータはHTMLフォームで使用されるデータ形式で、主にWebアプリケーションでクライアントがサーバーにデータを送信する時に使用される。フォームデータはキー・バリューのペアで構成されており、一般的に application/x-www-form-urlencoded または multipart/form-data 形式でエンコードされる。 application/x-www-form-urlencoded application/x-www-form-urlencoded は一般的なフォームデータ形式で、すべてのデータがURLエンコードされて送信される。この形式はテキストデータに適している。 一般的に次のような <form> タグの中に入れて使用する。 <form action="http://www.example.com" method="post"> <input type="text" name="name" value="John Doe"> <input type="text" name="age" value="30"> <input type="submit" value="Submit"> </form> webhook.siteで実際にデータを確認すると、ボディは次のように表示されるのを見ることができる。
2025年4月14日
AIを活用してHugoで全ての投稿を翻訳してみよう (feat. 私のブログが5か国語をサポートするようになった経緯)
課題 最近AIが発展し、業務にGeminiを利用するようになった。非定型データをこんなにも上手く扱う様子を見て、以前行っていた涙ぐましい作業が頭に浮かんできた。 さて、本題に入るが、昔から宿題のように感じていたことがある。それはブログの翻訳だ。過去に翻訳をするとブログを見る人が大幅に増えるという話を聞いたことがあり、以前はGoogle Translate APIを使って翻訳を試みようと思ったが、怠け心に負けてそのまま放置していた。 AI翻訳の分野もクオリティが非常に良くなってきたので、今回の機会に全ての投稿を翻訳してみるプロジェクトを始めることにした。そこで、この投稿では私がブログを翻訳することになった経緯、進め方、どのような機能があるのかを紹介したい。 CLIかWebサービスか 実は最初はWebサービスで作ろうと思っていた。フォルダをアップロードすると、自動的に翻訳されたファイルをダウンロードできるようにするものであった。 別途サーバーを維持する必要があり、設定なども別途管理しなければならないことが多く、欠点が多かった。 結局CLIを選択したのだが、Golangで実装したときにCLIが便利であったし(すでに多くの実装経験があるため…)、Github Actionsなどでも統合しやすいので、色々と有利な点が多かった。 後にはGithub Actionsでもリリースしなければならないな。(翻訳版はアーカイブに上げる必要があるかもしれないが…) 翻訳プロジェクトの開始 名前は適当に付けた。Hugoで作られたブログを翻訳するサービスなので、hugo-ai-translatorと名付けたが、今思うともっとGoらしい良い名前があったのではないかと思う…(hugo-trans、transifyなど、色々なネーミングが頭に浮かぶが、今のところは明確だと思う) もしおすすめの名前があれば教えてほしい…いつでも受け入れています まずはHugoのフォルダ構造を見てみた。 全てのフォルダ構造が同じわけではないが、ほとんどのHugoブログはcontentというフォルダの中にmdファイルを入れるところから始まる。 ほとんどがこのような構造をしている。 /content /some index.md _index.md これにmultilingualをサポートするには、次のようなフォルダ構造に変わる。
2025年3月6日
Hugo BlogでMermaidグラフをレンダリングしてみよう
現在私の技術ブログはHugoというテンプレートエンジンを使用しています。Hugoは静的サイトジェネレーターで、MarkdownファイルをHTMLに変換する役割を果たします。 私たちがよく使うマークダウンにはコードブロックというものがあります。 以下のGoコードもきれいに文法を強調して表示されます。 func main() { fmt.Println("Hello, World!") } Javaコードも同様にコードブロックを通して文法を強調して表示します。 public class Main { public static void main(String[] args) { System.out.println("Hello, World!"); } } } 実際、コードブロックはとても一般的な機能であり、多くのマークダウンでサポートされていますが、時にはダイアグラムを描く必要がある時もあります。
2025年3月6日
FSMとともに状態をより効率的に扱うために学ぶ Golang
状態が多すぎる! 業界で多くの経験を積むと、つまらないことの一つ一つにも状態があることに気付くようになります。 例えば、ユーザーのログイン状態、注文状態、支払い状態など、多くの状態が存在します。 これらの状態は一般的にコードやDBに保存されて管理され、状態に応じて異なるロジックを実行するために使用されます。 前提 RESTで扱うデータ自体もまた、Representational State Transferの略称で状態と関連していますが、この記事ではもう少し狭義の状態について扱います。 例えば、次のようなJSONレコードがあると仮定します。 { "name": "John", "age": 30, "state": "active" } RESTでは上記レコード全体を状態と見なしますが、この記事ではstateのような特定のフィールドを状態と見なし説明します。 もちろんFSMでも上記全レコードを状態と見なすことができますが、状態数に応じて非常に多くの分岐が発生するため、あまり適していません。 問題 一般的に状態を管理する際には、if-else文やswitch-case文を使って状態に応じて異なるロジックを実行するように実装します。
2025年2月22日
PRを作成する際にGithub Actionsを介してGolangテストを自動で実行し、レポートを生成してみよう
どこでも、どんな言語でも、テストのためにビルドやテストを自動化することは非常に重要といえます。 もちろんローカルで直接テストを実行してアップロードするようにすることもできますが、人が作業する際にはテストを実行せずにアップロードする場合も時々あるため、ローカルに依存しすぎるよりは、自動化をしておくと、長く使うためにも良く、保守にも費用が少なくて済むでしょう。 この記事では、Golangでテストのためのアクションを作成し、PRを作成する際に自動でテストを実行し、レポートを生成する方法について紹介します。 シナリオは以下のように設計しました。 Goのセットアップ Go Testの実行 テストレポートの生成 PRにコメントとしてレポートのリンクを残す テストコード Goコード math.gopackage math import "errors" var ( ErrDivideByZero = errors.New("cannot divide by zero") ) func Divide(a, b int) (int, error) { if b == 0 { return 0, ErrDivideByZero } return a / b, nil } このコードは Divide() という関数を実装したものです。単純に aをbで割る関数で、0で割ることは許可されていないため、エラーを返すようにしました。
2025年2月17日
GolangでPlaywrightを利用してクロールしてみよう
データを収集する方法はいくつかありますが、一番一般的な方法がクロール(Crawling)という技法を利用することです。 クロールとは直訳すると「這い回る」という意味です。ウェブクロールはウェブを這い回って情報を収集することだと考えればよいでしょう。 ツール Pythonで特に多く利用される理由は、Python言語の簡潔さもありますが、クロールに特化したライブラリが多いからです。よく知られたツールとしては、BeautifulSoup、Selenium、Scrapyなどがあります。 最近はPlaywrightというツールが登場していますが、一般的にはクロールよりもテスト自動化に多く使用されますが、クロールにも利用することができます。 単純にパーサーの役割を果たすBeautifulSoupとは異なり、Playwrightは実際のブラウザを基にブラウザ関連のアクションを制御できるため、JavaScriptを実行したり、SPA(Single Page Application)をクロールする際に有用に使用することができます。 このツールはNode.js、Python、Goなど、様々な言語をサポートしていますが、今回はGoでPlaywrightを使用してみようと考えています。 Playwrightのインストール GoでPlaywrightを利用するためには、まずplaywright-goというライブラリを利用しなければなりません。 以下のコマンドを使用してインストールできます。 go get github.com/playwright-community/playwright-go また、クロウラーを実行するブラウザの依存関係をインストールする必要があります。以下のコマンドを使用して簡単にインストールできます。 go run github.com/playwright-community/playwright-go/cmd/playwright@latest install --with-deps Playwrightを利用したクロールの実装 それでは実際にクロールをしてみましょう。
2025年2月14日
Golangでキャッシュなしでテストを実行する
Golangでテストを行うと、2回目以降はテスト速度が非常に速くなる。 1回目 ok github.com/my/package/test 126.752s 2回目 ok github.com/my/package/test (cached) 1回目は2分以上かかったが、2回目は計測する暇もなく速くテストが終了してしまった。
2025年2月14日
Golangでのlog.Fatal() vs panic()の違いについて
“この場合、log.Fatal()よりもpanic()を使用する方が良いと思います” 最近log.Fatal()を使用していたところ、上記のようなフィードバックを受けた。 え?log.Fatal()はログをもっとよく記録するんじゃないの?と思った。 恥ずかしい事実だが、最近になってようやくGolangでのlog.Fatal()とpanic()の違いを明確に知ることができたので、この機会にまとめてみようと思う。 log.Fatal()とpanic()の違い log.Fatal()とpanic()はどちらもプログラムを終了する関数だ。コードを見て動作を見てみよう。 package main import ( "log" "log/slog" ) func RunWithFatal() { log.Fatal("This is a fatal error") } func main() { RunWithFatal() slog.Info("This is not executed") } 上記のコードを実行すると次のような結果が得られる。
2025年2月11日
GolangでSingleflightを使って重複リクエストにも強いサーバーを作る
最近、会社が閉業することになり、しばらくフリーランスとして働くことにしました。 幸いにも優れた方に出会い、Goやウェブについて様々なスキルを学ぶことができました。その一つがsingleflightパッケージを使った改善でした。 Singleflightとは? 英語で直訳すると「単一飛行」という意味ですが、そんなに難しく考えず、一つの実行を保証するものと考えれば十分です。 例を見ればより簡単に理解できるでしょう。以下のコードを見てみましょう。 package main import ( "log/slog" "time" ) var counter int func DoSomething() { time.Sleep(5 * time.Second) counter++ slog.Info("result", "counter", counter) } func main() { for i := 0; i < 10; i++ { go DoSomething() } time.Sleep(10 * time.Second) } 2025/02/10 20:49:22 INFO result counter=3 2025/02/10 20:49:22 INFO result counter=4 2025/02/10 20:49:22 INFO result counter=10 2025/02/10 20:49:22 INFO result counter=5 2025/02/10 20:49:22 INFO result counter=2 2025/02/10 20:49:22 INFO result counter=6 2025/02/10 20:49:22 INFO result counter=7 2025/02/10 20:49:22 INFO result counter=9 2025/02/10 20:49:22 INFO result counter=1 2025/02/10 20:49:22 INFO result counter=8 当然の結果ですが、ゴルーチンなので順序は保証されませんが、カウンターは1から10に到達しました。
2025年2月10日
GolangでLombokを感じてみよう
Lombokとは? Lombokは、Javaでgetter、setterなどの繰り返しのコードを減らすためのライブラリです。 Go言語では特性上、Getter、Setter、コンストラクタの作成が非常に面倒です。 Javaにはこのような作業を自動で行ってくれるライブラリが存在します。 例えば、以下のような構造体にコンストラクタを作成すると仮定してみましょう。 type User struct { Name string Age int } おそらくほとんどの人が次のようにコンストラクタを作成するでしょう。 func NewUser(name string, age int) User { return User{ Name: name, Age: age, } } Javaではコンストラクタの生成を通常このように行います。
2023年10月24日
Type AliasとNamed Typeの違いについて学ぶ (Golang)
どちらもタイプ定義時に既存のタイプを再定義する方法であり、定義方法は以下のように定義します。 type MyStruct struct { Name string Age int } func(m MyStruct) Print(){ fmt.Println(m.Name, m.Age) } type MyStructNamed MyStruct // Named Type type MyStructAlias = MyStruct // Type Alias Type Alias type aliasの場合、既存のタイプに別名を指定する方式なので、既存タイプと同じメソッドを持っています。 package main func main() { var m MyStructAlias m.Name = "GiraffeWithCode" m.Age = 27 m.Print() // GiraffeWithCode 27 } Named Type Named Typeの場合、既存タイプと同じメソッドを持っていません。
2023年10月13日
Golangでcsvとしてエクスポートしたファイルでの文字化け対策
BOM ExcelはUTF-8エンコードされたCSVファイルを正しく読み取るために、ファイルのドキュメント前にBOMが必要です。 BOM: バイト順序マーク(Byte Order Mark, BOM)は、ユニコード文字U+FEFFバイト順序マークで、マジックナンバーとして文書の最前に追加しテキストを読むプログラムへ様々な情報を提供することができます。 コード内 Golangでcsvを使用する場合、次のように文字化けを防ぐことができます。 package main import ( "encoding/csv" "os" ) func main() { file, err := os.Create("test.csv") if err != nil { panic(err) } defer file.Close() // NOTE: UTF-8 BOM追加 file.WriteString("\xEF\xBB\xBF") writer := csv.NewWriter(file) defer writer.Flush() data := [][]string{ {"名前", "年齢", "住所"}, {"홍길동", "30", "서울"}, {"김영희", "25", "부산"}, } for _, value := range data { err := writer.Write(value) if err != nil { panic(err) } } } 参考 バイト順序マーク - ウィキペディア
2023年9月23日
GolangでのTemplateの使用 (Go Template)
Go Template Golangには、Go Templateという機能を活用して自分が指定したフォーマットに従って様々なデータをバインドしたり、操作した文字列として出力する機能があります。 text/template パッケージ テキストベースのテンプレートを処理するための二つの標準ライブラリパッケージで、定められたフォーマットや特定のロジックに従ってテキストを生成する場合によく使用されます。 text/templateというパッケージを使用してテキストを定められたフォーマットで出力する例は以下の通りです。 package main import ( "os" "log" "text/template" ) type Person struct { Name string Age int } func main() { t := template.New("hello") t, err = t.Parse("Hello {{.Name}}, you are {{.Age}} years old.\n") if err != nil { log.Error(err) } p := Person{Name: "GiraffeWithCode", Age: 27} t.Execute(os.Stdout, p) } コードからわかるように、Personという構造体の各フィールド Name, Age がテンプレート文字列 "Hello {{.Name}}, you are {{.Age}} years old.\n" の各要素にバインドされることが簡単に推測できます。
2023年9月23日