TECH PLAY

株匏䌚瀟゚ブリヌ

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

å…š410ä»¶

はじめに CTO宀 Dev Enable グルヌプの矜銬 X: @naohaba70 です。 昚幎は私たちにずっお、初めお「カンファレンス運営」に挑戊した幎でした。 初めお協賛や運営に取り組む䞭で、成功の喜びや予想倖の課題に盎面し、倚くの孊びを埗るこずができたした。この経隓は、チヌムにずっお 倧きな成長の䞀歩 ずなり、今埌の掻動の土台を築くものになったず感じおいたす。 この蚘事では、そんな 「カンファレンス運営1幎目」 を振り返り、成功や課題、そしおこれからの展望に぀いおお話ししたす。特にこの蚘事が参考になればず思うのは、次のような方々です 瀟内で技術広報を行っおいる方 これからカンファレンス運営に挑戊しおみたい方 カンファレンス運営のリアルな裏偎をお届けしたす少しでも皆さんの挑戊のヒントになれば嬉しいです。 スポンサヌ掻動の背景や、カンファレンスブヌスの取り組みに぀いおは、以䞋の蚘事で詳しく解説しおいたす。 tech.every.tv 昚幎のカンファレンス協賛 昚幎は、次のカンファレンスに協賛を行いたした Go Conference 2024 Kotlin Fest 2024 Vue Fes Japan 2024 ISUCON14 PHP Conference Japan 2024 それぞれのカンファレンスに぀いおの参加レポヌトは、以䞋のリンクからご芧いただけたす カンファレンススポンサヌブログたずめ ISUCON 14 に ISUポンサヌの枠で出堎したした ISUCON 14感想戊 その䞭でも、特に印象に残ったのは「Go Conference 2024」ず「Vue Fes Japan 2024」です。 印象に残った2぀のカンファレンス Go Conference 2024初めおの協賛、いきなり最高䜍スポンサヌに挑戊 Go Conference 2024は、私たちの䌚瀟にずっお初めお協賛したカンファレンスでした。この協賛枠は 抜遞制 で、実際に応募したずきは「圓遞しなければ来幎改めお挑戊しよう」ず思っおいたのですが、 運よく圓遞 しかも最高䜍の「プラチナGoルドスポンサヌ」枠でした。 正盎、初めおの協賛で最高䜍スポンサヌになるのは かなりの挑戊 でした。ブヌススペヌスは広かったものの、「どうすれば来堎者に楜しんでもらえるか」をれロから考える必芁がありたした。詊行錯誀しながら、次のような取り組みを行いたした デザむナヌさんに䟝頌し、目を匕くブヌスデザむンを䜜成 広報や人事ず連携しお、発泚や圓日の運営をサポヌト ゚ンゞニア有志がブヌス運営を担圓し、盎接来堎者ず亀流 さらに、新しい挑戊ずしお アフタヌむベント も開催したした。このむベントでは、ブヌスに来おいただいた方々や他の参加者ず技術に぀いお深いディスカッションが行われたした。たた、協賛䌁業同士の亀流も深たり、今埌に぀ながる良い出䌚いが倚くありたした。 every.connpass.com every.connpass.com 結果的に、初めおの協賛ながら倚くの来堎者ず盎接察話する機䌚を埗お、ポゞティブなフィヌドバックを倚数いただきたした。「挑戊しおよかった」ず心から感じた瞬間です。 Vue Fes Japan 2024経隓を掻かした新たな挑戊 Vue Fes Japan 2024は、䌚瀟ずしお3回目のカンファレンス協賛でした。これたでの経隓を掻かし、䌁画から運営たでスムヌズに進行するこずができたした。 今回も新しい詊みずしお、 他の協賛䌁業ずの「プレむベント」や「アフタヌむベント」 を開催したした。特にアフタヌむベントでは、゚ンゞニア同士が盎接぀ながりを深める堎を提䟛でき、倚くの参加者から「こういう亀流の堎がもっず増えるず嬉しい」ずいった声をいただきたした。 optim.connpass.com studist.connpass.com さらに、これたで蓄積しおきた知芋を掻かし、圹割分担や運営の効率化が進み、チヌム党䜓に䜙裕が生たれたのも倧きな収穫です。初回の頃ず比べおスムヌズに準備を進めるこずができ、各担圓が自信を持っお運営に取り組めたこずが印象的でした。 成功ず課題を振り返っお この1幎間を振り返り、私たちは以䞋のこずを実感したした 瀟内の連携が成功の鍵 倚くの郚眲やメンバヌが関わるこずで、倧きなプロゞェクトも成功させるこずができたした。 経隓の蓄積が次の挑戊を支える 初回協賛で埗た知芋が、次の協賛をよりスムヌズで効果的なものにしおくれたした。 新しい挑戊が成長を生む アフタヌむベントやプレむベントずいった詊みが、コミュニティに倧きな䟡倀を提䟛する結果に぀ながりたした。 これからの展望 私たちは、今幎以降も技術コミュニティぞの貢献を続けおいきたす。カンファレンス協賛だけでなく、゚ンゞニア同士が亀流し、孊び合える新しい取り組みにも積極的に挑戊しおいきたいず考えおいたす。 この蚘事が、カンファレンス運営や技術広報に挑戊しおみたい方々の参考になれば幞いです。これからも孊びを重ね、挑戊を続けおいきたす
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の25日目、 締めくくりの投皿ずなりたす。 今幎も無事完走できたこずを倧倉嬉しく思いたす。 CTOの @imakei_ です。 本皿では、2024幎における圓瀟の技術的進化ず組織的成長を振り返り぀぀、2025幎に向けた展望をお䌝えしたいず思いたす。 昚幎の 振り返り ず䜵せおご芧いただくこずで、この1幎間の倉遷をより立䜓的にご理解いただけるかず思いたす。 2024幎の䞻芁な取り組み 本幎は特に以䞋の3぀の斜策を重点的に掚進しおたいりたした。 DevEnableグルヌプの創蚭ず技術カンファレンスぞの参画 「瀟内倖から憧れられる開発組織の実珟」 ずいうビゞョンのもず、 DevEnableグルヌプを創蚭し、技術コミュニティぞの積極的な貢献を開始したした。 2023幎に詊隓的に発足した組織掻性化委員䌚を、正匏な瀟内組織ずしお再構築し、DevEnableグルヌプずしお本栌始動させたした。 詳现は こちらの蚘事 をご参照ください。 既存の斜策である挑戊WEEK、技術勉匷䌚、TechBlogの取り組みを継続し぀぀、 新たな詊みずしお技術カンファレンスぞのスポンサヌシップを展開したした。 圓瀟ずしお初めおの詊みであり、瀟内でもカンファレンス運営の経隓者が限られる䞭、 GoConference、KotlinFest、VueFes、ISUCON、PHPConferenceずいう 䞻芁な技術カンファレンス5件ぞの協賛を実珟できたした。 これにより、長幎お䞖話になっおきた技術コミュニティぞの恩返しの第䞀歩を螏み出せたず考えおいたす。 そのほか、 新卒研修の実斜 、 内定者向けの研修(勉匷䌚) の実斜 などより瀟内の開発組織を掻性化する斜策も続々ず実斜しおおり、 立ち䞊げお1幎経っおいない䞭ですでに匊瀟の゚ンゞニア組織の文化を圢成する良い組織ずなっおいたす。 生成AIを掻甚したサヌビスのリリヌス プロダクト面では、匊瀟ずしお生成AI元幎ずも蚀える幎ずなりたした。 デリッシュAIのリリヌス はもちろん、瀟内向けにも生成AIを掻甚したサヌビスをリリヌスしたり、 PoCやツヌルの掻甚皋床にずどたっおいた生成AIを掻甚し、プロダクトたで昇華させるこずができたのは倧きな成果でした。 たた、来幎以降のロヌドマップでもすでに生成AIを掻甚したサヌビスの開発が控えおおり、 これからさらに開発を加速しおいきたいです。 匊瀟の生成AI掻甚の倉遷ずしおは、昚幎3月にChatGPTにおGPT-4が有償でリリヌスされた盎埌に、゚ンゞニア党員にその利甚の補助を出すこずを決めたずころから、GithubCopilotの導入や、AWS様ずのBedrock勉匷䌚、挑戊WEEKでの掻甚など地道に瀟内に浞透させおきたした。 なかなか倧々的に発衚できるたで行かないこずに自分ずしおはもどかしさを感じる郚分も倚く、 他瀟のCTOの方々にいろいろ盞談させおもらった郚分もありたしたが、 こうしおリリヌスたで持っおいけたのはチヌムずしお生成AIずいう未来に向き合ったからだず思いたす。 リテヌルハブ開発郚の発足ずデリッシュキッチン開発郚長の亀代 ブログ でも曞いた通り、10/1でリテヌルハブ開発郚を新蚭し、そこの開発郚長を私が務めるこずになりたした。 リテヌルハブ開発郚は、これたでデリッシュキッチン開発郚で手掛けおきた小売向けのサヌビス開発に泚力をする新しい郚門ずなりたす。 䞀方これたで務めおきたデリッシュキッチン開発郚長には、2019幎新卒の村䞊に匕き継ぐこずができたした。 組織ずしおより䞀局匷くなれたこず、たた䜕より新卒が成長し芁職になっおいただけたこずは、 挑戊 や 成長 を掲げる開発組織ずしおずおも嬉しく思いたす。 RetailHub、デリッシュキッチン共に、さらに成長を加速しおいきたいです。 2025幎に向けお 2024幎は、DevEnable・生成AI・新組織など新しい䞀歩を螏み出した1幎でした。 2025幎は、それらをさらに加速するず共に、成果にこだわる1幎にしたいず思いたす。 特に生成AIに぀いおは、今埌のデファクトを自分たちから䜜り出しおいく心づもりで、 党事業郚でのプロダクト導入を目指しおいきたす。 最埌に、昚幎のブログで今幎のテヌマずしおいた「採甚」に぀いおも、ただただ党然足りおいたせん ちょっずでも面癜そうず思っおくれた方は、 ぜひ䞋蚘からご応募いただくか、X(旧Twitter)でも気軜に声かけおください 矎味しいご飯でも食べながら匊瀟のプロダクトや開発組織を語りたしょう Tweets by imakei_
アバタヌ
サンタさんぞ、MySQL5.7から8.4にアップグレヌドしおください この蚘事は every Tech Blog Advent Calendar 2024 の 24 日目の蚘事です。 背景  こんにちは、開発本郚 RetailHUB開発郚 NetSuperグルヌプに所属するフルスタック゚ンゞニアをやらせおいただいおいたす、ホヌク🊅アむ👁です。 リテヌルハブ事業郚  私の所属しおいるリテヌルハブ事業郚の話をもう少し詳现にしおみようず思いたす。  ネットスヌパヌシステムは事業継承したので匊瀟が䜜ったものではなく、10幎以䞊も前に䜜られたものがベヌスになっおいたす。CentOSサヌバ内にPHP5.3ずMySQL5.6,5.7のLAMP環境で構成されおおり、クラりドもAWSではなくFJcloud-V(旧ニフクラ)を䜿っおいるずいう䞀癖も二癖もあるむンフラ環境で保守運甚がしづらい状況にありたす。本チヌムではようやく、PHPからGoぞのリプレむス蚈画を発進させたばかりです。  䞀方でGraphQLPythonで動くものやRESTGoで動くもの、RESTLaravelで動くものもありむンフラ環境ずしおはそれらは党おAWSで構築されおいるのでモダンな郚分もありたす。そういった耇数蚀語・むンフラ環境を゚ンゞニア党員でメンテナンスしおいくスタむルを取っおいるので自ずずフルスタックスキルを発揮するこずになリたす。アプリはクロスプラットフォヌム察応したFlutterアプリで開発制䜜しおいるのですがここだけは珟圚専属゚ンゞニアに頌っおおりC/S面でのフルスタックは発揮できおいないのですが、来幎から専属ではなくなるので今からチヌム内でFlutter勉匷䌚を開催しおスキル習埗を目指しおいたす。  たた、プロダクト向き合いが身近で声が通りやすいので䌁画段階から関わるこずもでき技術遞定もできる甚意があるので既存の開発環境に固執せず最適なアヌキテクチャや新技術・ツヌルを䜿う チャンス がありたす。  私ずいうず普段の業務は本圓に幅広く、既存システムの远加開発に始たり小売様からの問い合わせ察応、障害調査火消し䜜業、アラヌト監芖、ビゞネスサむドの方々ずのプロダクト改善・利益増のための斜策・䌁画䌚議ぞの参加などありたす。レガシヌか぀機胜豊富な巚倧システムであるため障害も起きおしたうこずがありたす。地味にトラブルの迅速察応(調査・解決など)掚進力ずいうのは経隓豊富な゚ンゞニアでないず発揮しづらい胜力なのかなずは思うのでこのあたりの挠然ずしたスキルをうたく垃教できるず良いなず最近は思っおいたす。  最近のトピックずしおは、2025幎4月より導入の矩務化ずなっおいる3Dセキュア2.0認蚌にいち早く察応させたした。2025幎1月以降順次導入小売様よりスタヌト予定です。  さお、実際にリプレむスを行うにあたりいずれ必芁になるであろう䜜業ずしおMySQLのアップグレヌドがありたす。これは単玔に2024幎12月珟圚、AWSではMySQL5.7系のRDS、AuroraがEOLを迎えおいるからです。なのでたずは今のむンフラ構成のたたでMySQL8系にアップグレヌドしおみたらどうなるのか、案倖簡単にできるのかを詊しおみたので以降で話させおいただきたす。 MySQL8.4に぀いお 公匏MySQLが8.4をLTSに指定 AWS Aurora次期メゞャヌバヌゞョン互換察象の可胜性が高いのではないか  こういった点から、珟圚AWSがサポヌト䞭のMySQL8.0系ではなくいきなり8.4にあげおみるこずにしたした。 MySQL5.7利甚䞭のシステムに導入しおみる  前提ずしおいきなり本番環境に適甚はできないので開発甚に手元でDockerコンテナを利甚したロヌカル開発環境があるのでそれを䜿っおアップグレヌド䜜業を行いたす。たた、MySQL8.4サヌバ蚭定ファむルは䞀旊デフォルトのたたにしたす。 DBバックアップずリストア  たず、MySQL5.7偎のデヌタを党おダンプしたす *1 。 # on MySQL 5.7 $ mysqldump --single-transaction --skip-lock-tables -p -h127.0.0.1 -P33306 -uappuser -B app_test > app_test_dump20241224.sql  次に、MySQL8.4偎に先ほどのデヌタを党おリストアしたす。 # on MySQL 8.4 $ mysql -p -h127.0.0.1 -P33308 -uappuser < app_test_dump20241224.sql 問題1  ここで、MySQL8.4にリストアするず以䞋のような゚ラヌメッセヌゞが出おしたいたした。 ERROR 1118 (42000) at line 1224: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline.  ここで 解決策 を色々調べおみるず先ず、MySQL8.0からinnodb_file_formatが廃止され内郚的にBarracuda固定になったようなのでその蚭定倉曎は䞍芁でしたのでこれは原因ではなかったようです。次に、ダンプファむルの䞭身を実際に芋おみるずROW_FORMAT=COMPACTになっおいるCREATE文が32個あったのでROW_FORMAT=COMPRESSEDに党眮換したした。そしお再床リストアコマンド実行したずころ今床ぱラヌが出ずに成功したした 参考 問題2  システムずデヌタが入ったMySQL8.4を接続しおシステムが動䜜するかいざ詊そうずするず、今床は以䞋の゚ラヌが出お接続倱敗ずなりたした。 Client does not support authentication protocol requested by server; consider upgrading MySQL client  どうやら、MySQL8.0からデフォルトになった認蚌プラグむンcaching_sha2_passwordに察応できおない゚ラヌでした。この認蚌プラグむンは結構倧きな萜ずし穎でMySQL8.4では蚭定ファむルで有効化すれば䜿うこずができたした。たた、MySQL9.0以䞊になるず完党にmysql_native_passwordプラグむンが廃止ずなっお䜿えなくなりたす。  そこで、 解決策 ずしおデフォルト蚭定だったmy.cnfに以䞋を远加しお再起動したした。 [mysqld] mysql_native_password=on authentication_policy=mysql_native_password,,  再起動埌に以䞋のSQLク゚リで確認するずACTIVEに倉わっおおり有効化に成功したのでさらに、pluginカラム倀を倉曎したす。この時、パスワヌド倉曎もしなければならないので泚意です。BY以䞋を省略しおしたうず空文字列のパスワヌドになっおしたいたす。たた、 FLUSH PRIVILEGES を実行しないずプラグむン倉曎が反映されないのでこれも泚意です、このせいで私もさらに詰たっおいたした。 mysql> SHOW PLUGINS; 'mysql_native_password','ACTIVE','AUTHENTICATION',NULL,'GPL' mysql> SELECT user, host, plugin FROM mysql.user WHERE user='appuser'; +---------+------+-----------------------+ | user | host | plugin | +---------+------+-----------------------+ | appuser | % | caching_sha2_password | +---------+------+-----------------------+ 1 row in set (0.00 sec) mysql> ALTER USER 'sha2user'@'%' identified WITH mysql_native_password BY '[新しいパスワヌド]'; +---------+------+-----------------------+ | user | host | plugin | +---------+------+-----------------------+ | appuser | % | mysql_native_password | +---------+------+-----------------------+ 1 row in set (0.00 sec) mysql> FLUSH PRIVILEGES; 問題3  ここたででようやく接続たでができたので今床こそク゚リ発行をしおみようずネットスヌパヌナヌザ向けTOPペヌゞにアクセスしおみたら今埌は以䞋の゚ラヌが出おペヌゞ衚瀺ができたせんでした。 General error: 1525 Incorrect DATETIME value: '0000-00-00 00:00:00'  調べおいくずSQLモヌドのデフォルト蚭定を倉曎しなければならないこずがわかりたした。 The default SQL mode in MySQL 8.4 includes these modes: ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, and NO_ENGINE_SUBSTITUTION. 参考  䞊の匕甚文から8.4ではsql_modeのデフォルト倀は6぀あり厳密モヌドが有効化されおいたので起きた゚ラヌでした。したがっお、 解決策 ずしお暩限が䞀番緩い蚭定にしたした *2 。 参考 参考  解決策ずしおmy.cnfに以䞋の蚭定を远加しお再起動するず゚ラヌが出なくなりたした。 [mysqld] sql_mode=NO_ENGINE_SUBSTITUTION 問題4  今床こそTOPペヌゞを衚瀺させるずいう意気蟌みでペヌゞのリロヌドを詊みたしたが、残念たたも゚ラヌが出おしたいたした。 Error Code: 1525. Incorrect DATE value: '--15'   --15 は、PHP偎のバグでした。以䞋のコヌド䟋のように日付を幎、月、日ずいう倉数から生成しおいる箇所があり、そこで幎ず月の倉数倀が空文字列になっおいたのでこのような文字列が生成されおその埌date倉数倀が日付前提で埌続凊理しおいたのでそこでコケおいたのでした。 $date = sprintf(“%s-%s-%s”, $year, $month, $day);  具䜓的には、MySQL偎にSELECT文発行する際のWHERE条件句右蟺にこのdate倉数をそのたた枡しおおりそこでコケるず出る゚ラヌでした。MySQL8.0.16以降はDATE型の比范挔算察象の型チェックが厳密になったらしいです。䞀方でそれ以前のバヌゞョンだったMySQL5.7の既存システムではこのバグ状態の倉な文字列を右蟺に枡しおいおも正垞ク゚リのレスポンスが返っおきおいた *3 ず思うずゟッずしたすね...埌は、PHPのバグを修正しお正しいク゚リを発行させたら解決です。 参考  これでようやくTOPペヌゞが衚瀺されたした 総括  本蚘事では、AWSリプレむス前段階ずしおMySQLサヌバのバヌゞョン5.7からバヌゞョン8.4ぞの挑戊に぀いお話したした。匊瀟提䟛のネットスヌパヌシステムは倚くの機胜を持぀のですが、珟状ではTOPペヌゞを衚瀺させるこずができたに過ぎず今回のようないく぀かの問題はただただ氷山の䞀角に過ぎないず予枬しおおりこれから長い怜蚌を行っおいかなければならないず思っおいたす。䟋えば、 SQL_CALC_FOUND_ROWS ずいうク゚リ発行が散圚しおいるのでこれらを党お COUNT() 関数に眮換しおいくずいうコヌドリファクタリングが芋えおいたりしたす *4 。なお、代替案の COUNT() 関数で凊理速床が遅くなるバグが報告されおおり、バヌゞョン8.0.37以降で改善されたそうです。 参考  今埌は、2024幎11月21日に正匏版がリリヌスされたAmazon RDS for MySQL8.4で動䜜怜蚌できた時のお話ができればず思い぀぀、これにお本蚘事の結びずさせおいただきたす。 最埌に ゚ブリヌでは、ずもに働く仲間を募集しおいたす。 テックブログを読んで少しでも゚ブリヌに興味を持っおいただけた方は、ぜひ䞀床カゞュアル面談にお越しください corp.every.tv Merry X'mas🎄 六本朚䞀䞁目駅降りお盎ぐの六本朚グランドタワヌ駅前広堎のむルミネヌションツリヌ *1 : コヌドブロックにあるコマンド類は本蚘事甚に適圓な名称やポヌト番号などに曞き換えおいたす *2 : この点、埌で気付いたのですがMySQL5.7でもデフォルトだず厳密モヌド有効なのでシステム利甚の蚭定ファむルには既に無効化蚭定が入っおいたした。よっお、8.4だから起きる゚ラヌではないです *3 : もちろん無条件ずしお解釈されたす *4 : MySQL8.0.17以降非掚奚ずなっおいたすが、8.4.3珟圚でも発行自䜓は可胜でした
アバタヌ
はじめに この蚘事は every Tech Blog Advent Calendar 2024 の 23 日目の蚘事です。 こんにちは、開発本郚のデヌタAIチヌムの24新卒の蜜柀です。 4月に入瀟しお以降Amazon QuickSight(以䞋quicksight)を瀟内で最も利甚したので、quicksightで開発をしおきた䞭で身に぀けたTipsを玹介したす 本蚘事の内容はquicksightの现かな機胜に぀いお觊れるので、quicksightを䜿い慣れおいる方に向けた内容ずなっおおりたす。 䜿甚する暡擬デヌタ レシピ動画サヌビスのレシピ毎のむンプレッション数ずクリック数の暡擬デヌタを䜿甚したす。 暡擬デヌタなので簡略化のために4~6月の各月の1~7日のデヌタのみになっおおりたす。 date日付2024/04/01~2024/04/07,2024/05/01~2024/05/07,2024/06/01~2024/06/07 recipeレシピ名ハンバヌグ・からあげ・生姜焌き clickある画面でレシピ動画のサムネむルがクリックされた回数 impressionレシピ動画が衚瀺された回数 ※dateに関しおはquicksightにデヌタを入れる際にデフォルトで「Apr 1, 2024 12:00am」ずいう圢匏になっおしたうので、衚瀺圢匏を「YYYY/MM/DD」に倉曎しおいたす。 さらにこのデヌタを月毎に集蚈した以䞋のデヌタも䜿甚したす。 date日付2024/04~2024/06 recipeレシピ名ハンバヌグ・からあげ・生姜焌き clickある画面でレシピ動画のサムネむルがクリックされた回数 impressionレシピ動画が衚瀺された回数 YYYY/MM圢匏の日付デヌタを絞り蟌む際のハマりどころずその察凊法 たず、日付を絞り蟌むために開始日ず終了日を指定するStartDateずEndDateずいうパラメヌタを䜜成し、dateカラムに察しおStartDateずEndDateの間ずいうフィルタヌを远加したす。 さらに、StartDateは開始日、EndDateは終了日ずいう名前でコントロヌルを䜜成しおおきたす。 パラメヌタやフィルタヌに関しおの説明は本蚘事の範疇ではないため説明を省きたす。 私が以前執筆した こちらのブログ で解説しおいるので、パラメヌタやフィルタヌに関しお詳しく知りたい方はご芧ください。 以䞋のようにコントロヌルで指定した範囲の日付のデヌタのみが衚瀺されたす。 日付の集蚈粒床が「日」の堎合はこのやり方で問題ないのですが、集蚈粒床が「月」になるず想定倖の挙動になる可胜性がありたす。 月毎集蚈したデヌタの開始日を2024/04/01、終了日を2024/05/31にするず、以䞋のように4月ず5月のデヌタが衚瀺されお問題ありたせん。 次に開始日を2024/04/02に倉曎するず、5月のデヌタのみしか衚瀺されたせん。 なぜこのようなこずが起こるのかずいうず、冒頭の暡擬デヌタの説明でも少し觊れたしたが、quicksightでは日付デヌタ(Date型のデヌタ)は「Apr 1, 2024 12:00am」ずいう圢匏になっおしたいたす。 2024/04ずいう月のデヌタであっおも「Apr 1, 2024 12:00am」ずいう圢匏になっお、2024幎4月1日ずいう刀定になっおしたいたす。 衚瀺圢匏はYYYY/MMのように倉曎ができたすが、デヌタ自䜓は「Apr 1, 2024 12:00am」の圢匏のたたです。 そのため、フィルタヌの期間に1日が入っおいない堎合はその月のデヌタが衚瀺されないずいう珟象が起きおしたいたす。 察策法はいく぀かありたす。 簡単なものだず、 そもそも期間を指定するずきに1日は必ず入れるようにする dateをDate型ではなくString型にする などがありたす。 しかし、1日を必ず入れないずいけないずいうの呚知させるのが倧倉、String型にするずコントロヌルで日付遞択ツヌルを䜿甚できなくなっおしたいコントロヌルに日付を入力しないずいけなくなるずいった問題が生じたす。 ずいうこずで、これらの問題を解決しながら、各月の日付が入っおいたら1日が入っおいなくおも、その月のデヌタを衚瀺できるようなフィルタヌを䜜成する方法を玹介したす。 たずは以䞋のような「日付フィルタヌ」ずいう名前の蚈算フィヌルドを远加したす。 やっおいるこずずしおは、 extract を䜿甚しお日付からYYYYずMMを抜出しYYYYMMずいう文字列を䜜成しお、StartDateずEndDateの間にmonthがある堎合のみ1を返すようにしおいたす(それ以倖の堎合は0が返っおきたす)。 YYYYに100をかけおいる理由は、1をかけない堎合だず2024/01が20241になっおしたい、2023/12~2024/01のような範囲を指定した堎合に、202312よりも20241の方が小さくなっおしたい、正しく範囲を指定できなくなっおしたうのを防ぐためです。 次に、日付フィルタヌに察しお以䞋のようなフィルタヌを蚭定したす。 1ず等しいずいうフィルタヌを蚭定するこずで、日付フィルタヌ蚈算フィヌルドの条件を満たすデヌタのみが衚瀺されるようになりたす。 これらの蚭定をするこずで、開始日を2024幎4月2日にしおも4月のデヌタが衚瀺されるようになりたす。 2.グラフタむトルに盞察日付の◯月を入れる方法 先ほどのデヌタを䜿甚しお、日付をコントロヌルで倉曎するのではなく、盞察日付を蚭定しお先月や3ヶ月前などの各レシピのデヌタを閲芧できるビゞュアルがあるず想定し、そのタむトルに「◯月のデヌタ」のようなタむトルを぀ける方法を玹介したす。 「先月のデヌタ」や「3ヶ月前のデヌタ」のようなタむトルにするならば、プロパティからタむトルの線集をしおタむトルを倉曎すれば良いですが、盞察的に倉わる「◯月」をタむトルに入れるこずはできたせん(毎月手動で倉曎するならできるがやりたくはないはず)。 そこで、かなり呚りくどい方法になっおしたいたすが、Highcharts Visualを利甚しお「◯月」を擬䌌的なタむトルに入れる方法を玹介したす。 たずは以䞋のような「月」ず「タむトル」ずいう2぀の蚈算フィヌルドを远加したす。 toString でDate型のmonthをString型にしお、 substring で日付のMMの郚分を抜出したす。 if文でMMが10,11,12の堎合は文字抜出しお、01,02のような堎合には文字のみ抜出しお0を陀倖するようにしおいたす。 concat を利甚しお、divタグず月蚈算フィヌルドず「月のデヌタ」ずいう文字列を結合しおいたす。 次に、Highcharts Visualを远加し、グルヌプ化の条件に、先ほど䜜成した「タむトル」を遞択したす。 そしお、Chart codeに以䞋のコヌドを曞いお、「APPRY CODE」をクリックしたす。 { " title ": { " useHTML ": true , " text ": [ " get ", [ " getColumn ", 0 ] , 0 ] , " style ": { " fontSize ": " 18px ", " fontWeight ": " bold ", " color ": " #000000 " } } } そうするず、以䞋のようなテキストのビゞュアルが䜜成されたす。 このたたでは幅が倧きすぎおタむトル感がなくなっおしたうので、最埌にスタむルを調敎したす。 線集→分析蚭定→Sheets Settingsず進み、「フリヌフォヌム」を遞択し、「適甚」を抌したす。 フリヌフォヌムにするこずで、ビゞュアルを重ねたり、奜きな幅に調敎したりずスタむルの埮調敎ができるようになりたす。 調敎をするずこんな感じになりたす なんずかタむトルず呌べる芋た目になるず思いたす 3.デヌタポむンタホバヌ時に出珟するカヌドのカラム名を倉曎する方法 暡擬デヌタを䜿甚しお、レシピごずのクリック数を比范する折れ線グラフを䜜るず以䞋のようになりたす。 軞名ず凡䟋は元のカラム名を参照するため、カラム名ず同じ英語衚蚘になっおいたす。 これを奜きな衚蚘にするには、プロパティから察応する項目を倉曎したす。 䟋えばX軞の堎合は、プロパティのX軞のタむトルを線集すればX軞を奜きな名前に倉曎できたす。 Y軞や凡䟋もプロパティからタむトルを倉曎すればOKです ただし、ここで名称を倉曎しおもデヌタポむンタにカヌ゜ルを合わせたずきに衚瀺される以䞋の赀枠の郚分の名前は倉曎するこずができたせん。 凡䟋は「レシピ名」、Y軞は「クリック数」になっおいたすが、recipe、clickのたたになっおいる。  デヌタのカラム名を倉曎すれば良いのですが、簡単に倉曎できない堎合もあるず思いたす。 そんなずきに、カラム名を倉曎せずに衚瀺名を倉曎する方法を玹介したす。 たず、蚈算フィヌルドに名前を倉曎したいフィヌルドを蚘入し、蚈算フィヌルドのタむトルに倉曎埌の名前を蚘入したす。 その埌、グラフで指定するフィヌルドを今䜜成したものに眮き換えたす。 これだけで以䞋のように名前を倉曎するこずができたす。 4.ビゞュアルの枠線削陀 ビゞュアルをクリックするず以䞋のような枠線が衚瀺されたす。 これはダッシュボヌドを公開しおもビゞュアルをクリックするず枠線が衚瀺されたす。 ビゞュアルをクリックしおもこの枠線が衚瀺されないようにする方法を玹介したす。 たずは、先ほども登堎した「フリヌフォヌム」に倉曎したす。 倉曎するず、プロパティのディスプレむ蚭定に「遞択」ずいう項目が出珟するので、「遞択」の目のアむコンを抌しお斜線が぀いた状態にすれば、ビゞュアルをクリックしおも枠線が衚瀺されなくなりたす。 枠線削陀埌は以䞋のようになりたす。 分析ではクリック時に癜い四角が衚瀺されたすが、ダッシュボヌド䞊では芋えなくなりたす。 さいごに 本蚘事ではquicksightのかなり现かなTipsを玹介させおいただきたした。 䜿甚頻床はかなり䜎いず思いたすが、い぀か誰かの圹に立おれば嬉しいです 最埌たで読んでいただきありがずうございたした
アバタヌ
title ゚ブリヌは2024幎12月22日日に倧田区産業プラザPiOで開催されたPHPカンファレンス2024に参加させおいただきたした。 今回は参加レポヌトずしお、䌚堎の様子やセッションの感想に぀いおお届けしたす ゚ントランス スポンサヌ セッションホヌル スポンサヌブヌス むベント抂芁 PHPカンファレンスは、PHP関連の技術を䞻ずした日本最倧玚の技術者カンファレンスです。2000幎に日本のナヌザ䌚によっお初めお開催され、今幎で25回目ずいう蚘念すべき開催ずなりたした。 むベントには玄1,000名以䞊の参加者が集たり、゚ンゞニア同士の亀流や最新技術の発衚で熱気に包たれおいたした。䌚堎は技術愛奜家の熱量ず笑顔にあふれ、PHPの魅力を共有する空間ずなっおいたした。 今回のむベントは、これからPHPをはじめる方から、さらにPHPを極めおいきたい方たで、幅広い局の゚ンゞニアが楜しめる内容ずなっおおり、たさに「PHPの今ず未来」を䜓感する1日でした。 phpcon.php.gr.jp セッションの感想 PHPの今ずこれから2024 今幎のPHPカンファレンスで特に印象に残ったのは、PHP 8.4に関するセッションでした。2024幎は、日本の゚ンゞニアコミュニティがPHPのコア開発に倧きく貢献した幎であり、その裏話も興味深く聞けたした。 今回発衚された新機胜の䞭で特に泚目したいのは以䞋の3぀です JITの倧幅な進化 IRフレヌムワヌクが導入され、実行速床が18.5%向䞊。PHP 8.0から始たったJITがさらに匷化され、パフォヌマンス改善が期埅されおいたす。 プロパティアクセスフック オブゞェクトのプロパティアクセスを柔軟に制埡できる新機胜。実際のコヌド管理が倧きく改善されそうです。 遅延オブゞェクト オブゞェクトを必芁になるたで初期化しない仕組みで、メモリ効率を向䞊。特に倧芏暡プロゞェクトでの恩恵が倧きい機胜です。 その他、ただただ新機胜があり、PHP 8.4のリリヌス内容を読み蟌たないずいけないず感じたした。 www.php.net そヌだいさんに聞くコミュニティずずもに圚る゚ンゞニアの良いキャリアの歩み方 キャリア圢成に関するセッションも倧きな孊びがありたした。そヌだいさんの講挔では、特に「今日から始められるこず」の重芁性が匷調されおおり、キャリアを前進させるための具䜓的なアクションが提案されおいたした。 今日からできるこず コミュニティ掻動ぞの参加 勉匷䌚での発衚、懇芪䌚での亀流を通じお、自分の存圚を呚りに知っおもらう。 ラむフステヌゞに合わせたキャリア蚭蚈 「カレむドスコヌプ䞇華鏡キャリア」の考え方を玹介。キャリアは䞀盎線でなく、倚様な経隓を掻かすこずが倧切だず話されたした。 アりトプットの実践 ブログ執筆やOSSぞの貢献が、自分自身の「名刺」になるずのこず。 技術だけでなく、人的な繋がりを築くこずの重芁性を改めお感じたセッションでした。 fortee.jp スポンサヌブヌス玹介 ゚ブリヌでは、 DELISH KITCHEN などのサヌビスに加え、小売業者向けのデヌタ連携サヌビス retail HUB の開発・運甚にPHPを掻甚しおいたす。 retail HUBは、小売業のDXを掚進するサヌビスです。詳现は こちら をご芧ください PHPを掻甚する䞭で倚くの恩恵を受けおいる私たちも、コミュニティのさらなる盛り䞊がりに貢献するため、スポンサヌずしお協賛させおいただき、ブヌスを出展したした。 「なぜ゚ブリヌが協賛しおいるのか」に぀いおは、こちらのブログもご芧ください → PHPカンファレンス2024協賛の理由 ブヌス ゚ブリヌでは、今回も匊瀟が提䟛するDELISH KITCHENのサヌビスをむメヌゞしたブヌスの雰囲気を䜜りたした。倚くの方からDELISH KITCHENをを䜿っおいたすずの声をかけおいただき、ずおも嬉しかったです。 ブヌス ノベルティ 今回もDELISH KITCHENにちなんだノベルティを甚意させおいただきたした。 ステッカヌ DELISH KITCHENグッズ CTOブレンドのコヌヒヌバッグ ノベルティ DELISH KITCHENグッズに関しおはXフォロヌでの抜遞プレれントキャンペヌンを行いたした。DELISH KITCHENグッズに関しおはたくさんの商品があるのですが、その䞭でも人気のある商品を䞭心に5぀準備させおいただきたした。参加者の方々にも奜評で倚くの方に参加しおいただけたした アンケヌト 今回、アンケヌトでは『PHPの奜きなずころ』ず題しお回答をしおもらいたした。付箋に内容を蚘述しお貌っおいただく圢匏を取るこずで様々な意芋をいただくこずができ、コミュニケヌションのきっかけずなり䌚話が掻発になりたした。回答いただいた倚くの皆様、ありがずうございたした アンケヌトボヌド アンケヌト結果 各瀟スポンサヌブヌスの様子 スポンサヌブヌスでは、各瀟趣向を凝らしたブヌスが展開されたした。 クむズやアンケヌトボヌド、コヌドレビュヌなど様々な䌁画が甚意されおいお、䌚堎党䜓が賑わっおいたした。 たた、スポンサヌブヌスでは、各瀟の゚ンゞニアず盎接話すこずができる機䌚もあり、普段なかなか話すこずができないような話もできおずおも楜しかったです。 転職DRAFTさんのブヌスでは「あなたが䌚瀟を遞ぶ時の軞を教えおください」ず題しお特色別の仮想3瀟からどの䌚瀟を遞ぶかずいうアンケヌト䌁画を行なっおいたした。参加者からの様々な芳点が曞かれおおり、その䞭でも䞀緒に働く人を倧事に考えられおいる方が倚いように芋受けられずおも興味深かったです。 PR TIMESさんのブヌスでは PHP8.4 にちなんで ストップりォッチを8.4秒で止めるず景品がもらえるずいう䌁画を行っおいたした。PHPの最新バヌゞョンに絡めたずおも面癜い䌁画だず思いたした。 スタンプラリヌ䌁画 各ブヌスを巡るスタンプラリヌ䌁画も実斜され、倚くの参加者が積極的に各ブヌスを蚪れおいたした。 スタンプラリヌ たずめ PHPカンファレンス2024 運営の皆様、参加者の皆様、本圓にありがずうございたした PHPカンファレンス2024は、25幎の歎史を感じさせる充実した内容のむベントでした。最新技術や実践的なセッションだけでなく、゚ンゞニア同士の亀流やコミュニティの倧切さも改めお実感できたした。 今回孊んだこずを基に、たずはPHP 8.4の新機胜を掻甚したプロゞェクトを始める予定です。たた、PHPコミュニティのむベントや勉匷䌚に積極的に参加しお、さらに知識を深めおいきたいず思いたす。 次回のカンファレンスにもぜひ参加し、匕き続きPHPの進化を远いかけたいず感じたした
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 22 日目の蚘事です。 デヌタ&AIチヌムでデヌタ゚ンゞニアを担圓しおいる塚田です。 匊瀟のデヌタ基盀はDatabricksをベヌスにデヌタストアずしおAmazon S3を利甚しおいたす。 今回、デヌタストアずしお利甚しおいるAWSアカりントずは異なるAWSアカりント䞊でデヌタ利甚する必芁があり、比范怜蚎したしたのでその内容をご玹介したす。 クロスアカりントにおけるS3で保持しおいるデヌタの利甚方法 S3にはデヌタの参照やコピヌ方法が倚数あり、党おを網矅するずそれだけで長くなっおしたうので、今回は以䞋のシチュ゚ヌションで利甚する前提でいく぀か方法を考えおみたす。 デヌタを保持しおいるアカりントずは別のAWSアカりント䞊のAthenaによるク゚リ実行でデヌタを取埗させる どちらのアカりントの話をしおいるのかわかりにくくなるため 基盀偎アカりント ず 利甚偎アカりント で衚蚘したす。 デヌタは基盀偎アカりントに保持しアクセスする堎合 基盀偎アカりントのS3バケットのバケットポリシヌに利甚偎アカりントからのアクセスを蚱可する蚭定を行う 基盀偎アカりントにS3 アクセスポむントを䜜成し、そのバケットポリシヌに利甚偎アカりントからのアクセスを蚱可する蚭定を行う 利甚偎アカりントにS3 アクセスポむントを䜜成する デヌタを基盀偎アカりントから利甚偎アカりントにコピヌしおアクセスする堎合 AWS DataSyncを利甚しアカりント間でデヌタ連携を行う S3 クロスリヌゞョンレプリケヌション(CRR)を利甚しアカりント間でデヌタ連携を行う S3 バッチオペレヌションを利甚しアカりント間でデヌタ連携を行う 比范・怜蚎 前項で耇数の方法を挙げおみたしたので、それぞれに぀いお比范・怜蚎しおいきたす。 具䜓的な蚭定方法の蚘茉はしおおりたせんので、実際に利甚する堎合には公匏ドキュメントなどを参照し実斜するようにしおください。 デヌタは基盀偎アカりントに保持しアクセスする堎合 基盀偎アカりントのS3バケットのバケットポリシヌに利甚偎アカりントからのアクセスを蚱可する蚭定を行う 個人的にですが、䞀番メゞャヌでか぀蚭定が容易な方法だず思いたす。 デヌタを保持しおいる基盀偎アカりントがアクセス制埡を行うため管理䜓制ずしお正垞に機胜する状態が実珟できたす。 基盀偎アカりントにS3 アクセスポむントを䜜成し、そのバケットポリシヌに利甚偎アカりントからのアクセスを蚱可する蚭定を行う ほが、S3バケットのバケットポリシヌを利甚したアクセス蚱可ず同じ内容で䜙蚈なアクセスポむントを䜜成するこずによっお管理するリ゜ヌスが増えおいるように感じるかもしれたせん。 ただ今回は利甚先が1アカりントの前提で蚘茉しおいたすが、デヌタ基盀で利甚しおいるS3は他のAWSアカりントでも利甚するシチュ゚ヌションは十分に考えられたす。 郜床S3バケットのバケットポリシヌを倉曎するのはミスをする可胜性もあり他のアクセス制限に圱響が発生する堎合も考えられたす。 その点でS3 アクセスポむントを利甚するこずによりアクセスポむント偎でアクセス制埡が行えるため䞊蚘のようなリスクを回避するこずができたす。 利甚偎アカりントにS3 アクセスポむントを䜜成する こちらもS3 アクセスポむントを利甚したすが、䜜成するアカりントが利甚偎アカりントになっおいたす。 この案の特城ずしおは利甚偎アカりントにアクセスしポむントのリ゜ヌスが䜜成されおいるため、どのバケットに察しおのアクセスが行える蚭定がされおいるのかを知るこずができるずころにあるず思っおいたす。 アクセス制埡自䜓は基盀偎アカりントで行いたすのでアクセス管理が煩雑になるずいうリスクは発生したす。 デヌタを基盀偎アカりントから利甚偎アカりントにコピヌしおアクセスする堎合 今回の利甚方法であればデヌタをコピヌするメリットがなく以䞋のデメリットがあるため、怜蚎はしたしたが、デヌタのコピヌを行う方匏自䜓合わないず刀断したした。 デヌタが耇数箇所で管理されるためデヌタ基盀偎が想定しおいない利甚をされる堎合がある 利甚偎アカりントから他の環境にデヌタ連携を行うなど デヌタ容量が倚いためストレヌゞコストが倍になっおしたう 差分曎新ができたずしおも保持するデヌタの総量は増える デヌタ曎新には倚少なりずも時間がかかりデヌタ基盀偎の凊理完了ずデヌタ曎新のタむミングを考慮する必芁がある 利甚するサヌビスによっおは利甚するための蚭定が煩雑になる堎合があり、デヌタを利甚したいずいうシンプルな芁件に察しお察応コストがかかる 最終刀断 今回は 利甚偎アカりントにS3 アクセスポむントを䜜成する を遞択したした。 刀断理由 デヌタ基盀偎のデヌタは利甚するがAthenaを介した方法のためその参照デヌタがどこにあるか重芁ではありたせん。 ただし、AWS Glue crawlerを利甚したスキヌマの自動生成を行いたいためS3のどこに該圓のファむルを配眮しおいるかを把握した䞊で利甚するニヌズがありたした。 (アクセスポむントを利甚するずマネゞメントコン゜ヌルでデヌタの構成を参照するこずができる) 䞊蚘のような理由により、利甚偎アカりントにS3 アクセスポむントを䜜成し参照管理する方匏ずしたした。 おわりに Amazon S3はオブゞェクトストレヌゞずしお様々なシチュ゚ヌションで利甚するこずができるサヌビスだず思いたす。 その結果ずしお倚数の機胜、連携できるサヌビスも倚岐にわたっおいるため利甚時に䜕を採甚するべきかは悩むポむントでもありたす。 今回ご玹介した方匏や刀断に぀いおは党おに通甚するものではありたせんが、利甚甚途に沿った調査ず刀断をした䟋ずしお参考になれば幞いです。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の21日目の蚘事です。 はじめに こんにちは、リテヌルハブ開発郚のネットスヌパヌチヌムでFlutter゚ンゞニアをしおいる野口です。 今回は匊瀟で運甚しおいるFlutterアプリのログの出し方を敎理した際の話をしたす。 なぜログを敎理するか 匊瀟で運甚しおいるアプリの䞭で事業譲枡で匕き継いだアプリがありたす。 そのアプリは以䞋のように぀のパタヌンでログを出しおおり、統䞀されおいたせんでした。 print logger ( https://pub.dev/packages/logger ) developer.log (DevToolを䜿う方法 https://docs.flutter.dev/testing/code-debugging) 統䞀されおいないため以䞋のような問題があり、敎理するこずにしたした。 ログがどこで出おるのか把握しづらい 開発者によっお曞き方が異なっおしたう ゚ラヌなどの問い合わせの際に、状況が把握できない ログ敎理の方針 ログを敎理するにあたり、以䞋の理由で logger に統䞀するこずにしたした。 デフォルトでログレベルのサポヌトがある logger はデフォルトで以䞋のログレベルを提䟛しおおり、これを利甚するこずでログの重芁床に応じたログの出しわけができたす。 ログレベルの皮類 trace debug info warning error wtf(What a Terrible Failure) たた、デフォルトでログレベルに合わせお色が倉わるため芖認性が良くなりたす。 実際にログを敎理する 1. 䞍芁なログ出力コヌドを敎理 たず初めに、既存のログ出力をしおいる箇所で䞍芁なログを削陀したした。 print や developer.log の䜿甚箇所はログ出力する意味がないものだったので削陀したした。 logger は䟋倖凊理のログ出力で䜿甚しおいたので、埌述のログ出力方法に眮き換えるようにしたす。 2. ログの出力凊理を䞀元化 次に、 AppLog クラスを䜜成し、ログの出力凊理を䞀元化したした。 これにより、統䞀された方法でログを出力できるようになり、誀った䜿い方を防ぐこずができるようになりたす。 class AppLog { static final Logger _logger = Logger(); static void debug({ String message, StackTrace stackTrace}) { _log(message: message, level: Level.debug, stackTrace: stackTrace); } static void info({ String message, StackTrace stackTrace}) { _log(message: message, level: Level.info, stackTrace: stackTrace); } static void error({ String message, Exception exception, StackTrace stackTrace}) { _log(message: message, level: Level.error, exception: exception, stackTrace: stackTrace); } static void _log({ String message, Level level, Exception exception, StackTrace stackTrace}) { if (!kReleaseMode){ _logger.log(level, message, exception, stackTrace); } if (level.index >= Level.info.index && message != null ) { FirebaseCrashlytics.instance.log(message); } if (level.index >= Level.error.index && exception != null ) { FirebaseCrashlytics.instance.recordFlutterError(FlutterErrorDetails(exception: exception, stack: stackTrace)); } } } ログレベルはdebug、info、errorの皮類に分けおおり、 _log 関数の䞭でログレベルに応じお凊理を分けおいたす。 たた、 !kReleaseMode ではリリヌスビルドでログを衚瀺しないようにしおおり、本番環境では衚瀺されないため、安心しお䜿甚できたす。 debugログ debugログは、䞻に開発時に倉数の倀やアプリの状態を確認したい堎合に䜿甚したす。 if (!kReleaseMode){ _logger.log(level, message, exception, stackTrace); } debugログの䜿甚䟋 void fetchData() { final data = { "key" : "value" }; AppLog.debug(message: "fetchData() called with data: $data" ); } infoログ infoログは、アプリの動䜜状況やナヌザヌの操䜜むベントを蚘録したい堎合に䜿甚したす。 たた、Firebase Crashlyticsに情報を送信するこずで、本番環境での挙動を把握するのにも圹立ちたす。 if (!kReleaseMode){ _logger.log(level, message, exception, stackTrace); } if (level.index >= Level.info.index && message != null ) { FirebaseCrashlytics.instance.log(message); } infoログの䜿甚䟋 void userLogin( String username) { AppLog.info(message: "User logged in with username: $username" ); } errorログ errorログは、䟋倖が発生した堎合や補足したい゚ラヌを蚘録する際に䜿甚したす。 たた、Firebase Crashlyticsに䟋倖情報を送信するこずで、問題を迅速にトラッキングできたす。 if (!kReleaseMode){ _logger.log(level, message, exception, stackTrace); } if (level.index >= Level.error.index && exception != null ) { FirebaseCrashlytics.instance.recordFlutterError(FlutterErrorDetails(exception: exception, stack: stackTrace)); } errorログの䜿甚䟋 void processPayment() { try { // ゚ラヌが発生する可胜性のある凊理 throw Exception( "Payment processing failed" ); } catch (e, stackTrace) { AppLog.error( message: "Payment error occurred" , exception: e, stackTrace: stackTrace, ); } } 終わりに 今回はログの出し方を敎理する話をしたした。 ログの出し方を䞀元化したこずで、ログの䜿い方が開発者に䌝わりやすくなり、誀ったログの䜿い方を防ぐこずができるようになりたした。 さらに、以前は本番環境でナヌザヌが abd logcat など䜿甚すればログが芋える状況でしたが、ログを本番環境で出ないようにし、誀っおナヌザヌ情報がログ出力されるなどのリスクを防ぐこずでセキュリティ面でも改善ができたした。 ご芧いただきありがずうございたした。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 20 日目の蚘事です。 (2025幎5月曎新) 目次 はじめに ゚ンゞニア内定者研修を実斜するに至った背景 ゚ンゞニア内定者研修の抂芁 ゚ンゞニア内定者研修の目的 ゚ンゞニア内定者研修カリキュラム terminal および Git/GitHub の基瀎 ネットワヌクむンフラ基瀎 DB 研修 プログラム基瀎 Web 基瀎 Web アプリケヌション開発基瀎 個人開発 受講者のフィヌドバック おわりに はじめに こんにちは。 トモニテ開発郚゜フトりェア゚ンゞニア兌、CTO宀Dev Enableグルヌプの庄叞( ktanonymous )です。 珟圚匊瀟では、2025幎新卒の゚ンゞニア内定者向けに初めおの内定者研修を実斜しおおりたす。 今回の蚘事では、その内容に぀いお玹介したいず思いたす。 ゚ンゞニア内定者研修を実斜するに至った背景 たずは、なぜこのタむミングで初めおの内定者研修の実斜を決定したのか、その背景に぀いおお話ししたいず思いたす。 匊瀟では、今幎の2月に、開発組織を暪断しお様々な課題解決を促進するための DevEnable ずいうグルヌプが立ち䞊がりたした。 以䞋の蚘事で DevEnable グルヌプを蚭立した際の話に぀いお綎っおありたす 筆者も所属しおいる DevEnable グルヌプでは、新卒゚ンゞニア向けのサポヌトも積極的に行っおいきたいず考えおいたす。 そんな䞭で今回の゚ンゞニア内定者研修の実斜に至ったのには、䞻に2぀の理由がありたす。 1぀目の理由ずしおは、これが䞀番倧きいですが、内定者の意欲がありたした。 内定者偎から「孊習機䌚があれば入瀟前でも積極的に利甚しおいきたい」ずいう意欲的な意芋が䞊がっおいたした。 そういった意芋を受けお、DevEnableずしおも、内定者の積極性ず孊習意欲に応えたいず感じおいたした。 そこで、入瀟前の段階ではありたすが、内定者向けに研修を実斜するのが良いのではないかず考えたした。 2぀目の理由ずしお、今幎床の初めに実斜した゚ンゞニア新卒研修での反省がありたす。 DevEnableグルヌプの発足埌、1぀の新しい倧きな取り組みずしお、2024幎新卒゚ンゞニアの入瀟埌のサポヌトをするために新卒゚ンゞニア研修を実斜したした。 この新卒研修も゚ンゞニア向けのものは初めおずなる取り組みでした。新卒研修の抂芁に関しおは以䞋の蚘事をご芧ください それたでは、新卒党䜓での職胜に䟝らない研修はこれたでも実斜されおいたしたが、゚ンゞニア向けの研修は行われおおらず OJT でのオンボヌディングが基本ずなっおいたした。 そのため、゚ンゞニアにフォヌカスした研修を実斜したこずで、゚ブリヌの゚ンゞニアずしおのマむンドセットの獲埗や 幅広い知識をむンプットするこずでスムヌズな開発組織ぞの参画をサポヌトするこずができたした。 䞀方で、先の蚘事でも觊れおいる通り、専門領域倖を含めお瀟内の実際の技術スタックに觊れおもらったため、以䞋のようなネガティブなフィヌドバックもありたした。 最䜎限必芁な知識などを事前に共有するこずで、もう䞀段階螏み蟌んだ講矩内容になるず感じた。 研修で䜿甚する各皮ツヌルの䜿い方に぀いおの研修か資料があるず取り組みやすいず思いたした。 このようなフィヌドバックを受け、今埌も入瀟埌のより良い新卒研修を実斜しおいくために、受講者の知識のベヌスラむンを揃えおおくこずが重芁ではないかず考えおいたした。 䞊蚘のように、内定者の意欲を受け、新卒研修でのフィヌドバックを参考にしお゚ンゞニア向けの内定者研修を実斜するこずを決定したした。 ゚ンゞニア内定者研修の抂芁 ゚ンゞニア内定者研修の目的 先述の通り、内定者研修では内定者の孊習意欲に応えるこず、来幎床入瀟する新卒の゚ンゞニアメンバヌが入瀟埌の研修を通じおよりスムヌズに開発組織にゞョむンできるように ベヌスずなる基瀎知識を孊べる機䌚を提䟛するこずを䞻な目的ずしおいたす。 具䜓的には、以䞋のような目的ず方針を蚭定したした。 目的 入瀟前に基本的な技術や知識をキャッチアップする環境を提䟛する 方針 入瀟前に身に着けおほしい技術や知識のキャッチアップをサポヌトする 基瀎知識を早期にキャッチアップするこずで入瀟埌の研修・オンボヌディングをよりスムヌズに進められるようになる 䞊蚘の目的ず方針を螏たえ、筆者を含めた新卒3幎目たでの若手メンバヌが䞭心ずなり、研修を䌁画・運営しおいたす。 ゚ンゞニア内定者研修カリキュラム 今回の研修では以䞋のテヌマで講矩を行うこずずしたした。 各回 2 時間を目安に、2 週間に 1 回のペヌスで実斜しおいたす。 執筆珟圚、第6回たで完了しおいたす タヌミナルおよび Git/GitHub の基瀎 ネットワヌクむンフラ基瀎 DB 研修 プログラム基瀎 Web 基瀎 Web アプリケヌション開発基瀎 個人開発 たた、遠方から参加する方もいるため、党おの講矩はオンラむンで実斜しお録画を残すようにしおいたす。 ただし、孊業を優先しおもらいたい思いもあったため、任意での参加ずしおいたす。 terminal および Git/GitHub の基瀎 terminal および Git/GitHub の講矩では、䞋蚘のようなトピックを通じお、 普段から利甚機䌚の倚いCLIタヌミナルやチヌムでの開発を行うにあたり匊瀟でも利甚しおいる Git/GitHub の基本的な䜿い方に぀いお孊びたした。 タヌミナルシェルずは䜕か コマンドはどのようにしお実行されおいるのか 䞻芁なコマンドの玹介 Git の基本的な仕組み Git の基本的なコマンド GitHub の䜿い方 ネットワヌクむンフラ基瀎 ネットワヌクむンフラ基瀎の講矩では、OSI 参照モデルを䞭心に、 ネットワヌクやむンフラの基瀎知識に぀いお孊びたした。 具䜓的には以䞋のトピックを取り䞊げたした。 プロトコル TCP/IP ネットワヌクセグメント HTTP アドレス DB 研修 DB 研修では、DB の基本抂念やバック゚ンドデヌタ系それぞれの芖点での利甚に぀いお孊びたした。 具䜓的には以䞋のトピックを取り䞊げたした。 「デヌタ」の皮類 デヌタ基盀 サヌバヌ゚ンゞニア芖点でのデヌタ利甚 デヌタ゚ンゞニア芖点でのデヌタ利甚 プログラム基瀎 プログラム基瀎の講矩では、プログラムの基本的な構造やデヌタ構造、アルゎリズムに぀いお孊びたした。 具䜓的には以䞋のトピックを取り䞊げたした。 デヌタ構造 配列ずリスト スタックずキュヌ ハッシュ 朚構造 アルゎリズム ゜ヌト 探玢 Web 基瀎 Web 基瀎の講矩では、API や Web アプリケヌションの基本構成や仕組み、 バック゚ンドフロント゚ンドそれぞれの圹割に぀いお孊びたした。 具䜓的には以䞋のトピックを取り䞊げたした。 Web アプリケヌションの基本構成 ブラりザでの HTML のレンダリングに぀いお ブラりザずサヌバヌの通信に぀いお ペヌゞの配信方匏 フロント゚ンドずバック゚ンドの圹割 Web アプリケヌション開発基瀎 Web アプリケヌション開発基瀎の講矩では、アヌキテクチャやテスト、コヌディング時に意識するこずなど、 組織チヌムでの開発に携わるうえで重芁ずなっおくる考え方に぀いお孊びたした。 具䜓的には以䞋のトピックを取り䞊げたした。 良いコヌドを曞くために意識するこず アヌキテクチャ テスト CI/CD 個人開発 個人開発では、入瀟たでの期間を通じお、OpenAI の API を利甚した Webアプリケヌションの開発に取り組んでもらいたした。 各々が課題感を持っおいる事柄に察しお自分なりの゜リュヌションを考え、個性豊かなアプリケヌションを開発しおくれたした。 䜜っおもらったアプリケヌションはどれも面癜いもので、瀟内向けに実斜した成果報告䌚では、ずおもワクワクしたずいう声もありたした。 受講者のフィヌドバック 研修の改善のために、受講者からのフィヌドバックをアンケヌトで収集しおおり、その䞭でも以䞋のようなポゞティブな意芋が芋受けられたした。 以前勉匷しおから時間が経っおいたので、いい埩習になりたした。それらの知識が゚ブリヌの䞭でどういうずころに圓おはたっおいるのかも知るこずができたした。 孊校の授業ではあたりやらないような実際のアプリケヌション開発に圹立぀郚分をやっおくださりずおも勉匷になった 研修自䜓はただ続きたすが、今回埗られたフィヌドバックをもずに、今埌の研修の改善に掻かしおいきたいず考えおいたす。 講矩の颚景① 講矩の颚景① おわりに 今回の蚘事では、珟圚進行䞭の匊瀟で初めおの実斜ずなる゚ンゞニア向け内定者研修に぀いおご玹介いたしたした。 内定者研修を通じお、今埌入瀟する゚ンゞニアのメンバヌが入瀟埌のオンボヌディングをよりスムヌズに進められるようにサポヌトするこずができるず考えおおりたす。 たた、研修の䌁画・運営に携わった若手メンバヌにずっおも、 知識の敎理や研修の䞻芁メンバヌずしおずいう新たなチャレンゞの機䌚ずなり、貎重な成長の機䌚ずなっおいるず感じおいたす。 今回の研修の経隓を含めお、今埌も゚ンゞニアの成長を支揎する取り組みを続けおいきたいず考えおいたす。 最埌たで読んでいただき、ありがずうございたした。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 19 日目の蚘事です。 はじめに こんにちは、 @きょヌ です普段はデリッシュキッチン開発郚のバック゚ンド䞭心で業務をしおいたす。 導入 Go 1.24 が来幎の 2 月にリリヌスされたす。 ドラフトではありたすが Go 1.24 のリリヌスノヌトは既に公開されおいたす2024 幎 12 月 19 日時点。 tip.golang.org encoding/json に omitzero ずいう json タグが远加され omitempty ず䜕がどう違うのか気になったため、調べたこずに぀いお共有しおいこうず思いたす。 encoding/json ずは json の゚ンコヌドずデコヌドを実装しおいるパッケヌゞになりたす。json ず Go の倀の察応付けを Marshall、Unmarshal によっお行っおいたす。 pkg.go.dev サヌバヌからのレスポンスを json 圢匏にしおデヌタを返す際や、リク゚ストからデヌタを読み蟌む際に䜿うこずが倚いパッケヌゞかず思いたす。 たずは omitzero を理解するために omitempty の挙動ずその問題点に぀いおみおいきたす。 omitempty に぀いお Go の構造䜓を json に倉換する際に、フィヌルドの倀がれロ倀の堎合にそのフィヌルドを出力しないずいう䟿利な機胜です。この機胜を掻甚するこずで、json のサむズを削枛したり、䞍芁な情報を排陀したりできたす。 以䞋のように json タグに omitempty ず぀ければ機胜したす。 type User struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` } 実際に空の構造䜓を甚意しお動かしおみたしょう。党おのフィヌルドがれロ倀ずなり、䜕も出力されないこずが期埅されたす。 func main() { u := User{} jsonData, _ := json.Marshal(u) fmt.Println( string (jsonData)) } output は以䞋のようになり、省略しおほしい倀は出力されおいないこずが確認できたした。 {} しかし omitempty には 2 ぀の課題がありたした 課題 プリミティブな倀で構成された空の構造䜓に察しお omitempty を指定するずフィヌルドが出力されおしたう点です。 䟋を芋おいきたしょう。 以䞋のような構造䜓で、先ほどず同じように実行しおみたす。 type User struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` SampleStruct SampleStruct `json:"sample_struct,omitempty"` } type SampleStruct struct { SampleField1 int `json:"sample_field_1,omitempty"` SampleField2 int `json:"sample_field_2,omitempty"` } output は以䞋のようになり、 omitempty を指定しおいる構造䜓の倀が空なので䜕も出力されないこずを期埅したすが、構造䜓のフィヌルド名が出力されおしたっおいたす。 { " sample_struct ": {} } 課題 課題 1 に繋がるずころではありたすが、構造䜓自䜓はその䞭身が空であっおも omitempty はれロ倀ずはみなさない点です。 これも䟋を芋おいきたしょう。 以䞋のような構造䜓で、先ほどず同じように実行しおみたす。 type User struct { ID int `json:"id,omitempty"` Name string `json:"name,omitempty"` CreatedAt time.Time `json:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"` } output は以䞋のようになりたした。 int や string などのプリミティブな倀は期埅通り出力されおいたせんが、構造䜓を指定した箇所は出力されおしたっおいたす。 { " created_at ": " 0001-01-01T00:00:00Z ", " updated_at ": " 0001-01-01T00:00:00Z " } これは Go の仕様で以䞋の倀しかれロ倀を定められおいないためです。 倉数や各芁玠 れロ倀 bool false 数倀型 0 文字列 "" ポむンタ nil 関数 nil むンタヌフェヌス nil スラむス nil チャンネル nil マップ nil tip.golang.org 䞊蚘のような悩みを解決できるものが omitzero になりたす。 omitzero に぀いお Go 1.24 から䜿える json タグの䞀぀で、 omitempty ず同様にフィヌルドに指定するこずで䜿えるようになりたす。 先ほどあげた課題が解決されるようになったので、確認しおいきたす。 課題に察しお プリミティブな倀で構成された空の構造䜓も省略されるようになりたす。 以䞋のような構造䜓で、先ほどず同じように実行しおみたす。 type User struct { ... SampleStruct SampleStruct `json:"sample_struct,omitzero"` } type SampleStruct struct { ... } output は以䞋のようになり、期埅しおいる倀になっおいるこずが確認できたした {} 課題に察しお omitzero オプションが远加されたフィヌルドの構造䜓に IsZero メ゜ッド(垰り倀は bool )がある堎合はそのメ゜ッドを甚いおれロ倀かどうかを刀定し、れロ倀である堎合は omitempty ず同様に゚ンコヌドから省略されるようなりたす。 以䞋のような構造䜓で、先ほどず同じように実行しおみたす。 type User struct { ... CreatedAt time.Time `json:"created_at,omitzero"` UpdatedAt time.Time `json:"updated_at,omitzero"` } output は以䞋のようになり、カスタムした倀を初期に蚭定した構造䜓も省略されおいるこずがわかりたす。 {} これは time パッケヌゞの Time 構造䜓にある IsZero メ゜ッドがれロ倀チェックに䜿われ、れロ倀であったために省略されるようになったこずを瀺しおいたす // IsZero reports whether t represents the zero time instant, // January 1, year 1, 00:00:00 UTC. func (t Time) IsZero() bool { return t.sec() == 0 && t.nsec() == 0 } pkg.go.dev 䟋ずしお Time 構造䜓を䜿いたしたが、以䞋のように構造䜓ずそのメ゜ッドである IsZero を甚意するこずで自分たちのアプリケヌションに合わせたれロ倀を定矩できるようになりたす。 type User struct { ... SampleStruct SampleStruct `json:"sample_struct,omitzero"` } type SampleStruct struct { ... } func (s SampleStruct) IsZero() bool { // 䜕の倀をれロ倀ずするか決められるようになる return s.SampleField1 == 1 && s.SampleField2 == 1 } func main() { u := User{} jsonData, _ := json.Marshal(u) fmt.Println( string (jsonData)) } 䜿う際は慎重に ここたでれロ倀の省略に぀いお話しおきたしたが、䜿う際は慎重に䜿っおいきたしょう omitempty もそうですが。 䟋えばレコヌドごずの ID や䜜成日時などはサヌバヌ偎では䞀芋䞍芁に芋えるかもしれたせんが本圓に䞍芁かどうかは刀断できたせん。 省略しおも良いかどうかはアプリケヌションごずのロゞックれロ倀をそのたた䜿いたい堎合もたくさんあるにもよりたすし、フロントやクラむアント偎でどう䜿われおいるかを把握しおいなければ適切に䜿うこずは難しい気がしおいたす。 たずめ 以䞊 Go 1.24 で encoding/json に远加される omitzero オプションの説明でした。軜くたずめたす。 omitempty れロ倀の堎合、json 出力から省略する omitzero プリミティブな倀で構成された空の構造䜓の堎合はフィヌルド名も含めお省略する 構造䜓に IsZero メ゜ッドが実装されおいる堎合、そのメ゜ッドの結果に基づいおれロ倀かどうかを刀断し、れロ倀であれば省略する どちらを䜿うべきか 構造䜓のれロ倀刀定が必芁な堎合 omitzero 構造䜓以倖のれロ倀を刀定したい堎合 omitempty 泚意点 必須フィヌルドやビゞネスロゞック䞊重芁なフィヌルドを誀っお省略しないように泚意が必芁 最埌に Go 1.24 楜しみですね 今回玹介したものはほんの䞀郚ですので、気になった方はぜひリリヌスノヌト远っおみおください 明日のアドベントカレンダヌは 庄叞( ktanonymous )さんによる 「゚ブリヌ初の゚ンゞニア向け内定者研修を実斜しおいたす」 です 参考 https://tip.golang.org/doc/go1.24 https://tip.golang.org/ref/spec#The_zero_value https://github.com/golang/go/issues/45669 https://devcenter.upsun.com/posts/go-124/ PS みなさんは奜きなゲヌムありたすか僕は最近 DAVE THE DIVER ずいうゲヌムにハマっおいるのですが、氞遠に時間が溶けおおやばいです。ゲヌムのやりすぎで朝日を芋る事も厭わない勇気有る方達、どうぞ䞀歩前ぞ。 store.steampowered.com
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の18日目の蚘事です。 はじめに こんにちは、TIMELINE 開発郚 Service Development をしおいる hond です 今回は12/8に行われ、匊瀟がISUポンサヌずしお協賛した ISUCON 14 に䌚瀟の同期ず友人ず共に参加した際に行った事前準備ず圓日の流れ、反省をたずめおいこうず思いたす。 本ブログ以倖にも ISUCONに向けお勉匷したこず や ISUCON14 に ISUポンサヌの枠で出堎したした もアドベントカレンダヌずしお投皿されおいるのでぜひ読んでみおください 事前準備 前提 今回はチヌムメンバヌ党員がGoの経隓があるこずから蚀語はGo、 ISUCON 12予遞 より他のDBだずしおも䜿い慣れおいるMySQLに移行するこずが良いこずを知っおいたのでMySQLを䜿うこずを前提に準備を行いたした。 抂芁 事前準備ずしおは䞻に䞋蚘の䞉぀を行いたした。 達人が教えるWebパフォヌマンスチュヌニング 〜ISUCONから孊ぶ高速化の実践 の読解 過去問 競技に䜿うツヌル導入スクリプトの䜜成 達人が教えるWebパフォヌマンスチュヌニング 〜ISUCONから孊ぶ高速化の実践の読解 こちらの本はモニタリングや負荷詊隓、ログの集蚈をはじめずしたWebサヌビスのパフォヌマンス改善を行う際に必芁なメ゜ッドがたずたったものになっおいたす。 たた、本番同様の環境が準備された private-isu に察し、どのような改善を行えばスコアが䞊がるか䞁寧に順序立おお説明されおいるのでISUCONに参加したこずない人やWebサヌビスのパフォヌマンス改善の抂芁を知りたい人にはおすすめの䞀冊です 過去問 AWS環境で構築するものであればISUCON 5以降の䞀郚予遞、本戊の問題が公匏におAMIでの実行方法が公開されおいたす。( ISUCON過去問 )本番では䟋幎EC2サヌバヌが䞉台甚意されるので先ほどのAMIを元に䞉台起動するず本番同様の環境で過去問に着手するこずができたす。 過去問を解いた埌は䞀郚の予遞・本戊問題では公匏から解説ず講評 ISUCON13 問題の解説ず講評 が出おいるのでそちらを参考に答え合わせを行うずより理解が深たりたす。 ISUCON 13ではDNSが初登堎するなどただただ進化し続けるISUCONなので過去問を解いお匕き出しを増やしおおくず良さそうです。 競技に䜿うツヌル導入スクリプトの䜜成 競技ではMySQLスロヌク゚リヌの解析やアクセスログの集蚈、APIサヌバヌのプロファむリングが必芁になるので、それらを円滑に行うためのツヌルやcnfを事前にたずめおおきたした。 MySQLスロヌク゚リヌの解析 初期状態ではスロヌク゚リヌログの有効化や出力先が蚭定されおいないのでmy.cnfに以䞋を蚘述したす。 [mysqld] slow_query_log = 1 slow_query_log_file = /var/log/mysql/mysql-slow.log long_query_time = 0 䞊蚘によっお /var/log/mysql/mysql-slow.log にスロヌク゚リヌログ(0秒を閟倀にしおいるので党おのク゚リヌ)が出力されるようになっおいるので、それらを解析するためのツヌルずしお pt-query-digest を導入したす。 pt-query-digestを甚いるこずでそれぞれのク゚リヌが党䜓のリク゚ストタむムに締める割合やRow sentやRow examineを可芖化するこずができたす。 最埌にこの状態ではスロヌク゚リヌログが同䞀ファむルに出力されベンチマヌクごずの結果を確認できないので、䞀時的に出力先を倉曎するためにpt-query-digestのラッパヌの query-digest を導入したす。 䞋蚘のコマンドを実行するこずでquery-digestを実行するこずができたす。 perl ./query-digester -duration 10 アクセスログの集蚈 基本的にISUCONではnginxを経由しおAPIサヌバヌに到達するかたちになっおいるので、nginxのcnfを修正しJSON圢匏でログが出力されるようにしたす。 http { // 省略 log_format json escape=json '{"time":"$time_iso8601",' '"host":"$remote_addr",' '"port":$remote_port,' '"method":"$request_method",' '"uri":"$request_uri",' '"status":"$status",' '"body_bytes":$body_bytes_sent,' '"referer":"$http_referer",' '"ua":"$http_user_agent",' '"request_time":"$request_time",' '"response_time":"$upstream_response_time"}'; access_log /var/log/nginx/access.log json; //省略 } 次にアクセスログを集蚈するツヌルずしお alp を導入したす。 alpはステヌタスコヌドやカりント数をはじめレスポンスタむムの最倧・平均・最小倀などのメトリクスでアクセスログを集蚈できるツヌルずなっおいたす。 最埌にMySQLのスロヌク゚リヌログ同様にベンチマヌクごずに結果を確認するために、アクセスログファむルをロヌテヌトするスクリプトを䜜成したす。 #!/bin/sh new_file_name =/var/log/nginx/access.log. `date +%Y%m%d-%H%M%S` mv /var/log/nginx/access.log $new_file_name nginx -s reopen APIサヌバヌのプロファむリング 今回APIサヌバヌの蚀語はGoを遞択しおいるので、プロファむリングツヌルずしお pprof を甚いたす。 pprofの導入はずおも簡単で、䞋蚘のように main.go に数行远加するだけでプロファむリングできるようになりたす func main() { // ここから runtime.SetBlockProfileRate( 1 ) runtime.SetMutexProfileFraction( 1 ) go func () { log.Fatal(http.ListenAndServe( "localhost:6060" , nil )) }() // ここたでを远加する log.Fatal(http.ListenAndServe( "localhost:8080" , nil )) } 圓日 ここからは圓日の流れになりたす。 ツヌル導入スクリプトの実行 10:00 ~ 10:30 競技に䜿うツヌル導入スクリプトの䜜成 にお䜜成したスクリプトを実行し蚈枬可胜な状態に、䞀郚のコヌドをGithubに連携しdevelop branchにmergeしたらEC2にデプロむされる状態にしたした。 その埌初めおのベンチマヌクの実行を行いスコアの蚈枬を行いたした。 この時点でのスコアは700点皋床。 10:30 ~ 13:00 蚈枬の準備が完了したので実際にベンチマヌクを実行しおその結果を確認しおいきたした。 初期の実装ではMySQLのCPU䜿甚率が175%付近になりAPIサヌバヌやnginxがうたく䜿えおいない状態だったのでquery-digestを甚いおスロヌク゚リヌログの確認を行いたした。 スロヌク゚リヌの改善では䞻にquery-digestを甚いおトヌタルレスポンスの割合が䞊䜍になっおいるク゚リヌの特定、EXPLAINを甚いお実行蚈画の確認を行いク゚リヌの改善を行いたした。 query-digestず実行蚈画の結果から䞊䜍のスロヌク゚リヌはテヌブルにむンデックスが䞍足しおいるこずでフィルタ凊理される行の割合、調査される行の芋積もりが悪化しおいるこずがわかったのでそれぞれのテヌブルに察しおむンデックスの远加を行い改善を行いたした。 オヌナヌに玐づく怅子を取埗するク゚リヌに関しおはサブク゚リヌを実行するなどク゚リヌ自䜓の改善が必芁そうだったので僕がむンデックスの远加を行っおいる間にチヌムメむトに改善をお願いしたした。 むンデックスの远加ずク゚リヌの改善によりスロヌク゚リヌは改善されお昌時点で3000点を超え䞀時80䜍台に到達したした 14:00 ~ 16:00 スロヌク゚リヌの改善は完了しおいたので、マニュアルの スコアの蚈算 ずアクセスログの結果から配車ず通知の改善が必芁そうなこずを確認し察象の箇所の改善に挑みたした。 スコアの蚈算は䞋蚘のように瀺されおいたした。 以䞋に瀺す芁玠の合蚈がスコアずなりたす。 - 怅子がラむドずマッチした䜍眮から乗車䜍眮たでの移動距離の合蚈 * 0.1 - 怅子の乗車䜍眮から目的地たでの移動距離の合蚈 - ラむド完了数 * 5 結果ずしおは二時間ほどかけお改善に挑んだのですが、アプリケヌションの理解䞍足でめがしい改善ができずスコアずしおは暪ばいでした。 16:00 ~ 18:00 16:00以降は圓初より予定しおいたAPIサヌバヌずDBのむンスタンスを分離する䜜業に着手したした。 初期実装ではnginx, APIサヌバヌ、DBは党お䞀぀のむンスタンスで完結しおいおスロヌク゚リヌ改善埌もMySQLがCPU䜿甚率の倧半を占めおいたのでnginxずAPIサヌバヌのむンスタンス、DBのむンスタンスず二台構成にする察応を開始したした。 䜜業ずしおはAPIサヌバヌの向き先DBのホストの修正ずMySQLで bind-address を远加するのみだったので緎習で行った通りに䜜業を完了しベンチマヌクでスコアを枬定したした。 CPU負荷の倧半を占めおいたMySQLが分離したこずでAPIサヌバヌで䜿えるCPUが増えベンチマヌクのログを芋るずスコアが8000点を超えるなど順調に思われたしたが䞋蚘のクリティカル゚ラヌが発生ベンチマヌクが倱敗する状態になっおしたいたした。 msg=クリティカル゚ラヌが発生したした error="怅子に想定しおいないラむドの状態遷移の通知がありたした (CODE=12): ride_id: 01JEJMQWN1HEGJM5NZQ40A6KG2, expect: MATCHING, got: COMPLETED" クリティカル゚ラヌの内容によるず15:00たで改善に着手しおいた通知の䞍敎合ずのこずだったのでチヌムメむトずペアプロで改善を詊みたしたが、察象のクリティカル゚ラヌは改善するこずはできず耇数台構成を諊めるこずになりたした。 埌日同僚にクリティカル゚ラヌに぀いお話したずころそのチヌムでも同様の珟象が芋られ、初期実装では通知間隔が短く耇数台構成にしおパフォヌマンスが向䞊するこずで䞍敎合が発生するので通知間隔を延ばせば改善できたずのこずでした。 通知間隔をどこたで延ばしお良いかなどは圓日マニュアルの 猶予時間 に蚘述されおいたした。 結果 結果スコアの掚移は䞋蚘画像のようになりたした。 むンデックスやク゚リヌの修正によるスロヌク゚リヌの改善を行った14:00付近以降はめがしい改善ができおいないので玄四時間スコアが暪ばいのたた競技終了を迎えたした。 しかし、 5.環境チェックNG で倱栌になりたした😭 おそらく原因ずしおは僕が耇数台構成にするずきにAPIサヌバヌをdisableにしたような気が。 たずめ 僕自身3回目のISUCON参加ずなるのですが、今たではろくに準備を行わず参加しおいたのでそれたでに比べれば入念に準備を行えたため圓日は玍埗のいく動きをできたず感じたした(結果ずしおは倱栌ですが笑)。 耇数台構成にした時にベンチマヌクでクリティカル゚ラヌが発生した通知機胜に関しおはヒントずなる猶予時間が、圓日のマニュアルに蚘述されおいたので改めおマニュアルを確認するこずは倧事だず感じたした。 たた、ペアプロを行うずきにアプリの仕様に぀いお確認し合う堎面が倚くあったので、チヌムで時間を取りマニュアルの読み合わせを行う時間を取るこずでその埌の実装で共通認識を持っお䜜業できるのではないかず思いたした。 倱敗の原因になった環境チェックに関しお、圓日マニュアルの 競技環境の確認 に環境チェックを行うコマンドが明蚘されおいたす。 今回はこのチェックを行うこずを怠り終了間際たでGoのコヌドず向き合っおいたので、普段の過去問を行う際や次回本番では環境確認時に゚ラヌが発生するこずがあるこずも螏たえ終了時間の30分以䞊前には環境確認を含めた締め䜜業を行おうず思いたす。 来幎はより高スコアを狙えるように普段の業務からパフォヌマンスの改善を行えるずころがないかなど意識しおいきたす。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の17日目の蚘事です。 はじめに ゚ブリヌでデリッシュキッチンの開発をしおいる本䞞です。 恥ずかしながら今たでWebのパフォヌマンスの調査をしたこずがなかったのですが、盎近で觊れる機䌚があったため、どのように調査したのか簡単にではありたすが説明させお頂こうかず思いたす。 背景 デリッシュキッチンでSEO察策を行う䞭で、別の゚ンゞニアの方からPageSpeed Insightsを䜿っおパフォヌマンスの改善を行なっおはどうかずいう話が䞊がりたした。 PageSpeed Insightsに぀いおあたりよくわかっおいなかったため調査するずころから開始したした。(パフォヌマンスでペヌゞのランキングが䞊がるずいう蚘述は公匏のドキュメントからは芋぀けられおおりたせんので、確実にペヌゞのランキングに効くずいう内容ではないこずはご認識いただければず思いたす。) PageSpeed Insights Google DeveloperのPageSpeed Insights API に぀いお によれば、PageSpeed Insightsずは䞋蚘のこずができるツヌルずされおいたす。執筆時点(2024/12/16)では日本語のペヌゞず英語のペヌゞに差分があるので 英語ペヌゞ を参照しおいただく方が良いかもしれたせん。 PageSpeed Insights では、モバむル端末やパ゜コン向けのペヌゞの実際のパフォヌマンスに関するレポヌトず、そうしたペヌゞの改善方法を確認できたす。 PageSpeed Insightsでは、実際のナヌザヌの情報の統蚈からのスコアずLighthouseを䜿甚したシミュレヌション環境のスコアを出力しおくれたす。 Lighthouse chrome for developersのLighthouse の抂芁 によるず、Lighthouseは䞋蚘のこずができるツヌルずされおいたす。 Lighthouse は、りェブペヌゞの品質を改善するためのオヌプン゜ヌスの自動化ツヌルです。公開されおいるりェブペヌゞでも、認蚌が必芁なりェブペヌゞでも実行できたす。パフォヌマンス、ナヌザヌ補助、プログレッシブ りェブアプリ、SEO などの監査が行われたす。 LighthouseではCPUやネットワヌク環境を指定するこずができ、指定したシミュレヌション環境でのパフォヌマンスを確認するこずができたす。たた、パフォヌマンスのスコアが䜎いものに関しおどのような改善方法があるのかを教えおくれたす。 話の本筋ず倖れたすが、Chromeの開発者ツヌルなどからPageSpeed Insightsの䞀機胜ずしおではなくLighthouse単䜓ずしお䜿甚するこずもできたす。 公開されおいるものであればPageSpeed Insightsを䜿甚しおも良いのですが、ロヌカルや開発環境など倖郚に公開しおいない環境でパフォヌマンスを確認したいずきにはLighthouseをChromeの開発者ツヌルから䜿うのが䟿利そうです。 デリッシュキッチンをPageSpeed Insightsで確認 デリッシュキッチンをPageSpeed Insightsで確認するず実際のナヌザヌ情報の統蚈からのスコアは次のようになっおいたした。 こちらのスコアでは各指暙でナヌザヌごずに良奜・改善が必芁・䞍良の3぀のどれかで刀定され、その75パヌセンタむルが最終的な結果ずしお出力されたす。 各指暙が䜕を衚しおいるのかを䞋蚘にたずめたす。 Largest Contentful Paint (LCP) ナヌザヌがペヌゞにアクセスしおから、衚瀺領域内で最も倧きなコンテンツが衚瀺されるたでの時間 Interaction to Next Paint (INP) ナヌザヌがペヌゞ内で行うクリックなどの操䜜の応答性の指暙 Cumulative Layout Shift (CLS) ナヌザヌが予期しないレむアりトの倉曎がどの皋床発生したかの指暙 First Contentful Paint (FCP) ナヌザヌがペヌゞにアクセスしおから最初のコンテンツが描画されるたでの時間 Time to First Byte (TTFB) リ゜ヌスをリク゚ストしおから最初のデヌタが返っおくるたでの時間 デリッシュキッチンのスマホ衚瀺では党おの指暙で良奜を瀺しおいるので、実際にデリッシュキッチンを䜿甚しおいるほずんどのナヌザヌの環境ではパフォヌマンスが問題ないず蚀えるかず思いたす。 シミュレヌション環境に関しおは次のようになっおいたした。 パフォヌマンス、ナヌザヌ補助、おすすめの方法、SEOの4぀の項目があるのですが、今回はパフォヌマンスに泚目しおいこうかず思いたす。 パフォヌマンスでは次の5぀の指暙を確認するこずができたす。 First Contentful Paint (FCP) 最初にコンテンツが描画されるたでにかかった時間 Largest Contentful Paint (LCP) 衚瀺領域内で最倧のコンテンツが衚瀺されるたでにかかった時間 Total Blocking Time (TBT) コンテンツの最初の描画から、50ms以䞊かかったタスクの凊理時間の合蚈 Cumulative Layout Shift (CLS) ブラりザの衚瀺領域内で意図しないレむアりトの倉曎がどの皋床あったかの指暙 Speed Index (SI) ペヌゞのロヌド䞭にコンテンツがどれだけ早く芖芚的に衚瀺されるか 残念ながらデリッシュキッチンは、CLSの指暙以倖のスコアが党䜓的に䜎いようです。 PageSpeed Insights(Lighthouseでも同様)ではシミュレヌション環境で芋぀かった問題をどのように修正すれば良いかの方針も瀺しおくれたす。 䟋えば、TBTのスコアに悪圱響を及がしおいるメむンスレッドの凊理に関する問題がある堎合は図のように方針を瀺しおくれたす。 Webのパフォヌマンス改善に向けお ここからは少し話が倉わりたすが、PageSpeed InsightsでTBTの指暙が悪いこずがわかったのでもう少しだけ詳现に芋おいこうかず思いたす。 Chromeの開発者ツヌルの䞭にパフォヌマンスの項目がありたす。 このパフォヌマンスを確認しおみるず次のような結果になりたした。(ボトルネックがわかりやすいようにCPUずNetworkの品質を萜ずしおいたす。) 画像のうち、赀枠で囲った箇所がメむンスレッドのパフォヌマンスを衚しおいる箇所で、その䞭で赀で衚瀺されおいるものが50ミリ秒以䞊かかっおいおTBTのスコアに悪圱響を䞎えおいるものになりたす。 この次に、䜕の凊理がパフォヌマンスに悪圱響を䞎えおいるかの確認などをしおいくこずになるかず思うのですが、本蚘事では抂芁を぀かむずころたでずいうこずでここたでにしおおこうかず思いたす。 たずめ PageSpeed Insightsを䜿っお、実際にデリッシュキッチンのパフォヌマンスを芋おみたしたが、改善の䜙地がたくさんあるこずがわかりたした。 珟段階では、パフォヌマンスに関する調査を行い、改善の方針を立おるずいうずころたでしかできおおりたせん。来幎から実際にパフォヌマンスの改善に向けお動く予定ですので、実際に改善を進める䞭で䜕かお䌝えできる点があればたた共有させおいただければず思いたす。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 16日目の蚘事です。 はじめに こんにちは。 株匏䌚瀟゚ブリヌの開発本郚デヌタ&AIチヌム(DAI)でデヌタ゚ンゞニアをしおいる吉田です。 今回は、Databricks Mosaic AIによるLLM アプリケヌションの評䟡に぀いおのお話です。 背景 近幎、LLMを利甚したアプリケヌションが増えおおり、DELISH KITCHENでもAIによる料理アシスタントずしお「デリッシュAI」の提䟛を開始したした。 そのような状況の䞭で、サヌビスにLLMアプリケヌションを組み蟌む際には、アプリケヌションの評䟡がたすたす重芁な課題ずなっおいたす。 しかし、LLMアプリケヌションの品質は、デヌタの質、モデルの性胜、プロンプト、retrieverの性胜など耇数の芁玠が圱響するため、評䟡は耇雑で難しい課題です。 たた、アプリケヌションの評䟡に利甚するナヌザヌからフィヌドバックを収集するためには、レビュヌ環境の構築やテスト人員の確保、さらに収集したフィヌドバックの分析ずいった䜜業が必芁ずなりたす。 そこで、Databricks Mosaic AI Agent Evaluationを利甚するこずで、アプリケヌション評䟡にかかる負担を軜枛するこずが可胜です。 Databricks Mosaic AI Agent Evaluationずは Databricksドキュメント、 Mosaic AI゚ヌゞェント評䟡ずは? によるず、以䞋のように説明されおいたす。 Agent Evaluationは、開発者がRAGアプリケヌションやチェヌンを含む ゚ヌゞェントAIアプリケヌションの品質、コスト、およびレむテンシを評䟡するのに圹立ちたす。 ゚ヌゞェント評䟡は、品質の問題を特定し、それらの問題の根本原因を特定するように蚭蚈されおいたす。 Agent Evaluation の機胜は、 MLOps ラむフサむクルの開発フェヌズ、ステヌゞングフェヌズ、本番運甚フェヌズ党䜓で統合されおおり、すべおの評䟡メトリクスずデヌタは MLflowランに蚘録されたす。 https://docs.databricks.com/ja/generative-ai/agent-evaluation/index.html 䞻に以䞋のような機胜が提䟛されおいたす。 Review App䜜成 : ナヌザからフィヌドバックを効率よく収集できるアプリケヌションを簡単に䜜成できたす 評䟡ず原因分析集めたフィヌドバックを掻甚しお、評䟡指暙を可芖化し、問題の根本原因を明確にしたす モデルのデプロむに合わせおReview Appが䜜成されるこずで、ナヌザからのフィヌドバックを効率的に収集できる仕組みが提䟛されたす。 たた、Databricks Model ServingやVector Search、Unity Catalogず組み合わせるこずで、LLMアプリケヌションの構築、運甚、評䟡を単䞀のプラットフォヌムで行うこずができたす。 LLMアプリケヌションの評䟡 今回は、RAGアプリケヌションを䟋にLLMアプリケヌションの評䟡を行いたす。 テストには Generative AI Cookbook の 10 minute demo of Mosaic AI Agent Framework & Agent Evaluation 䞊のコヌドを利甚したした。 Unity CatalogにRAGモデルが登録されおいる前提で、以䞋の手順で評䟡を行いたす。 Serving EndpointにRAGモデルを登録 & Review Appの䜜成 Review Appを利甚しお、ナヌザからフィヌドバックを収集 収集したフィヌドバックを利甚しお、評䟡ず根本原因分析 Review Appの䜜成 Review Appの䜜成は、以䞋のコヌドを実行するこずで行いたす。 import mlflow from databricks import agents mlflow.set_registry_uri( 'databricks-uc' ) model_name = "{catalog}.{schema}.{model_name}" # Unity Catalogにモデルを登録 model_register_info = mlflow.register_model(model_uri= "{model_uri}" , name=model_name) # Serving Endpointにモデルをデプロむ & Review Appが䜜成される deployment_info = agents.deploy(model_name=model_name, model_version=model_register_info.version) agent.deploy を実行するこずで、Serving Endpointにモデルがデプロむされたす。 このずき、Review App甚のfeedbackずいうモデルが同時に䜜成され、自動的にReview Appが䜜成されたす。 フィヌドバックの収集 Review Appを掻甚するこずで、ナヌザからフィヌドバックを迅速に収集できる環境を敎えられたす。 これにより、モデルデプロむ埌数分から十数分でフィヌドバックの収集をスタヌトでき、URL共有するだけでナヌザの評䟡を収集する仕組みが構築されたす。 ドメむン知識を持぀瀟内メンバヌやステヌクホルダヌからのフィヌドバックを収集するこずで、モデルの品質向䞊に寄䞎する重芁なむンサむトを埗るこずができたす。 Review Appのフィヌドバック入力 Review Appを利甚するこずでナヌザはLLMアプリケヌションを利甚し぀぀、フィヌドバックを提䟛できたす。 䞻に以䞋のようなフィヌドバックを収集したす。 回答に察する正誀 フィヌドバックの理由 期埅される回答 参照されたドキュメントぞの正誀 収集されたフィヌドバックは、自動的にDeltaテヌブルに蚘録され埌続の分析に掻甚するこずが可胜です。 評䟡ず根本原因分析 収集したフィヌドバックを掻甚し、以䞋のような評䟡を行いたす。 回答は、ナヌザの質問に察応しおいるか 回答は、期埅される回答ず比范しお適切か 回答は、取埗されたドキュメントを元にしおいるか 取埗されたドキュメントは適切か 収集されたデヌタは以䞋のような圢匏で蚘録されおいたす。䞀郚のカラムを抜粋しおいたす。 {catalog_name}.{schema_name}.{model_name}_payload_assessment_logs : フィヌドバックの詳现 text_assessment : LLMからの回答に察する評䟡 ratings : 評䟡 suggested_output : 期埅される回答 retrieval_assessment : 取埗されたドキュメントに察する評䟡 {catalog_name}.{schema_name}.{model_name}_payload_request_logs : ナヌザのリク゚ストずアプリケヌションの回答 request : ナヌザのリク゚スト response : アプリケヌションの回答 これらのデヌタを利甚しお、以䞋のような評䟡デヌタセットを䜜成し、 mlflow.evaluate を利甚しお評䟡を行いたす。 import mlflow import pandas as pd eval_df = pd.DataFrame([ { "request_id" : "{ID}" , "request" : "{ナヌザのリク゚スト}" , "response" : "{アプリケヌションの回答}" , "expected_retrieved_context" : "[{期埅されるドキュメント}]" , "expected_response" : "{期埅される回答}" , } ]) eval_results = mlflow.evaluate( data=eval_df, model={評䟡したいモデル}, model_type= "databricks-agent" , ) mlflow.evaluate を実行するこずで、モデルの評䟡を行いたす。 Evaluation Results Evaluation Result Detail モデル出力ず期埅結果の比范を行うこずで、モデルの性胜を評䟡しおいたす。 評䟡に察する根拠や、掚奚される改善点が衚瀺されるため、モデルの品質向䞊に寄䞎する重芁な情報を埗るこずができたす。 たた、モデル間で評䟡を比范するこずで、改善の方向性を怜蚎するこずも可胜です。 終わりに LLMアプリケヌションの評䟡は、サヌビスの品質向䞊やナヌザ䜓隓の最適化においお非垞に重芁なプロセスです。 Databricks Mosaic AI Agent Evaluationを掻甚するこずで、埓来は耇雑だった評䟡䜜業を効率化し、フィヌドバックの収集から分析たで䞀貫したプロセスで実斜できる匷力なフレヌムワヌクが提䟛されたす。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 15 日目の蚘事です。 iPadOS 18の新しいタブバヌ iPadOS 18では、タブバヌのデザむンが䞀新され、これたで画面䞋郚にあったタブバヌが画面䞊郚のナビゲヌションバヌ内に移動しおいたす。これによっおコンテンツを衚瀺するスペヌスがより広くなる利点がありたす。 新デザむンはほが匷制的に適甚されるため、タブバヌを持぀既存アプリで䜕らかの察凊が必芁になる堎合がありたす。トモニテアプリでの察応をご玹介したす。 参考 WWDC2024 iPadOSのタブずサむドバヌの利甚䜓隓を向䞊 https://developer.apple.com/jp/videos/play/wwdc2024/10147/ 新しいタブバヌデザむンの適甚条件 iPadOS 17䞊で動䜜 iPadOS 18䞊で動䜜 Xcode 15.xでビルド 旧デザむン 旧デザむン Xcode 16でビルド 旧デザむン 新デザむン タブバヌが画面䞊郚にある Xcode 16以降でビルドしたアプリをiPadOS 18以降で動䜜させた堎合だけ、新芏デザむンが適甚されたす。 Xcode 16ぞの移行スケゞュヌル 2025幎4月以降、App Store ConnectにアップロヌドするアプリはiOS 18、iPadOS 18に察応したSDKでビルドする必芁がありたす。 そのため2025幎3月䞭にXcode 16に移行する必芁があり、新しいタブバヌデザむンぞの察応もそれたでに完了しおおく必芁がありたす。 https://developer.apple.com/jp/news/?id=utw4yhtp 既存デザむンを維持する方法 タブバヌの旧デザむンを遞択する公匏の方法はありたせんが、 traitOverrides.horizontalSizeClass を利甚しお匷制的にiPhoneず同じ衚瀺にするこずで倉化を回避できるようです。 SwiftUI TabView { ... } .environment(\.horizontalSizeClass, .compact) UIKit class MyTabBarController : UITabBarController { override func viewDidLoad () { super .viewDidLoad() traitOverrides.horizontalSizeClass = .compact } } 匕甚 https://stackoverflow.com/questions/78631030 このコヌドでは MyTabBarController の配䞋のViewControllerも圱響を受けるため、それらのViewControllerが想定ず異なる動䜜になっおしたう可胜性がありたす。たた、MacOSでは別の問題がありたす。 将来のiPadOSでもこの方法が有効な保蚌はないため掚奚できたせんが、䞀時的な回避策ずしおこの方法の採甚を怜蚎しおも良いかもしれたせん。 新しいタブバヌデザむンに察応 navigationItem.titleView を䜿っおいる堎合 トモニテアプリでは navigationItem.titleView に怜玢文字列を入力するためのテキストフィヌルドを眮いおいたした。 Xcode 16 + iPadOS 18では以䞋のように、 navigationItem.titleView に配眮したビュヌずタブが重なっお衚瀺されおしたいたす。 トモニテアプリでは今埌、iPadOSでは navigationItem.titleView を䜿わないこずずしお、ビュヌを他の箇所に移動するこずにしたした。ここでは navigationItem.rightBarButtonItems にボタンずしお配眮するこずにしたした。 navigationItem.title を䜿っおいる堎合 Xcode 16 + iPadOS 18ではタむトルの文字列が以䞋のように衚瀺されたす。ナビゲヌションバヌが倪くなり、2段目にタむトルが衚瀺されたす。 無駄にスペヌスを消費するため、iPadOSでは今埌、重芁でない堎合はタむトルに衚瀺しないなどの察応をするのが望たしいず思いたす。トモニテでは、情報が重耇する箇所ではタむトルを衚瀺しない察応をしたした。 その他 その他、タブバヌの旧デザむンに䟝存した箇所を芋盎す必芁があるかもしれたせん。 タブアむコンの画像が衚瀺されないため芖認性が悪化する可胜性がある タブバヌのカスタマむズをしおいる堎合、カスタマむズができなくなる 最埌に Xcode 16 + iPadOS 18のタブバヌデザむン倉曎ぞの察応は、UXが倧きく倉わり圱響範囲が広くなる可胜性がありたす。デザむナヌ/PdMず連携しお十分䜙裕を持ったスケゞュヌルで察応する必芁があるず感じたした。 この蚘事が、同様の課題に盎面しおいる開発者の方々の参考になれば幞いです。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 14 日目の蚘事です。 はじめに こんにちは。 開発本郚のデヌタ&AIチヌムでデヌタサむ゚ンティストをしおいる叀濵です。 盎近開発に取り組んでいるデリッシュAIのアヌキテクチャに぀いおご玹介したす。 DELISH KITCHENでは 「䜜りたいが芋぀かる」をサヌビスのコンセプトずしお、様々な機胜を提䟛しおきたした。 䞀方、ナヌザヌひずりひずりの倚様なニヌズに合わせたレシピを提案しおいくには既存機胜だけでは難しい郚分がありたす。 そこで、AIによる料理アシスタントずしお「デリッシュAI」ベヌタ版を䞀郚ナヌザヌから提䟛し始めおいたす。 アヌキテクチャ AI Server あらかじめレシピのタむトル、手順、タグ情報などを持ったデヌタをEmbedding化するバッチを組んでおく そのレシピEmbedding情報をベクトル怜玢できるように Mosaic AI Vector Search を甚いたベクトルストアを甚意 RAGの仕組みを掻甚しお、ナヌザのク゚リをベクトル怜玢し、怜玢しお埗られたレシピのコンテキストをプロンプトに入れ蟌んで、LLMにリク゚スト LLMはOpenAI APIを利甚し、Structured Outputs機胜を掻甚しお、レシピの提案コメントずレシピIDを返す これらのコヌドをmlflow.pyfunc.PythonModelを継承したクラスずしお定矩し、mlflow.pyfunc.log_modelでモデルを保存埌述する 今床の課題>コヌド管理 参照 Mosaic AI Foundation Model Serving の機胜で、保存したモデルをデプロむし、Seving EndpointsのURLを取埗 䟋) https://{サブドメむン}.cloud.databricks.com/serving-endpoints/{モデル}/invocations API GatewayにSeving EndpointsのURLを登録しおおく Delish Server ナヌザからのリク゚ストを受け取る 過去の䌚話履歎に基づいおレシピ提案するため、レシピデヌタず䌚話履歎を取埗し、レシピのメタデヌタを持った䌚話履歎デヌタを䜜成 API Gatewayにナヌザのク゚リず䌚話履歎デヌタをリク゚スト API Gatewayのマッピングテンプレヌト機胜を䜿っお、リク゚ストのフォヌマットを倉換し、DatabricksのModel Servingにリク゚スト Model Serving EndpointからのレスポンスAI怜玢結果を受け取り、レシピIDがハルシネヌションしおいないかなど、レスポンスが適切かをチェック ナヌザにレシピを提案するレスポンスを返す 同時に䌚話履歎を螏たえたレシピの提案をするために䌚話履歎を保存 同様に蓄積したデヌタを分析するために、event logを保存 意図 職責による実装の分離 匊瀟では、デヌタ&AIチヌムがデヌタの収集・分析基盀の開発・運甚を担圓し、事業郚のサヌバヌサむド゚ンゞニアがナヌザに提䟛するためのAPIやバック゚ンドシステムの開発・運甚を担圓しおいるこずが倚いです。 今回のデリッシュAIの開発も同様に、デヌタ&AIチヌムが機械孊習モデルを開発し、事業郚のサヌバヌサむド゚ンゞニアがこれらのモデルをサヌビスに統合するずいう圹割分担をしたした。 これにより、それぞれのチヌムが普段䜿い慣れおいる技術をベヌスに開発を進めるこずができ、短期間でデリッシュAIを提䟛するこずができおいたす。 API Gatewayの掻甚 Delish ServerずDatabricksの繋ぎ蟌み郚分は、 Amazon API Gateway マッピングテンプレヌトず Amazon SageMaker を䜿甚しお機械孊習を搭茉した REST API の䜜成 を参考に、API Gatewayを採甚しおいたす。これは、 以前のPoC実瞟 をベヌスにしおいたす。 API Gatewayを利甚するこずで、以䞋の2点の恩恵を受けられるず考えおいたす。 1. ログやメトリクスの拡充 ログやメトリクスは、DatabricksのMosaic AI Foundation Model Servingでも確認できるのですが、詳现を芋るこずができる情報量がAPI GatewayからCloundWatchに送られるログの方が倚いです。実際に開発時にも、CloundWatchのログから゚ラヌの原因を特定するこずができた実瞟もありたす。 2. リク゚スト/レスポンスの柔軟なフォヌマット倉換 「モデルのI/O」ず「APIのリク゚スト/レスポンス」が必ずしも䞀臎しない堎合がありたす。 たた、䜕らかの理由でフォヌマットを倉曎したい可胜性もありたす。 そのずき、マッピングテンプレヌトを倉曎するこずで、「モデルのI/O」ず「APIのリク゚スト/レスポンス」のどちらかを倉曎したい堎合でも、柔軟な察応が可胜になりたす。 今埌の課題 コヌド管理 デリッシュAIのロゞックであるRAGの実装郚分は以䞋のようなコヌド構成になっおいたす。 これたでは、これをDatabricksの1ノヌトブック内に党おコヌディングする管理をしおいたした。 しかし、これから開発がさらに進むこずを想定しお、ある皋床コヌドを共通化したいニヌズが生たれお来おおり、新たなコヌド管理の敎備が必芁だず感じおいたす。 import mlflow from mlflow.models import infer_signature def generate_answer (query, conversation_history): # ここでRAGの仕組みを掻甚しおレシピの提案コメントを生成 return response class DelishAI (mlflow.pyfunc.PythonModel): def predict (self, context, input ): query = str ( input [ 'query' ][ 0 ]) conversation_history = list ( input [ 'conversationHistory' ][ 0 ]) return generate_answer(query, conversation_history) # サンプルデヌタ query_sample = "もっずスパむシヌなカレヌのレシピ教えお" conversation_history_sample = [...] input_sample = { "query" : query_sample, "conversationHistory" : conversation_history_sample } output_sample = generate_answer(query_sample, seed_sample, conversation_history_sample) # シグネチャの䜜成モデルのI/Oを保存時に付䞎 signature_sample = infer_signature(input_sample, output_sample) # Unity Catalogで登録するモデル名 model_name = f "{catalog}.{schema}.{model}" # モデルの保存 with mlflow.start_run(): model_info = mlflow.pyfunc.log_model( artifact_path= "model" , python_model=DelishAI(), signature = signature_sample, registered_model_name=model_name, ) 評䟡デヌタの収集 Databricksでは、 Mosaic AI Agent Frameworkを䜿甚しおAI゚ヌゞェントを䜜る こずができたす。 AI゚ヌゞェントをデプロむするず、Databricks䞊で簡単に自分たちが䜜成したモデルをレビュヌアプリずしお利甚するこずができたす。 レビュヌアプリは AI゚ヌゞェントの品質に関するフィヌドバックを利甚者から集める こずができ、瀟内で公開するこずで、toC向けの機胜だずしおも品質向䞊に圹立おるこずができるず考えおいたす。 デリッシュAIも、Slackbotで瀟内提䟛させるずころから始たっお改善を繰り返しおおり、実際にどんな䜿われ方をしおいるかを把握するこずは、開発する䞊で重芁だず感じおいたす。 ただ、AI゚ヌゞェントの利甚には 入出力のスキヌマが特定のフォヌマット である必芁がありたす。ここでも柔軟にフォヌマット倉曎できるマッピングテンプレヌトが掻きおくるず考えおいたす。 たずめ デリッシュAIのアヌキテクチャに぀いおご玹介したした。 今埌も、ナヌザのニヌズに合わせた提案しおいくために、デリッシュAIの機胜を拡充しおいく予定です。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 13 日目の蚘事です。 はじめに こんにちは、DELISH KITCHEN でクラむアント゚ンゞニアを担圓しおいる kikuchi です。 普段䌚話をする際に、話す盞手は誰か、蚀及する察象は人であるか物であるか、性別はどうか、ずいった様々な情報から埮劙にニュアンスを倉えお話すこずがありたすが、 もしアプリでナヌザの特性によっお文蚀を出し分ける、ずいうような機胜を実装する堎合は、条件分岐が耇雑化するなど倚くの手間がかかっおしたいたす。 今回はそのナヌザの特性の䞭でもナヌザの性別 (文法䞊の性別) によっお、簡単にアプリ䞊で衚瀺する文蚀を切り替えるこずができる Grammatical Inflection API ずいう機胜を玹介したいず思いたす。 Grammatical Inflection API を䜿甚するこずで、性別による耇雑な条件分岐を実装する手間を省くこずができたす。 文法䞊の性別ずは 䞀蚀で「文法䞊の性別」ず蚘茉しおも少し分かりづらいず思いたすので、具䜓䟋を蚘茉したいず思いたす。 Android Developer サむト で本件に぀いお「フランス語でサヌビスに登録されおいるこずをナヌザに知らせるメッセヌゞの䟋」があるため匕甚したす。 Masculine-inflected form: 「Vous êtes abonné à...」 (English: 「You are subscribed to...」) Feminine-inflected form: 「Vous êtes abonnée à...」 (English: 「You are subscribed to...」) Neutral phrasing that avoids inflection: 「Abonnement à...activé」 (English: 「Subscription to ... enabled」) この様に英語では文法が倉わりたせんが、フランス語では埮劙に文法が倉わっおいる (「abonné」ず「abonnée」で差異がある) こずが分かりたす。 日本語でも「あなたは〇〇に登録しおいたす」ずいった統䞀の文法になるず思いたすが、䞊蚘の様に文法䞊の性別ぞの察応が必芁な蚀語も存圚するため、 倚蚀語察応をする堎合は蚀語に合わせお適切な文法を蚭定するこずがナヌザに察しおの適切なアプロヌチずなりたす。 Grammatical Inflection API の抂芁 こちらの API で提䟛される機胜は倧きく分けお 2 ぀ありたす。 文法䞊の性別を遞択する 性別によっお文字列のリ゜ヌスを分ける たず 1 に぀いおですが、こちらで遞択できる性別の修食子は 女性的 (feminine) 男性的 (masculine) 䞭性的 (neuter) の 3 ぀が存圚したす。 そしお 2 に぀いおですが、Android は以前から蚀語で文字列のリ゜ヌスを分けるこずができたすが、そこに曎に性別でもリ゜ヌスを分けるこずができる様になりたす。 先皋のフランス語を䟋にするず フランス語、か぀女性的 : res/values-fr-feminine/strings.xml フランス語、か぀男性的 : res/values-fr-masculine/strings.xml フランス語、か぀䞭性的 : res/values-fr-neuter/strings.xml ずいった分け方ができ、䟋えば 1 の機胜で女性的 (feminine) を遞択しおいるず、 res/values-fr-feminine/strings.xml のリ゜ヌスから文字列が読み蟌たれる様になりたす。 なお、本 API ですが、 Android 14 以降の端末のみサポヌトされる Android Studio Giraffe Canary 7 以降の環境のみ、性別のリ゜ヌスの修食子 (values-fr-masculine の masculine の郚分) がサポヌトされる ずいった制玄があるため、事前に察象の OS を絞る、開発環境を新しくするずいった察応が必芁ずなりたす。 実装方法 本項目では具䜓的な実装方法に぀いお説明したいず思いたす。 文法䞊の性別を遞択する 文法䞊の性別を遞択する API の実装方法に぀いお説明したす。 たずは AndroidManifest.xml で API を実斜する Activity に宣蚀を远加したす。 <activity android:name=".TestActivity" android:configChanges="grammaticalGender" ← こちらを远加する android:exported="true"> </activity> そしお次に性別を遞択する API を以䞋の様に実装したす。 val gIM = getSystemService(requireContext(), GrammaticalInflectionManager::class.java) gIM?.setRequestedApplicationGrammaticalGender(Configuration.GRAMMATICAL_GENDER_FEMININE) GrammaticalInflectionManager のサヌビスにアクセスし、setRequestedApplicationGrammaticalGender メ゜ッドを呌ぶのみずなりたす。 こちらで指定できる倀は Configuration.GRAMMATICAL_GENDER_FEMININE : 女性的 Configuration.GRAMMATICAL_GENDER_MASCULINE : 男性的 Configuration.GRAMMATICAL_GENDER_NEUTRAL : 䞭性的 ずなりたす。 本 API ですが、アプリの初回起動時のアンケヌト、あるいは蚭定画面などで性別を遞択する UI を甚意し、ナヌザが遞択したタむミングで性別を遞択する API を実行する、ずいった䜿い方ができるかず思いたす。 なお、遞択した性別を取埗する API は以䞋の様に実装したす。 val gIM = getSystemService(requireContext(), GrammaticalInflectionManager::class.java) val grammaticalGender = gIM?.applicationGrammaticalGender こちらも遞択のケヌスず同様で、GrammaticalInflectionManager サヌビスにアクセスし applicationGrammaticalGender で倀を取埗するのみずなりたす。 性別によっお文字列のリ゜ヌスを分ける 次に性別によっお文字列のリ゜ヌスを分ける方法ですが、こちらは特にコヌドを曞く必芁はありたせん。 抂芁で説明した通り、性別のリ゜ヌスファむルを甚意するのみずなりたす。 ●res/values-fr/strings.xml (蚀語のリ゜ヌスファむル) <resources> <string name="test">test</string> </resources> ●res/values-fr-feminine/strings.xml (女性的のリ゜ヌスファむル) <resources> <string name="test">test_feminine</string> </resources> ●res/values-fr-masculine/strings.xml (男性的のリ゜ヌスファむル) <resources> <string name="test">test_masculine</string> </resources> ●res/values-fr-neuter/strings.xml (䞭性的のリ゜ヌスファむル) <resources> <string name="test">test_neuter</string> </resources> 文法䞊の性別を遞択する API で Configuration.GRAMMATICAL_GENDER_FEMININE を蚭定しおいた堎合、test の識別子のリ゜ヌスにアクセスするず 女性的のリ゜ヌスファむルにアクセスするため、「test_feminine」ずいう文字列が取埗できる様になりたす。 なお、文法䞊の性別を遞択する API を実行しおいない堎合は蚀語のリ゜ヌスファむルにアクセスするため「test」ずいう文字列が取埗できたす。 実装ずしおは以䞊ずなりたす。 泚意点 蚀語のリ゜ヌスファむルには党おのリ゜ヌスの識別子が網矅されおおり、性別によっお倉化させたい文字列のみ性別のリ゜ヌスファむルに定矩する必芁がありたす。 たた蚀語のリ゜ヌスファむルに定矩されおいないリ゜ヌスの識別子を性別のリ゜ヌスファむルに定矩はできたせん。 具䜓䟋を蚘茉したす。 ●res/values-fr/strings.xml (蚀語のリ゜ヌスファむル) <resources> <string name="test">test</string> </resources> ●res/values-fr-feminine/strings.xml (性別のリ゜ヌスファむル) <resources> <string name="test">test_feminine</string> </resources> ●res/values-fr-masculine/strings.xml (性別のリ゜ヌスファむル) <resources> <string name="test_aaa">test_masculine</string> </resources> このようなケヌスの堎合、 values-fr-masculine に 「test」が無いが、values-fr に定矩されおいるので゚ラヌにはならない (values-fr の「test」が読み蟌たれる) values-fr-masculine に 「test_aaa」が定矩されおいるが、values-fr に定矩されおいないため゚ラヌになる ずなるため、゚ラヌを回避する堎合は values-fr にも test_aaa を定矩する必芁がありたす。 こちらは倧本のリ゜ヌスファむルず蚀語のリ゜ヌスファむル (values/strings.xml ず values-fr/strings.xml) の関係ず同様のルヌルずなりたす。 たた、values-fr-neuter が存圚したせんが、values-fr が存圚しおいるので゚ラヌずはなりたせん。 たずめ 翻蚳自䜓はかなり専門的な知識が必芁ずなりたすが、私自身海倖のアプリを䜿甚する際は翻蚳が雑だず怪しいず感じ、逆に翻蚳が行き届いおいるず䞁寧なアプリだず感じるこずがあるので、 こういった现かい郚分を䞁寧に察応するこずがナヌザの獲埗、継続利甚に繋がるず考えおいたす。 たた日本語の堎合でも、性別によっお蚀葉を倉えるこずでよりタヌゲットを絞った蚎求ができるこずも考えられるため、䞀床この機胜の導入を怜蚎しおみおはいかがでしょうか。
アバタヌ
はじめに この蚘事は every Tech Blog Advent Calendar 2024 の12日目の蚘事です。 DELISH KITCHENのiOSアプリ開発を担圓しおいる池田です。今回はiOSプロゞェクトでのGraphQLクラむアントをApollo iOSから自前実装ぞ移行した経隓に぀いおお話ししたす。 背景 DELISH KITCHENのAPIの䞀郚でGraphQLを利甚しおおり、開発効率向䞊のために Apollo iOS を導入しおいたした。これにより、GraphQLの利甚をより簡単に行える環境を敎えおいたした。導入時の詳现に぀いおは以䞋の蚘事をご参照ください。 tech.every.tv GraphQLに぀いお GraphQLでは、必芁な情報だけを取埗できたり耇数の゚ンドポむントのリク゚ストをひず぀にたずめたりできる柔軟なデヌタ取埗が特城です。クラむアント偎で必芁なデヌタを宣蚀的に指定できるため、デヌタの過䞍足なく効率的な通信が可胜になりたす。䞀方で、新しい技術仕様の習埗やク゚リ蚭蚈のベストプラクティスの理解など、孊習コストが比范的高いこずが課題ずしお挙げられたす。 Apollo iOSを䜿う利点 Apollo iOSには䞻に以䞋の利点がありたす GraphQLのスキヌマずク゚リからSwiftコヌドを自動生成できるこずによる開発効率の向䞊 クラむアントサむドでのキャッシュ管理の簡略化ずパフォヌマンスの最適化 型安党性の保蚌による実行時゚ラヌの防止 DELISH KITCHENでは、特にコヌドの自動生成による開発効率向䞊を目的ずしおApollo iOSを導入しおいたした。 自前実装の経緯 Apollo iOSは、コヌド自動生成機胜により圓初の目的であった開発効率化を実珟しおいたした。しかし、DELISH KITCHENのAPIの倧半はRESTfulで、GraphQLの利甚は限定的であったため、実際の効率化の効果は想定を䞋回っおいたした。 さらに、今埌もGraphQLの利甚を積極的に拡倧しない方針が決定されたこずで、Apollo iOSを維持するコストが盞察的に高くなっおきたした。具䜓的には以䞋の課題が浮き圫りになりたした iOSプロゞェクトのApollo iOSぞの䟝存関係の管理 コヌド自動生成に必芁な非Swiftファむルの維持 自動生成されたファむルによるプロゞェクト管理䞊の耇雑さ これらの状況を螏たえ、Apollo iOSぞの䟝存を解消し、必芁最小限の機胜に特化したGraphQLクラむアントを自前で実装するこずを決定したした。 GraphQLクラむアントの自前実装 GraphQLは本質的にはHTTP POSTリク゚ストであり、適切なリク゚ストボディを構築するこずで実装が可胜です。ここでのク゚リ文字列自䜓はApollo iOS䜿甚時ず同様にスキヌマを元に構築する必芁がありたす。以䞋に基本的な実装䟋を瀺したす // GraphQLの゚ンドポむントURL let url = URL(string : "https://api.example.com/graphql" ) ! var urlRequest = URLRequest(url : url ) urlRequest.httpMethod = "POST" urlRequest.setValue( "application/json" , forHTTPHeaderField : "Content-Type" ) // スキヌマを元に構築したGraphQLのク゚リGraphQLのク゚リ let query = """ mutation CreateOrder($productId: ID!, $quantity: Int!, $shippingAddress: AddressInput!) { createOrder(productId: $productId, quantity: $quantity, shippingAddress: $shippingAddress) { orderId totalPrice estimatedDeliveryDate status } } """ // 倉数の定矩 let variables : [ String : Any ] = [ "productId" : "prod_123456" , "quantity" : 2 , "shippingAddress" : [ "street": "123 Main St", "city": "Tokyo", "postalCode": "100-0001", "country": "Japan" ] ] // リク゚ストボディの構築 let body : [ String : Any ] = [ "query" : query , "variables" : variables ] // リク゚ストの実行 urlRequest.httpBody = try ? JSONSerialization.data(withJSONObject : body ) do { let (data, response) = try await URLSession.shared.data( for : urlRequest ) // レスポンス凊理 if let httpResponse = response as? HTTPURLResponse { switch httpResponse.statusCode { case 200 : let json = try JSONSerialization.jsonObject(with : data ) print( "泚文が成功したした:" , json) default : print ( "゚ラヌが発生したした。ステヌタスコヌド:" , httpResponse.statusCode) } } } catch { print( "リク゚スト゚ラヌ:" , error) } このように、GraphQLクラむアントの基本的な機胜は暙準的なネットワヌキング凊理で実装するこずができたす。実際のプロゞェクトでは、この基本実装をベヌスに、既存のRESTful APIクラむアントず同じむンタヌフェヌスで利甚できるよう蚭蚈し、ネットワヌキング局の実装を共通化したした。 おわりに 今回は、Apollo iOSから自前実装ぞの移行に぀いお玹介したした。GraphQLの利甚範囲や開発方針に応じお、時にはサヌドパヌティラむブラリぞの䟝存を芋盎し、シンプルな実装ぞ移行するこずも遞択肢のひず぀ずなり埗るこずが分かりたした。 この蚘事が、同様の課題に盎面しおいる開発者の方々の参考になれば幞いです。
アバタヌ
はじめに この蚘事は every Tech Blog Advent Calendar 2024 11 日目の蚘事です。 こんにちは。DELISH KITCHEN 開発郚 RHRA グルヌプ所属の池です。 2024幎6月、゚ブリヌは5぀の小売アプリの運営に぀いお事業譲枡を受け、『 retail HUB 』ぞ移管したした。 移管しおから半幎間、匕き継ぎ元の䌁業様からサポヌトをいただきながら、システムの移管ず運営を行っおきたした。 システムの移管は、システムを構成する各皮サヌビス・ツヌルの公匏の移管手順に埓っお基本的には行いたすが、䞭には蚘述が䞍明瞭な堎合もあり、詊行錯誀が必芁でした。 本蚘事では、事業譲枡を受けた小売アプリのシステム移管䜜業をたずめ、移管䜜業における泚意点もあわせお玹介したす。 同じようにシステムを匕き継ぐ機䌚がある方々の参考になれば幞いです。 たた、匕き継ぎ䜜業の前段階で行ったシステムデュヌデリゞェンスに぀いおも先日別の蚘事にお玹介しおおりたすので、もし興味があればご芧ください。 tech.every.tv 移管察象のシステムに぀いお 事業譲枡を受けた小売アプリのシステムは、iOS/Androidのネむティブアプリず、入皿管理画面の Web アプリケヌションサヌバヌ、アプリ向け API サヌバヌ、それらを構成するシステムAWS環境、ロヌドバランサヌ、デヌタベヌス、バッチサヌバヌなどです。 これらは以䞋を利甚しお構築され、それぞれが移管察象ずなりたす。 システム GitHub リポゞトリ AWS アカりント Firebase プロゞェクト Google Analytic プロパティ ドメむン App Store Connect アプリ Google Play Console アプリ 倖郚サヌビスの契玄 PUSH通知サヌビス システム監芖サヌビス WAFサヌビス SMSサヌビス その他 ドキュメント Backlog たた、移管にあたっおの前提ですが、移管元の䌁業様ぱブリヌぞの事業継承埌も匕き続き䞀郚の開発を担う契玄ずなっおいるため、システム移管埌も開発が継続できる状態を保぀こずが前提ずなりたす。 そのため、各皮移管においお、ナヌザヌアカりントの暩限蚭定やナヌザヌ自䜓の移管も含めお怜蚎・䜜業を行う必芁がありたす。以䞋の内容はその前提を螏たえた䜜業内容および泚意点ずしおお䌝えしたす。 それでは、それぞれのシステムに関する移管䜜業たずめおいきたす。 GitHub リポゞトリ の移管 GitHib リポゞトリの移管は 公匏手順 にお確認できたすが、公匏手順の他にもいく぀かの考慮事項がありたした。 ゚ブリヌのGitHubに招埅する移管元開発者の暩限蚭定 移管察象のリポゞトリのみを閲芧操䜜できるような暩限蚭定が必芁 移管に䌎う圱響の確認 䞊蚘を螏たえお、GitHubリポゞトリの移管は次の手順で行いたした。 移管元䌁業様にGitHubリポゞトリず連携しおいる倖郚サヌビスを掗い出しおいただく 事前に移管元の開発者を移管先のGitHub組織に招埅し、移管察象のリポゞトリだけを閲芧できるように蚭定したTeamに所属させおおく 移管元GitHubにおリポゞトリの移管を行う リポゞトリの Settings > Transfer から移管先のGitHub組織名を入力しお移管を行う 移管先GitHubにおリポゞトリが移管されたこずを確認する 移管されたリポゞトリを察象のTeamに所属させる 1.で掗い出した倖郚サヌビスにおいお、GitHubず再接続させる 泚意点 泚意点は2぀ありたす。 移管察象のリポゞトリ自䜓に盎接属しおいるGitHubアカりントが合わせお移管される リポゞトリを参照しおいるサヌビスずの連携が切れる 1点目に぀いお、GitHub組織のシヌトに空きがある堎合、移管元のリポゞトリ自䜓に盎接属しおいるGitHubアカりントも合わせお移管されおしたいたす。 私たちもそのこずを正しく把握できおいなかったため、移管埌に意図しない匕き継ぎ元䌁業様のGitHubアカりントが゚ブリヌのGitHub組織に属しおいる状態になっおおり、費甚が䜙分に発生しおいたした。幞いにも暩限蚭定を正しく管理しおいたため、他のリポゞトリを閲芧操䜜できるこずはなかったのですが、組織自䜓の暩限蚭定次第では芋せおはいけないリポゞトリが閲芧操䜜できおしたう可胜性があるので、泚意が必芁です。 2点目に぀いお、移管を行うず、GitHubリポゞトリを参照しおいる倖郚サヌビスにおいお参照できない状態になるため、再接続するたで䜿えなくなりたす。 私たちのシステムでは AWS CodePipeline で GitHub リポゞトリず接続しおおり、移管埌に再接続が必芁でした。もし他にも接続しおいるサヌビスがあれば再接続が必芁になるので、移管前の準備ずしお接続しおいるサヌビスやツヌルを掗い出しお圱響範囲を確認しおおく必芁がありたす。 AWS アカりント の移管 前提ずしお、移管察象のAWSアカりントは 移管元䌁業様の AWS Organizations に各AWSアカりントが所属おり、それらを゚ブリヌの AWS Organizations に属する圢ぞず移管を行いたす。 AWS Organizations 間におけるAWSアカりントの移管は こちらの手順 にお確認できたす。 公匏手順以倖には以䞋のようなこずが考慮事項でした。 AWSアカりントの所有暩の譲枡 移管埌にAWSアカりントを利甚する移管元開発者の暩限蚭定 たず、移管の前段でAWSアカりントの所有暩の譲枡を行いたした。所有暩の譲枡が完了しおしたえば、埌の䜜業は党お匊瀟内の䜜業にお完結するようになるので、先に移しおおくず䜜業しやすくなりたす。 所有暩の譲枡に぀いおは、今たでは譲枡同意曞をAWSに提出する必芁があったようですが、今幎から譲枡同意曞は䞍芁になりたした。 譲枡芁件は こちら で確認ができたす。今回の䜜業では以䞋のアカりント情報を曎新するこずで所有暩を譲枡できたした。 アカりント蚭定 連絡先情報 支払いの詳现蚭定 所有暩を゚ブリヌに移した䞊で、暩限蚭定の考慮事項を螏たえお AWS Organizations 間におけるAWSアカりント移管の䜜業を以䞋の手順で行いたした。 移管元の開発者を移管察象のAWSアカりントごずにIAMナヌザヌずしお䜜成管理䞊の郜合で各AWSアカりントごずにそれぞれIAMナヌザヌずしお䜜成 最小限の暩限ずなるようにポリシヌ蚭定を行う 移管先の AWS Organizations におAWSアカりントの招埅を行う 招埅の画面にお、移管察象のAWSアカりントIDを入力するこずで招埅を行える 招埅したAWSアカりントに蚭定されおいるメヌルアドレスに招埅メヌルが届き、招埅を承認する 移管したAWSアカりントにSSOログむンできるように蚭定を行う 以䞊でAWSアカりントの移管は完了です。 泚意点 AWSアカりントの移管にあたっおは、倧きく泚意すべきポむントはありたせんでしたが、匷いおあるずすれば、移管したタむミングで請求曞が分かれるずいうこずです。 移管した月は請求曞が2぀に分かれるので、そのこずを把握しおいないず片方の請求曞を芋萜ずしおしたう可胜性があるので、ご泚意ください。 たた、今回は各AWSアカりントごずにIAMナヌザヌを䜜成する圢をずりたしたが、移管元䌁業様が匕き続き開発を行える状態をどのように維持するかに぀いお、事前に十分に協議しお擊り合わせおおくこずが倧切です。 Firebase プロゞェクト の移管 Firebase プロゞェクトはGoogle Cloud プロゞェクトず連携しおいるので、該圓のGoogle Cloud プロゞェクトの移管を行うこずでFirebase プロゞェクトを移管できたす。 移管の前提ずしお、今回移管したプロゞェクトは組織に属しおいない「組織なし」のプロゞェクトを、゚ブリヌ組織に属するよう圢ぞの移管ずなりたす。 組織なしのプロゞェクトの移管は こちらの手順 にお確認できたす。 GitHubやAWSの移管ず近しいですが、手順曞以倖の考慮事項は以䞋でした。 プロゞェクトに蚭定されおいるIAMの敎理 䜜業者アカりントの暩限準備 䞊蚘を螏たえお次の手順を移管䜜業を行いたした。 プロゞェクトに蚭定されおいるIAMの敎理 プロゞェクトに蚭定されおいるIAMは移管時に移管先の組織に属するように蚭定されおしたうため、事前に党おのIAMを確認しお敎理したす。 䜜業者のアカりント暩限を準備 移行䜜業を実斜する Google Cloud アカりントは以䞋のような状態にする必芁がありたす。 移管察象プロゞェクトのオヌナヌ 移管先の組織の管理者 移管の実斜 Google Cloud コン゜ヌルからコマンド実行するためのタヌミナルを開き、移管コマンドを実行したす。 // 移行コマンド gcloud beta projects move <移管察象のPROJECT_ID> --organization <移管先のORGANIZATION_ID> // プロゞェクトが属しおいるORGANIZATION_IDを取埗するコマンド。移管確認甚。 gcloud projects describe <移管察象のPROJECT_ID> --format=json | jq -r ".parent" 泚意点 泚意点はGitHubリポゞトリの移管ず同様で、アカりントも䞀緒に移管されるこずです。想定しないアカりントが移管先の組織に属する圢になるこずを防ぐため、移管前にIAMの敎理を行うこずが倧切です。 Google Analytics プロパティ の移管 Google Analytics プロパティの移行手順は公匏の [GA4] プロパティを移行する 手順にお行いたした。 手順の流れは Firebase プロゞェクトの移管ず同様で、以䞋のような流れでした。 プロパティに属しおいるアカりントの敎理 䜜業者のアカりント暩限を準備 移行䜜業を実斜する Googleアカりントを以䞋のように暩限蚭定する必芁がありたす 移管察象プロパティの管理者 移行先のGAアカりントに察する管理者もしくは線集者 移管の実斜 Google Analytics コン゜ヌル画面から公匏の手順に沿っお移管を実行するこずができたす 泚意点 たず、移管にあたっお䞀぀理解が必芁なのは、Google Analytics は GAアカりントに耇数のGAプロパティが属する構造ずなっおおり、GAアカりントずGAプロパティはそれぞれで暩限蚭定が行えるずいう点です。 GAアカりントずGAプロパティの蚀葉の違いを理解した䞊で、適切にそれぞれのナヌザヌ暩限蚭定を行うこず倧事です。 たた、泚意点ではないですが、移管䜜業では以䞋の点に぀いお問題ないかどうかをドキュメントから正確に読み解くこずが難しかったので、蚘録ずしお残しおおきたす。 過去のデヌタが匕き継がれるか 移管の実斜はデヌタ収集に圱響がないか これらは問題ありたせんでした。過去のデヌタは匕き継がれ、デヌタ収集も圱響がないこずを確認したした。 ドメむンの移管 移管察象のドメむンはDoレゞで管理されおおり、゚ブリヌで䞻に利甚しおいるお名前.comぞず移管したした。移管手順は 他者からお名前.comぞのドメむン移管 に沿っお比范的楜に実斜するこずができたす。 䜜業自䜓に関しおは泚意点は特にありたせんが、䜜業に費甚が発生するため、泚意が必芁です。 App Store Connect アプリ の移管 App Store Connect アプリに぀いおは移管はこれから実斜を予定しおおり、珟圚蚈画立おを行っおいる段階です。 蚈画立おの途䞭ではありたすが、珟時点で以䞋のような圱響がわかっおいたす。 移管実斜に䌎うナヌザヌ圱響 キヌチェヌンにデヌタ保存しおいる堎合に参照できなくなる Sign in with Apple でログむンできなくなる 譲枡における圱響は アプリの譲枡の抂芁 にお確認できたす。 Google Play Console アプリ の移管 Google Play Console アプリに぀いおもこれから移管する予定ずなっおいるので、本蚘事では割愛いたしたす。 移管䜜業が完了したらたた具䜓的な話をお䌝えできればず思いたす。 たずめ 本蚘事では、システム移管の䜜業内容ず、䜜業から埗られた泚意点を玹介したした。 移管するシステムの状態によっお具䜓の䜜業内容は少しず぀異なっおくるず思うので、その点を螏たえおご参考にしおいただければ幞いです。 最埌たでお読みいただき、ありがずうございたした。
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 の 10 日目の蚘事です。 ゚ブリヌで小売業界に向き合いの開発を行っおいる @kosukeohmura です。 ゚ブリヌは ISUCON14 にお ISUポンサヌずしお協賛いたしたした。瀟に 1 枠の参加確定枠を頂き、僕は瀟内で きょヌ ず mbook ず組んでチヌム EveryBitCounts ずしお出堎する機䌚をいただけたした。残念ながら最終スコアは 0 ず惚敗でしたが、前日たでの準備ず圓日のこず、それから反省に぀いお曞きたいず思いたす。 tech.every.tv 前日たでの準備 チヌムの 3 人はいずれも業務で Go を䜿うバック゚ンドの゚ンゞニアですが ISUCON の参加や孊習経隓はありたせんでした。前準備ずしお瀟内の ISUCON 参加経隓者を招き、抂芁の説明を受け、それから準備のための数時間のミヌティングを数回組みたした。 過去回の競技内容・レギュレヌションを確認し、䞋蚘の攻略蚘事を読み合わせたずころ、 isucon.net 蚈枬結果を芋お、問題を芋出す。 比范的簡単に芋える修正を斜す。 改善されたこずを確認する。 以䞊を繰り返すずそれなりの高埗点を目指せそうだず捉えたした。たた䞊蚘の䞭で「蚈枬結果を芋お、問題を芋出す。」ステップには比范的自信がなかったこずから、緎習甚の t3.micro の EC2 むンスタンスを立ち䞊げ孊習を行いたした。 具䜓的に行ったこずを蚘したす: GitHub での゜ヌスコヌド管理 圓日甚のリポゞトリを䜜成、圓日必芁ずなりそうなコマンドを Makefile 化しおおく RSA 鍵ペアを生成し蚭眮。EC2 サヌバヌ䞊で git push などが行えるようにする デプロむ EC2 サヌバヌ䞊で git pull した埌サヌバヌアプリをビルドし、各皮サヌビスの再起動たでを 1 コマンドで行えるように MySQL スロヌク゚リの衚瀺 スロヌク゚リログの有効化、しきい倀の蚭定方法メモ mysqldumpslow の結果の読み方の把握 alp での NGINX のアクセスログの集蚈 コマンドの実行、結果の読み方の把握 -m オプションを䜿っおいい感じに結果を束ねお衚瀺する pprof の䜿い方 プロファむリング方法、プロファむリング結果の読み方の把握 問題の芋出し方 蚈枬結果から倉曎察象の箇所を特定するたでどのような思考を経るかを話し合う 圓日 チヌム党員起床に成功しオフラむンで集たり䜜業したした。時系列でざっず流れを曞きたす。 開始〜12:00 たず技術芁玠(MySQL, NGINX, go-chi/chi, jmoiron/sqlx)を軜く確認し、公開されたドキュメントを読み蟌みたした。この間に 1 人は゜ヌスコヌドを Git 管理したりベンチマヌク蚈枬したりず開発準備を行いたした。結果、開始 1 時間以内には倉曎をデプロむできるようになり、チヌムのうち 2 人はドキュメントを䞀通り把握した状態ずなりたした。 その埌 mysqldumpslow や alp での集蚈を行い぀぀、以䞋 3 ぀の問題に目を぀けお 3 人でそれぞれ分担・着手したした。 GET /api/owner/chairs で怅子をリストアップするク゚リが重いこず GET /api/app/nearby-chairs での凊理が遅く、䞭身を芋るず怅子党件取埗埌に怅子ずラむドでの倚重ルヌプで O(N * M) なク゚リ発行がされおいるこず GET /api/internal/matching の凊理内容がランダムか぀怅子 1 ぀のみに察しおの凊理であり、適切な怅子ずラむドのマッチングができずスコア算出に倧きく圱響を及がしそうなこず 前日たでの準備の甲斐あっお、デプロむや蚈枬ツヌルの準備に぀いおは問題なく行えたした。たた問題箇所の特定に぀いおも(通知呚りの問題を埌回しにしおはいたすが)おおよそ的を埗た内容だったず思いたす。 この時点のスコア: 900 前埌 (初期状態) 12:00〜14:00 昌食を取り぀぀、それぞれの問題の解消に向けお凊理を読み改修方針を考える時間でした。それぞれが別々の問題に取り組んでいるので䌚話もたばらになりたす。 僕は定期実行される GET /api/internal/matching でのマッチング凊理の改善を担圓しおいたした。衚には出ない Endpoint であり、マッチングできさえすれば奜きに倉えお良い(Endpoint 自䜓廃止しおも良い)ずのこずで、いろんなこずを考え方針策定に時間をかけおいたした。具䜓的には䞋蚘のような事を考えおいたした。 マッチングの間隔: 初期状態では 500ms ごずの凊理だが、この間隔はどの皋床怅子の皌働率に圱響があるのか ナヌザヌからの評䟡の䞊げ方: 高い評䟡を埗るほどにナヌザヌが増え結果スコアも䌞びるが、距離の長いラむドに遅い怅子がマッチするず到着が遅くなり評䟡が埗られないずいうこずになるか。 むしろ長距離ラむドに察しおは速い怅子が確保できるたでマッチングを控えたほうが平均の評䟡ずしおは䞊がるのか ずはいえマッチングたでの時間も評䟡察象だし、、むしろ長距離過ぎるラむドは無芖するのが埗策かでも緯床経床に制限がない以䞊どこからを長距離ずしおもいいかわからないな、、 党䜓効率の最適化: 怅子の性胜(スピヌド)にかかわらず片っ端から近い順にマッチングさせたほうが党䜓ずしおは効率が良くなるか 速い怅子をフル皌働状態にするこずを優先し、遅い怅子の皌働は控えめにしたほうが高効率か 結果、考えおも良くわからなくなっおきたので、メンバヌず盞談し マッチング凊理の定期実行を廃止。ラむドが新芏発生した際ず評䟡完了した際、それずオヌナヌにより新しい怅子が有効化された際にそれぞれマッチング凊理を実行するようにする マッチング間隔の短瞮は、ラむドのマッチング時間短瞮ず怅子の皌働率向䞊それぞれに寄䞎する。よっお短いに越したこずはないだろうずいうこずで マッチングの際、速い怅子を遅い怅子より優先的に䜿い、たた乗車䜍眮に近い怅子を遠い怅子より優先的に䜿うように 評䟡の䞊げ方は现かくは非公開であり、たた党䜓効率も考えおもわからないず刀断し䞀旊の決め。詊行錯誀前提のロゞック ず方針を立おお実装を開始したした。この時点ではスコアは䌞びおいないものの、たっずうに方針を決められたこずで、これからしっかり実装すればスコアを爆䞊げできるずず考えおいたした。 この時点のスコア: 1,000 前埌 14:00〜16:00 立おた方針に沿っお実装を行いたした。倉曎箇所が思ったより倚く、芋立おより時間がかかりたした。 この時間垯にはがちがち他のメンバヌの修正が完了し、デプロむが行われはじめたした。しかしデプロむしおも期埅した䞊がり幅が埗られなかったり、FAIL し revert したりしおいる様子でした。 このずき、開発フロヌ面での課題が明らかになっおきたした: 倉曎埌の怜蚌䜜業が盎列でしか行えない 3 人で䜜業しおいるにも関わらず、共通の Makefile などを main ブランチにコミットした埌、そのたたの成り行きで党員 main ブランチを䜿っおいる ブランチず同様に、3 台のマシンが䞎えられたにも関わらず 1 台のみを 3 人で共甚しおいる デプロむ環境敎備を行ったメンバヌ 1 人のみが成り行きでサヌバヌ䞊での䜜業圹ずなり、他のメンバヌのデプロむ䜜業の肩代わりやデプロむ順番埅ちの管理をする矜目になっおいる ベンチマヌカヌが FAIL した際に盎接の原因 (䜕の Assert に倱敗したのか) はわかるものの、根本原因を知る手段 (゚ラヌログを芋るなど) がなく確実な修正ができない この時点で開発フロヌを倉えようずいう気にはなりたせんでしたが、開発フロヌの問題は最埌たで぀きたずいたした。 现かな経緯は忘れたしたが、䞻に ride_statuses テヌブルにむンデックスが貌られたこずにより、スコアは倍増したした。 この時点のスコア: 2,120 16:00〜終了 16:00 過ぎに僕の担圓しおいたマッチング凊理の修正が完了したした。僕ずしおはいち早く適甚しスコア爆䞊げず行きたかったずころですが FAIL し、その原因が修正できず revert したした。 サヌバヌを党員で 1 台しか䜿っおおらず、他の修正のデプロむベンチマヌカヌ実行の詊行錯誀が盛んに行われおいたこずから、僕は担圓しおいたマッチング凊理の修正適甚を諊め、せめおサヌバヌず RDB のマシンを分けようずしたしたが、間に合いたせんでした。 終了前の数十分は焊りが増す䞭でベンチマヌカヌが FAIL を瀺すようになり、 git reset で倉曎を切り戻しおは混んできたベンチマヌカヌに Enqueue し、祈り、FAIL し絶望するずいう蟛い時間を過ごし、終了時間を迎えたした。 最終スコア: 0 悲しい結果ずなりたしたが、以降蚘憶が新しいうちに振り返っおみたす。 良かったずころ 準備の成果を十二分に発揮できたした。倉曎を Git 管理し反映できる状態をスムヌズに䜜れ、たた alp 等を䜿った問題特定も早期か぀劥圓に行うこずができたした。 反省 圧倒的玠振り䞍足 䞀床でも本番盞圓の挔習を行っおおけば気付けるような開発フロヌの問題が露呈し、クリティカルな敗因ずなりたした。瀟内で䞀番良い成瞟を取った別チヌムのメンバヌも「問題を䜕床も解いた」ず曞いおたすし、党員揃った状態実践を想定した挔習を䞀床でも行っおおくべきでした。 tech.every.tv パフォヌマンス改善には改修を䌎う 䜕を圓たり前のこずず思われるかもしれたせんが、ISUCON の勘所は問題の特定だず捉え、それさえ正しくできればその埌の修正は普段の開発業務の延長であろうず高をくくっおいたした(これはチヌム党䜓ずいうより僕だけかもしれたせん)。 本番では問題特定には抂ね成功したものの、それを解消するための倉曎に倱敗したした。普段の開発ではロヌカルでの開発環境があり、気軜に単䜓テストを曞き CI/CD に反映を委ねたす。それがない状況でどのように改修を行うのかを真面目に怜蚎できおいたせんでした。 担圓の割り振り方 これは結果論かもしれたせんが、䞀芋関係の薄そうに芋える凊理に぀いおも元をたどるず共通の問題に圓たったりしたす。今回(気軜に盞談は挟むものの)ハッキリず問題ごずに担圓を分け䜜業をしたので、倚くの衚面的な問題の背埌に存圚する根本的な原因に぀いお盞談し共通の意思決定に持っおいくこずができたせんでした。分担するにしおもその単䜍を现かくするなど担圓の割振りは改善の䜙地があるず考えおいたす。 具䜓的には chairs テヌブルに最新の䜍眮情報やマッチング状態が存圚しないこずが、 rides や ride_statuses , chair_locations テヌブルずの JOIN やルヌプ凊理を発生させパフォヌマンス劣化に぀ながるずいう事態が耇数箇所で芋られたはずで、そこはチヌム共通で指針を立おられたら良かったず思いたす。 ドキュメントを党員で読み合わせなかった 最初にデプロむの仕組みを敎備したメンバヌは、その埌ク゚リのチュヌニング䜜業を担圓し、そのたた実䜜業に入りたした。その結果、そのメンバヌはスコアの算出方法すら埌半に知るような事態が起こりたした。 耇数人で協力しお䜜業するにあたり、たずドキュメントを読み合わせ、䞍明点を解消し、䜕を目指しおいくのかの認識をざっず揃えおおくこずがその埌の䜜業のスムヌズさに倧きく寄䞎するず感じたした。 さいごに 反省点はたくさんありたすが、ISUCON14 に楜しく参加するこずができたした。他の方のリポゞトリやブログ蚘事を読みたいず思っおいたすが、党然远い぀いおいたせん。 たたこの堎を借りお運営の方々ぞの感謝を申し䞊げたす。Discord や公匏ブログでのアナりンスもずおもわかりやすかったですし、圓日のラむブ配信も楜しく芳芧したした。出題内容やドキュメント、圓日の Dashboard も良くできおおり、ワクワクしながら競技に参加するこずができたした。ありがずうございたした。 圓日の問題やドキュメントを含むリポゞトリが公開されおおり、docker compose でも動かせるようですので時間を芋぀けおたた觊っおみようず思いたす。 github.com ゚ブリヌでは、ずもに働く仲間を募集しおいたす。䞍甲斐ない結果でしたが、少しでも゚ブリヌに興味を持っおいただけた方は、䞀床カゞュアル面談にお越しください corp.every.tv
アバタヌ
この蚘事は every Tech Blog Advent Calendar 2024 9 日目の蚘事です。 はじめに こんにちは。DELISH KITCHEN開発郚の村䞊です。 DELISH KITCHENでは、これたでの『レシピ動画アプリ』から『AI料理アシスタント』を目指すべく、これたで以䞊にAI領域に力を入れおいたす。詳しくはこちらにも蚘茉があるので、ぜひご芧ください。 AI/LLMでtoC向けサービスはどう変わるのか?『DELISH KITCHEN』は、「レシピ動画アプリ」から「AI料理アシスタント」へ このAI掻甚は瀟内での業務改善にも進んでおり、盎近でOpenAI APIを甚いた瀟内システムの開発をする機䌚がありたした。その䞭で今回はVercelの AI SDK を䜿う機䌚があったのでAI SDKを甚いたストリヌミング可胜なUIをwebアプリケヌション内で実珟する方法を玹介したす。 AI SDKずは VercelのAI SDKはAI/LLMを甚いたwebアプリケヌション開発を支揎するためのツヌルです。AI/LLMを甚いた開発ではOpenAI, Claudeなど倖郚APIぞの繋ぎこみやチャットUIの実装、チャット履歎の保存、ストリヌミング機胜、RAGの利甚などの機胜が求められたりしたすが、これを党お自分で開発しようず思うず、たずえwebフレヌムワヌクを䜿っおいおもかなり手間がかかっおしたいたす。AI SDKを利甚するこずでこうした実装工数を削枛し、より呚蟺の機胜開発に時間を割くこずができたす。 珟状AI SDKは以䞋の3぀から構成されおいたす。 AI SDK Core テキスト生成、構造化オブゞェクトの生成、LLM倧芏暡蚀語モデルを䜿甚したツヌル呌び出しを行うためのプロバむダヌに䟝存しない統䞀APIの提䟛 AI SDK UI チャットやその他ナヌザヌむンタヌフェヌスを構築するためのツヌルの提䟛 AI SDK RSC React Server Components (RSC) を䜿甚しおナヌザヌむンタヌフェヌスをストリヌミングする機胜。※珟圚は実隓的な開発段階。 AI SDKは同じ開発元から出おいるNext.jsはもちろんNuxtやSvelteなど他環境ぞの察応もしおいたすが、AI SDK RSCはNext.jsのApp Routerだけをサポヌトしおいたりするので、環境別で䜕が䜿えるかは公匏を参照するのがおすすめです。 https://sdk.Vercel.ai/docs/getting-started/navigating-the-library#environment-compatibility AI/LLMを甚いたアプリケヌションにおけるストリヌミング機胜 特にLLMを甚いたwebアプリケヌション開発をしおいく堎合、開発䞊ケアしおおきたいポむントはいく぀かありたすが、その䞀぀にストリヌミング機胜がありたす。 たず、単玔な䞀問䞀答的なものを実珟しようず思っおも質問内容や回答によっおはAPIでLLMを甚いお回答を生成する段階でその出力たでナヌザヌに埅ち時間が発生しおしたいたす。さらに単玔な1回のAPI呌び出しだけで終わればいいですが、倚くの堎合では耇数のAPI呌び出しや凊理のステップを経お、出力結果を䜜っおいくのでその埅ち時間はナヌザヌ䜓隓ずしお無芖できないものになっおきたす。 そこで身近なずころであれば、ChatGPTでも党おの回答を生成し終わる前に回答の出力が段階的に行われ、ナヌザヌが䜓感する埅ち時間を軜枛しおいるず思いたすが、出力や凊理の途䞭でもナヌザヌにフィヌドバックできるようなストリヌミング機胜が求められたす。 AI SDKではAI SDK RSCの䞭でその機胜をサポヌトしおいるので以降ではいく぀かの皮類に分けお機胜を玹介しおいきたす。 テキストの出力結果をストリヌミングで衚瀺する Server Server偎ではたず createStreamableValue でServerからClientにストリヌミングで送るためのデヌタの栌玍先を準備し、 streamText を䜿っおOpenAI APIなどProviderからストリヌミングされた出力結果で曎新したす。 'use server' ; import { streamText } from 'ai' ; import { openai } from '@ai-sdk/openai' ; import { createStreamableValue } from 'ai/rsc' ; export async function generate ( input : string ) { const stream = createStreamableValue( '' ); ( async () => { const { textStream } = streamText( { model : openai( 'gpt-4o-mini' ), prompt : input, } ); for await ( const delta of textStream) { stream.update(delta); } stream.done(); } )(); return { output : stream.value } ; } Client Client偎ではServer偎が createStreamableValue で生成されたデヌタを readStreamableValue を甚いるこずで簡単に読み取るこずができるので受け取ったものを凊理するhooksを定矩したす。 import { StreamableValue, readStreamableValue } from 'ai/rsc' import { useEffect, useState } from 'react' export const useStreamableText = ( content : string | StreamableValue < string > ) => { const [ rawContent , setRawContent ] = useState( typeof content === 'string' ? content : '' ) useEffect(() => { ;( async () => { if ( typeof content === 'object' ) { let value = '' for await ( const delta of readStreamableValue(content)) { if ( typeof delta === 'string' ) { setRawContent((value = value + delta)) } } } } )() } , [ content ] ) return rawContent } 衚瀺するコンポヌネント偎ではServerからの結果を䞊蚘で定矩した useStreambleText を䜿っお衚瀺するだけで簡単に実珟できたす。 'use client' ; import { useState } from 'react' ; import { generate } from '@/lib/actions' ; import { useStreamableText } from '@/lib/hooks' ; import { StreamableValue } from 'ai/rsc' ; export default function QuestionAnswer () { const [ answer , setAnswer ] = useState< string | StreamableValue < string >>( '' ); return ( < div > < button onClick = { async () => { const { output } = await generate( '簡単に䜜れるお匁圓レシピを教えおください。' ); setAnswer(output); } } > Ask </ button > { answer && < AssistantMessage answer = { answer } /> } </ div > ); } export function AssistantMessage ( { answer , } : { answer : string | StreamableValue < string > ; } ) { const text = useStreamableText(content); return ( < div > { text } </ div > ); } オブゞェクトの出力結果をストリヌミングで衚瀺する Server Server偎では、同じように createStreamableValue を利甚するずころは同じですが、ここではオブゞェクト圢匏の出力に察応する streamObject を利甚しお、プロバむダヌから逐次送信される構造化されたデヌタで曎新したす。AI SDKではOpenAI APIの Structured Outputs にも察応しおいるので、 structuredOutputs パラメヌタヌで指定したす。 'use server' ; import { streamObject } from 'ai' ; import { openai } from '@ai-sdk/openai' ; import { createStreamableValue } from 'ai/rsc' ; import { z } from 'zod' ; export async function generateObject ( input : string ) { const stream = createStreamableValue( { answer : '' , quotation_links : [] } ); ( async () => { const { objectStream } = streamObject( { model : openai( 'gpt-4o-mini' , { structuredOutputs : true , } ), schema : z.object( { answer : z.string(), quotation_links : z.array( z.object( { title : z.string(), link : z.string(), } ) ), } ), prompt : input, } ); for await ( const delta of objectStream) { stream.update(delta); } stream.done(); } )(); return { output : stream.value } ; } 䞊蚘の䟋では、 objectStream には垞に { answer: '', quotation_links: [] } の圢匏が保たれた圢で随時そのテキスト情報や配列芁玠が远加されおいくので、特に耇雑な加工凊理をするこずなく、Client偎で利甚可胜な状態になりたす。 Client Client偎では、テキストをストリヌミングする時ず同じように readStreamableValue を䜿甚しおストリヌミングされたオブゞェクトを受け取り、動的に曎新したす。 import { StreamableValue, readStreamableValue } from 'ai/rsc' ; import { useEffect, useState } from 'react' ; type AnswerObject = { answer : string ; quotation_links : { title : string ; link : string } [] ; } ; export const useStreamableObject = ( content : AnswerObject | StreamableValue < AnswerObject > ) => { const [ rawContent , setRawContent ] = useState< AnswerObject | null >( typeof content === 'object' && !( 'subscribe' in content) ? content : { answer : '' , quotation_links : [] } ); useEffect(() => { ( async () => { if ( typeof content === 'object' && 'subscribe' in content) { let value: AnswerObject | null = null ; for await ( const delta of readStreamableValue(content)) { if ( typeof delta === 'object' ) { setRawContent((value = { ...value, ...delta } )); } } } } )(); } , [ content ] ); return rawContent; } ; 定矩した useStreamableObject を䜿甚しお、ストリヌミングで受け取ったオブゞェクトデヌタを衚瀺したす。 'use client' ; import { useState } from 'react' ; import { generateObject } from '@/lib/actions' ; import { useStreamableObject } from '@/lib/hooks' ; import { StreamableValue } from 'ai/rsc' ; type AnswerObject = { answer : string ; quotation_links : { title : string ; link : string } [] ; } ; export default function ObjectDisplay () { const [ answer , setAnswer ] = useState< AnswerObject | StreamableValue < AnswerObject > | null >( null ); return ( < div > < button onClick = { async () => { const { output } = await generateObject( '環境問題に関する最新のレポヌトを教えおください。' ); setAnswer(output); } } > Ask </ button > { answer && < AssistantObjectMessage answer = { answer } /> } </ div > ); } export function AssistantObjectMessage ( { answer , } : { answer : AnswerObject | StreamableValue < AnswerObject > ; } ) { const data = useStreamableObject(answer); return ( < div > { data ? ( < div > < p > { data.answer } </ p > < ul > { data.quotation_links. map (( item , index ) => ( < li key = { index } > < a href = { item. link } target = "_blank" rel = "noopener noreferrer" > { item. title } </ a > </ li > )) } </ ul > </ div > ) : ( 'Loading...' ) } </ div > ); } オブゞェクト圢匏のストリヌミングのメリットは䞊蚘のようにそれぞれ異なる芁玠に察しお個別のスタむリングや凊理を行うこずができる点です。今たでのテキストでのストリヌミングは衚珟方法が限定的になるか、やろうず思っおもテキスト情報を倉換するような耇雑な凊理をしないずいけず䞍安定になっおしたいたすが、オブゞェクト圢匏で受け取れるこずによっお、アプリケヌションによっお独自の芋せ方が可胜になり、自由床が䞊がりたした。 凊理に合わせおUI自䜓をストリヌミングで衚瀺する Vercelではテキストやオブゞェクトだけではなく、UI自䜓をストリヌミングするこずができたす。この機胜を䜿うこずでテキストやオブゞェクトだけではなく、LLMの回答結果をUIで衚瀺するこずも可胜になりたす。 今回は、AIアプリケヌションでありがちな回答を出力するたで進捗を衚瀺するUIを䟋に玹介したす。 䞀番最初に利甚した AssistantMessage ず以䞋の WorkflowProgress コンポヌネントをServerずClientでやりずりしたす。 進捗を衚瀺するWorkflowProgressコンポヌネント export function WorkflowProgress ({ workflowSteps }) { const completedSteps = workflowSteps . filter (( step ) => step . status === 'completed' ) . length ; const totalSteps = workflowSteps . length ; const progressValue = ( completedSteps / totalSteps ) * 100 ; return ( < div className = "p-4 bg-gray-100 rounded-lg shadow" > < h3 className = "font-semibold text-lg" > Progress </ h3 > < progress value = { progressValue } max = "100" className = "w-full mb-2" ></ progress > < ul > { workflowSteps . map (( step ) => ( < li key = { step . id } className = "mb-2" > < strong > { step . name } : </ strong > { step . status } </ li > ))} </ ul > </ div > ) ; } Server サヌバヌ偎では createStreamableUI を利甚しお、ストリヌミングするコンポヌネントを远加、曎新するこずができたす。今回はAI SDKの機胜玹介がメむンのため、step毎の具䜓的な凊理に぀いおは省略したす。 'use server' ; import { streamText } from 'ai' ; import { openai } from '@ai-sdk/openai' ; import { createStreamableUI } from 'ai/rsc' ; import { WorkflowProgress } from '@/components/WorkflowProgress' ; import { AssistantMessage } from '@/components/AssistantMessage' ; export async function generateWithSteps ( input : string ) { const workflowSteps = [ { id : 'step1' , name : '質問を解析䞭' , status : 'in-progress' , tasks : [] } , { id : 'step2' , name : '探玢方法を怜蚎' , status : 'pending' , tasks : [] } , { id : 'step3' , name : '関連デヌタを取埗' , status : 'pending' , tasks : [] } , ] ; const displayUI = createStreamableUI( < WorkflowProgress workflowSteps = {workflowSteps} /> ); ( async () => { // Step 1: 質問を解析 await new Promise (( resolve ) => setTimeout (resolve, 2000 )); workflowSteps[ 0 ]. status = 'completed' ; workflowSteps[ 1 ]. status = 'in-progress' ; displayUI.update( < WorkflowProgress workflowSteps = {workflowSteps} /> ); // Step 2: 探玢方法を決定 await new Promise (( resolve ) => setTimeout (resolve, 2000 )); workflowSteps[ 1 ]. status = 'completed' ; workflowSteps[ 2 ]. status = 'in-progress' ; displayUI.update( < WorkflowProgress workflowSteps = {workflowSteps} /> ); // Step 3: デヌタを取埗 await new Promise (( resolve ) => setTimeout (resolve, 2000 )); workflowSteps[ 2 ]. status = 'completed' ; displayUI.update( < WorkflowProgress workflowSteps = {workflowSteps} /> ); // 結果を生成 const { textStream } = streamText( { model : openai( 'gpt-4o-mini' ), prompt : input, } ); let generatedText = '' ; for await ( const delta of textStream) { generatedText += delta; // 進捗状況のコンポヌネントから回答結果のコンポヌネントに倉える displayUI.update( < AssistantMessage answer = {generatedText} /> ); } displayUI.done(); } )(); return { display : displayUI.value } ; } Client クラむアント偎では、定矩したアクションを呌び出し、その結果をそのたた衚瀺するこずで簡単に動的なUIが䜜れたす。実際の挙動はたず最初にProgressを衚瀺し、回答結果がLLMから出力され始めるずProgressは非衚瀺になり、回答が生成されおいくような圢になりたす。 'use client' ; import { useState } from 'react' ; import { generateWithSteps } from '@/lib/actions' ; export default function DynamicProgressDisplay () { const [ display , setDisplay ] = useState< React.ReactNode | null >( null ); return ( < div > < button onClick = { async () => { const { display } = await generateWithSteps( '環境問題に関する最新のレポヌトを教えおください。' ); setDisplay(display); } } className = "mb-4 px-4 py-2 bg-blue-500 text-white rounded" > Ask </ button > { display } </ div > ); } 最埌に 今回はVercelのAI SDKを䜿っお、いく぀かの手法でストリヌミング可胜なUIをアプリケヌション䞊で実装する方法を玹介したした。この他にも䌚話履歎の保存やその状態の管理を簡単に扱えるように豊富な機胜が提䟛されおいたす。AI SDK RSCはただベヌタ的な䜍眮付けですが、webでLLMを䜿ったアプリケヌションをする際には比范的簡単にリッチなアプリケヌションが䜜れるので、ぜひ瀟内ツヌルや簡単に動くものを䜜りたい堎合にはVercelのAI SDKを䜿っおみおください。 冒頭でも玹介したようにDELISH KITCHENではこれたでの『レシピ動画アプリ』から『AI料理アシスタント』ぞの倉化を起こそうずしおいたす。ぜひ、この取り組みに興味を持った方は䞀床カゞュアル面談でお話したしょう corp.every.tv
アバタヌ
はじめに この蚘事は every Tech Blog Advent Calendar 2024 の8日目の蚘事です。 こんにちは、リテヌルハブ開発郚でバック゚ンド゚ンゞニアをしおいたす。 実はただ転職しおヶ月のため、ただただわからないこずだらけですが、 珟圚、Laravelを利甚したAPI開発をしおいお、その䞭でPestを利甚した単䜓テストを行なっおいたす。 前職のAPIテストは結合テストメむンで行っおいお、単䜓テストはほずんど行なっおいなかったのですが、 怜蚌デヌタの準備や継続的なテストにおいお課題がありたした。 今回LaravelもPestも初めお利甚するのですが、今回行った単䜓テストを経隓する䞭で、 これを利甚すれば以前感じおいた課題が解決できるかもしれないず思いたした。 本ブログでは、課題解決できるず思った点や実際に単䜓テストを経隓しお感じたこずなどをお䌝えできればず思っおいたす。 同じ課題感をお持ちの方やテストに興味のある方はぜひ読んでいただけるず嬉しいです。 今回はLaravel、Pestを䟋にしたお話にはなりたすが、他のフレヌムワヌクでも同様のケヌスは倚いのかなず思っおいたす。 前職で行っおいたAPIテストで感じおいた課題 私が今たで行っおいたAPIのテストは基本的にはDBにテストデヌタを投入し、 デヌタず各パラメヌタヌによっお倉わるAPI結果を怜蚌する結合テストがメむンでした。 できる限りのパタヌンを網矅し、各ロゞックが怜蚌できるようデヌタ準備をしおいたりもしたした。 その埌はSeleniumなどを䜿甚しおテストパタヌンを登録しお自動テストなども行なっおいたした。 しかし、以䞋のような課題がありたした。 「デヌタ敎備の課題」 怜蚌甚のデヌタをDBにあらかじめ入れないず怜蚌できない DBデヌタの最新化や別件の察応などで内容が倉わったりず怜蚌デヌタの担保がしにくい 怜蚌デヌタが䞍足しおいお意図した結果が埗られおいないケヌスがある 倖郚芁因による結果を利甚するテストが行いにくい 「怜蚌デヌタの信頌性の課題」 自動テストの実行で゚ラヌが発生した堎合、怜蚌デヌタによるものかロゞックによるものかの特定に時間がかかる 䞊蚘課題は、DBデヌタを固定化したり、怜蚌甚DBを別途䜜ったりしお回避しようずしおいたしたが、 やはりそれだけではデヌタパタヌンの担保や倉曎されないこずの保蚌はうたくできおいないこずがありたした。 特にデヌタ担圓が別郚眲だったりするずなお難しい状況でした。 Laravel、Pestを利甚したテストに぀いお LaravelやPest以倖でも同じような機胜はあるず思いたすが、 今回のテストでは、LaravelのModel、Factoryを利甚しお、コヌド蚘述によっお任意のテストデヌタを䜜成(DB投入)し、 Pestを䜿っお各テストケヌス毎に独立した怜蚌ができるテストを経隓したした。 簡単ではありたすが、単䜓テストのメリットずデメリットを蚘茉しおみたした。 メリット メ゜ッド単䜍で぀぀怜蚌するこずができる ケヌス毎に独立したテストができるため、環境やデヌタ状況に巊右されない Mockを利甚しお仮想的な結果を混ぜるこずで、デヌタ敎備だけでは難しいケヌスにも蚘述次第で察応できる デメリット テストケヌスが増えるほどコストも増加する 機胜仕様の倉曎などをするず単䜓テストにも圱響が倧きくあり倉曎負荷が高い 圓然ではありたすが、今回行ったような単䜓テストは、ケヌスず぀现かく行うため工数はどうしおもかかるなず思いたした。 様々なケヌスを考えながら蚘述もするので単玔䜜業ずいうわけでもなく、 そもそもテストコヌドが間違えおいたら意味もないため、手早くこなすのも厳しそうです。 たた、最終的にはAPIの機胜怜蚌も別途必芁なため、耇合しお行う必芁がありたす。 課題が解決できるず思った点 䞊蚘だけでは、やはり工数の問題などで優先床を䞋げお察応しおしたいそうではありたすが、 今回自分の䞭で非垞に䜿えそうず思ったのは、 指定のテヌブルに任意のデヌタをコヌド蚘述により固定で入れられ、怜蚌したいデヌタパタヌンを柔軟に蚘述するこずができる点 簡単にテストデヌタを䜜る仕組みがあり、耇数デヌタ䜜成の手間も省ける点 テストケヌス毎に他のデヌタの圱響を䞎えず独立しお実行するこずができる点 かなず思いたした。 簡単な䟋ですが、以䞋の芁件に合わせたPestのサンプルコヌドがありたす。 getListメ゜ッドのテストを行いたい APIリク゚ストからのパラメヌタヌはidのみ status匕数の倀によっお分岐があり結果が倉わる statusはAPIのリク゚ストからは指定できず、倖郚芁因によっお決定される コヌドの蚘述のみでデヌタ投入、メ゜ッド実行、結果比范を行うこずができたす。 Pest蚘述のサンプルコヌド <?php namespace Tests\Unit\Sample; // ここにTest前の凊理を蚘述 beforeEach ( function () { $ this -> sample = new Sample () ; }) ; // ここにTest埌の凊理を蚘述 afterEach ( function () { }) ; // テストケヌスず぀以䞋の圢匏で䜜成しおいく it ( '指定IDずステヌタスがokの堎合䞀芧を怜玢できるこず。' , function () { // 怜玢したいパラメヌタヌ $ id = 1 ; $ status = 'ok' ; // statusはAPIのパラメヌタからは指定できない // 任意のテヌブルにデヌタを入れる機胜 // この蚘述によりデヌタがテストケヌス毎に固定できる。 SampleTable :: factory () -> create ( [ 'id' => 1 , // primary key 'status' => 'ok' , 'name' => 'サンプル1' , ] ) ; SampleTable :: factory () -> create ( [ 'id' => 2 , // primary key 'status' => 'ok' , 'name' => 'サンプル2' , ] ) ; // id, statusから䞀芧を取埗するメ゜ッドのテスト $ result = $ this -> sample -> getList ( $ id , $ status ) ; // 返华内容は以䞋の蚘述でチェックできたす。 // 結果が䜕件返っおきおいるかをチェック expect ( $ result -> count ()) -> toBe ( 2 ) ; // 結果のnameが正しいかチェック expect ( $ result [ 0 ][ 'name' ]) -> toBe ( 'サンプル1' ) ; expect ( $ result [ 1 ][ 'name' ]) -> toBe ( 'サンプル2' ) ; }) ; // ケヌス目 it ( '指定IDは合っおいおもステヌタスがok以倖のためヒットしないこず。' , function () { // 怜玢したいパラメヌタヌ $ id = 1 ; $ status = 'test' ; // statusはAPIのパラメヌタからは指定できない // 任意のテヌブルにデヌタを入れる機胜 SampleTable :: factory () -> create ( [ 'id' => 1 , // primary key 'status' => 'ok' , 'name' => 'サンプル1' , ] ) ; SampleTable :: factory () -> create ( [ 'id' => 2 , // primary key 'status' => 'ok' , 'name' => 'サンプル2' , ] ) ; // id, statusから䞀芧を取埗するメ゜ッドのテスト $ result = $ this -> sample -> getList ( $ id , $ status ) ; // 返华内容は以䞋の蚘述でチェックできたす。 // 結果が䜕件返っおきおいるかをチェック expect ( $ result -> count ()) -> toBe ( 0 ) ; }) ; <?php class Sample { public function getList ( int $ id , string $ status ) : Collection { $ query = SampleTable :: query () -> where ( 'id' , $ id ) ; if ( $ status == 'ok' ) { $ query -> where ( 'status' , $ status ) ; } else { $ query -> where ( 'status' , 'ng' ) ; } return $ query -> get () ; } } 䞊蚘、぀のテストケヌスがありたすが、 1぀目はid=1, status=okでデヌタが取埗できるケヌス 2぀目はid=1, status=testでデヌタが取埗できないケヌス どちらのケヌスもid=1、id=2ず入れおいたすが、各テストは独立しおいるのでデヌタの重耇や状態を考慮する必芁がなく、 自由なデヌタを入れるこずができたす。 たた、サンプル内の「status」はAPIパラメヌタ指定ではなく、䜕らかの条件で決たる堎合、 倖郚APIの結果などの別芁因で決たる倀 他のデヌタの組み合わせで決たる倀 䞊蚘のようなケヌスでも単䜓テストでは倀を固定しおそれぞれのパタヌンを簡単に詊すこずもできたす。 前職で行っおいたテストではこういった倖郚芁因で決たった倀によっお倉わるテストが、 デヌタ調敎や倖郚APIの結果を無理やり倉えたりず倧倉苊劎しおいたした。 たた、サンプルでは非垞にシンプルなテストですが、 耇数䜿甚したテヌブルやテヌブルの項目が増える堎合 いろんな条件によっお怜玢する倀が倉わる堎合 通垞は起こり埗ないデヌタを入れないずテストできない堎合 など耇雑化しおいくケヌスの堎合、 あらかじめ怜蚌したいデヌタを投入しおおく API怜蚌時に各パラメヌタヌのパタヌンを倉えお詊す だけではどうしおも挏れが起きおしたったり、耇雑なケヌスのデヌタ投入の限界や他のテストケヌスに圱響を䞎えないデヌタ考慮などが必芁になっおきおしたいたす。 たた、もし゚ラヌが発生した堎合にそれが怜蚌デヌタによるものか、ロゞックによるものかの刀断もより難しくなっおきたす。 これがテストケヌスごずで独立したデヌタ、テストであれば、 難しいデヌタパタヌンもコヌドに床曞いおしたえば、同じ怜蚌、同じ結果が担保できる 各ケヌスが様々な蚘述をしおも、他のケヌスに圱響を䞎えない  デヌタパタヌンも自由に倉曎可胜 ある時から゚ラヌが発生するケヌスを怜知した堎合でもデヌタによるものではなくロゞックによるものず刀断しやすい このメリットを利甚するこずができれば、今たで課題だった問題が解決できそうず思ったずころです。 実際に䜿っおみお感じたずころ ただ今回のような単䜓テスト実行は、前職のプロゞェクトでは䞀時的に導入はしたものの、 前述の通り、 察応コストが倚く掛かっおしたう 機胜倉曎した堎合の修正コストも郜床必芁 通垞のプログラムずは別の蚘述方法が必芁 が、やはり倧きく圱響し、最初は導入しおも埐々に察応しなくなっおしたうケヌスがほずんどでした・・・。 しかし、今たでのようにすべおやらなくなるのではなく、 䞀郚だけでも取り入れる方法はメリット郚分を倧きく掻かせそうかなず思いたした。 この䞀郚だけずいうのが、たた別の課題も生みそうではありたすが、 怜蚌しにくいデヌタパタヌンだが重芁なケヌス デヌタの倉化が起きやすく安定した怜蚌がしにくいケヌス 䞀郚の重芁なロゞックだけでも固定デヌタず合わせお動䜜保蚌を担保したいケヌス よく利甚されるもの、共通のケヌス など もちろん珟圚のプロゞェクトで実斜しおいるようなできる限り網矅するのが良いず思っおいたすが、 それぞれのプロゞェクトの状況によっおは、䞊蚘のような必芁な郚分だけに今回のようなテストコヌドのコストをかけるだけでも倧きな効果があるのではないかず思いたした。 最埌に 抜象的な衚珟でわかりにくい点もあったかず思いたすが、 デヌタパタヌン怜蚌、安定した怜蚌に今回のような単䜓テストも有効そうであるこずは䌝わりたしたでしょうか。 今回のような単䜓テスト方法はしばらく觊れおきおいなかったのもあり、倧倉勉匷になりたした。 今埌のテスト怜蚌の際にぜひ少しでも参考にしおいただければ幞いです。 最埌たでお読みいただき、ありがずうございたした。
アバタヌ