JavaScript Primerを読み終えての感想とまとめ
はじめに
JavaScript Primerはサイトで無料で読むことができます。
リファレンスとしても使えると思うので書籍版を買うのもいいかと思います。
JavaScriptとは
JavaScriptはECMAScriptという仕様によって動作が決められています。
ECMAScriptはどの実行環境においても共通な動作のみが定義されたもので、固有の機能とECMAScriptの機能を併せ持っているのがJavaScriptになります。
良かったところ
- 無料で読める
- リファレンスとしても使える
- 基礎文法だけでなく概念的な解説もある
- ハンズオンがある
- 内容がアップデートされている
学んだこと
変数
letは初期値を指定しなかった場合はundefinedという値で初期化される。
let hoge; console.log(hoge); // undefined
データ型
JavaScriptのデータ型は大きく分けると、プリミティブ型とオブジェクトに分類される。
プリミティブ型は次の7つ
| 型 | 値 |
|---|---|
| 真偽値(Boolean) | trueまたはfalseのデータ型 |
| 数値(Number) | 11や3.14などの数値のデータ型 |
| 巨大な整数(BigInt) | 9007199254740992nなどの任意精度の整数のデータ型 |
| 文字列(String) | 'hoge'などの文字列のデータ型 |
| undefined | 値が未定義であることを意味するデータ型 |
| null | 値が存在しないことを意味するデータ型 |
| シンボル(Symbol) | 一意で不変な値のデータ型 |
オブジェクトは配列、クラス、関数といったプリミティブ型以外のデータが該当する。
またプリミティブ型はイミュータブル(不変)で、オブジェクトはミュータブル(可変)な特性を持つ。
- プリミティブ型(イミュータブル)
let a = 'hello'; let b = a; console.log(a === b); // true: 同じ値を参照している a = 'world'; // 新しい値を代入 console.log(a === b); // false: aは新しい値を参照しているが、bは元の値を保持している console.log(b); // 'hello': bの値は変わっていない
aの値が変更されても、プリミティブ型である'hello'自体の値は変わらない。
よってその'hello'を参照しているbの値も'hello'のままである。
- オブジェクト(ミュータブル)
let a = { name: 'hello' }; let b = a; console.log(Object.is(a, b)); // true: 同じオブジェクトを参照している a.name = 'world'; // オブジェクトの内容を変更 console.log(Object.is(a, b)); // true: まだ同じオブジェクトを参照している console.log(b.name); // 'world': obj2も変更を反映している
let b = a;でaとbは同じオブジェクトを参照する。
更にオブジェクトのnameプロパティはプリミティブ型の'hello'という文字列を参照する。
a.name = 'world';によって、nameプロパティは'world'を指すように変更される。
よってそのオブジェクトを参照しているbのnameプロパティも'world'になる。
文字列に変数を埋め込む場合はテンプレートリテラルを使用する。
バッククォートで文章全体を、${}で変数を囲めばよい。
const name = '田中'; console.log(`${name}さん、こんにちは`);
演算子
値を比較する時は厳密等価演算子(===)を使う。
console.log(1 === 1); // => true console.log(1 === "1"); // => false
等価演算子の場合は左辺と右辺が同じ型となるように暗黙的な型変換が行われる。
console.log(1 === 1); // => true // 文字列'1'を数値の1に変換して比較している console.log(1 === "1"); // => true
暗黙な変換により意図しない結果となることがあるため、===を使う。
インクリメント演算子は位置によって挙動が変わる。
- 後置インクリメント演算子
let x = 1; console.log(x++); // 1 console.log(x); // 2
- 前置インクリメント演算子
let x = 1; console.log(++x); // 2 console.log(x); // 2
論理演算子
AND演算子(&&)は左辺の値の評価結果がtrueであれば、右辺の評価結果を返す。
左辺がfalseであれば左辺の値を返す。
console.log(true && 'hoge'); // 'hoge' console.log(false && 'hoge'); // false
次のような左辺が真偽値でない場合は、暗黙的な型変換によって、falseに変換される値をfalsyな値と呼ぶ
// 左辺はfalsyな値なので、falseに変換され、右辺は評価されない console.log(0 && 'hoge'); // 0 // 左辺はfalsyな値ではないので、trueに変換され、右辺が評価される console.log('bar' && 'hoge'); // 'hoge'
falsyな値は次の通り
falseundefinednull00nNaN""(空文字列)
OR演算子(||)の場合は、AND演算子と逆で左辺がfalseの時に、右辺が評価される
console.log(true || 'hoge'); // true console.log(false || 'hoge'); // 'hoge' console.log(0 || 'hoge'); // 'hoge' console.log('bar' || 'hoge'); // 'bar'
2条件のif~else文は三項演算子を使って1行で記述できる
const age = 20 const type = age >= 20 ? `adult` : `child`; // 上と同じ意味 const age = 20 let type; if (age >= 20) { type = 'adult'; } else { type = 'child'; }
関数
Rest parametersは仮引数名の前に...をつけた仮引数のことで渡された値が配列として代入される。
function hoge(...args) { console.log(args) // [1, 2, 3, 4] } hoge(1, 2, 3, 4);
オブジェクトに関数を定義する。
const hoge = { method() { return 'hello world'; } }; console.log(hoge.method()); // hello world
文と式
JavaScriptは 文(Statement)と式(Expression)から構成されている。
式
式は値を生成し、変数に代入できるもの(評価値を得れるものとも言える)。
// 1という評価値を表示 console.log(1); // 1という評価値を代入 const n = 1;
文
処理する1ステップが1つの文(if文やfor文)。
文は式ではないため、変数へ代入することができない。
// SyntaxError const hoge = if (true) { 'hoge' }
文の終わりにはセミコロンが必要
let x = 5;
文であってもセミコロンが省略可能なケースがある。
- ブロック文の終わり
if (hoge) { }
- 関数宣言の終わり
function hoge() { }
ループと反復処理
配列の反復処理
- forEach
const numbers = [1, 2, 3]; numbers.forEach((n) => { console.log(n); // 1, 2, 3 });
- for~of
const numbers = [1, 2, 3]; for (let n of numbers) { console.log(n); // 1, 2, 3 }
someメソッドは配列内の要素をテストし、一度でもtrueとなればtrueを返す。
const numbers = [1, 2, 3]; numbers.some(n => n > 2);
filterメソッドは配列内の要素をテストし、trueを返した要素のみを集めた配列を返す。
const numbers = [1, 2, 3, 4]; const even = numbers.filter(n => n % 2 === 0);
オブジェクト
プロパティ名と値に指定する変数名が同じであれば変数名を省略できる。
const name = 'Alice'; const obj = { name };
オブジェクトの分割代入。
const fruits = { name: 'apple', price: 120, }; const { name, price } = fruits; console.log(name); // 'apple' console.log(price); // '120'
オブジェクトの結合(オブジェクトマージ)
const objA = { a: 'a'}; const objB = { b: 'b'}; const merged = Object.assign({}, objA, objB); console.log(merged); // { a: 'a', b: 'b'}
assignメソッドに第一引数に既存のオブジェクトを渡した場合は、そのオブジェクトのプロパティが変更されてしまうので気をつける。
const objA = { a: 'a'}; const objB = { b: 'b'}; const merged = Object.assign(objA, objB); console.log(merged); // { a: 'a', b: 'b'} console.log(objA); // { a: 'a', b: 'b'}
スプレッド構文を利用した書き方のが個人的には好きです。
const objA = { a: 'a'}; const objB = { b: 'b'}; const merged = Object.assign(...objA, ...objB); console.log(merged); // { a: 'a', b: 'b'} console.log(objA); // { a: 'a'}
プロトタイプオブジェクト
Object.prototypeは、多くの便利なメソッドを持つ特別なオブジェクトである。
新しいオブジェクトを作ると、自動的にこのObject.prototypeの機能を使えるようになる。
よって以下のようにインスタンス化したobjのtoStringとObject.protype.toStringは同じものとなる。
// Object.prototypeを継承 const obj = { name: 'taro', } console.log(obj.toString === Object.prototype.toString); // true
配列
undefinedと空の要素を区別する。
const hoge = [1, undefined, 3]; const foo = [1, , 3]; console.log(Object.hasOwn(hoge, 1)) // true console.log(Object.hasOwn(foo, 1)) // false
配列の結合。
const arrayA = [1, 2, 3]; const arrayB = [4, 5, 6]; console.log([...arrayA, ...arrayB]); // [1, 2, 3, 4, 5, 6]
文字列
正規表現にマッチした文字を取得する。
const result = `hello world`.match(/l+/); // matchメソッドの返り値は配列 console.log(result[0]); // 'll'
タグ付きテンプレート関数
function tag(strings, ...values) { // stringsは文字列のパーツが${}で区切られた配列となる console.log(strings); // => ["template "," literal ",""] // valuesには${}の評価値が順番に入る console.log(values); // => [0, 1] } // ()をつけずにテンプレートを呼び出す tag`template ${0} literal ${1}`;
関数とスコープ
letは変数を宣言する前に参照されるとReferenceErrorとなる。
varは変数を宣言する前に参照されてもundefinedとなる。
varは宣言部分が暗黙的に最も近い関数またはグローバルスコープの先頭に巻き上げられ、代入部分はそのままの位置に残るという動作を行う。
console.log(hoge); var hoge = 'hoge'; // 上のコードは次のように解釈される。 var hoge; console.log(hoge); // 巻き上げにより宣言されているためundefined hoge = 'hoge';
関数宣言も同様にスコープの先頭に巻き上げられている。
hoge(); // hoge function hoge(){ console.log('hoge'); } // 上のコードは次のように解釈される function hoge(){ console.log('hoge'); } hoge(); // hoge
JavaScriptのメモリ管理の仕組みとしてガベージコレクションが採用されている。 これはメモリ上に確保したデータがどこからも参照されなくなった時点で自動でメモリ上から解放するというもの。
// 'hoge'という文字列のデータをメモリ上に確保 // 変数hogeはそのメモリを参照している let hoge = 'hoge'; // 'foo'という新たな文字列のデータをメモリ上に確保 // 変数hogeはこれを参照する hoge = 'foo'; // 'hoge'というデータはどこからも参照されなくなる // ガベージコレクションによって解放される
関数とthis
メソッド内のネストされた関数は外のthisを参照しない。
次のmapメソッドは、コールバック関数を単なる関数として呼び出すため、thisはundefinedになる(strict モードの場合)。
"use strict"; const hoge = { taxRate: 10, addTaxToPrices(prices) { return prices.map(function(price) { return price + price * (this.taxRate / 100); }); } }; hoge.addTaxToPrices([100, 120, 180]); // TypeError
これに対処するにはアロー関数でコールバックを書く。
アロー関数はスコープチェーンの仕組みと同様に外側の関数へthisを探しに行くためである。
"use strict"; const hoge = { taxRate: 10, addTaxToPrices(prices) { return prices.map((price) => { return price + price * (this.taxRate / 100); }); } }; hoge.addTaxToPrices([100, 120, 180]); // [110, 132, 198]
クラス
コンストラクタを使わずpublicクラスフィールドでプロパティを初期化できる。
class Counter { count = 0; increment() { this.count++; } } const counter = new Counter(); counter.increment(); console.log(counter.count); // 1
privateクラスフィールドで外から参照できないようにする。
class Counter { #count = 0; increment() { this.#count++; } } const counter = new Counter(); counter.increment(); console.log(counter.count); // Error
コンストラクタは親クラス→子クラスの順で行われる。
class Parent { constructor() { this.name = "Parent"; } } class Child extends Parent { constructor() { // 先にsuper()する super(); this.name = "Child"; } }
継承した場合のクラスフィールドの関係。
class Parent { parentField = '親クラスのパブリックフィールド'; #parentField2 = '親クラスのプライベートフィールド' } class Child extends Parent { childField = '子クラスのパブリックフィールド'; say() { console.log(this.#parentField2); } } // 親クラスのフィールドで定義したプロパティも子クラスからアクセスできる const child = new Child(); console.log(child.parentField); // `親クラスのパブリックフィールド` console.log(child.say()); // SyntaxError
Map/Set
Mapオブジェクトをnewで初期化するときに、コンストラクタに初期値を渡せる。
コンストラクタ引数として渡せるのはエントリーの配列で、エントリーとは、1つのキーと値の組み合わせを[キー, 値]という形式の配列で表現したもの。
const map = new Map([["key1", "value1"], ["key2", "value2"]]); // 2つのエントリーで初期化されている console.log(map.size); // => 2
JOSN
JSONの基礎文法
{ "object": { "number": 1, "string": "js-primer", "boolean": true, "null": null, "array": [1, 2, 3] } }
JSONをオブジェクトに変換する。
// JSONはダブルクォートのみを許容するため、シングルクォートでJSON文字列を記述 const json = '{ "id": 1, "name": "tanaka" }'; const obj = JSON.parse(json); console.log(obj.id); // => 1 console.log(obj.name); // => "tanaka"
文字列がJSONの配列を表す場合は、JSON.parseメソッドの返り値も配列になる。
const json = "[1, 2, 3]"; console.log(JSON.parse(json)); // => [1, 2, 3]
JSONは外部のプログラムとデータ交換する用途がほとんどで、そのデータが正しい保証はないので、try...catchで例外処理をするべき。
const userInput = "not json value"; try { const json = JSON.parse(userInput); } catch (error) { console.log("パースできませんでした"); }
オブジェクトをJSONに変換する。
const obj = { id: 1, name: "tanaka", age: null }; console.log(JSON.stringify(obj)); // => '{"id":1,"name":"tanaka","age":null}'
JSON.stringifyメソッドの第二引数はreplacer引数とも呼ばれ、関数あるいは配列を渡す。
関数を渡した場合は引数にプロパティのキーと値が渡され、その返り値によって文字列に変換される際の挙動をコントロールできる。
undefinedが返されたプロパティは、変換後のJSONに含まれなくなる。
const obj = { id: 1, name: "tanaka", age: null }; const replacer = (key, value) => { if (value === null) { return undefined; } return value; }; console.log(JSON.stringify(obj, replacer)); // => '{"id":1,"name":"tanaka"}
replacer引数に配列を渡した場合はプロパティの許可リストとして使われ、 その配列に含まれる名前のプロパティだけが変換される。
const obj = { id: 1, name: "tanaka", age: null }; const replacer = ["id", "name"]; console.log(JSON.stringify(obj, replacer)); // => '{"id":1,"name":"tanaka"}'
モジュール
名前つきエクスポートは、モジュールごとに複数の変数や関数などをエクスポートできる。
const foo = "foo"; export { foo };
名前つきインポートは、指定したモジュールから名前を指定して選択的にインポートできる。
// 名前つきエクスポートされたfooとbarをインポートする import { foo, bar } from "./my-module.js"; console.log(foo); // => "foo" console.log(bar); // => function bar()
デフォルトエクスポートは、モジュールごとに1つしかエクスポートできない特殊なエクスポート。
const foo = "foo"; export default foo;
export文を宣言の前につけると、宣言と同時にデフォルトエクスポートが可能。
このとき関数やクラスの名前を省略できる。
export default function() {}
難しかったこと
私はこれまで上から下へ順に実行されるコードしか書いてこなかったため、非同期処理における実行順序のイメージをつかむことが難しかったです。
特に以下の2点が理解しにくいポイントでした。
- 処理の実行順序が直感的ではないこと
- 非同期処理自体とその成功/失敗時の処理が、コード上で離れて定義されていること
これからコードをたくさん書いて慣れていきたいと思います。
「Everyday Rails - RSpecによるRailsテスト入門」を読み終えての感想
はじめに
「Everyday Rails - RSpecによるRailsテスト入門」を読み終えたので、感想や簡単な纏めを行います
よかったところ
- 書籍内のサンプルコードがGitHubに上げられており、ハンズオン形式で進めることができる。
- RSpecの構文や機能だけでなく、テストの書き方や考え方についても丁寧に解説されており、テストの設計思想について学ぶことができる。
- 書籍の内容は定期的にアップデートされており、常に最新の情報に基づいて学習することができる。
学んだこと
RSpecの導入
RSpecを使うためには、まずGemfileにRSpecを追加する必要があります。
group :development, :test do gem 'rspec-rails' end
その後、bundle installコマンドを実行してGemをインストールします。
$ bundle install
続いて、テストデータベースを作成します。
$ bin/rails db:create:all
最後に、RSpecをインストールします。
$ bin/rails g rspec:install
RSpecの出力形式をドキュメント形式に変更するには、.rspecファイルに以下の行を追加します。
--format documentation
モデルスペック
モデルスペックはモデルのバリデーションやインスタンスメソッドのテストを行います。
モデルスペックを作成するには、以下のコマンドを実行します。
$ bin/rails g rspec:model user
次のコードはitとexpectを使った基本的なテストです。
it "adds 1 and 1 to make 2" do expect(1 + 1).to eq 2 end
このテストは、1と1を足した結果が2になることを検証します。
expectとeqは、RSpecのマッチャと呼ばれるオブジェクトです。マッチャは、期待値と実際の値を比較するために使用されます。
モデルスペックでは、バリデーションのテストも書くことができます。
it "is valid with first name and last name" do user = User.new( first_name: "Yamada", last_name: "Taro", ) expect(user).to be_valid end
このテストは、first_nameとlast_nameが設定されたユーザーインスタンスが有効であることを検証します。
be_validマッチャは、valid?メソッドがTrueであることを検証します。
バリデーションによってエラーが発生するテストも必要です。
it "is invalid without a first name" do user = User.new(first_name: nil) user.valid? expect(user.errors[:first_name]).to include("can't be blank") end
このテストは、first_nameが設定されていないユーザーインスタンスが無効であることを検証します。
includeマッチャは、繰り返し可能な値(enumerable)の中に、ある値が存在することを検証します。
テストデータの作成
テストでは基本的に同じデータを何度も使い回します。
example(it)の度にインスタンスを作成するのはDRYではありません。
FactroyBotでは事前に作成するデータを定義しておき、簡単な構文でインスタンスを作成することができます。
FactoryBotを使うためには、まずgemを追加します。
gem "factory_bot_rails"
その後、ファクトリを生成します。
bin/rails g factory_bot:model user
ファクトリに作成するデータを定義します。
FactoryBot.define do factory :user do first_name { "Yamada" } last_name { "Taro" } end end
このファクトリは、first_nameが"Yamada"、last_nameが"Taro"のユーザーインスタンスを作成します。
このようにファクトリを作成しておくことで、モデルファイルで以下のようにインスタンスの作成やデータの保存が行えます。
# インスタンスの作成 FactoryBot.build(:user) # データの保存 FactoryBot.create(:user)
値をオーバーライドすることも可能です
FactoryBot.build(:user, first_name: nil)
本書では他にもFactoryBotの便利な機能について詳しく紹介されています。
- シーケンス: 重複しないIDを生成する
- トレイト: 共通の属性を定義する
- コールバック: インスタンス生成時に実行する処理を定義する
コントローラースペック
コントローラースペックではリクエスト処理や正しいルーティングが設定されているかをテストします。
コントローラースペックを作成するには、以下のコマンドを実行します。
$ bin/rails g rspec:controller home --controller-specs --no-request-specs
テストの書き方は次のようになります。
RSpec.describe UsersController, type: :controller do describe "#index" do it "responds successfully" do get :index expect(response).to be_successful end end end
このテストではUserコントローラーのindexアクションにGETリクエストを送信して、responseオブジェクトのステータスコードが200~299の範囲内であることを期待しています。
ステータスコードを直接 指定することもできます
RSpec.describe UsersController, type: :controller do describe "#index" do it "responds successfully" do get :index expect(response).to have_http_status "200" end end end
ルーティングにparamsが必要なときはhashで渡します。
it "redirects to the home" do user = FactoryBot.create(:user) get :show, params: { id: @user.id } expect(response).to be_successful end
システムスペック
システムスペックでは統合テストとも呼ばれ、ソフトウェア全体が⼀つのシステムとして期待どおりに動くことを検証します。
システムスペックではCapybareを使ってブラウザの操作をシミュレートしてテストを行います。Rails5.1以降であれば最初からインストールされているようです。
gem "capybara"
システムスペックを作成するには、以下のコマンドを実行します。
$ bin/rails g rspec:system users
次のコードはユーザーがログインできることを検証します。
scenario "user can log in" do user = FactoryBot.create(:user) visit root_path click_link "Sign in" fill_in "Email", with: user.email fill_in "Password", with: user.password click_button "Log in" expect(page).to have_content "Login succeeded" end
システムスペックのテストが失敗する場合に、任意の場所にsave_and_open_pageと記述することでブラウザの画面を確認することができます。
例えば上のコードでLoginボタンをクリックした時に、テストが失敗している場合は、次にようにします。
scenario "user can log in" do user = FactoryBot.create(:user) visit root_path click_link "Sign in" fill_in "Email", with: user.email fill_in "Password", with: user.password save_and_open_page click_button "Log in" expect(page).to have_content "Login succeeded" end
Launchy gemを入れておくことでsave_and_open_pageの処理が走ると自動でブラウザが立ち上がり、その時の画面状態を確認することができます
本書では、システムスペックを使った複雑なテストの書き方についても詳しく紹介されています。
むずかしかったところ
本書の第11章では、テスト駆動開発(TDD)に沿ってコードを書いていく手順が説明されていました。しかし私がこれまで課題等で行ってきた開発作業は、まずコードを書き、エラーが発生したら修正を加えるという、TDDとは真逆のアプローチでした。
そのため、実際にTDDを実践しようとすると、従来の開発手法とは全く異なる考え方が必要となり、習熟するには相当の練習が必要だなと感じました。
最後に
初心者でも分かりやすく、かつ網羅的にRSpecのことを学べる良書でした。
「プロを目指す人のRuby入門」を読んだ時もおもったのですが、伊藤淳⼀さんの解説って凄く分かりやすいですよね(本書では翻訳として参加されていますが)。
いつかRailsの技術書も出してほしいですね!
Ruby on Rails 5速習実践ガイドを読んだ感想
はじめに
Railsのインプットに現場で使える Ruby on Rails 5速習実践ガイドを使用しました。
読了したので内容の整理も兼ねて感想を書いていきます。
良かったところ
- Rubyの基礎構文からRailsテストまでハンズオンで学べる
- SlimやRspecなどの現場でよく使われる技術を学べる
- 現場に入った後でも参考になるようなセクションがある
- 必ずパスが指定されておりどのファイルにコードを記述すればよいか分かりやすい
悪かったところ
- コード説明がない箇所が稀にあり、意味を自分で調べる必要がある
- Rails5の環境構築が難しい(教材通りにしてもかなりエラーが発生する)
- ↑の理由によりRails7で環境構築を行い進めるが、やはりこちらでもエラーが多発する
- kindleでコードがコピーできない
そもそもRails5を想定した教材なのでRails7でエラーが多いのは仕方ないですね。
時間はかかりますがRails7で進めて、エラーの都度調べて修正するのが力もつくし良いかと思います。
学んだこと
Chapter1. RailsのためのRuby入門
Rubyの基礎構文を学習します。
チェリー本を既読していたので特に目新しい理解はありませんでしたが、Railsに入る前にRubyの復習を出来たので良かったです。
特にぼっち演算子(&.)はよく出てくるので覚えておきましょう。
Chapter2. Railsアプリケーションをのぞいてみよう
ローカルにRailsの開発環境を構築します。
自分はDockerを学習済みだったので、Dockerで環境構築を行いました。
(Dockerで環境構築した記事も作成しました)
Rails7 + postgreSQL + bootstrapの環境構築
書籍にDockerの環境構築の手順が書かれている訳ではないので、ここでかなり苦戦した記憶がありますが、最終的にはDockerの理解が深まったので良かったです。
Chapter3. タスク管理アプリケーションを作ろう
このChapterから本格的にアプリケーションを作成します。
RailsのViewはerbで記述するのが当たり前でそれしかないと思っていたのですが、erbと比較して少ないコード量で書けるSlimと呼ばれるテンプレートエンジンが現場でよく使われているそうです。
この書籍でもこのSlimを使用して進めていきます。
書き方の違いは次にようになります。
- html
<h1><%= @example %></h1>
- slim
h1= @example
slimはタグ(<>)を省略して記述している事が分かります。
画面に表示させたくない場合は=ではなく-を使用します。
実装する機能はCRUD機能と呼ばれる基本的なデータ操作です。
RailsはRESTの設計思想を取り入れており、ルーティングがその設定を担っています。
例えば、TODOアプリでタスクの一覧画面を表示するページがあったとします。
そのURIはリソース(この場合はタスク)を一意に識別できる必要があり、URIには/tasksが付けられます。このURIはtaskの複数形であるため、タスクの一覧を表示するページであることがURIから判別できます。
RESTについては以下の記事で解説しています。
REST APIについて理解する
Chapter4. 現実の複雑さに対応する
データの加工とログイン機能を実施します。
データベースにテーブルやカラムを追加したり修正する作業はマイグレーションという機能を使用して行います。
このマイグレーションを使ってデータベースに不正な値を登録できないようにデータを制限する事も可能です。
例えば特定のカラムでNULLを許容しない場合は次のように記述します。
class CreateTasks < ActiveRecord::Migration[7.0] def change create_table :tasks do |t| t.string :name, null: false t.text :description t.timestamps end end
この方法ではデータベースにデータを格納する時にエラーが発生します。
ですがデータベースに保存する前にもデータの検証を行う事が一般的です。
Railsではvalidatesメソッドを使ってモデルの検証を行う事が出来ます。
先程のNULLを許容しないvalidatesの書き方は次の通りです。
class Task < ApplicationRecord validates :name, presence: true end
ログイン機能はセッションを使用して実装されます。
サーバー側でログイン認証が完了した際に、セッションIDを発行しHTTPレスポンスとしてクライアント側に送ります。
クライアントはこのセッションIDをクッキーに保存し、HTTPリクエストを送信する際にセッションIDを含めて送ります。
サーバー側はこのセッションIDを参照することで、クライアントがログインしているかどうかを判断できます。
Chapter5. テストをはじめよう
Rspecを使ってテストを実装します。
Rspecを活用してテストを組み込むことにより、確かに時間投資は必要とされますがその見返りとして得られるメリットは計り知れません。
特にリファクタリング時にその機能性を保持しつつ改善を図る場合、テストが正常に通過することでその機能が確実に動作するという保証を得ることが出来ます。
テストには様々な種類がありますが、中でも重要なのがシステムテストです。
システムテストを行うことで、ブラウザを介したアプリケーション全体の挙動を外から確認することができます。
これにより予期せぬタイミングで特定の機能が停止するなどの問題を検出し、不具合の可能性を減らす事ができます。
個人的にはこのChapterが一番難しく感じました。 理解が浅いので別途専用の教材で学習予定です。
Chapter6. Railsの全体像を理解する
ルーティングの仕組みをRESTの考え方に基づいて学習します。
ルーティングの定義に非常に便利なresourcesメソッドがあります。
これは1発で7つのアクションに対応するルートを定義できます。
# 個別でルートを定義した場合 get '/tasks', to: 'tasks#index' get '/tasks/:id', to: 'tasks#show' get '/tasks/new', to: 'tasks#new' post '/tasks', to: 'tasks#create' get '/tasks/:id/edit', to: 'tasks#edit' patch '/tasks/:id', to: 'tasks#update' delete '/tasks/:id', to: 'tasks#destroy' # resourcesを使用した場合(上と同じ意味) resources :tasks
他にもアセットパイプラインを学習します。
これはCoffeeScript、SCSS、ERB、Slimで記述されたコードをコンパイルして、ブラウザが認識できるJavaScript、CSSファイルに集約する機能です。
特別設定せずとも自動で行われ、ファイル名にダイジェストと呼ばれるコード内容から算出したハッシュ値が付与されます。
コード内容が変わればハッシュ値も変わりますから、ブラウザのキャッシュの影響で修正が反映されないという問題を防ぐ事が出来ます。
Chapter7. 機能を追加してみよう
タスク管理アプリに便利な機能を追加します。
- 検索機能
- ソート機能
- メール送信機能
- ページネーション
- 画像登録機能
これは全てgemを使って実装することができ、自分で1から機能を実装する必要はありません。
Chapter8. RailsとJavascript
RailsでJavaScriptを動かします。
Ajaxという技術を活用することで、ページ全体を再読み込みすることなく特定のページ部分を更新できることを学びました。
またWebpackerとYarnを用いてReactを導入します。
WebpackerはRails 6まで推奨されていましたが、Rails 7からはJavaScriptやCSSのバンドリングにjsbundling-railsやcssbundling-railsの利用が推奨されているようです。
Chapter9. 複数人でRailsアプリケーションを開発する
チーム開発における注意点について説明されています。
特にGitの操作において注意すべき点が紹介されており、リベースやマージしたコミットをpushする際の確認ポイントについて学ぶことができました。
Chapter10. Railsアプリケーションと長く付き合うために
Rubyやgemのアップデートを行う際は、動作に影響が出ないか十分に検討する必要があります。 本書では小さなバージョンアップを推奨しており、アプリケーションはリリースしたら完成ではなく、継続的にバージョンアップすることが大切であると理解出来ました。
難しかったこと
個人的にSlimの書き方に慣れず苦戦しました。
erbは記述量が増えますが、直感的で分かりやすいなと感じました。
達人に学ぶ DB 設計 徹底指南書を読み終えての感想
達人に学ぶ DB 設計 徹底指南書 を読了したので、感想や分かった事について整理していきたいと思います。
良かったところ
この技術書の一番良かったと感じるポイントは何と言ってもその分かりやすさです。
タイトルに「達人」や「徹底指南書」と書いてあるので、取っ付きにくい難しい文章で書かれているのではないかとビビっていましたが、いざ読んでみるとそんな事は無く、例えや言い回しが秀逸で非常に分かりやすいです。
その他自分が感じた良かった点です。
- 以前の章の内容が出てきても解説が挿入されており、都度ページを捲って戻る必要がない
- DB 設計におけるアンチパターンの事例が紹介されており、やってはいけないDB設計が分かる
- 同じテーブルを使い回す事が多い為、テーブルの構造を毎回理解しなくて良い
- 重要な事については何度も説明されている
悪かったところ
練習問題によってはハンズオンで取り組み内容もありますが、基本的にこの技術書は読むだけです。個人的には手を動かしながらの方が分かりやすいので、ただただ読む作業が辛くはありました。
学んだこと
第1章 データベースを制する者はシステムを制す
データベースには複数のモデルが存在します。
主流なモデルは本書で取り扱われている RDB です。
| モデル | データ管理方法 |
|---|---|
| リレーショナルデータベース(RDB) | 二次元表で管理 |
| オブジェクト指向データベース(OODB) | オブジェクトとして管理 |
| XML データベース(XMLDB) | XML として管理 |
| キー・バリュー型ストア(KVS) | Key と Value の組み合わせで管理 |
| 階層型データベース | 階層構造で管理 |
システム開発は以下の順で進めるが、段階的に進める方法と循環的に進める手法がある。
- 要件定義
- 設計
- 実装
- テスト
| モデル | フロー | 用途 |
|---|---|---|
| ウォーターフローモデル | 一方向 | 大規模システム |
| プロトタイピングモデル | 循環的 | 小規模システム |
データベースの構造はスキーマで表され、 3 つのレベルに分けられたスキーマは 3 層スキーマと呼ばれる。
| スキーマ | 特徴 |
|---|---|
| 外部スキーマ(外部モデル) | ユーザーから見たデータベース |
| 概念スキーマ(論理データモデル) | 開発者から見たデータベース |
| 内部スキーマ(物理データモデル) | DBMS から見たデータベース |
第2章 論理設計と物理設計
概念スキーマを定義する設計を論理設計と呼びます。
論理設計のステップは以下の流れです。
- エンティティの抽出
- エンティティの定義
- 正規化
- ER 図の作成
内部スキーマを定義する設計を物理設計と呼びます。
物理設計のステップの以下の流れです。
- テーブルの定義
- インデックス定義
- ハードウェアのサイジング
- ストレージの冗長構成決定
- ファイルの物理配置決定
第3章 論理設計と正規化
キー
テーブルには基本的にキーと呼ばれる1行のレコードを一意に識別できる鍵が存在します。
| キー | 意味 |
|---|---|
| 主キー | 一意のレコードを識別するキー |
| 複合キー | 複数の列を組み合わせて作るキー |
| 候補キー | 主キーとして利用できる候補のキー |
| スーパーキー | 主キーに非キー列を付加したキー |
| 外部キー | テーブルに対して一種の制約を課すキー |
外部キーの制約で代表的なものは次の 3 つです。
| 制約 | 効果 |
|---|---|
| NOT NULL | データに NULL を格納する事を禁止する |
| UNIQUE | 列で重複する値を禁止する |
| CEHCK | データに格納する値の範囲を制限する |
正規形
正規形とはデータの冗長性を排除し、一貫性と効率性を保持するためのデータ形式です。 正規形のレベルは第 5 までありますが、実際の業務では第 3 正規形まで考える事がほとんどみたいです。
| 正規形 | 定義 |
|---|---|
| 第 1 正規形 | 1 つのセルの中には 1 つの値(スカラ値)しか含まない |
| 第 2 正規形 | 部分関数従属を解消する |
| 第 3 正規形 | 推移的関数従属を解消する |
| ボイスーコッド正規形 | 非キーからキーへの関数従属を解消する |
| 第 4 正規形 | 関連エンティティに含まれる関連は 1 つだけにする |
| 第 5 正規形 | 関連エンティティと関連を 1 対 1 に対応させる |
第4章 ER 図
ER 図はテーブル同士の関係性を図に表したものです。
代表的な記法としては IE 表記法と IDEF1X があります。
第 5 章 論理設計とパフォーマンス
実は正規化を行うと SQL の処理が遅くなるという副作用があります(更新速度は上がる)。
理由は正規化は基本的にテーブルを分割して行いますが、そうなると必要なデータを取り出す時にテーブル同士を結合する必要があります。
この結合が非常にコストが高い操作であり、結合するテーブル数やレコード数が増えれば増えるほど処理時間が遅くなります。
では正規化は行わない方がいいのでは無いかという話になってきますが、本書では可能な限り高次にする事が大原則と書かれています。
データ整合性とパフォーマンスはトレードオフの関係にあり、正規形を非正規形より高パフォーマンスにする事は不可能です。
整合性をどこまで落としてパフォーマンスをどこまで上げるか、要件を同時に満たせる平衡点を探し出す能力が求められます。
第 6 章 データベースとパフォーマンス
インデックスは技術書の索引のようなもので、特定のデータのポインタを意味します。
最も頻繁に使われるインデックスは B-tree インデックスです。 名前の通り、木構造でデータを保持します。 最下層のリーフと呼ばれるノードだけが実データに対するノードを持っています。
B-tree インデックスの特徴は以下の通りです。
均一性
B-tree の構造は平衡木でルートからリーフまでの距離が一定で、どんなキー値を使っても、常にリーフまでの距離が一定であるため、処理速度が一定になります。
持続性
テーブルのデータ量が増えても、検索や更新にかかる時間はほとんど増えません。
処理汎用性
他のインデックスと違い、挿入、更新、削除、検索の処理時間が大体同じです。例えばビットマップインデックスは検索性能は B-treeインデックス を上回りますが、更新に膨大な時間が掛かります。
非等値性
等号(=)による検索のみならず、不等号(<、>、<=、>=)や BETWEEN の範囲検索に対しても高速化を可能にします。ただし B-treeインデックス は特定データのポインタを持つため、否定条件(<>、!=)は、特定データ以外のデータとなり、絞り込みが効かず、意味がありません。
親ソート性
B-tree インデックスは構築時にキー値をソートして保持するため、B-tree インデックスが存在する列を ORDER BY で指定すると、ソート処理をスキップできます。
インデックスは無闇に作ればいいという訳ではありません。
インデックスを作る指針は以下の通りです。
- 大規模なテーブルに対して作成する
- データ量が少ない場合はフルスキャンの方が速いです
- カーディナリティの高い列に作成する
- データ量の時と同じように恩恵がありません
- SQL 文で WHERE 句、又は結合条件に使用されている
- そもそもその列が使われないと作る必要がありません
第7章 論理設計のバッドノウハウ
バッドノウハウの事例から見えた設計時のポイントは次の通りです。
- 配列型を使用しない。第 1 正規形を守る
- 列は意味的に分割できる限り、なるべく分割する(人名:田中大地 → 姓:田中 名:大地)
- 同一の列が 2 つの意味を持つダブルミーニングを避ける
- 水平分割ではなくパーティションを使う
- 垂直分割ではなくデータマート、又はサマリテーブルを作成する
第8章 論理設計のグレーノウハウ
グレーノウハウの事例から見えた設計時のポイントは次の通りです。
- いかなるテーブルであっても主キーを作成する
- 多段ビューは原則として 1 段にとどめる
- 既存のデータベースを改修する際は、データベース設計前にデータクレンジングを行う
第9章 一歩進んだ論理設計
RDB は木構造のデータを扱うのが苦手で、この弱点を克服するために複数のモデルが存在します。 木構造のデータとはトップのデータ(ルートノード)から下にデータが分岐して吊り下がっているようなデータ構造の事です。
| モデル | 特徴 |
|---|---|
| 隣接リスト | ノードのレコードに親ノードのポインタを格納する |
| 入れ子集合 | ノードを円と見なして、ノード間の階層関係を円の内包関係で表す |
| 入れ子区間 | 入れ子集合モデルの座標が整数なのに対して実数を用いる |
| 経路列挙 | ノードをディレクトリと見なし、各ノードまでの経路をパスで表す |
難しかったこと
スッキリわかる SQL入門を読んだ感想
今回SQLの勉強をするにあたり、スッキリわかるSQL入門(第3版)を教材としてインプットを行いました。
一通り読み終わったので感想を書いていきます。
SQLとは
感想に入る前にSQLについて軽く触れておこうと思います。
SQLは「Structured Query Language」の略称であり、データベースを作成・操作するための標準的なプログラミング言語です。 データベースとは顧客情報や商品リストなどのデータ集合を構造化し、効率的な管理や検索を可能にするシステムです。 データベースには複数の種類が存在しますが、本書ではリレーショナルデータベース(RDB)について説明されています。
良かったところ
- 講師と生徒の会話形式で話が進めため、初学者に寄り添った内容になっており分かりやすい
- 本書専用のクラウドサービス(dokoQL)が提供されており、環境構築なしにSQL文の作成・実行が行える
- 練習問題が256問と豊富にあり、手を動かしてSQL文を作成するため頭に入りやすい
- SQLの基本からデータベース設計までの内容が網羅されており、一冊である程度知識がつくようになっている
微妙だったところ
- dokoQLに答え合わせ機能がなく、入力内容と答えが一致しているか目視で確認する必要がある
- dokoQLのメイン画面から次の例題に移行するボタンがなく、毎度 ライブラリ>章の選択>例題をクリックする必要がある
- 練習問題で分からず答えを見た時に、解説がないので理解が出来ない場合がある(なぜその答えになるのか分からない)
学んだこと
第1章 はじめてのSQL
第2章 基本文法と4大命令
- 格納できるデータの種類(データ型)
- 4大命令(
SELECT・UPDATE・DELETE・INSERT)の基本構文
第3章 操作する行の絞り込み
- 演算子による操作する行の絞り込み
- NULLは何と比較してもtrueとならない(NULL同士でも)
- テーブルに存在する主キーの意味やルール
第4章 検索結果の加工
- SELECT分で取得したデータの加工法(
DISTINCT・ORDER BY・OFFSET...etc) - 集合演算子の使い方
第5章 式と関数
- 計算式を評価すると計算結果に化ける
- 文字列に
||や+計算式を用いる事で文字列の連結ができる - 関数の使い方とどのような関数があるか
第6章 集計とグループ化
- 集計関数(
SUM・MAX・MIN・AVG・COUNT)で検索結果のデータを集計 GROUP BYでグループ別に集計を行えるHAVINGとWHEREの違い
第7章 副問い合わせ
- SQL文の中に別のSELECT文を記述するサブクエリ(副問い合わせ)
- 単一行副問い合わせ(1つの値) →
SELECTSET - 複数行幅問い合わせ(n行1列) →
INANY - 表形式幅問い合わせ(n行m列) →
FROMINSERT
第8章 複数テーブルの結合
- 通常テーブルは複数のテーブルによって管理される
- 他の行と関連付けするために外部キーを使ってテーブルを結合する
- テーブル結合する上でのルール
第9章 トランザクション
- 複数のSQL文を1つのSQL文として扱うことができる(トランザクション)
- トランザクションは全て実行されたか、1つもされていないかのどちらかの状態になる
- トランザクションの分離レベルによって副作用(ダーティーリード・反復不能読み取り・ファントムリード)が起こる危険性を選択できる
- 分離レベルを厳密なものにしすぎると、DBMSの速度が損なわれる
第10章 テーブルの作成
- SQL文の種類
- データテーブルの作成・削除・更新
- 予期しない値が格納されないように制約を設ける(
NOT NULL・UNIQUE・CHECK)
第11章 さまざまな支援機能
- インデックスは主に列に対する検索の対象となった時に高速となる
- ビューはSELECT文の結果を仮想的なテーブルとして扱う事ができるが、その実態は単なるSELECT文なのでDBMSの負荷は変わらない(記述量は減る)
シーケンスは主キーが連番の場合などに自動で連番を生成してくれる
正確なデータ処理にはACIDが求められる
- Atomicity(原子性): 処理が中断されても中途半端にならない
- Consistency(一貫性): データの内容が矛盾した内容にならない
- Isolation(分離性): 複数の処理を同時実行しても副作用がない
- Durability(永続性): 記録した情報は消滅せず保持され続ける
第12章 テーブルの設計
- 概念設計・論理設計・物理設計によるデータベース設計
- ER図の見方
- 第1~3正規形でテーブルを適切に分割する
難しかったところ
本書では第一部(1~4章)と第二部(5~12章)に構成が分かれており、第一部ではSQLの基本構文、第二部では式や関数を使ったより実践的なSQLの構文を学習します。
第二部からは個人的には全て難しく感じましたが、特に第8章の「複数テーブルの結合」から中々理解が追いつきませんでした。
というのも、上から下へ順番に処理が行われないSQL文で、この行の時点ではこのような表形式になっていると頭の中でイメージできなくなってしまったからです。
最終章はデータベース設計について学ぶのですが、これは一番難しかったですね。主に以下の内容で頭がかなり混乱しました。
- ER図の出現
- データーベース設計における制約の多さ
- 正規化の概念
本来ボリュームがある内容を1章で纏められているため、ここはデータベース設計の教材を別で用意する事をお勧めします。 SQLについては問題形式で練習ができるサイトがあるようなのでそこで鍛えようと思っています。
Webの仕組みについて学習した内容を丁寧に整理してみた
はじめに
今回はWebの仕組みついてインプットを行ったので内容の整理も兼ねて記事を作成します。
この記事では普段私達が見ているWebページのデータはどこに存在するのか?
またそれをどうやって特定しているのか?に焦点を当てて紐解いていこうと思います。
データは遠くにあるが繋がっている
私達が普段見ているWebページのデータはWebサーバと呼ばれるコンピュータに保存されています。
Webサーバは世界中に点在しており、Webサーバが相互に接続され情報やデータのやり取りが可能な仕組みをネットワークと呼びます。
このネットワークにWebクライアントと呼ばれるPCやスマホを通して接続し、Webサーバから受け取ったデータをChromeやSafariなどのWebブラウザが画面に描画することでWebページを見ることが出来るのです。
URLとは何なのか
ネットワークのおかげでWebクライアントからWebサーバにアクセスできる事が分かりましたが、データは数あるWebサーバの中の1つに格納されているため、このサーバを特定する必要があります。 その役割を担うのが URL (uniform resource locator)です。
URLは次にように構成されています。
http://www.example.jp/home/index.html
- スキーム (http)
- どの通信プロトコルを使用するかを表します。
- ローカル名 (www)
- Webサーバの名前を表します。
- ドメイン名 (example.jp)
- Webサーバの位置を示します。
- パス名 (/home/index.html)
- Webサーバ内のデータの位置を示します。
主要な用語の説明を行なっていきます。
通信の決まり事
通信プロトコルとはコンピュータ同士が通信を行う上での規則・ルールの事です。
httpの前にTCP/IPの説明を行います。
TCP/IPはネットワーク全体でデータを送受信するための基盤のプロトコルで、データのパケット化(データの分割)やIPアドレスからWebサーバの住所の特定など、ネットワーク通信を行う上で必要不可欠なプロトコルです。
httpプロトコルはこのTCP/IPプロトコルの中の1つで、シンプルなプロトコルである事が特徴です。
このようにhttpはただただリクエストされたコンテンツをレスポンスするだけのシンプルなプロトコルです。
正直プロトコルに関してはルールと言えどざっくりとし過ぎていて、表面上しか理解できていません。
時が来たら勉強したいですね。
Webブラウザに表示されるURLは仮の姿
Webサーバの位置は本来IPアドレスと呼ばれる3桁の数字×4つで構成された番号によって特定できます。
192.168.255.255
ですがURLにその様な数字は見当たりません。
これはIPアドレスが人の目には識別が難しいためドメイン名に置き換えられているからなんです。
当然このドメイン名ではWebサーバの位置を特定できませんので、IPアドレスに変換する必要があります。
そこでDNSサーバと呼ばれるドメイン名とそれに関連付けられたIPアドレスのマッピングを持つサーバに「このドメイン名のIPアドレスを教えてください」と問い合わせることで、DNSサーバがこっそりと教えてくれます。
この問い合わせはユーザが手動で行う必要はなく、リクエスト時に自動で行われます。
普段私たちがあまり目にする事はありませんが、実はまだURLには位置を表す文字が隠されています。 Webサーバの位置とデータの位置まで分かってるのにこれ以上何が必要なんだ!?と驚かれるかもしれませんが、それを理解するためにはWebサーバの構造を知る必要があります。
Webサーバの構造
まずWebサーバはコンピュータであり、それ単体ではレスポンスを返す事は出来ません。 皆さんのPCやスマホもアプリケーションを通して動画を見たりゲームをしたりしますよね。 Webサーバにもアプリケーションが組み込まれており、そのアプリケーションでWebクライアントのリクエストを処理したり、レスポンスを返したりしているわけです。
ややこしいのですが、Webサーバ(コンピュータ)にはWebサーバ(アプリケーション)が入っており、このWebサーバ(アプリケーション)がHTTPリクエストを処理しています。
そしてアプリケーションはWebサーバだけでなく、アプリケーションサーバやデータベースサーバなど複数のアプリケーションが稼働している事が一般的です。
Webサーバ内にはたくさんのアプリケーションが稼働しているんですね。
「Webブラウザに表示されるURLは仮の姿」で説明した通り、URLからサーバの位置、データの位置まで特定する事が出来ましたよね。
ですがこの章でWebサーバ内には複数のアプリケーションが存在する事がわかり新たな問題が生まれます。
そうです。Webサーバの位置を特定してもどのアプリケーションにリクエストを送ればいいのか分からないのです!
この問題に対処するためにURLにはアプリケーションを区別するポート番号が隠されています。
アプリケーション毎に規格化されたポート番号があり、たとえばHTTPは通常80番ポートを使用します。
http://www.example.jp/home/index.html:80
このためスキームとして"http"が指定されている場合、ポート番号":80"は自動的にURLの末尾に追加されるようになっています。
これで本当にデータの位置まで特定する事ができましたね。
次に本来の話の趣旨とはズレますが、Webブラウザがどうやってデータを保持しているか説明します。
Cookieで状態を管理する
httpはシンプルな通信プロトコルであると説明しましたが、そのシンプルさが故に状態を管理する事ができません。
例えばユーザがショッピングサイトでログインページからログインしたとします。ですがhttpは過去の情報を保持していないので、ユーザが新たにリクエストするとWebサーバはユーザがログインしているのか、していないのか判断できないのです。
このように状態を保持しないプロトコルをステートレス・プロトコルといい、逆にFTPなど状態を保持するプロトコルはステートフル・プロトコルと呼ばれます。
ですが普段皆さんが使っているWebアプリではログインすれば商品を買い物カゴに追加できるし、購入することもできますよね。これはCookieと呼ばれる技術でデータを保存しているからなんです。
Cookieの仕組みを説明します。
- Webクライアントはログインデータを含めたhttpリクエストをWebサーバに送ります。(Webサーバにデータを送る方法としてGETメソッド、POSTメソッドが挙げられますがここでは割愛します)
- Webサーバは受け取ったログインデータをユーザリストの照合にかけ、照合が成功した場合Cookieにログインデータを格納します。このCookieはhttpレスポンスヘッダに格納され、Webクライアントへと送られます。
- WebクライアントはhttpレスポンスヘッダからCookieを取り出し、Webブラウザへ保存します。
- 再度Webクライアントがリクエストを送る時に、httpリクエストヘッダから受け取ったCookieをそのまま格納します。
- WebサーバはhttpリクエストヘッダからCookieを取り出し、再びユーザリストの照合にかける事でユーザがログインしているか判断します。
この一連のサイクルにより、一度ログインするとCookieにデータが保存され状態の管理を実現しています。
ここではログイン画面を例に出しましたが、現代ではセキュリティの観点からCookieにユーザIDやパスワードを直接保存する事はありません。
セッションIDと呼ばれるユーザ情報に紐付けられたIDを発行しCookieに保存するのが主流です。
このセッションIDを仮に盗聴されたとしても、紐付けられたデータが無いと何の意味も無いからですからね。
最後に
かなり省略しましたが、Webの基本的な仕組みが少しでも伝わっていれば嬉しいです。
この分野はかなり奥が深いと思うので、少しずつ学習していければいいなかなと思っています。
また学習した際には記事に纏めたいと思います。
挫折続きの僕がHappinessChainに入会して感じたこと
初めに
みなさんは人生において挫折経験はありますか?
現在25歳の僕は色々な物事に手を出しては挫折を繰り返してきました。
直近では動画編集ブームに乗り独学でスキル習得を試みるもののすぐに投げ出す。
趣味でピアノを始めようと電子ピアノを買ったが全く弾かずに売り払う。
原因は全て自分の怠惰によるものです。
やる気が出ない。仕事で疲れて勉強ができない。好きな事を辞められない。
自分に言い訳ばかりして生きてきました。
そんな自他共に認める自堕落な僕ですが、HapinessChain(以下HC)というプラグミングスクールに入会して3週間が経ちました。
今回は実際に入会して感じたことをテーマに記事を書いてみようと思います。
自己紹介
普段はITと全く関係のない業界で仕事をしている25歳のサラリーマンです。
昔からゲームが好きで休日は家に篭ってずっとVALORANTというゲームをしていました。
他には音楽が好きで大好きだったヨルシカのライブに最近行きました。\ ヨルシカハイイゾ/
余談ですがHC入会時にゲーム欲とおさらばするためゲーミングPCを売り払いました(悪魔の代償)。
HappinessChainに入ったきっかけ
プログラミングに興味を持ったきっかけは、時間・場所に囚われない働き方に魅力を感じ、自分もその環境で働きたいと思ったからです。
挫折し続けた過去から独学でやっても絶対に投げ出してしまう事は分かっていたので、プログラミングスクールの入会を検討しました。
ですがウェブ検索で上位にくるスクールはいずれも「入会費が高い」「卒業しても実務レベルには程遠い」「過酷な労働環境の就職先を紹介される」など、ネガティブな経験談が多く入会する事に躊躇いがありました。
そんな最中Qiitaでランキングに上がっていたHCに関する記事をたまたま見かけ、他のプログラミングスクールには無い多くの魅力が決め手となり入会を決意しました。
HC入会の決め手となった要素をいくつか挙げておきます。
- 入会費無料(現在は入会費あり)
- 料金月額制
- WEBエンジニアになる為の明確なロードマップがある
- モダンな技術を学べる
- 現役エンジニアの方にコードレビューをしてもらえる
- 現場レベルのアウトプット課題がある(その分ハード)
入会後の環境の変化
まず入会して驚いた事はHC生徒さんのやる気の高さです。
HCではその日に学習した内容は日報に纏めて提出する事が推奨されています。
この日報は他の生徒さんのも見る事ができ、学習時間やカリキュラムがどこまで進んでいるか分かるようになっています。
そのため同時期に入会した方と比べて自分の学習が遅れていればすぐに分かります。
ですが生徒さんによってはHC入会前に学習したカリキュラムは飛ばしていたりするので焦る必要はありません。
日報を見る事で焦るのではなく自分も頑張らねば!というプラスな気持ちになりますし、他の方の頑張りを見ると自分のやる気も自ずと上がります。
学習を継続する上で大切な事はモチベーション維持であり、そこに環境が与える影響を身を持って実感しました。
HapinessChainの魅力
黙々と頭を悩ませながら学習を進めていくという点では、HCの学習スタイルは独学に近しいものがあります。
ですが自分で調べた上で分からない事があればメンターの方が優しく教えてくれますし、個人的に一番の大きな魅力は明確なロードマップがある事です。
独学の場合は本当にこの勉強法でいいのか?このまま勉強してて意味があるのか?と、余計な事を考えてしまいモチベーションの低下に繋がる事があると思います。
その点HCは現役エンジニアの方がロードマップを作成しており、インプットとアウトプットが効率よく行えるようにコースが組まれているので安心して学習に専念できます。
コミュニティ面ではXやSlackでHCの生徒さんと繋がる事ができます。
HCには心温かい方が多くXのいいねやtimesでコメント・スタンプを残してくれるおかげでモチベーションが上がりますし、学習を継続していくという観点において1つの魅力だと感じます。
実際に自分は学習して感じた事をXでポストするのですが、いいねが来ると嬉しいですし明日も継続して投稿しようと思えます。まだ技術的な事は呟けていませんが、これから発信していけるように頑張りたいです。
他のプログラミングスクールに入った事がないので詳細な比較はできませんが、この2つはとても魅力的な要素であると感じています。
最後に
まだ3週間という短い期間ですが、僕が素直に感じた事を記しました。
過去に何度も挫折を繰り返してきた僕ですが、この環境であれば毎日継続していける自信があります。
これが人生のラストチャンスだと思い、過去の自分に戻らないようにこれからも頑張ります。
この記事が入会に迷っている方の参考になれば幸いです。