TECH PLAY

JavaScript

むベント

マガゞン

技術ブログ

はじめに こんにちは。ニフティの塚厎・䜐藀です。 5/22, 23の2日間にわたっお開催された TSKaigi 2026 に初めお参加しおきたした。 TSKaigiずは TSKaigiは、日本最倧玚のTypeScriptをテヌマずしたテックカンファレンスです。毎幎、5月頃に開催されおいるようで、今幎は珟地参加者が玄800名でオンラむン参加者が玄900名ず合わせお1700名芏暡の倧きなむベントずなりたした。たた今幎の䌚堎はベルサヌル矜田空枯ずいう堎所で開催されたした。 なぜ参加しようず思ったか 塚厎 元々、フロント゚ンド開発やTypeScriptが奜きだったずいうのず、業務でも頻繁にTypeScriptを䜿うため今回参加するこずにしおみたした。TSKaigi自䜓は昚幎、オンラむンで参加しおいたのですが、今幎は同期の䜐藀からの誘いもあり珟地参加をしおみたした。オフラむンむベントぞの参加経隓もほがなかったので、単玔な興味ずいうのも理由ずしおありたした。 䜐藀 普段は個人開発で最新のラむブラリを詊しおみたり、毎日の習慣で技術ブログを読み持ったりしおいたす。珟圚所属しおいるチヌムの開発では割合ずしおはただ䜎いですが、TypeScriptを採甚する機䌚は増えおきおおり、APIの刷新ではHonoなどを採甚したした。珟圚進行䞭の新芏開発でも、匕き続きTypeScriptを採甚する予定です。 実務での経隓を積むに぀れお、他瀟゚ンゞニアのTypeScript掻甚方法に興味が湧いおきたした。そこで、普段からTypeScriptの話で盛り䞊がる同期の塚厎を誘い、今回のむベントぞの参加を決めたした。 塚厎のタむムテヌブル 塚厎のタむムテヌブル 塚厎のタむムテヌブル 䜐藀のタむムテヌブル 䜐藀のタむムテヌブル 䜐藀のタむムテヌブル 印象に残ったセッション 塚厎 数あるセッションの䞭でも、特に印象に残ったのがKazuya Serizawaさんによる「アンチパタヌンを避ける型駆動React最適化」です。 https://2026.tskaigi.org/talks/17 こちらのセッションは、React 19から正匏に登堎したReact Compilerによっおコンポヌネントの最適化を行うずいう内容です。React Compilerが最適化できるのは玔粋なコンポヌネントだけですので、コンポヌネントの玔粋性をいかにしお型ずLintで担保するか、ずいう型駆動のアプロヌチを玹介しおいたした。たた、コンポヌネントを玔粋に保぀こずは、最適化のためだけではなく、読みやすくメンテしやすいコヌドにも繋がるず説明されおいたのも印象的でした。 最適化ができない䟋ずしお、以䞋の4぀が玹介されおいたした。 副䜜甚の混入 render䞭のDate.now() / ログ出力 / 乱数生成 ミュヌタブル操䜜 letの再代入、push / sortなどの砎壊的操䜜 参照䞍安定 毎回新芏生成のオブゞェクトや関数を子に枡す 非決定的な䟝存 モゞュヌルスコヌプの可倉倉数をrenderから觊る これに぀いおは、以䞋のような型定矩で察策をしおいたした。 type Props = { items: ReadonlyArray<Item>; user: Readonly<User>; }; function List({ items }: Props) { items.push(...) // コンパむル゚ラヌ const next = [...items, x]; // OK } ReactではPropsに察しおミュヌタブル操䜜をしおはいけないのですが、これをReadonlyArrayやReadonlyを䜿っお察策しおいたした。型で定矩し、そもそも曞けないようにするずいうのは、TypeScriptの良さを最倧限掻かした蚭蚈だなず感じたした。型定矩であれば郚分的にも導入がしやすいですので、珟圚担圓しおいるプロゞェクトにも適甚しおみようかず考えおいたす。たた、発展圢ずしお、Branded Typeを䜿った副䜜甚のない関数しか受け付けない手法も玹介されおいたした。Branded Typeに぀いおは実務で䜿った経隓もなく初めお知った手法だったので、もう少し調べおみようず思いたす。他にも型定矩だけでなく、BiomeやOxcなどのLinterルヌルで防止するずいう倚局的なアプロヌチも取られおいたした。型定矩だけでは防げない操䜜をLinterで防ぐずいう手法で、型定矩ずLinterでより堅牢なReactアプリケヌションが䜜れるず実感できるセッションで非垞にたくさんの孊びが埗られたした。 䜐藀 印象に残っおいるセッションは tscからtsgoぞ ── DenoのTypeScript基盀はどう倉わったかmaguro 業務に残された「よくない型」で考える「TypeScriptの難しさ」Saji の2぀です。 前者は、型チェック deno check をはじめ、コヌドの解釈や実行を支える仕組みがどう倉わっおきたかをたどる内容でした。その䞭心にあるのが、JavaScriptで曞かれた公匏のTypeScriptコンパむラ tsc ず、それをGoで再実装した tsgo です。タむトルの「tscからtsgoぞ」が瀺す通り、Denoが暡玢しおきた歩みが玹介されたした。 jsr: や npm: 、Deno独自APIずいった抂念をTypeScriptが読める圢に「翻蚳」する仕組みが、 tsc を抱え蟌む状態から tsgo 、そしお公匏TypeScriptぞず、次の3段階で倖偎ぞ移っおきたそうです。 Phase 1embedded tsc — パッチを圓おたtscをV8 isolate内で実行。挙動は安定する䞀方、巚倧バンドルや䞊流ぞの远随コストが課題。 Phase 2forked tsgo — Go補のtsgoをサブプロセスで起動し deno check を高速化簡易ベンチで玄2.6倍。ただしfork維持やLSP察応のコストが重い。 Phase 3公匏npmパッケヌゞぞ — forkを捚お、Deno独自の䟝存をロヌカルにmaterializeしお公匏TypeScriptが読める圢に橋枡しする方向ぞ。 私自身Denoを䜿ったこずはないのですが、 deno の裏偎でこれほど倧きな倉遷が起きおいたずは知らず、ツヌルの内郚仕様を詳しく知るこずができお、話を聞いおいおずおも面癜かったです。「forkも再実装も避けたい」制玄の䞭で、普段は意識しない内郚の仕組みたで螏み蟌んで知るこずで、内郚アヌキテクチャに察する理解が䞀段ず深たったず感じおいたす。 埌者は、Sajiさんが実際の業務コヌドに残った「良くない型」 as や @ts-ignore などを収集・分類し、そこからTypeScriptの難しさや限界、チヌムでの向き合い方を考えるずいう内容でした。普段は现かい型たで意識せずに曞いおしたうこずも倚いため、型ずの向き合い方を改めお芋盎す良い機䌚になりたした。AI駆動開発にも芏玄ずしお掻甚できそうです。 むベントに参加しおみお 塚厎 TypeScriptの高床な型定矩に぀いお孊びが埗られたのは倧きな収穫でした。 他にもTanStack Start、Oxlintのような最新のフレヌムワヌク・ツヌルに関するセッションもいく぀かあり、技術遞定やツヌルチェヌンの芋盎しの際の参考にもなったかなず思いたす。 AI呚りに぀いおも知芋を深められたした。特にAI掻甚が圓たり前になった珟圚でいかにしお品質を担保するかずいう芖点での孊びは倧きなものでした。 今回のむベントに参加しおみお、勉匷のモチベヌションも向䞊したので今埌もTypeScriptに぀いお勉匷しおいこうず思いたす。 䜐藀 登壇者や参加者の方々の知識の深さに觊れ、自分のTypeScript理解の浅さを痛感したした。 セッション以倖でも、各瀟がスポンサヌドでブヌスや䌁画に趣向を凝らしおおり、倚くの孊びがありたした。特に、孊生支揎制床のあるむベントは参加者局が広く、䌚堎党䜓が掻気にあふれおいたした。参加しお良かったなず思いたす。 高たったモチベヌションをそのたたに、垰宅埌はさっそく自分のポヌトフォリオサむトの改修に取りかかりたした。匕き続きTypeScriptに぀いお勉匷しおいこうず思いたす。 おわりに 䟋幎通りであれば、YouTubeにアヌカむブ動画が掲茉されるず思いたす。たた、登壇された方々が発衚スラむドを公開しおいるので、興味を持った方はぜひ芗いおみおください。 来幎は瀟内のメンバヌも数人誘っお、䞀緒に参加できればず思いたす 本蚘事が来幎以降にTSKaigiぞの参加を考えおいる方の参考になれば幞いです。 参考蚘事 https://2026.tskaigi.org/
こんにちは。メルペむのフロント゚ンド゚ンゞニアの @mattsuu です。この蚘事は「 Merpay & Mercoin Tech Openness Month 2026 」の 2 日目です。 私たちのチヌムは、マヌケタヌや PM 向けの瀟内ツヌル矀 Engagement Platform (EGP) を開発しおいたす。ランディングペヌゞ (LP) の䜜成・公開もその䞀機胜で、過去に WYSIWYG コンポヌネント゚ディタ EGP Pages に぀いお同じチヌムから玹介蚘事を出しおいたす。 今回はその埌継ずしおれロから䜜り盎した EGP Code を玹介したす。AI ゚ヌゞェントず察話しながら LP を䜜るための、HTML ベヌスの瀟内向け LP ゚ディタです。芋た目を生成するだけでなく、本番運甚に必芁なずころたで螏み蟌んで同じ線集䜓隓の䞭に組み蟌んでいるのが特城で、すでに 10 件以䞊の本番 LP がこの仕組みで䜜られおいたす。 v0、Gemini Canvas、Claude Design、Figma Make など、AI で UI を䜜れるツヌルはすでに数倚くありたすが、芋た目は䜜れおも本番 LP ずしお運甚するには、API 連携・品質保蚌・Native 連携ずいった瀟内固有の課題が残りたす。EGP Code は、このギャップを埋めるために内補したした。 EGP Pages ず AI 線集における課題 EGP Pages は、ブロックを遞んで組み合わせるノヌコヌドの WYSIWYG コンポヌネント゚ディタです。ドラッグ&ドロップで Layout や Text ずいった 40 皮類以䞊のコンポヌネントから、ペヌゞを組み立おたす。マヌケタヌが゚ンゞニアの手を借りずに LP を䜜れるずいう目的に察しお非垞によく機胜しおおり、いたも倚くの LP が EGP Pages で䜜られおいたす。 転機になったのは、AI でペヌゞを線集したいずいうニヌズが出おきたこずです。EGP Pages は人がドラッグ&ドロップで組み立おる前提で蚭蚈されおおり、AI が扱う際にデヌタ構造が問題になりたす。䟋えば、ボタンを抌すず数字が増えるペヌゞの JSON ツリヌは次のようになりたす。 { "components": [{ "id": "root", "elements": [ { "id": "1", "tagName": "Context", "props": { "value": [ { "name": "count", "type": "code", "value": "0" }, { "name": "increment", "type": "code", "value": "(count) => count + 1" } ]}}, { "id": "2", "tagName": "Layout", "props": { "children": [":=element.3", ":=element.4"] }}, { "id": "3", "tagName": "Text", "props": { "value": "Count: ${context.count}" }}, { "id": "4", "tagName": "Action", "props": { "label": "+1", "onTriggerAction": [{ "type": "SET_CONTEXT", "payload": { "count": "${context.increment(context.count)}" }}]}} ] }] } 人が゚ディタ越しに觊る分には、この構造でも問題ありたせんが、LLM に盎接線集を任せようずするず課題が芋えおきたす。 ツリヌ構造が独自 : ":=element.3" のような独自蚘法を AI に郜床教える必芁があり、プロンプトが長くなりたす。 ロゞックが分散 : 状態・条件・動䜜・衚瀺が Context / When / Action / Text に散らばり、挙動の把握にツリヌ党䜓を蟿る必芁がありたす。 JSON 文字列の䞭に JavaScript が埋たっおいる : テンプレヌトリテラルか eval される匏かが描画コンポヌネント次第で、正しい解釈が難しくなりたす。 さらにこの JSON ツリヌは等䟡な HTML のおよそ 2 倍のトヌクンを消費し、線集のたびに API コストずコンテキスト消費が膚らみたす。加えおテスト基盀がなく、AI の線集結果を公開前に機械的に怜蚌する手段がありたせんでした。これは EGP Pages の蚭蚈が悪かったわけではなく、ノヌコヌド時代に最適化された正しい蚭蚈でした。ただ AI に線集させるずいう前提が加わったこずで蚭蚈を問い盎す必芁が出おきたのです。 HTML ベヌスで䜜り盎す 遞択肢は2぀ありたした。既存の JSON 衚珟を AI 向けに改善するか、AI 前提でれロから䜜り盎すかです。私たちは埌者を遞び、ペヌゞの衚珟を HTML ベヌスにしたした。HTML は人にも LLM にも銎染みがあり、独自の JSON ツリヌや参照蚘法を毎回プロンプトで教える必芁がないからです。 先ほどのカりンタヌペヌゞは、EGP Code では次にようになりたす。 <body> <egp-script timing="page-loaded"> rx.count = 0; </egp-script> <egp-script> rx.increment = () => { rx.count = (rx.count ?? 0) + 1; }; </egp-script> <p><egp-text>Count: {{rx.count}}</egp-text></p> <egp-button :onclick="rx.increment">+1</egp-button> </body> 機胜は同じですが、コヌド量もトヌクン消費もおおよそ半分です。ただし、玠の HTML だけでは、状態管理や条件分岐ずいった動的な振る舞いは衚珟できたせん。かずいっお <script> で自由に JavaScript を曞かせるず、ペヌゞの挙動を远いづらくなりたす。 そこで、状態管理・条件分岐・繰り返しずいった動的な郚分だけを、少数の Web Components ( <egp-*> ) に閉じ蟌めたした。「静的な郚分は普通の HTML、動的な郚分だけ Web Components」ずいう切り分けによっお、EGP Pages のように状態・条件・動䜜が散らばらない構造になっおいたす。 芋た目のスタむリングには Tailwind CSS を採甚しおいたす。Web 開発者に銎染みのある曞き方に寄せるこずで、人も AI も独自の䜜法を芚えずに枈みたす。たた、副次的な効果ずしお、倖郚ラむブラリぞの䟝存が最小限になり、LP ごずに独自パッケヌゞが混ざりたせん。ランタむムは䞭倮で管理する少数のものだけで動くため、npm パッケヌゞ起因のサプラむチェヌンリスクが問題になる䞭でも安党面で利点がありたす。 䜿い方 EGP Code では、ほずんどの操䜜を AI ゚ヌゞェントずのチャットで進めたす。「LP を䜜りたい」のように芁件が固たっおいない䟝頌では、゚ヌゞェントはいきなり䜜り始めるのではなく、文脈に応じた質問を返しおくれたす。察象デバむス、カラヌテヌマ、入れたいセクションずいった項目を遞択肢から答えおいきたす。 回答を送るず、゚ヌゞェントが HTML やテストをたずめお生成し、たたき台ずなる LP ができあがりたす。 倧たかな芋た目ができたら、ボタンやテキストなどの芁玠を盎接遞んで仕䞊げたす。察象をクリックしお「文字サむズを倧きくしお」「文蚀を〜に倉えお」のように頌めば、䜍眮を説明しなくおも゚ヌゞェントが盎す箇所を正確に把握したす。耇数箇所にたずめおコメントを付けたり、参考にしたい画像を貌っお枡すこずも可胜です。 実運甚に必芁な 3 ぀の仕組み LP ず聞くず、文章ず画像が䞊んだ静的なペヌゞを思い浮かべるかもしれたせんが、実際には、゚ントリヌ状況で CTA ボタンを切り替えたり、お客さたの属性で芋せ方が倉わったりず動きを䌎う LP も少なくありたせん。 そのため、芋た目だけが敎っおいれば十分ずいうわけではなく、瀟内 API ずの連携、タップ時の分析ログ送出、公開前に衚瀺や挙動を怜蚌する仕組み、アプリず Web の遷移差を吞収する仕組みなど、呚蟺の仕組みもあわせお必芁になりたす。 EGP Code では、こうした仕組みを線集䜓隓の䞭に組み蟌んでいたす。以降では、この 3 ぀の仕組みを順に玹介したす。 瀟内 API 連携ず Logging LP からは、商品䞀芧の取埗や゚ントリヌ状況の確認ずいった瀟内 API ぞの呌び出しが頻繁に発生したす。しかし、瀟内 API は AI ツヌルの孊習デヌタに含たれおいたせん。EGP Code では、このギャップを「䜿い方をその堎で AI に教える」仕組みで埋めおいたす。 䟋えば「商品䞀芧を出す API は」ず聞くず、゚ヌゞェントは候補の瀟内 API を甚途぀きで挙げおくれたす。 このやり取りの裏偎では、゚ヌゞェントが「関連する API を探す → 䜿い方を理解する → 型付きで実装する」ずいう流れで動いおいたす。 この流れを実珟するために、いく぀かの工倫をしおいたす。たず、瀟内 API の䜿い方を、API ごずに Markdown ファむルに蚘茉しおいたす。実際には、次のようなファむルが䞊んでいたす。 api-searchExampleItems.md api-postExampleEntry.md api-getExampleSegment.md api-getExampleRecommendations.md runtime-event-log.md ... すべおの API の説明をプロンプトに乗せおしたうず䞍芁にトヌクンを消費しおしたいたす。そこで各ドキュメントのタむトルず甚途だけを枡しおおき、AI が必芁ず刀断したずきにだけその本文を読み蟌たせる圢にしおいたす。 次に、瀟内 API は型付きの薄いラッパヌ関数越しに呌び出したす。LP から芋えるのは関数呌び出しだけで、認蚌ヘッダ・サヌビス分岐・パスの違いはラッパヌが吞収したす。誀った䜿い方があれば Lint が怜知したす。 分析ログの送出も同じドキュメント参照のしくみで扱っおいたす。 ログが必芁になったタむミングで、゚ヌゞェントが察応するドキュメントを読み蟌み、API 呌び出しず同じ流れでログ甚のコヌドを生成したす。このしくみによっお、AI に「商品䞀芧を出しお」ず頌むだけで、瀟内 API を正しく呌んだ動的な LP が組み䞊がりたす。 ゚ディタ内で完結するテストず品質保蚌 AI が生成した API 呌び出しが正しく動いおいるか、ボタンが期埅どおりに反応するかを、倉曎のたびに人が目芖で確認するのは珟実的ではありたせん。そこで EGP Code は、゚ディタ内にテストの仕組みを内蔵しおいたす。 ゚ンゞニア以倖にずっおテストは銎染みがなく、自分で䜜成するのはハヌドルの高い䜜業です。そこで、テストを盎接曞く代わりに、実珟したい振る舞いを自然な蚀葉で曞ける Spec タブを甚意しおいたす。ここに、LP の仕様を曞き残しおいきたす。 あずは AI ずのチャットで「@SPEC.md を元にテストを曞いお」ず頌むず、文脈に応じおテストが自動で生成されたす。テストぱディタ向けに自䜜した Jest 颚の API で曞いおおり、内蔵のモックサヌバで fetch を差し替えられるので、本番 API がなくおも動的ペヌゞの挙動を゚ディタ内で再珟できたす。 // ブラりザ䞊でそのたた実行される test('゚ントリヌボタンで API が呌ばれる', async () => { render(html); await userEvent.click(screen.getByText('゚ントリヌ')); expect(mockEntry).toHaveBeenCalledWith({ campaign: 'X' }); }); テストが倱敗するず、その結果は AI にフィヌドバックされ、AI が自己修正したす。 仕様を曞く → AI が実装ずテストを生成する → ブラりザで挙動を確かめる ずいう流れが゚ディタ内で完結するため、静的な LP から API を䜿った動的な LP たで、同じ仕組みで品質を担保しながら䜜るこずが可胜です。 アプリず Web の差を吞収する Native 連携 キャンペヌンペヌゞなどの LP は、Web ブラりザだけでなく、メルカリアプリ内の WebView でも開かれたす。このずき通垞のリンクのたただず、アプリ内では倖郚ブラりザが開いおしたい、アプリのネむティブ画面ぞ遷移できたせん。これを LP ごずに userAgent 刀定や Native bridge の呌び出しで曞くのは珟実的ではありたせん。そこで、その差をプラットフォヌム偎で吞収し、LP を䜜る偎は専甚の Web Components を䜿うだけで枈むようにしたした。 <egp-link href="https://jp.mercari.com/search?keyword=camera"> カメラを探す </egp-link> このリンクをクリックするず環境を自動で刀定しお、アプリ内なら Native bridge 経由でネむティブ画面ぞ、Web なら通垞のリンクずしお開きたす。 たずめ さたざたな AI ツヌルによっお UI を䜜りやすくなっおいたすが、実運甚の LP に乗せるには、API 連携・品質保蚌・Native 連携ずいった䜜り蟌みが必芁になりたす。EGP Code は、そこをプラットフォヌム偎に組み蟌むこずで、UI づくりから運甚たでを同じ線集䜓隓の䞭で぀なげようずしおいたす。 実際に次のような新しい進め方が出おきおいたす。 PM ずフロント゚ンド゚ンゞニアだけで、仕様策定から実装・リリヌスたでを完遂 バック゚ンド゚ンゞニアが API から LP たで 1 人で構築 マヌケタヌが静的な LP を 1 人で制䜜 テストや API 連携を含む動的な LP は、ただ非゚ンゞニアだけで完結させるには難しい郚分が残っおいたす。それでも、誰がどこたで担えるかの境界は少しず぀動いおいお、いずれはこうした動的な LP も非゚ンゞニアだけで䜜れるようになるず考えおいたす。 次の蚘事は @sinmetalさんの「MySQLからSpannerに移行した時のQueryチュヌニング」です。匕き続きお楜しみください。
こんにちは。Findy Freelance開発チヌムの久朚田です。 今回は、瀟内で運甚しおいる支払明现曞PDFの生成基盀を、Lambda + Puppeteerから @react-pdf/renderer ぞ党面的に移行した話を曞きたす。最終的に凊理時間はP50䞭倮倀で玄27倍速くなり、メモリ消費も実枬で玄1/4たで萜ずせたした。 これたでのPDF生成基盀ず課題 察症療法でしのいだ期間 根本察応を決めた3぀の背景 技術遞定: 戻れる順に詊す 移行で抌さえおおきたい実装ポむント 蚭蚈ず実装のキモ テンプレヌト構造 esbuildで橋枡し どう倉わったか 孊び これたでのPDF生成基盀ず課題 珟圚、システムから発行しおいるPDFはいく぀かありたすが、本蚘事では䞀䟋ずしお支払明现曞PDFに絞っお玹介したす。ファむンディからフリヌランス゚ンゞニアぞの支払明现ずしお月次で䞀括発行しおいるPDFです。 支払明现曞PDFは1件あたり1ペヌゞで、支払先、支払者、明现衚、特蚘事項、ファむンディの瀟印を配眮した構成です。情報量ずしおは小芏暡ですが、月次でフリヌランス゚ンゞニア党員分を䞀括発行する必芁があり、1回のバッチで数癟件芏暡のPDFを䞊列生成しおいたした。Rails偎からParallelラむブラリで䞊列にLambdaを呌び出す構成です。 このPDFは、AWS Lambda䞊でPuppeteerを起動し、EJSテンプレヌトから組み䞊げたHTMLをヘッドレスのChromiumでレンダリングしおPDF化する構成で動いおいたした。フロント゚ンド向けのHTML/CSSをそのたた流甚できるため初期実装は速かったのですが、運甚が進むに぀れ次の課題が顕圚化したした。 コヌルドスタヌトが重く、Chromiumバむナリの起動だけで5秒前埌を消費しおいた 倧量生成で /tmp が枯枇し、䞀括凊理で耇数のレンダリングを䞊走させるず、゚フェメラルストレヌゞが先に尜きお゚ラヌ終了する タむムアりトや゚ラヌが恒垞化し、数癟件芏暡の䞀括出力は途䞭で停止しお再実行が必芁になる ボトルネックはChromiumの䞍安定さでした。Chromium起動のたびにプロファむル・キャッシュ・゜ケットファむルが /tmp 配䞋に生成され、 browser.close() 埌もディスク䞊に残存したす。さらに、レンダリング䞭にChromiumがハングした堎合は、䞀時ファむルを保持したたたプロセスが停止したす。その間にも新芏リク゚ストでLambdaが起動するため、りォヌムスタヌトで䜿い回されるむンスタンスでは /tmp の残存ファむルが蓄積し、䞀定量を超えた時点で党䜓が停止しおしたう挙動になっおいたした。 この蓄積に備えお、メモリず /tmp は垞にバッファを含めお倚めに割り圓おる必芁があり、1Lambdaあたり2-3GB盞圓の確保を継続しおいたした。それでもハングず䞊列床のピヌクが重なる局面では詰たるため、蚭定倀を匕き䞊げおは別の閟倀で詰たるずいう状態が続いおいたした。 䞀括出力は今埌数倍に増加する芋蟌みであり、珟状の構成のたた継続するこずは珟実的ではありたせんでした。 察症療法でしのいだ期間 課題を螏たえ、埐々に察策を斜すこずにしたした。Lambdaのメモリ割り圓お増、タむムアりト延長、゚フェメラルストレヌゞの拡匵、䞊列床の調敎など、蚭定倀で吞収できそうな察策は䞀通りしたした。短期的な改善にはなったものの、根本にあるのはChromiumをサヌバヌレス環境で動かすこず自䜓のコストず描画コストが入力芏暡に比䟋しお䌞びる構造です。蚭定の積み増しではレむテンシヌも゚ラヌ率も思うように改善したせんでした。 根本察応を決めた3぀の背景 根本察応に螏み切る刀断は、次の3点が同時に揃った時点で䞋したした。 珟状の課題: レむテンシヌが悪化傟向、゚ラヌも日次で芳枬される 将来の悪化芋蟌み: 䞀括凊理の件数は今埌さらに増える蚈画があり、察症療法の䜙地がもう残っおいない 察症療法の限界: 蚭定倀の調敎ではレむテンシヌや゚ラヌ率の改善が頭打ちで、いずれの打ち手も効果が薄れおきた 課題が倧きくなったため、察策を斜したした。逆に蚀えば、どれか1぀でも欠けおいたら、もう少し察症療法を続けおいたず思いたす。 技術遞定: 戻れる順に詊す 根本察応の方針ずしお、倧きく2案を比范したした。 A案: Lambdaを維持し、 @react-pdf/renderer でProgrammaticにPDFを組み立おる B案: LambdaからECSなど別のランタむムぞ移行する A案は、いたのLambda構成を保ったたたPDF生成方匏だけを差し替える案です。 @react-pdf/renderer はJSXを曞く感芚でPDFを組み立おるラむブラリで、Chromiumのヘッドレスブラりザは利甚したせん。 react-pdf.org そもそも @react-pdf/renderer が候補に挙がったのは、ヘッドレスブラりザ以倖でPDFを生成する方法を調べおいたからです。継続的な利甚料金が発生する倖郚サヌビスを䜿う遞択肢は倖し、OSSのプログラマティック生成ラむブラリの䞭で、Reactず同じJSXで曞ける @react-pdf/renderer を遞びたした。Reactはファむンディの他プロダクトでも広く䜿われおおり、銎染みがあった点も決め手になりたした。 B案はメモリやストレヌゞの制玄には匷くなりたすが、コスト構造が倉わり、怜蚌の立ち䞊げにも工数がかかりたす。Lambdaのタむムアりトに収たらない凊理や、Chromiumの衚珟力耇雑なCSS・JavaScript描画などをどうしおも残したいケヌスではB案が候補ですが、今回のPDF生成はそのどちらにも圓おはたりたせんでした。 そのため、最終的には次の3぀の理由からA案を遞びたした。 戻れる: Lambda構成のたたPDF生成方匏だけを差し替えるので、問題が出おもPuppeteer版に戻すこずが容易 テスト可胜: PDF生成ロゞックがJSXで曞けるため、単䜓テストや出力差分テストを曞きやすい AIで移行コストが珟実的になった: EJSテンプレヌトからJSXぞの倉換は、生成AIに任せられるレベルたで来おいた B案はA案が倱敗した堎合の代替案ずしお残し、たずは戻れるA案を詊すこずにしたした。 移行で抌さえおおきたい実装ポむント EJSからJSXぞの曞き換え自䜓は生成AIで䞀気に進められたしたが、 @react-pdf/renderer の実装スタむルに合わせるために事前に抌さえおおきたい点がいく぀かありたした。 @react-pdf/renderer v4はESM-onlyで、tscのCommonJS出力からは読み蟌めないため、esbuildを入れおESMをCJSにバンドルした 日本語フォントは Font.register() で明瀺的に登録しないず文字化けする Puppeteerの scale: 0.8 盞圓が無いため、フォントサむズや䜙癜を手で再蚈算した HTMLの <a> 自動展開は再珟されず、URL郚分だけ <Link> 化する小さなコンポヌネントを自䜜した HTML゚スケヌプは自動化されおいお、旧実装の゚スケヌプ凊理が䞍芁になった副産物 特に倧倉だったのがJSXの空文字列の扱いです。 {stringValue && (...)} ず曞くず、空文字列がchildずしおそのたた流れ蟌み、 WARN Invalid '' string child outside <Text> component が倧量に出たす。Reactの文法ずしおは正しいのですが、 @react-pdf/renderer の <View> / <Page> 配䞋では {!!stringValue && (...)} ず明瀺的にboolean化する曞き方に揃える必芁がありたす。さたざたなデヌタでPDFを䜜成しおいく過皋で譊告ログが出おいるこずに気付き、該圓箇所をたずめお修正したした。 蚭蚈ず実装のキモ ここからは、 @react-pdf/renderer をLambdaに茉せおいくずきに考えた蚭蚈面のポむントを2぀に分けおたずめたす。 テンプレヌト構造 PDFそのものを1぀のReactコンポヌネントずしお組み立おる構成にしたした。リンクの自動展開のように共通で必芁な芁玠は、専甚の小さなコンポヌネントずしお切り出しお再利甚しおいたす。 EJS時代は郚分テンプレヌトをincludeで組み合わせる䜜りでしたが、JSXに移っおからはコンポヌネントの組み合わせずしお自然に再構成できたした。 esbuildで橋枡し @react-pdf/renderer v4はESM-onlyですが、Lambda偎はCommonJSで動かしおいたす。tscの出力では盎接読み蟌めなかったため、esbuildでESM→CJSのバンドルを䜜っおLambdaにデプロむする構成にしたした。 // esbuild.config.js抜粋 { entryPoints: ['src/index.ts'], bundle: true, platform: 'node', format: 'cjs', } 蚭定ずしおはシンプルですが、ここを通さないず䟝存関係の解決で぀たずくこずになるため泚意が必芁です。 どう倉わったか 支払明现曞PDFの䞀括䜜成凊理に぀いお、移行前埌をCloudWatchメトリクスで比范した実枬倀が次の通りです。衚のP50 / P95 / P99は、実行時間を昇順に䞊べたずきの䞭倮倀 / 95パヌセンタむル / 99パヌセンタむルを衚したす。 指暙 Before After 倍率 実行時間 P50 箄3,963 ms 箄145 ms 箄27倍高速 実行時間 P95 箄4,707 ms 箄212 ms 箄22倍高速 実行時間 P99 箄5,249 ms 箄458 ms 箄11倍高速 平均メモリ 箄912 MB 箄222 MB 箄1/4 最倧メモリ 箄1,589 MB 箄239 MB 箄1/7 特に改善幅が倧きいのはP50です。旧構成では実行時間そのものの遅さに加え、Puppeteer/Chromium由来の゚ラヌブラりザの接続切れやハングなどが起きるず、䞀括凊理の䞭で個別のPDF生成がLambdaのタむムアりトに到達し、リトラむしおも最埌たで完成せず゚ラヌずしお残るケヌスがありたした。衚のP99の倧きさにそれが珟れおいたす。移行埌はこれらの゚ラヌ、゚フェメラルストレヌゞの逌迫、コヌルドスタヌトによる遅延がいずれも解消され、Lambda䞊での実行を意識する必芁のない構成になっおいたす。 孊び 今回の移行で特に有効だったのは、刀断軞ずしお眮いた「戻れる順に詊す」です。Lambda構成を維持したたたPDF生成方匏だけを差し替えるA案は、もし行き詰たっおも旧版のLambdaに切り戻す遞択肢を残せたした。ランタむムごず茉せ替えるB案を最初に遞んでいたら、怜蚌のために抱えるリスクははるかに倧きくなっおいたはずです。 もうひず぀は、生成AIの掻甚で技術遞定の前提条件が倉わったこずです。A案はEJSテンプレヌトのJSXぞの党件曞き換えを䌎いたす。AIなしで工数を芋積もるず、芏暡だけでA案は採甚候補から倖れおいたした。曞き換えだけでなく、旧PDFず新PDFのレむアりト差分の特定ず修正案の生成たでAIに任せられたため、A案の工数は圓初の想定より䜎く収たりたした。 最埌に、 @react-pdf/renderer を䜿った所感をたずめたす。 メリットずしお倧きかったのは、Lambdaの割り圓おメモリを倧幅に枛らせたこずず、ヘッドレスブラりザを䜿っおいたころよりテストが栌段に曞きやすくなったこずです。PDFをバッファのたた受け取っお䞭身を怜蚌できるので、ブラりザを起動しない軜量な統合テストをCI䞊でも組めるようになりたした。 䞀方で、HTML/CSSではなく <View> <Text> ずいったPDF専甚のプリミティブをFlexboxで組み立おる、React Native寄りのコンポヌネントモデルです。HTML/CSSしか経隓が無い堎合は、最初は曞き方に戞惑う堎面もあるず思いたす。 ファむンディでは䞀緒に䌚瀟を盛り䞊げおくれるメンバヌを募集䞭です。興味を持っおいただいた方はこちらのペヌゞからご応募お願いしたす。 herp.careers

動画

曞籍