Chienomi

(Beginners) webに関心があるプログラマの卵たちへ

プログラミング::beginners

プログラミングにおいてwebは花形であり人気なようだ。 確かにwebは非常に身近だし、あるいはそもそもweb以外のプログラミングが想像できない、という人もいるのかもしれない。

今回は「ウェブアプリケーションエンジニアになるためのHow To」の話 ではない。

今回の話は、webプログラミングに関心のある人が、webテクノロジーをどのように学んだら良いか、何をどれだけ学んだほうが良いかということであり、これはwebプログラミングにおいて求められるテクノロジーが広範であるということを前提としている。

部分的に見ればウェブアプリケーションエンジニアになりたい人の役にも立つかもしれないが、それを中心として話すわけではない。

一足飛びにモダンシステムに行かない

一番最初に言っておきたいのがこれだ。

モダンなwebテクノロジーは、かなり「きわどい」テクニックが多い。 美しいロジックを組み上げるよりも、ルールやロジックの隙をついて「正しくはないが正しく動作する」方法で速くしよう、というのが重要になっているのだ。

さらにいえば、webテクノロジーはGoogleが主導権を握っていることもあり、「広告のためのもの」がかなり多い。

初心者がいきなりこんな「際どいテクニック」を学ぶことにはまるで価値がない。 それは理解しているのではなく、ただなぞるだけになってしまう。

だから、学ぶのであれば綺麗に整った古典的なテクノロジーと理屈から学ぶのが良い。 ただし、webの世界においては「これが理想郷の話であり古き良き話である。現在はまた別」ということを常に念頭に置いておく必要がある。

手順に沿って

Unixシステム

まずはUnixシステムの初歩的な部分を知っておくのが良いだろう。 これを知らなければ多くの部分で意味するところがわからず回り道になってしまう。

  • ファイル名とファイルパスの規則
  • プロセスとファイルのUID/GIDとファイルパーミッション
  • TCPコネクション/Unixドメインソケット/名前付きパイプ (プロセス間通信)
  • /dev/null, /dev/zero, /dev/urandomに関する知識
  • Bashの初歩
  • cd, mkdir, rmなどのコマンド
  • コマンドが成功時には原則として出力を行わないこと
  • 終了ステータス
  • パイプ
  • テキストファイル (設定ファイルがテキストであること、を含む)
  • プロセス、プロセススケジューリングとfork/exec
  • シンボリックリンク/ハードリンク
  • プログラムの実行とスクリプト (shebang)
  • 文字エンコーディングと改行コード
  • 標準入出力とファイルディスクリプタ

ぐらいを知っていればだいぶ有利になる。

エディタの使い方

「テキストエディタというものがあり、優れたエディタは使いやすく、全てはこれによって生み出される」ということを理解していなければ、ひどく曖昧な理解のまま霧の中を進むことになるだろう。

ちゃんとエディタを理解し、周辺知識を持っておくのが第一歩と言っていい

HTML

HTML自体はとても簡単なものであり、「構造化されたテキストファイル」を理解するのに丁度いい。

HTML(というよりXML)の基礎は

<NAME ATTR>CHILD</NAME>

である。 これはプログラミング的には

name(child, attr)

であると言って良い。(言語によってはattrを先に取るほうが自然かもしれない)

まずはHTMLの表現と、それが意味的にどのように適用されるのかを理解しよう。

なお、ここで、値が動的に展開されたときに

  • HTMLはあらゆる場所で<>が出てくるとやばい
  • 属性値が出てくる場所で"で出てくるとやばい
  • あらゆる場所で&が出てくると思わぬことになる

ということを 必ず理解しておくこと。

これによって動的生成時に次のような変換が必要であることを学ぶことができる。

str = str.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;").gsub('"', "&quot;")

CSS

先に言っておくが、 CSSはもはや人間には理解不可能なレベルまで複雑化している。

CSSで書いたことが論理的に意味することと実際の作用が前提条件によって違うのだ。 ひとつのブラウザで理解するのですら、レンダリングエンジンのソースコードを読み解く必要があるが、世にある数多のレンダラー(といっても、私はBlink, WebKit, Mozillaのほかで現存するCSSパーサー/レンダラーはDilloのものとw3mのものくらいしか知らないが)について知悉するというのは到底できはしないだろう。

一例としては、table-cellとしてレイアウトされている隣接する要素があるとき、ブロックが先頭からの位置において固定されるなどして左右位置が揃わない場合でも、先頭のインライン要素のベースラインが低いほうに揃うようにブロックを移動し、または引き伸ばされる。

初歩的なことでいいのであればそれほど難しくない。 レイアウト系は難しいので、まずは色や文字の大きさなどから始めると良いだろう。

マスターするのは不可能なので、常にレベルアップし続けていく必要があると知っておくほうが良い。

JavaScript (DOM)

JavaScriptの学習段階はいくつかある。

まずいちばん最初は、グローバルスコープを用いた手続き型言語的なプログラミングである。 例えば次のようなものだ。

function times(x, y) {
  return x * y
}

var x = 10
var y = 30

alert(times(x, y))

次にDOMを習得することだ。個人的にはDOMこそJavaScriptで一番楽しい部分だと思っている。

まずはレガシーDOMからはじめることで感覚をつかめるだろう。 次のコードはドキュメント中の3番目のリンクのリンクアドレスを表示する。

alert(document.links[2].href)

W3C DOM、特にDOM level2を習得することは非常に大きなことだが、この時点では難しいかもしれない。 (次のコードは一部わざと違う書き方をしている)

var node = document.getElementById("TargetNode")
var newPara = document.createElement("p")
newPara.appendChild(document.createTextNode("これは新しい段落です"))
newPara.style.color = "#f30"
var cnode = node.firstChild
while (cnode) {
  if (cnode.nodeType == 1 && cnode.tagName == "p" ) {
    if (cnode.nextSibling) {
      cnode.parentNode.insertBefore(newPara, cnode.nextSibling)
    } else {
      node.appendChild(newPara)
    }
    break
  } else {
    cnode = cnode.nextElementSibling
  }
}

DOM level2ではあるのだが、addEventListenerなどに手を付けるのはまだ早いだろう。

HTTP 1.0

ここでHTTP 1.0を学んでおこう。

私はHTTPをオライリーの「Web クライアント プログラミング」という本で学んだ。 Perl、特にLWPでクライアントを作る本だが、HTTPの解説が非常に理解しやすい。 今インターネットリソースで適切な説明がされているものがないかと探してみたのだが、HTTPヘッダーに順番が存在することや、ヘッダーがどのように構成されているのか、といったことを含めてちゃんと書いているものは見当たらなかった。

HTTPヘッダーは1行目だけ特別であり、リクエストは

GET / HTTP/1.0

レスポンスは

HTTP/1.0 200 OK

のような行になっている。 それ以外は原則として

key: value

形式だ。空行を挟んでコンテンツボディになる。 ヘッダは

  1. 汎用ヘッダ
  2. リクエストヘッダ
  3. エンティティヘッダ

の順だ。

各ヘッダーにどのようなものがあるかと、

  • HTTPステータスコード
  • URIエンコーディング
  • MIMEタイプ

について理解しておくことが望ましい。

初歩的なCGI

CGIが最も周辺環境を考えずに書くことができる。 それはコマンドラインで実行するのと大差ないからだ。 それにレンタルサーバーや、手元のマシンで簡単に試すことができる。

CGIの場合、サーバーのレスポンスコードを制御することができないので、追加されるCGI field(汎用ヘッダセクションに書く)のことも忘れないようにしなければならない。 ちなみに、CGI fieldはHTTPヘッダーと思いっきり重複していて、HTTPヘッダーにないのはstatusのみ。(それと、拡張のX-CGI-*) ただし、順序としてはCGI-fieldにあるものが先にくるようなので(?)、だとするならばContent-TypeLocationもHTTP-fieldの汎用ヘッダよりも先に書け、ということになる。

とりあえずBashでハローワールド。

#!/bin/bash

cat <<EOF
"Status: 200 OK"
"Content-Type: text/plain; charset=US-ASCII"

"Hello, world!"
EOF

Perlでハローワールド

#!/usr/bin/perl

print <<'EOF';
Status: 200 OK
Content-Type: text/plain; charset=US-ASCII

Hello, world!
EOF

Luaでハローワールド。

#!/usr/bin/lua

print([[
Status: 200 OK
Content-Type: text/plain; charset=US-ASCII

Hello, world!
]])

PHPでハローワールド

#!/usr/bin/php

<?php
  header('Content-Type: text/plain; charset=US-ASCII')
?>
Hello, world!

言語は何が良いか? という質問はよくあるけれど、あなたが得意な言語があるならばそれで良い。

だが、強いて言うならばPHPかRubyが良い。 どちらもCGI, FastCGI, アプリケーションサーバーとライブラリにせよ動作環境にせよとても揃っている。

PHPはそこらへんの事情をあまり気にすることなく書くことができるが、上のコードを見ても面倒見すぎな気もする。 やっぱりRubyだろうか。

次はフォームからnameというパラメータを受け取るようにしてみよう。 PHP。1

#!/usr/bin/php-cgi

<?php
  header('Content-Type: text/plain; charset=US-ASCII')
?>
Hello, <% echo($_GET["name"]) %>!

Ruby

#!/usr/bin/ruby

require 'cgi'

cgi = CGI.new

puts <<EOF
status: 200
Content-Type: text/plain; charset=US=ASCII

Hello, #{cgi["name"]}!
EOF

Ruby/Rack (RubyGems ライブラリ)

#!/usr/bin/ruby
require 'rack'

Rack::Handler::CGI.run ->(env) {
  req = Rack::Request.new(env)
  [200, {"Conent-Type" => "text/plain; charset=US-ASCII"}, ["Hello, #{req["name"]}!"]]
}

RackはRackの知識が必要なので一旦置いといて、少なくともこれらが問題なく理解できるようになる必要があるだろう。

アプリケーションサーバー

モダンな話になってしまうが、アプリケーションサーバーはウェブサーバーにアプリケーションを組み込んだものだと考えれば良い。

RubyでSinatra (RubyGems ライブラリ)を使うとものすごく簡単にできる。

require 'sinatra'

get '/' do
  content_type "text/plain"
  Time.now.to_s
end

とくに指定しないとSinatraは127.0.0.1:4567にサーバーをオープンする。だから、

curl http://localhost:4567

みたいにしてアクセスできる。 curlを使ってHTTPでアクセスできているのでこれがウェブサーバーであることは明らかだが、ここで動的に生成された現在時刻が返ってくることで、このサーバー自体にアプリケーションが含まれていることがわかる。

Webrickを使うともっと明確にわかる。 同じスクリプトの中にサーバーの情報を書くからだ。

require 'webrick'

srv = WEBrick::HTTPServer.new({
  BindAddress: "127.0.0.1",
  Port: 20000
})

srv.mount_proc("/") do |req, res|
  res["Status"] = 200
  res["Content-Type"] = "text/plain; charset=UTF-8"
  res.body = Time.now.to_s
end

srv.start

データベース

ここでデータベースと言われて「SQLを学べばいい」と思ってしまうようだと未来がない。

データベースとはどのようにデータを保存し、どのように検索するかというものであり、最も基本的なデータベースはファイルである。

まずは単純なファイルから。

data = "Something"
File.open("database", "w") {|f| f.write data }

構造化されたファイル (ここではタブセパレーテッド)

File.open("database.csv", "w") {|f| f.puts data.map {|i| i.gsub("\t", " ")} }

オブジェクトダンプファイル

File.open("database.rbm", "w") {|f| Marshal.dump(data, f)} }

ディレクトリベースKVS

data.each do |k, v|
  File.open(k.gsub("/", "_"), "w") {|f| f.write v}
end

KVS

pstore.transaction do
  data.each do |k, v|
    pstore[k] = v
  end
end

SQL2

conn = PG::connect(:host => "localhost", :user => "postgres", :password => "admin", :dbname => "postgres", :port => "5432")
begin
  conn.prepare("state1", 'INSERT INTO footable (id, name, comment) values ($1, $2, $3)')
  data.each do |i|
    conn.exec_prepared('state1', i)
  end
ensure
  conn.finish
end

どのようなデータベースが存在し、どのようなデータ保存技法が存在し、どのようなケースにおいて何を選択するのが賢いのか、ということを学ぶのである。

SQLに関してはセキュリティリスクに関して学ぶことを忘れずに。 他のデータベースと比べるとSQLは考慮すべきセキュリティリスクが猛烈に多い。

セキュリティ, ファイアウォール, サーバー

これであなたはローカルにサーバーを立ててウェブアプリケーションを動かすことができるようになったはずだ。 だが、webアプリケーションというものは、「開発者と使用者が異なるプログラム」である。 つまり、セキュリティリスクの高いものである。

よって、web、及びプログラムに関する脆弱性とセキュリティ対策を 網羅的に 学ばなくてはならない。 ここは残念ながらstep by stepとはいかない。

そのために周辺知識であるファイアウォールを学ぶ必要がある。 結果的にはIPv4, IPv6, TCPに関する知識も必要である。

そして、サーバーに関する知識も必要だろう。

間違いなく言えるのは、 ログインや決済を伴うプログラム、外部に対して通信を放り込むプログラムを書いてはいけない ということだ。 「実力が身についた」と思っているレベルではまだ早い。 それが達人級にならなければ始めることすら許されないものであることを嫌というほど理解してから始めるべきだ。

アドバイスとして考えられるのは、まずレンタルサーバーから始めることだ。 アプリケーションレベルでのセキュリティだけを考えれば良いので楽であるし、そもそもこの時点ではサーバーを運営するために必要な知識・スキルがない。

サーバーを運営するようになったら、まずIPアドレスベースの制限をかけるという考えは避けたほうが良い。 有効なのは動的な(アクセス数の極端に多い、あるいは攻撃性のアクセスであるものを拒否する)ファイアウォールの構築と、ウェブサーバーのハッシュリミットである。

なお、そもそもセキュリティリスクを低減させることは手間を減らす、つまりは「手が及ばず攻略されてしまう」リスクを避ける上でも重要だ。PureBuilder Simplyで運営されている当サイトには大量の攻撃が毎日降ってきているが、そのほとんどはWordPress向けのものであり、そもそもサーバー的には静的なファイルを配信するであるPureBuilder Simplyによって構築されたサイトにとってはなんのダメージもないのだ。

また、どうしてもログインして何かをする必要があるのであれば、ForceCommandを伴うSSH鍵認証を使用することができるととても良い。 あなたが考えうるどんな認証システムよりもSSH鍵認証は堅固だろう。 例えばログイン時はSSHで認証すると有効期限の短いPINを発行し、ログイン画面ではPINによる認証を行う、などだ。 このほか、パスワードを使わず常にメールに対して認証メールを送る、という方法もある。

サーバー

ここまでくればあなたはウェブアプリケーションを含むウェブサイトを公開できるようになった。おめでとう。

次の一手を打つためにも、やはりレンタルサーバーに閉じ込められることなく自らサーバーを運営していきたいところだ。

ここではシステム、OSの広汎な知識、ネットワークに関する知識、さらなるセキュリティに関する知識が必要になる。 公開されるサーバーを立て、保守するために必要な知識は非常に幅広い。 しかし生半可な知識でやってしまうのはとても危険だ。

ここではここに到達するまでに費やした以上の時間を必要とするだろう。

さらに良いサイトのために

より様々なサイト、より高度なサイト、より美しいサイトを提供するために必要な知識はなんだろうか。

実のところ知識は様々である。

  • レイアウトやアニメーションなどを含むCSS全般の知識
  • プロトタイプベースオブジェクト指向プログラミングの知識
  • JavaScriptの本格的なプログラミングの知識
  • クライアントサイドプログラミングとクライアント環境に関する知識
  • XML, XSLT, YAML, JSONなどデータ表現形式と処理システムに関する知識
  • ウェブレンダリングに関する知識
  • プログラミングに関する知識 (例えばアルゴリズムやプログラミング技法)
  • フレームワークやサーバーサイドソフトウェアに関する知識
  • ネットワーク・プロトコルに関する知識
  • SSLに関する知識
  • 認証システムに関する知識

全てを完璧に履修するなど不可能なので、自分の得意な分野を伸ばしつつ関連分野を強化し、強みや魅力を作っていくと良いだろう。

遥かな高みへ

ウェブプログラミングは、個人的には全体としては好きなほうに入るが、それほど好きというわけではない。 最大の理由はプログラム的な工夫の余地が少ないからだ。

認証システム、ログイン管理、メッセージの受け取りと保存など、大抵のアプリケーションの大部分が共通しており、たくさん作っていると同じようなコードを作って大量に書くことになる。 そのようにある程度枠にはめられるからこそフレームワークが流行ったりするのだが、どうしてもそのようなアプリケーションは退屈になりやすいのだ。

もちろん、サービス的な意味で新しいとか斬新だとかそういうのはあるだろうけれども、それはビジネス的な意味での楽しみであってプログラミング・インタレストというわけではない。

ウェブプログラマが花形であるのは、偏に一般の目に触れやすく派手であること、その分数が多いことにある。 いつだって花形というのは派手な表舞台に立つものだ。

仕事として、ビジネスとしてといった観点を抜きに純粋に技術的に考えると、ウェブプログラミングというのは初心者が指向するにはいささか厳しい者がある。 プログラムとして求められる機能自体が明確に分離されておらず、なおかつ公開することを前提とし、さらに汚染された入力をプログラム内で処理する必要がある。結局、上から下まで全てを知り尽くさなければ最高のプログラムを書くことはできない。いや、十分なプログラムすら書けない。

全てを知ってコントロールしたいと考えるフリークならば悪くないのだが、「プログラミングとかいいなぁ」とか思うレベルだと「やめといたほうがいいのでは」と思ってしまうのが正直なところだ。 さらにいえば、デバッグも難しくて、「動けばいい」わけではない分実装難易度も高い。

ただ、ウェブプログラミングにも良いところはある。

個人的には複数の異なるレイヤーで構成されているのか面白い。 クライアント環境は描画を担っている上にプログラミング処理系も動く。そして、システムのネットワーク、ウェブサーバー、アプリケーションと4段階になっていて、どの段階で何をするかという工夫によって他のパートでやるよりもずっと美しい設計が出来上がったりする。これはなかなかおもしろい。 場合によってはアプリケーションサーバーやデータベースなど層が増えるため、設計面で工夫の余地が大きい。だいたい「ウェブアプリケーションこそ設計」であるのに、世の中では型にはめて作ることによる生産性ばかり考えて大胆な設計をしないものばかりであるが、設計に踏み込めば面白い要素は多い。

また、上から下まで興味がある人にとっては上から下まで触れる環境であることも魅力だ。

さらに、ウェブアプリケーションは得意な部分で勝負できるのもいい。 プログラミングはだいたい、苦手なものは作れなかったりするものだが、ウェブアプリケーションは最低限全てを満たしていれば得意なところを勝負どころにすることができる。

入り口がかなり厳しいが、楽しみどころは見つけやすい。ただ、とにかく手間がかかる上に目新しさに乏しいので好きではないが。

仕事として考えれば、普通はこれだけ広汎だからこそひとりで全てをこなすことはない。 私はずっとひとりで全部という形でやってきているけれども、それはかなり例外的だ。 こんなに多くのものを一人に求められるというのは基本的には考えがたいことだ。

でもだからこそ、自分でやるというのはなかなか楽しい。幅広い知識を獲得し、それを活かすことができるというのは、言ってみれば「ハッカー冥利に尽きる」のではないだろうか。

なにより興味を持ったことにトライするのは、とても良いことなのだから。


  1. php.iniにおいてcgi.force_redirect = 0としておく必要がある↩︎

  2. 改めてこうして書いてみてもSQLがダントツにめんどくさい上に型の制御もしづらいので、やっぱり私はSQLが嫌いだ。↩︎