Wiz テックブログ

Wizは、最新のIoTやICTサービスをお客様に届ける「ITの総合商社」です。

ドキュメント作成に向いている静的サイトジェネレータはどれ?

f:id:rainymoment0616:20210827092425p:plain

こんにちは、フロントエンドエンジニアの柳田です。

現在、チーム内でドキュメントを作成する機会が多くなってきました。

社内では、様々な方法でドキュメントを作成しているのですが、そのひとつの方法として、静的サイトジェネレータ(Static Site Generator:SSG)があります。

今回は、ドキュメント作成にあたって自分が試してみたSSGをいくつかご紹介したいと思います。

静的サイトジェネレータ(SSG)とは?

静的サイトジェネレータとは、入力ファイルから簡単に静的ページを生成してくれるツールのことです。

有名なものだと、Next.jsやGatsby、Nuxtがありますね。

実際に表示に利用されるものは静的ページのため、動的ページに比べてサイトパフォーマンスが高く、SPAに比べてSEO面で優れています。

一方、大規模サイトのようなページ数の多いものは生成するのに時間がかかったり、更新頻度が多い場合に頻繁に生成を行わなくてはいけないというデメリットもあります。

ドキュメント作成するにあたってのポイント

今回、ドキュメント作成にあたって、下記をポイントに置いています。

  • マークダウン形式で書くことができる(誰もが簡単にドキュメントを書くことができるし、他サービスなどへの移行も簡単)
  • ドキュメントとしての見た目が担保されたデザインである
  • バージョン管理ができる
  • 環境構築が簡単

ドキュメント作成に向いていると感じるSSG

VuePress

https://vuepress.vuejs.org/

Vue.jsベースのドキュメント作成に特化したSSGです。

f:id:rainymoment0616:20210827083538p:plain
VuePressを利用したドキュメントの画面

  • 環境構築が簡単
  • Vue.jsの知識がなくてもマークダウンを利用したドキュメント作成が可能
  • デフォルトのテーマがシンプルかつ、ドキュメント向き
  • GitHub等のバージョン管理ツールでの管理が可能

マークダウンの中で、Vueコンポーネントを利用することができるのも魅力のひとつで、独自に作ったコンポーネントも利用することが可能です。

Docusaurus

https://docusaurus.io/

Facebook製の、Reactベースのドキュメント作成に特化したSSGです(アイコンの怪獣が可愛い)。

f:id:rainymoment0616:20210827083557p:plain
Docusaurusを利用したドキュメントの画面

  • Reactの知識がなくても、マークダウンを利用したドキュメント作成が可能(※)
  • CodeSandBoxやStackBlitzを利用してお試し開発が可能
  • デフォルトのデザインがドキュメント向け
  • GitHub等のバージョン管理ツールでの管理が可能

開発環境を作成する前に、お試しで弄ることができるのは、魅力的なポイントのひとつだと思います。

※一部ページはReactコンポーネントで構成されているため、カスタマイズを行う場合にはReactの知識が少し必要になってきます

Hugo

https://gohugo.io/

GitHubのスター数53000超えの、Go言語ベースのSSGです。

f:id:rainymoment0616:20210827085219p:plain
Hugoを利用したドキュメントの画面

  • ビルド時間が早い
  • マークダウンを利用したドキュメント作成が可能
  • テンプレートデザインが豊富(※)
  • GitHub等のバージョン管理ツールでの管理が可能

テンプレートのデザインがとにかく豊富で、ドキュメント向きのものから、ブログやポートフォリオ、ギャラリーサイトなど、様々なタイプのデザインが揃っています。

(今回、ドキュメント作成にGeekdocというテーマを利用しています。)

※テーマによってはディレクトリ構成が変わったり、独自のコンポーネント記述があるものもあるので、テーマを変更する際は注意が必要です

まとめ

今回はドキュメント作成に向いていると感じたSSGをいくつかご紹介しました。

他にもたくさんあるので、プロジェクトの規模や用途にあったものを試して選んでみてください(『Jamstack.org』で色んなSSGを確認することができますよ)。

株式会社Wizではエンジニアを募集しています!

↓↓↓興味ある方はぜひご覧ください!↓↓↓

careers.012grp.co.jp

PlaywrightとTestCafe比較

f:id:watsonpomelo:20210823183820p:plain

こんにちは、フロントエンドエンジニアの菅野です。

E2Eフレームワーク,ライブラリといえば Selenium,Cypress,Puppeteer,TestCafe,Playwright などたくさんありますが、 今回は私もプロジェクトで導入しているTestCafeと昨年にリリースされたPlaywrightについて簡単に比較してみたのでお話したいと思います。

TestCafe

Playwright

どちらも最新リリースが10日以内(8/23時点)なので活発に開発されています。

PlaywrightはMicrosoftが開発しているので今後開発されなくなるリスクは少なそうに思います。

サポートブラウザ

TestCafe Playwright
Chrome
Safari
Firefox
Opera ×
Edge ○ ※Chromiumのみ
IE11 ×
Chrome mobile
iOS Safari

機能 / 特徴

TestCafe Playwright
ヘッドレスモード
TypeScriptサポート
同時テスト実行
デバッグモード
モバイル実機テスト ×
コード生成 ×

他にもたくさんあるのですがいくつか抜粋して書いてみました。

TestcafeではスマホでURLやQRコードを読み込めば実機上でテストが実行できる機能があり、 Playwrightにはテストケースをちまちま書かなくてもユーザーの操作からコードを生成してくれる機能があったりと、それぞれに特徴や強みがあるのが分かります。

コード / 実行時間

簡単にですがスクリーンショットを撮るコードで違いを見てみたいと思います。

TestCafe

import { Selector } from 'testcafe';

fixture`screenShot`
  .page('https://example.com');
test('top', async t => {
  await t
    .takeScreenshot({
      path: `${t.browser.name}/testcafe/top.png`
    });
});

Playwright

const { chromium } = require('playwright')

(async () => {
  const browser = await chromium.launch({
    headless: false
  });
  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://example.com');
  await page.screenshot({ path: `./screenshots/Chrome/playwright/top.png` });
  await browser.close();
})();

Playwrightではブラウザコンテキストとコンテキストの中でpageを生成する記述があるため、TestCafeと比べるとコードが少し長くなってしまいます。

あとは、browser.close();を書くことでブラウザを閉じ処理を終了するのですが、TestCafeは記述を書かなくても自動でブラウザを閉じてくれるのでそういう細かいところで便利だなと思います。

続いて実行時間を比べてみます。

今回はブラウザUIを表示させて時間を計測したかったので、Playwrightではデフォルトでtrueとなっているheadlessモードをfalseにしています。

TestCafe Playwright
Time 10183.922ms 2313.172ms

実行時間はPlaywrightが7秒以上速い結果となりました。

TestCafeはブラウザが立ち上がるまでに特に時間がかかってしまうのでそこで大きく差が出てしまいました。

まとめ

Playwrightは学び始めたところでまだまだ分かっていない部分も多いのですが、 比較をしてみると互いの長所短所が分かりやすくなりますね。

ユーザー操作からコードを生成してくれるPlaywrightはメンテナンスのしやすさからフォームテストなどのテストケースが多い場合に向いてると思います。

TestCafeはサポートブラウザが充実しているのでスマホ実機、IEなど様々な環境でテストを行いたい場合に選ぶと良さそうです。

プロジェクトによってどういったテストを行いたいのか、検証範囲はどこまでなのかなどがあると思いますので、ぜひそれぞれにあったフレームワーク,ライブラリを選んでみてください。

株式会社Wizではエンジニアを募集しています! ↓↓↓興味ある方はぜひご覧ください!↓↓↓ careers.012grp.co.jp

PHPでコンストラクタを複数定義する【初級〜中級者向け】

f:id:t-yone3:20210813094006j:plain

こんにちは、バックエンドエンジニアの米山です。

突然ですがコンストラクタ、使っていますか?

特に、ビジネスロジックをまとめるクラスを作った時や、似たような処理を行うクラスの初期化に役立つのがコンストラクタです。

今回は、そんなコンストラクタを複数作る手法と、私なりのオススメ度も併せてご紹介しようと思います。

それではいってみましょう〜!

そもそも、「コンストラクタ」って何?

Wikipediaにはこうあります。

コンストラクタ(英: constructor)は、オブジェクト指向プログラミング言語で新たなオブジェクトを生成する際に呼び出されて内容の初期化などを行なう関数あるいはメソッドのことである。

JavaPHPC#に代表されるようなオブジェクト指向言語では、クラスのインスタンスを作成する時に初期化処理を行うことができます。

この初期化処理のことを「コンストラク」と呼んでいます。

具体的にPHPを例に、コードで表すとこんな感じになります。

class SomeClass
{
    // これがコンストラクタ
    public function __construct()
    {
        logger()->debug('初期化処理だよん');
    }
}

class Hoge
{
    public function fuga()
    {
        // クラスのインスタンスを作ると、初期化処理が呼ばれる
        // ログに「初期化処理だよん」が出力される
        $instance = new SomeClass();
    }
}

コンストラクタを複数実装する方法

それでは本題に入りましょう。

コンストラクタを複数実装し、引数の数や内容で処理を分岐させます。
いくつか方法があるので、私なりのオススメ度も併せて記載しておきます。

【オススメ度:★★★☆☆】方法その1:コンストラクタをオーバーライドさせる

PHPでは関数のオーバーロードができません。

似たようなことをやるためには、abstractクラスを用意し継承した先のクラスで「自分自身のコンストラクタを呼ぶ」もしくは「親クラスのコンストラクタを呼ぶ」という分岐になります。

コンストラクタだけ、オーバーライドすることができます。

【余談】オーバーロードとオーバーライド

さて、上記で説明した通りなのですが「オーバーロード」「オーバーライド」は混同しやすい用語なので、解説を挟みます。

私なりに理解した内容で説明すると

  • オーバーロード:同名の、類似メソッド(または関数)の作成
  • オーバーライド:メソッド(または関数)の上書き

です。オーバーロードを実際にコードで表すとこうです。

PHPオーバーロードできないのでJavaで表現します)

class SomeClass {
    // 元のメソッド
    public void SampleMethod() {

    }
    // オーバーロードしたメソッド(返却値が異なる)
    public String SampleMethod() {
        return "hoge";
    }
    // これもオーバーロードしたメソッド(引数が異なる)
    public void SampleMethod(int value) {

    }
}

オーバーライドは下記のようになります(同じくJavaで表現します)

class AbstractSampleBase {
    // 親クラスのメソッド
    public String SampleMethod(String value) {
        return "hoge" + value;
    }
}

class ChildSample extends AbstractSampleBase {
    // 継承先で再定義する
    public String SampleMethod(String value) {
        return "fuga";
    }
}
閑話休題】結局、PHPでコンストラクタをオーバーライドすると・・・?

話を戻して、PHPでコンストラクタをオーバーライドさせると、こうなります。

abstract class SampleBase
{
    public $output;
    // 親クラスのコンストラクタは引数なし
    public function __construct()
    {
        $this->output = 'これは親クラスのコンストラクタです';
    }
}

class ConstructorSampleA extends SampleBase
{
    // 子クラスのコンストラクタは引数あり
    public function __construct($value)
    {
        $this->output = 'これは子クラスのコンストラクタです' . $value;
    }
}

これを、例えばControllerクラスなどで呼び出す場合、以下のようになります。

class SampleController extends Controller
{
    public function index()
    {
        // 親クラスのコンストラクタがよばれる
        $data = new ConstructorSampleA();
        // 下をコメントアウトすると子クラスのコンストラクタが呼ばれる
        // $data = new ConstructorSampleA('hello world');
        return view('sample', ['output' => $data->output]);
    }
}

【オススメ度:★★★☆☆】方法その2:コンストラクタをオーバーライドさせる(1メソッドで分岐)

方法その1と似ていますが、こちらのやり方では必ず子クラスのコンストラクタを呼び、引数の内容によって親クラスのコンストラクタを呼ぶ、というやり方です。

abstract class SampleBase
{
    public $output;
    // 親クラスのコンストラクタは引数なし
    public function __construct()
    {
        $this->output = 'これは親クラスのコンストラクタです';
    }
}

class ConstructorSampleB extends SampleBase
{
    // 子クラスのコンストラクタは引数あり(デフォルトはnull)
    public function __construct($value = null)
    {
        if (empty($value)) {
            // 親クラスのコンストラクタを呼び出す
            parent::__cunstruct();
        } else {
            // 引数の値があった時に子クラス側の処理を行う
            $this->output = 'これは子クラスのコンストラクタです' . $value;
        }
    }
}

【オススメ度:★☆☆☆☆】方法その3:func_num_argsとfunc_get_argsでコンストラクタを分岐する

この方法は、可読性の観点からオススメはしません。また、PHPのバージョンにより挙動が変わる可能性があります。

func_num_args関数でコンストラクタに渡した引数の数を取得・チェックし、引数の数に応じて呼び出すコンストラクタを変える方法です。
分岐先のメソッドに渡す引数はfunc_get_args関数で取得できます。

class ConstructorSampleC
{
    public $output;
    public function __construct()
    {
        $argCount = func_num_args();
        $argList = func_get_args();

        // 受け取った引数の数によって、呼び出すメソッドを分岐
        switch($argCount)
        {
            case 1:
                $this->construct1($argList[0]);
                break;
            case 2:
                $this->construct2($argList[0], $argList[1]);
                break;
            case 3:
                $this->construct3($argList[0], $argList[1], $argList[2]);
                break;
        }
    }

    private function construct1($value)
    {
        $this->output = 'これは引数1つのコンストラクタです' . $value;
    }

    private function construct2($value1, $value2)
    {
        $this->output = 'これは引数2つのコンストラクタです' . $value1 . $value2;
    }

    private function construct3($value1, $value2, $value3)
    {
        $this->output = 'これは引数3つのコンストラクタです' . $value1 . $value2 . $value3;
    }
}

class SampleController extends Controller
{
    public function index()
    {
        // 引数1つの時のコンストラクタ
        $data = new ConstructorSampleC('hello');
        // 引数2つの時のコンストラクタ
        // $data = new ConstructorSampleC('hello', 'world');
        // 引数3つの時のコンストラクタ
        // $data = new ConstructorSampleC('hello', 'world', 'hoge');
        return view('sample', ['output' => $data->output]);
    }
}

【オススメ度:★★★★☆】方法その4:interfaceを使ってクラスで分岐する

方法その1では継承を使いました。「同じような処理を行う」が「初期化処理が異なる」場合はinterfaceが有効です。

この例ではfactory関数を用意し分岐条件に応じてそれぞれの初期化を行う、別々のクラスを返すようにします。
継承を使う場合、継承元・継承先でコンストラクタの分岐を行うことで少々複雑さが増します。

主処理が同じ場合は

  • 初期化を行ってから主処理を行うクラス
  • 初期化を行わず、主処理を行うクラス

にクラスそのものを、分けてしまった方がわかりやすくなるケースがあります。

interface ISample
{
    // 共通で行う主処理
    public function execute();
}

class ConstructorSampleD implements ISample
{
    public function construct()
    {
        logger()->debug('引数が0の時のコンストラクタです');
    }

    public function execute()
    {
        // クラスごとに処理を分けても良いし、共通的な処理を作って呼び出しても良い
    }
}

class ConstructorSampleE implements ISample
{
    public function __construct($value)
    {
        logger()->debug('引数が1の時のコンストラクタです' . $value);
    }

    public function execute()
    {
        // クラスごとに処理を分けても良いし、共通的な処理を作って呼び出しても良い
    }
}

class ConstructorSampleF implements ISample
{
    public function __construct($value1, $value2)
    {
        logger()->debug('引数が2の時のコンストラクタです' . $value1 . $value2);
    }

    public function execute()
    {
        // クラスごとに処理を分けても良いし、共通的な処理を作って呼び出しても良い
    }
}

// クラスのインスタンスを作り出すfactoryクラス
class ConstructorSampleFactory
{
    public static function createSample($mode)
    {
        if ($mode === 1) {
            // 引数が0の時はConstructorSampleDクラスとしてコンストラクタを実行
            return new ConstructorSampleD();
        } elseif ($mode === 2) {
            // 引数が1つの時はConstructorSampleEクラスとしてコンストラクタを実行
            return new ConstructorSampleE('hoge');
        } elseif ($mode === 3) {
            // 引数が2つの時はConstructorSampleFクラスとしてコンストラクタを実行
            return new ConstructorSampleF('hoge', 'fuga');
        }
    }
}

この場合、呼び出し元では下記のようになります。

class SampleController extends Controller
{
    public function index()
    {
        // 引数0の時のコンストラクタ
        $data = ConstructorSampleFactory->createSample(1);
        // 引数1つの時のコンストラクタ
        // $data = ConstructorSampleFactory->createSample(2);
        // 引数2つの時のコンストラクタ
        // $data = ConstructorSampleFactory->createSample(3);
        return view('sample', ['output' => $data->output]);
    }
}

この例ではcreateSample関数の中で、直接コンストラクタに引数を渡していますが、呼び出し元(Controller)から引数を繋げたい場合は、createSample関数へ渡す引数を可変長引数リストにする等で対応可能です。

まとめ

PHPでコンストラクタを複数実装する方法をご紹介しました。

他の言語のようにオーバーロードができないので、少し不恰好になってしまいますが

  • 継承を使い、親クラスと子クラスでコンストラクタを分ける
  • func_num_args関数、func_get_args関数を使って、処理パターンを分岐させる
  • interfaceを使ってクラスそのものを分岐し、それぞれで初期化させる

まとめるとこの3パターンになります。

是非試してみて下さい。


Wizではエンジニアを募集しております。

興味のある方、ぜひご覧下さい。

careers.012grp.co.jp

EMになって雑食になり行動が変わった話

f:id:sevenium:20210816152010j:plain


はじめまして。
エンジニアリングマネージャー(以下EM)の岡本です。
今回は私がプレーヤーからマネージャーになって変化した「行動」について書いていきます。


マネージャーになり、基本的に何にでも興味を示し行動するようになりました。

エンジニアに関係ない記事や本でも興味があれば読んでみたり、
コミュニケーションが苦手なのに積極的に取るようになったり、
課題と感じたらとりあえず何かやってみたり、
と何でも好き嫌いなくまず行動するようになりました。

「こんなのできるのが当たり前じゃん!」と思う方もいると思いますが、
自分自身、性格と合わない行動が多く不思議に思っており、
今回振り返りも兼ねてまとめていきたいと思います。

経歴&自己紹介

私は2016年7月に中途でフロントエンドエンジニアとして入社しました。
2019年4月にマネージャーになるまではフロントエンド開発をメインでしておりました。

基本的に内気で初対面の人や人前で話すのが苦手です。
趣味はゲームやキーボードと完全なインドアタイプです。

こういった人間にEMが務まるのかと心配になりますが、とりあえず振り返っていきましょう!

【振り返り1】ビジネス意識が変わったから?

プレーヤーのときはプロダクトを作ること(コードを書きまくること)が楽しく、
その先にあるビジネス意識は薄く、設計やトレンドの技術に意識が向いていました。
何のために作るのかを理解してプロダクトに落とし込むことはできても、
そのプロダクトの利益を最大化するためにエンジニアとして何をすべきかという考えは殆どありませんでした。

しかしマネージャーとなると組織のパフォーマンスの最大化や、
さらなる効率化がミッションとなってくるため、
徐々にビジネスへの意識というのは高まっていきました。

ビジネスとエンジニアを繋ぐためだったり、
ビジネスインパクトを与えられるエンジニア組織を構築するための勉強をしたりと、
とりあえずはインプット量を増やすことを心がけました。

結果、ミッション達成のためにこちらから提案したり、
エンジニア組織についての思いをメンバーにアウトプットすることが増えたと感じます。

 

自身の成長が一番ではなく、
組織としての成功を一番として行動できるような「マインドの変化」が自分の中であったことが、行動の変化に結びついているのかもしれません。

 【振り返り2】 本を読むようになったから?

マネジメント能力を強化したく、色々本を読むようになりました。

  • 効率的な学び方
  • コーチン
  • エンジニア組織
  • リーダーシップ
  • ビジネス本
  • 経理
  • 心理学

などの本を読んでいて
「あの本に書いてあった内容とこの本のこの部分って似てるなー」
とか
「あの本で言ってたことってこういうことだったのか!」
と他の本を読んでいるときに気づくことがよくありました。

全く違う分野の本なのにこういう事が頻繁にあり、
「学ぶ」ことに境界線を作っていた自分が恥ずかしいと思うようになりました。

 

このように勉強する意識が変わり学びの視野が広がったことで、
様々な分野への興味に結びついているのかもしれません。

 

【振り返り3】そもそもコミュニケーション機会が増えたから?

EMになると様々な部署と連携した課題解決を行うようになるため、
今まで関わらなかったビジネスサイドの人やバックオフィスの人と関わりが増えました。
また1on1で多くのメンバーと話す機会もあり、
基本的に誰かとコミュニケーションを取ることが仕事になってきています。

様々な人とコミュニケーションを取るなか、
「ビジネスサイドの人の説明の納得感や語彙力すごいなー」
「この人の説明わかりやすいから今度真似してみよう」
「メンバーの話し方や表情から色々情報が取れるの大事だしおもしろい」
と様々な学びを得たり、コミュニケーションの重要性に気づいたというのが大きいです。

今までは自分がコミュニケーション苦手だからと、
積極的にコミュニケーションを取ってきませんでしたが、
「コミュニケーションを取っていないからこそコミュニケーションが苦手だった」ということに気づいたのです。

 

今はコミュニケーションに対するネガティブな印象は無くなり、
話すことで得られる学びの好奇心の方が強くなったため、
積極的な行動に変化しているのだと感じます。

最後に

一度マネジメントを経験することで目線が変わり、
今まで見えなかったもの・見ようとしなかったものが見えてくるようになることで「考え方=思考」が変化していきました。

マネジメントを「経験」することで「思考」が変化し、
「思考」が変化することでそれはやがて「行動」へと変化していきます。
その結果が今の私を作っているのではないかと今回振り返ってみて感じました。

これらの経験はマネジメントだけでなく、今後プレーヤーのときにも活かせることであると感じます。

 

EMになって学んだこと。
それは、「自分自身で壁や境界を作らず、何にでも興味を持ちインプットし思考し続けること」です。
雑食に何でも学ぶ姿勢が自身を成長させてくれるのかもしれません。

 
株式会社Wizでは雑食に学ぶエンジニアを募集しています!
↓↓↓興味ある方はぜひご覧ください!↓↓↓
careers.012grp.co.jp

TypeScript InterfaceとType Aliasについて

f:id:yukiji_03:20210805141123j:plain

はじめに

皆様こんにちは、フロントエンドの松本です。

本日はTypeScriptのInterfaceType Aliasについてお話していきたいと思います。

オブジェクトの宣言時などはどちらを使えばいいのか、頭を悩ませるトピックの一つでもあると思います。

それでは早速、両者の具体的な違いをみていきましょう。

型の宣言

// Interface
interface Book {
    title: string
    page: number
}

// Type Alias
type Book = {
    page: number
}

Interfaceは構造を定義するものなのでType Aliasと違って=が不要になっており、見た目としてはほとんど一緒です。

また、 Type Aliasは右辺に任意の型を指定できるという点で汎用的です。

Interfaceの場合、ブロックスコープでなければならないので、次のようなType AliasInterfaceに書き直す方法はありません。

type A = number
type B = A | string

宣言のマージ

同名のInterfaceは 型がマージされます(宣言のマージ)

interface Book {
    title: string
}

// Bookに page: number をマージ
interface Book {
    page: number
}

const book: Book = {
    title: 'TypeScript',
    page: 360
}

これをType Alias に書き直すとエラーが起こります。

type Book = {
    title: string
}

type Book = {
    page: number
}

// //Duplicate identifier 'Book'

拡張性

Interface は拡張性が高いです。

extendsを使うことで、継承したサブインターフェースを作成できます。

interface Movie {
    title: string
    time: number
}

//Movieを継承
interface Drama extends Movie {
    season: number
}

const kaigaiDrama: Drama = {
    title: 'The Walking Dead',
    time: 60,
    season: 10
}

また、InterfaceType Aliasをextendsすることも出来ます。

// Type Alias
type Movie {
    title: string
    time: number
}

//Movieを継承
interface Drama extends Movie {
    season: number
}

const kaigaiDrama: Drama = {
    title: 'The Walking Dead',
    time: 60,
    season: 10
}

Type Aliasは拡張こそできないものの、交差型を利用して既存のタイプを組み合わせることができます。

type Rice = {
    kind: '白米' | '玄米'
    gram: number
}

type Egg = {
    size: 'S' | 'M' | 'L'
}

type TamagokakeGohan = Rice & Egg

const tkg: TamagokakeGohan = {
    kind: '白米',
    gram: 250,
    size: 'L'
}

まとめ

Type AliasInterfaceはほとんど同じことができ、違いは小さなものです。

Type Aliasは型に名前をつけるもの、Interfaceは構造を定義するものです。

例えばオブジェクトの宣言時などはInterfaceを使用するなどして、ケースバイケースで使い分けると良いと思います。

個人的にはType Aliasで書いた方が安心できる派です。

Interfaceは宣言のマージにより、意図しないところで型が変化する恐れがあるので)

最後になりますが、Wizではエンジニアを募集中です。

興味のある方は是非覗いてみてください↓

careers.012grp.co.jp

NextAuth.jsでカスタムログインページを実装する方法

f:id:mt_816:20210804192049p:plain

こんにちは、フロントエンドエンジニアの松尾です。

最近はNext.jsが盛り上がっており、エコシステムも充実してきています。

その中に、NextAuth.jsという認証機能を簡単に実装できるライブラリがあり非常に便利です。

next-auth.js.org

本記事では、NextAuth.jsを使用したカスタムログインページの実装方法について説明したいと思います。

NextAuth.jsとは?

NextAuth.jsとは、Next.jsに簡単に認証機能を実装できるライブラリです。

アカウント登録も必要なく、ライブラリをインストールするだけで実装できます。

また、メールアドレス認証に加えて、GoogleTwitterFacebookといったサービスのOAuth認証を組み込むことが可能です。

NextAuth.jsでログインする場合、以下のようにNextAuth.js専用のログインページに遷移する形となっており、自由にページをカスタマイズできません。

f:id:mt_816:20210803152037p:plain

認証機能を特定のページに組み込みたい場合も多いのではないでしょうか?

そこで、カスタムログインページの実装方法について説明したいと思います。

実装方法

まず初めに、next-authをインストールしてください。

npm install next-auth

次に、NextAuth.jsのAPIルートを作成します。

/pages/api/auth/[...nextauth].jsを作成し、以下を記述してください。

import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'

// NextAuth.js関数に渡すオプション
const options = {
  providers: [
    Providers.Credentials({
      // NextAuthの認証関数。credentialsにログイン情報が格納される。
      authorize: async credentials => {
        if (
          // ログインID・パスワードは環境変数にて設定する。
          credentials.login === process.env.NEXT_PUBLIC_LOGIN_ID &&
          credentials.password === process.env.NEXT_PUBLIC_PASSWORD
        ) {
          // ログイン成功後ユーザー情報を返却する。値はsessionに格納される。
          return Promise.resolve({ name: 'admin' })
        } else {
          // ログイン失敗後認証を拒否し、エラーメッセージを返却する。
          return Promise.resolve(null)
        }
      },
    }),
  ],
  // ログインページを指定する。今回はトップページのため'/'を指定。
  pages: {
    signIn: '/',
  },
}

export default (req, res) => NextAuth(req, res, options)

最後に、認証機能を実装するページに以下を記述します。

import { useRouter } from 'next/router'
import { getCsrfToken, useSession, signOut } from 'next-auth/client'

const LoginPage = ({ csrfToken }) => {
  const { error } = useRouter().query
  const [ session ] = useSession()

  return (
    <div>
      <h1>カスタムログインページ</h1>
      {session ? (
        // ログイン状態の場合。ユーザー名、ログアウトボタンを表示。
        <>
          <div>ユーザー:{session.user?.name}</div>
          <button onClick={signOut}>ログアウト</button>
        </>
      ) : (
        // ログアウト状態の場合。入力フォームを表示。
        <form method='post' action='/api/auth/callback/credentials'>
          <input name='csrfToken' type='hidden' defaultValue={csrfToken} />
          <label>
            <div>ログインID</div>
            <input name='login' />
          </label>
          <label>
            <div>パスワード</div>
            <input name='password' type='password' />
          </label>
          <div>
            <button type='submit'>ログイン</button>
          </div>
          {/* ログイン失敗後、エラーメッセージを表示。*/}
          {error && <div>ログインID又はパスワードが間違っています。</div>}
        </form>
      )}
    </div>
  )
}

// POSTリクエスト(サインイン・サインアウトなど)に必要なCSRFトークンを返却する。
export const getServerSideProps = async context => {
  return {
    props: {
      csrfToken: await getCsrfToken(context),
    },
  }
}

export default LoginPage

ログインに成功すると、useSession関数のsessionにユーザー情報が格納されます。

以下のように、sessionにユーザー情報が含まれているか否かで表示を切り替えています。

▼ ログイン前

f:id:mt_816:20210804110407p:plain

▼ ログイン後

f:id:mt_816:20210804110414p:plain

また、認証失敗時に関しては、URLパラメータのerrorを参照し、エラーメッセージを表示させています。

▼ ログイン失敗時

f:id:mt_816:20210804135835p:plain

まとめ

以上NextAuth.jsを使用したカスタムログインページ実装についての説明でした。

今回は簡易的な認証機能の例でしたが、NextAuth.jsは、GoogleTwitterFacebookといったサービスの認証機能の組み込みや、データベース・メール送信サービスとの連携も可能となっています。

NextAuth.jsは無料で使用できますので、興味ある方は是非お試しください!

最後になりますが、Wizではエンジニアを募集中です。

興味のある方は是非覗いてみてください↓

careers.012grp.co.jp

npm packageの公開の手順

f:id:thunder_fury:20210727170909p:plain

はじめに

皆さんこんにちは、フロントエンドエンジニアのWooです。⚡️🌪

npmモジュール(npm パッケージ)を作成しデプロイしてオープンソースとして使えるところまでやってみたいと思います。

npm (Node Package Manager)

開始する前に、簡単にnpmについて調べてみたいと思います。

npmの役割はパッケージ管理ツールです。 ソフトウェアを管理し、インストールをサポートしてくれます。 この記事を書いた時点では公開のパッケージ数約1,688,642個以上のnpmパッケージが公開されており現在もまだnpmのパッケージが増えています。

npm インストール

Node.jsをインストールするとnpmも同時にインストールがされます。 下記のリンクでNode.jsのインストールが簡単にできます。

nodejs.org

package.json

package.jsonを生成するためにはプロジェクトのフォルダからnpm initのコマンドを叩く必要があります。 Package.jsonは開発に必要な情報などを記載する場所であり、必要なパッケージをインストールする時に自動で記載をしてくれます。

package-lock.json

package-lock.jsonは各パッケージの依存関係の管理をするファイルです。

npm initを実行した時には生成されませんが、このnpmパッケージをインストールしたり、変更、削除などの操作を行うと生成されます。

npmユーザー登録

npm公式ページで登録することができます。 お名前とメールアドレス、パスワードを入力した後、加入したメールアドレスに本人認証メールが届くので承認したらnpmパッケージをデプロイすることができます。

www.npmjs.com

npmパッケージ開発

npm 初期化

プロジェクトのフォルダ(リポジトリ)を作成してコマンドを叩きます。

npm init -y

package.jsonが生成されるので下記のように設定を行います。

{
  "name": "@[npmid]/[パッケージ名]", // <-追加
  "version": "1.0.0",
  "description": "どんなパッケージのかの説明", // <-追加
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": { // <-追加
    "type": "git",
    "url": "リポジトリのURL" 
  },
  "bugs": { // <-追加
    "url": "リポジトリのissuesのURL"
  },
  "homepage": "リポジトリのreadmeURL", // <-追加
  "keywords": [
    "キーワード", // <-追加
    "keyword" // <-追加
  ],
  "author": "開発者", // <-追加
  "license": "MIT" // <-変更
}

注意点としてはlicenseの設定ですが、自分はMITによくします。 MITとは、「コードは好きに使っても良いけど、公開されたコードを使って何か問題が起きた場合、作者は責任とらないよ」というlicenseで、 オープンソースにはMITライセンスをよく使うらしいです。 他にもいろいろなlicenseがありますので調べてみてください。


npmパッケージ作成

rootにindex.jsを作成します。
下記は簡単挨拶文を出力する簡単なサンプルコードです。

export class Test {
  constructor(name) {
    this.name = name
  }
  action() {
    return `Hello ${this.name}`
  }
}

その他最低限必要なファイルを追加してモジュールのアップロードの準備を完了します。

root/
├── index.js
├── .gitignore
├── .npmignore
├── README.md
├── LICENSE
└── package.json

npmログイン

ログインはディレクトリのrootからコマンドを叩きます。

> npm login

Username: 登録したID
Password: 
Email: (this IS public) 登録したメールアドレス
Logged in as youngsoohan on https://registry.npmjs.org/.

ログインが成功されたらアクセストークンが発行されコマンドでデプロイすることができます。 トークンはマイページで確認することができます。

npm デプロイ

デプロイは上記のpackage.jsonに記載したnameがパッケージ名になります。
約1,688,642パッケージの中から被らないパッケージ名で公開しなければいけません。

そのため@useridを頭につけると被ることはないので一般的には@useridをつけてデプロイをすることが多いです。

例)@npmid/modalのような感じです。

 "name": "@[npmid]/[パッケージ名]"

バージョンも設定する必要があり、npm init -yの時、defaultで1.0.0が記されます。
自分は正式のリリースとして問題なければ1.0.0でアップを行い、その前までの開発段階では0.0.1から設定して公開するようにしてます。
今回はサンプルとして公開し後ほど削除するので1.0.0で公開しました。

 "version": "1.0.0",

公開コマンド(下記は無料プランのコマンドです。)

npm publish --access public

npmのマイページに接続したらちゃんと公開されています!

f:id:thunder_fury:20210728140437p:plain

npmのデプロイ完了しましたー🔥

公開したパッケージ使ってみる

npm i @npmid/test
import { Test } '@npmid/test'

const test = new Test(`npm`)
console.log(test.action())

f:id:thunder_fury:20210728180556p:plain

npmにちゃんと挨拶をしていますので問題なく公開はされているようです。

npm パッケージ削除

注意点としては最後に消したいバージョンを書かないといけないのと、時間過ぎてしまうと自分で消すことができずnpmに問い合わせをしないといけないようなので必要でなければすぐ消しましょう⚡️。

npm unpublish @userid/test@1.0.0

最後に

今回の内容はサンプルとしての内容ですが実際にはフォームのバリデーションのパッケージを開発し公開しました。 初めて開発をして色々調べながらですがとても勉強になりました。
実際公開したパッケージをどんどんプロジェクトに導入し工数削減を目指して行きたいと思っております。


最後になりますが、Wizではエンジニアを募集中です!

興味のある方は是非覗いてみてください!↓

careers.012grp.co.jp