Webページが表示される仕組み②【DNS・IPアドレス・POST...】

今回はWebページが表示される仕組みの第二弾です!

第一弾ではユーザーがページをリクエストしてから、ブラウザが指定されたサーバーへ辿り着くまでをまとめました。

第二弾の今回は、ブラウザとサーバーのやり取りからまとめていきます。

ブラウザとサーバーの会話は3ステップで開始

ようやくサーバーに辿り着きましたが、簡単に目的のファイルを取得できるわけではありません。

まずは、ブラウザとサーバーが、会話をしても問題ないかの確認から始まります。

この確認が「TCP 3way handshake」と呼ばれるものです。
TCP:信頼性の高いやりとり。情報が送信できているかどうかを逐一確認する

①SYN:ブラウザ→サーバー
②SYN+ACK:サーバー→ブラウザ
③ACK:ブラウザ→サーバー
※SYN:Synchronized
※ACK:Ackowledge

①ブラウザ→サーバー:「話しかけてもいいですか?」という問いかけ
②サーバー→ブラウザ:「話しかけてもいいですよ。私もあなたに話しかけていいですか?」
③ブラウザ→サーバー:「分かりました!」

※サーバーとの会話の中身は次のコマンドで確認できます。
 「tcpdump port 見たいポート番号」

この3ステップが完了して、やっと取得したいファイルの要求を行うことができます。

リクエストヘッダでページを要求

ページのリクエストには決まったフォーマットを使います。
それが「リクエストヘッダ」と呼ばれるものです。
サーバーはリクエストヘッダを受け取って初めて、ブラウザが何を欲しているのかを理解します。

【リクエストヘッダ】
f:id:hnm-n-1029:20200829164257p:plain

■リクエストヘッダの構成
●リクエスト行●
メソッド:Webサーバーに対する要求を示す(GET、POST...)
パス:リクエスト対象のデータを示す(取得したいファイル)
バージョン:HTTPのバージョンを示す
●メッセージヘッダ●
Host以降の行で、ブラウザの情報をサーバーに伝える。
(Webブラウザの種類や、対応しているデータのタイプなどの情報)
●空白行●
1行空けることでメッセージヘッダの終わりを伝える。
●メッセージボディ●
Webサーバーにデータを送るために使われる。空の場合もある。

リクエストヘッダの内容に応じてサーバーがコンテンツを返す

リクエストヘッダを受け取ったサーバーは、その内容に応じてコンテンツを返します。

Webサーバー自体がコンテンツを持っている場合もありますが、
最近のモダンなWebアプリケーションでは、Webサーバーの後ろにいくつかの要素が隠れている可能性があります。

その要素として考えられるのが、アプリケーションサーバーやWebサーバーが持っているローカルディスクです。

Webサーバーとは別にアプリケーションサーバーがある場合、Webサーバーがアプリケーションサーバーに問い合わせて、コンテンツを受け取りユーザーに返す、という流れになります。

レスポンスヘッダとボディ

サーバーがコンテンツを取得してブラウザへ返す場合にも決まったフォーマットがあります。
それがレスポンスヘッダです。

【レスポンスヘッダ】
f:id:hnm-n-1029:20200829164317p:plain

■レスポンスヘッダの構成
●ステータス行●
バージョン:HTTPのバージョンを示す
ステータスコード:リクエストに対するWebサーバーの処理の結果を示す3桁の数字
テキストフレーズ:ステータスコードの内容をテキストで示す
●メッセージヘッダ●
Content~以降の行でWebサーバーの情報を伝える。
(ソフトウェア情報や返信するデータのタイプなど)
●空白行●
1行空けることでメッセージヘッダの終わりを伝える。
●ボディ●
HTMLや画像などのデータを格納する場所

これでようやくブラウザがHTMLを取得できました!

ただ実は、この一連のやり取りで取得できたのはHTMLだけになります。
HTMLに付随するCSSや画像は、また同じやり取りを経て取得しなければなりません。
ブラウザ大変....

ファイル毎に毎回TCP 3way handshakeするのは効率が悪くシステムへの負荷が高くなる....
そこで登場するのが「HTTP KeepAlive」という機能です。

効率的に会話する:HTTP KeepAlive


HTTP KeepAliveとは、一つのTCP接続を一回のHTTP通信で切断せず、複数のHTTPリクエスト/レスポンスを送受信するよう維持する機能のことです。
つまりKeepAliveを有効にした場合、HTMLファイルを受け取った後に続けてリクエストすることができます。(3way handshakeを省略することができます)

これで効率的にリクエストしたページが表示されました!

1ページ表示するだけでもこれほどのやり取りが行われているんですね...

Webページの表示については、キャッシュやクッキーなど他にも重要な機能があるので、そちらも今後まとめていきたいと思います。

今回は以上になります!



↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

Webページが表示される仕組み①【DNS・IPアドレス・POST...】

今回はITの基礎学習のために、Webページが表示される仕組みをまとめていきます。

この仕組みの理解は業務において必須でありますが、意外と初学者が見落としがちな部分なのでしっかり理解していきたいと思います。

本記事はムーザルちゃんねるさんの動画と「Web技術の基本」という書籍を元にしています。

ムーザルちゃんねるさん
https://www.youtube.com/watch?v=LaJWTPq5cSU

Web技術の基本
f:id:hnm-n-1029:20200828161947j:plain

では、早速まとめていきます!

Webページが表示されるまでの基本的な流れ

普段私たちが見ているWebサイトが表示されるまでの基本的な流れは次のようになります。

f:id:hnm-n-1029:20200828141716p:plain

①ユーザーがURLを指定(見たいページをクリック、URLを直接入力するなど)
②ブラウザがサーバーへHTMLをもらいにいく
③サーバーからブラウザへHTMLが渡る
④ブラウザが受け取ったHTMLを画面に表示させる

そもそもURLとは?

上記①で指定しているURLとはそもそも何を表すものなのでしょうか?

Webサイトの場所を示す住所のようなもの

URLは大きく分けて3つの要素で構成されています。

f:id:hnm-n-1029:20200828144724p:plain

スキーマ:通信方式(プロトコル)の種類(httpsftpなど)
ホスト:接続先のサーバーの識別名
パス:サーバー上のディレクトリやファイル

ホスト名が実質のサーバー名になりますが、ホスト名だけだと実はサーバーには辿り着けません。
それは、ホスト名はあくまでも仮想的な場所を示しているに過ぎないからです。
実際のサーバーの場所はIPアドレスが示しています。
つまり、サーバーに辿り着くためにはホスト名に紐づいたIPアドレスが必要になります。

そして、このIPアドレスを教えてくれるのが「DNSサーバー」になります。

URLを元にDNSサーバーからIPアドレスを取得する

ブラウザは一番最初にみなさんがお使いのインターネットプロバイダーDNSサーバーに聞いてみます。
聞かれたDNSサーバーは世界中に散らばっているサーバーに順繰り問い合わせていきます。

このような感じで。

f:id:hnm-n-1029:20200828154519p:plain


ルートサーバーに問い合わせ、「.com」の情報を知っているサーバーを教えてもらい、
そのサーバーにアクセスし....という感じで再帰的に問い合わせていき、最終的にIPアドレスを取得します。

ルーターがブラウザにIPアドレス(サーバー)の場所を教えてくれる

IPアドレスを取得しましたが、ブラウザはIPアドレスが示す場所を知りません。

それを教えてくれるのが「ルーター」です。

インターネットはたくさんの配線業社が相互に接続し合い、回線が網目状に張り巡らされている。(※説明をかなり省いています。)

このように張り巡らされた回線をどのような経路で進めばよいのかを各ポイントでルーターが誘導してくれます。

ルーターのおかげで、やっとブラウザがサーバーまでたどり着きました!

ようやくブラウザがサーバーへ欲しいファイルをお願いできます....が


一旦今回は、ここまでにします。すみません...

次回は、ブラウザとサーバーのやり取りの方法からまとめていきます!



↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

ReactアプリをGitHub Pagesにデプロイする方法

Reactでリプレイス中のポモドーロタイマーをGitHub Pagesにデプロイしました。

GitHubリポジトリの設定を変更するだけでできるだろうと思っていましたが、
少しだけ手間がかかったので、その方法を紹介していきます。

※Reactアプリが既に完成した前提で説明を進めていきます。

1.gh-pagesのインストール

//--save-devオプションでローカルにインストールできます。
$ npm install gh-pages --save-dev

2.package.jsonに設定を追加

package.jsonに以下のように、スクリプトとホームページを追加します。

{
  ...,
  "scripts": {
    ...,
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
  },
  ...,
  "homepage": "https://(user name).github.io/(repository name)/"
}

3.リモートリポジトリの追加とデプロイ

リモートリポジトリを追加し、npm run deployコマンドを打ちデプロイします。

$ git remote add origin https://github.com/(user name)/(repository name).git

$ npm run deploy

4.GitHubリモートリポジトリの設定を変更

最後に、Reactアプリのリモートリポジトリの設定を変更します。

Settings > GitHub Pages > Source で、「Branch: gh-pages」、「/(root)」を選択し「Save」をクリック。

f:id:hnm-n-1029:20200825105009p:plain


これで設定は終了です。無事に公開できました!

簡単にReactアプリをGitHub Pagesにデプロイできました。

今回は以上です!


↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

【Reactポモドーロアプリ】inputの入力値を別のcomponentで使用する

以前、JavaScriptでポモドーロタイマーを作成しましたが、現在そのアプリをReactへリプレイスしています。

React版でもユーザーが作業時間や休憩時間を設定できる機能を追加したのですが
実装までに苦戦したので、記録として残しておきます。

ただ、今回紹介する方法は師匠からのレビュー前のコードになります。
初心者の私が考えた処理手順になるので、もっといい方法はあると思います....
記録として残しておくという意味合いが強いので、その辺りご了承頂けますと幸いです。

※ポモドーロサイクルの実装もさらに苦戦したので、今度どこかのタイミングで記事にしたいと思います。順番が逆転してしまい、ごめんなさい....

ディレクトリ構成

f:id:hnm-n-1029:20200824192648p:plain

現在のディレクトリ構成はこのようになっております。

赤枠に注目すると分かるのですが、今回はPresentational Component(componentsフォルダ) とContainer component(containersフォルダ)に役割を分離させています。
Presentational Component:View担当
Container component:ロジック担当

React/Redux環境ではプラクティスの一つらしいです。
こちらの記事が分かりやすいと思います!
[redux] Presentational / Container componentの分離 - react-redux.connect()のつかいかた - Qiita

それぞれ関心事をきちんと分離することでコンポーネントの再利用性を高めることができます。

ポモドーロタイマーはそこまで大きいアプリではないですが、
関心事を分けることでコードがスッキリして見やすいので分けることにしました。

実現したいこと

f:id:hnm-n-1029:20200824194817p:plain

青枠内の作業時間に数字を入力後「OK」を押下と同時にその値が赤枠内に残り時間として反映される、ということ。

それぞれのviewは画像に記載した通り以下のPresentational componentが担当しています。
カウントダウンエリア(赤枠):CountDown.jsx
ユーザー入力エリア(青枠):userInput.jsx


作業時間の値は親コンポーネントである、App.jsxのstateで管理しようと考えました。
そして、赤枠内に表示されている残り時間はCountDown.jsxがStateで管理しているので
CountDown.jsxのStateの値をApp.jsxのstateの値で更新する、という流れです。

まとめると、値の流れとしては以下のようになります。

userInput.jsx→App.jsx→CountDown.jsx

①userInputで入力した値をAppに渡す
②AppのStateを更新
③更新したStateの値をCountDownへ渡す
④CountDownで受け取ったpropsでStateを更新させる

実際に書いたコード

順を追って説明していきます。

①userInput(view担当):onChange、onClickにコールバック関数を設定

青枠のviewを担当しているuserInput.jsxのコードは以下になります。
※作業時間のinputだけ抜粋しています。Material UIを導入していますが、Material UIについての詳しい説明は今回は省きます。

//components > userInput.jsx

const UserInputComponent = ({
  //Containers > userInput.jsxからpropsを受け取る
  handleInputWorkTime, inputWorkTime, callInputValueSet
}) => {
  return (
    <form className={classes.root} noValidate autoComplete="off">
      <StyledInputField>
        <StyledTextField id="standard-basic" 
          onChange={handleInputWorkTime} //inputの入力値を取得する関数
          value={inputWorkTime} //ユーザー側から設定を変更できるように
          label="作業時間"
        />
      </StyledInputField>
      <StyledButton variant="contained"
        type="submit"
        onClick={callInputValueSet} //App.jsxのStateの値を更新させる関数を呼ぶ関数
      >OK</StyledButton>
    </form>
  );
};

export default UserInputComponent;

まずは、以下2つの関数を記述。どちらもこれからcontainers>UserInput.jsxで作成する関数です。

①inputの入力値を取得する関数
②App.jsxのStateの値を更新させる関数を呼ぶ関数

②UserInput(ロジック担当):onChange、onClickに設定したコールバック関数のプログラムを定義

//components > UserInput.jsx

const UserInputContainer = (props) => {
  const[inputWorkTime, setInputWorkTime] = useState(25);
  
  const handleInputWorkTime = (e) => {
    //入力値を取得しState(UserInputContainerが持つstate)を更新
    setInputWorkTime(e.target.value); 
  }

  const callInputValueSet = (e) => {
    e.preventDefault(); //再レンダリングをキャンセル
    //AppのStateを更新する関数へ入力値を引数として渡す
    props.inputValueSet(inputWorkTime);
   }

   return (
       <>
  //UserInputComponent へpropsを渡す
         <UserInputComponent 
           handleInputWorkTime={handleInputWorkTime}
         inputWorkTime={inputWorkTime}
         callInputValueSet={callInputValueSet}
      />
    </>
  );
};

③Appで関数の定義と、Countdown(ロジック担当)へpropsを渡す

//App.jsx

const App = () => {
  const[workTime, setWorkTime] = useState(25);

  const inputValueSet = (inputWorkTime) => {
    setWorkTime(inputWorkTime);
  }

return (
    <div className="App">
      <GlobalStyle />
      <Header />
      <div className="module-spacer" />
      <CountdownContainer
        workTime={workTime}  //入力値をpropsとして渡す
      />
      <div className="module-spacer" />
      <InputTimeDisplay/>
      <div className="module-spacer" />
      <UserInputContainer 
        inputValueSet={inputValueSet} //関数をpropsとして渡す
      />
    </div>
  );
}

export default App;

④Countdown(ロジック担当):useEffectの定義

//Containers > UserInput.jsx

const CountdownContainer = (props) => {
//残り時間はleftSecで管理
let[leftSec, setLeftSec] = useState(props.workTime*60);

~中略~

//workTimeが更新されたらleftSecが更新される
  useEffect(() => {
    setLeftSec(props.workTime*60); 
  }, [props.workTime])

  return (
    <CountdownComponent
      workTime={props.workTime} //
    />
  );
}

⑤Countdown(View担当):受け取ったpropsを表示

const CountdownComponent = ({leftSec}) => {
  return (
    <>
      <StyledTimeDesplay>{secToMMSS(leftSec)}</StyledTimeDesplay>
      <div className="module-spacer" />
      <div>
       <StyledButton variant="contained" onClick={handleStart} disabled={active}>START</StyledButton>
       <StyledButton variant="contained" onClick={handleStop} disabled={!active}>STOP</StyledButton>
       <StyledButton variant="contained" onClick={handleReset} disabled={!active}>RESET</StyledButton>
      </div>
    </>
  );
}

export default CountdownComponent

※secToMMSSは秒を【○○:○○】の表示形式に変換する関数です。


これで、Inputに入力した値が、Countdownのviewに反映されました!

今回は以上です!


↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

【Git】git revertの使い方

git revert:過去のコミットの打消しコミットを新たに作る

過去のコミットに対して打消しコミットを作成し、変更を相殺させることでコミットを取り消すコマンドです。

コミットを追加して過去のコミットを取り消しているので、「コミットを修正した」という履歴が残ります。

似たような機能をもつコマンドとして、git resetがありますが、こちらはコミット自体が削除され履歴が残りません。なので、チーム開発の場合はメンバーに周知するためにもgit revertを使う方が良いように思います。

git revertの使い方とコマンド

基本的な使い方

今回は動きを確認するために以下4つのコミットを行ったテスト環境を作成しました。

"first commit"、"second commit"、"third commit"、"fourth commit"

f:id:hnm-n-1029:20200818184619p:plain

...4つも要りませんでしたが....笑

直近の"fourth commit"を取り消したいと思います。

①git revertコマンドの実行
//直近のコミットの取り消しコミットを作成
$ git revert HEAD

または

//指定したcommitの取り消しコミットを作成
$ git revert <commit ID>

※commit IDは$git logで調べることができます。
今回の場合は「$ git revert 45d58793184737346be9d3ced64331113483ca98」となります。

②コミットメッセージをvimで編集する

vimコマンド「:wq」(保存して終了)を入力して終了します。
これで取り消しコミットが作成されます。

f:id:hnm-n-1029:20200818185130p:plain

確認してみると....

取り消し履歴が作成されているのが確認できます。

f:id:hnm-n-1029:20200818190026p:plain

Pushを取り消す

GitHubにpushしてしまったコミットを取り消すには、上記で行った「コミットの取り消し」後に、以下のコマンドを実行します。

//ローカルとリモートを同期する
$ git push origin

するとGithub上で、取り消しコミットが追加されます。


今回は以上になります!


↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

【Git】git stashの使い方

gitをコマンドで扱うようになってから、リモートにpushした後に
branchを切るのを忘れてそのまま作業してしまうことがしばしばありました。

その際に使えるのが「git stash」というコマンドです。

今回はこの「git stash」の使い方をまとめたいと思います。

git stashとは?

コミットしていない内容を一時的に退避させることができるコマンドです。

どんな時に使える?

①作業中に別のブランチに切り替えたいとき
②作業ブランチを間違えた、もしくはブランチを切り忘れた


このようなときにgit stashを使うことで、作業内容を保存したまま
別のブランチへ移動したり、新たにブランチを切ることができます。

退避させた編集内容は、オプションによってチェックアウトしたブランチへ適用させたり
削除することもできます。

使い方とオプションによる挙動の違い

基本的な使い方は以下の2つのコマンドです。

//ワーキングツリー内の変更内容を退避する(addされていないファイルは対象外)
$git stash

//退避した内容を今いるブランチに適用する(直近の退避内容が適用される)
$git stash apply

オプションを付けずに「git stash」だけを行うと、addされていないファイルは
退避対象に含まれないので、addするか次に説明する「git stash -u」を行いましょう。

その他のオプション

//untracked file(addしていないfile)も含めて変更差分を退避させる
$git stash -u

//退避する内容にコメントを付ける
$git stash save "コメント"

//退避させた内容の一覧を表示
$git stash list

//特定の内容を適用させる(applyの後にstash名を指定する)
$git stash apply stash@{0}

//退避させた編集内容を適用と同時にリストから削除する
$git stash pop

//直近の退避内容を削除
$git stash drop

//番号を指定して削除
$git stash drop stash@{0}

//退避させた内容を全て消す(リストにある内容が全て消える)
$git stash clear


今回は以上になります!

gitコマンドを使いこなせるようになると
作業の効率化に繋がるので今後も調べながら実践していきたいと思います。



↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

【formspree】静的サイトに問い合わせフォームを実装する

Github Pagesにアップロードしたポートフォリオサイトに、「formspree」というサービスを使い、お問い合わせフォームの実装を行いました。

導入自体は難しくはありませんでしたが、解説されている記事が古いものばかりでしたので同じように困っている方に向けてまとめたいと思います。

①アカウント登録

「formspree」を使用するにはアカウントの登録が必要です。

f:id:hnm-n-1029:20200812181311p:plain

https://formspree.io/

上記URLにアクセスし「SIGN UP」または「GET START」をクリックするとアカウント登録画面があらわれます。


f:id:hnm-n-1029:20200812181446p:plain

Emailと任意のPasswordを入力し「REGISTER」をクリック。

登録したアドレスにメールが届くので承認するとサービスが使用可能になります。

②Formタグの設定


承認すると以下のような設定画面に遷移します。

f:id:hnm-n-1029:20200812182127p:plain


「Your form's endpoint is:」の下に記載されている「https://formspree.io」から始まるURLを自サイトのFromタグのactionに設定します。

これで設定は完了です!簡単ですね!

設定後は必ず、問い合わせ内容が設定したアドレスに届くか確認しましょう。


送信先のアドレスを変更したい場合は、「Form Details」の「Settings」で変更できます。
他にも送信完了後のサンクスページの作成などカスタマイズできるようなので、
自分の目的に合った仕様にカスタマイズしてみてください!

今回は以上になります!


↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp