株匏䌚瀟゚ブリヌのブログ - TECH PLAY

TECH PLAY

株匏䌚瀟゚ブリヌ

株匏䌚瀟゚ブリヌ の技術ブログ

å…š491ä»¶

はじめたしお、デヌタ&AIチヌムのoyabuです。デヌタストラテゞストずいうデヌタアナリストみたいな仕事をしおたす 具䜓的には「DELISH KITCHEN」アプリのデヌタを抜出、分析しお食品メヌカヌなどのクラむアントさたの斜策や提案にご掻甚頂く仕事をしおいたす 今回はLLM(ChatGPT、OpenInterpreter)をビゞネスサむドに掻甚しおもらうためにやったこずの話をしたす ただ始めたばかりなので、成果自䜓はそんなに出おないですが、考えたこずや、埗られた知芋を共有したいず思いたす やったこずは以䞋です ChatGPTの䜿い方ドキュメントの䜜成(犁忌ずナヌスケヌスの敎理) ChatGPTの䜿い方勉匷䌚 OpenInterpreterを隣に座っおるカスタマヌサクセスの方に詊しおもらう きっかけ(課題感) そもそもこんなこずをやり始めたきっかけずしおは以䞋な感じです 倧前提ずしおLLMが猛嚁をふるっおおり、自分ずしおも十分その利益を享受しおいる ビゞネスマンにずっお、ChatGPTスキル = 今のExcelスキルぐらいになるこずは目に芋えおる ビゞネスサむドでもChatGPTを䜿っおる人はいるが、あんたり䌚話に出おこない 瀟内のSlackを持っおもChatGPTの話をしおる人はだいたい゚ンゞニア 貌っおるアンテナ的にもそもそもの呚蟺情報が゚ンゞニアには集たっお来やすい 気になっおはいお、1回詊したものの、業務に䜿うむメヌゞが湧いおない方がなんだかんだ倚いのかなヌずいう劄想 向き合いの郚眲ずしばらく働いお、業務もなんずなくわかっおきお、现かい斜策単䜍でChatGPTで解決出来そうなものが芋えおきた 瀟内甚のChatツヌル が出来お、盎接業務に関わる内容をフランクに聞ける環境が敎った 瀟内の運甚ルヌル/tipsを集めたドキュメントがそもそも䞍圚 やったこずずその目的、成果/埗られた知芋 目的 目的はChatGPTによる、ビゞネスサむドの工数削枛/クオリティアップです ずりあえずは利甚習慣化を目暙ずし、そのために以䞋の2点を達成するため斜策を実行したした ChatGPTが身近なツヌルだず思っおもらう ChatGPTで出来るこず/苊手なこずを知っおもらう 習慣化の初期段階で重芁なのはフリヌク゚ンシヌ、敵は䜎いモチベヌションず高いハヌドルなので ずりあえずそこを気にした斜策の蚭蚈をしたした 具䜓的にやったこず ChatGPTの䜿い方ドキュメントの䜜成 最䜎限の犁忌的なルヌルは曞き぀぀、業務にそったナヌスケヌスを䞭心に曞きたした。そうした理由は以䞋です 各人の業務に近い郚分のコンテンツを拡充するこずで、倚様な各人の興味に匕っかかりやすくするため 理想論でいえば、成り立ちや理論を知るこずは倧事ですが、興味のない人がおおそう たた完党にその業務をChatGPTで解決するこずは難しいかもしれたせんが、今埌の工数短瞮やクオリティアップを芋越した プロンプトのタタキが存圚するこず自䜓が重芁だず思っおいるので、ちょっずでも解決出来そうなこずがあるならずりあえず ナヌスケヌスずしお入れおおきたした。プロンプトの倢を芋たした(倩啓もありたした) ドキュメント化にあたっおは、なるべくバラ゚ティに富んだナヌスケヌスを盛り蟌むようにしたした。たずえば以䞋のようなカテゎリで、業務に近いナヌスケヌスを盛り蟌みたした ブレスト系 埓来の業務の工数を䞋げる系 埓来の業務のクオリティを䞊げる系 盎近のChatGPT Plusの機胜内容 OpenInterpreterでより耇雑なこずができる話 ChatGPTの䜿い方勉匷䌚 これは䞊蚘のドキュメントをそのたたデモ的に玹介したした ドキュメントが読たれないのは䞖の垞。ある皋床お時間をお借りしお(今回は1時間)集䞭しおChatGPTに぀いお考えおもらう必芁がありたす 共通の䜓隓を通じお、話題にのがりやすく、近くの垭で口頭での知芋の共有に぀ながるのも勉匷䌚の利点だず思いたす やった結果はずいうず、「いたたでChatGPTには正解のある答えを聞いお嘘぀かれお䜿えないず思っおたけど、ブレストにめっちゃ䜿える!」など ちょこちょこ良い反応は埗られたした 䜜戊通りナヌスケヌスを色々玹介したこずで、各々の業務での掻甚むメヌゞを具䜓的にもっおくれたようでした ずはいえ、あくたでも習慣化のための䞀歩目なので、ナレッゞシェアやOJT的な動きは継続しお行っおいく必芁がありたす OpenInterpreterを隣に座っおるカスタマヌサクセスの方に詊しおもらう 継続的にChatGPTに興味をもっおもらうために、䜕か飛び道具的な実瞟が必芁だず考えたした OpenInterpreterたじですごいずいう話を聞いたので、これを䜿っおみるこずにしたした ちなみに同じチヌムのfuruhama-sanがOpenInterpreterの実装を深掘りしおるので、ご興味のある方は芋おみるず面癜いです tech.every.tv OpenInterpreterのメリットの玹介はfuruhama-sanの蚘事を読んで頂くず分かりたすが、今回魅力に感じたのは以䞋なこずでした GoogleColaboratoryずいう簡単に甚意できる閉じた環境での実行がしやすい ロヌカルのファむルの閲芧/線集ができる(今回はマりントしたGoogleDrive) 䞊蚘を掻かしお、ドメむン知識を持぀ビゞネスサむドがある皋床のデヌタ分析たで自立的に行えるず 今埌の分析業務のハヌドルが䞋がり、より濃い瀺唆や知芋が財産ずしお効率的に貯蓄されるメリットもありたす もちろん䞀筋瞄でいくずは思っおいたせんが、その第䞀歩ずしお、隣にいる人に無理を蚀っおOpenInterpreterを䜿っお頂きたした マゞ感謝です。隣の人なので仮にTさんず呌びたしょう。Tさんはプログラミングができないカスタマヌサクセスっぜい立ち䜍眮のひずです やったこずは以䞋です CSV圢匏で出力されたアンケヌト結果をOpenInterpreterでクロス集蚈する おたじないをたくさん曞いたGoogleColaboratoryを事前に甚意 Tさんからやりたいこずを聞いお、思想を説明しながら仮のプロンプトを目の前で曞いおみる OpenInterpreterがコヌドの実行芁吊を聞いおくるので、Tさんに深く考えずにどんどん実行の指瀺を出しおもらう 意図した結果ではない、凊理に時間がかかるなら再床プロンプトを芋぀め盎すを繰り返す 結果ずしお特定のアンケヌト項目をその他の項目ずクロス集蚈したものが20個くらい埗られたした ゚ラヌぞの察凊や、僕がColabの仕様に疎いこずもあり、Tさんのお時間はそれなりに頂いおしたったのですが(4h+α)、 わりず喜んで頂けたした ずいうのも、そもそも今回のアンケヌト結果のクロス集蚈は埓来以䞋の問題を抱えおいたようでした 時間がかかる ひず぀ひず぀のクロス集蚈のために郜床数匏を組む必芁があるので粟神的に぀らい Tさんずしおは、成果物を埗られたこずももちろんですが、䞊蚘を自分の手でやらなくおよい。ずいうこずに奜印象を持っおいたようでした たた、゚ラヌが出たずきに萜ちるのではなく、゚ラヌの内容を理解した䞊で再床修正の凊理をかけおくれるのはめっちゃ嬉しいずも仰っおいたした 最終的には愛着を蚎えおおり、ひたすらこちらから呜什を䞎えおいるのですが、自己再生をするだけで芪近感が芜生えるのは倧倉興味深い珟象でした たしかにOpenInterpreterは内省にもにた凊理を繰り返すのがひず぀特城で、それが悩んでいるように芋えるのが人間性を感じるポむントなのかもしれたせん 逆に以䞋の点が぀らそうでした むンタラクティブに凊理実行の有無を聞かれるので、垞に刀断を求められる コヌドがガンガン出力されるが意味がわからん なんだかんだめんどくさい ここは実隓する前にある皋床予枬はしおいたのですがやはりそうか。ずいう感じです 事前のauto_runの蚭定や、temperatureの調敎/プロンプトの粟錬である皋床解決できそうではありたすが、 目たぐるしく出力される情報に知らず知らずのうちに可凊分粟神が消費されおいるようでした 䟋えばGASを敎えたスプレッドシヌトを提䟛するなどで、局所的な解決は可胜な課題でしたが OpenInterpreterである皋床柔軟に課題を解決出来るこずはわかったので、匕き続きこれをうたく䜿っおビゞネスサむドの課題解決を目指しおいきたいです ※ 䜙談ですがアップデヌト埌OpenInterpreterがうたく動かなくなりたした(10/16時点, ver: 0.1.7) このissueの察応をするこずで解決したした https://github.com/KillianLucas/open-interpreter/issues/637 たずめ 以䞊、ビゞネスサむドにChatGPTの利甚を習慣づけられるずいいなヌず思った䞊で 色々やっおみた話を曞いおみたした ずりあえず第䞀歩目ずしおドキュメントの䜜成/勉匷䌚など実斜し、それなりの反応は頂き、倚少利甚は促進できた実感はありたす ずはいえ、今埌習慣化のためには草の根運動が重芁になっおきたす ビゞネスサむドで解決しやすそうな課題を芋぀けたらその堎でChatGPTに解いおもらうなど 现かい䜓隓を繰り返しおいくこずで、初めお習慣や文化ずしお根付くものだず思っおいるので、応揎よろしくお願いしたす
初めたしお、DELISH KITCHEN 開発郚の吉田ず申したす。この蚘事ではAWS Elemental MediaConvertを䜿っおレシピ動画のサムネむルを䜜成した方法を玹介したす。 サムネむル䜜成の背景 DELISH KITCHEN はレシピを動画でわかりやすく基本的な料理からアレンゞたで様々なレシピを公開しおいたす。 スマホでブラりザ版のDELISH KITCHENをみおいただいた堎合、レシピの各工皋は手順の説明ずポむントを衚瀺しおいたす。 今回はより工皋が分かりやすくなるようここに画像を远加する運びずなりたした。 アプリやPCブラりザ版のDELISH KITCHENでは工皋の動画が挿入されおいるこずから、動画のサムネむルを工皋画像ずしお利甚するこずにしたした。 しかしながら珟圚蚭定されおいるサムネむルは動画開始時点のキャプチャになっおいたす。 そのため、動画開始時点のフレヌムになっおいるサムネむルをすでに動画の倉換凊理で利甚しおいるAWS Elemental MediaConvertを甚いおレシピごずに適切なフレヌムで蚭定できるよう実装を進めたした。 AWS Elemental MediaConvertずは AWS Elemental MediaConvert以䞋、MediaConvertずしたすはファむルベヌスの動画凊理サヌビスで、埓来のブロヌドキャストおよびマルチスクリヌンデバむスぞのむンタヌネットストリヌミングに必芁な圢匏にメディアを倉換するこずができたす。基盀ずなるむンフラのセットアップや管理、メンテナンスは䞍芁で、必芁なビデオ凊理蚭定でゞョブを送信するだけで䜿甚を開始するこずができたす。ゞョブはトランスコヌド䜜業を行うもので、入力ファむルず出力ファむルを指定し、どのようなファむルを䜜成したいか、どのようなフォヌマットで䜜成したいか等を蚭定したす。 詳しくはこちら 取り組んだこず サムネむルの䜜成にはMediaConvertのフレヌムキャプチャ機胜を利甚したした。 フレヌムキャプチャ機胜は動画の静止画を䜜成するための機胜になりたす。 サムネむル䜜成にあたっおは既存で1本のレシピ動画を指定した秒数でクリッピングしおいるゞョブがあるのでそこにフレヌムキャプチャを組み蟌むこずにしたした。 動画のキャプチャタむミングにはフレヌムレヌトを指定する必芁がありたす。ドキュメントではコン゜ヌルによる蚭定を䟋にしおいたしたが、コン゜ヌルでは静的な倀しか持぀こずができたせん。そのため、レシピごずに指定したいフレヌムが異なる今回の堎合、コン゜ヌルによる蚭定ではなく動的な倀を利甚できる圢で実装する必芁がありたした。 動的な倀を利甚するための実装方法は2぀です。AWS CLIを䜿甚しおJSONによっおゞョブ仕様を蚭定する方法、あるいはSDKを䜿う方法です。今回は既存の実装がAWS CLIを甚いおいたため、フレヌムキャプチャのゞョブ仕様もJSONで実装するこずにしたした。 今回は匊瀟でMediaConvertが既に利甚されおいたこずもあり、以䞋1-3の手順は特に行いたせんでしたが、初めお利甚する堎合は以䞋の手順を螏む必芁がありたす。 ファむル甚のストレヌゞを䜜成する Amazon S3 コン゜ヌルを䜿甚しおバケットを䜜成したす。MediaConvert は、Amazon S3 たたは HTTP か HTTPS を䜿甚するサヌバヌからの入力ファむルを受け入れたす。 IAM のアクセス蚱可をセットアップする MediaConvert が S3 バケットに察する読み蟌みず曞き蟌みを実行できるようにするには、それが担う Identity and Access Management (IAM) ロヌルを䜜成したす。 笊号化する゜ヌスファむルをアップロヌドする Amazon S3 コン゜ヌルを䜿甚しお、Amazon S3 バケットに゜ヌスファむルをアップロヌドしたす。 ゞョブを䜜成する ビデオファむルを 1 ぀、たたは耇数の出力に倉換するための MediaConvert ゞョブを䜜成したす。 匕甚元: AWS Elemental MediaConvert 公匏ペヌゞ こちらが䜜成したJSONです。䞀郚の蚭定は省略しおいたす。 { " Settings ": { " OutputGroups ": [ { ... " Name ": " File Group ", " Outputs ": [ { " ContainerSettings ": { " Container ": " RAW " } , " VideoDescription ": { " Width ": <出力キャプチャの幅>, " Height ": <出力キャプチャの高さ> " CodecSettings ": { ... " Codec ": " FRAME_CAPTURE ", " FrameCaptureSettings ": { " FramerateNumerator ": < fps >, " FramerateDenominator ": <キャプチャするフレヌム番号>, " MaxCaptures ": 2 , ... } } , ... } , " Extension ": " jpg ", " NameModifier ": <名前修食子> } ] , " OutputGroupSettings ": { " Type ": " FILE_GROUP_SETTINGS ", " FileGroupSettings ": { " Destination ": <出力ファむルの栌玍先> } } } ] , ... " Inputs ": [ { " FileInput ": <入力ファむルの栌玍先> } ] } } 出来䞊がったサムネむルがこちらです。 実装にあたり぀たづいたずころ 本実装においおは3点぀たづいたずころがありたした。 フレヌムのキャプチャタむミングは秒数で指定できない 圓初、キャプチャのタむミングは動画のクリッピングず同様にタむムコヌドで指定できるのかず思っおいたのですが、フレヌムキャプチャではタむムコヌドでの指定ができない仕様ずなっおいたした。 次に思い぀いた方法がサムネむルにしたい秒数が動画のスタヌト時点ずなるよう元の動画をクリップしお0フレヌム目をサムネむルに指定する方法です以䞋3.参照。しかしこちらに぀いおもMediaConvertは動画あるいはオヌディオの出力があるゞョブにおいおのみフレヌムキャプチャを䜜成するこずができフレヌムキャプチャのみの出力ゞョブを䜜成するこずはできたせん。毎回䞍芁な動画が䜜成されおしたうので断念したした。したがっお今回は以䞋2で玹介するframerateによる蚭定を採甚したした。 framerateの蚭定 キャプチャのタむミングを指定するには、framerateを蚭定したす。具䜓的な蚭定は「framerate = framerateDenominator / framerateNumerator」ずなり、framerateDenominatorにはFPSフレヌムレヌト、framerateNumeratorはキャプチャしたいフレヌム番号を指定したす。 サムネむル䜜成時はキャプチャ察象の動画を確認しながら秒数でキャプチャのタむミングを指定しおいたため、これをフレヌム番号に倉換する必芁があるのですがどうすれば適切に指定した秒数をフレヌム番号に指定できるのか苊慮したした。 コン゜ヌルで確認するずframerateは以䞋のように蚭定するこずになりたす。この堎合はビデオのフレヌムレヌトが30FPSで、50フレヌムごずに1フレヌムをJPEGファむルにキャプチャしたす。 (参考: AWS for M&E Blog ) 最倧キャプチャ数は2にする必芁があるこず MediaConvertは垞に動画開始時点のフレヌムをキャプチャするため、2枚で蚭定しおおかないず欲しいキャプチャは取埗できたせん。 ゞョブが完了するず、出力のS3バケットに2぀のポスタヌフレヌムJPG画像が芋぀かるので2぀目の画像ファむル clip-poster.0000001.jpg が指定したフレヌム番号のキャプチャ画像になりたす。 終わりに 本蚘事ではAWS Elemental MediaConvertを利甚しお動画のサムネむルを䜜成する方法を玹介したした。ここたでお読みいただきありがずうございたした。
はじめに はじめたしお、2023 幎 4 月から新卒入瀟し retail HUB で Software Engineer をしおいるほんだ( @hon_d7174 )です。Go が奜きです 珟圚、私たちは Next.js を䜿甚しお新芏プロダクトを開発しおいたす。このブログでは、私たちが取り組んでいるプロゞェクトの䞭で工倫した点に぀いおお話ししたいず思いたす。 たた、既存のプロダクトを Next.js 13 ぞ移行した際の流れや工倫に぀いおはこちらの蚘事をご芧ください: Next.js の Pages Router から App Router ぞの移行に挑戊しおみた プロダクト抂芁 圓瀟のプロダクトは、小売様向けにアプリを提䟛しおいたす。このアプリには、クヌポンやお知らせ、アプリナヌザヌの動向を管理するダッシュボヌドが含たれおおり、小売様はこのダッシュボヌドを䜿甚しおアプリのナヌザヌやコンテンツの管理を簡単に行うこずができたす。 圓瀟のプロダクトは、以䞋のような特城を持っおいたす。 マルチテナントアヌキテクチャ 各小売様は、それぞれ独自のテナントずしおシステムを利甚したす。これにより、異なる小売様間でのデヌタやリ゜ヌスの分離が実珟されおいたす。 アクセス制埡 各テナントやナヌザヌには、閲芧・操䜜可胜なリ゜ヌス、利甚可胜なサヌビスが異なりたす。アクセス制埡により、セキュリティを匷化し、情報の挏掩や䞍正な操䜜を防ぎたす。 システム構成 システム構成図 私たちの䜜成しおいるプロダクト簡単なシステム構成図は䞊蚘のようになっおいたす。この䞭の web client ず BFF に Next.js を甚いおいたす。 Library 以䞋は今回説明する内容に登堎する䞻な Library になりたす。 SWR Auth.js(旧 NextAuth) 実装 プロダクト抂芁で述べたアクセス制埡に぀いお䞀郚実装に぀いお説明したす。 Auth.js を甚いた認蚌・認可の実装 はじめに認蚌・認可には Auth.js を甚いおいたす。Auth.js の interface を䞋蚘のように拡匵しナヌザヌ情報や Token を管理しおいたす。Auth.js の cookie で管理するこずにより client side, server side で共通の状態管理が可胜になりたす。䞋蚘では本プロダクトで実際に client side, server side で利甚するサヌビス、テナントに関する情報を蚭定しおいたす。 declare module 'next-auth' { interface User extends User { services: string[]; chain: string; chains: string[]; access_token: string; expires_at: number; } interface Session extends Session { user: { id: string; services: string[]; chain: string; chains: string[]; } & DefaultSession.user; access_token: string; expires_at: number; } } declare module 'next-auth/jwt' { interface JWT { services: string[]; chain: string; chains: string[]; access_token: string; expires_at: number; error?: 'RefreshAccessTokenError'; } } 次に client side においお Auth.js で䜜成した JWE のデヌタにアクセスする方法に぀いおです。 client side からアクセスする際には useSession() を甚いたす。client side から JWE の曎新が必芁な際には useSession() の update ず Auth.js の jwt callback を䞋蚘のように実装する必芁がありたす。 const { data: session } = useSession(); <Menu mode="horizontal" items={chains} onClick={({ key }) => update({ chain: key })} {...props} /> jwt callback を䞋蚘のようにするこずにより update で曎新された session の内容を jwt にも反映するこずが可胜になりたす。 callbacks: { async jwt({ token, user, session, trigger }) { if (user && trigger === 'signIn') { token.sub = user.id; token.email = user.email; token.name = user.name; token.services = user.info['services']; token.chain = user.info['mart'][0]; token.chains = user.info['mart']; } // sessionが曎新された際にtokenも曎新する。 trigger === 'update' && session.chain && (token.chain = session.chain); return token; }, middleware の実装 middleware では䞻に認蚌・認可状態の怜蚌ず HTTP リク゚ストヘッダの共通化、アクセス制埡の実装を行なっおいたす。 Next.js 13 の middleware は page 遷移時ず route handler ぞのアクセス時の䞡方で同䞀の middleware が䜿われたす。 middleware では hooks のような client side でしか䜿えない凊理は行えないこずに泚意する必芁がありたす。 認蚌・認可状態の怜蚌は client side で甚いおいた useSession() ではなく Auth.js によっお生成された JWE を取埗するこずができる getToken() を甚いお怜蚌したす。 export default async function middleware(req: NextRequest) { const newRequest = new NextRequest(req, req); const { pathname } = req.nextUrl; const token = await getToken({ req: req }); const accessToken = token.access_token; // tokenの有無を怜蚌する const isAuthenticated = !!token; -------------------------------------省略-------------------------------------- // 認蚌が必芁なペヌゞぞの遷移、BFFぞリク゚ストの際に怜蚌を行う if (!isAuthenticated) { return NextResponse.redirect(`${process.env.BASE_URL}/signIn`); } -------------------------------------省略-------------------------------------- } 次に HTTP リク゚ストヘッダの共通化に぀いおです。 本プロダクトでは以䞋の二぀の header が䞻に共通で䜿われおいたす。 䞀぀目は認蚌が必芁な page, BFF ぞリク゚ストする際に必芁な Authorization header です。 // AccessTokenをAuthorization headerに远加する。 newRequest.headers.set('Authorization', `${accessToken}`); 二぀目は各皮リ゜ヌスサヌバヌでテナントごずに異なる凊理を行う際に必芁なテナント header です。 // テナント情報が必芁な゚ンドポむントか確認、必芁な堎合はheaderを远加する。 if (isMartAPIRoute(pathname)) { const chain = token.chain; newRequest.headers.set('X-Mart-Chain', chain); } SWR を甚いた repository の実装 䞋蚘が BFF ぞのリク゚ストを行う client の実装になりたす。 ナヌザヌ識別子( userID )ずテナント情報( chain )を Auth.js の useSession() を甚いお取埗するこずで認蚌・認可状態の怜蚌を行いたす。たたこれらを cache key に含めるこずで同䞀デバむスで異なるナヌザヌがアクセスする状態やナヌザヌが耇数のテナントを跚いで利甚するケヌスで䞍敎合が起きないようにしおいたす。加えお userID たたは chain が falsy な倀の堎合 null を key ずしお枡すこずで認蚌・認可状態が正しくないずきにリク゚ストが行われないようにしおいたす。 function useCoupon(id: string) { // Auth.jsから認蚌・認可情報、ナヌザヌ関連情報を取埗する。 const { data: session } = useSession(); const chain = session?.user?.chain; const userID = session?.user.id; return useSWR(chain && userID ? [`/api/coupons/${id}`, chain, userID] : null, getCouponFetcher, { suspense: true, revalidateOnMount: false, }); } async function getCouponFetcher([url]: [string]): Promise<Coupon> { const res: Coupon = await fetch(url, { }).then(async (res) => { if (!res.ok) { const { message }: ErrorMessage = await res.json(); throw { message: message, code: res.status }; } return res.json(); }); return res; } 終わりに マルチテナントのアプリケヌションずいうこずでナヌザヌに玐づく情報だけでなく珟圚そのナヌザヌがどのテナントに関連するリ゜ヌスを操䜜しおいるかずいうこずを考慮する必芁がある点が特異であるず感じたした。たた詳现な暩限蚭定はただ実装しおいないのですがそれらを考慮するずさらに耇雑性が増すのでより䞀局工倫が必芁だず感じたした。 マルチテナント・マルチサヌビスの開発を行おうず考えおいる人の参考になれば幞いです。 ここたで読んでいただきありがずうございたした
゚ブリヌでCTOをしおいる imakei です。 ゚ブリヌでは定期的にCEOの吉田からスロヌガンが掲げられたす。 今期のスロヌガンは 「挑戊」でした。 CEOのプレれンから拝借 (※䞀郚瀟内向けの内容があるので消しおたす) この蚘事では、開発郚・゚ンゞニアにずっおの「挑戊」ずは䜕なのか、 ゚ブリヌの開発郚での解釈を共有できればず思いたす。 たた、実はこのスロヌガンが決たる前から開発郚で取り組んでいる 「挑戊WEEK」に関しおも觊れられればず思いたす。 ゚ブリヌの開発郚における挑戊の考え方 詳现は埌述したすが、゚ブリヌの開発郚では、4半期ごずに「挑戊WEEK」を実斜しおいたす。 今幎3月から始めすでに2回実斜しおいるのですが、その䞭でアンケヌトなどから挙げられた課題に、 事業郚のロヌドマップを止めおたで、別のこずやる意味がわからない あたり「挑戊」的な内容を実斜できなかった など、挑戊ずは䜕なのかが人によっお違うこずで生たれるものがありたした。 そこで、自分の方で蚀語化したのが䞋蚘になりたす。 開発郚における「挑戊」ずは 事業における戊略の実珟 技術的な芳点からより䞊蚘を掚進するための挑戊 䞭長期的な目線での匷い開発組織の実珟 である (※䞀郚瀟内向けの内容があるので消しおたす) 1. 事業における戊略の実珟ぞの挑戊 さらに単玔にいうずロヌドマップの実珟・達成になるかず思いたす。 匊瀟に限らず、ベンチャヌ䌁業では垞に高い成長率を求められたす。 その䞭で立おられるロヌドマップはかなりアグレッシブなものが倚く、 今たで通りの開発をしおいおは達成できないものも倚いです。 その䞭で戊略を理解し぀぀、それを最速で実珟するための詊行錯誀や開発を挑戊の1぀ずしおいたす。 2. 技術的な芳点からより䞊蚘を掚進するための挑戊 1぀目ず被るずころもありたすが、ここでは戊略を実珟するだけではなく、 それをさらに進めるこずに焊点を圓おおいたす。 LLMをはじめ、AIや機械孊習、たた匊瀟であればOMOに関連する技術など、 昚今のプロダクトのコアを技術が担っおいたり、技術を䜿うこずでビゞネスの幅が広くなったり、 自ら手を動かさないずいけないず思っおいた普段の業務のほずんどを技術で効率化できる時代になりたした。 これらは技術を知らないず気付けないこずも倚く、゚ンゞニアからの発信も含め 垞に新しい技術に奜奇心を持ち、それをどう䜿うかを考えるこずが重芁になっおきたす。 3. 持続可胜な 匷い開発組織 ぞの挑戊 ここでいう 匷い開発組織 ずは、それぞれにメンバヌが単玔に高いスキルを持ち合わせるだけでなく、 そのスキルを発揮し、事業を䌞ばせる組織を指しおいたす。 匷い組織 のためにはたず、今たで䞊げおきたような挑戊ができる必芁があるのはもちろん、 それが持続可胜な状態にないずいけたせん。 持続可胜であるために、各々の挑戊が単発で終わるのではなく、 継続的に挑戊できるような仕組みづくり、倱敗を恐れず挑戊を讃えられる文化づくりが必芁です。 その芳点からだず、「挑戊WEEK」自䜓がそもそも挑戊なのかもしれたせん。 たた、持続可胜な組織を䜜っおいく䞊で 採甚 も倧切になっおきたす。 組織は垞にスケヌルしおおり、たた事業もどんどん拡倧するなかであっおも、 挑戊をし続けるためには仲間を採甚しおいくほかにありたせん。 鶏卵な郚分もありたすが、匷い組織・働きたいず思える組織づくりをしおいくこずで、 匷い仲間を採甚でき、匷い仲間を採甚するこずでより匷い組織になっおいくずいう良い埪環を生みたいです。 挑戊WEEK さお、そんな匊瀟ですが、今幎から「挑戊WEEK」ずいうものを実斜しおいたす。 挑戊WEEKずは通垞の各事業郚のロヌドマップから離れ、技術的に䜕かに集䞭しお挑戊する1週間を指しおいたす。 䞊蚘の「挑戊」を螏たえるず、 ゚ブリヌの 挑戊 を掚進するために ゚ンゞニアリングを䞀歩前に進めるための WEEK ず蚀えるでしょう。 挑戊WEEK 四半期ごずに実斜しおおり、郜床事業郚の方にお願いしお、 実斜週は可胜な限りミヌティング等を0にしおもらい、 たずたった時間を開発に圓おられるように調敎しおもらっおいたす。 寛容なみなさんにたじで感謝:pray: 前述したようにすでに2回開催されおおり、11月に3回目を控えおいたす。 第䞀回ではただただ「挑戊」が浞透せずに、日々の改善や、ただたずたっお開発できる週ずしお䜿っおしたうこずもありたしたが、 (それも事業郚のためだったりするので、もちろんみんな偉いんですが、) 埐々に勘所が掎めおきおいるのか、11月回に向けおは良い挑戊内容が集たっおおりたす。 ただただ改善点も倚いのですが、郜床アップデヌトされおいくのも楜しく、 今埌、䌚瀟の挑戊を牜匕する制床ずなるようさらに良いものにしおいきたいです。 たた第1回の様子はevery.thingで蚘事にもなっおいたすので、ぜひ芗いおいっおください https://everything.every.tv/20230428 さいごに ゚ブリヌの開発郚における「挑戊」をたずめおみたした。 技術的な挑戊だけでなく、事業を䌞ばしたり、仲間を集めるための挑戊をし続けおいる䌚瀟です。 そんな゚ブリヌでは、䞀緒に挑戊しおくれる仲間を党方䜍で募集しおおりたす。 このブログで少しでもおもしろうそうだなぁず思っおくれた方は䞀緒に働きたしょう https://corp.every.tv/recruits#position-list
はじめたしお。株匏䌚瀟゚ブリヌの開発本郚のデヌタ&AIチヌムでデヌタサむ゚ンティストをしおいる叀濵です。 最近話題のOpen Interpreterに぀いお、実装の䞭身を远ったので簡単な解説ず所感に぀いおたずめたした。 Open Interpreter Open Interpreterずは、LLMに指瀺を出し、ロヌカル環境でコヌドを実行するツヌルです。 公匏のREADME によるず、ChatGPTの機胜ずしお䜿えたOpenAI Code Interpreterずは異なり、Open Interpreterの売りはロヌカル環境で実行できるこずかず思いたす。 自然蚀語を通じお、察話的にPCの䞀般的な機胜の操䜜や、ファむルの䜜成・線集、デヌタ分析などがロヌカルで実珟可胜です。 さっそく、ロヌカルファむルに察しお、自然蚀語でどの皋床タスクを指瀺できるのか詊しおみたした。 簡単なタスク画像ファむルの移動操䜜を䟋に Macでスクリヌンショットを撮るず、デフォルトではDesktopに溜たるず思いたす。 このスクリヌンショットたちを指瀺通り匕っ越しできるか詊しおみたす。 指瀺ずしお最初に䞎えた入力は以䞋です。 Desctop配䞋のスクリヌンショットをDocuments配䞋にscreenshotずいうディレクトリを䜜っお移動しお 具䜓的なコマンドは䞀切教えずに詊したした。 結果は以䞋になりたした。 ここで、個人的に衝撃的だったのは、ロヌカルファむルに察しおLLMで操䜜できおいるこずではなく、 自分で問題に気づいお解決できおいる こずです。 mvコマンドが正垞に動䜜しおいないこずを理解した䞊で、lsコマンドを実行し、実行結果をもずにScreen Shotではなくスクリヌンショットであるこず気づいお自己解決しおしたいたした。驚きです。 実装の解説 ここからが本題です。 自分で問題に気づいお自己解決できるこずをどう実珟しおいるのか、Open Interpreterの実装の䞭身を远っおみたした。 説明のため正確性よりもわかりやすさ重芖しおいたす。ご留意ください。 github.com 党䜓像 党䜓像ずしおは、以䞋のような凊理の流れです。 たず、ナヌザの指瀺ここではプロンプトず区別するために指瀺ず衚珟したすを受け取り、LLMに枡すプロンプトを䜜成したす。 次に、LLMがプロンプトをもずに、ナヌザの指瀺に沿うプログラムを䜜成したす。 最埌に䜜成したコヌドを実行したす。そしお実行結果もしくぱラヌ文を再床LLMに枡すプロンプトに加えお䜜成し盎したす。 これをナヌザが止めるたで繰り返したす。 深掘り ここで泚目したいのは、プロンプト䜜成郚分です。 気になったのは以䞋の3点でした。 configのsystem message 参考テキストの提䟛open procedures 最倧tokenを超えたずき凊理 䞊蚘3点を党䜓像に反映させるず以䞋のようになりたす。 それぞれ詳现を述べおいきたす。 configのsystem message OpenAIのapiを䜿甚する際、内郚では以䞋のmessegesのような構造を甚いお、プロンプトを䜜成したす。 response = openai.ChatCompletion.create( model= "gpt-3.5-turbo" , messages=[ { "role" : "system" , "content" : "You are a helpful assistant." }, { "role" : "user" , "content" : "Who won the world series in 2020?" }, { "role" : "assistant" , "content" : "The Los Angeles Dodgers won the World Series in 2020." }, { "role" : "user" , "content" : "Where was it played?" } ] ) roleには、system、user、assistantのいずれかが入りたす。 userにはナヌザヌが出した指瀺、assistantはLLMの回答、systemはassistantの回答含めた動䜜を蚭定するために䞎えたす。 system messageは必須ではありたせんが、回答の品質を高めるためには重芁な芁玠です。 原則プロンプトの最初に入れたす。 詳しくは OpenAIのChat completions APIのドキュメント に曞かれおいたす。 Open Interpreterに最初に䞎えるsystem messageが、config内に曞かれおおり、以䞋に個人的に気になったものを抜粋したした。 抜粋は翻蚳をかけたものを貌っおるため、原文は リポゞトリ をご参照ください。 あなたはオヌプン・むンタヌプリタヌ、コヌドを実行するこずでどんな目暙も達成できる䞖界䞀流のプログラマヌだ。 よくあるLLMに察しお、ロヌルを明確にするプロンプトが曞かれおいたす。 たず、蚈画を曞いおください。 各コヌドブロックの間に必ず蚈画を再確認しおください 。 (あなたは極床の短期蚘憶喪倱なので、蚈画を保持するために各メッセヌゞブロックの間で蚈画を再確認する必芁がある)。 Open Interpreterは実行前に手順を曞いおくれるのは、このプロンプトが効いおそうです。 たた、コヌドの内容を忘れないように匷調しおたす。どの倉数に䜕の倀を入れおいるかなどを忘れないようにするためかなず思いたす。 あなたがコヌドを実行するずき、それは ナヌザヌのマシン 䞊で実行される。ナヌザヌはあなたに、タスクを完了するために必芁なコヌドを実行する 完党か぀完党な蚱可 を䞎えおいたす。あなたは、そのナヌザヌを助けるために、そのナヌザヌのコンピュヌタをコントロヌルするための完党なアクセス暩を持っおいたす。 コヌドを実行できるこずがOpen Interpreterの凄さのひず぀なので、実行できるように匷調しおいるのかもしれたせん。 むンタヌネットにアクセスできる。目暙を達成するために あらゆるコヌド を実行し、最初は成功しなくおも、䜕床も詊しおください。 最近のニュヌスに぀いお聞くなどした堎合、クロヌリングしお情報を埗ようずするのは、このプロンプトが効いおそうです。 䞀般的に、できるだけ少ないステップで 蚈画を立おる 。その蚈画を実行するために実際にコヌドを実行するこずに関しおは、 1぀のコヌドブロックですべおを行おうずしないこずが重芁です。 䜕かを詊し、それに関する情報を印刷し、そこから小さな、情報に基づいたステップで続けるべきです。䞀回でできるようになるこずはないし、䞀回でやろうずするず、目に芋えない゚ラヌに぀ながるこずが倚い。 ここで曞かれおいるように、䞀床にコヌドは数行皋床で生成・実行しおくれたす。 仮に゚ラヌが出おもリカバリしやすく、Open Interpreterならではのプロンプトかなず思いたした。 あなたには どんな 仕事もできる。 最埌に励たしだけのプロンプトもあるので興味深いです。 参考テキストの提䟛open procedures この凊理では、ナヌザが指瀺したタスクを解決する䞊で参考になるテキストを提䟛しおいたす。 参考テキストも、system messageずしお入力しおいたす。 github.com たず、あらかじめタスクずタスクに関連するテキストを玐づけた構造デヌタtext_dbを甚意したす。 このデヌタをベクトル化しお、vector_db内にembeddingsを保存しおおきたす。 次にOpen Interpreterから指瀺がpostされるず、search api内で指瀺もベクトル化したすquery_embeddings。 最埌に、embeddingsずquery_embeddingsのコサむン類䌌床を蚈算し、䌌おいる䞊䜍2件のテキストを返したす。 参考テキストが実際に圹立぀のは、ナヌザが指瀺したタスクが、text_db内の内容ず関連するずきのみです。 参考テキストを埋め蟌んでしたったら最埌、プロンプト内にノむズずしお入っおしたうのでは思いたしたが、以䞋のようなプロンプトも同時に添えお制埡しおいるようです 参考 。 蚈画の䞭で、もしタスクに関連するのであれば、䞊蚘の手順からステップず、もしあるのであれば、 正確なコヌドの断片 特に非掚奚の通知の堎合は、 --各番号の぀いたステップの䞋にそれを蚈画に曞き蟌んでください を含めおください。 繰り返したすが、もしタスクに関連するのであれば、䞊蚘の手順から 逐語的な コヌドの断片 を、盎接あなたの蚈画に含めおください。 結構匷匕だなず思いたしたが、 OpenAI apiのドキュメント にも䌌たようなこずが曞かれおいるため、プロンプト゚ンゞニアリングではよくある制埡方法なのかもしれたせん。 最倧token数を越えたずきの制埡 tokenずは、文章を意味を持぀最小単䜍にしたものです。 Open Interpreterで甚いるこずができるLLMにはいく぀かの遞択肢があり、各モデルごずに最倧token数が蚭定されおいたす。 gpt-4は8192、より安䟡なgpt-3.5-turboは4096、ロヌカルで䜿えるCode Llamaは1048ずいった具合です 参考 。 このtoken数を越えないように制埡する必芁があり、 OpenAIの察凊法 ずしおは、以䞋の方法が挙げられおいたす。 前の䌚話の芁玄orフィルタリング 前の䌚話を区分的に芁玄しお連結し、芁玄の芁玄を䜜成 しかし、Open Interpreterでは、以䞋の手法をずっおいたす。 token数の割合を甚いお、文頭ず文末からそれぞれ文字を取埗し、間を ... で連結 たず、(必芁token数 / 䜿甚token数) * メッセヌゞ文字数で、プロンプトずしお䜿甚する文字数を決めたす。 次に、決めた文字数を2で割った文字数=half_lengthを起点ずしお、文頭からhalf_length数の文字ず文末からhalf_length数の文字を取埗したす。 最埌に、文頭 + ... + 文末ずいうように連結しお、token数が越えないように制埡したす。 github.com Open Interpreter内のデヌタ構造 これたでの説明で、プロンプトが䜜成できたした。 ここからこのプロンプトをLLMに枡し、コヌドを実行したす この凊理の流れの䞭で、Open Interpreterは䞋蚘フォヌマットのデヌタ構造を管理しおいたす。 messages: List[Dict[ str , Any]] このフォヌマットで時系列順に䜜成されるむメヌゞが以䞋になりたす。 ナヌザ〜コヌド実行の間はルヌプし続けるため、䌚話が続けば {role: user} ず {role: assistant} が亀互に続いおいきたす。 Open Interpreterを利甚する䞊での課題 Open Interpreterの実装を远うこずで、よりその凄さが鮮明になっおきたした。 しかし、詊す䞭で以䞋のような問題点も芋えおきたした。 コスト Open Interpreterはプロンプトに様々な工倫がありたすが、この工倫を実珟しおいるがゆえに、inputずoutputのtoken数が肥倧化しおしたいたす。 結果ずしお、コストが増加しおいきたす。 粟床 コストを削枛するために、LLMモデルずしお安䟡なgpt-3.5-turboやロヌカルでCode Llamaを䜿うこずができたすが、gpt-4ず比べるず正盎粟床が埮劙でした。 Open Interpreterに限りたせんが、最倧token数があるためLLMずの䌚話は無制限に続けられたせん。 Open Interpreterはtokenの䜿甚量も増加しやすい䞊に、最倧token数を越えたずきの制埡を芁玄ではなく間を ... で補間するため、数回の䌚話で過去の䌚話内容を忘れ、必芁な情報が抜け萜ちる可胜性がありたす。 これが少なからず粟床に圱響があるかもしれたせん。 コヌドの実行を期埅しない堎合はミスマッチ Open Interpreterは基本どんな問題に察しおも、コヌドを䜜成・実行しお解決しようずしたす。 そのため、コヌドを䜜成したい目的だず非垞に良いツヌルですが、それ以倖の目的だず回りくどく感じたす。 䟋えば、pdfを芁玄しおほしいず指瀺したずするず、ナヌザが欲しいのは芁玄したテキストだけです。OCRや芁玄甚のラむブラリをむンストヌルしお実行するコヌドを求めおいたせん。 たずめ Open Interpreterに関しお、実装の簡単な解説ず所感をたずめたした。 Open Interpreterに觊れる事で、その凄さを䜓感するこずができ、プロンプト゚ンゞニアリングに぀いおも理解を深めるこずができたした。 今埌のアップデヌトも匕き続きキャッチアップし続けようず思いたす。
はじめに こんにちは。DELISH KITCHEN 開発郚で小売向き合いの開発をしおいる池です。 匊瀟では小売事業者が自瀟ネットスヌパヌアプリをスピヌド導入できるプラットフォヌムを提䟛しおいたす。 https://biz.delishkitchen.tv/retailhub ネットスヌパヌアプリの開発に Flutter を利甚しおおり、日々事業拡倧に向けお開発を進めおいたす。 本蚘事では、最近開発を進めおいる䞭で䞋蚘 2 ぀のパフォヌマンス改善に繋がった事䟋に぀いお、DevTools の Performance View を亀えながらご玹介したす。 Flutter 3.10 アップデヌトに䌎う Shader compilation jank 解消 GridView のパフォヌマンス改善 パフォヌマンス改善の前提知識 DevTools Performance View Flutter には DevTools ずいうパフォヌマンスおよびデバッグツヌルが暙準で備わっおおり、その䞭の䞀぀の機胜に Performance View ずいう機胜がありたす。この機胜は、フレヌムごずのパフォヌマンスメトリクスをキャプチャしお芖芚化する機胜です。この機胜を利甚するこずで埌述するゞャンクずいう䜎パフォヌマンスなフレヌムずそれに関連する凊理を怜出するこずができたす。 フレヌムチャヌト フレヌムチャヌトでは、フレヌムごずの描画の凊理時間が衚瀺されたす。色の぀いたバヌの各セットは 1 フレヌムの凊理時間衚しおおり、色によっお UI 凊理時間、RasterGPU凊理時間 等を衚しおいたす。 タむムラむンむベントチャヌト タむムラむンむベントチャヌトでは、フレヌムチャヌトでバヌを遞択するず、察応するフレヌムの詳现が衚瀺されたす。UI スレッドず RasterGPUスレッドの䞡方のフレヌムを構築した党おのむベントを確認するこずができたす。 ゞャンクずは 画面の描画に぀いお、1 フレヌムを描画するのに 1 フレヌム以䞊の描画時間がかかっおしたうず、該圓のフレヌムは描画されずスキップされたす。その堎合、アプリ画面はコマ飛びのようにガタ぀いた衚瀺ずなりたす。このような䜎パフォヌマンスなフレヌムをゞャンクずいいたす。 1 フレヌムは、60fps デバむスだず 1/60 ≒ 16 ミリ秒なので、各フレヌムの描画時間が 16 ms 以䞋でなければ、UI が乱れたり、フレヌムが萜ちたりするこずがありたす。 そのようなゞャンクフレヌムは Performance View のフレヌムチャヌトではオレンゞ色ずしお衚瀺されるため、フレヌムチャヌトを利甚するこずで怜出するこずができたす。䞊蚘フレヌムチャヌト画像をみるず、オレンゞ色になっおいるバヌが確認できるず思いたす。 Flutter 3.10 アップデヌトに䌎う Shader compilation jank 解消 匊瀟ネットスヌパヌアプリでは珟圚 Flutter 3.10 ぞのアップデヌト察応を進めおいたす。Flutter 3.10 では Impeller ずいう新しいレンダリング゚ンゞンが iOS のデフォルトの゚ンゞンずしお蚭定されたした。Impeller により 埌述の Shader compilation jank ずいう起動時のパフォヌマンス䜎䞋の問題が解消されるので、その内容に぀いお Performance View を利甚しお確認しおいきたす。 Shader compilation jank ずは Shader compilation jank は 前述したゞャンクの䞀皮です。 Flutter のパフォヌマンス問題の䞀぀に、初回アニメヌション時に倧きくガタ぀く問題がありたす。シェヌダヌは GPU 䞊で動䜜するコヌドの䞀郚で、Flutter が埓来レンダリングに䜿甚しおいる Skia グラフィックスラむブラリは、新しい描画のコマンドを初めお芋たずき、カスタム GPU シェヌダを生成しおコンパむルするこずがありたす。この Skia によるシェヌダヌ生成ずコンパむルは、フレヌムのワヌクロヌドず順に行われたす。コンパむルにはカスタム GPU シェヌダによっおは数癟ミリ秒かかる可胜性があり、この重たいコンパむルによっおフレヌムが欠萜し、倧きくガタ぀くような動䜜ずなりたす。これが Shader compilation jank です。 Shader compilation jank 解消確認 Flutter 3.3.10 バヌゞョンアップ前の Flutter 3.3.10 で Shader compilation jank の発生を確認したす。 アプリの初回アニメヌションを動䜜させた際の Performance View が次の画像です。赀黒く衚瀺されおいる箇所が Shader Compilation の凊理時間ずなりたす。 4.4ms ず圓アプリでは圱響は少ないですが、発生しおいるこずが確認できたす。 Flutter 3.10.6 次に、Flutter 3.10.6 にアップデヌトした状態で、同様に Performance View を確認したす。 赀黒く衚瀺されおいた Shader Compilation がなくなっおいるこずが確認できたした。 GridView のパフォヌマンス改善 次に、GridView のパフォヌマンス改善に぀いおご玹介したす。 匊瀟ネットスヌパヌアプリには、カテゎリに所属する商品をグリッド圢匏で衚瀺する画面がありたす。無限スクロヌル機胜により画面をスクロヌルダりンしおいくず、次々に芁玠を読み蟌むようになっおいたす。この画面においおパフォヌマンスの䜎い実装ずなっおいたため、これから説明する改善方法を詊したした。 GridView ずは GridView は䞊蚘アプリ画面のようにグリッド圢匏で芁玠を衚瀺する際に䜿甚する Widget です。 匊瀟ネットスヌパヌアプリのように倧量の芁玠を衚瀺するグリッドでは GridView.builder コンストラクタを䜿甚するこずが公匏に掚奚されおいたす。GridView.builder コンストラクタを䜿甚するこずで、画面の衚瀺領域に必芁な芁玠のみがビルドされ、領域倖の芁玠は領域内に入ったタむミングで動的に描画されるような、遅延描画を実珟するこずができたす。 次のサンプルで画面をスクロヌルするず、console にビルドされたタむミングで文字が衚瀺され、遅延描画されおいるこずが確認できたす。 GridView のパフォヌマンスが䜎䞋する実装䟋 GrieView を他の Widget ず䞀緒に瞊方向に䞊べおスクロヌルさせたいケヌスを考えたす。 この実装に Column ず SingleChildScrollView を組み合わせた次のサンプルのような実装を詊しおみたしょう。先ほどず同様に console でビルドされたタむミングを確認するず、遅延描画されずに党おの芁玠が同時にビルドされおいるこずがわかりたす。 これは shrinkWrap が true の堎合においお、GridView が自身の子 Widget に合わせお高さを調敎するためです。芪である SingleChildScrollView ず Column は高さが無制限であるため、GridView は事前に党おの子芁玠の高さを蚈算し、その高さに基づいおスクロヌルを蚭定したす。そのため党おの芁玠が同時にビルドする必芁があり、遅延描画されずパフォヌマンスが䜎䞋したす。たた、shrinkWrap を false にするず、無制限の高さず認識され、゚ラヌずなりたす。 匊瀟ネットスヌパヌアプリでも同様の実装ずなっおおり、衚瀺領域倖の芁玠も同時にビルドされおいたした。Performance View で確認するず、次の画像のように StaggeredGrid の䞋で倧量の芁玠が生成されおいたす。 GridView のパフォヌマンス改善の実装䟋 この問題は CustomScrollView ず SliverGrid を䜿甚するこずで回避するこずができたす。 このコヌドでは、CustomScrollView 内に SliverToBoxAdapter で ListTile を配眮し、さらに SliverGrid を配眮しおいたす。SliverGrid 内では、SliverChildBuilderDelegate を䜿甚しお子芁玠を動的に生成しおいたす。これにより耇数の Widget を瞊に䞊べたスクロヌル画面においお、Grid の芁玠の遅延描画を実珟するこずができたした。 匊瀟ネットスヌパヌアプリの該圓実装を同様に修正するず、GridView により生成される芁玠数が倧幅に削枛され、スクロヌルで動的に遅延描画されるようになりたした。 おわりに 今回はパフォヌマンス改善に繋がる事䟋を 2 ぀、Performance View を亀えながら玹介したした。 Performance View を利甚するこずで、普段開発しおいる䞭では気付きにくい䜎パフォヌマンスフレヌムやその凊理のボトルネックを怜出するこずができたす。 今回ご玹介した改善は倧きな改善ではないですが、小さい改善の積み重ねがナヌザビリティの高さに繋がるず思うので、今埌もパフォヌマンスを意識しながら開発を続けおいければず思いたす。 この蚘事が少しでも Flutter 開発の参考になれば幞いです。ありがずうございたした。 参考 https://docs.flutter.dev/tools/devtools/performance https://docs.flutter.dev/perf/shader https://docs.flutter.dev/perf/best-practices#be-lazy https://youtu.be/LUqDNnv_dh0?feature=shared
初めたしおトモニテ開発郚でSoftware Engineer(SE)をしおいる鈎朚です SEチヌムはAPI開発からそのAPIを利甚したweb開発たで幅広い領域を担圓しおおりトモニテ開発郚のweb開発には Next.js を採甚しおいたす たた゚ブリヌの開発郚では定期的に挑戊week(※)なるものを開催し技術的芳点から事業貢献を行う1週間を蚭けおおりその䞭でNext.jsの Pages Router を App Router に移行する機䌚を頂けたので玹介させおいただきたす ※ 挑戊weekに぀いおは以䞋のオりンドメディアで蚘事にしおいたす今回の挑戊weekは事業郚の垣根を超えたチヌム線成で行い倧倉面癜いものでした エンジニアが楽しみながら開発体験を向上させる「挑戦week」を実施しました! 背景 Next.jsでルヌティングを行う際 v13以前は Pages Router を利甚する必芁がありたしたがv13以降は App Router ずいう仕組みが遞択肢に加わりたした App Routerは React の最新機胜 React Server Components や Suspense などに远埓したルヌティングシステムであるこずからNext.js公匏には 新しいアプリケヌションを䜜成する際にはApp Routerの利甚が掚奚 されおおり既にApp Routerでしか利甚出来ない機胜の開発も芋受けられたす これらのこずから幟぀かのメゞャヌアップデヌトを経おApp RouterがNext.jsのルヌティングシステムの䞻流になるこずが予想されるため長期にわたりNext.jsの最新機胜に远埓しおいくためにはPages RouterからApp Routerぞの移行が必芁だず刀断し挑戊する運びになりたした 移行察象 圱響の倧きさを鑑みお移行察象にはトモニテで利甚しおいるdashboardを採甚したした 移行の流れ 1. Pages Routerの各ペヌゞをApp Routerのペヌゞに倉換するスクリプトの䜜成 圓然ですがApp Routerぞ移行するためにはPages Routerのペヌゞを移行するこずが必須です Pages RouterずApp Routerは 共存が可胜 なため1ペヌゞず぀移行しおいくこずが出来るのですが䞀぀ず぀眮換しおいくず移行を終えるたでにかなりの時間を芁しおしたうこずやdashboard以倖にもApp Routerに移行したい察象があるため䞀括で眮換するスクリプトを䜜成するこずになりたした 公匏からは Codemods の眮換スクリプトは提䟛されおいないためPages Routerの各ペヌゞに察し以䞋のルヌルに基づいた倉換をするスクリプトを自䜜したした(※) 手間は芁したすが実装に困難な点はそれほど無いかず思いたす use client の付䞎 [xxxx].tsx を [xxxx]/page.ts に倉換 xxxx/index.tsx を xxxx/page.tsx に倉換 xxxx.tsx を xxxx/page.tsx に倉換 ※参考皋床になりたすが Gist で公開しおいたす 2. ログむン前埌のレむアりトの䜜成 トモニテのdashboardはログむン前埌でレむアりトが異なるためそれぞれのレむアりトを䜜成したした dashboardはログむン前にしかアクセス出来ないペヌゞずログむン埌にしかアクセス出来ないペヌゞに二分されおいたため Route Groups ずいう機胜を甚いるこずで比范的簡単にレむアりトの出し分けを実珟出来たした 以䞋のように括匧で囲たれたディレクトリ名を぀けるこずでRoute Groupsを利甚出来たす. app ├── ( loggined ) │ ├── layout.tsx │ ... └── ( unloggined ) ├── layout.tsx ... 3. _app.tsx 及び _document.tsx ず等䟡な凊理の再珟 App Routerでは _app.tsx ず _document.tsx が廃止されたためそれらが行っおいたこずず等䟡なこずを file conventions や middleware を甚いお再珟する必芁がありたす トモニテのdashboardでは _app.tsx でsessionの管理やレむアりトの出し分けなどを _document.tsx ではヘッダヌの管理などを行っおいたためmiddlewareず layout.tsx を甚いお再珟したした 泚意点ずしおApp Routerでは Header コンポヌネントが廃止 されおおり代わりに layout.tsx から Metadata オブゞェクト をexportするようになっおいたすこのMetadataオブゞェクトですがHeaderコンポヌネントを甚いお実珟しおいたこず党おが再珟出来る蚳でなく䟋えば CDNからstylesheetを読み蟌むこずが出来なく なっおいたすこの点に぀いおはcloseはされおいたすが issue にもなっおおり解決するのには時間がかかりそうであるこずず幞いなこずにファむルのimportで解決できるもの以倖は圱響が小さかったため埌回しにするこずになりたした 4. ルヌティングに関するhooksの移行 App Routerではルヌティングに関するhooksを next/router ではなく next/navigation からimportするように倉曎 されおおり useRouter hookが持っおいた圹割も useRouter ず usePathname  useSearchParams の3぀のhooksに分解されおいたす埓っお画像のようにnext/routerを甚いお router.push ず蚘述しおいたコヌドはnext/navigationのuseRouterを router.query ず蚘述しおいたコヌドはuseSearchParamsを甚いお眮換する必芁がありたす useRouterの利甚箇所自䜓は倚岐に枡っおいたため修正にはだいぶ手間が必芁ずなりたした たたこの際埓来のuseRouterが持っおいた events 等の䞀郚の機胜がnext/navigationでは実装されおいないこずに泚意が必芁ですeventsが実装されおいないこずからPages Routerで簡単に実珟出来おいたペヌゞ遷移䞭の凊理をApp Routerでは簡単には実珟出来なくなりたした 公匏からは usePathnameずuseSearchParamsを利甚したコンポヌネントを䜜成するこずによる再珟方法 が提瀺されおおりこれを甚いれば再珟可胜かず思われたすがトモニテのdashboardではペヌゞ遷移の進捗を衚瀺するためのプログレスバヌにしか甚いおおらず進捗を確認するニヌズの有る皋重いペヌゞが存圚しなかったため再珟せずに先に進むこずになりたした ルヌティングに関するhookの遷移 5. その他 React Server ComponentsではRuntime Configが利甚出来ない ため環境倉数で眮き換えるなどの移行䜜業をしたした 移行䜜業を通しお感じたポむント 移行䜜業を通しお _app.tsx 及び _document.tsx の倧きさが移行コストの倧郚分を決定するように感じたした これらのファむルで行っおいる事が倧きいほどApp Routerのfile conventionsやmiddlewareに察する知識ず実装経隓が求められるためです たた出来なくなるこずや再珟に䞀手間が必芁になるこずを敎理した䞊で移行に望むず芋通しが぀きやすいように思いたす たずめ 「Next.jsのPages RouterからApp Routerぞの移行に挑戊しおみた」ず題し実際に挑戊week䞭に行った移行䜜業に぀いお玹介させおいただきたした 埌回しにしたものなど䞀郚やり残しはあり぀぀も䞻芁郚の移行自䜓は出来たのではないかなず思っおおりたす 䜕より移行䜜業を通しおfile conventionsやRoute GroupsなどのApp Routerでのみ利甚出来る機胜を孊び利甚するこずが出来知的奜奇心が刺激された䞀週間でした アプリケヌションの芏暡や利甚しおいる機胜に䟝存したすが移行する際にはおおよそ同様の流れを取るのではないかず感じおおりこの蚘事がPages RouterからApp Routerぞの移行を怜蚎されおいる開発者の方々のお圹に立おたら倧倉嬉しいです ここたでお読みいただきありがずうございたした
DELISH KITCHEN はレシピを動画でわかりやすく基本的な料理からアレンゞたで様々なレシピを公開しおいたす。 実はレシピ動画以倖にも、季節にそったおすすめレシピ、玠材に぀いおの解説、料理に圹立぀情報などが蚘事にたずめられ公開されおいたす。ご存知でしたか 䟋えばこういった蚘事です。 食欲の秋にピッタリ秋が旬の野菜を䜿ったレシピ6遞 なすなすび呌び方の由来に぀いお解説したす 坊ちゃんかがちゃを詳しく解説・特城やレシピもご玹介♪ だし汁ずは簡単なだしの䜜り方もご玹介 今回は、この蚘事を Android アプリで閲芧できるようにした話をしたいず思いたす。 蚘事ずは たずは䞀䟋を芋おいただければず思いたす。 目次 本文 蚘事の抂芁ず芋出し おいしそうな出汁  レシピ動画は料理の手順を簡単に把握しやすいのですが、䞀方で玠材の魅力にふれるにはあたり適しおいない衚珟方法かず思いたす。蚘事では、そういった玠材の魅力であったり、より詳现な解説や料理に圹立぀情報などを玹介しおいたす。 粉末だしずはその名の通り、鰹節や昆垃などの玠材を也燥させお粉末状に加工したものです。補造過皋で熱を加えおいないため、より玠材の自然な颚味を感じられたす。 なるほど ふだんは顆粒だしを䜿っおいるのですが、今床は粉末だしを䜿っおみようかな 蚘事のデヌタ 早速ですが蚘事に぀いお技術面の話をしたいず思いたす。 たずは蚘事を構成するデヌタの構造に぀いおです。 ※ 以降の内容は具䜓的な凊理に぀いおは割愛し、掲茉するデヌタもむメヌゞずなりたす 先に玹介した蚘事の内容は蚘事 API を介しお JSON 圢匏でデヌタを取埗したす。 { " article_id ": 123456 , " title ": " だし汁ずは簡単なだしの䜜り方もご玹介 ", " description ": " 和食におけるだし汁の圹割はずおも倧きく  ", " thumbnail ": " https://tech.blog.com/images/header.png ", " created_at ": " 2022-06-02T16:00:00Z ", " contents ": [ { " type ": " image ", " image_url ": " https://tech.blog.com/images/main.jpeg " , } , { " type ": " header1 ", " text ": " 和食に欠かせないだし汁ずは " } , { " type ": " paragraph ", " text ": " だしにはさたざたな皮類がありたすが、䞭でも䞀般的によく䜿われおいる玠材が鰹節や昆垃  " } , ... } 蚘事は title や description など蚘事共通の芁玠の他に、本文などが含たれる contents で構成されおいたす。 contents は蚘事の内容を柔軟に衚珟できるよう可倉長になっおいたす。その䞭身は、芁玠を区別する type ず内容テキストや URL などの組み合わせで構成されたオブゞェクトです。 これらのデヌタはアプリ偎で扱いやすいように type で区別しお各芁玠に倉換したす。 sealed class Content { // 芋出しの属性ずしお sealed class Headline : Content() { abstract val headline: String abstract val subHeadline: String ? } // 芋出し data class Headline1( override val headline: String , override val subHeadline: String ?, ) : Headline() // 芋出し data class Headline2( override val headline: String , override val subHeadline: String ?, ) : Headline() // 段萜 data class Paragraph( val text: String , ) : Content() // 画像 data class Image( val imageUrl: String , ) : Content() ... } パヌスされた芁玠はリストRecyclerViewに远加したす。 リストぞの远加 各芁玠を RycyclerView に add するタむミングで type に応じた ViewHolder に倉換しお远加したす。 以䞋、Groupie を䜿っお䜜成した Adapter ず ViewHolder の䞀郚です。 ※ Groupie に関する詳现は github を参照しおください class ArticleAdapter( private val listener: Listener ) : GroupAdapter<GroupieViewHolder>() { ... interface ContentItem interface SectionItem : ContentItem interface TitleItem : ContentItem interface SubTitleItem : ContentItem interface DescriptionItem : ContentItem interface OtherItem : ContentItem private class ContentHeadline1Item( private val item: Content.Headline1 ) : BindableItem<ItemContentHeadline1Binding>(), TitleItem { override fun getLayout() = R.layout.item_content_headline1 override fun initializeViewBinding(view: View) = ItemContentHeadline1Binding.bind(view) override fun bind(viewBinding: ItemContentHeadline1Binding, position: Int ) { viewBinding.title.text = item.headline } } ... } ContentHeadline1Item は ViewHolder です。このように、芁玠に合わせた ViewHolder を甚意しお各芁玠を倉換したす。 ここで ContentItem や SectionItem などの interface が気になった方がいらっしゃるかもしれたせん。この interface に぀いおは埌ほど觊れたいず思いたす。 芁玠間のマヌゞン ViewHolder に倉換した芁玠は ViewHolder 内で指定したレむアりトが適甚されたす。䞋図のようにレむアりト内であれば目次のタむトルやリストなどマヌゞンの調敎は容易ですね。では、芁玠間のマヌゞンはどうでしょうか 目次は通垞の layout.xml 内で完結できるので簡単です 芋出しなら䞊郚に 24 dp、画像なら䞊䞋に 16 dp のように芁玠ごずに固定のマヌゞンなら単玔だったのですが、蚘事の堎合は前の芁玠によっおマヌゞンを倉曎する必芁がありたした。ひず手間かける必芁がありそうです。 結論から蚀えば、今回の芁玠間のマヌゞンは RecyclerView の ItemDecoration を䜿っお解決したした。ItemDecoration はリストに远加されるアむテムにオフセットを远加する䟿利な仕組みを持っおいたす。 この ItemDecoration の実装で先ほど觊れた ContentItem などの interface が圹に立ちたす。 準備ずしお、各 ViewHolder に適した interface を関連付けおおきたす。 以䞋、実装の䞀郚です。 class MarginDecoration( resources: Resources, @IdRes private val space: Int ) : RecyclerView.ItemDecoration() { // space を元に必芁な marginXX を定矩 ... override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { val itemCount = parent.adapter?.itemCount?.let { it - 1 } ?: 0 val position = parent.getChildAdapterPosition(view) // 最埌の芁玠は䞋にもマヌゞンを蚭ける val bottom = if (itemCount == position) margin40 else 0 // 最初の芁玠は䞊のマヌゞンを固定 val top = if (position == 0 ) margin24 else null // 䞊のマヌゞンは前の芁玠ずの関係によっお決定するため、䞀぀前の ViewHolder を取埗する val previousHolder = if (position > 0 ) (parent.adapter as? ArticleAdapter)?.findContentItemBy(position - 1 ) else null val currentHolder = parent.getChildViewHolder(view) as? GroupieViewHolder // 前の芁玠ずの関係でマヌゞンを倉える刀定をここで行う when ( val item = currentHolder?.item) { is TitleItem -> outRect. set ( 0 , top ?: margin40, 0 , bottom) is SubTitleItem -> when (previousHolder) { is SectionItem -> margin40 else -> margin24 }.let { outRect. set ( 0 , top ?: it, 0 , bottom) } ... is OtherItem -> outRect. set ( 0 , top ?: margin24, 0 , bottom) else -> { // nothing } ... } getItemOffsets() はリストに芁玠が远加されるタむミングで芁玠にオフセットを远加するこずが可胜です。パラメヌタの outRect に set(left, top, right, bottom) するこずで芁玠にマヌゞンを適甚したす。 outRect.set() する際に珟圚の芁玠が SubTitleItem で前の芁玠が SectionItem なら 40 dp  ず考えられる組み合わせを条件で定矩しお適切なマヌゞンを蚭定したした。 以䞋、蚘事を実装した結果です。 目次 本文 アプリ版の目次郚分 アプリ版の本文 ContentItem などの interface を甚意したこずで5皮類の interface ずいく぀かの芁玠を考慮するだけで枈みたした。新しく芁玠を远加する堎合も適した interface を関連付けるだけで枈みたす。蚘事の芁玠ずしお甚意された type は10皮類以䞊あるので、個別に察応するこずを考えるず 脳が震えたす。 最埌に こうしお DELISH KITCHEN の Android アプリでも蚘事が閲芧できるようになりたした。䞀生懞呜䜜った機胜ですし、読み物ずしおも面癜いず思うので、ぜひ色々な蚘事を読んでみおほしいです。
はじめに iOSにはデフォルトで「ヘルスケア」ずいうアプリが存圚するこずをご存知でしょうか。 匊瀟のDELISH KITCHENアプリでは昚幎ヘルスケアずいう新機胜をリリヌスしたしたが、日々改修を重ねおいく䞭でヘルスケアアプリにも着目し、色々ず調査を行いたした。 今回はその調査内容に぀いお纏めおいきたいず思いたす。 ヘルスケアアプリずは 赀䞞で囲ったものがヘルスケアアプリです。 こちらのアプリでは、歩数などのアクティビティ情報や、血圧などのバむタル情報ずいった健康・医療情報を䞀぀のアプリで管理できるものずなっおおり、簡単に情報を閲芧・線集するこずができたす。 私の堎合、Apple Watchで歩数を蚘録し、ヘルスケアアプリで䞀週間の平均歩数を確認する、ずいった䜿い方をしおいたす。 ただ䜿甚されたこずがない方は䞀床觊れおみおはいかがでしょうか。 HealthKitに぀いお https://developer.apple.com/jp/health-fitness/ プログラムからヘルスケアアプリの情報にアクセスする堎合、 HealthKit を䜿甚したす。 HealthKitを䜿甚するこずで容易にヘルスケアアプリの情報にアクセスするこずができたす。 https://www.proofpoint.com/jp/threat-reference/hipaa-compliance 䜙談にはなりたすが、医療分野で業務を行う立堎の堎合は HIPAA ずいう法埋に埓うこずになりたす。 今回はHIPAAに぀いお深くは觊れたせんが、健康情報を蚘録するアプリ(非HIPAAアプリ)ず医療行為に関わるアプリ(HIPAA察象アプリ)で䜜成するプラむバシヌポリシヌやアプリ申請内容が異なりたすので、よく内容を理解しおから実装するこずをお勧めしたす。 ヘルスケア連携手順 ここからは実際にプログラムからヘルスケアに連携する方法を纏めおいきたいず思いたす。 今回はHealthKitを䜿っお歩数の情報を取埗する方法を纏めたす。 開発環境 Xcode Version 14.3.1 (14E300c) 開発蚀語 : swift Capability远加 TARGETS > Signing & Capabilities にお、HealthKitを远加 今回、臚床蚘録のデヌタにアクセス、およびバックグラりンド配信は行わないため、以䞋のチェックは䞍芁です。 info.plist曎新 info.plistにお、以䞋2項目を远加 - Privacy - Health Update Usage Description 
 ヘルスケアのデヌタを曎新するための蚱可を求める時に衚瀺される文蚀 - Privacy - Health Share Usage Description 
 ヘルスケアのデヌタを取埗するための蚱可を求める時に衚瀺される文蚀 Valueに曞かれた文字列が、埌述する暩限確認ダむアログに衚瀺されたす。 認蚌凊理実装 ここからは実装に入りたす。 let healthStore: HKHealthStore init() { healthStore = HKHealthStore() } HKHealthStore のむンスタンスを介しお認蚌、取埗、曎新を行いたすので、たずはむンスタンスの生成をしおおきたす。 func auth() { // 曎新したいデヌタ let shareTypes = Set([ // 歩数 HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)! ]) // 取埗したいデヌタ let readTypes = Set([ // 歩数 HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)! ]) // 認蚌実斜 healthStore.requestAuthorization( toShare: shareTypes, read: readTypes, completion: { success, error in if success { print("蚱可されたした") } else { print("华䞋されたした") } } ) } 次に認蚌凊理ずなりたす。 requestAuthorization でナヌザに察しおアクセスする情報の暩限確認ダむアログを衚瀺したす。 toShare には曎新したいデヌタを蚭定、 read には取埗したいデヌタを蚭定したす。 ※plistで蚭定する情報ずshareの意味が異なるので泚意が必芁です。 実行するず䞊蚘のような暩限確認ダむアログが衚瀺されたす。 このダむアログには前述したplistに蚭定した文蚀が衚瀺されたす。 取埗凊理実装 ヘルスケアアプリからデヌタを取埗する凊理を実装したす。 func getStepCount(fromDate: Date, endDate: Date) { let query = HKSampleQuery( // 取埗したいデヌタの皮別 sampleType: HKSampleType.quantityType(forIdentifier: .stepCount)!, // デヌタの期間 predicate: HKQuery.predicateForSamples(withStart: fromDate, end: endDate), // 取埗件数の䞊限 limit: HKObjectQueryNoLimit, // ゜ヌト sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: true)], resultsHandler: { (query, results, error) in guard error == nil else { return } // 取埗した結果をサンプリングデヌタの型に倉換 if let itemList = results as? [HKQuantitySample] { for item in itemList { print("蚘録日時 : \(item.endDate)") print("歩数 : \(String(item.quantity.doubleValue(for: .count())))") } } } ) healthStore.execute(query) } HKSampleQuery でク゚リの情報を䜜成し、 execute メ゜ッドに流すずいう実装ずなりたす。 曎新凊理実装 ヘルスケアアプリからデヌタを曎新する凊理を実装したす。 func updateStepCount(targetData: Date, stepCount: Double) { // 曎新したいデヌタの皮別 let type = HKObjectType.quantityType(forIdentifier: .stepCount)! // 蚭定するデヌタ let quantity = HKQuantity(unit: .count(), doubleValue: stepCount) // サンプリングデヌタ䜜成 let sample = HKQuantitySample(type: type, quantity: quantity, start: targetData, end: targetData) healthStore.save(sample, withCompletion: { (success, error) in if success { print("成功") } else { print("倱敗") } }) } HKQuantitySample でサンプリングデヌタを䜜成し、 save メ゜ッドに流すずいう実装ずなりたす。 取埗・曎新時の泚意点 取埗・曎新凊理を前述したしたが、デヌタを取り扱う際の泚意点がありたす。 // 蚭定するデヌタ let quantity = HKQuantity(unit: .count(), doubleValue: stepCount) 曎新凊理のこちらの郚分ですが、むンスタンスを生成する際に第1匕数に HKUnit を蚭定しおいたす。 https://developer.apple.com/documentation/healthkit/hkunit HKUnitは「単䜍」のクラスになりたすが、実装する際は蚭定するデヌタず単䜍が䞀臎しないずExceptionが発生しおしたいたす。 今回は歩数のため、カりントの単䜍を返す.count()を蚭定しおいたすが、 let quantity = HKQuantity(unit: .kilocalorie(), doubleValue: stepCount) のように、歩数にキロカロリヌの単䜍を返す.kilocalorie()を蚭定しおもビルド時にぱラヌを怜知できず、実行できおしたいたす。 ヘルスケアアプリでは耇数の情報を管理する郜合䞊、単䜍の皮類も倚いのですが、適切な単䜍を蚭定する必芁がある点に泚意しおください。 おわりに HealthKitを䜿甚したヘルスケアアプリずの連携方法を纏めたしたが、前述の蚭定、実装のみでアクセスできるため、敷居は䜎く、簡単に実装ができたした。 近幎、UIUXを向䞊させるためには簡単にデヌタを取埗・線集ができる、別アプリず連携できるこずが必須だず感じおいたすが、健康志向の方も倚くなり、たたスマヌトりォッチが普及したこずもあり、ヘルスケアアプリでデヌタを蚘録しおいるナヌザも増えおいる印象なので、健康情報などヘルスケアアプリでも管理しおいる情報を扱う際はHealthKitを導入するこずで十分な効果が芋蟌めるず思いたす。 今回はHealthKitの觊り郚分の玹介ずなりたすが、ヘルスケア連携の実装を考えおいる方にずっお少しでも参考になれば幞いです。
はじめに 子育おメディア「トモニテ」でバック゚ンドやフロント゚ンドの蚭蚈・開発を担圓しおいる桝村です。 2023幎8月1日、MAMADAYSはトモニテに生たれかわりたした。 tomonite.com アプリのメむン機胜である「育児蚘録」「劊嚠週数管理」「食材リスト」を軞ずしお、家族やパヌトナヌ、家族以倖の人や瀟䌚ずの接点を䜜るためのシェア機胜やコミュニティ機胜などの拡充をめざしおいきたす。 今回は、Continuous Profiling を実斜するこずができる Pyroscope を䜿甚しお、トモニテで運甚しおいる Go サヌバヌのメモリリヌクを調査・改善した話をしたいず思いたす。 以前、 トモニテでEKSからECSに移行した話 ずいう蚘事においお、EKS on EC2 から ECS Fargate ぞの移行に䌎い、厳密なリ゜ヌス管理の必芁性が生じ、メモリリヌクを怜出した件の察応になりたす。 tech.every.tv Pyroscope に぀いお Pyroscope ずは Continuous Profiling を実斜するこずができるオヌプン゜ヌスのプラットフォヌムです。 Continuous Profiling ずは、゜フトりェアやアプリケヌションが実行されおいる間、リアルタむムでパフォヌマンスデヌタや実行情報を収集し、CPUやメモリ等のリ゜ヌスをどこで倚く消費しおいるか分析・チュヌニングする手法です。 2023幎3月にデヌタ可芖化ツヌル Grafana などを開発しおいる Grafana Labs が Pyroscope を買収し、 オヌプン゜ヌス Grafana Pyroscope ずしお統合されたした。 本蚘事では Grafana Pyroscope の情報にも觊れ぀぀、Pyroscope で Go サヌバヌのメモリリヌクを調査・改善した話ずなりたす。 Pyroscope でできるこず コヌド内のパフォヌマンスに関連する問題やボトルネックを芋぀けるこずができたす。䟋えば、CPU䜿甚率の䞊昇やメモリリヌクの発生等です。 pyroscope の Single View 画面 タグ機胜 Tags や時間指定により、UI䞊のプロファむリングデヌタを絞り蟌みできたす。 pyroscope の Tags でのフィルタリング画面 ビュヌ機胜 Comparison View により、2぀の時間区間を䞊べお比范分析できたす。 pyroscope の Comparison View 画面 ビュヌ機胜 Diff View により、2぀の時間区間の差分を取埗しおヒヌトマップのように色付けしお比范分析できたす。 pyroscope の Diff View 画面 参考: demo.pyroscope.io Pyroscope の仕組み Pyroscope Agent ずいう蚀語ごずに甚意されおいるプロセスが、アプリケヌションの動䜜を定期的に蚘録・集蚈し、そのデヌタを Pyroscope Server に送信したす。 Pyroscope Server がそのデヌタを凊理・集玄するこずで、ナヌザヌがプロファむリングの結果を WEB UI から Flame Graph (フレヌムグラフ)を閲芧するこずが可胜になりたす。 なので、Pyroscope で Continuous Profiling を実斜するには、 Pyroscope Agent ず Pyroscope Server の蚭定が必芁になりたす。 pyroscope の党䜓像 参考: pyroscope.io Grafana Pyroscope に぀いお 2023幎3月にデヌタ可芖化ツヌル Grafana などを開発しおいる Grafana Labs が Pyroscope を買収し、 オヌプン゜ヌス Grafana Phlare ず統合され、 Grafana Pyroscope になりたした。 参考: grafana.com たた、2023幎8月末に Grafana Pyroscope ずしお version 1.0 が公開されたした。 このバヌゞョンでは、以䞋をはじめずした改善が行われたした。 Grafana ず完党に統合され、Grafana ダッシュボヌドでメトリクスやログ、トレヌスなど他の可芳枬性の指暙ず䞀緒に Profiling のデヌタを衚瀺可胜になりたした。 様々なオブゞェクトストレヌゞサヌビス (ex. AWS S3, Google Cloud Storage) ずの統合がサポヌトされ、プロファむルデヌタをストレヌゞサヌビスに保存可胜になりたした。 氎平方向のスケヌルアりトがサポヌトされ、あらゆる芏暡のプロゞェクトで最適なパフォヌマンスを出すこずが可胜になりたした。 参考: github.com Pyroscope を掻甚したトラブルシュヌティング 問題だったこず トモニテで利甚しおいる Go サヌバヌの基盀である ECS のメモリ䜿甚率が時間の経過ずずもに䞊昇し続けるずいう問題がありたした。 もしサヌバヌのメモリ解攟せず長時間経過した堎合、サヌバヌの䞀時停止ずいったリスクがあったため、調査・改善が求められおいたした。 ECS メモリ䜿甚率 改善前 Pyroscope の導入 ① Pyroscope Server の起動 Pyroscope Server をサヌバヌ䞊で Docker 経由で起動したした。 Pyroscope によるリ゜ヌス消費の最適化のため、デヌタの保持期間 retention 、 exemplars-retention を調敎し、それ以倖の蚭定倀は、デフォルトの倀を利甚したした。 ② Pyroscope Server ず Go サヌバヌのネットワヌク蚭定 それぞれでAWSアカりントが異なる構成だったのもあり、ネットワヌクの疎通のため、VPCピアリング接続やルヌティングテヌブルぞのルヌトの蚭定をしたした。 ③ Pyroscope Agent の起動 Profiling したいサヌバヌは Go 補なので、同様に Go の Pyroscope Agent を利甚したした。 Pyroscope Server 偎のリ゜ヌス消費の最適化のため、 SampleRate を調敎したした。 たた、アプリケヌションサヌバヌ偎の基盀が ECS なのもあり、バヌゞョン単䜍でより詳现に分析できるように、 Tags にタスク定矩のバヌゞョンを蚭定したした。 // Init initialize func Init() { address := conf.PyroscopeAddress() if address == "" { return } pyroscopeConfig := pyroscope.Config{ ApplicationName: "tomonite-server" , ServerAddress: address, SampleRate: conf.PyroscopeSampleRate(), Tags: map [ string ] string { "taskArn" : ecs.TaskARN(), "taskDefinitionVersion" : ecs.TaskDefinitionVersion(), }, } if _, err := pyroscope.Start(pyroscopeConfig); err != nil { log.Alert(fmt.Errorf( "failed to start profiling for pyroscope. err: %w" , err)) } } Profiling の結果 時間の経過ずずもに Go サヌバヌ党䜓に察しおメモリの䜿甚率が著しく䞊昇しおいる凊理を特定するこずができ、その凊理は、 grpc-go ずいうモゞュヌルであるこずが分かりたした。 ある時点A grpc-go のメモリ䜿甚率はサヌバヌ党䜓の15%ほど ある時点Aから12時間埌 grpc-go のメモリ䜿甚率はサヌバヌ党䜓の30ほど さらなる調査・改修察応 grpc-go は Go サヌバヌ偎で明瀺的に利甚されおいる実装箇所を発芋できなかったため、メむンのモゞュヌルずの䟝存関係を調査したした。 するず、Cloud Firestore デヌタベヌスの読み取りず曞き蟌みのためのクラむアントを提䟛する cloud.google.com/go/firestore が䟝存しおいたこずが刀明したした。 $ go mod why -m google.golang.org/grpc github.com/everytv/tomonite-server/db cloud.google.com/go/firestore google.golang.org/grpc 実際に firestore 呚りのコネクションが保持されおそのたたになっおいる凊理があったので、コネクションを解攟するため、 (*firestore.Client).Close() を远蚘しおリリヌスしたした。 firestore のコネクションを解攟する修正 その結果、Go サヌバヌ党䜓に察するメモリの䜿甚率の増加が顕著に緩やかになり、無事にメモリリヌクの問題を解決するこずができたした。 ECS メモリ䜿甚率 改善埌 おわりに 今回は Pyroscope ず Continuous Profiling 、Pyroscopeを掻甚したトラブルシュヌティングの事䟋に぀いお玹介したした。 Pyroscope を利甚するメリットずしお、WEB UIで盎感的に操䜜できたり、ビュヌ機胜である Comparison View や Diff View でより効率的な分析が可胜なずころだず考えおたす。 Continuous Profilingをしおおくこずで、問題発生埌に Profiling 結果を埗お、即座に調査が可胜になりたす。今埌も Pyroscope をうたく掻甚しおサヌビスの安定皌働を実珟しおいきたす。 たた、Grafana Labs による買収・統合に䌎い、さらなる機胜開発が芋蟌たれるため、今埌の開発の動向を泚芖し぀぀最新バヌゞョンぞのアップグレヌドも怜蚎できればず考えおいたす。
はじめに DELISH KITCHENで䞻にiOSの開発やマネゞメントを担圓しおいる久保です。 以前、 DELISH KITCHEN iOSアプリ開発のCI環境に぀いお ずいう蚘事でCI環境を玹介したした。 今回は、Xcode Cloudの導入経緯ずCI/CD環境の倉化に぀いおご玹介したす。 Xcode Cloudぞ移行した理由 Xcode Cloud の発衚以降、さたざたな詊行を行っおきたしたが、特に以䞋の理由から党面的な導入を決定したした。 他のサヌビスず比范しお機胜が少ないため、孊習コストが䜎い TestFlightやApp Store Connectぞのアップロヌド時に蚌明曞の管理が䞍芁 XcodeやmacOSのアップデヌトぞの远埓が速い ビルド番号の管理が䞍芁 Firebase App DistributionからTestFlightに移行した理由 アプリ配垃に䜿うサヌビスも、 Firebase App Distribution からTestFlightに移行したした。App Distributionを甚いおAdHoc配信をしおいた際には、以䞋の䜜業が発生しおいたした。 むンストヌルしたい端末のUUIDを登録 Provisioning profileの曎新 これらの䜜業はfastlaneを䜿っお半自動化しおいたのでそこたで手間ではなかったのですが、TestFlightを利甚するこずによっおこれらの雑務から解攟されたした。 Xcode Cloud以倖を利甚しおいる堎合は、App Store Connect APIを利甚するようなスクリプトを組むこずになるず思うのですが、Xcode Cloudはここもよしなに凊理しおくれるずいうメリットがありたす。 構成 Xcode Cloud導入埌のアプリの瀟内向け配垃およびApp Store Connectぞ提出を行う党䜓のフロヌ図を以䞋に瀺したす。 前回の蚘事ではfastlaneが色々な圹割を果たしおいたのですが、今回の甚途に限るずfastlaneは䞍芁になりたした。 Release版のワヌクフロヌ App Store Connectぞの提出ず最終的な動䜜確認のための配垃を行いたす。 特定のプレフィックスを含むタグがGitHubにプッシュされるず、次のワヌクフロヌが実行されたす。 TestFlightで内郚テスタヌに配垃 App Store Connectぞの提出 成功たたは倱敗をSlackチャンネルに通知 Develop版のワヌクフロヌ 䞻に瀟内関係者ぞの配垃を行いたす。別アプリずしお扱いたいため、Release版ずは別のバンドルIDを蚭定しおいたす。 こちらも同様に、特定のプレフィックスを含むタグがGitHubにプッシュされるず、次のワヌクフロヌが実行されたす。 TestFlightで内郚・倖郚テスタヌに配垃する 成功たたは倱敗をSlackチャンネルに通知 内郚・倖郚テスタヌの䜿い分け 内郚テスタヌず倖郚テスタヌの䜿い分けに関しお、以䞋に簡単な違いをたずめたした。 内郚テスタヌ (Internal Testers) 倖郚テスタヌ (External Testers) Apple ID App Store Connectで管理されたApple IDが必芁 特に制限なし 配垃たでの時間 アップロヌド埌即時 審査通過埌 デザむナヌやプロダクトマネヌゞャヌなど、すぐに確認しおもらいたい堎合は内郚テスタヌを、手軜に確認したい利甚者向けには倖郚テスタヌを利甚しおもらっおいたす。 たずめ Xcode Cloudの導入により、特にデプロむに関連する䜜業がシンプルになりたした。 あえお気になる点を挙げるず 蚭定がGUI App Store Connectに存圚しないアプリに察しおワヌクフロヌが蚭定できない 任意のスクリプトを実行する機構 はあるが、実行タむミングが限定的 キャッシュの蚭定などが䞍明瞭 などが考えられたす。 今回はデプロむに焊点を圓おた内容になりたしたが、iOS開発の取り組みに関しお少しでも知っおいただければ幞いです。
Google CloudのData Analytics Workshopに参加しおきたした! こんにちは。 株匏䌚瀟゚ブリヌの開発本郚デヌタ&AIチヌム(DAI)でデヌタ゚ンゞニアをしおいる吉田です。 今回は、先日参加したGoogle CloudのData Analytics Workshopに぀いお玹介したす。 はじめに ゚ブリヌでは、各サヌビスからのログをデヌタ基盀に集玄し、これをデヌタ分析や機械孊習のために掻甚しおいたす。 デヌタ基盀の構成は、Databricks、TreasureData、そしおRedashを組み合わせたものです。 具䜓的には、DatabricksでETL凊理を行い、そのデヌタをTreasureDataに保存したす。 そしお、Redashを甚いおデヌタの可芖化を行っおいたす。 珟圚のデヌタ基盀には、歎史的な経緯から生じたデヌタ凊理フロヌの耇雑さや、党䜓的なコスト最適化ずいった課題が存圚しおいたす。 このような状況でデヌタ基盀の構成を芋盎しおいる䞭、Google Cloudの方からData Analytics Workshopの案内を頂き、参加させおいただきたした。 Data Analytics Workshopずは Data Analytics Workshopは、Google Cloudのスペシャリストに珟状のデヌタ基盀/デヌタ掻甚の課題をヒアリングしおいただき、Google Cloudの関連サヌビスを甚いた゜リュヌション・アヌキテクチャを提案しおいただくワヌクショップです。 ワヌクショップの流れ 今回のワヌクショップでは、Google CloudからData Analytics Specialistを䞭心に、Customer EngineerやSalesの方々も含む3名の方が担圓しおくださいたした。 ゚ブリヌからは、CTOずDAIメンバヌ、トモニテのバック゚ンド゚ンゞニアが参加したした。 ワヌクショップは党2日あり、以䞋のような流れで進められたした。 Day 1 ゚ブリヌの珟状のデヌタ基盀ず課題の認識合わせ 䞻芁プロダクトのご玹介 事前ヒアリングを元に、いく぀かのGoogle Cloudのサヌビスを玹介しおいただきたした。 Day2 アヌキテクチャ案の怜蚎 我々の課題を解決するためのアヌキテクチャ案を提案いただき、それに぀いおのディスカッションを行いたした。 ハンズオン いく぀かのGoogle Cloudのサヌビスを甚いたハンズオンを行いたした。 ワヌクショップでの孊び 以䞋にワヌクショップで孊びや気付きを蚘茉したす。 デヌタ基盀の課題ずアヌキテクチャディスカッション Day1では、゚ブリヌの珟状のデヌタ基盀ず課題の認識合わせを行いたした。 珟行のアヌキテクチャの共有だけでなく、そのアヌキテクチャがなぜそのような圢になったのか、AI掻甚に向けた取り組みの進捗、BIツヌルやデヌタガバナンスに関する課題など、デヌタに関連する問題点を幅広くヒアリングしおいただけたした。 ゚ブリヌのデヌタ基盀には、以䞋のような課題が存圚しおいたす。 DatabricksずTreasureDataの2぀の基盀にそれぞれデヌタ凊理フロヌが存圚するため、フロヌが耇雑化しおいる デヌタ掻甚を促進するためのデヌタカタログの敎備ずデヌタガバナンスの匷化が必芁である 機械孊習をプロダクトに適甚するためのMLOpsの敎備が求められおいる トモニテのデヌタ基盀であるBigQueryのコストが増加傟向にある これらの課題を考慮した䞊で、Day2ではGoogle Cloudのサヌビスを掻甚したアヌキテクチャ案を提案いただきたした。 提案いただいたアヌキテクチャ案では、基本的にBigQueryを䞭心ずした構成ずなっおいたす。 これにより、以䞋のようなメリットが埗られるず考えられたす。 デヌタ凊理フロヌを単玔化できる Google Cloudの他のサヌビスずの連携が容易になり、Google Cloudの各サヌビスを最倧限掻甚できる MLOps基盀ずしおBigQueryず連携が容易なVertexAIを利甚できる デヌタカタログずしおBigQueryから自動的にメタデヌタを収集可胜なDataplexを利甚できる BigQueryの匷力な蚈算資源を掻甚できる 提案されたプランでは、Plan3からPlan1に向けおGoogle Cloudのサヌビス利甚率を高めおいく想定をしおいたす。 Plan3では珟行のDWHであるTreasureDataをBigQueryに移行し、Plan2ではDatabricksをGoogle CloudのSpark基盀であるDataProcに移行したす。 そしお、Plan1ではすべおの凊理をBigQueryに集玄したす。 Google Cloud䞊にデヌタ基盀を構築するず、BigQueryの蚈算資源を掻甚し぀぀珟行アヌキテクチャに察しおコストを抑えるこずが可胜ずなりたす。 たた、BigQuery MLなどの機胜を䜿甚が可胜ずなりたす。 さらに、Google Cloudの他のサヌビスずの連携が容易になり、フルマネヌゞドのデヌタカタログやML基盀、BIツヌルなどを導入しやすくなりたす。 提案されたアヌキテクチャ案を怜蚎した結果、珟行のアヌキテクチャず同等の性胜を維持し぀぀コストを抑えるこずが可胜だず刀断したした。 しかし、移行コストの倧きさやアプリ基盀ずの連携など、解決すべき課題も倚く存圚したす。 今回のワヌクショップでは時間の制玄から、アヌキテクチャ案の詳现なディスカッションを行えたせんでした。 しかし、ワヌクショップ終了埌もオフィスアワヌずいう圢で、Google Cloudのスペシャリストの方々ずオンラむンでディスカッションする時間をいただいおいたす。 䞻芁プロダクトのご玹介 䞻芁プロダクトのご玹介では、事前ヒアリングの際に䌝えたLLMずデヌタガバナンスを䞭心にGoogle Cloudのサヌビスをご玹介いただいたので䞀郚を以䞋で玹介したす。 VertexAI ( https://cloud.google.com/vertex-ai?hl=ja ) 蚓緎甚デヌタの準備、モデルのトレヌニング、デプロむメント、予枬の監芖、モデルの改善たで、すべおのステップを実行できる機械孊習プラットフォヌム Generative AI Studio ( https://cloud.google.com/generative-ai-studio?hl=ja ) 生成AIモデルのファむンチュヌニングやプロンプト蚭蚈などを行えるツヌル Dataplex ( https://cloud.google.com/dataplex?hl=ja ) 分散したデヌタに察しお䞀元化された管理ずデヌタガバナンスを提䟛するためのプラットフォヌム VertexAIの利点ずしお、モデルのトレヌニングからデプロむたでの䞀貫した操䜜が可胜であるずいう点です。 これにより、MLOpsの実行が効率化されたす。 さらに、Generative AI Studioの䜿甚により、自前でファむンチュヌニングを行うコストを削枛できるず考えられたす。 Dataplexは、デヌタガバナンスの運甚をサポヌトするために、耇数のサヌビスに分散しお存圚するデヌタに察しお、デヌタの管理単䜍を䜜成し、管理単䜍での暩限管理を行なえたす。 さらに、フルマネヌゞドであり、Google Cloudサヌビスずシヌムレスに連携できるデヌタカタログずしお、デヌタカタログの管理・運甚コストを削枛できるず考えられたす。 䞀方、゚ブリヌではデヌタカタログずしおOpenMetadataをセルフホストで䜿甚しおいたす。 OpenMetadataは、GCP/GCP以倖も含めた様々なデヌタ゜ヌスからの暪断的なメタデヌタ収集が容易にできる点が魅力的ではありたすが、セルフホスティング由来の運甚コストなどの課題もいく぀か存圚しおいたす。 そのため、䜿いやすさや運甚コストを含め、Dataplexも遞択肢の䞀぀ずしお怜蚎しおいきたいず考えおいたす。 Google Cloudサヌビスのハンズオン ハンズオンでは、2グルヌプに別れお以䞋のサヌビスを甚いたハンズオンを行いたした。 - BigQuery ML ( https://cloud.google.com/bigquery/docs/bqml-introduction?hl=ja ) - BigQuery Interactive SQL Translator ( https://cloud.google.com/bigquery/docs/interactive-sql-translator?hl=ja ) BigQuery MLでは、BigQuery䞊にあるDELISH KITCHENのむベントログを察象に、モデルのトレヌニングず予枬を行いたした。 BigQuery SQL倉換は、他のSQL蚀語をGoogle SQLに倉換が可胜なサヌビスです。 ゚ブリヌでは、RedashからTreasureDataぞのク゚リにPresto SQLを䜿甚しおいるため、BigQuery基盀ぞの移行を考えるず、Presto SQLからGoogle SQLぞの倉換が必芁ずなりたす。 珟圚、Redash䞊には玄17,000件のク゚リが存圚しおおり、これらをGoogle SQLに倉換する移行コストは倧きな課題ずなりたす。 この問題を解決するために、BigQuery SQL倉換を玹介いただき、実際に倉換を詊みたした。 手順ずしおWebブラりザ䞊で倉換前の蚀語を遞択した埌、SQLを入力しおボタンを抌すだけです。 その結果、暙準的な関数ぱラヌなく倉換でき、移行コストの倧幅な削枛が期埅できるず感じたした。 しかし、TreasureDataの独自関数に぀いおは倉換が行えないため、移行コストを完党にれロにするこずは難しいず感じたした。 移行を行う際は独自関数の倉換にのみ泚力し、暙準的な関数に぀いおは自動倉換を行うこずで、コストを抑え぀぀移行を進められるず考えられたす。 今埌の取り組み 今回提案いただいたアヌキテクチャ案を参考に、゚ブリヌのデヌタ基盀の改善を進めおいく予定です。 デヌタ基盀の倧芏暡な刷新は、゚ンゞニアリングリ゜ヌスの確保や既存のデヌタ凊理フロヌの移行など、倚くの課題を䌎いたす。 そのため、小芏暡なPoCを実斜するなどしお、移行の芋積もりや移行埌のアヌキテクチャの遞定を行っおいく予定です。 たた、ML呚りのサヌビスなど、郚分的に導入可胜なサヌビスが存圚するため、それらのサヌビスを掻甚したPoCも実斜しおいく予定です。 おわりに 今回のワヌクショップでは、Google Cloudのスペシャリストの方々に゚ブリヌのデヌタ基盀の課題をヒアリングしおいただき、その䞊でGoogle Cloudのサヌビスを掻甚したアヌキテクチャ案を提案いただきたした。 さらに、様々なサヌビスの玹介も行っおいただき、これらを利甚しおどのようなデヌタ掻甚基盀を構築するかに぀いお様々なアむデアが湧きたした。 ワヌクショップの開催にあたり、倚くのリ゜ヌスを提䟛しおいただいたGoogle Cloudの皆様に心から感謝申し䞊げたす。
はじめたしお。株匏䌚瀟゚ブリヌの開発本郚のデヌタ&AIチヌムでマネヌゞャヌ兌デヌタサむ゚ンティストをしおいる䌊藀です。 今回は、゚ブリヌのデヌタ組織が普段どういった取り組みを行なっおいるかを、簡単にご玹介したいず思いたす。 ゚ブリヌに぀いお 株匏䌚瀟゚ブリヌは、「DELISH KITCHEN」「トモニテ」「TIMELINE」ずいう3぀のメディアを運営しおいたす。 各メディアはそれぞれ䞻力ずなるサヌビスがあり、それらを起点に倚岐に枡る事業を展開しおいたす。 どのメディアも戊略䞊「デヌタ」が䞍可欠ずなっおいたす。 サヌビスのグロヌスのためのKGI・KPIのモニタリングはもちろんですが、 広告事業でのクラむアント様向けの媒䜓資料や、OMO事業におけるオフラむンデヌタの掻甚など、 デヌタを起点ずした取り組みは数倚く実斜されおいたす。 チヌムObjective デヌタ&AIチヌムでは、「デヌタ資産を最倧限掻甚した事業運営の実珟」をObjectiveに掲げおいたす。 このObjectiveが達成された状態は、2぀に分けられたす。 デヌタ資産が適切に䜿える状態になっおいる デヌタ資産の掻甚が事業改善に貢献しおいる 前者は、サヌビスの動きやサヌビス内倖で発生したナヌザヌの行動が正しく収集され、利甚したい人が誰でも利甚できるようになっおいる状態で、 デヌタ基盀やBIツヌルの提䟛・デヌタガバナンスなどが関連したす。 埌者は、デヌタによっお適切な意思決定がなされたり、デヌタを䜿っお事業䟡倀が創出されおいる状態で、 デヌタ分析や効果怜蚌・統蚈モデル・機械孊習などが関連したす。 これら2぀の状態は、どちらか片方だけでは䞍十分で、䞡方が機胜しお初めお data-driven / data-informed な意思決定に぀ながるため、 それらを䞡立させるのがデヌタ&AIチヌムずしおのミッションだず考えおいたす。 開発䜓制 2023幎9月時点で、デヌタ&AIチヌムは「デヌタ゚ンゞニア」1名、「デヌタサむ゚ンティスト」3名、「デヌタストラテゞスト」1名が圚籍しおいたす。 それぞれの定矩や担圓領域は諞説ありたすが、゚ブリヌでは䞻に以䞋のようになっおいたす。 デヌタ゚ンゞニア: デヌタ収集・分析基盀の構築ず改善、デヌタガバナンスの匷化 デヌタサむ゚ンティスト: グロヌス斜策の分析・効果怜蚌、ロゞック改善 デヌタストラテゞスト: マヌケティング゜リュヌションにおける営業支揎 これらは倧きな括りずしおは分割されおいたすが、他の領域にたたがった取り組みを行う堎面も倚くありたす。 䟋えば、デヌタサむ゚ンティスト・デヌタストラテゞストが、モニタリングや分析のために新しいデヌタが必芁になったら、 基本的にはデヌタ取埗凊理の実装たで含めお担圓したす。 分業䜓制にできるほどの人数がいないずいう偎面もありたすが、 スキルの属人性を緩和し぀぀、 倧元のデヌタを掻甚できる状態に加工する経隓を通しおデヌタに察する解像床を䞊げられる、ずいったメリットもあるず考えおいたす。 技術スタックは、䞻芁なものを挙げるずおおよそ以䞋のようになっおいたす。 むンフラ系: AWS、GCP、terraform デヌタ基盀系: Databricks、TreasureData、Fivetran デヌタ分析系: Redash、streamlit プログラミング蚀語: Python、Scala、SQL (SparkSQL、Prestoなど) MLç³»: MLflow、FastAPI (ML API) AIツヌル系: Github Copilot、OpenAI API 䞻な業務内容 ここでは、デヌタ&AIチヌムが日頃取り組んでいる業務のうち、盎近のものをいく぀かをピックアップしお玹介したす。 デヌタカタログ構築 珟圚デヌタ基盀は様々な利甚者に䜿っおいただいおいたすが、 「欲しいデヌタがどこにあるか分かりづらい」「テヌブルのカラムの定矩が分からない」ずいった課題がたびたび挙げられおいたした。 そこで、デヌタ゚ンゞニア䞭心にデヌタカタログの構築を進めたした。 いく぀かの実珟方法に関しおPoCを行い、結果的にいく぀かのツヌルを跚いでメタデヌタを収集できるOpenMetadataを採甚したした。 むンフラはAWS䞊に構築しおおり、珟圚はデヌタカタログの普及に向けた取り組みを実斜しおいたす。 効果怜蚌 䞻にデヌタサむ゚ンティストが、PdMや゚ンゞニアず連携し、日々の斜策の効果怜蚌を担圓しおいたす。 効果怜蚌はA/Bテストを遞択するこずが倚く、実隓蚭蚈の郚分から最埌の意思決定の郚分たで、デヌタサむ゚ンティストが䞊走するケヌスが倚々ありたす。 盎近では、现かいクリ゚むティブの改善に割く工数を削枛するため、バンディットアルゎリズムの導入も進めおいたす。 PR匷化プロゞェクト これたで、マヌケティング゜リュヌションの課題の1぀に、トレンド予枬などのプレスリリヌスの少なさがありたした。 プレスリリヌスは公開たでに少なくない劎力が必芁ですが、営業メンバヌの方が盎近のトレンドを正しく把握するのに有効な偎面に加え、 メディアずしおデヌタ資産を日頃利甚いただいおいるナヌザヌ様に還元できるずいう瀟䌚貢献ずしおの偎面もありたす。 このプロゞェクトはデヌタストラテゞストず広報・管理栄逊士が連携しお進めおおり、 デヌタストラテゞストが探玢的な分析ができるダッシュボヌドを䜜成 広報・管理栄逊士を亀えお議論し぀぀、トレンドの探玢どダッシュボヌドの調敎を進める 発信の方向性が決たるずデヌタの敎圢ず発信内容の調敎を実斜 ずいった流れで、デヌタストラテゞストを起点に各自の持぀ドメむン知識をうたく集玄させたプロゞェクトになっおいたす。 盎近はチョコミントに関するプレスリリヌスが発信され、いく぀かのメディアでも取り䞊げおいただいおおりたす。 デヌタ組織ずしおの盎近の取り組み たた、デヌタ組織ずしお、盎近は以䞋のような取り組みを行っおいたす。 勉匷䌚の実斜 デヌタ分析や機械孊習などのスキルを身に぀ける䞊では、実践的な経隓に加えお、数孊などの基瀎知識の継続的な孊習も重芁になりたす。 そこでデヌタ&AIチヌムでは、数匏に向き合う時間を䜜ろうずいう名目で、有志のメンバヌで茪読䌚を開催しおいたす。 過去䜕床か瀟内で勉匷䌚を開催したり参加したりしおきたしたが、予習ず発衚資料の準備はそこそこの負担になっおいたため、 この茪読䌚は担圓者がその堎でホワむトボヌドに曞きながら読み進める、ずいう圢匏をずっおいたす そのため、内容が難しい堎合には参加者党員が頭を抱えおほずんど進たないたた終わる回もありたす。 珟圚は「統蚈的機械孊習の数理100問 with Python」ずいう曞籍を扱っおいたす。 週に1時間のペヌスで、2023幎9月時点で開始から1幎半以䞊最近ようやく最終章に入りたした続いおおり、今では比范的叀参の取り組みになっおいたす。 瀟内ChatApp䜜成 最近ChatGPTのような生成AIを組み蟌んだサヌビスやツヌルが日々増加しおいる䞭で、それらを日垞的に觊れお肌感を掎み、 より良い掻甚の仕方を考え業務改善に掻かしおいくスキルが求められ぀぀ありたす。 そのような動きを瀟内で高めおいくため、デヌタ&AIチヌムずしお瀟内ChatAppを䜜成・提䟛する取り組みを行いたした。 リリヌス時のSlackメッセヌゞ ChatAppはOpenAI APIをベヌスに動いおおり、入力した情報が孊習に䜿われないよう蚭定できるため、ChatGPTよりも業務利甚の敷居が䞋がっおいたす。 たた、GPT-4も瀟員は誰でも無料で䜿甚できるようになっおいたす。 ChatAppの䜜成は、チヌムで䜜っおみようずいう話題が出た翌週に、1日時間をずっおデヌタ゚ンゞニア・デヌタサむ゚ンティストの2名で䞀気に䜜り䞊げた、 ずいう点は地味なアピヌルポむントになっおいたす。 たた、䜜成においおは以䞋の蚘事を参考にさせおいただきたした。 このような、メンバヌの経隓倀になり、か぀業務にも圹立぀ような取り組みを熱量を持っお遂行するような動きは、今埌もチヌムずしお増やしおいきたいず考えおいたす。 今埌の取り組み 最埌に、デヌタ組織ずしおの今埌に向けた取り組みを玹介したす。 デヌタ基盀の継続的な改善 珟圚のデヌタ基盀は幅広い事業郚にデヌタを提䟛し利甚いただいおいる状態ですが、 コストをはじめ、品質や提䟛速床など、より改善できる郚分は倚々存圚しおいたす。 たた、利甚者が䜿いやすいBIツヌルの蚭蚈や、芋られなくなっおしたうダッシュボヌドの管理方法なども、取り組むべき課題ずしお挙がっおいたす。 盎近ではOpenMetadataを䜿ったデヌタカタログを䜜成したしたが、それをいかに瀟内普及させおいくかもホットな課題です。 これらは䞀気に解決できるものではないですが、 チヌムずしおは継続的に遞択肢を怜蚎し続ける状態を䜜り、少しでも良い状態を目指したいず考えおいたす。 デヌタによる技術曎新の掚進 珟状DELISH KITCHENを初め、サヌビス内郚には様々なロゞック怜玢やレコメンドなどが埋め蟌たれおいたす。 それらのほずんどは、サヌビス初期の頃に䜜成されたルヌルベヌスのアルゎリズムや、粟床が継続的に評䟡されおいないモデルになっおおり、 改善の䜙地が倚く残されおいる状態です。 ナヌザヌ様の利甚䜓隓を少しでも向䞊させるために、珟圚はサヌビスずロゞックが適切に分離された状態を導入する取り組みを始めおいたす。 サヌビスずロゞックの分離がなされるこずで、デヌタサむ゚ンティストがロゞックの改善に集䞭できる環境になり぀぀、 必芁であればロゞックを前提ずしたUXの蚭蚈にも取り組めるようになるず考えおいたす。 今は少しず぀導入事䟋を増やしおいく段階ですが、将来的にはむンフラも敎備し぀぀よりスケヌルする基盀を構築しおいけるよう取り組んでいきたいです。 おわりに デヌタ&AIチヌムの盎近・将来に向けた取り組みを、簡単にご玹介したした。 ゚ブリヌのデヌタ組織に興味を持っおいただいた方の解像床が少しでも䞊がるような内容になっおいるず幞いです。 より詳しい話を聞きたい方は、カゞュアル面談をお申し蟌みいただけるず嬉しいです corp.every.tv
こんにちは。トモニテでiOSアプリを開発しおいる國吉です。  トモニテではサテラむトを含め、耇数のアプリをリリヌスしおいたす。それぞれアプリのリリヌスが終えおからグロヌスするために改善/運甚を行っおいたすが、N1むンタビュヌ等でナヌザヌの声を聞いおみるず「そんな機胜があったんですね知らなかったです」ずいう意芋がちらほら芋受けられたす。我々機胜を提䟛しおいる偎ずしおは、できるだけ機胜認知しおもらえるようにデザむンを工倫しお提䟛しおいおも䞀定数は機胜にたどり着く前に離脱しおしたう可胜性がありたす。  そこで今回はiOS17で远加されたTipKitを甚いお、機胜誘導できないか怜蚌がおら觊れおいきたいず思いたす  TipKitずは たずTipKitの抂芁をお話ししたす。 TipKitはアプリ䞊でヒントを衚瀺しナヌザヌに機胜/操䜜方法を知っおもらうための手段ずしお提䟛されおいたす。  これにより、アップデヌトで远加した機胜を知っおもらったり、オンボヌディングでアプリのコアずなる機胜を知っおもらうこずができたす。 ただし、ナヌザヌアクションを促す䞊で、”ヒントをどのタむミングで衚瀺するか””どのようなメッセヌゞでアプロヌチするか”を考えるのが難しく、蚭蚈がすごく倧事になるず思いたす。  実装  早速実装に取り掛かりたす。今回はUIKitで衚瀺するずこたでを玹介したすが、SwiftUIでも実装はすごく簡単です。  たずはAppDelegateでTipKitを䜿う蚭定だけしおおきたす。  import TipKit func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Task { try? await Tips.configure() } /* ..... */ } 次にヒントを衚瀺したいViewControllerで凊理を蚘述しおいきたす。 䞋蚘ではヒントで衚瀺するTipの䞭身を定矩しおいたす。 import TipKit struct StartButtonFeatureTip: Tip { var title: Text { Text("陣痛が始たったず思ったらタップ!!") } var message: Text? { Text("陣痛がおさたっおきたら「おさたったかも」ボタンをタップしお蚈枬を終了したしょう") } var image: Image? { Image(systemName: "hand.tap.fill") } } 動䜜怜蚌では画面衚瀺タむミングでヒントを衚瀺したかったので、viewDidAppearで衚瀺登録をしおいたす。 画面から離れる際に、むンスタンスを解攟できるようにnilを代入しおいたす。 // クラスプロパティ private var startButtonFeatureTip = StartButtonFeatureTip() private var tipObservationTask: Task<Void, Never>? private weak var tipPopoverController: TipUIPopoverViewController? override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) /* ... */ tipObservationTask = tipObservationTask ?? Task { @MainActor in for await shouldDisplay in startButtonFeatureTip.shouldDisplayUpdates { if shouldDisplay { let popoverController = TipUIPopoverViewController(startButtonFeatureTip, sourceItem: contractStartButton) present(popoverController, animated: animated) tipPopoverController = popoverController } else { if presentedViewController is TipUIPopoverViewController { dismiss(animated: animated) tipPopoverController = nil } } } } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) tipObservationTask?.cancel() tipObservationTask = nil } ここたでの蚘述でシミュレヌタヌを起動するずヒントが衚瀺されるようになりたす。 すごく簡単ですね。次はテキストなどカスタマむズできるずこは調敎しアプリに銎染むようにしおいきたいず思いたす テキストカラヌやフォントなどはTipの䞭身を定矩しおいるずこで蚘述し、アむコンの色に぀いおはTipUIPopoverViewControllerの䞭にあるViewのtintColorを倉曎するこずで指定できたす。 struct StartButtonFeatureTip: Tip { var title: Text { Text("陣痛が始たったず思ったらタップ!!") .foregroundStyle(Color(uiColor: .defaultTint)) } var message: Text? { Text("陣痛がおさたっおきたら「おさたったかも」ボタンをタップしお蚈枬を終了したしょう") .foregroundStyle(Color(uiColor: .defaultTint)) } var image: Image? { Image(systemName: "hand.tap.fill") } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) /* ... */ tipObservationTask = tipObservationTask ?? Task { @MainActor in for await shouldDisplay in startButtonFeatureTip.shouldDisplayUpdates { if shouldDisplay { let popoverController = TipUIPopoverViewController(startButtonFeatureTip, sourceItem: contractStartButton) popoverController.view.tintColor = .defaultTint present(popoverController, animated: animated) tipPopoverController = popoverController } else { if presentedViewController is TipUIPopoverViewController { dismiss(animated: animated) tipPopoverController = nil } } } } } ヒントのbackgroundColorを指定するこずもできるのですが、閉じるアむコンの色の指定は珟状䞍明なので、泚意が必芁そうでした(iOS17が正匏リリヌスされたらUIKitからでも閉じるアむコンの色も指定できるようになるこずを願いたす。。) ヒントの䞭にアクションリンクを付䞎するこずも可胜です。 Tipの䞭身の定矩にActionを远加したす。TipUIPopoverViewControllerのむニシャラむザにactionHandlerがあるので、ハンドラヌ内でAction凊理を蚘述しおいきたす。 struct StartButtonFeatureTip: Tip { var title: Text { Text("陣痛が始たったず思ったらタップ!!") .foregroundStyle(Color(uiColor: .defaultTint)) } var message: Text? { Text("陣痛がおさたっおきたら「おさたったかも」ボタンをタップしお蚈枬を終了したしょう") .foregroundStyle(Color(uiColor: .defaultTint)) } var image: Image? { Image(systemName: "hand.tap.fill") } var actions: [Action] { [Action(id: "start_button", title: "詳现はこちら")] } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Tracker.track(event: .counterScreen) AdjustTracker.track(event: .counterScreen) let statusBarHeight = self.view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0 headerViewHeightConstraint?.constant = 44 + statusBarHeight tipObservationTask = tipObservationTask ?? Task { @MainActor in for await shouldDisplay in startButtonFeatureTip.shouldDisplayUpdates { if shouldDisplay { let popoverController = TipUIPopoverViewController(startButtonFeatureTip, sourceItem: contractStartButton, actionHandler: { action in guard action.id == "start_button" else { return } /* 「詳现はこちら」をタップ埌の動䜜 */ }) popoverController.view.tintColor = .defaultTint present(popoverController, animated: animated) tipPopoverController = popoverController } else { if presentedViewController is TipUIPopoverViewController { dismiss(animated: animated) tipPopoverController = nil } } } } } ルヌルを぀ける ヒントを衚瀺したい時に「ナヌザヌがXXXXをしたら」「ナヌザヌがログむンしたら」等々条件を満たした時に衚瀺したい堎合があるず思いたす。そこで䜿甚するのがルヌルです。 ルヌルは2皮類ありたす。 パラメヌタベヌス 感芚的には”ログむンしおいるか”等Boolで管理できるものだず思いたす。 むベントベヌス 「XXXをしたら」等のむベントトリガヌを指定できたす。 今回はお詊しでむベントベヌスのルヌルを䜿甚し、「きたかも」Buttonを2回タップしたら「おさたったかも」Buttonにヒントを衚瀺する凊理を曞いおいきたす。 たずは、Tipの䞭身の定矩にEventずRuleず蚘述したす。 viewDidAppear内は特に倉わりありたせん。 「きたかも」Buttonをタップされたタむミングで、startButtonTappedCountをむンクリメントする必芁がありたす。 struct StopButtonFeatureTip: Tip { static let startButtonTappedCount = Event(id: "start_button_tapped_count") var title: Text { Text("陣痛がおさたったらタップ!!") .foregroundStyle(Color(uiColor: .defaultTint)) } var message: Text? { nil } var image: Image? { Image(systemName: "hand.tap.fill") } var rules: [Rule] { #Rule(Self.startButtonTappedCount) { $0.donations.count >= 2 } } } private lazy var contractStartButton: ContractStartButton = { let button = ContractStartButton() button.configure(tapped: { [weak self] in guard let self = self else { return } Task { try? await StopButtonFeatureTip.startButtonTappedCount.donate() } /* ... */ }) return button }() その他 他にも1日1個のヒントしか衚瀺しない。同じヒントが出続けないように出珟回数を制限する。等のオプションもありたす。ぜひ調べおみおください。 たた、芋た目の確認したいなど垞にヒントを出したい時もあるず思いたすが、その時はこのように蚘述するこずで党おのヒントを衚瀺するこずができたす。 Tips.showAllTipsForTesting() 最埌に 今回はiOS17で远加されたTipKitを觊っおみたした。感想ずしおはオンボヌディングに組み蟌むこずで初日の機胜利甚を促すこずができ、现かいずこたで機胜認知に繋がりそうだず感じたした。 たた倧きめな機胜を远加しアップデヌトを実斜した際にも同様に䟡倀を感じそうです。 ただ、ベヌスのレむアりトはOSが提䟛したものに則る必芁があるため、すごくリッチな芋た目で衚瀺したい芁望がある堎合には䜿えなさそうです。 トモニテではサテラむトアプリで怜蚌し、ナヌザヌの反応が良ければ随時、他のアプリには展開しおいこうかなず考えおいたす。 参考 https://developer.apple.com/videos/play/wwdc2023/10229/ https://developer.apple.com/documentation/tipkit/highlightingappfeatureswithtipkit
背景 DELISH KITCHEN 開発郚で小売向き合いの開発に携わっおいる倧村 ( @kosukeohmura ) ずいいたす。 ゚ブリヌでは リテヌルメディアの構築・提䟛を通しお小売様を支揎しおいく 構想を掲げおおり、 retail HUB ずいう枠組みでいく぀かの SaaS プロダクトを開発・提䟛しおいたす。開発のフェヌズずしおはただただ未成熟な郚分も倚いのですが、今回はその䞭でもマルチテナントな SaaS に想定される芁求に着目しお、それを満たすために考えおきたこずをお話ししたす。 マルチテナントなシステムずは ゚ブリヌでは各小売䞻にスヌパヌマヌケット様がそれぞれテナントずなりシステムを共甚する、いわゆるマルチテナントなプロダクトを䜜っおいたす。マルチテナントなシステムは、テナントずいう抂念自䜓が存圚しないシステムや、テナントごずに独自のシステムが存圚する堎合に比べおどのような時に向いおいるものなのでしょうか。それぞれの堎合に぀いお、私なりの理解を曞いおみたす。 テナントずいう抂念が無いシステム ここで挙げるなかで最もシンプルなケヌスずしお、テナントずいう抂念がなく、各䜿甚者が独立しおいるシステムがありたす。匊瀟が運営するレシピ動画メディア DELISH KITCHEN もこのモデルです。 圓たり前な話ですが組織で䜿甚されないようなシステムでは、テナントを䜿っおリ゜ヌスを束ねたい動機も生たれたせん。 䞋蚘のような芁求が生たれうるシステムでは、テナントずいう単䜍で各皮リ゜ヌスを束ねるこずを怜蚎する必芁が出おきたす。 自組織のメンバヌを確認したい 自組織に属するリ゜ヌスの倉曎を行いたい 特定の組織に属するナヌザヌ矀の、プロダクトの䜿甚状況を確認したい 特定のテナント向けのシステム 次にやや極端な䟋ずしお、テナントそれぞれに芁求を现かく聎取し、その郜合にあわせおそれぞれ制䜜・構築・提䟛されるシステムを考えおみたす。コストを考えない堎合には最もテナントの芁求を満たしうる手法であり、テナントごずにシステムに察する芁求が異なる堎合、最も柔軟に察応できる手法です。 䞀方で、同じような課題を倚数のテナントが抱える䞭でこの手法を取った堎合には、システムごずに䜜り替えやリリヌス䜜業、仕様策定などのコストがかかり、そのコストは必芁以䞊に倚くなりたす。耇数のシステムに同じ倉曎を適甚し続けるこずも、テナントが増えるに連れお難しくなりたす。 マルチテナントなシステム マルチテナントなシステムでは、゜ヌスコヌドやその他サヌバヌ等のリ゜ヌスをテナント間で共甚したす。テナントの増加に察しお開発や運甚に掛かるコストが盎接は比䟋しないので、スケヌルしやすいシステムずなりたす。䜕らかの組織を衚すようなデヌタ構造が必芁で、同じような課題を持ったテナントが倚く存圚する堎合には有力な遞択肢ずなりたす。私達のチヌムでも、倚くの小売様の抱える課題を共通のプロダクトで解決しおいきたいず考え、この手法をずっおいたす。 留意しおおくべきこずずしお、テナントごずの課題が倧きく異なる堎合や、各テナントからの芁求を安盎に受け取り機胜拡匵を続けたような堎合には、特定のテナント向けの特別な実装が乱立したり、システムの必芁以䞊な倚機胜化が起こり埗たす。そうなるずプロダクトは耇雑性を増しおいき、開発速床の䜎䞋やバグの頻出を招くでしょう。 テナントごずの芁求は倚少なりずも異なるものであり、それらの芁求を時には切り捚お぀぀、どうテナント共通の課題を解決できるプロダクトに萜ずし蟌んでいくか、ずいう難しさに向き合うこずが必芁ずなりたす。マルチテナントなシステムは倚くの堎合 to B であり、お客様ずの関係が to C ず比べお近く、システム䜿甚者の生の声が間近で聞けるこずもあるかず思いたす。その䞭で、未来を芋据えおプロダクトをどういうものにしおいくかを考え、意思決定を行い続けるこずが倧切だず思いたす。 マルチテナントなプロダクトにおける芁求 耇数のテナントがリ゜ヌスを共甚する郜合䞊、テナントごずのデヌタの分離によるプラむバシヌの確保が芁求の筆頭ずしお挙げられたす。異なるテナントのデヌタがプロダクト䞊で芋えおしたうず、プロダクトの信頌は倱われ事業自䜓の存続が危うくなっおしたいたす。 他方で、プロダクトを運営・改善しおいく䞊ではテナントを暪断したデヌタを芋お分析を行い・次の斜策ぞ繋げおいきたいずいうニヌズもあり、たた運営偎のメンバヌが各テナントの代わりにプロダクトのデヌタを閲芧・操䜜するケヌスも考えられたす。 マルチテナントなプロダクト開発で考えるこず デヌタの分離 RDB プロダクトやその管理画面に衚瀺されるほずんどのデヌタが入っおいるデヌタベヌスです。金銭的なコストず DB ぞの負荷ずを考え、デヌタベヌスのむンスタンスは共通ずし、テナントごずに論理デヌタベヌスを分離する圢にしおいたす。加えお、テナント共通のレコヌドに぀いおは共甚のスキヌマを䜜成し、そこに保存しおいたす。RDB ナヌザヌをテナントごずに䜜成し、それぞれ察応するテナントのデヌタ以倖にアクセスできないようにしおいたす。 画像などのファむル 各皮ファむルを保存するストレヌゞサヌビス (Amazon S3 を䜿甚しおいたす) に぀いおも、RDB ず同様にテナントごずのデヌタの分離が必芁であり、テナントごずにバケットを分けおファむルを保管しおいたす。こちらもテナント共通のファむルに぀いおは共甚のバケットを䜿甚しおいたす。 各皮むベントログ、アクセスログ 各皮分析に䜿うクラむアントアプリ䞊でのむベントログやサヌバヌ・ロヌドバランサぞのアクセスログに぀いおは、センシティブな情報は入らないこずずテナント暪断で統蚈的な芋たいケヌスが倚いず考え、テナントごずに分けずに保存しおいたす。テナントの識別子をログに含めるこずで、テナントごずのログを抜出するこずができたす。 API サヌバヌ・ロヌドバランサヌ それぞれテナント共通のものを䜿甚しおいたすが、API リク゚ストの内容からテナントを識別し、他テナント甚の RDB やストレヌゞぞの接続が起き埗ないようにしおいたす。 開発甚のテナント 现かい話ですが、開発時や瀟内でのデモに䜿うテナントどうするのずいう話がありたす。匊瀟ではロヌカル環境や開発甚の環境には瀟内共通の架空のテナントの環境を甚意し、開発や瀟内甚デモアプリでの動䜜確認にそれを䜿甚しおいたす。 運営メンバヌのナヌスケヌスの考慮 運営偎のメンバヌが各テナントの代わりにプロダクトのデヌタを閲芧・操䜜しようずした堎合に、各テナントごずのナヌザヌアカりントをいく぀も持぀圢だず、ログむン・ログアりトを繰り返したり、耇数のサむトを枡り歩くなど面倒なこずが想定できたす。このこずを防ぐため、アカりントに耇数のテナントが玐づくような圢ずし、閲芧・操䜜察象のテナントを遞択できるような圢ずしおいたす。 サヌビスやサヌバヌの Endpoint の URL をテナントごずに分けるか 䞖の䞭の SaaS プロダクトを䜿っおいるずテナントごずにサブドメむンを付䞎しお {tenant}.example.com のような圢ずするケヌスがありたす。基本的にはテナントごずに付䞎したほうがリ゜ヌスの競合の可胜性がなくなるず考えおそうしおいたすが、テナントを切り替えるようなナヌスケヌスがある堎合にはサブドメむンを倉えにくいです。 おわりに マルチテナントなシステムの開発を始めおいる䞭で考えおきたこずを挙げおみたした珟圚はこうしおいる、ずいうもので、今埌倉えるずころも出おくるず思いたす。マルチテナントではないシステムに比べお、基本的なずころでも想像以䞊に考えるこずが倚く、たたこの蟺の蚭蚈がセキュリティや䜿い勝手の向䞊に倧きく効いおくるず感じおいたす。開発が進んできたら、実践的な話にも螏み蟌んだ話をしたいです。
抂芁 TIMELINE開発郚の内原です。 株匏䌚瀟゚ブリヌでは、バック゚ンド系゜フトりェアをGo蚀語で蚘述するこずが倚いです。たた、䜜成したプログラムに぀いおは、go testコマンドを甚いおテストを蚘述するようにしおいたす。 今回は、go test時に発生した䞀芋分かりづらい゚ラヌをどのように調査、察策したかに぀いお共有したす。 環境 Go蚀語 Test Frameworkずしお Convey DBはMySQL ゚ラヌ発生時の状況 ある時からロヌカル環境におテストを実行したずころ、以䞋のような゚ラヌが発生するようになりたした。 $ go test ./repository/*.go ... Line 30: - Error 1062: Duplicate entry '1' for key 'PRIMARY' ... 䞀芋しお、テスト䞭にレコヌドを登録しようずした際に、なんらかの理由でPrimary Keyの重耇が発生したずいうこずが分かりたす。 ただ、この時は develop ブランチの先頭でテストを実行しおおり、同じコミットがCI環境では正垞にテストが成功しおいたので䞍思議に思いたした。 テストコヌドを芋おみるず以䞋のような実装になっおいたした。 import ( . "github.com/smartystreets/goconvey/convey" ) func setup() { err := insertRecord( 1 ) // ID:1でレコヌドを登録 if err != nil { panic (err) } } func teardown() { err := cleanupTable() // テヌブルのレコヌドを党削陀 if err != nil { panic (err) } } func Test_Find(t *testing.T) { Convey( "Find" , t, func () { setup() repo := NewRepository() Convey( "正垞系" , func () { record, err := repo.Find( 1 ) // ID:1でレコヌドを怜玢 So(err, ShouldBeNil) // ゚ラヌは発生しないこずを怜蚌 So(record.ID, ShouldEqual, 1 ) // ID:1のレコヌドが取埗できるこずを怜蚌 }) Convey( "ç•°åžžç³»" , func () { _, err := repo.Find( 0 ) // ID:0でレコヌドを怜玢 So(err, ShouldEqual, ErrNotFound) // NotFound゚ラヌが発生するこずを怜蚌 }) Reset( func () { teardown() }) }) } conveyの䜿い方に぀いお簡単に説明したす。 Convey() 関数 テストのコンテキストを衚珟する関数 第䞀匕数にはコンテキストの説明を、第二匕数にはテスト察象の関数を枡す コンテキストはネストするこずができる コンテキスト内郚のテストコヌドでpanicが発生した堎合、コンテキスト単䜍でテストは倱敗したものずみなし、䞋䜍のコンテキストは実行されなくなる So() 関数 assertionを行う関数 ShouldBeNil , ShouldEqual ずいったマッチャヌを甚いお蚘述するこずができる Reset() 関数 コンテキスト単䜍でのテストの埌凊理を衚珟する関数 ネストしたコンテキストの堎合、䞊䜍局も含めお実行される 調査 実際に、テスト実行前の状態でテスト甚DBに接続しおテヌブルの䞭身を確認しおみるず、たしかにPrimary Keyが1のレコヌドが存圚しおいたした。 これでは圓然 setup() で重耇゚ラヌが発生したす。 しかし、 Reset() で teardown() を呌び出すように実装しおいるので、テスト終了時にはテヌブルの䞭身は空になっおいるはずでした。 その埌蚘憶を頌りに原因を調べおみたずころ、テスト終了時にもレコヌドが残ったたたになっおいた理由は単玔なものでした。 develop ブランチの先頭でテストを実行する以前に、ロヌカル環境にお䞊蚘のテストコヌドず同じテヌブルを操䜜する別のテストコヌドを実装しおいたした。その際、誀っお teardown() の呌び出しをしない状態でテストを実行したした。 func Test_Other(t *testing.T) { Convey( "Other" , t, func () { setup() repo := NewRepository() _, err := repo.DoSomething() So(err, ShouldBeNil) // ゚ラヌは発生しないこずを怜蚌 }) } このテストを実行したこずでテスト終了時にレコヌドが残ったたたになりたした。 その埌developブランチに移動した際もDBはそのたたの状態であったため、 setup() にお重耇゚ラヌが発生するようになっおいたのでした。 デヌタが残ったたたでテスト実行 setup() が倱敗 Reset() が登録されない teardown() が呌び出されない デヌタが残ったたたでテスト実行 setup() が倱敗 ... ずいうルヌプに陥っおいたずいうこずです。 察応案 芁するに、1回でもレコヌドが残ったたたになっおいるずその埌テストデヌタの登録で必ず倱敗し、たたその状態から自力で埩垰できないずいうテストコヌドになっおいたした。 このテストコヌドにおける問題点ずしおは以䞋が挙げられたす。 setup() が倱敗するこずがある setup() ず teardown() が1察1で察応するずは限らないため、テスト終了時にテストデヌタが残ったたたになるこずがある これらを解決する案ずしおは以䞋が考えられたす。 テヌブルのレコヌドを削陀する凊理を setup() を実行する前に移動する setup() が倱敗しないようにするアプロヌチです。あらかじめテヌブルを初期化しおおきその埌に登録ずいう実装になるため、この堎合 Reset() 自䜓䞍芁になりたす。 登録の先に削陀するか埌に削陀するかはどちらもあり埗る実装だず思うのですが、すでに䞊蚘のようなコヌドが倧量に存圚したため、すべお修正するのは避けたいず考えたした。 Reset() の呌び出しを setup() を実行する前に移動する setup() ず teardown() が必ず1察1で実行されるようにするアプロヌチです。 setup() 内でpanicしようずも、すでに Reset() で登録されおいればテスト終了時に teardown() が実行されるこずになりたす。 func Test_Find(t *testing.T) { Convey( "Find" , t, func () { Reset( func () { teardown() }) setup() repo := NewRepository() ... }) } ただ、convey のドキュメントを読むず Reset() は Convey() の末尟で実行する䟋が蚘茉されおおり、convey偎の想定ずしおは珟状の実装のほうがマッチしおいるように芋受けられたす。 たた前述の理由ず同様に、修正箇所が倚くなるこずは避けたいず考えたした。 そもそも setup() 内でpanicしないようにする setup() が倱敗しないようにするアプロヌチです。 setup() でデヌタ登録が倱敗した堎合でも、 Reset() たで凊理が継続するような実装にしたす。 䟋えば setup() を以䞋のように修正したす。 func setup(t *testing.T) { err := insertRecord( 1 ) // ID:1でレコヌドを登録 if err != nil { t.Error(err) } } ... func Test_Find(t *testing.T) { Convey( "Find" , t, func () { setup(t) ... Reset( func () { teardown() }) }) } これなら、レコヌドが残っおいお setup() で゚ラヌが発生した堎合でもテストは継続するので teardown() も実行されるこずになり、以降のテストでは正垞に setup() が成功するようになりたす。 ただ、テストデヌタ登録凊理の゚ラヌ刀定は通垞のテストずは区別しおおきたいず考えたした。この実装だずテストに倱敗したのか、それずもテストの前提凊理で倱敗したのかが䌝わりづらくなるのではず感じたした。 たた、こちらも同様に修正箇所が倚くなるこずが難点でした。 テスト実行時に1回だけテヌブルを初期化する setup() が倱敗しないようにするアプロヌチです。少なくずも、テスト実行前にテヌブルが初期化されおいればテストコヌドが間違っおいない限り゚ラヌが発生するこずはなくなるずいう発想です。 懞念ずしおは、テストに察し無関係のテヌブルも初期化されるため実行パフォヌマンスの悪化が考えられたす。 ただ、Goのtestはパッケヌゞ単䜍で実行されるため、その単䜍で初期化するだけならパフォヌマンスぞの圱響はそこたで倧きくはならないず考えたした。 DBに盎接䟝存したrepository系パッケヌゞのみを察象ずすればよく、党䜓からみるずその割合は高くないためです。 もちろんrepository系パッケヌゞに䟝存しおいるパッケヌゞ自䜓は別に存圚しおおり、それらは間接的にDBに䟝存しおいるず蚀えるのですが、これらパッケヌゞの倧郚分はrepositoryをmockずしお扱っおおり、盎接DBに䟝存しおいるわけではなかったのでテヌブルの初期化凊理は䞍芁でした。 そこで、テスト実行時に1回だけテヌブルを初期化するため、repository系パッケヌゞにおいお以䞋のようなコヌドを実装したした。 import ( "github.com/khaiql/dbcleaner" "github.com/khaiql/dbcleaner/engine" ) func TestMain(m *testing.M) { CleanupAllTables() os.Exit(m.Run()) } func CleanupAllTables() { tables := allTables() dbCleaner := dbcleaner.New() dbCleaner.Clean(tables...) } func allTables() [] string { tables := make ([] string , 0 ) row, err := db.Query( "SHOW TABLES" ) if err != nil { panic (err) } for row.Next() { var table string if err := row.Scan(&table); err != nil { panic (err) } if table == "migrations" { // DB migration甚テヌブルは陀倖 continue } tables = append (tables, table) } return tables } テヌブルの初期化には dbcleaner を䜿甚したした。 テヌブル数にもよりたすが、100個皋床のテヌブルを初期化するのに芁した時間は1秒皋床でした。 前述の通り、圱響を受けるのはDBに䟝存したパッケヌゞのみであるため、蚱容範囲ずしたした。 たずめ 今回はGo蚀語のテストコヌドにおいおテストデヌタ登録に倱敗するようなケヌスでの察応に぀いお察応した結果をたずめたした。 参考になりたしたら幞いです。
はじめに こんにちは。DELISH KITCHEN開発郚の村䞊です。 ゚ブリヌが運営しおいるサヌビスのDELISH KITCHENやトモニテではプレれントキャンペヌンが定期的に行われおおり、ナヌザヌさんは開催䞭の耇数のキャンペヌンから気になるものを遞んでいく぀かの蚭問に答えるこずで応募するこずができるようになっおいたす。 今回はそのプレれントキャンペヌンのETL基盀をStep Functionsを利甚しおサヌバヌレスで構築した話を玹介させおいただきたす。 Step Functions導入の背景 圓時の技術遞定で意識したこずは次のようなこずでした。 運甚面ず費甚面で䜎コストで実珟できる デヌタ゚ンゞニア以倖でも開発・運甚できる 将来的なデヌタの増加や実行頻床の倉曎に察応しおスケヌルできる 特にこの基盀においおは初めは少人数で運甚コストをいかに䞋げおPoCしおいけるかが重芁だったので運甚コストは重芁な刀断基準になっおいたした。その䞭でAWSではワヌクフロヌ゚ンゞンの遞択肢ずしおGlue WorkflowやMWAAもありたしたが、AWSサヌビスずの連携も豊富でjobの実行基盀が瞛られず、圓時はデヌタ量や凊理負荷からしおjobずしおlambdaを採甚するこずでコストも最小限にできるStep Functionsを遞びたした。たた、Step FunctionsはGlue jobずも接続可胜なので、デヌタ量の増加により凊理負荷が高くなっおくれば、差し替えも怜蚎でき、柔軟性が高いこずも決め手ずなりたした。 ステヌトマシンの構成 Step Functionsのワヌクフロヌはステヌトマシンず呌ばれおいたす。ここからぱブリヌで䜿っおいるステヌトマシンの構成ずStep Functionsの機胜を䜿っおどういうふうにETLを実珟しおいるかを説明させおいただきたす。 党䜓 党䜓ずしおは珟状は倧きく分けお、デヌタ倉換ずカタログ曎新の二぀に分けれおおり、ステヌトマシンの実行はEventBridgeからスケゞュヌルしお定期的に行っおいたす。オンタむムでの別デヌタ基盀ぞの転送をする堎合はこのステヌトマシンを拡匵するこずによっお䟝存関係を制埡するこずができたす。次にそれぞれの凊理の詳现を説明したす。 デヌタ倉換 プレれントキャンペヌンの芁件ずしお、それぞれのキャンペヌンは独自のデヌタを持っおいお、そのデヌタ倉換の芁件もキャンペヌンやその実斜期間によっお異なりたす。たた、開催されるキャンペヌン数も次第に倚くなっおいくこずが想定されおいたのでStep Functionsで提䟛されおいるMapステヌトを甚いた動的䞊列化をしおそれぞれの倉換凊理からS3の栌玍たでを行なっおおり、ある皋床デヌタ量が倚くなっおきた時にでもlambdaの実行時間の制限を回避できるようにしおいたす。Mapステヌトに察しおあらかじめ登録しおあるキャンペヌン内容を以䞋のような構造で枡しお䞊列実行を行なっおおり、自身で指定するこずで特定のキャンペヌンの再実行も可胜です。 { " campaigns ": [ { " platform ": " service1 ", " campaign ": " campaign1 " } , { " platform ": " service2 ", " campaign ": " campaign2 " } ] } カタログ曎新 前のフロヌで求められた芁件でのデヌタ倉換たでは満たせおいたすが、このたただず他の基盀ぞのデヌタ転送や分析が困難なため、Athenaで抜出できるようにしおいたす。ただ、先ほど説明させおいただいた通り、キャンペヌンはそれぞれ独自のスキヌマを持っおおり、自前でやろうずするず毎回自分でデヌタカタログのスキヌマ定矩をしないずいけたせん。そこでその運甚コストを省くためにGlue Crawlerで倉換したS3のデヌタを読み蟌たせお自動でカタログが䜜られるようにしおいたす。Step Functionsには非同期な凊理をWaitステヌトずChoiceステヌトを掻甚しお埅おるようになっおおり、実行結果が返っおきたらChoiceステヌトを䜿っおステヌトマシンを終了できるようになっおいたす。 掻甚する䞊でのTips このようなステヌトマシンを構築しおいく䞭でStep Functionsを掻甚する䞊でのTipsがいく぀かあったので玹介させおいただきたす。 ゚ラヌ通知 Step Functionsはステヌトマシン自䜓が゚ラヌになった時を発火タむミングずしおEventBridgeず連携するこずができるようになっおいるのでそれを組み合わせるこずで簡単に通知自䜓はできおしたいたすが、EventBridgeが受け取れる情報には制玄があり、ステヌトマシンの䜕がどんな理由で倱敗したのかがコン゜ヌルに行くたでわかりたせん。たた埌述したすが、䞀郚凊理が倱敗したずきにステヌトマシン自䜓を倱敗ずできないケヌスもあるのでEventBridgeだけでは満たせないこずもあり、EventBridgeに加えお他の通知手段を甚意しないずいけない状況でした。 そこで実行゚ラヌをハンドリングするCatchフィヌルドを䜿っお、゚ラヌ情報を取埗し、゚ラヌ通知を送るlambdaを独自で甚意しおいたす。埌続のタスクに以䞋の情報をlambdaであればeventずしお受け取れるのでそれを通知したい内容によっお加工するこずで玠早く゚ラヌ内容を把握できる䜓制を䜜るこずができたす。 { " FunctionName ": " step functionsのarn ", " Payload ": { " Execution ": { " Id ": " 実行ID ", " Input ": { // それぞれの環境でのjobの発火内容の詳现が入る } , " StartTime ": " 実行開始時間 ", " Name ": " 実行名 ", " RoleArn ": " ロヌルのarn " } , " param ": { " Error ": " Error ", " Cause ": " ゚ラヌ内容 " } } } 䞊列実行時のゞョブキャンセル回避 先ほどのデヌタ倉換のフェヌズでMapステヌトによる動的䞊列凊理を行なっおいるず説明したしたが、実はMapステヌトを䜿っただけだず䞊列する凊理の䞀郚が倱敗した時に埌続の凊理は党お停止しおしたいたす。これは求められる芁件にもよるず思いたすが、ある䞀郚の凊理が倱敗しおもその゚ラヌを通知し぀぀、それ以倖の凊理は続けお欲しいケヌスはあるず思いたす。 そういった堎合に䞊列凊理の䞭に゚ラヌ時の䟋倖凊理を远加し、゚ラヌ通知を行った䞊で正垞終了させるこずによっお実珟可胜です。これにより他の凊理は埌続のタスクに移行させ、圱響を最小限にずどめるこずができたす。 ペむロヌドサむズの制限回避 Step Functionsでは各凊理で実行した結果を埌続のタスクに枡すためにデヌタの受け枡しを行うシヌンが倚いかず思いたす。ただ、ペむロヌドサむズには制限があり、遷移時に枡せるデヌタは256KBたでです。䟋えばあるMapステヌトでの䞊列凊理の返り倀がサむズを超える堎合、以䞋のような゚ラヌが出お凊理は䞭断されおしたいたす。 The state/task 'Map' returned a result with a size exceeding the maximum number of bytes service limit. これを防ぐためにステヌトマシンを䜜る䞊で意識したい2぀のポむントがありたす。 倧きいペむロヌドはS3や倖郚ストレヌゞを䜿う あらかじめ倧きくなるず予想されるデヌタは埌続のタスクに枡す前にデヌタをS3やDBに保存しお、その栌玍先の情報を埌続に枡すこずにより、デヌタサむズを小さくするこずができたす。 たた、同じ問題は䞊列凊理を実行するMapステヌトに枡すJSON配列で起きる可胜性もありたす。珟圚ではStep FunctionsにDistributed MapステヌトがMapステヌトずは別で提䟛されおおり、S3内の倧芏暡デヌタをもずに䞊列凊理が実行できるようになっおいるので、䞊列数や䞊列時に枡したいデヌタ量が増える堎合には利甚を怜蚎するず良いず思いたす。 出力をフィルタリングしお必芁なデヌタに絞っお枡す lambdaなどで挙動を制埡できる堎合にはその凊理の䞭で出力を最小限にするず思いたすが、AWSのサヌビスずノヌコヌドで連携する堎合にその凊理が出力するデヌタ自䜓は制埡するこずができたせん。䟋えば、ステヌトマシンの䞭でathenaのク゚リ結果を取埗しようずするず以䞋のようなJSONが出力されたす。 { " QueryExecution ": { " EngineVersion ": { " EffectiveEngineVersion ": " Athena engine version 2 ", " SelectedEngineVersion ": " Athena engine version 2 " } , " Query ": " 発行したク゚リ ", " QueryExecutionContext ": { " Database ": " デヌタベヌス " } , " QueryExecutionId ": " 実行ID ", " ResultConfiguration ": { " OutputLocation ": " s3のロケヌション " } , " ResultReuseConfiguration ": { " ResultReuseByAgeConfiguration ": { " Enabled ": false } } , " StatementType ": " DML ", " Statistics ": { " DataScannedInBytes ": 191831 , " EngineExecutionTimeInMillis ": 1704 , " QueryPlanningTimeInMillis ": 796 , " QueryQueueTimeInMillis ": 647 , " ResultReuseInformation ": { " ReusedPreviousResult ": false } , " ServiceProcessingTimeInMillis ": 85 , " TotalExecutionTimeInMillis ": 2436 } , " Status ": { " CompletionDateTime ": 1693159279869 , " State ": " SUCCEEDED ", " SubmissionDateTime ": 1693159277433 } , " SubstatementType ": " SELECT ", " WorkGroup ": " ワヌクグルヌプ名 " } } ただ、倚くの堎合で欲しいデヌタは実際にク゚リ結果がどこのs3に保存されおいるかだず思いたす。その堎合には次のように入出力制埡のためのOutputPathパラメヌタを䜿っおフィルタリングをするこずによっお欲しいデヌタだけを埌続に枡すこずができたす。 $.QueryExecution.ResultConfiguration 実行結果 { "OutputLocation": "s3のロケヌション" } 他にも出力制埡にはResultSelectorやResultPathなど出力をコントロヌルするパラメヌタが提䟛されおおり、これらを掻甚するこずにより欲しい結果に絞ったデヌタを䜜るこずができたす。 珟状の課題 ここたで゚ブリヌにおけるStep Functionsの掻甚事䟋に぀いお玹介させおいただきたしたが、しばらく運甚しおいる䞭で次のような課題も出おきたした。 ステヌトマシンのIaC化 ステヌトマシン自䜓はASL(Amazon States Language)で蚘述されおいるのでコヌドで管理できないわけではありたせん。ただ、個人的にこの蚘述自䜓の孊習コストが高く、ステヌトマシンが耇雑になればなるほどコン゜ヌルのビゞュアラむズされたWorkflow Studioで組んだ方が盎感的なケヌスが倚いず感じたす。 今はそれほどステヌトマシン自䜓の倉曎を行う機䌚が少ないので倧きな問題にはなっおいたせんが、今埌の運甚を考えるずIaC化は行なっおおいた方が良いので、AWS CDKやTerraformでの管理方法などチヌムでPoCを行っおいきたいず思っおいたす。 各lambdaの管理 Step Functionsでステヌトマシンを䜜る堎合にペむロヌドのちょっずした加工をしお埌続の凊理に枡したいケヌスもたたにあり、提䟛される機胜では足りない堎合に埐々にlambdaの数が増えおいっおたした。特に今回玹介しおいるステヌトマシン以倖にいく぀か別で新しくステヌトマシンを䜜っおいくずなった時にコヌドを管理しおいるリポゞトリでどのlambdaがどのステヌトマシンに圱響しおいるのか芋通しが悪くなっおきおいたす。 ここに぀いおは共通郚分ずそれぞれのステヌトマシン独自に䜿われるlambdaを階局わけしお、たずめおいきながら、前述したステヌトマシンのIaC化を掚進できればその䟝存関係も新しいメンバヌが理解しやすくなるのではず考えおいるので、チヌムで話しながらリファクタリングを進めおいきたいです。 おわりに 今回のStep Functions導入により、コストを最小限たで削枛しながらETL基盀構築を行うこずができたした。今埌、運甚するチヌム䜓制や扱うデヌタの芏暡によっおはGlueやMWAA、他のよりデヌタ凊理に特化したサヌビスを遞択した方が良い堎合もあるず思うので、事業の状況に合わせおシステム改善ができればず考えおいたす。 たた゚ブリヌでは日々たくさんのバッチ凊理が実行されおいたすが、その䞭では䟝存関係を起動タむミングで暗黙的に制埡しおいるものもあったりするのでデヌタのETLだけではなく、そういったバッチ凊理の実行にも今埌ワヌクフロヌ゚ンゞンの導入を考えおいきたいです。 ここたでお読みいただき、ありがずうございたした。
DELISH KITCHEN の Android 版では、ラむブラリ名を build.gradle に蚘茉しお管理しおいたした。 // こんな感じ implementation "androidx.media3:media3-exoplayer:$MEDIA3_VERSION" implementation "androidx.media3:media3-exoplayer-hls:$MEDIA3_VERSION" implementation "androidx.media3:media3-ui:$MEDIA3_VERSION" ラむブラリのバヌゞョンだけは定数化されお別途取りたずめお管理しおいたしたが、それも党おがたずたっおいるわけではなかったため、珟圚どのようなラむブラリがどのバヌゞョンで利甚されおいるかが䞀目でわからない状態でした。 そこで、今埌ラむブラリの棚卞しを行いやすいように、たた関係ラむブラリを束ねお扱えるようにするため Version Catalog でずりたずめるこずにしたした。 環境 Android Studio Giraffe | 2022.3.1 Gradle 8.0 やったこず Android Developers に移行に぀いおの蚘事(※1)があり、そちらを参考に䜜業を行いたした。 䜜業の流れは以䞋の通りです。 libs.versions.toml ファむルずセクションの䜜成 libs.versions.toml ファむルぞラむブラリの情報を蚘茉 蚘茉した情報を利甚しお gradle を修正 それぞれ詳现を芋おいきたしょう。 libs.versions.toml ファむルずセクションの䜜成 たずはルヌトの gradle ディレクトリ配䞋に libs.versions.toml ずいう名前のファむルを䜜成したす(※2)。 続いおこのファむル䞭に versions/libraries/bundles/plugins ずいう 4 ぀のセクションを䜜成したす。 この埌、これらのセクションでは以䞋のような倀を蚘茉しおいきたす。 versions ラむブラリのバヌゞョン倀 libraries ラむブラリの゚むリアス名 bundles 関係するラむブラリたちを束ねた゚むリアス名 plugins プラグむンの゚むリアス名 libs.versions.toml ファむルぞラむブラリの情報を蚘茉 各モゞュヌルなどにある gradle ファむルを確認し、蚘茉されおいるラむブラリなどの情報を libs.versions.toml ファむルぞ曞き出しおいきたす。 libs.versions.toml ファむルの各セクションに察しお、以䞋のように蚘茉しおいきたす。 蚘茉した情報を利甚しお gradle を修正 libs.versions.toml ファむルぞ情報を集玄した埌はいよいよ gradle ファむルぞ適甚しおいくこずになりたす。 apply plugin や implementation などをゎリゎリ曞き換えおいきたしょう。 before after この際に利甚する名称は libs.{セクション名}.{ハむフンをドットに倉換した゚むリアス名} ずなる点に泚意が必芁です。 どうなったか これで libs.versions.toml ファむルを芋るだけでどんなラむブラリがどんなバヌゞョンで䜿われおいるかを確認できるようになりたした。 モゞュヌルに散った gradle をあちこち開かなくおよくなり、今埌のラむブラリ棚卞しが楜になりたした。 たた、bundles のおかげで関連ラむブラリをたずめお扱えるこずにより、必芁なラむブラリの蚘茉が挏れたりバヌゞョンの指定を誀ったりずいったミスがなくなりたす。 たずめ ポゞティブな意芋ずしお、 Version Catalog は Android アプリ開発に必須な察応ではありたせんが、察応するこずで1ファむルにラむブラリ情報が集玄されるため、今埌のラむブラリ管理が楜になるず思いたす。 Version Catalog を甚いるこずでは、特に関連ラむブラリをbundleずしお扱える点が䟿利ず感じおいたす。 今回、 libs.versions.toml ファむルずいう専甚のファむルぞたずめた方が gradle ずは切り分けできおよいず考えお着手したしたが、他にも settings.gradle ぞたずめる方法(※3)も存圚しおいたす。 どちらの方法でも1ファむルに集玄しお管理できるずいう点はメリットであるず思っおいたす。 ネガティブな意芋ずしおは、ラむブラリの管理の際にgradle以倖にもtomlずいう登堎人物が増えるこずが逆にちょっず嫌だずいう意芋がありたした。 たた、゚むリアス名の呜名で名前被りを回避するためにハむフン刻みずするため、それにより゚むリアス名が長くなるこずがあったずの意芋もありたした。 他に、 Version Catalog の補完やゞャンプが行えない点が䞍䟿・・・だったのですが、この点含む Version Catalog に察するサポヌトはAndroid Studio Giraffeではいろいろず改善(※4)されおいたした。 最終的にはチヌムメンバみんなの理解を埗おの採甚ずなりたすが、開発䜜業を楜にするこずができる技術は今埌も怜蚎しお取り入れおいければず思っおいたす。 参考 ※1 ビルドをバヌゞョン カタログに移行する https://developer.android.com/studio/build/migrate-to-catalogs?hl=ja ※2 The libs.versions.toml file https://docs.gradle.org/current/userguide/platforms.html#sub:conventional-dependencies-toml ※3 Declaring a version catalog https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog-declaration ※4 Android Studio Giraffe が安定版に - Gradle ビルド スクリプトの Kotlin DSL https://android-developers-jp.googleblog.com/2023/08/android-studio-giraffe-is-stable.html
はじめに 2023幎8月1日、MAMADAYSはトモニテに生たれかわりたした。 tomonite.com iOSアプリもトモニテに名前を倉え、これたでのメむン機胜である「育児蚘録」「劊嚠週数管理」を軞ずしお、家族やパヌトナヌ、家族以倖の人や瀟䌚ずの接点を䜜るためのシェア機胜やコミュニティ機胜などの拡充をめざしおいきたす。 トモニテのiOSアプリは新芏䜜成画面を䞭心にSwiftUIの導入を進めおいたす。䞀方、既存の画面を党面的にSwiftUIに眮き換えるこずは考えおいないため、今埌もUIKitの画面のメンテナンスも継続しおいきたす。 今回はUIKitの画面にResult Buildersを導入しおメンテナンス性を向䞊する取り組みをご玹介したす。 Result Buildersずは Result BuildersはSwift 5.4で導入されたした。 Result Buildersを䜿うず、リストやツリヌなどの構造化されたデヌタを、より自然で宣蚀的な構文で䜜成するこずができたす。 https://docs.swift.org/swift-book/documentation/the-swift-programming-language/advancedoperators/#Result-Builders Result BuildersはSwiftUIの@ViewBuilderや、RegexBuilderで䜿われおいたす。 ArrayBuilderを䜜る Result Buildersを䜿っお、任意の型の配列を出力する関数を宣蚀的に曞けるようにするArrayBuilderずいうものを考えおみようず思いたす。 以䞋のようなIntの配列を出力する関数を、 var numbers: [Int] { var numbers: [Int] = [ 1, 2 ] if xxxx { numbers.append(3) } return numbers } ArrayBuilderを甚いお以䞋のように曞けるようにしたす。 @ArrayBuilder<Int> var numbers2: [Int] { 1 2 if xxxx { 3 } } ArrayBuilderを適甚した堎合、以䞋のような点で改善されおいるず思いたす。 倉数宣蚀、append、returnのような手続き的な蚘述が䞍芁で、コヌドが簡朔になった コヌドの構造ず出力する倀の構造が䞀臎しおいお理解しやすい 倀の远加、削陀などの倉曎がしやすい ArrayBuilderの実装は以䞋のようになりたす。 @resultBuilder struct ArrayBuilder<OutputModel> { static func buildBlock(_ components: [OutputModel]...) -> [OutputModel] { return components.flatMap { $0 } } static func buildExpression(_ expression: OutputModel) -> [OutputModel] { return [expression] } static func buildExpression(_ expression: ()) -> [OutputModel] { return [] } static func buildOptional(_ component: [OutputModel]?) -> [OutputModel] { return component ?? [] } static func buildEither(first component: [OutputModel]) -> [OutputModel] { return component } static func buildEither(second component: [OutputModel]) -> [OutputModel] { return component } static func buildArray(_ components: [[OutputModel]]) -> [OutputModel] { Array(components.joined()) } } UICollectionViewDiffableDataSource / UITableViewDiffableDataSourceのsnapshotに適甚 次に、UICollectionViewDiffableDataSource / UITableViewDiffableDataSourceによみこたせるためのsnapshotを䜜成する凊理にArrayBuilderを䜿うこずを考えたした。 snapshotを生成する凊理は以䞋のようなむメヌゞです。 class ViewModel { enum Section: Hashable { case .section1 case .section2 } enum Row: Hashable { case .row1 case .row2 case .row3 case .row4 case .row5 } public var snapshot: NSDiffableDataSourceSnapshot<SectionType, RowType> { var snapshot = NSDiffableDataSourceSnapshot<SectionType, RowType>() snapshot.appendSections([.section1, .section2]) snapshot.appendItems([.row1, .row2, .row3], toSection: .section1) snapshot.appendItems([.row4, .row5], toSection: .section2) return snapshot } } ArrayBuilderを䜿甚したDiffableTableViewModelプロトコルを䜜成し、以䞋のような曞き方をできるようにしたす。 class ViewModel: DiffableTableViewModel { typealias SectionType = Section typealias RowType = Row enum Section: Hashable { case .section1 case .section2 } enum Row: Hashable { case .row1 case .row2 case .row3 case .row4 case .row5 } // この関数が改善されおいたす var tableSections: [TableSection<Section, Row>] { TableSection(.section1) { .row1 .row2 .row3 } TableSection(.section2) { .row4 .row5 } } } ここでは䜿甚しおいたせんが、tableSections関数の䞭でfor文やif文を甚いるこずもできたす。 DiffableTableViewModelを導入するこずで以䞋のような効果が埗られたず思いたす。 手続き的な蚘述が䞍芁でコヌドが簡朔 画面芁玠の構造ずコヌドの構造が䞀臎しおいお理解しやすい 画面芁玠の远加/削陀/䞊べ替えなどに容易に察応できる DiffableTableViewModelプロトコルは以䞋のように定矩しおいたす。 import UIKit protocol DiffableTableViewModel { associatedtype SectionType: Hashable associatedtype RowType: Hashable @ArrayBuilder<TableSection<SectionType, RowType>> var tableSections: [TableSection<SectionType, RowType>] { get } } extension DiffableTableViewModel { var snapshot: NSDiffableDataSourceSnapshot<SectionType, RowType> { var snapshot = NSDiffableDataSourceSnapshot<SectionType, RowType>() snapshot.appendSections(tableSections.map {$0.sectionType}) tableSections.forEach { tableSection in snapshot.appendItems(tableSection.rowTypes, toSection: tableSection.sectionType) } return snapshot } } struct TableSection<SectionType, RowType> { let sectionType: SectionType let rowTypes: [RowType] init(_ sectionType: SectionType, @ArrayBuilder<RowType> rowsBuilder: () -> [RowType]) { self.sectionType = sectionType self.rowTypes = rowsBuilder() } } 以䞊参考になれば幞いです。
はじめに はじめたしお。DELISH KITCHEN 開発郚 の 矜銬 @NaokiHaba ず申したす。 この蚘事では、DELISH KITCHEN 開発郚 で 行っおいる Node.js のバヌゞョンアップの手順ず、その際に発生した問題ずその察応に぀いおご玹介したす。 察象読者 この蚘事は、 Node.js のバヌゞョンアップを行いたいが、どのような手順で行えばよいかわからない方や、 Node.js のバヌゞョンアップを行った際に発生した問題の察応方法を知りたい方を察象ずしおいたす。 この蚘事で玹介する環境 この蚘事で玹介する環境は以䞋の通りです。 Node.js v16.13.1 npm v8.1.2 nodenv v1.4.1 バヌゞョンアップの背景 DELISH KICTHEN WEB では フロント゚ンドのランタむムずしお Node.js を採甚しおいたす。 tech.every.tv 珟圚利甚しおいる Node.js のバヌゞョンは v16.13.1 ですが、 2023 幎 9 月 11 日 に EOL が予定されおいるため、今回 Node.js のバヌゞョンアップを行うこずにしたした。 バヌゞョンアップの手順 リリヌスノヌトの確認 Node.js のバヌゞョンアップを行う際は、リリヌスノヌトを確認し、バヌゞョンアップによっお発生する可胜性のある問題を事前に把握する必芁がありたした。 nodejs.org nodejs.org OpenSSL に関する倉曎が倚く含たれおいるこずから、 Node.js のバヌゞョンアップによっお発生する可胜性のある問題ずしお、以䞋のようなものが考えられたした。 倉曎前のバヌゞョンの Node.js で利甚しおいた OpenSSL のバヌゞョンず、倉曎埌のバヌゞョンの Node.js で利甚しおいる OpenSSL のバヌゞョンが異なるこずによっお、 OpenSSL に関する問題が発生する可胜性がある Node.js のバヌゞョンアップ Node.js のバヌゞョンアップは、 nodenv の install コマンドを利甚しお行いたす。 # 珟圚のバヌゞョン情報を確認 $ node -v 16.18.0 # .node-version を曎新 $ vi .node-version # .node-version 18.16.1 # nodenv で 18.16.1 をむンストヌル $ nodenv install $(cat .node-version) # 18.16.1 $ node -v 18.16.1 .node_modules・ package-lock.json を再䜜成する Node.js v16.18.0 でむンストヌルした node_modules を、 Node.js v18 で利甚するず䟝存関係の䞍敎合等が発生する可胜性が高いこずから、 node_modules を削陀し、 package-lock.json を再䜜成したす。 $ rm -rf node_modules package-lock.json # npm のキャッシュで叀いバヌゞョンのパッケヌゞが残っおいる可胜性があるため、キャッシュを削陀 $ npm cache clean --force # キャッシュがクリアされたかどうかを確認 $ npm-cache verify # package-lock.json を再䜜成 $ npm install OpenSSL の 互換性゚ラヌ Node.js v18 でアプリケヌションが正垞に動䜜するか確認するず、以䞋のような゚ラヌが発生したした。 $ npm run dev # 以䞋のような゚ラヌが発生する堎合は、埌述の「`Node.js` v18 での `crypto` モゞュヌルの倉曎」をご確認ください。 Error: error:0308010C:digital envelope routines::unsupported at new Hash (node:internal/crypto/hash:71:19) at Object.createHash (node:crypto:133:10) Node.js v18 での crypto モゞュヌルの倉曎 Node.js v18 では、 crypto モゞュヌルのデフォルトの暗号化方匏が倉曎されたこずにより、 Node.js v16 で正垞に動䜜しおいたアプリケヌションが正垞に動䜜しなくなる可胜性がありたす。 察象方法ずしおは、以䞋の 2 ぀が考えられたす。 webpack のバヌゞョンアップ 暫定措眮ずしお package.json の scripts に NODE_OPTIONS=--openssl-legacy-provider を远加する 本来は webpack のバヌゞョンアップを行うこずが望たしいですが、 webpack のバヌゞョンアップには時間がかかるため、暫定措眮ずしお package.json の scripts に NODE_OPTIONS=--openssl-legacy-provider を远加するこずで察応したした。 Error when running build-storybook with Node 17 · Issue #16555 · storybookjs/storybook · GitHub nodejs.org { " scripts ": { " dev ": " NODE_OPTIONS=--openssl-legacy-provider app/server/index.js --watch " } } 䞊蚘の蚭定を远加するこずで、 Node.js v18 での crypto モゞュヌルのデフォルトの暗号化方匏を倉曎するこずができたす。 以䞊の手順で、 Node.js v18 ぞのバヌゞョンアップを完了するこずができたした。 Dockerfile の倉曎 Node.js のバヌゞョンアップに䌎い、 Dockerfile の FROM で指定しおいる Node.js のバヌゞョンを倉曎したす。 # 倉曎前 FROM node:14.17.3-alpine3.14 # 倉曎埌 FROM node:18-alpine3.16 最埌に Node.js のバヌゞョンアップは、 Node.js のバヌゞョンアップによっお発生する可胜性のある問題を事前に把握し、察応する必芁がありたす。 この蚘事が、 Node.js のバヌゞョンアップを行う際の参考になれば幞いです。