2020年5月4日

ReactでSVGファイルをコンポーネントとしてレンダリングする

egghead.ioのレッスンを観ていて良さそうなものがあったので、メモしておきます。


Add SVGs as React Components with Create React App 2.0
https://egghead.io/lessons/react-add-svgs-as-react-components-with-create-react-app-2-0

Create React App のドキュメントにも記述があります。
https://create-react-app.dev/docs/adding-images-fonts-and-files


Create React Appで作ったReactアプリケーションでは画像ファイルを下のようにインポートしてコンポーネント内で参照することが出来ます。

import logo from './logo.png';
function Header() {
  return <img alt="Logo" src="{logo}" />;
}

もちろん画像の形式がSVGであっても同様です。

import logo from './logo.svg';
function Header() {
  return <img alt="Logo" src="{logo}" />;
}

ただ、SVG形式の場合はインポートの仕方を下のように変えるとReactコンポーネントとしても使えるようになるそうです。

import { ReactComponent as Logo } from './logo.svg';
function App() {
  return (
    <div className="App">
<header className="App-header">
<Logo className="App-logo" />
</header>
</div>);
}


このようにすると、HTMLとしてレンダリングされる際のタグが imgタグではなく、svgタグになります。

svgタグになってうれしい点は、SVG内の個々の要素(図形)をCSSでコントロール出来るようになることです。


例えば、線の色や太さを変えたり、アニメーションを付け加えたりすることが可能になります。


下の例では、Reactロゴのpathエレメントの部分のみ点線にして、さらに点線のオフセットをアニメーションで動かしています。

.App-logo path {
  stroke: palegoldenrod;
  stroke-width: 10px;
  fill: none;
  stroke-dasharray: 35px 15px;
  animation: orbit 1s infinite linear;
} @keyframes orbit {
  to {
    stroke-dashoffset: 50px;
  }
}

実行するとこんな感じになります。









2020年4月20日

AWS AmplifyとDynamoDBでサーバーレスなREST APIを構築する

前回はユーザー認証機能を付けましたが、まだデータの永続化が出来ていないのでTodoアプリとしては未完成です。



  1) Next.jsでスタティック・エクスポートしたサイトをAWS Amplify Consoleでホスティングする

  2) AWS AmplifyでReactアプリにユーザー認証機能を追加する

  3) AWS AmplifyとDynamoDBでサーバーレスなREST APIを構成する




今回はサーバーレスなREST APIを追加してデータをデータベースに保存出来るようにします。

amplify status で現状を確認すると、下のようになっています。



下のコマンドでAPIカテゴリを追加します。

amplify add api 

まず GraphQL か REST かを選ぶように言われます。

Web上で見つかる情報にはGraphQLを使ったものが圧倒的に多いように思いますが、ここではあえてRESTを選びます。



DynamoDB上に作成するテーブル名まで入力すると、次に作成するカラム情報の入力になります。

ドキュメントをざっと読んだところ、DynamoDBでは基本的に1アプリケーションで使うテーブルは出来るだけ少ない方が望ましいらしく、「1テーブルで済ませられればそれが最も良い」との事です。



長年リレーショナルデータベースに馴染んだ身としてはなかなか目から鱗な考え方です。

どうやらDynamoDBでのスキーマ設計の勘所は、「パーティションキー」と「ソートキー」および「グローバル・セカンダリインデックス」「ローカル・セカンダリインデックス」を上手く使うことにあるようです。

DynamoDBのテーブル設計をするとき、自分に問いかけていること – 或る阿呆の記 ( https://hack-le.com/dynamodb-query/ )

この辺り、非常に奥が深そうで面白いのですがとりあえず今はシンプルなTodoリストアプリを作りたいだけなので、極力簡単な方法で行きたいと思います。



パーティションキーを「pk」、ソートキーを「sk」とし、3つ目のカラムはmap型で「data」としておきました。map型にはJSONを格納できるので、こうしておけば後からの仕様変更にもある程度柔軟に対応することが可能になるかと思います。

パーティションキーとソートキーを何にするかというのは、アプリケーションの要件によって大きく変わる部分で、開発効率にも大きく関わってきます。


今回は、pkカラムには認証されたユーザーのidに "user:" というプリフィックスを付加して格納し、skカラムにはTodoアイテムのid(ランダムに生成されたもの)の先頭に "todo:"というプリフィックスを付加して格納することにしました。

こうすることでユーザーのidが分かればそのユーザーに属するタスクの一覧を簡単に取得することが出来るようになります。

実際のクエリーとしては、
- pkが "user:" + ユーザーid に一致する
- skが "todo:" で始まる
という条件で検索することになります。

DynamoDBではパーティションキーは完全一致でしか検索出来ませんが、ソートキーは部分一致や範囲検索が可能なので、このようなクエリーが可能になります。

また、ユーザーに属するデータでTodo項目以外のデータを保存したい場合には、skに付けるプリフィックスを "todo:" 以外のものにすれば対応出来ます。

例えば、ユーザーごとのアプリ設定を保存したい場合は、pkは同じでskを "pref:" として保存すれば、簡単にそのレコードを一意に指定して読み出す事が出来ます。

1アプリケーションで一つのテーブルしか使わない、というのはこのように2つのキー(およびセカンダリインデックス)を上手く使って複数の種類のデータを保存する、ということになるのかなと思います。


カラム設定の入力が終わると、テーブルへのアクセス権の設定をするか聞かれるので、認証していないユーザーはAPIへのアクセスが出来ない様に設定しておきます。




次に amplify push を実行して、追加した設定をAWS側に反映します。


完了するとAWS側では下記のリソースが作成されています。

- API Gateway
- Lambda
- DynamoDB


これらのうち、Lambda関数は自動生成されたコードだと上手く動かず、何をしているのかを理解した上でアプリケーションの要件に合わせてそこそこ手を加える必要がありました。


/amplify/backend/function/(Function名)/src/app.js (一部のみ抜粋)
-----

-----


アプリケーション側で扱うModelとしては

    {      
        id: 'todo:12312-312132',      
        text: 'ミルクを買う',      
        done: false 
    }

のような形になっているのですが、DynamoDBに保存されるのは、

    {
        pk: 'user:aaaaaaaaaaa',
        sk: 'todo:12312-312132',
        data: {
            text: 'ミルクを買う',
            done: false
        }
    }

という形になっています。

このため app.js の内部でAPI Gatewayから受け取ったオブジェクトをDynamoDBのスキーマに合った形式に適宜変換してから保存しています。



さて、次はReactアプリの方を変更して行きます。


この辺りのドキュメントを見ながらAPIへのアクセス処理を実装します。

Amplify Docs –– Fetching Data 



基本的には、

import { API } from 'aws-amplify';

でAPIクラスをインポートした後、

await API.post(apiName, path, options);  // CREATE
await API.get(apiName, path, options);  // READ
await API.put(apiName, path, options);  // UPDATE
await API.del(apiName, path, options);  // DELETE

でLamdaで作成してあるAPIにアクセスすることが出来るので、各アクションからこれらを適宜呼び出しています。


React側の実装としては、Hooks(useEffect, useReducer)を使って作っています。


/services/todo-service.js
-----

-----



ここまででなんとかシンプルなTodoリストアプリが完成しました。



  • Amplify Consoleによるスタティックなサイトのホスティングは簡単・超便利!

  • Amplify CLIでのユーザー認証機能の追加はパラメータの設定が最初ちょっと大変だけど、何回か試行錯誤して分かってしまえばこちらも簡単で素晴らしい!

  • Amplify CLIでのREST APIの作成は、DynamoDBの特性を理解して設定する事が超重要!(*



* もちろんDynamoDB以外の任意のデータベースを使うことも可能。


現在のソースコードは下記から確認可能です!

https://github.com/mikehibm/amplify-test01




  1) Next.jsでスタティック・エクスポートしたサイトをAWS Amplify Consoleでホスティングする

  2) AWS AmplifyでReactアプリにユーザー認証機能を追加する

  3) AWS AmplifyとDynamoDBでサーバーレスなREST APIを構成する






 

2020年4月17日

AWS AmplifyでReactアプリにユーザー認証機能を追加する

前回はNext.jsでエクスポートしたWebアプリをAmplify Consoleでホスティングするところまで行いました。

今回はこのアプリにAWS Cognitoによるユーザー認証を追加したいと思います。



まず amplify status で現在の状態を確認しておきます。



次に、 amplify auth add を実行。




最初に「Do you want to use the default authentication and security configuration?」と聞かれます。

ここでデフォルト設定を使って試したところ、サインアップの際にメールアドレスの他に電話番号の入力も必須になってしまったので、それを回避するために今度は「Manual configuration」を選択して次に進みました。


次に、amplify push を実行して追加した設定をAWS側に反映します。


コマンドが完了すると、AWS側では必要なリソース(Cognito User Pool、Lambdaファンクションなど)が作成されているのが分かります。


次にReactアプリケーションの方でCognitoに接続するために、必要なライブラリを追加します。

npm i aws-amplify @aws-amplify/ui-react


ログイン画面の表示には「Amplify UI Components」を使います。

/pagesフォルダ内にlogin.jsを作成し下のコードを入力しました。

----------

----------

これだけで、http://localhost:3000/login を開くと下のようなログイン画面が表示されるようになります。



もちろん最初はユーザー登録が必要なので、「Create account」のリンクをクリックします。(この辺りの文言はカスタマイズ可能です。)


ここで、電話番号の入力欄が必須となってしまっています。
これを非表示にするためのカスタマイズ方法がドキュメントには載っているのですが、その通りにやってみても上手く行きませんでした。

検索したところ、UI ComponentのバグとしてGitHubのIssueに上がっているようです。ちょうどこの記事を書いている数時間前にFixがマージされているみたいなので、近日中には解決しそうです。今はとりあえずダミーの電話番号を入力して次へ進みます。


登録フォームをSubmitすると、確認コードの入力画面に移ります。




入力したメールアドレスに確認コードが送られているので、そのコードを入力して「Confirm」をクリックすれば登録完了です。



この時点でAWSコンソールでCognitoのUser Poolを見てみると、確かに1件のユーザー情報が登録されているのが分かります。


バックエンド側を何も作り込んでいないのにここまで自動的に出来上がってしまうのはすごいですね!


最後に上で作成したlogin.jsと同様に認証用のUIをindex.jsページにも追加します。

-----

-----

ここではサインアウトボタンは要らないので削除し、代わりに右上のヘッダー内に「Sign Out」リンクを追加ししました。

サインアウトの処理を実装するには、

import { Auth } from 'aws-amplify';

でAuthクラスをインポートし、リンクがクリックされたら

Auth.signOut();

を実行すればOKです。



次回はバックエンド側のREST APIをAmplifyのサーバーレスな機能を使って追加します!

AWS AmplifyとDynamoDBでサーバーレスなREST APIを構成する 




ここまでのソースコードはこちらで確認出来ます!