Web系システムのバグ、サーバから見るか？プログラムから見るか？

## はじめに
今後本格的にWeb系に従事する知人に、お遊びとして「こういうシチュエーションのときどうやって調査する？」って聞いてみたのですが、思いつきで出題してみたものの、冷静に考えると**「新人のとき教えられなかった、今新人に教えたい内容」**が色々詰まっていたので公開することにしました。

内容は決して難しいものではありませんが、一年目でここに列挙する内容を全て新人に教え込めてる現場ってそうそうないんじゃないかって気がします。
想定読者は経験年数を問わず駆け出しWeb系エンジニアです。

はじめにネタバレしておきますが、**「サーバから見る」も「プログラムから見る」も不正解(というか大抵の場合その前にやることがある)**です。
つまりタイトルはただのオマージュです。ごめん。

ここからは@Yametaro先生に捧ぐワイ記法でお送りします。

## ある日、某社にて
社長「大変やワイ君」

ワイ「なんや」

社長「我が社で運用している社内向け検索サービス**『社ーグル』**のことは知ってるな？」

ワイ「知らんがな」

社長「愛社精神の足らん社員だな……**食い物の名前で検索したらその食い物を好物とする社員が全員表示されるシステムだ**」

ワイ「何のために作ったんやそれ！？」

社長「ノリだ」

ワイ「ノリか」

社長「で、だ」

社長「このシステム、先日のデプロイ以降『唐揚げ』で検索しても『検索結果は０人です』と表示されてしまうようになった」

社長「唐揚げが好きな社員が0人ということはあり得ないだろう？」

ワイ「……せやろか？」

社長「**我が社の社員数は53万人です**」

ワイ「ト○タ自動車より多いやんけ！！！！！」

ワイ「確かにそれで0人はおかしいな」


社長「それでだ。**自称フルスタックエンジニア**のワイ君に原因を調査して欲しい」

ワイ「バカにしてんのか」

社長「本番サーバへのSSH接続用設定と本番DBへの接続設定はこれだ」

ワイ「現場寄りの社長やな！！！！！」

ワイ「……とまぁ、任されたけど……」

ワイ「さて、どこから手を付けるか……」

ワイ「ワイは今社内におって、『社ーグル』にはChromeからアクセスできる。確かに『唐揚げ』で検索してもヒット数は0。唐揚げが好きな社員が0人説はあり得ない……」

ワイ「さて読者の皆さん、問題です。**あなたがワイなら何をします？**」

筆者「みんなで考えてみよう！！！」

社長「不審者だ！つまみ出せ！」

ワイ「この茶番の意味？行稼ぎや」

ワイ「何？改行が足りない？しゃあないな……」

<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>

ワイ「考えてくれたなら次いってみよう！」

## 正解は……

ワイ「**[teratail](https://teratail.com/)に状況詳細を書き込んで解答を待とう**」

上司「待て待て待て待て待て！！！！！！！！！」

ワイ「どうしたエリート上司殿」

上司「平然と社内機密を社外に漏らすな！！！」

ワイ「わからんことは人に聞け言うたの上司殿やないですか！！！！」

上司「まずは考えろよ！！！！！！せめて俺に聞けよ！！！！！」

上司「問題が起こっているとしたらどこだと思う？」

ワイ「えーっと……**Chrome?**」

上司「……」

ワイ「……」

上司「バグは大雑把に
**1. ネットワーク
2. クライアント
3. サーバ(Webサーバorアプリケーションサーバ)
4. DB**
のどこかで起こっていると考えると良い」

ワイ「そうそう、Chromeって要はクライアントやろ？正解や！！！！」

上司「くっ……！！！！(間違ってるとも言いづらい)」

上司「ま、まぁいい。……じゃあどうする？」

ワイ「総当りや！！！！まずはネットワークから調査やな！！！！！ファイアウォールの設定正しいか調査するで」

上司「……**検索画面、トップにはアクセスできてるよね？**」

ワイ「せやな」

上司「それでネットワークが問題である可能性ってあると思うか？」

ワイ「……多分無いわな」

上司「多分じゃなくて99%ねえよ！！！！！よっぽど変な構築だったら別だけど……実はローカルでサーバが動いててAPIサーバだけリモートだったりとか……」

上司「あとDMZに置かれてるWebサーバからプライベート領域のDBにアクセスする際にエラー起こってるのなら確かに『ネットワークの問題』とも言えるけど……まぁその場合ローカルPCのファイアウォール設定チェックしても検知できないし、少なくとも最初に疑うべきとこじゃないな」

ワイ「上司殿、日本語を話してくれますまいか」

上司「○ね」

ワイ「Qiitaに書いてはいけないタイプの罵倒が聞こえた」

ワイ「しゃあないな、社長からもらった設定でSSH接続や、しかも資料にはログのディレクトリまで丁寧に書かれてるしそれ見たら分かるやろ」

上司「(あの社長、ログディレクトリの場所まで把握してんのか……自分で調査すればいいのに……)」

上司「……まあぶっちゃけそれでも良いんだが……もっと先にすることがある」

上司「茶番多すぎたから手短に言うぞ」

上司「**F12だ**」

ワイ「戦闘機！？」

上司「もういいからさっさと押せ！！！！！！！！！」

ワイ「(キーボード奪われた……)」

上司「ほら、このConsoleの部分に赤いメッセージが出とるやろ」

筆者「GoogleでQiitaを検索すると、こんな感じになりますね」

![スクリーンショット 2020-05-31 20.31.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247157/41be7afe-4873-6daa-4f6f-60bc13ec4806.png)

筆者「もしシステムにエラーがあって、かつ特定の条件が重なった場合ここに赤い色のメッセージが出るわけです。今からワイ君と上司さんが解説しますね」

社長「**この不審者……素早い！！！！**」

ワイ「なんかうるさいけど無視してメッセージ読むで。……んん？
**"Uncaught Type Error: Cannot read property 'resultData' of undefined."**」

ワイ「ははぁ、JavaScriptﾁｮｯﾄﾃﾞｷﾙワイなら余裕や。これは**"変数.resultData"** にアクセスしようとして変数がundefinedやから落ちてるんや。JavaScript全然分からない人は、undefinedは他の言語で言うところのnullの兄弟みたいなもんやと思えばええで」

ワイ「つまりこれはフロント……クライアントのバグや！！！！よーしさっそくフロントのコミットログを調査や！！！！！！どうや上司殿！！！！！！！」

ワイ「……あれ？フロントの直近のコミット、3年前やな……」

ワイ「つまり3年間バグが残っていた……？」

上司「とりあえず落ち着け」

上司「俺はコードを見ていないが、『**"resultData"を格納するなんらかの変数**』がUndefinedになっているのがエラーの原因なんだろ？」

上司「その変数って、ざっくり言えばなんだと思う？」

ワイ「『**"resultData"を格納するなんらかの変数**』……夢と希望かな？」

上司「そうだ。夢と希望、つまり**APIのレスポンス**、もっとざっくり言えば"URLを呼び出した結果"だな。両者は厳密に言えば違うもので、どちらも包括して正確な言い方をすると**HTTPリクエストに対するレスポンス**なのだが、少しややこしいので解説は端折るぞ」

ワイ「ついにツッコミがなくなった！！！！」

上司「検索のために何かのURLを呼んでいる。その結果がある変数に格納されていて、おそらく実際に表示するデータの本体を"resultData"という名前にしていたんだろう」

上司「おそらく **"response.resultData"** みたいなコードなんじゃないかな」

ワイ「ブッブー！！！"res.resultData"でした！！！！！ちなみに実装したのはワイ！！！！！」

上司「……お前後でクビな」

ワイ「マジで！？」

上司「そもそもお前がちゃんとフロントで例外処理してないからこうなるんだ……いや、そのあたり曖昧にしてた俺にも責任はあるんだが……」

上司「とにかく、だ。**これでもし検索用URLが正しい結果を返してるなら正真正銘、フロントのバグだ。**だが
1. フロントのコードが『レスポンスが異常値になる』ことを考慮していなかった
2. そして0件扱いになった
という可能性の方が高い。ぶっちゃけフロントのバグならテストのときに流石に見つかるからな」

上司「ということで検索用のURLが正しいレスポンスを返しているのか確認する必要がある。どうする？」

ワイ「せやな、[Postman](https://www.postman.com/)をダウンロードや！！！！」

上司「**お前のスキルで何故Postmanの存在を知ってる！？！？！？！？**」

筆者「API開発だと必須ですよね。APIのテスト用にとても便利なアプリです」

上司「貴様は引っ込んでろ！！！！！」

上司「……もちろんPostmanを使ってもいいが、社ーグルってログイン必須だよな？ということは認証トークンがAPIに引き渡されてるはずだけど設定方法分かるのか？」

ワイ「上司殿、今一度お願い申す、日本語を話してくれますまいか」

上司「……**F12でNetwork欄を見ようね**」

ワイ「優しさが辛い」

筆者「Qiitaで適当に検索すると、最初にsearch用URLが呼ばれているのがよくわかりますね」

![スクリーンショット 2020-05-31 21.14.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247157/b4bfd0d3-8128-fe74-f0f0-a40de8fde8a9.png)

上司「そう、Name欄には呼ばれたURLの末尾部分がダーッと並ぶ。検索の場合大抵search?○○みたいな名前だから見つけやすい。呼び出しに失敗した場合も簡単に分かる」

筆者「ちなみにこの画像ではStatus Codeが緑色で200なので成功してますね」

上司「解説ありがとう」

社長「誰か……その不審者を……」

上司「さて。ワイ君。ステータスコードの意味はわかるな？ **今お前が見ているNetworkの画面には赤色でsearchのAPIが表示されている。ステータスコードは"500"だ。**どういう意味だ？」

ワイ「**私はティーポットです**」

上司「それは418！！！！知ってる人だけ知ってる系のネタを挟むな！！！！！」

上司「500は**Internal server error**！！！」

上司「200, 401, 403, 404, 500くらいは最低限丸暗記しろ！！！！」

上司「……とはいえ、だ。それすらも覚えられなくても簡単な方法がある」

上司「ヘッダー欄の隣の隣のレスポンス欄を見れば良い」

![スクリーンショット 2020-05-31 21.16.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247157/221b26b7-87ec-4adc-2d03-ee895a5779b8.png)

上司「この画像の場合成功例だから検索結果のHTMLがレスポンスとして表示されてるな。だがお前の見ている画面だと……」

ワイ「ほんまや、Internal server errorって書いてる……」
【追記】
Internal server errorの場合、Chromeでは"Failed to load response data" と表示される場合があります(ここの法則性は見つけられてません)
その場合、headers欄のレスポンスヘッダを見て原因を特定してみてください。
【追記ここまで】

ワイ「つまりAppサーバーのエラーやな！！！！」

上司「それがそうとも限らない。むしろ500の場合DBのエラーである可能性も大いにある。まあ先程行った通り社ーグルはログイン必須だから、ログインできたということはDBに接続できているということだ。そのためDBのエラーである可能性は低い。だがもしログイン必須のアプリでないなら、これだけでDBのエラーかアプリのエラーか判別することはできない。ただDBのエラーならシステムまるごと潰れている可能性が高いがな」

上司「これがもし401 UnauthorizedとかのエラーならDBのエラーである可能性はほぼ無い。認証(ログイン)周りの問題である可能性が高いからそのスジでソースコードやAppサーバーのログを探ってみる。403 Forbiddenなら認証は完了しててその後のエラーだから、Validation周りに不具合がある可能性が高い」

上司「APIがタイムアウトを起こしているようなら、Webサーバの問題である可能性もあるし、DBの処理に著しく時間がかかっている可能性もある。アプリのログを見ても分からない場合インフラの運用者に協力を仰ぐこともあるな」

ワイ「なんや上司殿、急に早口になった上に歯切れが悪いで」

上司「早口になったのは想像より尺が長くなったからだ」

上司「あと歯切れが悪いのは……ここからは色々なパターンがあるから『これが正解』っていうのが無いんだよ。まあ要するに、だ。この長い記事の結論は……」

ワイ「**初手はF12！**や！！！！！」

上司「お前が言うんかい！！！！」

筆者「どうも、ありがとうございましたー」

社長「」

# まとめ
**「まずはF12でConsoleとNetworkを見ろ」**って言いたいだけなのにとんでもなく長い記事になってしまいました。
日頃は無意識レベルでやってることですが、言葉にしてみると結構いろんなことを考えてるもんですね。
「当然すぎるわい！！！！」って思った方はぜひ新人さんに教えてあげてください。
少なくとも筆者は体系的に教わらなかったので……(遠い目)

お読み頂きありがとうございました。
ではまた。