TECH PLAY

ニフティ株式会社

ニフティ株式会社 の技術ブログ

500

こんにちは。ニフティ株式会社の statiolake です。 最近プロダクトに Go を採用したこともあって、なんだかチーム内で Go に対する関心が高まっています。最近 Go 1.26 がリリースされたということで、チームで新機能を眺める会をしていました。 さて、今回のリリースにはいくつかのコア言語機能の変更が含まれているのですが、その中の一つに「ジェネリック型がその型パラメータリスト中で自分自身を参照できるようになった」という変更があります。 一見すると型としての表現力上がったように思える追加機能です。がしかし、考えれば考えるほど「本当にこれが必要な場面ってある?」「公式の例、自己参照なしでも同じことできそうじゃない?」という議論になりました。 そこでもう少し詳しく調べてみたところ、なかなか面白い内容だと感じたので記事にしてみます。 忙しい人向け 以下が、今のところの私の理解です。 Go 1.26 から、ジェネリック型の定義の中で自分自身を参照できるようになった ただし 公式リリースノートの Adder の例 は自己参照なし (単なる A any ) でも問題にならない 実際に自己参照が必要になるのは、 ジェネリックなインターフェースとカスタム型が相互に参照し合う 場合 それも型システムとして本質的に必要だったというよりは、Go のメソッドの制約をインターフェース側で回避するための workaround として機能する …ただ、あらかじめ逃げ道を作っておくのですが、私は Go や型理論のことをよく知っているわけではないので、完全に正しく理解できている自信はありません。 定義の中で自分自身を参照するとは まず、新機能を確認しましょう。 Go 1.26 のリリースノート には次のような例があります。 type Adder[A Adder[A]] interface { Add(A) A } func algo[A Adder[A]](x, y A) A { return x.Add(y) } ここで重要なのは type Adder[A Adder[A]] interface { ... } という部分です。 Adder というインターフェースの定義がまだ完了していない段階で、その型パラメータの制約として Adder 自身が登場しています。Go 1.25 以前では、このようにインターフェースの定義途中で自分自身を型パラメータの制約に使うことはコンパイルエラーになっていました。これが 1.26 で解禁された、というのが今回の変更です。 もやもや: 公式の Adder 例、自己参照なしでも書けないか? 正直に言います。最初に見たとき、「インターフェース側は A any にして、使う側で制約をかければ済むのでは?」と思いました。 type Adder[A any] interface { Add(A) A } func algo[A Adder[A]](x, y A) A { return x.Add(y) } 試してみると、これで普通に動きます。Go 1.25 でもバッチリです。 ではどういうときに本当に必要になるのでしょうか? 必要なのはどういうときか? それなりに考えましたが、自力では思いつけなかったため、調べました。するとそもそも実装されるきっかけになったと思しき Go の issue #68162 が見つかり、ここに答えがありました。 結論だけ簡単にお伝えすると、まず 型の定義側に型制約を書かざるを得ないケース が存在して、さらにその型制約が 相互再帰的に自分自身に返ってくる場合 に必要になるということでした。難しい。 まず、型の定義側に型制約を書かざるを得ないケースとはどういうことでしょうか? 通常 Go のメソッドはジェネリックになることができませんが、型変数を一切扱えないとなると、ジェネリックな構造体に対してメソッドを実装できないということになってしまい、困ります。そのためレシーバだけはジェネリックになれるようです。しかし、関数と違って その場で型制約を表現することができません 。そのため、メソッド内でレシーバのジェネリクスに対して何らかの要求をしたい場合には、構造体の定義時点でその制約を課しておく必要があるということでした。 1 次に、その型制約が相互再帰的に自分自身に返ってくるとはどういう時でしょうか? こちらについては、次の節から具体例で見ていきたいと思います。 必要なセットアップ まず、「比較可能な要素 ( Element ) のペア」を表す ElementPair という構造体を考えてみましょう。 type Element[E any] interface { Less(other E) bool } type ElementPair[E Element[E]] struct { First E Second E } func (ep ElementPair[E]) FirstIsSmaller() bool { return ep.First.Less(ep.Second) } ここで、 FirstIsSmaller() の中で ep.First.Less(...) を呼んでいます。すると E は Element である必要があります。ところが FirstIsSmaller がメソッドであるため、レシーバーである ElementPair[E] の E に対して E Element[E] という制約を 直接課すことはできません 。 ここでさきほどの議論の 1 つ目の前提が満たされ、 ElementPair には型定義の時点で E Element[E] という制約がつくことになりました。 ただし、ここまでは新機能の出番はありません。 相互再帰をすると問題が起きる 問題が起きるのは、 Element のメソッドが ElementPair を返したい場合です。 type Element[E Element[E]] interface { Less(other E) bool Zip(other E) ElementPair[E] // ← ここが相互参照のポイント } type ElementPair[E Element[E]] struct { First E Second E } func (ep ElementPair[E]) FirstIsSmaller() bool { return ep.First.Less(ep.Second) } Zip の戻り値として ElementPair[E] を書くためには、 ElementPair の型パラメータ制約である E Element[E] を満たす必要があります。そのため Element のインターフェース定義の時点で E が Element[E] を満たしていなければならない。 ここでさきほどの前提の 2 つ目が満たされます。結果、 type Element[E Element[E]] という自己参照制約が現れてしまいました。これが Go 1.26 で初めて書けるようになったものです。 相互参照の流れを整理するとこうなります。 ElementPair[E] は E Element[E] を要求する Element の中で ElementPair[E] を参照したい → E Element[E] が要求される type Element[E Element[E]] という自己参照が発生する 関数なら不要 ちなみに、メソッドではなく関数として書くのであれば、Go 1.25 でも問題ありませんでした。その場で制約が書けないのはメソッドに特有の制限だからです。 // インターフェースは E any にする type Element[E any] interface { Less(other E) bool Zip(other E) ElementPair[E] } // ElementPair も E any にする type ElementPair[E any] struct { First E Second E } // 関数なら利用側で制約を解決できる func FirstIsSmaller[E Element[E]](ep ElementPair[E]) bool { return ep.First.Less(ep.Second) } 制約の解決は関数の型パラメータ [E Element[E]] で行えるため、双方 any でよく、相互参照の問題が起きないのです。 まとめ Go 1.26 から、ジェネリック型の定義の中で自分自身を参照できるようになった ただし 公式リリースノートの Adder の例 は自己参照なし (単なる A any ) でも問題にならない 実際に自己参照が必要になるのは、 ジェネリックなインターフェースとカスタム型が相互に参照し合う 場合 それも型システムとして本質的に必要だったというよりは、Go のメソッドの制約をインターフェース側で回避するための workaround として機能する 普通のアプリケーションコードを書く分には出会わないまま過ごすかもしれませんが、Go のジェネリクスの仕様を深く理解する上では面白い題材でした。 まだまだわからないことが多いので、引き続き勉強していこうと思います。 なお、その制限が文法上の問題なのか言語の複雑性を増やさないための選択なのかは調べきれませんでした。公式 FAQ の こちら を見る分には文法上の制約っぽい説明がなされていますが、それだけかはわかりません。 ︎
アバター
はじめに はじめまして!カスタマーサポートグループサポートシステムチームの植田です。 プライベートでは、「旅行」「運動」「カラオケ」「勉強」、以外のことが好きです(インドア派です)。 お客様が自身の契約情報を確認する会員サポートページやカスタマーセンターのオペレータが使用する社内ツールの開発や運用を行っています。 直近ではカスタマーセンターのオペレータが使う社内ツールのAzureへのクラウド移行を担当しました。 これまでの経歴と転職のきっかけ 前職はCG業界で、CGプログラマーという仕事をしていました。 ただ、実際にCGを描いたり、ゴリゴリとプログラミングを書いたりする機会もなく、「自分のスキルを今後どう育てていけばいいのか……」とキャリアについて深く悩むようになっていました。 書けるのはpython,学部時代にちょろっとだけ触ったjava程度… そんな中でニフティの選考を受け、入社を決めた一番の理由は、「しっかりとした開発経験がない私でも、成長し活躍できるチャンスが十分にある」と感じたからです。 入社して感じたこと ギャップ:想像以上に「成長に貪欲」なカルチャー 入社前は、歴史ある企業ということもあり「保守的な雰囲気なのかな?」と勝手なイメージを抱いていました。しかし、実際に入社して良い意味で裏切られました。出会う人みんなが新しい技術や改善に対して前向きで、成長に貪欲なカルチャーが根付いています。 チームの雰囲気:温かく、熱量のあるコミュニケーション まったくの未経験に近い状態で入社した私に対しても、チームの皆さんは段階的に取り組めるタスクを用意してくださり、非常に温かく迎え入れてくれました。 一方で、会議になると次々と活発な意見が飛び交い、熱量に溢れています(自分も早く参加しなければ!)。 技術と業務:毎日が「はじめまして」の連続 前職では一人でPythonを書いていただけでした。しかし現在担当している、社内サービスの Azure へのクラウド移行では、 インフラ・環境構築: Terraform、Azure、Docker バックエンド・フロントエンド: Bash、SQLite、HTML / Jinja 開発プロセス: ペアプログラミング、コードレビュー などなど、はじめましての技術やプロセスが山のようにありました。覚えることは膨大で大変な部分もありますが、その分だけ「自分ができるようになっている」という成長実感も非常に大きいです。 今後の目標 短期的な目標 まずは、日々の業務でのさまざまな失敗や経験を通じて、エンジニアとしてのキャパシティを広げていきたいです。そのプロセスの中で、「自分はどんな作業が得意なのか」「どういう領域が好きなのか」といった、自分のエンジニアとしての特色を理解していきたいと考えています。 中・長期的な目標 ニフティには、多様な技術スタックを持つスペシャリストがたくさん在籍しており、その知見をオープンに共有し合う文化や習慣がしっかりと根付いています。 皆さんの知見を吸収して追いつけるよう努力しつつ、将来的には自分ならではの得意分野や役割を見つけ、「この分野なら任せて」と言えるような、発信する立場のエンジニアになりたいと思っています。 最後に 中途で入社すると、初めて触る技術やプロセスはもちろん、システム特有の業務知識、いわゆるドメイン知識(この言葉も入社してから知りました)など、キャッチアップすべきことが多くて不安になる場面もあると思います。 もちろんチームメンバーに相談することも大切ですし、頼りになるメンバーがニフティにはたくさんいます。ただ、ニフティには社内のナレッジが Notion にしっかりとまとまっており、さらに各種AIツールも積極的に活用できる環境が整っているため、「意外と何とかなる」環境だと感じています。 まだまだ未熟な私ですが、これからエンジニアとしてどんどん成長していきたいと思っています。「未経験のことが多くても挑戦してみたい!」「これからエンジニアとして本気で頑張っていきたい!」という方には、ニフティはとてもおすすめできる環境です。 最後まで読んでいただき、ありがとうございました。 皆さんと一緒に働ける日を楽しみにしています!
アバター
はじめに こんにちは。この記事はニフティの坂野とmoriです。この記事は共同執筆したものになります。 チームで開発をしていると、python,node.js等の実行環境やlinter,formatter等周辺ツールのバージョンを揃えたい、という場面は多いと思います。 そこでまず思いつくのがdevcontainerですが、ケースバイケースでオーバーエンジニアリングになりがちだと思っています。 やりたいのは「ツールのバージョンを揃える」だけなのに、コンテナ丸ごと用意するのは重すぎます。 Dockerfileやdevcontainer.jsonの構築・メンテナンスコストがかかる CI/CDで同じツールバージョンを使いたいとなると、GitHub Actions側でもコンテナビルドが必要になってさらに複雑化 ツール追加のたびにビルドし直し これらの問題は、 mise を使えばもっとシンプルに解決できます。 miseの強み mise はツールチェイン管理、環境変数管理、タスクランナーなど様々な機能を持つツールです。 以前はRTXという検索性の悪い名前でしたが、いつの間にか改名されていました。 mise.tomlというファイル1つで全部管理できます。 ローカル/CIで同じバージョン mise.tomlをリポジトリにコミットしておけば、ローカルでもGitHub Actionsでも同じツールバージョンが使えます。 CI側では jdx/mise-action を使うだけです。コンテナビルドは要りません。 TerraformのCIは例えばこんな感じで書けます。 # terraform-ci.yml steps: - uses: actions/checkout@v4 - uses: jdx/mise-action@v2 with: working_directory: terraform - run: mise check working-directory: terraform mise.tomlにツールもタスクも定義してあるので、CI側はmiseを入れて mise check するだけです。 devcontainerだとCI用のDockerfileを別途用意したり、コンテナレジストリの管理も必要になりますが、miseならtomlファイル1つで完結します。 コンテナ不要で構築コストが低い devcontainerは初回ビルドも再ビルドも待ち時間が発生します。 miseは mise install で必要なツールを直接インストールするだけなので、オーバーヘッドが小さいです。 新メンバーが入ってきたときも mise install だけで環境が揃うので、オンボーディングコストも低くなります。 ここからは、miseの各機能を紹介したあと、実際に弊社でどのように活用しているかを説明します。 Renovateでバージョンアップも自動化 Renovateはリポジトリ内の依存関係を自動検知し、バージョンアップのPRを自動で作成してくれるツールです。miseのmise managerにも対応しており、mise.toml内のツールバージョンを自動で検知してPRを作ってくれます。 前述の通り、mise.tomlはローカルとCIの両方で参照されるファイルです。つまり、Renovateがmise.tomlのバージョンを更新するだけで、ローカル環境とCIのツールバージョンが同時に更新されます。 弊社ではTerraformだけインフラへの影響が大きいので個別PRにし、それ以外のツール(tflint, trivy, gh cli等)はまとめて更新する設定にしています。 devcontainerだとDockerfile内のバージョン書き換え→イメージリビルドというフローが必要ですが、miseならtomlのバージョン文字列を書き換えるだけです。シンプルです。 miseの主な機能について ツール管理 様々なバックエンドに対応しており、一通りのツールがバージョン指定付きでインストールできます。 asdf aqua pipx npm cargo # npmバックエンドを使用した claude codeのインストール mise use -g npm:@anthropic-ai/claude-code # pipxバックエンドを使用した serenaのインストール mise use pipx:git+https://github.com/oraios/serena.git@v0.1.4 # 直接手に入る系(nvim, btop, uvなどもあります) mise use uv # ツールのアップデート mise upgrade ディレクトリごとのバージョン切り替え シェルの設定 をすると、ディレクトリごとにツールバージョンを切り替えられます。上位ディレクトリの設定は下位にも引き継がれます。 ~/hoge ❯ cat mise.toml [tools] uv = "latest" ~/hoge/fuga ❯ cat mise.toml [tools] uv = "0.8.1" ~/hoge/fuga ❯ uv --version uv 0.8.1 # fuga配下では0.8.1が使われる ~/hoge/piyo ❯ uv --version uv 0.8.23 # piyoではuv未定義だが、親のhogeの設定が効く 環境変数管理 ツール使用時やタスク実行時に使用される環境変数をmise.tomlに記述しておけます。 ディレクトリ下にいると通常のシェルでも呼び出せます。 [env] EDITOR = "code" mise.file = ".env" [tasks.hoge] env = { fuga = "piyo" } run = "echo $fuga" .envを読み込むこともできるので、認証情報などはそちらに分離するのがおすすめです。 タスクランナー mise.tomlに記述したタスクを mise run hoge で呼び出せます。 タスクを指定せず mise run するとTUIで選択できます。絞り込みも可能です。 ツールと同じく、上位ディレクトリのタスクも継承されます。 一例として、私が管理しているのTerraformリポジトリでは、fmt/lint/securityのチェックを全てmiseタスクで管理しています。 dir で実行ディレクトリを指定できます。DockerfileのWORKDIRと概ね同じです。 # terraform/mise.toml [tasks."fmt:check"] description = "Check Terraform formatting" dir = "cwd" run = "terraform fmt -check -recursive ." [tasks."lint:init"] description = "Initialize tflint plugins" dir = "cwd" run = "tflint --init --config=$MISE_PROJECT_ROOT/.tflint.hcl" [tasks."lint:check"] description = "Run tflint" dir = "cwd" depends = ["lint:init"] run = "tflint --recursive --config=$MISE_PROJECT_ROOT/.tflint.hcl" [tasks."security:check"] description = "Run trivy security scan" dir = "cwd" run = "trivy config --config=$MISE_PROJECT_ROOT/trivy.yaml ." おわりに 今回はmiseを使った開発環境のツール管理について紹介しました。参考になれば幸いです。
アバター
1. はじめに 弊社では入社一年目のエンジニアは全三期のOJTを通して部署を渡り歩き、業務や会社について知見を深めていくという制度があります。 OJTについての詳細は、私の同期が入社一年目の経験を基に記事を書いていますので、是非こちらをご覧ください! ニフティでの新卒一年目について そのOJTの第三期で、新システムへの移行に伴い旧システムの運用が停止したため、対象システムが動作していた環境を廃棄するための作業を行いました。 この記事では、システムの廃止で苦労した点、意識した点、学びを共有したいと思います。 2. 意識していた部分 「システムを廃止する」と聞くと一見単純に見えますが、実際には関連システムとの依存関係を一つずつ切り離し、必要なデータを保全し、ユーザー影響を最小化しながら進める必要があるため、想像以上に手順が多く、判断ポイントも多い作業でした。 特に、今回触れた環境は構築年代が古く資料が少ないため調査に手間がかかり、ネットワーク的にも隔離されている特殊な環境であったため、特有の制約があり苦労しました。 2-1. 順番が重要 廃止作業は、いきなりサーバを止めて消すのではなく、段階的に影響範囲を狭めていくのが安全です。 入口(ユーザアクセス)を別系統へ逃がす 周辺システムとの連携を止める 必要データをバックアップする ネットワーク的に遮断する(FWを閉じるなど) サービスを停止する リソースを削除する 2-2. 遮断の段階 今回一番意識したのは「一発で消さない」ことです。 例えばネットワーク遮断やサーバ停止は、どちらも使えなくするという意味では同じですが、復旧可能性と確認のしやすさが違います。 ここでいう「疎通だけ止める」は、FW(ファイアウォール)を閉じて外部から到達できない状態にする、といった対応です。メリットは、サーバ上のプロセスやデータを触らずに入口だけ塞げるため、切り戻しが必要になってもFWを戻すだけで復旧できる点です。 まず疎通だけ止める(FWを閉じるなどのネットワーク遮断) しばらく様子を見る 何も起きなければ削除に進む という順で進めると、万が一のときの切り戻しコストが下がります。 2-3. 「様子見」を挟む 古いシステムは依存関係がドキュメントに残っていないことが多く、停止後に思いがけない影響が出る可能性があります。 そのため、停止と削除の間に時間を置き、アラートや問い合わせなどの遅れて出る症状を拾えるようにし、切り戻しの手間とリスクを最小化しながら進めて行きました。 3. 実際の対応 システム廃止は、次の手順で進めました。 # 作業 1 新システムへのリダイレクト設定 2,3 周辺システムとの連携停止 4 バックアップ 5 FW遮断(疎通停止) 6 システム停止 7 リソース削除 3-1. リダイレクト設定 まずはじめに、旧システムへのアクセスを、新システムへリダイレクトする対応を行っていました。 事前に経路となるリンクを差し替える対応を行って頂いたのですが、念の為旧システムに来るアクセスを新システムにリダイレクトするようにしました。 Ansibleを用いて該当サーバで稼働するhttpdに設定を反映し、URLに付随するクエリパラメータにより遷移先が異なるため、動作確認は 3 パターン実施しました。 パターン 遷移先 用途 1 サービス終了ページ サービス終了済みの案内 2 新システム 新システムへのリダイレクト その他 新システム 例外処理 3-2. ファイル連携停止 関連システムとファイルの送受信でデータをやり取りしているバッチを停止しました。 この作業では他部署のシステムとの連携を解除するので、業務担当者との綿密な確認が必須でした。 手順としては、 先方でファイルの取り込みを停止 廃止するシステムでファイルの出力を停止 先方でファイルの出力を停止 廃止するシステムでファイルの取り込みを停止 の流れで行いました。 関連システムによってpull/pushの向きが違うなどの差がありましたが、基本的には取り込み処理停止→送信停止、の流れで行えば双方でのアラートを予防することができます。 3-3. 通信停止 上記とは別のシステムからのREST APIを使用した通信を切る対応を実施した。 こちらは部署内管理のものだったので構成の確認は比較的簡単にできたものの、開発経験がないPHPだったのと、ソースコードをコピペで作った関係で該当システムに無関係な記述が多量に含まれていて、必要な部分を抽出するのに手間取ってしまいました。 3-4. バックアップ 続いて関連情報のバックアップをしました。 バックアップ対象は色々考えられますが、今回は実際に動作していたソースコード(微妙にgitHub上のものと異なる)、他システムと授受していたファイル群、関連するログ、データベースのダンプ、実体をバックアップしました。 今回はサーバ上でバックアップ用ファイルを作成した後にAWS CLIを用いてバックアップを行いました。 なんとサーバ時刻の時刻がズレておりAWS CLI の認証が通らなかったため、作業前に sudo date -s で時刻合わせを行いました。普通であればNTPサーバの設定を行うところですが該当システムはもうすぐ廃棄するものであるため、dateコマンドで簡易的に時間をあわせるのみにしました。 # ソースコード tar czvf ./source.tar.gz {path_to_source} source # ログ tar czvf ./logs.tar.gz -C {path_to_log} logs # MySQLダンプ mysqldump {connection_info} > dump.sql # MySQL実体 # 実際にはmariadb sudo tar czvf ./mysql.tar.gz -C {path_to_mysql} mysqldb # httpdログ tar czvf ./httpd_logs.tar.gz -C /var/log httpd # dryrun で転送対象を確認してから本転送 aws s3 cp ./{backup_dir}/ s3://{bucket_name}/{backup_dir} \\ --recursive --dryrun --profile {profile_name} aws s3 cp ./{backup_dir}/ s3://{bucket_name}/{backup_dir} \\ --recursive --profile {profile_name} 3-5. FWを閉じる 続いてFWを閉じる対応をしました。 対象の環境に設定されているFWの設定を無効化することで、システムの停止をすることなく外部からの通信を遮断することができ、外部から見ると停止しているのと同じ状況を発生させることができます。 この対応を停止、削除の前に挟むことで切り戻しのコストを小さくしつつ、停止状況の再現ができました。 3-6. systemd unit停止 続いて本格的に停止を行っていきます。 停止対象は順番に、 zabbix-agent: システム状態の監視を行っている 事前にzabbixサーバから該当システムの監視を停止しておく必要があります。 keepalived: 主系/従系の切り替え用 httpd: php動作用 mariadb: phpのデータ保存に使用 です。 システム廃棄のための停止なので切り戻すことはありませんが、万が一のために順番を考えてsystemd unitを停止していきます。 停止したあと一日おいて問題が発生しないか様子見をします。 # 停止する順番を守る systemctl disable --now zabbix-agent.service systemctl disable --now keepalived.service systemctl disable --now httpd.service systemctl disable --now mariadb.service 3-7. リソース削除 最後にシステムを稼働させていたリソースを削除し、環境を完全に廃棄しました。 4. まとめ・振り返り 今回のシステム廃止では、単に停止するだけでなく、依存関係の切り離し、データ保全、利用者影響の最小化まで含めて段取り良く進める必要があり、想像以上に調整と確認が多い作業でした。 特に、構築年代が古く資料が乏しいことに加え、ネットワーク的に隔離された特殊環境だったため、調査と作業手順の確立に時間がかかりました。 システム停止に伴うトラブルを最小化するために詳細に調査を重ねた結果、想定外の対応範囲が増加し続け、当初の目標スケジュールを大幅に超過してしまいました。 また、周辺システム連携停止では、双方でアラートや業務影響が出ないように業務担当者と手順とタイミングを丁寧に合わせる必要があり、関係者調整の重要性を強く実感しました。 今回のレガシーシステムの調査、外部システム担当者との調整などの経験を本配属後の業務に活かしていきます。
アバター
はじめに こんにちは!新卒1年目のなべしまです。暖かくなってきましたね。 夏休みにはまだ早いですが、Terraformで自由研究を始めました!今回はTerraformでの試行錯誤を共有したいと思います。 筆者について 入社: 2025年4月(新卒1年目) 担当業務: 基幹システムグループ(ジョブローテ中) 接続サービス申込みに関連するシステム刷新を担当中 きっかけ 現在担当しているシステムでは、AWSを利用しており、リソース管理にはTerraformを活用しています。Terraformコードを書く際、先輩社員から「 -generate-config-out オプションを試してみてはどうか」と助言をもらいました。そこで、試行錯誤や調査の記録をブログにまとめることにしました。 既存リソースのIaC化 -generate-config-out オプションでは、 import ブロックを利用します。はじめに import ブロックについて触れ、 -generate-config-out オプションによるIaCを紹介します。 importブロック 従来は terraform import というCLIコマンドを直接実行する必要がありました。一方で、Terraform 1.5で導入された import ブロックを使うことで、「インポートすること自体」をIaCで定義できるようになりました。 import ブロックの書き方は非常にシンプルで、以下の2つの引数を指定します。これらは別のファイルにまとめて記述するのが一般的です。 # importブロックの記入例 import { to = aws_s3_bucket.my_bucket # Terraform上のリソースアドレス(インポート先) id = "my-actual-bucket-name" # クラウドプロバイダ上の実際のID(インポート元) } -generate-config-out オプション -generate-config-out は、Terraform 1.5から追加された terraform plan コマンドの新しいオプションです。主な仕様と特徴は以下の通りです。 IaCの自動生成 import ブロックで指定したリソースのうち、まだ設定ファイル上に resource ブロックが存在しないものを見つけると、クラウド上の実際の設定を読み取り、指定したファイル名でHCLコードを新規作成します。 コマンドの構文 terraform plan -generate-config-out=<出力先のファイル名>.tf のように、出力したいファイル名を指定して実行します。 既存ファイルの保持 指定したファイルが既に存在し、何らかのコードが記述されている場合、Terraformはエラーを返し、既存のコードを誤って上書きしないように保護します。 # リソースのインポートのコマンド例 terraform plan -generate-config-out=generate.tf 実際に書き起こしてみる 実際にS3バケットを作成し、IaCを書き起こしてみます。下図に示すような、S3バケットをAWSコンソールから作成します。 fig. IaC管理対象のS3バケット import ブロック用ファイルの準備をします。 # インポート用.tfファイル import { to = aws_s3_bucket.my_manual_bucket id = "aws-s3-sample-bucket-2026-03" } terraform plan を実行します。インポートされているログが出力されます。 # ターミナル上で実行 $ terraform plan -generate-config-out=generate.tf ... Terraform will perform the following actions: # aws_s3_bucket.my_manual_bucket will be imported # (config will be generated) resource "aws_s3_bucket" "my_manual_bucket" { acceleration_status = null arn = "arn:aws:s3:::aws-s3-sample-bucket-2026-03" bucket = "aws-s3-sample-bucket-2026-03" bucket_domain_name = "aws-s3-sample-bucket-2026-03.s3.amazonaws.com" bucket_prefix = null bucket_region = "ap-northeast-1" ... 出力された generate.tf を確認します。s3の設定が書き出されていることが確認できました。 # 以下はgenerate.tfの内容 # __generated__ by Terraform # Please review these resources and move them into your main configuration files. # __generated__ by Terraform from "aws-s3-sample-bucket-2026-03" resource "aws_s3_bucket" "my_manual_bucket" { bucket = "aws-s3-sample-bucket-2026-03" force_destroy = false object_lock_enabled = false region = "ap-northeast-1" tags = {} tags_all = {} } -generate-config-out の注意点 作業をする中で、私がハマったポイントを2点紹介します。 importブロックの id について import ブロックを書く際、インポート元のリソースを指定する id という項目があります。リソースの種類によって指定すべき値(フォーマット)が異なります。 S3バケットの場合: バケット名 例: aws-s3-sample-bucket-2026-03 ECSタスク定義の場合:ARN 例: arn:aws:ecs:us-east-1:012345678910:task-definition/mytaskfamily:123 リソースごとの正しい指定方法は、Terraform公式ドキュメント(Registry)の各リソースページにある、Importセクションを参照する必要があります。これは、Terraform 1.4 以前の import コマンドでも同様です。 resource ブロックについて import ブロックの to で指定したリソースが、すでにどこかの .tf ファイル内で少しでも定義されている場合、Terraformはコードの出力をスキップしてしまいます。 # 属性が空のリソースでも、`-generate-config-out`時にはファイル生成がスキップされてしまう resource "aws_s3_bucket" "my_manual_bucket" {} Terraformにおける「インポート」とは、コンソールなどから手動で作成した既存リソースを、Terraformの管理下(Stateファイル)に取り込む機能です。 -generate-config-out を使う際、すでに .tf ファイル内に該当の resource ブロックが少しでも書かれていると、Terraformは「インポート先のコードは用意されている」と判断してしまいます。 コードを自動生成させたい場合は、対象の resource ブロックを事前に定義しないようにしましょう。 有効な活用例 ここまで紹介した terraform plan -generate-config-out は、「既存リソースをあとからTerraform管理に移したいが、まずは現状の設定を安全にコード化して出発したい」という場面で特に役立ちます。 手動で作ったリソースの棚卸し 先にコンソールから作ってしまったリソースなどを、後からIaC化して管理したい場合 既存環境のIaC化の第一歩 既存システムを一気に書き直すのが難しい場合に、一部リソースをインポートしてコード生成し、差分を確認しながら段階的に移行 ただし、生成されたHCLはあくまで“たたき台”です。不要な属性の修正やモジュール化は、運用方針に合わせて必ず見直す必要がありそうです。 おわりに Terraformの terraform plan -generate-config-out は、 import ブロックと組み合わせることで、インポート作業をIaCとして残しつつ、既存リソースの設定をコードに起こす手助けをしてくれる便利な機能です。 今回の検証ではS3バケットを例にしましたが、リソースの種類によって id の形式が異なる点や、既存の resource 定義があると生成がスキップされる点など、実際に触ってみないと気づきづらい落とし穴もありました。 同じように試している方の参考になれば嬉しいです。 参考文献 Terraform公式ドキュメント: Import ブロック import block reference | Terraform | HashiCorp Developer Terraform公式ドキュメント: terraform plan -generate-config-out terraform plan command reference | Terraform | HashiCorp Developer Terraform Registry: 各リソースの Import セクション S3バケット: aws_s3_bucket | Resources | hashicorp/aws | Terraform | Terraform Registry ECSタスク定義: aws_ecs_task_definition | Resources | hashicorp/aws | Terraform | Terraform Registry
アバター
イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! 2025年、ニフティ社内でエンジニアハッカソン合宿が開催されました。 AIをテーマにした今回のハッカソンにて、優勝チームがアイディア創出に挑戦と学びの物語などについてをTech Talk#26のテーマとして開催しました。 今回のエンジニアハッカソンの概要としては、AIを企画から開発までを通しで行い、短期間で成果を出す目的で行われ、そこに中堅エンジニアが挑戦するというものです。 チームに分かれ激しい競争の中で勝ち抜いたその成果について、当日見ていない方も興味があれば是非 アーカイブ からご視聴いただければと思います。 エンジニアハッカソン合宿の様子はNIFTY engineeringにてブログ掲載されておりますので、よろしければこちらもご覧ください。 中堅エンジニアたちの激闘2日間 in 熱海 ~AIと共に~ CS教育のDX AIによる育成の効率化 現場が抱える課題に対し、AIを使用して解決するというアプローチで開発をしています。 開発にAIを使うだけでなく、AIを顧客役としたり、評価をさせるなど活用をしています。 また、対人ではなくAIを相手にすることで教育を受ける新人の心理的負担の軽減など、人間味ある課題解決を考えているところも興味深い観点かと思います。 技術スタックも紹介されており、似たような課題を感じている方は参考になるものと思います。 資料 AI 開発合宿を通して得た学び AIが速すぎて人間のレビューが間に合わないという問題もあり、速すぎるのもたいへんだなぁと感じる点もあり。 また「動作する」レベルから本格的なプロダクション移行時の品質基準をどう設定するかの難しさなどの課題があったようです。 AI開発により開発スピードや実装コスト削減を活かし、今後どのようにしていくのか。どんな気付きや学びがあったのかは是非アーカイブ動画もご覧いただければと思います。 最後に 次回のTech Talkは未定ではありますが、開催が決まりましたら connpass や X でお知らせいたしますので、よろしければ登録してお待ちいただけると幸いです。 アーカイブ(YouTube) 発表資料(Speaker Deck)
アバター
アイキャッチ はじめに こんにちは! 2025年新卒で、もうすぐ2年目の先輩になるエンジニアのパクパクです みなさん、「 クリーンアーキテクチャ(Clean Architecture) 」という言葉を聞いたことはありますか? OJTの最後にメール開発チームに配属されて、私もこのアーキテクチャにこれから触れていくことになりました。 この記事では、クリーンアーキテクチャを初めて触った新人エンジニアの視点で、概念を理解しつつ、実際のディレクトリにどう落とし込むかを順番に整理してみます。   1. 全部一つのディレクトリにまとめちゃダメですか? クリーンアーキテクチャがなぜ必要なのか、 おもちゃ作り にたとえて見てみます。   Before:ひとり工房 皆さんが「ひとりでおもちゃを作る工房」を運営していると想像してください。設計図を描いて、部品を作って、組み立てて、梱包して… 全部ひとりでやります。 最初はそれでも回ります。でも注文が増えると、だんだん問題が出てきます。 問題1:1か所直すと全部が揺らぐ 問題2:部品だけを単体で検査できない 問題3:材料の仕入れ先を変えにくい   コードにすると、だいたいこんな感じです。 def make_and_ship_toy(): # DBから部品データを取得 db = mysql.connect(host="localhost", user="root") parts = db.query("SELECT * FROM toy_parts") # 組み立て指示書を作成.. instructions = create_instructions(parts) # 倉庫に保存.... s3 = boto3.client("s3") s3.upload(instructions, "instructions.pdf") # 通知を送信...... smtp = smtplib.SMTP("example.com") smtp.send(notification)   たとえば mysql.connect を PostgreSQL に変えたら? db.query の戻り値 parts の型が変わり、本来 DB と無関係なはずの create_instructions(parts) まで修正が必要になります。   After:役割が分かれたおもちゃ工場 次は工場にアップグレードして、役割ごとにチームを分けてみましょう! Nano Banana生成画像 工場のチーム 役割 設計チーム 「車輪は4つ、色は赤」みたいなコアのルールを決める 製造チーム 設計チームのルールに沿って、作る手順を指揮する 材料調達チーム 外部の業者から部品を仕入れる 注文受付窓口 お客さまの注文を受けて、製造チームへ渡す こうやって分けると、さっきの3つの問題がきれいに解決します! 車輪の形を変えても → 設計チームだけ 直せばいい 車輪だけ単体検査できる → 設計ルールを 独立して テストできる 木材業者を変えても → 材料調達チームだけ 直せばいい(設計はそのまま)   これをプロジェクトのフォルダ構成に当てはめると、こうなります。 工場のチーム 業務のフォルダ 役割 設計チーム entities ビジネスルール 製造チーム usecases 処理の流れを指揮する 材料調達チーム gateways DB、S3、メールなど外部接続 注文受付窓口 apis 、 batches ユーザー操作や実行要求の受付   2. おもちゃ工場で見るクリーンアーキテクチャ 上のアーキテクチャを基にして、おもちゃ工場についての内容で画像を生成してみました。 Nano Banana生成画像   いちばん内側:きらきらコア(Entities) 工場の 核となる設計ルール です。 「車輪は4つで、色は赤」みたいに、誰が作ってもどこで作っても 絶対に変わらないルール です。 コードでは entities フォルダがこの役割を担当します。   2つ目の円:おもちゃをつくる手順(Use Cases) 工場の 作業手順書 です。 「1. 車輪を付ける → 2. 色を塗る → 3. 梱包する」みたいに、 具体的な手順の流れ を決めます。 コードでは usecases フォルダがこの役割です。 「部品を集めて → 組み立てて → 出荷する」みたいに、 処理の流れを指揮 します。   3つ目の円:つなぐための道具(Interface Adapters) 工場の 接続アダプタ です。大きく2種類あります。 どちらも「内側の世界」と「外側の世界」をつなぐ役割です。 入口 (注文受付窓口):ユーザーの要求を受けて工場の中へ渡す → apis 、 batches 出口 (材料調達ポータル):外から材料を持ってきて工場へ渡す → gateways   いちばん外側:外の世界の便利なもの(Frameworks & Drivers) 自分たちで作ったものではなく、 持ってきて使う便利な道具 です。 FastAPI(Webフレームワーク) SQLAlchemy(DB ORM)   依存は内側に向けてだけ! クリーンアーキテクチャでいちばん大事なルールは、これひとつです。 内側の円は、外側の円の存在を絶対に知らないこと! おもちゃ工場で言うと: 材料調達チーム(外側)が設計ルール(内側)を参照する → OK! 設計ルール(内側)が「A社の木材で作れ」と特定の業者(外側)を指名する → NO! 設計チームは「木材が必要」とだけ書いて、どの業者から持ってくるかは気にしません。 こうしておくと、業者をAからBに変えても、設計を変えずに済むからです! コードでも同じです。 entities (内側)は gateways (外側)を import しない usecases (内側)は「どんな倉庫に保存して」のような具体的な保存方法は知りません。ただ「倉庫に保存して」と依頼するだけです。   3. プロジェクトのフォルダ = 工場の区画図 では実際に、プロジェクトの app/ フォルダを見てみましょう。 ここで紹介するフォルダ名( entities 、 usecases など)はあくまで一例です。プロジェクトやチームによって命名は異なります。 app/ │ ├── entities/ コアのルール │ ├── toy_car.py ← おもちゃの車のルール │ ├── toy_robot.py ← おもちゃのロボットのルール │ └── ... │ ├── usecases/ 処理の流れ │ ├── gift_sets/ │ │ └── pack.py ← ギフトセットを梱包する流れ │ ├── toy_cars/ │ │ └── assemble.py ← おもちゃの車を組み立てる流れ │ └── orders/ │ └── cancel.py ← 注文キャンセルの流れ │ ├── apis/ 注文受付窓口 │ └── v1/ │ ├── toy_cars.py ← おもちゃの車の注文 │ └── orders.py ← 注文管理 ├── batches/ 指示書 │ └── pack_gift_set.py ← CLIコマンド │ ├── gateways/ 外部接続 │ ├── parts_db/ ← 部品データベース担当 │ ├── warehouse/ ← 倉庫(ファイル保存)担当 │ ├── delivery/ ← 配送(通知送信)担当 │ └── supplier/ ← 仕入れ先API担当 │ └── main.py   ポイント 1. gateways/ の下にサブフォルダがたくさんある! gateways は接続先が複数あるので、 接続先ごとにサブフォルダ を切っています。 gateways/ ├── parts_db/ ← 部品データベースから取得・保存 ├── warehouse/ ← 倉庫へアップロード・ダウンロード ├── delivery/ ← 配送(通知送信) └── supplier/ ← 仕入れ先API呼び出し   2. usecases/ の中も、業務ごとにフォルダがある! 手順書(Use Case)も種類ごとに分かれます。「ギフトセット梱包用」「おもちゃの車組み立て用」「注文キャンセル用」みたいな感じです。   動作の流れ おもちゃ工場には pack-gift-set という指示書があります。ギフトセットを組み立てて出荷する機能です。 実行すると、だいたいこんな順番で動きます。 [注文受付] [制作指示] batches/ → usecases/ CLIコマンド実行 処理の流れを指揮       ┌───────────────┴───────────────┐     [部品調達]           [完成品出荷]      gateways/           gateways/     DB参照・部品確認        配送通知・倉庫保存   担当者がCLIでコマンドを実行 → batches/ が注文を受付 usecases/ の手順書が「部品確認 → 組み立て → 出荷」の流れを指揮 gateways/ が実際にDBから部品を確認して、ギフトセットを組み立て、配送通知を送ります   おわりに 今回はクリーンアーキテクチャを おもちゃ工場 にたとえて整理してみました。みなさん、イメージできましたか? 私は難しい概念に出会うたびに、こうやって身近なものに置き換えて勉強しています。「これが工場だったら?」「これがレストランだったら?」みたいに考えると、急に難しかったものが少しずつ馴染んでくる気がするんです。 実はソフトウェアアーキテクチャには、クリーンアーキテクチャ以外にもいろいろあります。 レイヤードアーキテクチャ (Layered Architecture):層を上から下に積み重ねる構造 ヘキサゴナルアーキテクチャ (Hexagonal Architecture):ポートとアダプタで外部をつなぐ構造 オニオンアーキテクチャ (Onion Architecture):クリーンアーキテクチャに近い玉ねぎ構造 この記事でクリーンアーキテクチャに一度触れておくと、別のアーキテクチャに出会っても「これも役割分担の方法のひとつなんだな」と、少し自信が持てると思います。 読んでいただき、ありがとうございました   参考資料 The Clean Architecture — Robert C. Martin (Uncle Bob)  
アバター
はじめに はじめまして。2025年4月に新卒でニフティに入社した宮村です。 ニフティのエンジニア職には On-the-Job Training(OJT) という制度があり、私が入社した年度では入社後の約1年間で 3つの異なるチーム を経験しました。各期約3ヶ月、実際の業務に携わりながら技術とチームワークを学んでいく仕組みです。 この記事では、私がOJTで経験した3つのチームでの業務内容や、やりがい・苦労した点をエピソードベースでお伝えします。 「ニフティのOJTって実際どんな感じなの?」 という疑問を持つ方に、少しでもリアルな雰囲気が伝われば嬉しいです。 OJTの前に:新人研修(4月〜6月) OJTが始まる前に、約2ヶ月間の 新人研修 があります。研修は大きく3つのフェーズに分かれています。 共通研修(4月) まずはビジネス職と合同の共通研修からスタートします。社会人としての基礎やニフティの事業について学ぶ期間です。職種を問わず同期全員で受けるため、エンジニア以外の同期とも関係を築ける貴重な機会でした。 技術研修(5月) エンジニア職のみの技術研修に移ります。外部の研修会社による講義形式で、Web開発の基礎を体系的に学びます。HTML/CSS/JavaScriptといったフロントエンドの基礎から、Linuxやサーバの基本、バックエンド技術、コンテナ、テスト手法まで幅広くカバーされます。プログラミング経験の差に関わらず、全員が同じスタートラインに立てるよう設計されていました。 エンジニア定例(6月頭) 最後に、 社内のエンジニアが講師を務める研修 (エンジニア定例)を受けます。Git基礎、AWS基礎、生成AIなど、ニフティの現場で実際に使われている技術や手法を先輩エンジニアから直接学べます。外部研修で得た基礎知識を、ニフティの実務にどう活かすかという視点で深掘りしていく内容で、OJTに入る前の総仕上げとなりました。 OJTの基本的な流れ 各期の流れはおおむね共通しています。 目標設定 — 配属後、トレーナーや上長と面談し、その期で達成したい目標を決めます 開発業務 — スクラムなどの開発手法を通じて、実際のプロダクト開発に参加します 振り返り — 期の終わりに目標の達成度を振り返り、次期への引き継ぎを行います トレーナーの方が日々の相談相手としてついてくださるので、困ったときにすぐ聞ける環境が整っています。 一期:SSOチーム(6/12〜9/20) チームについて サービスインフラチームの うさかぴサブチーム に配属されました。名前から想像し辛いかもしれませんが、ニフティ全体で使われている認証・認可システム 「SSO」 やその他ユーザー管理に使用されるシステム を開発・運用するチームです。 SSOは全社共通の基盤であり、止まるとニフティの多くのサービスに影響が出ます。そのため、変更には慎重さが求められる環境でした。 使用技術 OpenID Connect(OIDC) Python(Django) AWS 主な業務内容 SSOをOIDCの標準仕様に近づける改修 @nifty 優待サービスの販路拡大に向けた改修 新規サービスにOIDC導入するための改修 やりがい 最もやりがいを感じたのは、 SSOをOpenID Connect(OIDC)の標準仕様に近づける改修 を行えたことです。 実は大学時代にOIDCを活用した研究をしていたため、この分野には馴染みがありました。スプリントのプランニングで標準化の方針が検討されていた際、開発を進める中で「この修正だけでは足りない箇所がある」ことに気づき、自ら追加の修正を提案・実装しました。 結果として、OIDCのライブラリを導入するだけでSSOを使えるようになり、プロダクトの利便性向上に貢献できました。 学生時代に学んだことが実務で活きた瞬間 は本当に嬉しかったです。 苦労した点 一方で、初めての配属ということもあり、 実務の開発プロセスを一から習得する 必要がありました。スクラム、テストコードの書き方、本番環境を止めずに品質を担保するための工夫など、学生時代には経験のなかったことばかりで最初は苦戦しました。 また、障害対応の場面に立ち会った際に自分の無力さを痛感したこともあります。当時は「一人で解決しなければ」という意識が強く、難しいタスクを抱え込みすぎてしまうこともありました。これは後の期で大きな学びに繋がります。 二期:ポータルチーム(9/21〜12/20) チームについて 第一開発チームの @niftyトップページを担当するサブチームに配属されました。 @niftyトップページ の開発・運用に加え、新規事業の開発も手がけるチームです。 サービスの企画担当者と密にコミュニケーションを取りながら進めるスタイルで、AWSをはじめとしたモダンな技術スタックを使った開発が特徴です。スクラム開発(2週間スプリント)を採用し、デイリースクラムでチーム全体の進捗を共有していました。 使用技術 microCMS Next.js AWS(Terraform) 主な業務内容 「@niftyトップページ」の開発・運用 新規事業のAWSインフラおよびフロントエンドの構築 やりがい・苦労した点 この期で最もやりがいと苦労を感じたのは、 未経験の状態からAWS環境の構築にゼロから挑戦したこと です。 当初はフロントエンド寄りの業務を想定していましたが、チームの状況や自分自身の希望もあり Terraform未経験の状態からインフラ構築 を任されることになりました。正直、最初は不安もありましたが、これが結果的に大きな成長のきっかけになります。 先輩方が過去に構築した環境を参考にしながら、VPC・DNS・CI/CDパイプラインの作成を進めました。ただ、今回の要件と過去の構成には差分があり、初めて触る技術の中でその差分を埋める作業には非常に苦労しました。 一期での反省を活かし、 困ったときは一人で抱え込まず、積極的にチームへ相談する ことを意識しました。先輩方の手厚いフォローのおかげで、最終的には新規事業のフロントエンドインフラをほぼ一人で構築し、動作確認まで完了できました。 また、一期でSSO(認証)の経験があったことが二期で活き、新規事業のSSO導入時に的確な助言ができたことも印象に残っています。 異なるチームでの経験が繋がる瞬間 は、OJTならではだと感じました。 三期:金剛チーム(12/21〜3/20) チームについて 入会システムチームの 金剛サブチーム に配属されました。代理店様が光回線やオプションサービスの入会に使用するシステムの、開発・運用を担当しています。 代理店様との接点が多く、社内外の複数部署との連携が求められる環境です。 使用技術 PHP(CakePHP) AWS(Terraform) 主な業務内容 「@nifty つなぎモバイル」をより多くの方に申し込めるよう、獲得代理店様向けの申込ツールを改修 サインアップシステムにクレカチェック機能を追加 光コラボ入会時のオプションサービス申し込みフォームのデザイン刷新 「ニフティ会員特別でんきプラン」申し込みシステムのAWS移行(Terraform) やりがい・苦労した点 三期では 複数のプロダクトを横断して改修する という、これまでにない経験をしました。 中でもオプションサービス申し込みフォームのデザイン刷新は、サービスの企画や制作を行うチーム、光コラボ申し込みシステムのチーム等と連携しながら進行するプロジェクトで、初めて経験する 多方面とのマルチパスな調整 に難しさを感じました。ただ、一期・二期で培った「相談すること」「周囲を巻き込むこと」の大切さを実践できている手応えがあります。 また、でんきシステムのAWS移行では、一期のAWS経験と二期のTerraform経験がそのまま活きました。OJTを通じて積み上げてきた技術が 三期で統合された ように感じ、自分自身の成長を実感できた瞬間でした。 OJT全体を通して 3チームの共通点 スクラム開発 — 三部署ともスクラムを採用しており、一度身につけた開発の型はどのチームでも活きました AIの活用推進 — 一期ではAI推進チームのメンバーがおり、二期ではKiroを使ったspec開発も実施されるなど、各チームで積極的にAIを取り入れています 人の温かさ — どのチームでも、わからないことがあれば時間を割いて丁寧に教えてくださる先輩方ばかりでした 技術の幅の広がり OJTを通じて触れた技術は、期ごとに大きく異なります。 一期 : Python / Django / OIDC 二期 : Next.js / microCMS / Terraform 三期 : PHP / CakePHP / Terraform バックエンド → フロント+インフラ → レガシー+モダンの混在環境と、 毎期まったく異なる技術スタックに触れられる のはOJTの大きな魅力です。 一番の成長:「相談する力」 振り返ると、OJTを通じた一番の成長は 技術力よりも「相談する力」 かもしれません。 一期では「一人でやろうとしすぎていた」という反省がありました。二期ではそれを意識的に改善し、積極的にチームへ相談するようにしました。そして三期では、トレーナーから「自走できる」と評価されるまでになりました。 一人で抱え込まないこと は、技術を学ぶことと同じくらい大切なスキルだと実感しています。 こうした成長を実現できたのは、 3つのチームを渡り歩きながら、毎回新しい環境で「相談する」経験を積み重ねられるニフティのOJTだからこそ だと強く感じています。チームが変わるたびに関係構築からやり直す大変さはありますが、その繰り返しこそが「相談する力」を本物にしてくれました。 ニフティに興味を持ってくれた方へ 私が入社した年度では、OJTを通じて約1年間で 3つの異なるチーム を経験しました。毎回新しい環境・新しい技術・新しい人間関係にゼロから飛び込むのは正直大変でしたが、だからこそ 圧倒的に視野が広がり、確かな成長を実感できた 1年間でした。 私が伝えたいことは3つです。 学生時代の経験は活きる場合がある — 私の場合はOIDCの研究経験が一期で即戦力になりました。どんな経験も無駄にはなりません 困ったら相談してほしい — ニフティには優しい先輩方がたくさんいます。一人で抱え込む必要はありません 毎期が新しいチャレンジ — 「できない」が「できる」に変わる瞬間を、OJTでは何度も味わえます この記事が、ニフティに興味を持ってくれた方にとって、少しでも働くイメージを持つきっかけになれば幸いです。
アバター
ニフティには所属部署での業務のほかに、有志による社内活動が存在します。もちろん強制ではなく、それぞれが興味のある分野について、自主的に活動しています。なかには会社公認のもと予算がつき、社内業務に貢献しているケースも。業務とは別のやりがいや、自分の専門外の知見を得られることが、一つのモチベーションになっています。その一つが、オンラインサポートチーム。オンライン会議や社内イベントなど、配信関連のクオリティを向上させる目的で発足し、現在は10名ほどで活動しています。 前編ではチームのメンバーに、具体的な活動内容について語ってもらいました。後編ではチーム外活動を行うモチベーションやメリット、今後やってみたいことなどを聞いていきます。 チーム外活動の魅力は、「自分が興味のある分野」で会社に貢献できること みなさんが業務外の活動であるオンラインサポート(以下、オンサポ)に取り組む、一番のモチベーションを教えてください。 Sさん 私はやはり、好きな機材を触れるのが大きいですね。カメラだけじゃなく、大きめの照明やビデオスイッチャーなど、個人の趣味レベルではなかなか扱わないものを使って試行錯誤できるのが楽しいです。 それからオンサポには、たとえば Sさん だったら音響関係、 Aさん だったら動画編集など、自分がそれまで知らなかった分野に強い人たちも集まっていて、色んなことを吸収できるのも大きいですね。それを吸収したからといって自分のキャリアに役立つかどうかは分かりませんが、間違いなく人生を豊かにしてくれると思います。 Aさん 似た答えになってしまいますが、私もガジェットをいじったり、配信周りのソフトウェアを調整したりするのが好きなので、会社がそういう人材を求めているなら手を挙げてみようと。最近はオンサポの「チーム運営」を任されていて、それもいい経験になっています。本業では部署移動したばかりで、組織の動きまで考える余裕はなかなか持てませんが、そのぶんオンサポで経験できているのは自分のキャリアという観点でも大きいのかなと思いますね。 Sさん 自分は普段は社内プラットフォームチームという部署にいて、ニフティの従業員が使う社内ツールなどの運用を担っています。従業員が“お客様”という点は、オンサポも同じ。ユーザーがすぐ近くにいて、反応や感想を直接もらえるのはモチベーションにつながっています。 また、オンサポでは本業ではなかなか発揮する機会のない知見やスキルを活かして、会社に貢献することができます。それもやりがいの一つですね。 ちなみに、オンサポチームの活動と所属部署での業務の割合は、どのように定められていますか? Sさん 明確なルールが定められているわけではなく、メンバー各自が所属部署の上長と調整してオンサポでの活動時間を確保しています。僕の場合はなんとなく「業務時間全体の3分の1くらいまで」ということで、上司の承認をもらっている形ですね。いずれにせよ、本業に支障が出ない範囲内での活動に止めるという認識はみんな持っていると思います。 上長の方は、チーム外活動に対しても理解があると。 Sさん 僕の上長に限らず、チーム外活動に対して否定的な人は基本的にいないという認識です。もちろん、本業が忙しくなって離脱していく人はいますが、最初から「やめてほしい」と言われることはまずないと思います。 オンサポ以外にも、さまざまなチーム外活動が発足 オンサポ以外にも、こうしたチーム外活動というのは盛んに行われているのでしょうか? Aさん そうですね。私はオンサポ以外でいうと、「エンジニアリング可視化プロジェクト」という活動にも参加しています。ニフティには数多くのシステムやプロダクトがあり、関わる人数も膨大なため、各エンジニアがどのサービスにどれくらいの時間をかけているのかが把握しにくいという課題がありました。 また、そのサービスに精通している人が何人いるのか、サービス自体がどんな状態になっているのかも、より正確に把握できたほうがいいよねと。そこで、そうした様々なパラメーターやエンジニアリングの要素を見える化して、チーム体制やサービス自体の改善につなげるのがエンジニアリング可視化プロジェクトです。 Sさん 僕の場合はオンサポ以外に、採用ブランディングワーキンググループでも活動を行っています。採用にまつわるブランディングを行うことで、求職者にニフティのことを知ってもらおうというものです。 複数のチーム外活動を兼務するメリットというと、どんなことが思い浮かびますか? Aさん まずは部署をまたいで色んな人が集まっているので、顔が広くなること。それから自分が所属しているチーム以外の文化や考え方、手法を知れることです。開発のアプローチ一つとっても結構チームによって異なるので、エンジニアリング可視化プロジェクトで実際にソフトウェアを作る時にも、他部署の先輩のやり方を学べるのはとても大きいですね。それを自分のチームに持ち帰ることもあって、本業にもいい効果が生まれていると感じます。 みなさんが今後、オンサポチームでやってみたいこと、あるいは、他に興味のあるチーム外活動があれば教えてください。 Sさん 私はとりあえず、オンサポの活動を続けていきたいです。これまでさまざまなイベントなどで試行錯誤を重ねてきて、今の環境下ではベストに近いクオリティの配信ができるようになりましたが、会場や配信場所が変わってもこれを維持できるように色んな構成を試してみたいと思っています。どんな環境でも高いレベルの配信を実現できるチームになりたいですね。 Sさん まずオンサポに関しては、2025年は Sさん が「 InnerSource Gathering Tokyo 2025 」という、インナーソースに関するナレッジの創出と共有に特化したコミュニティのイベントに、配信機器スタッフとして参加しています。今後もニフティが参加する外部イベントの配信に積極的に関わることで、ニフティとしてのブランディングにつなげていきたいという思いはありますね。それから、もう一つの採用ワーキンググループでも、「採用につなげる」「社内の知名度を上げる」など、より具体的な成果を出していきたいです。 Aさん Sくん が言うように、オンサポとして現在の環境下での配信クオリティの向上については、わりとやり切ったと思っています。ただ、このまま現状維持を目指すだけだと、モチベーションという意味でも活動を続けていくのは難しい。我々が持つ「配信もできるエンジニア」という特性を活かして、何か新しいことを見つけていかなくてはいけません。たとえば、先ほど Sくん が言ってくれた技術コミュニティのイベントに協力して、ニフティのエンジニアの外部露出だったり、ニフティ自体の知名度向上だったりに貢献していきたいです。 個々の「やりたい」という思いを尊重してくれる会社 それでは最後に、みなさんが感じる「ニフティの良いところ」を教えてください。 Aさん 先ほど Sくん も少し話していましたが、ニフティには個人が「やりたいこと」を尊重してくれる文化があります。有志によるチーム外活動がこれだけ盛んなのも、会社全体にチャレンジしやすい雰囲気があるから。少なくとも、会社をよくしようという思いで新しいことを始めようという人を否定する上長はいないので、そこはニフティの良さの一つですね。 Sさん Aさん に同感です。私はオンサポには途中参加なのですが、メンバーを募集していた時期がちょうど、本業がとても忙しい時期だったんです。ただ、それでも参加したいという思いがあり上司に相談したところ、「(本業の)今のヤマを超えたら本格的に活動を始めるとして、とりあえず応募しておいたら」と提案してくれました。上司からすると「この忙しい時期に何を言い出すんだ」と難色を示しても無理はないのですが、完全にダメとは言わない。メンバーの「やりたい」という思いを汲んで、いい方向に導いてくれる人が多いように感じます。 Sさん そもそも、そういったことを相談しやすい風通しの良さもありますよね。上司だからと変に遠慮をすることもなく、本音で話すことができます。チーム外活動もそうですし、たとえば今の部署では経験できない仕事や経験を積みたいと思った時に、部署異動の相談なども普通にできますから。本当に自分がやりたいことベースで、会社生活を送れるのはニフティの良さだと思います。 前編もご覧ください! 今回はニフティのオンラインサポートチームのインタビューの様子をお届けしました。あわせて前編もご覧ください。 【インタビュー】本業外の社内活動も、真剣だから面白い。配信における映像や音響のクオリティ向上に取り組む“オンサポ”チームの活動【ニフティ オンラインサポートチーム前編】 ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! このインタビューに関する求人情報/ブログ記事 ニフティ株式会社 求人情報
アバター
はじめに こんにちは。ニフティ株式会社の高垣と申します。 私が所属しているチームでは、会員様向けのお問い合わせに対応するコールセンターの業務改善にAIを活用しています。その中で、Amazon BedrockのLLMを呼び出すLambdaを実装する際に 「LLMの出力を安定してJSON形式で受け取りたい」 という課題にぶつかりました。 本記事では、この課題を Converse APIのTool Use で解決した方法をご紹介します。BedrockでLLMの出力を構造化データとして扱いたい方の参考になれば幸いです。 簡単な業務背景 弊社のコールセンターでは、お電話をいただいた際にお客様がニフティのご契約者かどうかを確認する「本人確認」のステップがあります。これにAIを活用することで、本人確認にかかる時間を短縮し、お客様がよりスムーズにお問い合わせできるのではないかというアイデアが生まれました。私はこの取り組みの中で、Amazon Bedrockを呼び出すLambdaの作成を担当しました。Bedrockから受け取った結果を後続の処理にJSON形式で渡す必要があったのですが、ここで課題が発生しました。 invoke_model の課題:LLMの出力が文字列になる 通常、Python(boto3)のLambdaからBedrockを呼び出す際は invoke_model がよく使われます。しかし、この方法ではLLMからの回答が「文字列(String)」として返ってきます。 LLMの特性上、出力は確率に基づいて生成されるため、たとえプロンプトでJSONを返すように指示しても、常に期待通りのデータ構造になるとは限りません。その結果、後続の処理でパースに失敗し、エラーを招くリスクがありました。 Converse APIのTool Useによる解決 そこで今回は、この不確実性を排除するために Converse API を採用しました。Converse APIを活用することで、LLMからの出力を安定して「JSON形式」で受け取れるようになり、後続処理への連携が非常にスムーズになりました。 Converse APIのドキュメント 具体的には、 toolConfig の tools に期待するJSONの形式を定義し、Converse APIでBedrockを呼び出す際にtoolを指定します。 以下にサンプルコードを載せます。 import boto3 bedrock_client = boto3.client("bedrock-runtime") #tool_listの定義 tool_list = [ { "toolSpec": { "name": "parse_json", "description": "JSONにパースする", "inputSchema": { "json": { "type": "object", "properties": { "A": {"type": "number", "description": "Aの説明"}, "B": {"type": "string", "description": "Bの説明"}, }, "required": ["A", "B"], # レスポンス時に必須なもの } }, } } ] # Bedrockを呼び出し response = bedrock_client.converse( modelId=MODEL_ID, #モデルIDを指定(anthropic.claude-3-sonnet-20240229-v1:0など) toolConfig={"tools": tool_list, "toolChoice": {"tool": {"name": "parse_json"}}}, messages=[ { "role": "user", # 送信者のロール "content": [{"text": user_prompt}], # ユーザーからの入力プロンプト } ], system=[{"text": system_prompt}], # システムプロンプトの設定 ) このサンプルコードでは {A: 1, B: "test"} のような形式のJSONレスポンスが返されます。 tool_list の inputSchema で定義したスキーマに沿って、LLMの出力が構造化されます。 レスポンスからJSONデータを取り出すには、以下のようにします。 # レスポンスのcontentリストからtoolUseブロックを探して取得する json_result = None for block in response["output"]["message"]["content"]: if "toolUse" in block: # toolUseのinputには、パース済みの辞書(dict)データが格納されている json_result = block["toolUse"]["input"] break # 見つけたらループを抜ける if json_result is not None: print(json_result) # {'A': 1, 'B': 'test'} else: print("レスポンスにtoolUseブロックが含まれていませんでした。") 終わりに invoke_model ではLLMの出力が文字列になるため、JSON構造を保証するのが難しいという課題がありましたが、Converse APIの Tool Use 機能を活用することで、この問題をシンプルに解決できました。 LLMの出力を構造化データとして扱いたい場面では、ぜひConverse APIを利用してみてください。 今後も、お客様の満足度向上のために、AIやクラウド技術を活用したコールセンター業務の改善に取り組んでいきます。 参考 https://catalog.workshops.aws/building-with-amazon-bedrock/en-US https://docs.aws.amazon.com/boto3/latest/reference/services/bedrock-runtime/client/converse.html
アバター
ニフティには所属部署での業務のほかに、有志による社内活動が存在します。もちろん強制ではなく、それぞれが興味のある分野について、自主的に活動しています。なかには会社公認のもと予算がつき、社内業務に貢献しているケースも。業務とは別のやりがいや、自分の専門外の知見を得られることが、一つのモチベーションになっています。 その一つが、オンラインサポートチーム。オンライン会議や社内・社外イベントなど、配信関連のクオリティを向上させる目的で発足し、現在は10名ほどで活動しています。具体的な活動内容や、活動にかける思いについて、メンバーに聞きました。 自己紹介 Aさん 2019年4月に新卒入社。普段の業務内容はカスタマーサポート業務に関するアプリケーション及びシステムの開発・運用。オンラインサポートチームでの役割は、チームの運営実務・業務調整、社内における配信イベントの運営・機材サポート。趣味はゲーミングガジェット集め、eスポーツ観戦、写真撮影。 Sさん 2019年4月に新卒入社。普段の業務内容はデータセンターとオフィスのネットワーク設計・構築・運用。オンラインサポートチームでの役割は、社内・協賛イベントにおける配信イベントの運営・機材サポート・動画/写真撮影。趣味は廃道探索・環境測定。 Sさん 2021年4月 に新卒入社。普段の業務内容はコラボレーションツールおよび会計システムの運用・保守。オンラインサポートチームでの役割は、会議運営・音声編集・音声機材導入など。趣味は楽器演奏(ギター/ベース)と DTMでの楽曲制作。 オンライン会議やイベント配信の精度向上を目的に、専門チームが発足 みなさんは「オンラインサポートチーム(以下、オンサポ)」のメンバーということですが、所属部署や普段の業務はバラバラとお聞きしました。 Sさん そうですね。オンサポは会社公認の有志による活動で、さまざまな部署からメンバーが集まっています。ちなみに私は普段、社内プラットフォームチームに所属し、GWSやSlackといった外部ツールや、社内会計システムの運用などを担当しています。趣味はDTMを使った楽曲制作や、楽器の演奏です。 Aさん 私はカスタマーサポートグループ所属で、コールセンター業務にあたるスタッフさんが使うソフトウェアの開発や運用を行っています。趣味はゲームで、ゲーミングガジェット集めやeスポーツ観戦です。 Sさん 所属は Aさん と同じカスタマーサポートグループで、オフィスやコールセンターなどで使うネットワークのメンテナンスをしているエンジニアです。今は主に、オフィス周りのネットワークを設計しています。趣味は廃道の探索と環境測定です。 そもそもオンサポとはいつ頃から、どんな目的で発足したのでしょうか? Sさん 正式なキックオフは2022年の4月ですが、チームが立ち上がる前から活動自体は始まっていたと記憶しています。当時はコロナ禍がまだ収束しきっていないタイミングで、在宅でのリモート会議や、全国の拠点をオンラインでつないで全社的な報告会を行う時、あるいは社内イベントの配信の際などに、画質や音質といった面でストレスを感じることが多かったんです。 そこで、当時のプラットフォームチームのリーダーの呼びかけによって、部署を超えて課題感を持つ有志が集まり、オンラインでの環境をサポートするチームが立ち上がったというのが大まかな経緯です。現時点でのメンバーは10人ほど。入れ替わりもありますが、ここにいる3人はわりと長いほうですね。 ちなみに、みなさんはどんな思いからオンサポへの参加を決めたのでしょうか? Sさん 周囲からも「オンライン会議の品質を上げたい」という声は聞いていましたし、個人的にマイクなどの音響関係に興味を抱いていたこともあって、そうした部分で携われたらなと思い、参加することにしました。 Aさん そもそもチーム発足前から改善に向けて試行錯誤している人たちがいて、私はその様子を横から眺めていたのですが、正式にオンサポチームができるにあたって当時のリーダーがメンバーを募集したんです。みんなが頑張っている姿も見てきたし、個人的にも配信系に興味があったので、じゃあやってみようと。 Sさん 私はオンサポが立ち上がって少し軌道に乗ってきた頃に社内に向けた募集があって、参加を決めました。オンサポの活動自体は知っていましたし、趣味で少しカメラをやっていることもあって、業務として撮影機材を扱えるのは面白そうだなと。 趣味の範疇では触れない、本格的な機材を扱えるのが楽しい オンサポのミッションは「オンライン配信環境の品質改善」ということですが、具体的な活動内容や、これまでの成果を教えてください。 Sさん 今は隔週で1回1時間程度の定例会議を行なっています。社員からオンサポに寄せられた、配信環境にまつわる相談や問い合わせに対してみんなで議論をして、対応策を考えたり、プロジェクト化してメンバーをアサインしたり、進行中の案件の進捗を共有したりといった感じですね。 Sさん プロジェクトの具体的な内容としては、まず、「(品質を上げるための)機材の選定」です。映像関連はカメラ、音響関連はマイクやミキサーなど。実際に導入してみて、運用しながらどれくらい性能の向上や工数の削減につながったかを検証していきます。検証を踏まえて使い方を工夫したり、設定を変えたり、必要であれば別の機材を導入したりして、クオリティを上げていくのがメインの役割です。 Aさん 社内向けとなると、どこまでクオリティにこだわるかの線引きが難しいのですが、「音声が明瞭に聞こえる」というのは大前提として、映像もできるだけ鮮明にストレスなく届けられるようにしたいと考えています。 たとえば、社内オンラインイベントの配信中に映像や音響関連で気づいたことがあればSlackに書き込んで、イベント終了後にみんなで振り返りを行なっています。それで、次回はまた違う設定を試してみたり。社内だからこそ、ある程度の失敗は許容してもらえる前提で試行錯誤できるのは大きいですね。やっていくうちにノウハウを積み上げて、クオリティが上がってきた実感があります。 ちなみに、 Sさん は音響、 Sさん は撮影機材に関心があったということですが、オンサポチームでもご自身が興味のある分野を担当されているのでしょうか? Sさん オペレーションに関しては全員が分かっている状態にすることが前提ですが、それに加えて各自が得意な領域、関心のある分野で役割を担っているイメージですね。僕の場合は音声機材だったりしますし、 Sさん はカメラをはじめとする映像周りの機材、 Aさん は調整周りを担当することが多いです。 Sさん 得意といっても僕の場合は趣味で一眼レフカメラを触っていた程度なのですが、オンサポでの活動を通じて学べたことも多く、いい経験をさせてもらっていると思います。先ほども言いましたが、趣味の範疇ではなかなか触れない機材を扱えるのも、すごく楽しくて。最近ではPTZカメラという、遠隔で方向や角度を調整したり、ズームしたりできる機材を導入しました。セミナーなどを配信する際、最後列から撮影しても観客の方を映さずに登壇者にズームできるので、とても便利ですね。 従業員の方からは、オンサポチーム発足後の配信のクオリティに対して、どんな声が寄せられていますか? Aさん 評判はいいと思います。特に、日頃から外部のエンジニアが集まるオンラインイベントに参加している人からは「うちのオンサポはレベル高いね」と言ってもらえますね。 業務外活動の範疇で、できることを模索 もともとは社内の配信の品質を上げる目的で発足したオンサポですが、今は活動も幅も広がっているのでしょうか? Sさん そうですね。たとえば、最近は動画制作の相談を受けるケースも出てきました。社内のとあるチームのイベント配信を我々が請け負った時に、後でアーカイブとして残す、あるいは外部に発信できるような形に編集することもあります。あとは、NIFTY Tech Dayというニフティが社外向けに行なっている技術カンファレンスがあるのですが、セッションの間に流す社員インタビューを僕らが撮影したこともありました。 ただ、配信とセットで撮影したり、動画を編集することはあっても、動画制作単体の依頼は受けていません。社内にはメディア関連の制作を請け負うチームも別にありますし、基本的にオンサポは配信のサポートがメインなので。どこまで枠を飛び越えるべきかの判断は、なかなか難しいですね。 メンバーとしては、活動の幅を広げていきたいのか、それとも役割を絞って、あくまで配信のクオリティを突き詰めていきたいのか、どちらですか? Aさん そこは正直、悩みどころですね。配信のクオリティでいうと、ある意味でやり尽くした部分もあります。これ以上となると、さらに高い機材を導入するかみたいな話になりますが、果たして社内向けでそこまでやる必要があるのかと。また、コストカットや効率化についても突き詰めてきましたので、たとえば当初は何かのイベントを配信する時に準備と片付けを合わせて3時間かかっていたところを、今では合計1時間で終わらせられるようになっています。 Sさん かといって、活動の幅を広げようにもなかなか難しいですよね。たとえば、オンサポがクリエイティブチームみたいな形で、幅広く動画制作を請け負う方向性もあるとは思います。ただ、そうなるともう“仕事”の範疇になってしまう。オンサポは本業外の社内活動という位置付けなので、あまり手を広げすぎるのもどうなんだろうと。 結局のところ、我々の軸足はあくまでエンジニアなので、今は本業に支障のない範囲で次に何ができるのかを模索しているところです。 ただ、オンサポの活動自体はこれからも続けていきたいと考えていらっしゃいますか? Sさん そうですね。活動自体は楽しいので、僕自身は可能な限り続けたいと思っています。もちろん、年次を重ねてさらに多忙になればそうも言っていられなくなるかもしれませんが、仮に僕らが抜けたとしても“ユーザー”が困らないよう、配信にまつわる困りごとをカバーするような体制や仕組みは作っておきたいですね。 後編に続きます! 今回はニフティのオンラインサポートチームのインタビューの様子をお届けしました。続きは近日公開の後編の記事をご覧ください。 このインタビューに関する求人情報 /ブログ記事 ニフティ株式会社 求人情報
アバター
はじめに こんにちは。ニフティの山田です。 AWSを利用するうえで、S3を使うことは多いと思います。静的ファイル配信のみならず、アプリケーションから参照されることも多いでしょう。このようなアプリケーションの動作確認やテストの上では、ローカル環境で動くS3エミュレータがあると便利です。 しかし、従来有力だった以下の2つはいずれも無償提供が実質終了してしまいました。 MinIO: Web UI機能が削除され、バイナリ提供が終了したため、実質的にOSS版は終了 Web UI機能削除: https://github.com/minio/object-browser/pull/3509 バイナリ提供終了: https://github.com/minio/minio/pull/21625 localstack for AWS: 利用にはlocalstackアカウントの発行と認証が必要となり、CIでの実行には有償契約が必須 告知: https://blog.localstack.cloud/the-road-ahead-for-localstack/ そこで今回はこの代替となるものを探してみました。 移行先 Dockerイメージでサーバを立てることができ、複数コンテナの連携や複雑な初期化が必要ないものを選択します。 S3Mock SeaweedFS RustFS イメージ名 abode/s3mock chrislusf/seaweedfs rustfs/rustfs 言語 Java Go Rust GitHub Stars 1,031 29,900 20,900 Web GUI × ◯ ◯ path-style access ◯ ◯ ◯ virtual-hosted-style access × × × バケット基本操作 ◯ ◯ ◯ オブジェクト基本操作 ◯ ◯ ◯ ListObjectsV2 ◯ ◯ ◯ multipart upload ◯ ◯ ◯ タグ ◯ ◯ ◯ 署名付きURL △(署名を無視) ◯ ◯ sigv4auth × ◯ ◯ バケットポリシー × ◯ ◯ バージョニング ◯ ◯ ◯ ライフサイクルポリシー × △(TTL設定のみ) ? CORS × ◯ ? 表は公式ドキュメントを参照したものであり、すべての機能を検証したわけではない点にご留意ください。 S3Mock 文字通りテストなどで利用するS3のモック用途で作られたものです。Testcontainersでの利用も公式に想定されています。 GitHub でAPIごとのサポート状況がリストアップされている点も親切です。 docker composeでの起動例は以下の通りです。 services: s3mock: image: adobe/s3mock:4.11.0 environment: - COM_ADOBE_TESTING_S3MOCK_STORE_RETAIN_FILES_ON_EXIT=true - COM_ADOBE_TESTING_S3MOCK_STORE_ROOT=data - COM_ADOBE_TESTING_S3MOCK_STORE_INITIAL_BUCKETS=my-bucket ports: - 9090:9090 volumes: - s3mock-data:/data volumes: s3mock-data テスト用だけあってデフォルトだと終了時にデータ消去が走ってしまいますが、環境変数で永続化が可能です。また自動作成するバケット名を環境変数で設定できる点が便利です。 Pros エラーレスポンスなど、S3の挙動再現には最も期待できる Cons 署名など高度機能には対応していない Web GUIはない SeaweedFS 分散ストレージOSSの一つです。FUSE、Hadoopなど多くのプロトコルに対応しており、S3互換APIも備えています。 分散システムだけあってMaster・Volume・Filerなど複数コンポーネントから構成されていますが、S3をシングルノードで起動するだけなら以下でできます。 services: seaweedfs: image: chrislusf/seaweedfs:4.09 ports: - 8333:8333 # S3 API - 8888:8888 # Filer UI - 9333:9333 # Master UI command: ["server", "-s3", "-dir=/data", "-ip=0.0.0.0"] volumes: - seaweedfs-data:/data volumes: seaweedfs-data: 上記設定だと無認証でアクセス可能です。簡易的ですがUI(上記のFiler UI)も備わっています。 Pros S3の高度機能にもある程度対応している Web GUIがあるので確認はしやすい Cons 分散システムであるため、構成要素は多い 不具合があった際のデバッグは難しい あくまでもS3互換であるため、エラーレスポンスなど細かいとことで違いが出る可能性がある RustFS 同じく分散ストレージOSSです。…とはいえまだアルファ版でありシングルノードでしか動作しません。MinIOの代替を目指している模様です。 docker composeでの起動方法は以下の通りです。 services: seaweedfs: image: rustfs:rustfs:1.0.0-alpha.82 environment: - RUSTFS_ACCESS_KEY=rustfsadmin - RUSTFS_SECRET_KEY=rustfsadmin - RUSTFS_CONSOLE_ENABLE=true ports: - 9000:9000 # S3 API - 9001:9001 # Admin UI volumes: - rustfs-data:/data volumes: rustfs-data:   アクセスキー・シークレットが必須となります。(デフォルトはrustfsadmin/rustfsadmin) Web GUIへのアクセスにも必要です。 OpenTelemetryにも対応しているらしく、docker composeの サンプル が用意されています。 Pros Web GUIを持ち、SeaweedFSよりモダンなデザイン Cons まだalpha版 ドキュメントがあまり詳細でない 認証トークンをハードコード して CVSS 9.8 の脆弱性を出したことがある 結論 個人的には テスト(Testcontainer)用 S3Mock docker compose用 GUI不要なら: S3Mock GUI必要なら: SeaweedFS で良いのではないかと思っています。localstackほどの機能網羅性はありませんが、単純なCRUDL用途であれば実用に耐えるでしょう。私の担当プロダクトでは規模の小さいものからS3Mock/SeaweedFSへの置き換えを行っています。 Tips: 初期化方法 ローカル実行時やテスト開始時など、バケット作成・初期データ投入を自動的に行いたいケースがあります。 locakstackでは特定ディレクトリにhookスクリプトを置いておくと自動実行される機能がありましたが、上記のいずれもその機能はありません。(S3Mockのみ、バケット作成は自動実行可能) 初期化したければ、別途初期化用のコンテナを用意することになります。 ex) SeaweedFSに対してバケット作成、初期データ投入 seaweedfs: image: chrislusf/seaweedfs:4.09 ports: - 8333:8333 # S3 API - 8888:8888 # Filer UI - 9333:9333 # Master UI command: ["server", "-s3", "-dir=/data", "-ip=0.0.0.0"] volumes: - seaweedfs-data:/data healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:8333"] interval: 5s timeout: 5s retries: 10 seaweedfs-init: image: amazon/aws-cli:2.33.15 depends_on: seaweedfs: condition: service_healthy environment: AWS_ACCESS_KEY_ID: test AWS_SECRET_ACCESS_KEY: test AWS_DEFAULT_REGION: ap-northeast-1 volumes: - ./mock-contents/data:/data:ro entrypoint: /bin/sh command: - -c - | aws --endpoint-url http://seaweedfs:8333 s3 mb s3://seaweedfs || true aws --endpoint-url http://seaweedfs:8333 s3 sync /data/ s3://seaweedfs/ echo "Initialization complete" 初期化処理を行うコンテナ(上記のaws-cliコンテナ)を用意し、そこで初期化処理を実行する 上記ではバケット作成とs3 syncで初期化している depends_onで起動順の制御が必要 プロセスの起動完了後に実行される必要があるため、S3コンテナ側にヘルスチェックの設定が必須 その他の選択肢 分散ストレージ Ceph Garage Zenko など、S3互換APIを持つストレージOSSは他にもありますが、分散化を前提としておりセットアップが複雑です。ローカル・テスト用で使うには不向きかもしれません。 プロキシ S3Proxy APIだけを提供し、他のストレージバックエンド(Google Cloud Storageなど)に対するプロキシとして機能するものです。 ファイルシステムバックエンドがあるので単独でも使えそうではありますが未検証です。 AWS SDKモック moto (python) aws-sdk-client-mock (JavaScript) ユニットテスト用途であれば昔ながらの方法に立ち戻り、S3互換サーバを立てるのではなく、AWS SDKをモック化することで対応可能です。SDKレスポンスの再現度という点では最も力が入っています。 ただしテスト時にしか使えないので、ローカルPC上で環境を再現する用途には不向きです。また、複数アプリケーションを起動してのテストにも対応できません。 インメモリモック gofakes3 (Go) インメモリでS3互換サーバを立てるものです。SDKモックとは異なりますが、テスト用途であり、やはり環境再現などには使えません。 おわりに 今回は、ローカルS3エミュレータの代替となるサービスについて解説しました。 参考になれば幸いです。  
アバター
はじめに こんにちは。 寺島です。 普段はマイニフティチームでスマホアプリ(マイ ニフティ)の開発に携わっています。 最近はAIでの開発が活発になってきましたね! 私は主にClaude Codeでの開発を行っています。 基本的には、快適に開発をお任せしている状況なのですが、 Claude CodeでiOSアプリの開発を行う中で、とてもネックになる部分が出てきました。 ビルドがうまくできない 新規グループやファイルの追加が、Xcodeで認識されない 上記の2点で本当によく詰まってしまい、開発体験が著しく下がってしまう状況でした。 ビルドは、対象のシミュレーターを起動してみたいなことが苦手のようで、延々と転け続けていましたし、ファイル追加は、Xcodeprojの操作が苦手なようで既存の構成を壊してしまっていました。 最近は公式のMCPなどで解決ができるようなのですが、もっと手軽にスクリプトで解決する方法がありますので、ご紹介させていただければと思います。 この記事では、プロジェクトルートの .claude/ 配下に scripts/ ディレクトリを配置し、CLAUDE.mdやSKILLS.mdから呼び出すパターンを紹介します。 scriptsの解説 .claude/scripts/ ディレクトリにプロジェクト固有のスクリプトを配置します。 今回は、CLAUDE.mdなどから相対パスで呼び出す形で実現します。 嬉しいこと ビルドで失敗することがなくなります .xcodeproj を編集を代わりにやってくれます ディレクトリ構成 your-project/ └── .claude/ ├── CLAUDE.md # 「./.claude/scripts/on_build.shや./.claude/scripts/on_new_file を実行」と記載 └── scripts/ ├── on_build.sh # ビルド実行スクリプト └── on_new_file.sh # ファイル作成スクリプト 1. ビルドスクリプト(.claude/scripts/on_build.sh) 実装例(iOS/Xcode) : ※ 指定するシミュレーターは必ず利用できるシミュレーターを指定してください #!/bin/bash # iOS: Xcodeビルド # 環境に応じて以下を変更してください: # - ワークスペース/プロジェクト名 # - スキーム名 # - ターゲットデバイス xcodebuild -workspace YourApp.xcworkspace \ -scheme YourScheme \ -sdk iphonesimulator \ -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \ -derivedDataPath ./.build \ build 実行権限を付与: chmod +x .claude/scripts/on_build.sh 2. ファイル作成スクリプト(.claude/scripts/on_new_file.sh) 環境固有の部分があります。 : Dir.glob('*.xcodeproj').first – プロジェクトファイルの検索パターン(環境に応じて変更が必要) project.targets.first – ターゲットの選択(複数ターゲットがある場合は適宜変更が必要) 実装例(Xcodeプロジェクトへの自動登録) : #!/bin/bash # iOS: Xcodeプロジェクトにファイルを追加するフック # 前提: xcodeproj gem がインストール済み(gem install xcodeproj) # 使用例: ./.claude/scripts/on_new_file.sh app/Module/Feature/NewFile.swift FILE_PATH="$1" if [ -z "$FILE_PATH" ]; then echo "使い方: $0 <ファイルパス>" exit 1 fi if [ ! -f "$FILE_PATH" ]; then echo "エラー: ファイルが存在しません: $FILE_PATH" exit 1 fi # Rubyスクリプトを実行してXcodeプロジェクトに追加 ruby -r xcodeproj <<RUBY def find_or_create_group(project, path_components) current_group = project.main_group path_components.each do |component| next_group = current_group.children.find { |child| child.is_a?(Xcodeproj::Project::Object::PBXGroup) && child.display_name == component } if next_group.nil? next_group = current_group.new_group(component, component) end current_group = next_group end current_group end begin project_path = Dir.glob('*.xcodeproj').first if project_path.nil? puts "エラー: Xcodeプロジェクトが見つかりません" exit 1 end project = Xcodeproj::Project.open(project_path) file_path = '${FILE_PATH}' path_parts = file_path.split('/') file_name = path_parts.pop group_path = path_parts group = find_or_create_group(project, group_path) # ファイルの重複チェック existing_file = group.children.find { |child| child.is_a?(Xcodeproj::Project::Object::PBXFileReference) && child.display_name == file_name } if existing_file puts "エラー: ファイルは既にプロジェクトに存在します: #{file_path}" exit 1 end # 新規ファイルの追加 file_ref = group.new_file(file_name) project.targets.each do |target| target.add_file_references([file_ref]) end project.save puts " ファイルを全ターゲットに追加しました: #{file_path}" rescue => e puts "エラー: #{e.message}" exit 1 end RUBY 実行権限を付与: chmod +x .claude/scripts/on_new_file.sh CLAUDE.md/SKILL.mdでの活用方法 プロジェクトルートの .claude/CLAUDE.md 等に以下を記載することで、Claudeにスクリプトの存在を伝えられます: ビルド手順 コード変更後は必ず以下を実行してビルドを確認してください: ./.claude/scripts/on_build.sh ビルドエラーが発生した場合は、エラーメッセージを確認して修正してください。 新規ファイル作成時 新規ファイルを作成した場合、以下を実行してプロジェクトに登録してください: ./.claude/scripts/on_new_file.sh <作成したファイルのパス> まとめ scriptsパターンは、Claude Codeにおける「プロジェクト固有の処理」を一箇所に集約し、プロンプトをシンプルに保つための設計パターンです。 設計の利点 : Claudeは標準化されたインターフェース(スクリプト)を呼び出すだけ チームでリポジトリ共有するだけで環境構築が完了 この仕組みにより、Claude CodeでのiOS開発がより快適になります。 おわりに 色々な実行方法がある中で、今回は選択肢の一つとして.shで管理する方法をご紹介しました。 もっと快適にするために、サブエージェントから呼び出したりなど様々あると思います。 色々試すものの一つになれると幸いです。
アバター
ニフティで利用しているAWS、Notion、GitHubなどのクラウドやSaaSの管理・運用を担当している石川です。 最近社内で利用しているIDaaS(Identity as a Service)をOneLoginからMicrosoft Entra ID(旧Azure AD)へ移行しました。 同じ「IDaaSの切り替え作業」でも、アプリケーションごとに事情はまったく異なり、想定外のハマりどころが数多くありました。本記事では、実際の移行作業を通じて得られたノウハウを共有します。今回は私が移行したアプリの中でも AWS、GitHub Enterprise 、 Notion 、Redash の4つを例に取りつつ説明していきます。 この記事の読み方 各セクションの内容を 一般論 (IDaaS移行全般で押さえるべき知識)と 体験談 (筆者が実際に経験した具体事例)に分けています。 IDaaS移行は「認証設定の差し替え」だけではない 1. ユーザープロパティの確認と表示名の定義 移行を機にデータが「整理」されることがある 表示名の設計は悩みどころ 2. Entra IDのグループ管理の注意点 3. IDaaSごとに異なるマッピングの仕様 4. アプリごとに異なるプロビジョニングの挙動 5. IDaaS移行時のセッション・トークンへの影響 6. グループプロビジョニングの罠 ネストされたグループは同期できない 旧IDaaSで作成されたグループは引き継げない グループ削除時の権限孤立リスク Entra IDのプロビジョニング間隔は40分固定 OneLoginからの移行で行ったグループ切り替え手順 Notionでの権限孤立チェックの限界 グループプロビジョニングは多用しないのが無難 7. アプリのバージョンが古い場合のリスク 対応策の検討 8. 申請フロー・運用ワークフローの再構築 AWS IAM Identity Centerグループ管理のTerraform化 まとめ IDaaS移行は「認証設定の差し替え」だけではない IDaaSを変えるというと、SAML設定のURLや証明書を貼り替えるだけに思えるかもしれません。しかし実際には、以下のような周辺領域にまで影響が波及します。 ユーザープロパティのマッピング (表示名・属性情報などの紐付け) IDaaS/SaaSごとのプロビジョニング(SCIM)挙動差異の把握 グループ管理の方式変更 既存セッションや認証トークンへの影響 申請フロー・運用ワークフローの再構築 認証設定そのものよりも、これらの周辺対応のほうがボリュームが大きいです。 1. ユーザープロパティの確認と表示名の定義 一般論 新旧のIDaaSで連携するADやLDAPが同じでも、 IDaaS側でのプロパティの持ち方や参照先が異なる ことがあります。 移行を機にデータが「整理」されることがある IDaaS移行のタイミングで、AD側の属性を正しいフィールドに寄せ直す(=綺麗にする)動きが起きることがあります。その上で「名」と「体」が合っていないフィールドがあると、 一時的なものか恒常的なものかを事前に確認 しておかないと、マッピングで使っている値が突然変わるリスクがあります。 表示名の設計は悩みどころ SaaS側のユーザー表示名をどう構成するかは意外と悩むポイントです。 SaaSの挙動で特に確認すべき点は以下の2つです。 ユーザーサジェストはどのフィールドに対して行われているか → 表示名でしか行われないとローマ字でユーザーを検索できない、逆も然り 同姓同名の区別が付くか → ユニークなIDがないと区別が付かない → サジェスト時は補助情報としてユニークなIDが出て区別が付くが、設定後は区別が付かない場合もあり 体験談 検索サジェストやユーザー名オンマウスで、固有ID(メールアドレス or ユーザーID)を併記してくれているアプリに関しては表示名を 姓 名 。そうでないものに関しては 姓 名 メールアドレスのユーザー部 という併記構成にしています。冗長に見えますが、以下のメリットがあります。 常に同姓同名の区別 がつく 漢字の読み方がわからない場合もメールアドレスにある ローマ字で補完 できる TIPS : 通常であれば表示名には英字を推奨します。日本語話者が圧倒的多数の環境では 漢字 + ローマ字 の併記が現実的な妥協点になります。サジェストが前方一致でしか機能しないサービス(Amazon Qなど)もあるので、 ローマ字 + 漢字 のパターンも有効です。 2. Entra IDのグループ管理の注意点 一般論 アプリによっては、全社員にデフォルトで権限を与えるものがあります。その場合、以下のような流れでライセンスが消費されます。 Entra IDにユーザーが追加される Entra IDの特定グループ(動的グループ)に自動で追加される そのグループにアプリの利用権限が付与されている アプリにユーザーがプロビジョニングされる アプリの利用シートが増える つまり、Entra IDにユーザーを追加すると同時に、アプリのユーザー数が増えてライセンスが消費または追加されます。 事前に確認すべきポイントは2つです。 動的グループがどのような基準で構成されているか その基準に合致するプロパティが、どのような運用で付与されるか 例えば、Entra ID上でテストアカウントを作成したところ、意図せず動的グループに含まれてしまい、アプリ側のユーザーが自動生成されてライセンスが消費される、といったことが起こり得ます。 体験談 移行前に確認したところ、人間だけのはずのグループにテストアカウントが含まれていたり、入社の数ヶ月前からアカウントが存在していてライセンスが無駄に消費されていたりするケースがありました。 また過去には、LDAPで更新ミスが発生し大量のユーザーが削除されたことがありました。その際、LDAP連携していたIDaaSおよびプロビジョニング先のアプリ側でも、大量のユーザーが無効化される障害が発生しました。 ユーザーの無効化・削除によって保管データに不可逆な変化が生じるタイプのアプリでは、プロビジョニングの誤削除防止機能を有効化し、閾値を設定することを強く推奨します。 Microsoft Entra プロビジョニング サービス で誤削除防止機能を有効にする – Microsoft Entra ID | Microsoft Learn 3. IDaaSごとに異なるマッピングの仕様 一般論 Entra IDでは、プロビジョニング時の属性マッピングに 独自の式 を使います。 Join() 、 IIF() 、 Replace() などの関数を組み合わせてフィルターや値変換を行うことができます。 Microsoft Entra アプリケーションのプロビジョニングで属性マッピングの式を記述するためのリファレンス – Microsoft Entra ID | Microsoft Learn 体験談 # 表示名を 姓 名 メールアドレスのユーザー部にする式 Join(" ", [surname], [givenName], Item(Split([mail], "@"), 1)) # 特定のグループは同期先では別名に置換、決まったprefixが付いたグループはprefixを置換して同期 IIF( [displayName] = "ほげほげ", "group-hogehoge", IIF( [displayName] = "ふがふが", "group-fugafuga", IIF( InStr([displayName], "YourApp-team-", , ) > 0, Replace([displayName], "YourApp-team-", , , "team-", ), [displayName] ) ) ) 上の例はNotionの表示名マッピングですが、こうした式を 本番で一発勝負で試すのは危険 です。 SCIMに対応した検証用アプリを用意して、本番適用前にマッピング式の動作確認をしましょう。Entra IDには式ビルダーのテスト機能もありますが、実際のプロビジョニング結果で確認するのが確実です。 4. アプリごとに異なるプロビジョニングの挙動 一般論 SCIMプロトコルを使っていても、 利用するIDaaSとSaaSによって挙動が異なります 。 体験談 過去、実際に遭遇した問題は以下のとおりです。 SaaSのSCIMエンドポイントへの アクセス過多でエラー (初回同期時) IDaaS or SaaSのSCIMが RFCどおりに実装されておらず 、一部の同期処理が失敗 IDaaS or SaaSが グループ名変更やグループ削除に対応していない グループプロビジョニングで メンバーの同期状態と実態に齟齬 がよく出る サポートに依頼して改修してもらったり、自前でSCIM APIを叩いて同期用Lambdaを動かしたりと、正常な状態にするまでにはそれなりに時間がかかります。 そのため、できる限り検証環境を用意して事前に確認することを推奨します。ただし、SaaSでは 検証環境の用意が難しい ため、本番環境で一発勝負になることもあります。 今回は、自前での用意が難しいものだけ検証なしで本番一発勝負で切り替えました。 Notion:本番環境で一発勝負 SAML SSOはOrganizationレベルの設定のため、Workspaceごとの事前検証が不可能 GitHub Enterprise Cloud:日常的に使っていないOrganizationで検証 SAML SSOをEnterpriseレベルで行うとSCIMが利用できないため、Organizationレベルで設定 AWS:検証用の別Organizationで検証 Redash:検証用の別環境を作成 TIPS: GitHubのSCIM設定で、Azure AD OAuthアプリがひとつでもOrganizationに対して承認状態になっていると、SCIMセットアップ時のGitHub OrganizationにGrantする画面が表示されないという問題に遭遇しました。個人設定のOAuthアプリからRevokeすることで解消しましたが、ドキュメントに記載がないのでハマりどころです。 5. IDaaS移行時のセッション・トークンへの影響 一般論 SAMLのIdPを切り替える際は、既存セッション・認証トークンへの影響をアプリケーションのサポートに事前確認することが重要です。 体験談 GitHubでの移行時に、影響範囲を事前にGitHubサポートへ確認しました。 Enterprise管理者のアカウントから英語で質問するとレスポンスが早い気がします。 項目 影響 対応 ブラウザアクセス 再認証が必要 SAMLで保護されたリソースにアクセスする際に、新しいIdP(Entra ID)での認証が必要 既存セッション 有効期限まで維持 強制的なログアウトはないが、期限切れ後の再ログイン時に再認証 OAuth/PAT 機能継続 特別な対応不要 Copilot 初回のみ再認証 変更後の初回使用時に再サインインとSAML SSO完了が必要 GitHub OrganizationのIDaaS切り替え時の認証情報への影響 TIPS: IDaaS変更についてドキュメントに具体的な記載がないことがままあります。影響がないかサポートに問い合わせましょう。また影響があるにしろないにしろ、切り替え前にユーザーへの周知を忘れずに。あとで来る問い合わせの量が減ります。 6. グループプロビジョニングの罠 IDaaSとの連携で一番厄介だと思っているのがグループプロビジョニングです。 一見便利なのですが運用まで考えると考慮しないといけない事項が多くて、有効活用できるシーンは限られます。 一般論 ネストされたグループは同期できない Entra IDのSCIMプロビジョニングは、 ネストされたグループ(Group in Group)のメンバーを読み取れません 。直接メンバーとして割り当てられたユーザー・グループのみが同期対象です。 Nested groups.  The Microsoft Entra user provisioning service can’t read or provision users in nested groups. The service can only read and provision users that are immediate members of an explicitly assigned group. Understand how Application Provisioning in Microsoft Entra ID – Microsoft Entra ID | Microsoft Learn グループ設計時にネストを前提としている場合、フラットな構造に見直す必要があります。 旧IDaaSで作成されたグループは引き継げない 旧IDaaSのグループプロビジョニングで作成されたグループを、Entra IDで そのまま同期状態で引き継ぐことはできません 。グループにはメールアドレスのようなユニークIDにしやすい項目が存在しないため、IDaaS側が独自にIDを割り振ります。そのため、移行時にIDを引き継げず、グループの再作成が必要になります。 グループ削除時の権限孤立リスク 削除予定のグループだけに権限が付いたリソースがないか、事前に確認が必要です。そのグループが唯一の権限付与先になっている場合、削除後にリソースへアクセスできなくなります。 Entra IDのプロビジョニング間隔は40分固定 Entra IDのグループプロビジョニングは約40分間隔で実行されます。即時性がないため、急ぎのオペレーションにはグループプロビジョニングは不向きです。ユーザー単位のプロビジョニングならばオンデマンドで実行できるため、少数であれば手動での即時同期が可能です。 体験談 OneLoginからの移行で行ったグループ切り替え手順 OneLoginで作成されたグループをEntra IDに引き継げなかったため、以下の手順で対処しました。 旧グループを事前にリネーム (古いことを記すプレフィックスを付与)して名前の衝突を回避 Entra ID側で 新しいシンプルな命名規則 (YourApp- team-xxx )でグループを再作成 グループプロビジョニング グループへの権限付与などを利用者に依頼(APIで再付与できない場合) 旧グループは棚卸しタイミングで削除 TIPS: グループのリネームはAPIがないと手作業になります。システム的にカバーできない手順がある場合は、数日前から計画的に進めるのがおすすめです。 Notionでの権限孤立チェックの限界 Notionではコンテンツ検索で対象グループが権限を持つページを洗い出せますが、 そのグループ「だけ」が権限を持っているかどうかは目視確認が必要 で、網羅的なチェックは困難でした。 グループプロビジョニングは多用しないのが無難 実際に運用を考えてみると、追加・削除・統合が発生する組織変更のたびにグループの付け替えや廃止管理に悩まされます。SaaS側にグループ管理の手段がSCIMしかない場合を除き、グループプロビジョニングは最小限にとどめるのがよいと感じています。 7. アプリのバージョンが古い場合のリスク 一般論 SaaSではなくオンプレでOSSを動かしている場合、アプリケーションのバージョンが古いとIDaaSのSAML構成をそのまま組めないことがあります。 体験談 実際にこの問題に直面しました。利用しているRedash v10.1.0ではマニュアル通りでは動きません。 SAML & Azure/Entra · getredash redash · Discussion #7026 Authentication Options (SSO, Google OAuth, SAML) 対応策の検討 案 結果 Redashをアップグレード アップグレード後の動作確認に手間がかかるため最終手段 別のIdPを使う(AWS IIC等) 動作はしたので選択肢としてあり 設定だけで回避する 動作したので採用 Redash v10.1.0だとEntra IDのSAML認証が通らないバグ、どこかで見た話だなと思ったら過去にOneLoginでも同じような不具合を喰らっていました。 RedashをECS Fargate構成にしてv7からv10へアップグレードする – NIFTY engineering マニュアルだとEntity IDにRedashインスタンスのURLを入れるようにと書いてますが、Microsoft Entra Identifierを入れれば利用できるようになります。これならコード修正・イメージ再作成も不要です。 TIPS: OSSのドキュメントやIssueの情報を鵜呑みにせず、実際に試してみることが大事です。なお、FirstName, LastNameのクレーム名に名前空間は不要でした。 8. 申請フロー・運用ワークフローの再構築 一般論 IDaaS移行に伴い、以下のような 運用フローの再構築 が必要になる場合があります。 アプリ利用者の追加・削除フロー グループ作成・メンバー変更の申請フロー 例外的なメンバー追加の手順 退職者管理の仕組み 一連の自動化処理 この部分は日常的な運用工数や利用者の使い勝手に直結するため、しっかりとフローを設計して手作業の運用は可能な限りゼロにすることを目指しましょう。 体験談 AWS IAM Identity Centerグループ管理のTerraform化 AIで加速するAWS運用効率化 〜IAM Identity Center IssueOpsとセキュリティ基準自動作成〜 | ドクセル AWS IAM Identity Center(以下AWS IIC)への権限割り当てには、TerraformによるGitOpsを採用しています。今回の移行に伴い、グループ作成とメンバー割り当てもできるように改修しました。 グループへの割り当てでは、実際に誰が何の権限を得るのか分かりにくくなるため、レビューをサポートするGitHub Actionsを追加しました。 まとめ IDaaSの移行は、表面的には「認証設定の差し替え」ですが、実態は アプリケーションごとの仕様差異・制約・運用フロー再構築との格闘 です。 IDaaS移行で押さえておきたいポイント ユーザープロパティの差異 を事前に洗い出す Entra IDのグループ管理 の基準や動的グループの挙動を確認する マッピング式 はIDaaSごとに仕様が異なるため、検証環境でテストする プロビジョニング挙動はアプリごとに異なる 前提で、できること・できないことを整理する セッション・トークンへの影響範囲 をサポートに確認し、ユーザーへ周知する グループのネスト非対応 やグループ引き継ぎ不可など、SCIMの仕様制約を把握する アプリのバージョン が古いとそもそもSAML構成ができないことがある 運用フローの再構築 (申請・自動化)まで含めて移行計画を立てる 同様の移行を検討されている方の参考になれば幸いです。
アバター
イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! テーマ ハッカソン優勝チームが語る!実践的AI活用アイデアの創出と実装 NIFTY Tech Talk #26 2025年、ニフティ社内でエンジニアハッカソン合宿が開催されました。 AIをテーマにした今回のハッカソンにて、優勝チームがアイディア創出に挑戦と学びの物語などについて語ります。 チームに分かれ激しい競争の中で勝ち抜いたその成果を是非ともご覧ください。 開催概要 日程:2月19日(木)12:00〜12:30 YouTube Liveで配信 登録はこちらから https://nifty.connpass.com/event/383673/ こんな方におすすめ AI開発に興味がある方 開発にAIを利用している方 社内でのAI活用やハッカソン企画を検討されている方 タイムテーブル 時間 コンテンツ 12:00 – 12:05 オープニング 12:05 – 12:10 エンジニアハッカソン概要 12:10 – 12:25 【仮】AI開発の活用と学び 12:25 – 12:30 クロージング 登壇者プロフィール 添野 翔太(ファシリテーター) ニフティ株式会社 サービスシステムグループ 第1開発チーム 新卒入社9年目。 主に@niftyトップページの開発・運用を担当し、日々スクラムマスターとして奮闘しています。 深田 健太(登壇者) ニフティ株式会社 サービスシステムグループ 第三開発チーム @niftyメールや@nifty安心メールパックの開発をしています。 社内のブカツ制度では、ビリヤード部の部長をしてます。 清水 利音(登壇者) ニフティ株式会社 基幹システムグループ サービスインフラチーム ログインシステムなどを開発しているサブチームのリーダーを担当しています。 ニフティグループでは一緒に働く仲間を募集中です 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティエンジニアのX(旧Twitter)アカウント NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 @NIFTYDevelopers アンチハラスメントポリシー 私たちは下記のような事柄に関わらずすべての参加者にとって安全で歓迎されるような場を作ることに努めます。 社会的あるいは法的な性、性自認、性表現(外見の性)、性指向 年齢、障がい、容姿、体格 人種、民族、宗教(無宗教を含む) 技術の選択 そして下記のようなハラスメント行為をいかなる形であっても決して許容しません。 不適切な画像、動画、録音の再生(性的な画像など) 発表や他のイベントに対する妨害行為 これらに限らない性的嫌がらせ 登壇者、主催スタッフもこのポリシーの対象となります。 ハラスメント行為をやめるように指示された場合、直ちに従うことが求められます。ルールを守らない参加者は、主催者の判断により、退場処分や今後のイベントに聴講者、登壇者、スタッフとして関わることを禁止します。 もしハラスメントを受けていると感じたり、他の誰かがハラスメントされていることに気がついた場合、または他に何かお困りのことがあれば、すぐにご連絡ください。 ※本文章はKotlinFest Code of Conductとして公開された文章( https://github.com/KotlinFest/KotlinFest2018/blob/master/CODE-OF-CONDUCT.md )を元に派生しています。 ※本文章はCreative Commons Zero ライセンス( https://creativecommons.org/publicdomain/zero/1.0/ ) で公開されています。
アバター
はじめに 皆さん初めまして、2025年10月に入社しました田中と申します。 所属はサービスシステム第一開発チームです。 担当業務は主に@niftyトップページの改修・運用などを担当しています。 趣味は料理とドラマ鑑賞です。 たまにジムに行きます。 特に昔のドラマを見ていると、その時代の街の風景や時代の価値観を感じられて面白いです。 最近だと東京ラブストーリー、ロングバケーション、やまとなでしこを見ました。 本記事では私の転職のきっかけや、実際に感じたことを伝えれればと思います。 少しでも参考になれば嬉しいです。 経歴と転職のきっかけ 前職では主に官公庁のシステム開発に従事していました。 転職をしたいと思ったきっかけは下記です。 工程や分野問わず幅広い仕事がしたい モダンな技術にも触れられる仕事をしたい プライベートの時間で個人開発をちょこちょこやっていたこともあり、上記について強く思うようになっていきました。 重視していたことは下記です。 一緒に働く人が穏やかな人が良い どちらかといえば対面がベースになった働き方がいい AIを活用していること ITエンジニアはリモートワークを希望することが多い印象です。 しかし、自分は一緒に働く人が周りにいたほうが仕事が捗りやすいタイプでちょっとしたことでも聞ける環境が良いなと思っていました。 ニフティに決めた理由は上記で挙げたものに一致していたことと、特に面接を通じて人の良さを感じたことでした。 その他にも転職期間中に技術書典があり、ニフティの配布物をもらったのも実は決め手の一つでもありました。 入社して感じ た こと 文化について 人の良さをまず感じました。 入社後は週1で1 on 1の時間を設けていただいたり、振られるタスクもドメイン知識が身に付くように配慮してくださいました。 休みも取りやすく、フレックス制度も周りを見ていると上手に活用していると感じます。 業務について 使用する技術は業務で触れてこなかったものもあるので日々挑戦、といった感じです。 AIについてはGitHub CopilotやClaude, Gemini等 を活用していてコードを自分自身でEditする機会が前職と比べてかなり減りました。 またチームは主にアジャイルでタスクを進めており、レトロスペクティブやバックログリファインメントなどのスクラムイベントをやっています。最初はこの文化に少し戸惑うこともありましたが、すぐに慣れました。 最初小さな範囲での設計→実装→テスト→リリースを完了した時は単純に嬉しかったです。 企画の方やSREチームとのやりとりもあり他のチームとコミュニケーションをとる機会も多いです。 今後に向けて まだまだチームで使用している技術やドメイン知識が十分でないのでそちらを深めていきたいと思っています。 Udemyも会社が発行したアカウントで視聴ができるのでそちらも活用しながら、成長していき会社やお客様に貢献してきたいと思っています。 最後に ここまで読んでいただきありがとうございます。 「新しい技術に挑戦したい」 「対面でのコミュニケーションも大事にしたい」 「人が良い会社で働きたい」 など考えている人であればニフティは良い環境だと思います。 是非皆さんと一緒に働ける日を楽しみにしています!
アバター
はじめに こんにちは。H173です。 ヤマハルーター RTX1300使っていますか? IPv4 over IPv6にネイティブ対応したルーターは海外ベンダー製品ではまだ少なく、エンタープライズ向けのRTX1300は貴重な存在です。 自宅で使用している方も少なくないのではないでしょうか。 今回は、RTX1300をMAP-E環境で使用する際のNAT設定について解説します。 MAP-E と NAT IPv4 over IPv6の主要な方式としてDS-LiteやMAP-Eがあります。 いずれもIPv4アドレスを複数のユーザーで共有する方式ですが、利用可能ポート数に厳しい制約があるのが特徴です。 DS-Lite: VNE側の設備(AFTR)でNATを行う MAP-E: エンドユーザー側のルーターでNATを行う DS-Liteではエンドユーザー側でできることが限られますが、MAP-Eの場合はユーザー側で外側ポートをいかに効率よく使用できるかが重要になります。 ポートセービングIPマスカレード ヤマハルーターにはポートセービングIPマスカレードという機能が搭載されています。 これは、従来のNAPTでは1つのセッションで1つの外側ポートを消費するところを、宛先IPアドレスおよび宛先ポート番号が異なれば、同じ外側ポートを再利用できるという機能です。 この変換方式はRFC 4787で定義されるAddress and Port-Dependent Mapping(APDM)を基本として、外側ポートの再利用を積極的に行う処理が加えられたものと考えられますが、本記事では詳しい解説は割愛します。 NATテーブルの状態を調べる MAP-E 環境では外側ポートの枯渇問題が常に隣り合わせです。 ポート枯渇が発生する原因はさまざまあり、実利用環境でどのような通信が原因となって発生しているか調べる必要があります。 まず、NATテーブルの消費状況をクライアントごとに確認します。 show nat descriptor address このコマンドで、特定のクライアントが異常にセッションを消費していないかを確認します。 さらに、以下のコマンドで現在の全セッションを出力して詳しく調査します。 show nat descriptor address [NATディスクリプタ番号] detail 出力されたリストの宛先IPとポートを見ることで、特定のアプリケーションが大量に通信している、DNSリクエストが滞留しているといった傾向に目星をつけることができます。 また、以下のconfigを投入することでアドレス変換の結果をSyslogに出力させることができます。 nat descriptor log on ※NATディスクリプタのログは大量に出力されるため通常はoffにしておくことを推奨します。 もし外側ポートの枯渇が発生している場合、以下のようなログが出力されます。これが出力されていたら対策必須です。 [NAT] Session was limited. Port is exhausted for [宛先IPアドレス] ポート枯渇の例 ポート枯渇の原因は環境により様々ですが、代表的なパターンを紹介します。 1. UDP/IP通信の大量発生 もっとも典型的な原因です。 TCPと異なりUDPはコネクションレスであるため、通信終了の明確な合図がありません。そのため、ルーターはタイマーの時間切れまでそのNATエントリーを保持し続けます。 特に、最近のQUIC/HTTP3を使用するアプリケーションはUDPを多用します。これらが終了した後もエントリーが残り続け、新しい通信のためのポートが確保できなくなるケースです。 2. Short-livedコネクション TCPであっても、短時間に接続・切断を繰り返す通信(Short-livedコネクション)は要注意です。RTX1300はデフォルト設定でTCPFINを60秒間保持します。秒間数回のリクエストが発生するような環境では、この終了待ちエントリーだけで数千セッションに達し、ポートを食いつぶすことがあります。 3. VPNやSASEエージェントの利用 盲点になりやすいポイントです。クライアントPCでVPNエージェントやSASEエージェントを使用している場合、ルーターから見ると全ての通信の宛先がVPNゲートウェイのIPひとつだけに見えてしまいます。 ヤマハルーターのポートセービングIPマスカレードは、宛先IPおよび宛先ポートが別ならポートを再利用できる仕組みでした。しかし、通信の宛先が全て同じゲートウェイを向いてしまうと、ポートの再利用ができなくなります。 結果として、MAP-Eの割り当てポート上限に達した時点で通信ができなくなります。 ポート枯渇対策 1.タイマーの調整 ポート枯渇を防ぐにはNATの消去タイマーの値を変更することが第一に挙げられます。RTX1300のデフォルト設定ではTCP/UDPともに900秒という非常に長い値が設定されています。特にUDPの900秒保持はRFC 4787の推奨値である300秒と比較しても過剰なため、まずはUDPの消去タイマーから調整するとよいです。 以下はUDPのNAT消去タイマーを300秒に設定する例です。 nat descriptor timer [NATディスクリプタ番号] protocol=udp 300 実際は60秒未満あるいはもっと短い値に設定しても問題がないケースも多いですが、極端に短くすると通信品質に影響が出る可能性があるため、外側ポートの使用率を見ながら調整することを推奨します。 2. 動作タイプの変更 ポートセービングIPマスカレードには動作タイプが存在します。デフォルトは2で、TCPパケットに対してのみ有効です。タイプを3にすると、UDPに対してもポートセービングIPマスカレードが有効になります。UDPパケットが原因でポート枯渇が発生する場合に有効です。 nat descriptor backward-compatibility 3 3. DoT/DoHの利用 NATテーブルを確認すると、UDP:53のエントリーが大量に残っているケースがよく見受けられます。 UDP:53のタイマー短縮も効果的ですが、クライアント側のアプローチとして DNS over TLS (DoT) やDNS over HTTPS (DoH) を有効にする方法があります。 DNS名前解決がUDP:53からTCP:443、TCP:853に移行するため、UDPタイマー待ちによるテーブルの無駄遣いを減らすことができます。 このように、ルーターの設定だけでなく、クライアント側から発生する通信内容を変更することでポートの節約に繋がる例もあります。 まとめ コンシューマー向けルーターではブラックボックスになりがちなNATテーブルの状態確認やタイマーの設定変更ができる点は、RTX1300をはじめとするヤマハルーターの強みです。 タイマーのデフォルト値は過剰な値が設定されていますが、実利用環境にあわせて調整していくことで効率的で安定した運用が可能になります。
アバター
この記事は、 ニフティグループ Advent Calendar 2025  21日目の記事です。(遅刻) はじめに こんにちは。ムサシです。 みなさんは昔ゲームの攻略本やらPC系の雑誌やらについていた、デスクトップマスコットというものを知っているでしょうか? デスクトップに常駐して時計やカレンダーを表示してくれたり、キャラクターがおしゃべりをしてくれたり、メールが来ているのをお知らせしてくれたりといろいろなものがありました。 某借金を返すゲームの攻略本についていた時計が好きだったんですよね。 というわけで、昔を懐かしみながら自分もかわいい時計を作りたいと思います。 デスクトップマスコットもいろいろ現在に生き残っているものがあるのですが、今回はその中でもカスタマイズ性が高く、時計も作れそうな「伺か」を触ってみようと思います。 「伺か」とは、簡単な言語でカスタマイズ可能な、おしゃべりするキャラクターを常駐させられるアプリのようです。 準備 まず伺かをインストールします。 起動すると、デフォルトで設定されたキャラクター(伺かでは、キャラクターのことを「ゴースト」と呼ぶそうです)と設定画面が立ち上がります。 立ち上がった設定画面を見るといろいろいじれそうでした。メールの確認や、動画配信で利用する際の表示設定など、ここにある機能だけでも普通に便利そうです。 この「ゴースト」が大量にユーザの手で作成されており、好きなゴーストを見つけて常駐させるのが一般的な使い方だと思うのですが、今回は作っていこうと思います。 時計を作るには 見た目を変えたり、会話内容を変えるのであればすごく簡単にできるようなのですが、時計を作るにはもうひと手間かかります。 まず時間を取得する必要があります。こちらはYAYAというライブラリを連携させることで簡単に行えるようです。 伺かは「栞」と呼ばれる簡単な言語によって、少し複雑な実装ができるようになるようなのですが、YAYAはその「栞」の一種となります。 (独自の用語が多く、説明すると長くなってしまうので簡易的な説明にとどめさせていただきます。) YAYAを用いたサンプルゴーストとして「ややめちゃん」というゴーストが配布されており、これを基にして新しくゴーストを作って良い旨が書いてあったので、今回はこの子をいじって行こうと思います。とてもかわいいです。 はろーYAYAわーるど(紺野ややめ): https://ms.shillest.net/yayame.xhtml 一応ですが、何か更新があるかもしれないのでGithub上から最新のYAYAのdllファイルをゴーストの中に入れておきましょう。 また、伺かはanimationという機能で画像切り替えができるようです。 まばたきや表情変化に使うのが主目的のようですが、これとYAYAを組み合わせて時計にしていきましょう。 まとめると、YAYAで時刻を取得→animationでゴーストの数字画像を対応するものに切り替えるという流れで作成していきます。 素材を作る 時計の画像はデフォルトのキャラクターが250*380くらいで作られていたので、400pxの正方形で作成して行こうと思います。 イメージはこんな感じ。 手癖でキャラをでっちあげます。できました。ときちゃんと呼びますね。(安直) ゴリゴリ素材を書いていきましょう。 必要なのは、土台になる部分、数字(1~9)です。 土台はこんな感じで、数字部分はお気に入りのフリーフォント「 ビルの谷間と高架下 (リンク先はBooth)」をお借りしようと思います。 土台部分の名称を surface0.png 、数字部分を digit_0.png 〜 digit_9.png としました。 画像が作成出来たら中身の部分を作っていきます。 時間部分の処理 栞は複数の役割を持ったイベントを持っており、そのイベント内に処理を記述すると反映されます。詳しくは こちら で。 時間については OnMinuteChange{} という1分毎に起きるイベントがあるので、そこに「時間と分を取得して各桁の数字を返す処理」を書き加えます。 サンプルのややめちゃんは yaya_aitalk.dic 内で既に OnMinuteChange{} を使っていたので、この中に書き加えるか上書きしてしまいます。 YAYAは複数の.dicファイルに記述された処理を見て動くのですが、重複して書かれているとエラーが起きてしまうので、サンプルファイルをいじるときは気を付けましょう。(1敗) OnMinuteChange { //GETTIMEで現在時刻を取得 _now = GETTIME() _h = TOINT(_now[4]) //時間(0〜23) _m = TOINT(_now[5]) //分(0〜59) //テストメッセージ。1番目のキャラクターに喋らせる。 "\C\0\b[2]現在の時刻は %(_h)時 %(_m)分 です。\e" //各桁に分ける _h10 = _h / 10 _h1 = _h % 10 _m10 = _m / 10 _m1 = _m % 10 //テストメッセージ2。2番目のキャラクターに喋らせる。 "\C\1\b[10]現在の時刻は %(_h10)%(_h1)時 %(_m10)%(_m1)分 だよ。\e" //アニメーション用の返り値 "\C\0\s[0]\i[100,%(_h10)]\i[101,%(_h1)]\i[102,%(_m10)]\i[103,%(_m1)]\e" } テスト用に時刻が取得できているか喋らせてみました。 1分毎に現在の時刻を話してくれたので、問題なさそうです。次に行きましょう。 アニメーションの処理 先ほどの返り値に対応した画像を表示するように surfaces.txt に画像の対応や処理について書いていきます。 animationXXX.interval,neverで「イベントを受け取るまで次の処理を自動実行しない」という状態になり、時間が来たら次の画像が表示となるはずです。 charset,UTF-8 surface0 { // 土台 element0,base,surface0.png,0,0 // animation番号, 描画タイミング, 描画法, ID, 待機時間, X, Y // 時の10の位 (ID 100) animation100.interval,never animation100.pattern0,overlay,100,-1,50,175 animation100.interval,never animation100.pattern1,overlay,101,-1,50,175 animation100.interval,never animation100.pattern2,overlay,102,-1,50,175 // 時の1の位 (ID 101) animation101.interval,never animation101.pattern0,overlay,100,-1,120,190 animation101.interval,never animation101.pattern1,overlay,101,-1,120,190 animation101.interval,never animation101.pattern2,overlay,102,-1,120,190 animation101.interval,never animation101.pattern3,overlay,103,-1,120,190 animation101.interval,never animation101.pattern4,overlay,104,-1,120,190 animation101.interval,never animation101.pattern5,overlay,105,-1,120,190 animation101.interval,never animation101.pattern6,overlay,106,-1,120,190 animation101.interval,never animation101.pattern7,overlay,107,-1,120,190 animation101.interval,never animation101.pattern8,overlay,108,-1,120,190 animation101.interval,never animation101.pattern9,overlay,109,-1,120,190 // 分の10の位 (ID 102) animation102.interval,never animation102.pattern0,overlay,100,-1,213,230 animation102.interval,never animation102.pattern1,overlay,101,-1,213,230 animation102.interval,never animation102.pattern2,overlay,102,-1,213,230 animation102.interval,never animation102.pattern3,overlay,103,-1,213,230 animation102.interval,never animation102.pattern4,overlay,104,-1,213,230 animation102.interval,never animation102.pattern5,overlay,105,-1,213,230 animation102.interval,never animation102.pattern6,overlay,106,-1,213,230 // 分の1の位 (ID 103) animation103.interval,never animation103.pattern0,overlay,100,-1,280,255 animation103.interval,never animation103.pattern1,overlay,101,-1,280,255 animation103.interval,never animation103.pattern2,overlay,102,-1,280,255 animation103.interval,never animation103.pattern3,overlay,103,-1,280,255 animation103.interval,never animation103.pattern4,overlay,104,-1,280,255 animation103.interval,never animation103.pattern5,overlay,105,-1,280,255 animation103.interval,never animation103.pattern6,overlay,106,-1,280,255 animation103.interval,never animation103.pattern7,overlay,107,-1,280,255 animation103.interval,never animation103.pattern8,overlay,108,-1,280,255 animation103.interval,never animation103.pattern9,overlay,109,-1,280,255 } surface100 { element0,base,digit_0.png,0,0 } surface101 { element0,base,digit_1.png,0,0 } surface102 { element0,base,digit_2.png,0,0 } surface103 { element0,base,digit_3.png,0,0 } surface104 { element0,base,digit_4.png,0,0 } surface105 { element0,base,digit_5.png,0,0 } surface106 { element0,base,digit_6.png,0,0 } surface107 { element0,base,digit_7.png,0,0 } surface108 { element0,base,digit_8.png,0,0 } surface109 { element0,base,digit_9.png,0,0 } ざっくりとした説明になりますが、前半は画像の表示や位置の調整、後半はIDと画像の指定を行っています。 動かしてみよう! さて、再起動して動かしてみます! ??????? というわけで失敗しました。 時刻が正しく表示されない 時を刻まない 数字が消える ときちゃんもたまに消える テストで入れたおしゃべりの時報だけは正しいので、アニメーションや画像連携周りがおかしいのだと思います。 年を越してしまうのでアドベンドカレンダーとしてはこんな感じで終わろうと思います。 いかがでしたか?(やけくそ) 修正できたらまたブログ書きます。orz 余談 そういえば、VOICEVOXなどで声をあてる機能がデフォルトで付いています。 VOICEVOXインストール後、設定>音声認識/合成>音声合成の設定>ボイスID横の参照ボタンから声を選んでください。 キャラを右クリックして出るメニューから機能>音声合成を押して有効にしたら声がつきます。 最後に 時計作りは失敗しましたが、楽しかったので是非触ってみてください…! 関連記事など ばぐとら研究所 …伺かを構成しているソフトウェアが配布されています。初めはフルセット版のSSPをDLしましょう。 ukadoc …伺かのWikiです。 AYAYA/03 …YAYAのWikiです。 紺野ややめ …YAYAのテンプレートゴースト、紺野ややめちゃんの紹介サイトです。 【無料頒布】日本語フォント「ビルの谷間と高架下」を作りました …お気に入りのフォント制作者様のnoteです。 VOICEVOX …おしゃべりに声を付けたい人は是非。春日部つむぎちゃんが好きです。 著作権周り SSPの基本情報 この記事で紹介した「SSP」は、C.Ponapalt様およびDOIchan!様に著作権が帰属するフリーウェアです。 ヘルプドキュメント(Chameleon Ponapalt様、す~ちゃん様著作)に基づき紹介しています。 「YAYA」テンプレートゴースト 紺野ややめ シェル作者サトウ M様、ゴースト作者はリンク先のGithubを参照ください。 Public Domain (Unlicense)です。
アバター
この記事は、 ニフティグループ Advent Calendar 2025 20日目の記事です。 はじめに こんにちは。ニフティの並木です。 今回は、Pythonのpatch.objectについてご紹介します。 ・本記事の動作確認環境:Python 3.13 ・公式ドキュメント: https://docs.python.org/ja/3.13/library/unittest.mock.html patch.objectとは patch.object は、Pythonのオブジェクト(インスタンスやクラス)の一部を、一時的にモックに置き換える機能で、テストの時に活用することができます。 具体的なテストコードを見ていきます。 import unittest from unittest.mock import patch # テスト対象のクラス(実際の開発では別ファイルにある想定) class FileManager: def _get_latest_record(self): """DBにアクセスして最新のファイル名を取得する複雑なメソッド""" # 中身は省略 pass def generate_file_name(self): """新しいファイル名を生成するメソッド""" latest = self._get_latest_record() if latest is None: return "file001.csv" return f"file_{latest}.csv" # テストコード class TestFileManager(unittest.TestCase): def test_generate_file_name_when_db_is_empty(self): """DBにレコードがない(Noneが返る)場合の挙動を確認するテスト""" file_mgr = FileManager() # _get_latest_record が None を返すように一時的に置き換える with patch.object(file_mgr, '_get_latest_record', return_value=None): # 内部で _get_latest_record が呼ばれる result = file_mgr.generate_file_name() # 検証 self.assertEqual(result, "file001.csv") if __name__ == "__main__": unittest.main() 本来、 _get_latest_record というメソッドは、実際にDB見に行って「最後に保存されたファイル名」を探してくる複雑な処理を行っています。 しかし、今回のテストでやりたいことは「DBに該当レコードが無い時の挙動」を確かめることです。 実際にDBを空にするのは大変ですし、他のテストに影響が出てくるかもしれません。 そこで、 patch.object を使い 「このテストの間だけ、 _get_latest_record は何も考えずに None を返すようにして」 と命令しているのです。 with ブロックを使っている理由は、 「テストが終わったら、 _get_latest_record をモックオブジェクトから、本物に戻すため」 です。 with ブロックを抜けると、 モックオブジェクト に置き換わっていたメソッドが、自動的に 元の本物のメソッド へと戻されます。 patch.object の書き方 with patch.object(対象のオブジェクト, "メソッド名などの文字列", return_value=返り値): 第1引数 : ターゲットとなるインスタンスそのものを渡します。今回はインスタンスを渡していますが、クラスを渡すこともできます。 第2引数 : 置き換えたいメソッドの名前を「文字列」で指定します。メソッド名を打ち間違えても、実行するまでエラーに気づきにくいため注意が必要です。 return_value : そのメソッドが呼ばれたときに、何を返してほしいかを指定します。上の例ではNoneを設定していました。 おわりに Pythonのpatch.objectについてご紹介しました。 テストコード作成の際にぜひ利用してみてください。 また、デコレータを使ってモックオブジェクトを作る方法もあるそうです。機会があればこちらも使ってみたいと思います。 ※公式ドキュメント: https://docs.python.org/ja/3.13/library/unittest.mock-examples.html#patch-decorators 明日は、nemu_nemu_musashi さんの「デスクトップマスコットを作りたい」です。お楽しみに!
アバター
この記事は、 ニフティグループ Advent Calendar 2025 22日目の記事です。 はじめに こんにちは。添野隼矢です。 本記事では、NotionDBに新規で追加されたページを、Notion Automationを使ってSlackに通知する方法をご紹介します。 Notion Automationについては、こちらをご参照ください。 https://www.notion.com/ja/help/guides/category/automations 背景 ニフティでは、技術やイベントレポートなど、共有したい資料やレポート、メモを書くためのNotionDBがあります。 多くの人にこのNotionDBに新しく追加されたページを見てもらいたいという要望があり、NotionDBに新しくページが追加された際に「新しくページが追加されました!」と通知されるチャンネルを作りたいという意見が出ました。 どうすれば簡単に通知できるか悩んでいたところ、Notion Automationが使えるかもという助言を頂き、実装できましたので、本記事にてご紹介したいと思います。 要件 Notion Automaitonを使用 ページ作成通知で通知してほしいもの ページタイトル ただし、ページタイトルが作成後きまっていないもの/未設定のものはuntitled pageというタイトルにする 作成者の名前 メンション通知は不要 例えば、AさんがNotionDBにページを追加した際、SlackにAさんのメンション(@Aさん)ではなく、Aさんの名前を通知させたいです。 作成されたページへのリンク Notion Automationで通知されるとNotionへのリンクボタンが自動で通知されます。 作成手順 通知したいNotionDBのNotion Automaiton作成画面を開きます。 新規トリガーで追加されたページを選びます。 ここで、要件の通り、通知の際にページ作成者へのメンションを防ぐための変数とタイトルがない場合にuntilted pageとする変数を追加します。 ■ ページ作者変数(今回はcreater_nameという変数名にしました。) ■ ページタイトル変数(今回はtitleという変数名にしました。) 次にSlack通知を送信するアクションを追加します。 通知するチャンネルを選んで、カスタムメッセージを以下のように編集します。 有効化してテストします テスト ページを追加してみました。 Slackに通知されました。 おわりに 今回は、Notion Automationを使ってNotionDBに新規で追加されたページをSlackに通知する方法をご紹介しました。 みなさまの何かのお役に立てれば幸いです。
アバター