【IT業界面接対策】面接への「心構え」と「よく聞かれる質問と回答例」

こんにちは、スタルヒンです。新卒の方の面接の準備をそろそろし始める時期ですよね。いまからIT業界へ就職や転職を考えられている方向けの面接時のアドバイスのブログとなります。ご活用いただけましたら幸いです

私はインターネットが世に普及する前からプログラミングの世界で生業を行ってきました。40年の経験があります。若い時から教わる方がいない環境でしたので、20年は入社面接官の経験があります。役員経験も長くあります。いまは58歳にしてフリーランスでいまだ現役プログラマーです。会社側の考え方もわかってますので皆さんへ有意義なアドバイスができるかと思います。

はじめての就職や転職を考えているかたは以下の不安や疑問を抱えていませんか?
エンジニア転職したいけど面接でどんな質問をされるかわからないなー
エンジニア面接でどんな回答をしたらいいんだろう?

面接内容がわからず準備が出来ないと不安になりますよね。今回は面接にのまれないためにも、相手を知り心づもりをおこなってから、面接で聞かれる質問や回答例について、私が会社役員の時の良く行った質問やそれに対する相手にとっては興味ある回答例を考えてみました。実際の例ですのでみなさんが実践できるように解説していきます。

最近の面接の傾向【IT企業側にまず立ってみよう】

大幅に質問される内容が変わったわけではありませんが、コロナの影響でリモートワークが流行りはじめたことでリモートワークに関する質問が増えています。特に社内SEはリモートワークを提供するためのシステムや仕組みを担当しますので、リモートワークをどのように受け入れて、どのような改善をしていくべきかを聞かれるケースが増えています。リモート作業でも十分コミュニケーションが取れるかどうかと、リモートワークでのセキュリティの考え方をしっかり持たれているかが、採用側では知りたいことなのです。

働き方について

面接官)
出社とリモートワークどちらの働き方を希望しますか?

リモートワークについては正解はありませんのでどちらの働き方を希望するか正直に話せば問題ありません。もしリモートワークを認めない企業であれば、そもそも入社したいかちゃんと考えたほうがいいと思います。そのような企業は考えが古く、IT技術に関してもレガシーな可能性が高いので、自身のキャリアアップに繋がらない恐れがあります。

ゼロトラストについて

https://www.nri-secure.co.jp/service/zerotrust

面接官今はVPNを使用してリモートワークしてもらっていますが、ゼロトラストを実現したいと考えています。ゼロトラストに向けての経験や実績はありますか?

回答例)
最初は出社することにより会社の方たちとのコミュニケーションを行った後にコミュニケーションが可能で仕事ができるようになった時点でリモートワークへ切り替えさせていただければと思っております。その際に経験はありませんが、ゼロトラストには注目していますので実現に必要であるSaaS統合認証基盤のOktaとAzureADについてセミナーに参加したり、書籍を読んだりしていつでも参加出来るように準備をしております

社内SEの界隈ではやはりゼロトラストがリモートワークに必須のセキュリティモデルと考えられていますので質問される可能性が高いです。何かしらの経験があれば回答できるように整理しておくとよいでしょう。もし経験が無くても上記の回答のように事前に勉強していることをアピールするのもいいですね。

コミュニケーションについて

面接官入社後はリモートワークになり、対面でメンバーと会う機会が少ないと思いますが、他のメンバーとの関係性構築に不安はありませんか?

リモートワークでの関係性構築はなかなかと大変です。自分も案件受託で新メンバーとコミュニケーションを意識して対応しております。現職や学生間でもリモートワークをしているようであればその経験を語るのがよいでしょう。もし経験が無く不安であれば、その会社ではどのような取り組みをしているかを聞いてみるのもいいかと思います。

面接官)
リーダシップを発揮した経験は?

回答例)
私が考えるリーダーシップは、各メンバーが責任を自覚できるように働きかけることです。

高校生のときにテニス部に所属していましたが、思うような結果を出せず、部員もやる気を失って練習を休みがちでした。それを見て私は、顧問の先生と部長にミーティングの場を持つよう提案しました。

やる気を出すにはどうしたらいいか、結果を出ために何をするべきか意見を求め、それぞれが責任を持つことを話し合いました。このミーティングで部員みんなが責任感が持つようになり、練習に打ち込んで大会でも上位入賞を果たすことができました。

この経験は、入社後にチームで仕事に取り組むときにも活かせると思います。

その他のよく聞かれる質問と回答例

自分は数千人も面接してきましたが基本的に聞く質問はほぼ同じです。面接官が何故この質問をするのか考えて、そこから回答を準備出来るとよいでしょう。以下に質問と回答例をまとめていますので面接前までに頭の中で整理してみてください。

自己紹介

面接官)
あなたのこれまでの経歴を教えてください

回答例)
〇〇〇と申します。宜しくお願い致します。私は大学卒業後から現在までに〇社でエンジニアを担当しました。1社目は新卒で〇〇会社に入社し、主に×××サーバーの構築や運用保守を経験しました。2社目は・・・・
現在は〇〇会社で××グループのマネージャーとして〇名のメンバーのピープルマネジメントと××、××、××などの案件のPMを担当しております。

回答例)

私は現状に満足せずに、常に現状の構成が最適なのか、部分最適になっていないか、過剰にコストがかかってないかを考え、サーバーの価格交渉から実際の運用、他システムとの連携までを想定・検討し、ゼロベースで環境を構築することを徹底してきました。御社でも積極的に新しい技術や新システムの導入にチャレンジし、社員が働きやすい環境を実現します。

学校卒業後から現在までの職務経歴をメインに紹介と同時に自身のPRポイントのアピールを盛り込むといいでしょう。あまりに長すぎると面接官が飽きてしまいますので2~3分でまとめてみてください。

面接官)
今回のポジションでどう活躍できるか、どのような強みが活かせますか?

回答例)
現職でも〇〇グループのリーダーとしてXXX、XXX、XXXの導入や運用改善に着手してきました。入社当初、〇〇の導入を検討していたが案件を先導して進めるPMがいないことにより導入が進んでいなかったのですが、私がPMとして訴求ポイントを経営陣やグループ会社の担当者に伝えていくことで従業員側から〇〇を利用したいと声があがるようにアピールしたことで導入の合意を得ることができ、コロナ対応に間に合わせることが出来ました。御社でもコロナ対応、ユーザーの利便性向上に向けてシステム導入を率先して引っ張っていきます。

ただ実績をあげたことをアピールするのではなく、自身の長所や強みがどのようにその経験に活かされたかを盛り込んでいきましょう。社内SEではヘルプデスク対応など受け身である人が多くいますが、自身が率先してシステム導入や改善を進めたことをアピールできると面接官の印象がいいですね。

転職理由、志望動機

面接官)
今回の転職理由を教えてください

回答例)
全体を俯瞰、全体最適を意識したシステム提供を実現できる環境でよりユーザや事業に貢献していきたいと考え転職を決意しました。その一手段として例えば・・・

転職理由は必ず聞かれる質問です。本来の理由であれば「収入をあげたい」「残業を減らしたい」「有名企業で働きたい」などでしょうが(自分もそうです)、ここはもっと掘り下げて何故転職を考えたのかを考察していきましょう。
例えば「収入をあげたい」が本音であれば、「収入をあげたい」→「もっと給与をもらえる技術がある」→「技術を活かしたい」と掘り下げることが出来ます。「技術を活かしたい」ことが本質であればそこに肉付けをして「自身の技術を活かせる環境で社会や事業に貢献したい」となり、これが転職理由となります。

面接官)
弊社の志望動機はなんですか?(なぜ弊社がよいか、弊社でどうなっていきたいか)

回答例)
現在の会社での課題や悩みの解決のために情報を検索したところ、御社では既に対応している記事を多くみつけました。例えば〇〇〇の記事を拝見し、技術力が高いことはもちろんですが、ユーザーの潜在的な課題を導き出し、それに対してスピード感をもって仕組みを創り上げてきたことで他社よりも2歩も3歩も進んでいると感じました。また、単一のシステムでの対応ではなく色々な検索の仕組みに対応するためにこの仕組みを導入していることに自身が目指している全体を俯瞰して部分最適ではなく全体最適を意識した仕組み創りに既に着手していることを感じ取りました。私も同じ思想のもとで従業員が自身の仕事に集中できる仕組みを作り上げていきたいと強く思いました。

志望動機もかなり頻度が高い質問です。転職理由と同様に本音を掘り下げていき、志望動機を考えていきましょう。ここで重要なのはちゃんと企業研究を行うことです。その企業の現状や強み、大事にしていることを理解出来ていなければ別の企業でいいんではないかと思われてしまいます。私が企業研究する場合には、その企業のTechブログや記事などを読み込み、どんな課題に対してどのような技術を用いて対応したのか、その対応によりどのような効果が表れたかを確認することで、その企業が何を重視しているかを知ることが出来ます。

例えばSlack関連の開発や改善の記事があるのであれば、この企業はコミュニケーションについて課題を感じ、コミュニケーションを活性化させたいと考えていると推測できます。つまり社員間の情報共有を大切にしている企業であるということです。このようにその企業の情報からニーズを確認し、それを盛り込んだ志望動機を考えることが重要です。

キャリアプラン

面接官)
入社をして何をしたいと思っていますか?

面接官3~5年先にこうなっていたいというイメージを教えてください

回答例)
社内システム全てを把握し、個別最適ではなく全体最適を自らが先導して進めていける人物をイメージしています。システム投資は、経営戦略を実現するもので、それが構築できるのは複数の現場を俯瞰する情報システム部門が最も適していると考えています。自分の○○○という強みを発揮して現場のヒアリングから潜在的な課題を導き出し、従業員が自身の仕事に集中できる仕組みを作り上げていきたいです。

面接官はこの質問でどのチーム(プロジェクト)にもっとも適しているかを判断したいと考えています。まったく経験の無いことをやりたいとアピールすると面接官もイメージがしづらいと思いますので、章立てて「まずは○○の経験を活かして、△△に携わりたいと考えています。そちらで実績を積み、2〜3年後にはマネージャーを目指したいと考えています。」といったようにまずは即戦力として活躍できるポジションをアピールし、そこで実績を積んだうえで数年後に目指したい姿をアピールできると面接官もイメージしやすいと思います。

目標が高すぎる(社長になりたいなど)となれなかった時に退職のリスクがあると思われてしまいますし、逆に低いと入社しても成長しないのではないかと思われてしまうので、現実的な目標をイメージするようにしましょう。

今までの経験

面接官)
今までの業務の中での成功体験をお聞かせください

回答例)
システムやグループ会社毎にセキュリティルールがバラバラだったものを現状のリスクを洗い出し、それをリスクヘッジできるようにするために基本となるセキュリティポリシーを設計(IP制御からデバイス制御)しました。それを経営陣やグループ会社に説明することで今のままではリスクがあること、それをルールを定義することでリスクヘッジ出来ることを理解してもらい、セキュリティポリシーを刷新することが出来ました。これによりセキュリティを強化したうえでリモートワークへシフトすることが出来ました。

面接官は成功体験を聞くことでどんなことに気をつけて、何を重視してやり遂げたかを確認したいと考えています。ただメンバーとしてプロジェクトに関わっただけでは弱いので、自身が主体的にどんな役割を担ったか、どんなことを工夫したかを盛り込むようにしましょう。

面接官)
現職での失敗体験があれば聞かせてください

回答例)
グループのメンバーと案件を進める際に案件の進め方から注意ポイントまで細かく指示を出して案件を進めたことがあり、案件自体は問題なく完了まで進めることが出来ましたが、同様の案件の際も自分が指示をしないとメンバーが進められなくなってしまった経験がありました。メンバーの成長も考え、あえて1から10までやることや意義、目的を言わずにメンバー自身が考える能力を身に着けられるようにマネジメントのやり方を変えました。これによりメンバーが成長し、メンバーに安心して案件を任せられるようになっただけでなく、メンバーが私から認められていると感じたようで私への信頼感もあがり、グループのパフォーマンスが向上しました。

面接官はこの質問で失敗から何を学んだのか?どのように乗り切ったかを確認したい考えています。ただ失敗の話をするだけでなく、なぜ失敗したのかを考察する能力もみていますので、ちゃんと失敗の本質について向き合うことが大事です。失敗の原因がわかればそこに対してどのように対処すればいいかが見えてくると思います。

一番ダメなのは他責にすることなので、「○○のせいで」や「自分のせいではない」といった回答はしないように気をつけてください。

こちらからの質問も用意しましょう

面接では最後にこちらから質問する時間があります。質問が無いと回答してしまうと「うちの会社に興味がないんじゃないか?」と思われてしまう可能性がありますので、事前に聞きたいことを2〜3つ用意しておきましょう。

質問例)
御社のHPブログで〇〇に取り組んでいるという記事を拝見しました。○○に課題を感じ、取り組んでいると感じましたが、リモートワークに対してはどのような取り組みを検討されているのでしょうか?

質問例)
○○様の目から見て、現状の社内システムの課題は何でしょうか?

質問例)
これから導入予定(導入を検討している)のシステムはありますか?

質問例)
〇〇様のお立場だからこそわかる御社の魅力や、今後強化していくべき点を教えていただけますか?

質問をすることでよりその企業の理解が深まり、企業を選定する情報が増えるだけでなく、面接官へ志望度をアピールすることにもつながります。回答例にある通り、Techブログを読んで、自分はこう考えているがいかがでしょうかといった質問であれば、ちゃんと企業研究をしていることが伝わりますし、物事の本質を捉える能力があることもアピールできます。

絶対にしてはいけない質問は以下の通りです。

  • 給与や福利厚生について聞くこと
    →お金にしか興味がないと思われる
  • 残業時間を聞くこと
    →定時しか働かないと思われる
  • 企業のホームページなどに情報があることを質問すること
    →企業研究が足りないと思われる
  • 回答しづらい内容の質問
    →回答ができなく、雰囲気が悪くなる

まとめ

面接でよく聞かれる質問を回答例をもとに解説していきました。1番大事なことは自分の経験や思いについて深堀をすることで何をそこから学んだのか、何が自身の強みなのか、それらを活かして会社に貢献できるかを面接官に伝える準備を行うことです。就職・転職を考えているエンジニアの方々の参考になれば幸いです。

プログラミングの心構え、若者よ努力してプログラムを好きになれ!【他人にやあされている練習を努力とは言わねだろう by メジャー吾郎】

プログラミング歴40年の田舎在宅プログラマーが感じたことを発信していきます

アクティブ・ラーニングについて https://menta.sutaruhin.com/?p=3242

メジャー公式サイト https://www.shopro.co.jp/tv/major2nd/

デスノート http://www1.ntv.co.jp/deathnote/character/index.html

Re.ゼロ公式サイト http://re-zero-anime.jp

一部抜粋いたしましたが、上記をクリックし詳細をご確認ください

2016年から6年間CodeCamp講師でしたが本日解除されました。くそお世話になりました【サンジ風】

悲しいお話ではありません。プログラミングスクールへの参画を数社とこちらのCodeCampさんにお世話になっておりました。最近はCodeCampさんの生徒さんが著しく少なくなってきましたので、またコンビニ並の単価だったのでここ数年は幽霊講師でした。先月アンケートで「昨年度の運営利益の用途」と「カリキュラムの問題」の指摘をさせて頂き、対策案をご意見をさせていただきましたところ契約続行しないということになりました。致し方ないかなと感じております

しかし、いきなり講師間の連絡であるslackが閉鎖されたのでご挨拶も行えず、継続講習の問題発生時の相談窓口も閉鎖されたので触れてはいけないことだったんだー。と切羽詰まっている現状を知ることができました。

本当に先生方や、生徒様いままで大変お世話になりました

がんばって躍進されてください。ご健康をお祈りいたします。

CodeCamp様も益々のご発展をお祈りいたします。お世話になりました。

※数年前までは、時給を倍にするからこの時間は講師をお願いしますと言われていたのですが。懐かしい思い出です(当時はびっくりしてました)

マンツーマン講義はチケット制度ですが、若い先生が多くはいられてますので是非、下記クリックより無料相談を行ってご検討ください。

マルチ認証の実現方法【Laravel6:User/Admin分離ログイン】

前提知識

  • Laravel6が使える
  • LaravelのAuth認証を実装した事がある

php artisan make:authは別記事を見てください

参考ソース:Laravel6-Multi-Login

ログイン機能を実装

Migration

まずはMigrationとModelをartisanコマンドを使って作成していきましょう
今回はサボらずに実戦でよく用いられるModelsディレクトリを用意します。

php artisan make:model Models/Admin -m

元々用意されているcreate_users_table.phpの中身をコピーして
作成したcreate_admins_table.phpに貼り付けましょう。database/migrations/2020_02_01_123456_create_admins_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateAdminsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('admins');
    }
}

Migrationを実行しておきましょう

php artisan migrate

これでテーブルの準備は出来ました。

Seeder

UserAdminでログインの検証がしたいので、ターミナルからコマンドを叩いて、それぞれテストユーザーを予め作っておきましょう

php artisan make:seeder UsersTableSeeder
php artisan make:seeder AdminsTableSeeder

Userdatabase/seeders/UsersTableSeeder.php

<?php

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('users')->insert([
            'name'              => 'user',
            'email'             => 'user@example.com',
            'password'          => Hash::make('12345678'),
            'remember_token'    => Str::random(10),
        ]);
    }
}

Admindatabase/seeders/AdminsTableSeeder.php

<?php

use Illuminate\Database\Seeder;

class AdminsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('admins')->insert([
            'name'              => 'admin',
            'email'             => 'admin@example.com',
            'password'          => Hash::make('12345678'),
            'remember_token'    => Str::random(10),
        ]);
    }
}

同時に実行出来るように元々用意されていうDatabaseSeeder.phpで設定します。database/seeders/DatabaseSeeder.php

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call([
            UsersTableSeeder::class,
            AdminsTableSeeder::class,
        ]);
    }
}

Seederを実行しておきましょう

php artisan db:seed

これでテストデータの準備は出来ました。

Model

先ほどのmigrationを作る工程のコマンドでModelsというディレクトリが作成されているはずなので、
最初から用意されているUser.phpModelsにぶち込んでおきましょう。

namespaceを変更するのを忘れずに!!app/Models/User.php

namespace App\Models;

別ファイルでUser.phpをuseしている箇所があるので、そちらも抜けなく変更しましょう。

下記ファイルのApp\User部分をApp\Models\Userに変更します。

  • app/Http/Controllers/Auth/RegisterController.php
  • config/auth.php
// App\User
App\Models\User

では管理者用のAdmin.phpも作成しておきましょう。

extendsがModelからAuthenticatableに変わっているので注意しましょう

app/Models/Admin.php

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class Admin extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

認証方式の追加(Guard)

それじゃAdminの認証方式を追加します。
この記事を読むと良いです。

Laravel の Guard(認証) って実際何をやっているのじゃ?

config/auth.php

<?php

return [

    // デフォルトの認証をwebからuserに変更
    'defaults' => [
        'guard' => 'user',
        'passwords' => 'users',
    ],

    'guards' => [
        'user' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        // Admin用の認証を追加
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ]
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ]
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],

        'admins' => [
            'provider' => 'admins',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ]
    ],

    'password_timeout' => 10800,

];

Controller

さて下の構成にするためにまずはControllers配下にAdminUserディレクトリを作成しましょう。

controllers
   ├── Admin
   │   ├── Auth
   │   │   ├── LoginController.php
   │   │   ├── RegisterController.php
   │   └── HomeController.php
   ├── User
   │   ├── Auth
   │   │   ├── LoginController.php
   │   │   ├── RegisterController.php
   │   └── HomeController.php
   └── Controller.php

作成できたら以下のコマンドをターミナルから入力してファイルを作成しましょう。
HomeControllerはLogin後の画面を出力するために使用します。

Admin

php artisan make:controller Admin/HomeController --resource

User

php artisan make:controller User/HomeController --resource

….完了したら元々あるAuthディレクトリをそれぞれの階層に設置してください!

Adminapp/Http/Controllers/Admin/HomeController.php

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class HomeController extends Controller
{

    public function __construct()
    {
        $this->middleware('auth:admin');
    }

    public function index()
    {
        return view('admin.home');
    }

}

Userapp/Http/Controllers/User/HomeController.php

<?php

namespace App\Http\Controllers\User;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class HomeController extends Controller
{

    public function __construct()
    {
        $this->middleware('auth:user');
    }

    public function index()
    {
        return view('user.home');
    }

}

Routing(ルーティング)

作成したControllerとパスを紐づける&Auth認証をそれぞれ指定します。routes/web.php

<?php

// ユーザー
Route::namespace('User')->prefix('user')->name('user.')->group(function () {

    // ログイン認証関連
    Auth::routes([
        'register' => true,
        'reset'    => false,
        'verify'   => false
    ]);

    // ログイン認証後
    Route::middleware('auth:user')->group(function () {

        // TOPページ
        Route::resource('home', 'HomeController', ['only' => 'index']);

    });
});

// 管理者
Route::namespace('Admin')->prefix('admin')->name('admin.')->group(function () {

    // ログイン認証関連
    Auth::routes([
        'register' => true,
        'reset'    => false,
        'verify'   => false
    ]);

    // ログイン認証後
    Route::middleware('auth:admin')->group(function () {

        // TOPページ
        Route::resource('home', 'HomeController', ['only' => 'index']);

    });

});

Auth

Laravel6.8以降から$redirectToのプロパティがRouteServiceProviderの定数で管理されるようになりました!今回はRouteServiceProviderの例を書いていきます!

参考
【Laravel6.8以降】認証関連のリダイレクト先の設定変更がDRYになったよ

RouteServiceProvider

ではデフォルトのリダイレクト先が/homeになっている部分を変更して、なおかつAdminのリダイレクト先を指定する定数も追加します。

Laravel6.8以下のバージョンの方はLoginControllerなどに用意されている$redirectToプロパティの値を変更してください。

app/Providers/RouteServiceProvider.php

// Userのリダイレクト先
public const HOME = '/user/home';

// Adminのリダイレクト先
public const ADMIN_HOME = '/admin/home';

Authenticate(未ログイン時の挙動)

未ログイン時にログイン認証が必要なページにアクセスした時のリダイレクト先を指定します。app/Http/Middleware/Authenticate.php

<?php

namespace App\Http\Middleware;

use Illuminate\Support\Facades\Route;
use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
    protected $user_route  = 'user.login';
    protected $admin_route = 'admin.login';

    protected function redirectTo($request)
    {
        // ルーティングに応じて未ログイン時のリダイレクト先を振り分ける
        if (!$request->expectsJson()) {
            if (Route::is('user.*')) {
                return route($this->user_route);
            } elseif (Route::is('admin.*')) {
                return route($this->admin_route);
            }
        }
    }
}

RedirectIfAuthenticated(ログイン時の挙動)

逆にログインしてる時に/loginにアクセスしてきた時のリダイレクト先を指定します。app/Http/Middleware/RedirectIfAuthenticated.php

<?php

namespace App\Http\Middleware;

use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->check() && $guard === 'user') {
            return redirect(RouteServiceProvider::HOME);
        } elseif (Auth::guard($guard)->check() && $guard === 'admin') {
            return redirect(RouteServiceProvider::ADMIN_HOME);
        }

        return $next($request);
    }
}

User

ログイン

app/Http/Controllers/User/Auth/LoginController.php

<?php

namespace App\Http\Controllers\User\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    use AuthenticatesUsers;

    protected $redirectTo = RouteServiceProvider::HOME;

    public function __construct()
    {
        $this->middleware('guest:user')->except('logout');
    }

    // Guardの認証方法を指定
    protected function guard()
    {
        return Auth::guard('user');
    }

    // ログイン画面
    public function showLoginForm()
    {
        return view('user.auth.login');
    }

    // ログアウト処理
    public function logout(Request $request)
    {
        Auth::guard('user')->logout();

        return $this->loggedOut($request);
    }

    // ログアウトした時のリダイレクト先
    public function loggedOut(Request $request)
    {
        return redirect(route('user.login'));
    }
}
新規登録

app/Http/Controllers/User/Auth/RegisterController.php

<?php

namespace App\Http\Controllers\User\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Auth;
use App\Models\User;

class RegisterController extends Controller
{
    use RegistersUsers;

    protected $redirectTo = RouteServiceProvider::HOME;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest:user');
    }

    // Guardの認証方法を指定
    protected function guard()
    {
        return Auth::guard('user');
    }

    // 新規登録画面
    public function showRegistrationForm()
    {
        return view('user.auth.register');
    }

    // バリデーション
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name'     => ['required', 'string', 'max:255'],
            'email'    => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }

    // 登録処理
    protected function create(array $data)
    {
        return User::create([
            'name'     => $data['name'],
            'email'    => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
}

Admin

基本的に上のuserの部分をadminに直すだけです。
なのでコメントは外します。

ログイン

app/Http/Controllers/Admin/Auth/LoginController.php

<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    use AuthenticatesUsers;

    protected $redirectTo = RouteServiceProvider::ADMIN_HOME;

    public function __construct()
    {
        $this->middleware('guest:admin')->except('logout');
    }

    protected function guard()
    {
        return Auth::guard('admin');
    }

    public function showLoginForm()
    {
        return view('admin.auth.login');
    }

    public function logout(Request $request)
    {
        Auth::guard('admin')->logout();

        return $this->loggedOut($request);
    }

    public function loggedOut(Request $request)
    {
        return redirect(route('admin.login'));
    }
}
新規登録

app/Http/Controllers/Admin/Auth/RegisterController.php

<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Auth;
use App\Models\Admin;

class RegisterController extends Controller
{
    use RegistersUsers;

    protected $redirectTo = RouteServiceProvider::ADMIN_HOME;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest:admin');
    }

    protected function guard()
    {
        return Auth::guard('admin');
    }

    public function showRegistrationForm()
    {
        return view('admin.auth.register');
    }

    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name'     => ['required', 'string', 'max:255'],
            'email'    => ['required', 'string', 'email', 'max:255', 'unique:admins'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }

    protected function create(array $data)
    {
        return Admin::create([
            'name'     => $data['name'],
            'email'    => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
}

View

viewは以下の構成で実装します。

views
├── user
│   ├── auth
│   │   ├── login.blade.php
│   │   └── register.blade.php
│   └── home.blade.php
│
├── admin
│   ├── auth
│   │   ├── login.blade.php
│   │   └── register.blade.php
│   └── home.blade.php
│
└── layouts
    ├── user
    │    └── app.blade.php
    │
    └── admin
         └── app.blade.php
layouts

Userresources/views/layouts/user/app.blade.php

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav mr-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ml-auto">
                        <!-- Authentication Links -->
                        @unless (Auth::guard('user')->check())
                            <li class="nav-item">
                                <a class="nav-link" href="{{ route('user.login') }}">{{ __('Login') }}</a>
                            </li>
                            @if (Route::has('user.register'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('user.register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('user.logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('user.logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endunless
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

Admin

こっちも基本的にuseradminに変えてるだけresources/views/layouts/admin/app.blade.php

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav mr-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ml-auto">
                        <!-- Authentication Links -->
                        @unless (Auth::guard('admin')->check())
                            <li class="nav-item">
                                <a class="nav-link" href="{{ route('admin.login') }}">{{ __('Login') }}</a>
                            </li>
                            @if (Route::has('admin.register'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('admin.register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('admin.logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('admin.logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endunless
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

View(ログイン)

スクリーンショット 2020-02-20 16.51.54.png

先ほどSeederで登録した情報でログインできます

Userresources/views/user/auth/login.blade.php

@extends('layouts.user.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('user.login') }}">
                        @csrf

                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>

                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <div class="col-md-6 offset-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>

                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Login') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Adminresources/views/admin/auth/login.blade.php

@extends('layouts.admin.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('admin.login') }}">
                        @csrf

                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>

                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <div class="col-md-6 offset-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>

                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Login') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
View(新規登録)
スクリーンショット 2020-02-20 16.52.39.png

Userresources/views/user/auth/register.blade.php

@extends('layouts.user.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Register') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('user.register') }}">
                        @csrf

                        <div class="form-group row">
                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>

                            <div class="col-md-6">
                                <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>

                                @error('name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">

                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>

                            <div class="col-md-6">
                                <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Register') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Adminresources/views/admin/auth/register.blade.php

@extends('layouts.admin.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Register') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('admin.register') }}">
                        @csrf

                        <div class="form-group row">
                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>

                            <div class="col-md-6">
                                <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>

                                @error('name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">

                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>

                            <div class="col-md-6">
                                <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Register') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

View(ログイン後の画面)

スクリーンショット 2020-02-20 16.53.24.png

Userresources/views/user/home.blade.php

@extends('layouts.user.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Dashboard</div>

                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

                    You are logged in!
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Adminresources/views/admin/home.blade.php

@extends('layouts.admin.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Dashboard</div>

                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

                    You are logged in!
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

ご活用ください

プログラマーがスキルアップするための「伝える力」と「質問力」

 ITの世界は同調の世界です。会社員であれば上司や先輩、同僚に、フリーランスであれば仕事のオーナーやリーダまたは同じフリーランス要員などチームでシステムを作り上げていきます。そのなかのメンバーとして自分の理解度を正しく伝え、質問することによりスキルアップできると主に全体のシステム進捗アップを行って効率よく進めていきます。よって日常は指示を仰ぐ、意見をもらうなど、職場は毎日「質問」であふれています。質問を上手に使いこなすことで、チームでのコミュニケーションが良好になったり、仕事を円滑に進められたりするなど、好循環が生まれます。逆に質問は相手の貴重な時間を奪っていることを最も考えなければいけないことなのです。そこで今回は「質問力」を磨くポイントやテクニックをご紹介します。質問力アップの参考にしてフルスタックエンジニアを目指してみてください。

「質問力」アップで得られるメリット

質問とは「わからないところや疑わしい点について問いただすこと。また、その内容」。それ以外にも、円滑なコミュニケーションによって仕事の効率が上がったり、新しい情報を知ることで自身の成長に繋げられたり、質問によってさまざまなことを得ることができます。「質問力」が持つ力を知れば、あなたもきっと磨きたくなるはずです。

メリット1. 疑問が解消されスキルアップできる

質問することで得られる代表的なものは、不明だったことが明確になったり、新たな情報を得られることです。また質問力がつけば、インフラなど専門外の分野についても質問できるようになり、それによって得られた情報を理解・習得していくことができます。さらに、頭の中で考えていることを言葉にすることで、質問した人、質問された人双方の考えが整理され、情報共有を図りやすくなります。

メリット2. 思い込みが取り払われる

誰しも、多かれ少なかれ持っている思い込み。とりわけ、ネガティブな思い込みは行動を制限してしまいます。または仕様の勝手な思い込みでチームのメンバーに大きな代償となってのしかかる危険もあります。誰かに問いかけたり会話したりすることで、その思い込みには実は根拠がないことに気づくことができます。自分の論理を他人へ話すことにより指摘で気付かされることや、自分の矛盾に気づくことで早期に悪い芽が刈り取ることができるようになっていきます

メリット3. 相手との心の距離を縮められる

質問には、質問をする側と質問をされる側をフラットな関係にする力があります。例えば、上司からの指示・命令について部下が質問することで、上司に気づきを与えるきっかけになるなど、一方通行のコミュニケーションから双方向のコミュニケーションに変化させる可能性を持っています。また、相手の得意分野や関心ごとなどについて質問をすることで興味や好意が伝わり、「私に興味を持ってくれている!」と感じてもらうことができます。大切なスキンシップなのです。

メリット4. 人脈に変えられる

このスキルならばこの人が一番詳しいや素直に質問する姿勢し理解することで仲間意識が生まれます。Aさんとチーム組むと質問の相乗効果で進捗や品質が良くなると思われるとめっけ物です。何年も間が開こうが仲間なのです。フリーランスであれば困ったときに助けてもらえます。人は宝なのです。

仕事がうまく回り出す!質問力を上げるポイント&テクニック

ちょっとしたポイントを意識すれば、誰でも「質問力」を上げることが可能です。良い質問は会話を盛り上げるだけでなく、新しい知識や情報を手に入れるチャンスにもなります。質問力を上げて、開発チーム内での良好な関係づくりやスキルアップに生かしましょう!

ポイント1.「傾聴の姿勢」を大切にする

質問する時には、相手への興味や尊敬の念が欠かせません。うなずきながら、目を見ながら、笑顔で安心感を与えながら聞く、などが質問する時のマナーです。また、何度も同じ質問をすることも失礼になるので、聞いたことをきちんとメモするのも忘れずに。

ポイント2.オウム返しを巧みに使う

相手の発言をそのまま繰り返す「オウム返し」。これにより、相手の話に関心を持っていることが伝わり、そこから会話を深めるきっかけになります。業務にまつわる話の場合は、その内容を別の言葉で言い換えて確かめることで、認識のズレやミスを防ぐことができます。ただし会話に限りますslackなどチャットツールで行うと「こいつわかってないな。仕様の根本から話さないといけないから近づくのはやめよう」と逆効果。即刻クビもありえますのであくまでも会話テクニックのみでお使いください。

ポイント3.ネガティブな質問はしない

「おっしゃっている意味がわかりません」「それは必要な作業でしょうか?」など、相手の考えや発言を打ち消すような質問はNGです。相手のやる気を削ぎ、本来望んでいる解決が遠のいてしまいます。相手の言っていることが理解できない場合は、「それはこういう理解で良いでしょうか?」など、確認する質問に置き換えるようにしましょう。

ポイント4.相手に答えを丸投げしない

「どうすればいいですか?」など、相手にだけ考えさせるような受け身の質問は好ましくありません。答えだけわかれば良い。このような考えから発せられる言葉です。調教できない人材のレッテルがその瞬間はられます。「この問題に対して、こう対応しようと思っていますが、ご意見をお聞かせいただけますか?」など、自分なりの仮説を持って、それに対する意見を聞くのが良いでしょう。また、その問題の経過や結果の報告を忘れないことも大切です。ゆとり世代は根本から考え直してください。日本人は受ける教育で育ってます。「でどうなんですか?」筆者は実際いまでもたくさん浴びせさせれてます。質問を受ける側の気持ちなど微塵も感じない人間にだけにはならないでください。このキーワードもフリーランスであれば即刻クビです。

ポイント5.クローズドな質問とオープンな質問を使い分ける

答えが限定される「クローズドな質問」は余計な話をしてほしくない場合や、シンプルにイエスかノーかだけを知りたい場合に役立ちます。逆に、相手に自由に答えてもらう「オープンな質問」は、自分では答えを予想できない時や自由な意見がほしい時に使いましょう。さらに、5W1H(※)を含めた問いかけにすると、より具体的な答えを得ることができます。2種類の質問を、上手に使い分けるようにしましょう。
※5W1HとはWho(だれが)When(いつ)、Where(どこで)、What(なにを)、Why(なぜ)、 How(どのように)を指し示すビジネス用語。

ポイント6.ネタに困ったら「木戸に立ちかけし衣食住」

初対面の人などと話す時、共通の話題がすぐに見つからない場合は、会話をはじめるきっかけに「木戸に立ちかけし衣食住」というキーワードを覚えておくと便利です。「き」は季節や気候、「ど」は道楽や趣味、「に」はニュース、「た」は旅や休日、「ち」は知人や地域、「か」は家族やペット、「け」は健康、「し」は仕事、そして「衣食住」にまつわる質問です。ただし、プライバシーに踏み込みすぎた質問には注意しましょう。

「質問力」を上げて、仕事効率も人間関係も良好に

質問というと、「分からないことを聞く」と捉えがちですが、目的を持った問いに変化させることで、得られる情報の量や質が変わります。また、相手や自分のマインドを良い方向へと導くこともできます。質問することに苦手意識がある人はプログラミの世界には向いていません。誰でも最初は苦手です。しかし、日々 “良い質問”を意識して使うことで質問する力はどんどん磨かれていきます。質問力が上がれば、今よりもっと仕事がしやすくなり自分のスキルアップで価値がアップしていきます。転職は引く手あまた。フリーランスは営業しなくてもひっきりなしに連絡が途切れなく入ってきます。成功すること間違いなし! ぜひ、今日から“質問力磨き”をはじめましょう。

2021.08.21 GitHubでhttpsのパスワード認証が廃止されました。Please use a personal access token instead.

作業中にgit pushしたところいままでうまく言っていたのですが下記エラーが発生しました。

$ git push origin main
remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information.
fatal: unable to access 'https://github.com/名前/リポジトリ.git/': The requested URL returned error: 403

Please use a personal access token instead.
訳: 代わりにパーソナルアクセストークンを使用してください。

事件です。GitHubで2021年8月13日にパスワード認証が廃止されたそうです。
悔しいですね。個人アクセストークンを使うようにしましょう

GitHub 個人アクセストークンを作成する

GitHubにアクセスするとき必要となる、個人アクセストークンを作成していきます。

個人アクセストークンとは、「コマンドラインでのGit操作」や「Gitを使用するアプリケーション」「Gitリポジトリに直接アクセスするアプリケーションやサービス」を利用するときのパスワードの代わりになるものです。GitHubは、2021年8月13日以降、Git操作におけるパスワード認証を廃止し、トークンベースの認証を必須にする方針を発表しています。 これにより、パスワードを用いてのアクセスができなくなってしまいます。したがって、この操作は必ず行っておきましょう。セキュリティの観点から、GitHubは、過去1年間使用されていない個人アクセストークンは自動的に削除します。削除された場合は、再度作成する必要があります。

トークンの作成

1. GitHubのWebページにアクセスし、右上にあるプロフィールの画像をクリックします。

2. メニューからSettingsをクリックします。

3. 左サイドバーからDeveloper settingsをクリックします。

4. 続いて、左サイドバーからPersonal access tokensをクリックします。

5. Generete new tokenをクリックします。

6. 作成するトークンにわかりやすい名前をつけ、このトークンに付与するスコープとして、repogistを選択します。なお、選択したrepo は、このトークンを使用してコマンドラインからリポジトリにアクセスすることができる権限です。では、Generate token をクリックして実際に作成します。

7. 作成されたトークンをメモとして残しておき、忘れないようにしてください。メモを書き間違うことが心配な場合は、スクリーンショットを撮っておくと良いでしょう。

なお、この画面はセキュリティ上の理由で、別ページに移動すると、再度見ることができなくなるので、注意しておいてください。
今回作成したトークンは、ターミナルを使ってコマンドラインからGithubのリポジトリにアクセスする際にパスワードとして使用することになります。

GitHubへリポジトリを反映するための一連の流れ

GitHubへリポジトリを反映させるためには、下記の手順で実行しましょう。

  1. GitHubにリモートリポジトリを作成する
  2. ローカルリポジトリにリモートリポジトリを登録する
  3. ローカルリポジトリのブランチ名を変更する
  4. 登録したリモートリポジトリへプッシュする
  5. GitHubのリモートリポジトリにプッシュされたか確認する

GitHubにリモートリポジトリを作成する

first-gitという名前でGitHubにリモートリポジトリを作成して、公開しましょう。

まずはGitHubへアクセスし、ログインしてください。次に+ボタンから「New repository」をクリックしましょう。

GitHubへリモートリポジトリを作成します。「Repository name」にfirst-gitと入力して、「Create repository」をクリックしてください。

これでリモートリポジトリが作成されましたが、まだ中身は空っぽです。ローカルリポジトリをリモートリポジトリにもコピーして反映させるためにプッシュというコマンドが必要となります。まずはその準備のためにローカルリポジトリにリモートリポジトリを登録していきます。

ローカルリポジトリにリモートリポジトリを登録する

ローカルリポジトリにリモートリポジトリを登録するには、先ほどのキャプチャ内にあった下記のコマンドを実行します。

git remote add origin https://github.com/ユーザ名/first-git.git

このコマンドは、ローカルリポジトリにoriginという名前でリモートリポジトリ(ここではfirst-git.git)を登録するという意味です。ユーザ名には皆さんのGitHubでのユーザ名を入力してください。

これでローカルリポジトリにリモートリポジトリを登録できました。以降、このローカルリポジトリでは、originを使用してリモートリポジトリとやり取りを行うことができます。したがって、git remote add origin ...のコマンドは何度も実行する必要はありません。

リモートリポジトリの登録状況を確認したい場合は下記のコマンドで確認することができます。

git remote -v

ローカルリポジトリのブランチ名を変更する

次に、ローカルリポジトリのブランチ名をmasterからmainに変更します。これは、5.4 ブランチのデフォルトのブランチ名で解説したとおり、リモートリポジトリのデフォルトブランチ名(main)とローカルリポジトリのデフォルトブランチ名(master)が異なっているためです。

git branch -M main

上記コマンドを実行すると、これまで(master)とターミナルに表示されていましたが、(main)に変更されます。

Macの方の注意事項です

macOS キーチェーンからの認証情報を削除する必要があります

git credential-osxkeychain erase

下記は参考サイトです

https://docs.github.com/ja/github/getting-started-with-github/getting-started-with-git/updating-credentials-from-the-macos-keychain

わかりにくければ、下記のほうが簡単に書かれてます

https://zenn.dev/hayata_yamamoto/articles/github-access-key

conoHaでLaravelデプロイ方法

プロダクト開発の概要
今回のプロダクト開発では、フレームワークを利用して開発したWebアプリケーションを公開するための一連の流れについて学習します。
Conoha Wing の設定
今回は共用のレンタルサーバーを利用した Web アプリケーションの公開を行います。
レンタルサーバーとは、1台の Web サーバーを複数人で共有し安価に Web サーバーを利用する仕組みです。
第1章では Conoha WING と呼ばれる レンタルサーバーのホスティングサービスを利用し、基礎的な設定を行います。
ネットワークとセキュリティの基礎
Web サーバーを利用する際には、ネットワークに関する基礎的な知識が必要になります。
各種設定を単なるおまじないではなく、どの様な意味があり、何のために実行しているのかを理解できる様になるため、初歩のネットワーク知識を身につけていきましょう。
また、セキュリティに関連する設定方法についても学んでいきます。
Linux の基本コマンド
レンタルサーバーを操作する時には、 ssh と呼ばれるものを利用して、遠隔でサーバーにアクセスし Linux の各種コマンドを実行する必要があります。
Linux の基本コマンドを理解し、サーバー操作の基本を身につけましょう。
バージョン管理とGit
継続的に Web アプリケーションの開発を行い、適宜最新のものを公開したり、不具合があった場合に過去のバージョンに戻ったりするためには、バージョン管理用のソフトウェアを利用する必要があります。
バージョン管理用ソフトウェアのデファクトスタンダードである Git を利用し、バージョン管理の基本を学びます。
リポジトリからの環境構築
Git を利用して保存されたアプリケーションの履歴データを「リポジトリ」と呼びます。
GitHub と言うサービスを利用してクラウド上にアップロードされている既存のリポジトリを、ローカルにダウンロード(クローン)して、開発環境を整えるための基本的な手順を学びます。
既存サイトのセキュリティ修正
Web アプリケーションを公開する際には、各種のセキュリティに配慮する必要があります。
どの様なセキュリティ項目に配慮する必要があり、具体的にどの様に対策すれば良いのかを学びます。
コードの品質
公開した Web アプリケーションは、その後メンテナンスを繰り返し継続的に運用されていきます。
非効率で品質の低いコードは、一見要件を満たしている様に見えても、
問題が発生した際に、どの部分のコードが問題になっているのか判別しにくい
1箇所を修正するたびに複数の関連箇所を修正する必要がある
効率的なコードに比べて動作が著しく遅い
などの問題が発生しやすくなります。
高い品質のコードとはどの様なものかを学びます。
デプロイ方法
リポジトリ上に構築された Web アプリケーションをレンタルサーバー上にダウンロードして公開するためには、一連のプロセスで順序よく設定を行う必要があります。
公開(デプロイ)のための一連の操作方法を学びます。
最終課題・オリジナルサービスのデプロイ
ここまで学んだ内容を利用して、要件に従ってリポジトリ上にオリジナルサービスを構築し、デプロイしていただきます。

この章では

この章では、レンタルサーバーの契約・セットアップの方法と、動作確認を行います。

(なお、今回紹介するレンタルサーバーの契約は有料になります。学習の際に、実際にレンタルサーバーの契約を行って学習するかどうかは受講者様の任意となり、終了の要件とはなりません。)

学習の全体像

レンタルサーバー とは

レンタルサーバーとは、運営会社が保有するサーバー(Webサーバーがインストールされたマシン)の利用権を貸し出すサービスで、ホスティングサービスとも呼ばれます。

レンタルサーバーには共用サーバーと専用サーバーという契約形態があります。

専用サーバーと共用サーバー

共用サーバーとは、一つのサーバーを複数人で利用する契約形態で、比較的安価に利用できると言う特徴があります。安価な代わりに、権限を与えられた一部の設定項目を除いて、サーバーの設定は予め設定された内容から変更できない、他のユーザーのサービスの稼働率が高い場合に自分のサービスの動作に影響する可能性があるなどの欠点があります。

専用サーバーとは、一つのサーバーを占有する契約形態で、料金は共用サーバーに比べて高くなりますが、管理者権限を利用してサーバーの設定変更が可能であり、他のユーザーの影響を受けない、などのメリットがあります。

今回のテキストでは、比較的安価な共用サーバーを利用して Web アプリケーションのデプロイ方法を学びます。

Conoha WING とは

レンタルサーバーとして、今回は Conoha WING を利用します。

Conoha WING は GMOインターネット株式会社の提供するサービスの一つで、WINGパックプランを利用すると独自ドメインの取得がセットになると言う特徴があります。

Conoha WING は まず Conoha サービスにアカウント登録したうえで、コントロールパネルから利用したいサーバーを追加し、契約を行います。

Conoha WING のサーバー詳細

Conoha WING で利用できるサーバーの詳細は以下の通りです。(Wingパック、ベーシックプラン)

ディスク容量(SSD)
OSCloudLinux
WebサーバーApache + nginx
サイト数無制限
ドメイン無制限
データベースMySQL
データベース数無制限

Conoha WING の料金について

Conoha WING は有料のサービスです。

利用料金は利用するプランと支払い方法に応じて増減します。

詳しくは下記のページをご参照ください。

https://www.conoha.jp/wing/pricing/?btn_id=wing-glonavi_pricing

以降の説明では、WINGパックのご契約を前提に解説を行います。

Conoha WING 登録の流れ

Conoha WING における登録の流れについて解説を行います。

1. Conoha にアカウントを登録する

https://www.conoha.jp/wing/?btn_id=top_wing

上記のリンクの「ログイン」ボタンをクリックすると、以下の画面に遷移します。

ログイン画面

「新規アカウント登録はこちら」のリンクをクリックしましょう。

新規登録画面

以降、画面の指示に従い、会員登録を済ませていきましょう。

Conoha にログインする

会員登録が済んだら、再度以下のリンクから Conoha にログインしましょう。

https://www.conoha.jp/wing/?btn_id=top_wing

ログインが完了すると、Conohaのダッシュボード画面が表示されるので、WING のタブを表示させましょう。

サーバーの追加

Conoha ダッシュボード

追加ボタンをクリックし、サーバーの追加画面に遷移します。

サーバー追加画面

ご希望の契約期間を選択し、任意の初期ドメイン名を入力して「続ける」ボタンをクリックしましょう。

なお、無料独自ドメインを利用できるのは3ヶ月以上の契約期間が前提となるWINGパックのみですのでお気をつけください。

お支払い画面

お支払い画面で支払い方法を選択し、決済ボタンをクリックすると、支払いが行われサーバーが追加されます。

独自ドメインの取得を促すポップアップが表示される場合、現時点では取得しないようにしておきましょう。

SSH とは

SSH とは Secure SHell(セキュアシェル) の略で、ネットワーク上のマシンにアクセスして安全に操作するための仕組みです。

SSH を利用することで、自分が直接操作しているコンピュータ(以下、クライアント)から、安全(Secure) にサーバーを操作できます。

通信の暗号化

SSH ではクライアントとサーバーの間の通信が暗号化されます。これにより、サーバーへのログイン情報など、重要な情報が外部に漏れることを防いでいます。

認証方法

SSH でサーバーにログインする際の認証方法は、大きく分けて

  • パスワード認証方式
  • 公開鍵認証方式

の2種類があります。

公開鍵認証方式では、キーペアと呼ばれる2つのファイルを生成します。このキーペアは漏洩しても問題ない「公開鍵」と呼ばれるファイルと「秘密鍵」と呼ばれる秘匿すべきファイルで構成されます。

公開鍵をサーバーに、秘密鍵をクライアントに保管します。秘密鍵を持っているのは正式なユーザーのみのため、サーバーにログインする際にサーバー側の公開鍵とクライアント側の秘密鍵が一致していれば認証完了です。

秘密鍵ファイルを保持しているユーザーのみログインでき、またその仕組みによって秘密鍵そのものを送信する必要がなくなっている公開鍵認証方式は、パスワードを知っていれば誰でもログインでき、パスワードが盗み見られる可能性があるパスワード認証方式に比べて、より安全であると言われています。

Conoha WING における SSH

Conoha WING ではこの公開鍵認証方式を利用してサーバーにログインする仕組みになっています。

次節で、SSHを利用した具体的なサーバーへのアクセス方法について確認します。

FTPクライアントの使い方

Conoha WING へのファイルの送受信には FTP クライアントを利用するのが便利です。

FTP クライアントとは

FTP クライアントとは、FTP(File Transfer Protocol)と言う仕組みを利用して、ファイルの送受信を行うためのソフトウェアです。

このテキストでは、フリーで利用できる FTP クライアント FileZilla を利用したファイルの送受信方法をお伝えします。

FileZilla のダウンロード

FileZilla プロジェクト日本語トップページ

上記のページから FileZilla をダウンロードし、インストールしてください。

ssh キーの生成

SSHキーの生成1

画像の指示に従い、「+SSH Key」のボタンをクリックしてください。

SSHキーの生成2

新しく作成する SSH キーペアに任意の名前を設定し、保存ボタンをクリックしてください。

ssh キーをダウンロード

保存ボタンをクリックすると、以下のウィンドウが表示されます。

ssh キーをダウンロード

「ダウンロード」ボタンをクリックして、秘密鍵をダウンロードします。

ダウンロードしたファイルは、紛失すると該当のキーペアによる接続ができなくなります。

紛失しない様、大切に保管しておきましょう。

ユーザーディレクトリ直下に .ssh というフォルダを作成してその中に保管するのが一般的です。

また、保存場所に移動後、ssh キーファイルのプロパティを開き、保存場所のパスを確認してメモしておきましょう。

ssh 情報の確認

ダウンロード後、該当のキーをクリックするとそのキーの詳細情報が確認できます。

ssh 情報の確認

FileZilla による接続で利用するので、この画面を開いたままにしておきましょう。

FileZilla で サーバーに接続

必要な設定が揃ったので、FileZilla を利用してサーバーに接続しましょう。

FileZillaのウィンドウ左上、サイトマネージャーを開いて以下の様に設定します。

FileZillaで接続

接続ボタンをクリックして、サーバー内のファイル、フォルダの一覧が表示されれば成功です。

接続成功

エラーが発生する場合は、接続情報の設定に誤りがないか確認してみましょう。

サンプルファイルの作成

FileZillaでの接続に成功したら、サンプルファイルをアップロードして動作確認を行いましょう。

index.php12<?phpecho 'Hello, WING!';

上記のファイルをローカルのエディタで作成もしくは cloud9 上で作成してダウンロードしておきます。

FileZilla でアップロード

右ペインで home/[ユーザー名]/public_html/[ドメイン名] となっているフォルダを選択します。

左ペインで、先ほど準備した index.php を選択して右クリックし、コンテキストメニューからアップロードを選択します。

成功すると、index.php が右ペインに現れます。

動作確認

アップロードが完了したら、Webブラウザから、該当のドメイン

http://設定したサブドメイン名.conohawing.設定したドメイン

にアクセスしましょう。

動作確認

上記の様に表示されれば動作確認はバッチリです。

データベースの動作確認

つづいて、データベースの動作確認を行いましょう。

データベースの作成

データベースの作成は Conoha ダッシュボードから行います。

サイト管理 > データベースに移動しましょう。

データベースの作成1

データベース名を「[あらかじめ設定された文字列]_sample」で作成しておきましょう。

ユーザーの作成

続いて、データベースを利用するユーザーを作成します。

ユーザーの作成1

ユーザー名、パスワードを設定し、データベースのネームタグを指定したら「保存」をクリックします。

phpMyAdmin にログインする

作成したユーザーを選択し、 phpMyAdmin のリンクをクリックしましょう。

phpMyAdminにログイン

作成したユーザー名・パスワードでログインします。

作成したデータベースを選択し、テーブル messages を以下の設定で作成します。

messages を作成

ひとこと掲示板の動作確認

以下のサンプルを作成して、前回 index.php をアップロードした home/[ユーザー名]/public_html/[ドメイン名] のフォルダにアップロードしましょう。

上部のデータベースの接続情報部分には Conoha の管理画面から

  • 接続先ホスト
  • データベースユーザ名
  • データベースのパスワード
  • データベース名

を確認して入力してください。

bbs.php

<?php
$host     = '接続先ホスト'; // 接続先ホスト
$username = 'データベースユーザ名';   // データベースユーザ名
$password = 'データベースのパスワード';       // データベースのパスワード
$dbname   = 'データベース名';   // データベース名
$charset  = 'utf8';   // データベースの文字コード 
$messages = [];$errors = []; // MySQL用のDSN文字列
$dsn = 'mysql:dbname='.$dbname.';host='.$host.';charset='.$charset;
   if($_SERVER['REQUEST_METHOD'] === 'POST'){
   $name = '';  if(isset($_POST['name']) === true){
    $name = $_POST['name'];  }   $comment = '';
  if(isset($_POST['comment']) === true){
    $comment = $_POST['comment'];  
}
   if($name === ''){
    $errors[] = '名前を入力してください';
  }
  if(mb_strlen($name) > 20){
    $errors[] = '名前は20文字以内で入力してください';
  }
   if($comment === ''){
    $errors[] = 'コメントを入力してください';
  }
  if(mb_strlen($comment) > 100){
    $errors[] = 'コメントは100文字以内で入力してください';
  }
} try {
  // データベースに接続
  $dbh = new PDO($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4'));
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
   if($_SERVER['REQUEST_METHOD'] === 'POST' && count($errors) === 0){
    try {
      $sql = 'INSERT INTO messages(name, comment) VALUES(?, ?)';
      // SQL文を実行する準備
      $stmt = $dbh->prepare($sql);
      $stmt->bindValue(1, $name, PDO::PARAM_STR);
      $stmt->bindValue(2, $comment, PDO::PARAM_STR);
      // SQLを実行
      $stmt->execute();
    } catch (PDOException $e) {
      echo '書き込みできませんでした。理由:'.$e->getMessage();
    }
   }
   // SQL文を作成
  $sql = 'SELECT name, comment, created from messages';
  // SQL文を実行する準備
  $stmt = $dbh->prepare($sql);
  // SQLを実行
  $stmt->execute();
  // レコードの取得
  $messages = $stmt->fetchAll();
 } catch (PDOException $e) {
  echo '接続できませんでした。理由:'.$e->getMessage();
}

 function h($string){
  return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
?>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>一言掲示板</title>
  </head>
  <body>
    <?php foreach($errors as $error){ ?>
      <p><?php print h($error); ?></p>
    <?php } ?>
    <form method="POST">
      <label for="name">名前: </label>
      <input type="text" id="name" name="name">
      <label for="comment">コメント: </label>
      <input type="text" id="comment" name="comment">
      <input type="submit" value="投稿する">
     </form>
    <ul>
      <?php foreach($messages as $message){ ?>
        <li>
          <?php print h($message['name']); ?>: 
         <?php print h($message['comment']); ?>: 
         <?php print h($message['created']); ?> 
       </li>
      <?php } ?>
  </body>
</html>

http://[設定したサブドメイン名].conohawing.[設定したドメイン]/bbs.php

にアクセスして、掲示板に書き込めれば、データベースは正しく動作しています。

まとめ

この章では、Conoha WING の利用開始と基本的な使い方について学びました。

レンタルサーバーは、基本的な設定が予め行われているため、スピーディに利用開始できます。

ssh と データベース の設定を理解しておくことでスムーズに初期設定を済ませられるので復習しておきましょう。

ネットワークとセキュティ

この章ではネットワークとセキュリティの基礎知識について学びます。

Webサーバーを利用する際には、ネットワークに関する基礎的な知識が必要になります。

各種設定を単なるおまじないではなく、どのような意味があり、何のために実行しているのかを理解できるようになるため、初歩のネットワーク知識を身につけていきましょう。

また、セキュリティに関連する設定方法についても学んでいきます。

TCP/IPとは

インターネット上でのデータのやりとりに使われるプロトコル(規約、取り決め)がTCP/IPと呼ばれるものです。

パケット通信

TCP/IPの仕組みを理解するためにまずデータの送受信はパケット通信という仕組みで行われていることを理解する必要があります。データをネットワーク上を通じて送信する場合、巨大なファイルをそのまま送信することはできないため、パケットと呼ばれる小さな塊に分割して送信することになります。

パケット(小包)と呼ばれるのは、各パケットがデータの小さな塊と、データの宛先との組み合わせでできていることを意味しています。宛先の書かれた小包によく似ていますね。

パケット通信

TCP

Transmission Control Protocol の略で、データの送受信を制御するためのプロトコルです。パケットは一つの大きなファイルを分割したものです。一つの大きなファイルをどう分割するか、また分割されて送られてきたパケットをどうやって元のファイルに戻すのかが TCP で決められています。

IP

Internet Protocol の略で、ネットワーク間のプロトコルです。一つ一つのパケットをどのような形式にするかを決めているのが IP というプロトコルです。データのどの部分に宛先を示すデータを書くか、宛先はどのような形式で書くかなどが決められています。

まとめ

上記の TCP(ファイルをパケットに分割するときのルール)とIP(パケットを送信するときのルール)を合わせて TCP/IP と呼ばれます。特に IP と言う言葉は耳にする機会が多くなるので意味合いを確認しておきましょう。

サーバーとは

インターネットの仕組みの最大の特徴は「クライアントサーバーシステム」であるということです。

普段何気なく使っている「サーバー」という言葉が本質的に何を表しているのかを学んで行きましょう

クライアントサーバーシステム

インターネットがどういう仕組みであるかを正しく理解するために重要なのはこのクライアントサーバーシステムという概念です。

クライアントサーバーシステムとは文字通り「クライアント」と「サーバー」によって構成されるシステムです。

クライアントサーバーシステム

サーバーの由来

サーバー(Server)とは元々英語で「給仕人」のことで、いわゆる執事をイメージしていただくと良いでしょう。24時間不眠不休でクライアントから要求(リクエスト)を出されるのを待ち続けるプログラムです。

サーバーは普段は待ち受け状態ですが、リクエストを受けると必要な仕事を行い、その結果をクライアントに返信(レスポンス)します。

クライアントとは

クライアントとは「依頼人」のことで、いわゆるご主人様をイメージすると良いでしょう。クライアントは必要に応じてサーバーにリクエストを行い、返ってくるレスポンスを受け取ります。

インターネットアプリケーションの基本はクライアントサーバーシステム

この、クライアントサーバーシステムがインターネット上のアプリケーションの基本です。

例えばWebの仕組みの場合にはクライアントに当たるのはWebブラウザです。

例えばChromeのようなWebブラウザにURLを入力すると、該当するサーバーにリクエストが送られて、結果としてWebページのデータがレスポンスとして返って来ます。

この時、あらかじめ用意してあるファイルをそのままレスポンスとして渡すのがWebページ、サーバー上のデータを操作して、その結果をレスポンスとして渡すのがWebアプリケーションということになります。

サーバーという言葉が意味するもの

サーバーは元々上記の様な働きをするプログラムを指し、別名デーモンとも呼ばれます。Webブラウザからのリクエストを待ち続ける Web サーバープログラムや、SSHクライアントからのリクエストを待ち続けるSSHサーバー(sshd)などが挙げられます。

しかし、一般にはこのサーバープログラムが稼働しているマシンそのものもサーバーと呼ばれることがあるので、どちらの文脈で「サーバー」ということがが使われているか注意する必要があります。

IP アドレスとは

IPアドレスとはIPパケットを送受信する宛先を示す番号です。

IPV4とIPV6と言う2つのプロトコルがあります。

IPV4は32ビットの数値、IPV6は128ビットの数値で一つのアドレスを表します。ここでは基本的なIPV4の仕組みについて説明します。

IPV4

IPV4では32ビットを8ビットずつ4つに分割し0-255までの10進数4つをドットで連結した形式(ドットアドレス)で表現するのが一般的です。

例) 192.168.0.1

最上位のビットに近い側をネットワーク部、最下位のビットに近い部分をホスト部と呼びます。どの部分までがネットワーク部でどの部分までがホスト部を示すかについては、以前はアドレスクラスというもので決められていたが、現在は可変長サブネットマスクやCIDR(Classless Inter-Domain Routingの略, サイダーと読む)の使用が一般的です。

ドメインとは

IPアドレスは意味を持たないため、人間にとって覚えにくいものです。

そこで、人間が意味を理解できる単語などでIPアドレスにドメイン名という別名をつけることが可能です。スマートフォンの電話帳において電話番号と個人名などが結びつけられているのと同じ様に、IPアドレスはドメイン名と結びつけられています。

例えばウィキペディアのIPアドレスは198.35.26.96です。しかし単なる番号の羅列は覚えづらく、不便です。

そこで、このIPアドレスに wikipedia.org という別名をつけることで番号の羅列を記憶しなくてもwikipediaのwebサイトにアクセスできるようになっています。

ドメインの取得と仕組み

ドメイン名は世界中で絶対に重複しないようになっています。

DNS(Domain Name System)によってドメイン名とIPアドレスの正引き、逆引きが行われています。

ドメイン名はICANN(Internet Corporation for Assigned Names and Numbers)という組織によって一元管理されています。

  • 先願主義
  • whois情報の公開
  • 商標権者優先

などのルールがあります。

ドメインの取得

ドメインは、レジストリとレジストラと呼ばれる組織によって取り扱われています。それらの役割分担について確認し、私たちがドメインを取得するためにはどの様な組織に何を依頼するのか確認しておきましょう。

レジストリとは

レジストリとは NIC(Network Information Center)とも呼ばれるトップレベルおよびセカンドレベルのドメイン名のデータベースを維持管理する組織です。

.com や .net 、 .jp などのドメインによってレジストリは異なります。例えば .com / .net は VeriSign という組織によって管理されています。

レジストラ

レジストラとはレジストリと契約しドメイン登録の業務を行う業者です。ICANNによって認定されています。

私たちがドメインの取得手続きを申し込むのはこのレジストラになります。多くのレジストラは複数のレジストリから提供されるドメイン名を扱っています。

例えるならば、携帯電話のキャリアがレジストリにあたり、代理店がレジストラにあたるというイメージを持っておくと良いでしょう。

私たちが携帯電話を契約する時には通常メーカーではなく代理店に対して申し込む様に、ドメイン名を取得するにレジストラに対して申し込むことになります。

  • お名前.com
  • バリュードメイン
  • ムームードメイン

など様々なレジストラがあり、それぞれに独自の料金設定があります。

WINGにおけるドメイン名の登録

Conoha WING では、 WING パックで契約を行った場合、独自ドメインを1つ無料で取得できます。

この節では WING での独自ドメイン取得手続きについて解説します。

新規ドメインの取得

新規ドメインの取得

Conoha ダッシュボードの左メニュー「ドメイン」をクリックすると、以下のウィンドウが表示されます。

ドメイン取得の確認

追加する、をクリックしてください。

ドメイン検索

取得したいドメイン名が利用可能かどうかを検索できる画面になるので、取得したいドメイン名を入力し、虫メガネのアイコンをクリックします。

利用できるドメイン一覧

チェックマークと「カートに追加」ボタンが表示されているドメイン名は利用可能です。

ドメイン名を決めたら、「カートに追加」ボタンをクリックし、「追加済み」と表示されたら「次へ」をクリックしましょう。

申し込み画面

申し込み画面が表示されるので、各種情報を入力し、「次へ」をクリックしましょう。

確認画面

上記の画面で、取得したいドメイン名に誤りがないことを確認し、「決定」ボタンをクリックするとドメインの取得が行われます。

ドメインリスト

自動的にドメインリスト画面に遷移し、取得したドメイン名が表示されます。

サーバーにドメインを追加

続いて、サーバーにドメインが追加されていることを確認します。

左メニューから「サーバー管理」 > 「ドメイン」をクリックします。

表示されるドメインリストに取得したドメイン名が含まれていれば、正しく追加されています。

ドメインリスト

FileZilla から確認

FileZilla で、 home/[ユーザー名]/public_html/ を確認すると home/[ユーザー名]/public_html/[取得したドメイン名] というフォルダが確認できます。

FileZilla から確認

該当のフォルダが確認できれば、ドメイン取得関連の処理は完了です。

home/[ユーザー名]/public_html/[取得したドメイン名] にテスト用のファイルをアップロードし、

http://[取得したドメイン名]/[テスト用のファイル名]

にアクセスして、表示を確認しておきましょう。

ただし、設定したドメイン名が反映されるまでにしばらく時間がかかります。表示されない場合は 15分から1時間程度待って確認すると良いでしょう。

(FileZilla の使い方に忘れている場合は、 1-6 FTPクライアントの使い方 1-7 サンプルファイルの作成を参考にしてみましょう。)

DNS とは

IPアドレスとドメイン名の相互変換を行うサーバープログラムがDNSサーバーです。

ドメイン名からIPアドレスを調べることを正引き、IPアドレスからドメイン名を調べることを逆引きと言います。

例えば、Webブラウザのアドレスバーにドメイン名を打ち込むと、まずはその情報がDNSサーバーに送信され、対応するIPアドレスが取得されます。

取得したIPアドレスを利用して初めて該当のWebサーバーにアクセスできる仕組みになっています。

Conoha WING における DNS サーバー

Conoha WING の WING パックで独自ドメインを取得した場合、自動的に DNSサーバーが設定されています。

設定を確認してみましょう。

DNS設定

上記の様な形式で設定されています。(各レコードの意味合いについてはこのテキストでは割愛します。)

HTTPS と SSL

新規作成した独自ドメインは、現時点では暗号化通信ができない状態になっています。

暗号化通信を可能にするためには、SSLの設定を行い https でのアクセスを可能にする必要があります。

SSL とは

Secure Sockets Layer とは、通信内容を暗号化する仕組みです。公開鍵暗号を応用して、受信者のみが復号できる暗号化を施すため、パケット通信の内容が盗み見られたとしても、元の情報を読み取ることはできなくなります。

HTTPS とは

HTTPS とは Hypertext Transfer Protocol Secure の省略系で、HTMLなどの通信に使われるデータを暗号化する安全な通信規約のことです。

HTTP では通信データは平文、すなわち暗号化されていないそのままのテキストデータで送受信されているのに対して、 HTTPS では通信データが暗号化されます。

この暗号化の際に、前述の SSL が利用されます。

URLの最初の部分が http:// から始まっていれば HTTP による通信、 https:// から始まっていれば HTTPS による通信です。

Conoha WING における HTTPS

HTTPS を利用するためには各種のSSL設定が必要になります。

Conoha WING においては、あらかじめ設定されている xxxx.conohawing.com ではHTTPS が利用できますが、取得した独自ドメインでは SSL 設定を行って初めて HTTPS が利用できます。

SSL証明書の取得

それでは、Conoha WINGにおけるSSL証明書の取得方法を確認しましょう。

サイトセキュリティ画面

まず、サイト管理 > サイトセキュリティ を選択して 「切り替え」ボタンを押すと、設定対象のドメインを切り替えられます。

独自ドメインの設定に切り替え

作成した独自ドメインの「切り替え」ボタンをクリックすると、画面遷移するので、再度 サイトセキュリティ画面に移動します。

利用設定を ON

無料独自SSLを展開して、利用設定を ON にします。

これで、SSLの設定が完了します。

これで、しばらく(数時間から1日程度)待つと

https://[取得したドメイン名]/[テスト用のファイル名]

で独自ドメインにアクセスできる様になります。

通信は暗号化される様になりました。これで SSL の設定は完了です。

ポートとは

ポートとは、ネットワーク上におけるコンピュータの受付窓口の様なものです。

ポートには 0 から 65535 までのいずれかの数値が割り振られています。これをポート番号と呼びます。

まずは市役所の窓口のようなものが0番窓口から65535番窓口まである様なものだと思っていただけると良いでしょう。

ポート番号

上述の通り、各ポートに割り振られた番号をポート番号と呼びます。これらの窓口では、各サーバープログラムがリクエストを待ち続けることになります。

市役所の窓口に、各受付担当が配属されているのと同じ様なイメージです。

ウェルノウン(よく知られた)ポート

ウェルノウンポート とは、各サービスごとのよく利用されるポート番号のことです。

ウェルノウンポートには 0 〜 1023番のポートが利用されています。

  • Webサーバー(HTTP): 80番ポート
  • Webサーバー(HTTPS): 443番ポート
  • SSHサーバー: 22番ポート

といった様に、各サーバープログラム(サービス)ごとにポート番号が設定されています。

ウェルノウンポートを利用するのは必須ではありません。

「このマシンでは8080番を Webサーバー(HTTP) の窓口にしよう!」

といったことも可能です。

ファイアウォールとは

ファイアウォールとは、各ポートに対して行われるアクセス制限をおこなうソフトウェアです。火事の被害を防ぐための防火壁の様に、特定のポートに対するアクセスを遮断する役割を果たします。

アクセス元の制限

例えば社内でファイルの共有を行う場合にはLAN経由で同僚のマシンから自分のマシンのポートに対してアクセスが行われることになります。

この時、「社内LANではファイルの共有を受け付けたいが、インターネットからこのファイルに不特定多数の方にアクセスされるのは困る」というケースが発生します。

その場合に、このファイル共有で利用するポートについて

  • 社内LAN: アクセスを受け付ける
  • インターネット: アクセスを受け付けない

などの設定ができるのがファイアウォールと呼ばれるソフトウェアです。

自分自身のみがアクセス

前述のポートはざっくりと3種類のネットワークに対して開かれています。自分自身とLANとインターネットです。

例えば、Webページの開発を行う時には、Webサーバーを立ち上げて自分自身だけがアクセスできる様にしておくことが多くなります。

この場合、LANに対してもインターネットに対してもアクセスを受け付けない様に設定しておくのが望ましいです。

LANからのみアクセス

前述の社内ファイル共有の様なケースでは、

  • LAN: アクセスを受け付ける
  • インターネット: アクセスを受け付けない

という設定をするのが望ましくなります。

インターネットからアクセスを受け付ける

自分自身で Web サーバーを立ち上げてサイトを公開する場合には、該当のポートについて、インターネット上からのアクセスも受け付けることになります。

オンラインゲームなども、各ゲームサーバーから自分のマシンへの通信を受け付ける必要があるので、インターネット上からのアクセスを受け付ける必要が出てきます。オンラインゲームを利用する時にポート開放手続きを求められるケースがあるのはこのためです。

不要なポートを閉じる

ポートは0番から65535番までありますが、全てのポートが利用されていることはまずありません。その場合、そもそも利用しないポートが大量に存在します。いわば、受付がいない窓口が大量にあるということになります。

このときに、利用していないポートは閉じておくことが重要です。

そもそもネットワーク上からアクセスする必要があるポートはごくわずかです。

基本的には全てのポートをLANからもインターネットからもアクセスできない様に設定しておき、通信が必要なポートのみ、必要な対象(LAN or インターネット)からのアクセスを受けつける様に設定しておくのが望ましくなります。

IP制限

インターネット上からアクセスを受け付ける場合も、インターネット上の特定のコンピュータからアクセスを受け付けたい場合もあります。

その場合に利用されるのがIP制限です。

インターネット上の特定のコンピュータは IP アドレスによって識別できるので、

  • 開発者AのIPアドレスからはアクセスを受け付ける
  • それ以外のIPアドレスからはアクセスを受け付けない

の様な設定が可能です。これにより、インターネットを通じて開発者Aさんは様々なところからアクセスできるが、それ以外の人のアクセスは受け付けない、といった使い方ができる様になります。

また、Webサーバーを公開しているが、特定の迷惑ユーザーからのアクセスのみ遮断する、といったことも可能です。

WAFの設定

Conoha WING では WAF(Web Application Firewall) と呼ばれる、Webアプリケーションのセキュリティに特化したファイアウォールを利用できます。

WAF とは

WAFはファイアウォールの一種ですが、単に特定のネットワークからのアクセスを制限するだけでなく、特定の通信パターンを認識し、攻撃であると認識してポートを遮断する機能を持っています。

これにより、悪意を持ったユーザーからのアクセスを動的に遮断できます。

WAF の必要性

上記の機能は Web アプリケーションにとってとても重要です。Webアプリケーションには多かれ少なかれ何らかの脆弱性を抱えていることが多く、悪意を持ったユーザーはそれを探し出して攻撃してきます。

Webアプリケーションは、インターネットを通じて不特定多数からのアクセスを受け付ける必要があるのであらかじめアクセスを遮断することは困難です。

WAFでは、様々な脆弱性に対する典型的な攻撃のパターンを認識し、それらのパターンを認識次第該当の通信を遮断します。そして、攻撃として認識するパターンは日々更新され新たな攻撃にも対処できる様になっています。

Conoha WING における WAF の設定

Conoha WING では サイト管理 > サイトセキュリティ から WAF の設定が可能です。

サイトセキュリティ画面

WAF をON にするだけで上記の機能が有効化されます。

まとめ

この章では、基本的なネットワーク用語とセキュリティの基礎的な考え方について学びました。

今回取り上げた内容は、Webアプリケーションの公開には切っても切れない必要不可欠な部分になります。

世界中の誰もがアクセスできる Web アプリケーションは、いわば往来のど真ん中に自動販売機をおく様なもので、厳重なセキュリティを施さなければ容易に攻撃の対象となってしまいます。

とくに、ログインが必要なアプリケーションの場合、つねに個人情報を守る必要があります。単にサービスにアクセスするために任意に設定したパスワードさえ、同じユーザーが複数のサービスで使いまわしている可能性があるので個人情報ととらえることができます。

サーバー利用の際の基礎知識をしっかり身につけ、各設定項目の意味合いを理解できる様に知識を広げていきましょう。

この章で学ぶこと

この章では Conoha WING や AWS Cloud9 で利用されている Linux の使い方について学びます。

Linux ではシェルと呼ばれる各種コマンドを受け取る仕組みを通じてコンピュータを操作します。

今回はその中でも広く利用されている bash のコマンドについて解説します。

学習の全体像

リポジトリのクローン

課題用リポジトリをクローンします。

GitLab の画面左上「プロジェクト」を開き、 laravel を選択しましょう。

クローンのボタンを押して「HTTPSでクローン」のコピーボタンをクリックするとクローンURLがクリップボードにコピーされます。

cd ~/environmentgit clone [フォークしたリポジトリでクローンurlを確認]

各種ファイルのダウンロードが終わるまでしばらく待ちましょう。

Laravelの初期設定

つづいて、clone した Laravel プロジェクトを動作させるための初期設定を行います。

セットアップ

下記の setup.sh を新規作成し、以下の内容をコピー & ペーストしてください。

(講師注: Amazon Linux 2 用のインストールスクリプトです。)

#!/bin/bash 
# PHP 7.2 を削除
# sudo yum remove php* 
# lamp-mariadb10.2-php7.2 を無効化
# sudo amazon-linux-extras  disable lamp-mariadb10.2-php7.2  
# 利用するphpのバージョンを7.4に切り替え
sudo amazon-linux-extras disable php7.2
sudo amazon-linux-extras install -y php7.4sudo yum clean metadata && sudo yum -y install php-devel php-xml php-gd php-intl php-mbstring php-opcache php-xmlrpc  
# タイムゾーンの設定
sudo timedatectl set-timezone Asia/Tokyo
# ロケールの設定
sudo localectl set-locale LANG=ja_JP.utf8 
# PHPの設定ファイルを追加
sudo echo "date.timezone = Asia/Tokyo" |sudo tee /etc/php.d/999-php-codecamp.inisudo 
echo "display_errors = On" |sudo tee -a /etc/php.d/999-php-codecamp.inisudo echo "mbstring.internal_encoding = UTF-8" |sudo tee -a /etc/php.d/999-php-codecamp.ini 
# MariaDB をインストール
sudo amazon-linux-extras install -y  mariadb10.5 
# mariadb を起動
sudo systemctl start mariadb
# 自動起動設定
sudo systemctl enable mariadb

上記を保存して、

bash setup.sh

を実行するとインストールが開始されます。終了までしばらく待ちましょう。

mariadb の設定

sudo mysql_secure_installation

上記のコマンドを実行し、mariadb のセキュリティ設定を行います。

設定画面1

上記の画面で root ユーザーの現在のパスワードを求められるので、そのまま Enter を押します(現在のパスワードは空白)

設定画面2

続いて、上記の画面で unix ソケットの設定について聞かれます。こちらは n を入力して Enter を押します。

設定画面3

root ユーザーのパスワードを変更するかと聞かれるので Y を入力して Enter を押します。

パスワードの入力は、入力した文字列が表示されません。「secret」とキー入力して Enter を押すと再度パスワード入力を求められるので、 もう一度「secret」とキー入力して Enter を押します。

設定画面4

匿名ユーザーを削除するか聞かれます。セキュリティ上削除すべきなので、 Y を入力して Enter を押します。

設定画面5

現在利用している仮想マシン以外からのアクセスを禁止するか聞かれます。Y を入力して Enter を押します。

設定画面6

test という名前のテスト用データベースを削除するか聞かれます。Y を入力して Enter を押します。

設定画面7

最後に設定変更を反映させるために、特権テーブルの再読み込みを行うか聞かれます。Y を入力して Enter を押します。

設定画面8

以上で初期設定は完了です。

接続確認

最後に、正しく設定できているか確認するため、データベースに接続確認しておきましょう。

mysql -u root -p

と入力し、パスワードを聞かれるので「secret」と入力して Enter を押します。

接続確認

上記の画面が表示されれば確認完了です。

exit;

で画面を抜けましょう。

composer のインストール

Laravel は composer と呼ばれる PHP の各種ライブラリをインストールするツール(パッケージマネージャ)を利用してインストールします。

curl -sS https://getcomposer.org/installer | php

cloud9 のターミナルから、上記のコマンドを実行すると、composer がインストールされます。

composer のインストール位置を変更

composer のインストール先を現在のディレクトリから /usr/bin/ というディレクトリに移動させます。

sudo mv composer.phar /usr/bin/composer

これで composer を利用する準備は完了です。

composer の起動テスト

composer

上記のコマンドを実行して、各種コマンドの一覧が表示されればインストール成功です。

composer

隠しファイルの表示

画面左の歯車マーク(画像参照)をクリックし、「Show Hiddwn Files」にチェックを入れます。

隠しファイルの表示

これにより、隠しファイルが表示されるようになります。

laravel フォルダを右クリックし、コンテキストメニューから 「Refresh」を選択して表示を更新しておきましょう。

.env.example から .env を作成

以降の操作はクローンした laravel ディレクトリで行います。

cd ~/environment/laravel/

で該当のディレクトリに移動し、

cp .env.example .env

上記のコマンドで .env.example から .env を作成しておきましょう。

.envの修正

つづいて、laravelの環境設定ファイルとなる隠しファイル .env を修正します。

.env
(省略)DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=Laravel
DB_USERNAME=root
DB_PASSWORD=secret

上記のDB_CONNECTION=mysql から始まる行をさがして、 DB_DATABASEとDB_PASSWORDの値を上記のように編集して保存すれば完了です。

composer で必要なファイルをインストール

composer install

上記のコマンドを実行するだけで自動的に必要なライブラリがインストールされます。

アプリケーションキーの初期化

php artisan key:generate

上記のコマンドで、 Laravel が各種暗号化に利用するアプリケーションキーが作成されます。

このコマンドを忘れると動作しないので気をつけましょう。

データベースの初期設定

続いて、データベースの初期設定を行いましょう。

データベースにログイン

mysql -u root -p

上記のコマンドを実行すると

Enter password:

と聞かれますので「secret」と入力し、Enter キーを押すとログインできます。

データベースの作成

続いて

CREATE DATABASE laravel;

上記の内容を入力して Enter キーを押すと、「laravel」という名称のデータベースが作成されます。

作成したデータベースの確認

では、正しくデータベースが作成できたか確認しておきましょう。

SHOW DATABASES;

データベースの一覧が表示されますので、その中に先ほど作成した

laravel

が存在していれば OK です。

SQL の入力画面から抜ける

これでこのプロジェクトで利用するデータベースの作成が完了しました。

exit;

上記を実行すると通常のターミナルに戻ります。

以上でデータベースの設定は終わりです。

データベースのマイグレーション

Laravel では、データベースの更新履歴をマイグレーションファイルとして残しているため、クローンしたリポジトリに合わせたテーブルの作成が一瞬でまとめて実行できます。

php artisan migrate

上記のコマンドを実行すると、元のリポジトリで実行されていた全てのマイグレーションがまとめて実行されます。

マイグレーションファイルの意義を際確認しておきましょう。

seed データの投入

Laravel では seed という仕組みを利用することで、データベースの初期データを投入できます。

php artisan db:seed

上記のコマンドを実行すると、Seeder クラスの処理が実行されて、初期データが投入されます。

以下の UsersTableSeeder と PostsTableSeeder クラスの run メソッドの内容を確認しておきましょう。

database/seeds/UsersTableSeeder.php
public function run()  {
      $users = [
          [
              'id' => 1,
              'name' => 'admin',
              'password' => 'admin',
              'admin' => 1,
          ],
          [
              'id' => 2,
              'name' => 'testuser1',
              'password' => 'password',
              'admin' => 0,
          ],
          [
              'id' => 3, 
             'name' => 'testuser2',
              'password' => 'password',
              'admin' => 0,
          ],
      ];
       foreach($users as $user){
          DB::table('users')->insert($user);
      }
database/seeds/PostsTableSeeder.php

public function run()  {
      $posts = [
          [
              'user_id' => 1,
              'comment' => 'こんにちは!管理者です。',
          ],
          [
              'user_id' => 1,
              'comment' => 'こんばんは!管理者です。',
          ],
          [
              'user_id' => 1,
              'comment' => 'おはようございます!管理者です。',
          ],
          [
              'user_id' => 2,
              'comment' => 'こんにちは。',
          ],
          [
              'user_id' => 2,
              'comment' => 'こんばんは。',
          ],
          [
              'user_id' => 2,
              'comment' => 'おはようございます!',
          ],
      ];
       foreach($posts as $post){
          \App\Post::create($post);
      }
  }

動作確認

最後に、各画面の動作確認を行いましょう。

php artisan serve --port=$PORT

上記のコマンドで Laravel の開発用サーバーを立ち上げて、各画面を確認しましょう。

1. ログイン画面

作成中!!!

2. サインアップ画面

3. 一覧画面

4. 新規投稿画面

5. 投稿編集画面

6. プロフィール編集画面

デプロイ方法

この章では、Laravel で作成したWebアプリケーションのデプロイ方法を学びます。

デプロイとは、開発環境で制作しているアプリケーションを、本番環境で公開することです。

公開するための方法はいくつかありますが、今回はリモートリポジトリに push した環境を、本番環境で clone してから公開用の各種設定を行う流れで実施します。

リポジトリの準備

まずは、ここまで作成したリモートリポジトリについて、デプロイの準備を整えます。

今回は、ここまでプルリクエストをマージしてきた develop ブランチを開発環境用とし 、 master ブランチの内容を本番環境に反映させることとします。

ここまで develop にマージしてきた内容を本番環境に反映させるため、あらかじめ develop ブランチを master ブランチにマージするプルリクエストを発行して、マージしておきましょう。

リポジトリのclone

続いて、リポジトリの clone を行います。

ssh での接続

ssh を利用して Conoha WING のサーバーに接続します。

Cloud9 に ssh キーをアップロード

まず、以前に Conoha WING からダウンロードした ssh用の秘密鍵を AWS Cloud9 にアップロードします。

まず、ドラッグ & ドロップでエディタ上のトップ階層(~/environment/)にアップロードしましょう。

続いて ~/.ssh というフォルダに mv コマンドで移動させて、chmod コマンドでパーミッションを600にしておきましょう。

ssh コマンドで Conoha WING に接続

ssh -i ~/.ssh/[秘密鍵のファイル名] -p [sshのポート番号] [sshユーザー]名@[サーバーのドメイン名]

上記のコマンドを実行すると、Cloud9 から Conoha WING のサーバーにログインできます。

composer のインストール

続いて、composer をインストールします。

curl -sS https://getcomposer.org/installer | php

上記のコマンドで composer をインストールし、

./composer.phar self-update

上記のコマンドでアップデートを確認しておきましょう。

クローン先のフォルダを作成

続いて、クローン先のフォルダを作成します。

mkdir ~/laravel_projects

作成したらこのフォルダに移動しておきましょう。

cd ~/laravel_projects

git clone を実行

続いて、

git clone laravel のクローンURL

を実行し、このフォルダ内にリポジトリがクローンされれば準備完了です。

本番環境の設定

つづいて、クローンした Laravel プロジェクトが動作するように各種設定を行いましょう。

データベースの作成

Conoha WING の 管理画面にアクセスして、1-8 を参考に [接頭辞]_production という名前のデータベースを作成しておきましょう。

このデータベースが本番環境で利用されるデータベースになります。

また、データベースユーザーの接続先データベースに、新しく作成したデータベースを追加しておきましょう。

.env ファイルの修正

続いて、.env ファイルを修正します。

cd development_sample

まず上記のコマンドでプロジェクトフォルダに移動して、

cp .env.example .env

上記のコマンドで .env を .env.example からコピーし、

DB の接続情報を Conoha WING における

  • データベースのホスト名
  • データベースユーザーのユーザー名
  • パスワード
  • データベース名

に修正しておきます。

1 章の動作確認で利用した bbs.php を参考にすると良いでしょう。

修正の際には vim を利用しましょう。

必要なライブラリのインストール

続いて、php 関連のライブラリをインストールしましょう。

~/composer.phar install

上記のコマンドで、プロジェクトで利用される各種 php 系のモジュールが vendor フォルダにまとめてインストールされます。

アプリケーションキーの初期化

php artisan key:generate

上記のコマンドで、 Laravel が各種暗号化に利用するアプリケーションキーが作成されます。

このコマンドを忘れると動作しないので気をつけましょう。

本番データの投入

続いて、作成した環境に必要なテーブルとレコードを投入しましょう。

マイグレーションの実行

まず、マイグレーションを実行します。

php artisan migrate

上記のコマンドを実行します。

接続情報が正しく設定されていれば、今回のプロジェクトで利用されるテーブルがまとめて設定されます。

さらに、

php artisan db:seed

上記を実行して、シードデータをデータベースに投入しておきましょう。

動作確認

最後に、

cd  ~/public_html/[独自ドメイン名]

でドメインのルートディレクトリに移動し、

rm index.php

で確認用のphpファイルを削除し、

ln -s ~/laravel_projects/development_sample/public/index.php index.phpln -s ~/laravel_projects/development_sample/public/css ./css

として、シンボリックリンクを張りましょう。

これにより

「独自ドメインの index.php にアクセスすると、自動的に Laravel プロジェクトの public/index.php にアクセスしていることになる」

という状況にします。

.htaccess の書き換え

続いて、全てのアクセスを public/index.php でうけるために .htaccess の追記を行います。

~/public_html/[独自ドメイン名] のフォルダで

vim .htaccess

として、 .htaccess のファイルを書き換えます。

.htaccess
<IfModule mod_rewrite.c>
    RewriteEngine On    RewriteCond %{REQUEST_FILENAME}
 !-f    RewriteRule ^ index.php [L]
</IfModule>

こちらを保存して完了です。

本番環境の更新

本番環境の更新を行う場合には 該当のプロジェクトフォルダで git pull を実行します。

  1. 開発環境で修正や機能追加をおこない、プルリクエストを通じてdevelop にマージしておく
  2. 本番環境を更新するタイミングで、develop ブランチを master ブランチにマージ
  3. 本番環境で git pull を実行し、更新を反映させる
  4. マイグレーションが更新されている場合は、本番環境で再度 php artisan migrate を実行する。

以上が基本的な本番環境の更新の流れとなります。

まとめ

この章では、開発環境から本番環境へのデプロイの流れについて確認できました

がんばってプログラミングを学習していきましょう

Laravel6でのログイン失敗回数オーバでロックさせるコーディング【Throttle機能】

Throttle機能とは

Throttle機能とは、ログイン失敗時に失敗した場合、一定の指定回数を超えた場合にログインにロックを掛けて、一定の時間を経過したらロックを解除する機能です。

Throttle機能を使う

実装方法は $maxAttemptsと$decayMinutesをLoginController内に設定するだけです。

<?phpclass LoginController extends Controller{    use AuthenticatesUsers;    protected $maxAttempts = 5; // 5回失敗したらロックする    protected $decayMinutes = 30; // ロックは30分間

ロックした時にメッセージを出すようにする

しかし、$maxAttemptsに設定された回数失敗したらロックされるのですが、ロックされた旨のメッセージは設定した回数失敗した際には表示されません。
ロックしたらその時に知らせてほしいものです。そこで、ロックしたメッセージがすぐに表示されるようにしてみます。

ロックのエラー処理は、loginメソッド内でやっています。 ログイン判定前に、処理をしているようなので、ログイン判定後に改めてロックの判定を行うように追記します。

<?php
    public function login(Request $request)    {
       $this->validateLogin($request);
       if (method_exists($this, 'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);
            return $this->sendLockoutResponse($request);
        }          if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        } 
        $this->incrementLoginAttempts($request);
         // このif文を追記
        if (method_exists($this, 'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);
            return $this->sendLockoutResponse($request);
        }
          return $this->sendFailedLoginResponse($request);
    }

これでロックしたらすぐにメッセージが出るようになります。

失敗回数はログインID毎ではなく全体での回数にする

Laravelではログインの失敗のカウントは、ログインID毎に判別しているようです。
つまり、あるアカウントはロックされても、別のアカウントならログイン可能ということです。 これを全体で失敗した回数にしたいと思います。

throttleKeyというメソッドでログインを判別するためのキーを生成しているので、これをLoginController内でオーバーライドして修正します。

Str::lower($request->input($this->username())).’|’.$request->ip()となっていて、ログイン名(通常はemail)とIPを元にキーを生成しています。 このキーを元にカウントアップするので、アカウントごとに失敗した回数が設定されるというわけですね。

なので、ここを以下のように修正します。

    protected function throttleKey(Request $request)    {
        return $request->ip();
    }

これでアカウントは関係なく同一IPからの失敗でカウントされるようになります。

以上になります。参考にされてくださいね。

プログラムオンライン学習!「教育訓練給付金制度」お金come back!できるお得おすすめ講座。

トータル費用を考えて高度な学習を受けよう

ハローワークへ事前申請すれば50%~70%戻ってくる制度があることをご存知でしょうか。最終課題までクリアーしないといけませんが、逆に頑張れるというものではないでしょうか。まず制度の内容を説明し、その後、プログラミング歴40年の筆者オススメの講座紹介いたします。国が力を入れているのはデータサイエンスとAIです。ITの世界はコロナ不況より新たな変革期に入ってきたと筆者は考えております。いち早くキャッチし自分に優位な知識を養いましょう。

「教育訓練給付制度」とは

厚生労働省が個人に向けて支援する「教育訓練給付金制度というものがあります。

教育訓練給付金とは

働く方の主体的な能力開発の取組み又は中長期的なキャリア形成を支援し、雇用の安定と再就職の促進を図ることを目的とし、教育訓練受講に支払った費用の一部が支給されるものです。
また、初めて専門実践教育訓練(通信制、夜間制を除く)を受講する方で、受講開始時に45歳未満など一定の要件を満たす方が、訓練期間中、失業状態にある場合に訓練受講をさらに支援するため、「教育訓練支援給付金」が支給されます.。
引用元:厚生労働省公式HP「教育訓練給付制度」

教育訓練給付金制度は、さらに下記3つに分かれています。そしてどれもハローワークに申請する必要があります。だいたい通算3年以上会社員として勤務し保険が給料天引きになっている方が対象です。この条件を、満たしていれば、離職後1年以内であれば利用できます。

①一般教育訓練給付金

一般教育訓練給付金は、雇用の安定と再就職を促進する目的で設けられた、雇用保険の給付制度のこと。多くのプログラミングスクールが、一般教育訓練給付金の認定校に登録されています。支給額は、支払った受講料の20%に相当する金額。上限は10万円で、受講料の20%が4,000円を超えない場合は給付金の対象外となります。

②専門実践教育訓練給付金

専門実践育訓練給付金とは、雇用保険の一般被保険者等または一般被保険者等であった方が、厚生労働大臣指定の専門実践教育訓練を卒業した場合に受講料の一定額をハローワークが支給する制度のことです。つまり、在職中・離職後にかかわらず指定されたプログラミングスクールを受講して卒業すれば返金されるということ。

指定されているスクールは、一般教育訓練給付金で認可されたところよりもハイレベルであることが多いです。支給額は受講料の50%で、年間の上限は40万円です。また、専門実践教育訓練を卒業した後1年以内に転職して雇用保険の被保険者になった場合、受講料の70%(年間上限56万円)を受給することができます。

③特定一般教育訓練給付金

特定一般教育訓練給付金は、令和元年10月に設立された新しい制度です。特定一般教育訓練給付金は対象講座が指定されており、下記の4つになります。

  • 独占資格や必置資格に関する養成課程
  • 情報通信技術に関する資格のうちITSSレベル2以上の情報通信技術に関する資格取得を目標とする課程
  • 新たなITパスポート試験合格目標講座
  • 短時間のキャリア形成促進プログラムおよび職業実践力育成プログラム

初回の給付を受けたい場合は支給要件期間が1年です。支給額は受講料の40%(上限20万円)となります。条件が合えば、申請してみてはいかがでしょうか。

経済産業省が認定する「第四次産業革命スキル習得講座」

政府が支給する給付金は、教育訓練給付制度だけではありません。教育訓練給付制度は厚生労働省が認定したものですが、経済産業省も「第四次産業革命スキル習得講座」を認定しています。国が力入れてますので今後生徒が増えていきます。先行されてみてはどうでしょうか

講座の要件
・育成する職業、能力・スキル、訓練の内容を公開していること
・必要な実務知識、技術、技能を公表していること
・実習、実技、演習又は発表などが含まれる実践的な講座がカリキュラムの半分以上を占めていること
・審査、試験等により訓練の成果を評価していること
・eラーニング等の社会人が受けやすい工夫をしていること
・事後評価の仕組みを構築していること 等

引用元:経済産業省公式HP「第四次産業革命スキル習得講座認定制度」

一番のおすすめ「 経済産業省が認定する「第四次産業革命スキル習得講座」 :AIスキル

私が、データサイエンスとAIにつきましては、豊富なメンターを集めている

AI(人工知能)特化型プログラミングスクール「Aidemy Premium Plan」

がおすすめです。

・AIエンジニア/データサイエンティストにキャリアチェンジしたい
・業務課題(研究課題)をAIを使って解決したい
・教養としてAIについて知りたい
・AIに関してのスキルを身に着け就職活動に活かしたい…

など、これらをお考えの方にマッチしております

■関連実績
・日本最大級のAI/人工知能プログラミングスクール
・「Aidemy Premium Plan」受講者総数 700名突破
・SaaS型AI学習サービス「Aidemy」会員登録者数 55,000名突破
・法人向けAI内製化支援サービス「Aidemy Business」導入企業数 120社突破
・2018年 グッドデザイン賞 (Aidemy)
・「Forbes 30 UNDER 30 JAPAN 2019」エンタープライズ・ビジネス部門 受賞(弊社代表 石川聡彦)
・計算生物学およびバイオインフォマティクスの国際会議 「ICCBB2019」ベストプレゼンター賞 受賞
・第16回 日本e-Learning大賞「AI・人工知能特別部門賞」受賞
・「HRアワード2019」プロフェッショナル部門 入賞
・一般社団法人日本経済団体連合会(経団連)加盟済

■出版
・『人工知能プログラミングのための数学がわかる本』代表 石川聡彦 著
・『Pythonで動かして学ぶ!あたらしい深層学習の教科書 機械学習の基本から深層学習まで』代表 石川聡彦 著
・『独学プログラマーのためのAIアプリ開発がわかる本』河合大 著
・『ブロックチェーンプログラミングのためのコンピュータサイエンスがわかる本』代表 石川聡彦 共著
・『現場で使える!Python深層学習入門 Pythonの基本から深層学習の実践手法まで』木村優志 著
・『図解即戦力 機械学習&ディープラーニングのしくみと技術がこれ1冊でしっかりわかる教科書』山口達輝、松田洋之 著

これらの実績も判断材料にされてください。

【給付金】他にもおすすめプログラミングスクール

  • DMM WEBCAMP —- 転職成功率98%。転職できなければ全額返金。DMM.comグループならではの非公開求人も多数
  • TechAcademy(テックアカデミー) —— 最短4週間で未経験からプロを育てるオンライン完結のスクール
  • Aidemy(アイデミー) —- ブラウザさえあれば10秒で学習スタート!3ヶ月でAI人材をめざす
  • ヒューマンアカデミーITカレッジ —- 初心者・文系出身者でもOK、人材育成のヒューマンアカデミーだから安心
  • DIVE INTO CODE —– 同期とのペアプロやディスカッションで切磋琢磨。必要なスキルがイチから身につく

お得ですので、是非ご検討くださいね

例えば 40万円の費用だと70%戻ってくると28万円が戻ってくるので実質は12万円です。助成金対象は国が進めていることもあり優遇されている点も検討内容に入れてみてはどうでしょうか。大きな自己投資です。後悔しないように無料相談を受けてから検討してください。

プログラミングでの副業・転職・就職の前にブログから始めましょう【経過がポートフォリオになります】

プログラミングを始められる方は、サービス開発後にポートフォリオ公開用としてブログを行いますが、逆にブログで学ばれたことをブログへアウトプットしましょう。理解にも繋がり人間味があるポートフォリオになりますよ。またアフリエイトで収益も入ってきてウハウハですよ。 簡単でマイドメイン名も入って800円/月程です。これもスキルの一環ですぜ!