スクラムガイドを読んだのでまとめてみる
ちゃんとスクラムを実践する機会がありそうなので読んでみました。
読んだのは、2020年版のスクラムガイドです。
https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf
スクラムのベースにある考え方
スクラムは、経験主義とリーン思考の2つの考え方に基づいています。
また、経験主義を実現するために、透明性・検査・適応の三本柱を持っています。
- 経験主義:知識は経験から⽣まれ、意思決定は観察に基づく。
- 透明性:プロセスや作業は見える化する。透明性の担保によって検査が可能となる。
- 検査:望ましくない変化や問題を検知するため、頻繁に検査する。検査によって適応が可能となる。
- 適応:問題や課題が見つかった場合、プロセスや構成要素を調整する。
- リーン思考:ムダを省き、本質に集中する。
スクラムの価値基準
スクラムチームの作業・⾏動・振る舞いの⽅向性を⽰す価値基準です。
- 確約(Commitment)
- 集中(Focus)
- 公開(Openness)
- 尊敬(Respect)
- 勇気(Courage)
この価値基準が満たされることで、経験主義の透明性・検査・適応がぐっとやりやすくなります。
スクラムって、プロジェクトを回す型が大事かと思ってたけど、
それよりも価値基準に基づいた文化醸成が重要そう👀
スクラムチームの構成
スクラムチームには3つの役割があり、サブチームや階層構造は存在しません。
スクラムマスター(1人)
スクラムガイドで定義されたスクラムを確⽴させることの結果に責任を持つ役割。
スクラムチームを様々な形で支援する。
プロダクトオーナー(1人)
スクラムチームから⽣み出されるプロダクトの価値を最⼤化することの結果に責任を持つ役割。
効果的なプロダクトバックログ管理にも責任を持つ。
開発者(複数名)
各スプリントにおいて、利⽤可能なインクリメントのあらゆる側⾯を作成することを確約する役割。
スクラムのイベント4つ
スプリントプランニング
スプリントで実⾏する作業の計画を⽴てるイベントで、スプリントの最初に行われます。
重要なトピックは、以下の3つです。
- そのスプリントの価値:プロダクトオーナーはプロダクトの価値と有⽤性をどのように⾼めるか提案し、チームでスプリントゴールを設定する
- そのスプリントで何ができるか:プロダクトバックログから今回のスプリントで対応するアイテムを選択する
- 選択した作業をどのように達成するか:完成の定義を満たすインクリメントを作るために必要な作業を計画する
デイリースクラム
スプリント中、毎日15分程度行われるイベントです。
計画された今後の作業を調整しながら、スプリントゴールに対する進捗を検査し、必要に応じてスプリントバックログを適応させます。
スプリントレビュー
スプリントの成果を検査し、今後の適応を決定するためのイベントで、スプリントの終わりに行われます。
スクラムチーム外も含めた主要なステークホルダーに作業の結果を提⽰し、プロダクトゴールに対する進捗について話し合います。
スプリントレトロスペクティブ
品質と効果を⾼める⽅法を計画するためのイベントで、スプリントの一番最後に行われます。
課題や問題、その要因や、どう解決されたかを話し合います。
改善案があれば、次回以降のスプリントで対応します。
スクラムの作成物
プロダクトバックログ
プロダクトの改善に必要な物事の⼀覧です。
プロダクトバックログには以下が含まれる必要があります。
- プロダクトゴール(プロダクトの将来の状態)
- プロダクトゴールを達成する「何か(what)」の定義
また、スクラムチームは、プロダクトバックログのリファインメントを継続的に行います。
リファインメントとは。
プロダクトバックログアイテムがより⼩さく詳細になるように、分割および定義をする活動。
透明性の向上につながる。
スプリントバックログ
スプリントゴールを達成するために、開発者がスプリントで⾏う作業がリアルタイムに反映されるものです。
スプリントバックログには以下が含まれる必要があり、デイリースクラムで進捗を検査できる程度の詳細さが必要です。
- スプリントゴール(開発者が確約する、スプリントの唯⼀の⽬的。「なぜ」の部分。)
- スプリント向けに選択されたいくつかのプロダクトバックログアイテム(何を)
- インクリメントを届けるための実⾏可能な計画(どのように)
インクリメント
プロダクトゴールに向けた具体的な踏み⽯で、新たなインクリメントはこれまでのインクリメントに追加されます。
プロダクトバックログアイテムが完成の定義(プロダクトの品質基準を満たすインクリメントの状態を⽰した正式な記述)を満たしたときにインクリメントが誕⽣します。
インクリメントってピンと来ないなと調べたところ、
「スプリントに基づく利用可能な最終プロダクト」というのが一番イメージつきました💡
感想
思っていたより文化醸成や、参加する人たちの姿勢が、スクラムをうまく回すには必要なのではと思いました。
それでいうと、個々人のスクラムへの理解も大事だけど、スクラムマスターがどう動くか・どう浸透させるかっていうのが成功の分かれ目なのかなあ🧐
Bunとは?Remixと一緒に動かしてみた
Bunとは
JavaScriptのランタイム。Node.jsと同じ立ち位置のもので、Node.jsと代替できるようにデザインされている。
ただ、バンドラー・テストランナー・パッケージマネージャーとしてのツールを備えている。
以下、特徴で気になったことをメモ。
- 高速な実行速度は、JavaScriptCoreエンジンとZigがポイントとなっている
- TypeScriptのネイティブサポート
- JSXのネイティブサポート
- ネイティブでWatch modeあり
- npm互換のパッケージマネージャー
- Jestに互換性のあるテスト構文
Remixと一緒に動かしてみる
Remixインストール
※bunはすでにインストール済みです。
bun create remix
インストール結果を見てみる
npm
、yarn
と比較すると、
- 同様に
node_modules
内にインストールされたパッケージが入ってます lock
ファイルはbun
のもの。バイナリファイルなのでvscodeでは開けませんでした
サーバー起動する
bun run dev
うむ。通常通り起動できました。
テストはどうだろう
bunはテストランナーでもあるとのことで、テストを書いてみようと思いました。
が、検証途中でJest, Vitestと変わらなさそうと思ったので中断しました🙏
Testing Library系(React Testing Library、DOM Testing Library、HappyDOM)にも互換性があるので、テストコードはほぼほぼ同じになる見込みです。
感想
「bunにしたら何か大きな違いがあるかも!」と思って動かしてみましたが、正直なところ既存技術との互換性が高いのもあって、あまり違いは感じられませんでした。
ただ、処理速度は速いに越したことはないし、TSのネイティブサポート、テストランナーなどのツールをデフォルトで提供してくれることは、設定の手間も減るので嬉しいことだと思います!
bun導入して良さそうなプロジェクトあったら導入して色々比較してみたいな。
Next.js14のチュートリアルやってみて思ったことを書きなぐる
え、起動早くない?
yarn dev
実行したところ、サーバーが起動するのが早いなと感じた。
使いたいライブラリめもっとく
clsx。これ何回か見たことあるけどいいよね。
今度合いそうなプロジェクトあったら使おう。
page.tsxがなるものがページの肝ね
ページ作る時の話で、/
のパスのページファイルは、/app/page.tsx
となるらしい。
あ、ページにしたいファイルはpage.tsx
という命名にしないと公開されないんだ!app
ディレクトリにページ公開しないファイルもいっぱいあるしね。
まあ確かに、元々のNext.jsだと/sample
のページを作るとしたときに、ファイルを/pages/sample.tsx
にするか/pages/sample/index.tsx
にするかちょっと悩んだもんな。
やっときたねLayout...。ずっと待ってた。
page.tsx
と並列で配置するlayout.tsx
が肝ですな。
ページコンポーネントをchildren
として受け取るlayout.tsx
をpage.tsx
と並列で配置すれば、そこ以下の階層のページ含めてレイアウトが適用される〜。
いじってみたところ、上層にレイアウトファイル置くと、下層のページにレイアウトファイル追加しても上書きされなさそうだから、下層ページにレイアウト置くことが多くなるのかなあ。 Route Group作ったらその層だけで適用できた!
結局レイアウトコンポーネントも他ページとも共通化したいだろうから、共通のレイアウト用コンポーネントを各ページのlayout.tsx
に呼び出す形になるのかなあ。
Router周りのhookがけっこう変わってる
今まではuseRouter
に現在のルート情報や、ルーター周りのアクションメソッドが全部入ってたけど、分けたみたいだな。
分割した方が読み込むコードが最適化されるとかそういう経緯なのかなあ🧐
usePathname
今のpathnameを返す
const pathname = usePathname()
// urlが /dashboard であれば '/dashboard'と返る
useParams
今のdynamic paramsを返す
const params = useParams()
//
useSearchParams
今のクエリストリングの情報を返す。
get(), has(), getAll(), keys(), values(), entries(), forEach(), toString() のメソッドが用意されてる
const searchParams = useSearchParams()
console.log(searchParams.get('page'))
// /search?page=1 だと "1"
useRouter
useRouterはpush
やreplace
などアクション系メソッドが集約されてる
reportWebVitalsがhookになった(useReportWebVitals)
元々web vitalsを取得するreportWebVitals
があったけどhookになった様子。
使い道としては、ローカル開発時に出力結果みるとか、本番環境とかでも出力結果を他分析ツールに送りたい時とか。
Routeセクションはちょっとまじで読み返す
https://nextjs.org/docs/app/building-your-application/routing
データ取得方法の検討も必要になりそう(並列処理or逐次)
逐次的にせざるを得ない(1つ目の返り値を元に2つ目のリクエストを走らせる)場合以外は、並列にした方がいい感じかな。ちなみに並列処理が全部終わったかは、Promise.all()
, Promise.allSettled()
あたりが使える。ただ、そのうちの1つのAPIがめちゃ遅かったら、他のも影響を受けるため、やっぱり適材適所。
Static Rendering / Dynamic Rendering
Server Renderingのストラテジー。
Static RenderingはSSGの時のサーバー側のレンダリング方法で、Dynamic RenderingはSSRの時。
言葉的に初めて聞いたからメモした。
Static Renderingしたときは、コンテンツはキャッシュされる。そのため、ウェブサイトの読み込みが早く、サーバーのロードも減って、クローラーがインデックスしやすいのでSEOにも有効。
loading.tsx
loading.tsxってファイル追加すれば、promiseがpending状態の間、そこで定義したUIを表示してくれちゃう。
スケルトン表示とかに良いね〜〜。
PPR
Partial Prerenderingのこと。いくつかの部分を動的に保ちながら、静的なローディングシェルでルートをレンダリングすることができる機能。ダイナミックな部分は並列にストリーミングされる。
続く
【Vue】Uncaught (in promise) TypeError: 'get' on proxy: property 'property' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value
エラー内容
Vue2からVue3に移行する際、以下のエラーが出ました。
Uncaught (in promise) TypeError: 'get' on proxy: property 'property' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value
原因
調べたところ、Vueのissueに上がってました。
リアクティブオブジェクトを扱う際、Vue2ではgetter/setterを利用していた一方、Vue3ではProxyを利用するようになりました。ちなみに、refは、Vue3でもgetter/setterを使ってます。
私の場合、クラスのインスタンスをリアクティブな値として登録していたのですが、
リアクティブ化を想定されていない、ディープな値までProxy化された結果、このエラーが発生したと認識してます。
対応
shallowReactive
またはmarkRaw
を使うことで解決しました。
shallowReactive
通常のreactiveはディープなリアクティブを提供するのに対して、shallowReactiveは浅いリアクティブを提供します。具体的には、ルートレベルのプロパティのみリアクティブとなります。
markRaw
オブジェクトがProxyに変換されないようにします。オブジェクト自体が返されます。
使い方
リアクティブな値の宣言時に、デフォルト値をラップします。
どちらも以下のように、Composition API, Options API のどちらでも使用可能です。
Composition
const foo = shallowReactive({})
Options
<script>
export default {
data() {
return {
foo: shallowReact({})
}
}
}
</script>
フロントエンドのセキュリティ対策
フロントエンドで特に注意すべき攻撃
XSS(Cross Site Scripting / クロスサイトスクリプティング)
悪意のあるスクリプトがウェブサイトに注入される攻撃。ユーザーからの入力を検証またはエンコードせずに生成する出力内で使用する場合に発生しやすい。ブラウザが保持し、そのサイトで使用されているクッキー、セッション・トークン、その他の機密情報にアクセスすることができ、HTMLページのコンテンツを書き換えることもできる。
- OWASP 「Cross Site Scripting (XSS)」より抜粋。
※悪意のあるCSSを注入する「CSSインジェクション」も存在する。
CSRF(Cross-Site Request Forgeries / クロスサイトリクエストフォージェリ)
エンドユーザーが現在認証されているウェブアプリケーション上で、望ましくない動作を実行させる攻撃。攻撃者が選択したアクションを実行させることができる。被害者が一般ユーザの場合、CSRF 攻撃が成功すると、ユーザは送金、電子メールアドレスの変更など、状態を変更する要求を実行させられる。犠牲者が管理者アカウントの場合、CSRF はウェブアプリケーション全体を危険にさらす可能性がある。
- OWASP 「Cross Site Request Forgery (CSRF)」より抜粋。
XSS対策
不明な値をHTML/CSS/各属性として出力する際は事前にサニタイズする
文字列をHTMLとして出力する場合があるかと思いますが、悪意のあるスクリプトが入ると、簡単に実行されるため、事前にサニタイズしましょう。
最近のフロントライブラリでは基本的には自動でエスケープしてくれますが、ReactではdangerouslySetInnerHTML
、Vueではv-html
を使用すると、文字列がエスケープされずに出力されます。
また、文字列をHTMLとして挿入する場合に限らず、CSSでもurl()
を利用してスクリプトを実行されたり、CSSインジェクションが可能になることもあります。
また、aタグのhref属性でもjavascript:
から始まるとJavaScriptが実行可能なので、不明な値が来る場合は事前にサニタイズするべきです。
サニタイズしてくれるライブラリ等
- DOMPurify
- XSS
- sanitize-html
- HTML Sanitizer API(2022年10月現在、実験的機能)
CSP(Content-Security-Policy)を設定する
有効なソースのドメインを指定することで、ブラウザが実行可能なスクリプト等を制限する設定です。
サーバーからHTTPヘッダー「Content-Security-Policy」を返すか、metaタグを追加することで設定可能で、スクリプト、画像、メディアなど各ソースごとに設定できます。
XSS対策に有効ではありますが、ウェブアプリに入れている計測系サービスのスクリプト等も拒否される場合があるので、設定時には依存するサービスの確認・対応が必要となります。
eval関数を使用しない
eval関数は引数の文字列をJavaScriptとして実行する関数です。
引数に悪意のあるスクリプトが入る可能性がある場合、使用しないのがベストです。
XSS/CSRF共通
情報の保持場所・保持方法を十分に検討・判断する
情報の機密性に合わせて適切な保持場所・保持方法は十分に検討すべきです。
よくある保管先は、Cookie, localStorage, sessionStorage, インメモリ等。Web Workerを使用しているケースもあるようです。
Cookieのオプション属性を適切に設定する
特に重要な属性は以下3つです。
- HttpOnly(
Boolean
): JavaScriptによるCookieの取得を許可しない - SameSite(
strict
|lax
|none
): Cookieを送信するドメインを制限する - Secure(
Boolean
): HTTPSのリクエストのみ、Cookieの送信を許可する
その他
実行環境やライブラリに脆弱性がないか
使用しているバージョンのNode.js等に脆弱性がないか、依存先のライブラリに脆弱性がないか、は定期的にチェックしましょう。
ライブラリに脆弱性がないかは、npm audit
やyarn audit
で確認可能です。
機密情報がブラウザに公開されてないか
サービスによっては、APIキーなど機密性の高い情報がある場合があります。そういった情報がブラウザに公開されていないか確認しておきましょう。
環境変数を使用する場合もあるかと思いますが、Next.jsなどではブラウザへの公開/非公開を設定可能です。
セルフレビューの観点と意識していること
最近聞かれることがあったので、自分がセルフレビュー時に意識していること・観点を言語化しておこうと思いました🙋♀️
※以下、「Pull Request」を「PR」、「Merge Request」を「MR」と記載します。
セルフレビューとは
他の人にレビューを依頼する前に、自分でレビューを行うことです。
自分でレビューを行い、気づいた点はレビュー依頼前に対応することで、PR/MRの精度を高めることができます。
精度が高いと指摘箇所が減るので、結果的にレビュアーや自分の負担を減らせたり、マージまでの時間が短くなったりします👍
セルフレビューのやり方
使用するツールは人それぞれかと思いますが、変更の差分を確認できるものをおすすめします。
私の場合は、GitHubやGitLab上でPR/MRを作成する時、まずはDraft状態にして、そこでセルフレビューを行なっています。
セルフレビューする時に意識すること
PRを自分のものとは思わないこと🙅♀️ です。
他の人のPRをレビューするつもりで確認します。
主観的だとどうしても見落としが発生したり、視野が狭くなります。
客観的に自分のコードを確認することが大切です。
あと、焦りも抜け漏れを生むので、「早くPR出したい!」という気持ちがもしあったとしても、ぐっと抑えて落ち着いてやりましょう。
セルフレビューする時の観点
超基本的なこと
- 不要なファイルはコミットされてないか
- 不要なログはないか
- タイポ(綴り間違い等)はないか
バグがないか
- 既存処理に影響はないか(特に共有コードに変更を加えた場合など)
- テストは通るか
- エラー発生時の処理もカバーできているか
- イレギュラーな動きをする場合がある値(文字列なら空文字
""
, 数字なら0
など)への対応も適切か
コードの質
- わかりやすいコードか(何のための何をするコードか、他者が見てもわかるか)
- 変数・関数などの命名は適切か
- 共通化できる/した方が良い部分はあるか
- 分離した方が良い部分はあるか
- もっと良いやり方はないか(ベストと思えるコードか)
- 変更や追加した箇所のテストを追加したか
コーディング規約に沿っているか(プロジェクトでコーディング規約がある場合)
実際に動かしてみる
コードを確認するだけではなく、セルフレビュー時に動作確認も行うことをおすすめします🙋♀️
- 動作は問題ないか
- イレギュラーだけど起こりうる値が来たり、操作が行われた場合どうなるか
レビュアーへの配慮
レビュアーが気になりそうなことはコメントしておく
なぜ必要なのか、なぜこれを使ったのか等、レビュアーが気になりそうなことは、PRにコメントしておくのがおすすめです。
やり取りの削減に繋がりますし、もし後からそのPRを見直した時にもわかりやすいです。
※コード内に残した方がいいコメントはコード内に残しましょう。
悩んでいることや質問があればコメントしておく
自分はこのコードに行き着いたけど、本当にこれでいいか悩む時は「他に良いやり方あったら教えてください」と、PRの該当箇所にコメントするといいです。お互いの知識共有やより良いコードに繋がります。
最後に
セルフレビュー時の観点をお伝えしましたが、結局通常のレビューをする時と同じですね😂
レビュー観点が身につくと、それを想定したコードが書けるようになるので、コードの質も上がっていきます。レビュー観点は、自分がレビューを受ける時にも吸収していきたいですね!
ローカル環境でクロスドメイン状態を作り出す方法
ローカル環境でクロスドメイン状態を作る方法を記録しておきます。
なぜクロスドメイン状態をローカルで作りたかったか
本番環境に近い状態を一時的に作りたかったからです。
本番環境ではフロントとバックエンドが異なるドメインで動いていました。しかし、ローカル環境ではフロントもバックエンドも同じドメイン、localhost
で動いてました。
その結果、ローカル環境と本番環境で、通信時のCORS周りの挙動が異なっていました。
前提
- CORSの設定は済んでおり、ローカル環境でのフロント・バックエンド間の通信は既にできている
方針
フロントを127.0.0.1、バックエンドをlocalhostで動かします。
そうすることで、異なるドメイン間の通信を再現することができます。
バックエンド側で行うこと
バックエンドはlocalhostで動きますが、いくつか準備することがあります。
SSL化(https化)する
バックエンド側をSSL化(https化)します。
クロスドメイン通信時、通信先がhttpではCookieの共有が行われないからです。
Dockerを利用してバックエンドを起動している場合
https-portalというイメージを利用すれば簡単にできます。docker-compose.yaml
に以下コンテナを追加すればOKです。
https-portal:
image: steveltn/https-portal:1
ports:
- '8043:443' # https://localhost:8043で立ち上がります
environment:
STAGE: local
DOMAINS: 'localhost -> http://host.docker.internal:8080' # https://localhost:8043をhttp://localhost:8080(実際にバックエンドが立ち上がっているポート)に接続します
127.0.0.1をCORSの許可オリジンに追加する
Access-Control-Allow-Originに該当する設定が、バックエンド側コードに存在するかと思います。
そこにフロント側が動くオリジン(例: http://127.0.0.1:3000
)を追加します。
フロント側で行うこと
バックエンドと通信するエンドポイント変更
https化したエンドポイントに変更してください。
実際に動かしてみる
フロント側はlocalhostではなく、http://127.0.0.1:3000
など127.0.0.1にアクセスしてください。