WEB系に転職して1ヶ月

SI企業から事業会社(WEB系企業)に転職して1ヶ月半が経ちました。

そこで1ヶ月半経って感じたSI企業との違い、現職の課題、個人としての1ヶ月半の反省・今後に向けてと言ったところを今後の自分のためにまとめておきたいと思います。

入社して感じたこと

転職を考えた理由

私は20代後半なのですが、社会人になってからずっとSI企業に所属していました。
これまではシステムをもっとこうした方が利用者にとって使いやすいのでは?と思ったり、この実装がイケてないからリファクタリングしよ
うとか思って行動しても、エンドユーザーの都合(予算など)や逆にSI企業側として採算が合わないから対応出来ないなど多々ありました。
また、技術的にもSI企業にいる方々にとっては重要なものではなく、プログラミングはオフショアなどが行うもの(悪く言えば下っ端?がやるもの)、うちの会社は全員PMを目指すべきみたいな考え方が一般的になっていました。
※もちろん全てのSI企業がそうだとは思っていませんが、私の周りはそういう会社ばかりでした。

私も以前の会社に入社したばかりの頃は「PMを目指すべき」「コーディングは道具であり、要件定義などを中心にやりたい」と考えていました。
実際にコーディングをオフショアに依頼して、自分はプロジェクトを推進したり要件定義だけを中心に行うということを続けていました。
ですが、コーディングのバグが多かったり可読性が低かったりして、開発に時間がかかったり手戻りが発生したりということがあり、コーディングの重要性を認識するようになったのですが、周りの人はコーディングに興味がないので、改善しようにも出来ないという状況になりました。

そういったことから、自分で企画からコーディングまでしていける環境つまりWeb系企業へ転職しようと決意したことが今回の転職のきっかけになります。

入社した会社について

入社した会社は事業会社です。
一般の方が利用するサービスだけでなく、toB向けもある会社になります。(というかBtoBtoCという事業)
私はtoC向けのサービスを希望していたので、toCサービスを行うことになりました。

勤務時間はIT業界では珍しく9:00~17:20で、残業もあまりないので毎日早く帰って家でもプログラミングする時間が増えました。
エンジニアは朝が弱いとよく言いますが、私にとっては早く帰れることの方が嬉しく、今は業後も充実した日々を送っています。

その他、書籍を会社が購入してくれたり、リモートワークが出来るようになってきたりと以前までの会社であれば考えられない良い環境です。

肝心の開発環境になりますが、以下のような環境になります。

言語 Java8(APIサーバ)、PHP5.6(WEBサーバ)
フレームワーク Seasar2、Zend1、jQuery、Angular
DB OraclemySQL
インフラ AWS
コード管理 git
その他 ApacheTomcat、JIRA、Hipchat、

私は現在はサーバサイド(APIサーバ)をメインで対応しています。

私と同じtoC向けのシステムを作成している人は、40人くらいいるのでは?と思っています。
外部委託や他社からエンジニアが常駐していたりします。なので社員よりも外部の会社の人が多いです。

入社して感じた課題

この1ヶ月の仕事内容ですが、最初の2週間程度はほぼ仕事が振られない状態でしたので、既存のソースをなるべく理解するようにしていました。
そのあとは徐々に仕事が増えてきて、既存コードの修正(軽微なもの)やバグ対応、最近では新規にAPIを作成したりなどをしていました。

まだ1ヶ月半で詳細まで理解出来ていない部分もありますが、この会社のシステム部分に感じた課題をまとめておこうと思います。

技術面の課題

フレームワークを使えていない

現在はSeasar2を利用しているのですが、フレームワークの使い方を間違っているようなロジックが見受けられました。
例えば全てのAPIが同一のURLになっていて、リクエストパラメータ内にSERVICE=(対象サービス)を設定することで、APIを変化させるというような仕様になっています。
このようなロジックになっていることで、例えば全サービスで利用する1つのFormを全員でメンテナンスしていくというような状態となり、巨大なロジックが出来上がっていて、可読性が大変低い、同時に開発する人とコンフリクトが発生する可能性が高い(かつ同一名称をつけたら大変なことに・・・)みたいな状態になっています。
ちなみに、全てのAPIがGETメソッドでもPOSTメソッドでも動くようなイマイチ仕様になっています。

まだログの取得などもAOPを利用すれば簡単に出来てかつ、それぞれの業務ロジックに無駄な処理を書かなくて良いと思うのですが、それも利用できていないなどの問題もあります。

トランザクションの操作についても考えられていない状態(なので途中でExceptionを起こしたら不整合になる)であったり、考えられていてもトランザクションの設定位置がLogicパッケージに対して行われていたりするので、1Actionの中で複数のLogicを読んでいたら破綻する状態となっています。

・責務が意識されていない

例えばMVCが意識できておらず、ViewにModel依存のロジックがガリガリ書かれている、Modelで返却する値(XML)を組み立てている。
SQLにIFがめっちゃ書かれている(S2DAOなのでSQLファイル内でIFを使えます)、SQLでなんでもやろうとする。
リクエストパラメータの入力チェックをしていないので、Logic内で無理やり「try catch」。
Webサーバ側にロジックを書きすぎていて二重に実装している。

といったところが気になっています。

MVCが意識できていないということは、改修する際にどこを修正すれば良いのかの判断が難しくなりバグや調査時間がかかる原因になると考えています。

SQL内にIF文が多く書かれていると、単体テストが難しくなる(めんどくさい)ので、バグの検知が難しくなります。
また、SQLでなんでもやろうとしているので超長いSQLが出来上がります。周りの人もですが、書いた人本人も1ヶ月後には絶対に読めない状態になっていると思います。

リクエストパラメータのチェックというのは本来であれば処理の最初にすべきだと思います。
最初にすることで後続で想定外のことが起きにくいですし、処理も速く終わらせることが出来ます。
しかしリクエストパラメータのチェックをあまりしていないため、Logic側でエラー処理をしていて(おそらくテスト段階で困ったから無理やりつけた)、しかもそれをLogic内で握りつぶしていて、本当にロジックの問題などで起こるExceptionが発生した際にもHTTPステータス200応答かつスタックトレースとかも出力しないため、バグがあるのかどうかもわからない状態になっています。

現在のシステムはWebサーバとAPサーバが分かれていて、Webサーバ側はPHP・Zendによりフロント側(画面系)の処理を、APサーバはJavaSeasar2でDBに接続できるようになっておりビジネスロジックが書かれています。APサーバでAPIを用意してWebサーバがそれを呼び出すというような状態になっています。
ですが、PHP側にもAPIを呼び出すためのAPI的なもの(Facadeと読んでいる)が用意されていて、JavaScript側ではPHPAPIを呼び出して、PHPAPI内でAPサーバのAPIを呼び出すというような仕組みとなっています。
しかもPHP側のAPIにもAPサーバにあるものと同じようなロジックを入れていることで二重にロジックが書かれていたり、そもそもWEBサーバとAPサーバが分かれているのってそれぞれに役割分担をさせて負荷を軽減することだと思うのですが、Web側にゴリゴリロジックを書いているので逆に負担かかってない?って状態になっています。

スーパークラスがなんでもやっている

API(Service層)では共通のAPIを親クラスに持っています。
そしてその親クラスにはいわゆる共通系ロジックが書かれている状態です。
なので、各APIからその親クラスのメソッドを呼び出したり、逆に親クラス側のメソッドから子クラスでオーバーライドしたメソッドを呼び出していたりしていわゆるスパゲッティコード状態になっています。
コードを読むのが本当に辛いです。

Template Methodパターンになってはいるのですが、具体的な業務ロジックまでスーパークラスガリガリ直接書いている状態なので、それを利用していないサブクラス側でも毎回それを呼び出して無駄になっているし、可読性は低いしという状況です。

よく言われていることですが、継承はAisBの関係の時に利用すべきであって、そうでない場合(無駄なロジックが入っている)にはコンポジションを使いましょう。

その他技術的に課題に感じる部分は色々ありますが、大きくこんなところです。
つまり技術的課題がたくさんかつ修正も難しい状態になっています。

人材/体制的な課題

・ベンダー依存

今でこそ社員がコードを書いていたりするんですが、昔は外部に委託していたみたいで、今なお社員よりも外部ベンダーが多い状況です。
なのでベンダー依存が高いです。
依存していることの何が悪いかという話になりますが、古い方々が多いので先ほどあげたような技術課題が解消されないまま残っている原因になっているのでは?と思っています。
しかも可読性が低いので、古くからいる人がいなくなるとどうすれば良いのかわからないロジックも多いので、辞めてしまうリスクも高いです。

その他自分の会社のシステムではないので、技術課題を感じても何も出来ないというのもベンダー側としてはあるのかもしれません。(私も転職前はそうでした。声には出すけど何も動き出さない)

ただこの課題は上の方も認識しているみたいで、最近は私含めて転職者を多く受け入れて内製化を進めようとしています。

・SEとプログラマという役割分担

SIにはよくある構造だと思うのですが、この現場もSE(要件定義とかやる人)とプログラマで役割が分かれています。
が、私としてはこの構造は微妙かなと思っていて、プログラマも全体像を理解して開発しないと良いシステムは作れないと思っています。

今はプログラマはSEに言われたことをそのまま開発するような役割分担になっていますが、そうではなく最初からプログラマが積極的に関わって一緒に考えていくような仕組み作りをしていければと思っています。

・開発着手まで

開発着手までは企画→見積もり→開発という流れになっています。
この見積もりの部分が問題だと思っていて、開発リーダー的な人(古くからいるベンダー)が影響調査してどう実装するか(具体的なコードまで)も決めて見積もるという流れになっています。
プログラマはその内容を見て開発するという流れです。

特に問題だと思うのは、影響調査して実装も決めちゃうならもうそこで開発すれば良くない?というスピード感です。
簡単な修正であっても見積もりしてから開発になるので、無駄が多いです。

というかどう実装するかまで決めるならプログラマはいらないという話ですね。

・人材のスキル面

今回の転職ではWeb系企業をいくつか受けたのですが、どの会社もスキルが高い方が多い印象でやはりSIとは違うなぁという印象だったのですが、今の会社に限って言えばスキルはそこまで高くないかもしれません。
Web系は家でも勉強しているのが当たり前だと思っていたのですが、そういう方はあまり多くなく、私が勉強している方という状況です。
今回の転職で優秀な方と働きたかったのでそこはすごく残念ですが、選んだのは私ですし、逆にいえば私が色々変えていけると思っているのでそこは諦めます。

が、今のレガシーなシステムを改善していくには優秀な方が必要だと思うので、アプリケーション側に詳しい方が増えるといいなぁと思います。
幸い上の方はプログラミングに対して前向きな方が多いので(SIのようにマネージャーになるべきみたいな考え方は少ないかも)、そこは改善しやすいのかなと思います。
ただ中途採用で優秀な方を雇うには給与が低いので難しい面があるかもしれません。

個人的にやりたいこと(目標)

上記のような課題を感じた中で個人的に今後やりたいこと・それまでに身につけたいことをまとめていこうと思います。

仕事面

・影響力の向上

これは他のやりたいことを実現するためにマストで取り組まないといけないことだと思っています。
今の状態だと来たばかりで何が出来るのかも分からないのに口だけ課題を言い続ける若手という扱いになってしまうので、早急に「この人は出来る」「この人じゃないとダメだ」「辞められると困る」という状態に持っていかなきゃいけないです。

しかも自分の成長やモチベーションを考えるとなるべく早急に色々と変えていきたいので、そのためには半年以内にはそういった状態に持っていこうと思います。

そのためには、技術力の証明、問題解決力を見せる、人間的な信頼関係を構築、仕事に対しての前向きさを見せる。などをしていかないといけないです。
特に技術力についてが難しいなと思っていて、単純に案件をこなすだけだとそこを理解してもらうのは難しいと思うので、仕事では人より早くかつバグが少ないコードを書いていかないといけないし、それだけだと短期間で信頼してもらうには難しいのでQiitaなどで個人の名前で色々と記事を書いて見せつけていくなどをしていかないとと思っています。

・既存のコード理解

先ほどから言っているように既存のコードは可読性がかなり低くて読むのが辛い状況なのですが、それでもこれから修正していくには理解しないとどうしようもないと思うので、重要なロジックは読んで理解しようと思っています。
できれば入社して半年(9月頃まで)には全体像を理解しておきたいなと考えています。

・テストコードの定着

今は一切テストコードが書いていない状況です。(それはつまり単体テストが出来ていない状態でcurlAPIを叩いているということになります)
今後リファクタリングをしていきたいと考えているのですが、そのためにはテストコードによってデグレが発生しないようにしてからだと思うので、テストコードを書くという習慣を定着させていきたいと思っています。

今まで書いていなかったのを書くということなので、最初はコーディングスピードが落ちると思うのですが、そこだけを見て却下されないように説得できる材料を増やしていかないとなと思います。
まずは私がテストコードを書いて、スピードが変わらないことを証明していかないとなと・・・

・テックリードしていきたい

最後にこれは曖昧な表現かつ長期的な目標になってしまうのですが、テックリードしていけるようになりたいなと思っています。
今の会社で技術に強い人がいないので、抜け出た存在になっていこうと思っています。

その上で社内の開発スピードをあげたり、新しいものを取り入れて業界の中で競い合っていける技術力を社内に広めたいです。
あとは社外的な部分でも、今の会社に優秀なエンジニアが入社したいと思えるように何か還元できればなと思います。

正直現時点での想いなので、いつまでにとかは考えていないですし、今後は変わるかもしれませんが、目標として持って今後定期的に振り返っていこうと思います。

個人的な目標

その他個人的に勉強していきたいところをまとめておきます。

プログラミング

当面はひたすらにJavaを勉強していこうと思ってます。
今回の転職活動で勉強不足を理解出来たので、1年後には社外でも評価されるようなエンジニアになっていたいと思います。
そのために必要だと思うのが以下の要素です。

・Effective Java

本を読んで実際に利用して、効果的なプログラミング方法を理解していきたいと思っています。

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

Effective Java/ジョシュア・ブロック/柴田芳樹
価格:4320円(税込、送料無料) (2019/4/20時点)


デザインパターン

こちらもデザインパターン本を読んで、現在のコーディングを修正するのに活用して覚えていこうと思います。
エンジニアとして理解しておかないといけないものなので、早期に学びます。


OSS活動に参加

何かしらのOSS活動に参加したいと思っています。
今、いくつかのOSSライブラリのコードを読むことが増えてきたのですが、やはり綺麗なわかりやすいコードになっているので、それを真似しながら成長していきたいと思います。

フレームワーク理解

spring bootや業務で利用しているSeasar2などのフレームワークについてコードを読んで理解したいと思っています。
転職活動でもフレームワークの違いなどについて聞かれたものの答えられなかったので、1年後にはフレームワークの特徴など、深いレベルで会話できるようになりたいなと思います。

プログラミング以外

AWS

業務で利用しているのですが、ほぼ理解できていないので、勉強しなきゃなぁと思っています。
AWSのyoutubeセミナーが良いと聞いたので、通勤時間に見ようと思っています。

・フロントエンド

私はサーバーサイドをやっていきたいと思っているので、フロントエンドをどこまで勉強すべきかが悩ましいところですが、Javascriptを普通に触れるくらいと出来れば、React、Vueあたりに触れておきたいと思っています。

・DB

あまりDBの細部まで詳しくはない状況なので、Oracle Silverレベルまでは理解しておかないとなぁと思ってます。(資格を取るかは別だけど)
あとは普段から統計情報を見る習慣がつけばなと思います。

・その他

docker、gRPC、Swaggerなど最近よく聞くものについて勉強していかないとなと思ってます。
業務では利用しないので、プライベートで勉強しつつ良いものを展開していく方法でいいかなと思ってます。

以上です。
今後は定期的に振り返りを行い、成長度合いを確認するようにしたいと思います。

SI企業で身につけたもの・身につけられなかったもの

私は新卒で入社してから、今までSI企業に所属しておりました。
しかし2019年3月から自社サービスを提供している会社、いわゆるWEB系企業に転職することになりました。
転職後に活躍するために、SIerとして身につけられた部分、身につけられなかった部分を整理したいと思います。

前提

仕事内容

私は現在20代後半です。これまで大きく3つの現場で働いてきました。
最初はインフラ系の現場、次はユーザー企業で業務システムの開発(ただしVBA)、最後が大手SI企業に派遣され、金融系のWEBシステム開発を行ってきました。

今回は、特に最後の現場である大手SI企業でのWEBシステム開発の経験を中心に整理していきたいと思います。

SIで身につけられたこと

WEBシステムの基礎知識

最後の現場では、金融系のWEBシステム開発を行っておりました。
以下のような環境です。

バックエンド言語 Java6
フレームワーク 独自フレームワーク
フロント jQuery
DB Oracle
サーバ WebLogicApachepostfixなど(オンプレ)
ソース管理 SVN

ただし実際のコーディングはオフショアにて実施しており、私の役割としては要件定義、外部設計くらいまでとプロジェクトマネジメント的な部分がほとんどでした。
テストなどもオフショアにて実施して、日本にいるエンジニアはEXCELに貼り付けられた画面キャプチャーを見てレビューするという典型的なSI企業という感じでした。

そんな状況ではありますが、自分達でテストをすることもありますし、オフショアに対して正確に指示を出さないといけないので、WEBに関する基礎的なスキルは身に付けることが出来ました。

具体的には、GET・POSTの違い、クッキーの用途、セッションとは何か、リクエストからレスポンスまでの流れ、APIとは何か、フォワード・リダイレクトとは、HTTPステータスコード、サーバーごとの役割(APサーバ、HTTPサーバなど)の部分です。

要件からシステムに落とし込む方法

これがSI企業で一番身につけられた部分ではないかなと思います。
ユーザー(最後の現場では金融機関)から何をしたいのかをヒアリングして、今あるシステムにどのように落とし込むのかを考えて、次の工程(外部設計)に引き渡しました。

特にユーザー企業の方は、システムについて詳しくない場合が多く、またどういうシステムを作りたいのかを自分達で決められないことが多いので、こちらから方向性を定めて進めてあげることが重要になります。
(もちろんシステム化したい大枠の部分は決まっているのですが、具体的な機能とかになると実現方法を決められないことが多い。こちらからいくつかの提案をするか、もしくはあえて1つしか提案せずに進めるかなどしていました)

この経験はWEB系企業に転職してからも確実に活かせる部分だと思っています。
システム開発は、開発することが目的ではなく、システムを開発することでビジネスにどう貢献出来るのかが重要です。
これを意識しながら、システム開発すべきところ、すべきでないところを整理して、要件を定義することが出来たのは良い経験だったと思います。
おそらくWEB系企業に新卒で入った場合には、若いうちはシステムを作ることにこだわってしまって、本当に必要なものを作るという意識を持てる方は多くないのではないかと思います。(独断と偏見ですが、、、)

プロジェクト推進力

抽象的な話になってしまうのですが、プロジェクト推進力は身につけられたのではないかと思っています。

SIerではある程度大きな規模のプロジェクトリーダーを担当したり、改善活動を企画して進めるなどを経験できました。
1メンバーとしてではなく、自分が中心となって、周りの人達に協力を促しながら進めるという経験は、なかなか得られないものだと思いますし、今後必ず必要となるスキルだと思います。

あとは今後WEB系企業でプロジェクトを進めていくためには、周りから信頼されるだけの技術力を身に付ける必要があると思います。技術力への好奇心が高い方々と競争していく必要があるため大変だとは思うのですが、SI企業での経験を強みに力をつけていこうと思います。

SIで身につけられなかったこと

コーディング力

コーディング力といっても色々あるとは思うのですが、特に足りていないのは保守性を意識したコーディング(オブジェクト指向を意識したコーディング)、コーディング速度(前述の保守性を意識した上での)などです。
単純にJavaを書くだけであれば、まぁ普通に書けるのですが、オブジェクト指向を意識してかけているとはいえない状況です。
でもそれって書けないと同じことですよね・・・これから死にものぐるいで勉強したいと思います。

先ほどの環境を見てわかるように、随分レガシーな環境で働いていたと思います。
かつ、自分でコーディングしていない、オフショアが書いたコードはオブジェクト指向が意識出来ていない、元請け企業がコーディングの重要性を理解していない、既存のシステムが保守性を意識出来ていないので余計な調査コストがかかる、テストコードを書く文化がないので余計なノンデグテストがありすぎてテストがしんどい。
というような状況でした。

中でもテストについては本当にうんざりしました。オブジェクト指向を理解していないので、1つのクラスの中身の計算を変更して、でも入出力の部分は変わらないのにノンデグテストで関連する画面を全画面打鍵するみたいなことも多数ありました。
無駄なテストというのはモチベーションに大きく影響するということを学べたのは大きかったかなと思います。

オブジェクト指向での単体テストの重要性はかなり高いです。
適切にオブジェクトを作成できていて、単体テストをしっかりとできていれば、システムとしてのバグも減少できるということですね。

技術の会話ができる同僚

これは自分のスキルという話ではないのですが、欲しかったなぁという思いから念のため入れておきます。
SIerで技術の会話ができる同僚はほぼ出来ませんでした。SI企業では技術に対して興味がある人が本当に少ないです。
休日に勉強するような人はほとんど皆無ですし、勉強会にも積極的に参加しようとはしていません。
会社で身につけられることだけで十分となってしまって、遊ぶことばかりになっています。

私は成長できるためには環境が一番重要だと思っています。
周りが優秀な方ばかりで休日も勉強するという方が多いのであれば、自分も自然と勉強するでしょうし、それによって競い合いながらどんどん成長していきます。
なのでそういう仲間がいれば自分ももっと成長できたという意味でこの項目をあげました。
次の現場では成長意欲が高い方が多いといいなと思います。刺激を受けつつ成長していきたいです。

最後に・・・

私がSIで身につけられた部分としては、マネジメント部分がほとんどです。
反対に身につかなかった部分は、技術力です。
エンジニアとして生きていく以上は技術力はマストなものだと思うのですが、SI企業ではそれが身につきにくい環境だと思います。
それは誰でも開発できるように推進してきたからこその結果なのかもしれませんが、個人のスキルとしては弊害になっていますし、それを続けていけば、継続した開発というのは難しくなっていき、SI業界も厳しい状況になるのでは?と思っています。

なのでSI企業に所属しているエンジニアで、個人的に技術力に興味を持って勉強している人はSIにそのままいるというよりは、WEB系に来たいという人が多いと思います。
ただそうなるタイミングが重要で、配属された先が技術が身につく環境であれば(できればWEBシステム)、すぐに転職を考えるかと思うのですが、業務システムを何年か経験してからWEBシステムを担当することになって危機感を持って転職をしても、すでに年齢の面で不利になっていてなかなか転職が難しくなってしまうと思います。
なのでタイミングと運がすごく重要です。
できれば成長意欲が高い人には、その人にあった環境が与えられるといいなと思います。

2018年の振返りと今後に向けて

2018年を振り返って、感じたことをまとめておきたいと思います。
2019年は飛躍の年にしたいと考えているので、今回の反省を次回に生かします。

2018年の振返り

初めての大規模な機能開発

現在の現場に入ってから初めての大規模なシステム開発に携わりました。
プロジェクトとしては、生体情報を利用してログインするアプリのフェーズ2対応です。
開発した機能

  • アプリで利用するAPIの開発
  • 管理者が生体情報を削除する画面の作成
  • 削除処理された生体情報のIDを外部ベンダーへ連携して、実際のデータも削除するバッチ処理の作成

開発期間は、2017年12月頃〜2018年5月になります。

苦労したこと

  • 初めてユーザー企業と大きな案件を詰めるという経験をして、どのように進めていけばいいのか、また自分で決めていい範囲がわからず、要件の詰め方に苦労した。
  • 今回の開発では、アプリチーム、外部ベンダーなど開発関係者が多数いた。
    アプリ側でAPIを利用するために挙動を伝えたり、外部ベンダーとの通信要件的な部分を整理していくのが大変だった。
    外部ベンダーとの通信要件は自分が理解出来ていないSSLの部分だったので、消化できていないままなんとなくの理解で進んでしまっていた。(ここは後で問題となった)
  • オフショアと直接やりとりをしていたので、説明負荷がかなり大きかった。
    また仕様変更などを伝えるとなかなか理解してもらえず、すぐに対応してもらえないという辛さがあった。
  • リリース直前にSSLで接続できないことが判明した。
    SSLが何かを理解していない状態で進んでいたため、やっぱりそこで問題が発生した。

学んだこと(得たもの)

  • プロジェクトの進め方。要件の詰め方。
    ユーザーがなかなか要件を決めなかったので、その対処法を身につけた。具体的には、こちらから要件を具体的なフローや図を使って明確にしていき、詰めていくことが重要。
  • 利用者にわかりやすいAPIを作ることの重要性の理解
    今回のプロジェクトでは、既存のスマホサイトで利用しているAPIを展開する方針だったが、アプリに展開するには画面のレイアウトなどが適していなかった。
    元々のAPIが汎用性があるものではなかったのが問題だったので、わかりやすいAPIや様々な利用方法を想定したAPIを作成する重要性を理解した。
    また、APIをわかりやすくて文書化する難しさなど理解出来た。
  • 技術的な理解の重要性の理解
    外部ベンダーとの通信要件について、理解出来ないまま進んだのはよくなかった。
    しかし今回は周りに理解出来ている人がいない状況だったので、相談するにも出来なかった。ではどうすべきかという話だが、自分で前もって勉強すべきだった。今回は問題が見つかってから本を買って勉強したが、今後は事前に家で勉強するべき。
  • コミュニケーションの取り方。
    オフショアに仕様の説明することの難しさを実感した。どうしても伝わらないときにはコードで会話することが重要となる。また、仕様変更などが発生した際には、その経緯などを出来る限り詳細に伝えて納得感を持ってもらうことが重要。
  • 暗号化の知識
    SSLで躓いたことで、暗号化の仕組みに興味を持つことが出来た。
    本を読んで様々な暗号について知識を得ることができた。

    参考にした書籍

    [商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

    暗号技術入門第3版 秘密の国のアリス [ 結城浩 ]
    価格:3240円(税込、送料無料) (2019/1/2時点)


炎上プロジェクトを経験

人生で初めて炎上プロジェクトと言えるようなものを経験することができました。
プロジェクトとしては、PCサイトにある機能をスマホサイトでも利用できるようにする対応です。
開発した機能


スマホサイトはSPAで開発しています。PCサイトはAPIを利用していないので、PCサイトのロジックをAPIにもコピーして作りました。
開発期間は、2018年3月頃〜2018年8月になります。

苦労したこと

  • 規模に対してスケジュールがかなりタイトだった。
    元々はPCサイトの画面をそのままスマホサイトに提供するというものだったが、スマホサイトに適した画面にしたことで、画面遷移が複雑になったり、APIを適切な単位に分けたりとしたことで、予定以上の工数がかかってしまったことが原因。結果的には予算的にも大赤字となった。
  • 障害が多発した。
    スマホサイトの開発をした経験がある人が少なかったので、例えばボタンをタップした後のローディング処理の漏れなどがあり、多くの障害対応を行った。
    特に一番苦労したのはブラウザバックで、画面遷移が複雑になったことで、ブラウザバックで多数の問題が発生して、リリースの直前まで障害対応を行った。

学んだこと(得たもの)

  • スマホサイト作成時の画面の設計
    スマホサイトでの適切な画面が何か考えることが出来た。PCサイトのままSPサイトにするのではダメで、スマホサイトでは情報量を減らしたり工夫しないといけない。画面を分割したりするには業務知識がないと適切な画面の判断が出来ない。
    このプロジェクトでは業務知識があったので適切な判断が出来た。
  • スマホサイトを作ることの難しさを理解した
    特にフロントエンドの難しさを痛感した。使いやすい画面遷移が作りやすい画面遷移という訳ではない。使いやすさと同時に作りやすい画面も意識したい。(使いやすさと作りやすさのバランス)
    もしくは設計でのデータの持ち方などを考えて作る。
  • スケジュール管理の重要性理解
    かなりスケジュールがタイトで仕様変更も多数発生している中で、毎日残業づくしの日々だった。スケジュール管理の重要性や、顧客からの要望をはじきかえす重要性を強く感じた。人生で一番辛い期間だったがスケジュールについて考える良い機会となった。

自社サービスの会社へ入社するための準備

以前にWeb系のスタートアップから声をかけられてから転職意欲が高まったという記事を書きましたが、それからはプライベートでなるべく多くの時間を勉強するように意識して行動しました。

参考:Web系のスタートアップから声をかけていただいた話

開発した機能

学んだこと(得たもの)

  • gitの基本
    以前までであれば、苦手意識がありましたが、今はあまりないです。が、一人で開発しているので戻しの方法などはすぐには出来ないので、そこは転職後にチーム開発を経験することで身につけたいです。
  • heroku、AWSなどの関連サービスに触れることが出来た。反面、AWSの設定の意味がわからないまま進んでいるので、どこかで整理したいです。

    参考にした書籍


  • テストコードの重要性の理解
    現在の現場ではテストコードを書く習慣がなく、リファクタリングもなかなか出来ないような環境ですが、テストコードを書くことで気軽にリファクタリングを出来るということがわかり、それがあるべき姿だよな。と当たり前ですが、理解できました。
  • サービスを作ることの難しさ(SEOやデザインなど含めて)
    システムを開発してリリースすることがゴールではなく、その後どのようにPVを集めるのか。リピーターを増やすのかという難しさを感じました。特にデザイン部分は専門ではないのもあり、自分で作っているにも関わらず気に入らないという課題があります。今後どうするかは検討中です。

    参考にした書籍


  • 学習する習慣の身につけ方
    私にとっての学習する習慣の身につけ方としては、Webサービスを開発することだということが分かりました。
    Webサービスを開発していくことでモチベーション高く勉強出来ているので、そこは今後も継続したいと思います。反面、具体的にどのような技術を勉強していくのか、身につけなければいけない技術を身につけるためにどのようなサービスを開発していくのかを考えて対応していきたいと思います。

転職活動開始

ある程度Webサービスの開発も形になったので、11月くらいから転職活動を開始しました。詳細については転職活動が終わってから別記事にしたいと思っていますが、転職活動で色々な方と会話させていただく中で、多くのことを考えさせられる良い機会となりました。
特に技術面で多くの指摘をされて、レガシーな環境なんだと改めて理解しました。
面接では技術的にバカにされているように感じてかなり悔しい思いもしましたが、そういう反応されてしまったらしょうがないと思い、次回の面接に向けて足りていないところを聞いたりしているところです。

SIでマネジメント的な部分をしてきた人がWeb系企業へ転職する際には、ポテンシャル採用になると思うので、いかに早くキャッチアップ出来るのかを説明できる必要があります。
SI企業でぬるま湯に浸かってきた人(私も含めて)には、思った以上に転職活動で苦戦するので焦ると思います。
転職が成功してから工夫した点も別記事に書こうと思いますが、普段から何を考えてどのように行動しているのかを見られることが多いと感じました。

2019年の目標

技術力を飛躍的に向上させる

2019年の最大の目標は、やはり技術力を大幅に向上させることです。
今身につけなくてはいけないと考えているのは以下のような部分になります。

  • 保守性を考えたり綺麗なコードを書くスキルを身につける
  • コードを書く速度を速くする
  • メモリなども意識した速いコードを書くスキルを身につける
  • OSSのコードを多く読む
  • gitを使いこなす。単純なコマンドだけでなく、戻しの方法やコンフリクトの解消方法など
  • dockerなどコーディングする際の周辺知識を身につける
  • 新しい技術を取り入れ始める
  • 新しい職場で中心人物になる

2019年は新しい目標を立てて2週間に1回は振り返りをして行きたいと思います。
Web系企業で働いている同じ年齢の方々がかなり活躍しているのを見て、悔しいので1日も早く追いついて、そして追い抜きたいと思います。
そのためには、定期的な反省と成長実感が重要だと思っています。
転職は決まっていませんが、新しい職場では中心人物となってサービスを作って行こうと思います。

vbaで配列内を高速で検索する(二分探索)

今回はvbaで配列の中に対象のデータが存在するかを高速で検索する方法についてです。
配列の中から検索するには単純なものだと、中身を全てループさせればいいですが、それだとデータの数や検索数が増えた時に処理に時間が掛かってしまいます。
そんな時に今回ご紹介する二分探索の方法を利用すれば、超高速で検索することが可能となります。

配列の中から値を検索

二分探索プログラム

では早速検索するプログラムを記載します。

●モジュール名:BinarySearch

[vb] Option Explicit

'対象の数値が素数か確認する ' +prime_number_list+ に素数の一覧を設定 ※ソートされている必要がある ' +search_number+ に検索対象の数値を設定 Public Function IsPrimeNumber(ByVal prime_number_list As Variant, search_number As Long) As Boolean

Dim high_i As Long
Dim low_i As Long
Dim mid_i As Long

high_i = UBound(prime_number_list)
low_i = 0

Do While low_i <= high_i

    mid_i = (high_i + low_i) / 2

    If prime_number_list(mid_i) = search_number Then
        IsPrimeNumber = True
        Exit Function
    ElseIf prime_number_list(mid_i) < search_number Then
        low_i = mid_i + 1
    Else
        high_i = mid_i - 1
    End If
Loop

IsPrimeNumber = False

End Function [/vb]

配列から検索するプログラムを使う

では先ほどの二分探索プログラムを利用してみます。

テスト用のプログラムは以下のものです。
IsPrimeNumber ファンクションを利用する際の注意点としては、検索対象の配列がソート順に並び替えられていることです。
配列の並び替えは、VBAで配列をクイックソートするの記事を参考にしてください。

●モジュール名:BinarySearchTest

[vb] Option Explicit

'二分探索テスト用プログラム Public Sub Main()

Dim array_data As Variant

'0~1500までに存在する素数を設定
array_data = Array(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, _
                67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, _
                137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, _
                197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, _
                269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, _
                347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, _
                419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, _
                487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, _
                571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, _
                643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, _
                727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, _
                811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, _
                883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, _
                977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, _
                1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, _
                1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, _
                1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, _
                1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, _
                1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, _
                1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, _
                1487, 1489, 1493, 1499)

Dim start_time As Single
start_time = Timer

Dim i As Long
Const TO_NUMBER As Long = 1500

Dim prime_count As Long
prime_count = 0

'0~1500までの間に存在する素数の数をカウントする
For i = 0 To TO_NUMBER
    If IsPrimeNumber(array_data, i) Then
        prime_count = prime_count + 1
    End If
Next

Debug.Print "0~1500までの間に素数は『" & prime_count & "』個存在します。"
Debug.Print "処理時間は『" & Round(Timer - start_time, 2) & "』秒です"

End Sub [/vb]

では実行結果を確認してみます。

0~1500までの間に素数は『239』個存在します。
処理時間は『0.02』秒です

正常に配列の中にデータが存在するかをチェック出来ていることがわかります。
また速度の面でも、このくらいのデータ量だと分かりにくいのですが、かなり高速に検索が出来ています。
データ量が多ければ多いほど二分探索は効果を増します。

是非VBAで配列の中に値が存在するかを確認する際にこのプログラムを利用してみてください。

VBAで配列をクイックソートする

データの並び替えを行うアルゴリズムはいくつか存在しますが、今回はその中でも一番効率良く高速であるとされているクイックソートVBAで行う方法についてです。
配列の並び替え等によく使われるので、覚えておくといいでしょう。

クイックソート

クイックソートモジュール

こちらが配列をクイックソートするモジュールです。

●モジュール名:QuickSort

[vb] Option Explicit

'与えられた配列データを昇順に並び替える Public Sub Sort(sort_data As Variant)

Dim left As Long Dim right As Long

left = LBound(sort_data) right = UBound(sort_data)

Call Quick_Sort(sort_data, left, right)

End Sub

'クイックソートプロシージャ Private Sub Quick_Sort(ByRef sort_data As Variant, ByVal left As Long, ByVal right As Long)

If left >= right Then
    Exit Sub
End If

Dim pivot As Long
Dim l_hold As Long
Dim r_hold As Long

pivot = sort_data(Int((left + right) / 2))

l_hold = left
r_hold = right

Dim tmp As Long

Do While (l_hold <= r_hold)

    Do While (sort_data(l_hold) < pivot)
        l_hold = l_hold + 1
    Loop
    Do While (sort_data(r_hold) > pivot)
        r_hold = r_hold - 1
    Loop

    If l_hold <= r_hold Then
        tmp = sort_data(l_hold)
        sort_data(l_hold) = sort_data(r_hold)
        sort_data(r_hold) = tmp
        l_hold = l_hold + 1
        r_hold = r_hold - 1
    End If

Loop

Call Quick_Sort(sort_data, left, r_hold)
Call Quick_Sort(sort_data, l_hold, right)

End Sub [/vb]

クイックソートモジュールを使ってみる

では先ほどのクイックソートモジュールをテストしてみます。

テスト用のプログラムは以下のものです。

●モジュール名:QuickSortTest

[vb] Option Explicit

'クイックソートテスト用プログラム Public Sub Main()

Dim array_data As Variant

array_data = Array(4, 3, 10, 13, 2, 4, 1, 8, 2, 7)

Debug.Print "?\?[?g?O"
Call DebutPrintData(array_data)

Call Sort(array_data)

Debug.Print "?\?[?g??"
Call DebutPrintData(array_data)

End Sub

'配列の値を全て出力する。 Private Sub DebutPrintData(ByRef array_data As Variant)

Dim i As Long

For i = 0 To UBound(array_data)

    Debug.Print array_data(i)

Next

End Sub [/vb]

では実行結果を見てみましょう。

ソート前 4 3 10 13 2 4 1 8 2 7

ソート後 1 2 2 3 4 4 7 8 10 13

このようにarray_data配列の中身が値の小さい順に並び替えられたことが分かるかと思います。

このような方法でVBAクイックソートを実現することが可能です。
是非VBAで配列を並び替える際に利用してみてください。

もし配列の中に値が存在するかをチェックするロジックを作成したい場合には、vbaで配列内を高速で検索する(二分探索)の記事をご覧ください。

Javaでパスワード登録&ログイン処理を行う方法(AES暗号化)

前回の記事(JavaでAES(共通鍵暗号)による暗号化)にて、JavaでAES暗号化の方法をご紹介致しましたが、今回はそれの応用編になります。
JavaのWebアプリケーションで、AES暗号化を利用してユーザー登録処理とログイン処理を行うコードをご紹介致します。

まずは画面のイメージからご紹介致します。

パスワード登録&ログイン処理のイメージ

ユーザー登録画面

1.ユーザー登録画面

ユーザー登録画面

2.ユーザーIDとパスワードを入力

ユーザー情報入力

※パスワードには、「Test1234」を入力しています。
 セキュリティ的に入力中のパスワードは表示されないように、input属性に [type="password"]を設定しています。

●DB状態


3.ユーザー登録完了


●DB状態


ログイン画面

1.ログインを実施。ただし一回失敗させるパターン


※パスワードには、「aaa」を入力しています。

2.ログイン失敗後


3.ログイン成功後


※パスワードに「Test1234」を入力後の画面表示です。

ソースコード

サーバーサイド

●RegistUser

[java]

package webapp;

import java.io.IOException;

import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;

import dao.UserPasswordDao; import util.CipherUtil;

public class RegistUser extends HttpServlet { @Override

protected void doPost( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException{

    int SqlResult;
    String userName;
    String Password;

    userName = req.getParameter("userName");
    Password = req.getParameter("Password");

    res.setContentType("text/html; charset=Windows-31J");

    UserPasswordDao UserPassDao = new UserPasswordDao();

    UserPassDao.setUserName(userName);
    UserPassDao.setPassword(CipherUtil.encode(Password));

    SqlResult = UserPassDao.insert001();
    String disp;

    if (SqlResult == 0) {
        req.setAttribute("ErrMsg","データの重複が発生しました。");
        disp = "/RegistUser.jsp";
    }else{
        disp = "/RegistUser_Finish.jsp";
    }

    RequestDispatcher dispatch = req.getRequestDispatcher(disp);
    dispatch.include(req, res);

}

} [/java]

●LoginUser

[java] package webapp;

import java.io.IOException; import java.sql.SQLException; import java.util.List;

import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;

import Beans.UserPasswordBean; import dao.UserPasswordDao; import util.CipherUtil;

public class LoginUser extends HttpServlet { @Override

protected void doPost( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException{

    int SqlResult;
    int ErrCode;
    String ErrMsg;

    String UserName = req.getParameter("userName");
    String Password = req.getParameter("Password");;

    res.setContentType("text/html; charset=Windows-31J");

    SqlResult = 0;
    ErrCode = 0;
    ErrMsg ="";
    try {
        List<UserPasswordBean> list = UserPasswordDao.getInstance().select001(UserName);

        if (list.size() == 0) {
            ErrCode = 10;
            ErrMsg = "該当のユーザーは存在しません。";
        } else {
            if (CipherUtil.passcheck(Password,list.get(0).getPassword())) {
                ErrCode = 0;
            } else {
                ErrCode = 10;
                ErrMsg = "パスワードが誤っています。";
            }
        }

    } catch (SQLException e) {
        throw new ServletException(e);
    }

    String disp;

    if (!(ErrCode == 0)) {
        req.setAttribute("ErrMsg",ErrMsg);
        disp = "/Login.jsp";
    }else{
        disp = "/Login_Finish.jsp";
    }

    RequestDispatcher dispatch = req.getRequestDispatcher(disp);
    dispatch.include(req, res);
}

} [/java]

●UserPasswordDao.java

[java] package dao;

import java.sql.ResultSet; import java.sql.SQLException; import java.util.List;

import Beans.UserPasswordBean;

public class UserPasswordDao {

private String UserName = "";
private String password = "";

public static UserPasswordDao getInstance(){
    return new UserPasswordDao();
}

private static final String insert001 = ""
        + "insert into "
        + "  user_password "
        + "values"
        + "  ("
        + "    'USER_NAME',"
        + "    'PASSWORD'"
        + "  );";

private static final String select001 = ""
        + "select "
        + "  user_name,login_pw "
        + "from "
        + "  user_password "
        + "where "
        + "  user_name = 'USER_NAME';";

public int insert001() {

    String sql = insert001;
    sql = sql.replaceAll("USER_NAME",UserName);
    sql = sql.replaceAll("PASSWORD",password);

    try {
        return DBManager.simpleUpdate(sql);
    }catch (SQLException e) {
        return 0;
    }
}

private ResultSetBeanMapping<UserPasswordBean> allMapping=
        new ResultSetBeanMapping<UserPasswordBean>(){

    public UserPasswordBean createFromResultSet(ResultSet rs)
        throws SQLException {

        UserPasswordBean page = new UserPasswordBean();
        page.setUserName(rs.getString("user_name"));
        page.setPassword(rs.getString("login_pw"));

        return page;
    }

};

public List<UserPasswordBean> select001(String UserName) throws SQLException {

    String sql = select001;
    sql = sql.replaceAll("USER_NAME",UserName);

    System.out.println(sql);

    return DBManager.simpleFind(sql, allMapping);

}

public String getUserName() {
    return UserName;
}
public void setUserName(String UserName) {
    this.UserName = UserName;
}

public String getPassword() {
    return password;
}
public void setPassword(String password) {
    this.password = password;
}

} [/java]

●CipherUtil

[java] package util;

import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.util.Base64;

import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec;

public class CipherUtil {

/*
 * 暗号化の方法
 * 暗号化アルゴリズム・・・AES
 * ブロック暗号のモード・・・CBS
 * パディング方式・・・PKCS5Padding
 */

//暗号化のキーを指定する。
private static final byte[] KEY = "abcdefghijklmnop".getBytes();

//public暗号化メソッド
public static final String encode(String plainText){

    SecureRandom random;
    byte iv[] = null;

    try {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        random = SecureRandom.getInstance("SHA1PRNG");

        iv = new byte[cipher.getBlockSize()];
        random.nextBytes(iv);

    } catch (Exception e){
        System.out.println(e);
        e.printStackTrace();
    }

    return encode(plainText,iv);
}

//実際の暗号化処理
private static final String encode(String plainText,byte[] iv){

    String encryptText = null;

    try {
        //初期化ベクトル (IV)をセットする。
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);

        //暗号化方式、ブロック暗号のモード、パディング方式を指定する。
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //暗号方式等をセットして、暗号器を生成する。
        cipher.init(Cipher.ENCRYPT_MODE , new SecretKeySpec(KEY, "AES"), ivSpec);

        //暗号化対象の文字列を暗号化する。
        byte[] cryptogram = cipher.doFinal(plainText.getBytes("MS932"));

        //暗号化の結果を格納する変数を生成
        byte[] byteResult = new byte[iv.length + cryptogram.length];

        //byteResult変数に対して、初期ベクトル + 暗号データを格納する。
        System.arraycopy(iv, 0, byteResult, 0,iv.length);
        System.arraycopy(cryptogram, 0, byteResult, iv.length, cryptogram.length);

        //暗号化されたデータに対してbase64エンコードを行い、文字列化する。
        encryptText = Base64.getEncoder().encodeToString(byteResult);

    } catch (Exception e){
        System.out.println(e);
        e.printStackTrace();
    }

    return encryptText;

}

//初期ベクトルを取得するメソッド
private static final byte[] getIV(byte[] BaseDecode) {
    Cipher cipher = null;
    byte[] iv = null;

    try {
        cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    } catch (Exception e) {
        System.out.println("err");
    }

    iv = new byte[cipher.getBlockSize()];

    System.arraycopy(BaseDecode, 0, iv, 0, iv.length);
    return iv;

}

//暗号化済みの文字列と、比較対象文字列を比較する
public static final boolean passcheck(String plain, String encoded) {

    byte[] BaseDecode = null;

    BaseDecode = Base64.getDecoder().decode(encoded);
    String encodedFromPlain = encode(plain, getIV(BaseDecode));

    return encoded.equals(encodedFromPlain);

}

} [/java]

jsp参考

JSP側はあまり関係ありませんが、参考として記載しておきます。

●RegistUser.jsp

[html] <%@ page language="java" contentType="text/html;charset=Windows-31J" %> <%@ page pageEncoding="Windows-31J" %>

<HTML>

<HEAD>

<TITLE>ユーザー登録</TITLE>

<style type="text/css"> .block1 { display: inline-block; width: 7em } </style>

</HEAD>

<BODY>

<%

String ErrMsg = (String)request.getAttribute(&quot;ErrMsg&quot;);

if (ErrMsg != null) {

%> <strong><font color=#ff0000><%= ErrMsg %></font></strong> <% } %>

<h1>ユーザー登録</h1>

登録するユーザー、パスワードを入力してください。 <form Name="PostForm" Action="RegistUser" method="post"> <span class="block1">ユーザー名:</span><input type="text" name ="userName" /><br> <span class="block1">パスワード:</span><input type="password" name ="Password" /><br> <input type="submit" value="ユーザー登録" /> </form>

</BODY>

</HTML> [/java]

<p style="margin-top:4.0em;"></p>

<p> ●RegistUser_Finish.jsp </p> [html] <%@ page language="java" contentType="text/html;charset=Windows-31J" %> <%@ page pageEncoding="Windows-31J" %>

<HTML>

<HEAD>

<TITLE>ユーザー登録完了</TITLE>

<style type="text/css"> .block1 { display: inline-block; width: 7em } </style>

<meta http-equiv="Pragma" content="no-cache"> <meta http-equiv="Cache-Control" content="no-cache"> <meta http-equiv="Expires" content="0">

</HEAD>

<BODY>

<% String userName = request.getParameter("userName"); %>

<h1>ユーザー登録完了</h1>

以下の内容でユーザーを登録しました。<br> <span class="block1">ユーザー名:</span><%= userName %><br> <span class="block1">パスワード:</span>*********<br>

<a href="Login.jsp">ログインはこちらから</a>

</BODY>

</HTML> [/html]

●Login.jsp

[html] <%@ page language="java" contentType="text/html;charset=Windows-31J" %> <%@ page pageEncoding="Windows-31J" %>

<HTML>

<HEAD>

<TITLE>ログイン</TITLE>

<style type="text/css"> .block1 { display: inline-block; width: 7em } </style>

</HEAD>

<BODY>

<%

String ErrMsg = (String)request.getAttribute(&quot;ErrMsg&quot;);

if (ErrMsg != null) {

%> <strong><font color=#ff0000><%= ErrMsg %></font></strong> <% } %>

<h1>ログイン</h1>

ユーザー、パスワードを入力してください。 <form Name="PostForm" Action="LoginUser" method="post"> <span class="block1">ユーザー名:</span><input type="text" name ="userName" /><br> <span class="block1">パスワード:</span><input type="password" name ="Password" /><br> <input type="submit" value="ログイン" /> </form>

</BODY>

</HTML> [/html]

●Login_Finish.jsp

[html] <%@ page language="java" contentType="text/html;charset=Windows-31J" %> <%@ page pageEncoding="Windows-31J" %>

<HTML>

<HEAD>

<TITLE>ログイン成功</TITLE>

<style type="text/css"> .block1 { display: inline-block; width: 7em } </style>

<meta http-equiv="Pragma" content="no-cache"> <meta http-equiv="Cache-Control" content="no-cache"> <meta http-equiv="Expires" content="0">

</HEAD>

<BODY>

<% String userName = request.getParameter("userName"); %>

<h1>ログイン成功</h1>

ログインしました。<br> <span class="block1">ユーザー名:</span><%= userName %><br>

</BODY>

</HTML> [/html]

コード解説

ユーザー登録とログイン処理で重要なのは、ユーザー登録時のパスワード暗号化と暗号化されているパスワードとログインパスワードの比較チェックの部分となります。
なのでコード解説もそこを中心に行います。

まず、ユーザー登録時には CipherUtil.encodeメソッドを利用してパスワードを暗号化します。
暗号化した文字列を、user_passwordテーブルにユーザー名と一緒に格納します。

ここで利用した CipherUtil.encodeのポイントとしては、初期化ベクトル (IV)をランダムに作成して、かつ暗号化後のパスワードの先頭に付与している部分ですね。
ランダムな初期ベクトルを利用することで、同じパスワードであっても別の文字列に変換されます。
また、ランダムな初期ベクトルなので、後々パスワード比較する際に、比較できなくなってしまうので、暗号化後のパスワードの先頭に付与しておきます。

次にログインパスワード認証の部分では、CipherUtil.passcheckメソッドが重要になります。
このメソッドは、入力されたログインパスワードと、user_passwordテーブルから取得した暗号化されたパスワードの比較メソッドになります。

前回の記事では、decodeメソッドを用意して、暗号化されたパスワードを平文に戻すことが出来るようにしていましたが、ログインパスワードのチェック処理では平文に戻す必要はありません。
むしろセキュリティ的には暗号化されたもの同士でパスワードチェックをするべきです。
※なので、decodeメソッド自体を用意しない。

CipherUtil.passcheckメソッドの返り値が trueであれば、パスワードがあっているということで、ログイン成功となります。

コードの解説としてはこのくらいになります。

ではまた次回。

Web系のスタートアップから声をかけていただいた話

つい先日web系のスタートアップ企業から、入社しないかとツイッターで声をかけていただき、面談をしてきましたので今回はその話をさせてください。

私は約2年前に1度転職をしていますが、転職を決めたときから、もう1回転職をするつもりで行動してきました。
次の転職では自社で何かサービスをしている会社の社内SEもしくはWeb系企業を考えていました。

今の職場では大手Sierに派遣されて、JavaでWebアプリケーションの開発をしていて、家での勉強もJavaでやっていました。
Web系企業に入るには、Ruby on RailsなどのJavaではない言語のスキルが必要というのは理解していたのですが、Javaを完璧にするまではRailsの勉強を後回しにしようと思い、そうしてきました。

しかし、今回お声かけいただいて話を聞けたことで、考え方が変わり、JavaはいいからRailsを勉強しようと気持ちが変わって行動し始めることが出来ました。そしてWeb系企業へかなり興味を持つことが出来たので、この気持ちを忘れないように自分の備忘録もかねて記事を書くことにしました。

最初に言ってしまうと、今回お声かけいただいた会社には入ることはありませんでしたが、私の今後を変える大きな出来事となりましたので、記事を書かせていただきます。
大変感謝しています。ありがとうございます。

※企業名は迷惑をかけたくないので秘密と非公開とさせていただきます。

何回かの面談

ツイッターでのDM

初めはツイッターでのDMからでした。
某Web系企業の社長から、興味ないですか?転職とか考えてないですか?と連絡がありました。
会社について調べてみると、いくつかのWebサービスを運営していて、その中の一つは私も知っているような有名なものでした。

私はあまりスキルを持っていないので、一度小さな会社に行ってしまうと、良い会社に入れないと考えており、ベンチャーはリスクだと考えていました。なので普通のベンチャー企業であれば興味を持たなかったと思うのですが、一つ大きいサービスを持っていて将来性を感じたので、一気に興味を持ちました。
かつこの頃、仕事で大きなミスをして顧客からかなり詰められていたので、1日も早く働きやすい環境へ行きたかったというのもあります。その時の話はまた別の機会にしたいと思ってます。

最初連絡いただいた瞬間は、まったく想定していなかったことだったので、急な転職活動を始めることに対しておっくうに感じましたが、ここで動かなかったら貴重なチャンスを逃す!と思って話を伺いに行くことにしました。

1回目の面談

DMをいただいてから1週間後、休みを取ってその会社に行きました。

私は普段はスーツで働いているのですが、その会社のエンジニアは私服で働いているということもあり、服装に悩んだのですが、中間くらいのビジネスカジュアルな感じで行きました。
スーツでも使っているスラックス(ただし素材が少しカジュアルめ)とポロシャツにしました。

ここで驚いたのは、着いて社長が出てきてくれたのですが、完全に私との約束を忘れていました。
結構焦りましたし、この会社大丈夫か?と思いましたね。
(その社長はかなりの人と面接してるみたいなので、忘れてしまったんだと思いますが…)

しかし実際に会話をしてからは結構スムーズに進みました。
最初は社長と一対一での会話で、会社全体のこととか、自分のキャリアを軽く話したりとかしていたんですが、途中からエンジニアの方も呼んでくださって、軽くスキルの確認とかをしていただきました。

ただし、エンジニアの方は普段はRailsを使っている方々なので、私のスキルを深くは理解は出来なかったのだろうと思います。

コードを書くときに気をつけてることを聞かれたのですが、普段あまり意識していなかったので(実は意識しているのかもしれませんが)、DRYと答えました。
逆に面接していただいている方に意識している部分を聞くと、責務と答えてらっしゃいましたね。
その時は責務が何か分からなかったのですが、Railsの勉強をしていると頻繁にでますね。責務。
言語や環境が違うだけでここまで話が通じ合わないとは思わなかったので、焦りました。

そして1回目の面談は30分くらいで終わって帰宅しました。

帰りの電車の中で、仮の採用連絡をいただきました。

スタートアップだと未経験(Railsのという意味)を採用するのはリスクがあると思います。
しかしRailsを分からないにも関わらず仮採用の連絡をいただけたのは、その会社で以前に未経験で採用した方がある程度の成果を出し始めていることや、私が普段から勉強している部分を認めていただけたのかなとポジティブに判断しています。

反面、提示いただいた条件(給与)については、未経験(Rails)を考慮されて、年収で350万円程度と今の年収から大きく下がるものでした。

半年で成果を出せれば今と同じくらいの年収(だけど、福利厚生が圧倒的になくなる)を出してくれるとのことでしたが、明確な基準があるわけでもなく、半年たってかなり成長していても上げてくれるとは限らないので、かなり不安の大きいものでした。

なのでもう一回、今度は会社の制度や給与体系についてお話しを伺いに訪問することにしました。

2回目の面談

2回目の面談は人事部の方と、1回目の面談の翌日に行うこととなりました。
※面接はスピードが大事だと思いますが、スタートアップだとよりそうだと思います。

翌日は業後にお伺いして、面談してくださる人事部の方と夜からお会いしました。

社長からOKをもらっていたというのもあり、人事部の方とは面談というよりは会社説明の要素が強かったです。

説明としては、まずは企業理念的な部分、会社の規模(社員数など)、運営しているサービスとそのPV推移、開発環境、評価制度についてといった流れで説明していただきました。

売上は非公開ということもあり、詳細は教えていただけませんでしたが、利益は出ていないとのことでしたね。
最近のスタートアップだと、赤字のまま上場を目指す企業も多いとのことでした。(メルカリなど)

参考までに、開発環境としては、サーバーサイドはRails 、フロントはReact、Vue.js、jQuery、ソース管理はもちろんGitHub、サーバとしてはAWSやさくらVPSとのことでした。

そしてこの日の一番の目的である給与体系についてですが、給与を決める要素としては、スキルに応じて大きく3つのステージ(クラス?)に分かれているとのことでした。
そして未経験(Railsを)の方だと一番低いところから始まるとのことで、私もそれに従って一番下のところで入社いただくと説明を受けました。 その給与が大体年収で350万より少ないくらいでしたね。

その次のステージ(真ん中)になると、プラス100万円くらいから始まるとのことでした。
私が生活するには、せめてここにならないと辛いと思ってます。

そして、今いる社員はどのステージにどのくらいの割合になっているのか確認したところ、一番上のステージの方はおらず、私の前に未経験で入社された方は、ちょうど半年くらいでしたが、まだ一番下のステージの真ん中くらいとのことでした。

正直、転職で年収が下がることを悪いことだとは思っていなくて、そこで得られる経験や、そこで出来る(させてもらえる)仕事と、今の職場と年収とを天秤にかけて、メリットの方が大きいと感じるのであれば、チャレンジしても良いはずです。

そして私も、自分の会社に貢献でき、自分の成果が会社の成長に表れるという点でかなり惹かれていました。
ただ、何回も書いているようにお金の面でかなり不安で、かつ家族を持っていて、近い将来子供も欲しいと考えていることから、とても決めきれない状況となりました。

そこで面談していただいた人事部の方に、未経験で入社された方が、それまでにどのようなスキルを持っていて、入社してからどのような勉強をして、今どのくらいのスキルがついているのかを聞いていただくことにしました。

意図としては、未経験の方が辿った半年間と、自分が入社してから辿るであろう半年間を比較して、スキルの成長面で私が大きく上回る可能性が高い場合には、真ん中のステージにしていただける可能性が高いと判断して、入社を前向きに進めたいと考えました。

なのでこの日はそこで終わりとしました。

未経験入社されたエンジニアの方との面談

その翌週(土日を跨いだので4日後くらい)に人事の方から連絡をいただき、直接会話した方が良いだろうということで、未経験で入社されたエンジニアの方との面談をセッティングしていただきました。

その日は有給を取って会社へ向かいました。

2名の未経験入社されたエンジニアの方がいて、1人はIT企業自体が初めて、もう1人はSierからの入社とのことでした。
気になっていたスキルの部分に関しては、おそらくRailsという意味ではまだまだ私は及ばないと思いますが、半年でここまで!?というレベルには感じませんでした。(ただ面談で口頭なので雰囲気で申し訳ないのですが…)

ただやはり日々の勉強はしているようで、全くの未経験から半年でここまで分かっているのかというレベルではあるのかなと思います。(Sierと比較してですが)

普段の業務では、先輩方は厳しいということもなく、かつやりたいと声をあげたらやらせてもらえる環境だと言っていました。
残業もそこまで多いわけではなさそうです。

そして一番気になっていたお金の面についてですが、、、
その二人も入社前に半年で頑張ったらこれくらいと提示を受けていたと思うのですが、やはりそこで言われた金額には全然届いていないような印象を受けましたね。
正直にその段階で、入社は諦めました。

その二人も転職して給与が大きく下がったとのことでしたが、給与が下がった分に釣り合った良い経験が出来ると考えて転職したと言っていました。
もちろん私も比較したのですが、今は家族がいて、ちょっとずつ上げてきた年収が下がる恐怖(かつ一度下がればなかなか上がらないだろうと思っている)を考えると、この給与では例え魅力があっても厳しいなと考えました。
やっぱり家族が出来るとリスクは取りづらいなぁと。ここで感じましたね。
結婚してなくてもやめた可能性が高いですが。

その時面談してくださった二人はとても楽しそうに仕事をしているだろうことが伝わって、しかも未経験の後輩が出来るだろうと思っていて目をキラキラさせて説明していただいてたのが、罪悪感として残っています。

その後はみんなが働いているフロアにお邪魔させていただき、創業当初からいる方と少し話をしたり、人事部の方と最後に会話をして終わりました。

そしてその3~4日後に、電話で選考辞退の電話をして転職はしないこととしました。

今回の転職活動で得たもの

web系に転職したい

今回の転職活動では転職しないことにしましたが、かなり得るものの大きい転職活動となりました。

特に、今まではweb系企業になんとなく興味があるくらいの感覚でしたが、絶対web系企業に転職する!
くらいの気持ちを持つことが出来たのは個人的に大きいです。

Sierにいると自分達のためにシステムを作るということはなく、お客さんがいて、その人達が望む通りのシステムを作るだけになります。
言い方を変えると、お客さんが求めている機能しか作れないし、業務をしらない人が提案しても受け入れてもらいにくいし、予算の問題もあるし、そもそも提案して作ったシステムが本当に役立つものなのか、またリリースしてから使われ方を確認して改善するみたいなことが出来ないのはつまらないなぁと思います。

web系企業はというと、自社サービスの開発となり、ある程度の何をすれば良いのかが分かるし、提案しても受け入れてもらいやすいと思うし、自分が作ったものがどのくらい良いものだったのか、何が悪かったのか、どうすれば良いのかがわかり、楽しいだろうなぁと思います。
(それはブログも同じですね。自分が書いた記事がどう評価されているのかがPVを見れば分かる。)

それに働く環境という点でもエンジニアにとってはかなり良い職場が多いと思います。

なので、絶対web系に転職する!
という気持ちです。

勉強の仕方がわかった

web系に転職したいという気持ちが強くなっているので、この1ヶ月かなり勉強するモチベーションを保てています。
今回の転職活動するまでも、なんとなくパソコン立ち上げて勉強してました。しかしなんかwebアプリケーションを作りたいなぁと思っても、環境作る方法が分からず(サーバ構築めんどくさくて)、諦めてました。

しかしこの1ヶ月で1つwebアプリを公開出来ました。(といっても静的なページが多い)
これまで自分のwebサービスを立ち上げたくても出来なかったの公開まで行けたのは、以下の要素のおかげだと思ってます。

1.絶対転職するというモチベーション
一番の要素はやはりこれです。
web系企業に入社するためには、技術力がないといけないと思います。
その技術力を証明するのに、自分のwebサービスを持っているのが良いと思ってます。

2.勉強の方法を教えてもらえた
今回、未経験での採用になってしまうということで、どう勉強すれば良いのかを面談で教えていただけました。 具体的にはRails tutorialのことです。

他の言語だとこういったサイトはあまり知らないので、本から勉強していくタイプなので、今回もRailsの本を買いました。
しかしRails tutorialというサイトを教えてもらって勉強を開始してからは、このサイトだけで、自分のwebサービスを公開することが出来ました。

ただただその言語だけを教えてくれる本とは違い、Railswebサービスを公開するにあたり必要な周辺情報まで理解することが出来る最高のサイトに出会い、webエンジニアとして大きく階段を上ることが出来たと考えています。

3.同年代でモチベーションの高い人たちと触れ合えた これもモチベーションの話になりますが、同年代でエンジニアとして成長へのモチベーションが高い人に会えたのは大きいです。
Sierにいると今の自分に満足していて、勉強を怠っているエンジニアの方はかなり多いです。
そのため、年上であっても自分より出来ない人はかなり多くいて、自分が一番出来るというある意味天狗状態で働いてきました。

しかし今回誘われた会社で出会ったエンジニアの方は、みんな成長へのモチベーションが高くかつエンジニアとして立派で、しかも年下というのに驚きました。
自分はまだまだ若いと思って油断していましたが、かなり強い危機感を抱くことが出来ました。

私は負けず嫌いなので、今回のことは今後に大きく役立つはずです。

いつ転職するか

2018年12月末で今の職場を退職し、2019年からweb系企業で働くことを目指してます!

この約半年の間に、自分のwebサービスを2つ立ち上げて(1つは公開出来たので、収益化していきたい)、技術力を上げていきます。
そのためには、日々の勉強をもっとやりたいし、仕事で得られるものもあるので、手を抜かずに精一杯やっていきたいと思います。

最後に 今回、ツイッターで声をかけていただいた某企業の社長さん、その会社の人事の方、エンジニアの方、本当にありがとうございました。
色々とお時間取ってくださったのに、選考辞退となってしまって本当に申し訳ないと思ってます。
一緒に会社を大きくしていきたかったです。
事業が大きくなっていくことを祈ってます。
この記事は絶対見てないとは思いますが、本当に感謝しています。ありがとうございました。

多少おっくうでも大きな物を得られる可能性があるので、興味があったら行動することが大事ですね。

また半年後に転職の記事が書けるように、日々楽しみながら勉強していきます!