scriptタグのdefer/async属性の違い [30seconds of interviews]

30secondsofinterviews.org

30 seconds of interviewsを一通り読んで面白かったので紹介していきたい

What are defer and async attributes on a <script> tag?

<script src="myscript.js"></script>
<script src="myscript.js" defer></script>
<script src="myscript.js" async></script>
  • いずれの属性も指定しない場合、スクリプトは同期的にダウンロード・実行され、ドキュメントのパースはスクリプトの実行が終了するまで中断される。スクリプトは順番に実行される

  • defer属性はドキュメントのパース中にスクリプトをダウンロードする。DOMContentLoadedイベントリスナ内のスクリプト実行と同様に、ドキュメントのパース完了後に実行される。deferスクリプトは順番に実行される。

  • async属性はドキュメントのパース中にスクリプトをダウンロードする。パースが完全に終了する前に、スクリプトを実行する。asyncスクリプトは必ずしも順番に実行されない。

  • tips

    • スクリプトが互いに依存している場合は、deferを使用する。
    • スクリプトが独立している場合は、asyncを使用する。
    • DOMが必ず準備されていなければならない、かつ実行するスクリプトがDOMContentLoaded内に置かれていない場合、deferを使用する。

Vue.js 基底inputコンポーネントを作る

inputの基底コンポーネントを作る。

v-modelは使わずに、@inputでそのまま親に変更されたvalueをemitする。

# input基底コンポーネント
Vue.component('baseInput', {
  props: ['initValue'],
  template: `
    <input
      type="text"
      :value="initValue"
      @input="$emit('inputText', $event.target.value)"
    />
  `
})

example

See the Pen vue.js base-input component by tic40 (@ccpzjoh) on CodePen.

Vue.js inheritAttrsの挙動を調べる

https://vuejs.org/v2/api/#inheritAttrs

v2.4から inheritAttrsが使えるようになった。

By setting inheritAttrs to false, this default behavior can be disabled. The attributes are available via the $attrs instance property (also new in 2.4) and can be explicitly bound to a non-root element using v-bind.

inheritAttrs: falseを設定することで、属性を継承しなくなるようだ。実際にどのような挙動になるか試してみた。

js

// hrefにsrcを指定する<a>タグ基底コンポーネント
Vue.component('inherit-attrs-link', {
  inheritAttrs: true,
  props: ['src'],
  template: '<a :href="src">{{ src }}</a>'
})

Vue.component('not-inherit-attrs-link', {
  inheritAttrs: false,
  props: ['src'],
  template: '<a :href="src">{{ src }}</a>'
})

new Vue({
  el: '#app'
})

html

<div id="app">
    <!-- src, href の両属性を指定して結果を見る -->
    <inherit-attrs-link
      src="http://example.com"
      href="http://inherit-href.example.com"
      target="_blank"
    />
    <not-inherit-attrs-link
      src="http://example.com"
      href="http://inherit-href.example.com"
      target="_blank"
    />
</div>

Result

<!-- inheritAttrs: true -->
<a href="http://inherit-href.example.com" target="_blank">http://example.com</a>

<!-- inheritAttrs: false -->
<a href="http://example.com">http://example.com</a>
  • inheritAttrs: trueの場合
    • 親の属性が継承される。
    • コンポーネントで指定したhrefよりも親のhref指定が優先されている。
  • inheritAttrs: falseの場合
    • 親の属性が継承されないため、target属性がつかなかった。
    • コンポーネントで指定したhrefが反映された。

Source

See the Pen inheritAttrs by tic40 (@ccpzjoh) on CodePen.

v-bind="$attrs" を加えてみる

公式でv-bind="$attrs" を使う例が提示されていたので、そちらの挙動も確認してみる。

js

// v-bind="$attrs" をtemplateに加える
Vue.component('inherit-attrs-link', {
  inheritAttrs: true,
  props: ['src'],
  template: '<a v-bind="$attrs" :href="src">{{ src }}</a>'
})

Vue.component('not-inherit-attrs-link', {
  inheritAttrs: false,
  props: ['src'],
  template: '<a v-bind="$attrs" :href="src">{{ src }}</a>'
})

html

前例と同じ

Result

<!-- inheritAttrs: true -->
<a href="http://inherit-href.example.com" target="_blank">http://example.com</a>

<!-- inheritAttrs: false -->
<a href="http://example.com" target="_blank">http://example.com</a>
  • inheritAttrs: trueの場合
    • 同様の結果
  • inheritAttrs: falseの場合
    • v-bind="$attrs"を指定することで、実質属性が継承されるようになり、target属性が反映された。
    • v-bind="$attrs"でのhref指定(http://inherit-href.example.com)より、:href="src"の指定(http://example.com)が優先された。

Source

See the Pen inheritAttrs-bind$attrs by tic40 (@ccpzjoh) on CodePen.

まとめ

  • 最後の例のように、受け取るprops名と、付与する属性名が異なっている場合は、inheritAttrs: falseにして置いたほうが副作用がない。
  • 基底コンポーネントは、基本的には、inheritAttrs: false にしておいた方が良さそう

GCPでクラウド破産しかけた話

初学のために、GCPインスタンスを立ち上げたり消したりしていたところ、思わぬ請求が来てしまった。

事実としては、App Engine Flex を3バージョン作成し、それぞれ2インスタンス、そのインスタンスには2つのvCPUと2GB RAMを割り当てたものを一ヶ月以上放置していた。その結果一ヶ月で数万円の請求が来るという事態になった。

f:id:tic40:20180630233427p:plain

なぜ起きたか

  • 不要なインスタンスを立ち上げたまま放置してしまった。削除のし忘れ。
  • 支払いアラートを低い金額で設定していたにもかかわらず、アラートメールに気づかず無視してしまった。

支払いをどうしたか

情けない話だが、GCPのサポートに経緯を連絡し、その結果請求額を半分にしてもらった。 とはいえ、不正利用ではない限り当然全額支払わなければならないので、次回は対応できないと言われた。

GCPクラウド破産しないために気をつけること

支払い(予算)アラートを設定する

Billingから予算の設定が可能。そこで、その予算の*%使用したときにアラートを飛ばす、という設定ができる。

f:id:tic40:20180701122928p:plain

支払いアラートメールは必ずチェックする

アラート設定しても見逃すと意味がない。アラートメールは Billing Alert という件名で、Google Cloud Platformから送信される。 見落とさないようにフィルターしておいたほうがいい。

f:id:tic40:20180701123324p:plain

猛省してます。以上。

はてブクライアントを作った

Rails, React/Redux構成で習作として何かWebアプリケーションを作ろうと思い、GW終わりから3日ぐらいでWebアプリケーションを作った。

f:id:tic40:20180510174108p:plain

こちら: https://tic40-emperor.herokuapp.com

*HerokuのFreeプランなので、サーバがsleep中だと表示にかなり時間がかかります

やりたいこと

  • Rails, React/Redux構成でWebアプリケーションを作る
  • できれば無料で

アプリケーション内容

特にアイデアが浮かばなかったので、普段よく使ってるサービスで何か作れないかなと思い、はてなブックマークの閲覧用クライアントを作ることにした。

  • はてなが提供しているAPI*1RSSからエントリー情報を取得する
  • はてなブックマークのホットエントリー一覧表示
  • エントリーの詳細情報(ブクマ数、コメント数、投稿日など)の表示
  • エントリーのコメント表示

技術構成

技術構成は以下

  • インフラ: Heroku(Free Dyno plan)
  • DB: Postge
  • キャッシュサーバ: Redis
  • CI/CD: CircleCI
  • 開発環境: Docker
  • Back-end FW: Rails v5.2.0, Node.js v9.5.0
  • Front-end FW: React v16 / Redux
  • CSS FW: Bulma

開発環境構築

Dockerで構築した

https://github.com/tic40/emperor/blob/master/Dockerfile https://github.com/tic40/emperor/blob/master/docker-compose.yml

DB設計

ログインもユーザ管理もしないので、ぱぱっと書いておわり

f:id:tic40:20180510180457j:plain

  • feeds: rssフィード情報を保持する
  • entries: エントリー情報を保持する
  • comments: エントリーに紐づくコメントを保持する

API設計

ReactからAPI経由でデータを取得する必要があるため、APIを設計する。

基本的にREST準拠で、レスポンスはjsonで返却する。

  • GET /api/v1/entries
    • エントリー情報全取得
  • GET /api/v1/entries/:feed_id
    • エントリー情報をfeed_id指定で取得

クローリング設計

はてブRSS*2から最新のホットエントリ一覧を取ってくる。 例えば総合カテゴリなら以下のURIから取得できる。

RSSのフィードはこちらから定期的に取りに行く必要があるので、スケジューラーでクローリングタスクを回すことにした。 Herokuアドオンのスケジューラー(無料)を使い、1時間おきに以下のクローリングタスクを走らせている。

https://github.com/tic40/emperor/blob/master/lib/tasks/task_crawl.rake

エントリー詳細情報の取得

RSSからホッテントリ一覧は取得できるが、エントリーに対するコメントなどの詳細情報は取得できない。 はてなが提供している はてなブックマークエントリー情報取得API*3 から取得することにした。

デプロイ

GitHubのmasterブランチ更新時に自動デプロイが走るようにCircleCIのデプロイレシピを記述した。

https://github.com/tic40/emperor/blob/master/.circleci/config.yml

実装

  • RailsAPIモード、かつv5.1から追加されたwebpackerオプションを指定して作成した
Rails new . --webpack=react --api
  • APIのレスポンスはHerokuのRedisアドオン(無料)を使い、キャッシュすることで高速した
  • フロントエンドの構成はReduxを使うため、以下のような構造にした。
# directories
app/javascript/packs/
   |-actions
   |-components
   |-containers
   |-reducers
   |-utils

感想とか

  • はてぶのAPIは user-agent ヘッダーをつけてリクエストする必要がある
  • Heroku無料プランだとDBレコードが10000までという制限がある。エントリーに対するコメントもDBにインサートしているため、あっという間に10000行を越えてしまった。特にデータを溜める意味はなかったので、DBにデータをためないようにRSS更新の際にレコード削除して直近のRSSに含まれているエントリー情報だけDBに保持することにした。
  • 1時間に一度しかクローリングしてないので、リアルタイム性は全然ない。
  • Herokuは楽
  • やっぱり個人で開発するならReactよりVueの方がいいかな。

GitHub

ソースはこちら

github.com

CircleCI 2.0からHerokuにRailsアプリを自動デプロイする

Herokuのアカウント作成

Heroku

適当なRailsアプリケーションをGitHubに作成

GitHubに適当なリポジトリを作成してclone

$ git clone git@github.com:{account}/{repository}.git

railsアプリケーションを作成する

$ cd {repository}
$ rails new .
$ vi app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  def hello
    render html: 'hello, world'
  end
end
$ vi config/routes.rb
Rails.application.routes.draw do
   root 'application#hello'
end

Heroku用セットアップ

Gemfileに以下を追記

group :development, :test do
+  gem 'rspec-core'
+  gem 'rspec_junit_formatter'
+  gem 'sqlite3'
  gem 'byebug',  '9.0.6', platform: :mri
end

+ group :production do
+  gem 'pg'
+ end

localhost:3000 でrailsアプリケーションがちゃんと動いているか確認。

$ bundle install
$ rails s

Heroku CLIのインストール

ここから、それぞれの環境に合わせてインストール

Heroku CLI | Heroku Dev Center

Heroku CLIがインストール済みかどうか確認する。

$ heroku -v
heroku/7.0.22 darwin-x64 node-v10.0.0

Heroku コマンドで、ログインし、createコマンドでアプリケーションを作成する

$ heroku login
$ heroku create {heroku上のapp name(指定なしの場合はランダムな名前になる)}

f:id:tic40:20180503205922p:plain

CircleCIアカウントを作成

ここから Continuous Integration and Delivery - CircleCI

GitHubと連携し、Add Project から対象のリポジトリを選択する

f:id:tic40:20180503210137p:plain

CircleCI用のconfigファイルを作成する

Create a folder named .circleci and add a fileconfig.yml (so that the filepath be in .circleci/config.yml).

指示どおりに.circleci/config.yml を作成し、

$ cd {repository dir}
$ mkdir .circleci && vi .circleci/config.yml

画面に表示されているサンプルをコピーペーストする

f:id:tic40:20180503210447p:plain

schema.rbがないとCircleCIのビルドにこけるので、db:migrateを実行しておく

$ rails db:migrate

GitHubへここまでの作業をpushする

$ git add -A
$ git commit -m 'init'
$ git push origin master

上記push後にCircleCI上でビルドが走ればOK。

f:id:tic40:20180504013530p:plain

CircleCIからHerokuへの自動デプロイ設定を行う

以下を.circleci/config.ymlの最下部に追加

      - deploy:
          name: Deploy Master to Heroku
          command: |
            if [ "${CIRCLE_BRANCH}" == "master" ]; then
              git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git master
            fi

HEROKU_API_KEY、HEROKU_APP_NAME の環境変数をCircleCIダッシュボードから設定する。 environment variablesを開き、Add variablesを選択し、それぞれ値を設定する。

f:id:tic40:20180504011718p:plain

HEROKU_API_KEYは、Heroku上のaccount settingsから取得できる。(revealを押せば表示される)

f:id:tic40:20180504013214p:plain

ここまでできたら、最後に変更分をpush

$ git add -A
$ git commit -m '適当なコメント'
$ git push origin master

f:id:tic40:20180504013755p:plain

Deploy Master to Heroku タスクが成功していればOK。HerokuのアプリケーションURIにGETリクエストを投げてhello worldが表示されているか確認してみる

$ curl https://tic40-emperor.herokuapp.com/
hello, world

Web基礎知識をおさらいする

基礎中の基礎。いつでも体系的に口頭で説明できるように自戒を込めて。

HTTPってなに?

Hypertext Transfer Protocol.RFC2616で規定された、TCP/IPをベースとしたネットワークプロトコル

TCP/IPってなに?

ネットワークプロトコル。以下のように4層の階層型なっている

データ送信時は上から下の層へデータは流れる。データ受信時は当然逆の流れ。

HTTPSってなに?

ネットワークプロトコル。httpプロトコルとの違いは、TCPとHTTPの間にさらにTLS(Transport Layer Security)プロトコルを利用する点。 TLSを利用することで、セキュアな通信を実現している。

TLSについて

  • TLSでは一般にSSL(Secure Sockets Layer)と呼ばれる
  • 以下のセキュリティ上の機能を提供する
    • クライアントとサーバの認証機能
    • 通信データ暗号化
    • 通信データの改ざん検出
  • TLSTCP上で利用されるため、まずTCP接続を確立する必要がある
  • TCP接続後にTLSハンドシェイクを行い、TLS接続を確立する。
  • TLSハンドシェイクでは、TCP接続の3ウェイハンドシェイクでやり取りする処理に加えてさらにパケットを最低1往復半する必要がある。

ブラウザ上にWebページが表示されるまでの流れは?

クライアント->サーバ

  1. ブラウザにURL(http://example.com など)を入力する
  2. ゾルバを介してDNSサーバに問い合わせを行い、IPアドレスを取得する
  3. HTTPメッセージを作成し、上記で取得したIPアドレスへHTTPリクエストを送信する

サーバ->クライアント

  1. サーバがHTTPリクエストを受信すると、サーバ側ソフトウェアがリクエストを解析し、内容に応じて処理を実行する
  2. HTTPメッセージを作成し、クライアントにHTTPリクエストを送信する
  3. クライアントは受け取ったレスポンスを解析して、その内容に基づいてWebページを表示する

ブラウザがWebページが描画されるまでの流れは?

サーバからHTTPレスポンス受信後〜

  1. Loading: リソース読み込み
  2. Scripting: JavaScriptの実行。ブラウザのJavaScriptエンジンがコンパイルを行う
  3. Rendering: レイアウトツリーを構築する。CSSがどこに適用されるかはここで計算する
  4. Painting: レンダリング結果を描画する。ブラウザのグラフィックエンジンがレンダリングを行う

代表的なWebアプリケーション脆弱性は?

Webアプリに外部からの入力に応じて変化する箇所がある場合、この部分のHTML生成に問題があるとXSS脆弱性が発生する。 例えば、ユーザのクッキー値を送信するスクリプトを設置しておくことで、ユーザのセッションIDが盗まれるなど。

  • SQL Injection

ユーザの入力を発行するSQLに含めるような処理がある場合、適切に対応しないとSQLが改ざんされて、場合によってはDB内の全てのデータ盗まれてしまう。 対策としては、プレースホルダ方式を使い、後からSQLが改ざんされないようにするなど。

攻撃者によって用意された攻撃用Webページにユーザがアクセスすると、攻撃用Webページ内にあらかじめ用意されていた不正なリクエストが攻撃対象サーバに送信する。攻撃対象サーバ上のWebアプリケーションは不正なリクエストを処理し、ユーザが意図していない処理が行われてしまう。

クッキー生成の不備、セッションIDの盗聴、XSSによる漏洩、PHPやブラウザの脆弱性、URLにセッションIDを保持している場合など、セッション管理に不備があると第三者に利用者に対するなりすましを行われてしまう。

マスタリングTCP/IP 入門編 第5版

マスタリングTCP/IP 入門編 第5版

プログラマのための文字コード技術入門 (WEB+DB PRESS plus) (WEB+DB PRESS plusシリーズ)

プログラマのための文字コード技術入門 (WEB+DB PRESS plus) (WEB+DB PRESS plusシリーズ)

暗号技術入門 第3版

暗号技術入門 第3版

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)