Merged. github.com
最近リリースされたhttp2コアモジュールのdocsの修正のみですがnode.js本家へのPRマージされました。
今後は実際のコードの改修とかどんどんやっていきたい。
Merged. github.com
最近リリースされたhttp2コアモジュールのdocsの修正のみですがnode.js本家へのPRマージされました。
今後は実際のコードの改修とかどんどんやっていきたい。
先日 node.jsにおいて http/2の実装がマージされました。
experimentalな機能で、現在だとnode nightlyバージョン(9.x系)で実行時にオプションを付けることで http2 コアモジュールが利用可能です。
近々node.js 8.xに取り込まれるようで年内にはちゃんと使えるようです。
今回は、このhttp2コアモジュールについて簡単な使い方と内部実装について調べたことをまとめます。
ドキュメントはnodeのmasterブランチにマージされており、見ることができます。
- github.com
http2の実装はnghttp2というCライブラリが採用されております。 このライブラリはhttp/2の実装のなかで人気であり、
RFC7540 HTTP/2 とRFC7541の仕様に基づいて実装されています。
- github.com
http2 コアモジュールのJS apiは下記で定義されています。
- node/http2.js at master · nodejs/node · GitHub
http/2のJS apiの各実装は lib/internal/http2配下のファイルにあります。内部の実装を見たい人はここで見ることができます。
- node/lib/internal/http2 at master · nodejs/node · GitHub
nghttp2ライブラリへのインターフェースはsrc/node_http2.ccと src/node_http2.hで定義されています
nghttp2とコアモジュールのやり取りはこのあたりを見ればよいかと思います。
- node/node_http2_core.cc at master · nodejs/node · GitHub
- node/node_http2.h at master · nodejs/node · GitHub
上記のhttp2コアモジュールを使うにあたり、nodeの実行時に –expose-http2 フラグを追加することで、 require(‘http2’) が使えるようになります。
これは、すでに http2というnpmモジュールが存在しているため、 –expose-http2フラグを付けないと下記のnpmモジュールをrequireするようになり、既存のnpm http2モジュールを使っているプロジェクトは注意が必要です。
- www.npmjs.com
http2のコアモジュールを使うために、node.jsの最も最新のnightly版(現時点で9.x系)を使えるようにします。
mac OSの環境が前提ですが、 node.js nightly版を簡単にインストールできる node-nightlyというnpmモジュールを使います。
node-nightlyモジュールインストール $npm install --global node-nightly nightly最新版のnodeインストール $ node-nightly Downloading the nightly version, hang on... node-nightly is available on your CLI! nightlyバージョンの確認 $ node-nightly -v New nightly available. To upgrade: `node-nightly --upgrade` v9.0.0-nightly20170806e96ca62480
これでnode.js nightly版を簡単にインストールすることができました。
ここでさっそくhttp2のTLS/SSLを使ったセキュアなサーバを作ってみます。
http2.createSecureServer()というメソッドで、サーバのインスタンスを作成することができます。
http2.createSecureServer()の内部の実装は、主要なところだけ抜粋すると以下のようになっています。 - https://github.com/nodejs/node/blob/master/lib/internal/http2/core.js#L2427-L2434
function createSecureServer(options, handler) { if (typeof options === 'function') { handler = options; options = Object.create(null); } debug('creating http2secureserver'); // Http2SecureServerのインスタンスを返す return new Http2SecureServer(options, handler); } ・・・ // Http2SecureServerクラス class Http2SecureServer extends TLSServer { // コンストラクタ constructor(options, requestListener) { options = initializeTLSOptions(options); super(options, connectionListener); this[kOptions] = options; this.timeout = kDefaultSocketTimeout; this.on('newListener', setupCompat); if (typeof requestListener === 'function') this.on('request', requestListener); this.on('tlsClientError', onErrorSecureServerSession); debug('http2secureserver created'); } // setTImeoutメソッド setTimeout(msecs, callback) { this.timeout = msecs; if (callback !== undefined) { if (typeof callback !== 'function') throw new errors.TypeError('ERR_INVALID_CALLBACK'); this.on('timeout', callback); } return this; } }
このメソッドの戻り値として、Http2SecureServerクラスのインスタンスを返します。
Http2SecureServerクラスはTLSServerクラスを継承しており、接続においてSSL/TLSを使う事となります。
TLSServerクラスはtlsというコアモジュールで定義され、実装は下記になります。
tls_wrapでTLSServerクラスの実装が見れます。
[https://github.com/nodejs/node/blob/master/lib/tls_wrap.js#L784-L893]
かなり階層が深くなりますが、TLS/SSLを使ったセキュアなサーバを作る場合、http2.createSecureServerの引数にオプションを渡すことができ、
接続において使うTLS/SSLの証明書や秘密鍵を設定することができます。
optionとして、オブジェクトのkey, certにそれぞれ指定してあげます。
node/_tls_wrap.js at master · nodejs/node · GitHub
今回はここで証明書と秘密鍵を指定するために、ローカルで自前で準備していきます。
ここではあくまでローカル環境でのhttp2サーバを作ることが目的のため、自前でローカルpcでopensslコマンドを使って作成します。
- stackoverflow.com
mac OSでopensslを使って以下のコマンドを実行することでカレントディレクトリに下記2つのファイルが生成されるので、
サーバインスタンス生成時にこれを指定します。
- server.crt: SSLサーバ証明書
- server.key: SSL公開鍵暗号用の秘密鍵
さっそく上記の説明を踏まえてhttp2サーバを立ててみます。
ここではあくまでhttp2コアモジュールのサンプルコードをそのまま使用します。
http2サーバインスタンス生成時に 証明書・秘密鍵を指定し、
3000番ポートで待ち受けます。
https://localhost:3000をブラウザでアクセスしてhello worldを表示させる簡単なものを作ります。
サーバを立てた後、アクセスがくると サーバインスタンスに対して"stream"イベントが発火されるので、
ハンドラ関数を設定します。
ここではresponse statusと hello worldを記述した簡単なhtmlをstreamで返します。
// http2コアモジュールをrequire const http2 = require('http2'); // createSecureServerでのオプションを指定する const options = { key: fs.readFileSync('server.crt'), // 公開鍵暗号の秘密鍵 cert: fs.readFileSync('server.key') // TLS/SSL サーバ証明書 }; // Create a plain-text HTTP/2 server const server = http2.createSecureServer(options); // アクセスが来た場合、streamイベントが発火されう server.on('stream', (stream, headers) => { stream.respond({ 'content-type': 'text/html', ':status': 200 }); stream.end('<h1>Hello World</h1>'); }); server.listen(3000);
ここで、サーバを立ち上げる事ができたので、chromeブラウザで https://localhost:3000へアクセスしてみます。
上述したように、今回はTLS/SSL証明書を自前で用意したため、chromeでwarning画面がでますが、無視して、「詳細設定」→「localhost にアクセスする(安全ではありません)」をクリックすることでアクセスが可能です。
これでhttp/2のセキュアなサーバにアクセスできました。
今回は簡単な開設のため、http/2固有の機能を使ったサーバの作り方は解説せず、ここで終わります。
http/2固有の機能を使うことでよりパフォーマンスが向上するため、時間があるときに、http/2RFCをちゃんと読んで仕様を把握していきたいと思います。
babel.jsをつかって ES6のシンタックスで書かれたjavascriptファイルをES5のシンタックスにトランスパイルする方法をまとめました。
実務で使ったことなかったので勉強がてらさらっとまとめます。
babelをコマンドラインで利用するに当たって、babelのコマンドラインクライアントである、npm モジュールのbabel-cliをインストールする必要があります。
開発環境のみで使うので –save-devでpackage.jsonに対してdevDependenciesにモジュールの情報を追記させます。
$ npm install --save-dev babel-cli
今回はサンプルとして今回 hapiというhttpサーバモジュールを使うのでこちらもインストールしておきます
$ npm install --save hapi
babelでトランスパイルする際に、利用するプラグインを指定する必要があります。
今回はES6(ES2015とも言う)からES5へトランスパイルするため、ES2015 presetを指定します。
babeljs.io
babelコマンドの –presetsオプションでも指定できますが、毎回打つのは面倒なので、 babelの設定を記述するファイルである .babelrcを作成し、json形式で記述します。
.babelrc
{ "presets": ["es2015"] }
presets項目に使用するプラグイン名を指定します。
ここで、トランスパイル元のjsファイルである src/index.jsを作成します。
hapiモジュールを使って簡単なhttpサーバを作ります。
全てES6(ES2015)のシンタックスで記述します。
import Hapi from 'hapi'; const server = new Hapi.Server(); server.connection({ host: 'localhost', port: 8000 }); // Add routing server.route({ method: 'GET', path: '/hello', handler: function(request, reply) { console.log('[/hello] requested'); reply('hello world'); } }); server.start();
これを importや constを使わない、ES5形式にトランスパイルさせます。
トランスパイルした後のファイルは dist/index.jsに作成させるとします。
さっそくコマンドラインでbabelコマンドを使ってトランスパイルさせます。
babelコマンドの –out-fileオプションをつかって、トランスパイル後のファイルの出力先を指定します。
$ babel src/index.js --out-file dist/index.js
これによって、 dist/index.jsにES5形式にトランスパイルされたjsファイルができあがります。
'use strict'; var _hapi = require('hapi'); var _hapi2 = _interopRequireDefault(_hapi); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var server = new _hapi2.default.Server(); server.connection({ host: 'localhost', port: 8000 }); // Add routing server.route({ method: 'GET', path: '/hello', handler: function handler(request, reply) { console.log('[/hello] requested'); reply('hello world'); } }); server.start();
元のjsファイルで使っていた import, const が使われずにそれぞれ require varを使うようにトランスパイルされているのが確認できます。
babelをつかったトランスパイルの手順は以上となります。
まだ試していませんが、最新のES2017形式のファイルをES5にトランスパイルする場合は ES2017 presetを使えばできます。
babeljs.io
node.jsにおいて、文字列をbufferへ変換させたいときの方法をまとめます
環境 node.js 8.1.3
node.js 8.x系におけるBufferインスタンスの作成において、 new Bufferは既にdepricatedになっている*1ため、ここではbufferインスタンスの作成はBuffer.allocを利用します。
Buffer | Node.js v8.1.3 Documentation
Buffer.alloc(size)の他に、インスタンス生成時にデータを初期化させないBuffer.allocUnsafe(size)があります。
Buffer.allocは、インスタンス生成時にデータをzero-filledする初期化コストがかかるため、Buffer.allocUnsafeよりパフォーマンスは劣りますが、sensitiveなデータを含まないことが保証されるので、速度を最優先させる場面以外において、基本的にBuffer.allocを使う方が良いかと思います。
https://nodejs.org/dist/latest-v8.x/docs/api/buffer.html#buffer_class_method_buffer_allocunsafe_size
文字列をbufferに変換する場合において、Buffer.allocを利用時にBufferサイズ指定する必要があります。 下記に文字列をBufferに変換するスクリプトにあるように、ここで毎回文字列のサイズを渡してBufferインスタンスを生成します。
function bufferFromString(str) { // Allocates a new Buffer of size bytes. // If fill is undefined, the Buffer will be zero filled; // set size of string length var buffer = Buffer.alloc(str.length) buffer.write(str) return buffer } const text1 = bufferFromString('hello'); const text2 = bufferFromString('wor'); console.log('----text1', text1); console.log('----text2', text2); // text1のbufferにworを書き込む text1.write('wor'); // 先頭の3文字分 のみ更新させるため、4,5文字目は変わらない // hello → worlo console.log(text1.toString('utf8'));
ただ、欠点なのは毎回Buffer.allock(size)でインスタンスを生成した後、Buffer.write()でデータを書き込む必要があるため、若干冗長です。
Buffer.fromの引数に文字列を渡すと、一発で文字列をBufferに変換できます。
> Buffer.from("hello world") <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
文字列からBufferへの変換方法をざっとまとめました。 Buffer.alloc Buffer.allocUnsafe Buffer.fromはそれぞれ挙動に違いがあるので、公式ドキュメントをちゃんと読んで、状況に応じて使い分けるとよいかと思います。
AWS lambda functionが呼び出された際に、AWS dynamoDBのテーブルにデータを追加していく方法についてまとめます。
今回は、API Gateway と lambdaで構築した line botに対して送信されたデータを dynamoDBに追加していくところまで説明します。
AWSの IAM manageページから、新規にIAMユーザを作成します。
このユーザは、Dynamo DBに対する書き込み、読み込み権限そして、lambda関数からdynamoを使う権限を付与します。
「ユーザを追加ボタン」でユーザ名を入力し、「アクセスの種類」で、プログラムによるアクセスと、AWSマネジメントコンソールへのアクセスを許可します。
ユーザの作成を完了すると、 「アクセスキーID」と、「シークレットアクセスキー」が取得できます。 この2つをlambdaからdynamoDBへのアクセス時に必要なので、メモしておきます。
作成後、IAMユーザの設定画面に移り、「アクセス権限を追加」ボタンをおし、アクセス権限を追加していきます。
予めグループを作成し、そのグループにアクセス権限を追加する方法もありますが、今回はユーザに対してアクセス権限を追加します。
「アクセス許可の付与」画面で、「既存のポリシーを直接アタッチ」項目を選択し、もともとAWS側で用意されているポリシーをユーザに追加します。 DynamoDB用のアクセス権限を追加するため、「AmazonDynamoDBFullAccess」ポリシーを探し、選択します。
これでIAMユーザを作成し、そのユーザに対してlambdaからDynamoDBへのデータの読み書きする権限を付与できました。
DynamoDBにデータを追加するためのテーブルを作成します。
DynamoDBのマネジメントコンソールにアクセスし、「テーブルの作成」ボタンからテーブル作成画面に遷移します
テーブルの設定はデフォルトにし、テーブル名、プライマリーキーを設定します。
プライマリーキーはDynamoDBのテーブル内のアイテム毎にユニークなIDのことです。
本稿では、テーブル名はmessage プライマリーキーは_id と設定していきます
これでDynamoDBのテーブルを作成することができました。
IAMユーザの作成、DynamoDBのテーブルも作成できたので、次にlambda関数で、linebotに対して送られたメッセージをDynamoDBに保存する関数を実装していきます。
前回の 「aws lambdaとapi gatewayで linebotを作成する」で作成したlambda関数をベースに、DynamoDBにデータを追加する処理を実装します。
AWSの各サービスに対するクライアントsdkは公式で用意されており、
npm の aws-sdkモジュールがあるので、コレを利用します。
www.npmjs.com
DynamoDBへのアクセス時に、先程IAMユーザの作成時に生成されたAPIアクセスキーとシークレットアクセスキーを利用します。
コードに直接埋め込むのは管理上よくないので、lambdaの環境変数に指定します。
DynamoDB用クライアントインスタンスの作成は下記の用な感じで行います。
exports.handlerの内部で記述すると、関数が読み込まれるたびに初期化が走るため、exports.handlerの外部で初期化させます。
const https = require('https'); const AWS = require('aws-sdk'); // dynamoDBクライアントインスタンス初期化 // IAMユーザのAPIアクセスキー、シークレットアクセスキーを指定する。 // region apiVersionも指定する。 const dynamo = new AWS.DynamoDB({ region: 'ap-northeast-1', apiVersion: '2012-08-10', accessKeyId: process.env.DYNAMO_API_ACCESS_KEY, secretAccessKey: process.env.DYNAMO_USER_SECRET_ACCESS_KEY });
登録するデータは、linebotから来るリクエストデータにはいっている、ユーザID、メッセージタイプ(text や sticker[スタンプ]など)、ユーザが入力したテキスト を登録していきます。
メッセージが送信される毎にデータを登録していくため、ユニークキーであるidには一意なものを指定します。
ここでは、とりあえず $USERID$DATETIME を指定します。
データの登録は、DynamoDBインスタンスのputItemメソッドを使って行います。
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#putItem-property
上記ドキュメントに書かれているサンプルコードですが、パラメータにテーブル名、そして 追加するアイテムを指定してきます。
アイテム内の各アトリビュートは、文字列(S)や数値(N) など、それぞれ登録するデータの型をキーに指定します。
このあたりの仕様はドキュメントを参照してください。
/* This example adds a new item to the Music table. */ var params = { Item: { "AlbumTitle": { S: "Somewhat Famous" }, "Artist": { S: "No One You Know" }, "SongTitle": { S: "Call Me Today" } }, ReturnConsumedCapacity: "TOTAL", TableName: "Music" }; dynamodb.putItem(params, function(err, data) { if (err) console.log(err, err.stack); // an error occurred else console.log(data); // successful response /* data = { ConsumedCapacity: { CapacityUnits: 1, TableName: "Music" } } */ });
実際に書いたlambda関数は以下になります。
const https = require('https'); const AWS = require('aws-sdk'); // dynamoDBクライアントインスタンス初期化 // IAMユーザのAPIアクセスキー、シークレットアクセスキーを指定する。 // region apiVersionも指定する。 const dynamo = new AWS.DynamoDB({ region: 'ap-northeast-1', apiVersion: '2012-08-10', accessKeyId: process.env.DYNAMO_API_ACCESS_KEY, secretAccessKey: process.env.DYNAMO_USER_SECRET_ACCESS_KEY }); const tableName = 'message'; // linebotから送信されたメッセージデータから返信用メッセージデータを作成する // 本稿では、来たメッセージをそのまま返す function createResponseMessage(messageData) { const message = messageData.message; let resMessage; if (message.type === 'text') { // text resMessage = { type: message.type, text: message.text } } else if (message.type === 'sticker') { // stamp resMessage = { type: message.type, packageId: message.packageId, stickerId: message.stickerId }; } return resMessage; } exports.handler = (event, context, callback) => { const messageData = event.events && event.events[0]; const replyToken = messageData.replyToken; const message = messageData.message; const text = message.text; const source = messageData.source; const resMessage = createResponseMessage(messageData); const data = JSON.stringify({ replyToken: replyToken, messages: [resMessage] }); // LINE reply apiのレスポンス設定 const Authorization = 'Bearer ' + process.env.ENTER_ACCESS_TOKEN; opts = { hostname: 'api.line.me', path: '/v2/bot/message/reply', headers: { "Content-type": "application/json; charset=UTF-8", "Authorization": Authorization }, method: 'POST', }; const req = https.request(opts, function(res) { res.on('data', function(res) { console.log(res.toString()); }).on('error', function(e) { console.log('ERROR: ' + e.stack); }); }); req.write(data); req.end(); // DynamoDBへのメッセージデータ追加 const userId = source.userId; const params = { TableName: tableName, Item: { _id: { S: userId + '_' + Date.now(), }, userId: { S: userId }, messageType: { S: message.type } } }; // スタンプでなく文字列が投稿されたとき、textアトリビュートを追加する if (message.type === 'text') { params.Item['text'] = { S: message.text }; } dynamo.putItem(params, function(err, data) { if (err) { console.error("Error occured", err); } console.error(data); }); };
最後に、linebotに対してメッセージを送信したあと、DynamoDBコンソールでデータが追加されていることを確認します。
AWSの API Gateway、lambdaを利用し、ユーザからのメッセージが来たときにlambda関数を呼び出し、ユーザへレスポンスを返すline botを作ります。
行った作業手順を一通りまとめます。
まず、line messaging api用のbotアカウントの作成を行うため、line business centerへログインします。
business.line.me
「アカウントリスト」タブを選択し、「アカウント作成」→「Messaging APIを始める」で、アカウントを作成する事ができます。
作成後、line@MANAGERページにて作成したアカウントの設定を行っていきます。
アカウント設定開始するとBot設定画面に遷移します。
Bot設定項目の中で「Webhook送信」項目を「利用する」に設定することで、messaging apiを利用できます。
またline business centerへもどり、「アカウントリスト」→作成したアカウントの「LINE Developers」から、botアカウント用のapiアクセストークンを確認することができます。
「ISSUE」ボタンを押すことでアクセストークンを確認できます。 このアクセストークンは後ほど使います。
awsコンソールから「サービス」→「Amazon API Gateway」を選択し、新しいAPIの作成します。
「新しい API」にチェックを入れ、適当なAPI名を入力します。
いったんおいておき、lambda関数の作成を行います。
早速aws lambdaを使ってユーザからのメッセージを受け取り、messaging apiを使ってレスポンスするための関数を書いていきます。
「Lambda関数の作成」ボタンから 「設計図の選択」画面に遷移し、そこで、lambdaで使うランタイムと、関数のサンプルを選ぶことができます。
ランタイムはNode.js と pythonから選ぶことができ、本稿では Node.js 6.10を選択します。
関数のサンプルは、「Blank Function」を選択します。
続いて、「トリガーの設定」画面に遷移し、lambdaを実行するためのトリガーを選択します。
本稿では、lambda関数へのエンドポイントとして、 AWS API Gatewayを利用しますので、「API Gateway」を選択します。
作成時に下記の3つの入力項目を設定します。
- API名 任意の名前
- デプロイされるステージ prod
- セキュリティ AWS IAM
line botに対してユーザから送られるメッセージは、下記のreply APIの仕様に基づいて送信されます。
ユーザから受け取ったリクエストから、replyToken, textを取得し、reply api を利用することでユーザに対して返信することができます。
LINE API Reference
ここでは、ユーザから来たメッセージをシンプルにそのまま返すようにします。
lambda関数は、awsのlambda関数の設定画面で入力できるため、今回はそのまま入力していきます
const https = require('https'); exports.handler = (event, context, callback) => { // メッセージデータ取得 const messageData = event.events && event.events[0]; const replyToken = messageData.replyToken; const message = messageData.message; const text = message.text; const data = JSON.stringify({ replyToken: replyToken, messages: [{type: "text", text: text}] }); // line messaggin apiのアクセストークンを環境変数ACCESS_TOKENに設定する const Authorization = 'Bearer ' + process.env.ACCESS_TOKEN; opts = { hostname: 'api.line.me', path: '/v2/bot/message/reply', headers: { "Content-type": "application/json; charset=UTF-8", "Authorization": Authorization }, method: 'POST', }; const req = https.request(opts, function(res) { res.on('data', function(res) { console.log(res.toString()); }).on('error', function(e) { console.log('ERROR: ' + e.stack); }); }); req.write(data); req.end(); };
lambda関数が毎回実行されるたびに、デフォルトでexports.handler メソッドが呼ばれます。 引数は3つあり、 event.events内に、lineユーザからのメッセージのリクエストデータが格納されます。
line reply APIのリクエスト時に、先程「LINE Developers」管理画面で取得した アクセストークンが必要で、 リクエストヘッダーの Authorization に 追加します。
コードの中に直接アクセストークンを埋め込むと、管理上面倒なため、lambdaの環境変数を利用します。
上記のように、キーと値を入力することで、lambda関数内部で process.env.ACCESS_TOKENとして呼び出して使うことができます。
他にもいろいろ設定する項目があり、代表的なものをあげますが それ以外の項目はデフォルトのままでよいかと思います。
関数の設定 名前: 任意 Lambda 関数ハンドラおよびロール ・ハンドラ index.handler ・ ロール 「テンプレートから新しいロールを選択」 ・ロール名 任意のロール名 詳細設定 メモリ:128MB
ここで、作成した API GatewayによるAPIと lambda関数を紐付けます。
lambdaの設定画面より「トリガー」タブを選択→「トリガーを追加」を選択 → 「API Gatway」を選択し、先程作成したAPIのAPI名、デプロイされるステージ、セキュリティ項目を入力します。
「メソッドの作成」→「POST」を選択しチェックボタンを押し、下記のように作成したlambda関数の設定を入れます。
これで、API Gatewayによって作った apiに対して POSTでリクエストを送ると lambda関数を呼び出すといって一連の設定ができました。
最後に、 LINE Developer管理画面で、Webhook URLを設定していきます。
先程のlambdaの設定画面の「トリガー」タブに、連携したAPIのURLが表示されています。
これを LINE Developer管理画面 に登録します。
登録後、Webhook URL 項目に「verify」ボタンができるので、これを押すとapiとの疎通確認を行います。
これでsuccessが出たら設定完了です。
verify時に下記の文言がでてエラーが出た場合は、再度api gateway と lambdaの設定を見直す必要があります
A http status of the response was '502 Bad Gateway'.
verify時にlambdaのcloud watchログを見ると reply apiで下記のエラーが出ていますが、api gatewayと lambdaの設定自体が正しければ successになっているので、特に問題は無いかと思います。
{"message":"Invalid reply token"}
これで一通りの設定が完了したので、実際にbotに対してメッセージを送ってみます。
成功すると以下の通りにメッセージを返してくれるようになります。
うまくいかない場合は lambdaのcloudwatchのログを見て、lambda関数内でエラーが出ているか確認することと、API Gatewayの設定が正しいか再度確認することが大事です。