非エンジニアが menstackでwebアプリをつくった話 #nodejs
TRANSCRIPT
※A(ngularJS)は使ってないのでMENstackです
非エンジニアがMENstackでWebアプリをつくった話
自己紹介
■名前:
木下 雄策(27歳・福岡出身) @afroscript10
■略歴:
九州大学で宇宙の研究
2013年 レバレジーズ入社
■今のお仕事:
日本のエンジニア業界を最強にすること!!
→エンジニア特化型Q&Aサイト【teratail】のDevRel担当
→ITの勉強会【ヒカ☆ラボ】
■その他
→Gs'ACADEMYでプログラミング修行中…
→LIGブログ「0エンジニアから0.5エンジニアへ」連載中
おことり
僕、エンジニアでない上に、プログラミングも超初心者なので、もし間違ってたらすみません。。。
でもプログラミングへの愛はあるんです。。。
はじめたきっかけ
Node.js??
JSでサーバ側も書けるらしい!!他の言語いらないじゃん!!
超かっこいい!!
JSでサーバ側も書けるらしい!!他の言語いらないじゃん!!
超かっこいい!!
…で調べてみると、
非同期処理が特徴??大量リクエストの処理が早い??
C10K問題 (クライアント1万台接続問題 )を解決する??
なんかすごい!
でも、まぁ僕が1万台接続の負荷対策することないしな…
普通のWebアプリのサーバ側として使ってみました。
結論
非エンジニアがMENstackでWebアプリつくろうとしたら
ドットインストールでなんとかなる!
スタート時のスペック
スタート時(1.5ヶ月ほど前)の木下のスペック
・PHP→去年8ヶ月独学で勉強⇒挫折。
・HTML/CSS/JS/PHP→G'sアカデミーで一通り触ったくらい
・Unixコマンド→pwd/ls/cd/vi使うくらい
・Node.js/Express/MongoDB→まったくノータッチ
スタート時(1.5ヶ月ほど前)の木下のスペック
・PHP→去年8ヶ月独学で勉強⇒挫折。
・HTML/CSS/JS/PHP→G'sアカデミーで一通り触ったくらい
・Unixコマンド→pwd/ls/cd/vi使うくらい
・Node.js/Express/MongoDB→まったくノータッチ
今のスペック
今の木下のスペック
1ヶ月半で
Node.js × Express × MongoDBで
ごく簡単なWebアプリ(データのCRUD)は
できるようになった。
これくらいはつくれた
G’sアカデミーの卒業制作(プログラミング学習サイト)のサーバ側を書きました。
G’sアカデミーの卒業制作(プログラミング学習サイト)のサーバ側を書きました。
MongoDBからとってきて表示
一応、Demo
こんな感じでつくりました
(超神速で解説します)※あと45枚スライドあります (笑)
ディレクトリ構造はこんな感じ
ディレクトリ構造はこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
ディレクトリ構造はこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
ってコマンド打つと、まずはここが読まれる
$ node app.js
ディレクトリ構造はこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
ってコマンドで、モジュールを installしたらここにどんどん入ってく
$ npm install ●●●●
ディレクトリ構造はこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
何かやりたいと思ったら、https://www.npmjs.com/でモジュール探して、以下のコマンドで install。 $ npm install ●●●●
ディレクトリ構造はこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
最初は気にしない。(どのモジュール使ってるかとかの情報が入ってる )
ディレクトリ構造はこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
見たまんま。画像/JS/CSS等の静的ファイルが入ってる
ディレクトリ構造はこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
app.jsで使う関数とか変数とかで外部ファイルに分けたいやつを入れる感じ? (多分)
ディレクトリ構造はこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
その名の通り、view関連のファイル。今回はejsパッケージ使ってます。⇒ https://www.npmjs.com/package/ejs
ディレクトリ構造はこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
レッスン受講ページ
レッスン一覧ページ
サーバ側のコードはこんな感じ
サーバ側のコードはこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
ここを見ていきます!
モジュールの宣言とか
//「このモジュールを使いますよ」宣言
var express = require('express'), bodyParser = require('body-parser'), methodOverride = require(‘method-override’), cookieParser = require('cookie-parser'), session = require('express-session'), csrf = require('csurf'), path = require('path'), app = express(), MongoClient = require('mongodb').MongoClient, mongodb = require('mongodb'), settings = require('./routes/settings');
モジュールの宣言とか
//「このモジュールを使いますよ」宣言
var express = require('express'), bodyParser = require('body-parser'), methodOverride = require(‘method-override’), cookieParser = require('cookie-parser'), session = require('express-session'), csrf = require('csurf'), path = require('path'), app = express(), MongoClient = require('mongodb').MongoClient, mongodb = require('mongodb'), settings = require('./routes/settings');
まずは、「このモジュールを使います〜」って宣言をつらつら。必要なモジュールを読み込んでくれる。
モジュールの宣言とか
//「このモジュールを使いますよ」宣言
var express = require('express'), bodyParser = require('body-parser'), methodOverride = require(‘method-override’), cookieParser = require('cookie-parser'), session = require('express-session'), csrf = require('csurf'), path = require('path'), app = express(), MongoClient = require('mongodb').MongoClient, mongodb = require('mongodb'), settings = require('./routes/settings');
Express使うためのオブジェクトをつくる
モジュールの宣言とか
//「このモジュールを使いますよ」宣言
var express = require('express'), bodyParser = require('body-parser'), methodOverride = require(‘method-override’), cookieParser = require('cookie-parser'), session = require('express-session'), csrf = require('csurf'), path = require('path'), app = express(), MongoClient = require('mongodb').MongoClient, mongodb = require('mongodb'), settings = require('./routes/settings');
MongoDB使うためのおまじない
モジュールの宣言とか
//「このモジュールを使いますよ」宣言
var express = require('express'), bodyParser = require('body-parser'), methodOverride = require(‘method-override’), cookieParser = require('cookie-parser'), session = require('express-session'), csrf = require('csurf'), path = require('path'), app = express(), MongoClient = require('mongodb').MongoClient, mongodb = require('mongodb'), settings = require('./routes/settings');
自作の外部ファイルや、自作のモジュール使うときはこんな感じ。「./」を忘れずに!「 .js」は省略可!
DBとの接続/静的ファイル読み込み設定
//DBとの接続var lessons;
MongoClient.connect("mongodb://"+settings.host+"/"+settings.db, function(err,db){ lessons = db. collection("lessons"););
//静的ファイルの設定app.set('views', __dirname+'/views');app.set('view engine' , 'ejs');
DBとの接続/静的ファイル読み込み設定
//DBとの接続var lessons;
MongoClient.connect("mongodb://"+settings.host+"/"+settings.db, function(err,db){ lessons = db. collection("lessons"););
//静的ファイルの設定app.set('views', __dirname+'/views');app.set('view engine' , 'ejs');
コレクションを扱うためのオブジェクトを作成
「静的ファイルはデフォルトで viewsファイル読んでください」って意味 (多分)「__dirname」は現在のディレクトリ
外部ファイルからの呼び出し
//DBとの接続var lessons;
MongoClient.connect("mongodb://"+settings.host+"/"+settings.db, function(err,db){ lessons = db. collection("lessons"););
//静的ファイルの設定app.set('views', __dirname+'/views');app.set('view engine' , 'ejs');
外部ファイルの変数 /関数の呼び出しはこんな感じ
//routes/settings.jsexports.host = '127.0.0.1';exports.db = 'tsukutta';
外部ファイル側はこんな感じ「exports.」をつければ外部ファイル呼び出しができるようになる。
middlewareを読み込む
//middlewareを読み込むapp.use(express.static(path.join(__dirname, 'public')));app.use(bodyParser.urlencoded({extended: false}));app.use(bodyParser());app.use(methodOverride( '_method'));
middlewareを読み込む
//middlewareを読み込むapp.use(express.static(path.join(__dirname, 'public')));app.use(bodyParser.urlencoded({extended: false}));app.use(bodyParser());app.use(methodOverride( '_method'));
app.useで読み込まれるものをmiddlewareという。
リクエストが来ると、上から順次適用していく。⇒順番が重要!!
※詳しくはドットインストールで。http://dotinstall.com/lessons/basic_expressjs/26506
ルーティング//ルーティング
app.get('/',function(req, res) {
res.render('lesson/index' );
});
app.get('/lesson/take/:id([0-9]+)' ,function(req, res) {
var stream = lessons.find({lessonId:Number(req.params.id)}).stream();
stream.on("data", function(item) {
res.render( 'lesson/take' ,{lesson:item});
});
});
ルーティング//ルーティング
app.get('/',function(req, res) {
res.render('lesson/index' );
});
app.get('/lesson/take/:id([0-9]+)' ,function(req, res) {
var stream = lessons.find({lessonId:Number(req.params.id)}).stream();
stream.on("data", function(item) {
res.render( 'lesson/take' ,{lesson:item});
});
});
HOMEディレクトリ('/')にアクセス来たら、views/lesson/index.ejsを読み込む
ルーティング(パラメータを使う)//ルーティング
app.get('/',function(req, res) {
res.render('lesson/index' );
});
app.get('/lesson/take/:id([0-9]+)' ,function(req, res) {
var stream = lessons.find({lessonId:Number(req.params.id)}).stream();
stream.on("data", function(item) {
res.render( 'lesson/take' ,{lesson:item});
});
});
./lesson/take/1 とかにアクセスがきたら
views/lesson/index.ejsを読み込む「:●●」でパラメータを渡すことができる ※([0-9]+) は正規表現でパラメータを数値のみに限定
ルーティング(パラメータを使う)//ルーティング
app.get('/',function(req, res) {
res.render('lesson/index' );
});
app.get('/lesson/take/:id([0-9]+)' ,function(req, res) {
var stream = lessons.find({lessonId:Number(req.params.id)}).stream();
stream.on("data", function(item) {
res.render( 'lesson/take' ,{lesson:item});
});
});
与えられたパラメータを使って
MongoDBからデータを取ってくる。
※パラメータを使うには、「req.params.●●」で使える。
取ってきたデータを lessonって変数に入れつつ、
views/lesson/take.ejsを読み込んで表示。
サーバを待機状態に
//サーバを待機状態に
app.listen(3000);console.log("server starting...");
フロント側のコードはこんな感じ
フロント側のコードはこんな感じ
参考:http://roudev.blogspot.jp/2012/11/nodejsweb.html
.├── app.js├── node_modules├── package.json├── public│ ├── img│ ├── js│ └── stylesheets│ └── lessons.css├── routes│ └── settings.js└── views ├── footer.ejs ├── header.ejs └── lesson └── index.ejs └── take.ejs
ここを見ていきます!
フロント側のコードはこんな感じ
//サーバを待機状態に
<h2 class="mini_title_str">
<%= lesson.lessonContents.Chap1.chapTitle %>
</h2>
ここっ!!
フロント側のコードはこんな感じ
//サーバを待機状態に
<h2 class="mini_title_str">
<%= lesson.lessonContents.Chap1.chapTitle %>
</h2>
・ejsモジュール使っている場合、 <% %>の中にJSを記述
・値を表示させる場合は、 <%= %>の中に記述。 ※「=」忘れがち!!
・エスケープしたくなかったら、 <%- %>
ここっ!!
こうやって勉強した
こうやって勉強した■15.11.07Node学園祭で NodeSchoolをやった
■ドットインストールで以下3つをやった。
・mongoDB入門(この内容をQiitaにまとめました )
・Node.js入門
・express入門
→これでほぼつくれます!!
■ドットインストール注意点
・3系と4系に注意(動画は3系、この資料で紹介しているのはv4.1.2)
・動画下の「この動画について」の「補足情報」に載ってるんだ…
→あとは以下のサイトでだいたいOK:
http://d.hatena.ne.jp/tomute/20140514/1400075607
※app.use(methodOverride()); のとこだけうまくいかなかった…
→そこはここで解決:
http://chaika.hatenablog.com/entry/2015/10/06/183604
■コマンドライン(ターミナル)が不安な方は…
・UNIXコマンド入門
こうやって勉強した■15.11.07Node学園祭で NodeSchoolをやった
■ドットインストールで以下3つをやった。
・mongoDB入門(この内容をQiitaにまとめました )
・Node.js入門
・express入門
→これでほぼつくれます!!
■ドットインストール注意点
・3系と4系に注意(動画は3系、この資料で紹介しているのはv4.1.2)
・動画下の「この動画について」の「補足情報」に載ってるんだ…
→あとは以下のサイトでだいたいOK:
http://d.hatena.ne.jp/tomute/20140514/1400075607
※app.use(methodOverride()); のとこだけうまくいかなかった…
→そこはここで解決:
http://chaika.hatenablog.com/entry/2015/10/06/183604
■コマンドライン(ターミナル)が不安な方は…
・UNIXコマンド入門
非エンジニア的に困ったとこ
コールバック関数の感覚が馴染みづらい
コールバック関数の(木下的な)イメージ
■普通の関数のイメージ:
・言われたらやりますって感じ
・スポーツ格闘技型 (普段から鍛えてるけど、試合の日じゃないと戦わないよ )
function(x,y){ 処理 } …関数が呼び出されたら、処理します。
引数がある場合:関数が呼び出されたら、与えられた x,yって値を使って、処理する
→「x,yって武器が与えられて、それ使って試合しろって言われたから、それ使って戦います。」
(x,yは引数なので、基本的に絶対使わなくちゃだめ なものって感じ )
コールバック関数の(木下的な)イメージ
■コールバック関数のイメージ:
・言われなくてもいつでも準備万端って感じ
・ストリートファイト型 (ケンカにゴングはないからつねに臨戦態勢だよね的な )
●●(’××',function(err,res,req){ 処理 }); …●●が、××な状態になったらすぐ処理するように待機しています!
そのときにerrとresとreqを使うかも。
→「××な状態になった瞬間がゴングだ! errとresとreqって武器は元から隠しもってるから、
いざとなったら使うぜ!」
(err,res,reqは引数というより、始めから用意されている使っても使わなくてもいい 道具って感じ)
同期的な書き方と非同期的な書き方が
混同…
同期的/非同期的な書き方の混同
//ドットインストールはこんな感じ
MongoClient.connect("mongodb://"+settings.host+"/"+settings.db, function(err, db) {
db.collection("users", function(err, collection) {
var stream = collection.find().stream(); stream.on("data", function(item) { console.log(item); }); stream.on("end", function() { console.log("finished.") }); });});
ドットインストールは接続〜処理まで一環して非同期的に書いてた。
<こんな感じの書き方 >```DBに接続できたら… →コレクション扱うObjを作成したら… →処理()〜 →接続終了```
⇒接続部分切り出せないのか …?? DBの処理書くとこ全部接続から 書かないといけないのか …??
同期的/非同期的な書き方の混同
//こんな感じで、接続と処理を分けれた
var lessons;
MongoClient.connect("mongodb://"+settings.host+"/"+settings.db, function(err,db){ lessons = db.collection("lessons"););
app.get('/lesson/take/:id([0-9]+)',function(req, res) {
var stream = lessons.find({lessonId:Number(req.params.id)}).stream();
stream.on("data", function(item) {
res.render('lesson/take',{lesson:item});
});
});
コレクション扱うオブジェクトだけ先に作成したら、うまくいった。
分かったら簡単でしたが、けっこう頭こんがらがりました …
まとめ
まとめ
■非エンジニアでもコマンドライン(ターミナル)が大丈夫なら、
ドットインストール見てやるだけで動くものつくれる!
■普通のWebアプリのサーバ側としてでもOK。(むしろ使っていきたい)
■なによりサーバ側とフロント側の文法が変わらないのがうれしい!!
■なのでJSやってると学習コスト低いと思う。
■バージョンアップによる仕様変更が、初心者としては辛いかも。。
非エンジニアこそ全部JSで書こう!!
ご静聴、ありがとうございました!