INNOBASE技術ブログ

技術的なことエンジニア的なこと制作的なこと全般

WEB製作における職種名と担当作業のまとめ

WEB製作において職種名で呼ばれている人が、実際にどんな作業を担当しているのか、新人向けに資料をまとめてみたのでこれをご紹介

乱暴にまとめた表

f:id:mr51:20150113165714p:plain

この記事の前提

この記事は筆者の主観によるものです。
筆者の経験 (2001年〜2015年現在) での話ですが、日本のWEB業界は初期少人数(極端な話一人)で全てを行うという状態から徐々に専門の職種が分化していった歴史的経緯があります。
そのためエンジニア(サーバーサイドエンジニア)と呼ばれる人の中には非常に多くの担当領域をカバーする人もいます。
この専門職の分化は、効率化という側面と各分野の技術が高度になりスペシャリストが登場したという側面があります。
現在でも全領域を担当する人もいるので、自分の職種名の職域にこだわることなく、キャリアを作っていくことが良いとか思ったりしています。
※スマホアプリ製作まで含めると表が見難くなったので一旦WEBサービス製作で記事をまとめました
※異論ご指摘歓迎、コメントまで

各作業工程の詳細

企画・要件定義
  • 作業内容:何を作るか、なんのために作るかをまとめます
  • 主な担当者:プランナー
  • 成果物:企画書、要件定義書
UI/UXデザイン
  • 作業内容:ページの種類、ページに表示する要素・動作、導線をまとめます。
  • 主な担当者:プランナー、UI/UXエンジニア、WEBデザイナー
  • 成果物:サイトマップ、ワイヤーフレーム
ページデザイン
  • 作業内容:どのように表現するかページをデザインします
  • 主な担当者:WEBデザイナー、デザイナー
  • 成果物:ページをデザインした画像、psd、各項目のデザイン後のサイズをまとめたもの

※ 簡素化するために表から省略…デザイナー・クリエーターに対し悪意があるわけではないです。

クリエイティブ製作
  • 作業内容:ページ内で使用する、画像等の製作
  • 主な担当者:WEBデザイナー、デザイナー
  • 成果物:画像ファイル、flashファイルなど
HTML/CSS
  • 作業内容:画像ファイルでもらったデザインをWEBブラウザで表示できるようにHTML・CSSを作成します
  • 主な担当者:WEBデザイナー、マークアップエンジニア
  • 成果物:HTML / CSS / JS
JS フロントエンド実装
  • 作業内容:ブラウザ上で動作するアニメーションやUIをJS等で実装します
  • 主な担当者:フロントエンジニア
  • 成果物:JS、場合によってはflashなど
プログラム実装、環境構築
  • 作業内容:環境を構築、前段までの作業で実装定義されたHTML、仕様を元に動くものを作ります
  • 主な担当者:エンジニア、サーバーサイドエンジニア
  • 成果物:本番環境、本番環境で動くもの、ソースコード

FuelPHP (oil) でファイル指定でユニットテストを行う方法 + oilのメモ

結論:oilでファイル指定でユニットテストを行う方法

php oil test --file=fuel/app/tests/********.php

↑このコマンドでファイル指定のユニットテストが実行できます。

結論を見つけるまでの過程

読み飛ばし可…oilのhelpのみかたとか実態の場所とかのメモです。

  • phpunit 生で使っている時に使っている方法(file pathを単純に引数で渡す)で動かなかった…
  • php oil help 実行
********** Custom Oil **********

Usage:
  php oil [cell|console|generate|package|refine|help|server|test]

Runtime options:
  -f, [--force]    # Overwrite files that already exist
  -s, [--skip]     # Skip files that already exist
  -q, [--quiet]    # Supress status output
  -t, [--speak]    # Speak errors in a robot voice

Description:
  The 'oil' command can be used in several ways to facilitate quick development, help with
  testing your application and for running Tasks.

Environment:
  If you want to specify a specific environment oil has to run in, overload the environment
  variable on the commandline: FUEL_ENV=staging php oil <commands>

More information:
  You can pass the parameter "help" to each of the defined command to get information
  about that specific command: php oil package help

Documentation:
  http://docs.fuelphp.com/packages/oil/intro.html

※↓ここに注目、コマンドに続けて help を打てば良いと

More information:
  You can pass the parameter "help" to each of the defined command to get information
  about that specific command: php oil package help
  • php oil test help 実行
Usage:
  php oil [t|test]

Runtime options:
  --file=<file>              # Run a test on a specific file only.
  --group=<group>            # Only runs tests from the specified group(s).
  --exclude-group=<group>    # Exclude tests from the specified group(s).
  --testsuite=<testsuite>    # Only runs tests from the specified testsuite(s).
  --coverage-clover=<file>   # Generate code coverage report in Clover XML format.
  --coverage-html=<dir>      # Generate code coverage report in HTML format.
  --coverage-php=<file>      # Serialize PHP_CodeCoverage object to file.
  --coverage-text=<file>     # Generate code coverage report in text format.
  --log-junit=<file>         # Generate report of test execution in JUnit XML format to file.
  --debug                    # Display debugging information during test execution.

Description:
  Run phpunit on all or a subset of tests defined for the current application.

Examples:
  php oil test

Documentation:
  http://fuelphp.com/docs/packages/oil/test.html


* --file で引数渡せば良いと…解決!
* ※見て分かるようにHP 上にちゃんとドキュメントあります。 http://fuelphp.com/docs/packages/oil/test.html

oil test の処理の実態の場所

* fuel/app/classes/oil/classes/command.php ←この辺りに処理が書いてあります。
* task (refine)と同じノリで1ファイル切ってあると勘違いしていました(^_^;)

oil refine help で定義したtaskの一覧が見える

php oil refine help

help の動作確認していたところ、task一覧が見えることがわかってきました。
地味に便利ですね!

GitLab:There isn't anything to merge. branchA and branchB are the same. と出て merge requestできなかった時の対応

f:id:mr51:20141217123841p:plain

ある日GitLabでmerge requestができなくなった。
…2度めです。
一度目の話はこちら
GitLabの merge requestで502が出て対処した話 - INNOBASE技術ブログ


メッセージを見ると

There isn't anything to merge.  branchA and branchB are the same. 

いやいやいや、そんなこと無いよ!
ブランチの中身全然違うから!


githost.log を確認してみます。

December 17, 2014 12:13 -> ERROR -> Grit::Git::GitTimeout

※GitLab Admin Areaから確認できます。


どうやら前回とは違うタイムアウトが起きている様子。
このエラーの原因は複数あるようです。
ググったところ git の timeout設定で解決できる方法が出てきました、が今回私はこの方法で解決することができませんでした。
gitlabで巨大コミットをMerge Requestをした時にGit TimeoutErrorになる件 - kz-dev


結論から言うとgit gc で解決することが出来ました。
大きなマスターデータのSQLをpushした後から不調だったので、試しにgit labホスト上でgit gcをしたところmerge requestが可能になりました。
merge requestができなくて困っている方はお試しあれ


GitLab 上でのオペレーション。※rpm default install 時

su git
cd /var/opt/gitlab/git-data/repositories/【グループ名】/【プロジェクト名】.git
git gc

javascript⇔PHPの暗号と復号(下)

前回の記事でCryptoJSを利用して、javascriptでデータを暗号化し、phpで複合化する方法を紹介しましたが、今回は引き続き、phpでの暗号とjsでの復号を紹介します。

改めて、以下の前提で実装します。

バックエンド
・PHP 5.4
・mcrypt利用

フロントエンド
・Javascript
・CryptoJS利用

暗号化
・方式:AES
・アルゴリズム:RIJNDAEL_128
・モード:CBC

早速、コードが以下になります。

PHPでデータを復号

<?php
    public static function encrypt($data, $key_base64, $iv)
    {
        // 送信するデータをエンコード
        $data_plain = json_encode($data);

        // Pkcs7パディングを実施
        $block_size = mcrypt_get_block_size(ENCRYPTION_ALGORITHM, ENCRYPTION_MODE);
        $pad = $block_size - (strlen($data_plain) % $block_size);
        $data_plain_padded = $data_plain . str_repeat(chr($pad), $pad);

        // 事前に共有されているBASE64共通鍵をデコード
        $key = base64_decode($key_base64);
        
        // 暗号化する
        $data_cipher = mcrypt_encrypt(ENCRYPTION_ALGORITHM, $key, $data_plain_padded, ENCRYPTION_MODE, $iv);
        $data_cipher_base64 = base64_encode((String) $data_cipher);

        return $data;
    }

javascriptでデータ暗号化

    // 共通鍵生成処理
    function createKey() {

        // パスワード、SALT、キーサイズ、往復回数指定で共通鍵を作成
        var key = CryptoJS.PBKDF2('123456789abcdefg', 'abcdefghijklmnopqrstuv1234567890', {keySize: 4, iterations: 500});

        return key;
    }

    // 復号化処理
    function decrypt(dataCipher, ivString) {
        var key = createKey();

        // 初期化ベクトルを作成
        var iv = CryptoJS.enc.Base64.parse(ivString);

        // 復号化
        var dataPlain = CryptoJS.AES.decrypt(dataCipher, key, {iv: iv});

       // JSONデータにデコード
       var data = JSON.parse(CryptoJS.enc.Utf8.stringify(dataPlain));

        return data;
    }

また、前回の記事でコメントを頂いたjserさんのご指摘通り、各ブラウザは既にW3CのWebCryptoAPIをサポートし始めたので、今後はWEBクライアント側暗号の標準になるでしょう

cordovaで携帯の電話機能とメーラーを呼び出す

まずは、必要となるプラグインを追加します。

cordova plugin add org.apache.cordova.inappbrowser

cordovaのプラグインを追加した後、対応プラットフォームを追加し直さないと、
プラグインが効かないので、気をつけてください。

cordova platform remove ios
cordova platform add ios

そして、電話機能とメーラーの呼び出しをAngular Wayで書くと

・ディレクティブ

angular.module('starter.directives.common', [])

    // 電話発信
    .directive('tel', ['callService', function(callService) {
        return {
            scope: {
                number: '=tel'
            },
            link: function(scope, element, attrs) {
                element.on('click', function(event) {
                    event.preventDefault();
                    callService.dial(scope.number);
                });
            }
        };
    }])

    // メール送信
    .directive('mailto', ['mailService', function(mailService) {
        return {
            scope: {
                mail: '=mailto'
            },
            link: function(scope, element, attrs) {
                element.on('click', function(event) {
                    event.preventDefault();
                    mailService.send(scope.mail);
                });
            }
        };
    }]);

・サービス

angular.module('starter.services.common', [])

    // 電話発信
    .factory('mailService', ['$window', function($window) {

            function send(mail) {
                $window.location.href = "mailto:" + mail;
            }

            return {
                send: send
            };
        }])

    // メール送信
    .factory('callService', ['$window', function($window) {
            function dial(number) {
                $window.open('tel:' + number, '_system');
            }

            return {
                dial: dial
            };
        }])

・画面上

<input type="button" value="call" tel="01234567890">
<input type="button" value="send mail" mailto="abc@gmail.com">

以上、プラグインのおかげで、割と簡単に実現できました。

スケールアウトの基礎的な考え方

WEBアプリケーションをスケールアウトする、あるいは大規模サービスを構築するときに、どう考えてサーバーを増やしてゆくかをまとめてみました。
この記事はインターン向けに社内Confluenceに書いていた基礎知識ドキュメントをどうせなら一般公開しようというシリーズの記事です。

そもそもスケールアウトとはなんぞや?

WEBアプリケーションで大規模なアクセスを処理する解決策としてサーバーを増やす事を「スケールアウト」といいます。
大規模なアクセスを処理する他の選択肢として、サーバーそれぞれのスペックを上げることは「スケールアップ」といいます。
ちなみに大きく展開をしたり、大規模にする概念そのものを「スケール」といい、「事業をスケールする」というような使い方がされています。

スケールアウト サーバー構成構成モデルの紹介

1番小さな構成、スケールアウト前の状態

f:id:mr51:20141031102415p:plain
一つのサーバー上に WEBサーバー / DBサーバー / アプリケーション(PHP)/ ストレージ / KVS など様々なミドルウェアを搭載して1サーバーで運用している状態を最初の状態として考えます。

f:id:mr51:20141031102419p:plain
1サーバー上で多くの機能(ミドルウェア)を動かしサービスを運営していると、メモリ・CPU使用率その他リソースに無理が出てきます。
そこでそれぞれの機能(ミドルウェア)を別サーバーで運用するというのが最初の考え方です。

ミドルウェアをそれぞれ別サーバーで運用している構成

f:id:mr51:20141031102423p:plain
各ミドルウェアをそれぞれ別サーバーで運用している状態がこちら
さらにアクセスが増えた場合、1機能1サーバーであるこの状態から、1機能多数サーバーの状態を作っていくことになります。

各機能のサーバーが複数台ある構成

f:id:mr51:20141031102431p:plain
各サーバーを増やした状態がこちら、この構成は1例です。
WEBサーバーへの不可を分散させる仕組みとしてスイッチがWEBサーバーの上流に出てきています。
他にもDNSラウンドロビンといったアクセス分散のシステムがあります。

将来スケールアウトさせるサービスを作るときに注意すること

スモールスタートで初期投資を抑えて、段階的にスケールアウト投資を行いサービスを大きくするという事例はよくあると思います。
ところが、いざスケールアウトに望むとなるとスケールアウトを考慮していない作りになっていて改修に莫大な工数がかかるという事例もよく聞かれます。
では何を考慮すれば将来のスケールアウト時に困らないのか?もちろんケースバイケースなのですが、以下に考慮すべき点の概要を思いつく限りあげておきます。

  • ミドルウェアの接続設定を設定ファイルにまとめる、コード中にベタ書きしない
  • 疎結合のシステムを意識する : DB設計リレーションとかあまりガチガチにするとDB分割できなかったり
  • 重要:アプリケーションサーバーが複数になった時に起こることを考えておく
    • セッションが複数サーバーで利用できるようにする or セッションが張られたサーバーに常にアクセスされるようにする
      • memcached でセッションのデータを管理するとか、アプリケーションサーバーの外でデータ管理すると複数サーバーまたがるセッションが実現できます
      • mod_jk とかセッションに紐付いたアプリケーションにロードバランシング中でも常にアクセスさせる仕組みがあります。
    • WEBアプリケーション上でアップロードされるコンテンツを別途ストレージサーバーに保存する ※うっかりアプリケーションサーバー上に保存すると、データにアクセスできたりできなかったりする状況におちいります。
    • ローカルのファイルを書き換えて処理の制御をする時は注意:ロックファイルとか意味をなすか考えよう?
    • キャッシュの種類とキャッシュ場所に注意:ローカルメモリにキャッシュすると高速ですがそのアプリケーションサーバー上でしか利用できません、memcachedなど外部にキャッシュすると複数のアプリケーションサーバー上からキャッシュが利用できます。

処理量の増加以外のスケールアウトのメリット

帯障害性

HDDは壊れるもの、24h可動のサーバーでHDD RAID5構成の場合、平均2年でHDD障害が出てきます(9年自社サービス サーバー15台を運用していた体感値)。
他にも天災、論理的にデータが壊れる、うっかり削除してしまう等々障害が起きた時にそのサーバーを切り離せばサービスの停止タイムを最低限にサービスを再開することができます。

段階的スケールアウト、その考え方

実際にはいきなりすべてのサーバーを分離する必要はなく、必要に応じてサーバーを増やしていくことになります。
どんな時にどうサーバーを増やしてゆくか、簡単に思いつくものをあげておきます。

  • SQLのレスポンスが遅い、処理が重い:DBサーバーを分離
    • それでもinsert update 系クエリが遅い:master / slave を分離 select の負荷を分離する
    • select 系クエリが遅い:slaveの台数を増やす
    • それでも select が遅い:水平分割・垂直分割の検討
    • それでも select が遅い:積極的にキャッシュを行ってそもそものSQL発行回数を減らす
  • アプリケーションサーバー部分、プログラムの実行が重い:アプリケーション・サーバーを増やす
    • この時WEBサーバーと必ずしも分離する必要、スイッチを用意する必要はありません、proxyサーバーを上流に立てて裏のアプリケーションサーバーを分散させたり方法はいろいろ
  • SSLなどユーザーとの通信コストが重い:WEBサーバーとアプリケーションサーバーの分離

クラウドとスケールアウト

AWSの登場でスモールスタートからのスケールアウトといえばクラウドという雰囲気が流れていますが
必ずしもAWSが最適解でないと感じています。

AWSのすごいと思うところ

  • 完全にAPI化されていて、分単位・数十秒単位でサーバーを増やして負荷に対応できる。
  • SQL / ストレージなど スモールスタート、スケールアウトで工数が発生しない仕組みがある

AWSのデメリットだと思うところ

  • 習得コストが大きい
  • 同程度の処理をさばく構成をVPSや物理サーバーと比べた場合コストが高くなることが多い
  • AWSと密結合になってしまい、他の環境でサービスを動かしにくい

分単位でアクセス負荷増加に自動的に対応しなければならない場合、現状クラウドサービスが唯一のソリューションだと思いますが、そうでない場合環境コピー機能のあるVPSで数時間で手動対応することが可能となってきています。
割安にスモールスタートでサービスを初めて徐々に増加するアクセスに対応するのであればVPSがよいソリューションなのではないかなと感じています。

javascript⇔PHPの暗号と復号(上)

ハイブリッドアプリとAPIサーバ間で通信を行う際、データを暗号化する必要がある場合、アプリ側の暗号復号処理はjavascriptで実装しないと行けません。javascript暗号化ライブラリについて少し調べたところ、個人的にお勧めできるのは以下二つです。

ある開発者が趣味で開発したものですが、今一番使われているJS暗号化ライブラリだと思います。ドキュメントが結構シンプルですが、わからない場合stackflowで調べたら、大体解決できます。ただし、今後がちゃんとメンテナンスされるか、ちょっと心配です。

Stanford Computer Security Labが中心で進めているプロジェクトです。まだそんなに使われていないのですが、将来性が高いと思います。


今回は利用者が多いCryptoJSを選びました。そして、以下の前提で実装します。

バックエンド
・PHP 5.4
・mcrypt利用

フロントエンド
・Javascript
・CryptoJS利用

暗号化
・方式:AES
・アルゴリズム:RIJNDAEL_128
・モード:CBC


まず、javascriptで暗号化して、PHPで復号する方法を紹介します。
早速、コードが以下になります。

javascriptでデータ暗号化

    // 共通鍵生成処理
    function createKey() {

        // パスワード、SALT、キーサイズ、往復回数指定で共通鍵を作成
        var key = CryptoJS.PBKDF2('123456789abcdefg', 'abcdefghijklmnopqrstuv1234567890', {keySize: 4, iterations: 500});

        return key;
    }

    // 暗号化処理 
    function encrypt(dataPlain, key) {
     
        // 初期化ベクトルを作成
        var iv = CryptoJS.enc.Hex.parse('12345678901234567890123456789012');        
     
        // 暗号化
        var dataCipher = CryptoJS.AES.encrypt(JSON.stringify(dataPlain), key, {iv: iv});

        return dataCipher;
    }

    // BASE64にエンコードされた共通鍵は事前にPHP側と共有
    var key = createKey();
    var keyBase64 =  key.toString(CryptoJS.enc.Base64);

    // では暗号化しましょう
    var dataPlain = 'encrypt me, please!';
    var dataCipher = encryp(dataPlain, key);

    // PHP側に送るデータ
    var params = {
        "data_cipher_base64": dataCipher.ciphertext.toString(CryptoJS.enc.Base64),
        "iv_base64": dataCipher.iv.toString(CryptoJS.enc.Base64)
    };

PHPでデータを復号

<?php
    public static function decrypt($data_cipher_base64, $key_base64, $iv_base64)
    {
        // 受信した暗号化されたデータとivをデコード
        $data_cipher = base64_decode($data_cipher_base64);
        $iv = base64_decode($iv_base64);

        // 事前に共有されているBASE64共通鍵をデコード
        $key = base64_decode($key_base64);
        
        // 復号する
        $data_plain_padded = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data_cipher, MCRYPT_MODE_CBC, $iv);

        // デフォルトでPkcs7パディングされるので、削除しないと
        $length = strlen($data_plain_padded);
        $pad = ord($data_plain_padded[$length - 1]);
        $data_plain = substr($data_plain_padded, 0, -$pad);
        $data = json_decode($data_plain, true);

        return $data;
    }

以上で、次回はPHPでの暗号化とjavascriptでの復号を紹介します。