TECH PLAY

ニフティ株式会社

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

517

こんにちは、新卒1年目の後藤と滝川と藤岡です! 新人研修の一環としてニフティ2024年度新卒入社の9名がAWS JumpStart 2024 for NewGradsに参加しました! AWS JumpStart for NewGradsとは?   新卒1年目 のエンジニアの方々を対象とした、2日間の実践的な研修プログラム。 将来的にAWS活用をリードする人材になるための第一歩をスムーズに踏み出せるようなコンテンツをというコンセプトで企画されているため、単なるAWSサービスの学習だけでなく、チームに分かれて要件に合った適切なアーキテクチャを検討・設計する経験を積む部分にフォーカスした内容。 プログラムの到達目標 一般的なリファレンスアーキテクチャを理解できる AWSのコアサービスの概要や選定基準を理解できる AWSのアーキテクチャ図を作成する流れを知る 参加するまでやること 事前学習では、システムアーキテクチャ設計の考え方とAWSの主要サービスについて動画を視聴しました。 1.Cloud for Beginners(約40分) Webサービスの基礎 2-1.AWS Skill Builder (約4時間) AWS の基本的な概念を包括的に or 2-2.Cloud for Beginners(約2時間) Webサービスの基礎(1の続き) 内容 アジェンダ AWSやアーキテクチャ設計に関する講義 チームに分かれてのWebアプリケーションのアーキテクチャ検討 ハンズオンワークショップ 実施期間 開催日時 (各回2日間) 2024 年 5 月 21 日(火)~ 22 日(水)各日 09:00 – 18:00 オンライン開催 2024 年 9 月 26 日(木)~ 27 日(金)各日 09:00 – 18:00 オンライン開催 スケジュール 1日目 VPC、ECS Fargate、Application Load Balancer (ALB)、RDSを使用してToDoアプリケーションの構築 VPCの設定:ネットワーク環境を構築 ECS Fargateの設定:コンテナ化されたToDoアプリケーションのデプロイ AutoScaringの設定 ALBの設定:トラフィックの分散とルーティングの実装 RDSの設定:データベースの構築(Writerインスタンス、Readerインスタンスの作成) システムの耐障害性テスト(Taskを意図的に停止させ、システムの挙動を観察) Taskの停止:手動でTaskを終了 新しいTaskの立ち上がりを確認:システムが自動的に新しいTaskを起動 サービスの継続性を確認:アプリケーションが中断することなく稼働し続けることを確認 RDSの構成確認RDSの構成 Writerインスタンスにフェイルオーバーを行い、Readerインスタンスと入れ替わることを確認 成果 2日目 アーキテクチャ検討 miroを使用し、まずは個人でアーキテクチャを検討していく課題です。 ECサイトの作成を目的として1日目の内容を参考にしながら各自、アーキテクチャ図を作成していきます。 そして、午後からは5人1グループずつほどに分かれ各グループでそれぞれのアーキテクチャ図を発表しました。 各グループは運営の方が決め、初対面の人同士で議論し組み合わせながらチームでひとつのアーキテクチャを完成させました。 以下それぞれ3人のチームのアーキテクチャ図です。 成果 後藤チーム 私たちのチームでは、Amazon ECRでコンテナイメージを一元管理し、Elastic Load Balancerで負荷分散を実現しました。Auto Scalingにより、スケーラブルな環境を構築しています。セキュリティ面では、VPCによるネットワーク分離とIAMを最小権限の原則に則ってアクセス制御を導入しました。さらに、AWS LambdaとAPI Gatewayを活用したサーバーレス構成を採用し、CloudWatchで監視を行っています。データ管理にはAmazon RDSを使用し、クラスタ構成にすることで高可用性を確保しました。外部API連携やCI/CDパイプラインは、AWS CodePipelineとCodeBuildを用いて自動化しています。チームメンバーのAWSの経験が浅い中、1日目や資料を確認しながらなんとか作成することができました。チームメンバー全員のAWS経験が浅かったにもかかわらず、1日目の学びと提供された資料を活用しながら、協力してアーキテクチャ図を完成させることができました。 滝川チーム 私たちのチームでは1日目で作成したToDoアプリをベースに構成図を作成していきました。 意識した点は可用性を高めること、管理の容易さの2点です。 可用性の点では、AZにリソースを分散配置、RDSの冗長化構成の導入、AutoScalingを活用した自動スケーリング機能の実装。 管理の容易さの点では、CI/CDの構築、CloudFrontを利用したコンテンツ配信の最適化、カスタマーサポート用の管理画面へのセキュアなアクセス経路の確立。 実際のAmazon ECサイトをモデルとしてイメージすることで、必要な要素を明確に把握し、効率的に作業を進めることができました。 藤岡チーム 私たちのチームでは2つの可用性ゾーン(Availability Zone 1とAvailability Zone 2)を設定することで、冗長性と高可用性を確保しつつ「Elastic Load Balancing」を中央に配置し、ユーザーからのリクエストが均等に分散されるようにしました。ロードバランサーの後ろには「Auto Scaling」機能で、需要に応じてサーバー数を自動的に増減させています。また、アプリケーションはコンテナとして実行しています。 チームメンバーで全体的にAWSの経験はそこまでなかったですが、1日目の資料を参考にしたり運営の方への質問、さらにAmazon Bedrockを活用することでなんとか形にすることができました。 感想 後藤 AWSについて、業務で少し触れた程度の経験しかありませんでした。アーキテクチャ図の作成を通じて、これまで業務で使用したことのないサービスについても知識を深めることができました。さらに、チームでリソースの必要性を議論する機会があり、実際の業務に近い形で経験を積むことができました。この研修は非常に学びの多いものでした。 滝川 AWS JumpStartでは特に可用性に重点が置かれている気がしました。具体的には、Auto ScalingやRDSのライター・リーダーインスタンス、Application Load Balancer (ALB)の重要性について学ぶことができました。クラウドを利用する上で、セキュリティは利用者自身が責任を持って管理しなければならない領域のため、今回セキュリティについて学べたことは非常に有意義でした。 藤岡 私はAWSを触ったことがある程度の状態で今回の研修に参加しました。2日間の研修の中でも主に実践課題に取り組むことで理解が深まったと感じました。アーキテクチャ検討では、正解は1つではなくチーム内でも様々な意見が出ましたが、生成AIであるBedrockをうまく活用することができチームの方向性が定まっていきました。今回の研修で、答えのないところから構築していくAWSの面白さを感じたとともに生成AIの活用の幅広さを体感できました。
こんにちは。2024年に中途入社した おおい と申します。 今回はニフティに中途入社してみて私自身が感じたことを書いてみようと思います。 ニフティという会社の雰囲気をなんとなく感じていただけると嬉しいです。 自己紹介 前職はSIerでエンジニアとして、主にJavaを使ってシステム開発業務をしておりました。 ご縁がありニフティに入社し、基幹システムグループのサービスインフラチームに配属となり、 @nifty v6サービス の申込システム担当として業務にいそしんでいます。 入社後に感じたこと そんなSIer出身の私がニフティに入社してから感じたことを3つお伝えします。 わりと自由 服装や髪型が自由であることや、社内で使用しているコミュニケーションツールでちょっとした雑談があるなど働くうえでかなり自由にさせてもらえているなあと感じました。 社内イベントも活発で、イベント時には多くの方が全力で楽しんでいる姿をよく目にします。 また業務においても裁量をもって遂行させてもらえているので、これまで未経験だった技術スタック (Python、DevContainer etc…) を使用して開発してみるといったこともできています。 思ったよりも運用業務が多い 前職ではお客さまのシステムを開発しており、そのほとんどが開発業務でした。 一方でニフティではお客さまに直接サービスを提供している会社なので、時期や所属部署などにもよりますが、日々の業務では開発に関する業務に比べると運用に関する業務のほうが多めに感じました。 ただ入社して日が浅い私にとっては、担当システムの知識を習得するのにとても助かった点だったりします。 挑戦する機会がたくさん 先に「業務においても裁量をもって遂行させてもらえる」と書いたとおり日々の業務ではもちろん、このエンジニアブログの執筆や定期的に開催されるLT大会への登壇など、挑戦することによって自分を成長させることができる機会がたくさんあると感じました。 おわりに ニフティは自由な雰囲気の中で挑戦、成長できる環境が整っています。 日々の運用業務を通じてシステムへの理解を深めながら、新しい技術にも挑戦できる。そんなバランスの取れた環境で、エンジニアとしての可能性を広げていけることを実感しています。 これからもこの環境を最大限に活かしながら、お客さまのため、ニフティの発展のために貢献していきたいと思います。
イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! 天気予報があなたの手元に届けられるまで   〜今日傘を持って出掛けることができるための仕組みの裏側〜 2008年に開始した@nifty天気予報がついにリニューアル。その開発のウラ側についてのテーマでお話をしました。 また、Tech Talk#22はコラボ企画となっており、ウェザーニューズ様とコラボしています。 リニューアルで使用したNext.js、Go、GraphQLでどのように開発していったのか、気象データを提供する側の仕組みなどをウェザーニューズとニフティの技術者が紹介しております。 当日見ていない方もご興味あれば是非、 アーカイブ からご視聴いただければと思います。 【10/2 (水曜日) 19:00~20:30 ハイブリッド開催】ウェザーニューズ社コラボイベント! Next.js/Go/GraphQLで生まれ変わった@nifty天気予報 開発のウラ側を開催します! 【LT1】天気予報があなたに届けられるまで ウェザーニューズからの登壇者によるお話しで、気象情報の収集から提供についてが主な内容です。 天気予報という身近なものでその仕組みでとても興味深いお話しでした 資料 【LT2】@nifty天気予報:フルリニューアルへの挑戦 このプロジェクトの全体像がよくわかります。体制や採用した技術などなど。 @niftyが提供する天気サービスは1997年から始まったらしく1997年入社な私としてはちょっと親近感わきます 資料 【LT3】@nifty天気予報のフロントエンドを実装するまで タイトルどおりフロントエンド開発についてです。 採用した技術の理由など、これからの開発の参考になるという方もいるのではないでしょうか。 資料 【LT4】AWS AppSyncを用いたGraphQL APIの開発について バックエンドのお話しです インフラ構築のお話しからやっているので、これからシステム構築されるような方に参考になるかもです 資料 まとめ 初の他社様とのコラボレーションとなりましたが、いままでのオフライン参加よりも多くの方に来ていただけました。質疑応答でも毎回何件か質問いただき、なかなか盛り上がったのではないかと思います。 フロントエンド、バックエンドで同じ技術を使うではなく、それぞれに適したものを検討・採用されており、工夫されて作られているのだなという感想を持ちました。 ご視聴された方々に少しでも参考になる内容であったなら幸いです。 次回予告 次回のTech Talkは2024年11月26日12:00~13:00開催を予定しております。 テーマはTerraformの入門編です アーカイブ(YouTube) 発表資料(Speaker Deck)
ねんがんの Notionフォーム がリリースされました! プロパティの入力値をフォームのフィールドとして設定でき、フォームを好きな場所に埋め込むことも可能です。Notion AIと組み合わせれば、自由記述からのキーワード抽出ができ、さらにグラフで可視化もできます。アンケートフォームなどでの活躍が期待できますね。 Notionフォームで主にできること プロパティの入力 リレーションの選択も可能 ページ本文の入力 フィールドの入力必須化 フィールドの説明 フィールドの種類 プロパティの種類に応じて変化 フィールドのValidation 見出しやテキストの挿入 入力後の自動処理 データベースオートメーション フォームの埋め込み Notionフォームには基本的な機能はありますが、まだ完全とは言えません。 特にValidation機能がないのが個人的に困っている点。プロパティなら仕方ないと思っていましたが、フォームの形をしているとこの機能が欲しくなります。 そこで今回は、NotionフォームにValidation機能を追加できないか検証してみます。 入力時やPOST前に制限をかけることはNotion単体ではできないため、入力後に値が期待通りかチェックする方法を取ります。今回のアップデートでオートメーションのアクションに数式や変数が使えるようになったので、これを早速活用してみます。 データベースオートメーションと数式(Formula)でValidationっぽいものを作る 覚えておくべきオートメーションの仕様 トリガー → アクション * n の2ステップまで アクションはおそらく非同期で処理される アクションを実行するユーザーは Notion (ユーザープロパティ等では選択できないシステムユーザー) Notion ユーザーが実行したアクションは新たなオートメーションをトリガーしない アクションの実行有無に関する条件分岐は設定できない Slackへの通知内容はカスタマイズできない メールアドレスと文字列のprefixをチェックするサンプルフォーム フォームに入力してもらいたいフィールドを用意。 メールアドレスとSlackのチャンネル名を入力してもらい、入力規則に沿っているか確認するのが目的。 次に入力された内容が正常か確認するための Validate と Log プロパティを用意。 Validation本体はオートメーションを利用。 数式で正規表現にマッチするか調べて変数に格納。 変数の値を元に Validate と Log プロパティの値を更新、最後に入力したユーザーに通知を送信。 これで入力値が正しければ正常なメッセージの通知が行われ、間違っていれば修正を促す通知がフォーム送信者に送られる、Validationっぽいものが完成です。 ただし、この構成には課題もあります。初回フォーム送信時は問題ありませんが、編集時には文字を記入中でもトリガーが発火されるため、通知が頻繁に来てしまいます。通知はデータベースのページごとにまとめられるので、まだ許容できる範囲ではありますが、トリガーはボタンにしてもいいかもしれません。 まとめ 自力でValidationっぽいものは作れましたが、完成度がいまひとつですね。 フォームの内容に厳密性を求める場合は、まだGoogleフォームなど他のツールを使った方が良さそうです。フォーム機能の改善については、Notionにリクエストを送り実装されるのを期待して待ちましょう。 触ってみると分かりますが、最近のアップデートで表現力や柔軟性はかなり上がっています。特にオートメーションで数式や変数を使用できるのを求めていた人は多かったのではないでしょうか。この記事を読んで「今ならここまでできるのか、それならこれもできるのでは?」と新しいアイデアが湧くきっかけになったら嬉しいです。
ニフティ株式会社でエンジニアリングマネージャーをしています芦川です。 今回は、インナーソースを日本に広めるべく、InnerSource Commons Japanの運営メンバーとしての立場からも意見を書いてみたいと思います。 ニフティではここ数年間、社内にインナーソースを導入するための活動を行ってきました。 導入のきっかけや詳しい活動内容については、以下をご参照ください。 インナーソースを導入してみた その① お試し導入編 インナーソースを導入し てみた その② 土台作り編 導入後、学習を続けていく中で、社内でインナーソースが広まりやすい部分と、広まりにくい部分が見えてきたように感じます。実際の社内の結果や、今後に期待できる点も含めて書いてみたいと思いました。 ここからの話は、あくまで「こういうパターンだとインナーソースは広まりやすい」という話になります。本来、インナーソースでは社内で自由にコントリビュートできる環境を目指しています。パターンにあてはまらなくても当然個人のスキルアップやエンジニア体験の向上が期待できます。そのため、「これ以外のパターンはやらない方がいい」という制限を意図しているわけではありませんのでご注意を! では、インナーソースが効果的に導入できるパターンについて、モデル図を作成しましたのでご紹介いたします。 効果的な導入1️⃣ みんなで便利ツールにコントリビュートしよう! これはご想像の通りだと思いますが、出退勤や日報(弊社には日報文化があります)、コミュニケーションツールなど、業務に直接関わらないツールについて、エンジニアが自ら作成しやすく、また、作成したツールを社内に広めやすい環境があります。このようなツールに対してインナーソースは非常に効果的です。まさにOSSのように、コントリビュートが生まれやすい環境と言えるでしょう。 効果的な導入2️⃣ プラットフォームチームのリポジトリにコントリビュートしよう! ここからは、チームトポロジーのチームタイプに基づいた例になります。 もう少し業務的な例として、プラットフォームチームのリポジトリへのコントリビュートが挙げられます。プラットフォームチームは、広範な部署からの依頼を受けることが多く、全社にわたる影響を持つこともあります。 ニフティでは、リポジトリ内での定義追加などをプルリクエストとして他チームから受けることで、依頼のワークフローが簡略化され、リードタイムが短縮されました。新機能のエンハンスや新しいAPI I/Fの提案などもっといきたいところはありますが、まだそこは道半ばではあります。 なぜプラットフォームチームだけがそうなのか、と考えた理由ですが、上の便利ツールと同じくメリットを享受できる人が、つまり利用者が単純に多くなるからです。 そもそもコントリビュートは、コントリビュートしたいモチベーション、つまり利用者自身へのメリットがあることが前提になります。 例えば、特定のストリームアラインドチームの持つリポジトリへ、プラットフォームチームのメンバーがコントリビュートするのか?そこでの業務的なメリットはなんだろうか?そのモチベーションは果たしてなんでしょうか?と考えていくと、4つのチームタイプの中ではプラットフォームへのコントリビュートが広まりやすいのでは?という話になります。 (コンプリケイテッドなシステムについて、スキルアップを目指してコントリビュートする例も、組織的な観点から見ると、おそらくその会社にとって重要な意味をもつシステムであるはずなので、そのシステムを知る人間が増えるということは会社にとってすごく大事なところです。もっと将来ではそういうところも目指していきたいです。) 効果的な導入3:ドメイン知識が似てビジネスの方向性が同じストリームアラインドチームのコラボレーション ここからは、弊社でまだ実践例が少ないものの、期待が大きいパターンになります。 コントリビュートのモチベーションが広まりやすさに大きく関係すると考えていますが、その意味では、ドメイン知識が似てビジネスの方向性が同じストリームアラインドチーム同士では、お互いにメリットが生まれることが多いと思います。 例えば、あるチームが作成したAPIを、別のストリームアラインドチームが共通のものとして利用するケースです。弊社では、郵便番号から住所を引くAPIや共通のID登録を行うAPIなどがこれに該当します。こうしたコラボレーションでは、プルリクエストを柔軟に行うことでリードタイムが短縮され、他のチームにもメリットが生まれます。 逆に、ドメインが全く異なるストリームアラインドチーム同士のコラボレーションは、両者にメリットが少なく、認知負荷の増大にもつながるため、あまり広がりそうにないのかな、と思っています。 効果的な導入4:ドキュメントへのコントリビュートを全社的に推進しよう! 最後に、広がりが期待できるのがドキュメントへのコントリビュートです。 最近発表された「 State of InnerSource Report 2024 」でも言及されていますが、企業がインナーソースを導入する際に、ドキュメント管理は比較的ハードルが低いとされています。 documentation-as-codeが一般的な初期のInnerSourceプロジェクトタイプとして登場していること。この傾向は、組織がInnerSourceの取り組みを始める際に、実践的でハードルの低い入り口を見つけていることを示唆している。 State of InnerSource Report 2024 , P2 ただし、ニフティの場合、すべての開発ドキュメントがgitで管理されているわけではなく、主にNotionに保存されています。また、全社の人事・総務なども含めたドキュメントを対象とする場合、GitHubアカウントがエンジニアにしか配布されていないことが課題となっています。 しかし!コードとして管理されているドキュメントに限らず、「コントリビュートして互いに助け合い、良いドキュメントを作る」ことを目的とするならば、Notionのサジェスト機能を活用できるのではないかと考えています。 Notionサジェスト機能について サジェストモードでNotionページを編集すると、ページの持ち主に対して編集の提案ができ、承認されると反映されます。これは、ゆるいプルリクエストのようなもので、社内でのインナーソース活動と同義です。エンジニアに限らず誰でも使える機能であり、インナーソースの認知を広げる起爆剤としても活用できるのではないでしょうか。 以上、ニフティでもインナーソースはまだまだ促進中で、未来は明るく、これからもやれることがたくさんあると感じています。今後も導入事例や展望を発信していきます! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは、ニフティ株式会社の小林です。 最近、日常業務の効率化に向けて積極的に新しい方法を探求しています。 百聞は一見に如かず 文章での説明に加えて、視覚的な例を用いて質問をさせてください。 以下、左右にあるサイトを見比べて何が異なるのかを探してみましょう。 ※ 私の友達であるmyfriendGPTを利用し、適当にコンテンツを作成しています。  ⇒ ChatGPT APIを活用したSlackbotを作成して、30分以内に会社内に有能な友達を作る 1問目 質問 以下の画像でお互いに違う箇所はどこでしょうか。 答え 申し訳ありません。実は、画像の内容は同一ですが、URLのみが異なっています。 2問目 質問 以下の画像でお互いに違う箇所はどこでしょうか。今度はURLは同じとします。 答え ID部分が異なっています。 似て非なる環境が生み出す問題 上部の質問で登場した環境は非常に似ていて、開発環境と本番環境の見た目の近さが思わぬ問題を引き起こすことがあります。 以下のような問題が時折発生すると考えています。 本番環境でのデータ調査を意図していたが、開発環境で実施していた 気付くまでに不要な工数がかかってしまった 開発環境での作業を想定していたが、本番環境で実施していた 内容次第では障害に繋がるリスクがある なぜ起こるか 本問題は、システム的要因と人的要因が絡み合って発生すると考えています。 システム的要因 開発環境と本番環境の区別が不明確 視覚的な差異が少なく、誤操作のリスクが高い ログイン情報の長期保持 アカウント切り替え時の確認不足を誘発 人的要因 作業への慣れ 警戒心の低下や確認作業の省略に繋がる 疲労 注意力の低下 視覚的な認識能力の低下 小さな文字の見落とし 焦点が合わせにくくなる 対応策 このブログの本題となりますが、以上の発生に対する対応策を提示します。 本番環境のみタイトルを赤色にする ブログ用の極端な色付けのため、実際は目に優しい色でも問題ないと思います。 特定のURL/文字列に該当する場合に制御できる 気付きそうですね どう実現したか Tampermonkeyを使用 Tampermonkeyは、ウェブページのHTML要素やCSSを動的に変更できるJavaScriptコードを実行する拡張機能です。主にChromeで使用されますが、EdgeやFirefoxなど他のブラウザでも利用可能です。 以下等、他に影響を与える実行をしないこと 過剰なリクエストの送信 著作権侵害 プライバシーの侵害 マルウェアの配布 上記の通り、Tampermonkeyは要素変更以外にも多様な機能を持つため、意図しない影響を及ぼさないよう注意が必要です。HTMLやCSSの要素変更のみであれば、サーバには影響を与えないことを「開発者ツール」のNetworkから送信されていないことから確認しております。 以下に例を示しますが、より深い理解を得たい方は、「DOM」や「JavaScript」について学習することをお勧めします。 URLベースでスクリプトを変更可能 Tampermonkey内の@matchに該当のURLを含めるとURLベースで実行することが出来ます。 // ==UserScript== // @name Header Background Color Changer // ~~~ // @match http://localhost:8088/hoge.html // ~~~ // ==/UserScript== 文字列制御でスクリプトを実行可能 コード内に以下のような特定の文字列の有無で実行を制御するように変更することが出来ます。 // ページ内に特定の文字列があるかチェックする関数 function checkForTargetString() { return document.body.innerText.includes('1992TORI'); } 最後に 本番環境と開発環境の視覚的な違いについては、実装上必要な場合もありますが、一般的には両環境の見た目を同一に保つことが、運用とテストの観点から望ましいと考えています。 このような場合、Tampermonkeyなどのブラウザ拡張機能を活用し、開発者側で対応することをお勧めします。これらのツールは簡単にオン/オフを切り替えられるため、視覚的なテストにも柔軟に対応できます。 本ブログが、人ではなく仕組みで解決する方法の1つになって頂ければ幸いです。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
前回の記事では、ニフティのメールチームで働く社員に、業務内容やチーム環境についてインタビューを行いました。 今回はインタビューの後編をお届けします。前編はこちらの記事をご覧ください。 【前編】@niftyメールの開発チームに業務のあれこれ聞いてみた!   過去頑張ったエピソードを教えてください。 M.Sさん 冒頭に触れた基盤移行ですね。基盤移行のきっかけにもなったのですが、ニフティのメール基盤は独特な作りをしていて作った人にしかわからないような状態でした。期限が迫る中、バグを踏んで対応に追われたり色々あったなという感じです。 Y.Mさん 3月に入ったばっかりなのでまだあまりエピソードはありませんが、メールを理解するところかなと。アーキテクチャはしっかりされていながらも、理解しないと進められないところもあり、OJTではそのあたりを知る機会がなかったので勉強しながら進めています。 K.Fさん 私も基盤移行です。スケジュールが厳しくて、2週間で社内向けのメール送信システムを作らなければいけない状況もありましたが、何とか完遂しました。 休日はどう過ごしていますか? M.Sさん 去年くらいから犬を飼い始めたので犬と遊んでいます。社交的なわんこなので、週末はドッグランに連れて行っています。 M.Sさんのわんこ K.Fさん リモート会議の時にワンちゃんの声がして癒されました。 M.Sさん 足元にじゃれついてきて鳴くので、その時の声かな? Y.Mさん 家でのんびりしていて、ゲームをやっています。金曜に友人からゲームの誘いがあって、朝までやって、昼起きて夜はまたゲームしています。 M.Sさん 月曜は辛くないですか?笑 Y.Mさん 日曜は早めに切り上げています。笑 夜遅いながらも、朝も早めに行動していて最近8時に出社しています。朝は涼しいとマネージャーに教えてもらったので… K.Fさん 最近ビリヤードにはまってます。バッティングセンターに行ったりもして、おとといは群馬でパターゴルフをしたりしています。ゴルフ、サバゲ―にも興味が出てきて忙しいです! M.Sさん K.Fさんが多趣味なのは知ってましたが、ゴルフは初めて聞きました。 Y.Mさん きくたびに新しい趣味が出来てる気がしますw K.Fさん 筋トレははまりすぎて日常になりました。社内のSlackの筋トレチャンネルで筋トレ情報をのやりとりをしていたりします。ブカツを通してボルダリングにも興味を持ち、プライベートで登ったりもしています。 いま楽しみなことは何ですか? M.Sさん 来月沖縄に行くので楽しみです。2年前に行った時は台風が直撃してずっと雨だったので、今回こそは晴れてほしい。沖縄料理が好きです。 K.Fさん NIF BARというニフティ社員のためのバータイムが始まったので、お酒を飲みながらボドゲをやりたいです。 Y.Mさん 実家に帰るので、実家で焼肉を食べるのが楽しみです! 今回はニフティのメールチームのインタビューの様子をお届けしました。ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する .is-style-rounded + .has-text-color{ font-size:95%; }
今回はメールチームへのインタビューです!   M.Sさん 2018年5月にキャリア採用で入社しました。入社から現在までメールチーム一筋です。2023年10月から体制変更に伴いメール開発サブチームのリーダーを担当しています。@nifty 安心メールパックやWebメールの開発・運用を中心に、@niftyメールを構成するシステムの面倒を幅広く見ています。仕事の後はスプラトゥーンをゆる〜くプレイしています。2023年に犬を飼い始めてから毎日溺愛しています。 K.Fさん 2020年4月に新卒で入社しました。2か月の研修を経て2020年6月に配属されました。私が配属された当初はメールチームでAWSが利用されておりませんでしたが、新規サービスを中心に積極的にAWSを導入していきました。今は@nifty 安心メールパックの開発・運用を担当しています。週末はバイクでツーリングに出かけたり、キャンプで自然を満喫しています。楽器やビリヤード、ボルダリングなど、いろんな趣味に挑戦するのが好きです。体力づくりのため、週に5日ほど筋トレに励んでいます。 Y.Mさん 2023年4月に新卒で入社しました。1年間3つのチームの業務を体験できるジョブローテーションを経て、2024年3月にメール開発チームへ配属されました。担当業務は@nifty 安心メールパックの開発・運用です。ゲームが好きで、友人とスプラなどをやります。社会人になってからはおいしいランチも趣味です ニフティメールとは何ですか? M.Sさん ニフティメールはざっくりというとニフティの会員向けに提供しているメールサービスです。歴史が長く、パソ通時代のNIFTY-Serveから続いているニフティでも歴史のあるサービスなんです。無料で利用できる迷惑メールフィルタ、有償のメールウイルスチェック、@nifty セキュア・プライバシー for Mailなど、様々なオプションが利用できるメールサービスです。 そういえばK.FさんとY.Mさんはニフティ、ニフティメールを入社前から知っていましたか? K.Fさん 実は全くニフティのことを知りませんでした。知らずに説明会にきてしまったのですが笑、説明会で魅力的な会社だと感じられたので入社しました。横のつながり、縦のツナガリが感じられて、透明感が高く風通しがよさそうだと感じました。なんていうかこう…まぶしく見えました。自社開発メインなのも大きかったです。 Y.Mさん 同じく私もニフティのことを知らなかったです。デイリーポータルZの会社として認識していて、興味を持って参加しました。説明会の雰囲気も良かったので入社を決めました。 M.Sさん 私はニフティが回線サービスを提供していることも知っていました。学生時代にユーザーでもあったので、ちょっとショックですね。笑 安心メールパックとは何ですか? M.Sさん 通信端末が故障した時に修理費用を補償するサービスのことです。 (安心メールパックのサービスサイトは こちら ) 名前にメールという単語がついているんですが、メールアドレスが一つついてくるためです。( セカンドメール と言います) 2021年3月末くらいからサービスを立上げ、1か月後の4月末にはリリースしました。かなり急ピッチでの開発となりましたが、企画からの強い要望があり、最優先で対応しました。 K.Fさん インフラ、バッチの開発を担当していたのですが、2年目でまだ経験が浅かったのですが、PythonやTerraformなど初めて触る技術で楽しく開発させていただきました。 M.Sさん AWSの環境構築はすべてK.Fさんが担当してくれました。 K.Fさん 資格勉強はしていて、AWS認定資格をいくつか取得したのですが実践は初めてでした。 Y.Mさん 急いで作られたとは仰ってましたが、アーキテクチャがしっかりしているのですごくやりやすいです。 メールシステムにはどういった技術が使われていますか? M.Sさん Python、Terraform、AWSがメインです。 Webメールの現在のバージョンは2010年に作られていて、バックエンドがJava、Tomcatです。フロントエンドはPrototype JavaScript FrameworkやjQueryを使用しているので、Webメールをモダンな技術を使って刷新しようというプロジェクトを計画しています エンジニアが触ってて楽しい技術を採用していきます! K.Fさん 心躍ります! Y.Mさん わくわくが止まんないです。 メールチームのチームはどういう構成になっていますか? M.Sさん メール開発チーム、メールDevOpsチームの2チーム体制です。もともとメールDevOpsチームが主に担当していた配送基盤をKDDIさんの基盤へ移行したのですが、現在のメールDevOpsチームはニフティに残った配送系システムの運用や認証系システムの開発・運用を主に担当しています。 メールDevOpsチームは、現在Gmailの送信者ガイドライン対応などのメール配送の重要な部分をより良くするための開発をKDDIさんと協力しながら進めています。 ※メール基盤の移行プロジェクトについてはこちらの動画をご参照ください! 開発チーム、DevOpsチーム間のやりとりはどうしていますか? M.Sさん 迷惑メールフィルタの案件は両チームで動かしています。毎週水曜日にメールチーム全体の定例会で話をしたり、普段からSlackでやり取りしています。 K.Fさん AWSの技術的な相談をしたりしています。コンテナ周りの知識をDevOpsチームに教えています。 M.Sさん K.Fさんは先生役なんです。 安心メールパックについては企画とのやり取りがありますね。 Y.Mさん メールチームの定例に企画の方が参加されるので、新しい企画についての話をしたりしています。 M.Sさん 今年くらいからY.Mさん、K.Fさんに企画とのやり取りは任せています。 企画とやり取りしているときに心掛けていることはありますか? Y.Mさん システム的な話をしても伝わらないので、企画の方に歩調を合わせるようにしています。 K.Fさん 企画から要望があっても嫌な顔をしない、断らないことを心掛けて要望に応えられるように調整するように意識しています。 M.Sさん おかげで企画との関係は良好です! 現在のチームにはどういった課題がありますか?それに対してどうやって取り組んでいますか? M.Sさん メール開発チームとして始動したのが去年の10月で、サブチームリーダーとしての立ち回りもそこからなのでチーム運営をどうしようかという課題はありました。 スクラム、アジャイル開発のフレームワークを取り入れて日々試行錯誤しながら取り組んでいます。スクラムマスターも私が担当しています。 Y.Mさん スクラムのイベントをM.Sさんが企画してくださり、どんどん良くなってます。 K.Fさん 時間を確保してスプリントレビューやレトロスペクティブをやることで、Webメールへの理解が深まったり、アイスブレイクを通してチームのプライベートの姿を知れてチームが良くなっていく実感があります。 M.Sさん K.Fさんの話にもあったんですが、業務と人がセットになって属人的になってしまっていました。開発チームのタスクをチーム全体で見られるようにしないといけないなと考えていました。 優先的なPBIに対してフロー効率を考えてみんなで当たっていくことを目標に、プロダクト勉強会を週一でやっています。 K.Fさん Webメールの森羅万象を知りたいです! M.Sさん 森羅万象。笑 私もWebメールの全てを理解できているわけではなく、主担当になってから5カ月くらいなので勉強しながらやっています。教える立場になってはじめて気づくこともあります! Y.Mさん Webメールなんも分からん状態なので、安心メールパックのことをやりつつ学んでいきたいです。 後編に続きます! 今回はニフティのメールチームのインタビューの様子をお届けしました。続きは後編の記事をご覧ください。 【後編】@niftyメールの開発チームに業務のこととプライベートの過ごし方を聞いてみた! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する .is-style-rounded + .has-text-color{ font-size:95%; }
概要・はじめに こんにちは。ニフティ インフラチームの西澤です。 みなさんは毎月クラウドにかかっているコストはどのように管理していますか? ニフティでは複数のクラウドを利用しており、私の所属しているチームでは全社的なクラウドのコスト管理を行っています。 本記事では弊社で実施しているクラウドコストの管理方法についてご紹介させていただきます。   クラウドコスト管理の目的 クラウドを利用し始めた当初は、予算と実績の乖離が非常に大きかったため、一定の範囲内に抑えるための施策として取り組みが始まりました。 また、予実管理を通じてクラウド利用料の最適化を図ることも目的の一つとなっています。   主な実施内容 [1]クラウドコスト見通し額の記載 まず、各チームが利用しているアカウントについて、月ごとの見通し額を算出した上でスプレッドシートに記載してもらっています。 基本的には年度の初めに1年間分の見通し額を入力してもらいますが、変更が必要な場合は随時金額の修正が可能です。 変更の際はSlackのワークフローから申請してもらい、その内容がスプレッドシートに記録される仕組みになっています。 直接編集にしていないのは、金額の変更理由や履歴を追跡できるようにすることと、承認のフローを組み込むことが目的です。 [2]見通し額と予測額を比較してSlackへアラート通知 続いて、想定した見通し額通りの利用となっているかを確認するため、各アカウントごとにその月の予測額を毎日取得しています。 そして、記載してもらっている見通し額と予測額を比較し、定めている閾値を超えた場合はSlackチャンネルへアラートを通知します。 ※予測額の算出や取得方法は各クラウドで異なります アラート通知内容 利用者はアラートの内容に基づいて、現時点で見通し額と予測額のずれが発生している原因を確認したり、今後の見通し額の変更を実施します。 見通し額との乖離を通知することで、意図せずリソース利用に増減があることに気づいたり、見通し額の精度を向上させることを目指しています。 [3 ]実績確定後の見通し額との差額比較 次に、実績が確定し、各クラウドベンダーから請求書をもらった後の対応です。 各アカウントごとに見通し額と実績を比較し、乖離があったアカウントを抽出して利用者へ連絡します。日々見通し額のアラートを出していますが、想定外の事態やアラート確認漏れ、見通し額の申請漏れなどにより乖離が発生する可能性があります。 [4]差額が出た理由の確認 連絡を受けた利用者は差額内容を確認し、差額が出た原因や今後の対策を報告してもらっています。毎月理由は様々ですが、以下のようなものが主な例として挙げられます。 ログ量の増加 通信量やデータ転送量の変動 料金の値上げ 割引適用の期限切れ 検証に伴うリソース追加 ※利用後の停止予定を忘れてしまうなど 特にネットワークやログなどは予測が難しかったり、検証中の料金については意識しにくいことがあるため、こういった理由が多い印象です。 [5]コスト評価ミーティング コストに関する情報が出そろったら、確定した実績について各部署のリーダー向けにコストの評価ミーティングを実施しています。 確認しているのは以下のような内容で、認識の相違や課題などがないか話し合う場にもなっています。 クラウドの金額 クラウド全体および各クラウドごとの金額推移 前月と比較し、差額が大きかったアカウントの増減要因 アカウントごとの実績推移および今後の見通し額推移 見通し額との差額が出ていたアカウントの理由 さらに、各部署にこの場で課題となった情報を持ち帰り、適正化に取り組んでもらっています。このように様々な観点や立場からコストを確認し、クラウド利用部署全体で意識していくことでコスト削減につなげています。 取り組みによる効果 クラウドコスト管理の運用が始まったのは2021年からと比較的最近ではあるのですが、 年間の金額を2023年と比較すると、約25%ほど削減できたという結果になりました。 全てがこの取り組みの影響ということではなく、利用者の協力もあってのことですが、効果が金額として顕著に出てくるとやりがいがありますね!   おわりに 今回はニフティで実施しているクラウドコストの管理方法について簡単に紹介をさせて頂きました。 クラウドコストを管理することで減額の効果は出ていますが、管理を始めてから時間が経過し、クラウドの新規利用や各クラウドの割引施策などにより、アカウントごとの正確な金額の算出や予測は難しくなってきています。 また、クラウドベンダー側の仕様変更で現状の運用には即していない部分も出てきており、様々な課題が浮かび上がってきているため、システムやフローを全体的に見直すタイミングに差し掛かっていると感じています。 こういったエンハンスの取り組みに興味のある方、ぜひニフティで一緒に働いてみませんか?以下のリンクよりご応募お待ちしています! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに おはようございます。IWSです。 AWSを使っている人であれば一度は見たことがあるであろう Amazon CloudWatch のダッシュボード(以下ダッシュボード)。 私達のチームではコンソール画面からぽちぽち手動で作っていたのですが、今回 Terraform で1から環境を作る機会があり、ダッシュボードも Terraform を使って作ってみました。 せっかくなので備忘録がてら記事にしていこうかなと思います。 Terraform 今回使うTerraformのディレクトリ構成はこちらです。関係しないファイルなどは省略しています。 . ├── dev │ └── dashboard.tf # 各環境ごとの値をmoduleに渡す ├── modules │ └── dashboard │ ├── api.tf # 各グラフの設定 │ ├── main.tf # ダッシュボード本体の設定 │ └── variables.tf └── prod └── dashboard.tf # 各環境ごとの値をmoduleに渡す module にダッシュボードとそのグラフの設定を書き、各環境ごとの値(ECS クラスターの名前や ALB の ID など)を dev, prod 配下の dashboard.tf から渡しています ダッシュボードをつくろう Terraform でダッシュボードを作るには aws_cloudwatch_dashboard を使います。 https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_dashboard resource "aws_cloudwatch_dashboard" "main" { dashboard_name = var.dashboard_name dashboard_body = jsonencode( { "start" : "-P4W", # 過去4週間 "periodOverride" : "inherit", "widgets" : [] # グラフの設定 }) } dashboard_name : ダッシュボードの名前 dashboard_body : ダッシュボードや表示するグラフを設定、JSON 形式 start , end : ダッシュボードで使用する時間範囲 periodOverride : ダッシュボードとグラフの時間範囲どちらを優先するか widgets : 表示するグラフの設定 dashboard_body の詳細はAWSのドキュメントにあります https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/APIReference/CloudWatch-Dashboard-Body-Structure.html periodOverride を inherit にしておくとグラフごとに自由に時間範囲を設定できます。 逆にすべて統一したい場合は auto にしておけばダッシュボードの時間範囲が優先されるのでそちらを設定しましょう。 グラフをつくろう グラフの設定は widgets に書きます。 例として、ECS の CPU Utilization のグラフを作るコードは以下のとおりです。 [ { "type" : "metric", "x" : 0, "y" : 0, "width" : 6, "height" : 6, "properties" : { "metrics" : [ [ "ECS/ContainerInsights", "CpuReserved", "ServiceName", "${var.api.ecs_service_name}", "ClusterName", "${var.api.ecs_cluster_name}", { "id" : "m1", "visible" : false, "stat" : "Maximum" } ], [ "ECS/ContainerInsights", "CpuUtilized", "ServiceName", "${var.api.ecs_service_name}", "ClusterName", "${var.api.ecs_cluster_name}", { "id" : "m2", "visible" : false, "stat" : "Maximum" } ], [{ "expression" : "m2/m1*100", "label" : "CPUUtilization" }] ], "period" : 300, "stat" : "Average", "region" : "ap-northeast-1", "title" : "API ECS CPUUtilization", "liveData" : false, "legend" : { "position" : "right" }, "yAxis" : { "left" : { "label" : "Percent", "showUnits" : false } } } } ] 何を設定するか上から見ていきましょうか。 x,y ではグラフをどの位置に配置するかの設定をしています。 [ { "type" : "metric", "x" : 0, "y" : 0, ... Terraformからダッシュボードを作る場合は、どこにウィジェットを配置するかを左上を (0, 0) として座標で設定することができます。デフォルトで次に使用可能な位置が使われるとあるため上から詰め込んで表示するのであればとくに気にする必要はありません。 グラフの設定については properties の metrics から設定できます。 このコードでは、 ECS/ContainerInsights から CpuReserved と CpuUtilized のグラフを取得、 expression で率を計算して表示しています。(計算に使った m1, m2 のグラフは "visible" : false, で非表示にしています) "properties" : { "metrics" : [ [ "ECS/ContainerInsights", "CpuReserved", "ServiceName", "${var.api.ecs_service_name}", "ClusterName", "${var.api.ecs_cluster_name}", { "id" : "m1", "visible" : false, "stat" : "Maximum" } ], [ "ECS/ContainerInsights", "CpuUtilized", "ServiceName", "${var.api.ecs_service_name}", "ClusterName", "${var.api.ecs_cluster_name}", { "id" : "m2", "visible" : false, "stat" : "Maximum" } ], [{ "expression" : "m2/m1*100", "label" : "CPUUtilization" }] ], ... } ちなみにこんなグラフも作れます。 しきい値を超えた場合赤く警告が出るゲージのグラフです。 レスポンスタイムのようなしきい値を超えた場合に分かりやすくして表示ほしいようなものにおすすめです。 [ [ "AWS/ApplicationELB", "TargetResponseTime", "LoadBalancer", "${var.api.load_balancer_id}", "TargetGroup", "${var.api.target_group_id}", { "id" : "m1", "visible" : true, "label" : "Average", "stat" : "Average", "color" : "#ff402e" } ], [ "AWS/ApplicationELB", "TargetResponseTime", "LoadBalancer", "${var.api.load_balancer_id}", "TargetGroup", "${var.api.target_group_id}", { "id" : "m2", "visible" : true, "label" : "p99", "stat" : "p99", "color" : "#ff402e" } ] ], "period" : 30 * 24 * 60 * 60, "view" : "gauge", "region" : "ap-northeast-1", "title" : "API ECS responseTime", "liveData" : false, "legend" : { "position" : "right" }, "yAxis" : { "left" : { "label" : "Seconds", "min" : 0, "max" : 1 } }, "annotations" : { "horizontal" : [ { "label" : "Target", "value" : 0.2, "color" : "#7bff71", "fill" : "below", } ] } "view" : "gauge", を指定することでこのグラフを作ることができます。 "color" : "#ff402e" がグラフのデフォルトの色です。なぜか赤を設定していますが理由は後述します。 ゲージの横にある小さいバーは水平注釈です。しきい値内の範囲を表しています。このグラフでは value で 0〜0.2 をしきい値内(正常値)として設定しています。 "annotations" : { "horizontal" : [ { "label" : "Target", "value" : 0.2, "color" : "#7bff71", "fill" : "below", } ] } color と fill でしきい値以下のときのグラフの色とどちらに出すかを設定しています。ここでは #7bff71 (緑色)と below を指定していますね。 "fill" : "below", でしきい値以下のときに color の色で塗りつぶすように設定しています。デフォルトは赤色、しきい値以下のときは緑に塗りつぶしてねとしているわけです。 「デフォルトは緑色、しきい値以上のときは赤色にするでもいいじゃん」となりそうなのですが、反対の above にすると水平注釈の位置が最大値側になったりと少し変に感じるのでこのようにしています。 まとめ 今回は線とゲージのグラフをお見せしました。もちろん線とゲージ以外の種類のグラフもTerraformから作ることができますのでぜひご自身の環境でも色々試してみてください! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
Notionを日々便利に使わせてもらっていますが、たまに感覚的に合わない挙動が行われることがあります。個人的によく遭遇するパターンについて回避策を書いていこうと思います テキストをコピペしたら1つのブロックになってしまった 回避策:Markdownで書いてから貼り付ける 回避策:Notion AIに頼む テキストをコピペしたら1つのデータベースアイテムになってしまった 回避策:Spredsheetなど表計算ソフトを中継してから貼り付ける 回避策:ブロックにしてからD&Dする 回避策:CSVとしてインポートする テキストをコピペしたら1つのブロックになってしまった 改行ごとにブロックにして欲しかったのに1つのブロックに書かれた文章になってしまうケース。 原因は貼り付けた内容がMarkdownとして認識されているからだと思います。 Markdownでは行末にスペース2つか空行を追加しないと改行だと認識されません。 通常であればスペースとして変換されるところを、ブロック内改行(Shift+Enter)にしているところを見ると、完全にMarkdownを再現しているわけではなく、Notionに合った形で変換しているのでしょう。 原因が推察できれば、おのずと回避策もわかってきます。 回避策:Markdownで書いてから貼り付ける ワンステップでできないのが歯痒いのですが、現状これが早い解決策だと思います。 エディタで行末にスペース2つ付けるだけですし(正規表現で $ に対して処理)、下書きファイルの拡張子をmdにしておけば拡張機能の設定によっては自動で付けてくれます。 貼り付ける際に内容によってはコードブロックにされてしまうので、ペーストしてスタイルを合わせる(Ctrl+Shift+V)を利用しましょう。 ちなみにSpredsheetを中継する技は今回は使えません、行ごとに水平線が差し込まれます。 回避策:Notion AIに頼む Notion AIに頼めばブロックに分けて貼り付けてくれます。 コードブロックにしたい内容も改行されてしまうのではないか、と思われるかもしれませんがちゃんと認識してコードブロックに変換してくれました。 テキストをコピペしたら1つのデータベースアイテムになってしまった 改行ごとに別アイテムにして欲しかったのに1つのデータベースアイテムのタイトルになってしまうケース。 リッチテキストエディタを採用しているツールは多々ありますが、テーブル表示のコピペ挙動はそれぞれ違っていることが多く、Notionデータベースも同じ。これに関しては挙動から回避策を探っていくしかないです。 こちらの回避策は複数あるものの、残念ながらどれもワンステップでできません。 回避策:Spredsheetなど表計算ソフトを中継してから貼り付ける 貼り付ける数が多くなければ体感一番早いのはこの方法です。 複数プロパティでもそのまま貼り付けられるので汎用性が高いです。 貼り付けたい順番とプロパティの順番が異なっていたら、一時的ビューを作るなどして貼り付けたい並びに調整しましょう。 回避策:ブロックにしてからD&Dする データベースアイテムもページ、もっと言えばブロックなので、ブロックをD&Dすればいいという理屈です。 上に書いた方法でテキストを改行ごとのブロックにした後に、データベースへD&Dすればデータベースアイテムとして扱われます。 回避策:CSVとしてインポートする 貼り付ける数も多くて、プロパティもしっかり入れたい時はCSVインポートが適切です。 データのインポート – Notion (ノーション)ヘルプセンター 1行目に書くプロパティ名を間違えると、新規プロパティとして作成されてしまうので注意が必要。 重要なデータベースにインポートしたい場合は、一度新規データベースとしてインポートしたあとに移動させるといいでしょう。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
こんにちは。会員システムグループでエンジニアをしている山田です。 今回はterraformの注意点について紹介します。 概要 terraform import はterraformを使っていなかった頃のリソースを取り込むためのものであり、定常運用で使うものではないという話です。 terraformで運用するならterraformだけで完結させてください。importは常用するための機能ではありません。 importの限界 importを前提とした運用には以下の問題があります import漏れ コンソールで作成したものを後からimportする、という運用では、作られたリソース全てがterraform管理下にある、という保証ができません。 コンソール上でよしなに作成されたIAM RoleやS3 Policy、Route53レコードなどを見逃してしまい、terraform管理外だから不要だと思って消したら実は必要だった、というような事態が発生します。 再現性がない 1から実行されたことのないterraformコードには再現性がありません。 例えば暗黙的な依存関係にあるInternet GWとNAT GWは resource "aws_internet_gateway" "igw" { ... } resource "aws_nat_gateway" "ngw" { ... depends_on = [aws_internet_gateway.igw] } のように depens_on を明示しないと実行に失敗しますが、importではこれに気づくことができません。 importブロックが使えないことがある terraform 1.5よりimportブロックが使えるようになりました。 これはimportの内容をtfファイルに記述できるものであり、コマンド実行前にplanができることでterraform importコマンドを使うより安全な操作が行えます。 しかしながら、AWS APIが非対称なケース、つまりPOSTで設定した内容と、GETで帰って来る内容が異なるようなリソースでは実行に失敗します。 ex) aws_lb_listener の default_action resource "aws_lb_listener" "https" { ... default_action { type = "forward" target_group_arn = aws_lb_target_group.a.arn } ... } Planning failed. Terraform encountered an error while generating this plan. ╷ │ Error: Invalid Attribute Combination │ │ Only one of "default_action[0].target_group_arn" or "default_action[0].forward" can be specified. │ default_action[0] │ │ with module.ecs_front.aws_lb_listener.https, │ on ../../modules/ecs_front/lb.tf line 41, in resource "aws_lb_listener" "https": │ 41: resource "aws_lb_listener" "https" { │ 上記例では default_action[0].target_group_arn のみを指定しようとしていますが、AWS APIのGET結果は default_action[0].target_group_arn と default_action[0].forward が両方指定された状態で返ってきてしまいます。両方指定された状態をterraform planは許容しないのでエラーとなります。 terraform importコマンドを使えばplanを実行することはないため、失敗することはなくなります。ただしimport後にchangedが出てしまうことがあります。 不要なchanged terraform importしたリソースは、 再度plan・applyすると必ずchangedが出る ものがあります。 これは主にAWS上に存在せず、tfstate上にのみ存在する値があることによって発生します。 ex) aws_ecs_service の wait_for_steady_state など、terraform自体の実行を制御するパラメータ ex) sensitive = true の設定などにより、terraform上でsensitiveとして扱うべきパラメータ これらがあるため、「importした後にchangedが出なくなるようにコードを編集すればOK」とはいきません。特に前者は tfstateを直接編集しないとapplyすらできない というケースも存在します。 import不可リソース 稀ですがimportが不可能なリソースも存在します。AWS Providerの場合はドキュメントにimportできない旨の記載があります。 ex) aws_lb_target_group_attachment これはimportができないため、applyしてみるしかありません。 (上記の場合、全く同じ設定でapplyすればエラーなく成功します) どうすればよいのか コンソール作業をやめて terraformですべて作るようにしましょう 。 一度コンソール作業を行わないとどのようなリソースを作ればよいのかわからない、という場合は以下のような手順にします。 コンソール上でお試しリソースを作り、動く状態にする このとき、リソース名は本番用とは別の名称にする 同等の構成を再現できるようにterraformコードを書き、applyする こちらのリソース名は本番相当とし、お試し用とは被らないようにする お試しリソースを手動で全削除する 動作検証を行う Terraformで0から作成できる状態を担保し、コードが信頼できる状態を保つようにしましょう。importは最後の手段です。 今回はTerraformの適切な使用方法と、Terraform inportの限界について紹介しました。 Terraformを利用する際はぜひこれらの注意点を念頭に置いて実践してみてください。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは! ニフティ株式会社、エンジニア定例運営の小林、柴田です。 ニフティでは毎年エンジニアの新人研修を先輩エンジニアが 内製 で行う文化があります(通称、 エンジニア定例 と呼ばれています)。 開催期間としては短期集中的に6月(一部5月)に実施し、準備は2か月前後で行います。 前年より資料の一般公開に取り組んでおり、今年も同様に一般公開いたします。 – 前年: ニフティ株式会社 エンジニア新人研修の内容を公開します | 2023年度版 新人研修の狙い ニフティ全体の技術力向上 以下を経験することで社内全体の技術力向上を目的とし、エンジニア定例の目指すところとしています。 体系立った基礎学習 チームでのサービス開発 短期的なサービス運用 生徒から講師による継続的運用 内製力の強化 育成強化 Webサービスを開発する上で必要なスキルを体系立てて学んでもらう スキルアップ 業務では学べない技術を積極的に取り組んでいく 今回の公開講義資料について 研修の到達目標として、「Webアプリケーションを独力で開発、クラウド上でリリースできるレベルの知識、技術力を身に付ける」を掲げており、それに伴う講義を行っています。 今年は新しく「サーバー運用入門」が追加され、「機械学習」については近年のトレンドを加味し「生成AI」に着目した内容に刷新しております。 それでは、以下公開可能な講義資料を掲示しますので、学習にお役立てください。 講義資料一覧 Git2024 AWS2024 [NEW!] サーバ運用入門2024 コンテナ2024 データベース2024 セキュリティ2024 オブジェクト指向2024 機械学習2024 Webアプリ2024 モバイルアプリ2024 ※ニフティ社内の開催日順で記載しています。 終わりに ニフティでは、長年にわたり自社開発の研修プログラムを実施しています。 このプログラムは、継続的な改善と進化を遂げており、教材の更新や新しい講座の導入、さらには前年度の新入社員が翌年には講師を務めるなど、独自の取り組みを行っています。 この制度の大きな利点は、 受講者としての経験を講師の立場で活かせること 、と感じています。 私自身、過去の受講で気づいた改善点を教材に反映できるだけでなく、講義後のフィードバックを通じて自己成長の機会を得られています。 この循環型の学習システムは、個人のスキル向上と同時に、研修プログラム全体の質の向上にも貢献しています。  弊社のエンジニア育成への取り組みや、最新の技術動向について詳しく知りたい方は、ニフティエンジニアブログをぜひご覧ください。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
チームの今後について 現在のチームの課題と、それに対する取り組みについてお聞かせください R.Fさん 私たちのチームが持っているシステムの数が膨大な為、知識の吸収やメンバーの作業リソースが追い付いていないのが課題ですね。さらに、レガシーなシステムも扱う中で勤続年数が5年目以下の社員しかいないのが現状です。 なので、そもそもチームでの扱う必要があるシステムなのか、維持する必要があるのか、といった前提から考えていくような提言を現場からしていけるようになっていきたいです。 お金を使ってメンバーやリソースを増やして解決したとしても、それって課題をすり替えてるだけで、後々より大きなツケを払わないといけなくなるんですよね。なので現場の課題を組織全体の課題に昇華させる必要があります。そういった現場から組織に働きかけるような取り組みをしていきたいです。 M.Mさん やっぱりレガシーシステムの刷新や取り扱いは課題として上がりますね。 R.Fさんが言っていたようにサービス数が膨大なので管理が大変ですし、言語や扱う分野もバラバラなので難しいです。全部をPythonに当てはめることは難しいので、リプレイスをする際には適切な技術選定をしていく必要もあります。 また、お客様のお金を扱うサービスでは強固なセキュリティの担保が必要なのでそういった点についても考えながら刷新に取り組んでいます。 S.Oさん M.M君と似た話ですが、チーム内にはレガシーなシステムが複数存在し、ものによってはブラックボックスになっているものもあるので、拡張や運用する際の障害になるという課題があります。 何も考えずに最新技術を取り入れるだけだと将来の負債にもなりえるため、そのシステムにあった最適な技術を取り入れていきたいです。 課題は山積みですが、今できる工数削減などを実践してチームの時間を増やす取り組みをしています。具体的には、追加機能が作られた際には都度テストの工数がかかっていたのですが、一部を自動化することで他のやりたいことに時間をさけるようにしました。 見えるところからコスト削減をしていって、課題に向き合う時間を増やしていきたいです。 休日の過ごし方について お休みの日はどんなことをされていますか?趣味やマイブームについて教えてください S.Oさん 私は旅行や音楽鑑賞ですね。 先週は金沢で海鮮料理を楽しんだり、21世紀美術館で作品に触れたりしていました。朝の散歩がてら兼六園に行って落ち着きながら回ることが出来たのも良かったです。 また、R.Fさんに影響されてアーキテクチャやクラウドに関する勉強をしています。最近はSQSやLambdaの並列化などを勉強してチームに還元できたので達成感がありました。 M.Mさん YouTube見ることがなんだかんだで多くなっています。スポーツ全般見るのが好きで、特にサッカーは一番です。最近は国立競技場でのJリーグを試合を観戦しに行きました。 自主学習では幅広く勉強しており、特にセキュリティに関する勉強をしています。ニュースを追ったり、効率的なソフトウェア管理方法を模索していたりします。 R.Fさん スクラム系であったり、組織に関する本を読んでいます。 今はニフティのN1!制度の中でアジャイルコーチを任命されているのですが、スクラムに触れた当初はスクラムのことを信じ切れていませんでした。 スクラムに限った話ではないかもしれないですが『導入すればうまくいく』みたいなことに懐疑的で、今までと違うやり方を導入していこうとしたらどこかにしわ寄せは出てしまうはずなのに、いろんな事例とかを見てもなかなかそういう話って書いてなくて、違和感を感じていました。 組織で仕事をしているので一部の人だけ楽しんでる、みたいなことは避けたくて、スクラムに関する書籍を読んだりやイベントに参加してきて学びを深めています。そのおかげで最近はスクラムへの考え方が変わってきました。 最近読んだ本の中では「ワインバーグのシステム思考法」「学習する組織」「大規模スクラム Large-Scale Scrum(LeSS) アジャイルとスクラムを大規模に実装する方法」が印象に残っています。 ニフティのN1!制度について このインタビューに関する求人情報 /ブログ記事 課金システムチームの求人情報 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する .is-style-rounded + .has-text-color{ font-size:95%; }
自己紹介 R.Fさん ■入社年度 2020年4月 ■主担当サービス チームの業務全般 ■趣味 スクラム関連の本を読む S.Oさん ■入社年度 2021年4月 ■主担当サービス 課金系/書面発送/サービス申込サービスの開発・運用 ■趣味 旅行/音楽フェス M.Mさん ■入社年度 2021年4月 ■主担当サービス 課金系/書面発送/サービス申込サービスの開発・運用 ■趣味 動画鑑賞/サッカー観戦大河ドラマ鑑賞/囲碁 課金システムチームとは 課金システムチームとはどういったシステムを管理しているチームですか R.Fさん ニフティの回線をお客様に契約を申し込んで頂く所から、ご利用料金の請求まで様々なシステムを取扱っています。 一例ですが、回線サービスの申し込み管理システムであったり、申込サービス内容や取り扱い説明書の書面データを発送するシステム、ご利用料金の決済システムなど様々なシステムを取扱っています。 PPPoE 固定IPオプション https://csoption.nifty.com/staticip/pppoe/ @nifty MAX光 https://setsuzoku.nifty.com/max/ その中でも、皆さんはどのようなシステムを担当されていますか R.Fさん メンバー全員でチームが抱えているシステムの面倒を見れるようにしてます。 というのも、うちのチームでは古くから扱っているシステムがあってそれぞれ担当者がいたんですけど、その担当者が離れた途端にそのシステムについて理解している人がいなくなってしまうといった過去があったんです。 そういった経験から、属人化を防ぐためにチームの方針として新旧のシステム問わず知見をメンバーに共有することを徹底して、全員の経験値が平等になるようにしています。 メンバーへの知見共有はどのように行われているのですか S.Oさん 自分たちはスクラム開発を取り入れていて、全員に対して課題報告や知見共有のタイミングがあるので、そういった時間で話をしています。 加えて、一つのタスクに対してもチーム全体で関わるようにしたいのでモブプログラミングを取り入れて、複数人で作業することを意識したりしています。 M.Mさん 成果物がある程度できたところで共有会を欠かさず実施したり、議論が必要な部分では全員で話し合うことを意識しています。 タスクのチケット管理をしていて、そのチケットの中に残すべき証跡はしっかり残すようにして貰ったりしていますね。形にすべきものはしっかりドキュメント化するように各メンバーが意識するようにしています。 なんにせよ、担当している人しかわからない。属人化を防がないといけないので何かは残してね。という状況にしていますね。 R.Fさん 長く使われているシステムをアップデートするためにレポジトリを確認してみたら、コメントが何もなくてコミットメッセージが少しあるだけで意図が分からなかったりして手がかかってしまったことがあったんですよ。 目の前のタスクを早くこなすことも勿論大事ですが、5年後10年後そのサービスを扱う人を考えてものづくりをする必要があると考えています。 業務内容について 業務で扱っている技術について教えてください M.Mさん 基本的にはバックエンド系の開発ではPython,Docker、クラウドではAWSを主に使っております。 既存システムの調査ではGo, PL/SQL, Java, C, JavaScript, Perlなどを見ますね。ちょっとした確認程度も含めて色々な技術に触れる機会があります。 S.Oさん 新しく触ったりする技術や機能は基本的にメンバー間で知識が偏らないように進めています。 最近では監視設定を導入する際に、メンバーが新機能を見つけては共有会などを開いたりしていました。 業務時間内で調査する時間を意識的に取って、メリットデメリットを共有したりなどを話し合って技術選定をしています。 R.Fさん S.O君やM.M君がよく便利な機能を見つけて提案してくれていてとても助かっています。 今まで取り扱っていたシステムは社内向けのAPIだったため、厳密な性能を求められていませんでした。そこからお客様が見るデータを取扱うように切り替わったので、監視の徹底やAPIの安定稼働をシステム的に達成するためにAWSのApplication Signalsを導入しました。 S.Oさん 新しい技術の導入は、以前の開発案件で上がった課題を基に考えることが多いですね。 M.Mさん 雑談から「ここの部分まだいけてないよね」とか共有したり、話をしていく中で「その手があったか!」みたいな気付きもあって、そこから課題の共有や発見、解決に繋がったりしています。 業務で苦労した、頑張った案件や経験はどういったものがございますか R.Fさん 私がサブチームリーダーになる前のチームは、個人の開発力でタスクをこなしていく風潮が強かったんです。なので「チームで開発を行っていく」という意識を定着させることに取り組みました。 開発に関する課題を開発力のある人が解決してくことで、仕事が回る場面も勿論ありました。そうすることで新しい技術をチームに取り入れる機会もあったんですが、全員がその技術を扱えるわけではなかったり、長い目で見た時のパフォーマンスやチームの再現性に繋がらない状況でした。 このままでは将来を見越した開発という観点で安心できないと考え、チームビルディングを考えるようになりました。 タスクの大小に関わらず『チームとしてどうすべきか』という観点で話し合い、チーム全員で仕事の進め方を決めるようにしています。個人ではなくチームとして活動する意識を日頃から持つことを意識しています。普段の声掛けやコミュニケーションや打ち合わせなどでこういったイメージを持ってもらうようにしています。 S.Oさん 私は他チームが持っていた請求系システムを刷新したことが一番記憶に残っています。レガシーなシステムだったので構造を理解するのに苦労はしましたが、請求系のドメイン知識や、PL/SQLに関する知識が付きました。 また、リプレイス先の技術選定については、前プロジェクトで似たような構成でシステム刷新したことから、同様にAWSを採用しました。 以前開発したシステムに関する課題の解決策を考えて適応することで、アーキテクチャや監視、テスト周りをより良いものに作ることができ、結果としてシステム全般に関する面でチームとして成長できたという実感があります。 M.Mさん 細かい機能追加ももちろん頑張っていますが、なんだかんだで毎年一件ぐらい新規開発案件があって、そこでは特に気合を入れて頑張っています。先ほどのS.Oさんの話にもあったように前回の開発の経験から反省を生かしてより良いシステムを作る雰囲気がありますね。 同じ様な構成のシステムであったとしても、クラウドが持つ便利な機能を新たに取り入れたり、システムごとのやりたいことを考えて構築しています。より良くできたら達成感がありますね。 最近はこういった経験から、ただ作るだけでなく運用の負担を軽くするために何が出来るかを考えられるようになってきたのでチームとして良い傾向だと感じています。 後編に続きます! 今回はニフティの課金システムチームのインタビューの様子をお届けしました。続きは後編の記事をご覧ください。 【インタビュー】ニフティの課金系システムチームに聞く!チームの課題と余暇の過ごし方【課金システム後編】 このインタビューに関する求人情報 /ブログ記事 課金システムチームの求人情報 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する .is-style-rounded + .has-text-color{ font-size:95%; }
はじめに こんにちは。ニフティ株式会社の添野 隼矢です。 Vitestの Browser Modeが興味深いと聞き、実際に試してみようと思い、 本ブログ記事を書くことにしました。 以下、Vitestの公式ガイドの「Getting Started」に従って、Browser Modeを試していきます。 https://vitest.dev/guide/browser/ 今回は、以下の項目でテスト環境を作成します。 テスト言語:TypeScript ブラウザプロバイダ:Playwright ブラウザ:Chromium フレームワーク:Svelte $ pnpx vitest init browser This utility will help you set up a browser testing environment. ? Choose a language for your tests › - Use arrow-keys. Return to submit. ❯ TypeScript - Use TypeScript. JavaScript Choose a language for your tests › TypeScript ? Choose a browser provider. Vitest will use its API to control the testing environment › - Use arrow-keys. Return to submit. ❯ playwright - Playwright relies on Chrome DevTools protocol. Read more: https://playwright.dev webdriverio preview Choose a browser › chromium Choose your framework › svelte Install Playwright browsers (can be done manually via 'pnpm exec playwright install')? … yes Installing packages... @vitest/browser, @testing-library/svelte, playwright, @sveltejs/vite-plugin-svelte Packages: +111 -3 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- Progress: resolved 208, reused 164, downloaded 7, added 111, done node_modules/.pnpm/msw@2.4.1/node_modules/msw: Running postinstall script, done in 1.5s devDependencies: + @sveltejs/vite-plugin-svelte 3.1.2 + @testing-library/svelte 5.2.1 + @vitest/browser 2.0.5 + playwright 1.46.1 Done in 5s Created a config file for browser tests vitest.config.ts Added "test:browser" script to your package.json. Installing Playwright dependencies with `pnpx playwright install --with-deps`... Packages: +3 +++ Progress: resolved 3, reused 3, downloaded 0, added 3, done Downloading Chromium 128.0.6613.18 (playwright build v1129) from https://playwright.azureedge.net/builds/chromium/1129/chromium-mac-arm64.zip 138.1 MiB [====================] 100% 0.0s Chromium 128.0.6613.18 (playwright build v1129) downloaded to /Users/Library/Caches/ms-playwright/chromium-1129 Downloading FFMPEG playwright build v1009 from https://playwright.azureedge.net/builds/ffmpeg/1009/ffmpeg-mac-arm64.zip 1 MiB [====================] 100% 0.0s FFMPEG playwright build v1009 downloaded to /Users/Library/Caches/ms-playwright/ffmpeg-1009 Downloading Firefox 128.0 (playwright build v1458) from https://playwright.azureedge.net/builds/firefox/1458/firefox-mac-arm64.zip 79.6 MiB [====================] 100% 0.0s Firefox 128.0 (playwright build v1458) downloaded to /Users/Library/Caches/ms-playwright/firefox-1458 Downloading Webkit 18.0 (playwright build v2051) from https://playwright.azureedge.net/builds/webkit/2051/webkit-mac-13-arm64.zip 68.3 MiB [====================] 100% 0.0s Webkit 18.0 (playwright build v2051) downloaded to /Users/Library/Caches/ms-playwright/webkit-2051 Add "@vitest/browser/providers/playwright" to your tsconfig.json "compilerOptions.types" field to have better intellisense support. Created example test file in vitest-example/HelloWorld.test.ts You can safely delete this file once you have written your own tests. All done! Run your tests with pnpm test:browser pnpm run test:browser を実行すると、 @sveltejs/vite-plugin-svelte パッケージがES module (ESM)であるのに対し、 CommonJSのコンテキストでインポートしようとしているとエラーが出ます。 そのため、package.jsonに以下を追加します。 "type": "module" package.jsonの修正後、再度実行すると新たなエラーが発生します。 表示されたエラーは、Svelteコンポーネント内でTypeScriptを使用しているにも関わらず、 TypeScriptのプリプロセッサが設定されていないというものになります。 そのため、以下の内容のsvelte.config.jsを追加します。 import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; const config = { preprocess: vitePreprocess(), }; export default config; svelte.config.jsを追加後、再度 pnpm run test:browser を実行したところ、新たなエラーが発生しました。 表示されたエラーは、プロジェクトにSvelteパッケージが導入されていないことを示しています。 そのため、Svelteを追加します。 $ pnpm add svelte 再度実行すると、ブラウザが正常に立ち上がり、テストが正常に実行されていることを確認できました。 $ pnpm run test:browser RERUN x1 ✓ vitest-example/HelloWorld.test.ts (1) ✓ renders name Test Files 1 passed (1) Tests 1 passed (1) Start at 17:20:25 Duration 13ms PASS Waiting for file changes... press h to show help, press q to quit 次に、Vitestの動作をより深く理解するために、initで自動生成されたコードを以下のように修正してみます。 最初は何も表示されていない画面を表示し、1秒後に “Hello Vitest!” というテキストが表示されるような処理に変更 HelloWorld.svelte <script lang="ts"> export let name: string </script> <h1>Hello {name}!</h1> HelloWorld.test.ts import { expect, test } from 'vitest' import { render } from '@testing-library/svelte' import HelloWorld from './HelloWorld.svelte' test('renders name', () => { const { getByText } = render(HelloWorld, { props: { name: 'Vitest' }, }) const element = getByText('Hello Vitest!') expect(element).toBeInTheDocument() }) 修正後のコード HelloWorld.svelte <script lang="ts"> import { fade } from 'svelte/transition'; export let name: string; let visible = false; setTimeout(() => { visible = true; }, 1000); </script> {#if visible} <h1 transition:fade>Hello {name}!</h1> {/if} HelloWorld.test.ts import { expect, test } from 'vitest' import { render } from '@testing-library/svelte' import HelloWorld from './HelloWorld.svelte' test('shows greeting after delay', async () => { const { getByText } = render(HelloWorld, { props: { name: 'Vitest' } }) // 最初はHello Vitest!が表示されていないことを確認 expect(() => getByText('Hello Vitest!')).toThrow() // 遅延後にHello Vitest!が表示されることを確認 await new Promise(resolve => setTimeout(resolve, 1100)) expect(getByText('Hello Vitest!')).toBeVisible() }) テストを実行した結果、以下の動作が確認できました。 1. テスト開始直後:画面上に何も表示されない。 2. 1秒経過後:画面に “Hello Vitest!” というテキストが表示される。 上記より、以下の点が確認できました。 1. Vitestが1秒の遅延を正確に処理し、その後の状態変化を適切にテストできていること。 2. 実際のブラウザに近い環境でコンポーネントが正しくレンダリングされ、DOMの更新が適切に行われていること。 3. 意図した動作(最初は何も表示せず、1秒後に特定のテキストを表示する)が正確にテストされ、期待通りの結果が得られること。 この結果から、Vitestの Browser Modeが非同期処理を含むUIコンポーネントのテストに効果的であることが分かりました。 特に、時間に依存する動作や状態変化を伴うコンポーネントのテストにおいて、Vitestが優れた能力を発揮することが確認できました。 VitestのBrowser Modeの機能を試していく過程で、Playwrightでよく使用していたスクリーンショット機能を試してみました。 await page.screenshot({ path: 'screenshot.png' }) しかし、上記をテストコードに追加して実行したところ、 page オブジェクトでエラーが発生しました。 上記のエラーより、Vitestの環境ではPlaywrightの page オブジェクトが直接利用可能ではないことが分かりました。 まとめ VitestのBrowser Modeは、公式には執筆時点ではExperimentalの機能になります。 上記のスクリーンショットの件などもあり、Playwrightの直接的な代わりにはならなさそうですが、単純なコンポーネントテストであれば、Browser Modeも考慮にいれてよいかもしれません。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに N1! 制度 でスクラムエバンジェリストを担当している西野です。 8月にCTIでCo-Avtiveコーチングの研修をうけてきたのですが、スクラムマスターにかなりフィットするスキルだなと思ったのでレポートしてみたいとおもいます。 コーチングに興味を持ったきっかけ 数年前、スクラムマスターについてより深く学ぶ「アドバンスドスクラムマスター」の研修を受けた際、コーチングをやってみるワークがありました。 スクラムチームの成長を助ける方法として良さそうだと思ったので、その研修以来1on1の場を通じてメンバーにコーチングをやっています。 スクラムチームに対しコーチングを始めたころは手応えを感じていたのですが、時間が経つにつれて 「恣意的に会話誘導しているかも…」 「アドバイスをしてしまっていないか?」 「1on1を終えたメンバーに活力を与えられているのだろうか?」 といった自身のスキルの限界を感じるシーンが増えてきて、一度きちんと学びたいという気持ちが膨らんできました。 その時ふと、「コーチング研修を受けて良かった」と言っていた人がいたことを思い出し、コーチング研修に興味を持つようになりました。 なお、ニフティのN1! ではこういった研修などに使える活動予算が年間100万円(!)もあるので、その予算を使わせてもらっています。 スクラムマスターはコーチング研修をうけるべき? これはかなり食い気味に「YES」です。 スクラムガイドにも ⾃⼰管理型で機能横断型のチームメンバーをコーチする。 組織へのスクラムの導⼊を指導・トレーニング・コーチする。 https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf とコーチングへの言及があります。 ティーチング(教える)ことは学校や会社で経験していますが、自分の場合きちんとしたコーチングを受けた経験はありませんでした。 スクラムマスターに必要なスキルである「コーチング」を、頭ではなく体で覚える経験は、なかなか他の手段で代替が効かないものだと思います。 そもそもコーチングってなに? 一般的にコーチというと、スポーツのコーチというイメージが強いと思います。 自分の場合コーチングについて学び始める前は、コーチは「プレイヤーになにかアドバイスをする人」または「練習をさせる指導者」というイメージを持っていました。 スクラムガイドにある「コーチ」や、ここでいう「コーチング」は上記とは異なります。 「アドバイスをする」「練習などの行動を指示する」のはコーチングではなくティーチングと呼ばれるスキルです。 コーチングは「気持ちを上向かせる」「相手の成長をサポートする」、要は「他者の在り方を支援」をすることに特化したスキルだと今は思っています。 (念の為、近年のスポーツのコーチは、いわゆる自己成長を促すコーチング要素が多くなっていると聞いたことを付け加えておきます) コーチング研修を受けてみてどう変わったか 研修の詳細を書くことはできないのですが、もし書けたとしても「体験しないと伝わらないな」という内容が多かったので、研修を受けて気付いたことを中心にまとめます。 話題にフォーカスして人を見ていなかったと気づけた 研修をうけて一番衝撃が大きかったことは、これまでやってたつもりの「傾聴」が全然できていなかったことでした。 傾聴のつもりで自分がやっていたのは、「相手の話の内容をちゃんと理解して、次に何の質問をしようか一生懸命考えている」という状態でした。 相手の会話にどう切り込もうか考え込んでいるうちに、相手が何の話をしているかわからなくなってしまうことは普段の経験でもあるのではないでしょうか。 話の内容を深く理解することと、相手を知ることは必ずしもイコールではありません。 ほかに、メモをとりながら1on1をしていたことも、話の内容ばかりに目を向けてしまって相手を見ないことに繋がってしまっていたかもと思うようになりました。 相手の表情や声のトーン、体の動きをよく観察することも含めて傾聴です。 今までの1on1の場で、相手に意識がむいていない瞬間がどれほど多かったか気付かされました。 コーチングは異空間 「傾聴」をはじめとして、普段の会話とコーチングの対話には大きな差があります。日常会話ではまずしないな、と思うような質問をあえてすることが相手の新たな気づきに繋がります。 コーチングを日常の会話と同じ流れでやってしまうと、相談会や愚痴の発散で終わってしまいがちです。 「ここはコーチングの場だ」という違いを、コーチだけでなくコーチをうける側(クライアント)にも強く意識してもらう必要があります。 実際、研修の理解を深めるためにコーチングをやってみたときに、コーチングの場であることをクライアントが意識できず失敗してしまったことがあります。 練習として家族にコーチングをしてみたのですが、「コーチングの練習をしたい」としか伝えておらず、コーチングとは何か、この場をどうしていきたいかという前提の会話をしなかったために「何回も同じような質問をされて嫌な気持ちになった」というフィードバックをもらってしまいました。 家族間なので正直にフィードバックがもらえてまだよかったのですが、今までチームメンバーに対してこういうコーチングをしていたのかもしれないと思うと怖くなります。 効果的にコーチングを行うための聞き方や質問の仕方は日常会話とはかなりかけ離れています。研修中、飛躍した提案や、考えたこともないようなことを聞かれてクライアントとしてドキッとするシーンもありました。 研修を受けてみると、その「ドキッとする」気持ちは自分が思い悩むだけでは出ない答えに繋がることが体験できます。 一人で考えていると、どうしても思考の範囲が狭くなってしまいます。一見突拍子もなく感じる質問が、それを打破するきっかけになるのです。 このように「相手をドキッとさせるかもしれない」ような質問をする場でもあるので、受ける側とどんな場をつくっていくのかを擦り合わせないと、場が成立しないばかりか、ストレスを感じる方向に働いてしまう可能性があることにも気づけました。 スクラムマスターとしてどうコーチングを行うか 最後に、コーチングの研修をうけてみて自分がスクラムマスターとしてチームに接する上で変えようと思ったことを挙げていきます。 自律には「自分の価値観」が必要 スクラムマスター同士で集まると「どうやったらチームの自律を促せるのか」という話題があがることが過去何回かありました。 自律を養うためには「自分で決定、行動すること」を繰り返す必要があると思っていますが、そもそも決定することが苦手という人もいるでしょう。 決定は得意という人でも、決めたことを必ずやり切れる人は滅多にいません。 人が意志決定する重要なファクターとして、自分の「価値観」があります。 どれだけ世の中的に良いとされていても、自分にとって価値があると思えないことを無理に決定してしまうと、モチベーション持てずにやりきることができません。 仕事における「自分の価値観」が見つかるようチームをサポートする 仕事のフレームワークはさまざまにありますが、スクラムは「価値観」に言及していることが特徴的だと思います。 スクラムがうまく機能していないときは、スクラムにおける価値基準と、その人の仕事の価値観を照らし合わせることをしていないのではないか、と気づきました。 しかし、働くうえで何を大事にしているかをぱっと答えられる人はそれほど多くはないでしょう。自分も改めて考えると言語化が難しいです。 スクラムの価値基準は「確約」「集中」「公開」「尊敬」「勇気」ですが、その価値はわかりつつも、チームメンバーの価値観がスクラムの価値基準とどの程度マッチしているのか、あまり向き合ってこなかったと思います。 スクラムマスターとして、まずチームメンバーそれぞれが「自分の価値観」が見つけられるようサポートする必要があるのではないか?と思い至りました。その手段としてコーチングがあります。 「自分自身」に向き合うことは、多くの人が無意識に避けている部分です。 コーチングの時間を持つことで、意図的に「自分」や「自分の価値観」に向き合ってもらうことができます。 スクラムマスターは、そのときコーチとして「自分」に向き合うことの抵抗感をやわらげ、自分自身と向き合うことから逃げないように導くことで、指示以外の方法でその人の行動を変容させることができるのではないかと思えるようになりました。 過去ではなく未来に目を向けさせる 研修前にコーチングを意識して指示ではなく質問をするよう心がけていた時期もありますが、「なんでこのアクションになったの?」「なんでこの決定になったの?」という「WHY」の問いかけをしてしまうことがありました。 これは質問される方はもちろん、質問をする側も息苦しく、これを繰り返しても安心できるチームになりそうなイメージがわきませんでした。 今なら、この問いが効果的でない理由がわかります。未来に焦点を当てた問いかけではないからです。 もし同じことを問うなら「このアクションをすることで、何を得たい?」「この決定をすることはこの先どんな影響がある?」というような質問に変えると思います。 スクラムマスターとして、変えられない過去に焦点を向けるより、変えられる未来の行動に焦点をあわせてもらうほうが、よりスクラムチームの成長をサポートしやすいからです。 自分に向き合い、変化し、成長するために 私はコーチングの研修をうけてみて「スクラムチームが無意識のうちに逃げていることに対面させ、行動の変容を促し、成長を導く」ということにもっとチャレンジしたいと思うようになりました。 コーチングを通じてチームメンバーが自身の価値観を見つけ、自律的に行動できるようサポートすることは、スクラムチーム全体の成長に繋がります。 もうひとつ重要な気づきがあります。スクラムマスター自身が肉体的・精神的に元気で前を向ける状態にないとコーチは難しいということです。 自分のことでいっぱいいっぱいのときは、他人に目を向けられません。他人をみれないときは、スクラムマスターとして十全には振る舞えません。 チームメンバーが価値観に向き合うよう促すと同時に、スクラムマスターである自分自身の価値観にも向き合っていく必要性を感じています。 この自分自身と向き合う手段として、外部のコーチをつけてみるという選択肢があることを知れたのもよかったです。 さいごに コーチングの基礎研修を受けた後のアクションとして、コーチングの学習・実践をより深めていく方向もあったのですが、今はまず自分の価値観を知る方向でアプローチしていこうと考えています。 誰の人生においても、自分とどう向き合って生きていくかは大きな課題として立ち塞がります。 スクラムマスターは、スクラムチームや組織など外側に目を向けている時間が長いです。そんな中で、スクラムチームや組織の中に自分がいることを忘れていた気がします。 コーチングの学習を通じて、他者の成長を促すだけでなく、スクラムマスターである自分自身と正しく向き合おうという視点が持てたことは大きな変化でした。 記事の最初に「スクラムマスターはコーチングの研修を受けるべき」と書きましたが、これはチームのためだけでなく、スクラムマスターの自己管理のためでもあります。 スクラムマスターが成長する一つの手段として、コーチングの研修を受けてみるのはかなり効果的だと思いました。 今は、社内にコーチ制度を導入するために、コーチングに興味がある人を集めてコーチングの価値を広められるよう頑張っています。なにか実を結んだら、この取り組みも記事にしたいと思います。お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! テーマ 天気予報があなたの手元に届けられるまで   〜今日傘を持って出掛けることができるための仕組みの裏側〜 2008年に開始した@nifty天気予報がついにリニューアル。その開発のウラ側をお話ししていきます。 また、今回はコラボ企画となっており、ウェザーニューズ様とコラボします! リニューアルで使用したNext.js、Go、GraphQLでどのように開発していったのか、気象データを提供する側の仕組みなどをウェザーニューズとニフティの技術者が紹介していきますので、是非ご参加ください。 今回はハイブリッド開催となっております。 お時間やご興味のある方は是非新宿まで足をお運びください! 開催概要 日程:10月2日(水)19:00〜20:30 ハイブリッド開催 現地参加: ニフティ 新宿本社 来場予定で19:30以降に来られる方は「 @NIFTYDevelopers 」からご連絡お願いします オンライン参加: YouTube Live 登録はこちらから https://nifty.connpass.com/event/328943/ こんな方におすすめ 以下について興味があるかたにおすすめです Next.js、Go、GraphQLについて興味がある方 サービスリニューアル時の技術選定に興味がある方 他社とのデータ連携を行うシステムに興味のある方 デザインシステムを用いたフロントエンド開発に興味がある方 タイムテーブル 時間 コンテンツ 19:00 – 19:10 オープニング 19:10 – 19:25 天気予報があなたの手元に届けられるまで(仮) 19:25 – 19:40 @nifty天気予報:フルリニューアルへの挑戦 19:55 – 20:10 @nifty天気予報のフロントエンドを実装するまで 20:10 – 20:25 AWS AppSyncを用いたGraphQL APIの開発について 20:25 – 20:30 クロージング 登壇者プロフィール 登壇者未定(ウェザーニューズ) ウェザーニューズ様からの登壇となります。 登壇者決まりましたら反映いたします 渡邊 大介(ニフティ) 自社WEBサービスの開発・運用担当をしています。 @nifty天気予報ではPLをしています。 佐々木 優(ニフティ) @nifty天気予報で開発を担当しています。 フロントエンドの他、最近はAWSのやサービスのインフラコストを削減できないかなどを勉強中です。 川端 航平(ニフティ) SREとして社内横断でプロジェクトサポートをしています。 @nifty天気予報のフルリニューアルプロジェクトにもサポートとして参加。 ニフティグループでは一緒に働く仲間を募集中です 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティエンジニアのX(旧Twitter)アカウント NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 @NIFTYDevelopers アンチハラスメントポリシー 私たちは下記のような事柄に関わらずすべての参加者にとって安全で歓迎されるような場を作ることに努めます。 社会的あるいは法的な性、性自認、性表現(外見の性)、性指向 年齢、障がい、容姿、体格 人種、民族、宗教(無宗教を含む) 技術の選択 そして下記のようなハラスメント行為をいかなる形であっても決して許容しません。 不適切な画像、動画、録音の再生(性的な画像など) 発表や他のイベントに対する妨害行為 これらに限らない性的嫌がらせ 登壇者、主催スタッフもこのポリシーの対象となります。 ハラスメント行為をやめるように指示された場合、直ちに従うことが求められます。ルールを守らない参加者は、主催者の判断により、退場処分や今後のイベントに聴講者、登壇者、スタッフとして関わることを禁止します。 もしハラスメントを受けていると感じたり、他の誰かがハラスメントされていることに気がついた場合、または他に何かお困りのことがあれば、すぐにご連絡ください。 ※本文章はKotlinFest Code of Conductとして公開された文章( https://github.com/KotlinFest/KotlinFest2018/blob/master/CODE-OF-CONDUCT.md )を元に派生しています。 ※本文章はCreative Commons Zero ライセンス( https://creativecommons.org/publicdomain/zero/1.0/ ) で公開されています。
はじめに みなさん、こんにちは!「爆速でシリーズ」って聞いたことありますよね?でも、そのまま真似してみても、環境の違いやバージョンアップのせいで動かないことってありますよね。でも大丈夫!今回は、そんな壁に負けずに挑戦してみましょう! お題概要 今回のミッションは、S3のバケットを確認して、ファイルのダウンロードとアップロードができるようにすることです。わくわくしますね! お題詳細 ある日、企画部門から「システムで生成されるCSVデータを確認したい」という依頼が飛び込んできました。時間がない中で、どうしよう?どうしよう?と悩んでいたら、「S3を閲覧するツールを作ってみては?」というアイデアが浮かびました。他にも方法はあるかもしれませんが、今はこのアイデアで突き進むしかありません! そこで、SvelteKitを使ってS3バケットの一覧参照、ダウンロード、アップロードができるツールを作ることにしました。さあ、一緒に挑戦してみましょう! 作業概要 ここからが本番です!以下の順番で作業を進めていきます SvelteKitの初期設定 .envファイルの作成 ディレクトリ、ファイルを作る 追加でいろいろインストールする ダウンロード機能を実装する アップロード機能を実装する 一覧表示機能を作成する 表示用ページを作る 起動する 動かしてみた 準備はいいですか?それでは、一つずつ見ていきましょう! 1. SvelteKitの初期設定 まずは、SvelteKitのプロジェクトを立ち上げます。今回は、Node.jsのコンテナ内で作業していますが、コンテナの設定は省略させていただきます。ごめんなさいね! 公式ページ を参考に、以下のコマンドを実行しました $ npm create svelte@latest ./ > npx > create-svelte ./ create-svelte version 6.3.10 ┌ Welcome to SvelteKit! │ ◇ Which Svelte app template? │ SvelteKit demo app │ ◇ Add type checking with TypeScript? │ Yes, using TypeScript syntax │ ◇ Select additional options (use arrow keys/space bar) │ Add ESLint for code linting, Add Prettier for code formatting, Add Playwright for browser testing, Add Vitest for unit testing │ └ Your project is ready! Install more integrations with: npx svelte-add Next steps: 1: npm install 2: git init && git add -A && git commit -m "Initial commit" (optional) 3: npm run dev -- --open To close the dev server, hit Ctrl-C Stuck? Visit us at https://svelte.dev/chat 選択肢はこんな感じで進めました SvelteKit demo appを選択 TypeScriptを使用 ESLint、Prettier、Playwright、Vitestを選択 最後に、npm installを実行して準備完了です! npm install 2. .envファイルの作成 S3にアクセスするための大切な情報を.envファイルに保存します。セキュリティ面で気になる方もいるかもしれませんが、今回は爆速重視で進めちゃいます! touch .env .envファイルの中身はこんな感じです # 機密 AWS_ACCESS_KEY_ID= # IAMで発行したキーを入れてください AWS_SECRET_ACCESS_KEY= # IAMで発行したシークレットキーを入れてください AWS_REGION=ap-northeast-1 # 使用するリージョンを指定してください AWS_ENDPOINT_URL=https://s3.amazonaws.com # 必要に応じて変更してください S3_BUCKET_NAME= # 使用するバケット名を入れてください エンドポイントは、業務用に別のものを使う可能性もあるので、変更できるようにしています。臨機応変に対応できるって素晴らしいですよね! 3. ディレクトリ、ファイルを作る さて、ここからが本格的な作業の始まりです!各機能のコードを置くためのディレクトリとファイルを作成しましょう。 mkdir ./src/routes/api mkdir ./src/routes/api/download mkdir ./src/routes/api/upload mkdir ./src/routes/api/list-files touch ./src/routes/api/download/+server.ts touch ./src/routes/api/upload/+server.ts touch ./src/routes/api/list-files/+server.ts 正直に言うと、私もSvelteKitをまだ完全には理解していません。でも大丈夫! 社内でよくSvelteKitの話を耳にする 最新技術に乗り遅れたくない GPTがあるから何とかなる! という3つの理由でSvelteKitを選びました。ファイル構成が正攻法かどうかはわかりませんが、とにかくチャレンジです! 4. 追加でいろいろインストールする S3の操作には@aws-sdk/client-s3を使います。これはGPTさんが教えてくれました。ありがとう、GPTさん! さらに、@types/nodeもインストールしておくと良さそうです。準備万端ですね! npm install @aws-sdk/client-s3 npm install --save-dev @types/node 5. ダウンロード機能を実装する GPTさんに「S3のバケットのダウンロード機能をSvelteKitで作成したい」とお願いして、こんなコードができました src/routes/api/download/+server.ts import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"; import type { RequestHandler } from '@sveltejs/kit'; import { error } from '@sveltejs/kit'; import { env } from '$env/dynamic/private'; const s3Client = new S3Client({ region: env.AWS_REGION, credentials: { accessKeyId: env.AWS_ACCESS_KEY_ID, secretAccessKey: env.AWS_SECRET_ACCESS_KEY }, endpoint: env.AWS_ENDPOINT_URL, }); export const GET: RequestHandler = async ({ url }) => { const fileName = url.searchParams.get('file'); if (!fileName) { throw error(400, 'File name is required'); } const bucketName = env.S3_BUCKET_NAME; const s3Key = fileName; try { const command = new GetObjectCommand({ Bucket: bucketName, Key: s3Key, }); const response = await s3Client.send(command); if (!response.Body) { throw error(404, 'File not found'); } // ストリームをArrayBufferに変換 const chunks: Uint8Array[] = []; for await (const chunk of response.Body as AsyncIterable<Uint8Array>) { chunks.push(chunk); } const fileBuffer = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0)); let offset = 0; for (const chunk of chunks) { fileBuffer.set(chunk, offset); offset += chunk.length; } return new Response(fileBuffer, { headers: { 'Content-Type': response.ContentType || 'application/octet-stream', 'Content-Disposition': `attachment; filename="${fileName}"`, }, }); } catch (err) { console.error("Error", err); throw error(500, 'Failed to download file from S3'); } }; 詳細は省きますが、なんとなくバイナリデータをレスポンスに入れている感じがわかりますね。面白いです! 6. アップロード機能を実装する アップロード機能もGPTさんにお願いしました。結果はこちら src/routes/api/upload/+server.ts import { error, json } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit'; import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; import { env } from '$env/dynamic/private'; const s3Client = new S3Client({ region: env.AWS_REGION, credentials: { accessKeyId: env.AWS_ACCESS_KEY_ID, secretAccessKey: env.AWS_SECRET_ACCESS_KEY }, endpoint: env.AWS_ENDPOINT_URL, }); export const POST: RequestHandler = async ({ request }: { request: Request }) => { try { const formData = await request.formData(); const file = formData.get('file') as File; if (!file) { throw error(400, 'No file uploaded'); } const bucketName = env.S3_BUCKET_NAME; const key = `in/${Date.now()}-${file.name}`; const fileBuffer = await file.arrayBuffer(); const putObjectParams = { Bucket: bucketName, Key: key, Body: Buffer.from(fileBuffer), ContentType: file.type, }; const command = new PutObjectCommand(putObjectParams); await s3Client.send(command); return json({ message: 'File uploaded successfully', key }); } catch (err) { console.error('Error uploading file:', err); throw error(500, 'Internal server error'); } }; const key = の部分に「in/」があるのは、今回はアップロード先を固定してみたかったからです。必要なければ削除してOKです!(直に書くのはよくないですね!) 7. 一覧表示機能を作成する 一覧表示機能も、もちろんGPTさんにお願いしました import { S3Client, ListObjectsV2Command } from "@aws-sdk/client-s3"; import type { RequestHandler } from '@sveltejs/kit'; import { json, error } from '@sveltejs/kit'; import { env } from '$env/dynamic/private'; const s3Client = new S3Client({ region: env.AWS_REGION, credentials: { accessKeyId: env.AWS_ACCESS_KEY_ID, secretAccessKey: env.AWS_SECRET_ACCESS_KEY }, endpoint: env.AWS_ENDPOINT_URL, }); export const GET: RequestHandler = async () => { const bucketName = env.S3_BUCKET_NAME; try { const command = new ListObjectsV2Command({ Bucket: bucketName, Delimiter: '/' }); const response = await s3Client.send(command); const allFiles = []; if (response.Contents) { const files = response.Contents .filter(item => item.Size !== 0) // サイズが0のファイルを除外 .map(item => ({ key: item.Key, size: item.Size, lastModified: item.LastModified })); allFiles.push(...files); } // バケット直下のディレクトリの処理 if (response.CommonPrefixes) { const dirs = response.CommonPrefixes.map(prefix => ({ key: prefix.Prefix, isDirectory: true })); allFiles.push(...dirs); } return json({ files: allFiles }); } catch (err) { console.error("Error listing files:", err); throw error(500, 'Failed to list files from S3'); } }; ListObjectsV2Commandで取得したファイル情報をallFilesに詰め込んでいるんですね。わかりやすい! 8. 表示用ページを作る 最後に表示用ページを作ります。既存のファイルを上書きしちゃいましょう! src/routes/+page.svelte <script lang="ts"> class HttpError extends Error { constructor(public status: number, message?: string) { super(message || `HTTP error! status: ${status}`); this.name = 'HttpError'; } } import { onMount } from 'svelte'; interface S3File { key: string; size: number; lastModified: string; } let files: S3File[] = []; let loading = true; let error = ''; let fileInput: HTMLInputElement; onMount(async () => { await fetchFiles(); }); async function fetchFiles() { try { loading = true; const response = await fetch('/api/list-files'); if (!response.ok) { throw new HttpError(response.status); } const data = await response.json(); files = data.files; } catch (e) { if (e instanceof HttpError) { error = `Failed to fetch files: ${e.message}`; } else { error = 'An unexpected error occurred while fetching files'; } console.error(e); } finally { loading = false; } } function formatBytes(bytes: number, decimals = 2) { if (bytes === 0) return '0 Bytes'; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; } async function downloadFile(fileName: string) { try { const response = await fetch(`/api/download?file=${encodeURIComponent(fileName)}`); if (!response.ok) throw new Error('Download failed'); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); } catch (error) { console.error('Error downloading file:', error); alert('Failed to download file'); } } async function uploadFile() { if (!fileInput.files || fileInput.files.length === 0) { alert('Please select a file to upload'); return; } const file = fileInput.files[0]; const formData = new FormData(); formData.append('file', file); try { const response = await fetch('/api/upload', { method: 'POST', body: formData }); if (!response.ok) { throw new HttpError(response.status, 'Upload failed'); } alert('File uploaded successfully'); await fetchFiles(); // Refresh the file list } catch (error) { if (error instanceof HttpError) { console.error('Error uploading file:', error.message); alert(`Failed to upload file: ${error.message}`); } else { console.error('Unexpected error during upload:', error); alert('An unexpected error occurred during upload'); } } } </script> <h1>S3 Bucket Files</h1> <div class="upload-container"> <input type="file" bind:this={fileInput}> <button on:click={uploadFile}>Upload File</button> </div> {#if loading} <p>Loading...</p> {:else if error} <p class="error">{error}</p> {:else if files.length === 0} <p>No files found in the bucket.</p> {:else} <table> <thead> <tr> <th>File Name</th> <th>Size</th> <th>Last Modified</th> </tr> </thead> <tbody> {#each files as file} <tr> <td> <button on:click={() => downloadFile(file.key)} class="download-link"> {file.key} </button> </td> <td>{formatBytes(file.size)}</td> <td>{new Date(file.lastModified).toLocaleString()}</td> </tr> {/each} </tbody> </table> {/if} <style> .upload-container { margin-bottom: 20px; } table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } th { background-color: #f2f2f2; } .error { color: red; } button { margin-left: 10px; padding: 5px 10px; background-color: #007bff; color: white; border: none; cursor: pointer; } button:hover { background-color: #0056b3; } .download-link { background: none; border: none; color: #007bff; text-decoration: none; cursor: pointer; padding: 0; font: inherit; } .download-link:hover { text-decoration: underline; } </style> styleもそれっぽく作ってくれていますね!tableタグしか使えない私には夢のようです! 9. 起動する いよいよ起動です!以下のコマンドで立ち上げましょう npm run dev -- --host 0.0.0.0 毎回、「 — –host 0.0.0.0 」を入力するのが面倒な方は、vite.config.tsをこんな感じに変更しておくと便利ですよ import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; export default defineConfig({ plugins: [sveltekit()], server: { host: '0.0.0.0' } }); これで npm run dev だけで起動できます。簡単ですね! 10.動かしてみた 単に開いてみた アップロードしてみた OK押してみた あれ? inとoutのSizeとLast Modifiedの表示、なんだか不思議ですね!まるで宝探しゲームみたい。これは次のアップデートで解明する楽しい謎になりそうです! おっと、「ファイルを選択」ボタンの横のファイル名がクリアされていないみたい。でも大丈夫!これはユーザーに「あなたが最後に選んだファイルだよ〜」って親切に教えてくれているんですね。ちょっとしたサプライズ機能かも?(笑) 終わりに(後日談というか今回のオチ) なんと驚くことに、私たちが苦労して作ったものと似たようなものを公式が作っていたんです! https://github.com/aws-amplify/amplify-ui/issues/5731 「時代が私に追いついた!」なんて言えたら格好いいんですけどね(笑)。でも、これって逆に考えると、私たちが本当に必要とされているものを作っていたってことですよね。すごくない? この経験を通して、私はとても大切なことを学びました。それは、「SvelteKitがよくわからなくても、GPTの力を借りればここまでできる」ということです。正直、最初は不安でしたが、やってみたらどんどん形になっていって、すごくワクワクしました! みなさんも、「やったことないからわからない」って思っても、まずは一歩踏み出してみてください。きっと新しい発見があるはずです。失敗を恐れずに、どんどんチャレンジしていきましょう! そうそう、最後にちょっとした宣伝です。ニフティでは、このような面白い挑戦を常に募集しています。一緒に新しいことにチャレンジしてみませんか?きっと楽しい経験になりますよ! さあ、次はどんな冒険が待っているでしょうか。楽しみですね!それでは、また次回の「爆速シリーズ」でお会いしましょう。バイバーイ!  終わりの終わりに いかがでしたでしょうか。何とも言えないテンションだったかと思います。今回はある程度、原稿を書いた状態で、「明るい口調で書き直して!」とGPTさんにお願いしてみたところこのような文章になりました。中の人はこんなキャラではありません。お許しください。ただ、最後の「時代が私に追いついた」の部分については、「本当に必要とされているものを作っていた」なんてポジティブな考えはまったく思っていませんでした。こういうものの考え方もあるのだなあと感心しました。コード生成だけではなく考え方の参考にもなると気づかされまして今後、活用していきたいと思います。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
こんにちは。ニフティ株式会社のyamanakaです。 所属しているチームでPlaywrightのフィクスチャを導入したため、備忘録も兼ねて調べた内容やフィクスチャ概要から実際の使用例( @nifty紹介特典 から紹介コード払い出し)とともに紹介します。 はじめに ウェブアプリケーションの自動テストは、品質保証プロセスにおいて不可欠な要素となっています。その中で、Microsoftが開発したPlaywrightは、強力で使いやすいクロスブラウザテスト自動化ツールとして注目を集めています。 Playwrightは、Chromium(Chrome、Edge)、Firefox、WebKitベースのブラウザに対応し、JavaScriptやTypeScriptを使用して直感的なAPIを提供します。これにより、高速で信頼性の高いE2Eテストを簡単に作成できます。 しかし、効率的なテスト環境を構築するには、単にテストケースを書くだけでは不十分です。ここで重要な役割を果たすのが「フィクスチャ」です。 フィクスチャとは フィクスチャとは、テストの実行に必要な前提条件や環境を設定するためのコードのことです。Playwrightにおいて、フィクスチャは以下のような役割を果たします。 1. テスト環境の一貫性を保証 2. コードの重複を削減 3. テストの可読性と保守性を向上 4. テスト実行の効率化 例えば、ブラウザの起動、ページの読み込み、ユーザーログインなど、多くのテストケースで共通して必要な操作をフィクスチャとして定義することで、テストコードをクリーンに保ち、効率的に管理できます。 フィクスチャの定義 Playwrightでは、 test.beforeEach() や test.beforeAll() などのフックを使用してフィクスチャを定義します。一般的な形式は以下のとおりです。 javascript import { test as base } from '@playwright/test'; export const test = base.extend({ myFixture: async ({ }, use) => { // フィクスチャのセットアップ const value = 'フィクスチャの値'; // フィクスチャの使用 await use(value); }, }); 別ファイルでブラウザ、コンテキスト、ページフィクスチャをそれぞれ作成します、そしてこのフィクスチャを使用するテストファイルでは、以下のようにインポートして使用します。 javascript // myFixtures.js import { test as base, chromium } from '@playwright/test'; export const test = base.extend({ page: async ({ }, use) => { const browser = await chromium.launch(); const context = await browser.newContext(); const page = await context.newPage(); await use(page); await context.close(); await browser.close(); }, }); javascript // myTest.spec.js import { test } from './myFixtures'; test('フィクスチャを使用するテスト', async ({ page }) => { await page.goto('https://example.com'); // テストロジック }); 上記により、基本的なブラウザ、コンテキスト、ページフィクスチャを使いこなすことで、テストの基盤を強固にできます。 カスタムフィクスチャの作成 基本的なフィクスチャに加えて、プロジェクト固有のニーズに合わせてカスタムフィクスチャを作成することができます。 これにより、テストコードの再利用性と管理性をさらに向上させることができます。 ユーザー認証とAPIレスポンスのモック化を例にあげます。 javascript import { test as base } from '@playwright/test'; export const test = base.extend({ loggedInPage: async ({ page }, use) => { // ログイン処理 await page.goto('https://example.com/login'); await page.fill('#username', 'testuser'); await page.fill('#password', 'testpass'); await page.click('#login-button'); await use(page); }, // APIモックを設定するフィクスチャ mockApi: async ({ page }, use) => { // APIリクエストのモックを設定 await page.route('**/api/dashboard', route => { route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ username: 'testuser', lastLogin: '2023-04-01' }) }); }); // モック設定済みのページをテストに提供 await use(page); }, // ログイン済みかつAPIモック適用済みの環境を提供するフィクスチャ preparedTestEnvironment: async ({ loginPage, mockApi }, use) => { // loginPage に mockApi の設定を適用 await mockApi(loginPage); // 準備完了した環境をテストに提供 await use(loginPage); }, }); この組み合わせたフィクスチャを使用するテストは以下のようになります。 javascript import { test } from './myFixtures'; // テスト test('ダッシュボードテスト', async ({ preparedTestEnvironment }) => { // 実際のダッシュボードページに遷移 await preparedTestEnvironment.goto('https://example.com/dashboard'); // ここで 'https://example.com/api/dashboard' にリクエストを送る // テストアサーションなど }); 遷移する処理とAPIリクエストのモックを別々の概念として考えながら組み合わせてユニットテストを構築すると良いのかなと記事にしながら思いました。 フィクスチャのスコープ Playwrightでは、フィクスチャのスコープを適切に設定することで、テストの効率と信頼性を向上させることができます。 テストファイルスコープ テストファイル内でのみ使用するフィクスチャは、そのファイル内で定義します。 javascript import { test as base } from '@playwright/test'; const test = base.extend({ localFixture: async ({}, use) => { // このファイル内のテストでのみ使用可能なフィクスチャ await use('local value'); }, }); test('ローカルフィクスチャを使用', async ({ localFixture }) => { console.log(localFixture); // 'local value' }); グローバルスコープ プロジェクト全体で使用するフィクスチャは、専用のファイル(例: fixtures.ts )で定義し、インポートして使用します。 javascript // fixtures.ts import { test as base } from '@playwright/test'; export const test = base.extend({ globalFixture: async ({}, use) => { await use('global value'); }, }); // test.spec.ts import { test } from './fixtures'; test('グローバルフィクスチャを使用', async ({ globalFixture }) => { console.log(globalFixture); // 'global value' }); 非同期フィクスチャの扱い方 Playwrightのフィクスチャは非同期操作を効果的にサポートしており、async/awaitを使用して簡単に非同期処理を組み込むことができます。 基本的な非同期フィクスチャ javascript import { test as base } from '@playwright/test'; export const test = base.extend({ asyncData: async ({}, use) => { const data = await fetchDataFromApi(); await use(data); }, }); test('非同期データを使用', async ({ asyncData }) => { console.log(asyncData); // テストロジック }); エラーハンドリング エラーが発生した場合のハンドリング処理も忘れず作成しましょう。 javascript import { test as base } from '@playwright/test'; export const test = base.extend({ errorProneFixture: async ({}, use) => { try { const result = await fetchDataFromApi(); await use(result); } catch (error) { console.error('フィクスチャの初期化中にエラーが発生しました:', error); throw error; // テストを失敗させる } }, }); フィクスチャの依存関係 フィクスチャは他のフィクスチャに依存することができ、これによって複雑なテスト環境を構築できます。 javascript import { test as base } from '@playwright/test'; export const test = base.extend({ config: async ({}, use) => { // 設定をロード const config = await loadConfig(); await use(config); }, apiClient: async ({ config }, use) => { // configフィクスチャを使用してAPIクライアントを初期化 const client = new ApiClient(config.apiUrl); await use(client); }, loggedInUser: async ({ page, apiClient }, use) => { // apiClientフィクスチャを使用してユーザーを作成 const user = await apiClient.createUser(); // ページにログイン await page.goto(config.loginUrl); await page.fill('#username', user.username); await page.fill('#password', user.password); await page.click('#login-button'); await use(user); }, userWithData: async ({ loggedInUser, apiClient }, use) => { // ログイン済みユーザーにデータを追加 const userData = await apiClient.addUserData(loggedInUser.id, { posts: ['Test Post'] }); await use({ ...loggedInUser, ...userData }); }, }); test('ユーザーデータのテスト', async ({ userWithData, page }) => { await page.goto('https://example.com/profile'); const postTitle = await page.textContent('.post-title'); // 検証 expect(postTitle).toBe('Test Post'); expect(userWithData.posts).toContain('Test Post'); }); この例では、複数のフィクスチャが互いに依存関係を持っています。 1.  apiClient  は  config  に依存 2.  loggedInUser  は  page  と  apiClient  に依存 3.  userWithData  は  loggedInUser  と  apiClient  に依存 実践的な例:払い出したURLに遷移するテスト ニフティでは光回線サービスをお友達に紹介すると、紹介した方&紹介された方それぞれにニフティポイントをプレゼントするサービスがあります。( @nifty紹介特典 ) 今回は上記サービスから紹介コードを払い出し、紹介ページから申込画面に遷移する内容を例にあげます。 import { test as base } from '@playwright/test'; import { expect } from "@playwright/test"; export const test = base.extend({ mainPage: async ({ page }, use) => { await page.goto('https://setsuzoku.nifty.com/syokai/index.htm'); await use(page); }, searchUrl: async ({ mainPage }, use) => { await mainPage.getByRole('link', { name: '紹介する' }).click(); // ページにログイン await mainPage.fill('#id_user_id', 'xxxxxxxx'); await mainPage.click('#next'); await mainPage.fill('#id_pw', 'xxxxxxxx'); await mainPage.click('#login'); const url = await mainPage.evaluate(() => { const element = document.querySelector('.shokaiCode_signUp a'); return element ? element.href : null; }); await mainPage.goto(url); // url を返すように変更 await use({ page: mainPage, url: url }); }, }); test('払い出されたURLに遷移', async ({ mainPage, searchUrl }) => { const { url } = searchUrl; // URLを確認する await expect(mainPage).toHaveURL(url); }); おわりに Playwrightのフィクスチャを導入することで、コードの再利用や共通化することができるためチームとしても一貫したテストを採用しやすくなります。 APIレスポンスのモック化について調べながらコードを書きましたが、ユニットテストに対して柔軟で安定したテスト環境が構築できると思いました。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する