Next.js (App Router) でLINE LIFFアプリを爆速スタート！



## はじめに

みなさん、こんにちは！今回は、Next.jsのApp Routerを利用して、LINE LIFFアプリを開発する方法についてご紹介します。私自身、LIFFを使用したアプリケーションの開発をする機会があり、Create LIFF AppがNext.jsのPage Routerにしか対応していなかったため、検証用にApp RouterにLIFFの導入を行ったので、是非、ご参考にしてください!

## 実装時のパッケージのバージョン
```json:package.json
"dependencies": {
   "@line/liff": "2.23.1",
   "next": "14.0.4",
   "react": "^18",
   "react-dom": "^18"
}
```

## 0. App Router対応したリポジトリが欲しい方
こちらが今回検証に作成したリポジトリになります。
cloneして、よしなに利用してください

https://github.com/hisuzuya/App-Router-LIFF.git


## 1. 開発環境の構築

まず、Next.jsアプリケーションのための開発環境を整えましょう。
App Router 以外は、お好みでカスタマイズしてください。

```bash
npx create-next-app@latest

> create-next-app@14.0.4
> Ok to proceed? (y)
> ✔ What is your project named? … my-app
> ✔ Would you like to use TypeScript? … No / Yes　  (Yes)
> ✔ Would you like to use ESLint? … No / Yes　  (Yes)
> ✔ Would you like to use Tailwind CSS? … No / Yes　  (Yes)
> ✔ Would you like to use `src/` directory? … No / Yes　  (Yes)
> ✔ Would you like to use App Router? (recommended) … No / Yes　  (Yes) ← こちらのみYes必須
> ✔ Would you like to customize the default import alias (@/*)? … No / Yes　  (Yes)
> ✔ What import alias would you like configured? … @/*
> Creating a new Next.js app in /※※※/※※※/my-app.

```
参考画像
![スクリーンショット 2024-01-19 10.26.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3231246/b6405150-32cb-0084-ca62-3afeeac80e38.png)


## 2. LIFFパッケージのインストール
LIFFのnpmパッケージをインストールします。

```bash
npm install @line/liff
```


## 3. ngrokの準備
ngrokが入っていない方は、[こちら](https://qiita.com/mininobu/items/b45dbc70faedf30f484e)の記事がわかりやすいです

https://qiita.com/mininobu/items/b45dbc70faedf30f484e

ngrokを用意し、ホスティングを起動します。

```bash
ngrok http 3000
```

上記コマンド実行後に以下のようにURが生成されるのでそれをメモしておきます。
`https://bb56-2400-2200-64d-f093-473-aa42-902a-342.ngrok-free.app/`


![スクリーンショット 2024-01-19 10.24.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3231246/96c796e9-2ea9-1674-bb7f-098f609cc241.png)



## 4. LIFF ID の用意
次に LIFF ID を用意します。

LINE Developersにログインし、プロバイダーを作成したらチャネルの作成から「LINE ログイン」を選択します。
LIFFタブから LIFF を作成します。エンドポイント URL には先程メモした ngrok の URL を記入します。

![スクリーンショット 2024-01-19 10.13.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3231246/b8c89f1e-6233-241f-1b80-440b6eafc410.png)

登録後に表示されるLIFF IDを環境変数に記載してください。

```.env.local
NEXT_PUBLIC_LIFF_ID=*****
```

## 5. 必要なファイルの用意

useLiffのカスタムフックスを作成しています。
[こちら](https://github.com/hideokamoto/use-line-liff)の`use-line-liff`パッケージのコードを参考させていただきました。

### LiffProvider.tsx
```react:LiffProvider.tsx
'use client';
import React, {
  createContext,
  FC,
  PropsWithChildren,
  useCallback,
  useContext,
  useEffect,
  useState,
} from 'react';
import { Liff } from '@line/liff';

const LiffContext = createContext<{
  liff: Liff | null;
  liffError: string | null;
}>({ liff: null, liffError: null });

export const useLiff = () => useContext(LiffContext);

export const LiffProvider: FC<PropsWithChildren<{ liffId: string }>> = ({
  children,
  liffId,
}) => {
  const [liff, setLiff] = useState<Liff | null>(null);
  const [liffError, setLiffError] = useState<string | null>(null);

  const initLiff = useCallback(async () => {
    try {
      const liffModule = await import('@line/liff');
      const liff = liffModule.default;
      console.log('LIFF init...');

      await liff.init({ liffId });

      console.log('LIFF init succeeded.');
      setLiff(liff);
    } catch (error) {
      console.log('LIFF init failed.');
      setLiffError((error as Error).toString());
    }
  }, [liffId]);

  // init Liff
  useEffect(() => {
    console.log('LIFF init start...');
    initLiff();
  }, [initLiff]);

  return (
    <LiffContext.Provider
      value={{
        liff,
        liffError,
      }}
    >
      {children}
    </LiffContext.Provider>
  );
};

```

lauout.tsxファイルにchildrenをLiffProviderでラップする
```react:layout.tsx
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import '@/styles/globals.css';
import { LiffProvider } from './components/LiffProvider';

const inter = Inter({ subsets: ['latin'] });

export const metadata: Metadata = {
  title: 'LIFF App for App Router',
  description: 'Generated by create next app',
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang='en'>
      <body className={inter.className}>
        <LiffProvider liffId={process.env.NEXT_PUBLIC_LIFF_ID || ''}>
          {children}
        </LiffProvider>
      </body>
    </html>
  );
}

```


あとは、使いたいコンポーネントで、以下のように`useLiff`を呼び出すだけです！
```react:Profile.tsx
'use client';
import { useLiff } from '@/app/components/LiffProvider';
import { Profile } from '@liff/get-profile';

import { useState } from 'react';

export function Profile() {
  const [profile, setProfile] = useState<Profile | null>(null);
  const { liff } = useLiff();

  useEffect(() => {
    if (liff?.isLoggedIn()) {
      (async () => {
        const profile = await liff.getProfile();
        setProfile(profile);
      })();
    }
  }, [liff]);

  return (
    <div>
      {profile && (
        <>
          {/* eslint-disable-next-line @next/next/no-img-element */},
          <img
            src={profile.pictureUrl}
            alt='profile'
            className='rounded-full w-20 h-20 mx-auto mb-4'
          />
          <p className='text-center font-bold text-xl'>userId: {profile.userId}</p>
          <p className='text-center text-gray-500'>displayName: {profile.displayName}</p>
        </>
      )}
      {profile ? (
        <button
          onClick={() => {
            liff?.logout();
            location.reload();
          }}
          className='bg-red-500 text-white px-4 py-2 mt-4 rounded-md hover:bg-red-600'
        >
          logout
        </button>
      ) : (
        <button
          onClick={() => liff?.login()}
          className='bg-blue-500 text-white px-4 py-2 mt-4 rounded-md hover:bg-blue-600'
        >
          login
        </button>
      )}
    </div>
  );
}

```

## 起動方法

以下コマンド実行後、ngrokのURLをアクセスします。

```bash
npm install
npm run dev

# 先ほど起動している状態なら必要ないです
# ※起動する度にURLが変わるので、LINE Developersで改めてURLを設定してください
ngrok http 3000　
```


## まとめ
これで、LIFFアプリをNext.jsのApp Routerを使用して開発する準備が整いました！
今回はngrokで確認をしていますが、最終的にはvercelでホスティングなどしてください。

NextjsでのLIFFアプリの記事が、増えることを祈っています☺️