jihwang.kim
Professional Project

Solution Square (Global)

2024.10 - Present

概要

製品情報と技術資料を確認し、必要な製品を選定するLS ELECTRICのグローバル顧客ポータルです。複数の地域と言語に対応し、製品の選定結果を相談や見積もり依頼、北米顧客ポータルでの購入につなげます。

役割・技術スタック

Java/Springベースのバックエンドサービスと、フロントエンドのリクエストを受けるAPIサーバー(BFF)で、ビジネスロジックと新規サービスを開発しました。機能開発に加え、ランタイム、デプロイ、認証などの共通基盤を整備する役割を担いました。ReactとTypeScriptのフロントエンドでは、製品、ダッシュボード、検索、社内AIアシスタント(AI Chat)の開発に参加しました。

Java, Spring Boot, React, TypeScript, Azure, Elasticsearch, Jenkins

JavaSpring BootReactTypeScriptAzureElasticsearchJenkins

実績 01

認証ロジックの共通化

背景・原因
顧客ポータルと、自動化機器の状態を監視する資産管理サービスは、同じコードを基に開発されました。管理主体が分かれた後、認証ロジックは各アプリケーションにコピーされ、別々に修正されていました。一方の修正が他方に反映されず、認証の挙動がずれて原因の特定も難しくなっていました。
検討・選択理由
認証を専用サーバーへ分ける案を先に検討しましたが、見送りました。運用対象が一つ増え、全リクエストにネットワークホップが加わるコストを、二つのアプリケーション規模で負う理由がなかったためです。リポジトリには共通モジュールを置く慣例があり、それに従う方が変更範囲を最小にできました。抽出はセッション、認証トークンとSAMLユーティリティ、JWTの順に分けました。一度に移すと、問題が起きたときに原因の範囲が広がるためです。アダプターを置き、二つのアプリケーションを一度に変えず接続点だけを置き換えました。
結果
認証ロジックの変更が一箇所の修正で完結し、両アプリケーションの認証挙動が同じになりました。移行の過程で使われていないコードと重複依存を整理し、JWTライブラリも一つに統一しました。
振り返り
レガシー改善では、一度に作り直すより既存の慣例に従って変更範囲を減らし、検証可能な単位に分けて移す方が安全です。

実績 02

通知テンプレートと受信者の管理システム構築

背景・原因
メール通知テンプレートは言語別のHTMLファイル54個としてハードコードされていました。テンプレートごとにリクエストクラスとエンドポイントがあり、通知の種類が増えるほどコードも増えていました。文言や受信者の変更にもデプロイが必要でした。
検討・選択理由
コードにあったテンプレートをデータへ移しました。文言と受信者の変更をデプロイから切り離さなければ、管理者が直接扱えないためです。テンプレートはチャネル、イベントタイプ、サービスタイプ、言語の四軸の組み合わせで識別します。既存の送信方式を使う新しい通知は、テンプレートデータの追加で対応できます。受信者はテンプレートに結び付けず、別のレジストリに分けました。組織や担当者が変わってもテンプレートを直さずに済みます。個別のREST APIの上に管理画面用の統合APIを加え、画面に必要なAPIリクエスト回数を減らしました。送信の実装は共通のテンプレート送信一つにまとめました。
結果
管理者がデプロイなしでテンプレートと受信者を管理できるようになりました。その後の新機能の通知は、送信ロジックを新しく作らず既存ロジックを再利用して接続しました。受信者と送信履歴は個人情報の暗号化対象としても整理しました。
振り返り
同じ形のコードが繰り返し増えるのは、データモデルが必要だという兆候と捉えています。識別の基準をデータへ移せば、新しい要求をコードではなくデータの追加で処理できます。

実績 03

個人情報の暗号化と検索の維持

背景・原因
個人情報保護の基準を高めるため、氏名、電話番号、メールアドレスなどのフィールドを暗号化する必要がありました。しかしAES-GCMで暗号化すると値をそのまま比較できません。値が完全に一致する行を探す既存の完全一致検索が使えなくなります。暗号化保存と運用に必要な検索を両立させる必要がありました。
検討・選択理由
暗号化したデータを保存しながら検索を維持できるよう、検索用のブラインドインデックスを併せて設けました。ブラインドインデックスは、元の値の代わりに一方向の変換値を保存する索引です。暗号化したままでも同じ値を探せます。暗号文には鍵識別子を併せて保存し、復号に使う鍵を特定できるようにしました。鍵の交換中は旧鍵と新鍵を併せて保持し、既存データも読めるようにしました。反映は書き込み経路、検索経路、過去データの補完の順に分けました。逆の順序では補完中に入ったデータが漏れ、一度に行うと戻せる地点が残らないためです。移行期間は平文と暗号文の両方を読めるようにし、途中で問題が起きてもサービスが止まらないようにしました。
結果
個人情報フィールドを暗号化して保存しながら、運用に必要な完全一致検索を維持しました。移行の途中でも画面はそのまま動作しました。過去データを補完する作業は行の状態を基準に再開でき、中断後に再実行しても安全です。
振り返り
セキュリティ要件を運用中のシステムへ適用するには、既存機能を保つ移行経路まで設計する必要があります。暗号化移行の各段階は、中断後に再実行できる状態にすべきです。

実績 04

デプロイ自動化と承認ベースのロールバック

背景・原因
三つのモジュールをデプロイするたびに、担当者が前のビルドの完了を待ち、次のデプロイを順に実行していました。三つを並列でビルドするには、ビルドサーバーのメモリが不足していました。誰がいつ何をデプロイしたかもチームに共有されていませんでした。
検討・選択理由
ビルドサーバーを増設する代わりに、ビルドは一つずつ順に行い、パイプラインがすぐ次のモジュールへ進む構成にしました。デプロイの完了はロールアウトと起動成功の表示で確認します。同じ対象へのデプロイが重ならないよう同時実行も防ぎました。ロールバックは自動化していません。担当者が復旧するバックアップと理由を指定して初めて承認手続きが始まります。デプロイ結果はTeamsとメールで伝えます。主な通知チャネルに障害が起きても、代替経路に結果が残るようにしました。通知が一つのチャネル障害で途切れると、自動化そのものを信頼できなくなるためです。通知の失敗がデプロイ状態を変えないよう二つの経路を分けました。通知本文には認証情報と内部アドレスを含めていません。
結果
ビルドサーバーを増設せずに、担当者が次のデプロイを実行するために待つ過程を減らしました。デプロイは人が順序を覚える作業から、決まった段階に従う手続きに変わりました。結果はTeamsとメールで自動共有されます。
振り返り
通知は成功経路だけでなく、送信失敗時の代替経路まで設計してこそ信頼できます。自動化の対象は作業そのものより、作業の間の待ち時間だという基準を得ました。

実績 05

マージ前コードレビューと認証情報の管理

背景・原因
変更レビューの品質はレビュアーによって異なっていました。コミット単位に分かれたコードレビューでは見落としが生じる余地もありました。認証情報はリポジトリとコマンド引数に含まれていました。
検討・選択理由
デプロイ前に変更をレビューするため、AIコードレビューをマージリクエストに接続しました。マージリクエストのレビューとプッシュ後の検査は別のトリガーに分けました。入力は変更差分とデプロイに影響するパスに限定しました。毎回リポジトリ全体をレビューさせるとコストだけが増え、人が結果を読まなくなるためです。承認まではツールに任せていません。認証情報はリポジトリとコマンド引数から取り除き、シークレット管理の共通コンポーネントから注入する形に変えました。
結果
AIコードレビューをマージリクエストに接続し、マージ前にセキュリティ脆弱性と潜在的な障害をレビューするようにしました。認証情報はリポジトリとコマンド引数からなくなり、注入箇所が一つにまとまりました。
振り返り
出力のマスキングは偶発的な露出を減らすだけで、境界にはならないと判断しました。認証情報は値そのものをパイプラインの外で管理するという基準を適用しました。

実績 06

ビルド環境とキャッシュの管理

背景・原因
ビルドに使うツールのバージョンと依存関係のインストール基準を、プロジェクト設定に合わせる必要がありました。ツールのバージョンや環境設定が変わったのに、以前のビルド結果が再利用されることも防ぐ必要がありました。
検討・選択理由
Nodeとpnpmのバージョンがプロジェクト設定と一致するかを検査します。依存関係はlockfileで固定したものをインストールする構成にしました。ツールのバージョンと環境設定をGradleのビルド入力に含め、値が変われば再ビルドするようにしました。Jenkinsの作業領域を整理する際は、再利用するキャッシュと依存関係のディレクトリを残すよう範囲を調整しました。
結果
ビルドに使うツールのバージョンと依存関係のインストール基準を、プロジェクト設定に合わせました。ツールのバージョンや環境設定が変われば再ビルドし、再利用するキャッシュは作業領域の整理後も残ります。
振り返り
ビルド結果を再利用するには、結果に影響するツールのバージョンと環境設定までビルド入力として管理するという基準を適用しました。

実績 07

サービス状態の診断

背景・原因
サービスの状態を診断する際、プロセス自体の問題と、DBやキャッシュなど外部依存の問題を区別する必要がありました。
検討・選択理由
状態診断を、プロセスが生きているか、リクエストを受ける準備ができたか、外部依存が正常かに分けました。外部依存の障害が、そのままプロセス再起動の判断につながらないようにするためです。
結果
三つの状態を区別するアプリケーションコードと運用指針を用意しました。クラスターの状態検査設定の適用は、別の運用範囲です。
振り返り
状態検査は、プロセスの問題と外部依存の問題を分けてこそ再起動の要否を判断できるという基準を適用しました。

実績 08

Java 11から21を経て25へ進めた段階的なランタイム現代化

背景・原因
外部で開発されたモバイルアプリが会員認証とファイル管理を使えるよう、専用APIが必要でした。このAPIをJava 21とSpring Boot 3で新しく作りました。その結果、Java 11のモジュールとJava 21のモジュールが一つのリポジトリに共存し、認証コードも複数箇所に分かれました。既存のランタイムは古いJavaバージョンに縛られていました。フロントエンドのビルドツールと検索クライアントも、サポート終了が近い世代でした。
検討・選択理由
Java 11から25へ一度に上げれば、全体の作業期間は短くできました。しかしランタイム、フレームワーク、依存関係が同時に変わると、回帰の原因と戻す範囲を切り分けにくくなります。そこで既存モジュールをまずJava 21へ移してビルドを統合し、実行とデプロイの互換性を安定させました。その後Java 25とSpring Boot 4へ広げました。Jackson 3とElasticsearch Java Client 9.xへも移行しました。コード修正の前に、ビルドツール、フレームワーク、サードパーティライブラリ、ベースイメージの互換性を一つの表にまとめました。コンパイルとテストが通っても、実行時にずれる項目があります。リフレクション、シリアライズ、TLS、タイムゾーン、エンコーディングは別の検証項目にしました。
結果
JDKを二段階に分けて上げ、段階ごとに戻せる状態を保ちました。移行後、プロキシの背後でプロトコルが異なって認識され、統合ログインの流れが壊れる問題が現れました。独自処理を取り除き、フレームワークの標準設定で復旧しました。非標準ポート環境でのサービス識別子と、元のリクエスト送信者のIP伝達も同じ方法で正しました。同じ期間にフロントエンドのビルドをCRAからViteへ、検索クライアントを7.xから9.xへ移しました。既存の検索クエリが同じ結果を返すことも検証しました。
振り返り
ランタイムのアップグレードで作業が多くかかったのは、バージョンを上げることではありませんでした。環境差で変わった挙動を見つけて復旧する作業の方が、大きな比重を占めました。

実績 09

SAML標準連携への移行とSSO拡張の基盤

背景・原因
統合ログインでは、外部の認証を受ける経路と、Solution Squareが認証結果を外部サービスへ渡す経路を区別する必要がありました。
検討・選択理由
独自実装を継ぎ足し続けると、規格が変わるたびに自前のコードが追従しなければなりません。そこで既存の独自処理をフレームワークの標準連携へ移し、プロバイダーごとの設定をレジストリに分けました。Solution Squareが認証プロバイダー(IdP)として動作するエンドポイントには、専用のセキュリティチェーンを置きました。IdPは、認証結果を他のサービスへ渡す側を指します。その経路に必要なセキュリティ規則が、他のサービスに影響しないようにするためです。認証応答の署名とトークン表記を整理し、機微な値がログに残る箇所も取り除きました。
結果
プロバイダー設定と認証処理の境界を分けました。応答を受信して検証するテスト用サービス(SP)も作りました。実際の外部サービスへ接続する前に、認証応答とログインの流れを確認できます。新しい連携を検証する基盤が整いました。
振り返り
外部サービスとの認証連携では、実環境につなぐ前に、応答とログインの流れを検証する手段を先に用意すべきだという基準を得ました。

実績 10

ルールベース製品選定ツール(Drive Panel Selector)

背景・原因
顧客は仕様に合うドライブとパネルの構成を探すたびに、技術問い合わせを行う必要がありました。営業からは、システム内で相談から見積もり問い合わせまでつながるツールが求められました。これを多段階の選定ツールとして実現しました。
検討・選択理由
推薦は学習モデルではなく、明示的なルールとして設計しました。製品構成は、オプション間の制約が明確な工学的問題だからです。誤った構成が見積もりや受注につながる領域では、予測より、誰でも検証し説明できる規則が適しています。起動方式などの選択肢は、前の段階で選んだドライブ容量に応じて絞り込まれます。顧客が成立しない組み合わせに到達できないようにするためです。結果画面のモデル画像表示、顧客の直接入力項目、管理者の問い合わせ履歴連携、重複問い合わせの防止も併せて作りました。選定ツールを画面で終わらせず、相談と見積もりの流れまでつなぐためです。
結果
顧客は技術問い合わせなしで仕様に合う構成を絞り込み、結果モデルに到達できます。その場で問い合わせや見積もり依頼へ進めるようにもなりました。
振り返り
推薦のように見える要求も、オプション間の依存関係を明示的なルールにすれば予測可能に扱えます。ツールは結果画面だけでなく、相談と見積もりまでつながって完成します。

実績 11

オンライン製品選定と北米顧客ポータルの購入連携

背景・原因
従来は営業担当者とのオフライン相談が必要で、顧客が必要な製品を選定するまでに長い時間がかかっていました。顧客がオンラインで自ら製品を選定し、購入まで進める流れが必要でした。
検討・選択理由
社内の営業担当者と製品担当者とともにセレクターを企画し、自社の要件に合わせて実装しました。前述のDrive Panel Selectorの相談と見積もり機能とは別の機能です。Servo製品の選択画面に、北米顧客ポータルの購入連携を実装しました。購入リクエストは、ログインユーザーとリクエスト識別子を基準に、選択した品目と数量を保存します。購入資格と許可された組織を確認し、同一で期限内のリクエストには既存の結果を返します。同じ識別子で品目、数量、順序を変えたリクエストは競合として扱います。同じリクエストの再送と内容が変わったリクエストを区別する必要がありました。再試行でも購入内容とユーザー権限を保つためです。北米ポータルへ移動する前には、所有権と期限を再度検査します。
結果
顧客がオンラインで製品を選定し、Servo製品の選択結果を北米顧客ポータルでの購入へつなげられるようにしました。選定した製品と購入リクエストを北米顧客ポータルへ渡す流れを実装しました。
振り返り
オンライン購入の連携では、画面をつなぐ前に、リクエストの再利用、内容の競合、権限、期限の判定方法を先に決めるという基準を適用しました。

実績 12

EAIで受信する品目データの重複防止と再処理

背景・原因
購入連携に必要な製品コードなどの基礎データは、社内システム間を連携するEAIインターフェースで受信しました。同じリクエストを再び受けても重複して反映せず、失敗した件は再処理できる必要がありました。複数のインスタンスが同じデータを同時に処理する状況にも備える必要がありました。
検討・選択理由
リクエスト識別子と業務キーにDBの一意制約を置きました。同時保存が衝突した場合は、既存の行を読み直して処理状態を判別します。成功済みの件は重複に分類します。失敗した件や処理の猶予時間を過ぎた件は、既存の受信行で再処理します。複数インスタンスの同時処理では、DBの条件付き更新で処理権を確保します。結果の記録時にも所有権トークンを検査し、以前の作業が他の作業の処理状態を上書きしないようにしました。業務データには最後に反映した順番を保存し、それより大きい受信順番だけを適用します。遅れて処理された過去の受信分が、その後に受信した値を上書きしないためです。大量リクエストは保存前に件数の上限を検査し、超過したリクエスト全体を拒否しました。一部を保存してから拒否すると、送信側がどこまで処理されたか判断しにくいためです。許容範囲内では、一件の失敗がバッチ全体を戻さないよう件ごとに処理しました。
結果
受信テーブルへの保存だけでなく、実際の業務データへの反映結果まで確認して応答に含めました。応答は成功、重複、失敗の件数を区別します。重複と失敗の項目には、位置、状態、エラーコード、エラーメッセージ、再試行の可否を一貫した形で返します。送信側が後続の処理を判断できるようにするためです。
振り返り
連携では、重複を防ぐ仕組みと失敗した件を再処理する経路を併せて設計する必要があります。重複だけを遮断すると、失敗した件を復旧する方法がなくなるためです。

実績 13

AI検索ストリームの接続維持とリクエストのライフサイクル管理

背景・原因
当初のリクエスト・レスポンス方式では回答に時間がかかるとタイムアウトが発生し、その後チームがストリーム方式を導入しました。既存のストリームは共通の非同期処理で外部サービスの応答を待っていましたが、ブラウザーの接続終了は外部リクエストのキャンセルにつながっていませんでした。回答が一時的に止まる区間に送る接続維持信号もありませんでした。
検討・選択理由
開始イベントとハートビートを送り、応答がない区間も接続を管理するようにしました。遅いブラウザーが共通タイマーを占有しないよう、定期的な書き込みは別の処理に分けました。外部応答の読み取りは同時実行数を制限した専用ワーカーに移しました。ブラウザーの切断や処理期限の超過時には外部応答を閉じ、読み取り処理をキャンセルします。完了イベントを受け取った場合だけ成功と判定します。
結果
接続維持信号と、キャンセル時に外部接続を片付ける経路を実装しました。同時リクエストの上限を超えるとすぐに拒否し、完了イベントがないまま接続が終わった場合はエラーとして扱います。
振り返り
ストリーム方式に変えるだけでは、時間のかかるリクエストの管理は終わりません。接続維持、外部処理のキャンセル、同時実行数の制限、完了判定を一つのライフサイクルとして扱う必要があります。
一覧へ戻る
(c) 2026 Jihwang Kimv20261016.01
ポートフォリオPDFをダウンロード