LoginSignup
2
4

More than 3 years have passed since last update.

Ruby on Rails チュートリアルの中で覚えておくべきこと・概念

Last updated at Posted at 2020-10-22

はじめに

私はWeb系の自社開発企業に転職のためにRailsの勉強をしました!
その勉強の一環でRailsのチュートリアル第6版を実施していて、私はこれが大事 & 覚えておくべきと思ったことを記載します。

  • Railsのチュートリアルをやったけど、あんまり頭に入ってない方
  • Railsのチュートリアルってどんなこと学べるの?と思ってる方
  • Railsでアプリを作成するときの基本を復習、学びたい方 は本記事が参考になるかと思います!

※本記事は5章〜14章までしか網羅してません。(5章からQiitaで記事に纏めて、備忘録にしようと思ったので。。。)
 気が向いたら、1章〜4章も後で更新します。STAR WARS方式で。

RubyとRailsの基礎用語、概念

★gemとは
- RubyGemsが公開してるRubyのパッケージのこと
- それらパッケージを管理するパッケージ管理システムのこと

★bundle install
- Gemfileに記載してあるRailsでの開発に必要なものをInstallするコマンド

★rails db:migrate
- railsで使用するデータベースの構造(テーブル、カラム)を変更するときに利用する機能
 ・rails db:migrateの大まかな使用の流れ
 1. マイグレーションファイルを作成&内容を記載
 2. $ rails db:migrateコマンドでマイグレーションを順番に実行してデータベースに変更を加える

第5章 レイアウトを作成する

5.2.1 アセットパイプライン

アセットパイプラインというのは主に(以下の)3つの主要機能に分かれている。
(以下 ※説明 ★簡単に要約)

[アセットパイプラインの最大のメリット]
本番アプリケーションで効率的になるよう最適化されたアセットも自動に生成されること。(=本番環境でページの読み込みを早くするのを自動的に行なってくれる。)
そうすることにより、「開発環境ではファイルの可読性」を保ち、「本番環境ではアプリの実行速度をあげる」という2つの異なる環境で各々に最強の環境を提供するという両立ができる。
★要するに、「開発環境」も「本番環境」も最適化されて、最高に効率/利便性が良いということ

  1. アセットディレクトリ
      ※開発に必要なもの(アセット)を置いておくディレクトリ

    • app/assets: 現在のアプリケーション固有のアセット
    • lib/assets: あなたの開発チームによって作成されたライブラリ用のアセット
    • vendor/assets: サードパーティのアセット(デフォルトでは存在しません)
  2. マニフェストファイル
      ※上記の「アセット」マニフェストファイルを一つのファイルに纏めることができる。

    • アセットを纏める処理を行うのはSprocketsというgem
    • マニフェストファイルで纏めれるのはCSSとJavaScript。(※画像ファイルには適応されない。)
 *= require_tree . #app/assets/stylesheetsの中の全てのCSSファイルを含むようにしてる
 *= require_self  #マニフェストファイル(application.css)自身も含むようにしてる

マニフェストファイルの中の上記のようなコメントをSporocketsが読み込んでいる。

3.プリプロセッサエンジン
  ※Railsはプリプロセッサエンジンを介して、アセットを実行し、ブラウザに配信できるように、マニフェストファイルを用いて結合し、サイト用に準備する

★要するにアセットを一つに纏めて、ブラウザで使いやすくしてる

5.3.4 リンクのテスト

    assert_select "a[href=?]", help_path, 

※help_pathが"/help”という定義は行っている前提。
上記のコードは、以下の"/help”パスが存在するか確認するテスト。
"a[href=?]"の?の部分がhelp_pathに置き換わる。(“/help”が存在するか確認)

<a href="/help”>...</a>
    assert_select "a[href=?]", root_path, count: 2

のcountはroot_pathへのリンクは2つあり、2つテストすることを指す。

assert_selectには色々な指定の仕方があります。
以下がそのうちのいくつかの代表例です。
image.png

第5章のまとめ

 - Railsのパーシャルは効率化のために使われ、別ファイルにマークアップを切り出すことができる。
 - Bootstrapフレームワークを使うと、レスポンシブで良いデザインを素早く実装できる
 - SassとAsset Pipelineは、(開発効率のために切り分けられた)CSSの冗長な部分を圧縮し、本番環境に最適化した結果を出力する
 - Railsのルーティングは自由にルールを定義できて、そのときに名前付きルートも使えるようなる。
 - 統合テストはページ感遷移を効率的にシュミレーションできる。

第6章 ユーザーのモデルを作成する

6.1 Userモデル

Active Record:データベースとやりとりをするデフォルトのRailsライブラリ

モデルを作成するときは、generate modelというコマンドを使います。
例) nameやemailといった属性を付けたUserモデルの作成

$ rails generate model User name:string email:string

[Railsの命名慣習]
- コントローラ名:複数形(例:Users)
- モデル名:単数形(例:User)
  └モデルは一人のユーザを表すから
- テーブル名:複数形
  └テーブル(DB)は複数のユーザの情報を持つから

6.1.3 ユーザーオブジェクトを作成する

  • user = User.new: インスタンスの生成
  • user.save: モデルの保存 ※下記の>>はrails console上で実行してることを意味する。
>> user = User.new(name: "Michael Hartl", email: "michael@example.com")
>> user.save
  • User.create: Active Recordを通じて、モデルの生成と保存を同時に行う方法
>> User.create(name: "Another Sky”, email: "anothersky@example.org")
  • モデルへのアクセス (<オブジェクト名>.<キー>) 例)
>> user.name
=> "Michael Hartl"
>> user.email
=> "mhartl@example.com"
>> user.updated_at
=> Mon, 23 May 2016 19:05:58 UTC +00:00
  • Active Recordでデータが形成された順で検索する方法
>> User.find(1)
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.com",
created_at: "2019-08-22 01:51:03", updated_at: "2019-08-22 01:51:03">
  • Active Recordで特定の属性(データ)でユーザーを検索する方法
>> User.find_by(email: "michael@example.com")
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.com",
created_at: "2019-08-22 01:51:03", updated_at: "2019-08-22 01:51:03">

6.2 ユーザーを検証する

Active Recordでよく使われるValidation(検証)ケース
1. Presence(存在性)
2. Length(長さ)
3. Format(フォーマット)
4. Uniqueness(一意性)

テスト駆動開発のテストの進め方
1. 有効なモデルのオブジェクトを作成
2. その属性のうちの1つを有効でない属性に意図的に変更
3. バリデーションで失敗するかどうかをテストする
4. 念のため、最初に作成時の状態に対してもテストを書いておき、最初のモデルが有効であるかどうかも確認
5. 4.のテストすることで、バリデーションのテストが失敗したとき、バリデーションの実装に問題があったのか、オブジェクトそのものに問題があったのかを確認することができる

rails test:models: モデルに関するテストだけを走らせるコマンド

$ rails test:models

6.3.1 ハッシュ化されたパスワード

マイグレーション名は自由に指定できる。
末尾を指定(to_usersに)しておくことで、usersテーブルにカラムを追加するマイグレーションがRailsによって自動的に作成される。

例)add_password_digest_to_usersというマイグレーションファイルを生成するためには、次のコマンドを実行します。

$ rails generate migration add_password_digest_to_users password_digest:string

6章のまとめ

  • Active Recordを使うと、データモデルを作成したり、操作したりするための多くのメソッドが使える
  • Active RecordのValidationを使うと、モデルに対して制限を追加できる
  • よくあるValidationは、「存在するか」「長さ」「フォーマット」
  • データベースにインデックスを追加すると検索効率が飛躍的に向上するし、データベースレベルでの一意性を保証するためにもインデックスを使える。

第7章 ユーザー登録

7.3.3 エラーメッセージ

「shared」: 複数のビューで使われるパーシャルは専用のディレクトリ

7.3.4 失敗時のテスト

assert_select: テストの対象がCSSの
・クラスの場合→div#CSSのid名

assert_select 'div#error_explanation'

クラスの場合→div.CSSのクラス名

assert_select 'div.field_with_errors'

7.6.1 本章のまとめ

  • debugメソッドを使うことで、有意なデバッグ情報を表示できる
  • Sassのmixin機能を使うと、CSSのルールをまとめたり、変数のように他の場所でもmixinで指定したCSS情報を使用できる
  • Railsでは簡単に標準的なRESTfulなURLを通した、データ管理が可能
  • form_withヘルパーは、Active Recordのオブジェクトに対応したフォームを作成する
  • flash変数を使うと、一時的なメッセージを表示できる
  • 統合テストを使うと、送信フォームの振る舞いを検証したり、バグの発生を検知できる

第8章 基本的なログイン機構

8.1 セッション

  • HTTPはステートレスなプロトコル

    • 状態管理がないプロトコルということ
    • 前後のリクエストの情報を全く利用せず、独立したトランザクション(処理)として扱われる
    • なのでHTTPプロトコル内「には」、ユーザのID等の情報を保持する「手段」がない
    • ユーザ情報などをWebアプリケーション上で管理する際は、「セッション(Session)」で半永続的な接続をクライアントとサーバ間に別途設定する必要がある

8.1.5 フラッシュのテスト

assert_templateとは:assert_template後にあるURLがビューを描画しているかをテストする。
※下記の場合は、sessions/newがビューを描画してるかのテスト

    assert_template 'sessions/new'

コラム 8.1. 「||=」とは何か?

Rubyではnilとfalseを覗いて、あらゆるオブジェクトがtrueになるように設計されている。
Rubyでは、||演算子をいくつも連続して式の中で使う場合、項を左から順に評価し、最初にtrueになった時点で処理を終えるように設計されてる。
この評価法は短絡評価(short-circuit evaluation)と呼ぶ。

&&演算子も似たような設計になってるが、項を左から評価して最初にfalseになった時点で処理を終了する点が異なる点である。

8.2.4 レイアウトの変更をテストする

.& safe navigation演算子(または"ぼっち演算子) 
Rubyのぼっち演算子を使うと、obj && obj.methodのようなパターンをobj&.methodのように凝縮した形で書けます。
例を上げると、以下のような論理演算子コードが


    if user && user.authenticate(params[:session][:password])

以下のように簡略化できます。


    if user&.authenticate(params[:session][:password])

&& user.authenticate&.authenticate と簡略化できています。
Ruby初学者としては簡略化しすぎでは?と少し混乱するのですが、
ぼっち演算子は使用されることが多いようなので、自分で使ってみて覚える努力が必要そうです。

8.4.1 本章のまとめ

  • Railsのsessionメソッドを使うと、ページ遷移時の状態を保持できる。一時的な状態の保持にはcookiesも使える。(※今後あらゆるブラウザがクロスドメインでのcookies共有を禁止にするため、Rakute○のドメインで取得した、Rakute○IDなどを別ドメイン(Rakute○以外のドメイン)では使用できなくなる。)
  • sessionメソッドを使うと、ユーザIDなどをブラウザに一時保存できる
  • テスト駆動開発(TDD)はレグレッションバグを防ぐときに便利
  • 統合でストでは、ルーティング、DBの更新、レイアウトの変更が正常に実施されてるかテストできる

9章 発展的なログイン機構

9.1.1 記憶トークンと暗号化

cookieを盗み出す有名な方法は4つある。
1. 管理の甘いネットワークを通過するネットワークパケットからパケットスニッファという特殊なソフトで直接cookieを取り出す
2. DBから記憶トークンを取り出す
3. クロスサイトスクリプティング(XSS)を使う
4. ユーザがログインしてるPCやスマホを直接操作してアクセスを奪い取る

1.の対処は7.5のSSLをサイトに対応すること
2.の対応は本チュートリアルでは、DBには記憶トークンをハッシュ化して保存してること
3.の対応はRailsでは自動的に対策されてる
4.システム側で根本的防衛策を講じることは不可能

■attr_accessorの利用用途
読み取りも書き込みもできるオブジェクトの属性を定義したい時
 
・name, descriptionという属性を持つUserオブジェクトを定義の仕方
Railsの場合


class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.string :description

      t.timestamps null: false
    end
  end
end

DBを扱わない純粋なRubyコードの場合


class User
  attr_accessor :name, :description
end

ちなみに attr_reader は読み出し専用の属性を定義したいときに使い、
attr_writer は書き込み専用の属性を定義したいときに使う。

コラム 9.2. 10種類の人々

「この世には10種類の人間がいる。2進法を理解できる奴と、2進法を理解できない奴だ」は、この業界に古くから伝わるジョーク(らしい)です。
ローランドみたいですね(笑)もしかしたらローランドはここからパクったのか??

■三項演算子
以下のif elseコードが

  if boolean?
    var = foo
  else
    var = bar
  end

以下の様に短縮できる

var = boolean? ? foo : bar

三項演算子をメソッドの戻り値として使うこともよくあります。
※true, falseで関数の実行結果を判断するなど。

9.3.2 [Remember me]をテストする

assert_equalは、<期待>, <実際の値>の順で値を並べる。

assert_equal <expected>, <actual>

9.4.1 本章のまとめ

  • Railsではページ遷移の際に「状態」を保持することができる。ページの状態を長時間保持したいときは、cookiesメソッドを使って永続的なセッションにしましょう
  • remember_token と remember_digest をユーザごとに関連付けて、永続的セッションが実現できる
  • cookiesメソッドを使うと、ユーザのブラウザにcookiesなどを保存できる
  • (一般的に)セッションとcookieをそれぞれ削除すると、ユーザのログアウトを実現できる

第10章 ユーザーの更新・表示・削除

10.1 ユーザーを更新する

target="_blank" を使用すると、リンク先を新しいタブ(またはウィンドウ)で開くようになるので、別のWebサイトへリンクするときに便利な要素。
★個人的にリンク先へ飛ぶときは別のタブが嬉しいから、自分が実装するときはこれは絶対実装したいなと思った!
 個人的にはPC用(PCが主なクライアントが想定)のサイトは絶対コレ導入すべきやと思う!

<a href="https://gravatar.com/emails" target="_blank">change</a>

Railsは、form_with(@user) を使ってフォームを構成すると、@user.new_record?true のときにはPOST を、false のときにはPATCH を使います。

10.2.3 フレンドリーフォワーディング

*Tutorialでは@userではなくuserが使われているが、使うとusers_login_testでエラーが発生するので@userを使う。

sessions_controller.rb
  def create
    @user = User.find_by(email: params[:session][:email].downcase)              # paramsハッシュで受け取ったemail値を小文字化し、email属性に渡してUserモデルから同じemailの値のUserを探して、user変数に代入
    if @user && @user.authenticate(params[:session][:password])                 # user変数がデータベースに存在し、なおかつparamsハッシュで受け取ったpassword値と、userのemail値が同じ(パスワードとメールアドレスが同じ値であれば)true
      log_in @user                                                              # sessions_helperのlog_inメソッドを実行し、sessionメソッドのuser_id(ブラウザに一時cookiesとして保存)にidを送る
      params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)   # ログイン時、sessionのremember_me属性が1(チェックボックスがオン)ならセッションを永続的に、それ以外なら永続的セッションを破棄する
      redirect_back_or @user                                                    # userの前のページもしくはdefaultにリダイレクト
    else
      flash.now[:danger] = 'Invalid email/password combination'                 # flashメッセージを表示し、新しいリクエストが発生した時に消す
      render 'new'                                                              # newビューの出力
    end
  end

*実際のエラー文

Error:
UsersLoginTest#test_login_with_remembering:
NoMethodError: undefined method `remember_token' for nil:NilClass
    test/integration/users_login_test.rb:60:in `block in <class:UsersLoginTest>'

10.5.1 本章のまとめ

  • ユーザーは、編集フォームからPATCHリクエストをupdateアクションに対して送信し、情報を更新する
  • Strong Prameters (params[:foobar])を使うことによって、安全にWeb上から変更させることができる
  • beforeフィルタを使うと、特定のアク4が実行される前にメソッドを呼び出すことができる(※めちゃ便利!!色んな活用用途がありそう!!)
  • Authorization(認可)のテストでは、特定のHTTPリクエストを直接送信する簡単なテストと、ブラウザの操作をシミュレーションする(ユーザが実際にする操作)難度の高いテスト(統合テスト)の2つを実行した(個人的には、統合テストのシミュレーションに必要なテストを考えるのは、論理的に考える必要があって、建設的に思考をこらせて楽しかった。)
  • フレンドリーフォワーディングは実際のアプリを作るときは絶対いるから、覚えて積極的に実装すべき(UX的に基本的にフレンドリーフォワーディングは必須)
  • rails db:seed コマンドは、db/seeds.rb にあるサンプルデータをDBに流し込む
  • render @usersを実行すると、自動的に _user.html.erb パーシャルを参照し、各ユーザーをコレクションとして表示する
  • boolean型のadmin属性をUserモデルに追加すると、admin?という論理オブジェクトを返すメソッドが自動的に追加される

第11章 アカウントの有効化

ユーザActivationの流れ
1. ユーザーの初期状態は「有効化されていない」(unactivated)にしておく。
2. ユーザー登録が行われたときに、有効化トークンと、それに対応する有効化ダイジェストを生成する。
3. 有効化ダイジェストはデータベースに保存しておき、有効化トークンはメールアドレスと一緒に、ユーザーに送信する有効化用メールのリンクに仕込んでおく。
4. ユーザーがメールのリンクをクリックしたら、アプリケーションはメールアドレスをキーにしてユーザーを探し、データベース内に保存しておいた有効化ダイジェストと比較することでトークンを認証する。
5. ユーザーを認証できたら、ユーザーのステータスを「有効化されていない」から「有効化済み」(activated)に変更する。

11.3.3 有効化のテストとリファクタリング

assigns メソッドを使うと、対応するアクション内のインスタンス変数にアクセスできるようになる。
例えば、Usersコントローラの create アクションでは @user というインスタンス変数があるが、テストで assigns(:user) と書くと、userインスタンス変数にアクセスできるようになる。

testでエラーが出た。。
こちらのteratailの記事を参考に以下のコードを

  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      if user.activated?
        log_in user
        params[:session][:remember_me] == '1' ? remember(user) : forget(user)
        redirect_back_or user
      else
        message  = "Account not activated. "
        message += "Check your email for the activation link."
        flash[:warning] = message
        redirect_to root_url
      end
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

以下のようにすると成功しました〜

  def create
    @user = User.find_by(email: params[:session][:email].downcase)
    if @user && @user.authenticate(params[:session][:password])
      if @user.activated?
        log_in @user
        params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)
        redirect_back_or @user
      else
        message  = "Account not activated. "
        message += "Check your email for the activation link."
        flash[:warning] = message
        redirect_to root_url
      end
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

11.5.1 本章のまとめ

  • アカウント有効化はActive Recordオブジェクトではないが、セッションの場合と同様に、リソースでモデル化できる
  • Railsはメール送信で扱うAction Mailerのアクションとビューを生成できる
  • Action MailerはテキストメールとHTMLメールの両方を利用できる
  • Mailer Actionで定義したインスタンス変数は、他のアクションやビューと同様、Mailerのビューから参照できる
  • アカウント有効化のために、生成したトークンを使って一意のURLを作る
  • SendGridを使うと、production環境からメール送信できる

12章 パスワードの再設定

12.3.3 パスワードの再設定をテストする

・assignsメソッドはコントローラのインスタンス変数をテストするメソッド。
 引数にインスタンス変数をシンボル型で渡す。
 そうすることでインスタンス変数にアクセスできるようになり、テストができる。

    @user = assigns(:user)

12.5.1 本章のまとめ

  • Railsはメール送信で扱うAction Mailerのアクションとビューを生成できる
  • より安全なパスワード再設定のために、ハッシュ化したトークン(ダイジェスト)を使う

第13章ユーザーのマイクロポスト

13.4.1 基本的な画像アップロード

■Active Storage
Active Storageを使うことで画像を簡単に扱うことが出来、画像に関連付けるモデルも自由に指定できます。
Active Storageは汎用性が高く、平文のテキストやPDFファイル、音声ファイルなど様々なバイナリファイルを扱えます。

Active Storage APIの中で最初に知っておく必要があるのはhas_one_attachedメソッドです。これは、指定のモデルと、アップロードされたファイルを関連付けるのに使います。
has_one_attached は指定のモデルとアップロードされたファイルを関連付けるのに使えます。
has_one_attached の場合、「マイクロソフト1件に付き画像は1件」ですが、
has_many_attached を使えば、「マイクロソフト1件に付き複数の画像」を添付できます。

13.5.1 本章のまとめ

  • Rails は複数のキーインデックスをサポートしてる
  • Userは複数のMicropostsを持っていて(has_many)、Micropostは一人のんUserに依存してる(belongs_to)
  • user.microposts.build(...)というコードは引数で与えたユーザに関連付けされたマイクロポストを返す
  • dependent: :destroy オプションを使うと、関連付けされたオブジェクトと自分自身を同時に削除する
  • fixtureは関連付けを使ったオブジェクトの作成もサポートしてる
  • パーシャルを呼び出すときに一緒に変数を渡すことができる
  • where メソッドを使うとActive Recordを通して選択(部分集合を取り出すこと)ができる ※生のSQL文と同じ様な文章で取得できる

第14章ユーザーをフォローする

14.2.2 統計と[Follow]フォーム

@user ||= current_user

上記のコードは @user がnilではない場合は何もせず、nilの場合には @user にcurrent_userを代入するというコード

14.4.3 本章のまとめ

  • has_many :throughを使うと、複雑なデータ関係をモデリングできる
  • has_manyメソッドには、クラス名や外部キーなど、いくつものオプションを渡すことができる
  • 適切なクラス名と外部キーと一緒にhas_many/has_many :throughを使うことで、能動的関係(フォローする)や受動的関係(フォローされる)がモデリングできた
  • ルーティングは、ネストさせて使うことができる
  • whereメソッドを使うと、柔軟で強力なデータベースへの問い合わせが作成できる
  • Railsは(必要に応じて)低級なSQLクエリを呼び出すことができる

番外編

■assertの一覧表

メソッド 説明
assert_template(expected, message = nil) そのアクションで指定されたテンプレートが描写されているかを確認する
assert_not( test, [msg] ) testfalseかを確認する。
assert_select "div.nav" selector(div)に合致した要素の内容を引数equality(nav)でチェック

※assert_selectは柔軟でパワフルな機能で、多くのオプションがあるが、レイアウト内で頻繁に変更されるHTML要素 (リンクなど) をテストするぐらいに抑えておくとよい。

「!!」(「バンバン(bang bang)」と読みます)
オブジェクトをBoolean(論理)値に変換できる演算子。
nilはfalseになります。

>> !!nil
=> false

その他のあらゆるRubyのオブジェクトは、ゼロですらtrueです。

>> !!0
=> true

■! ビックリマーク(感嘆符)について
◆!マークを使うことで、データ(〜〜属性)を直接変更できる。
★!を使わん場合

  before_save { self.email = email.downcase }

★!を使う場合

  before_save { email.downcase! }

◆!をメソッドにつけることで例外を発生させられる!!
create, saveでの例。
! をつけない場合(create save)
・処理に実行して、レコードの作成/保存に失敗して際、 nil が返される。
! をつける場合(create! save!)
例外(例: ActiveRecord::RecordNotFound ERROR )を発生させられる。


■HTMLのtype= “email”
htmlでtype=“email”にすると、携帯電話から入力フォームをタップすると、メールアドレス用に最適化された特別なキーボードが表示される。

private キーワード
- そのファイル(クラス)内でしか使わないメソッドを定義するために使われる
- 他のファイル(クラス)ではprivate内で定義されたメソッドは使用できない。
- 他のファイルでは使われないメソッドをprivateにすることで、想定外のエラーが避けられる

  private

    def user_params
      params.require(:user).permit(:name, :email, :password, :password_confirmation)
    end

PATCH Method for HTTP
- Putメソッドは更新というよりは置換
- Putsで置き換え先が「空」でも、値を置き換える(置き換えようとしてる値をそのまま代入する)
- Patchメソッドは既存のリソースを更新・変更・修正

■<<演算子(Shovel Operator)
<< で配列の最後に追記することができます。

user.following << other_user

さいごに

結構時間かかりました。。Cloud9のエラー等も伴いましたが、結果1ヶ月強くらいかかってしまいました。。
結構ボリューミーだったので仕方ないと思ってますが、もっと早く終わらせてる人もいるので、Portfolio作りは早く終わらせようと思います!!

読んでいただきありがとうございました!!

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4