- 投稿日:2020-01-03T23:56:27+09:00
GitHub Pages でReact Appを公開
はじめに
create-react-appで作成したアプリをGitHub Pagesで公開してみます。リポジトリの作成
$ yarn create react-app myapp --template typescript $ cd myapp$ git commit -m "first commit" $ git remote add origin git@github.com:pure-adachi/myapp.git $ git push -u origin master
gh-pagesでデプロイ
gh-pagesインストール$ yarn add gh-pagespackage.json// ... "homepage": "http://pure-adachi.github.io/myapp", "scripts": { // ... "deploy": "yarn build && gh-pages -d build" },デプロイ
$ yarn deploy何度かパスワードを聞かれました。
コミット&プッシュせずに、更には
masterブランチですがデプロイ出来ました。自動で
gh-pagesブランチが作成されて、GitHub Pagesのブランチ設定も行われます。
-bオプションでブランチの指定が出来ますが、Github Pagesで配信可能なブランチはmasterかgh-pagesなので
デフォルトのgh-pagesブランチを使うことにしました。http://pure-adachi.github.io/myapp にアクセスすると動作が確認出来ました。
終わりに
簡単に出来ましたね
参考文献
- 投稿日:2020-01-03T21:38:43+09:00
Firebase+React+Reduxで多機能チャットを実装しよう【リアルタイムチャット~React編~】
Firebase&React&Reduxで多機能チャットを実装しよう【リアルタイムチャット編】
今回から本格的にReactとReduxを触っていきます。
一気に難易度が上がるので覚悟してください!笑ガイド
- 環境準備編
- リアルタイムチャット~React編~ ←イマココ
- リアルタイムチャット~Redux編~
- ログイン機能
- チャットルーム選択機能
- チャットルーム設定機能
- デモページ
- GitHub
ディレクトリ構成
前回までのおさらい。
ディレクトリ構成は現在以下の通りです。要らないファイルを削除して、ディレクトリを追加作成します。
Beforereact-chat │- build/ │- public/ │ │- favicon.ico │ │- index.html │ │- logo192.png <-- REM │ │- logo512.png <-- REM │ │- manifest.json │ └ robots.txt │- src/ │ │- firebase/ │ │ │- config.js │ │ └ index.js │ └ index.js │- node_modules/ │- .firebaserc │- database.rules.json │- firebase.json │- package.json │- package-lock.json └ storage.rules.jsonAfterreact-chat │- build/ │- public/ │ │- favicon.ico │ │- index.html │ │- manifest.json │ └ robots.txt │- src/ │ │- components/ <-- ADD │ │- containers/ <-- ADD │ │- firebase/ │ │ │- config.js │ │ └ index.js │ │- templates/ <-- ADD │ │- index.js │ └ style.css <-- ADD │- node_modules/ │- .firebaserc │- database.rules.json │- firebase.json │- package.json │- package-lock.json └ storage.rules.jsonReact基本ファイルの準備
create-react-appで作成した開発環境の基本を簡単に説明しておきます。
public:雛形となるhtmlファイルを格納するディレクトリsrc:コンパイル前のソースコードを格納するディレクトリnpm run build:src配下のファイルをコンパイルするコマンドbuild:コンパイルされたファイルを格納するディレクトリ雛形となるHTMLファイル
雛形となるHTMLファイルには以下を記載します。
- サイトのタイトルやメタ情報
- Firebaseに必要なスクリプト
- Reactコンポーネントをrenderするルートdiv
public/index.html<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Firebase React Chat!</title> <!-- update the version number as needed --> <script defer src="/__/firebase/7.5.1/firebase-app.js"></script> <!-- include only the Firebase features as you need --> <script defer src="/__/firebase/7.5.1/firebase-auth.js"></script> <script defer src="/__/firebase/7.5.1/firebase-database.js"></script> <script defer src="/__/firebase/7.5.1/firebase-messaging.js"></script> <script defer src="/__/firebase/7.5.1/firebase-storage.js"></script> <!-- initialize the SDK after all desired features are loaded --> <script defer src="/__/firebase/init.js"></script> </head> <body> <div id="root"></div> </body> </html>Reactコンポーネントをindex.jsファイル
npm run buildでコンパイルする際に参照されるjsファイルです。
細かい説明は省きますが、後ほど登場するファイルたちの情報をまとめておきます。src/index.jsimport React from 'react'; import * as ReactDOM from 'react-dom'; import {Provider} from 'react-redux'; import {RootContainer} from './containers'; import {configureStore} from './modules'; import './style.css' ReactDOM.render( <Provider store={configureStore()}> <RootContainer /> </Provider>, document.getElementById('root') );
ReactDOM.render(element, container)で、Reactコンポーネント(element)を雛形HTMLのDOM(container)にレンダーして表示させています。先ほど作成したHTMLファイル内の、idがrootのdiv要素にレンダーさせているということですね。
それでは、レンダーされるコンポーネントを作っていきましょう。
Reactコンポーネントの作成
ReactをReduxと共に用いる場合、コンポーネントは2つに分けるのが一般的です。
- Presentational Components --> 見た目(View)を担当する。「コンポーネント」と呼ばれる
- Container Components --> ロジック(振る舞い)に関わる。「コンテナー」と呼ばれる。
より詳細な定義はコチラの記事を参考にしてください。
上記を踏まえて、ディレクトリ構成で追加したディレクトリに格納するファイルの役割は以下の通りです。
components: 最小構成要素(パーツ)となるPresentational Components、子コンポーネントです。templates:components内のパーツを組み合わせたPresentational Components、親コンポーネントです。containers:templatesのPresentational Componentsと"状態"を紐づけるための中継役、Container Componentsです。ログインページを例にすると
componentsは「ボタン」や「テキストボックス」
templatesは「ボタン」や「テキストボックス」を組み合わせた「ログインフォーム」
containersはログイン処理後の見た目(View)がどのように変化するのか定義少し難しいですよね。
「コンポーネントは見た目(View)のみを持ち、状態(State)と分離させる」というReduxの設計思想を実現するための構成だと思ってください。
それでは、実際にリアルタイムチャットを実装するためのファイルを書いていきます。
componentsファイルの作成
チャットを実装するためには以下のパーツが必要だと考えました。
- メッセージ表示エリア
- メッセージ入力エリア
- メッセージ送信ボタン
この記事では2と3について解説します。
当記事ではMaterial-UIを使っていますが、詳しい解説を割愛します。
デザインのカスタマイズには癖があるけど、それっぽいUIが簡単に作れて便利だゾ。また、デモページを作った都合上、独自CSSを使っています。
CSSはGithubのソースコードから確認してください。(src/style.css)に記述しています。src/components/Chat/TextInput.jsimport React from 'react'; import TextField from '@material-ui/core/TextField'; const TextInput = (props) => { return ( <form className="p-chat__textarea c-grid-center" noValidate autoComplete="off"> <TextField id="standard-text" className="c-grid-full" margin="normal" label="メッセージを入力..." multiline rowsMax="4" onChange={e => props.onChange(e.target.value)} value={props.value} /> </form> ); }; export default TextInput;src/components/Chat/SendButton.jsimport React from 'react'; import Button from '@material-ui/core/Button'; import SendIcon from '@material-ui/icons/Send'; const SendButton = (props) => { return ( <Button variant="contained" color="primary" className="p-chat__button-send" onClick={() => props.onClick(props.value, props.roomId, props.fromId, props.toId, props.userIds)}> <SendIcon /> </Button> ); } export default SendButton;それぞれのポイントを解説します。
両コンポーネントは、Stateless Functional Componentsという形式で宣言されています。
初期のReactでは、Classを用いてコンポーネントを宣言していました。
近年のReactでは、Stateless Functional Components、つまり"関数"として宣言することが推奨されています。Stateless Functional Componentsには以下のメリットがあります。
- constructorを使わなくて良い
- thisが必要ない
- 状態を持たない(stateless)コンポーネントにできる
つまり、コードがシンプルになってハッピーってことですね☆
また、ES6のアロー関数を使ってさらにシンプルに書いています。
そして重要なのが、関数の引数に渡している
propsです。
この部分const TextInput = (props) => {です。
この部分const SendButton = (props) => {ですよ。
propsは親コンポーネントから渡された引数を一挙に受け取ります。
親コンポーネントからvalueとして渡した引数を、子コンポーネントでprops.valueのように参照することができます。
props.onClickやprops.onChangeは、状態を変更するためのActionsを呼び出します。
Actionsについては[次の記事]で解説します。templatesファイルの作成
templatesディレクトリ配下にChat.jsを作成します。
Chat.jsは親コンポーネントとして、先ほど作成したコンポーネントをまとめます。ファイルが長いので部分ごとに解説します。
import
ファイル冒頭のimport文です。
src/templates/Chat.jsimport React, {Component} from 'react'; import {Chat, Common} from '../components'; import {database} from '../firebase/index'
components/index.jsでexportした"Chat"と"Common"のコンポーネントをimportします。constructorとrender
src/templates/Chat.jsclass ChatTemplate extends Component { constructor(props) { super(props); } /* 中略 */ render() { return ( <div className="p-chat"> <Common.NavBar value={this.props.messages} actions={this.props.actions.messages} back={this.props.actions.messages.backToRooms} configure={this.props.actions.messages.configure} signOut={this.props.actions.messages.signOut} /> <div className="p-chat__area" id="scroll-area"> {this.props.messages.msgs.map((m, i) => ( <Chat.AlignItemsList key={i} msgs={m} /> ))} </div> <div className="c-grid__row"> <Chat.TextInput onChange={this.props.actions.messages.change} value={this.props.messages.value} /> <Chat.SendButton onClick={this.props.actions.messages.submit} value={this.props.messages.value} roomId={this.props.messages.roomId} fromId={this.props.messages.userId} toId={this.props.messages.partnerId} userIds={this.props.messages.userIds} /> </div> </div> ); } } export default ChatTemplate親コンポーネントでは
componentDidMount()などのライフサイクルを使いたいので、Stateless Functional ComponentsではなくClass Componentsで宣言します。constructorの宣言をします。
this.propsでreduxのStoreに保存されているGlobal Stateを参照できます。
(ごめんなさい、Storeについても[次の記事]で解説します...!)src/templates/Chat.jsconstructor(props) { super(props); }続いて、
render部分です。
return()のなかにDOMやコンポーネントを記述します。render() { return ( /* レンダーするDOMを記述する。 作成した子コンポーネントもこの中で宣言して使う。 */ ) }"Chat"としてimportした子コンポーネントを呼び出す方法は以下の通りです。
例...TextInputコンポーネントを呼び出す。return( <Chat.TextInput /> )さらに、引数を渡してみましょう。
src/templates/Chat.jsreturn( <Chat.TextInput onChange={this.props.actions.messages.change} value={this.props.messages.value} /> )少し分かりづらいかもしれませんが
this.props.actions.messages.changeというActionsをonChangeとしてTextInputコンポーネントに渡しているthis.props.messages.valueというGlobal StateをvalueとしてTextInputコンポーネントに渡しているこれによって、TextInputコンポーネントでは
props.onChangeやprops.valueとして渡された値を参照できる。ライフサイクルメソッド
ReactのClass Componentsではライフサイクルメソッドが使えます。
ライフサイクルメソッドが分かりやすく図解されている記事はコチラ代表的なメソッドは以下。
componentWillMount()--> 現在は非推奨なので使わない。render()--> Viewを描画する。componentDidMount()--> API連携など、通信が必要な処理はここで。componentDidUpdate()--> stateが変更されて再render()が走った後にcomponentWillUnmount()-->componentDidMount()で確保したリソースを解放する
src/templates/Chat.jsでは各メソッドで以下の処理を実行している。
componentDidMount()--> Firebase DBにメッセージのデータが追加されたらViewを再描画するようにリスナーを仕掛ける。componentDidUpdate()--> メッセージ一覧の最下部にスクロールする。componentWillUnmount()--> Firebase DBへのリスナーを解除する。ソースコード載せると長〜いので、Githubを確認してください?
リスナーの設定は
firebase.database().ref().on()で。
逆にリスナーの解除はfirebase.database().ref().off()です。リスナー設定/解除の詳細はFirebaseクライアントSDKのドキュメントを参照。
データの取得|Firebase Realtime Databasecontainersファイルの作成
最後にContainer Componentsを作成します。
src/containers/Chat/Main.jsimport ChatTemplate from '../../templates/Chat'; import {bindActionCreators, compose} from 'redux'; import {connect} from 'react-redux'; import {actions} from '../../modules/chat/index'; const mapStateToProps = state => { return { messages: state.messages, }; }; const mapDispatchToProps = dispatch => { return { actions: { messages: bindActionCreators(actions.messages, dispatch), }, }; }; export default compose( connect( mapStateToProps, mapDispatchToProps ) )(ChatTemplate);すでに説明した通り、Container Componentsの役割は中継役です。
ReactとReduxを繋げる
react-reduxというライブラリのconnectメソッドを使います。
connectメソッドは、以下2つをReactコンポーネントで参照できるようにしているイメージです。
mapStateToProps: Reduxで管理しているstate(状態)mapDispatchToProps: state(状態)を変更するためのReduxのActions上記のContainer Componentsの書き方は汎用的に使えるはずです。
まとめ
Reactだけならまだしも、Reduxが絡むと途端にややこしくなります。
特にContainer Componentsで
connectするあたりは、最初何をやっているのか全く理解できませんでした。
分からなくてもコピペしておきましょう。
この記事で少しでも理解を深めていただければ幸いです。Reactは公式のドキュメントが充実しているので、(自戒も込めて)よく読んだ方がいいですね。
[次の記事]でははいよいよReduxのActions, Reducers, Storeなどを解説していきます。
- 投稿日:2020-01-03T15:32:26+09:00
フロント初心者がGatsbyでBlogを公開・運用するまで ~プロジェクト作成から公開の確認編~
はじめに
フロント初心者がGatsbyで試行錯誤しながら、Blogを作成・運用したのでそのまとめです。
Gatsbyの使い方からNetlify CMSを使った公開・運用までを2~3回に分けて書いていきたいと思います。Blog作成の経緯
元々、「Gatsbyって凄いらしいし、使ってみたいなぁ。」とは思っていましたが、作りたいと思えるものがなかったため、特に何もせず1年が終わろうとしていました。
そんな中お正月に実家に帰省すると、母から「Blogを始めたいんだけど、どうすればいい?」と言われたのをきっかけに「じゃあ、ホームページ兼Blogを作ったるよ!」という話になったのがきっかけです。Gatsbyって何?
Gatsby.jsはReactベースの「静的サイトジェネレーター」の一つでコンテンツ管理システムの一種です。
コンテンツ管理システムとして有名なWordPressとの大きな違いは、ビルド時にHTMLを生成して、リクエストを返却します。
詳しくは『Reactベース静的サイトジェネレータGatsbyの真の力をお見せします』こちらの記事が非常に分かりやすくてオススメです!Gatsbyのインストール
まずはGatsbyのインストールをしていきます。
npmとyarnによって、微妙にインストール方法が違います。
今回は個別のプロジェクト内だけで使用するわけではないので、globalを指定してインストールしていきます。# npm $ npm install --global gatsby-cli# yarn $ yarn global add gatsby-cliインストールが完了しているはずなので、Versionを確認していきます。
下記のようにVersionが確認できれば、Gatsbyの準備はOKです。# 表示されるVersionは個々人で異なります。 $ gatsby --version Gatsby CLI version: 2.8.22 Gatsby version: 2.13.33Gatsbyの開発環境構築
GatsbyCLIの準備ができたので、LOCAL環境の開発環境を構築していきます。
Gatsbyの開発環境構築は大きくわけて、Githubリポジトリを指定する方法と指定しない方法の2通りがあります。# Githubパスを指定しない方法 $ gatsby new sample info Creating new site from git: https://github.com/gatsbyjs/gatsby-starter-default.git ... ... success Saved lockfile. ✨ Done in 15.59s. info Your new Gatsby site has been successfully bootstrapped. Start developing it by running: cd sample gatsby developGithubパスを指定しない方法では、自動的に
gatsby-starter-default.gitのパスが指定されてLOCALにフォルダが作成されます。
これだけでも十分に簡単にプロジェクトを始めていくことができますが、自分の目的にあったStarterを選択することで、より簡単にプロジェクトを開始することができます。
Starterは『Starter Library - Gatsby.js』から好きなものを選ぶことができます。
今回は、NetlifyCMSでHostingする予定なので、『gatsby-starter-netlify-cms』を使っていきます。# Githubパスを指定する方法 $ gatsby new sample https://github.com/netlify-templates/gatsby-starter-netlify-cms info Creating new site from git: https://github.com/netlify-templates/gatsby-starter-netlify-cms ... ... success Saved lockfile. ✨ Done in 15.59s. info Your new Gatsby site has been successfully bootstrapped. Start developing it by running: cd sample gatsby develop指定パスからプロジェクトが作成されていることを確認できます。
あとは指定の通り、cd sampleでプロジェクトのフォルダに移動して、gatsby developのコマンドを実行し、localhost:8080にアクセスするだけです。
ほとんど、CLIでコマンドを叩いているだけですが、こんな素晴らしいサイトが出来上がっています。
後は自分の好みに合わせて作り込んでいくだけです。静的サイトのHosting
[Netilfy CMS側の準備]
次に静的サイトのHostingを行っていきます。
今回は、Netlify CMSを使っていきます。
Netlify CMSを初めて使うので、最初はお試しでGetting Startの内容に沿って、Hostingしていきます。
注意
NetlifyCMSを使ってHostingする場合には、NetlifyCMS用のStarterを選択するか、Pluginを追加する必要があります。
これを飛ばして作業を進めてしまうと/adminにアクセスできず、記事の投稿などをすることができないため、CMSとして役割を果たすことができません。
(Githubへ直接Pushするという方は問題ありません!!)Netlify CMSへリポジトリの登録
まずはNetlify CMSとGithubの連携を行います。
(Githubに登録していない人は、Githubの登録から始めましょう!)Netlify CMSに移動します。
今回は、GatsbyのプロジェクトをHostingしていくのでGatsbyロゴの下にある、[Deploy to netlify]を選択します。
Githubの連携を求められるので、[Connect to Github]を選択して、Githubと連携します。
次に進むとHosting用のリポジトリを作成するように促されるので、リポジトリを作成します。リポジトリを作成すると、管理画面へ自動的に移動します。
・・・
・・
・
最低限のHostingに必要な作業はこれだけです。新規で作成されたリポジトリを元にして、すでにサイトがHostingされています!
次は自作したプロジェクトのHostingを行っていきます。Githubにリポジトリの作成とPush
[LOCAL環境準備]
Githubにリポジトリがないことには、何も始まらないので、兎にも角にもまずはリポジトリを作成します。
リポジトリができたら、RemoteをLOCALのリポジトリへ登録していきます。
その後に今回作ったプロジェクトをGithubへPushします。
gatsby newを実行した時点でLOCALのリポジトリは作成されているので、Push対象のRemoteを追加します。# Remoteリポジトリの追加 $ git remote add origin git@github.com:atEaE/gatsby-blog-inaoka.git # RemoteへPush $ git push origin master[Netlify CMSの準備]
Nelify CMSの準備といっても特別なことは特にしません。
最初に登録したリポジトリを使うか、後から登録したリポジトリを使うかの違い程度です。
まずは、[New site from Git]をクリックして、連携するサイトを選択します。
今回は、Githubにリポジトリを作成しているので、[Github]を選択します。
最初と同様に連携を求められるので、そのまま勧めていくとリポジトリの選択画面に移ります。
すべてのリポジトリをInstallしても構いませんが、時間がかかるので、Only select repositoriesにチェックをいれ、リポジトリを選択しましょう。後は、Buildコマンド、公開用のフォルダを指定してあげればOKです。
今回はGatsbyを使用しているの
gatsby buildが選択されていることを確認します。
公開用のフォルダルートは個々人によって異なるとは思いますが、Gatsbyのプロジェクトを使っている方はほとんどの人がpublic/になるかと思います。後はDeployされたサイトへのアクセスを確認します。
確認できたので、ひとまずHostingの確認は完了です。
本当はカスタムドメインの設定、HTTPSのために証明書の設定等を行う必要がありますが、今回はここまでとします。
- 投稿日:2020-01-03T14:46:26+09:00
React初心者めちゃくちゃHelloWorldする
概要
React初心者によるReact初心者のためのReact勉強メモです
2020/01/03 Happy New Year
![]()
・ノーマルHello World
・関数コンポーネントHello World
・クラスコンポーネントHello World導入
Create React Appを使用して環境構築を行います
npmのバージョンが5.2以上であればnpxがデフォルトで入っているはずなので下記で導入ができますnpx create-react-app my-app cd my-app npm startSass & Scss 導入
こちらの記事を参考にnode-sassを導入してsassを使えるようにしました
①まずはノーマルのHello world!
/src/index.jsを下記の様に書き換えてください。/src/index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import './sass/index.scss'; const say = 'Hello, World!'; ReactDOM.render( <div> <h1>{ say }</h1> </div>, document.getElementById('root') );ポイント
- ReactとReactDomをimportしていないと怒られます
- ReactDom.render内で、表示するDOMを定義しています
- getElementで取得している
id="root"というDOM内に追加されます- ReactDom.renderで返されるDOMは1つDOM要素である必要があります
今回は1つの<div>要素を返していることになります- 3行目の
import './sass/index.scss';でscssを読み込んでいます
/src/sass/のディレクトリにindex.scssを作成して、背景色等を変更し、変更が適用されるか確認してみましょう/src/sass/index.scssbody { background: #f2f2f2; }表示結果
上記の様に実装すると、このように表示されるはずです
![]()
変数sayに入れる文言を変更してみて、表示される内容が変わるか試してみてください!②関数コンポーネントでHello World!
今度は
/src/index.jsを下記の様に書き換えてください。/src/index.jsimport React from 'react'; import ReactDOM from 'react-dom'; import './sass/index.scss'; const say = 'Hello, World!'; // 関数コンポーネント function Hello(props) { return <h2>Hello, {props.name}!</h2>; } ReactDOM.render( <div> <h1>{ say }</h1> <Hello name="hoge" /> </div>, document.getElementById('root') );ポイント
- 関数コンポーネントとは文字通り、関数によって作られたコンポーネントです
render内で呼び出す際には関数名を呼び出します
- 関数コンポーネントは引数としてpropsを受け取ります
render内で呼び出しの際に記述しているname="hoge"がprops.nameで使用できます
- render内で記述されるタグは頭文字が小文字の場合DOM要素・頭文字が大文字の場合コンポーネントとして認識されます
表示結果
上記の様に実装を行うとこのように表示されるはずです
Helloコンポーネントを呼び出している箇所で渡す名前を自分の名前に変更してみてください
挨拶してくれるはずです![]()
③クラスコンポーネントでHello World!
今度は
/src/index.jsを下記の様に書き換えてください。import React from 'react'; import ReactDOM from 'react-dom'; import './sass/index.scss'; const say = 'Hello, World!'; // クラスコンポーネント class Hello extends React.Component { constructor(props) { super(props); this.state = { name: 'Bob' } } setName = (e) => { this.setState({ name: e.target.value }); } render() { return ( <div> <h2>Hello, {this.state.name}!</h2> <label for='name'>input your name!</label> <input type='text' onChange={this.setName} /> </div> ); } } ReactDOM.render( <div> <h1>{ say }</h1> <Hello /> </div>, document.getElementById('root') );ポイント
- クラスコンポーネントは独自のstate(状態)を持つことができます
- stateを使用する場合にはコンストラクタが必須になり、このコンストラクタ内でstateの初期値を設定します
また、このコンストラクタでは必ず引数にpropsを受け取り、super(props)を行う必要があります
constructor(props) { super(props); this.state = { name: 'Bob' } }
- stateは直接変更をしてはいけません
今回の場合は<input type='text' onChange={this.setName} />でthis(コンポーネント内の).setNameを
呼び出して、その関数内でsetStateを使用してstateを変更していますsetName = (e) => { this.setState({ name: e.target.value }); }
- クラスコンポーネント内の
render()で返却するDOM要素を定義しています
ここでもDOM要素を1つで返す必要があるので、<div>で囲っています- コンポーネント内のstateをDOM要素内で取得する場合は
{this.state.name}というように記述しますrender() { return ( <div> <h2>Hello, {this.state.name}!</h2> <label for='name'>input your name!</label> <input type='text' onChange={this.setName} /> </div> ); }表示結果
- 投稿日:2020-01-03T10:20:19+09:00
React/Next.jsアプリケーションを作成し、AWS EC2を使って本番環境にデプロイするまで
対象
- Next.js 等、Node.js で動かすアプリをローカル環境で作成することはできるが、それを本番環境で動かす方法と仕組みがわからない人
- Heroku や Zeit Now を使うと簡単にリリースできるが、その仕組がさっぱりわかっていない人
この記事の存在意義
初心者が掲題のことをやろうと思ったときに、全体感を把握できる記事が見当たらなかったので、こういう記事があっても良いかなと思った。
流れ
以下のような手順で進める。
1. ローカル環境で動く Next.js を用いたサンプルアプリの作成
2. EC2 インスタンスの作成
3. EC2 インスタンス上でのサンプルアプリの起動&接続1. ローカル環境で動く Next.js を用いたサンプルアプリの作成
- 事前に npm をインストール
npm install -g npm
↓の公式チュートリアルの Getting Started の部分を進める。
それ用の GitHub リポジトリを用意して、package.json と pages ディレクトリ は remote に push しておく。
2. EC2 インスタンスの作成
公式に従ってやっていく。
自分の環境から ssh できるようにしておき、かつ http 通信はできるようにしておく。3. EC2 インスタンス上でのサンプルアプリの起動&接続
3.1 外部アクセスをローカルホストにつなげるようにする
外部からアクセスがあったときに、そのアクセスをサンプルアプリが受け取って結果を返さなければならない。
しかしここまでのところ、localhost:3000 でアプリケーションを起動することしかできていない
そこで、「外部からのアクセスを受けたらそれをローカルホストにつなげるような仕組みを用意する」ことにする。nginx を使ってそれを実現する。すなわち、「nginx をリバースプロキシとして用い、ポート80へのアクセスをlocalhostの3000ポートに振り向ける」ようにする。
- ssh で EC2 インスタンスに入る。
ssh -i <key.pem> <public ip address>
- nginx の install
sudo su - root yum update -y yum install nginx -y↑これを実行すると、nginx の install 時に Amazon Linux 2だとエラーになる。
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd No package nginx available. Error: Nothing to do nginx is available in Amazon Linux Extra topics "nginx1.12" and "nginx1" To use, run # sudo amazon-linux-extras install :topic: Learn more at https://aws.amazon.com/amazon-linux-2/faqs/#Amazon_Linux_Extrasエラーメッセージの指示に従う。
sudo amazon-linux-extras install nginx1.12
- nginx の起動
sudo systemctl start nginx.serviceこの時点で、ブラウザからアクセスすると以下のような状態になっているはず。
- nginx の設定変更(リバースプロキシの設定)
# 〜省略〜 http { # 〜省略〜 server { # 〜省略〜 location / { # 以下の行を追加 proxy_pass http://localhost:3000; } # 〜省略〜 } }この1行を追加することにより、ローカルホストの port:3000 に向けることができるようになる。ただし、nginx の設定を反映させるために nginx を再起動させることを忘れてはならない。
- nginx の再起動
sudo systemctl restart nginxこの時点で、ブラウザからアクセスすると以下のような状態になっているはず。
これはまだサンプルアプリを起動しておらず、振り向けた先から応答が返ってこないから。
3.2 サンプルアプリの起動
- ssh で EC2 インスタンスに入る(既に入っていればそのままでOK)。
ssh -i <key.pem> <public ip address>
- git の install
sudo yum -y install git vim ~/.ssh/id_rsa # 自分のローカルのものをコピーしてくる chmod 400 ~/.ssh/id_rsa git clone https://github.com/<your sample app repository>
- npm/node の install(公式のダウンロード方法はこちら)
sudo su - root curl -sL https://rpm.nodesource.com/setup_13.x | bash - yum install -y nodejs
- 確認
$ npm -v 6.13.4 $ node -v v13.5.0
- アプリケーションのビルド
cd <your repository> npm install npm run build
- アプリケーションの実行
npm run startブラウザから見てみると、以下のように適切に表示されているはず!
まとめ
個人開発の際、なるべく早めに本番環境へのデプロイができることを確認しておくと、(不慣れな方は特に)不確実性を(そして心理的不安を)大きく減らすことができるかと思います。
まだ問題の切り分けを行いやすい最初のうちにこそ、本番環境でのデプロイを一度試してみることをおすすめします。

















