LIFF Mockを使ったら、LIFFアプリ開発がとてつもなく楽になった

## 目次
- LIFFとは
- LIFF Mock活用前のコード
- 抱えていた問題
- 解決に用いたLIFF Mockとは
- 解決方法
- まとめ

## LIFFとは
LINE Front-end Frameworkの略です。

> LINEが提供するウェブアプリのプラットフォーム

と公式リファレンスに記載ありました。また、

> このプラットフォームで動作するウェブアプリを、LIFFアプリと呼びます

とあるので、以降はLIFFアプリという名称も使っていきたいと思います。

LIFFアプリでは、LINEのユーザーIDなど、LINEの持つ情報を活用して、機能を作ることができます。

https://developers.line.biz/ja/docs/liff/overview/

## LIFF Mock活用前のコード
React × TypeScriptで作ったアプリをNetlifyにHostingして、それをLIFFアプリとして公開しています。
LIFFアプリを作る時、ユーザーへwebページを表示する前に、LINEログインをしてもらうようにしました。それが以下のコードです。（本筋とは関係ないChakraのコードが混じっています。。すみません。:bow: ）

```typescript
import React from 'react';
import {createRoot} from 'react-dom/client';
import './index.css';
import App from './App';
import {extendTheme, ChakraProvider} from '@chakra-ui/react';
import liff from '@line/liff';

const colors = {
  brand: {
    900: '#1a365d',
    800: '#153e75',
    700: '#2a69ac',
  },
};

export const theme = extendTheme({colors});

const liffId = 'liffId'; // 個々のLIFFIDになります
liff
  .init({liffId})
  .then(async () => {
    if (!liff.isLoggedIn()) {
      await liff.login();
    }
    root.render(
      <ChakraProvider theme={theme}>
        <App />
      </ChakraProvider>
    );
    return;
  })
  .catch((e) => {
    alert(`LIFF error: ${e.message}`);
  });
```

## 抱えていた問題
これでは `yarn start` をしてlocalhostでページを開きLINEログインすると、本番環境にリダイレクトしてしまいます。（イメージは以下です）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462620/062d58ad-987c-501e-d1fa-772b10db125e.png)


今のままだと、ログイン後の画面の実装をしたとしても確認方法は

- LINEログインの機構をコメントアウトする
- storybookでコンポーネント単位で見た目を整えていく

など手間がかかったり、局所的な解決しかできなかったです。
（僕の調べ方が甘かったりはするかも。。）

理想の挙動はlocalhostにリダイレクトされて欲しいです。（イメージは以下です）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462620/86ceadf7-fdd5-1330-2a7e-a540119cd575.png)


## 解決に用いたLIFF Mockとは
ここでは上記の問題を、LIFF Mockを用いて解決します。
LIFF Mockとは

> LIFFアプリのテストを簡単にするためのLIFFプラグイン

です。また、

> LIFFアプリがLIFFサーバーから独立し、LIFF APIがモックデータを返すため、単体テストや負荷テストをより簡単に行うことができます

とあるので、テストの際に使われることを想定したプラグインだと考えられます。

https://developers.line.biz/ja/news/2022/04/25/liff-plugin/#liff-mock-20220425

今回僕はこのLIFF Mockを開発環境で用いて、以下のような構成にしてみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462620/5e6da183-b0d1-89e2-b0f4-494c067432a8.png)


## 解決方法手順
手順は

- liff-mockをインストール
- 本番環境以外はLiffMockPluginを使用する
- 環境変数を生成する
- liff.initしてからレンダリング処理を行う

です。では、順を追ってみていきます。

### liff-mockをインストール
```sh
$ yarn add @line/liff-mock
```

なお、

> LIFF Plugin feature is available since LIFF SDK v2.19.0.

とあるように、LIFF Mockを使用するには、LIFF SDKをv2.19.0以上にあげる必要があります。

### 本番環境以外はLiffMockPluginを使用する
コードは以下になります。

```typescript
if (process.env.NODE_ENV !== 'production') {
  liff.use(new LiffMockPlugin());
  // https://github.com/line/liff-mock
}
```

### 環境変数を生成する
```typescript
export const generateEnv = () => {
  let liffId: string;
  let mock: boolean;
  switch (process.env.NODE_ENV) {
    case 'production':
      liffId = process.env.REACT_APP_LIFF_ID ?? '';
      mock = false;
      break;
    case 'development':
    case 'test':
      liffId = 'dummyId';
      mock = true;
      break;
  }
  return {liffId, mock};
};

const {liffId, mock} = generateEnv();
```

`generateEnv` という関数を用いて、各環境の変数を定義しましたが、ここは環境別に用意できればなんでも良いです。
mockに関しては、本番環境ではfalse、開発環境ではtrueになるようにします。

### liff.initしてからレンダリング処理を行う
liff.init時にmock modeにするかどうかを指定します。

```typescript
liff
  .init({liffId, mock}) // mockをliff.init時に渡している
  .then(async () => {
    if (!liff.isLoggedIn()) {
      await liff.login();
    }
    root.render(
      <ChakraProvider theme={theme}>
        <App />
      </ChakraProvider>
    );
    return;
  })
  .catch((e) => {
    alert(`LIFF error: ${e.message}`);
  });
```

これで、本番環境は実際のLINEのサーバーと通信をし、開発環境ではLINEのモックサーバーと通信をするようになります。

### 最終的なコード
最終的にはこうなります。

```typescript
import React from 'react';
import {createRoot} from 'react-dom/client';
import './index.css';
import App from './App';
import {extendTheme, ChakraProvider} from '@chakra-ui/react';
import liff from '@line/liff';
import LiffMockPlugin from '@line/liff-mock';
import {generateEnv} from './generateEnv';

const colors = {
  brand: {
    900: '#1a365d',
    800: '#153e75',
    700: '#2a69ac',
  },
};

export const theme = extendTheme({colors});

const root = createRoot(document.getElementById('root') as HTMLElement);
if (process.env.NODE_ENV !== 'production') {
  liff.use(new LiffMockPlugin());
  // https://github.com/line/liff-mock
}
const {liffId, mock} = generateEnv();

liff
  .init({liffId, mock})
  .then(async () => {
    if (!liff.isLoggedIn()) {
      await liff.login();
    }
    root.render(
      <ChakraProvider theme={theme}>
        <App />
      </ChakraProvider>
    );
    return;
  })
  .catch((e) => {
    alert(`LIFF error: ${e.message}`);
  });
```

## まとめ
これで、開発環境と本番環境の差分を気にしなくて良くなりました！

しかし、まだ開発初期なので、今後この解決方法が役に立たなくなる時が来るかもしれません。その時は懺悔の記事を書きますのでお楽しみに（？）しておいていただけると幸いです :bow: