20210226のReactに関する記事は5件です。

create-next-app --example with-reduxを使った際の各ファイルの相関図

初投稿。

ど素人非エンジニアがNext.js、Reduxを学ぶ中で、
create-next-app --sample with-reduxが非常に便利なので、これを用いて勉強中です。
プロジェクト内に生成される各ファイルの関係性について自分の理解を深めるために、手元メモを備忘録として作ったので、自分のために参考メモとして残しておきたいと思います。

create-next-appしてみる。

Next.jsとreduxを使う場合

npx create-next-app --example with-redux [project_name]

フォルダ構成はこんな感じ

スクリーンショット 2021-02-26 23.28.55.png

各ファイルの関係性

拙い自分の理解です・・・
細かい部分など省いていますし、間違いなどが多々あるかもしれないです。
スクリーンショット 2021-02-26 23.32.11.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactModal と EventListener と useCapture

react 2年目がいろいろ学んだことやハマったことの記録。
ファンクションキーのイベントリスナー登録で悩んだお話です。

ボタンにファンクションキーを割当てる

この機能は、他の方によってすでにフック関数で実装されていました。
ファンクションキー押下の検知は、keyDownイベントリスナーを登録することで制御しています。

export const useFunctionKey = (
  code: string, 
  disabled: boolean, 
  callback: VoidFunction
): void => {
  const fn = useCallback(
    e => {
      if (e.key === code && !disabled) {
        callback();
        e.cancelBubble = true;
        e.returnValue = false;
        e.stopImmediatePropagation();
        e.preventDefault();
      }
  }, []);

  useEffect(() => {
    document.addEventListener('keydown', fn, false);

    return () => {
      document.removeEventListener('keydown', fn, false);
    };
  });
};

モーダルと親画面でファンクションキーを割り当てる

いくつかの画面でモーダルを表示する機能があり、モーダルにもファンクションキーを割り当てたボタンが配置されています。親画面と同じファンクションキーであったり、そうでないものもあります。

モーダルが表示されている状態でファンクションキーを押したら、現状どうなるのか?それを調べた上、以下の機能を満たすよう、必要な修正をすることになりました。

  • モーダルが表示されている間はモーダルのファンクションキーのみ有効にする
  • 上記以外のキーは、親画面に割り当てられたファンクションキーも含めて無効になる
  • モーダルが閉じられたら、親画面のファンクションキーのみ有効になる(モーダルのファンクションキーは解除される)

イベントリスナー登録・解除

モーダルもすでに作成されていましたが、ファンクションキーの制御は入ってません。
モーダル上で入力されたキーを判定してイベント伝搬を制御する処理を追加します。

const AllFunctionKeys = ['F1',...()...,'F12'];

ReactModal.setAppElement('body');

const Modal: React.FC<Props> = ({
  show,
  functionKeys = [],
  children,
}) => {
  const handleModalKeyDown = React.useCallback(
  e => {
    if (!AllFunctionKeys.includes(e.key) || functionKeys.includes(e.key)) {
      // ファンクションキー以外の入力、またはボタンに割り当てたキーであればスルー
      return;
    }

    // 対象外のファンクションキーの場合はイベント伝搬を止める(キーを無効にする)
    e.cancelBubble = true;
    e.returnValue = false;
    e.stopImmediatePropagation();
    e.preventDefault();
  },
  [functionKeys],
  );

  return (
    <>
      <ReactModal
        isOpen={show}
        shouldReturnFocusAfterClose={false}
      >
        {children}
      </ReactModal>
    </>
  );
};

次にこのイベントリスナーをどのタイミングで登録して解除すべきか。要件は「モーダルが開いている間のみ」です。
モーダルはReactModalを用いた共通コンポーネントとして実装されています。
ReactModalには、モーダルを開いたとき・閉じたときのコールバック関数を定義できるとわかったので、モーダルが開いたタイミングでイベントリスナーを登録し、閉じたタイミングで解除する、としました。

// モーダルを開いた時
const handleAfterOpen = React.useCallback(() => {
  document.addEventListener('keydown', handleModalKeyDown, true);
}, [handleModalKeyDown]);

// モーダルを閉じた時
const handleAfterClose = React.useCallback(() => {
  document.removeEventListener('keydown', handleModalKeyDown, true);
}, [handleModalKeyDown]);

  return (
    <>
      <ReactModal
        isOpen={show}
        shouldReturnFocusAfterClose={false}
        onAfterOpen={handleAfterOpen}    // 追加
        onAfterClose={handleAfterClose}  // 追加
      >
        {children}
      </ReactModal>
    </>
  );

モーダルのイベントリスナーのほうを優先してほしいため、addEventListenerの第3引数をtrueに指定します。

イベント伝搬の制御

上記の実装でテストしてみます。
親画面に、F1,F3を割り当てたボタンを、モーダルにF1,F2を割り当てたボタンを配置します。

const EventListenerTester: React.FC = () => {
    const [show, setShow] = React.useState(false);
    const handleClick = React.useCallback((message: string) => {
        console.log(message);
    }, []);

    const handleClickModal = React.useCallback(() => {
        setShow(!show);
    }, [show]);

    return (
        <>
        <FunctionKeyButton shortcutKey={'F1'} onClick={() => handleClick('page F1')} >ボタン</FunctionKeyButton>
        <FunctionKeyButton shortcutKey={'F3'} onClick={() => handleClick('page F3')} >ボタン</FunctionKeyButton>
        <Button onClick={handleClickModal} >モーダル</Button>
        <Modal functionKeys={['F1', 'F2']} show={show}>
            <>
                <div>モーダルのテスト</div>
                <div>
                    <FunctionKeyButton shortcutKey={'F1'} onClick={() => handleClick('modal F1')} >ボタン</FunctionKeyButton>
                    <FunctionKeyButton shortcutKey={'F2'} onClick={() => handleClick('modal F2')} >ボタン</FunctionKeyButton>
                    <Button onClick={handleClickModal}>閉じる</Button>
                </div>
            </>
        </Modal>
        </>
    );
};

まず親画面のみ出ている状態で、F1->F2->F3の順でファンクションキーをクリックします。

page F1
page F3

これは想定通りの動きとなってます。
その後、モーダルを表示して同じようにF1->F2->F3の順でファンクションキーをクリックします。

page F1
modal F2

となりました。期待したのは両方ともモーダルで登録したほうのリスナーが実行されることでしたがF1のみNGでした。
最後のF3については正常にキャンセルされた、ということも確認できました。

モーダルで使うファンクションキーは、そのままボタンのリスナーへイベント伝搬してほしいため、スルーするよう実装しました。しかし、ボタンに登録したリスナーはデフォルトの「登録された順番で実行」されるため、親画面のほうのリスナーが最初に実行されて、イベント伝搬が止められました。そのため、モーダルのボタンで登録されたリスナーが実行されなかったということになります。

ボタンに登録する場合のリスナーも「モーダルに配置するボタンなのかどうか」を考慮してリスナーの実行順序をコントロールしないといけないということです。

フック関数に引数を追加することにします。
ファンクションキーは親画面で使うほうが多いと思われたため任意設定とし、未指定時はfalseでよいようにします。

export const useFunctionKey = (
    code: FunctionKey,
    callback: VoidFunction,
    disabled: boolean,
    modal?: boolean, // 追加
): void => {

// 省略

useEffect(() => {
  document.addEventListener('keydown', fn, modal); // 第3引数を修正

return () => {
  document.removeEventListener('keydown', fn, modal); // 同上
};

テストのほうも、モーダルに配置するボタンは「モーダル指定」に修正します。

<div>モーダルのテスト</div>
<div>
  <FunctionKeyButton shortcutKey={'F1'} onClick={() => handleClick('modal F1')} modal>ボタン</FunctionKeyButton>
  <FunctionKeyButton shortcutKey={'F2'} onClick={() => handleClick('modal F2')} modal>ボタン</FunctionKeyButton>
  <Button onClick={handleClickModal}>閉じる</Button>
</div>

さきほどと同じようにF1〜F3まで順番にファンクションキーを押していきます。

page F1
page F3

さきほどと同様な動きのままで影響ありません!
そしていよいよモーダル表示時です。

modal F1
modal F2

無事にモーダルのほうのイベントリスナーが実行されました。
これで、機能要件の1つめ、2つめはOKとなりました。

最後は3つめ、モーダルを閉じたらモーダルで登録したイベントリスナーは削除し、もとの親画面のイベントリスナーが有効になることです。

実はここもNGでした。。
モーダルのイベントリスナー登録の条件が間違っており、閉じたときに正常にイベントが削除されていませんでした。
原因は、前回の記事と同様のため詳細は書きませんが、useCallbackのdepsに、リスナーを指定してしまったこと。イベントリスナー登録時と解除時で中身が変わってしまうため、リスナーの解除ができてませんでした。

こちらもこのように書き換えて、機能要件3つめも無事解決となりました。

    const handleAfterOpen = React.useCallback(() => {
        document.addEventListener('keydown', handleModalKeyDown, true);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    const handleAfterClose = React.useCallback(() => {
        document.removeEventListener('keydown', handleModalKeyDown, true);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScript + React プロジェクト作成法

  • TypeScript1 + React2 環境構築のために検索していたけれど、 だいたいのブログでこのコマンドを推奨される。
    npx create-react-app react-ts-app --typescript
    しかしこのコマンドを打ってもうまくプロジェクトが作れず、地味に詰まったのでメモ。

前提 :

  • Node.js インストール済 (筆者環境v14.15.1)

パッケージ管理別.bin 実行コマンド

npm(Node.jsのデフォルトパッケージ管理) の場合 : npx
yarn の場合 : yarn run

手順 :

1. tsconfig.jsonの作成・初期化

  • ローカルインストールされたスクリプトの実行
npx tsc --init

2.TypeScript + React プロジェクト作成コマンドを打つ

npx create-react-app {プロジェクト名} --template typescript
npx create-react-app react-ts-app --template typescript

備考 :

version 確認コマンド

npx tsc --version

  1. 動的型付けの JavaScript を静的型付けにした言語 

  2. JavaScript フレームワークの一つ。
    xml で html コンテンツを JavaScript に記述できる。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【NoCode】5年目iOSエンジニアが、アプリのE2Eテスト自動化サービスを作った話

iOSエンジニア歴も早5年目となりました。

その経験を活かして、iOSアプリのテスト自動化サービス「SmartQA」を作りました!

今回の開発では、Firebaseが大活躍してくれました。

SmartQAをリリースする上で、AppiumやFirebaseの技術的な知見をここに残せたらと思います。

概要

cover-icon.png

SmartQA - E2Eテスト自動化をノーコードで

ビルドをアップロードするだけで、ブラウザ上で簡単にテストの自動化ができます。

image.png

UIが変わってしまった場合でも、要素探索を自動で修復してくれます。

複数端末対応しているので、「iPhone SEiPadだとクラッシュしてしまった」なんてことも検知できます。

現在はiOSアプリのみ対応しております。

私について

10歳の頃にアセンブリに出会ったのがきっかけで、プログラミングの世界にのめり込みました。

どんなプログラムも足し算・引き算・比較・ジャンプで動いてることに感動したことを今でも覚えています。

京都大学工学部情報学科を卒業して、Levetty株式会社を立ち上げました。

在学中にiOSエンジニアとして働き始めました。

当時京都では学生がエンジニアとして働ける会社がほとんどなく、大学生協のアルバイト募集に稀に出現するエンジニア募集を見つけるしかありませんでした。

IMG_6460.JPG

大学生協を毎日クロールするスクリプトを組んで、LINEに通知して最速で応募したりしてました。

開発体制

開発期間としては、9月に実現できるのか技術検証を行い、10月から開発を始めました。

基本的に僕1人で開発を進めていたのですが、仲の良いエンジニア3人に手伝ってもらえたのがすごく大きかったです。

設計を綺麗にしてくれたり、インフラ整えてくれたり、なかなか手が回らない部分の実装をしてくれたり、サービスのアイデアをいただけたり。

とても感謝しています。

使用した技術

Appium

iOSやAndroidでシミュレータを操作するのに必要なライブラリです。

iOSで使う場合は、内部でXCUITestを使用しています。

Firebase

  • Authentication
  • Cloud Firestore
  • Cloud Functions
  • Cloud PubSub
  • Cloud Storage
  • Hosting

React.js / Redux / TypeScript

フロントは、SPA(シングルページアプリケーション)で作成しました。

Material-UIが大活躍してくれました。

Express / Node.js / TypeScript

テスト実行サーバー、macOS上で動作する簡易的なサーバーは全てExpressで実装しました。

AWS CDK / Ansible

サーバー側の実装は、Cloud Functionsに寄せていました。

しかし、540秒の実行時間の制限がきつく、テスト実行処理はAWSのEC2で運用することになりました。

手伝ってくれている凄腕エンジニアがAWS CDKAnsibleを使って構築してくれていました。

macOSを何台も用意する必要があるので、Ansibleを触ったのですが、革命を感じました。

CDKについては、僕は触ってないので分かりません、、。が、めちゃくちゃ良いみたいです。

苦労したこと

大量のMacのセットアップ

iOSのシミュレータを使用するには、macOS環境が必要です。

SmartQAでは、macOSを貸し出してくれるMacStadiumというサービスを利用することにしました。

借りたMacを1つ1つ設定していくのですが、それはもう大変でした。

途中からAnsibleで自動化され楽にはなったものの、それでもXcodeをインストールするのは手動じゃないとダメだったりします。(2ファクタ認証の関係で)

平成時代から続くiOSシミュレータ自体のバグ

5年前に、AppleのDeveloperフォーラムに投稿されている内容です。
Hardware keyboard not working in simulator (toggle in on)

Macのキーボードをシミュレータに接続しない設定で起動しても、接続されているバグがあるんですよね。

キーボードが接続されていると、UITextFieldにフォーカスするために、2回タップが必要だったり。

実際とは違う挙動をしてしまうんですよね。

もちろんApple信者としては、仕様として受け入れることにしました

調べた限り対策は1つしかなく、ここを2回クリックしてオンオフするしかないみたいです。

image.png

AppleScriptというものをこの時初めて知り、起動直後にConnect Hardware Keyboardを2回クリックするスクリプトを用意しました。

tell application "System Events"
  set hwKB to value of attribute "AXMenuItemMarkChar" of menu item "Connect Hardware Keyboard" of menu 1 of menu item "Keyboard" of menu 1 of menu bar item "I/O" of menu bar 1 of application process "Simulator"
  click menu item "Connect Hardware Keyboard" of menu 1 of menu item "Keyboard" of menu 1 of menu bar item "I/O" of menu bar 1 of application process "Simulator"
  click menu item "Connect Hardware Keyboard" of menu 1 of menu item "Keyboard" of menu 1 of menu bar item "I/O" of menu bar 1 of application process "Simulator"
  if ((hwKB as string) is equal to "missing value") then
    do shell script "echo 'hardware keyboard is off'"
  else
    click menu item "Connect Hardware Keyboard" of menu 1 of menu item "Keyboard" of menu 1 of menu bar item "I/O" of menu bar 1 of application process "Simulator"
  end if
end tell

iOSアプリのUI要素のXMLの情報量が極端に少ない!

Appiumでは、XCUITestdebugDescriptionを利用してWebでいうところのDOMを取得しています。

そのDOMをXML形式で取得できるのですが、classidなどの情報があるWebに比べて情報量が圧倒的に少ないです。

<XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="パスワード" name="パスワード" label="パスワード" enabled="true" x="24" y="87" width="75" height="20"/>
<XCUIElementTypeSecureTextField type="XCUIElementTypeSecureTextField" value="Abc1234" name="" label="" enabled="true" x="190" y="87" width="161" height="21"/>
<XCUIElementTypeTextField type="XCUIElementTypeTextField" value="ピッカー入力" name="" label="" enabled="true" x="24" y="131" width="327" height="22"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="Login" label="Login" enabled="true" x="165" y="185" width="45" height="34">
  <XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="Login" name="Login" label="Login" enabled="true" x="165" y="191" width="45" height="22"/>
</XCUIElementTypeButton>

こちらがXMLの一部です。

UIButtonUITextFieldUILabelについて得られる情報は、要素の種類座標サイズがあります。

<XCUIElementTypeImage type="XCUIElementTypeImage" enabled="true" x="171" y="598" width="45" height="44"/>
<XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" x="235" y="612" width="17" height="16"/>

しかし、UIImageViewUIViewの場合は、座標サイズだけが頼りになります。

そこで、何かしらの方法を用いて要素の推定を行う必要があります。

今回は、ルールベースAIを組み合わせて推定することにしました。

ちなみに、ソースコードを変更できるのであれば、Accessibility Identifierを割り当てると、nameが固有の値になるので、追従がしやすくなります。

Appiumの謎のエラーでシミュレータが起動しなくなる

クラウドのmacOSで、いつも通り何度もシミュレータを立ち上げていたところ、Appiumでエラーが出力され、シミュレータが強制終了するようになりました。

image.png

Appiumの再インストール、Xcodeの最新版をインストールなど様々な方法を試しても治りませんでした。

ふと、macOS自体を再起動したところ正常に動き始めました。

????

憶測ですが、シミュレータを立ち上げごとにゾンビプロセスが生成されてしまってるのかもしれません。

この日を境にmacOSは、定期的に再起動される運用になりました

よかったこと

Appiumサーバーを複数立てることで並列化が簡単

テストの実行を早く終わらせるために、並列化をしようと試みました。

Appiumサーバーを複数起動することで実現することができました!

(WebDriverAgentAppiumのポートがかぶらないように気をつけてください)

FirestoreのCollection Groupが強力

SmartQAでは、セキュリティを最大限考慮した設計になっています。

project配下に情報を置くことで、セキュリティルールで縛りやすくしています。

その代わり、横断検索が大変になってしまいます。

2019年にリリースされたCollection Groupで、解決することができました。

/project/{projectID}/collectionA/{documentID}

通常、このような階層でcollectionAに対して検索する場合、それぞれのプロジェクトに対して実行しなければいけません。

しかし、CollectionGroupでは、同じコレクション名であれば横断的に検索をすることができます。

どんな階層に存在していても可能です。

インデックスを設定するだけで使用できて、非常に簡単でした。

さいごに

インフラからバックエンドフロント、更にはmacOSのシミュレータの資源割付まで、幅広い実装でエンジニア人生の集大成ともいえるプロダクトでした。

テスト実行の安定化や、機能追加に励んでいきたいと思います!

長期的には、Android対応などやることがたくさんあります。

全然人手が足らなくて、エンジニアさんやQAさんを絶賛募集しております。
少しでも興味があれば、contact@levetty.co.jp@koyatarooに連絡お待ちしております!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Next.js + RailsでポートフォリオサイトをISR対応&メンテナンスフリー化した

2年ほど前にNuxt.jsを使ってポートフォリオサイトを作成しました。
今回、このサイトをNext.js + Railsでリニューアルしたので、経緯を記事にまとめます。

リニューアル後のページ

https://portfolio.y-uuu.net/
image.png

デザインは前回のものを踏襲していて、ほとんど変わっていません。

リポジトリ

リニューアルの目的

Next.jsを使って何か作りたい

nextjs.png

昨年からReactやNext.jsを触ってノウハウを蓄積するようにしています。私自身普段はRailsを使った開発をしているので、Next.jsを採用するとしたらRailsと組み合わせて使う可能性が高いです。

昨今のフロントエンド界隈の盛り上がりを横目に、フロントエンドにNext.js/バックエンドにRailsを用いて、何か作りたいと思っていました。

デプロイなしで内容を更新したい

image.png

前回、勢いでポートフォリオサイトを作成したものの、単なる静的ページとして公開していたので記載内容を変更するためにはVueコンポーネントを直接編集する必要がありました。

今回は管理ページを別途作成し、ログインすることで記載内容を容易に追加・変更できるようにしています。

メンテナンスフリーにしたい

image.png

image.png

リニューアル後のサイトではQiitaやZennに公開した記事、SpeakerDeckに公開したスライドをを自動的に収集し、メンテナンスしなくても内容が自動更新されるようになっています。

システム構成

Next.jsのデプロイ先としてVercelを、Railsのデプロイ先としてherokuを使っています。
また、画像の格納先としてAWSのS3を利用しました。

Untitled_Portfolio_Site_-_Cacoo.png

ライブラリ・フレームワーク

フロントエンド

  • React
  • Next.js
  • React Hook Form
  • react-dropzone
  • react-spinners
  • react-tippy
  • axios
  • SWR
  • Tailwind CSS

バックエンド

  • Ruby on Rails
  • devise
  • faraday
  • rails_same_site_cookie
  • AWS SDK for Ruby V3

実装上のポイント

ISR(Incremental Static Regeneration)

Next.jsを使ってISRを実現しています。herokuがレスポンスを返す時間に関わらず、来訪者がすぐにページを閲覧できるようにする狙いです。

以下はISRの挙動の解説です。

Next.js(ISR有効)は、アクセスがあった際に生成済みの静的ページをレスポンスします。このとき、herokuへのアクセスは発生しません。

Notification_Center.png

前回のページ生成から指定した時間を経過した後にアクセスが発生すると、静的ページを再生成します。このときNext.jsはサーバーサイドでページを再生成するのを待たず、いったん前回の静的ページをレスポンスします。

Untitled_Portfolio_Site_-_Cacoo.png

再生成が完了すると、以降その静的ページをレスポンスします。

前提として、herokuのFreeプランだと30分間アクセスがない場合にdynoがSleepするので、次にアクセスがあった場合にdynoが起動するまで数十秒ほど待たされてしまうという問題があります。本来の使い方ではないかもしれませんが、バックエンドの処理に時間がかかる場合でも生成済みのページを即時にレスポンスできるという点で、ISRは有用だと感じました。

記事・スライドの自動収集

yuuu-portfolio-v2-api_·_Scheduler___Heroku.png

Heroku Schedulerを使うことで、日次でrakeタスクを実行して、記事・スライドを自動収集しています。

QiitaはAPIを、ZennはFeedを使って、自分自身の記事を収集しDBに保存しています。

namespace :qiita do
  desc "Fetch articles from qiita"
  task fetch: :environment do
    res = Faraday.get('https://qiita.com/api/v2/users/Y_uuu/items?per_page=100')
    return if res.status != 200

    items = JSON.parse(res.body)
    items.each do |item|
      next if Article.find_by(link: item['url']).present?

      item_res = Faraday.get(item['url'])
      next if res.status != 200

      Article.create(
        title: item['title'],
        body: item['body'].truncate(100) + '...',
        published_at: Time.zone.parse(item['created_at']),
        link: item['url'],
      )
    end
  end
end

SpeakerDeckは収集方法を悩んだのですが、よくよく調べると https://speakerdeck.com/yuuu.atom のように、自身のアカウント名の末尾に .atom を付与することでFeedを取得できることがわかったので、これを使って収集するようにしました。

認証

image.png

最初は「SPAの認証はJWT」という思い込みがあったのですが、いろいろ調べていくうちに「cookieを使った認証でも問題ない」との結論に至りました。認証のバックエンドもRailsで、deviseというGemを使ったよくある実装です。

ただし、今回はCrossOriginな構成のためつまづきポイントが多くありました。具体的な実装方法は別記事にまとめたので、興味のある方は参照ください。

Rails 6.1対応版: APIモードのRailsに対してCrossOriginなSPAからSession認証する方法

ファイルアップロード

image.png

当初は、バックエンドがRailsということで、Active Storageを使ってファイルアップロードを実現する予定でした。実装をしていく上で「わざわざActive Storageを使う必要があるのか?」という疑問が生じ、最終的にはS3の署名付きURLを使ってアップロード・閲覧する方式に変更したという経緯があります。

こちらも別記事にまとめたので、興味のある方は参照ください。

RailsをバックエンドとしたSPAでのファイルアップロード機能の作り方に悩んだ話

感想

ISRが良い

image.png

SSRとSSGのいいとこ取りができていて良いです。SSGのようにデプロイのビルドが長くなることもなく、かつSSRを使った場合に比べてページの表示が高速なので満足です。

Vercelが良い

image.png

今回初めてVercelを使ってみたのですが、GitHubのリポジトリを指定するだけで簡単にCI/CDを構築できました。前回Netlifyを使った時も同様の感動があったのですが、とかくNext.jsを使う場合はほとんど設定が不要で、噂通りVercelとの組み合わせがベストだと実感しました。

バックエンドのRails・herokuも良い

image.png

死んだと言われて久しいRailsですが、自分にとってはやはり最速で実装ができるフレームワークです。バックエンドは必要最低限実装しつつ、フロントエンドの実装に注力するスタイルで開発が進められました。

herokuを使うことでデプロイも非常に簡単でした。

まとめ

個人的には十分満足できるポートフォリオサイトが完成しました。

Next.js + Railsで何か作ろうとしている人の参考になれば幸いです。質問・感想などありましたら、ぜひコメントをお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む