TECH PLAY

株式会社ラクス

株式会社ラクス の技術ブログ

919

はじめに こんにちは! エンジニア2年目のTKDSです! この記事ではDaggerについて紹介します。 この記事は課内で行ったLTをもとにしたものです。 はじめに Daggerとは? アーキテクチャ概要 Dagger Function Dagger Module 実際につかってみる ユースケース1:テストのパイプラインを記述 ユースケース2:DB依存の単体テストでDBのコンテナを用意する まとめ Dagger とは? CIの関数(Dagger Function)化 関数を CLI 、 SDK 、HTTPリク エス トなどから実行可能 関数→モジュールにして再利用可能 既存のモジュール(自分や他人が作ったもの)を再利用可能 一度書けばどこでも(ローカルPC、Actionsなど)実行可能 以上の特徴を持つ、コンテナ内でパイプラインを実行する、プログラム可能なCI/CD エンジンです。 アーキテクチャ 概要 Daggerを構成する要素は以下の図のとおりです。 GraphQL ServerがDagger Engine内の各要素を操作します。 コンテナの実行、ファイルや ディレクト リとのやり取りなど基本的な操作はCore API として用意してあり、そこに作成したDagger Module(CI の塊:Dagger Function)を追加して、CIを実行可能にします。 Dagger Function CIの手順はDagger Engineが生成したコンテナ内で実行されます。 Dagger Functionの 特徴 は以下の三点です。 再現性(Reproducibility) 毎回同じ方法で実行され、実行環境外との依存関係が発生しません。 キャッシュ(Caching) 実行結果をキャッシュすることで、実行を高速化できます。 セキュリティ(Security) デフォルトではホスト環境(ホストファイル、 ディレクト リ、 環境変数 など)にアクセスできません。 特に便利なのは、再現性とセキュリティの特徴です。 Daggerはコンテナ内で実行され、デフォルトで実行可能な操作の権限が制限されているため、セキュリティが強化されます。 また、CI実行環境がコンテナ単位で独立しているため、ホスト環境や他のコンテナに依存せず、実行環境外との依存関係が発生しません。 Dagger Module 前述のDagger Functionをまとめたものです。 他の人が作ったものを利用することもでき、CIの再利用性を高めてくれます。 実際につかってみる 現在のドキュメント では以下の SDK が記載されています。 Go Python TypeScript 今回はこの中で一番好きな言語であるGoを使います。 インストール方法などは公式ドキュメントを参考にしてください。 ユースケース 1:テストのパイプラインを記述 1つめは操作の流れをコードに書き、Dagger CLI から実行する使い方です。 以下の画像のようにGoのコードで行う操作を記述します。各操作は次のとおりです。 Container().From : 新規のコンテナ作成 WithExec():コンテナ内で実行されるコマンド. 引数に変数の値を引き渡せる Container.Stdout() : 標準出力 dagger call scan-image --image-ref alpine:latest 上記のコマンドを実行すると結果は以下のように出力されます。 alpine:latest (alpine 3.19.1) ============================= Total: 2 (UNKNOWN: 0, LOW: 2, MEDIUM: 0, HIGH: 0, CRITICAL: 0) ┌────────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬───────────────────────────────────────────────────────────┐ │ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │ ├────────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼───────────────────────────────────────────────────────────┤ │ libcrypto3 │ CVE-2024-2511 │ LOW │ fixed │ 3.1.4-r5 │ 3.1.4-r6 │ openssl: Unbounded memory growth with session handling in │ │ │ │ │ │ │ │ TLSv1.3 │ │ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-2511 │ ├────────────┤ │ │ │ │ │ │ │ libssl3 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └────────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴───────────────────────────────────────────────────────────┘ 脆弱性 チェックのCIを実行することができました。 ユースケース 2:DB依存の 単体テスト でDBのコンテナを用意する 2つ目は、コンテナの操作を直接テストコード内に書く使い方です。 実はDaggerのgo sdk はGoのコード内に記述しておくことで、Dagger CLI を使わずに実行することが可能です。 以下のようにテストコードを用意します。 実行結果は以下のとおりです。 Daggerを使って、 単体テスト のコード内にDBを用意するコードを記述して実行することができました! まとめ 今回はDaggerを紹介しました。 個人的には、 ユースケース 2のDB依存のテストケースにコンテナ起動まで記述する使い方がおすすめです! 今までは、別でコンテナを起動してテストを実行する必要がありましたが、Daggerを使えば、特にDBの起動状態を意識することなくテストが実行可能です。 また、コンテナを破棄すれば状態をリセットできるため、フレーキーテストなどの削減などにも役に立つのではないかと感じました。 今回の記事は以上です。 ここまで読んでいただきありがとうございました!
アバター
こんにちは、フロントエンド開発課所属の koki _matsuraです! 本記事では、E2EテストライブラリであるPlaywrightのv1.40 ~ 最新版v1.43で追加された機能の中から僕の独断と偏見でいくつかを紹介したいと思います。 では早速、紹介していきます! 以下は目次です。 v1.40の新機能 Test Generatorにアサーションコード生成機能 toBeVisibleアサーション toContainTextアサーション toHaveValueアサーション v1.41の新機能 screenshot関数のstyleオプション toHaveScreenshot関数のstylePathオプション v1.42の新機能 addLocatorHandler関数 タグの追加 v1.43の新機能 LocatorとFrameLocatorの相互変換 clearCookiesのフィルター機能 まとめ v1.40の新機能 Test Generatorに アサーション コード生成機能 Playwrightの目玉機能の画面上の操作をテストコードに書き起こしてくれる、Test Generatorにv1.40から アサーション コードを生成する機能が追加されました。 下画像はv1.39とv1.40のTest Generatorの初期画面を比較したものです。 v1.39 v1.40 違いは画面上部のツールです。 このツールには6つのアイコンがあります。 左から順に「 ツールバー の移動」「Test Generateの中断」「要素の取得」「toBeVisible アサーション 」「toContainText アサーション 」「toHaveValue アサーション 」を対応します。 今まではTest Generatorの中断や要素の取得はできていました。しかし、 アサーション は対応していなかったのでユーザが自身で書かなければなりませんでした。 v1.40からは簡単なテストであれば、最初から最後までTest Generatorで書くことが可能になります! toBeVisible アサーション 使い方は簡単で、目のマークをクリックし、 アサーション したい要素をクリックするのみです。 上画像のように「 ラク スの思い」というlinkをクリックすると、下記のテストコードが生成されます。 await expect ( page.getByRole ( 'link' , { name: 'ラクスの思い' } ) .first ()) .toBeVisible (); toContainText アサーション これも同様に、'ab'のアイコンをクリックして、 アサーション 対象をクリックします。 すると、textBoxが出てくるのでここに含むと思われるテキストを入力します。 チェックマークをクリックすればコードが生成されます。 await expect ( page.locator ( '#APjFqb' )) .toContainText ( 'ラクス' ); toHaveValue アサーション ここも同じです。 アサーション 対象をクリックするだけで生成されます。 await expect ( page.getByRole ( 'combobox' , { name: '検索' } )) .toHaveValue ( 'ラクス' ); v1.41の新機能 screenshot関数のstyleオプション VRT(Visual Regression Test)を行う際にscreenshot関数を使った人も多いと思います。 この関数にはオプションがいくつかあるのですが、v1.41でstyleオプションが追加されました。 このオプションには記述したスタイルを スクリーンショット 作成時に反映してくれます。 下記が具体的な書き方です。 await page.screenshot ( { style: ` ${ カスタムなスタイル } ` , path: ` ${ 画像の保存先のパス } ` , } ); どの様な場面で使えるのかを一例を交えて紹介します。 下画像のようなトップ画面があるとします。(Muiのテンプレを使わせていただきました) このトップ画面をスクショしたいのですが、一定の確率で邪魔なやつが出現します。 styleオプションが出るまでは、消す操作を行ったり、コード自体をいじったりして対応していました。ですが、一定の確率で出現する場合、消す操作をテストコードに入れると出現しなかった時、エラーを起こしてしまいます。 そういう時にこのstyleoptionが使えます。 使い方は簡単で、邪魔なやつの要素を指定して、非表示にするだけです。 テストコードだと下記の様になります。 邪魔なやつには id="obstruction-card" を付与しています。 await page.screenshot ( { style: "#obstruction-card { display : none }" , path: "tests/screenshot/sample.png" , } ); これだけで保存されたスクショには邪魔なやつは写っていないはずです。 toHaveScreenshot関数のstylePathオプション toHaveScreenshotは事前に保存していた画像と指定したpageまたはlocatorの画像を比較し、差分が出ている場合には差分の画像も出力してくれる便利な関数です。 この関数にも先ほどに似たスタイル系のオプションが増えました。 少し違うのは、 スタイルシート ではなく、 スタイルシート へのパスを記述する点です。 下記の様に書きます。 sample.png は事前に用意しておく画像です。 また、stylePath内のスタイルはこれからスクショする画像に対して、反映されます。 await expect ( page ) .toHaveScreenshot ( "sample.png" , { stylePath: "tests/sample.css" , } ); v1.42の新機能 addLocatorHandler関数 ※ これはExperimentalです。 この関数はLocatorとHandlerの2つの引数を取り、そのLocatorに対して、Handlerの内容を実行します。 await page.addLocatorHandler ( $ { Locator } , $ { Handler } ); 具体的な使い道としては、v1.41の一例で出したものと同じでE2Eテスト中に表示される邪魔なやつを処理したいときです。 上画像では「 Sign In」のボタンの上に邪魔なやつが表示されています。 もしこの状態で下記ようなSignInボタンを押す処理が入っているテストを実行すると失敗してしまいます。 test ( "Sign in ボタンを押すと、ログイン画面が表示されること" , async ( { page } ) => { await page. goto( "http://localhost:5173/" ); await page.getByTestId ( "sign-in-button" ) .click (); expect ( page.url ()) .toEqual ( "http://localhost:5173/login" ); } ); この様な場合に、邪魔なやつへの操作を最初に定義することで適切にテストが通ります。 async ( { page } ) => { await page.addLocatorHandler ( page.getByTestId ( "obstruction-card" ), async () => { await page.getByTestId ( "card-cancel-button" ) .click (); } await page. goto( "http://localhost:5173/" ); ... ) タグの追加 各テストに対して、タグがつけられる様になりました。 test関数の第二引数のオプションから指定します。 付けたいタグが1つの場合はstring型でも大丈夫です。 test ( 'sampleTest' , { tag: [ '@sample' ] , } , async ( { page } ) => { // ... } ); タグを指定できることによって、テストをタグ絞り込みで実行できます。 例えば、 @sample とタグ付けしたテストのみ実行したい場合は、下記の様にテスト実行を行います。 npx playwright test --grep @sample 今までのテスト実行では任意のテストのみ実行するのは難しかったので、個人的には非常に嬉しい機能です。 v1.43の新機能 LocatorとFrameLocatorの相互変換 LocatorにはFrameLocatorへ変換をかけるcontentFrameという関数、 FrameLocatorにはLocatorへ変換をかけるownerという関数が追加されました。 // FrameLocator型への変換 const frameLocator = locator.contentFrame (); // Locator型への変換 const locator = frameLocator.owner (); 個人的にはFrameLocatorオブジェクトを保持しておき、好きなタイミングでLocatorにすぐに変換できるowner関数が嬉しいです。 clearCookiesのフィルター機能 Cookie を削除するclearCookies関数のオプションにフィルターの機能が増え、特定の Cookie のみを削除するということが可能になりました。 使い方は下記の様に Cookie 情報を引数に与えることでフィルターをかけます。 具体的には、 cookie の name または、 domain または、 path を指定します。 await context.clearCookies ( { $ { Cookie情報 } } ); 具体的なテストコードの一例を載せておきます。 適当な Cookie を2つ設定し、 name : id-1 のものを削除しています。 test ( "clearCookies id-1" , async ( { context } ) => { await context.addCookies ( [ { name: "id-1" , value: "value-1" , url: "http://localhost:5173/" , } , { name: "id-2" , value: "value-2" , url: "http://localhost:5173/" , } , ] ); await context.clearCookies ( { name: "id-1" } ); const cookies = await context.cookies (); expect ( cookies ) .toEqual ( [ { domain: "localhost" , expires: -1 , httpOnly: false , name: "id-2" , path: "/" , sameSite: "Lax" , secure: false , value: "value-2" , } , ] ); } ); } ); まとめ 本記事ではPlaywright v1.40~v1.43の中から僕の独断と偏見でアップデートした部分を紹介させていただきました。 個人的にはexperimentalではあるのですが、addLocatorHandlerが一番嬉しい機能でした! Playwrightは去年あたりからユーザが増え、それに伴い、ものすごく便利な機能がどんどんと増えていて、開発で使っている身としては嬉しい限りです。 今回紹介していない機能もありますので、よければ公式サイトの Release Note を見ていただければなと思います。 最後まで読んでいただきありがとうございました!
アバター
はじめに こんにちは、新卒2年目の菊池(akikuchi_rks)です。 近年、 Android アプリ開発 のみならず、サーバーサイドの開発言語としてもKotlinが急速に注目を集めています。私自身もKotlinを使ってサーバーサイドの開発を行っており、豊富な機能やシンプルな文法に魅力を感じています。 Kotlinを使用していて特に感じるのは、そのコレクション関数の充実性です。コレクション操作はプログラミングにおいて頻繁に行われるため、これらの関数が豊富であることはKotlinの特長のひとつと言えると思います。 また、これに ラムダ式 を組み合わせることで、よりシンプルで効率的なコーディングが可能となります。 この記事では、Kotlinでコーディングをする際に欠かせないコレクション関数に焦点を当て、その中からいくつかピックアップして紹介していきます。 初心者の方でもわかりやすいように、具体的なコード例も交えながら説明していきますので、ぜひ最後までご覧ください。 はじめに コレクションとは、ラムダ式とは コレクションとは ラムダ式とは コレクション関数の活用例 map filter any, all, none associate, associateBy, associateWith groupBy partition コレクション関数を使うもう一つのメリット ミュータブルな変数を使わないほうがいい理由 1. 可読性が低下する 2. 予期せぬバグを生む まとめ コレクションとは、 ラムダ式 とは まずはそもそもコレクションとは何なのか、 ラムダ式 とは何なのか簡潔に説明します。 コレクションとは まず、Kotlinにおけるコレクションは複数の要素をまとめて管理するためのデータ構造です。 主な種類としては、 List 、 Set 、 Map などがあります。 List : 要素の順序付き集合。同じ要素を複数持つことができる。 Set : 要素の順序がない集合。重複する要素を持たない。 Map : Keyと値 Value のペアの集合。Keyは一意で重複しない。 val sampleList = listOf( 1 , 2 , 3 ) val sampleSet = setOf( 1 , 2 , 3 ) val sampleMap = mapOf( 1 to "one" , 2 to "two" , 3 to "three" ) println(sampleList) // [1, 2, 3] println(sampleSet) // [1, 2, 3] println(sampleMap) // {1=one, 2=two, 3=three} 上記のようにコレクションを宣言する際には listOf 、 setof 、 mapOf を用いて インスタンス を生成します。 これらのコレクションは読み取り専用のコレクションでそれぞれ宣言した後に要素の追加、削除、更新はできません。 後で要素の変更を行いたい場合にはミュータブル(変更可能な) MutableList 、 MutableSet 、 MutableMap を使います。 しかし、詳細は後述しますがコレクションを扱う場合は、書き換える必要がないものは変更不可のコレクションを使用するのが望ましいです。 そして、これらのコレクションを操作するための関数がコレクション関数です。 Kotlinの標準ライブラリとして用意されており、コレクションの変換、フィルタリング、グルーピングなど様々な操作を行うための関数が揃っています。 ラムダ式 とは ラムダ式 は、無名関数(名前を定義しない関数)をさらにコンパクトにし、最小限の記述量で定義できるようにしたものです。 val square: ( Int ) -> Int = { x -> x * x } val result = square( 5 ) // 25 この例では、 square という名前の変数に、 ラムダ式 が代入されています。 変数自体には名前が付ける必要がありますが ラムダ式 は Int を受け取り、Int を返す無名関数です。 { x -> x * x } という部分が ラムダ式 であり、 -> の左側に引数、右側に実際の関数の処理を記述します。 ラムダ式 は他の関数の引数として直接渡すこともできます。 fun operateNumber(x: Int , operation: ( Int ) -> Int ): Int { return operation(x) } val result = operateNumber( 5 ) { it * it } println(result) // 25 この例では、 operateNumber という関数が定義されています。 この関数は、整数値とそれを操作する関数を受け取ります。 このような関数を引数として受け取る関数に ラムダ式 を直接記述することで関数定義の手間を省くことができます。 ラムダ式 内で使われている it という引数は受け取るパラメータが1つしかない ラムダ式 を記述する際に使うことができる暗黙の引数で -> の左側に引数を宣言をする手間をさらに省くことができます。 前述したコレクション関数は関数を引数にとるものが多いため、 ラムダ式 と組み合わせて使用されることが多く、これらのコレクション関数と ラムダ式 を組み合わせることでシンプルで効率的なコーディングが可能となります。 コレクション関数の活用例 コレクション関数を ラムダ式 と組み合わせて活用することによりコードをシンプルにする具体例をいくつか紹介していきます。 map 以下は整数値のListの各要素を2倍にする際のコーディング例です。 val numbers = listOf( 1 , 2 , 3 ) val result = mutableListOf< Int >() for (i in numbers) { result.add(i * 2 ) } println(result) // [2, 4, 6] このようなListの各要素の値に何らかの変換処理を加えて別のリストを生成する際には map が利用できます。 map は ラムダ式 で各要素に加えたい変換処理を指定することで、その変換処理を加えた結果のリストを生成することができます。 val numbers = listOf( 1 , 2 , 3 ) val result = numbers.map { it * 2 } println(result) // [2, 4, 6] filter 以下は整数値のListから奇数のものだけを取り出す際のコーディング例です。 val numbers = listOf( 1 , 2 , 3 ) val result = mutableListOf< Int >() for (i in numbers) { result.add(i % 2 == 1 ) } println(result) // [1, 3] このようなListから特定の条件を満たす要素のみを抽出して取り出すような処理は filter を使うことで、シンプルに記述することができます。 filter では ラムダ式 に条件式を記述し、その式が true に判定される要素のみを抽出したListが生成されます。 val numbers = listOf( 1 , 2 , 3 ) val result = numbers.filter { it % 2 == 1 } println(result) // [1, 3] filter の逆で ラムダ式 の条件式が false に判定される要素のみを抽出したい場合には filterNot が使えます。 any, all, none 以下は整数値のListの要素に偶数か含まれているかを判定する例です。 val numbers = listOf( 1 , 2 , 3 ) var result = false for (i in numbers) { if (i % 2 == 0 ) { result = true break } } println(result) // true こちらは any を使うことでシンプルに記述することができます。 any はコレクションの要素に対して、指定した条件式が true になるものが存在するかどうかを判定します。 val numbers = listOf( 1 , 2 , 3 ) var result = numbers.any { it % 2 == 0 } println(result) // true any と同じようにコレクションの要素がある条件に合致しているかどうかを判定するコレクション関数に all や none があるので併せて紹介します。 any : 一つでも条件を満たす要素があれば true (空の場合は false ) all : 全ての要素が条件を満たせば true (空の場合は true ) none : 一つも条件を満たさなければ true (空の場合は true ) いずれも使い方は any と同様です。 associate, associateBy, associateWith 以下は文字列のリストから元の文字列をKey、その文字列の長さを Value としたMapを生成する例です。 val colors = listOf( "red" , "yellow" , "blue" ) val result = mutableMapOf< String , Int >() for (color in colors) { result[color] = color.length } println(result) // {red=3, yellow=6, blue=4} このようにListからMapを生成する際は associate が使えます。 associate は Key to Value といった形式のPair型を返す ラムダ式 を指定することで、指定したKeyと Value のMapを生成することができます。 val colors = listOf( "red" , "yellow" , "blue" ) val result = colors.associate { it to it.length } println(result) // {red=3, yellow=6, blue=4} associate と似たコレクション関数に associateBy , associateWith があります。 associate : ラムダ式 でKeyと Value をPair型で指定 associateBy : ラムダ式 でKeyを指定、 Value は各要素の元の値となる associateWith : ラムダ式 で Value を指定、Keyは各要素の元の値となる Keyをカスタムしたいか、 Value をカスタムしたいか、あるいは両方カスタムしたいかで使い分けます。 例として associateWith を用いて上記の処理を書き換えると以下のようなコードになります。 val colors = listOf( "red" , "yellow" , "blue" ) val result = colors.associate { it.length } println(result) // {red=3, yellow=6, blue=4} groupBy 以下は整数値のListの要素を偶数と奇数に分類する際のコード例です。 val numbers = listOf( 1 , 2 , 3 , 4 , 5 ) val evens = mutableListOf< Int >() val odds = mutableListOf< Int >() for (i in numbers) { if (i % 2 == 0 ) { evens.add(i) } else { odds.add(i) } } val result = mapOf( "even" to evens, "odd" to odds) このようなコレクションの要素をある特徴によって分類したい場合は groupBy を使用することでシンプルに記述することができます。 groupBy は ラムダ式 が返した値をKeyとしてその値ごとに各要素を分類し、Mapとして返します。 val numbers = listOf( 1 , 2 , 3 , 4 , 5 ) val result = numbers.groupBy { if (it % 2 == 0 ) "even" else "odd" } println(result) // {odd=[1, 3, 5], even=[2, 4]} ここでは、偶数の場合に返される文字列 even と奇数の場合に返される文字列 odd をKeyとしているため、リストの値が偶数の場合は even をKeyとする Value のリストに値が格納され、リストの値が奇数の場合は odd をKeyとする Value のリストに値が格納されます。 ちなみに、 groupBy の第二引数にさらに ラムダ式 を指定すると、各要素に任意の変換処理を加えた値をMapに詰めることができます。 groupBy と前述した map の処理を同時に行うことができ、かなりコードがコンパクトになります。 val numbers = listOf( 1 , 2 , 3 , 4 , 5 ) val result = numbers.groupBy({ if (it % 2 == 0 ) "even" else "odd" }, { it * 3 }) println(result) // {odd=[3, 9, 15], even=[6, 12]} partition groupBy の例のように、単純にリストの要素を2つのグループに分割するだけであれば partition も利用することができます。 val numbers = listOf( 1 , 2 , 3 , 4 , 5 ) val result = numbers.partition { it % 2 == 0 } println(result) // ([2, 4], [1, 3, 5]) partition は groupBy と同様にコレクションの要素を ラムダ式 で指定した条件で分割する関数です。 groupBy と異なるのは partition は ラムダ式 に true , false で判定される条件式を指定する必要がある点と、返される値がMapではなくPairであるという点です。 partition では ラムダ式 の条件が true となった要素がfirstのリストに格納され、 false となった要素がsecondのリストに格納されます。 コレクション関数を使うもう一つのメリット 上記の活用例であげたコードを見ていて気付かれた方もいるかと思いますが、コレクション関数を活用することで var や mutableList といったミュータブルな変数の使用を減らすことができます。 この変数の可変性を減らせることがコレクション関数を使用する重要なメリットの1つであると思っています。 ミュータブルな変数を使わないほうがいい理由 ミュータブルな変数をなるべく使わないほうがいい理由を以下に挙げます。 1. 可読性が低下する 変数が再代入できるようになっていると、その変数の値がどのように変化するかを追跡するのが難しくなります。 その結果、コードの理解やメンテナンスが難しくなります。 2. 予期せぬバグを生む 変数の再代入は、その変数を参照している他の部分に影響を与える可能性があります。 特に、大規模なアプリケーションでは変数の書き換えが思わぬ箇所に影響を与え、予期しないバグや振る舞いを発生させる可能性が高くなります。 これらのデメリットを踏まえると、ミュータブルな var や mutableList といった注意深く使用する必要があり、使用は最小限に抑えたほうが良いです。 以上の理由から、コレクション関数を活用してミュータブルな変数の使用を抑えることはコードの可読性、堅守性を上げることにも繋がると言えます。 まとめ Kotlinのコレクション関数と ラムダ式 はコードをシンプルで効率的にするための強力なツールです。 これらを使いこなすことで、コードの可読性を向上させ、より効率的に開発を進めることができます。 さらに、コレクション関数と ラムダ式 を活用することでミュータブルな変数の使用を抑えられ、コードの堅牢性向上も期待できます。 Kotlinには本記事で紹介したコレクション関数以外にも便利なものが数多く揃っているので、今後もこれらのコレクション関数を活用して効率的な開発を行っていきたいです。
アバター
※注意:本記事内での計測結果は記載の条件下によるものとなります。異なる環境においては異なる結果が予想されますのでご認識ください。 こんにちは。 株式会社 ラク スにて、主に先行技術検証を担当している「技術推進課」という部署に所属している鈴木( @moomooya )です。 ラク スの開発部ではこれまで社内で利用していなかった技術要素を自社の開発に適合するか検証し、ビジネス要求に対して迅速に応えられるようにそなえる 「技術推進プロジェクト」 というプロジェクトがあります。 このプロジェクトで「DBセキュリティ」にまつわる検証を行なったので、その報告を共有しようかと思います。 今回はDBセキュリティの中でも、DBデータの暗号化の話が中心となります。 ちなみに中間報告時点で公開した記事はこちらになります。 tech-blog.rakus.co.jp DBセキュリティについて 本記事におけるDBセキュリティ カバーする範囲 カバーしない範囲 今回扱うデータ暗号化手法 今回実施した検証内容 検証概要 比較した環境 共通した条件 AWS実行環境 テストデータ 検証結果 ディスク暗号化ではほぼ性能劣化は見られなかった 暗号化カラムへの操作 非暗号化カラムへの操作 CPU, メモリへの負荷 性能面以外の注意点 LUKSはバックアップ運用や、サーバー再起動時に注意が必要 まとめ 暗号化手法ごとの特徴 LUKS pgcrypto TDEforPG 導入するとしたら ストレージ盗難対策なら マイナンバーなどの機微情報を保護するためなら DB全体の暗号化をするなら DBセキュリティについて 本記事におけるDBセキュリティ カバーする範囲 本稿で扱う「DBセキュリティ」については入室管理、アカウント管理、権限管理、データ暗号化、アクセス監視といった部分を対象とし、中でも技術的な検証が必要になるデータ暗号化について検証を進めていきます。 これらは 個人情報の保護に関する法律についてのガイドライン(通則編) にて 10-6 技術的安全管理措置 として記述されています。 カバーしない範囲 「DBセキュリティ」の範囲として定義した通り、 ウェブアプリケーション 経由のセキュリティは扱いません 。 なので DBMS への通常アクセスによるセキュリティに関しては対象外とします。 今回扱うデータ暗号化手法 今回の検証で扱ったデータ暗号化手法は以下のとおりです。 ディスク暗号化 DBのデータファイルが格納されているディスクごと暗号化 LUKSを利用 DB機能による透過的暗号化 DBMS 側でデータ入出力時に暗黙的に暗号化/復号化を行う NEC 製の Transparent Data Encryption for PostgreSQL を PostgreSQL (以下、TDEforPG)に組み込んで利用 機能として列単位暗号方式(カラム指定で暗号化)と行単位暗号方式(指定テーブルの全カラムを暗号化)があるので両方検証 プロプライエタリ な製品なので詳細な計測結果は非公開とします アプリケーションによるデータ暗号化 アプリケーションによりデータを暗号化してから DBMS に格納 pgcryptoを利用 gitlab.com jpn.nec.com www.postgresql.org 今回実施した検証内容 検証概要 今回の検証では暗号化処理による性能劣化度合いの計測を中心に取り組んでいきます。 計測方法としては、非暗号化環境と各種暗号化環境を構築し、それぞれの環境で同一の性能計測テストを実施、測定結果の差分を暗号化処理によって劣化した性能として見ていきたいと思います。 性能計測テストでは 参照系としてselect 暗号化カラムを条件にした場合 非暗号化カラムを条件にした場合 更新系としてinsert, update を中心とした計測を行いました。 計測観点では 処理速度 CPU負荷 メモリ負荷 の観点で計測を行なっています。 比較した環境 非暗号化環境 この環境での計測結果を基準値とする LUKS環境 ディスク暗号化 pgcrypto環境 暗号化カラムと非暗号化カラムが混在するテーブルを持つ TDEforPG(列暗号方式) 暗号化カラムと非暗号化カラムが混在するテーブルを持つ TDEforPG(行暗号方式) 全てのカラムが暗号化されたテーブルを持つ 各環境は AWS EC2上に作成しており、EC2起動テンプレートを用いて環境構築を行った。 非暗号化環境の起動テンプレートをベースとして、暗号化手法のみが差分となるように各環境の起動テンプレートを作ることで環境差異を暗号化手法のみとなるようにした。 共通した条件 AWS 実行環境 EC2 m4.xlarge EBS gp2 10GB EC2で利用した インスタンス は性能が可変のTシリーズではなく、通常の汎用 インスタンス であるMシリーズを利用しています。 ストレージについてはEBSの汎用 SSD gp2を利用しています。 テストデータ 100万レコードのテーブル データの内容はランダムな値をデータとして格納 データ型は整数、実数、テキスト、真偽値を持つ テストデータとしては実際のサービスで利用したときの処理速度に近くなるようにある程度のデータを用意しました。 検証結果 暗号化処理が増えることにより数十%程度の性能劣化は想定していましたが、一部のパターンで注意すべき性能劣化を計測することができました。 ディスク暗号化ではほぼ性能劣化は見られなかった LUKSによる暗号化環境では、今回計測した全ての項目で非暗号化環境とほぼ同じ性能結果になりました。 厳密には128bitでの暗号化時は劣化なし。256bitの場合は10%程度の劣化が見られる感じです。 LUKSについてはディスクパフォーマンスの ベンチマーク も行いましたが、こちらではランダムライトで性能劣化が見られましたが、それ以外は性能劣化はありませんでした。 しかし、ウェブ上で事例を探すと SAS HDDの場合にシーケンシャルI/Oが劣化したり、NVMe SSD の場合にCPU負荷が上がる事例がありました。 今回はgp2を利用しましたが、EBS gp3を利用していたらCPU負荷の増加があったかもしれません。 暗号化カラムへの操作 どの暗号化方式でも処理速度の劣化は発生しました。 特に注意する必要があるのはpgcryptoで、暗号化したカラムを条件にしたselectで7,600倍 の処理速度の劣化が見られました。実測値として、非暗号化時の 74ミリ秒に対して9.4分 です。 TDEforPGの場合だと、行単位暗号化で4倍程度、列単位暗号化で2倍程度の劣化で済んでおり、性能面でかなり優位性があります。 暗号化カラムを条件とした検索が必要な場合はpgcryptoではなく、TDEforPGを選択する強い理由になると思います。 非暗号化カラムへの操作 pgcryptoに関しては非暗号化カラムを条件としたselectでも10〜20%程度の処理速度劣化の影響が見られました。 また pgcryptoではinsertでも75倍の処理速度劣化、updateでは25倍の処理速度低下 が見られました。 これはインデックスが有効な状態ではそれぞれ、17倍、18倍程度まで軽減されることを確認できました。 TDEforPGを用いた場合は、行単位暗号化、列単位暗号化ともに若干影響はあるように見えましたが劣化は5〜10%程度であり、あまり問題ない範囲かと思われます。 CPU, メモリへの負荷 今回の検証においては、すべての操作で暗黙的に暗号化/復号化が行われるTDEforPGの行暗号化方式で、CPUの平均で30%程度、メモリピークで30%程度の負荷増加が見られました。 それ以外の暗号化手法については誤差程度の差しか観測できませんでした。 性能面以外の注意点 LUKSはバックアップ運用や、サーバー再起動時に注意が必要 性能面ではあまり心配のいらないLUKSですが、ディスク全体を暗号化する都合上LUKSヘッダーという領域を必要とします。 LUKSヘッダーが破損するとディスク全体が読めなくなるため、バックアップ運用にてLUKSヘッダーもバックアップ対象に含める必要があります。 また、サーバー再起動時には都度暗号化ディスクの再マウントが必要になります。自動化することが可能なので設定しておくと良いと思います。 まとめ 暗号化手法ごとの特徴 LUKS Linux の標準機能の一つとなっており、性能面への影響も少ないため導入しやすい。 ただしディスク暗号化のため、ストレージの盗難 1 にしか対処できず OSへの不正ログイン DBMS への不正ログイン が行われた場合には効果がない。 pgcrypto PostgreSQL の公式モジュールとして提供されているため導入ハードルは低い。 また、アプリケーションでの暗号化となるため、 ストレージ盗難 OSへの不正ログイン DBMS への不正ログイン に対しても耐性をもつ。 しかし、性能面での影響は大きく、特に暗号化カラムを条件としたselectは非実用的に思える。 検索条件にならないような機微情報カラムをピンポイントで暗号化するようなケースが使い所となる。 実装面でもアプリケーションのDBアクセス部分の改修が必要になるためDB全体および広い範囲を暗号化するような用途には向かない。 TDEforPG DBMS 機能として動作する透過的暗号化機能なので ストレージ盗難 OSへの不正ログイン への対策となる。 性能面はpgcryptoよりも全体的に良好で、暗号化カラムを検索条件にしなければならないときには有力なソリューションとなる。 商用製品のためコスト面で何らかの対応が必要になるが、利用料金も極端に高額なわけではない 2 ため、昨今のセキュリティ意識の高まりを考慮すると現実的な落とし所を見つけられそうです。 導入するとしたら ストレージ盗難対策なら ストレージ盗難のリスクに対応するためであれば LUKS はデメリットが少なく、導入ハードルは低いといえます。 マイナン バーなどの機微情報を保護するためなら pgcrypto によるカラム単位での暗号化で良いと思います。検索条件にしなければ現実的な対応だと思います。 もし暗号化カラムを検索対象にする必要があるのであれば、 TDEforPGの列単位暗号方式 の導入を検討する必要があります。 DB全体の暗号化をするなら ストレージ盗難対策までで良ければ先述の通り LUKS が良いと思います。 OSへの不正ログインまで対応するのであれば、 TDEforPGの行単位暗号方式 を用いた透過的暗号化の導入を検討することになります。 いかがでしたでしょうか。 思っていたよりもツールによって棲み分けができているように思えました。 技術選定の助けとなれば幸いです。 物理ストレージの盗難ももちろんですが、仮想ストレージファイルの盗難も含みます。 ↩ 具体的な金額はお問い合わせください。 ↩
アバター
はじめに 皆さんこんにちは、新卒1年目新米エンジニアのkananpaです。 今回は、ネットワークにおいて重要な概念であるサブネットについて、実際の業務で学ぶ機会があったため、まとめてみました。 私自身、名前は聞いたことがあったものの今回はじめて詳しく調べました。 初学者の方にも理解してもらいやすいようにまとめたため、最後まで読んでいただけるとありがたいです。 はじめに サブネットとは IPアドレスとは サブネットマスクとは ネットワークアドレスの計算方法 PHPによる実装方法 まとめ サブネットとは サブネットとは、あるネットワーク内の小さなネットワークのことを指し、 この小さなネットワークに分割することをサブネット化するといいます。 このサブネット化には以下のような役割があります。 IPアドレス の効率的な管理 例えば、 IPアドレス を割り当てる際にクラスアドレッシングによりクラスBで割り当てるとき、最大65536個のデ バイス に割り当てることができます。 しかし、そんなに多くのデ バイス がない場合、多くの IPアドレス が未使用となり無駄になります。 サブネット化を行うと、格納するデ バイス の台数に合ったネットワークを構築できるため、無駄になる IPアドレス を減らすことができます。 このことにより、 IPアドレス の枯渇問題の解消にもつながります。 ネットワークルーティングのパフォーマンス向上 サブネット化されていない状態で数百万台ものデ バイス が存在するネットワークの中で一つのデ バイス と通信したい場合、一つ一つに通信し該当のデ バイス を探します。 しかし、サブネット化し小さなネットワークに分割することで、最初から該当のデ バイス の存在する小さなネットワークに通信できるため、無駄な通信を格段に減らすことができます。 組織の要件に合わせた柔軟なネットワーク設計とセキュリテイの強化 「あるデータにおいてデ バイス Aにのみアクセスを許可したい」など同じネットワーク内のデ バイス でもアクセス制限を設けたいといった場合があります。 そのような場合に、 サブネットマスク でネットワークを分けることで上記のような設計が容易になります。 IPアドレス とは IPアドレス とは、インターネットに接続するデ バイス すべてに割り当てられた住所のようなもので、0~255の数字4組で表現されたものです。 この IPアドレス の表記はネットワーク部と ホスト部 に分かれており、このネットワーク部と ホスト部 を識別するための数値の事を サブネットマスク といいます。 サブネットマスク とは サブネットマスク とは前述の通り、ネットワーク部と ホスト部 を識別するための数値の事で、この数値によりサブネット化されたネットワークの範囲を指定することができます。 サブネットマスク の表記には、 IPアドレス 同様0~255の数字4組で表現されたものと、 IPアドレス の後に「/」をつけてビット数を指定するものがあります。 以下の図のように「123.45.67.89」という IPアドレス に「255.255.255.0」という サブネットマスク が指定されている場合を考えます。 この サブネットマスク を2進数に直すと「1」の数が24個になります。この左から3組までがネットワーク部となり、3組の数値が一致するものが同じネットワークとなります。 ネットワークアドレスの計算方法 サブネット化されたネットワークに通信する場合、ルータは IPアドレス から サブネットマスク 値を用いてネットワークアドレスを算出して、割り当てます。 それではこの算出方法を紹介します。 ① IPアドレス と サブネットマスク 値を2進数に変換する ② 論理積 (AND演算)を求める これより算出された値がネットワークアドレスとなります。 論理積 では 以下表のように2つの入力値が共に真(1)の場合にのみ真(1)を出力し、それ以外の場合には偽(0)を出力します。 入力値1 入力値2 出力値 偽(0) 偽(0) 偽(0) 偽(0) 真(1) 偽(0) 真(1) 偽(0) 偽(0) 真(1) 真(1) 真(1) IPアドレス からネットワークアドレスを算出することで、その IPアドレス がその サブネットマスク 内に含まれているかを判別することができます。 PHP による実装方法 以下は上記ネットワークアドレスの算出を PHP で実装したものです。 IPアドレス は通常4組の数字で書き込まれるため、まずlong形に変換します。 そして、コンピューターは IPアドレス を2進法のコード (1と0の連数) として読み取るため、ビット 演算子 ( 論理積 )を用いて計算していきます。 <? php $ ipAddress = "123.45.67.89" ; //指定IPアドレス $ subnetMask = 24 ;     //CIDR形式のサブネットマスク指定 // 指定されたIPアドレスをlong型に変換 $ ipAddressLong = ip2long ( $ ipAddress ) ; // サブネットマスクを数値に変換. $ subnetMaskNumeric = -1 << ( 32 - $ subnetMask ) ; //CIDR形式でない場合はそのままip2long("255.255.255.0")を使えばよい // リクエストIPをサブネットマスクでビット演算. $ networkAddress = $ ipAddressLong & $ subnetMaskNumeric ; まとめ 今回はサブネットについて基本的な概念から計算方法までを紹介させていただきました。 私のようにネットワークについて触れる機会は少ないという方でも、 IPアドレス やサブネットといった名前を耳にする機会が多々あると思うので、 基本的な仕組みを理解したいという方に参考になればうれしいです。
アバター
はじめに こんにちは、株式会社 ラク ス開発本部長の公手です。 普段はブログを書くことが少ないのですが、今回は当社のエンジニアやデザイナーたちが特に大切にしている顧客視点について共有したいと思い、投稿することにしました。 この投稿を通じて、社内のエンジニアやデザイナーに顧客視点の重要性を再確認してもらい、それぞれの役割の中で使い勝手の良い SaaS を開発するためにどのようなアクションを起こすべきかを考えてもらえるきっかけになればとの狙いもありますし、 ラク スの開発組織が顧客視点を最優先に考える組織であることを、社外のエンジニアの皆さんにも知っていただければ幸いと考えております。 はじめに ラクスがプロダクト開発において徹底してきたミッション 欠けていた顧客視点(持っていると勘違い) 顧客視点の獲得に向けて 顧客視点獲得の効果 組織の成長による顧客視点低下への対策 ミッションの言語化と浸透 圧倒的な使いやすさを求めて ラク スがプロダクト開発において徹底してきたミッション ラク スの開発本部が掲げるミッションは、 「顧客をカスタマーサクセスに導く圧倒的に使いやすい SaaS を創り提供する」 です。 実は2017年と結構最近までは 言語化 されておりませんでしたが、2000年代初期の SaaS 開発時から徹底してこのミッションを果たしてきたと思っています。 ラク スはベストオブブリード型のプロダクトを提供することを戦略としています。 当社の代表的なプロダクトである、楽楽精算や楽楽明細という名称を聞くと、楽楽シリーズという統合スイート型プロダクトをイメージされるかもしれませんが、実際にはプロダクト間連携機能などはあまりなく、例えば楽楽精算は経費精算分野で、楽楽明細は請求書発行分野で、それぞれがベストとして選ばれるプロダクトになることを目標として単独進化・開発が進められてきました。 他プロダクトの仕様や使い勝手で引っ張られることもありません。その領域の顧客の声を真摯に受け止め、プロダクトに反映していけば、その分野においては顧客のペインを確実に解決できる良いプロダクトを最短で開発できます。 まだベストオブブリードという言葉も広まっていなかったこともあり、明確にそういう戦略を意識して開発してきたわけではありません(というのが私の感覚)。各プロダクトにおいて、その分野に特化して、ビジネスチームと開発チームが一丸となって顧客ペインの解決にひたすら取り組んできた結果、顧客に高く支持されるプロダクトにまで成長し、幾つかのプロダクトはその領域で高いシェアを獲得することができました。 ※複数の高いシェアを持つプロダクトがあることも ラク スの大きな特徴の一つです。 のちのちベストオブブリードという言葉を知り、「あぁ、僕らがやっているのはこれなんだ」と思った記憶があります。 この成功の背景には、エンジニアやデザイナーたちが「顧客の成功を支えるためには使いやすさを徹底的に追求すべきだ」という考えを自身に深く根付かせ、それを実際に徹底して実行してきたことがあります。 欠けていた顧客視点(持っていると勘違い) プロダクト開発(だけでなくあらゆるサービス)においては、顧客視点(顧客解像度を高く持ち顧客と同じ視点に立てること)を持つことは今や当たり前に語られていると思います。ソフトウェア開発の現場でも、エンジニアやデザイナーが単に技術的なスキルを持っているだけでは不十分で、実際にユーザーや顧客が求める価値を提供できるよう、その視点やニーズを深く理解することが当たり前に求められています。 わかったつもりになっているという罠がありますが、当社で最初の SaaS プロダクトの「メールディーラー」の開発を2003年に当社の創業メンバーから引き継いだ私もその罠にはまっていました。営業資料の読み込みや役員からのプロダクトの説明を受けただけで顧客を理解したつもりになっていました。 当時のメールディーラーはまだ PMF (Product-Market Fit)にも至っておらず、私と二人三脚でメールディーラー事業を担当していた当時の事業責任者HIさんは PMF を目指して奮闘していました。そうしてようやくECショップの店長さん達に刺さり、その領域で PMF を達成しました。そこで上がってくる機能要求は私の想像していたものと違うものも多く、HIさんと意見の相違もありました。顧客の解像度のレベルが全く違うために私が開発するものがHIさんのイメージするものと微妙に違うということが結構ありました。 顧客視点の獲得に向けて 当時は小さな ベンチャー企業 だったこともあり、夜中遅くまで開発をしていた(笑)私に大変気を使いながら、私の顧客視点強化を目指し、いろいろな取り組みを提案(やってくれ依頼)してくれました。 営業同行する 実際に営業もしてみる 営業の商談議事録を読む システム障害時後に報告書を書いて顧客のところに謝罪に行く プロダクトに関するメールでの顧客対応をすべてやる などです。 「こんなことまでやらないといけないのか」と思いつつやっていましたが、やるうちに顧客解像度が相当上がり、HIさんや新しく入った営業責任者とかなり近いレベルまで顧客理解が進みました。 メールのサポートは特にしんどかったですね。。操作の流れをテキストで説明するのは難しく、うまく説明できずに何度もメールでやり取りをする、時にはお電話で説明させていただくということありました。 説明しなくても理解してもらえるUI/UXを作ろう、と心の底から思いました(笑) 顧客訪問すると、使っているブラウザ、モニタのサイズなんかも目で見てリアルにわかります。 顧客視点獲得の効果 その後は認識のずれも相当少なくなり、仕様の決定などもスムーズになりました。 また、マインド面でも大きな変化がありました。人は困っている人を助けたくなる性質があり、知っている人だとなおさらその思いが強くなります。顧客解像度が高くなればばなるほど親身に顧客のことを考えられるようになり、早く顧客を助けたいという思いが強くなりました。新機能開発、バグの修正、トラブル時の対応速度もかなり早くなりました。顧客解像度の高いエンジニアとそうでないエンジニアでは行動が変わってきます。例えば、一つのバグが引き起こす事象によって、解像度が高ければ、単にその操作ができないということだけでなく、その操作ができないことにより、そのユーザーがどのように困るのかを想像できます。それが想像できるかできないかで、バグ修正の速度感や判断が変わってきます。これはインフラで障害が発生した場合などの対応でも同様のことが言えます。 その後しばらくはメールディーラーの開発チームでは営業同行やメールサポートを必ず経験させるようにしてきました。 組織の成長による顧客視点低下への対策 組織が大きくなるにつれ役割の分担が進み、カスタマーサクセス(CS)部門、営業部門、 マーケティング 部門、開発部門がそれぞれ専門組織化しました。開発部門は効率化を求めて開発に注力すべきとなってきました。営業やCSから席も遠くなるなどして顧客の声を感じられる機会も減り、エンジニアやデザイナーの顧客解像度は自然と低くなってきやすくなります。 とはいえ、エンジニアやデザイナーが300人近くなった現在でも、それぞれが高い顧客視点を保持できていると思っています。 どういう工夫があるのかというと、実は特段工夫はありません。当たり前に常日頃から顧客理解の大切さを訴え続けるのみです。 顧客視点を高く保つことの大切さは常々エンジニアマネージャーからメンバーに伝えられ、顧客ファーストで判断をしているのかメンバーへの問いかけが日々続けられています。 それ以外でも、(メールでの顧客対応はさすがにやらなくなりましたが)各プロダクト開発チームの判断で、CSのメール対応を読んだり、入社後の営業同行などを継続してくれていたりもします。 プロダクト開発の各工程にはCSや営業部門には積極的に入ってもらい、意見をどんどん吸い上げています。 ミッションの 言語化 と浸透 カルチャーとして顧客視点を最重要視し開発することは普通に行われていたのですが、 より浸透させて根付かせるため、また採用の際にも説明しやすいようにと2017年に冒頭で紹介したミッションとして 言語化 しました。 「顧客をカスタマーサクセスに導く圧倒的に使いやすい SaaS を創り提供する」 現在ではマネジメント層だけでなく、インナー ブランディング 担当チームがあり、ミッションの浸透や顧客視点強化を訴え続けてくれています。 半期に一度このミッションの浸透度や理解度、顧客理解の重要性などを部門内アンケートで確認しています。 圧倒的な使いやすさを求めて 開発組織にはバックエンドエンジニア、フロントエンドエンジニア、UI/UXデザイナー、プロダクトマネージャー、AIエンジニア、アーキテクトなど様々なエンジニア・デザイナーがおります。それぞれが自身の専門性を活かしながら「何がお客様にとって最高に使いやすいものなのか?」を追求して開発をしてくれています。高い顧客視点をもって我々のミッションを遂行していけば、今後も高く顧客に支持されるプロダクトを生み出していけるはずです。 社内のエンジニア・デザイナーの皆さんがこの投稿を読むことで、改めて顧客解像度を高く持つことの重要性を理解し、さらなる顧客視点の強化に努めていただけると幸いです。また、この投稿が(あまりたいしたことは書いていないですが)社外のエンジニア・デザイナーの皆さんの参考になり、よいプロダクトが世に生み出されることの一助となって人々のペインが解決されていけば幸甚でございます。 最後にですが、もし我々のミッションに共感されて興味を持たれた方がございましたら、ぜひカジュアル面談にお越しください。 私も含め当社のエンジニアマネージャーにていろいろとお話しできればと思っています。
アバター
こんにちは、メールディーラー開発課のUKoniです。 2023年9月のことですが、弊社で開催した 【ラクスMeetUp】持続的改善の実践/UI刷新・SQL改善・EOL対応 で登壇させていただきました。 そこで話した、長寿サービスの密結合システムからViewを分離した話をご紹介します。 発表資料 speakerdeck.com 発表資料 概要 作業内容 1. 旧画面のコードから機能一覧を作成する 2. IDEの機能を使用して、共通利用するロジックをメソッドに切り出す 3. 切り出したメソッドのユニットテストを作成する 4. ビューロジックとビジネスロジックを分割する 手順 ビューロジック JavaScriptコード HTMLコード(bladeファイル) ビジネスロジック Actionクラス Responderクラス その結果・・・ UIを新しくすることができました。 Before After 今回の結果 苦労した話 今後の展望 まとめ 概要 メールディーラーでは、最近のメールプロダクトのトレンドやお客様からのご要望などを検討した結果、UIを刷新することになりました。 詳細な背景は下記記事を、刷新内容はスライドをご参照ください。 市場トレンドと顧客ニーズを密に連携~老舗メールプロダクトのUI刷新プロセス~ | エンジニアストーリー技術・デザイン情報 | 株式会社ラクス キャリア採用 しかし、以下の理由から現状では無理な状態でした。 多機能かつ複雑なロジックのため、手動テストで品質が担保できない 自動テストがない 作成するにしてもビューロジックと ビジネスロジック が密結合の状態のため、細かい仕様の確認ができない 自動テストの作成が難しいのはビューロジックと ビジネスロジック が混在していることが原因です。 ならば、ビューロジックと ビジネスロジック を分離してしよう、ということになりました。 作業内容 こちらの説明の際に、以下のサンプルコードを元に説明します。 <?php // HTMLのコード start $ isEdit = $ _REQUEST [ 'mode' ] === 'edit' ; if ( $ isEdit ) { if ( $ _REQUEST [ 'input' ] === 'radio' ) { print "<input type='radio' id='male' name='gender' value='0'>男" ; print "<input type='radio' id='female' name='gender' value='1'>女" ; } else { print "<input type='text' id='input' name='text' value=''>" ; } } // HTMLのコード end // JavaScriptのコード start if ( $ isEdit ) { $ js = "alert('edit');" ; } else { $ js = "alert('view');" ; } print "<script> { $ js } </script>" ; // JavaScriptのコード end // PHPのコード start if ( $ isEdit ) { $ sum = 0 ; foreach ( $ _REQUEST [ 'count' ] as $ count ) { $ sum += $ count ; } } // PHPのコード end // HTMLのコード start print "<p> { $ count } </p>" ; // HTMLのコード end 1. 旧画面のコードから機能一覧を作成する 機能落ちや想定外の不具合を防ぐために、どういう機能があるかを一覧にしました。 サンプルコードの機能を一覧にすると、以下のようになります。 2. IDE の機能を使用して、共通利用するロジックをメソッドに切り出す 表題の通りです。 新UIでも使用するロジックを共 通化 します。 3. 切り出したメソッドの ユニットテスト を作成する こちらも表題の通りです。 ここでようやく、旧画面のロジックを「テスト可能な状態」にすることができました。 4. ビューロジックと ビジネスロジック を分割する Laravel/Vue.jsを導入して、テンプレートエンジンに渡すようにします。 手順 旧画面のコードをそのままLaravelへ移植 HTML作成のコードを コメントアウト 機能一覧に従って、新UIを実装 ③で作成した機能のActionクラスの自動テストを作成・実施 想定通りレスポンスパラメータが送信されているかを確認 不要なコードを削除( コメントアウト したコードなど) 機能一覧を使って、受入テストを実施(最終確認) 詳しい内容は、同じ課のメンバーが発表していますので、こちらをご参照ください。 fortee.jp 上記の作業をした結果、コードは以下のようになりました。 ビューロジック JavaScript コード const vm = { mounted () { if ( params . isEdit ) { alert ( 'edit' ) } else { alert ( 'view' ) } } } export default vm ; HTMLコード(bladeファイル) <?php @ if ( $ isEdit === 'edit' ) @ if ( $ input === 'radio' ) < input type = 'radio' id = 'male' name = 'gender' value = '0' > 男 < input type = 'radio' id = 'female' name = 'gender' value = '1' > 女 @ else < input type = 'text' id = 'input' name = 'text' value = '' > @ endif @ endif < script src = "/js/page_a.js" ></ script > < p > {{ $ count }} </ p > ビジネスロジック Actionクラス <?php class PageAAction { protected $ Domain ; protected $ Responder ; public function __construct ( Domain $ Domain , Responder $ Responder ) { $ this -> Domain = $ Domain ; $ this -> Responder = $ Responder ; } public function __invoke ( Request $ request ) : Response { return $ this -> Responder -> response ( $ request , $ this -> Domain -> get ( $ request )) ; } } Responderクラス <?php class PageAResponder { protected $ response ; protected $ view ; public function __construct ( Response $ response , ViewFactory $ view ) { $ this -> response = $ response ; $ this -> view = $ view ; } public function response ( Request $ request , $ data ) : Response { $ isEdit = $ request -> get ( 'mode' ) === 'edit' ; if ( $ isEdit ) { $ sum = 0 ; foreach ( $ request -> get ( 'count' ) as $ count ) { $ sum += $ count ; } } $ this -> response -> setContent ( $ this -> view -> make ( 'page_a' , [ 'isEdit' => ( $ isEdit ) ? 'edit' : 'view' , 'input' => $ request -> get ( 'input' ) , 'count' => $ sum , ]) ) ; return $ this -> response; } } その結果・・・ UIを新しくすることができました。 Before After 今回の結果 自動テストを作成したので、今後の追加実装や改修がしやすくなった クリティカルな不具合が少なかった 新UIへの移行の手法が確立できたので、他の機能でも活用できる 苦労した話 3000行超えの複雑なコードから機能一覧を作るのがしんどかった 旧画面のロジックを移植したことで変数のスコープが変化し、それが原因でバグが発生した 今後の展望 今回の刷新で対象外だった画面を新UIに改修 今回改修したロジックの リファクタリング ADR 実装に沿った実装にする デグレ 防止のために旧画面のロジックをそのまま移植しているため、適切な箇所に実装する まとめ 長寿サービスの密結合システムからViewを分離した話をご紹介しました。 同じような悩みを持っている方のお役に立てたら幸いです。
アバター
はじめに こんにちは。 ラク スの経費精算プロダクト「楽楽精算」のプロダクトマネージャー(PdM)組織で責任者をしております稲垣です。 楽楽精算では プロダクトマネジメント に関する専門組織を設けており、市場や顧客ニーズを迅速に製品に反映できるように努めています。 ※具体的な業務内容はPdMメンバーの記事もご参照ください tech-blog.rakus.co.jp PdMはビジネスと開発の架け橋となってプロダクトの価値を最大化するという役割上、必要とされるスキル範囲も広くなります。 下記に紹介する書籍のような業務やスキルが日々関わってきますので、ご参考になれば幸いです。 はじめに 実務に役立つプロダクトマネジメントおすすめ書籍10選 PdM全般 プロダクトマネジメントのすべて 事業戦略・IT開発・UXデザイン・マーケティングからチーム・組織運営まで ジョブ理論 イノベーションを予測可能にする消費のメカニズム INSPIRED 熱狂させる製品を生み出すプロダクトマネジメント All For SaaS SaaS立ち上げのすべて プロダクトマネージャーのしごと 第2版 ―1日目から使える実践ガイド 思考力 イシューからはじめよ――知的生産の「シンプルな本質」 解像度を上げる――曖昧な思考を明晰にする「深さ・広さ・構造・時間」の4視点と行動法 言語化・コミュニケーション力 「言葉にできる」は武器になる。 マーケティング マーケティング思考 業績を伸ばし続けるチームが本当にやっていること ユーザーの「心の声」を聴く技術 ~ユーザー調査に潜む50の落とし穴とその対策 おわりに 実務に役立つ プロダクトマネジメント おすすめ書籍10選 以下に書籍をご紹介していきます。カテゴリは以下の通りです。 PdM全般 思考力 言語化 ・コミュニケーション力 マーケティング PdM全般 プロダクトマネジメント のすべて 事業戦略・IT開発・UXデザイン・ マーケティング からチーム・組織運営まで 及川 卓也 (著), 曽根原 春樹 (著), 小城 久美子 (著) まさに プロダクトマネジメント の教科書的な本といえます。 ジョブ理論 イノベーション を予測可能にする消費のメ カニ ズム クレイトン M クリステンセン (著), タディ ホール (著), カレン ディロン (著), デイビッド S ダンカン (著), 依田 光江 (翻訳) 人がモノを買う行為の本質を理解することができます。 INSPIRED 熱狂させる製品を生み出す プロダクトマネジメント マーティ・ケーガン (著), 佐藤 真治 (監修), 関 満徳 (監修), . (その他), 神月 謙一 (翻訳) プロダクトマネジャーとは何かがわかり、実例も参考になる本です。 All For SaaS SaaS 立ち上げのすべて 宮田 善孝 (著) 事前/深掘り調査とプロトタイプ、開発、GoToMarket戦略、リリースに分けて、検討内容や注意点が丁寧に説明されています。 プロダクトマネージャーのしごと 第2版 ―1日目から使える実践ガイド Matt LeMay (著), 永瀬 美穂 (翻訳), 吉羽 龍太郎 (翻訳), 原田 騎郎 (翻訳), 高橋 一貴 (翻訳) PdMのコアスキルである、コミュニケーション、 組織力 、リサーチ、実行の4つのスキルについての習得法を学ぶことができます。 思考力 イシューからはじめよ――知的生産の「シンプルな本質」 安宅和人 (著) 問題解決、 ロジカルシンキング を使い課題の本質をつかむことを学ぶことができます。 解像度を上げる――曖昧な思考を明晰にする「深さ・広さ・構造・時間」の4視点と行動法 馬田隆明 (著) 物事の本質をつかみ、思考を深めるための方法を学ぶことができます。 言語化 ・コミュニケーション力 「言葉にできる」は武器になる。 梅田 悟司 (著) PdMに必要な 言語化 の重要性を学ぶことができます。 マーケティング マーケティング 思考 業績を伸ばし続けるチームが本当にやっていること 山口 義宏 (著) そもそも マーケティング とは何か、基礎から学びたい方におすすめです。 ユーザーの「心の声」を聴く技術 ~ユーザー調査に潜む50の落とし穴とその対策 奥泉 直子 (著) 具体的なインタビューの方法や、内容について紹介した書籍です。 おわりに 以上、当社のPdM組織で実際に推薦し、役立てている書籍をご紹介しました。 ラク スのPdMはビジネスサイド(営業、製品企画、カスタマーサクセス)、開発、デザインを巻き込んで顧客価値の最大化に努めます。そのため、思考力、 言語化 、コミュニケーションといったソフトスキルの書籍も取り上げました。 実務上は顧客調査に関連した SWOT 、3C分析、STP分析等のほか、顧客解像度を高めるためのインタビューなど直接顧客接点を持つことも多く、開発以外にもビジネスや顧客に関する背景知識は重要となります。 これらの知見をどのように ラク スの業務に活かしているのかは、私たちの登壇での トーク 内容もご覧ください。 logmi.jp 最後に、PdMの仕事にご興味をお持ちいただけましたら、カジュアル面談等も実施しておりますのでお気軽にお申込みください。 ラク スでのPdMの役割等もお話させていただきます。 career-recruit.rakus.co.jp career-recruit.rakus.co.jp
アバター
はじめに こんにちは。フロントエンド開発課に所属している新卒1年目のm_you_sanと申します。 3月6日にTypeScript5.4がリリースされました。 そこで、今回は個人的に気になった機能についてピックアップして紹介したいと思います。 はじめに 型の絞り込み NoInfer まとめ 型の絞り込み 関数 クロージャ 内の型の絞り込みの挙動が少し変わりました。 以下の getUrls では、まず始めにtypeof 演算子 を使用して、第一引数の url がstring型かどうかを確認しています。 string型であった場合、 URL の インスタンス を生成します。 その後、 map 関数内で URL のプロパティである searchParams を使用しています。 function getUrls ( url: string | URL , names: string [] ) { if (typeof url === "string" ) { url = new URL ( url ); } return names.map ( name => { url.searchParams. set( "name" , name ); return url.toString (); } ); } このとき従来の挙動の場合、 map 関数の クロージャ 内では、 if (typeof url === "string")... 内で絞り込んだ型情報が保持されず、 url がstringとURLのユニオン型になるため、 searchParams の部分でエラーが発生します。 そのため、 map 関数 クロージャ 内で再度、型情報を絞り込む必要がありました。 return names.map ( name => { // もう一度型情報を絞り込む if (typeof url === "string" ) return; url.searchParams. set( "name" , name ); return url.toString (); } ); しかし、5.4から クロージャ 内で型情報が保持されるようになったため、上記のようにわざわざもう一度型情報を絞り込む必要がなくなりました。 ただし、以下のようにネストされた関数内で、型情報を絞り込んだ変数が代入された場合は、型情報が保持されなくなります。 function printValueLater ( value: string | undefined ) { if ( value === undefined ) { value = "missing!" ; } setTimeout (() => { value = value ; } , 500 ); setTimeout (() => { // エラーが発生する console .log ( value.toUpperCase ()); } , 1000 ); } また、functionを使って関数宣言をした場合、ホイスティングができるため、型情報を保持することができません。 以下のコードの場合、 toUpperCaseFn は if (value === undefined)... より前に呼び出すことが可能なので、 value をstringに絞り込むことができません。 アロー関数の場合は、ホイスティングができないので、正常に型情報を絞り込むことが可能です。 function printValueLater ( value: string | undefined ) { // ここで呼び出すことが可能 // toUpperCaseFn(); if ( value === undefined ) { value = "missing!" ; } function toUpperCaseFn () { // エラーが発生する console .log ( value.toUpperCase ()); } // エラーは発生しない const toUpperCaseFn2 = () => console .log ( value.toUpperCase ()); } NoInfer NoInferは新しく追加されたユーティリティ型で、不要な 型推論 をブロックすることができます。 以下のコードでは、 colors の配列要素の一部が defaultColor の型になるようにしています。 しかし、配列の要素にないものを defaultColor に設定しても、 型推論 が効いてしまうため、型エラーが発生しません。 この場合、 defaultColor から ジェネリクス C を推論させないようにする必要があります。 function createStreetLight < C extends string >( colors: C [] , defaultColor?: C ) { // ... } createStreetLight ( [ "red" , "yellow" , "green" ] , "blue" ); createStreetLightの型情報 そこで、NoInferを使用すると 型推論 がブロックされ、 defaultColor が colors の配列要素の一部であることをチェックできるようになります。 function createStreetLight < C extends string >( colors: C [] , defaultColor?: NoInfer < C >) { // ... } // blueは配列の要素に存在しないため、エラーが発生 createStreetLight ( [ "red" , "yellow" , "green" ] , "blue" ); createStreetLightの型情報 まとめ 今回は、TypeScript5.4の新機能から個人的に気になったものを紹介しました。 クロージャ 内の型情報の保持は、今後開発を進める中で役立つ場面が出てくるのではないかと思いました。 5.4の機能について、更に詳しく知りたい方は、リリースノートをご覧ください。 参考資料 devblogs.microsoft.com
アバター
こんにちは。 株式会社 ラク スで先行技術検証をしたり、ビジネス部門向けに技術情報を提供する取り組みを行っている「技術推進課」という部署に所属している鈴木( @moomooya )です。 今回は毎年春先の社内ビアバッシュで新人向けに「一歩目の学習方法」として発表している話をしようと思います。 学習とは この記事の対象 学習に対する向き合い方 まず最初は 学習作戦その1「ちょい足し学習」 例)HTTPメソッドを扱ったとき 学習作戦その2「外から情報を仕入れる」 よくある情報源 技術書 技術同人誌 ウェブサイト 勉強会 SNS 飲み会 GitHub 脱初級者 手を動かす(検証と実践) 自由にできるサーバーを用意する 情報発信 無理に全部やらなくていい エンジニアにとっての勉強 学習とは [名](スル) 1 学問・技術などをまなびならうこと。「学習の手引」「学習会」 2 学校で系統的・計画的にまなぶこと。「英語を学習する」 3 人間も含めて動物が、生後に経験を通じて知識や環境に適応する態度・行動などを身につけていくこと。不安や嫌悪など好ましくないものの体得も含まれる。 4 人工知能 (AI)がデータを読み込み、基準や規則性を見つけ出すこと→ 機械学習   出典: デジタル大辞泉 ( 小学館 ) 誤解を恐れず、噛み砕いて言うとすれば「昨日よりもなにか一つうまくやれるようになること」です。 (4の語義は今っぽいですね) この記事の対象 若手エンジニアに向けたスライドが元ネタになっています。 想定としては今後のITエンジニアとして成長していくための長期的な観点で書いています。短期的な成果 1 につながるような考え方ではありません、というか短期的な成果は現場の仕事を覚えるという一点に収束するので特に触れません。 あと「脱初級」の定義って誰が定めてるんだよ、と思われるかもしれませんが、ターゲットとしている若手エンジニアに関しては「そんな定義気にせず知識と経験をため込む」段階なので定義とか気にしてる段階じゃないです。たぶん本記事の内容が「そんなの日常生活じゃね?」ってなってたら世間的に見ても技術的には多分もう初級じゃないです 2 。 また、経験ある人たちは学習習慣ついていると思うので勝手に高みを目指してください。 学習に対する向き合い方 おそらく学習についての記事を書くと「そんな知識いまさら使うのか?」「もっとこういう部分から学んだほうがいい」「効率悪そう」「タイムパフォーマンスがどうこう……」とかそういう話が出てくると思いますが 3 、長期的に考えたときに最初から 使いそうな知識だけ身につけることは不可能 です。無駄知識を持っていない凄腕エンジニア 4 なんて会ったことありません。 経験を積むことで、今後のキャリアのために必要な内容を取捨選択する、ということはあると思います。しかし最初は気にせず知識を取り込んでいくでいいと思います。いつどんな経験が役に立つかわかりませんしね 5 。 私も仕事としてITエンジニアをし始めたときは「仕事に使う知識を身につける」というスタンスでしたが、全然学習が捗りませんでした。しばらくしてから「仕事とか気にせずに気になったものや興味を持ったものは全部調べる」というスタンスに切り替えてからは無駄な知識も多いですが、学習が捗るようになりました。 まず最初は とはいえ若手エンジニアの皆様に置かれましては目の前の業務を遂行できるように頑張ってもらうのが最優先かな、と思っています。 一方で目の前の業務しかやらないと出来ることが増えないです。IT業界は規格化された部分は多くありますが、ローカルルール 6 などもたくさんあります。 なので、長期的なキャリアのためには業務だけではなく、職場以外を情報源とした学習も並行して行うことをおすすめします。 学習作戦その1「ちょい足し学習」 とはいえ「業務で忙しくて他のことができない」というのは容易に予想できる状況です 7 。 そんな方は業務でやっていることに ちょい足し する「ちょい足し学習」から始めてみましょう。 日常の業務ではわからないことが多く、調査に時間を使うことも多いと思います。また手順書が整備されていて、指示通りに手を動かすだけの整備された環境でも手順書の指示内容が「なぜ正しいのか」理解しきれていないこともあると思います。そんなときに日々の業務に必要な知識を裏付ける知識をちょい足しするのです。 例)HTTPメソッドを扱ったとき GET/POST、次いでPUT/DELETEあたりは触る機会が多いと思いますが、HTTPメソッドはこれだけではありません。 使わなかったけど仕様として定義されている他のメソッドを調べてみる リク エス トやレスポンスのデータ構成を調べてみる ヘッダに設定可能なパラメータを調べてみる テキスト以外のデータの扱いを調べてみる そもそも誰が仕様を定義しているのか など派生的に学べる内容はたくさんあります。 ちょい足し学習した後は必ず何かしらにアウトプットするようにしましょう。ブログでもなんでもいいです。検索して見つかるような場所に記録しておくと良いと思います。1年後には学習内容をすっかり忘れて調べ直しているはずです。そのときに自分が調べた内容の記事が見つかれば、すぐに知識を思い出すことが出来るでしょう。 学習作戦その2「外から情報を 仕入 れる」 興味の幅を広くして、いろいろな情報源に自然と触れる環境を作っておくと 頑張らなくて良くなる ので継続しやすいです。 基本的に触れる情報は多ければ多いほどよいです。ノイズが増えることを恐れるよりは、多い情報から必要なものを取捨選択する能力を鍛えることを考えたほうがいいです。 よくある情報源 技術書 体系的にまとまっているため新しい領域の知識を身につけるのにおすすめ。昔から読み続けられている、いわゆる古典と言われている技術書も普遍的な内容が多いため読んでおくと良いと思います。 古典技術書の名著については過去に記事を書いています。 tech-blog.rakus.co.jp お金に余裕があるなら O'reilly Online Learning (年額$499 8 )の利用も強くおすすめします。 情報学の学位持っていない方は 情報科学 の本を1冊くらい通読しておくと良いと思います。例えばこういうのとか。 https://amzn.asia/d/7Wbvfc3 amzn.asia ボリューム的に結構大変だと思うのでゆっくり読めばいいと思う。最初から読んでもいいけど、 スキルアップ が行き詰まった感を覚えたときに立ち戻ったりするのが良いかも。点と点を繋いでくれたり、応用力がついたり、概念を誤って理解することを減らしたりする助けになるかもしれないです。 技術同人誌 年々賑わってきている印象があります。 コミケ などでも扱われますが、技術書 オンリーイベント としては年2回行われている 技術書典 が国内最大のイベントです。 techbookfest.org 技術同人誌は商業誌で扱われないようなピンポイントなテーマでまとめられた本が期待できます 欲しいテーマで書かれたものが存在するかどうかは運次第なところもあるので、オンラインで指名買いするよりはオフラインでイベント会場を散策して気になる本を買うほうが楽しいと思います。 また情報のアウトプットの場としても有用です。だいたいしっかりした技術ブログ記事数本分の文量 9 があれば薄めの本が1冊作れるかと思います。 執筆時は Re:VIEW という 組版 システムが使いやすいです。 github.com ウェブサイト 基本的に一次情報である公式サイトを参考にするようにしましょう。公式サイトはほとんどが英語サイトとなりますが、なんとか読みましょう。 Google翻訳 を使ってもいいと思います。 他には企業ブログや個人ブログ、各種ニュースサイトなど、気になるものはチェックするようにしましょう。 RSSリーダー を活用してもいいし、ブックマークから直接訪問しても良いと思います。 はてなブックマーク のテク ノロ ジー カテゴリをウォッチするのも良いと思います 10 。 b.hatena.ne.jp 勉強会 コロナ禍を通して状況が色々変わってきていると思いますが、オフラインでの勉強会も有用です。オンラインでも参加しないよりはしたほうがいいのですが、オフラインでの強みは発表コンテンツの後の懇親会にあります。 似たような技術コンテンツに集まっている人たちは似たような課題にぶつかっている人も多く、いろいろな試行錯誤の情報を共有することができます。発表スライドには乗せにくい情報なども懇親会では議論できたりするので、個人的には可能ならオフラインでの勉強会への参加をおすすめしたいです。 コツとしては コンプライアンス 違反にならない範囲で自分が抱えている課題や、過去の泥臭い解決策なんかを開示し つつみん なで共有していく感じです。外向きのキラキラした話とは別に泥臭い実態の話が聞ければ値千金です 11 。 正直勉強会への参加は 地域格差 が大きいとは思いますが、現実として東京(やその他大きな都市)のエンジニアにとっては大きなアドバンテージなので活用してみてほしいです。 イベント探しは今や Connpass 一強だと思うので、Connpassで探しておけばいいと思います。 参加するテーマとしてはこれから取り組むような領域の勉強会に参加してコミュニティの雰囲気とか知るために行くのがおすすめです。 connpass.com SNS これもこの数年でだいぶ状況が変わってきました。 以前は「 Twitter アカウントはエンジニアの名刺 だから必ずアカウントを作りましょう」と言っていましたが正直現状の Twitter (現X)だとそこまで言い切れない……。 Blueskyが招待制を脱したり、Threadsも Facebook や Instagram からの導線を強化してユーザーが増えているようですが、 Twitter (現X)を置き換える程にはなっていないです。Misskeyはエンジニア向けって感じでもないですし、エンジニアがどこに腰を落ち付けることになるかは先数年様子を見ることになりそうです。個人的にはBlueskyあたりに移住するのがいいとは思っていますが……(所有 ドメイン で身分証明とか出来るし)。 現状ではまだ Twitter (現X)で発信しているエンジニアがまだ多いので、一旦は Twitter (現X)アカウントを作って気になるエンジニアを片っ端からフォローしましょう、という感じです。自動的に情報が流れ込んでくる状態を作れるので情報収集が楽になります。 他にも Youtube ではカンファレンスや勉強会の アーカイブ が公開されていたりするので、気になるチャンネルを登録しておくのも良いと思います。 もっと小規模なところで行くとVRChat内でもITエンジニア向けのコミュニティがあったりするので、自分がよく使っている SNS でエンジニアまたはコミュニティをフォローして、日常的に発信された情報が目に入る状態にしておくのが良いと思います。 飲み会 エンジニア同士が飲みに行けば、技術の話に花が咲くってものです。 普段一緒に働いているチームでもいいですが、普段仕事で接していない人と話すと新鮮な話が聞けて楽しいと思います。 大学時代の友人だったり、配属が違う同期であったり、前職の同僚であったり、趣味で知り合った人であったり、探すといろいろいるものです。 飲み会と書いてはいますが、酒を飲まなくても食事だけでも会話をする場が作れればいいと思います。 ただし、チーム外の人と話す際には機密事項には注意して話すようにしましょう。 社名とか製品名とか固有名詞をそのまま発言しないのは大人の嗜みです 12 。 GitHub GitHub も SNS だと言われそうですが、やはりこれは別枠で。 大量の ソースコード が読み放題です。新しめの フレームワーク とかで「 ディレクト リ構造どうしたらいい?」「この処理の実装セオリーはどんなコード?」と悩むことはないでしょうか。そんなときにも オープンソース は役立ちます。同じ フレームワーク で書かれた ソースコード を探して、いくつか読んでセオリーを見いだせます。 当然、利用している OSS ライブラリの挙動調査もできますし、気になる所があればプルリク投げて貢献してもいいです 13 。 ソースコード の修正だけではなく、ドキュメントの翻訳や修正も感謝されると思います。 脱初級者 手を動かす(検証と実践) やはり動くものを作ってみるのがもっとも学びになります。 色々聞いてわかった気になっても実際に実装していくとわからない部分や、うまくいかない部分が見えてきます。 そういった部分を一つずつ解消していくことが確実に力になっていくと思います 14 。 自由にできるサーバーを用意する 私はWebアプリケーションエンジニアなので、学習の際にはアプリケーションを動かすためのサーバーが欲しくなります。 昔はサーバーを1台用意するのは結構お金がかかったので大変だったのですが、今 15 だと3万円くらいから購入できる低価格ミニPCがあるのでこれをサーバーにするのが簡単な方法かと思います。3万円台で Intel N100(第7世代 Core i5 、第9世代 Core i3 くらいの性能 16 )だったり、2.5GbEポートが複数ついてたりするのでネットワークの勉強にも使えそうです。 物理マシンを用意する方法は色々融通が効くので私は好んでいます。 もう少し頑張ってみるとすると、Proxmox VEなどを使って仮想ホストサーバー(≒ プライベートクラウド )を構築してもいいかもしれません。大量に インスタンス を建てなければそれほどのスペックも必要ないはずです。上述の低価格ミニPCでも出来るし、2~3台購入して クラスタリング してもいいし。 www.proxmox.com もうちょっと安く……という方には VPS の利用が良いと思います。(上述の低価格ミニPCよりも)低スペックなサーバーになりますが、月額$4とか600円とかで国内に配置されているサーバーを利用することができます。 海外業者だとDigital Oceanとか Vultr とか、国内業者だと さくらインターネット とかConoHa VPS が有名かと思います。 ちなみに VPS の利用はウェブサイトを公開する場合にも便利です。実害が出るかどうかは別として、ウェブサイトを公開するともれなくクラック対象として攻撃にさらされます。万が一侵入されたときにも 自宅サーバ ーではなく、 VPS (や後述の パブリッククラウド など)であれば被害を限定することができるので多少安心できます。 もっと安く、できれば無料で……という方には大手 パブリッククラウド の無料枠を用いるとか、フロントエンドだけを試すなら静的 ホスティング サイト(NetlifyとかVercelとか)が無料枠内でも結構使えます。 個人的には最初から パブリッククラウド を使うのは、ハマりどころが多い印象があるので学習のハードルが上がると感じています。とはいえ今は利用者も多くなり、出回っている情報も増えました。直接仕事につながりやすいスキルにもなるので選択肢に含むのはアリでしょう。 www.digitalocean.com vps.sakura.ad.jp www.conoha.jp www.netlify.com vercel.com 情報発信 学習したら発信することで更に定着させましょう。 「手を動かす」でも書きましたが、学んだ内容を文章に書き起こしてみると、自分の理解が不足している部分がわかってきます。 不足を埋めるように調べて、調べた内容が正しいか試してみて、文章を補足していくということを繰り返すと確かな知識が身についていきます。 発信する場はどこでもいいと思います。多くの人の目に触れる場所の方が承認欲求が満たされてモチベーションを維持しやすいというのはありますが、逆にプレッシャーに感じてしまう人はパブリックな場所であればどこでもいいと思います。 例えば私の身の回りだと…… テックブログに記事を書く 社内ビアバッシュでLTする 個人ブログとかQiita、Zenn的なところに記事を書く 社外勉強会で発表(LT含む)する みたいな方法が身近な方法です。 ちなみに発信を続けていくと「社外勉強会で登壇依頼される」みたいなパターンもあるかもしれません、ありがたい話ですね。 記事を書くのか、人前で発表するのかは個人の好みもあると思うので好きな方法を取ればよいと思います。 ただ社外勉強会に参加するときはLTでもいいので登壇すると、懇親会での話のタネができて コミュ力 が低くても交流しやすいのでおすすめです。 ちなみにLTについては手前味噌ですがこちらの記事もどうぞ。 qiita.com 無理に全部やらなくていい 本記事に書いた内容を「すべてやらなきゃ」となると多分続かないです、大変すぎる……。 ただ、興味を覚えたものについてはどれかやっている状態を続けるといいと思います。「ここ最近は技術書読みがマイブーム」とか「無償にコードを書きたい/読みたい」とか「勉強会めぐりしたい気分」とか。「ITポエム書きたい」でもいいかもしれない 17 。 そんな生活を数年続けていたらいつの間にか初級者とは見られなくなっていくと思います。『一万時間の法則』の一万時間にはあまり意味がないらしいけど、日常生活に組み込まれるレベルで努力を続けた人は一流になっていくというのはあるかと思います 18 。 エンジニアにとっての勉強 社内のエンジニアと話していてかっこいいセリフがあったのでこれを引用して締めとします。 「勉強とは意識してない。ただの日常生活の一環です」 とはいえ短期的に業界内の認知度向上というか個人 ブランディング みたいな方法はあるにはあります。たとえば特定の フレームワーク などの技術要素に徹底的に特化して学習することで、特定領域の第一人者になる方法があります。ただこの業界は特定の技術はいつか必ず廃れるので、長期的に食べていくためにはあまり特化しすぎるのも考えものかな、と思っています。もっとも第一人者になると人脈なども自然と形成されるので、そこから知識を横に広げていくというは全然ありでしょうけれどね。 ↩ ビジネスという意味ではまた別ですが。 ↩ どうせ はてブ コメントとかにもそういう言う事書かれると思う。 ↩ むしろ膨大な「無駄知識」が凄腕エンジニアを凄腕たらしめているとすら思えます。 ↩ 電子工作(記憶装置を取り付けないと電源オフで状態が破棄されるのが当たり前の世界)と、マイクロサービスやコンテナ化の文脈で頻出するステートレスの感覚が似ていたりとか。 ↩ 自社でしか通じない言葉や、過去のしがらみなどに基づく固有のやり方、予算や製品特性などの制限上セオリーから外れた最適解など。こういうのは無くせるに越したことはないけれど「仕事(≒ビジネス)が出来るエンジニア」になるためには適切に接していく必要があると思います。 ↩ そうでないなら恵まれた環境にあると言えます。十分な学習時間を確保しましょう。 ↩ 個人利用であればO'reilly Online Learningの年会費は ACM 会員になることで合計$174( ACM 会員費$99 + 優待金額$75)で利用することもできます。 ↩ 本記事くらいの文量で3記事分くらいあれば40ページ前後の薄い本が出来ると思います。 ↩ 私は はてな アカウント自体は はてなダイアリー 提供当初の頃に作って使っていましたが、 はてなブックマーク は過去のBANまつりの時期に利用し始めたせいで、初めてブックマークしたタイミングでBANされてしまって以来使えません。 個人的には はてなブックマーク (の運営)は好きではないです。 ↩ 世の中で流行ってるとかモダンとか言われている技術について話していて、表向きは ブランディング などの理由から便利な面をアピールしている場合でも、突っ込んで聞いていくと泥臭い苦労の上で成り立ってたりすることは多々あります。このあたりのエピソードを知っていくと聞きかじりの浅い知識を脱することができるようになってきます。 ↩ 外食してると結構他の席のお客さんから仕事の話が聞こえてくることがあります。たまに「それ機密情報じゃない?」みたいな会話があって他人ながらひやひやしたりも……。 ↩ ちなみに私は初プルリクを投げたら、直前にプロジェクトが凍結されていて無駄に終わったという悲しい過去を持っています。 ↩ 人にもよりますが、うまくいかないことにぶつかるとイライラします。すごくイライラします。正しく設定/実装しているはずなのに動かない、ということはザラです。それでもそれを解決したときの達成感により繰り返してしまうような人がエンジニア向きなんだろうな、と思っています。ちなみにうまくいかない理由はだいたい自分のミスのせいです。 ↩ 少し前だと鼻毛鯖とか安鯖なんて呼ばれていた低価格サーバーがありましたが今はなくなりました。 ↩ ちなみに我が家のサーバーはサーバー用CPUの Xeon を使っていますが、第2世代の Xeon E3-1280という古いCPUなので性能的には Intel N100と同等です。Webアプリケーションの学習用としては特に不便していません。古い分、電力消費が Intel N100(6W)と比べて大きい(95W)ですが。 ↩ Qiitaとかの「技術記事を投稿するプラットフォーム」とされている場所に投稿してると反感買うかもしれないので注意。自分のブログとか SNS なら誰も文句言わないはず……たぶん。 ↩ 色んな人を見ているとどう見ても「努力だけで到達できるように思えないレベル」の人はいると思いますが、そのへんは適性+努力の「超一流」なのでまた別枠かな、と。 ↩
アバター
ラク ス ベトナム 責任者の寺田です! 2014年より、 ラク ス ベトナム は、 ラク スの開発子会社として共に SaaS 開発を進めています。 ラク スでは、今後グローバルな開発の重要性が更に増大すると考えており、今回のブログでは、そんな ラク スの日本ー ベトナム 間のグローバル開発の様子と今後の展望を簡単にお伝えしたいと思います。 ラク スのグローバル開発は、日本と ベトナム がお互いにワンチームである意識を強く持ち、開発に取り組んでいる点が特徴です。 その上で、 ベトナム チームには、より重要な役割を担う事が期待されています。そのため、ぜひ、これから入社される方と一緒に、開発領域のさらなる拡大や成長を加速させていきたいです! 海外での開発業務の実施や、海外組織との開発コミュニケーションに興味を持たれている方にとって、この記事が参考となれば幸いです。 ラクスベトナムについて ミッション・ビジョン・バリュー 開発体制と開発プロセス 体制例 コミュニケーション 今後のグローバル開発への想い ラク ス ベトナム について ラク ス ベトナム (以下RV)は、 ラク スが提供する SaaS 開発に特化した ベトナム 、 ホーチミン の開発拠点です。(現在、 ホーチミン には2つの拠点があります) 現在、RVでは ラク スの5サービス(楽楽精算、楽楽明細、楽楽販売、メールディーラー、配配メール)の担当開発チームが立ち上がり、事業成長を支えており、 ラク スの製品開発を進める上で、なくてはならない開発パートナーとなっています。 相互理解・協力体制を重視しており、近年は低退職率(年間5%以下)と共にメンバーが一丸となってチーム成長に取り組み、着実に組織を拡大しています。 ミッション・ビジョン・バリュー RVのミッションは「高技術と高品質で、最高の SaaS サービスを提供することでエンドユーザ、働く仲間に最高のHappyを届ける」です。 そのために「 ベトナム で クラウド ( SaaS )領域でナンバー1の技術、品質を持ち、最高のサービスを提供して、最高にHappyな会社になる」というビジョンを掲げています。 ミッションのビジョンの実現のために、現時点では「Quality First」、「Fast Resulting」、「Deep Thinking」、「Challange」、「Active Communication」、「One for All&All for One」という6つのバリューを常日頃の業務で大事にしつつ、開発に取り組んでいます。 開発体制と 開発プロセス RVは、寄せられている期待に応える形で、年々体制を拡大し続けています。(現在の社員数は、60名ほどです) それぞれのサービスについて、PM、PL、開発メンバー、QAが在籍する様々な規模(5〜20名ほど)の Java / PHP の開発チームがあります。 複数チームが連動したり、QAの専門チームを置いている開発チームもあります。 RVは、主に基本設計、詳細設計、実装、品質管理などの工程を担っており、日本と密接に協力しながら開発を進めています。 体制例 国内エンジニア…PM 国内ブリッジSEチーム(以下、BrSE)…PMO、BrSE RVのエンジニア…PM、PL、PG、QA 通常は、日本国内の開発チーム内で開発全体のPMを アサイ ンし、BrSEがRVとの開発コミュニケーション支援を行います。 RVでは、設計・実装・ 単体テスト ・第 三者 テストを行い、成果物をBrSEが受け入れる形で進めています。 詳細な業務の流れは、下記の通りです。 ① 国内PMから案件提案 ② 国内BrSEチームで案件の要求整理・RVへ連携 ③ RVで調査・設計の提案 ④⑤ BrSE・国内PMの調査・設計内容レビュー ⑥ RVで実装・ 単体テスト 項目の作成 ⑦⑧ BrSE・国内PMのコード・テスト項目レビュー ⑨ RVで 単体テスト ・第 三者 テストの実施 ⑩ BrSE・国内PMでの納品物受入 コミュニケーション 日本側メンバー、 ベトナム 側メンバー共に、ワンチームとして相互理解・協力体制を大切にしています。 開発では、資料・会議ともに、技術翻訳・通訳の専門チームのメンバーが日本語と ベトナム語 を相互に翻訳・通訳してくれています。 また、彼らは、日本人出張者や駐在者の意図を汲んだコミュニケーションもサポートしてくれています。 日本側でも ベトナム人 ブリッジSEが活躍しており、進捗フォローや分かりにくい要求事項を常日頃からRVメンバーと認識合わせし、思い違いなどがなくスムーズな開発が出来る様に努めています。 ベトナム 側の成長課題に、日本側が一緒に取り組んでいる所も ラク スのグローバル開発の特徴かと思います。 また、日本側メンバーと ベトナム 側メンバーがお互いを知り、尊重し合うというマインドも重視しています。 例えば、日本側と ベトナム 側のメンバーが定期的に出張を実施して、仕事を一緒にしつつ、1on1や懇親会でコミュニケーションを取っています。 それにより、お互いの課題を知り、課題の解決の為に協力するという文化が育まれていますね。 今後のグローバル開発への想い 今後、 ラク スの事業拡大と組織成長に伴い、日本と ベトナム がより一体となったグローバル開発を進めていこうとしています。 将来的には、日本と ベトナム 間での連携を深化させた上で、まだ実現出来ていない ベトナム 側での製品バージョンリリースも実施していきます。 その実現のために取り組んでいる事柄は多いですが、直近では、RVの開発では以下の2つのテーマを重視して取り組んでいます。 RVでの技術課題解決 :チーム毎に得意技術分野を持ち、サービスの新機能要求に対する技術ソリューションを、 ベトナム 側で検討・決定していく。 チームでの内部品質担保 :性能、保守性、信頼性をチーム内で担保して、クオリティの高い設計・実装をしていく。 また、RVメンバーの スキルアップ の為に、例えば、以下の様な施策にも取り組んでいます。 Github Copilotなどの開発ツール/開発技術の積極的な活用 技術スキル、ソフトスキル向上の為の組織的なト レーニン グ 開発作業だけでなく、 スキルアップ 時間も計画的に確保し、その時間を活用した定期的なチーム毎の技術 セミ ナーや改善活動 日本と ベトナム の相互出張によるペアワークを中心にした設計・実装スキルの向上 RVでは、これらの取り組みを マイルストーン を設けて仕組み化し、 PDCA を回していき、開発力を更に高めていこうとしています。 日本側も ベトナム 側と共に成長していく事で、製品を進化させていくメンバーを強化し、グローバル開発として一体となって製品をスケールしようとしています。 ラク スのグローバル開発の現状と今後の展望について簡単にお伝えしましたが、 「面白そうだな」「自分のスキルが丁度活きるフェーズだな」と思われた方、ぜひ一緒に働きましょう!
アバター
はじめに こんにちは、技術広報の菊池です。 セキュリティの確保は技術的な課題にとどまらず、お客様の満足、さらには企業の存続に直結する重要なトピックスです。 私たち SaaS 企業も例外なく、常に変化する脅威にさらされており、日夜対策のアップデートが求められますので、 私も自身の理解を深めるためにキーワードと各分野の歴史をまとめてみました。 本記事で取り上げるセキュリティ主要7分野では、新しい技術の登場と共に、新たな脅威が絶えず発生し、その対策の進歩も伺えました。 今回は、アプリケーション、ネットワーク、エンドポイント、データ、 クラウド 、 アイデンティティ とアクセス管理、インシデント対応と復旧のセキュリティについて、 その概要と1980年代〜現代に至るまでの歴史、脅威と対応策の進化を総括しました。全てはカバーしきれませんでしたが、代表的なツールも紹介しています。 それぞれの分野では個別の発展がありますが、 クラウド とモバイルの進展の影響は共通しており、下記のような大きな流れが存在しているといえそうです。 政治・経済・社会・技術が加速的に複雑化 生活習慣、ビジネスモデル、働き方、デ バイス などが変化 既存のシステム境界があいまいになる 攻撃手法が進化し、 ゼロデイアタック を筆頭に事前対策が困難になる 防衛技術を進化させるだけでなく、防衛思想もシステムの境界防御からゼロトラストをはじめとした被害限定化の思想へと変化 これを念頭に、各分野をご覧いただければ幸いです。 はじめに セキュリティ主要7分野・脅威の進化と対応 アプリケーションセキュリティ 脅威の進化と対応の歴史 主要なセキュリティ施策 代表的な対策ツール ネットワークセキュリティ 脅威の進化と対応の歴史 主要なセキュリティ施策 代表的な対策ツール エンドポイントセキュリティ 脅威の進化と対応の歴史 主要なセキュリティ施策 代表的な対策ツール データセキュリティ 脅威の進化と対応の歴史 主要なセキュリティ施策 代表的な対策ツール クラウドセキュリティ 脅威の進化と対応の歴史 主要なセキュリティ施策 代表的な対策ツール アイデンティティおよびアクセス管理 (IAM) 脅威の進化と対応の歴史 主要なセキュリティ施策 代表的な対策ツール インシデント対応と復旧 脅威の進化と対応の歴史 主要なセキュリティ施策 代表的な対策ツール おわりに セキュリティ主要7分野・脅威の進化と対応 アプリケーションセキュリティ アプリケーションセキュリティは、ソフトウェア内のセキュリティ機能を強化し、アプリケーションレベルでの攻撃や 脆弱性 から保護する取り組みです。開発段階からリリース後の運用フェーズに至るまでの、コードの安全性、データ保護、アプリケーションの耐久性など幅広い対策が含まれます。 脅威の進化と対応の歴史 1980年代 初期のコンピュータウイルスや トロイの木馬 が登場。プログラムのセキュリティもまだ基本的なレベルに留まっていました。 1990年代~2000年代 インターネットの普及と共にWebアプリケーションの 脆弱性 (例: SQLインジェクション 、 クロスサイトスクリプティング )が顕在化し、セキュリティ対策が強化され始めました。 近年 クラウドコンピューティング 、 API 、モバイルアプリの普及により、脅威もより複雑に進化しました。 セキュリティ対策を 開発プロセス の全てのフェーズに組み込み自動化するDevSecOpsの概念も浸透し始めています。 主要なセキュリティ施策 SQLインジェクション 防御: 不適切な入力処理によりデータベースが攻撃され、データベースへの 不正アクセス や情報漏洩が発生するのを防ぎます。 ユーザー入力の サニタイズ やプリペアド ステートメント の使用で、不正なデータベース操作を防止します。 クロスサイトスクリプティング ( XSS ) 防御: 悪意のある スクリプト がウェブページに注入され、ユーザーデータの盗難や セッションハイジャック が発生するのを防ぎます。ユーザー入力の サニタイズ とバリデーションが重要です。 クロスサイトリクエストフォージェリ ( CSRF ) 防御: ユーザーのブラウザを通じて不正なリク エス トが送信され、不正なリク エス トが実行されるのを防ぎます。 トーク ンベースやサーバー検証により、正当なユーザーのリク エス トのみを許可する対策が有効です。 セキュアコーディング: 人の作業が 脆弱性 につながることもあります。 脆弱性 のないコードを書くため、コーディング規約の遵守と仕組み化、教育も重要です。 暗号化: データの盗聴や漏洩を防ぐため、 トランスポート層 とデータストレージの両方での暗号化が推奨されます。 代表的な対策ツール 以下は、アプリケーションセキュリティ分野の代表的な対策ツールです。複数の役割を同時にこなせるツールもあります。 静的アプリケーションセキュリティテスト (SAST) ツール: ソースコード を分析し、 脆弱性 を特定します。例: SonarQube、 Checkmarx。 動的アプリケーションセキュリティテスト (DAST) ツール: 実行中のアプリケーションをテストし、 脆弱性 を探ります。例: OWASP ZAP、 Burp Suite、Checkmarx。 Webアプリケーション ファイアウォール (WAF): ウェブアプリケーション への不正なアクセスや攻撃を阻止します。例: Cloudflare、 ModSecurity。 依存関係管理ツール: ソフトウェア依存関係の 脆弱性 を追跡し、アップデートを管理します。例: OWASP Dependency -Check、Snyk。 ネットワークセキュリティ ネットワークセキュリティは、ネットワーク、その利用者や情報資産を 不正アクセス 、攻撃、侵害から保護するための対策やプロセスです。これには、物理的なセキュリティ措置からソフトウェアベースの保護、ポリシーの設計と実施までが含まれます。目的は、データの機密性、完全性、可用性を保つことです。 脅威の進化と対応の歴史 1980年代 ファイアウォール という概念が登場し、組織内外のネットワーク境界を保護することが重視されました。 1990年代~2000年代 インターネットの爆発的な普及により 不正アクセス など脅威も多様化。侵入検知システム(IDS)や侵入防止システム(IPS)などが開発されました。 近年 クラウド ベースのネットワークセキュリティ、エンドツーエンドの暗号化、ゼロトラストネットワークの概念が普及。 ネットワークの境界が曖昧になる中で、セキュリティはより動的かつ分散された形へと進化しています。 主要なセキュリティ施策 ファイアウォール トラフィック を監視し、設定されたルールに基づいて制御します。 不正アクセス を阻止する基本的な防御手段です。 侵入検知システム (IDS) / 侵入防止システム (IPS) ネットワーク トラフィック を分析し、異常や攻撃の兆候を検知します。IDSは警告を発し、IPSは攻撃を阻止します。 仮想プライベートネットワーク ( VPN ) インターネット上で暗号化された通信チャネルを作り、安全な通信を実現します。リモートアクセスやデータの保護に重要です。 アンチウイルス /アンチ マルウェア ウイルス、 スパイウェア 、 トロイの木馬 などの悪意あるソフトウェアから保護します。システムに定期的なスキャンを行い、感染を防止します。 データ損失防止 ( DLP ) 機密データが不正に外部に送信されるのを防ぎます。データの使用と転送を監視し、ポリシー違反を検知します。 代表的な対策ツール 以下は、ネットワークセキュリティ分野の代表的な対策ツールです。 ファイアウォール  例: Cisco Firepower、Palo Alto Networks Firewall IDS/IPS  例: Snort 、Suricata VPN  例:NordVPN、ExpressVPN アンチウイルスソフト ウェア  例: McAfee 、 Norton DLP  例: Symantec DLP 、 McAfee Total Protection for DLP エンドポイントセキュリティ エンドポイントセキュリティは、ネットワークに接続される各端末(コンピューター、 スマートフォン 、 タブレット など)のセキュリティを指します。 この目的は、これらのデ バイス を サイバー攻撃 、 マルウェア 、その他の脅威から保護することです。 エンドポイントセキュリティは、機器の物理的およびソフトウェア的な両面から、企業や個人のデータとシステムを守ります。 脅威の進化と対応の歴史 1980年代~1990年代 初期の ウイルス対策 ソフトウェアが登場。主に単一のデ バイス を保護することに焦点を当てていました。 2000年代 モバイルデ バイス の普及に伴い、セキュリティソリューションは多様なデ バイス に対応する必要が出てきました。 近年 リモートワークの増加とIoTデ バイス の普及により、エンドポイントの種類・利用シーンが一層複雑化。セキュリティ管理がより重要になっています。 主要なセキュリティ施策 アンチウイルス /アンチ マルウェア マルウェア やウイルスからデ バイス を保護し、感染したファイルの検出と削除を行います。 ファイアウォール デ バイス への 不正アクセス をブロックし、許可された通信のみを通過させます。 デ バイス 管理 ネットワーク内の全デ バイス を管理し、 セキュリティポリシー の遵守を保証します。 暗号化 データを暗号化し、万が一のデータ漏洩時にも情報を保護します。 パッチ管理 ソフトウェアの 脆弱性 を修正するために定期的にセキュリティパッチを適用します。 代表的な対策ツール 包括的なエンドポイントセキュリティソリューション 例: Symantec Endpoint Protection、 McAfee Endpoint Security: アンチウイルス 例: Bitdefender 、 Kaspersky デ バイス 管理ツール 組織内の全デ バイス のセキュリティ状態を一元管理し、ポリシー遵守を支援します。 例: Microsoft Intune、 VMware Workspace ONE  データの暗号化 デ バイス 盗難や紛失時のデータ漏洩を防ぎます。例: BitLocker、 VeraCrypt パッチ管理ツール ソフトウェアアップデートを集中管理し、セキュリティパッチの適用を自動化します 例:WSUS、 SCCM データセキュリティ データセキュリティは、データの機密性、完全性、および可用性を保護するプロセスおよび手法を指します。 これには 不正アクセス 、データ漏洩、改ざんや紛失を防ぐための対策が含まれます。 脅威の進化と対応の歴史 1980年代~1990年代 データ暗号化とアクセス制御が主要な焦点で、物理的なデータ保護が重要視されていました。 2000年代 オンラインでのデータ交換の増加に伴い、データ漏洩対策としての暗号化技術が強化されました。 近年 ビッグデータ とプライバシーに関する法律(例: GDPR )の登場により、データのプライバシー保護が重要視されるようになりました。データの分類と機密性の管理が進化しています。 主要なセキュリティ施策 データ暗号化 データを暗号化し、 不正アクセス 者による読み取りや理解を防ぎます。ストレージおよび転送中のデータに使用されます。 アクセス制御 ユーザ認証と権限付与により、特定のユーザまたはシステムのみがデータへのアクセスを許可されます。 データマスキング 機密データを変更または隠蔽し、本物のデータにアクセスする必要のない場合に使用されます。 バックアップと ディザスタリカバリ データのコピーを定期的に作成し、データの損失や破損時に復元できるようにします。 データ損失防止 ( DLP ) 不正または誤ったデータの転送や漏洩を防ぎます。データの使用と転送を監視します。 代表的な対策ツール データ損失防止ソリューション 例: Symantec DLP 、 McAfee Total Protection for DLP : データ暗号化 例:BitLocker、VeraCrypt アクセス制御と認証管理 例: Active Directory 、Okta バックアップと ディザスタリカバリ 例:Veeam、Acronis データ監視と分析 例: IBM Guardium、Informatica クラウド セキュリティ クラウド セキュリティは、 クラウド ベースのサービスとインフラスト ラク チャ(例: パブリッククラウド 、 プライベートクラウド 、ハイブリッド クラウド )の保護に特化したセキュリティ対策です。 これには、データの保護、アクセス管理、脅威からの保護などが含まれ、 クラウド 環境特有のリスクと課題に対処するための技術とポリシーが重要となります。 脅威の進化と対応の歴史 2000年代 クラウドコンピューティング の出現により、新たなセキュリティ上の課題が登場しました。 2010年代 クラウド サービスプロバイダーによるセキュリティ機能の提供が拡充してきましたが、環境が複雑になるにつれセキュリティリスクも増大。 近年 マルチ クラウド およびハイブリッド クラウド 環境の普及に伴い、 クラウド セキュリティポスチャ管理(CSPM)、 クラウド アクセスセキュリティブローカー(CASB)などの技術が登場しています。 主要なセキュリティ施策 アクセス制御 クラウド リソースへのアクセスを厳格に管理し、認証されたユーザーのみがアクセスできるようにします。 データ暗号化 クラウド 内で保存されているデータを暗号化し、 不正アクセス によるデータ漏洩を防止します。 侵入検知と侵入防止システム (IDS/IPS) ネットワーク トラフィック を監視し、 潜在的 な攻撃や脅威を検出し、ブロックします。 データ損失防止 ( DLP ) 重要なデータの不正な移動や漏洩を防ぎ、情報の安全性を保ちます。 イベントログの管理と分析 クラウド アクティビティのログを集中管理し、不正行為や脅威の早期発見に役立てます。 代表的な対策ツール 包括的な クラウド セキュリティ 例: Amazon Web Services ( AWS ) Security、 Microsoft Azure Security Center クラウド ワークロードのセキュリティ 例: Symantec Cloud Workload Protection データ損失防止 例: McAfee MVISION Cloud イベントログの管理と分析 例:Splunk、LogRhythm アイデンティティ およびアクセス管理 (IAM) アイデンティティ およびアクセス管理(IAM)は、適切な人々が適切なリソースへのアクセス権を持つことを保証するプロセスと技術です。 IAMシステムは、個々のユーザーの アイデンティティ を識別し、その アイデンティティ に基づいてリソースへのアクセスを管理・監視します。 脅威の進化と対応の歴史 1990年代~2000年代 認証はパスワードベースが主流でした。しかしパスワードの弱点が明らかになるにつれ、追加の認証手段が求められました。 2010年代 多要素認証(MFA)が普及。IAMシステムが企業内のユーザー管理に不可欠となる。 近年 バイオメトリック認証や 人工知能 (AI)を用いた認証方法が登場。IAMはより洗練され、組織内のアクセス管理を自動化するシステムが増えています。 主要なセキュリティ施策 ユーザー認証 パスワード、生体認証、 スマートカード などを用いてユーザーの身元を確認します。 シングルサインオン (SSO) 一度のログインで複数の関連システムやアプリケーションにアクセスできるようにします。 多要素認証 (MFA) セキュリティを強化するために、2つ以上の認証方法を組み合わせて使用します。 アクセス管理ポリシー ユーザーのアクセス権限を定義し、適切なアクセスレベルを設定します。 プロビジョニングとデプロビジョニング 新しいユーザーのアクセス権の設定や、不要になったアクセス権削除を行います。 代表的な対策ツール IAMソリューション SSO、MFA、アクセス管理ポリシーなどを提供しています。 例: Microsoft Azure Active Directory 、 Okta: パスワード管理ツール 例: LastPass 、 Dashlane 多要素認証 例:Duo Security、 RSA SecurID アイデンティティ 管理とガバナンス 例:SailPoint, IBM Security Identity Governance and Intelligence インシデント対応と復旧 セキュリティ違反やその他のサイバーインシデントが発生した際に、迅速かつ効果的に対応し、組織の運用を正常な状態に戻すプロセスです。このプロセスには、事前のリスク評価、インシデント対応計画の策定、インシデントの検出、対応、分析、および将来の脅威に対する備えの強化、バックアップと復旧の手順が含まれます。 脅威の進化と対応の歴史 1980年代~1990年代 初期の セキュリティインシデント への対応は比較的基本的であり、主に物理的な脅威やデータ損失に焦点を当てていました。 2000年代 サイバー攻撃 の複雑化に伴い、専門的なインシデント対応チームとプロセスが形成され始めました。 近年 ゼロデイアタック を筆頭に、 サイバー攻撃 は高度化しています。事前にリスクを特定し、迅速に対応するためのソフィスティケートされたツールと手法が開発されています。 また、データバックアップと災害復旧計画の重要性が高まっています。 主要なセキュリティ施策 インシデント対応計画 インシデント発生時の対応手順、責任者、コミュニケーションプランを定めた計画を事前に立案しておく必要性が高まっています。 インシデント検出 サイバー攻撃 やセキュリティ違反を早期発見するためのシステムとプロセスです。 脅威インテリジェンス 最新の脅威情報を収集・分析し、将来の攻撃に備えることを指します フォレンジック 分析 インシデント後の詳細な調査を行い、原因と影響を特定することです。 ディザスタリカバリ とビジネス継続計画 ( BCP ) 大規模なインシデント後に、ビジネス運用を迅速に回復するための計画です。 Security Operation Center (SOC) セキュリティ装置、ネットワーク、サーバ、アプリケーションなどから出力されるログを常時監視・分析し、インシデントの検出・対処を行う専門組織を指します。 代表的な対策ツール セキュリティ情報およびイベント管理(SIEM)ツール 例:Splunk、 IBM QRadar 脅威インテリジェンスとインシデント対応 例:CrowdStrike、 FireEye フォレンジック 分析 例:EnCase、 FTK (Forensic Toolkit) ディザスタリカバリ ソリューション 例: VMware Site Recovery、 Zerto おわりに ここまで、セキュリティの主要7分野について、概要と脅威の進化と対応策の歴史、主要なセキュリティ対策と代表的な対策ツールをまとめました。 セキュリティ各分野の歴史は加速化する複雑性との闘い、その中での費用対効果の模索とも言えそうです。 当社でも引き続きセキュリティのトレンド情報や傾向を収集し、 脆弱性 情報を収集、対策に励んでいますので、今後も注視を続けたいと思います。
アバター
はじめに メールディーラー開発課のyamamuuuです。 2024/03/7(木) ~ 03/9(土)の3日間に渡ってPHPerKaigi 2024が開催されました。 今回もオンライン・オフライン両方のハイブリッド開催でした。 phperkaigi.jp ラク スはシルバースポンサーとして協賛し、3名が登壇した他、数名のメンバーが参加しました。 今回は ラク スからの登壇者本人と参加者によるレポートを紹介させていただきます。 はじめに 参加レポート php-src debug マニュアル 10年モノのレガシーPHPアプリケーションを移植しきるまでの泥臭くも長い軌跡 ウキウキ手作りミニマリストPHP Laravel OpenAPIによる "辛くない" スキーマ駆動開発 こんな静的解析導入は負けフラグ 帰ってきた「完成度低いの歓迎LT大会」(PHPerKaigi出張版) WebAssembly を理解する 〜VM の作成を通して〜 初PHPでサーバーモデルについて考えた話 PHP8の機能を使って堅牢にコードを書く 「わたしたちのコード」を安定させるためにフレームワークとの距離を保つ PHP 8.3で追加されたjson_validate()を徹底的に深掘りしてみよう B+木入門:PHPで理解するデータベースインデックスの仕組み PHP 本体のバグを見つけたら適切に報告しよう Webアプリケーションの効率を再定義するBEAR.Sundayの分散キャッシングフレームワーク キャッシュと向き合う、キャッシュと共に生きる 保守開発メインでやってきたエンジニアが『リーダブルコード』を機能削除の観点から語る 超巨大!超重要!な処理のリファクタリングにどのように向き合っているか privateメソッドのテストって書かない方がいいんだっけ? Composerを便利に使うために私がやっていること by きんじょうひでき ラクスからの登壇セッションのご紹介 レガシーシステムへのPHPStan導入から半年での課題と効果 PHP8.2にバージョンアップしたら文字化けが発生して道頓堀に飛び込みたくなった話 PHPでOfficeファイルを取り扱う! ~PHP Officeライブラリをプロダクトに組み込んだ話~ まとめ PHPerのためのコミュニティ PHPTechCafe 参加レポート php -src debug マニュアル report by id:dd_fortran おのぽんさん( @onopon_engineer )による発表です speakerdeck.com PHP 本体の ソースコード である php -srcの デバッグ 方法について話されていました。 PHP の デバッグ 環境の構築は少しハードルがありますが丁寧に解説されていたので、試したことがない人でも参考になると思います。 デバッグ した内容が echo だったこともあり、Opcodeの話をされており少し初学者には難しい内容だったかと思いました。 デバッグ したことがある人がさらに1歩理解を進めようと思っている方にはおすすめです! もし、初めて デバッグ してみようと考えている方は var_dump などの標準関数で試してみるのがよいと思います。 10年モノのレガシー PHP アプリケーションを移植しきるまでの泥臭くも長い軌跡 report by : id:rakuinoue toshimaruさん( @toshimaru_e )による発表です speakerdeck.com レガシーな PHP アプリケーションを Rails アプリへ移植を進めていく中での課題や解決方法、実際に行われた事などをお話されていました。 レガシーなアプリケーションの技術的負債をどのように解消するかの方法はいくつかありますが、 それぞれの手法について課題やメリットなどを整理し、フィットする手法を見定め実践されたことが説明されていたので、非常に参考になりました。 各チームともいろいろな調整や決定事項をさだめて移植を完了させていくステップはリーダー的ポジションの人などには有益なお話だったと思います。 ウキウキ手作り ミニマリスト PHP report by : id:hirobex uzullaさん( @uzulla )による発表です speakerdeck.com PHP を自分でビルドしたことがある人は少ないのではないでしょうか? Dockerやapt-get、 brew でインストールする人が多いと思います。 そんな人こそ、この トーク を見て自前でビルドした PHP と、自動でインストールされる PHP に、どのように違いがあるのか知ってみましょう! Laravel OpenAPIによる "辛くない" スキーマ 駆動開発 report by id:takaram 武田 憲太郎さん ( @KentarouTakeda )による発表です。 speakerdeck.com WebアプリのフロントエンドをReactなどで作ることが一般的になり、バックエンドで API を作るケースは増えていると思います。この場合、フロントエンドとバックエンドのエンジニア間で API の仕様を定めて開発を行う必要がありますが、 Excel で作ったような従来の仕様書を使う開発では様々な課題があります。 そこで、 API の仕様を記述するフォーマットであるOpenAPI、およびOpenAPIの周辺ツールを上手く利用することで、スピードと品質を両立した開発を行うことができる、ということを紹介してくださいました。 ちょうど個人的にOpenAPIが気になっていたので、ぜひ活用してみたいと思えるセッションでした! 余談ですが、「"仕様"という抽象に実装を依存させる」ことを依存関係逆転の原則になぞらえて説明されていたのが分かりやすく 目から鱗 でした。 こんな静的解析導入は 負けフラグ report by : id:hirobex うさみけんた さん( @tadsan )による発表です fortee.jp PHPStanを導入するにあたり、どのような バッドノウハウ があるかを教えていただける トーク です。 実際にPHPStanを導入している人や、これからPHPStanを導入しようと考えている人にオススメです! 帰ってきた「完成度低いの歓迎LT大会」(PHPerKaigi出張版) report by id:dd_fortran すぎやまさん ( @oogFranz ) による発表です note.com サイボウズ のGaroon開発チームでは「完成度低いの歓迎LT大会」を開催しているといった内容で、実際に3人の方がLTで登壇されていました。 登壇のハードルを下げるために実施しており、登壇経験が少ないメンバーでも参加しやすい取り組みいいなと思いました。 弊社でもビアバッシュが定期的に開催されていますが、発表のクオリティが高くて発表しづらいといった声もあるので導入してみたいと思いました! WebAssembly を理解する 〜 VM の作成を通して〜 report by id:dd_fortran nsfisisさん( @nsfisis )による発表です blog.nsfisis.dev WebAssembly(Wasm)を実行する VM を PHP で作成され、その作成方法の概略についての内容でした。 Wasmに興味がある方(特にPHPerでもある方)にはとても興味深い内容でした。 どのように処理を作っていけばいいかやWasmの仕様書の読み進め方などについて話されており、とても参考になりました。 私も時間を作ってWasmを触ってみようと思いました! 初 PHP でサーバーモデルについて考えた話 report by : id:hirobex sadnessOjisanさん( @sadnessOjisan )による発表です speakerdeck.com 当初とは若干 トーク タイトルが変わっており「サーバーとは何かを理解して、コンテナ1つで実行しよう」というタイトルになっています。 皆さんが本番環境で PHP を”動かす"ときって、どうしていますか? 基本的には、 Apache やNginx経由で PHP ファイルを呼び出していると思います。 ビルトインサーバーという手もありますが、本番環境では推奨されていません。 最近ではDockerを利用することも多いと思いますが、 PHP と Apache が入ったDockerコンテナは、1プロセス1コンテナの原則を破っています。 なぜ PHP がこのような動かし方になっているのか、どうすれば理想のDockerを作れるのか。 その答えが、この トーク にあります。 PHP8の機能を使って堅牢にコードを書く report by id:dd_fortran Endo Futoshi さん( @Fendo181 )による発表です speakerdeck.com PHP8以降の新機能を使って、堅牢なコードを書くためのテクニックを話されていました。 読み取り専用クラス(Readonly Classes)や 列挙型( enum )を使って、コードを堅牢にする方法を紹介していました。 PHP8以降の新機能を利用したことがない方でも理解しやすい内容だったので初学者にもおすすめです! 「わたしたちのコード」を安定させるために フレームワーク との距離を保つ report by id:takaram 大橋 佑太さん ( @blue_goheimochi ) による発表です。 speakerdeck.com Laravelなどの フレームワーク には、多くの便利な機能が用意されていて、それらを使いこなすことで迅速な実装が可能になります。 しかし、依存しすぎてしまうと フレームワーク 側の仕様変更の影響をもろに受けてしまい、対応が辛くなるという諸刃の剣でもあります。 そのため、自分の実装する ビジネスロジック と フレームワーク を利用するロジックとを分離し、「 フレームワーク と適切な距離を保つ」ことが大事であると説明されていました。 処理を分けていたことで実際にメリットがあったという実例も紹介されており、今後のクラス設計などで意識したいと感じることができました。 PHP 8.3で追加された json _validate()を徹底的に深掘りしてみよう report by id:dd_fortran 柚口 ましろ うさん( @yu_mashirou )による発表です speakerdeck.com PHP8.3で追加された json_validate() について、その使い方や実装の仕組みについて解説されていました。 json_decode() があるから不要なのでは?と思っている方はぜひ見てみてください。 B+木入門: PHP で理解するデータベースインデックスの仕組み report by : Y-Kanoh 富所亮 さん( hanhan1978 )による発表です speakerdeck.com 普段は自分の業務周辺知識に関する発表をを中心に聴いて回るのですが、今回はまったく知らない領域の発表も聴いてみようと思い参加しました。 名前ぐらいしか聞いたことがないものですが、そもそも"データ構造"であることから、どのようにデータが追加され追われていくのかまで優しく説明されていました。 すごくわかりやすい解説で、知識の世界を広げることができました。 PHP 本体のバグを見つけたら適切に報告しよう report by id:dd_fortran 工藤 剛さん( @zeriyoshi )による発表です speakerdeck.com PHP でバグを見つけたときに詳細なログや スタックトレース を出力する方法を紹介していました。 PHP 本体の詳細なログを出力する方法は知らなかったのでとても参考になりました。 バグを見つけた際には詳細なログを出せるように試しておこうと思いました。 Webアプリケーションの効率を再定義するBEAR.Sundayの分散キャッシング フレームワーク report by : id:hirobex 郡山 昭仁さん( @koriym )による発表です speakerdeck.com そもそもキャッシュとは何なのか。 なぜキャッシュを利用するのか。 キャッシュの難しさとは何なのか。 理想のキャッシュとは? を語った上で、BEAR.Sundayなら達成できます。 という、キャッシュについての広い勉強ができる トーク でした。 この トーク 中に話されていた「不易流行」 「流行」だけを積み上げても、いつか時代とともに使えなくなる日が来る。 そのときにまた「流行」で作り直してもタイマーが0になるだけ。 「不易」つまり、変わらないものを積み上げることで、時代を超えて変わらない価値を届けられる。 という言葉に胸を打たれました。 キャッシュと向き合う、キャッシュと共に生きる report by id:takaram 曽根 壮大さん ( @soudai1025 )による発表です speakerdeck.com Web開発でキャッシュの利用を検討する上での注意点に関するお話でした。 「キャッシュは麻薬(=劇的な効果があるが一度使うと止めづらく、運用も難しくなる)」であるため、使う前に本当に必要か注意深く検討する必要がある、ということが繰り返し説明されています。 検討の上で必要と判断したら、キャッシュの更新・生存期間の アルゴリズム を決める必要がありますが、それらの各種 アルゴリズム についても説明がありました。 キャッシュの アルゴリズム にどんなものがあるのか、私は全然認識できていなかったので、今後のキャッシュ検討のときにこのスライドがとても役に立ちそうです! 「利用用途、 アルゴリズム を決めてから保存先のデータストアを選ぶこと」というのも重要だと感じました。ついつい「今回はRedisを使ってキャッシュをして…」のように、データストアから決めてしまいがちな気がするので気をつけたいところです。 保守開発メインでやってきたエンジニアが『リーダブルコード』を機能削除の観点から語る report by : id:ymyhero7 ririhoshiさん( @_riri_hoshi )による発表です fortee.jp 実際に機能削除をされた際に直面した問題と、そこから得た教訓をリーダブルコードの観点で3点紹介してくださいました。 命名 時に解釈の余白を持たせない、表記揺れさせない コメントをアップデートし続ける 「後で使うかもしれないから」と使わない共通関数を生み出さない どれも歴史のあるサービスを開発していると非常に共感できる内容でした。 発表の最後に、「既存仕様・実装のしがらみの中でシンプルで美しいコードを追求し続けることの積み重ねがサービスの未来をつくる」とおっしゃっていたことが心に刺さりました。 私もその言葉を胸に刻んで開発していきます! 超巨大!超重要!な処理の リファクタリング にどのように向き合っているか report by : id:hirobex 今濱 眞さん( @seainthepast )による発表です fortee.jp タイトル通り、超巨大!超重要!な処理を リファクタリング するために行っている計画と、 計画中に取り組んで良かったこと等をお話していただきました。 同じように、超巨大!超重要!な処理を リファクタリング を考えている人は一見の価値があると思いました。 privateメソッドのテストって書かない方がいいんだっけ? report by : id:hirobex asumikamさん https://twitter.com/asumikam による発表です speakerdeck.com shoulditestprivatemethods.com なぜprivateメソッドのテストを安易に作ってはいけないのか、という トーク です。 最近はprivateメソッドのテストは書かないということが広まってきているとは思いますが、 なぜ書いてはいけないのか、逆に、どういう時なら使ってもいいのか、というのを説明できる人は少ないと思います。 この トーク を聞くことで、その理由が理解できると思います。 Composerを便利に使うために私がやっていること by きんじょうひでき report by id:dd_fortran きんじょうひできさん( @o0h_ )による発表です speakerdeck.com composerの便利なコマンドや設定を紹介していました。 知らなかったコマンドもあり、とても参考になりました。 ラク スからの登壇セッションのご紹介 レガシーシステム へのPHPStan導入から半年での課題と効果 report by id:dd_fortran speakerdeck.com PHPカンファレンス 関西2024に続き、同様の内容で登壇してきました。 同じ内容での2回目の登壇となっていたのですが、 (疲れていたこともあり) 練習不足なこともあり登壇の話し方がうまくいかなかったので反省です...。 内容としては レガシーシステム にPHPStanを導入した事例について話させていただきました。 登壇後には他社のエンジニアの方から声をかけていただき、参考になったという声や同様の課題を抱えているといった話をしました。 他社のエンジニアと交流する機会は多くないため、とてもいい経験となりました!! PHP8.2にバージョンアップしたら文字化けが発生して道頓堀に飛び込みたくなった話 report by : id:neroblubros speakerdeck.com 私も PHPカンファレンス 関西2024で登壇した内容とほぼ同様の内容でお話しました。 リリース後に文字化けが発生したことを切り口に PHP のバージョンアップや運用とお客様対応の話をしました。 刺さるかな?と気になっていましたが、2件の鋭い質問をされてとてもいい経験になりました。 PHPerKaigiの規模の大きさにただただ驚きましたが、貴重な体験ができたし、 自分が世間で通じること、足りないことが整理できて登壇して良かったと思っています。 PHP でOfficeファイルを取り扱う! ~ PHP Officeライブラリをプロダクトに組み込んだ話~ report by : id:hirobex speakerdeck.com 登壇しました。 ちゃんとウケを狙ったところで笑えてもらえたので良かったです。 PHP でOfficeファイルを取り扱う時は、メモリ枯渇に気をつけてください。 LTなのにAsk the speakerで質問しに来てくださった方がいて嬉しかったです。 まとめ 今回もたくさんの参加レポートを執筆しました。 内容も レガシーシステム からの脱却や静的解析、Composerの便利コマンドと多岐に渡りなかなか読み応えのあるものになったのではないでしょうか。 PHPerのためのコミュニティ PHPTechCafe ラク スでは PHP に特化したイベントを毎月開催しております。 その名も「PHPTechCafe」!! 次回は 2024/03/26(火)に『PHPerのための「PHPTechCafeの5年間を振り返る」 PHP TechCafe』 をテーマに開催します! まだまだ参加者を募集していますので、ぜひお気軽にご参加ください。 👉 PHPerのための「PHPerのための「PHPTechCafeの5年間を振り返る」PHP TechCafe 最後までお読みいただきありがとうございました!
アバター
はじめに こんにちはこんばんは! 昨今、セキュリティへの関心が非常に高まっています。 二段階認証を取り入れる企業が多くなってきました。 最近の例で言うと、 Github が2023年3月ごろに二段階認証を義務化したのは記憶に新しいと思います。 そこで、今回は認証の基礎知識をおさらいした上でTOTPを使った二段階認証の仕組みと導入時の注意点について解説します! ※本記事の内容は、ビアバッシュ(社内の技術共有会)にて登壇発表した内容です。 ビアバッシュの取り組みについては以下の記事を読んでみてください! tech-blog.rakus.co.jp はじめに 基礎知識 二要素認証とは? 二段階認証とは? 二要素認証と二段階認証の違い ワンタイムパスワードとは? HOTPとTOTPについて HOTPとは? TOTPとは? TOTPの時刻ズレ対策 導入編 TOTPの時刻ズレ対策の実装 TOTPの注意点 まとめ 基礎知識 認証と聞くと、二段階認証と二要素認証が思い浮かびますね! この2つはどう区別されるのでしょうか? 二要素認証とは? 以下の要素を2つ以上組み合わせて認証することを二要素認証と言います。 記憶情報 ID、パスワード、出身地の情報など本人が記憶している情報 生体情報 虹彩 、指紋など本人の生体情報 所持情報 パソコン、 スマホ 、カード、USB機器など本人が所持している情報 二要素認証 たとえば、ATMでお金を引き出すケースを想定しましょう。 まずATMに本人が持っているカードを差し込む必要があります。このカードが所持情報となります。 次に4桁のパスワードを入力します。このパスワードは記憶情報となります。 ですので、所持情報と記憶情報の二要素認証をクリアして初めてATMでお金を引き出すことができます。 このケースでは二要素ですが、二要素以上ある場合は多要素認証(MFA)とも呼ばれます! 二段階認証とは? ある画面で認証を行い、さらに別の画面でも認証を行うことを二段階認証といいます。 二段階認証 たとえばログイン画面でパスワードを入力したあと、次の画面で秘密の質問に答えるようなケースです。 このケースでは二段階ですが、3つ以上の段階を踏む場合は多段階認証とも呼ばれます! 二要素認証と二段階認証の違い 二要素認証は認証の要 素数 について着目し、二段階認証は認証のステップ数について着目しています! たとえば、ログイン画面でIDとパスワードを入力後、次の画面でメールに届いた ワンタイムパスワード を入力するケースは二要素認証/二段階認証どちらに当てはまるでしょうか? 正解は「 どちらとも言える 」です。 「記憶情報であるパスワード」と、「(メールが届く端末を所持しているという意味で)所持情報であるメールに届いた ワンタイムパスワード 」の二要素の組み合わせです。 しかも、「IDとパスワード入力画面」と「 ワンタイムパスワード の入力画面」の二段階のログインです。 ですので、 二要素認証でもあり二段階認証でもある と言えます! ワンタイムパスワード とは? 一度しか使えない期間限定のパスワードのことを ワンタイムパスワード と言います。 OTP(One-Time-Password)と略されます。本記事も以降はOTPとします。 ワンタイムパスワード OTPの代表的な生成方法はHOTPとTOTPがあります。 HOTPもしくはTOTPを使って二段階認証を実現するのが基本セオリーです! HOTPとTOTPについて いずれも RFC に規定されており、秘密キーを利用してOTPを生成します。 また、OTP生成時はクライアントとサーバで同じ秘密キーを利用します。 秘密キーは認証するクライアントとサーバしか知り得ない情報です。 Github で二段階認証をする場合、 QRコード をGoogleAuthenticatorなどの認証アプリで読み取ると、6桁のOTPが表示されます。 このとき、秘密キーは QRコード 化されており、認証アプリがその秘密キーを使って6桁のOTPをHOTP/TOTP方式で生成しています。 Github の例 Github では秘密キーを QRコード で表示しているページのsetup_keyをクリックすると、秘密キーが平文で表示されます。 平文の秘密キー HOTPとは? HMACベースの ワンタイムパスワード アルゴリズム です。 RFC 4226 - HOTP: An HMAC-Based One-Time Password Algorithm 日本語訳 秘密キーと通算の認証回数からOTPを生成する方法です。 クライアント側は、クライアント側に保持した秘密キーと通算の認証回数を基にOTPを生成してサーバに送信します。 サーバ側は、サーバ側に保持したクライアントと同じ秘密キーと通算の認証回数を基にOTPを生成します。 両者とも、秘密キー・通算の認証回数・生成 アルゴリズム が同じ → 同じOTPが生成されるため、認証OKとなります! HOTP成功例 秘密キーが同じでも通算の認証回数がクライアントとサーバで異なると、生成されるOTPも異なるため認証に失敗します。 HOTP失敗例 認証が途中で中断されるなどでクライアントとサーバで認証回数のずれが発生することがあります。 また、クライアント側でHOTPを作成するときに認証回数も更新する実装かつ何度もHOTPを再生成する仕様にしてしまうと、認証回数のずれが発生します。 対応策として、認証回数ズレの補正や認証回数リセットの要件検討/実装が必要です。 TOTPとは? 秘密キーと認証時の現在時刻からOTPを生成する方式です。 RFC 6238 - TOTP: Time-Based One-Time Password Algorithm 日本語訳 クライアント側は、クライアント側に保持した秘密キーと現在時刻を基にOTPを生成してサーバに送信します。 サーバ側は、サーバ側に保持したクライアントと同じ秘密キーと現在時刻を基にOTPを生成します。 なお、現在時刻はサーバ側の方が必ず後になってしまうので、OTPには発行されてから30秒間を有効とする猶予期間があります。 両者とも、秘密キー・現在時刻・生成 アルゴリズム が同じ → 同じOTPが生成されるため、認証OKとなります! TOTP成功例 サーバ側は、30秒毎に有効なOTPを更新します。 ですので、発行されてから認証までに30秒を越えたOTPを送信すると、有効ではないOTPとして判断され認証に失敗します。 TOTP失敗例 例えば、クライアントの端末の時間とサーバ側の時間にズレがある場合は認証に失敗します。 TOTPの時刻ズレ対策 TOTPで生成されたOTPは 30秒単位で有効期限を伸ばす ことができ、時刻ズレを吸収することができます! TOTPの有効期限延長 HOTPと比較して認証のズレ対策に柔軟性があり実装コストも低いです。 導入編 では、TOTPを使って二段階認証を導入するために考慮が必要なポイントを抑えていきましょう! TOTPの時刻ズレ対策の実装 TOTPの生成と認証がセットになった リポジトリ はいくつもあります。 今回は、以下の リポジトリ のプログラムを参考にしてみます。 github.com PHPGangsta/GoogleAuthenticator.php の verifyCode 関数を見てみましょう。 PHPDocにもある通り引数 $discrepancy の値を増やすことで認証期限を延長できます。 /** * Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now. * * @param string $secret * @param string $code * @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after) * @param int|null $currentTimeSlice time slice if we want use other that time() * * @return bool */ public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null) { if ($currentTimeSlice === null) { $currentTimeSlice = floor(time() / 30); } if (strlen($code) != 6) { return false; } for ($i = -$discrepancy; $i <= $discrepancy; ++$i) { $calculatedCode = $this- > getCode($secret, $currentTimeSlice + $i); if ($this- > timingSafeEquals($calculatedCode, $code)) { return true; } } return false; } TOTPの注意点 OTPは30秒に1つ生成されます。 たとえば、OTP"555555"が11:59:30、OTP"666666"が12:00:00に生成されたとします。 OTP"555555"の有効期限は11:59:30〜11:59:59の間です。 OTP"666666"の有効期限は12:00:00〜12:00:29の間です。 このとき、12:00:00〜12:00:29の間で有効なOTPは "666666"のみです。 そのため、12:00:00〜12:00:29の間でOTP"555555"を使って認証した場合は 失敗 します。 ここで、OTPの有効期限を1分に延ばしたとき、OPT"555555"の有効期限は11:59:30〜12:00:29になります。 すると、12:00:00〜12:00:29の間で有効なOTPは "555555"と"666666"の2つになります。 そのため、12:00:00〜12:00:29の間でOTP"555555"を使って認証した場合は 成功 します。 つまり、OTPの有効期限を延ばすほど有効なOTPが増えます! 有効なOTPが増える その結果、セキュリティレベルを下げてしまいますので以下のような対策を検討する必要があります。 総当たり攻撃( ブルートフォース攻撃 )対策 ログイン回数に制限を設ける 3回ログインに失敗したらログインできない、などの アカウントロック機能 を併用する 失敗が続いたときはアクセス元の IPアドレス からのログインを不可 とする 不正アクセス 対策 ログイン時にメール通知 する 不正アクセス を判別するためのログを記録する OTP不正利用対策 認証に利用したOTPを利用不可にする 特に、総当たり攻撃対策は必須と言っても過言ではないでしょう! まとめ 二段階認証を組み込むことで、セキュリティレベルを向上させることができます。 しかし、なんとなく導入してしまうと思わぬ落とし穴に気づかない可能性があります。 システムに要求される利便性とセキュリティレベルをうまく調整しましょう!
アバター
はじめに こんにちは。フロントエンド開発課に所属している新卒1年目のm_you_sanと申します。 最近話題のRemixを使って、シンプルなTodoアプリを作成する方法をご紹介します。 Todoアプリの作成を通じて、簡単な フルスタ ック開発を体験していただければと思います。 はじめに プロジェクトの作成 モデルの定義 Root Routeについて ルーティングについて 一覧画面の作成 新規追加画面の作成 編集画面の作成 削除機能の追加 まとめ プロジェクトの作成 はじめに以下のコマンドを実行して、プロジェクトを作成します。 ※Node.js v18以上、npm v7以上がインストールされていることが前提です。 npx create-remix @latest -- template remix-run/indie-stack 今回はindie-stackというテンプレートを使用しています。 このテンプレートを使用することで、プロジェクトに SQLite 、 Prisma 、tailwind css などが予めインストールされた状態になります。 ※今回はデザインはこだわらないので、tailwind css の設定は削除しています。 さらに詳しくテンプレート機能について知りたい方は、 公式のドキュメント を読んでみてください。 npm run dev を実行し、以下のような画面が表示されれば、プロジェクトの作成は完了です。 モデルの定義 次に prisma にモデルの定義をしていきます。 schema.prisma を開いて、最終行に以下のコードを追加してください。 model Todo { id String @id @default(cuid()) title String deadline String isDone Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } 保存した後、以下のコマンドで マイグレーション を実行します。 npx prisma migrate dev --name "create todo model" 次に/modelsの直下に todo.server.ts を作成します。 todo.server.ts では、バックエンドのデータを操作するための関数をまとめています。 // todo.server.ts export const getTodos = () => { return prisma.todo.findMany (); } ; export const getTodo = ( id: string ) => { return prisma.todo.findUnique ( { where: { id } } ); } ; export const createTodo = ( todo: Pick < Todo , "title" | "deadline" >) => { return prisma.todo.create ( { data: todo } ); } ; export const updateTodo = ( id: string , todo: Pick < Todo , "title" | "deadline" | "isDone" >, ) => { return prisma.todo.update ( { where: { id } , data: todo , } ); } ; export const deleteTodo = ( id: string ) => { return prisma.todo. delete( { where: { id } } ); } ; Root Routeについて 画面を作成する前に、Root Routeについて簡単に解説したいと思います。 Root Routeは、/appにある root.tsx のことを指しています。 root.tsx は、アプリケーション全体の共通設定やヘッダー、フッターといった共通のレイアウトを定義することができます。 また、 root.tsx はアプリケーションのトッ プレベ ルで定義され、他のページの親 コンポーネント となっています。 root.tsx の子 コンポーネント は <Outlet /> 内で レンダリング されます。 // root.tsx export default function App () { return ( < html lang = "en" > < head > < meta charSet = "utf-8" / > < meta name = "viewport" content = "width=device-width,initial-scale=1" / > < Meta / > < Links / > < /head > < body > { /* ここで子がレンダリングされます */ } < Outlet / > < ScrollRestoration / > < Scripts / > < LiveReload / > < /body > < /html > ); } ルーティングについて Remixでは、 ファイルシステム ベースのルーティングになっており、app/routes配下に置いたファイルがそのままアプリケーションのURLとなります。 また、動的セグメントを利用したい場合は、 $ をファイル名に付けます。 例 ファイル名 URL _index. tsx / todos._index. tsx /todos todos.create. tsx /todos/create todos.$todoId.edit. tsx /todos/{todoId}/edit 一覧画面の作成 Todo一覧画面を作成していきます。 まず、トップページの方を修正していきます。 // _index.tsx export default function Index () { return ( < main > < h2 > トップページ < /h2 > < Link to = "todos" > Todo一覧画面へ < /Link > < /main > ); } 次に/routes直下に todos._index.tsx を作成します。 // todos._index.tsx export const loader = async () => { return json ( { todos: await getTodos () } ); } ; const Todos: FC = () => { const { todos } = useLoaderData <typeof loader >(); return ( <> < h2 > Todo一覧画面 < /h2 > < ul > { todos.map (( todo ) => ( < li key = { todo.id } > < p > タイトル: { todo.title } < /p > < p > 期限: { todo.deadline } < /p > < p > 進捗: { todo.isDone ? "完了" : "未着手" } < /p > < /li > )) } < /ul > < / > ); } ; export default Todos ; loader 関数は、データ取得する際に使用するバックエンド API の役割をしています。 この関数はサーバサイドで呼ばれ、先程 todo.server.ts で定義した getTodos で取得したデータを返却します。 コンポーネント 内で定義されている useLoaderData は、 loader 関数で返却されるデータを取得するためのhooksです。 今回は useLoaderData でtodoを全件取得し、map関数で展開して、一覧表示させています。 この時点では、todoが登録されていないため、何も表示されません。 新規追加画面の作成 /routes直下に todos.create.tsx を作成します。 // todos.create.tsx export const action = async ( { request } : ActionFunctionArgs ) => { const formData = await request.formData (); const title = formData. get( "title" ); const deadline = formData. get( "deadline" ); const errors = { title: title ? null : "タイトルは必須です" , deadline: deadline ? null : "期限は必須です" , } ; const hasErrors = Object .values ( errors ) .some (( errorMessage ) => errorMessage ); if ( hasErrors ) return json ( errors ); invariant (typeof title === "string" ); invariant (typeof deadline === "string" ); await createTodo ( { title , deadline } ); return redirect ( "/todos" ); } ; const CreateTodo: FC = () => { const errors = useActionData <typeof action >(); return ( <> < h2 > 新規追加画面 < /h2 > < Form method = "post" > < div > < label > タイトル: { errors?.title ? ( < p style = {{ color: "red" }} > { errors.title } < /p > ) : null } < input type= "text" name = "title" / > < /label > < /div > < div > < label > 期限: { errors?.deadline ? ( < p style = {{ color: "red" }} > { errors.deadline } < /p > ) : null } < input type= "date" name = "deadline" / > < /label > < /div > < div > < button type= "submit" > 登録 < /button > < /div > < /Form > < / > ); } ; export default CreateTodo ; 一覧画面と異なり loader 関数ではなく、 action 関数を使用しています。 loader 関数がデータの取得に対して、 action 関数は、データの更新やサーバーへのリク エス トなどを実行するバックエンドの API の役割をしています。 loader 関数と同じくサーバサイドで呼ばれます。 action 関数内では、まず formData.get でフォームから送られてきた title と deadline を取得しています。 ※ action 関数内で使用している formData や request は Web標準 の API です。詳細は MDN を参照してみてください。 title と deadline が未入力だった場合は、エラーメッセージを返却します。 invariant は引数の内の条件式がfalseだった場合、例外を投げて処理を中断します。今回は、 title と deadline の型情報を絞り込むために使用しています。 登録完了後は、一覧画面にリダイレクトするようにしています。 コンポーネント 側では、 useActionData で action 関数から返却されるデータを受け取っています。エラーがあった場合は、エラーメッセージをそれぞれ、ラベルの下に表示するようにしています。 次に一覧画面から新規追加画面に遷移できるようにします。 Remixの Link コンポーネント は、React Routerの Link コンポーネント をラップしたものなので、 相対パス を適用できます。そのため、 to="/todos/create" ではなく、 to="create" と記載するだけで新規追加画面へ遷移することが可能です。 // todos._index.tsx ・・・中略・・・ < h2 > Todo一覧画面 < /h2 > < Link to = "create" > 新規追加画面へ < /Link > ・・・中略・・・ 最後に動作を確認してみます。 登録完了後、一覧画面に遷移されて登録したtodoが表示されていると思います。 新規追加画面 登録完了後の一覧画面 編集画面の作成 /routes直下に todos.$todoId.edit.tsx を作成します。 // todos.$todoId.edit.tsx export const loader = async ( { params } : LoaderFunctionArgs ) => { invariant ( params.todoId ); const todo = await getTodo ( params.todoId ); invariant ( todo ); return json ( { todo } ); } ; export const action = async ( { params , request } : ActionFunctionArgs ) => { const formData = await request.formData (); const title = formData. get( "title" ); const deadline = formData. get( "deadline" ); const isDone = formData. get( "isDone" ) === "done" ; const errors = { title: title ? null : "タイトルは必須です" , deadline: deadline ? null : "期限は必須です" , } ; const hasErrors = Object .values ( errors ) .some (( errorMessage ) => errorMessage ); if ( hasErrors ) return json ( errors ); invariant ( params.todoId ); invariant (typeof title === "string" ); invariant (typeof deadline === "string" ); await updateTodo ( params.todoId , { title , deadline , isDone } ); return redirect ( "/todos" ); } ; const EditTodo: FC = () => { const { todo } = useLoaderData <typeof loader >(); const errors = useActionData <typeof action >(); return ( <> < h2 > 編集画面 < /h2 > < Form method = "POST" > < div > < label > タイトル: { errors?.title ? ( < p style = {{ color: "red" }} > { errors.title } < /p > ) : null } < input type= "text" name = "title" defaultValue = { todo.title } / > < /label > < /div > < div > < label > 期限: { errors?.deadline ? ( < p style = {{ color: "red" }} > { errors.deadline } < /p > ) : null } < input type= "date" name = "deadline" defaultValue = { todo.deadline } / > < /label > < /div > < div > < input id = "done" type= "radio" name = "isDone" value = "done" defaultChecked = { todo.isDone } / > < label htmlFor = "done" > 完了 < /label > < input id = "notDone" type= "radio" name = "isDone" value = "notDone" defaultChecked = { ! todo.isDone } / > < label htmlFor = "notDone" > 未着手 < /label > < /div > < div > < button type= "submit" > 更新 < /button > < /div > < /Form > < / > ); } ; export default EditTodo ; 編集画面では、 loader 関数を使って動的セグメントで指定した todoId を取得して、それに対応するtodoを取得しています。 更新リク エス トにも、 todoId が必要なので、 action 関数の方でも todoId を取得しています。その他の処理は、新規追加画面の action 関数とほぼ同じです。 useLoaderData でデータを取得する流れは一覧画面と同様で、フォームのデフォルト値に取得したデータを設定しています。 編集画面は完成したので、一覧画面から遷移できるようにします。 // todos._index.tsx ・・・中略・・・ < li key = { todo.id } > { /* タイトルから編集画面に遷移できるようにする */ } < Link to = { ` ${ todo.id } /edit` } > タイトル: { todo.title } < /Link > ・・・中略・・・ 最後に動作を確認してみます。 一覧画面(todo更新前) 編集画面 一覧画面(todo更新後) 削除機能の追加 最後に削除機能を追加します。 /routes直下に todos.$todoId.delete.ts を作成します。 // todos.$todoId.delete.ts export const action = async ( { params } : ActionFunctionArgs ) => { invariant ( params.todoId ); await deleteTodo ( params.todoId ); return redirect ( "/todos" ); } ; action 関数内では、動的セグメントで指定した todoId を取得して、それに対応するtodoを削除しています。削除完了後は、一覧画面にリダイレクトします。 一覧画面からtodoを削除できるようにしたいので、一覧画面に削除ボタンを追加して、押下時に /todos/{todoId}/delete にリク エス トを送るようにします。 // todos._index.tsx ・・・中略・・・ < p > 進捗: { todo.isDone ? "完了" : "未着手" } < /p > < Form action = { ` ${ todo.id } /delete` } method = "post" > < button type= "submit" > 削除 < /button > < /Form > ・・・中略・・・ 削除ボタンを Form コンポーネント で囲って、 action にパスを指定しています。これにより、ボタン押下時に todos.$todoId.delete.ts の action 関数が呼び出されます。 なお、 Form コンポーネント の action を省略した場合は、同ファイル内の action 関数が呼ばれます。 動作を確認して、削除ボタン押下時に一覧画面からtodoが削除されていれば、実装完了です。 まとめ 今回はRemixで簡単なTodoアプリを作成する方法について、紹介させていただきました。 Todoアプリの作成を通じて、フロントエンドとサーバーサイドのコードを同時に扱う体験をしていただけたと思います。 また、この記事がRemixをこれから学ぼうとする方々の理解を深める助けになれば幸いです。 私自身もまだRemix初学者なので、引き続き知識を深めていきたいと考えています!
アバター
弊社で毎月開催し、 PHP エンジニアの間で好評いただいている PHP TechCafe。 2023年5月のイベントでは「型定義」について語り合いました。 弊社のメンバーが事前にまとめてきた情報にしたがって、他の参加者に意見を頂いて語り合いながら学びました。 今回はその内容についてレポートします。 rakus.connpass.com PHPと型 静的型付け言語 動的型付け言語 一般的な誤解 PHPの型 単一の式が持つ型 型システムで扱える型 never型について void型について self,parent,static型について resource型について evalでresource型を宣言すると リテラル型について ユーザー定義型について 複合型について 型のエイリアス mixed iterable PHPで取り入れられた型表現 型宣言のメリット PHPの歴史を振り返る PHPのドキュメントを見ると、Nullの表現が3種類ある PHPの歴史的な理由 implod()の歴史的な理由 PHPマニュアル 日本語版で「歴史的」を検索したらたくさん出てくる PHPDocの役割 メリット デメリット 言語仕様の型表現とPHPDocのどちらを使うべき? 事前にいただいた質問コーナー never型(noreturn型)のうまい使い所、(一部過激派による)ジェネリクス待望論などあれば聞きたいです テストコードは静的解析の対象に入れるべきか否か 型運用のための設計についてお聞きしたいです。(DTOクラスはPHPでも使うものなのかなど) まとめ PHP と型 静的型付け言語 変数や関数の引数、戻り値などに対してあらかじめ指定された型以外を使用できなくする言語です。例としては Java があります。 入力や出力を縛る言語です。 型推論 という機能で、明示的に型を指定しなくても文脈などから コンパイラ が予測してくれるというパターンもあります。 動的型付け言語 変数や関数の引数、戻り値の型を実行時の値によって決定することが出来る言語です。 PHP はこちらに分類されます。 ただ PHP のように型を指定できる動的型付け言語もあります。 一般的な誤解 「 コンパイラ 型は静的型付け言語である」や「 インタープリタ ー型は動的型付け言語である」というのは間違いです。 静的型付け言語は コンパイル 時に変数や関数の型が確定されます。一方、動的型付け言語では実行時に型の決定を行うことができます。 「 PHP は型宣言できるので定義上は静的型付け言語だと言えてしまう。値の持ち方は動的型付け言語そのものなんですが」というコメントもありました。 PHP の型 単一の式が持つ型 null bool int float string array object callable resource 型システムで扱える型 組み込みの型 ヌル(null) スカラー 型: 論理値(bool) 整数(int) 浮動小数点数 (float) 文字列(string) 配列(array) オブジェクト(object) リソース(resource) never void クラス内での関係を示す相対型: self, parent, static リテラル 型 false true ユーザー定義型(一般的に、クラス型とも呼びます) インターフェース クラス 列挙型( Enum ) callable never型について neverは8.1以降で使えるようになった、関数が戻ってこないことを示す戻り値の型です。 exitがコールされるか、例外がスローされるか、無限ループに入るかのいずれかであることを意味します。 使い方としてはneverや@return neverで書いた関数の直後のコードが絶対実行されないとPHPStanが警告してくれるのでどこかでexitしてしまうようなコードに「exitするから気をつけろよ」ということを示すことができます。 クラスにしたときに「この関数を呼んだら終わります」と明記できるという意見がありました。 void型について 関数が値を返さないことを示す型です。 使いどころとして以下のような意見、コメントがありました。 「voidは重要、副作用があることを暗示してくれる」 「事故防止のため、コードに密結合したところに付箋を貼れる意味がある」 「voidが呼ばれていると中でなにかやっているぞ、なにか更新しているぞってなりますよね」 「純粋なコードとそうじゃないものを色分けできる」 self,parent,static型について self,parent,staticも型にあたります。返り値の型に宣言することができます。 「よく自分自身を返すときには使います」という意見がありました。 resource型について resource型は型宣言には記述できません。 evalでresource型を宣言すると 関数のパラメータにresource型を宣言するとバージョン8.0以降では「サポートされている組み込み型ではないため、クラス名として解釈されます。」というWarningが出ます。 「PG系の関数やファイル系の拡張関数がresourceではなく新しいクラスを返すようになる変更があり、元々resource型だったため型宣言が上手く出来ないからという経緯があるのでは」という意見がありました。 またバージョンによってエラー内容は異なります。 リテラル 型について false型とtrue型です。falseが返ることを宣言できます。 8.0.0でfalse型、8.2.0でtrue型がサポートされました。 8.2.0以前では、false型はunion型の一部としてのみ使える型でした。 以下の意見がありました。 「false型が出てきた背景は、 PHP の標準関数のなかにfalseを返すものが多かったため」 「trueは一応成功してくれればそのまま、失敗はエラー処理にできる」 「例外を使えという話ではあるが、レガシー向け対応」 ユーザー定義型について 一般的にクラス型と呼ばれます。 Enum もここに含まれます。 複合型について 交差型とUnion型。 交差型を構成する個別の型は & でつなぎます。Unionは | でつなぎます。 以下の意見がありました。 「Union型は引数や戻り値で指定する時によく使うかなと思います。」 「交差型を使う例として、インターフェースでイベントを受け取るんですが、実装しているどのイベントなのかっていうのをメソッドの中でチェックしなければいけない……という時に、&で繋いでいけばメソッドの中でチェックする処理を書かずに済むというやり方もあります。」 「型を指定しておけば、与えられた引数のチェックをしなくてよくなるっていうのがいいですね」 &で繋ぐ書き方の例としてはこちらの記事が参考になります。 doganoo.medium.com 型の エイリアス mixed mixedはobject | resource | array | string | float | int | bool | nullの エイリアス です。 五年前くらいに会社のチャットで「mixedって意味あんのかよ、レガシーをそのまま放置しているだけじゃないか」という意見があり、 「いやいやラベル付けのためにはいるんじゃない?」や「どうしてもここはmixedにするしかなかったって意思表示ができる」 という話をしていました。 以下の意見・コメントがありました。 「ここにresource入るんですね。 エイリアス と言いつつ型宣言出来ないじゃん!」 「ここは分からないからmixedにする、頑張ったけどって意味づけも出来るかなと」 「型がなかったプロジェクトにあとから型をつける場合に、mixedに逃げる形になっています」 「どうしようもなかったってラベリングには使えそうですね」 「is_mixed()それはTrueしか返らない」 iterable Traversable | arrayの エイリアス です。 foreachで回せます。 PHP で取り入れられた型表現 そもそもですが PHP は元々型表現があってないような動的なものでしたが、徐々に指定できるように取り入れられました。 例えば戻り値に指定しますよとか、引数に宣言できるようになりましたよというところ。 7.1.0まで遡ると、nullableという型が追加、voidが追加と徐々に増えています。 8.0.0でUnionとmixedが出来るようになりました。PHPerKaigiでmixedステッカーがありましたね。 8.1.0で戻り値にのみ指定できる型として、never型が追加されました。そして戻り値をvoidとした関数からリファレンスを返すことが非推奨になりました。交差型が追加されました。 8.2.0でnullとfalse型が独立して使えるようになり、trueが追加されました。DNF型も追加されました。DNF型とはANDとORの組み合わせを使えるものです。 以下の意見がありました。 「unionがなかったのでmixedで書かれているものがあった」 「unionのおかげでmixedってどうしても書かなきゃいけないシーンは減ったかもしれません」 型宣言のメリット 作った関数が誤った意図で使われることを防げる。 受け取った値のチェックを型宣言に任せられる。 毎回is_arrayでチェックしなくてよい。 PHP の歴史を振り返る PHP のドキュメントを見ると、Nullの表現が3種類ある 「null」と「Null」と「NULL」とあって、文脈上なんかあるのかなと思ったんですが、英語の文法上、先頭にいると大文字になってそれがそのまま翻訳されていると考えられます。 歴史的経緯と翻訳の都合。 PHP の歴史的な理由 以下のスライドで紹介されています。 お前は PHP の歴史的な理由の数を覚えているのか from Kousuke Ebihara www.slideshare.net implode() urlencode()/rawurlencode() double型/float型とgettypeの返り値 Phar アーカイブ の マニフェスト 情報 Zend EngineのHashTable implod()の歴史的な理由 inplodeは歴史的な理由により引数をどちらの順番でも受け付けることができます。 PHP2から3の間に生まれ、PHP3.0a3にて、第一引数と第二引数を交換したので、splitと同じように動作するようになりました。 わずか3ヶ月の歴史。 PHP マニュアル 日本語版で「歴史的」を検索したらたくさん出てくる Code search results · GitHub 検索でヒットしたのは12箇所。 以下の記述がありました。 「歴史的な理由によりfloatの場合は"float"ではなく、"double"が返されます」 「gettypeは歴史的な理由で決まった型の名前を返します。この関数はそれよりも実際に使われている型により近い名前を返すという点が異なります」 皆さん歴史的なものがほしいときはgettype()を、そうでないときはget_debug_type()を使いましょう。 PHPDocの役割 型宣言と切っても切り離せないのがPHPDocです。 PHP の ソースコード に記載するコメントです。 実装者がコードの内容を確認できるだけでなく、ルールに従って記載することで IDE や静的解析などのツールで活用することができます。 下記を参考にしていました。 PHPDoc リファレンス — phpDocumentor メリット 静的解析に使えること、コメントなので実際の挙動に影響を与えない。 なのでバグにならないことが保証されている。 リファクタリング でとっつきやすい。 以下資料を参考 speakerdeck.com デメリット 所詮はコメントなので、内容が間違っていても動く。 片方が書かれていない、書いてあるけど型の指定が無い、実際はstring型なのにコメントはint型になっているなど。 以下の意見がありました。 「書かれている内容が間違っている可能性があるのは問題ですね」 「 PHP を長年愛用されているレガシーの皆様にとっては、それでも IDE で縛れるのがいいのかなと思います」 「サボらずにDocもメンテしましょう」 言語仕様の型表現とPHPDocのどちらを使うべき? 以下のような意見・コメントがありました。 「新規プロダクトと既存プロダクトで変わってくる」 「新規プロダクトでは型宣言は必須。既存ならPHPDoc」 「ビジネス上でスピードを求められるならとりあえず無しで作る戦略はアリなのかな。ところが後々困るのは自分だよな......」 「既存もテストを書いて型宣言しよう」 「手戻りのバグ調査の 工数 って見えにくいですからね。既存は入出力が空のところが多いからリスクが大きすぎる」 「昔から型は口ほどに物を言うと言いますからね」 「スピードを求めるからこそ型宣言」 「既存のプロダクトに型をつけるのは危険過ぎる。テストがしっかりと出来ているなら問題ないんですが...」 「私は実際プロダクトで型をつけてすっごく変なルートで入ってきて落ちるというのを何度も見ましたし」 「複雑なデータは型を検討していると先が見えてくるのでそういう意味で重要ですね」 この方法ならレガシーにもテストを書けますという記事を共有いただきました。 qiita.com スクレイピング によるテストです。この方法なら スクレイピング してこれがあるかどうかというものなら簡単にかけます。 「既存のプロダクトは頑張って先程の方法か、無難に PHPUnit に書くのか、頑張ってテストに型をガシガシ書くのか、PHPDocから攻めるのか」 「テストを書く時間がないときはスピードを求めるからこそ型宣言に立ち返りましょう」 事前にいただいた質問コーナー never型(noreturn型)のうまい使い所、(一部過激派による) ジェネリクス 待望論などあれば聞きたいです リダイレクトしてしまう場合。レガシーコードのときに付けるなど。 ジェネリクス 待望論についてはarray_shapeを使いましょうという結論です。 PhpStormを利用していればPHPDocの中にAttributesで中のコードを書くことで静的解析のように利用できます。 また、言語組み込みのジェンネリクスは実行速度に影響があるため不要であり、PHPDocで書けば十分ということでした。 エディタが警告を出してもらうことで問題はないのでarray_shapeのような静的解析でよいのではないでしょうか。 テストコードは静的解析の対象に入れるべきか否か 静的解析の対象です。入れてもデメリットが無いと思います。 ただし、静的解析ツールからの警告やエラーメッセージに対してイライラしてしまうことがあるかも知れません。 また、データプロバイダ(テストデータを提供するメソッドや機能のこと)には多くの場合、複数のパラメータが必要となり、これらのパラメータに適切な型を指定するためにPHPDocのコメントを書くことが推奨されています。 しかし、これにより PHPUnit のコメントが長大になってしまい逆に読みにくくなるという点はデメリットと言えるかも知れません。 以下のようなコメントがありました。 「たくさんのパラメータが必要なデータプロバイダこそ複雑なことをしている自覚を持ってちゃんとした型を書いてほしい」 型運用のための設計についてお聞きしたいです。( DTO クラスは PHP でも使うものなのかなど) スピードと質を求めるなら型はしっかり設計しておくべきです。 以下のコメントと意見がありました。 「最近はCopilotである程度いい感じに補完してくれるので DTO を書きやすくなっている」 「生存期間が短いものは配列でいい、旅をするものは DTO にしてほしい」 「Readonly出来るようになったから DTO しやすくなった」 「製品コードやテストコードで実行する解析やルールを変えることがあるので、個人的には製品コードとテストコードで同等の静的解析をするかどうかが問題」 まとめ 今回は PHP の型について参加者の方々にそれぞれの型の特徴や使い所、歴史的背景などについて語り合っていただきました。 PHP は型指定ができる動的型付け言語という独特な言語であり、長い歴史からくる仕様などの事情が語られていてとても勉強になりました。 PHP の型について理解をより一層深めることができたのではないでしょうか。 「 PHP TechCafe」では今後も PHP に関する様々なテーマのイベントを企画していきます。 皆さまのご参加をお待ちしております。
アバター
はじめまして、rks_rtnkです。 ラク スでは毎年、 「Rakus Tech Lab」という チャット アプリ開発 体験を行うエンジニア インターン を開催しています。 2023年も4回開催しまして、非常に多くの学生の皆さんに参加いただきました。 今年、運営に携わった私から、2023年の インターン を振り返りつつ、紹介させていただきます。 もくじ 紹介 タイムスケジュール 開発の流れ 成果発表・懇親会 参加者の声 まとめ・所感 終わりに 紹介 まずはこの インターン 「Rakus Tech Lab」について、紹介させてください。 Rakus Tech Labは、2014年から開催しており、2023年にちょうど10年目を迎えました。 2023年の参加者を含めると、これまでに 600人以上 の学生に参加いただいており、 東京大阪あわせて20回以上開催している インターン になります。 当社に入社した新卒社員のうち、 6割以上 がRakus Tech Labの参加者となっており、 ラク スとしても採用のために、力をいれている施策の一つでもあります。 ただ、この インターン を開催するに至った経緯(目的)は、当時も今も変わらず、 ・ リアルな仕事への理解 ・ ラク スの風土を理解 ・ お互いの理解 ・ 体験を通じた「お互い」の成長 以上の4点です。 縁あって皆さんに ラク スに入社したいと思っていただけることが、何よりの喜びではありますが、 ぜひ参加される皆さんには、 ・ 実際の開発業務の雰囲気、実際のオフィスで働くイメージを感じる ・ ラク スの社員にどんな人がいるのか知る ・ 就活サイトなどでは聞けない生の社員の声を聞く といった、参加者だからこそ得られる機会を大切にしていただきたいと思っています。 ラク スとしても、学生の皆さんにそうした価値をたくさん提供できるように努めております。 「 ラク スのことそんなに知らない…」という学生の方も、この インターン 中に様々なことをお伝えする機会を設けていますので、 ぜひ、本記事と こちら から詳細を確認いただき、ご参加いただければと思います…! タイムスケジュール (前置きが長くなってしまいましたが・・・)2023年度のRakus Tech Labは、以下の内容で実施しました。 ※内容やスケジュールは毎年変更されますのでご了承ください ・ 東京3回、大阪1回 各回5日間×3時間 ・ 4名前後で1チームとなり、グループでオリジナルのチャットアプリを作成 ・ チームごとに ラク スの若手エンジニア1名ずつが付き添い、講師としてサポート 5日間の流れとしては、まずは初日に チュートリアル で開発環境に慣れていただいて、2日目から本格的な開発を行いました。 最終日には、完成したチャットアプリのお披露目として、成果発表を行っていただきました。 開発体験以外にも、1~3日目の冒頭には、開発統括部長、製品開発課長、若手エンジニア(新卒1~3年目)が それぞれの立場から、業務や製品、 ラク スについて紹介する時間を設けました。 ラク スのことを知っていただきたいというだけではなく、 インターン 中のチーム開発に役立つ話をしたり、IT・ SaaS 業界について理解を深めてもらえれば、という思いで実施しました。 開発の流れ チュートリアル  ⇒ 基本要件 ⇒ テーマ決め ⇒ 追加要件 の流れで進行しました。 学生の皆さんがスムーズに開発に入れるように、開発環境、原形となる ソースコード はこちらで用意したものを使用しました。 AWS 上に開発環境を構築して、バックエンドは Node.js 、フロントエンドは Vue.js で構成しました。 各チームごとに分かれ、 チュートリアル を通して基本的な進め方を学んでいき、 その後、こちらで定めた共通の基本要件を満たすように、チャット アプリ開発 を行ってもらいました。 <基本要件の例>  ・ログイン時、ユーザ名未入力の場合は、エラーダイアログを表示する。  ・投稿したら、自分以外のクライアントにも投稿文を表示させる。  ・新しい投稿は画面上部に表示されるようにして、古い投稿は下部に流れるようにする。 基本要件の開発が終わったら、追加機能の実装に進みます。 追加機能についても、こちらで用意した要件から選ぶこともできますが、 基本的には、各チームでチャットアプリのテーマを決めてもらい、それに合う独自機能を実装してもらいました。 チャットアプリというベースはあるものの、1からテーマを考えて、機能を考えていくことに、 皆さん初めは戸惑っている様子でしたが、議論が進み始めると、いくつも候補が出てきてしまい、絞り込むのに苦労している様子でした。 講師担当のエンジニアも、チームごとに1名ずつサポートとして付き、 何かトラブルや困りごとがあればすぐに相談できる体制を整えていたので、行き詰まることもなく、 どのチームも活発な議論を交わしながら、楽しみつつ、取り組んでいただけたのではないかと思います。 成果発表・懇親会 最終日には、各チームが作ったチャットアプリのお披露目として、成果発表を行いました。 ビジネスシーンを想定したテーマをもとに作成した、投稿に「報告」「連絡」「相談」の種別を持たせるビジネス向けチャットアプリ すでに製品化されているような機能性を短期間で実現した、ChatGPTを利用して入力支援を行ってくれるチャットアプリ など、学生の皆さんがユーザ目線になってどのように工夫を凝らしたのか、どのようなターゲットに向けて開発をしたのか、色々な思いを聞かせていただきつつ、完成したチャットアプリを見ることができて、私自身とても参考になりました。 また、講師を務めたエンジニアからも、「自分が学生だった頃はこんなに出来なかった。レベルが高い」と驚きの声が挙がっていました。 ※成果発表の様子※ 成果発表の後は、講師以外の社員も招いて懇親会を行いました。 コロナによる制限もなくなり、数年ぶりに飲食しながら学生の皆さんと社員で交流を行うことができました。 業務に関する質問やプライベートの過ごし方など、様々な話で盛り上がりを見せ、1時間の懇親会があっという間に終わってしまいました。 ※懇親会の様子※ 参加者の声 参加いただいた学生の皆さんにアンケートを実施しました。 Rakus Tech Labの満足度としては、全4回の平均で、 100点満点中 92.5点 と、我々としても非常に満足な結果(点数)でした。 良い意見だけでなく、自身の取り組みが不足していた…と悔しさを滲ませる感想もありましたが、 皆さん、RakusTechLabの目的でもある 仕事への理解 ラク スの風土を理解 お互いの理解 体験を通じた「お互い」の成長 をしっかりと達成することができたようで、満足度の高得点以上にその点が嬉しかったです。 そのアンケートの回答を一部抜粋して、紹介させていただきます。 チーム開発の雰囲気や ラク スの社風や社員の雰囲気などを掴むことができました。また企業説明や若手社員の新人時代の経験など他では聞けないことが多く、大変勉強になりました。自分が ラク スで働く社員となったときのキャリアビジョンやイメージを掴むことができました。 他社の インターン と異なり、 インターン 中に説明会のようなものや懇親会が開催があり、会社への理解度がとても高まった。他の インターン では得られないやりがいや知識や達成感を得ることができた。 オフラインでの実施だったので会社の雰囲気がよくわかった。特に作業スペースの横では実際にミーティングをしていたり、1人の作業場で集中しているところも見れて、かなり会社についてわかった。 RAKUS Tech Labを通じて単純に技術だけででなく、メンターの方やメンバーとのコミュニケーションについても学びが多くありました(むしろ後者の方が多かったかもしれないです) 以上のように、開発体験だけでなく、多くのことを感じて、学びになったとの声を頂けて、非常に嬉しい限りです。 まとめ・所感 参加学生の中で、個人開発の経験はあるという方は多くいますが、チーム開発経験があるという学生の方はそれほど多くないので、 開発中は、チーム開発特有の問題であるGitの使い方、コンフリクト、開発体制、分担で苦労している様子でした。 講師としてサポートしている若手エンジニアも、あまり干渉しすぎないようにしていますが、 学生の皆さん同士で対処できない場合には、指針を示したり、いくつか選択肢を与える等して、 「導く」ことに重きを置いてサポートしていたのがとても印象的でした。 学生の皆さんもそうした課題やトラブルに向き合うことを苦にせず、乗り越える楽しみを感じながら、取り組んでいる様子でした。 そうしたチーム開発ならではの楽しさや難しさを経験していただく、という部分が本 インターン の醍醐味ではあるのですが、今年は他の部分においても、何か学生の皆さんにとってプラスになる取り組みができないかと考え、「学生×講師の1on1」を実験的に取り入れました。 ラク スでも日常的に行っている1on1を体験いただくために、講師と学生が1対1で会話する機会を設けました。 実施前はあまり盛り上がらずに終わってしまったらどうしようか…と心配していましたが、 いざ始まると、終了時間になっても話が止まらず、時間オーバーしてしまう組が多数出る状況でした。 1対1になることで、他の人がいる場だと聞きづらい質問や相談が活発に出ていたそうで、学生の皆さんからも以下のようなお声を頂きました。 「普段の働き方やエンジニアのキャリアについてなど、現場のエンジニアの方に質問できて非常に参考になった」 「就職活動の悩みに対して、現場のエンジニアとして働かれている率直な意見を聞くことができた」 「会社の雰囲気や評価制度などかなり踏み込んだ話ができてとても参考になった」 チーム開発に取り組んでいると、開発作業優先になってしまうという部分もあるかと思うのですが、 開発体験から少し離れて、1on1という時間を設けることで、学生の皆さんが自身の考えを整理したり、 就活の不安や疑問、 ラク スの実情を聞くことができる良い機会になったようで、発案者として安堵しました。 (運営として、時間管理をもっとしなければ・・・という点は今後の反省です。。。) 終わりに 2023年度、Rakus Tech Labへご参加いただき、本当にありがとうございました。 今回のチーム開発を経て、何か少しでも得るものがあったとしたら幸いです。 私としても、学生の皆さんが意欲的に自ら学び、吸収して、アウトプットしていく様子を目の当たりにして、大きな刺激を受ける良い機会になりました。 改めまして、ありがとうございました。ご参加いただいた皆さんのさらなる活躍を期待しております! ラク スではエンジニア インターン 「Rakus Tech Lab」を東京および大阪で、毎年実施しています。 毎年春(3月~4月)頃から、エントリーを受け付けしておりますので、ご興味ある学生の方は、 ぜひ下記サイトより、エントリーいただければと思います。 ※2024年度 インターン (26卒対象)は 2024/3/1 にエントリー開始いたしました! fresh-recruit.rakus.co.jp
アバター
こんにちは!新卒1年目のos188です。 私が担当する商材は、リリースから10年以上が経過し、膨大な量の ソースコード が存在します。 大部分は オブジェクト指向 プログラミングで書かれていますが、 コードを読んで勉強しているとき、古い部分で手続き型プログラミングによって書かれているところを見つけました。 新しい部分と比較すると「読みづらいな、処理を追いかけにくいな」と感じることが多く、 大規模な ソースコード だとこんなにも差が出るのかと感心しました。 今回は、手続き型プログラミングを大きなプロジェクトや複雑な処理に適用した際のやりづらさと、 オブジェクト指向 プログラミングによる解決策について説明します。 手続き型のやりづらさ 1. データの変更が処理に影響を与えやすい カプセル化する 2. コードの重複が発生しやすい 継承とポリモーフィズムを用いる 3. データと処理が分離される クラスで管理する まとめ 手続き型のやりづらさ データの変更が処理に影響を与えやすい コードの重複が発生しやすい データと処理が分離される サンプルコードを用いてそれぞれ具体的に説明していきます。 1. データの変更が処理に影響を与えやすい 手続き型プログラミングでは、データの変更が処理に影響を与える可能性が高いです。 関数によるデータ参照がバグを生みやすく、特に大規模なプログラムでは変数名の衝突や意図しない変更が起こりやすいです。 以下の例では、 グローバル変数 が別の関数によって予期せず書き換えられています。 <?php $ tax_rate = 0.10 ; // 税率がグローバル変数 function calculate_total_price ( $ price ) { global $ tax_rate ; // 税率を取得する return $ price + ( $ price * $ tax_rate ) ; // 税込み価格を返す } function calculate_reduced_tax_price ( $ price ) { global $ tax_rate ; // 税率を取得する $ tax_rate = 0.08 ; // 税率が上書きされた…! return $ price + ( $ price * $ tax_rate ) ; // 税込み価格を返す } echo calculate_total_price ( 100 ) ; // "110" echo calculate_reduced_tax_price ( 100 ) ; // "108" echo calculate_total_price ( 100 ) ; // "108" (税率10%のつもりが…) カプセル化 する オブジェクト指向 では、 カプセル化 によってこの問題を解決できます。 クラス内のデータが隠蔽されており外部からアクセスできないため、書き換えられることがありません。 <?php private $ taxRate ; // 税率が外部からアクセスできない! public function __construct ( $ taxRate ) { $ this -> taxRate = $ taxRate ; } public function calculateTotalPrice ( $ price ) { return $ price + ( $ price * $ this -> taxRate ) ; // 税込み価格を返す } } $ taxCalculator = new TaxCalculator ( 0.10 ) ; // 税率10%のインスタンスを作成 echo $ taxCalculator -> calculateTotalPrice ( 100 ) ; // "110" $ reducedTaxCalculator = new TaxCalculator ( 0.08 ) ; // 軽減税率用のインスタンスを作成 echo $ reducedTaxCalculator -> calculateTotalPrice ( 100 ) ; // "108" echo $ taxCalculator -> calculateTotalPrice ( 100 ) ; // "110" (税率10%のまま!) 2. コードの重複が発生しやすい 手続き型プログラミングでは、コードをモジュール化して再利用することが難しいため、コードの重複が発生しやすくなります。 以下の例では、ほぼ同じ処理が書かれていたり、同じ関数を何度も呼び出しています。 これは非常に簡単な例ですが、複雑な処理が多くなるとどうしても重複が発生します。 <?php function bark_dog () { echo "犬がワンと鳴いた \n " ; } // 似たような処理… function meow_cat () { echo "猫がニャーと鳴いた \n " ; } // 何度も同じ関数を呼んでいる bark_dog () ; // "犬がワンと鳴いた" meow_cat () ; // "猫がニャーと鳴いた" bark_dog () ; // "犬がワンと鳴いた" bark_dog () ; // "犬がワンと鳴いた" meow_cat () ; // "猫がニャーと鳴いた" 継承と ポリモーフィズム を用いる 一方、 オブジェクト指向 なら継承することで同じ処理を再利用することができます。 また、 ポリモーフィズム によってコードの重複を防ぐことができます。 <?php class Animal { protected $ animal ; protected $ cry ; public function __construct ( $ animal , $ cry ) { $ this -> animal = $ animal ; $ this -> cry = $ cry ; } public function cry () { echo $ this -> animal . "が" . $ this -> cry . "と鳴いた \n " ; } } class Dog extends Animal { public function __construct () { parent ::__construct ( "犬" , "ワン" ) ; } // Animalクラスを継承しているので、cry()関数を使える! } class Cat extends Animal { public function __construct () { parent ::__construct ( "猫" , "ニャー" ) ; } // Animalクラスを継承しているので、cry()関数を使える! } // ポリモーフィズムでまとめてオブジェクトを扱える! $ animals = [ new Dog () , new Cat () , new Dog () , new Dog () , new Cat ()] ; foreach ( $ animals as $ animal ) { $ animal -> cry () ; // 出力省略 (犬、猫、犬、犬、猫の順番で鳴く) } 3. データと処理が分離される 手続き型プログラミングでは、データとそれに関連する処理が分離されがちです。これにより、意図しない副作用を招く可能性があります。 以下の例では、いろいろな値を グローバル変数 で管理しており、コードの理解や変更が困難になっています。 <?php function getAdultAge ( $ date ) { if ( $ date < strtotime ( "2022-04-01" )) { return 20 ; } else { return 18 ; } } function reachBirthday ( $ name , $ age ) { // 外部の状態を変更するような関数 $ age ++ ; echo " $ name は誕生日を迎え、 $ age 歳になりました \n " ; return $ age ; } function isAdult ( $ name , $ age , $ adult_age ) { // 外部の状態に依存した関数 if ( $ age >= $ adult_age ) { echo " $ name は成人です \n " ; } else { echo " $ name は未成年です \n " ; } } $ person_name = "John" ; $ person_age = 17 ; // 成人年齢をグローバル変数で管理する必要がある $ adult_age = getAdultAge ( strtotime ( "2024-01-01" )) ; isAdult ( $ person_name , $ person_age , $ adult_age ) ; // "Johnは未成年です" // Johnの年齢もグローバル変数で管理する必要がある $ person_age = reachBirthday ( $ person_name , $ person_age ) ; // "John は誕生日を迎え、18 歳になりました" isAdult ( $ person_name , $ person_age , $ adult_age ) ; // "Johnは成人です" クラスで管理する オブジェクト指向 プログラミングでは、クラスによってデータと処理がセットで管理されます。 それぞれのデータをクラス内で管理できるので、クラスの外ではデータの状態を気にしなくて大丈夫です。 <?php class Person { private $ name ; private $ age ; // 名前と年齢はこのクラス内で管理する public function __construct ( $ name , $ age ) { $ this -> name = $ name ; $ this -> age = $ age ; } public function reachBirthday () { $ this -> age ++ ; echo " $ this -> name は誕生日を迎え、 $ this -> age 歳になりました \n " ; } public function getName () { return $ this -> name ; } public function getAge () { return $ this -> age; } } class JudgeAdult { private $ person ; private $ adult_age ; // 成人年齢の処理はこのクラス内で完結する // つまり、成人年齢の仕様に変更があってもこのクラスを見るだけで良い! public function __construct ( $ person , $ date ) { $ this -> person = $ person ; if ( $ date < strtotime ( "2022-04-01" )) { $ this -> adult_age = 20 ; } else { $ this -> adult_age = 18 ; } } public function isAdult () { if ( $ this -> person -> getAge () >= $ this -> adult_age ) { echo $ this -> person -> getName () . "は成人です \n " ; } else { echo $ this -> person -> getName () . "は未成年です \n " ; } } } $ john = new Person ( "John" , 17 ) ; $ john_judge = new JudgeAdult ( $ john , strtotime ( "2024-01-01" )) ; // 成人年齢は JudgeAdultクラスで管理するので、気にしなくて良い! $ john_judge -> isAdult () ; // "Johnは未成年です" // Johnの年齢はPersonクラスで管理するので、気にしなくて良い! $ john -> reachBirthday () ; // "John は誕生日を迎え、18 歳になりました" $ john_judge -> isAdult () ; // "Johnは成人です" まとめ 今回は、手続き型プログラミングのやりづらさと、 オブジェクト指向 プログラミングによる解決策について取り上げました。 データの変更が処理に影響を与えやすい→ カプセル化 する コードの重複が発生しやすい→ 継承と ポリモーフィズム を用いる データと処理が分離される→ クラスで管理する 全体を通して、コードの再利用性、保守性が向上したことを感じていただけたでしょうか? 大規模な開発になればなるほど、これらの影響がどんどん大きくなります。私は毎日 オブジェクト指向 プログラミングの恩恵を享受しています! 最後まで読んでいただきありがとうございました。
アバター
こんにちは、モバイル開発チームのhyoshです。 弊社では各分野の特定のテーマに沿ってエンジニアが議論する「TechCafe」というイベントを定期開催しています。 PHPTechCafe フロントエンドTechCafe そして先日私を含めた弊社モバイル開発チームが初となる「モバイルTechCafe」を開催しました! rakus.connpass.com 本ブログでは開催までの準備過程や当日の内容についてレポーティングさせていただきます。 TechCafeについて 準備編 テーマ選定 参加者選定 打ち合わせ 当日編 紹介したイベント 複雑さに立ち向かうためのコードリーディング入門 認証体験向上のためにpasskeys(パスキー)に対応する 〜 メリット・対応方法について 集まれKotlin好き!Kotlin愛好会 WebViewと向き合う Compose で Android/iOS アプリを作る Androidエンジニアが1人という不安と向き合う Japan IT Week やってみての感想 おわりに TechCafeについて 弊社TechCafeはエンジニア同士の交流の機会を提供する、エンジニアと技術が交差する憩いの場(カフェ)になれるようなイベントを目指しています。 TechCafe というイベントそのものが学びの場となり、運営メンバーも含め、参加者全員がエンジニアとしてレベルアップしていけるように支援することを目的として開催しております。 今回最初に企画が持ち上がった際には、個人でのLT等の活動経験はあるとはいえ組織的な社外向けイベントというのは経験がなく、上手くやれるのかという不安はありました。 ただ上述の目的にもあるように例え上手くできなくても開催すること自体が参加者にとって少しでも学びのきっかけとなり、チームにとっても新たな視点をもたらす糧になるのではと思い「まずはやってみよう」の精神で引き受けました。 準備編 テーマ選定 イベントの肝となるテーマを決めるにあたってはまずはチーム内でブレストを行いました。 折角なので全員で協力して成功を目指すイベントにすべく参加者に限定せず全員で行ったのですが、いざ始めると想像よりも闊達に意見が飛び交い初回は案出しだけで終わることになりました。 ブレストの結果 そして二度目の打ち合わせで候補の絞り込みを行いましたが、選定にあたっては次の観点を重視しました。 レベルを問わずより多くの参加者に新しい気付きをもたらすことができること 時間をかけすぎずに今持っている情報を使って議論ができること 何より自分たちが「楽しんで」話せる内容であること 結果として記念すべき初回テーマは 「モバイルエンジニアにおススメの技術イベントを語る」 に決めました。 参加者選定 参加者については当初は希望者のみで考えていましたが、テーマ的に特定の技術スタックが関係ある訳でもなく誰でも参加可能なのとその方がより当チームの雰囲気が伝わるのではと考え5人全員での参加としました。 他TechCafeでは2~3人程度が基本だったので多いかなという懸念もありましたが、終わってみると初回で不慣れな中、あまり各々が多い量を話せないという状況では丁度よかったと思います。 また役割については ファシリテーター (私)、コメント確認などの分担を決めました。 打ち合わせ 初回という事もあったので当日までの打ち合わせは数回に分けて念入りに行いました。 まずは参加者別にインタビューを行いテーマに対してどのようなネタを持っているかを自身が壁打ち役となり膨らませていきました。 そして全員分出揃ったら全体の繋がり含めて本番タイムラインをイメージして当日shownoteを検討していきました。 最終的に出来上がったshownoteが以下となります。 hackmd.io 最後に全員での本番を想定したリハーサルを行い当日に臨むことになりました。 当日編 紹介したイベント ここでは簡単に当日どのようなイベントを取り上げたかを紹介させていただきます。 複雑さに立ち向かうためのコードリーディング入門 fortee.jp 長期記憶や短期記憶といった 脳科学 側面からコード読解術を解説していく一風変わっていますが納得感のあるセッションです。 モバイルに特化した話ではないですが「確かに思い返すとあの場面では長期記憶が使えていたのかも」といった体験談で当日は盛り上がりました。 認証体験向上のためにpasskeys(パスキー)に対応する 〜 メリット・対応方法について fortee.jp 最近大手企業でも導入事例が増えているpasskeysについて改めて概要から iOS アプリにおける導入方法までが解説されたセッションです。 導入によるメリット・デメリット、特にユーザー目線だとどのような価値があるのかなどの議論で盛り上がりました。 集まれKotlin好き!Kotlin愛好会 love-kotlin.connpass.com Kotlinに関する事ならジャンル問わず何でもOKの定期開催イベントです。 LT中心ですが公式リファレンスを読んで雑談したりなどフリーな雰囲気で、紹介メンバーは何かしら新しい気づきを得ることができているそうです。 ちなみにSwift版も開催されているので iOS エンジニアも安心です。 WebViewと向き合う speakerdeck.com あまり取り上げられる機会の少ないWebViewについて改めて留意すべき点など解説されており、求めている人には非常に有益なセッションです。 ネガティブな方向で語られる事もあるWebViewですが当チームでも改めてその意義や活用できるシーンなどを議論するきっかけにできました。 Compose で Android / iOS アプリを作る speakerdeck.com Compose for iOS と KMP を用いてKotlin100%で iOS アプリを作るまでを解説したセッションです。 当日は クロスプラットフォーム でどこまでネイティブに近づけられるのかといった議論で盛り上がりました。 Android エンジニアが1人という不安と向き合う speakerdeck.com エンジニアが自身1人だけで正解が分からない中でどのように正しい情報や技術を取得していったかという学習方法やキャリアへの考え方が述べられたセッションです。 自分と境遇が似ており参考になったということでメンバーから紹介されましたが、アウトプット(自分で手を動かして物を作ること)の重要さに全員共感しました。 Japan IT Week www.japan-it.jp 特にモバイル特化という訳ではないですが由緒ある大規模ITカンファレンスです。 過去に組み込み開発経験もあり参加したこともあるメンバーからの紹介でしたが、実際に体験できるオンラインイベントならではの面白さといった観点等で語り合いました。 上記元々用意していたイベント紹介以外にも「登壇時の心構え」を語り合ったりもしていると、気づけばあっという間に予定の1時間半を経過し初めてのTechCafeは無事にクロージングを迎えました。 やってみての感想 初めは緊張もあったのですが徐々に慣れていつものチームの空気が出てきてカフェに見合った自然な空気をお届けできたのではと思います。 5人という人数も終わってみれば誰かに偏ることもなくわいがやな雰囲気を作り出すことができたので正解でした。 準備については入念に行ったことで当日つつがなく進行することができましたが、次回もし機会があるならよりライブ感を楽しむためにあえて軽めにというのでも良さそうだなと思いました。 とまぁ色々気づきはあったのですが 何よりも予想以上に楽しかったです! 自分達が楽しめるかというのはテーマにも掲げていましたが、終わってみれば全員が楽しめて業務とはまた違う共同作業を通しての達成感を味わうことができたのが一番の収穫でした。 初めは不安もありましたが得られることも多いので、また機会があれば是非開催できればと思っています。 おわりに 今回は初めてモバイルTechCafeを開催してみた体験をレポートにしてみましたがいかがだったでしょうか? 改めての学習のきっかけや結束力強化にも繋がりますので、このようなイベントを検討されている方の参考になりましたら幸いです。 再掲になりますが弊社では今回のモバイルTechCafe以外にも「 PHP TechCafe」や「フロントエンド TechCafe」といったイベントを定期開催しています。 どのような方にも楽しめて学べる場を目指し活動しておりますのでぜひご参加ください!
アバター
こんにちは。大阪楽楽開発課のdaina_rksです。 Laravelの マイグレーション を活用して、テーブル定義を更新しているサービスは多いと思います。 しかしサービスが継続するにつれ、気づけば大量の マイグレーション ファイルが存在している、、、なんて経験はありませんか? 私が携わっていたプロジェクトでも同じ悩みに直面していました。 この悩みに対して、私は マイグレーション ファイルを全て削除する ということを行いました。 今回はそのときの経験について、なぜ マイグレーション ファイルを削除するに至ったのか、削除するにあたって行なったこと、削除した結果どんな効果があったのかをご紹介します! マイグレーションファイルを全て削除するに至った理由 問題 マイグレーションファイルを全て実行するのに時間がかかる マイグレーションファイルのメンテナンスコストがかかる アイデア アクション ダンプ&リストアの仕組み構築 ダンプ リストア マイグレーションファイルの削除 環境変数の更新 ディレクトリ操作 不具合対応 結果 まとめ 参考情報 マイグレーション ファイルを全て削除するに至った理由 問題 私のプロジェクトでは、 マイグレーション ファイルが大量に存在することが原因で以下のような問題がありました。 マイグレーション ファイルを全て実行するのに時間がかかる 実行するファイル数が多いと必然的に完了までの時間が長くなっていました。また、テーブル定義更新以外の処理も行なっている マイグレーション ファイルもあり、その実行にさらに時間がかかっていました。 結果として全て完了するまでにおよそ13分もかかるため、新規DBの構築や自動テスト用のDB構築に時間がかかっていました。 マイグレーション ファイルのメンテナンスコストがかかる 実行済みの マイグレーション ファイルに対しても、 PHP もしくはLaravelのEOL対応が必要であるため、メンテナンスコストがかかっていました。(当時で150個の マイグレーション ファイルが存在していたため、1~2人日ほどコストがかかっていました。) ア イデア この問題の解決策として、 DB再構築時はダンプファイルからリストアする というア イデア を思いつきました。 そうすれば マイグレーション ファイルを実行する必要がなくなるため、DB構築の速度改善が見込めます。 ※実際にア イデア 段階で簡易的に試したところ大幅な速度改善がありました。 またダンプファイルから再構築できるようになるため、1度実行した マイグレーション ファイルは不要となります。このことから既に実行済みの過去の マイグレーション ファイルを削除しても問題がないため、メンテナンスコストの削減も見込めます。 ア イデア アクション 上記のア イデア を実現するためにやるべきことは以下の2つです。 DBのダンプ&リストアの仕組み構築 過去の マイグレーション ファイルの全削除 ダンプ&リストアの仕組み構築 1つ目のアクションとして、 DBのダンプファイルを生成する仕組み と リストアする仕組み が必要となります。 ダンプ ダンプファイルは マイグレーション ファイルが実行された直後のプレーンな状態のDBから、定期的に生成される必要があります。 そこでDBのダンプ処理はJenkinsのジョブで行うこととしました。 私のプロジェクトでは、Jenkinsサーバーでアプリケーションは動いていなかったため、常にプレーンな状態のDBが存在していました。またジョブを定義すれば定期的にダンプ処理を実行することができます。(Jenkinsサーバーで1バージョンの開発が完了するたびに ソースコード のバックアップを取るジョブが実行されていたため、そのジョブの中でダンプ処理を実行するようにしました。) ダンプファイルを生成したタイミングで、実行済みの マイグレーション ファイルを削除する処理をジョブの中に加えると、今後追加される マイグレーション ファイルに関しても、実行後に削除されるという運用に乗せることができました。 ダンプの仕組み リストア 環境構築手順や自動テストの仕組みをなるべく変更したくないため、リストアの処理は LaravelのArtisanコマンド で実行できるように作成しました。( php artisan migrate を実行していた部分を php artisan restoreDb に変更するだけで済むようにしました。) リストア後に確認しなければならないことは、ダンプ元となったDBと差分がないことです。 具体的には スキーマ とテーブルの所有者や権限が同じこと、テーブル定義およびレコードに差分がないことを確認する必要があります。 所有者と権限に関しては目視で確認、テーブルに関しては平文でダンプファイルを取得しdiff コマンドで差分がないことを確認しました。 リストア時に確認すべきこと マイグレーション ファイルの削除 2つ目のアクションは不要となった実行済みの マイグレーション ファイルを削除することです。 本来ならば マイグレーション ファイルはテーブル定義の更新のみを行なっているはずなので、ダンプファイルが正しく生成されていれば削除しても問題はありません。 しかし冒頭の問題で挙げた通り、テーブル定義の更新以外の処理を行なっている マイグレーション ファイルがいくつか存在しているため、何も対応せず削除すると不具合になり得るものがありました。 テーブル定義の更新処理以外の処理を行なっている マイグレーション に関しては、処理の内容に応じて以下の対策を行いました。 環境変数 の更新 私のプロジェクトでは、 環境変数 に変更がある場合は 環境変数 のテンプレートファイル .env.example を修正する運用でした。 しかし開発初期ではこの運用が定められていなかったため、 マイグレーション ファイル内で sed コマンドを実行し、 環境変数 を更新する運用を行なっていたようです。(かなり危ない運用) 環境変数 の更新を行なっている マイグレーション ファイルに関しては更新内容を確認し、最新バージョンの .env.example に存在しない値があれば、追記してから対象の マイグレーション ファイルを削除しました。 ディレクト リ操作 マイグレーション ファイル内でmkdirコマンドやchmodコマンドを実行し、 ディレクト リ操作や権限変更を行なっていました。 おそらく、あるバージョンでファイルアップロード機能が実装された際、 マイグレーション を活用してアップロードファイルの一時置き用 ディレクト リを作成していたようです。 ディレクト リ操作の処理に関しては環境構築用の Ansible のPlaybookに、必要な ディレクト リの作成処理および権限変更処理を記述し、 マイグレーション ファイルを削除しました。 不具合対応 画像のリサイズやファイルの移動など不具合対応用の処理が、 マイグレーション ファイル内で行われていました。 このような処理は今後必要になる可能性がある(実際に複数の マイグレーション ファイルで同様の処理が行われていました)ため、 LaravelのArtisanコマンド で実行できるように処理を切り出し、対象の マイグレーション ファイルを削除しました。 結果 上記のアクションを行い、 DB構築時はダンプファイルからリストアする というア イデア を実現できました。 肝心の結果ですが、DBの構築時間は13分から1.8秒(!!)まで短縮することができました。 これにより新規環境構築時間は10分以上短縮され、DB接続を伴う自動テストの実行は数時間から約10分まで短縮されました。 (DB接続を伴う自動テストに関してはテストメソッドごとに全テーブルを DROP →全 マイグレーション を実行という仕組みだったので、CI完了時間が数時間かかっていました。 そもそも自動テストの仕組みを見直せていなかったことが要因ですが、そのことについては今回は目を瞑リました。 ) メンテナンスコストに関しても、EOL対応ごとに1~2人日分短縮されることになったため、問題を解決することができました。 まとめ 今回は過去の マイグレーション ファイルの削除についてご紹介させていただきました。 マイグレーション ファイルを全て削除した結果、新規環境構築時間や自動テストに係る時間が大幅に短縮でき、メンテナンスコストの削減効果もありました。 冒頭に挙げた問題と似た問題を抱えているエンジニアの方や、今後 マイグレーション ファイルの運用を見直したいエンジニアの方のご参考になれば幸いです。 参考情報 今回のアクションで実施した ダンプ&リストアの仕組み構築 ですが、実はLaravelの Squashing Migrations という機能ですでに実現されています。 ア イデア 段階でこの機能を利用することを検討したのですが、この機能でダンプされる対象が 1 スキーマ のテーブル定義のみ でした。 私のプロジェクトの場合、ダンプ対象は 2つの スキーマ のテーブル定義と初期データ用のレコード であり、Laravelの機能では実現できなかったため利用することを見送りました。 もし マイグレーション ファイルの削除を行う場合、LaravelのSquashing Migrationsを利用できれば運用変更に係るコストを抑えることができるため、ぜひ選択肢に入れていただけたらと思います。 laravel.com
アバター