TECH PLAY

ニフティ株式会社

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

500

この記事は、 ニフティグループ Advent Calendar 2023 14日目の記事です。 はじめに こんにちは。会員システムグループの渡邊です。 最近、あるサイトのリニューアルプロジェクトを担当しており、その中で私はエンジニアですがサービス設計にも携わる機会がありました。 そこでエンジニアがサービス設計に積極的に参加することには多くのメリットを感じたので、今回は紹介していきます。 サービス設計の流れ まず、今回行ったサービス設計は以下の流れで行いました。 既存の課題や成功要因の洗い出し ユーザー行動の理解 競合サービスとの比較 アイデアの発想とブレスト コンセプトの選定 ワイヤーフレームの作成 すべての工程を企画側と一緒に行い、認識のズレをなくすことを重視しました。 そこで、いくつかの気づきがありましたので以下のようにまとめました。 1. 技術的な実現性の向上 まず、エンジニアがサービス設計に参加することで、提案されたサービスの技術的な実現性を早い段階で評価できます。 サービス設計の段階はついつい、夢を詰め込み過ぎちゃうことがあるため実際に設計、実装を行うエンジニアがいると非現実的な機能や要件を未然に防ぎ、実装可能な範囲でサービスを設計することができます。 しかし、エンジニアは今後の実装を見据えて考えてしまい、手堅い構造にしてしまう傾向があるため、ここでは柔軟な考え方が必須です。 2. パフォーマンスと拡張性の確保 サービス設計で考慮されたユーザーストーリーや要件を元に、エンジニアがシステムを設計を行うため、サービス設計を深く理解する必要がありますが、サービス設計も行ったエンジニアがシステム設計を行うと将来的な変更や機能の拡張がスムーズに考えることができるようになります。 実際には、サービス設計の段階で出てきたアイディアが初期のリリースでは厳しいが、その後のリリースでは可能という判断もできました。 3. ユーザビリティと技術のバランス 次に、ユーザビリティと技術的な側面のバランスが取りやすくなります。 ユーザーエクスペリエンスを向上させる一方で、実装の複雑さや開発リソースの最適な活用を考慮し、実現可能なサービスをデザインできます。 開発を進めていく中で、新しいアイディアが出てくることも多々あるのでこういったことが、サービス設計から盛り込めるのは非常にいいですね。 4. 早期段階での課題解決 実装段階で発生するかもしれない問題や技術的な課題を早い段階で見つけ出し、解決することができます。これにより、開発プロセスがスムーズに進み、最終的なサービスの品質向上が期待できます。 おわりに エンジニアがサービス設計に積極的に参加することで、ユーザビリティと技術のバランスを取ることができます。これにより、ユーザーエクスペリエンスを向上させながら実装の複雑さを最小限に抑え、企画側とのやり取りの頻度を減らすこともできます。 その結果、サービス設計は単なるアイデアだけでなく、具体的で実現可能なものとなり、プロジェクトの成功に向けたしっかりとした基盤を築くことができるようになると考えています。 ビジネス視点でサービスを見ることがあまりなかったので、今回のサービス設計は良い経験になりました。 今後も試行錯誤を通じて得た気づきは積極的に公開していきたいと思います! 明日は、 rubihiko さんです。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは、新卒一年目OJT中の平野です。 今年は暖冬と言われていますが流石に夜はエアコンと電気毛布が手放せなくなりました。春が待ち遠しいです。今回は簡単なToDoアプリをペアプログラミングで開発しました。その中で、宣言的UIやSvelteの記法、リアクティブ、フロントエンドの歴史などを知ることができました。今回はそのペアプロを通して得られた知見を共有していきたいと思います。先輩社員のたけろいどさんにもご協力いただきナビゲーターが得られた知見も記載していますので、ぜひご覧ください! 背景 弊社のエンジニアにはOJTという教育制度が導入されています。OJTは、実際の業務を通じて知識や技術を身につける制度です。 私たち新入社員は新人研修を終えた後3ヶ月ずつ3つの部署を渡り実際の業務にあたります。この期間は私のようなトレーニーに対し、一人のトレーナーがついて業務のサポートを行っていただきます。現在、私には先輩社員のたけろいどさんにトレーナーとしてついていただいています。積極的に私の業務や目標達成のためのフォローをしたいただいており、感謝の毎日です。 そんなたけろいどさんにSvelteについて勉強したいと言ったところ、一ヶ月間、毎週1時間ペアプログラミングで開発を行う時間をとっていただくことになりました。たけろいどさんの専門分野でもあるフロントエンドについて今回の開発を知見を深めることができました。 つくったもの 今回は4時間ほどの開発時間をいただけたので、コード・見た目ともに非常にシンプルなToDoリストを作成しました。少ない開発時間でしたがリアクティビティなどのSvelteの特徴やフロントエンドの歴史について知る機会になりました。 また、ペアプログラミングで作ったToDoリストを個人でBootstrapを用いて簡単に装飾、機能を追加を行い、簡単なカンバンボードのような見た目のものを作りました。バックエンドの処理は作成していないのでページをリロードするとデータはなくなってしまいますが、それっぽいものはできたかなと思います。class名だけで画面を簡単に装飾できて便利ですね。ホットリロードというコードの変更がすぐに画面に反映される仕組みも相まって楽しい開発体験でした。 作成した画面 ドロップダウンメニューやドラッグ&ドロップで移動、削除ボタンといった機能を実装 既存のカンバンボードを見てみると使いやすいよう工夫がされていてまだまだ自分の作った物には改善の余地があることが分かります。勉強になりました。 今回フロントエンドでの開発に触れて、デザインやUIの部分でやってみたいことがいくつか出てきたので、勉強を進めていきます。 これまで大学の授業などでHTML、CSS、JavaScriptなどを触ったことはありましたが、今回初めてモダンフレームワークに触れました。今回学んだことについていくつか書いていきます。 ペアプログラミングで学んだこと モダンフレームワークを触った感想として、これまで開発者が複雑に感じていたことをシンプルにできるような仕組みがいくつか導入されていることが分かりました。 例えばSvelteにはコンポーネントベースのアーキテクチャ、宣言的UI、といったモダンフレームワークの特徴があります。 コンポーネントベースのアーキテクチャを簡単にいうとUIをコンポーネントに分割してカプセル化することで再利用やメンテナンスを簡単にするのもです。 宣言的UIは、インターフェースを構築する際のアプローチです。SvelteではHTMLのような構文を用いてUIの構造を宣言し、フレームワークが内部的に必要なDOM操作を処理しています。コードが直感的で読みやすいという利点があります。 逆にJavascriptは命令的UIというアプローチで要素をどのように変更するかを詳細に記述しています。こちらには細かい制御が可能といった利点があります。私が今回Svelteで開発を行った感想として、HTML、CSS、JavaScriptを使った開発と比較して楽で直感的な開発ができました。Svelteのこれらの特徴の恩恵にあやかることができたのではないかと思います。 ペアプログラミングをやってみた感想 今回、トレーナーのたけろいどさんと開発を行いましたが、詳しい方に教えていただきながら開発することで、環境構築やわからない部分に詰まることもなく開発することができました。また、開発中は自分がやっていることと関連する知識を教えてくださり、ゲームのチュートリアルのように、ストレスフリーに知識を得ることができました。これはたけろいどさんの手腕のおかげであり、自分がいずれ同じようなことをすることが来たときの参考になりました。学びの多い4時間でした。 先輩社員の視点:ペアプログラミングの効果 改めまして、先輩社員のたけろいどです。 ペアプロを通して学んでいた知見を書いていきます。よろです。 新しい視点の価値 トレーニーとのペアプログラミングは、私にさまざまな気づきを与えてくれました。日常的に行っている実装作業に対しても、彼らは多くの疑問を投げかけ、「知識の呪い」を改めて感じさせてくれました。特にリアクティビティの概念に関しては、その重要性を再認識しました。 私はjQueryやvanillaJSに精通しているため、リアクティビティの複雑さやその仕組みを理解しています。しかし、トレーニーはJavaScriptの経験が少ないため、リアクティビティがどのように機能しているのか、その重要性を十分に把握していないようでした。 そこで、リアクティビティの基本原理と、それがどのようにTodoアプリに応用されているかを実際にコードを書きながら説明しました。このプロセスを通じて、トレーニーはリアクティビティの重要性や、現代のフレームワークが開発者の作業負荷をどのように軽減しているかを実感できたと思います。 リアクティビティの例はほんの一つに過ぎませんが、私たちが気づかないうちに「知識の呪い」は数多く存在しています。そのため、積極的にコミュニケーションを取り、トレーニーの成長を支援していくことが重要だと感じています。 通常業務との兼ね合い ペアプログラミングは、トレーニーの育成において優れたツールであり、コミュニケーションがとりやすいことから非常に有効だと考えています。しかし、トレーナーおよびトレーニー双方の時間を拘束するため、過度に時間を割くと通常業務に影響を及ぼす恐れがあります。特に、toCのサービスを扱う私たちの場合、日々お客様のトラブル解決などの運用業務が発生し、これらに迅速に対応する必要があります。そのため、運用業務とペアプログラミングの時間配分のバランスを取るのが難しいと感じています。 私たちのチームではスクラム方式で開発を進めており、タスクには優先度が設定されています。このアプローチにより、運用業務とペアプログラミングとを比較的良好なバランスで進めることができました。さらに、ペアプロに割く時間を1時間に制限することで、作業にメリハリをつけ、業務との並行実施が可能となりました。 一方で、反省点もあります。時間を区切ってペアプロを行うことはメリハリをもたらしましたが、その1時間でどこまで進めるかという目標を事前に共有することができませんでした。その結果、途中で作業が終了してしまったり、時間配分に誤りが生じたりすることがありました。次回からは、トレーニーと事前に目標を明確に共有することで、互いに安心してペアプログラミングに取り組めるようになるという重要な気づきを得ることができました。 まとめ 今回はトレーナーのたけろいどさんとペアプログラミングを通じてToDoアプリを作成しました。Svelteの特徴やトレーニーへの教え方など、今後自分に必要になる知見を得られたと思います。たけろいどさんにはこの場を借りて感謝を伝えます。ありがとうございました。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 14日目の記事です。 はじめまして。新卒1年目の高田です。 弊社にはジョブローテーションという制度があり、新卒で入社した社員は様々な部署を数ヶ月単位で横断します。 その中で私は未経験でアプリ開発に着手させていただいており、今回はそこでの学びの一つをブログとして執筆していきたいと思います。 作るもの それでは、タイトル通り円グラフを描画してみます。 今回は、下図のような図形を作っていきます。 アニメーションをつけるとこんな感じ まずは円弧を描いてみる まずは、円弧を描くところから始めましょう。 描画にはCanvasのdrawArcを使用します。 startAngleで開始地点を指定して、sweepAngleで弧の角度を指定します。そうすると、指定した開始地点から時計回りに描画をしてくれます。例えば、開始地点を0度、角度を270度に設定すると下図のような円を描くことができます。 Canvas(modifier = Modifier.size(200.dp)) { drawArc( color = Color.Red, startAngle = 0f, sweepAngle = 270f, useCenter = false, style = Stroke( width = 20.dp.toPx() ) ) } グラフっぽくする 円弧の描画を少し応用して、グラフを作ってみます。 以下の実装では、それぞれの円弧を組み合わせて、円を構成させるということをしています。 // テスト用のデータを用意する val data :List<Float> = listOf(20f, 15f, 5f, 10f) // 描画用のデータに変換する val sweepAngleList: List<Float> = data.map { 360f * it / data.sum() } val colorList: List<Color> = listOf( Color.Red, Color.Blue, Color.Green, Color.LightGray, ) val pieChartData: List<Pair<Color, Float>> = colorList.zip(sweepAngleList) // グラフを描画する pieChartData.forEachIndexed { _, data -> drawArc( color = data.first, startAngle = -90f, sweepAngle = data.second, useCenter = false, style = Stroke(width = chartBarWidth.toPx(), cap = StrokeCap.Butt) ) startAngle += data.second } 円グラフっぽくなりましたね!? 静止画だけで良い人はここまで。 最後にアニメーションをつけて少し豪華にしていきます。 アニメーションもつけて豪華にする ここからアニメーションをつけて少し豪華にします。 もっと良い実装方法があるのかな〜とは思いますが、筆者は以下のように実装を進めてみました。 以下の実装では、 Animatable  のリストを作成し、それぞれの円弧に対して遅延して順番にアニメーションを適用することで、円弧が描画されているようなアニメーションを実現しています。 // Animatableのlistを作成する val animaTableList = List(datasets.size) { remember { Animatable(0f) } } animaTableList.forEachIndexed { index, animaTable -> LaunchedEffect(animaTable) { // 各アニメーションを遅延させる delay(index * 600L) animaTable.animateTo( targetValue = 1f, animationSpec = tween( durationMillis = 600, easing = LinearEasing ) ) } } Canvas(modifier = Modifier.size(radiusOuter)) { var startAngle = -90f // 各円弧に順番にanimaTableListを適用して、描画順にアニメーションを再生させる pieChartData.forEachIndexed { index, data -> drawArc( color = data.first, startAngle = startAngle, sweepAngle = data.second * animaTableList[index].value, useCenter = false, style = Stroke( width = chartBarWidth.toPx(), cap = StrokeCap.Butt ) ) startAngle += data.second } } どうでしょうか?それっぽいアニメーションになってますかね!? 最後に 「Androidでアニメーション付き円グラフを作ってみよう」ということで、円弧の描画から円グラフへのちょっとした応用までやってみました。 もっといい方法をお前に教えてやるぜ!という方がいましたら、下記リンクからコンタクトをどうぞ! (冗談です) 明日は、dev-shimadaさんの「ログのエラー検知をノーコードでslackに通知してみる」です。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 10日目の記事です。 「CLIを使っていて、複数の選択肢から1つ選びたい。なおかつ値を入力するのではなく、カーソルを移動して選択したい。」と思ったことはありませんか?少なくとも私にはありました。 bashには dialog コマンドが実装されており、予め設定した選択肢から1つを選択するような操作画面を出すことができます。 しかし、powershellにはそういった機能はありません。正確にはあるにはあるのですが、.NET Frameworkの機能を使って別ウィンドウを新たに生成するといったものになっています。できれば別ウィンドウは開かず、 dialog のようにCLI上ですべて完結したいですよね。 そこで今回は、powershellを用いて dialog を実装してみようと思います。 つくってみた array を渡すと選択肢が表示されて、選択した要素のindexが返ってくるようなpowershellコマンドを作ります。 環境 PS C:\Users> $PSVersionTable Name Value ---- ----- PSVersion 7.4.0 PSEdition Core GitCommitId 7.4.0 OS Microsoft Windows 10.0.19045 Platform Win32NT PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…} PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1 WSManStackVersion 3.0 powershellのモジュールについて powershellモジュールのディレクトリ構成は以下の様になります。 MyPsModule/ └ MyPsModule.psm1 作成したモジュールは、 .psm1 で保存します。 保存するディレクトリ名とモジュールファイル名は同じにしてください。powershellはモジュール置き場に置かれたディレクトリ名と同じpsm1モジュールを検索して読み込みます。 どこに置けばよいかは、 $env:PSModulePath で確認することができます。 PS C:\\Users\\xxxxx> $env:PSModulePath C:\\Users\\xxxxx\\Documents\\PowerShell\\Modules;C:\\Program Files\\PowerShell\\Modules;c:\\program files\\powershell\\7\\Modules;C:\\Program Files\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules ここで表示されているいずれかのディレクトリにファイルをモジュールディレクトリを格納してpowershellを再起動すると、自動でモジュールが取り込まれます。 実装 MyPsModule.psm1 function Show-Dialog { param ( [string[]] $array ) # 何列目を選択中か $row = 0 # メニューカーソルの位置を取得 $firstLine = $Host.UI.RawUI.CursorPosition while ($true) { $counter = 0 # # 情報を1つずつ出力 foreach ($item in $array) { if ($counter -eq $row) { Write-Host "> " -NoNewline Write-Host $($item) -BackgroundColor White -ForegroundColor Black } elseif ($counter -eq ($array.Length - 1)) { Write-Host " " -NoNewline Write-Host $($item) -ForegroundColor White -NoNewline } else { Write-Host " " -NoNewline Write-Host $($item) -ForegroundColor White } $counter++ } $key = [Console]::ReadKey($true) # enterが押されたらrowを格納して終了 if ($key.Key -eq 'Enter') { return $row } # 入力ボタンに応じてrowの値を変更 elseif (($key.Key -eq 'UpArrow') -and ($row -gt 0)) { --$row } elseif (($key.Key -eq "DownArrow") -and ($row -lt ($array.Length - 1))) { ++$row } $cursorPos = $Host.UI.RawUI.CursorPosition $cursorPos = $firstLine $Host.UI.RawUI.CursorPosition = $cursorPos } } Set-Alias -Name dialog -Value Show-Dialog Export-ModuleMember -Function Show-Dialog -Alias dialog 動作確認 選択肢が表示され、カーソルが動くことを確認しました まとめ 今回はpowershellでdialog機能を作成してみました。まだまだ荒削りなので、エラー処理ができていなかったり、カーソルが1度でも改行された状態でコマンドを使うとバグが出たりします。 今後はそのあたりもつめつつ、より実用性のあるパッケージにしていきたいです。 参考記事 シェルスクリプトでカーソルメニューつくる | matsub Powershellでプロセスのメモリ使用量をロギングする ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 13日目の記事です。 はじめに こんにちは。新卒4年目の大里です。12月になって寒くなってきたため、今回はp5.jsで雪が降るようなアニメーションを実装しました。 p5.jsとは? p5.jsとはクリエイティブ・コーディングのために作られたJavaScriptのライブラリです。クリエイティブ・コーディングはプログラミングを使ってアート作品などを創作することを指します。 公式ページ: https://p5js.org/ 完成例 実行例 p5.jsを使って実装すればブラウザ上で表示できる以下のようなアニメーションを作成できます。 p5.jsで雪を降らした様子 ファイル構成 p5.jsでアニメーションを制御しているファイルはsketch.jsです。index.htmlではsketch.jsのインポートとCDNからp5.jsの取得を行っています。 └─Snow index.html sketch.js index.htmlの中身 <html> <head> <meta charset="UTF-8"> <script src="https://cdn.jsdelivr.net/npm/p5@1.8.0/lib/p5.js"></script> <script src="./sketch.js"></script> </head> <body> </body> </html> sketch.jsの中身 let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); for(let snow of snows) { snow.updatePosition(); snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(-20, -1000); size = random(6, 20); speed = random(1, 2); sides = 6; angle = TWO_PI/ this.sides; updatePosition() { this.y += this.speed; if(this.y > height + 30) { this.y = random(-20, -300); } } drawSnowFlake() { push(); stroke(255); translate(this.x, this.y); for (let i = 0; i < this.sides; i++) { line(0, 0, this.size/2*cos(this.angle * i), this.size/2*sin(this.angle * i)); } pop(); } } 実行方法 index.htmlをブラウザで開けば雪が降るアニメーションが表示されます。 作成手順 雪を降らすアニメーションの実装の仕方を段階を追って説明します。アニメーションはsketch.jsのみで実装しているため、これ以降はsketch.jsのみに言及します。 背景を描く 実装例 function setup(){ // キャンバスのサイズ指定 createCanvas(windowWidth, windowHeight); } function draw(){ // 背景を記述 background(30,30,55); } コード説明 setup関数はアニメーションを描写する前に一度だけ実行される関数です。このコードではcreateCanvasという関数を使ってキャンバス(画面)の大きさを設定しています。 draw関数はアニメーションを描写する際に1フレームごとに実行される関数です。デフォルトでは60fpsで実行されます。このコードではbackground関数を使ってキャンバスの背景の色をRGBで指定しています。 windowWidth, windowHeightはp5.jsが提供する環境変数です。ウィンドウの幅、高さを表しています。 実行例 背景を描いたコードの実行例 円を描く 実装例 function setup(){ createCanvas(windowWidth, windowHeight); } function draw(){ background(30,30,55); // この行以降の図形描写には輪郭線の描写なし noStroke(); // この行以降の図形描写では白色で塗りつぶす fill(255); // 円を描く circle(width/2, height/2, 100); } コード説明 circle関数では引数でx軸の座標、y軸の座標、大きさを指定しています。widthはキャンバスの幅、heightはキャンバスの高さを表す環境変数です。 p5.jsの原点はキャンバスの左上に位置しています。x軸の正方向は原点から右方向です。y軸の正方向は原点から下方向になります。 キャンバスの座標軸 実行例 p5.jsで円を描いた様子 円を大量に描く 実装例 let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); // 雪の配列を作成する for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); // 配列全ての雪を描写する for(let snow of snows) { snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(0, height); size = random(6, 20); drawSnowFlake() { noStroke(); fill(255); circle(this.x, this.y, this.size); } } コード説明 雪(円)を大量に描くために雪クラスの配列を用いています。雪クラスではコンストラクターとして円のx座標, y座標, 円の大きさをランダムで設定しています。drawSnowFlakeメソッドで円を描くメソッドを定義しています。 実行例 p5.jsで大量に円を描いた様子 それぞれの円を降らす 実装例 let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); for(let snow of snows) { // フレームごとに雪の位置を更新する snow.updatePosition(); snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(-20, -1000); size = random(6, 20); speed = random(1, 2); updatePosition() { this.y += this.speed; if(this.y > height + 30) { this.y = random(-20, -300); } } drawSnowFlake() { noStroke(); fill(255); circle(this.x, this.y, this.size); } } コード説明 雪を降らせるためにコンストラクターではy座標の修正と速度の設定を行いました。y座標はキャンバスから見えない上方向に設定しています。 updatePositionメソッドでフレームごとにy座標が増えていくように更新するようなメソッドを実装しました。加えて、雪がキャンバスから外れたときにキャンバスの上方向に戻るようにしています。 実行例 p5.jsで円を大量に降らした様子 円を雪の結晶に変える 実装例 let snows = []; function setup(){ createCanvas(windowWidth, windowHeight); let snowNum = random(100, 200); for (let i=0; i < snowNum; i++) { snows.push(new Snow()); } } function draw(){ background(30,30,55); for(let snow of snows) { snow.updatePosition(); snow.drawSnowFlake(); } } class Snow { x = random(0, width); y = random(-20, -1000); size = random(6, 20); speed = random(1, 2); sides = 6; angle = TWO_PI/ this.sides; updatePosition() { this.y += this.speed; if(this.y > height + 30) { this.y = random(-20, -300); } } drawSnowFlake() { // 現在のスタイル設定と変換を保存する push(); // 輪郭線を白色で描写する stroke(255); // 原点を雪の位置に移動させる translate(this.x, this.y); // 線を6本放射線状に描く for (let i = 0; i < this.sides; i++) { line(0, 0, this.size/2*cos(this.angle * i), this.size/2*sin(this.angle * i)); } // スタイル設定と変換を元に戻す pop(); } } コード説明 円から雪の結晶に変えるためにdrawSnowFlakeメソッドを修正しました。push関数を使って現在の原点を保持しています。その後translate関数で原点を現在の雪の位置に移動させています。これをしておくと、雪の位置を原点として6本の放射線を描くための座標指定が簡単になります。最後にpop関数で原点をキャンバスの左上に戻します。push関数とpop関数はセットで使う必要があります。 実行例 p5.jsで雪を降らした様子 おわりに 今回はp5.jsを使って雪が降るアニメーション作成を行いました。p5.js特有の関数や環境変数を覚える必要がありますが、簡単にアニメーションをプログラミングすることができました。p5.jsはJavaScriptのライブラリであるため、Webサイトの装飾にも使うことができると思います。 明日は、D_Wさんのエンジニア目線で考えるサービス設計です。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 8日目の記事です。 はじめに コロナに罹患してしまってアドカレ更新がすっかりおくれてしまいました。アドカレ掲載日前には書き上げていたんだよ。本当だよ。 寒いですね!って書き出しにしたらなんか妙に暖かい予報の日がふえてきました。西野です。 私はマイ ニフティというニフティ会員向けアプリでスクラムマスターをやっています。ほかにも、N1!というニフティのスペシャリスト制度でスクラムエヴァンジェリストを担っています。 というわけで、今日のアドベントカレンダーでは単なる1on1ではなく「スクラムを支援する方法としての1on1」について紹介します。 1on1の失敗 数年前、自分が初めて1on1で聞く側(コーチ)の立場になったときは、あまり効果的なものにできませんでした。聞き手としての能力不足などもありますが、1on1の目的を誤っていたことに一番の理由があったと今は考えています。 機能しなかった1on1の目的 チームメンバーのキャリアパスをフォローする 抱えている課題について聞き、解決するためのアクションを出す ぱっと見、1on1の目標としてそれなりにありがちというか、そこまで悪い感じはしないとは思います。 実際、チームメンバーの目標や課題を知る場としては悪くはありませんでした。しかし、以下の理由で効果的に機能しませんでした。 チームメンバーの課題に対して良いアドバイスができない チームメンバーの目標や課題を、1on1をやっている自分だけが知っている(チームに共有しない)メリットがわからない 当時はスクラムマスターとしてではなくチームリーダーとして1on1をやっているという意識だったこともあり、なにかチームメンバーの悩みを解決したり、目標に近づけるようなアクションを 自分が提示しなくてはいけない と思い込んでいました。しかし、人の課題すべてに答えを見つけられる人間なんていません。当然失敗しました。 その後コーチングについて知ったことで、今までの活動がうまくいかなかった理由がわかりました。 アドバイスは成長にはつながらない 「良いアドバイスができなかった」と書きましたが、もし完璧なアドバイスができたとしても、1on1に参加する人全員にとって意義のある時間にはできなかったと思います。 なにか課題があってアドバイスでそれがうまくいったとしても、それは成長ではありません。 スクラムマスターがチームのお母さんにならないための方法 でも書きましたが、考えれば自分でできたはずのものをアドバイスによって達成させてしまったなら、成長機会を奪った可能性さえあります。 コーチングの基本である「人の話を聞く」ことの背景には「その人の中に答えがあると信じる」ことがあります。それができないまま1on1をやってもほとんど得られる価値はありません。 もしアドバイスがほしいなら、特定の人でなくチームメンバー全員に話した方がいろいろな意見がもらえたり、フォローもしてもらえるかもしれません。1on1をアドバイスの場にしてしまうことが非効率な理由はそこにもあると思います。 スクラムマスターとして1on1をやろう この失敗をふまえて、チームリーダーとしてではなくスクラムマスターの活動のひとつとして、1on1の目的を変更しました。 うまくいかなかった1on1 チームメンバーのキャリアパスをフォローする 抱えている課題について聞き、解決するためのアクションを出す ↓↓↓ 改善後の1on1 傾聴力の訓練機会を増やす 相互のフィードバック機会を増やす スクラムマスターにしてほしい支援を伝える 以前の1on1の時に思っていた「チームメンバーを1on1によって助けたい」という気持ちは完全に捨てました。スクラムマスターとしても利があること、得になることを考えています。 この1on1の目的が、そのままスクラムマスターが1on1をやったほうがいい理由3つにつながってきます。 「傾聴力の訓練機会を増やす」のはなぜか ただ話を聞くだけは、多くの人ができている(つもりになっている)と思います。 しかし「話を聞き、その人の中にある答えが見つかるような質問をする」という聞き方はなかなかできるものではありません。 もともとある程度の上手い下手はあれど、一人では練習ができないので他人に協力してもらい練習する必要があります。 有志でコーチングの練習をしよう もしあなたがスクラムマスターではないとしても、将来マネジメント職を目指すなら今のうちに1on1の練習、特に「傾聴」の練習は絶対にしたほうがいいです。 現在自分は月1で1on1をしていますが、これだけでは到底訓練としては十分な回数ではありません。そのため、月1の1on1に加えて、隔週でコーチングの練習をしています。 自分のコーチングを効果的にスキルアップするためにはコーチ(聞き手)・プラクティショナー(話し手)のほかにそれを観察する「オブザーバー」が必要です。 オブザーバーを交えたコーチングの練習方法 コーチ・プラクティショナー・オブザーバーの3人1組をつくる 5分間、以下を行う プラクティショナー:なんでもいいので相談をする(スクラムに関する相談など) コーチ:その相談の答えをプラクティショナー自身が見つけられるように質問をする オブザーバー:コーチとプラクティショナーを観察する。オンラインの場であればビデオやボイスはオフにして存在感を消す。プラクティショナーが話しづらそうにしていないか、コーチはどんな表情で話を聞いているか、よい質問ができているかなどを観察する。 メンバーを変えてこれを繰り返す(3人であれば3回) これを「コーチングスキルアップセッション」と題し、1on1とは別に、コーチング能力を高める場として社内の有志で集まって隔週開催しています。 ここで得たスキルをチームの1on1で発揮していくようなイメージです。 「相互のフィードバック機会を増やす」のはなぜか プロダクトオーナー・デベロッパーはレトロスペクティブの場や、日々の中でスクラムマスターからなんらかのフィードバックを受け取る機会があります。 しかし、スクラムマスターは「スクラムマスターとして良かった・微妙だった」といったフィードバックをされる場がありません。自分がスクラムマスターとしてよりよく活動できているか、悩んだことがある人は多いと思います。 もう一点、スクラムマスターはスクラムの理解と実践を支援する立場にありますが、例えば誰かが良い行動・または改善できそうな行動をしているときに、すぐにフィードバックできるとは限りません。あとから思い返して気づくいたり、様子を見ていたら機を逃してしまうこともあるでしょう。 1on1の場でフィードバックの枠をつくれば、違和感なく相互にフィードバックすることができます。普段から問題なくフィードバックし合えている関係性であればこれは不要になってしまうかもしれませんが、チーム形成の途中にあるようなチームであれば、機能しやすいと思います。 もしかしたら、他者にフィードバックすること・されることに少し不安になってしまう人もいるかもしれません。こういうときは、一度に大量のフィードバックをしないことをお互いに約束しましょう。GOODひとつ、デルタ(改善点)ひとつくらいであれば、不安感も軽減されるはずです。 「スクラムマスターにしてほしい支援を伝える」のはなぜか スクラムマスターは、スクラムチームや組織へのスクラム理解と確立のための支援を行いますが、その支援の方法はどこにも決められていません。また、チームのどの部分を支援すべきかも、誰も教えてはくれません。 スクラムマスターとしてチームを観察していると、チームの長所、または足りていない部分が見えてくると思います。しかし、逆にチームからスクラムマスターはどう見られているでしょうか。自分が十分に支援ができているか気になるが、それをチームメンバーに問いかけるのは難しいという人もいるでしょう。 1on1の場であれば、比較的それがやりやすいです。事前に「自分はスクラムマスターとしてスキルアップしたい。なので、スクラムマスターからどのような支援があると嬉しいか知りたい」と言っておくことをお勧めします。そうすれば、スクラムチームもポジティブな気持ちでスクラムマスターに要望を伝えられます。 スクラムマスターはスクラムを教える立場になりがちなため、自分自身の「スクラムマスター」という役割をチームに主体的に理解してもらうことは難しいのではないかとずっと思っていました。 ですが、スクラムマスターもまたチームによって育ててもらうという考えを持つようになったことで、「チームからスクラムマスターとしての課題を与えてもらう」「その課題がわかるチームになるためにスクラムの理解を促す」という好循環が生み出せるようになった気がします。 スクラムマスターが1on1をやったほうがいい3つの理由 まとめです。 人の課題を聞く機会が増えるほど傾聴力の訓練ができる スクラムマスターとしてのフィードバックをもらう機会ができる チームメンバーが具体的に必要としているスクラムの支援がわかる この3つは、なかなか普段の業務の中では機会をつくりにくいものです。 もちろん1on1をやらなくてもスクラムマスターは務まりますが、1on1を行った方が効果的なアプローチがしやすくなります。特にチーム形成がまだ過渡期にあるチームだと機能しやすく、スクラムマスターにとっても得なことが多いです。 最後に:1on1は人のためならず…… 1on1というと、どうしても聞く側(コーチ)が上位者で、話す側(プラクティショナー)が下のようなイメージを持っていました。 しかし、プラクティショナーだけでなく、コーチのほうもスキルアップしたいものや得たいものを明確にすることで、1on1の場を「与える人・請う人」ではなく、「与える人同士」の状態に近づけることができます。 スクラムマスターのフィードバックループをどうまわすかを考えた時に、1on1はかなり機能的だと思います。1on1は、相手のためだけでなく「自分と相手のために」やるものじゃないかと思います。 スクラムマスターがスキルアップする手段として、1on1にぜひ挑戦してみてください! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する 明日は、三浦さんによる「フォースフィールドアナリシスでチームのアクションを作る」です。 一生懸命書いてる感じがslackから伝わってきたので…きっと書けてるとおもいます(圧) お楽しみに!
この記事は、 ニフティグループ Advent Calendar 2023 12日目の記事です。 はじめに こんにちは。ニフティに新卒で入社して五年目の佐々木です。今回はAWSのサービスの一つである CloudFront についてコスト削減を行う方法を紹介します。 以前ご紹介したS3のコスト削減については、 こちら のブログ記事をご参考ください。 背景 ニフティではサービス基盤にAWSを活用しており、コスト削減のためにサービスのインフラ効率を追求する取り組みや、有志で取り組んでいる社内でのコスト削減勉強会なども実施しています。 前回の記事 に引き続き、CloudFront周りでもコスト削減を行う機会があったので、コストを下げられるポイントや注意点について簡単にご紹介したいと思います。 この記事の内容 触れること CloudFrontのコスト削減方法 CloudFrontのコストを削減する上での注意点 実際に試した社内でのコスト削減事例とTips 触れないこと CloudFront自体の詳細の説明 自社での詳しいインフラ構成(コスト削減が目的のため、今回は一般的なインフラ構成のご紹介に限らせていただきます) CloudFrontの基本 CloudFrontとは、ざっくり説明するとウェブコンテンツの配信速度の高速化を目的としたAWSのCDN(Content Delivery Network)サービスです。CDNはサーバーの負荷を軽減し、コンテンツをキャッシュすることで素早くユーザにデータを提供するためのシステムです。 CloudFrontを用いることで、システムのサーバーの負荷を下げたり、エッジロケーションの活用によるコンテンツの配信の高速化などを実現できるので、そのような目的でコンテンツを配信するサーバーの前段に置かれることが多いです。 CloudFrontのコスト削減 課金形態 大規模サービスなどでは、CloudFrontはAWSの中でも特に活用されることが多いサービスですが、課金形態としてはリクエスト量に応じて課金されるため、リクエスト数が多いサービスだと特にコストが高くなってしまいがちです。 サービスの運用にかかるコストを正確に把握するためにも、まずはCloudFrontの課金形態をご紹介します。 2023年11月現在では以下のようになっています。 データ転送に対する課金 エッジロケーションから外部のインターネットに向けて転送されたデータ量に対して課金されます HTTP/HTTPSリクエストに対する課金 リクエストの数とタイプ(HTTP または HTTPS)に応じて課金されます その他にも課金要素はありますが、主に重要なのはこの2つだと思います。 詳細はこちらの AWS公式の資料 をご参考ください。 コスト削減方法 次に、CloudFrontに対する一般的なコスト削減方法をご紹介します。 CloudFront Security Savings Bundle を利用する CloudFront利用料の前払いの義務を果たすことで利用料に対する割引が受けられる制度です。CloudFrontの向けのSavings Plansのようなものです。 1年間、月額最低使用料をコミットする前提で、CloudFrontの請求額に対して最大30%のクレジットが適用されます。 既に運用しているサービスのアーキテクチャに変更予定がない場合や、年間で利用されるCloudFrontの料金がある程度決まっている場合は、もしまだ利用していなければ迷わず購入してよいと思います。一番手軽かつ削減幅も大きいです。 キャッシュを最適化する CloudFrontにはCDNとして一般的なキャッシュ機能が存在します。 画像などのコンテンツをキャッシュさせ、キャッシュヒット率を上げることで、エッジロケーションからのサービス提供を増やしてコスト削減ができます。 削減方法として主に2つで、どちらの方法でもコスト削減ができますが、できるのであればなるべくユーザーに近い箇所でキャッシュをすることでより大きなコスト削減効果が狙えるかと思います。 ブラウザキャッシュ:ブラウザ側でキャッシュを行い、CloudFrontへのリクエストそのものを軽減させる方法 CDNキャッシュ:CloudFront側でキャッシュを行い、CloudFrontの背後にあるサーバーへのリクエストを軽減させる方法。(この場合はCloudFront自体のコスト削減効果はなく、オリジンとして設定している側のコストが減る可能性があります。) データ転送量を削減する 画像やファイルの圧縮を行い、データサイズをできるだけ小さくすることで、そもそものCloudFrontからのデータ転送量を削減できます。 CloudFrontを経由させる必要がないものはCloudFrontを使わない こちらはコンテンツを取得するための構成や取得内容の根本を見直す方法です。 表示するコンテンツの内容次第では必ずしも常にCloudFrontを経由して使う必要はないため、別の代替案などを検討することなどでコスト削減の効果があるかもしれません 不正なbotアクセスを遮断する もし不適切なbot等のトラフィックがもしあれば、CloudFrontの前段にあるWAFで該当のUAやIPを遮断することで、不要なリクエストを削減できる可能性があります。 しかし、サイトにアクセスしているbotがGooglebotなどの有益なクローラーである可能性もあるため、遮断する際に本当に不要なbotアクセスなのかをしっかり調べた上で防ぐなどの注意が必要です。 WAF自体へのリクエストやWeb ACLにルールを追加することでもコストはかかり、必ずしもコスト削減に繋がるとは限らないので、周辺のインフラ環境も含めて考慮が必要です。 実際に社内で試したこと 画像ブラウザキャッシュでのリクエスト削減 こちらはブラウザキャッシュを最適化することで、CloudFrontへのリクエスト自体を減らしてしまう方法です。 画像やCSS、JSファイルなどあまり変更されないコンテンツはブラウザキャッシュを効かせることで、そもそものリクエストを大幅に削減することができます。 担当したサービスでは、ブラウザキャッシュのキャッシュ有効期間(Cache-Control など)を見直すことで、結果的に合計1,000USD/月 程度のコスト削減ができました。 キャッシュ周りのコスト削減に関しては、 SvelteKit, Next.jsの導入事例紹介など 〜ニフティのフロントエンドの今とこれから〜 でも紹介しています。 データ取得経路をCloudFront経由の構成からS3直アクセスにする こちらに関しては、データ取得の経路そのものを変えてしまう方法になります。 この方法については、コンテンツ自体の取得は必須であるものの、アクセス自体をCloudFrontを経由せずとも行える場合に有効だと思います。 取得経路そのものを変えることになるのでやや実装自体が手間ではありますが、取得するコンテンツのリクエスト数が多い場合は効果が大きくなります。 比較として、2023年11月現在ではap-northeast-1 の場合、 CloudFront と S3 のリクエスト料金は以下のようになっており、CloudFrontと比べるとS3のアクセスの場合はリクエスト量だけで半分以下のコストになることが分かります。 CloudFront 0.0120 USD(HTTPSリクエスト1万件あたり) 0.0090 USD(HTTP リクエスト1万件あたり) S3 0.0037USD(GETリクエスト1万件あたり) コンテンツの取得構成変更 次に社内での削減事例を簡単にご紹介します。 今回コスト削減を行った担当サービスでは、WEBサイトの表示に必要なデータを取得するためCloudFrontを経由していましたが、コスト削減のためこれをCloudFrontを経由せずにデータ取得を行う構成に変更しました。 詳細のインフラ構成はここでは割愛しますが、以下のようにインフラ構成を変更しました。 変更前の構成 ブラウザ → CloudFront → フロントエンドサーバー → CloudFront → バックエンドサービス 変更後の構成 ブラウザ → CloudFront → フロントエンドサーバー → バックエンドサービス 今回の構成においては、バックエンドで取得するコンテンツがキャッシュによる恩恵が少ないものだったこともあり、バックエンドの前段にCloudFrontを挟むのは過剰な構成になっていました。 そこで、コスト削減のためにフロントエンドから直接バックエンドを叩く形に変更しました。この方法を取ることで、周辺のインフラ構成も含めて合計1,200USD/月 程度のコストを削減することができました。 ただ、取得するコンテンツの種類によっては、CloudFrontでのパスのルーティングや、WAFを用いたセキュリティの制限をかけた方が良い場合もあるかと思うので、そこはコンテンツの要件と照らし合わせて構成を練る必要がありそうです。 不要な海外botアクセスの制御 こちらも同じく、不要なアクセスそのものを減らし、CloudFrontへのリクエスト料金を削減してしまう手法です。 もし、不適切なbotなどのアクセスがある場合は、WAFなどで防ぐことができます。 明らかにコストが跳ね上がっている場合は異常に気づけますが、今回はこれとは別にCost ExplorerでCloudFrontの料金をリージョン別に見ていてたまたま気づき、特定のUAからの海外アクセスが定期的に発生していることを発見できました。 こちらに関してはWAFで特定のUAをブロックすることで簡単にアクセスを制御することができました。 学んだ教訓とTips 今回のコスト削減の実施全体を通しての感想ですが、CloudFrontについてはとにかくリクエスト料金が高く付きがちなので、如何にリクエスト量を効率的に削減できるか?が勝負になるのかなと個人的には感じました。 また、普段キャッシュを意識するとコンテンツ取得の前段にCloudFrontを挟もうという設計になることが多いかと思いますが、コスト削減の観点で見ると「CloudFrontを経由させずにS3に直アクセスをさせる」ことで意外とコストを減らせるのだな、という気づきを得られたことが一番の発見でした。 心構えとしてのTipsになってしまいますが、「とりあえずCloudFrontを前段に設置しよう!」という考えではなく、結局はコスト面やリソース効率なども考慮した上でCloudFrontの利用やキャッシュ設定を適切にチューニングしていくことを意識して設計してくことが大事だと思いました。 後からアーキテクチャに手を加えると大変なこともあるので、設計の段階でコスト効率の良いアーキテクチャを考慮できると良いのかなと感じました。これは今度の業務にも学びとして活かしていきたいです。 まとめ 今回はCloudFrontのコスト削減方法についてご紹介しました。 実際のコスト削減方法についてですが、社内事例として紹介したように、現状のコストを分析することで不要なbotアクセスなどの発見に繋がることがあったので、意図していないコストに気づくという観点でもまずは「普段から使用しているサービスをCostExplorerなどで見ておく癖をつける」といった点に尽きるのかなと思います。 また、合わせて利用しているサービスの料金形態を把握することで、具体的なコスト削減のアイデアが浮かんでくると思うので、実際のコストと照らし合わせて確認するようにすると良いと感じました。 他のAWSサービスでもコスト削減事例が思いついたら別のブログ記事にする予定なので、また機会があれば紹介したいと思います! 参考記事 https://aws.amazon.com/jp/cloudfront/ https://aws.amazon.com/jp/s3/pricing/ We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass 明日は、kinari321 さんの「isucon初参加&0点劇」というテーマの記事のようです。お楽しみに!
この記事は、 ニフティグループ Advent Calendar 2023 12日目の記事です。 ニフティのN1! Machine Learning Product Engineer 中村です。 最近はAmazon Bedrockを活用した生成AIをプロダクト実装することにハマってます。 NIFTY Tech Book #1を作りました 完成したNIFTY Tech Book #1 以前の投稿 でもお知らせした通り、技術書典15および全社イベントのNIFTY Tech Day 2023で来場者特典として、NIFTY Tech Book #1を配布させていただきました。 技術書典では120冊が3時間ほどで頒布終了となり、終了後も「まだ配布していますか?」と伺っていただいた方も多くいらっしゃいました。(間に合わなかった方々、ごめんなさい!) まだ電子版は購入可能になっていますので、まだご覧になっていない方はぜひこちらからご覧ください。 どうやって作ったのか? というわけでNIFTY Tech Bookというものを作ったのですが、自分はこういった印刷物を作成したことが今まで一度もありませんでした。どういうふうに進めて、どういうことを考慮するべきなのか?ということを自分なりにまとめてみたいと思います。 なおニフティには技術広報という固有の肩書きは現在は存在しません。そのため、エンジニアリングを楽しもう・みんなで作ってみよう、という有志の会で成り立っています。 まず応募しよう 技術書典は半年に1度開催されている技術書同人誌イベントです。 https://techbookfest.org/ その他にも最近では技術書同人誌博覧会というイベントも年に2回ほど開催されています。 https://gishohaku.dev/ これは持論ですが、人間は締め切りがないと基本的に動けません。自分も他社の合同技術本を見ながら、「こういう本が自分でも作れたらな〜」と思っていただけで、実際には何も行動していませんでした。良くないですね。 そこで今回は、技術書典15にえいやと応募してしまって、やるしかないという状況を自分で作ってしまって、そこからどんどん動き出していきました。締め切り駆動開発は偉大です。 人を集めよう 自分が作りたいなと思っていたのが、「みんなでわいわいと集まって作る合同本」だったので、やると決めたら人を集めていきます。 ニフティでは2ヶ月に1度ほどLT大会が開催されるのですが、LT大会に登壇し、最後に番宣をするという形で人を集めたり、直接声をかけたりして、参加者を募っていきます。 Slack上で少し反応してくれている人には「じゃあ書こうね」とか「〇〇の内容でどうよ?」と声をかけていき、最終的に8人の執筆者が集まりました。 幸運なことに表紙を描いてもらえることにもなったので、表紙のデザインや入稿時のデータ作成などを分担することもここでできました。 (Special Thanks to 表紙を描いてくれたムサシ) 内容について 一般的な合同技術本であれば、エンジニアリング周りのことだけを扱うのだと思います。しかし、編集長の自分自身が「Team Geek」だったり「アジャイル式健康改善ガイド」だったり「Cooking for Geek」だったりのマインド・健康・人間の暮らしのような本も好物にしているため、今回は技術に関連する内容であれば無制限として執筆者のみなさんには書いてもらいました。 あとで執筆者に聞くと、「何でも書いていいよ、と言われたので本当に自由に書いた」という意見をいくつかもらいました。結果的に本全体の内容バランスも良かったので、この方針は今回の執筆者には上手くはまったと思っています。 発売後に行われた技術書典のYouTubeでの紹介でも、「C言語でQRコードを書くという内容から、ランニングを続ける技術まで本当に幅広い」ということで特徴づけられていたので、自由に書いてもらって良かったなと思っています。 執筆しよう 執筆作業に入るわけですが、今回はRe:VIEWというツールを使わせていただきました。 Re:VIEWは裏側はTeXで動作する執筆ツールで、執筆内容なども含めて全てをGitHub上で管理する体制にしました。同時に執筆者のためのDiscordサーバも開設し、コミットがあるたびにDiscordに通知が飛んでくるようにして、互いに執筆しなければいけないという空気感を作ることに(多分)成功しました。 「作業が終わったらPRを出してレビューしてもらうこと!」としていたので、誰が執筆が完了したのかが認識でき、自分自身も校閲漏れがなく進められたので、もう一度本を作るとしてもRe:VIEWで作ると思います。 (素晴らしいツールを開発していただいている開発者およびコミュニティのみなさんありがとうございます) 校閲しよう 執筆者に執筆してもらい、全部の文章が集まったら、校閲作業が入ります。校閲作業が必要なので、印刷所の入稿ぎりぎりを締め切りにすると痛い目をみます。(今回は2週間前を締め切りとしていたので大丈夫でした) 有志で集まって書いているとはいえ、会社の名前で書いている書籍のため、会社のネガティブキャンペーンになりそうな内容については編集者としては許すわけにはいきません。 ということで校閲をするわけですが、120ページもある本文を校閲するのはなかなか骨が折れます。 エンジニアらしくこの作業を自動化できないんだろうか?と考え始めました。 AIは編集長の夢を見るか? そこで、人知れずチャレンジしていたのがAIによる校閲作業です。実は私は本業は機械学習エンジニアなので、昨今のAIブームの流れには乗れているはずです。というわけで、大規模言語モデル(Amazon Bedrock, Claude2)を使って校閲をさせてみます。 ・プロンプト あなたは優秀な日本語の編集長です。これから入力する本文に対して、会社の評判を下げるような文章や、誤字脱字、内容の間違いがあれば、行番号とともに指摘してください。入力本文の最初を1行目とします。もし指摘事項がない場合には「指摘事項はありません」と返してください。 ### 出力例 line 1 : 「解凍」ではなく「解答」を使うべきです line 5 : 会社のネガティブキャンペーンになっている可能性があります ・出力(全て) 指摘事項はありません ・・・うまくいかない・・・。 というわけで、なぜかわかりませんがAIによる校閲は全くできませんでした。自分で人力で行ったほうが早いと判断して、2日ほどで黙々と校閲作業を行い、結果をGitHubにコミットして執筆者に見てもらうという作業を続けて、最終版に向けた校閲を行いました。 今でも生成AIはどんどん進化しており、次巻を作るときにはもっと優れたAIやプロンプトが生まれているのではないかという期待を持っています。次回こそAIによる校閲が成功すればいいなと思っています。 いざ、入稿! 校閲も済んだら入稿です。冒頭にも述べたように、自分には入稿の経験が全くないです。しかし、Re:VIEWを使って執筆している場合、設定を少し変更するだけで入稿用の本文データが生成できます。隠しノンブルなども完璧に入れてくれるため、これによって知識がない自分でも入稿を行うことができました。 そして、これは書籍の中にも書いているのですが、ここでトラブルがありました。 NIFTY Tech Bookは最初から左綴じを想像して作っており、入稿も左綴じでお願いをしました。しかし、自分の伝達ミスで、表紙データが右綴じを想定したデザインになっており、表紙を捲ると逆向きに始まってしまうという状態になっていました。 幸いなことに、印刷所の人が向きが逆なことに気づいてくださり、再入稿を行うことでトラブルを未然に防ぐことができました。印刷所の皆さん、その節はありがとうございました。 印刷所に入稿するという経験はなかなかないですが、印刷所の方々はプロなので、そこに上手く甘えつつ入稿に挑戦するというのがベストなように感じています。 技術書典の場合は直接持ってきてくれる! あとは実際の本の完成を今か今かと待ちます。 技術書典の場合、バックアップ印刷所に指定されている印刷所(今回は日光企画さんにお願いしました)を使うと、オフライン開催の当日に、会場へ直接搬入されます。そのため、自分達も完成品を当日に初めて見ることになります。 技術書典15は11時開場ですが、10時に会場入りしたメンバーで「やっぱり紙の本はいいね」「実際に物になると嬉しいね」「この厚さを無料配布している企業はここしかない!」などなど、会場前から盛り上がっていました。 みんなも紙の本を書こう そのような流れで今回技術書典に参加してきました。 最初は自分の思いつきから始まった今回のNIFTY Tech Book #1ですが、執筆をしてくれた有志のみなさんからは 「紙の本になって実物を見ると頑張って書いて良かったなと思った」 「実物が想像以上に厚くなってびっくりした」 「もう少しテーマを絞って、ニッチなテーマで有料にして売ってみたい」 「#2もあるならぜひ書きたい」「今回は準備してくれてありがとう」 という嬉しい言葉をたくさんもらいました。 最初は知らないことだらけなので大変なのですが、終わってみれば「案外大したことなかったな」と思いますし、何物にも変え難い達成感を感じられます。技術書典が終わった後の1人打ち上げは最高の気分でした。(そういえば執筆者打ち上げをしてないなとこの記事の執筆中に思い出しました) この記事を読んでいるあなたにも、ぜひ本の執筆、できれば紙の本の印刷に挑戦してもらえれば、この体験記を書いた意味があるなと思います。みなさんの挑戦を応援しています。 NIFTY Tech Book #2も夢想中なので、ぜひ一緒に書いてくれる人を募集しています! 明日の ニフティグループ Advent Calendar 2023 は「12月だからp5.jsで雪を降らす」です。ビジュアルが見える系のプログラミングって面白いですよね。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 11日目の記事です。 はじめに こんにちは。ニフティ株式会社の添野翔太です。 AWS Lambda でGoランタイムがサポートされなくなるため 弊社のブログ記事 を参考にカスタムランタイムに移行しようとした際にハマった問題と解決方法をお伝えします。 また、このハマったポイントはサービス固有のものではないので、見ていただいた方の助けになっていれば幸いです! 背景 AWS LambdaでGoランタイムがサポートされなくなるためカスタムランタイムに移行しようとしました。 ハマったところ 弊社のブログ記事 を参考にして、実施したところ、 INIT_REPORT Init Duration: 10009.48 ms Phase: init Status: timeout INIT_REPORT Init Duration: 10010.51 ms Phase: invoke Status: error Error Type: Runtime.Unknown というエラーが出るようになりました。 原因 この問題の原因としては aws-lambda-goモジュール のバージョンがカスタムランタイムに対応したバージョン(v1.18.0/ 関連PR )よりも古いもの(v1.14.0)を使っていたことでした。 そこで、アップデートをしたところエラーは出なくなりました。 % go get github.com/aws/aws-lambda-go go: upgraded github.com/aws/aws-lambda-go v1.14.0 => v1.41.0 provided.al2はカスタムランタイムなので、 Lambda ランタイム API がサポートされているバージョンを使ってビルドしていない場合、カスタムランタイムがAWS Lambdaから呼び出しイベントを受信し、応答データをAWS Lambda実行環境内に送り返せない状況になります。そしてタイムアウトになるようです。 まとめ 今回は、AWS LambdaでGoランタイムからカスタムランタイムに移行した際にハマったことと解決策について紹介しました。 明日は @uyuqui1718 さんの「AWSのコスト削減を試した話(CloudFront編)」です。お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 11日目の記事です。 基幹システムグループ N1! オートメーションスペシャリストの南川です。 現在、私が所属しているチームでは複数のプロダクトの開発をしており、プロダクトごとに GitHub リポジトリも分かれています。複数のリポジトリの Issue を一つの Project で管理するために、 Issue を Project に追加する作業を行う必要がありますが、それらを手動でやるとなると面倒だと思います。手動でやるとなると手間もかかりますし、Project への追加し忘れや追加先を間違えるといったミスも出てきます。この作業を自動化することで、手作業によるコストや作業ミスを減らし、ストレスの無い開発をすることができます。 今回は、GitHub の Issue を自動で Project に追加する方法として以下の3つを紹介します。 (方法 A) 組み込みの自動化ワークフローを使う (方法 B) GitHub Actions + actions/add-to-project を使う (方法 C) GitHub Actions + GitHub CLI を使う なお、こちらの記事は2023年11月に開催された技術書展15にて頒布したニフティの技術書「 NIFTY Tech Book #1 」で掲載された第8章の内容とほぼ同一のものとなります。無料で電子版を頒布しているので、他の記事も気になる方はダウンロードしてみてください。 また、明日に技術書の主催による技術書を出版するまでの記事も投稿されるので、そちらも要チェックです! (方法 A) 組み込みの自動化ワークフローを使う 2023年1月 エンタープライズアカウント向けに、特定の条件を満たしたIssueをProjectに追加する組み込みワークフローがリリースされました。 組み込みワークフロー では、ノーコードで以下のような一連の流れを自動化することができます。 項目 (Issue や Pull request) がプロジェクトに追加された場合、それらのステータスを Todo に設定する。 項目が Close された場合、それらのステータスを Done に設定する。 新規作成や更新された項目が特定の条件を満たしていた場合、それらを Project に追加する。 設定方法については 公式のドキュメント が分かりやすいのでそちらをご参照ください。 メリットとデメリット この方法のメリットは、コードを書かずに GUI 上で設定でき、手軽に導入できるという点です。組み込みワークフローは複製することができ、複数のリポジトリに対する導入も容易だと思います。 しかし、作成できる個数に上限が決まっているというデメリットもあります。このワークフローを作成できる上限はプランによって異なり、以下の表の通りです。 プラン 上限個数 GitHub Free 1 GitHub Pro 5 GitHub Team 5 GitHub Enterprise Cloud 20 GitHub Enterprise Server 20 自動追加ワークフローの個数の上限 Each workflow can target a different repository, allowing you to add items from up to four repositories. https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/adding-items-automatically また、最大4つのリポジトリまで項目を追加できるということで、それ以上のリポジトリを持っている私のチームではこの方法では厳しいと判断しました。 そのような場合に、これから紹介する2つ目、3つ目の方法が有用です。 GitHub Actionsを使う GitHub Actions は GitHub まわりのワークフローを自動化するプラットフォームで、以下の用途などに使うことができます。 Issue が作成されたら、 Project に追加する。 Pull request が作成されたら、 lint 、テストを実行する。 失敗した場合はマージをブロックする。 Pull request がマージされたら、開発環境にデプロイする。 コミットがプッシュされたら、 AWS CodeCommit とミラーリングする。 GitHub Actions で Issue を Project に追加する方法は以下の2通りになります。 actions/add-to-project を使う。 GitHub CLI ( gh project item-add コマンド) を使う。 それぞれの方法についてこれから説明していきます。 (方法 B) GitHub Actions + actions/add-to-project を使う actions/add-to-project は、 Issue や Pull request を Project に追加する Action です。 GitHub Actions と actions/add-to-project を使って、新たに作成した Issue を Project に自動追加させる手順を以下に示します。 (1) Personal Access Token の作成 公式のドキュメント を参考に、 Classic 版の Personal Access Token (PAT) を作成します。指定するスコープは以下の通りです。 project repo (プライベートリポジトリの場合) 参考 : https://github.com/marketplace/actions/add-to-github-projects#creating-a-pat-and-adding-it-to-your-repository (2) Repository Secret の追加 公式のドキュメント を参考に、 Repository Secret に手順 (1)で作成した PAT を追加します。 今回追加する Secret は以下の通りです。 Name : ADD_TO_PROJECT_PAT Secret : < 手順 (1)で作成したPAT ( ghp_ …) > (3) ワークフローファイルの作成 .github/workflows 配下に以下のファイルを作成します。 name: Add issue to project on: issues: types: - opened jobs: add-to-project: name: Add issue to project runs-on: ubuntu-latest steps: - uses: actions/add-to-project@v0.5.0 with: project-url: https://github.com/<orgs|users>/<ownerName>/projects/<projectNumber> github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} 解説 name: Add issue to project name では、リポジトリの Actions タブで表示されるワークフロー名を指定しています。 ここで指定した名前は、 Actions タブの実行履歴で表示されます。 on: issues: types: - opened on では、このワークフローを実行するトリガー (条件) を指定しています。 issues は、ワークフローのリポジトリ内の Issue に対し、 types で指定したアクティビティが発生したときにイベントをトリガーするイベントです。 今回は opened というアクティビティを指定しており、 Issue が Open になる(作成される)時にトリガーが発動します。 jobs: add-to-project: name: Add issue to project runs-on: ubuntu-latest steps: ... jobs では、トリガーが発動した際に実行するジョブを定義します。 今回は add-to-project というIDのジョブを定義し、名前 ( name ) とジョブを実行するマシン ( runs-on ) を指定しています。 - uses: actions/add-to-project@v0.5.0 with: project-url: https://github.com/<orgs|users>/<ownerName>/projects/<projectNumber> github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} uses では、このステップで実行するアクションを指定し、そのアクションの入力パラメータは with で指定します。 with で指定する入力パラメータは以下の通りです。 project-url (必須) Issue を追加する GitHub Project の URL。 github-token (必須) repo と project のスコープを持つ Personal Access Token(PAT) 。 labeled (オプション) Project に追加する対象となる Issue をフィルタリングするためのラベル。 複数指定する場合はカンマ区切りで指定します。 指定しない場合は全ての Issue を Project に追加します。 label-operator (オプション) ラベルフィルタの動作を指定します。 AND, OR, NOT のいずれかを指定します。デフォルトは OR です。 (方法 C) GitHub Actions + GitHub CLI を使う GitHub CLI は、コマンドラインから GitHub を使用するためのオープンソースのツールです。 Issue の作成や一覧表示、 Pull request の差分表示やマージなどが出来ます。ドキュメントは こちら になります。 また、 GitHub Actions ではシェルを使用したコマンドラインプログラムも実行することができます。そして、 GitHub CLI はプリインストールされており、 GitHub Actions のワークフロー上で使うことができます。 GitHub Actions と GitHub CLI を使って、新たに作成した Issue を Project に自動追加させる手順を以下に示します。なお、手順(1)(2)は方法Bの手順(1)(2)と同様のため、省略します。 (3) ワークフローファイルの作成 .github/workflows 配下に以下のファイルを作成します。 name: Add issue to project on: issues: types: - opened jobs: add_issue_to_project: name: Add issue to project runs-on: ubuntu-latest steps: - run: gh project item-add $PROJECT_ID --owner $PROJECT_OWNER --url $ISSUE env: GITHUB_TOKEN: ${{ secrets.ADD_TO_PROJECT_PAT }} ISSUE: ${{ github.event.issue.html_url }} PROJECT_OWNER: <ownerName> PROJECT_ID: <projectNumber> 解説 - run: gh project item-add $PROJECT_ID --owner $PROJECT_OWNER --url $ISSUE env: GITHUB_TOKEN: ${{ secrets.ADD_TO_PROJECT_PAT }} ISSUE: ${{ github.event.issue.html_url }} PROJECT_OWNER: <ownerName> PROJECT_ID: <projectNumber> run では、このステップで実行するコマンドを指定します。 GitHub CLI を使う各ステップでは、環境変数 GITHUB_TOKEN で必要なスコープを持つトークンを定義する必要があります。 今回は、 2023年7月に追加された gh project item-add コマンドを使って、 Project に Issue を追加します。 env では、このステップで利用する 環境変数 を定義します。 「 ${{ github.event.issue.html_url }} 」 でトリガーの発火元(作成した) Issue の URL を取得できます。 これは、 コンテキスト (Context) と呼ばれ、これを使うことで、変数やランナーの環境、ステップの情報などにアクセスできるようになります。 メリットとデメリット GitHub Actions を使う方法のメリットは、自由度が高いという点です。今回は Issue の Project への追加だけですが、 actions/slack-send を使って Slack チャンネルに Issue が作成されたことを通知するといったことも可能です。 しかし、ワークフローファイルを作成するのが手間であるというデメリットもあります。ただし、一度作ってしまえばコピペだけで済むのでそこまで気にならないでしょう。 方法B vs 方法C actions/add-to-project と GitHub CLI のどちらを使えば良いかという話ですが、個人的にはどちらの方法でも良いかと思います。影響がありそうな違いでいうと、 actions/add-to-project は利用するバージョンを固定できますが、 GitHub CLI はプリインストールのためバージョンを固定するのは難しいかと思います。そのため、 GitHub CLI で今回使った gh project item-add コマンドについて破壊的変更があった場合、今回のコードに修正が必要になる可能性があり、その部分のメンテナンスコストを鑑みて決定することになるでしょう。 終わりに 今回は、作成した Issue を自動で GitHub Project に追加する方法を3つ紹介しました。紹介したそれぞれの方法についてですが、以下の判断基準で選ぶと良いと思います。 対象となるリポジトリが少なく、手軽に導入したい場合は、組み込みワークフローを使う方法A。 通知等の機能を追加するといった自由度を求める場合は、 GitHub Actions を使う方法B,C。 方法BとCはお好みで選ぶ。 方法Cはバージョン固定が難しいため、破壊的変更があった場合は対応が必要。 GitHub はアップデートで自動化などに役立つ便利な機能が追加されることがあるため、 変更履歴 を定期的に見てみるのもオススメです。 明日は、@iNakamura さんの「出版について何も知らない状態から技術書典に参加する技術」です。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 10日目の記事です。 こんにちは。会員システムグループでエンジニアをしている山田良介です。 私の担当するシステムではNext.jsへのシステムリプレースを行い、順調に稼働しています。開発効率向上、テスタビリティの向上など恩恵が大きい一方で、ブラウザサポートの面で課題も見えてきました。 Next.js化での課題 ReactはもともとSPAのためのフレームワークです。HTMLはほぼ空の状態からスタートし、ブラウザ上でJavaScript(React)がほぼ全てのDOMを構築します。 サーバサイドレンダリング(SSR)フレームワークであるNext.jsでもその基本は変わりません。サーバ上で一度JavaScriptを実行してHTMLを構築したのち、ブラウザ上でもう一度JavaScriptを実行し、状態を同期(hydration)することになります。HTMLのDOM全体をJavaScriptが管理している、という状況はReact単体で利用している場合と変わりません。 ここで次の2点が問題になります。 未キャッチのエラーが発生した場合、 ページ全体が非表示 になってしまう ブラウザによって対応する JavaScriptの文法が異なる ため、予期せぬ文法エラーが起こる 後者が非常に厄介です。「このブラウザはECMAScript20XXまで対応」のような形ならまだ対応しやすいですが、現実には個別の文法ごとに対応状況がバラバラで、全ての文法を網羅的に確認するのは現実的に不可能です。そして文法エラーが発生した場合、前者によりページ全体が落ちてしまいます。 Chrome、Edgeなどのモダンブラウザには自動アップデート機能があるため、基本的に最新版を使っている前提が置けます。しかしSafariはOSごとにバージョンが固定であるため、古いmacOS、iOSの環境でページ全体の表示ができない不具合が発生し、これに悩まされることになりました。 フォールバック対応 まずは緩和策での対応です。 User Agentによる強制リダイレクト 古今すべてのブラウザへ対応するのは不可能です。IE11などサポート対象外とするブラウザを明確に決め、User Agentによる判定でエラーページに遷移させることとしました。 if (checkSupportedBrowser(ua)) { ... } else { return { redirect: { permanent: false, destination: '/error_page' } } } なおエラーページもNext.jsで作ってしまうと、遷移先のエラーページも表示できないという事態に陥るため注意が必要です。 Error Boundaryの設置 Next.jsでエラーを未キャッチにしてしまうとDOM全体がアンマウントされてしまいますが、 Error Boundary を設置することで代替コンポーネントを表示することができます。これは通常のエラー処理でも有用です。 標準機能としては特定のメソッドを実装したコンポーネントを作る必要がありますが、サードパーティーの react-error-boundary などを利用することもできます。 最上位の_app.tsxに最低限設置するほか、必要に応じて各所に挿入することで非表示部分を最小限に抑えることができます。 _app.tsx const MyApp = ({ Component, pageProps }: AppProps): JSX.Element => { return ( <ErrorBoundary FallbackComponent={() => <DefaultFallbackPage />}> <Component {...pageProps}> </ErrorBoundary> ); }; export default MyApp; ただしErrorBoundaryコンポーネントそのものやフォールバック先のコンポーネントも文法エラーになってしまうことがあるため、レガシーブラウザ対応という観点では100%安全ではありません。 JavaScriptダウングレード対応 JavaScriptのトランスパイルにより古いブラウザに対応させることで、対応するブラウザの幅を広げる対応です。 トランスパイルの設定 TypeScriptの出力ターゲットをサポートしたいブラウザの対応範囲まで下げます。 tsconfig.json { "compilerOptions": { "target": "es5" ... } } またpackage.jsonでbrowserslistを指定することで、トランスパイル時に対応ブラウザが考慮されるようにします。文法は playground で確認すると良いでしょう。 Next.jsの場合、webpackでのJavaScriptおよびCSS Modulesのトランスパイル結果に影響します。 package.json { ... "browserslist": [ "> 0.5%", "Safari >= 11", "iOS >= 11", ... ] } transpilePackagesの指定 Next.jsはデフォルト設定のままだと、プロジェクト内のソースのみをトランスパイルし、node_modulesをトランスパイルしません。従って、ライブラリで新しい文法が使われている場合に対応できません。 node_modules内のパッケージをトランスパイルするためには、next.config.jsでtranspilePackagesを指定します。 next.config.js const transpilePackages = [ '@tanstack/query-core', '@tanstack/react-query', 'ky', 'zod', ]; /** @type {import('next').NextConfig} */ const nextConfig = { ... transpilePackages: transpilePackages } module.exports = nextConfig; 一括で全てトランスパイル対象にするようなオプションはなく、対象とするパッケージ名を個別に指定する必要があります。 なおjestで next / jest を使用している場合、この設定はjestのtransform設定にも反映されます。 polyfillの追加 上記設定ではトランスパイルで解決できる文法が修正されますが、polyfillが必要な文法に対応できません。エラーとなる文法を特定した上で、 polyfill.io を利用することで対処します。 _document.tsx const Document = (): JSX.Element => { return ( <Html> <Head /> <body> <Main /> <NextScript /> <Script src="https://polyfill.io/v3/polyfill.min.js?features=globalThis" strategy="beforeInteractive" /> </body> </Html> ); }; export default Document; polyfill.ioはUser Agentを元にブラウザごとにpolyfillを返してくれるサービスです。featuresでpolyfill対象の文法を指定することができます。上記例ではglobalThisに対するpolyfillが返ります。 調査環境の整備 上記対応は一度設定すれば恒久的に使えるようなものではありません。 改修やパッケージアップデートなどで要対応箇所が増える可能性があるため、継続的なメンテナンスが必要です。そのためにはデバッグ環境を用意する必要があります。 ブラウザテストサービスの利用 レガシーブラウザでの検証を実機で行うのは困難です。古いOSを搭載した実機を、確認したいブラウザバージョン数の分だけ用意する必要があります。またOSが古いことによるセキュリティリスクもあります。 このため、リモートでブラウザを借りて利用できるSaaSを利用することにしました。代表的なところだと BrowserStack や Sauce Labs などがあります。ブラウザ経由でSaaS側が用意したブラウザを操作することができ、開発者ツールなども使用可能です。 サービスによっては日本国内のサーバがなかったり、日本語入力に難があったりなどの問題はあるものの、最低限の動作確認はできるようになりました。 自動検知 リリースのたびにレガシーブラウザまで手動検証しなければならないのは面倒なので、なんとかして自動化したいところです。上記に挙げたサービスでは各種E2Eテストツールにも対応しているため、CIでの自動化ができそうです。これは現在検証中です。 ただしレガシーブラウザを対象とすることを考えると、以下の制約があります。 ツールはSelenium一択状態 JavaScript文法エラーそのものを検出することはできない Cypress や Playwright といった最近のE2Eテストツールは操作対象のブラウザバージョンが指定されており、基本的に最新のものを要求します。従ってレガシーブラウザ対応では利用できません。WebDriver対応ブラウザに対して汎用的に使える Selenium か、そのラッパーが現状唯一の選択肢になっています。 また文法エラーはブラウザコンソールログへの出力で確認が可能ですが、コンソールログを取得する機能はWebDriverの規格にはありません。Chromeの独自機能としては存在しますが、Safariなどでは利用できません。このため、エラー検知は画面要素の検証によって行い、具体的なエラー内容は別途手動でデバッグするしかなさそうです。 また、 Sentry などのエラートラッキングツールを利用することでも検知は可能です。ただしSentryに到達する前に文法エラーでJavaScriptの読み込みが停止した場合、検知不可能になります。 まとめ Next.jsでのレガシーブラウザ対応についてご紹介しました。 Next.jsに限らず、JavaScriptを多用するモダンなフロントエンドフレームワークは互換性の問題を抱えがちです。サポートするブラウザのポリシーを明確にし、検証体制を整えた上で臨むようにしましょう。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 7日目の記事です。 こんにちわ!NIFTY engineering運用チームのいかりがわです! 今回はWordPressのテーマをGitHub管理できるようにし、EC2に自動でデプロイするようにしたので、その手法をまとめていきたいと思います。 背景 私たちが運用しているNIFTY engineeringでは、何らかの変更があったときは FileManager というWordPressのファイル管理用プラグインを使って手動で変更を加えていました。 しかし、これでは手動での変更反映になり、以下のような問題が起こります。 人為的なミスが発生する可能性が高まる 設定や手順のミス、環境の不整合などが起きやすくなります。 一貫性が保てない NIFTY engineeringでは、本番環境、開発環境の2つの環境を用意しています。 運用上では開発環境で確認後、本番環境にデプロイするようにしていますが、緊急時の対応などは急いで対応するため、本番のみに反映しがちになります。 ニフティのプロジェクトでは基本的にCI/CDパイプラインが整備されています。 NIFTY engineeringもCI/CDのパイプラインを使用して変更を容易にしていきたいと考えており、AWS CodePipelineによる自動デプロイのインフラを作っていこうと考えました。 手法 私たちは、GitHub ActionsからAWS CodeCommitへソースコードをコピーして、CodePipelineを使用するようにしました。 また、WordPressのテーマをEC2に自動デプロイする手法を採用しました。 具体的な流れは以下の通りです。 まず、DeveloperがソースコードをGitHubリポジトリにプッシュします。 GitHub Actionsはリポジトリの変更を監視し、変更があったらトリガーします。 変更があると、GitHub ActionsはCodeCommitにソースコードをコピーします。 CodeCommit内のコードが変更されると、CodePipelineがトリガーします。 CodePipelineのデプロイフェーズにてAWS CodeDeployが実行されます。 CodeDeployにて、EC2の特定のディレクトリに変更をデプロイします。 これで、GitHubでのソースコードの管理が容易になり、自動デプロイによる効率的な運用が可能となります。 構成図 構成図は以下のようになっています。構成図内の処理の順番は手法の手順に沿っています。 TerraformでAWSのリソースを作る では実際にCI/CDインフラを作ってみたいと思います。 まず、AWSのリソースをTerraformで作っていきます。 ちなみにここではTerraformのプロバイダ設定やバージョンの定義は割愛して、リソースの定義のみにしています。 また、EC2インスタンスはすでに起動していることを想定します。 CodeCommit CodeCommitのリポジトリを作成します。リポジトリ名を指定するだけです。 resource "aws_codecommit_repository" "repository" { repository_name = "codecommit-repository" } CodeDeploy resource "aws_iam_role" "deploy" { assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Sid = "" Principal = { Service = "codedeploy.amazonaws.com" } }, ] }) } resource "aws_iam_role_policy_attachment" "deploy" { role = aws_iam_role.deploy.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole" } resource "aws_codedeploy_app" "deploy" { compute_platform = "Server" name = "deploy" } resource "aws_codedeploy_deployment_group" "deploy" { app_name = aws_codedeploy_app.deploy.name deployment_group_name = aws_codedeploy_app.deploy.name deployment_config_name = "CodeDeployDefault.OneAtATime" deployment_style { deployment_option = "WITHOUT_TRAFFIC_CONTROL" deployment_type = "IN_PLACE" } ec2_tag_set { ec2_tag_filter { key = "Name" type = "KEY_AND_VALUE" value = {デプロイ先EC2インスタンス名} } } service_role_arn = aws_iam_role.deploy.arn auto_rollback_configuration { enabled = true events = ["DEPLOYMENT_FAILURE"] } } aws_iam_role、aws_iam_role_policy_attachmentを使用し、CodeDeployで使用するIAMロールを作成します。 指定したポリシーはマネージドポリシーの AWSCodeDeployRole です。 また、aws_codedeploy_appを使用して、CodeDeployのリソースを作成し、aws_codedeploy_deployment_groupにて、CodeDeployで使用するデプロイメントグループを作成します。 以下のようにec2_tag_setでNameタグを指定することで、その名前と一致するEC2インスタンスを指定することができます。 これにより、CodeDeployがEC2へデプロイできるようになります。 ec2_tag_set { ec2_tag_filter { key = "Name" type = "KEY_AND_VALUE" value = {デプロイ先EC2インスタンス名} } } CodePipeline resource "aws_iam_role" "pipeline" { assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Sid = "" Principal = { Service = "codepipeline.amazonaws.com" } }, ] }) } resource "aws_iam_policy" "pipeline" { name = "codepipeline-policy" path = "/service-role/" description = "Policy for CodePipeline to deploy" policy = file("policies/codepipeline_policy.json") } resource "aws_iam_role_policy_attachment" "pipeline" { role = aws_iam_role.pipeline.name policy_arn = aws_iam_policy.pipeline.arn } resource "aws_codepipeline" "pipeline" { name = "pipeline" role_arn = aws_iam_role.pipeline.arn artifact_store { location = "unique-s3-bucket-name" type = "S3" } stage { name = "Source" action { category = "Source" configuration = { BranchName = "main" PollForSourceChanges = false RepositoryName = "codecommit-repository" } name = "Source" output_artifacts = ["SourceArtifact"] owner = "AWS" provider = "CodeCommit" run_order = "1" version = "1" } } stage { action { category = "Deploy" configuration = { ApplicationName = aws_codedeploy_app.deploy.name DeploymentGroupName = aws_codedeploy_app.deploy.name } input_artifacts = ["SourceArtifact"] name = "Deploy" owner = "AWS" provider = "CodeDeploy" run_order = "1" version = "1" } name = "Deploy" } depends_on = [aws_codedeploy_deployment_group.deploy] } aws_iam_role、aws_iam_policy、aws_iam_role_policy_attachmentを使用して、CodePipeline用IAMロールを作成しています。 また、aws_iam_policyではpolicies/codepipeline_policy.jsonというファイルを参照しており、そこにポリシーの設定が記述されています。(後述) aws_codepipelineを使用し、CodePipelineのリソースを定義していきます。 stageを使用することで、CodePipeline内の各フェーズを定義することができます。 今回は上で定義したCodeCommitとCodeDeployのリソースを紐付け、フェーズとして定義していきます。 stage { name = "Source" action { category = "Source" configuration = { BranchName = "main" PollForSourceChanges = false RepositoryName = "codecommit-repository" } name = "Source" output_artifacts = ["SourceArtifact"] owner = "AWS" provider = "CodeCommit" run_order = "1" version = "1" } } stage { action { category = "Deploy" configuration = { ApplicationName = aws_codedeploy_app.deploy.name DeploymentGroupName = aws_codedeploy_app.deploy.name } input_artifacts = ["SourceArtifact"] name = "Deploy" owner = "AWS" provider = "CodeDeploy" run_order = "1" version = "1" } name = "Deploy" } CodePipeline用IAMロール AWS公式のユーザーガイドを参考に作成しています。 https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/security-iam.html policies/codepipeline_policy.json { "Statement": [ { "Action": [ "iam:PassRole" ], "Resource": "*", "Effect": "Allow", "Condition": { "StringEqualsIfExists": { "iam:PassedToService": [ "ec2.amazonaws.com" ] } } }, { "Action": [ "codecommit:CancelUploadArchive", "codecommit:GetBranch", "codecommit:GetCommit", "codecommit:GetUploadArchiveStatus", "codecommit:UploadArchive" ], "Resource": "*", "Effect": "Allow" }, { "Action": [ "codedeploy:CreateDeployment", "codedeploy:GetApplication", "codedeploy:GetApplicationRevision", "codedeploy:GetDeployment", "codedeploy:GetDeploymentConfig", "codedeploy:RegisterApplicationRevision" ], "Resource": "*", "Effect": "Allow" }, { "Action": [ "ec2:*", "cloudwatch:*", "s3:*", "sns:*" ], "Resource": "*", "Effect": "Allow" } ], "Version": "2012-10-17" } CodePipeline用S3バケット CodePipelineでは各フェーズ間のデータの受け渡しに使用されます。 今回の例だと、CodeCommitからCodeDeployへソースコードを受け渡すために使用されます。 resource "aws_s3_bucket" "codepipeline" { bucket = "unique-s3-bucket-name" } resource "aws_s3_bucket_ownership_controls" "codepipeline" { bucket = aws_s3_bucket.codepipeline.id rule { object_ownership = "BucketOwnerEnforced" } } EventBridge これまでの定義で一通りのリソースは定義されました。しかし、これでは何をもってCodePipelineが実行され、デプロイが走るのかが定義されていません。 ここでEventBridgeを定義し、CodeCommitのリポジトリでソースコードが変更されたときにCodePipelineがトリガーされるようにしていきます。 resource "aws_iam_policy" "events_trigger" { description = "Policy for notice update to codepipeline" name = "events-trigger-policy" path = "/service-role/" policy = jsonencode({ "Statement": [ { "Action": [ "codepipeline:StartPipelineExecution" ], "Effect": "Allow", "Resource": "*", } ], "Version": "2012-10-17" }) } resource "aws_iam_role" "events_trigger" { name = "events-trigger-role" assume_role_policy = jsonencode({ "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "Service": "events.amazonaws.com" } } ], "Version": "2012-10-17" }) } resource "aws_iam_role_policy_attachment" "events_trigger" { role = aws_iam_role.events_trigger.name policy_arn = aws_iam_policy.events_trigger.arn } resource "aws_cloudwatch_event_rule" "trigger" { name = "codecommit-trigger-rule" description = "update event" event_pattern = templatefile("events/events_trigger_rule.tpl.json", { repository_arn = aws_codecommit_repository.repository.arn }) is_enabled = "true" } resource "aws_cloudwatch_event_target" "trigger" { arn = aws_codepipeline.pipeline.arn role_arn = aws_iam_role.events_trigger.arn rule = aws_cloudwatch_event_rule.trigger.name } IAMロールはCodePipelineと同様のやり方で定義しています。 EventBridgeはCodePipelineを実行することができれば良いので、StartPipelineExecutionというポリシーのみ定義しています。 { "Statement": [ { "Action": [ "codepipeline:StartPipelineExecution" ], "Effect": "Allow", "Resource": "*" } ], "Version": "2012-10-17" } イベントパターン イベントパターンの定義は別ファイルで定義しています。 CodeCommitリポジトリでソースコードが変更されたときにトリガーされるようにしています。 events/events_trigger_rule.tpl.json { "detail": { "event": [ "referenceCreated", "referenceUpdated" ], "referenceName": [ "${branch_name}" ], "referenceType": [ "branch" ] }, "detail-type": [ "CodeCommit Repository State Change" ], "resources": [ "main" ], "source": [ "aws.codecommit" ] } IAM 最後にIAMユーザーです。 GitHubからCodeCommitへソースコードをプッシュできるようにするために作成します。 作成したIAMユーザーはGitHubリポジトリのシークレットに紐づけて使用します。 resource "aws_iam_user" "github" { force_destroy = "false" name = "github-user" path = "/" } resource "aws_iam_user_policy_attachment" "github_codecommit" { user = aws_iam_user.github.name policy_arn = "arn:aws:iam::aws:policy/AWSCodeCommitPowerUser" } Terraformでの反映 applyして、AWSの環境にリソースを追加します。 terraform apply 無事反映されました!例としてCodePipelineの画面です。 SSHキーの登録 ローカル環境にて、ssh-keygenコマンドを実行します。 ssh-keygen 保存先とパスフレーズの入力を求められるので任意の保存先、パスフレーズを入力します。 Generating public/private rsa key pair. Enter file in which to save the key (~~~/.ssh/id_rsa): Enter passphrase (empty for no passphrase): 指定した保存先にid_rsaとid_rsa.pubが出力されました。 id_rsaをGitHubへ、id_rsa.pubをIAMユーザーへそれぞれ登録していきます。 ls -la | grep id_rsa -rw------- 1 username staff 2610 12 6 13:43 id_rsa -rw-r--r-- 1 username staff 579 12 6 13:43 id_rsa.pub IAMユーザーにSSH公開キーを登録 id_rsa.pubを開いて、中身をIAMユーザーに登録します。 コンソールからIAMユーザーのページへ遷移し、Terraformで作成したIAMユーザーを探します。 IAMユーザーを選択し、セキュリティ認証情報へ遷移します。 すると、AWS CodeCommitのSSH公開キーという項目があるので、SSH公開キーのアップロードを選択します。 そして、先ほど作成したid_rsa.pubの内容をコピペします。 すると、SSHキーIDが表示されるようになるので、これをコピーしておきます。(GitHubに登録します) IAMユーザーの設定は完了です。 GitHubに非公開キーとIAMユーザーのIDを登録 続いてGitHub Actionsに必要なシークレットを登録していきます。 必要なシークレットは以下のようになっています。 CODECOMMIT_SSH_USERNAME IAMユーザーのSSHキーID CODECOMMIT_SSH_PRIVATE_KEY id_pubの中身 GitHubリポジトリのページより、Settingsへ遷移します。 そして、左メニューからSecrets and variablesより、Actionsの項目を選択します。 ここからRepository secretsを登録できるので、New repository secretsで変数を登録していきます。 これでSSHキーの登録は完了です。 GitHub Actions 続いてGitHub Actionsを作っていきます。 簡単にディレクトリ構成を紹介します。 対象のリポジトリは以下のようなディレクトリ構成をしています。 . ├── .github │ └── workflows │ ├── mirror_codecommit.yaml │ └── scripts │ └── mirror.sh ├── .gitignore ├── README.md ├── appspec.yml └── myTheme ├── <テーマに関連するファイル群> ├── : └── : .github/workflows/mirror_codecommit.yaml 実行されるGitHub Actionsの設定ファイル .github/workflows/scripts/mirror.sh 上記GitHub Actionsにて、実行されるシェルスクリプト このシェルスクリプトでCodeCommitへのコピーを行います appspec.yml CodeDeployで使用されるデプロイの手順を定義する設定ファイル myTheme/ WordPressに配置されるディレクトリ デプロイ対象となる mirror_codecommit.yaml GitHub Actionsの設定ファイルを作成していきます。 name: Mirror codes to CodeCommit on: push: branches: - "main" permissions: contents: read jobs: mirror-to-codecommit: name: Mirror codes to CodeCommit runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Git push env: TARGET_REPO_URL: ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/<CodeCommitのリポジトリ名> SSH_USERNAME: ${{ secrets.CODECOMMIT_SSH_USERNAME }} SSH_PRIVATE_KEY: ${{ secrets.CODECOMMIT_SSH_PRIVATE_KEY }} run: bash "${GITHUB_WORKSPACE}/.github/workflows/scripts/mirror.sh" ここではmainブランチへのプッシュを検知し、ソースコードをCodeCommitリポジトリにミラーリングするジョブを定義しています。 後述のシェルスクリプトを使用してCodeCommitにプッシュが行われます。 SSHキーは先ほど登録したシークレットから取得されます。 mirror.sh 続いて、GitHub Actionsから実行されるシェルスクリプトです。 #!/usr/bin/env sh set -eu # copy credential mkdir -p ~/.ssh echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa # push to mirror rpository export GIT_SSH_COMMAND="ssh -v -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no -l $SSH_USERNAME" git remote add mirror "$TARGET_REPO_URL" git push --tags --force --prune mirror "refs/remotes/origin/*:refs/heads/*" あらかじめGitHub Actions上で登録された環境変数を読み取り、SSHキーなどのプッシュに必要な情報を取得します。 その情報を使用して、指定のCodeCommitのリポジトリにコードをプッシュしています。 appspec.yml 最後にappspec.ymlです。こちらはCodeDeployで使用されるデプロイの手順を定義する設定ファイルとなっています。 version: 0.0 os: linux files: - source: myTheme/ destination: /bitnami/wordpress/wp-content/themes/myTheme file_exists_behavior: OVERWRITE filesを使用して、デプロイ元のファイルと、デプロイ先EC2インスタンスのディレクトリを指定します。 今回の例だと、myTheme配下にテーマを配置しているので、こちらをデプロイするようにしています。 files: - source: myTheme/ destination: /bitnami/wordpress/wp-content/themes/myTheme また、以下の設定を追加することで、ファイルが存在する場合は上書きするように設定しました。 file_exists_behavior: OVERWRITE これで、全ての設定が完了しました! 動かしてみる 全ての設定が完了したので、実際にリポジトリにプッシュして、GitHub ActionsやCodePipelineが動作することを確認してみます。 変更を反映していつものようにリポジトリにプッシュしてみます。 git push origin main すると、GitHub Actionsが動き出しました! CodeCommitにミラーリングされています! (リポジトリ名などが違いますがそこはご愛嬌…) CodePipelineもこんな感じで実行されています! これで一通りのデプロイまでの流れを試すことができました。 まとめ 今回はCodePipelineを使ってWordPressのテーマをデプロイしてみました。 これでNIFTY engineeringの変更が容易になり、より快適なブログライフを送ることができるようになりました! WordPressのGitHub管理やCI/CDは悩ましいところが多いのでぜひ参考にしてみてください! 明日は、 @kanishionori さんの「今すぐ1on1をやった方がいい3つの理由」です。お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 6日目の記事です。 ニフティにはWEBサービスの基盤として20年物の自社製CMSがあります。10年以上前から時代に合わないものになっていましたが、まだ多くのWEBサイトで使用され続けています。しかし自社製CMSをメンテナンスし続けるのは困難なため、今回いくつかのサイトをmicroCMS+Astroの構成に移行しました。 本日は、その作業の中でもコンテンツ管理について話していこうと思います。 自社製CMSの役割と機能 自社製というのもあり多くの機能がありますが、主に必要とされているのは以下の4つ。 コンテンツ管理機能 ファイルサーバー機能 静的ページ出力機能 動的ページ動作環境 WordPressで例えると以下のようになります。 コンテンツ管理機能 → 複数サイトやカスタム投稿機能 ファイルサーバー機能 → FileManagerプラグイン 静的ページ出力機能 → Simply Staticプラグイン 動的ページ動作環境 → WordPressテーマのphp ブログのようなコンテンツメインのサイトなら、WordPressへの移行も有用な選択肢のひとつです。 ただLPのようなページが多くある場合、WordPressでは要件が合わない場面が多々あります。かといってレンタルサーバーでは機能面で心許ない。自社製CMSと同じ機能をフルで持ったCMSは非常に高額であるか、存在しないことが多いです。 そこでコンテンツ管理以外の機能はホスティング先などに任せて、Headless CMSを使用することが適切ではないかと考えました。 Headless CMSの選定 自社製CMSを使ったサイトでもピンからキリまであって、比較的シンプルに利用していたサイトを移行対象として選定しました。選定のポイントとしては以下の点を重視して選びました。 開発者ではない人でもコンテンツ運用できるか ユーザーフレンドリーなUI 日本語対応 開発者がサイト開発しやすいか APIやSDKや連携の充実度 管理がしやすいか SaaSとして提供されているか(CMS自体の管理はしたくない) SAMLやSCIM対応 費用 いくつか運用上欲しい機能が足りていない面もありましたが、今後のアップデートに期待することにして、感覚的に使えて触り心地のよかったmicroCMSを選びました。 移行後の変化 デプロイフロー Headless CMSに変えることで、いままであったファイル管理やホスティング先はどうしたかというと、こちらもマネージドのものに変えました。 ファイル管理はGUIベースからGitHub管理に変更しました。これでようやく一般的なホスティング環境をそのまま使えるようになるので、いまはAmplifyを使っています。GitHubにpushするかmicroCMSでコンテンツを編集すると、Amplifyのビルドが回るように設定しています。 リッチテキストエディタ 自社製CMSでもコンテンツのテキストフィールドにhtml直書きしてコンテンツ運用していました。ここはmicroCMSに移行しても変わらない点です。ただcssの調整ができた部分についてはリッチテキストエディタも採用しています。テーブル表記はリッチテキストエディタのほうがタグ記述ミスが少ないので運用負荷が特に下がります。 ひとつのコンテンツ内でhtml直書きかリッチテキストエディタどちらかしか使えないのではなく、必要に応じて使い分けができるのは良いポイントですね。 最後に 実はサイトを作り直したと言ってもいちから書き直したわけではなく、コンテンツ取得や利用部分のロジックは残してテンプレート言語を変更し、コンポーネント構成のためフォルダ構成も綺麗にしただけです。そのためコンポーネントごとにちゃんと独立してなかったりjQueryは残っています。 それでも自社製CMSからの脱却は済み、microCMSでコンテンツ運用は問題なくできていて、各種フローも綺麗にできたので、基盤の移し替えとしては良い結果となったと思います。前と比べかなり低コストで運用できていますし。 今回はコンテンツ管理についてお話ししましたが、ほかにもフレームワーク・テンプレート言語選定、複数環境対応などなどネタはたくさんありますので別の機会に書かせていただこうかと思います。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 5日目の記事です。 はじめに こんにちは、最近はひょんなことから見つけた古のWebページに衝撃を受けている宮本です。jQueryが生まれる前の時代のページともなると流石に趣が違いますね。 さて、今回は Astro の機能のひとつについてご紹介したいと思います。 Astroって? AstroはJavaScriptを使ったモダンWebフレームワークの一つです。JS製のWebフレームワークというとNext.jsが有名ですが、Astroはコンテンツが多い静的サイトに特化している点でかなり特徴的です。どのくらい特徴的かというと、Astro単体だとReactのstateにあたる機能が一切存在せず、クライアント側のDOM動作はほぼ素のJavaScriptやjQueryになるくらい特徴的です。 まだAstroを触ったことがない方は、ぜひ公式サイトをご覧ください。 https://docs.astro.build/ja/concepts/why-astro/ 同じレイアウトで異なるJSとCSSを読み込みたい 前述の通り、純粋なAstroの場合は細かいDOM操作はほぼ素のJavaScriptやjQuery頼りになります。拡張としてReactやSvelteのコンポーネントを部分的に導入することもできますが、単純な操作の場合は意外と昔ながらの方法でも十分に感じます。 さて、そうなると出てくるのが「レイアウトが同じだけど、このページだけ〜するから特定のJSライブラリをscriptタグで読み込みたい」という悩みです。もちろんscriptタグで読み込まずとも、Astroなのでnpmやyarnのようなパッケージ管理ソフト経由でjQueryなどをインストールし、内部でまとめてビルドしてしまうこともできます。こちらの方がだいぶスマートに感じます。 しかし、場合によっては昔ながらのscriptタグでの読み込みがしたい場合もあります。あるんです。たとえば、ただのhtmlだった既存のページを大量にAstro化する場合など……。また、この場合はJSが元々外部ファイルとして作成されている場合もあります。 CSSも同様に、時と場合によっては同じレイアウトで異なるCSSを読み込みたくなります。Astroにはデフォルトで簡単にスタイルをコンポーネント内で閉じる機能があるので、今後はそうした機能を使っていきたいですね。 複数のCSSやJSを読み込む場合には、読み込み順序を間違えると正常なページ表示にならない可能性もあります。これもCSSやJSファイル自体を修正しようとすると、程度にもよりますがなかなか大変だと思います。可能であれば、読み込み順を変えて対応してしまいたいです。 そうなってくるとレイアウトを新しく作る必要がありそうですが、ほとんど一緒なのに読み込むJS、CSSが違うだけでわざわざ新しいレイアウトは作りたくありません。特にそれを使うのが1~2ページだと、なんとも残念な気分になります。ということで、レイアウトは増やすことなく自由にJSとCSSファイルを読み込めるようにします。 ページごとにJSとCSSを読み込めるようにする 同じレイアウトでも自由に読み込むJS/CSSを制御できるようにしたレイアウトの例がこちらです。 --- import Header from "./Header.astro" import Footer from "./Footer.astro" const { title } = Astro.props --- <html> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <title>{title}</title> <slot name="OverrideHead"> <link rel="stylesheet" type="text/css" href="/css/base.css" /> <slot name="AppendHead"> </slot> </head> <body> <Header /> <slot /> <Footer /> <slot name="OverrideFoot"> <script is:inline src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <slot name="AppendFoot"> </slot> </body> </html> 名無しのslotはページ側の内容を表示するためですが、それ以外の名前付きslotの役割は以下のようになっています。 AppendHead slot: head要素の最下部に自由に読み込めるタグを追加できるslot AppendFoot slot: body要素の最下部に自由に読み込めるタグを追加できるslot OverrideHead slot: head要素内で読み込むCSS/JSを全て上書きするslot OverrideFoot slot: body要素の最下部で読み込むタグを完全に上書きするslot AppendHead/Footではほぼ基本的なslotタグの使い方です。OverrideHead/Footでは、 slotのフォールバックコンテンツ機能 を使っています。あらかじめ用意したslotに挿入がない場合はLayout側で指定した要素をそのまま表示、Layoutの呼び出し側で指定があればそちらを表示してくれます。 使用例 --- import Layout from "@layout/Layout.astro" --- <Layout title="ページ内で読み込むJS/CSSを追加する"> <Fragment slot="AppendHead"> {/* Layoutで指定した/css/base.cssに加えて以下を読み込む */} <link rel="stylesheet" type="text/css" href="/css/content.css" /> </Fragment> <p>ページ内で読み込むJS/CSSを追加する例</p> <Fragment slot="AppendFoot"> {/* Layoutで指定したjQueryに加えて次のファイルを読み込む */} <script is:inline src="/js/hoge.js"></script> </Fragment> </Layout> --- import Layout from "@layout/Layout.astro" --- <Layout title="ページ内で読み込むJS/CSSを全て制御する"> <Fragment slot="OverrideHead"> {/* Layoutのcssの読み込み順序を完全に上書き */} <link rel="stylesheet" type="text/css" href="/css/content.css" /> <link rel="stylesheet" type="text/css" href="/css/base.css" /> </Fragment> <p>ページ内で読み込むJS/CSSを全て制御する例</p> <Fragment slot="OverrideFoot"> <script is:inline src="/js/hoge.js"></script> {/* ここに追加しない限りjQueryの読み込みは行われない */} </Fragment> </Layout> さいごに 今回はAstroのLayout機能で柔軟にCSS/JSの読み込みを制御する方法を紹介させていただきました。一からAstroを使って新規にWebサイトを作成する場合はあまり必要ないかもしれませんが、既存のサイトをAstroに書き換える際には役に立つかもしれません。 明日は、@yukiex さんの EMとしてISUCONに参加するようになって です。 お楽しみに! 参考 Getting Started Astro Documentation We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
インナーソースコミュニティであるInnerSource Commons JapanのMeetupに当社エンジニアが登壇いたします。 イベントの詳細、参加につきましては下記ページを参照ください。 InnerSource Commons #11 – connpass 当社では、今回登壇する基幹システムグループの芦川、小松を中心にインナーソースの推進活動をしております。活動の様子につきましては、 インナーソースを導入してみた その① お試し導入編 をぜひご一読ください。 また、芦川がチームリーダーをしているサービスインフラチームの紹介記事もあります。インナーソースのような活動を尊重するチーム文化が窺い知ることができますので、こちらも合わせてお読みいただけると幸いです。 基幹システムグループ サービスインフラチームの紹介です
この記事は、 ニフティグループ Advent Calendar 2023 3日目の記事です。 はじめに こんにちは。ニフティ株式会社の並木です。 今回は、Lambdaで「AWS Secrets Manager」を使う方法をご紹介いたします。 AWS Secrets Managerとは AWSのサービスの一つで、APIキーなどの他人に知られては困る情報を管理してくれます。 LambdaでAPIを叩くにあたってAPIキーの設定が必要になったのですが、APIキーをハードコーディングせずに管理する方法はないかと考えていた時に、Secrets Managerの存在を知りました。 導入までの手順は大きく分けて以下の3つになります。 ①シークレットを作成する ②シークレットにアクセスするためのリソースポリシーを追加する ③Lambdaでシークレットを使う ①シークレットを作成する まずは、APIキーなどの隠したい情報をSecrets Managerに保存していきます。 公式ドキュメントは こちら です。 今回は、APIキーを保存するにあたり、以下の値を設定しました。 ・シークレットのタイプ:「その他のシークレットのタイプ」を選択。 ・キー:任意の名前。後にLambdaで情報を取り出す時にここで付けた名前を使います。 ・値:隠したい情報。今回の場合はAPIキーを設定します。 ・暗号化キー:「aws/secretsmanager」を選択。 「次」を押下すると、以下のページが出てきます。 ・シークレットの名前:任意の名前。後にLambdaで情報を取り出す時にここで付けた名前を使います。 ・説明:シークレットの説明を書いておくことができます。日本語も使えました。 ・リソースのアクセス許可:このタイミングでも設定可能ですが、今回は「②シークレットにアクセスするためのリソースポリシーを追加する」で設定するので飛ばします。 さらに「次」を押下すると、シークレットの自動ローテーションの設定ページが出てきます。 今回は設定せずにそのまま「次へ」を押下します。(後から設定することも可能です) これでシークレットの作成は完了です! ②シークレットにアクセスするためのリソースポリシーを追加する ①で作成したシークレットをLambdaで呼び出すためには、リソースポリシーの追加が必要になります。 そのためには「Lambdaの実行ロールのARN」が必要となるので、まずはそちらを取得していきます。 Lambdaの管理画面のアクセス権限を表示します。 ロール名のリンクを押下すると、Lambdaの実行ロールの管理画面に飛びます。 Lambdaの実行ロールの管理画面に記載されているARNをメモしておきます。 これで「Lambdaの実行ロールのARN」が取得できました! 次に、①で作成したシークレットの管理画面を表示し、「許可を編集」を押下します。 公式ドキュメント に記載されている以下の「mypolicy.json」の定義をコピーして使います。 ARN部分のみ、先ほど取得してきた「Lambdaの実行ロールのARN」に置き換えて保存します。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789012:role/MyRole" }, "Action": "secretsmanager:GetSecretValue", "Resource": "*" } ] } これでリソースポリシーの追加は完了です! ③Lambdaでシークレットを使う Lambdaでシークレットを使うには、レイヤーの追加が必要になります。 「AWS Parameters and Secrets Lambda Extension」を選択し、「追加」を押下します。 シークレットを呼び出すためのコードをLambdaに書いていきます。 ①で作成したシークレットの管理画面にサンプルコードが表示されているので、それを活用します。 言語がいくつか選択できますが、今回はPythonのサンプルコードを使用します。 サンプルコードをベースに、コードを書いてみました。 サンプルコードには「secret = get_secret_value_response[‘SecretString’]」までしか書かれていなかったため、その後どのようにして値を取り出せばよいのか分からず、個人的にはここで少し詰まりました。 「ast.literal_eval()」を使うと良いようです。 import ast import boto3 from botocore.exceptions import ClientError def get_secret(): secret_name = "①で設定したシークレットの名前" region_name = "リージョン名" # Create a Secrets Manager client session = boto3.session.Session() client = session.client( service_name='secretsmanager', region_name=region_name ) try: get_secret_value_response = client.get_secret_value( SecretId=secret_name ) except ClientError as e: # For a list of exceptions thrown, see # https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html raise e # Decrypts secret using the associated KMS key. secret = get_secret_value_response['SecretString'] # Your code goes here. secret_list = ast.literal_eval(secret) value = secret_list['①で設定したキー'] おわりに 他人に知られては困る情報の管理や呼び出しが簡単にできて便利だと感じました。 ハードコーディングを防ぐためには欠かせないサービスだと思いますので、ぜひ利用してみてください。 明日は、 nahiro_tus さんの「Notionの作図機能についてまとめる」です。 お楽しみに! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 3日目の記事です。 はじめに こんにちは。会員システムグループでエンジニアをしている山田です。 私の担当しているプロダクトではシステム刷新を進めており、20年来のレガシーなJavaシステムからNode.js(Next.js)を利用したフロントエンドシステムへのフルリプレースを行いました。その後の運用体制を整えていく中で、GitHub Dependabotを使ったアップデート運用を導入したので、この紹介になります。 これまでの課題 今までミドルウェアや依存パッケージなどのアップデート対応には明確な指針がなく、だいたい以下のどちらかの運用になっていました。 なんらかの問題(脆弱性など)が起こるまで放置 担当者が気づいた時に更新 前者は変更の少ない言語環境であれば機能する可能性がありますが、Node.jsのような依存パッケージ数も変更頻度も多い環境では大きなリスクを生みます。いざ変更が必要になった時には変更差分が把握しきれない量になっていたり、依存関係の都合で複数のアップデートを同時に行う必要が生じることも頻繁にあります。しかもこれが突発的に発生することで、必要な開発の妨げになる可能性を孕んでいました。 一方で後者は属人化しており、担当者の知識や意識に左右されます。担当者の異動などで継続不能になる可能性を孕んでいます。 以上から、継続的にアップデートを続ける体制を作っていく必要がありました。 Dependabotとは DependabotはGitHubの機能として利用できるサービスであり、プロジェクト内の依存関係に含まれる脆弱性やアップデートの有無を検知して自動でプルリクエストを作成したり、アラートを出すことができるサービスです。 類似のサービスとして Renovate がありますが、RenovateはGitHub AppsのインストールやGitHub Organization管理者による設定が一部必要と、導入にややハードルがあります。 一方でDependabotはGitHub組み込みの機能なので、リポジトリごとの設定で有効化することで使い始めることが可能です。 リポジトリ設定の「Code security and analysis」から有効化できます 設定 Dependabotの設定 Dependabotの設定は . github / dependabot . yml に記述します。 アップデート可能なパッケージがある場合、Dependabotはこの設定を元に自動的でプルリクエストを作成します。 version: 2 updates: - package-ecosystem: "npm" directory: "frontend" schedule: interval: "weekly" groups: nextjs: patterns: - next - react - react-dom - "@types/react" - "@types/react-dom" - eslint-config-next dependencies: dependency-type: "production" devDependencies: dependency-type: "development" ignore: - dependency-name: "*" update-types: ["version-update:semver-patch"] 詳細な文法は ドキュメント を参照していただくとして、大まかに以下の設定をしています。 package-ecosystem npmなどパッケージ種別を指定します。指定値としてはnpmでもyarnでも「npm」の指定になります。 実際にDependabotがどのパッケージマネージャに対応しているかは、ドキュメントで確認が必要です。例えば「pip」はpipenvとpoetryに対応していますが、ryeには対応していません。 言語ごとのパッケージだけでなく、Dockerfileのベースイメージなどにも対応しています。 directory チェック対象ディレクトリ指定です。モノレポ構成などの場合に、個別ディレクトリを指定することで分けて管理することができるようになります。 schedule チェック間隔です。daily、weeklyなどが指定できます。 groups グルーピングの設定です。2023年になってから 追加された 機能になります。 デフォルトでは、パッケージ1つ1つのアップデートに対してプルリクエストが作成されてしまうため、多すぎて管理しきれなくなります。groupsを設定することにより、指定したパターンに一致するパッケージアップデートを1つのプルリクエストにまとめることができます。 私のプロジェクトでは Next.js関連 TypeScript関連 その他dependencies その他devDependencies くらいのグループに分割して管理するようにしています。 ignore 除外条件です。特定パッケージやバージョンを除外することができます。 パッチバージョンまですべて追う必要はないと考えているため、パッチバージョンの除外指定を入れています。 以上の設定の他にも、プルリクエストコメントやラベルのカスタマイズなどが可能です。 設定によっては自動マージも可能ですが、リスクを鑑みて適用していません。 他のGitHub Actionsの修正 Dependabotに限った話ではないのですが、botアカウントは権限が絞られているため、GitHub Actionsのワークフローをbot権限で実行してしまうと失敗する場合があります。 私のプロジェクトではプルリクエスト起票者をAssigneeに追加するワークフローがあったため、botアカウントを除外するような対応を行いました。 jobs: add-info: name: Auto Assign runs-on: ubuntu-latest if: ${{ !contains(github.actor, '[bot]') }} # [bot]を含む場合は除外 steps: ... 運用 アップデート対応 Dependabotのチェック頻度をweeklyに設定し、毎週のミーティングで自動作成されたプルリクエストを全て確認し、アップデート可否の判断をするようにしました。 動作そのものはGitHub Actionsで組まれたCIで担保していますが、隠れたリスクの発見や業界情勢の把握のため、全てのリリースノートを確認するようにしています。 Gihub管理のパッケージならコミットメッセージにリリースノートが載るので、確認は楽です 除外対応 CIを用意しているとはいえ、Next.jsのメジャーバージョンアップなどの影響の大きい変更はリスクがあるため、一定期間を置いてから適用するようにしています。 この間、そのままだと同一グループの他のパッケージもアップデートができなくなってしまいます。このような場合はプルリクエストコメントに @dependabot ignore <パッケージ> とコマンドを打つと、アップデート対象から一時的に除外できます。アップデートできるようになったら @dependabot unignore <パッケージ名> で解除します。 運用してみて 良かったところ 目論見通り、属人化していたアップデートを誰でも行うことができるようになりました。 またリリースノートの確認を行うようになったことでコミュニティの動きをチームメンバー全員が把握できるようになり、知識向上にもつながるようになりました。 微妙なところ 一方で微妙だな、と思うポイントもいくつかあります。 コンフリクト時の挙動 複数グループにアップデートがある場合、1つのプルリクエストをマージするとロックファイルがコンフリクトを起こすため、他のプルリクエストは修正が必要になります。 このような場合、Dependabotは rebaseしてforce-push 既存プルリクエストをcloseし、新規に作成 のどちらかの対応を行うようなのですが、どちらになるかが現状予測できません。無駄なプルリクエストを減らすため、できれば前者に寄せたいところです。 またいずれの場合も数分待たされるので、グループ数が多いとすべて解消するのに時間がかかります。CIの実行時間もかかるので、グループ数はなるべく少なくした方が良いでしょう。 複数グループに同じパッケージが出現する グループについて、ドキュメントには以下の記載があります。 Dependabot creates groups in the order they appear in your dependabot.yml file. If a dependency update could belong to more than one group, it is only assigned to the first group it matches with. つまり上から順に評価され、複数グループに同じパッケージがマッチすることはないと読めます。 これはスケジュール通りの実行時には確かに機能するのですが、コンフリクト解消時にはうまく機能してくれないようで、rebaseされたタイミングで複数グループに同じパッケージが存在する状態になってしまうことがありました。 グループの設定で exclude - patterns を設定すれば確実に除外ができるのですが、全てのグループに除外設定を書く必要が出てくるため非常に煩雑です。 複数種にまたがるアップデートを同時に行えない Node.jsのメジャーバージョンを上げたい場合、私のプロジェクトでは 本番用のDockerfileのベースイメージ 開発用のDockerfileのベースイメージ GitHub Actions上でのsetup-node を揃えてバージョンアップする必要があります。しかしDependabotのグループ設定は複数ファイルにまたがる更新に対応しておらず、複数のプルリクエストに分かれてしまいます。 メジャーバージョンアップ自体は頻繁に発生するものではないので、個別で手動対応するようにしています。 まとめ GitHub Dependabotを利用したアップデートプルリクエストの自動作成と、その運用についてご紹介しました。 Node.jsに限らず最近の言語ではOSSパッケージへの依存度が高く、そのアップデート対応は煩雑になりがちです。GitHubを利用されている方は、ぜひ活用して見ることをお勧めします。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
この記事は、 ニフティグループ Advent Calendar 2023 2日目の記事です。 はじめに こんにちは!中途入社1年目の福島です。 私は日々の業務に加えて、世間の皆様にニフティについてより深く知っていただくために、ブログ運用チームの一員として活動しています。 先日、当ブログが念願の10,000MAUを達成しました! この記事では、10,000MAUを達成するまでに行ったブログ運用チームの取り組みをご紹介します。 他社でブログを運用している方やこれから運用したいけど何をすればいいかわからない方の参考になれば嬉しいです。 活動内容 ブログ関連作業効率化 X(旧Twitter)投稿自動化 これまで手動で行っていたXへのブログ更新ポストを自動化することで、投稿文のミスやダブルチェックの手間を削減し、スムーズに記事をポストできるようにしていました。 ブログを更新しました! 今回の記事は「Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた」です。 https://t.co/jODL6wIIbS #nifty_dev #RSS #Twitter #Zapier #自動化 — NIFTY Developers (@NIFTYDevelopers) July 26, 2023 自動でポストされた投稿 これにより簡単に記事を共有しより多くの方に閲覧いただける環境が整いました! ! が、残念なことに、開発後、ZapierのX(旧Twitter)関連の機能がサービス終了となり、現在は使用できなくなってしまいました… 現在別の手段で自動ポストできるように誠意修正中です!! ※当時の実現方法はこちら Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた – NIFTY engineering 社内ルールの整備 執筆から公開までの手順の整備 執筆~公開までの手順がわかりにくいとストレスになります。 皆さんに楽しく、スムーズにブログ執筆行っていただくために、明確な執筆手順を整備しました。 問い合わせ窓口もSlackに開設しており、連絡があった内容はTIPSとして随意追記しています。 記事のテンプレートを準備 「ネタはある!けどブログ記事の書き方がわからない….」という社員のために、テンプレートを準備して書きやすくしました。 WordPressの再利用ブロックからテンプレートを呼び出し、穴埋めするだけで簡単にブログ記事が書けちゃいます! 記事数を増やす試み 執筆計画の作成 エンジニア全員に年度初めに執筆スケジュールを記載してもらうことで、計画的にブログを書けるように調整しています。 アドベントカレンダー(執筆イベント) ニフティではイベントとして毎年アドベントカレンダーを実施しており、アウトプットの恒例行事となっています。 ありがたいことに毎年多くのメンバーが参加し、記事を執筆していただけています。 2023年度はまさに現在実施中です!!良い記事がたくさん投稿されるのでぜひチェックしてみてください! https://qiita.com/advent-calendar/2023/nifty ネタ切れ対策 社内のナレッジからのブログネタの選定・依頼 社内のナレッジを活用し、ブログに転用可能な情報を見つけ出して執筆依頼することで、多岐にわたるテーマをカバーできるようにしています。 今年度の新卒とトレーナーにブログ執筆依頼 「新人の方ニフティで学んだことをアウトプットしてもらう」、「ブログ記事に新しい視点や経験を取り入れる」という2つの目的を持ち、新人とトレーナーに積極的に執筆依頼を行っています。 最近の記事だとこちらの記事が今年度の新卒の方が書いた記事です。 非常に優秀な方々で面白い記事なのでぜひ読んでみてください!! 新卒1年目の働き方改革: AWS+Boltで作成したSlackアプリによる出退勤タスク自動化術 – NIFTY engineering LambdaでS3に置かれたファイルの署名付きURLをSlackに通知してみた – NIFTY engineering ブログネタ集めを楽にする(リアク字チャンネラー ) 社内でのアイデア共有を活発化させ、ブログネタの収集を楽にするためにニフティで活発に利用されているSlackで誰かが「ブログ書いて」の絵文字のリアクションを押すと自動的にブログネタが流れてくるチャンネルを作成しました。 執筆者のフィードバックを活用したコンテンツ改善 アンケート(半期に1回) 執筆者であるエンジニア全員に対して半期に1回、アンケートを行っています。 執筆者の要望を把握し、それに基づいて企画を立案、運用を改善することで、執筆者に気持ちよく記事を書いてもらうことを目指しています。 これらの活動により、読者の皆様への価値提供の向上はもちろん、目標だった10,000MAU達成にも繋がりました。 また、今後も良いブログにしていくため、以下の活動を計画しています。 WordPressの使い勝手を向上させるためのカスタマイズ より一層のパフォーマンス向上を目指し、別のプラットフォームへの移行検討 記事の作成から公開に至るフローの簡略化 これからも改善を重ね、皆様に愛されるブログへと成長させていきます。 どうぞよろしくお願いいたします! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する 明日は、並木さんによる「LambdaでSecretsManagerを使ってみた」です。 お楽しみに!
この記事は、 ニフティグループ Advent Calendar 2023 1日目の記事です。 はじめに こんにちは。ニフティ株式会社の会員システムグループの上原です。 2023年ニフティグループAdvent Calender1日目に滑り込みの投稿です。 今回は、ISUCONという競技イベントにニフティ社員でチームを組んで参戦したので、そのご報告になります! ISUCONとは? ISUCONとは制限時間8時間でお題となるwebアプリケーションを限界まで高速化して性能を競いあうコンテストです。 今年は11/25(土) 10:00-18:00に開催されました。 LINEヤフー株式会社が主催しており、優勝すると100万円を獲得できます。 ※ 「ISUCON」は、LINEヤフー株式会社の商標または登録商標です。 チーム「nisucon-bu」のメンバー なおき(私) アプリケーション・データベースのチューニング・コード内部調査 初参加 yukiex 環境構築・インフラ たくさん参加している mito アプリケーション・データベースのチューニング・マニュアル読み込み 2回目 お題 ライブ配信サービス「ISUPipe」の高速化 お題に対する所感 こんいす〜 今回のお題は動画配信サービスなのでドメイン知識の理解は辛くない スコアはベンチマーカーが配信中に投げ銭をして、どれくらい売上を獲得できたかで決まる いつもはエンドポイントの実行結果で決まるが、今回は売上でスコアが決まるので面白いなと思った 動画配信をチューニングするかと思って驚いたが、動画配信はチューニング対象外 運営が用意したCloudFrontから動画配信されるので、どう頑張ってもチューニングできない 最悪ブラウザから動画を見れなくても構わない(らしい) リバースプロキシ(nginx) + go(アプリケーション) + データベース(mysql) + DNS(PowerDNS)の構成 DNS以外はいつも通りの構成 DNSが選手のサーバー上にあり、ベンチマーク実行中はDNS水責め攻撃が行われる いつも通りアプリケーションやデータベースを頑張ってチューニングしても、DNS水責め攻撃を対策しないとスコアが上がらない DNS水責め攻撃とは、適当なサブドメインをくっつけてどんどんDNSサーバーに問い合わせることで、DNSをダウンさせる攻撃 例えばnifty.comであれば、hogehoge.nifty.comのIPアドレスは?、fugafuga.nifty.comのIPアドレスは?、puipui.nifty.comのIPアドレスは?… とランダムにサブドメインをつけてDNSに問い合わせていく DNSサーバーに対するDDoS攻撃 これまでのISUCONで出てこなかった内容なので、全然対策できていない この辺りの知識が必要らしいが、全く知らなかった… DNS権威サーバのクラウドサービス向けに行われた攻撃および対策 〜後編〜 | さくらのナレッジ DBやめてみた / DNS water torture attack and countermeasures やったこと アイコンの静的配信、トランザクション削除(mito) PowerDNS Recursor による水責め対策(TTLを減らす、タイプANYリクエストを抑制)(yukiex) 統計(LivestreamStatistics, UserStatistics)のN+1を改善(なおき) 【反映せず】icon のハッシュ値をDBに保持(yukiex) TAG処理のN+1をIN句に変更、インデックス追加(mito) NGワード除去処理を修正、インデックス追加(mito) 【反映せず】DNS 用 records テーブルに対するindex追加(なおき) Livestream のキャッシュ機構を追加(なおき、mito) サーバの分散化(yukiex) その他(themes、reservation_slots、livestreams)のインデックス追加(mito) https://github.com/niftyisucon/isucon13 タイムライン 環境構築、初回ベンチマーク(46分) 10:00 ISUCONのポータルサイトに置いてあるCloudFormationのYAMLファイルをダウンロードした後、自分のAWS環境にサーバー構築を行う(なおき) 10:00 マニュアルなどを読む(yukiex, mito) 10:08 サーバー構築が終わったので、サーバー内のファイルをGit管理にしたり必要なコマンドを導入する(yukiex) 10:08 引き続きマニュアルを読む(mito) 10:08 データベースのスキーマや実行しているプロセスの調査(なおき) 10:12 初回ベンチマークを実行。3757点。 10:37 alpとpt-query-digestによるログ解析が完了。 10:46 環境構築完了 チューニング 午前 10:39 スロークエリに対してインデックスを貼る(mito) 11:00 DNS水責め攻撃の対策のため調査(yukiex) 11:35 PowerDNS Recursor の導入(yukiex) 設定方法とかもChatGPTに聞いたらしいです 11:00 – 12:00 ブラウザでアプリケーションを触りながらAPIのエンドポイントをまとめていく(なおき) 午後 12:00 ユーザーのiconをDBからはがしディスクに保存、nginxで配信するようにする(mito) https://github.com/niftyisucon/isucon13/pull/12 13:15 nginxの設定に手こずり、苦戦したが完了(mito) 6905点 12:00 配信のstatisticsエンドポイントで使用しているクエリを変更(なおき) https://github.com/niftyisucon/isucon13/pull/16 https://github.com/niftyisucon/isucon13/pull/19 配信の順位や投げ銭の最大値などを返すエンドポイント N+1問題でパフォーマンスが落ちていた キャッシュさせて回避するか、クエリを変更するかで迷った挙句、yukiexさんからアドバイスを受けクエリを変更することにした 13:30 ランキングの順位がバグりまくって大変だったが、ベンチマークの指摘とデータベースの中身をにらめっこしつつ直す(なおき) 6422点 13:30-14:15 トラブルでベンチマークがうまく動かなくなり、何もできなくなる 14:15 iconのハッシュ値をDBに追加(yukiex) マージせず 15:12 タグのN+1問題を解消、インデックス追加(mito) https://github.com/niftyisucon/isucon13/pull/21 7776点 15:20 ユーザーのstatisticsエンドポイントのN+1問題をクエリを変更して解消(なおき) https://github.com/niftyisucon/isucon13/pull/24 ユーザーの順位などを返すエンドポイント こちらもランキングの順位がバグりまくったが、ベンチマークの指摘とデータベースの中身をにらめっこしつつ直す 8703点 15:30 DNSサーバーのisudnsデータベースのrecordsに対しインデックスを追加したが、スコアが下がったため巻き戻し(なおき) インデックスを貼った結果、INSERT UPDATEの時間が悪化したのだと思われる 9972点 15:35 icon取得にかかっている不要なトランザクションを除去(mito) https://github.com/niftyisucon/isucon13/pull/27 10667点 16:15- サーバーを1台から複数台構成に変更するための準備(yukiex) サーバー3台のうち2台を複数台構成に変更するために使用 残り1台でアプリケーション・データベースのチューニングを試してみる 15:45 ライブ配信で投稿できるコメントのNGワードにインデックスを追加(mito) https://github.com/niftyisucon/isucon13/pull/29 11709点 16:15 ライブ配信で投稿できるコメントのNGワード削除処理を修正(mito) https://github.com/niftyisucon/isucon13/pull/34 NGワードを追加すると過去のコメントも含めて削除する処理になっていた N+1問題の解消 11795点 16:30 – 17:15 配信情報を取得する関数にキャッシュを導入(なおき) https://github.com/niftyisucon/isucon13/pull/35 自分の書いた章を読みながら実装( NIFTY Tech Book #1:ニフティ執筆部 ) 21743点 17:30 インデックス追加(mito) https://github.com/niftyisucon/isucon13/pull/40 残り時間30分でやったチューニング 16449点 終盤(複数台構成に変更、ログ出力停止、再起動試験対策) 17:15- 2台構成に変更(yukiex) isu1: nginx, app(Go), PowerDNS Recursor, PowerDNS, mysql (PowerDNSのバックエンド) isu2: mysql (app) 17:45 ログ出力停止(mito) https://github.com/niftyisucon/isucon13/pull/41 https://github.com/niftyisucon/isucon13/pull/42 17:49 ベンチマーク実行 25,674点 18:00 競技終了 結果 661チーム中81位(上位12.04 %) スコア 25,674 自チームのスコア変動をグラフ化してみると17:00からの伸びがすごい 他のチームの状況 1位のスコアがすごい(468,006点) 2位のスコアと2倍近く差をつけている うまくできたこと 1時間以内に環境構築・準備を終えることができた 練習ではごたつくこともあったが、事前準備でなんとかなった 練習でやったことは確実にこなすことができた N+1問題の解決やキャッシュを入れ込むことができた 自分の書いた本のおかげ ニフティ社員で執筆した技術総合誌「NIFTY Tech Book #1」のダウンロードよろしくお願いします(宣伝) NIFTY Tech Book #1:ニフティ執筆部 ChatGPTやCopilotを駆使して素早く問題の解決ができた AIすごい 役割分担がうまくいった 自分はゴリゴリコードを直していきつつ、マニュアルを読み込んで仕様を完全理解していたmitoさんに軌道修正してもらいながら、yukiexさんがインフラに手を入れる 17:00からグイグイ点数が上がった 反省点 個人練を頑張りたい 低レベルな技術を理解することは大事 DNS様 まだまだやりたいことはあったが、時間が足りなかった。 無駄な時間を削ぎ落としたい プリペアードステートメントを切ったり、コネクション数を増やすのを忘れた 1,2行追加するだけでスコアが上がるのに… 次回参加時は手順に入れておく 途中ベンチマークが動かないトラブルがあって何もできなかった 何も検証できない時でも何をするか決めておく ランキングを作る部分はISUCONでよく出てくるなぁと感じたので、Redisを使っていい感じにしたい これから練習していって知見をためていく 学び GoでDNSを自作できるらしい github.com/miekg/dns というライブラリを使えばいけるらしい 実装例 https://gist.github.com/ryotarai/2938ac139c23417da222c1a387415836 上位層は当たり前のように知っていて驚き discordにGitHubの通知を入れるのは便利 他の人が何をやっているのかわかって安心感がある まとめ 自分は初参戦のため普段の練習の力が出せるか不安でしたが、N+1問題を解消したりキャッシュを導入できたので十分成果は出せたのかなと思います。 ISUCONで初めてDNSの話が出てきたのでギョッとしてしまいましたが、結局基本が大事 午後は1万点を超えずになかなか辛い気持ちになっていましたが、17:00からぐんぐん点数が伸びていったので興奮しました。 途中、1時間くらいトラブルでベンチマークが実行できず手持ち無沙汰な時間を過ごしたのは残念でしたが、全体的には問題の質が高く楽しく解くことができました。 終了後はISUCON13の参加者全員が集まるdiscordで感想ややったことが共有され非常に勉強になりました。 次回ISUCONではさらに成長して上位層に食い込めるよう挑戦していきたいと思っています。 8時間という長丁場でパフォーマンスチューニングだけを考え、ひたすらボトルネックを解消していく機会はISUCON以外にはないと思います。皆さんもぜひISUCONに参加してパフォーマンスチューニングを身につけてみてください。 明日は、fu9shimaさんの「【祝10000MAU!】エンジニアブログ運用チームの活動まとめてみた」です。 どんなことをしているか気になりますね!お楽しみに! We are hiring! ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も受け付けています! カジュアル面談 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering ニフティ株式会社 – connpass
前編はこちらの記事をご覧ください。 【インタビュー】主力事業を支える回線サービスシステム開発の裏側とは?【入会システム前編】 入会システムチームを更に深堀ります。 別チームから異動してきてどういった印象を受けましたか? D.Tさん まず最初に思ったのは扱っているサービスが多いと思いました。そうなると属人化が起こってもおかしくはないと思うのですが、スクラムを通してコミュニケーションを積極的に取って認識違いを失くしたりタスク内容などを全員が把握できる状態にしているので、誰が何をやっているのかわからない・・・といったことはないですね。 どのサブチームもスクラム開発を取り入れているのでしょうか? K.Nさん そうですね、ニフティ全体としてもスクラム開発を実践しているチームは結構あります。 D.Tさん K.Nさんはスクラムマスターの資格を取ってましたよね。 K.Nさん 去年取得しました。 社内でもスクラムマスター同士が集まれる環境があって、そこでそれぞれのチームの悩みなどを持ち寄って情報交流などをしています。 ひと言で言うとどんなチーム? K.Tさん アットホームなチームだなと思います。 D.Tさん フラットなチームですよね、異動してきたときハードルを感じなかったです。 K.Nさん お子さんがいる方も多いですね。私も小学生の子供がいるので熱が出ちゃったり、PTAの役員を引き受けちゃったりという事情はあったりしますが、チームメンバーも家庭の事情を分かってくれているので調整ができます。休暇も比較的自由にとれるので働きやすい会社だなと思います。 D.Tさん 女性に限らず、男性も子育てで休暇をとる人も多いです。それと、頻繁に飲み会があったりしますよね K.Nさん リーダー自ら声かけたりとね笑 D.Tさん 2次会の出席率が高いですよね、多いと7割くらいいくんじゃないかな笑笑 強制参加ではないですか?笑 「「それは全くないです!!」」 最後に、皆さんの仕事のやりがいを教えてください K.Tさん コードを書くことが好きなので開発そのものがとても楽しいですが、作ったものがお客様にご利用いただけているということがやりがいになっています。 自分で利用するためのシステム開発は入社前から趣味でやっていましたが、自分以外の方に使っていただけるのはとても嬉しいことなんだなと思いました。 K.Nさん 昔はリリースできたら達成感があって嬉しいという気持ちが大きかったのですが、今は達成して誰かが喜んでくれることが一番に感じています。 D.Tさん お客様の要望を叶えたり、チームの働き方を変えて良い方向に向かっていることが実感できたりと、何かを革新した時ですね。何かを作る・変えるだけではなくてその結果良い方向に向かったりすることが大事だなと感じます。 今回はニフティの入会システムチームのインタビューの様子をお届けしました。ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する .is-style-rounded + .has-text-color{ font-size:95%; }