Casual Developers Note

エンジニアやデザイナー向けの技術情報・英語学習情報・海外留学情報・海外旅行情報を提供中。世界を旅して人生を楽しもう。

  • ホーム
  • 技術 Tips & Tutorials
  • 技術塾
  • ライフハック
  • 海外留学
  • 英語学習
  • コラム
  • お問い合わせ
現在の場所:ホーム / アーカイブreact

2018年4月20日 By KD コメントを書く

初級JavascriptフルスタックエンジニアのためのReactとExpressの同時開発チュートリアル(基本的なアプリ作成と同時開発環境構築編)

初級JavascriptフルスタックエンジニアのためのReactとExpressの同時開発チュートリアル(基本的なアプリ作成と同時開発環境構築編)

フルスタックエンジニアの需要はスタートアップであればあるほど強いものです。そして、エンジニアにとってフロントエンドとバックエンドの両方を扱うことは楽しみでもあります。今回はよくあるReactとExpressを使ったアプリケーションを作成し、同時開発の方法を紹介します。

はじめに

スタートアップにおいて、フルスタックエンジニアの需要が高いのは理に適っています。なぜなら、フロントエンドエンジニアやバックエンドエンジニアという枠組みでエンジニアを採用してしまうと、単純に2倍のコストがかかるからです。そして、役割を分けるということはコミュニケーションコストの発生を防ぐことができません。仕事をしている人なら誰でも知っている話ですが、コミュニケーションコストが最も高く付きます。なぜ周りのマネージャーが仕事をしていないかのように見えるのか?なぜならコストの大半をコミュニケーションに費やしているからですね。私もマネジメントの仕事をしたことがありますが、正直、開発の仕事より何倍もストレスが多いです。

話が少し脱線してしまいましたが、今回は、JavascriptのフルスタックエンジニアとしてReactとExpressを同時に開発する事を想定して、基本的なサンプルアプリケーションを構築しながら、同時にフロントエンドとバックエンドを開発する環境構築方法を紹介していきます。IsomophicとかUniversalとかそういう話はしませんのあしからず。

MongoDBの準備

それでは、ReactとExpressの同時開発環境を作るために、まずは簡単ではありますが、フロントエンドをReact、バックエンドをExpressとMongoDBでサンプルアプリケーションを構築していきましょう。

MongoDBを起動する(Mac編)

今回は開発用のデータベースとしてMongoDBを使います。Macの場合は、Homebrewでインストールし、launchctlで常駐化させます。

$ brew install mongodb
$ ln -s /usr/local/opt/mongodb/homebrew.mxcl.mongodb.plist ~/Library/LaunchAgents
$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mongodb.plist
$ mongo --version
MongoDB shell version v3.6.3
...

これでPCを起動するとMongoDBが自動的に立ち上がります。デフォルトのポートは「27017」です。

MongoDBを起動する(Docker編)

最近はなんでもDockerにするのが主流ですし、MacでもWindowsでも起動できるので普通に楽ですね。

一応ですが、Dockerをインストールしていない場合は、HomebrewからCask経由で簡単にできます。

$ brew cask install docker
$ docker --version
Docker version 17.12.0-ce, build c97c6d6

続いて、以下のコマンドを実行すればOKです。

$ docker container run -d --rm --name mongo-docker -p 27018:27017 mongo:3.6.3
$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                      NAMES
75002cc641b5        mongo:3.6.3         "docker-entrypoint.s…"   12 seconds ago      Up 22 seconds       0.0.0.0:27018->27017/tcp   mongo-docker
$ docker exec -it mongo-docker mongo --version
MongoDB shell version v3.6.3
...

ポートは「27018」を指定しています。

Expressのサンプルアプリケーションの開発

フルスタックの経験が少ないエンジニアがSPAのアプリケーション開発しようとする場合、なぜかフロントエンドから作ろうとしがちですが、SPAのアプリケーションを作る場合はAPIから開発するのが基本です。(もちろん最低でもモックなどの見た目はあった方がよいですが)

Expressのプロジェクト作成

それでは、まずは必要なフォルダ作成やパッケージインストールからはじめましょう。

$ mkdir full-stack-app
$ cd full-stack-app/
$ brew install yarn
$ yarn global add nodemon
$ yarn init -y
$ yarn add express mongoose body-parser
$ mkdir server
$ touch server/server.js
$ mkdir server/models
$ touch server/models/User.js
$ tree server/
server/
├── models
│   └── User.js
└── server.js

MongooseでUserモデルを作る

MongoDBのスキーマを定義します。「User.js」は以下のようにします。

const mongoose = require('mongoose');
const { Schema } = mongoose;

const userSchema = new Schema({
  name: {
    type: String,
    required: true,
    trim: true,
    unique: 1
  },
  age: Number
});

const User = mongoose.model('users', userSchema);

module.exports = User;

ExpressでREST APIを作る

ここは、単純にUserオブジェクトをCRUDするREST APIを作りましょう。「server.js」は以下のようにしましょう。

const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const PORT = process.env.PORT || 3001;
const User = require('./models/User');

mongoose.connect('mongodb://localhost:27018/sampledb');

const app = express();
app.use(bodyParser.json());

app.post('/users', (req, res) => {
  const user = new User(req.body);
  user.save((err, addedUser) => {
    if (err)
      return res.status(400).json({
        errorMessage: 'faild to add the user. User name must be unique.'
      });
    res.send(addedUser);
  });
});

app.get('/users', (req, res) => {
  User.find({}, (err, users) => {
    res.send(users);
  });
});

app.get('/users/:id', (req, res) => {
  const id = req.params.id;
  User.findById(id, (err, user) => {
    if (err)
      return res.status(400).json({
        errorMessage: 'the user not found.'
      });
    res.send(user);
  });
});

app.delete('/users/:id', (req, res) => {
  const id = req.params.id;
  User.findByIdAndRemove(id, (err, deletedUser) => {
    if (err)
      return res.status(400).json({
        errorMessage: 'faild to delete the user.'
      });
    res.send(deletedUser);
  });
});

app.delete('/users', (req, res) => {
  User.remove({}, err => {
    if (err)
      return res.status(400).json({
        errorMessage: 'faild to delete all users.'
      });
    res.send(true);
  });
});

app.listen(PORT, () => {
  console.log(`Server up on ${PORT}`);
});

簡単ではありますがバックエンド完成です。

Expressのサンプルアプリケーションの動作確認

出来上がったバックエンドの動作確認をしましょう。コンソールを2つ開いて下さい。

1つ目のコンソールでExpressのサンプルアプリケーションを起動します。

$ node server/server.js
Server up on 3001

2つ目のコンソールからcurlで確認していきます。

$ curl -H 'Content-Type:application/json' -d '{"name":"Tom","age":20}' http://localhost:3001/users
{"name":"Tom","age":20,"_id":"5aa1486997f7dd76ae3ddec1","__v":0}
$ curl -H 'Content-Type:application/json' -d '{"name":"John","age":30}' http://localhost:3001/users
{"name":"John","age":30,"_id":"5aa1487397f7dd76ae3ddec2","__v":0}
$ curl -H 'Content-Type:application/json' -d '{"name":"Mary","age":25}' http://localhost:3001/users
{"name":"Mary","age":25,"_id":"5aa1487b97f7dd76ae3ddec3","__v":0}
$ curl http://localhost:3001/users | jq
...
[
  {
    "_id": "5aa1486997f7dd76ae3ddec1",
    "name": "Tom",
    "age": 20,
    "__v": 0
  },
  {
    "_id": "5aa1487397f7dd76ae3ddec2",
    "name": "John",
    "age": 30,
    "__v": 0
  },
  {
    "_id": "5aa1487b97f7dd76ae3ddec3",
    "name": "Mary",
    "age": 25,
    "__v": 0
  }
]
$ curl http://localhost:3001/users/5aa1486997f7dd76ae3ddec1 | jq
...
{
  "_id": "5aa1486997f7dd76ae3ddec1",
  "name": "Tom",
  "age": 20,
  "__v": 0
}
$ curl http://localhost:3001/users/5aa1487397f7dd76ae3ddec2 | jq
...
{
  "_id": "5aa1487397f7dd76ae3ddec2",
  "name": "John",
  "age": 30,
  "__v": 0
}
$ curl http://localhost:3001/users/5aa1487b97f7dd76ae3ddec3 | jq
...
{
  "_id": "5aa1487b97f7dd76ae3ddec3",
  "name": "Mary",
  "age": 25,
  "__v": 0
}
$ curl -X DELETE http://localhost:3001/users/5aa1486997f7dd76ae3ddec1
{"_id":"5aa1486997f7dd76ae3ddec1","name":"Tom","age":20,"__v":0}
$ curl http://localhost:3001/users | jq
...
[
  {
    "_id": "5aa1487397f7dd76ae3ddec2",
    "name": "John",
    "age": 30,
    "__v": 0
  },
  {
    "_id": "5aa1487b97f7dd76ae3ddec3",
    "name": "Mary",
    "age": 25,
    "__v": 0
  }
]
$ curl -X DELETE http://localhost:3001/users
true
$ curl http://localhost:3001/users | jq
...
[]

ちゃんと動いていますね。

Reactの開発準備

フロントエンドはひとまず雛形だけ作ります。

プロジェクトの作成

流行りの「create-react-app」を使ってささっと作りましょう。

$ yarn global add create-react-app
$ create-react-app client
$ cd client
$ yarn add axios
$ rm src/*
$ touch src/index.js
$ touch src/App.js
$ tree src/
src/
├── App.js
└── index.js

index.jsの作成

ここは一般的な形にします。

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

App.jsの作成

ここも表示だけの最小限の形にします。

import React, { Component } from 'react';

class App extends Component {
  render() {
    return <div>Hello App!</div>;
  }
}

export default App;

余談ですが、ここでエディタのVisual Studio Codeを使っていると、Reactのスニペットで一瞬でここまでできあがります。使っていない方はVSCodeのプラグインを探してみて下さい。

開発のためにフロントエンドとバックエンドを同時に起動させる

ここでフロントエンドとバックエンドをつなげて開発するための設定を行います。そうしないとコンソールをフロントエンドとバックエンドで2つも開かなければなりません。めんどくさいです。なので、同時起動の方法を説明します。YarnとNPMの両方の設定を載せますが、このチュートリアルではYarnを使っているので、最後までやりたい方はYarnの設定だけして下さい。

プロキシ設定

フロントエンドをポート3000、バックエンドをポート3001で起動して接続させるために、フロントエンドの「package.json」に設定をする必要があります。

$ cd client

フロントエンドの「package.json」に以下のスクリプトを追加します。「package.json」がフロントエンドとバックエンドで2つあるので注意して下さい。

  "proxy": {
    "/users/*": {
      "target": "http://localhost:3001"
    }
  }

同時に起動させる方法(Yarn編)

プロジェクトのフォルダに移動します。

$ cd ..
$ pwd
/Users/you/full-stack-app

必要なパッケージをインストールします。

$ yarn add --dev npm-run-all react-scripts

バックエンドの「package.json」に以下のスクリプトを追加します。

  "scripts": {
    "start": "node server/server.js",
    "dev:server": "nodemon server/server.js",
    "dev:client": "yarn --cwd client start",
    "dev": "npm-run-all -p dev:*"
  },

あとは以下のコマンドで同時開発を開始できます。

$ yarn dev

同時に起動させる方法(NPM編)

ここまでYarnを使って開発していましたが、NPMでも似たような環境が作れます。

プロジェクトのフォルダに移動します。

$ cd ..
$ pwd
/Users/you/full-stack-app

必要なパッケージをインストールします。

$ npm install --save-dev concurrently

バックエンドの「package.json」に以下のスクリプトを追加します。

  "scripts": {
    "start": "node index.js",
    "server": "nodemon index.js",
    "client": "npm run start --prefix client",
    "dev": "concurrently \"npm run server\" \"npm run client\""
  },

同じく以下のコマンドで同時開発を開始できます。

$ npm run dev

起動すると以下の画面がブラウザで自動で立ち上がります。

スクリーンショット 2018 03 08 1 45 17

これで、フロントエンドとバックエンドを同時に開発する環境ができあがりました。

Reactのサンプルアプリケーションの開発

ここまできたら、Reactを仕上げてしまいましょう。

今回はユーザ情報を管理する簡単なアプリケーションとして作っていきましょう。

デザイン用のReactコンポーネントのインストール

Reactの良いところは、さまざまなフロントエンド用のデザインがReactコンポーネントとして提供されていることです。今回はReact Materializeを使うことにします。

$ cd client
$ yarn add react-materialize
$ cd ..
$ pwd
/Users/you/full-stack-app
$ tree client/public/
client/public/
├── favicon.ico
├── index.html
└── manifest.json
$ tree client/src/
client/src/
├── App.js
└── index.js
$ yarn dev

最後のコマンドでReactとExpressが両方起動して表示されました。

index.htmlの作成

「index.html」は以下のようにします。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="theme-color" content="#000000">
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">

  <!-- Import Google Icon Font -->
  <link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  <!-- Import materialize.css -->
  <link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/css/materialize.min.css" rel="stylesheet">
  <title>User Management App</title>
</head>

<body>
  <noscript>
    You need to enable JavaScript to run this app.
  </noscript>
  <div id="root"></div>

  <!-- Import jQuery before materialize.js -->
  <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/js/materialize.min.js"></script>
</body>

</html>

単にReact Materialize用の設定を追加しただけです。

App.jsの作成

これはReactのメインですね。以下のようにします。

import React, { Component } from 'react';
import axios from 'axios';
import { Button, Row, Input } from 'react-materialize';
class App extends Component {
state = {
name: '',
age: '',
users: [],
error: '',
message: ''
};
componentWillMount() {
axios.get(`/users`).then(response => {
this.setState({ users: response.data });
});
}
handleInputName = event => {
this.setState({ name: event.target.value });
};
handleInputAge = event => {
this.setState({ age: event.target.value });
};
submitForm = event => {
event.preventDefault();
axios
.post(`/users`, {
name: this.state.name,
age: this.state.age
})
.then(response => {
this.setState({
users: [...this.state.users, response.data],
error: '',
message: `Info: ${response.data.name} was added.`
});
})
.catch(error => {
this.setState({
error: `Error: ${error.response.data.errorMessage}`,
message: ''
});
});
};
deleteUser = id => {
axios
.delete(`/users/${id}`)
.then(response => {
this.setState({
users: this.state.users.filter(
user => user._id !== response.data._id
),
error: '',
message: `Info: ${response.data.name} was deleted.`
});
})
.catch(error => {
this.setState({ error: `Error: ${error.response.data.errorMessage}` });
});
};
deleteAllUsers = event => {
axios
.delete(`/users`)
.then(response => {
this.setState({
users: [],
error: '',
message: 'Info: all users were deleted.'
});
})
.catch(error => {
this.setState({
error: `Error: ${error.response.data.errorMessage}`,
message: ''
});
});
};
clearMessage = event => {
this.setState({ message: '' });
};
clearError = event => {
this.setState({ error: '' });
};
renderUsers() {
const users = this.state.users;
return users.map(user => (
<tr key={user._id}>
<td>{user.name}</td>
<td>{user.age}</td>
<td>
<Button onClick={user_id => this.deleteUser(user._id)}>Delete</Button>
</td>
</tr>
));
}
render() {
return (
<div>
<div
style={
this.state.error
? {
backgroundColor: '#f72e2e',
textAlign: 'center',
padding: '10px 0'
}
: null
}
onClick={this.clearError}
>
{this.state.error}
</div>
<div
style={
this.state.message
? {
backgroundColor: '#42a7f4',
textAlign: 'center',
padding: '10px 0'
}
: null
}
onClick={this.clearMessage}
>
{this.state.message}
</div>
<div style={{ width: '80%', margin: '0 auto', textAlign: 'center' }}>
<h2>User Management App</h2>
<h4>Add New User</h4>
<form onSubmit={this.submitForm}>
<Row>
<Input
type="text"
placeholder="Enter name"
s={5}
label="Name"
value={this.state.name}
onChange={this.handleInputName}
/>
<Input
type="number"
placeholder="Enter age"
s={5}
label="Age"
value={this.state.age}
onChange={this.handleInputAge}
/>
<Button type="submit" s={2}>
Add
</Button>
</Row>
</form>
<h4>Delete All Users</h4>
<div>
<Button onClick={this.deleteAllUsers}>Delete All</Button>
</div>
<div>
<h4>Users</h4>
<table>
<thead>
<tr>
<th style={{ width: '30%' }}>Name</th>
<th style={{ width: '30%' }}>Age</th>
<th style={{ width: '30%' }} />
</tr>
</thead>
<tbody>{this.renderUsers()}</tbody>
</table>
</div>
</div>
</div>
);
}
}
export default App;

これでフロントエンドの開発も完了です!

完成したサンプルアプリケーションで遊んでみる

それでは最後に、作ったサンプルアプリケーションで遊んでみましょう。

ユーザを追加してみる

「yarn dev」で表示して確認しましょう。

スクリーンショット 2018 03 08 5 35 18

名前と年齢を入力してADDボタンでユーザが追加されました。

スクリーンショット 2018 03 08 5 36 34

同じ名前でADDボタンを押すとエラーが表示されました。

スクリーンショット 2018 03 08 5 36 47

データベース上にもちゃんとデータが登録されています。

スクリーンショット 2018 03 08 5 37 02

ユーザを削除してみる

ユーザの横のDELETEボタンを押すと、ユーザが削除されました。

スクリーンショット 2018 03 08 5 37 31

データベース上からも削除させています。

スクリーンショット 2018 03 08 5 37 43

DELETE ALLボタンを押すとユーザ情報が全て削除されました。

スクリーンショット 2018 03 08 5 38 01

データベースも空です。

スクリーンショット 2018 03 08 5 38 05

少し動きで気になる点がありますが、サンプルアプリケーションはうまく動いているようです。せっかくなので、何か機能を追加してみて下さい。

最後に

いかがでしたか?これでReactとNodeJSでフロントエンドとバックエンドを同時に開発することができるようになったのではないでしょうか。それにしてもJavascriptでフロントエンドもバックエンドも開発できるのは本当に素晴らしいです。小さい規模の開発であればバックエンドでNodeJS以外を選択する理由はほとんどありませんし、フロントエンドはReactを選んでおけば今のところ間違いありません。それでは。

環境

  • PC : macOS High Sierra 10.13.3
  • docker : Docker version 17.12.0-ce, build c97c6d6
  • mongoイメージ : 3.6.3
  • NodeJS : v9.5.0
  • yarn : 1.3.2
  • create-react-app : 1.5.2
  • [server] express : 4.16.2
  • [server] mongoose : 5.0.7
  • [server] body-parser : 1.18.2
  • [server] npm-run-all : 4.1.2
  • [server] react-scripts : 1.1.1
  • [server] concurrently : 3.5.1
  • [client] axios : 0.18.0
  • [client] react : 16.2.0
  • [client] react-dom : 16.2.0
  • [client] react-scripts : 1.1.1
  • [client] react-materialize : 2.1.0

カテゴリ : 技術 Tips & Tutorials タグ : express, nodejs, react

  • « 前のページ
  • 1
  • 2
  • 3
  • 4

ブログ更新情報や海外の関連情報などを配信する無料メルマガ

Sponsored Links

About Author

KD

世界を旅し日本を愛するエンジニア。大学でコンピュータサイエンスの楽しさを学び、日本の大手IT企業で働く中で、新しい技術やスケールするビジネスが北米にある事に気づく。世界に挑戦するための最大の壁が英語であったため、フィリピン留学およびカナダ留学を経て英語を上達させた。現在は日本在住でエンジニアとして働きつつ、次の挑戦に備えて世界の動向を注視している。挑戦に終わりはない。このブログでは、エンジニアやデザイナー向けの技術情報から、海外に留学したい人向けの留学情報、海外に興味がある人向けの海外旅行情報など、有益な情報を提供しています。

https://casualdevelopers.com/

最近の投稿

  • 2020年JS周辺のバックエンド寄りの注目技術!ネクストNodeJSの「Deno」と分散型パッケージレジストリの「Entropic」の紹介

    2020年JS周辺のバックエンド寄りの注目技術!ネクストNodeJSの「Deno」と分散型パッケージレジストリの「Entropic」の紹介

    2020年1月13日
  • 今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~

    今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~

    2019年11月4日
  • ReactのためのEslintおよびPrettierの設定方法 ~Airbnb JavaScript Style Guideの適用~

    ReactのためのEslintおよびPrettierの設定方法 ~Airbnb JavaScript Style Guideの適用~

    2019年10月30日
  • BashからZshに移行する方法(Mac編)

    BashからZshに移行する方法(Mac編)

    2019年10月21日
  • Create React Appを使わないでゼロからReactの開発環境を構築する方法(Webpack/Docker編)

    Create React Appを使わないでゼロからReactの開発環境を構築する方法(Webpack/Docker編)

    2019年9月30日

カテゴリ

  • 技術 Tips & Tutorials (100)
  • 技術塾 (6)
  • ライフハック (26)
  • 海外留学 (12)
  • 英語学習 (3)
  • コラム (6)

アーカイブ

最高の学習のために

人気記事ランキング

  • MySQLで「ERROR 2003 (HY000): Can't connect to MySQL server」と怒られた時の対処法
    MySQLで「ERROR 2003 (HY000): Can't connect to MySQL server」と怒られた時の対処法
  • Jupyter Notebookで「The kernel appears to have died. It will restart automatically.」というエラーが出た場合の原因と対処法
    Jupyter Notebookで「The kernel appears to have died. It will restart automatically.」というエラーが出た場合の原因と対処法
  • SAKURAのメールボックスで独自ドメインのメールを設定し、Gmail経由で送受信する方法
    SAKURAのメールボックスで独自ドメインのメールを設定し、Gmail経由で送受信する方法
  • バンクーバー留学豆知識:バンクーバーのATMで日本の銀行のキャッシュカードを使ってお得にお金を引き出す方法
    バンクーバー留学豆知識:バンクーバーのATMで日本の銀行のキャッシュカードを使ってお得にお金を引き出す方法
  • Amazon EC2インスタンスにSSHできなくなった時の対処法
    Amazon EC2インスタンスにSSHできなくなった時の対処法
  • Expressで「Cannot set headers after they are sent to the client」と怒られた時の対処法
    Expressで「Cannot set headers after they are sent to the client」と怒られた時の対処法
  • TumblrからWordPressにブログ移転する最適な方法
    TumblrからWordPressにブログ移転する最適な方法
  • Go言語のためのVisual Studio Codeの設定方法
    Go言語のためのVisual Studio Codeの設定方法
  • バンクーバー留学豆知識:水道水は美味しく飲めるBritaさえあれば!
    バンクーバー留学豆知識:水道水は美味しく飲めるBritaさえあれば!
  • SpringBootのProfile毎にプロパティを使い分ける3つの方法
    SpringBootのProfile毎にプロパティを使い分ける3つの方法

Bitcoin寄付 / BTC Donation

Bitcoinを寄付しよう

BTC
Select Payment Method
Personal Info

Donation Total: BTC 0.0010

このブログの運営のためにBitcoinでの寄付を募集しています。お気持ち程度の寄付を頂けると管理者の励みになります。

Bitcoin寄付について知りたい方はこちらの記事へ

ビットコイン取引ならここ

  • ホーム
  • 技術 Tips & Tutorials
  • 技術塾
  • ライフハック
  • 海外留学
  • 英語学習
  • コラム
  • サイトマップ
  • タグ一覧
  • プライバシーポリシー
  • お問い合わせ

Copyright © 2023 KD - Casual Developers Notes