20210220のNode.jsに関する記事は6件です。

サーバーレスで動的サイト作成(Firebase Hosting + Cloud Functions + Node.js + Express + EJS)

本記事でやること

下記の構成でサーバレスで動的サイトを作成する。

  • Firebase
    • Firebase Hosting
    • Firebase Cloud Functions
  • Node.js
    • Express(フレームワーク)
    • EJS(テンプレートエンジン)
    • Express Generator(雛形の作成)

プロジェクトディレクトリの新規作成から、Firebase へデプロイして動作確認をするところまでを掲載します。
各種ツールはインストールしておいてください。

環境

  • Windows 10
  • Ubuntu(WSL) 18.04.5 LTS
  • npm v6.14.11
  • firebase v9.3.0

事前準備

  1. Firebase コンソールから新規プロジェクト作成する
  2. 料金プランを Blaze (従量制)に変更しておく ※ 使用量に応じて料金が発生するのでご注意ください

手順

新規プロジェクトフォルダを作成

mkdir testapp
cd testapp

Firebase Hosting を初期化

firebase init hosting

# 対話式の質問には下記のように回答してください。
? What do you want to use as your public directory? public
? Configure as a single-page app (rewrite all urls to /index.html)? Yes
? Set up automatic builds and deploys with GitHub? No

public フォルダは必要ないので削除

rm -r public

express-generator で ソースフォルダを作成

# テンプレートエンジンは EJS を使用
express -e functions

npm を初期化

npm init

EJS をインストール

npm install -save ejs

関連パッケージを削除・インストールしておく

npm uninstall debug --save
npm install firebase-functions firebase-admin static-favicon morgan cookie-parser body-parser

index.js を編集

先頭と末尾に下記を追加

index.js
const functions = require('firebase-functions');
中略
module.exports.func_https = functions.https.onRequest(app);

firebase.json を編集

firebase.json
{
    "hosting": {
        "public": "functions",
        "ignore": [
            "firebase.json",
            "**/.*",
            "**/node_modules/**"
        ],
        "rewrites": [
            {
                "source": "**",
                "function": "func_https"  これに変更
            }
        ]
    },
    "functions": {               以下の3行を追加
        "runtime": "nodejs12"    追加
    }                            追加
}

ローカル環境で動作確認

Firebase エミュレータを実行して動作確認する

firebase emulators:start

localhost にアクセスして下記画像のようになればOK
エミュレータ起動確認.PNG

Firebase へデプロイ

firebase deploy -i

Webコンソールで Hosting と Cloud Functions を確認すると、データがアップされています。

Hosting でURLを確認してアクセス
Hostingで確認.PNG
無事デプロイができました!

参考にさせていただいた記事

【Firebase】Cloud Functions + Express + EJSで動的コンテンツを配信する
大変参考になりました。ありがとうございました。

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

【Node.js】Node.jsの3大フレームワークについて

プログラミング勉強日記

2021年2月20日
Expressを使うことが多いが、他のNode.jsのフレームワークが気になったので簡単にまとめる。

Expressとは

 ExpressはNode.jsを活用したWebアプリ開発を効率よく進めることができ、よく使われている標準的なものである。
 特徴としては、情報量が多く日本語のリソースも多い。また、拡張モジュールが豊富なので追加することで柔軟な対応ができる。Expressは記述量が少なくサーバー制御が可能で複雑なシステムも簡単に拡張することができる。

Meteorとは

 Meteor(読み方:「メテオ」)フレームワークはフルスタックで、サーバー制御からフロントエンドまですべてをJavaScriptで書くことができる。MeteorはNode.jsをベースとしていて、JavaScriptとHTML, CSSだけでWebアプリを開発できるプラットフォームである。
 特徴としては、デバイスを含めてプラットフォームに依存しなく簡単に記述することができる。また、迅速な開発が可能でSPAを早く構築して公開するまでをサポートする。既存の開発そのものを変革する仕組みであったり、モバイル向けのネイティブアプリ作成も可能であったりと、魅力的な機能が豊富にある。

Sailsとは

 MVCをサポートとした開発効率を重視したフレームワークである。
 特徴としては、MVCをサポートしているRailsのような操作で開発環境を進められる。デフォルトでWeb SocketをサポートしたリアルタイムWebアプリ開発もできる。Web APIを簡単に作成して公開することもできる。

参考文献

【Node.js入門】3大フレームワーク「Express」「Meteor」「Sails」の特徴とは?
Express Web フレームワーク (Node.js/JavaScript)
オープンソースのフレームワーク/Meteorとは
Node.jsのWebフレームワーク「Sails」を使ってpub/subアプリを作ってみる

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

LambdaからS3を利用する

はじめに

チュートリアル: Amazon S3 で AWS Lambda を使用するで公開されているチュートリアルを試しながら、LambdaからS3を利用した時のメモになります。
(Cloud9の環境構築はCloud9_環境構築2021にまとめています。)

S3にアップロードされる画像のサムネイルを作成する

  1. IAM コンソールの [Roles] ページを開きます
  2. [ロールの作成] を選択します
  3. 次のプロパティでロールを作成します
  4. [信頼されたエンティティ] – [AWS Lambda]
  5. [Permissions (アクセス許可)] – [AWSLambdaExecute]
  6. ロール名 – lambda-s3-role

チュートリアルにあるサンプルを準備します。

$ cd ~
$ mkdie lambda-s3
$ cd lambda-s3
$ vi index.js
---
(チュートリアルの内容)
---
$ zip -r function.zip ./index.js
$ aws lambda create-function --function-name CreateThumbnail \
--zip-file fileb://function.zip --handler index.handler --runtime nodejs12.x \
--timeout 10 --memory-size 1024 \
--role arn:aws:iam::<<作成したロールを渡してください>>:role/lambda-s3-role \
--cli-binary-format raw-in-base64-out
$ aws lambda update-function-configuration --function-name CreateThumbnail --timeout 30 //TIMEOUT=30
$ cd ~/enviroment/
$ mkdir nodejs
$ cd nodejs
$ npm init ※パラメータはデフォルト値を指定します。
$ npm install sharp // 画像処理パッケージをインストールします。
$ cd ~
$ zip -r nodejs.zip ./nodejs/ // ディレクトリ名は変更できないようです。
$ less nodejs.zip // 内容を確認します。
$ aws lambda publish-layer-version \
  --layer-name <mylayer> \
  --zip-file fileb://nodejs.zip \
  --compatible-runtimes nodejs12.x
$ aws lambda update-function-configuration \
  --function-name CreateThumbnail \
  --layers <yourlayerバージョンARN>
// 動作確認
$ vi inputFile.txt
---
(チュートリアルの内容)
※画像ファイルとバケット(src/dist)を準備します。
---
$ aws lambda invoke --function-name CreateThumbnail --invocation-type Event --payload fileb://inputFile.txt outputfile.txt

(note: export NODE_PATH=/home/ec2-user/environment/nodejs/node_modules)

上記LambdaとVue Applicationsを連携します。

How to Build Serverless Vue Applications with AWS Amplifyで公開されているチュートリアルを連携します。

$ aws lambda add-permission --function-name CreateThumbnail \
  --principal s3.amazonaws.com \
  --statement-id s3invoke \
  --action "lambda:InvokeFunction" \
  --source-arn arn:aws:s3:::(ソースバケット) \
  --source-account (ユーザID12桁)
// ポリシーの確認
$ aws lambda get-policy --function-name CreateThumbnail

S3 バケットにLamdaへのイベントを設定する

通知を設定するには

Amazon S3 コンソールを開きます。

ソースバケットを選択します。

[Properties (プロパティ)] を選択します。

[Events (イベント)] で、以下の設定を使用して通知を設定します。
Name: lambda-trigger。
Events: All object create events。
Send to: Lambda function。
Lambda: CreateThumbnail。

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

【Node.js puppeteer7.1.0でブラウザ操作自動化&スクレイピング】

スクレイピングしたくてやっぱスクレイピングといえばPythonかなーと思ったけど、Node.js今自分の中で熱いからライブラリーー探したらあったよってこと?

読み方はパペッティア

Chromeの操作を自動化できるライブラリ
色々な操作をする時の参考になればいいなとおもう。
使ってみた感想はNode.js慣れしている人ならかなり簡単にスクレイピングできるなって感じです。


よく操作するクラスの種類

・Browser 
  ブラウザの生成、終了のときくらいしかつかわない

・Page  
  最も利用するクラス。ページの移動、formへの入力、Elementの取得などができる

・ElementHandle 
  Pageクラスの「.$(), .$$(), .$eval(), .$$eval() 」などを利用してElementを取得したときに返されたりする。指定したElementに直接click()やhover()をさせたりできる。


【操作するブラウザを生成】
const browser = await puppeteer.launch({
    headless: false //falseにしておくとBrowserが表示されて動く
    slowMo: 50      //ブラウザ操作のスピード調整ができる
})
const page = await browser.newPage()
await page.setViewport({  //ブラウザ幅、高さの設定
      width: 1200,
    height: 800,
})

【指定したURLにログイン】
// LOGIN_URL = formのあるページ
// LOGIN_USER_SELECTOR = input[type=text]
// LOGIN_USER = username
// LOGIN_PASS_SELECTOR = input[type=password]
// LOGIN_PASS = password
// LOGIN_SUBMIT_SELECTOR = input[type=submit]
// TARGET_URL = login後開きたいページ

await page.goto(LOGIN_URL, { waitUntil: 'domcontentloaded' })
await page.waitForTimeout(2000) 
await page.type(LOGIN_USER_SELECTOR, LOGIN_USER) 
await page.type(LOGIN_PASS_SELECTOR, LOGIN_PASS)

await Promise.all([ // ログインボタンクリック
    page.waitForNavigation({ waitUntil: 'networkidle0' }),
    page.click(LOGIN_SUBMIT_SELECTOR),
])
await page.goto(TARGET_URL)

・{ waitUntil: load, domcontentloaded, networkidle0, networkidle2 } の4つのoptionがあり、ページ遷移が終わったことを決定するタイミングの違いがある。

page.waitForTimeout(2000) 指定したミリ秒で処理を一時停止する。

 → goto()の後は数秒待たせないとtype() によるform入力がうまく行かない確率が高まる。

click()waitForNavigation() を同時に行うのは、click後のページ遷移が完了する前に次の処理を行ってエラーになるのを防ぐため。


【別ウィンドウ、サブウィンドウ、ポップアップへの対処法】
const [popup] = await Promise.all([
  new Promise(resolve => page.once('popup', resolve)),
  page.click('a[target=_blank]'),
])

click() で開かれたサブウィンドウなどは[popup]に入る。サブウィンドウやポップアップを操作するにはこの[popup] を操作する。


【要素を取得するよく使う4つの方法+1】
const element = await page.$("#name")
const elements = await page.$$(".names")
const value = await page.$eval("#name", el => el.value)
const values = await page.$$eval(".names", el => el.value)
const nameArray = document.querySelectorAll('.names')

・$(), $$(), は引数のセレクタでElementHandleクラスを生成する。

内部的に、document.querySelector()document.querySelectorAll()が行われている。

・$eval(), $$eval()は第2引数に関数をセットすることで引数に指定した要素を操作した値を取得したりできる。

・ElementHandleクラスは、forEachなどができなくて不便なことがある。そういうときは、

document.querySelectorAll()で要素を取得すれば普通にforEachなどを適用させられる。


【タブ切り替え】
await page.bringToFront()
// page.に指定したページを操作できるように切り替える

【実際に使うときの流れ】

ログイン→ログイン後画面でクリック→出てきたサブウィンドウ操作→元画面に戻る→終了

const puppeteer = require('puppeteer')
require('dotenv').config()
const env = process.env

(async () => {
    //ブラウザ生成
    const browser = await puppeteer.launch({
      headless: false //falseにしておくとBrowserが表示されて動く
      slowMo: 50
    })
    const page = await browser.newPage()
    await page.setViewport({
      width: 1200,
      height: 800,
    })
    //form入力&submitでログイン
    await page.goto(env.LOGIN_URL, { waitUntil: 'domcontentloaded' })
    await page.waitForTimeout(2000) //少し待たせないと入力ミスが起きやすい
    await page.type(env.LOGIN_USER_SELECTOR, env.LOGIN_USER) 
    await page.type(env.LOGIN_PASS_SELECTOR, env.LOGIN_PASS)
    await Promise.all([ // ログインボタンクリック
    page.waitForNavigation({ waitUntil: 'networkidle0' }),
    page.click(env.LOGIN_SUBMIT_SELECTOR),
  ])
    //ログイン後ページへ飛ぶ
    await page.goto(env.TARGET_URL)

    //clickによって表示されたpopup取得
    const [popup] = await Promise.all([
    new Promise(resolve => page.once('popup', resolve)),
    page.click('input[value="~"]'),
  ])

    //もとのページへback
    await page.goBack({ waitUntil: 'networkidle0' })
    //ブラウザ終了
    await browser.close()
})()

・dotenvモジュールで定数管理している


puppeteerを使う上での注意点

ページロードが終わるタイミングと要素取得のタイミングがズレると要素の取得ができなかったり、ページ遷移が終わる前に次の処理に行ってしまっていたりするので、常にタイミングには気をつけないといけない。

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

Node.js製サーバの起動方法をforeverからsystemdに移行する

foreverで起動していたExpressのサーバをOS再起動時に自動起動させたかったのでSystemd管理に移行します。
initd向けにforeverの自動起動の仕組みを提供するinitd-foreverというパッケージもあったり、forever自体を起動する方向もアリですが、Systemdでは自動再起動の制御が細かくなっているなど、foreverに期待していた役割自体も担えそうでしたのでシンプルにSystemd → Nodeにしました。
最低限の設定をイメージしてこんな感じになりました。

/etc/systemd/system/mydaemon.service
[Unit]
Description=My Node.js Daemon

[Service]
User=myuser
Group=mygroup
#Environment="NODE_ENV=production"
WorkingDirectory=/path/to/
ExecStart=/usr/bin/node app.js
Restart=always

[Install]
WantedBy=multi-user.target

あとは
systemctl enable mydaemon.service で自動起動有効化し、
systemctl start mydaemon.service で起動します。

User/Groupは指定しなければrootになってしまうため指定しています。
Environmentは今回不要でしたがExpressで使う機会が多そうなのでコメントアウト状態でサンプルとして入れています。
対象のスクリプトはnode app.jsで起動して、Ctrl+Cで終了させるような動きになっている必要があります。
Restart=alwaysによってプロセスが落ちても自動的に復帰させます。
デフォルトで10秒に5回の再起動に制限されますがStartLimitInterval StartLimitBurst で調整できます。

foreverのwatchに対応する機能は無さそうですが今回の目的には不要でした。

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

【React Native】簡単なAPI連携のアプリケーションを作成する ③ESLint+Prettierの導入(寄り道)

はじめに

本記事は下記の記事の続編になります。
【React Native】簡単なAPI連携のアプリケーションを作成する ①準備編
【React Native】簡単なAPI連携のアプリケーションを作成する ②住所検索の実装
実践的なアプリを作成するというよりは、ハンズオン的にReact Nativeを触ってみるといった趣旨になっています。
なお思いついた順で記事にしているため、本来は実装を開始する②の前に
本記事の内容であるLintやフォーマッターを実施する方が好ましいのと、
今回の内容は必須ではないため、スキップしていただいても問題ないと思います。
(毎回アレ?ってなるので備忘で残したかったんです。。)

本記事で実現すること

VSCode上でコードを編集後、Ctrl+sなどで保存するとシュバッと綺麗にしてくれる。
フォーマッターでは判断できない場合はエラーになり綺麗にならない。
(たまにはLintを無視したいので抑制する)

本記事で説明すること

  • ESLintの適用手順
  • Prettierの適用手順

本記事で解説できているか怪しいこと

  • 最新のESLint+Prettierのベストプラクティスかどうか

Prettier と ESLint の組み合わせの公式推奨が変わり plugin が不要になった
などの記事で触れられているように、去年くらいから公式の推奨方法などが変わっているそうです。
参考にした情報によっては現在のベストプラクティスとは異なる可能性があります。
今回紹介するのは手順の一例程度に捉えていただければ幸いです:pray:

環境

  • eslint:7.20.0
  • eslint-config-prettier:7.2.0
  • prettier:2.2.1
    ※eslint-plugin-prettierは入れないらしい。
  • VSCodeの拡張機能でESLintPrettierを入れておく。

githubリポジトリ

https://github.com/pbyoshida/postal-app

導入編

それではローカル環境でのLint、フォーマッターの構築を進めていきます。

各モジュールの導入

とりあえずはnpmでインストールしていきましょう。
※Lintやフォーマッターは開発者が使うので--save-devを付けます。

$ npm install --save-dev eslint eslint-config-prettier prettier

package.json見てみましょう。

package.json(導入時点)
{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject"
  },
  "dependencies": {
    "axios": "^0.21.1",
    "expo": "~40.0.0",
    "expo-status-bar": "~1.0.3",
    "react": "16.13.1",
    "react-dom": "16.13.1",
    "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
    "react-native-web": "~0.13.12"
  },
  "devDependencies": {
    "@babel/core": "~7.9.0",
    "eslint": "^7.20.0",
    "eslint-config-prettier": "^7.2.0",
    "prettier": "^2.2.1"
  },
  "private": true
}

devDependenciesに導入できていそうですね。

ESLintのセットアップ

以下のコマンドでセットアップを開始します。

$ npx eslint --init

最初はどのようにESLintを利用するのかの選択です。

? How would you like to use ESLint? ...
  To check syntax only
  To check syntax and find problems
> To check syntax, find problems, and enforce code style

一番下を選択してコードスタイルを適用します。
次はモジュールの種類です。

? What type of modules does your project use? ...
> JavaScript modules (import/export)
  CommonJS (require/exports)
  None of these

一番上のJavaScriptを選択します。
次はフレームワークの選択です。

? Which framework does your project use? ...
> React
  Vue.js
  None of these

今回はReactなので一番上を選択します。
次はTypeScriptかどうかの選択です。

? Does your project use TypeScript? » No / Yes

今回は違うのでNoを選択。
次は実行環境です。

? Where does your code run? ...  (Press <space> to select, <a> to toggle all, <i> to invert selection)
  Browser
√ Node

こちらはNodeを選択します。
次はコードのスタイルを聞かれます。

? How would you like to define a style for your project? ...
> Use a popular style guide
  Answer questions about your style
  Inspect your JavaScript file(s)

今回はUdemyの講座で使用していたAirbnbのスタイルを適用しようと思います。

? Which style guide do you want to follow? ...
> Airbnb: https://github.com/airbnb/javascript
  Standard: https://github.com/standard/standard
  Google: https://github.com/google/eslint-config-google

Airbnbを選択します。
次はconfigファイルの形式です。

? What format do you want your config file to be in? ...
  JavaScript
  YAML
> JSON

ここではjsonを選びましょう。(多分一般的かなと)
最後は依存するライブラリを自動的にインストールしてくれます。yesを選択してインストールしましょう。
(ついでにここまでの選択も載せておきます。)

√ How would you like to use ESLint? · style
√ What type of modules does your project use? · esm
√ Which framework does your project use? · react
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · node
√ How would you like to define a style for your project? · guide
√ Which style guide do you want to follow? · airbnb
√ What format do you want your config file to be in? · JSON
Checking peerDependencies of eslint-config-airbnb@latest
The config that you've selected requires the following dependencies:

eslint-plugin-react@^7.21.5 eslint-config-airbnb@latest eslint@^5.16.0 || ^6.8.0 || ^7.2.0 eslint-plugin-import@^2.22.1 eslint-plugin-jsx-a11y@^6.4.1 eslint-plugin-react-hooks@^4 || ^3 || ^2.3.0 || ^1.7.0
? Would you like to install them now with npm? » No / Yes

さて、セットアップ後のpackage.jsonは以下になりました。

package.json(セットアップ後)
{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject"
  },
  "dependencies": {
    "axios": "^0.21.1",
    "expo": "~40.0.0",
    "expo-status-bar": "~1.0.3",
    "react": "16.13.1",
    "react-dom": "16.13.1",
    "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
    "react-native-web": "~0.13.12"
  },
  "devDependencies": {
    "@babel/core": "~7.9.0",
    "eslint": "^7.20.0",
    "eslint-config-airbnb": "^18.2.1",
    "eslint-config-prettier": "^7.2.0",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-react": "^7.22.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "prettier": "^2.2.1"
  },
  "private": true
}

セットアップ後は.eslintrc.jsonというファイルが作成されています。

.eslintrc.json
{
  "env": {
    "es2021": true,
    "node": true
  },
  "extends": [
    "plugin:react/recommended",
    "airbnb",
    "eslint-config-prettier", // 追加
    "prettier" // 追加
  ],
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 12,
    "sourceType": "module"
  },
  "plugins": ["react"],
  "rules": {
    "import/prefer-default-export": 0, // 追加
    "no-use-before-define": 0 // 追加
  }
}

もしも無視したいESLintのルールなどがある場合には上記のファイルに追記していきます。
上では追加とコメントしている4行を追記しています。

VSCodeの設定

続いてVSCodeの設定をしていきます。
.vscodeというフォルダを作成し、その中にsettings.jsonというファイルを作成します。
image.png
中身はとりあえずこんな感じにします。

settings.json
{
  /* Linter and Formatter */
  // linter
  "eslint.packageManager": "npm", // ESLintライブラリ解決時に使うパッケージマネージャ
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true, // eslint
    "source.fixAll.stylelint": true // Stylelint
  },
  // formatter
  "editor.defaultFormatter": "esbenp.prettier-vscode", // デフォルトフォーマッターをPrettier
  "editor.formatOnSave": true,
  "editor.formatOnPaste": true,
  "editor.formatOnType": true,
  // Prettier対象外言語
  "prettier.disableLanguages": ["markdown"]
}

このあたりは好みがあると思うので、色々と試してみてください。

Prettierの設定

最後にPrettierの設定です。.prettierrcというファイルを作成し、その中に適用したいルールを記述します。

.prettierrc
{
  "trailingComma": "es5",
  "tabWidth": 2,
  "semi": true,
  "singleQuote": true
}

一部デフォルトでそうなっているようなものもありますが、とりあえずは上記のような形で記載します。
各オプションはドキュメントを参考にしてください。

動作確認

動作をさせようとしていたらESLINTの箇所に下記のようなマークが出ていました。
image.png
そこをクリックして、
image.png
「Allow Everywhere」を押して承認します。

ESLintの確認

image.png
前回のソースコードを見てみると2か所怒られていたようです。。
1個目はswitch文にdefaultのケースが無い
2個目はaddressが重複して定義されている
とのことで修正します。。
image.png

APIのステータスが200でも400でも500でもない場合って何なんだ、、と思いつつとりあえず予期しない動作としました。
2個目は変数名を変えています。

ただし、どうしてもESLintのルールを無視したい時などがあれば以下のようにコメントしてください。

グローバルに無視する場合
/* eslint-disable */
次の1行だけ無視したい場合
/* eslint-disable-next-line */

Prettierの確認

こちらは適当にスペースを入れまくってみます。
image.png
こんな感じにガタガタでも、Ctrl+sで保存すると、
image.png
シュバッと綺麗に。気持ちいい。。

最後に

前回の記事は「まぁこんな感じじゃないの」と思いながら実装していたので、改めてLintやフォーマッターを入れてみると全然整っていなかったことが分かります。
チームで開発する上ではこういった議論や指摘で時間をロスしないために、あらかじめルールを決めておくことが大切だと実感できますね。
今回は寄り道しましたが、次回もう一つのAPIを実行するコードを実装してシリーズを終わりたいと思います。

2021/2/21 最終回を書きました。
【React Native】簡単なAPI連携のアプリケーションを作成する ④天気情報表示の実装

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