- 投稿日:2020-06-26T23:21:58+09:00
React-TypeScriptへ移行しむずしかったこと
ReactをTypeScriptで記述した際に難しかったことについて
初めましてQiita初投稿です。
初心者がReactをTypeScriptで記述した際に難しかったことについてまとめてみました。複雑なデータの型定義
外部のAPIからデータを取得する際に取得するAPIのデータの型の定義に苦戦しました。
{ 0: { id: 1, name: 'sample', text: 'Hello World' } }このようなデータの型の定義の仕方に苦戦しました。
私はType
やinterface
を用いて型の定義をしてみようとしていましたが、上手くできませんでした。type.tstype ChildData = { id: number; name: string; text: string; } type ParentData = { [data:string] : ChildData }stack overflowで解決策をいただきました。
このように複雑なデータの型は分解して定義をするのが良いと分かりました。Reactのイベントの型
Reactでイベントを扱う場合には型が必要だと知らずにはじめたので最初は戸惑いました。
sample.tsxconst addState = (e: React.ChangeEvent<HTMLInputElement>) => { setState(e.targer.value) }
React.ChangeEvent<HTMLInputElement>
について最初は分かりませんでした。
現在はイベントに対して型が必要だと理解しています。DOMのイベントの型を定義しているという認識です。tsconfig.json
初めて
tsconfig.json
を見たときは意味が分かりませんでした。
TypeScriptの設定を記述するファイルだと認識しています。
まだ理解できていないところが多いです。現在はtsconfig.json
を編集せずに開発をしていますが、今後はany
を使えないように設定して開発をしてみたいと考えています。
- 投稿日:2020-06-26T23:08:34+09:00
Reactアプリ - 文章中キーワード黒塗りはがし
はじめに
Reactを使って文章中の特定のキーワードの黒塗り文字列をクリックして黒塗りをはがして読めるようにするアプリを作りましたので紹介いたします。
このアプリは単純で、黒塗りをはがすためにはキーワードの黒塗り部分をクリックします。再びキーワードをクリックすると黒塗りに戻ります。
前提条件
node.jsがインストールされている事が必要です。まだインストールしていない場合には、インストールしておいてください。
Reactプロジェクトを作成します
以下のように
create-react-app
コマンドを実行します。少し、時間がかかります。
- macOS
terminal(base) macpro:dev teruroom$ cd /Users/teruroom/dev/react/ (base) macpro:react teruroom$ npx create-react-app blackout-peeling
- Windows10
powershellPS C:\> cd C:\dev\react\ PS C:\dev\react\> npx create-react-app blackout-peelingmacOS実行結果Creating a new React app in /Users/teruroom/dev/react/blackout-peeling. ・・・・・・・・途中省略・・・・・・・・・・・ Success! Created blackout-peeling at /Users/teruroom/dev/react/blackout-peeling Inside that directory, you can run several commands: npm start Starts the development server. npm run build Bundles the app into static files for production. npm test Starts the test runner. npm run eject Removes this tool and copies build dependencies, configuration files and scripts into the app directory. If you do this, you can’t go back! We suggest that you begin by typing: cd react-boardgame npm start Happy hacking!デフォルトで生成されたファイルを全て削除します
- macOS
terminal(base) macpro:react teruroom$ cd blackout-peeling/src (base) macpro:src teruroom$ rm -f *
- Windows10
powershellPS C:\dev\react> cd .\blackout-peeling\src PS C:\dev\react\blackout-peeling\src> rm -Force *必要なJavaScriptとCSSファイルを作成します
- macOS
terminal(base) macpro:src teruroom$ touch index.css index.js
- Windows10
powershellPS C:\dev\react\blackout-peeling\src> New-Item -type file index.css PS C:\dev\react\blackout-peeling\src> New-Item -type file index.jsJavaScriptとCSSを実装します
以下のようにクラスの階層構造を設計しました
Page
クラス:ページ
|
+--Sentence
クラス:ページ内の文章
|
+--Keyword
クラス:文章内のキーワードindex.jsimport React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; const white = '#fff'; const crimson = '#880E4F'; const marginwidth = '5px'; const fontweight = 'bold'; class Keyword extends React.Component { constructor(props) { super(props); this.state = { value: null, forecolor: blackOutColor, backcolor: blackOutColor, margin: marginwidth, fontweight: fontweight, }; this.changeColor = this.changeColor.bind(this); } changeColor() { const newColor = this.state.backcolor === blackOutColor ? white : blackOutColor; this.setState({ forecolor: blackOutColor, backcolor: newColor }) } render() { return ( <span style={{ color: this.state.forecolor , background: this.state.backcolor , marginLeft: this.state.margin , marginRight: this.state.margin , fontWeight: this.state.fontweight }} onClick={() => this.changeColor()} > {this.props.value} </span> ); } } class Sentence extends React.Component { blackOut(keyString) { return <Keyword value={keyString} />; } render() { return ( <div> <div className="maintitle">ホゲホゲについて</div> <ol> <li className="subtitle">FOO</li> <ul> <li>FOOは、{this.blackOut('BAA')}システムから利用申請し、「{this.blackOut('利用許可')}」を得てから使用する</li> <li>FOOを{this.blackOut('ホゲ')}やハゲによる攻撃から守るため、BOO対策製品を導入する</li> </ul> 一部省略 </ol> </div> ); } } class Page extends React.Component { render() { return ( <div className="page"> <div className="page-sentence"> <Sentence /> </div> <div className="page-info"> <div>{/* status */}</div> <ol>{/* TODO */}</ol> </div> </div> ); } } ReactDOM.render( <Page />, document.getElementById('root') );
- index.css
index.cssbody { width: 1100px; font: 18px "Meiryo", "Century Gothic", sans-serif; margin: 0px; cursor: pointer; } ol, ul { padding-left: 30px; } td { width: 50%; } .maintitle { width: 100%; font-size: 30px; color: #fff; background: cornflowerblue; } .subtitle { width: 95%; font-size: 22px; font-weight: bold; color: rgb(26, 75, 182); background: rgb(210, 210, 211); margin: 4px; padding: 4px; } .page { display: flex; flex-direction: row; } .page-sentence { margin-left: 20px; } .page-info { margin-left: 20px; }React Webアプリを開発用に起動します
terminal(base) macpro:blackout-peeling teruroom$ npm startpowershellPS C:\dev\react\blackout-peeling> npm startWeb ブラウザで http://localhost:3000 が自動的に立ち上がりページが表示されます。以降、VSCodeで実装コードを変更し、保存するたびごとにWeb ブラウザの http://localhost:3000 が自動的に再読み込み(Reload)されるようになります
最適化ビルドを実行します
React Webアプリが完成したら、最適化ビルドを実行してから公開します
- macOS
terminal(base) macpro:blackout-peeling teruroom$ npm run build
- Windows10
powershellPS C:\dev\react\blackout-peeling> npm run build
- 実行結果
macOSの場合
省略Windows10の場合> security-compliance@0.1.0 build C:\dev\react\blackout-peeling > react-scripts build Creating an optimized production build... Compiled successfully. File sizes after gzip: 39.89 KB build\static\js\2.73f5bc0c.chunk.js 5.5 KB build\static\js\main.11e48eb2.chunk.js 782 B build\static\js\runtime-main.623a1e28.js 310 B build\static\css\main.3c6f1d95.chunk.css The project was built assuming it is hosted at /. You can control this with the homepage field in your package.json. The build folder is ready to be deployed. You may serve it with a static server: npm install -g serve serve -s build Find out more about deployment here: bit.ly/CRA-deployReact Webアプリを本番用に起動します
オプション
--host='0.0.0.0'
をつけてコマンドを実行し、インバウンドとして全てのIPアドレスからのリクエストを受け付けるようにします
- macOS ➡️ ubuntsサーバにデプロイ
bashteruroom$ npm start --host='0.0.0.0'
- Windows10 ➡️ Windows Server 2016にデプロイ
powershellPS C:\dev\react\blackout-peeling> npm start --host='0.0.0.0'React Webアプリを利用します
WebブラウザでURL http://192.168.0.16:3000 (192.168.0.16はWebアプリを動かしているPCのIPアドレス)にアクセスします
macOSの場合
デフォルトWebブラウザの
Safari
や後からインストールしたChrome
で表示されますWindows10の場合
デフォルトWebブラウザの
Edge
や後からインストールしたChrome
で表示されます。IE11
では残念ながらそのままでは表示や動作が上手くいきません。react-app-polyfill
ライブラリを別途インストールし、index.js
ファイルの先頭で
import 'react-app-polyfill/ie11';` のようにインポートする必要があります。諸々の都合により
IE11
を既定のブラウザにしている場合には、以下のようにWindowsショートカットなどにURLを仕込んでおいてください。ショートカットを開くとEdge
によりReact Webアプリが開かれるようになりますmicrosoft-edge:http://hoge-foo.ad.baa.co.jp:3000/
- hoge-foo.ad.baa.co.jp:Active Directry内のReact Webアプリを起動しているPCの端末名
黒塗りをクリックすると剥がれてキーワード文字が読めるようになります
- 投稿日:2020-06-26T18:41:16+09:00
完成!!初めてReact.jsでWebサイト作った!!
とりあえず完成!
つまずいた点たくさんありました...
ハマったポイントを記事にしたのはまだ1つだけですが,今後作ってきます一通り完成したので報告致します.<--急にかしこまる
https://shinnosuke7031.github.io/react-test/意識したのは,ページ遷移しないで表示内容を変えること!
デザインはまだまだですが...
ぜひアドバイスとかいただけると幸いです
- 投稿日:2020-06-26T16:03:07+09:00
僕が考えた最強の React ファイル構成
技術スタック
Grouping by file type
https://reactjs.org/docs/faq-structure.html
基本的にこの考え方でフォルダを分ける
ファイル構成
- app.yaml
- app.storybook.yaml
- cloudbuild.yaml
- firebase.json
- api # 検討中
- foo.ts
- assets
- components
- Foo.ts
- constants
- containers
- foo.ts
- context
- firebase
- functions
- layouts # 検討中
- middleware
- models # 検討中
- pages
- _app.tsx
- _document.tsx
- _error.tsx
- index.tsx
- public / static
- favicon.ico
- store # 検討中
- store.ts
- rootReducer.ts
- actions
- foo.ts
- facades # 検討中
- reducers
- foo.ts
- selectors
- foo.ts
- states
- foo.ts
- stories
- foo.stories.ts
- test
- pages
__snapshots__
- index.test.tsx / index.spec.tsx
- types # 検討中
※ 自動生成されるファイルについてはいくつか省略してます
参考
- 投稿日:2020-06-26T14:46:52+09:00
関数合成とReactコンポーネントとReact hooks
はじめに
Reactが好まれる理由の一つとしてただの関数としてかけるからというのがあります。Reactの関数コンポーネントは関数のように合成できます。React hooksもただの関数ですので合成できます。簡単な例で説明できたら分かりやすいのではないかと思い、書いてみます。
普通のJavaScript
関数がない(一つだけ)の場合は、例えば次のようになります。
const main = (args) => { const a = args[0] * 2; const b = args[1] * 2; console.log([a, b]); return a + b; };関数は、再利用性を増すために使うことができます。
const double = (x) => x * 2; const main = (args) => { const a = double(args[0]); const b = double(args[1]); console.log([a, b]); return a + b; };また、たった一度しか使わない関数であっても、名前をつけて読みやすくするために関数化する場合があります。
const double = (x) => x * 2; const printForDebug = (x) => console.log(x); const main = (args) => { const a = double(args[0]); const b = double(args[1]); printForDebug([a, b]); return a + b; };簡単な例ですが、これが普通のJavaScriptの場合です。
Reactコンポーネント
ReactコンポーネントはReactNodeを返す関数として書くことができます。JSXで書くことが多いので、それにならうと例えば次のようになります。
const App = () => { return ( <div> <h1>Hello</h1> <h2>World</h2> </div> ); };これを関数に分割して合成するには次のように書けます。
const Title = () => <h1>Hello</h1>; const Subtitle = () => <h2>World</h2>; const App = () => { return ( <div> {Title()} {Subtitle()} </div> ); };これは完全に同じ結果を生み出します。ただの関数ですから。
しかし、このパターンはあまり見たことがないでしょうし、実際非推奨です。推奨される書き方は、次のようになります。
const Title = () => <h1>Hello</h1>; const Subtitle = () => <h2>World</h2>; const App = () => { return ( <div> <Title /> <Subtitle /> </div> ); };見慣れた書き方かと思われます。こちらが推奨される理由は、TitleやSubtitleの関数を呼び出すタイミングをReactが制御できるからです。
<Title />
はcreateElement(Title, null)
と同等です。(執筆時点) 参照この書き方は、Reactコンポーネントを関数合成する際の制約とも言えます。関数として直接合成するのではなく、createElementを介して合成することで、Reactのスケジューリングの恩恵を受けることができます。
React hooks
では、React hooksの場合はどうなるでしょうか。同じように簡単な例を考えます。
const App = () => { const [a, setA] = useState(0); const incrementA = () => { setCountA((c) => c + 1); }; const [b, setB] = useState(0); const incrementB = () => { setCountB((c) => c + 1); }; useEffect(() => { console.log([a, b]); }); return ( <div> {a} <button onClick={incrementA}>+1</button> {b} <button onClick={incrementB}>+1</button> </div> ); };共通している処理があるので関数に切り出してカスタムフックにしてみましょう。
const useCount = (initialCount) => { const [count, setCount] = useState(initialCount); const increment = () => { setCount((c) => c + 1); }; return [count, increment]; }; const App = () => { const [a, incrementA] = useCount(0); const [b, incrementB] = useCount(0); useEffect(() => { console.log([a, b]); }); return ( <div> {a} <button onClick={incrementA}>+1</button> {b} <button onClick={incrementB}>+1</button> </div> ); };場合によっては、共通処理でなくてもカスタムフックにすることで見通しが良くなるかもしれません。
const usePrintForDebug = (x) => { useEffect(() => { console.log(x); }); }; const App = () => { const [a, incrementA] = useCount(0); const [b, incrementB] = useCount(0); usePrintForDebug([a, b]); return ( <div> {a} <button onClick={incrementA}>+1</button> {b} <button onClick={incrementB}>+1</button> </div> ); };Reactコンポーネントの関数合成に制約があったように、React hooksにおける制約もあります。 Rules of Hooks に詳しく書かれていますが、この制約の範囲内でしかカスタムフックは作れないことになります。
React hooksの制約は直感的ではないと言えるかもしれませんが、Reactコンポーネントの(緩い)制約も決して直感的とは言えないでしょう。制約の数は少ない上、パターン化されているので迷いは少ないと思いますが。React hooksの制約は eslint plugin でほぼチェックできますので、これを使うことが「パターン」として必須と言えるでしょう。
おわりに
React hooksを使う際にどの程度で関数化すなわちカスタムフック化したらいいかは、一言で答えられない問題かと思います。一言で答えるなら、Reactコンポーネントを分割するように分割したらいいのではないかと言います。つまり答えになっていませんが。私自身は関数は小さくしたい派なので、コンポーネントもカスタムフックも小さく分割して合成することを目指します。この辺りはReactとしての制約はなく柔軟なので、プロジェクトでの方針や規約を決めるといいかもしれません。(コンポーネントが大きすぎるとパフォーマンスに影響は出そうです。カスタムフックが大きすぎてもパフォーマンスには関係なさそう)
コンポーネントの場合はAtomic Designなどの方法論があるのでカスタムフックにもそのような方法論があったり、アレンジして適用できるかもしれません。
- 投稿日:2020-06-26T06:01:18+09:00
Next.jsとGo言語(gqlgen)でGraphQLを使ったアプリケーションを構築する方法
この記事では、フロントエンドにNext.js、バックエンドにGo言語(gqlgen)を用いて、フロントエンドとバックエンド間のAPIにGraphQLを使ったアプリケーションを構築する方法をまとめます。
作成したのは以下のような簡易ブクマアプリです。
フロントエンドはほぼNext.jsのexamplesのままで、本記事ではバックエンドの実装をメインに解説します。背景
個人的に、「フロントエンドフレームワークとGo言語の組み合わせで何か開発をしてみたい」と以前から思っていました。
そんな中、ReactのフレームワークであるNext.jsの使い勝手が良さそうという噂を耳にしました。加えて、Next.jsのexamplesが充実していて、Apolloを使ったアプリケーションのひな形が簡単に作れることを知ったので、今回Go言語のバックエンドと組み合わせて動かしてみることにしました。
構成要素
図に書いてみると思っていた以上に構成要素が多かったので、それぞれの役割を大まかに説明します。
名前 種別 役割 JavaScript プログラミング言語 今回のフロントエンドの実装に用いるプログラミング言語 React ライブラリ コンポーネントベースでUIを構築できるJavaScriptライブラリ Apollo Client ライブラリ GraphQLに対応した状態管理ライブラリ Next.js フレームワーク Reactのサーバーサイドレンダリング(SSR)に対応するフレームワーク GraphQL クエリ言語/ランタイム API向けに作られたクエリ言語およびランタイム Go言語 プログラミング言語 今回のバックエンドの実装に用いるプログラミング言語, golangと表記されることもある gqlgen ライブラリ SchemaベースでGraphQLサーバを構築するためのライブラリ UIの構築
まずはNext.jsのexamples/with-apolloをベースにアプリケーションを作ります。
$ yarn create next-app success Installed "create-next-app@9.4.4" with binaries: - create-next-app ✔ What is your project named? … with-apollo-ui ✔ Pick a template › Example from the Next.js repo ✔ Pick an example › with-apollo Creating a new Next.js app in /Users/yokazaki/src/github.com/yuuu/with-apollo-ui. # ログ省略 $ cd with-apollo-ui $ yarn dev出来上がったアプリケーションは、URLとタイトルをセットで登録・閲覧できる、いわゆる簡易ブクマアプリです。
この時点では、リクエストの送信先となっているバックエンドのGraphQLサーバはインターネット上に公開されているものを利用しています。
このため、列挙されているURLとタイトルは、世界中のユーザが登録したものがそのまま表示されています。GraphQLサーバの構築
スキーマベースでGraphQLサーバを構築できることと、機能の拡張性を考慮して、以下記事を参考にEcho+gqlgenを使って構築しました。
gqlgen + EchoでgolangなGraphQLサーバを作るチュートリアル
ベース構築
$ mkdir with-apollo-api $ cd with-apollo-api $ go mod init github.com/yuuu/with-apollo-api $ go get github.com/99designs/gqlgen $ go get github.com/rs/cors # gqlgenでgraph/schema.graphqlsを生成 $ gqlgen initスキーマ定義
graph/schema.graphqls
にGraphQLのスキーマを記述します。graph/schema.graphqlstype Post { id: ID! title: String! votes: Int! url: String! createdAt: String! } type PostsMeta { count: Int! } type Query { allPosts(orderBy: OrderBy, first: Int!, skip: Int!): [Post!]! _allPostsMeta: PostsMeta! } enum OrderBy { createdAt_ASC, createdAt_DESC } type Mutation { createPost(title: String!, url: String!): Post! updatePost(id: ID!, votes: Int): Post! }スキーマを記述したらソースコードを生成します。
$ rm graph/schema.resolvers.go $ gqlgenQueryとMutationを実装
生成された
graph/schema.resolvers.go
を以下のように変更します。graph/schema.resolvers.gopackage graph // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. import ( "context" "fmt" "sort" "strconv" "time" "github.com/yuuu/with-apollo-api/graph/generated" "github.com/yuuu/with-apollo-api/graph/model" ) var posts []*model.Post = make([]*model.Post, 0) func (r *mutationResolver) CreatePost(ctx context.Context, title string, url string) (*model.Post, error) { post := model.Post{ ID: fmt.Sprintf("%d", len(posts)+1), Title: title, URL: url, Votes: 0, CreatedAt: time.Now().Format("2006-01-02 15:04:05"), } posts = append(posts, &post) return &post, nil } func (r *mutationResolver) UpdatePost(ctx context.Context, id string, votes *int) (*model.Post, error) { if votes == nil { return nil, nil } i, _ := strconv.Atoi(id) posts[i-1].Votes = *votes return posts[i-1], nil } func (r *queryResolver) AllPosts(ctx context.Context, orderBy *model.OrderBy, first int, skip int) ([]*model.Post, error) { if skip > len(posts) { skip = len(posts) } if (skip + first) > len(posts) { first = len(posts) - skip } sortedPosts := make([]*model.Post, len(posts)) copy(sortedPosts, posts) if orderBy != nil && *orderBy == "createdAt_DESC" { sort.SliceStable(sortedPosts, func(i, j int) bool { return sortedPosts[i].CreatedAt > sortedPosts[j].CreatedAt }) } slicePosts := sortedPosts[skip : skip+first] return slicePosts, nil } func (r *queryResolver) AllPostsMeta(ctx context.Context) (*model.PostsMeta, error) { postsMeta := model.PostsMeta{Count: len(posts)} return &postsMeta, nil } // Mutation returns generated.MutationResolver implementation. func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} } // Query returns generated.QueryResolver implementation. func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver }Playgroundで動作確認
以下コマンドでGraphQLサーバを起動します。
$ go run server.go
ブラウザで http://localhost:8080 にアクセスすると、PlayGroundが表示されます。
まずはmutationから動作確認してみましょう。
Yahoo! JAPANを登録してみます。
次にGoogleを登録してみます。
続いてQueryを試してみます。
このように、GraphQLの各リクエストが問題なく動作していることがわかります。UIとGraphQLサーバを組み合わせる
CORS設定
現状、Next.jsが動いているオリジン(http://localhost:3000) とGraphQLサーバのオリジン(http://localhost:8080) が異なるため、このままではGraphQLサーバへのリクエストが失敗します。
server.goを以下のように変更することで、 http://localhost:3000 からの要求を受け付けられるようにします。
server.gopackage main import ( "log" "net/http" "os" "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/playground" "github.com/rs/cors" "github.com/yuuu/with-apollo-api/graph" "github.com/yuuu/with-apollo-api/graph/generated" ) const defaultPort = "8080" func main() { port := os.Getenv("PORT") if port == "" { port = defaultPort } srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}})) http.Handle("/", playground.Handler("GraphQL playground", "/query")) c := cors.New(cors.Options{ AllowedOrigins: []string{"http://localhost:3000", "http://localhost:8080"}, AllowCredentials: true, }) http.Handle("/query", c.Handler(srv)) log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) log.Fatal(http.ListenAndServe(":"+port, nil)) }Next.jsのリクエスト先のURLを変更
lib/apolloClient.js
のServer URLを http://localhost:8080/query に変更します。lib/apolloClient.jsimport { useMemo } from 'react' import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http' let apolloClient function createApolloClient() { return new ApolloClient({ ssrMode: typeof window === 'undefined', link: new HttpLink({ uri: 'http://localhost:8080/query', // Server URL (must be absolute) credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers` }), cache: new InMemoryCache(), }) } export function initializeApollo(initialState = null) { const _apolloClient = apolloClient ?? createApolloClient() // If your page has Next.js data fetching methods that use Apollo Client, the initial state // gets hydrated here if (initialState) { _apolloClient.cache.restore(initialState) } // For SSG and SSR always create a new Apollo Client if (typeof window === 'undefined') return _apolloClient // Create the Apollo Client once in the client if (!apolloClient) apolloClient = _apolloClient return _apolloClient } export function useApollo(initialState) { const store = useMemo(() => initializeApollo(initialState), [initialState]) return store }動作確認
UIとGraphQLサーバをともに起動します。
$ cd with-apollo-ui # 移動先パスは適宜変更ください $ yarn dev # 以下は別のterminalで $ cd with-apollo-api # 移動先パスは適宜変更ください $ go run server.gohttp://localhost:3000 へアクセスすると、URLの追加や投票が正常に動作することが確認できます。
まとめ
Next.jsのサンプルが充実しているおかげで、簡単にアプリケーションを構築できました。これに認証やバリデーションを追加して、UIを自分好みにカスタマイズすれば簡単にサービスをリリースできそうです。
Next.js・Go言語ともにもっと事例が増えると良いなと思っています。
みなさまも、ぜひお試しください。