Ruby on Railsなどの多くのフレームワークが採用しているMVCアーキテクチャはModel・View・Controllerの3つで構成されており、それぞれが役割を持っています。
データベースとのやり取りを引き受けているのがモデルです。
これらの役割についてしっかりと学ぶことでRailsの理解が一気に深まりますので、是非マスターしましょう。

RailsではActive RecordというORMが使われており、データベースのデータをオブジェクト指向に対応させて取り扱うことができます。
このActive Recordの理解を深めることで、RailsのModelを使いこなしましょう!

オンラインでプログラミング学習を始めてみたいなら、米国シリコンバレー発祥の世界最大級のオンライン学習プラットフォーム、Udemyがおすすめです。
こちらの記事で紹介するフレームワーク、Ruby on Railsも学ぶことができます!
よくわかるRuby on Rails入門 オンライン講座

Active Recordの役割

Ruby on RailsはMVCアーキテクチャというデザインパターンを取り入れたフレームワークです。
その中でもモデルに該当する機能のことをActive Recordといいます。

通常の開発では、ビジネスロジックを解決するためのプログラムとは別に、データベースとのレコードのやり取りを行うためのSQLを記述する必要があります。
しかしRailsではActive Recordの機能により、SQLを一切記述せずとも開発を行うことができます。

Active Recordは、データベースと接続してレコードとプログラム上のオブジェクトを結びつけるインタフェースです。
開発者はSQLの代わりに、オブジェクト指向に基づいたプログラムでデータベース操作ができるようになります。
このような技術のことをORM(Object/Relation Mapping)といいます。

さらに、モデル同士のアソシエーション(関連付け)の表現やバリデーションなども行うこともできます。

Active RecordオブジェクトとデータベーステーブルにはRailsのCoC(Convention Over Configuration)の理念に従った、命名規則があります。
Active Recordオブジェクトの名前は、アッパーキャメルケース(単語の頭文字を大文字にして連結する規則)で名詞の単数形にします。
そのモデルに対応するテーブルの名前は、スネークケース(単語をアンダースコアで連結する規則)で名詞の複数形にします。

Railsのgenerateコマンドを使えば、基本的にはこのルールに従った名前でモデルやテーブルが生成されます。

Active Recordオブジェクトデータベーステーブル
Userusers
FavoriteProductfavorite_products
PersonPeople
Active Recordの命名規則の例

現場を意識した本格的な学習カリキュラムで、本物のスキルを身につけたいならこちら!
困ったときにいつでも相談できるバディ制度でわからないこともすぐに解決できます。
プログラミングスクールといえば【RUNTEQ】

モデルの基本操作

Active Recordを使って、データベースのCRUD操作の基本を見ていきます。
CRUDとは、基本的なデータベースに対する4つの操作の頭文字をとった概念です。
データベースに登録されるデータ(レコード)の扱いは、この4つの操作ですべて表現できます。

Createレコードの登録
Readレコードの取得・参照
Updateレコードの更新
Deleteレコードの削除

Active Recordでモデルを操作すると、内部的にSQLが発行されてデータベースにアクセスされます。
発行されたSQL文も確認しながら進めていきましょう。

ここからはイベント管理アプリを作ってActive Recordの操作を紹介していきます。
同じ環境で試してみたい方は、以下に従ってコマンドを実行しておいてください。

# 新規プロジェクトの作成
$ rails new event-app
$ cd event-app

# scaffoldコマンドでEventモデル周りのファイルを自動生成
$ rails generate scaffold event name:string hold_date:date

# データベースの作成
$ rails db:create

# マイグレーションの実行
$ rails db:migrate

また、Rubyのコンソール(irb)を使って動作確認を行っていくため、consoleコマンドでコンソールの画面を開いておきましょう。

$ rails console
Loading development environment (Rails 6.1.4.1)
irb(main):001:0>

Create

createメソッドでは、データベースに対してレコードの新規登録を行うことができます。

irb(main):001:1* Event.create(
irb(main):002:1*   name: 'sample event',
irb(main):003:1*   hold_date: Time.parse('20220101'),
irb(main):004:0> )
   (1.4ms)  SELECT sqlite_version(*)
  TRANSACTION (0.1ms)  begin transaction
  Event Create (1.3ms)  INSERT INTO "events" ("name", "hold_date", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "sample event"], ["hold_date", "2022-01-01"], ["created_at", "2021-11-06 05:37:38.448063"], ["updated_at", "2021-11-06 05:37:38.448063"]]
  TRANSACTION (3.8ms)  commit transaction
=>
#<Event:0x000001c4f8c74b88
 id: 1,
 name: "sample event",
 hold_date: Sat, 01 Jan 2022,
 created_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00,
 updated_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00>

また、newメソッドで新しいインスタンスを生成し、値を代入した後にsaveメソッドでレコードを追加する方法もあります。

irb(main):005:0> event = Event.new
=> #<Event:0x000001c4f785fad0 id: nil, name: nil, hold_date: nil, created_at: nil, updated_at: nil>

irb(main):006:0> event.name = 'sample event 2'
=> "sample event 2"

irb(main):007:0> event.hold_date = Time.parse('20230101')
=> 2023-01-01 00:00:00 +0900

irb(main):008:0> event.save
  TRANSACTION (0.2ms)  begin transaction
  Event Create (1.2ms)  INSERT INTO "events" ("name", "hold_date", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "sample event 2"], ["hold_date", "2023-01-01"], ["created_at", "2021-11-06 05:39:27.708632"], ["updated_at", "2021-11-06 05:39:27.708632"]]
  TRANSACTION (3.9ms)  commit transaction
=> true

以下は反復処理によって繰り返しデータを投入する例です。

irb(main):009:1* (1..10).each do |i|
irb(main):010:2*   Event.create(
irb(main):011:2*     name: "The #{i}th event",
irb(main):012:2*     hold_date: Time.parse('20200101').since(i.years),
irb(main):013:1*   )
irb(main):014:0> end
  TRANSACTION (0.1ms)  begin transaction
  Event Create (1.8ms)  INSERT INTO "events" ("name", "hold_date", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "The 1th event"], ["hold_date", "2021-01-01"], ["created_at", "2021-11-06 05:41:18.624319"], ["updated_at", "2021-11-06 05:41:18.624319"]]
  TRANSACTION (5.9ms)  commit transaction
...

コンソールから抜けるには、exit()と入力します。

データベースにレコードが登録されているか確認するため、dbconsoleコマンドを使いSQLを発行してみましょう。

$ rails dbconsole
SQLite version 3.35.2 2021-03-17 19:07:21
Enter ".help" for usage hints.
sqlite> SELECT * FROM events;

1|sample event|2022-01-01|2021-11-06 05:37:38.448063|2021-11-06 05:37:38.448063
2|sample event 2|2023-01-01|2021-11-06 05:39:27.708632|2021-11-06 05:39:27.708632
3|The 1th event|2021-01-01|2021-11-06 05:41:18.624319|2021-11-06 05:41:18.624319
4|The 2th event|2022-01-01|2021-11-06 05:41:18.637409|2021-11-06 05:41:18.637409
5|The 3th event|2023-01-01|2021-11-06 05:41:18.647427|2021-11-06 05:41:18.647427
6|The 4th event|2024-01-01|2021-11-06 05:41:18.658049|2021-11-06 05:41:18.658049
7|The 5th event|2025-01-01|2021-11-06 05:41:18.669229|2021-11-06 05:41:18.669229
8|The 6th event|2026-01-01|2021-11-06 05:41:18.680486|2021-11-06 05:41:18.680486
9|The 7th event|2027-01-01|2021-11-06 05:41:18.700573|2021-11-06 05:41:18.700573
10|The 8th event|2028-01-01|2021-11-06 05:41:18.717142|2021-11-06 05:41:18.717142
11|The 9th event|2029-01-01|2021-11-06 05:41:18.734082|2021-11-06 05:41:18.734082
12|The 10th event|2030-01-01|2021-11-06 05:41:18.770335|2021-11-06 05:41:18.770335

Read

レコードを検索するReadでは取得したいデータに応じて柔軟な操作が必要となります。
全ての組み合わせは網羅しきれませんが、頻出のパターンを紹介していきますので要件に合わせて応用していけるようにしましょう。

IDによる検索

findメソッドではidを指定して1件のレコードを取得します。
条件に合うレコードが見つからない場合、ActiveRecord::RecordNotFound の例外を発生させます。

irb(main):001:0> Event.find(1)
   (1.0ms)  SELECT sqlite_version(*)
  Event Load (0.3ms)  SELECT "events".* FROM "events" WHERE "events"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=>
#<Event:0x0000023615cb44e8
 id: 1,
 name: "sample event",
 hold_date: Sat, 01 Jan 2022,
 created_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00,
 updated_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00>

irb(main):002:0> Event.find(100)
  Event Load (0.3ms)  SELECT "events".* FROM "events" WHERE "events"."id" = ? LIMIT ?  [["id", 100], ["LIMIT", 1]]
C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/activerecord-6.1.4.1/lib/active_record/core.rb:338:in `find': Couldn't find Event with 'id'=100 (ActiveRecord::RecordNotFound)

ID以外による検索

find_byメソッドではid以外のカラムから条件に合う1件のレコードを取得します。
条件に合うレコードが複数ある場合、最初の1件のみ取得されます。
また、条件に合うレコードが見つからない場合、nilを返します。

irb(main):001:0> Event.find_by(name: 'sample event')
  Event Load (0.4ms)  SELECT "events".* FROM "events" WHERE "events"."name" = ? LIMIT ?  [["name", "sample event"], ["LIMIT", 1]]
=>
#<Event:0x00000236156af070
 id: 1,
 name: "sample event",
 hold_date: Sat, 01 Jan 2022,
 created_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00,
 updated_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00>

irb(main):002:0> Event.find_by(name: 'undefined event')
  Event Load (0.3ms)  SELECT "events".* FROM "events" WHERE "events"."name" = ? LIMIT ?  [["name", "undefined event"], ["LIMIT", 1]]
=> nil

全データの取得

allメソッドではレコードを全件取得します。

irb(main):001:0> Event.all
  Event Load (0.5ms)  SELECT "events".* FROM "events"
=>
[#<Event:0x0000023616d9cf80
  id: 1,
  name: "sample event",
  hold_date: Sat, 01 Jan 2022,
  created_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00,
  updated_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00>,
 #<Event:0x0000023616d9ce40
  id: 2,
  name: "sample event 2",
  hold_date: Sun, 01 Jan 2023,
  created_at: Sat, 06 Nov 2021 05:39:27.708632000 UTC +00:00,
  updated_at: Sat, 06 Nov 2021 05:39:27.708632000 UTC +00:00>,
 #<Event:0x0000023616d9cd50
...

条件による複数レコードの検索

whereメソッドでは条件を指定して、条件に一致するすべてのレコードを取得します。

irb(main):001:0> Event.where('hold_date < ?', Time.parse('20230101'))
  Event Load (0.3ms)  SELECT "events".* FROM "events" WHERE (hold_date < '2022-12-31 15:00:00')
=>
[#<Event:0x0000023614feb1f8
  id: 1,
  name: "sample event",
  hold_date: Sat, 01 Jan 2022,
  created_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00,
  updated_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00>,
 #<Event:0x0000023614feb018
  id: 3,
  name: "The 1th event",
  hold_date: Fri, 01 Jan 2021,
  created_at: Sat, 06 Nov 2021 05:41:18.624319000 UTC +00:00,
  updated_at: Sat, 06 Nov 2021 05:41:18.624319000 UTC +00:00>,
 #<Event:0x0000023614feae38
  id: 4,
  name: "The 2th event",
  hold_date: Sat, 01 Jan 2022,
  created_at: Sat, 06 Nov 2021 05:41:18.637409000 UTC +00:00,
  updated_at: Sat, 06 Nov 2021 05:41:18.637409000 UTC +00:00>]

最初・最後のレコードの取得

最初のレコード、最後のレコードを取得するには、それぞれfirstメソッドlastメソッドを使います。
また、最初(最後)から指定した数のレコードを取得することもできます。

irb(main):001:0> Event.first()
   (1.1ms)  SELECT sqlite_version(*)
  Event Load (0.2ms)  SELECT "events".* FROM "events" ORDER BY "events"."id" ASC LIMIT ?  [["LIMIT", 1]]
=>
#<Event:0x00000253cd383060
 id: 1,
 name: "sample event",
 hold_date: Sat, 01 Jan 2022,
 created_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00,
 updated_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00>

irb(main):002:0> Event.last()
  Event Load (0.4ms)  SELECT "events".* FROM "events" ORDER BY "events"."id" DESC LIMIT ?  [["LIMIT", 1]]
=>
#<Event:0x00000253cf1beac0
 id: 12,
 name: "The 10th event",
 hold_date: Tue, 01 Jan 2030,
 created_at: Sat, 06 Nov 2021 05:41:18.770335000 UTC +00:00,
 updated_at: Sat, 06 Nov 2021 05:41:18.770335000 UTC +00:00>

irb(main):003:0> Event.first(3)
  Event Load (0.4ms)  SELECT "events".* FROM "events" ORDER BY "events"."id" ASC LIMIT ?  [["LIMIT", 3]]
=>
[#<Event:0x00000253ced762b0
  id: 1,
  name: "sample event",
  hold_date: Sat, 01 Jan 2022,
  created_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00,
  updated_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00>,
 #<Event:0x00000253ced75e50
  id: 2,
  name: "sample event 2",
  hold_date: Sun, 01 Jan 2023,
  created_at: Sat, 06 Nov 2021 05:39:27.708632000 UTC +00:00,
  updated_at: Sat, 06 Nov 2021 05:39:27.708632000 UTC +00:00>,
 #<Event:0x00000253ced75b30
  id: 3,
  name: "The 1th event",
  hold_date: Fri, 01 Jan 2021,
  created_at: Sat, 06 Nov 2021 05:41:18.624319000 UTC +00:00,
  updated_at: Sat, 06 Nov 2021 05:41:18.624319000 UTC +00:00>]

複雑な条件による検索

レコードの検索には様々な条件を指定することができます。
Active Recordでは、メソッドチェインによりSQLのクエリを構築していくことができます。

selectメソッドでは取得するカラムを指定します。
limitメソッドでは取得する最大件数を指定します。
orderメソッドではレコードの並び順を指定します。
offsetメソッドでは取得したいレコードのオフセットを指定します。

irb(main):001:0> Event.select(:name).limit(3).order(hold_date: :desc)
  Event Load (0.5ms)  SELECT "events"."name" FROM "events" ORDER BY "events"."hold_date" DESC LIMIT ?  [["LIMIT", 3]]
=> [#<Event:0x00000253cea029d0 id: nil, name: "The 10th event">, #<Event:0x00000253cea02570 id: nil, name: "The 9th event">, #<Event:0x00000253cea021d8 id: nil, name: "The 8th event">]

また、SQLのGROUP BY句やINNER JOIN句、LEFT OUTER JOIN句はそれぞれgroupメソッドjoinsメソッドleft_outer_joinsメソッドで表現することができます。

Update

既存のデータを取得し、updateメソッドを使ってレコードを更新します。

irb(main):001:0> Event.find(1).update(name: 'updated event')
   (0.6ms)  SELECT sqlite_version(*)
  Event Load (0.2ms)  SELECT "events".* FROM "events" WHERE "events"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  TRANSACTION (0.1ms)  begin transaction
  Event Update (2.5ms)  UPDATE "events" SET "name" = ?, "updated_at" = ? WHERE "events"."id" = ?  [["name", "updated event"], ["updated_at", "2021-11-06 07:56:59.826455"], ["id", 1]]
  TRANSACTION (3.2ms)  commit transaction
=> true

irb(main):002:0> Event.find(1)
  Event Load (0.1ms)  SELECT "events".* FROM "events" WHERE "events"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<Event:0x00000263d16a5fe0 id: 1, name: "updated event", hold_date: Sat, 01 Jan 2022, created_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00, updated_at: Sat, 06 Nov 2021 07:56:59.826455000 UTC +00:00>

レコードの新規登録と同様の記述方式で更新する方法もあります。
新規登録では event = Event.new として新規レコードを作成していましたが、更新の場合はRead系のメソッドで既存のデータを取得します。
そのあとはカラムに対応したプロパティを更新して、最後にsaveします。

irb(main):001:0> event = Event.find(2)
   (0.7ms)  SELECT sqlite_version(*)
  Event Load (0.2ms)  SELECT "events".* FROM "events" WHERE "events"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
=> #<Event:0x000001cc0702c6d0 id: 2, name: "sample event 2", hold_date: Sun, 01 Jan 2023, created_at: Sat, 06 Nov 2021 05:39:27.708632000 UTC +00:00, updated_at: Sat, 06 Nov 2021 05:39:27.708632000 UTC +00:00>
irb(main):002:0> event.name = 'updated event 2'
=> "updated event 2"
irb(main):003:0> event.save
  TRANSACTION (0.1ms)  begin transaction
  Event Update (2.5ms)  UPDATE "events" SET "name" = ?, "updated_at" = ? WHERE "events"."id" = ?  [["name", "updated event 2"], ["updated_at", "2021-11-06 08:11:00.975118"], ["id", 2]]
  TRANSACTION (3.4ms)  commit transaction
=> true

irb(main):004:0> Event.find(2)
  Event Load (0.2ms)  SELECT "events".* FROM "events" WHERE "events"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
=> #<Event:0x000001cc0a053700 id: 2, name: "updated event 2", hold_date: Sun, 01 Jan 2023, created_at: Sat, 06 Nov 2021 05:39:27.708632000 UTC +00:00, updated_at: Sat, 06 Nov 2021 08:11:00.975118000 UTC +00:00>

Delete

削除したいデータを取得し、deleteメソッド、もしくはdestroyメソッドを使ってレコードを削除します。
削除したデータは参照できなくなるため、findメソッドで取得を試みると ActiveRecord::RecordNotFound の例外が発生します。

irb(main):001:0> Event.find(1).delete()
   (0.5ms)  SELECT sqlite_version(*)
  Event Load (0.1ms)  SELECT "events".* FROM "events" WHERE "events"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Event Destroy (5.7ms)  DELETE FROM "events" WHERE "events"."id" = ?  [["id", 1]]
=> #<Event:0x000001dae4b6cd88 id: 1, name: "updated event", hold_date: Sat, 01 Jan 2022, created_at: Sat, 06 Nov 2021 05:37:38.448063000 UTC +00:00, updated_at: Sat, 06 Nov 2021 07:56:59.826455000 UTC +00:00>

irb(main):002:0> Event.find(2).destroy()
  Event Load (0.2ms)  SELECT "events".* FROM "events" WHERE "events"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  TRANSACTION (0.1ms)  begin transaction
  Event Destroy (0.7ms)  DELETE FROM "events" WHERE "events"."id" = ?  [["id", 2]]
  TRANSACTION (2.8ms)  commit transaction
=> #<Event:0x000001dae50a3bf8 id: 2, name: "updated event 2", hold_date: Sun, 01 Jan 2023, created_at: Sat, 06 Nov 2021 05:39:27.708632000 UTC +00:00, updated_at: Sat, 06 Nov 2021 08:11:00.975118000 UTC +00:00>

irb(main):003:0> Event.find(1)
  Event Load (0.2ms)  SELECT "events".* FROM "events" WHERE "events"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/activerecord-6.1.4.1/lib/active_record/core.rb:338:in `find': Couldn't find Event with 'id'=1 (ActiveRecord::RecordNotFound)

destroyメソッドはトランザクションによりレコードの削除を行っているので、より安全性を求められるシステムに利用されます。

プログラミングでわからないことがあったら、その道のプロに質問するのが一番!
現役で活躍中のエンジニアと繋がって、効率的にWeb開発のスキルを身につけましょう。
プログラミング学習のスキルプラットフォーム【MENTA】

Scopeの定義

Scopeとはよく使うクエリに名前を付けておいて、再利用しやすくする機能のことです。
モデルに対して定義しておくことでそのモデルでのみ有効になり、メソッドとしてクエリを発行できます。
全てのモデルで使用したい場合は、モデルクラスの親クラス(継承元)であるApplicationRecordにScopeを定義します。

ここではEventモデルに対して、すでに終了しているイベントを検索するScopeを定義してみます。
このスコープは、archiveというメソッド名で使用することができます。
モデルファイルは app/models/event.rb にあります。

class Event < ApplicationRecord
  scope :archive, -> { where('hold_date < ?', Date.today) }
end

コンソール上で、定義したスコープを使って検索を行ってみます。

irb(main):001:0> Event.archive
   (0.8ms)  SELECT sqlite_version(*)
  Event Load (0.4ms)  SELECT "events".* FROM "events" WHERE (hold_date < '2021-11-06')
=> [#<Event:0x00000286067ce708 id: 3, name: "The 1th event", hold_date: Fri, 01 Jan 2021, created_at: Sat, 06 Nov 2021 05:41:18.624319000 UTC +00:00, updated_at: Sat, 06 Nov 2021 05:41:18.624319000 UTC +00:00>]

確かに過去のイベント情報が取得できています。

また、検索条件を指定できるScopeも定義もできます。
()に書かれているのが検索条件として受け取る引数です。

class Event < ApplicationRecord
  scope :archive, -> { where('hold_date < ?', Date.today) }

  # イベント名をもとにその開催日を取得
  scope :get_date, -> (name) { select('hold_date').where('name like ?', "%#{name}%") }
end

使用する際は同様に、メソッドとして呼び出します。

irb(main):001:0> Event.get_date('The 1th')
   (0.9ms)  SELECT sqlite_version(*)
  Event Load (0.2ms)  SELECT "events"."hold_date" FROM "events" WHERE (name like '%The 1th%')
=> [#<Event:0x0000018b0d7e8cd8 id: nil, hold_date: Fri, 01 Jan 2021>]

まとめ

今回はRailsでモデルを役割を担うActive Recordの基本操作について見てきました。
以下に使用したCRUD系のメソッドをまとめました。

Create系createメソッド
newによるインスタンス生成 → saveメソッド
Read系findメソッド
find_byメソッド
allメソッド
whereメソッド
Upcate系updateメソッド
Read系メソッドによるレコード取得 → saveメソッド
Delete系deleteメソッド
destroyメソッド

Active RecordではSQLのCRUD操作を非常に直感的にわかりやすく再現してくれています。
また、アプリケーションに合わせて独自のScopeを設定することで、開発が楽になります。

また、Active RecordのようなORMを使うことでSQLインジェクションのような攻撃を防ぐこともできます。
SQLインジェクションは、サーバとの通信の際にSQLを送信して本来の処理では取り出さないようなデータを取得しようとする悪質な攻撃です。

Ruby on Railsが提供するモデル操作の便利な機能を使いこなせるようになりましょう!

ひとりではなかなかプログラミングの学習が続かない、未経験だから不安が多い、という方はプログラミングスクールを利用してみるのも有効です。
本物のエンジニアに学ぶことで、時間がない方でも最短でスキルを身につけることができます。
現役エンジニアのパーソナルメンターからマンツーマンで学べる

お悩みの方は、まずは無料キャリアカウンセリングにお申込みください。

関連記事

この記事のタグ