Ruby on Rails開発のインターン (Day 11)

こんにちは、皆さん!

今日の朝はちょうど座った時に避難訓練があった。ちょっとタイミングがね...(笑)
それはさておき、今日はHeroku上のバグを修正できるようにがんばっていくぜ!


  • トピック:
  1. モデルクラスの中で関数を使う
  2. tunemygcジェムはrakeタスクを妨害する可能性がある


  • 疑問:


  • 問題:


  • 学習した内容:
  1. モデルクラスの中で関数を定義し、他のファイルでそのクラスからなるオブジェクトに対してその関数を使える


  • 今後やりたいこと:


  • リソース:


モデルクラスの中で関数を使う

Userというモデルがあり、 そのuserには'name', 'email', 'age', 'gender', 'address'があるとしよう。もし、この状況で頻繁にuserの詳細をアウトプットさせるようだったら、毎回下記のようにコードを打ち込むことになる。

"Name: " + @user.name + " Email: " + @user.email + " Age: " + @user.age + " Gender: " + @user.gender + " Address: " + @user.address

これはめんどくさいし、後で修正するときにすごく手間がかかる。
ということで、次のようにapp/models/user.rbファイルの中に関数を定義する。

def info
   "Name: " + @user.name + " Email: " + @user.email + " Age: " + @user.age + " Gender: " + @user.gender + " Address: " + @user.address
end

そしたら、詳細をアウトプットしたいとき、info関数を下記のように使えばできる

# users_controller.rb
def index
    @users = User.all
    @users.each do |user|
          puts user.info
     end
end

def show
    @user = User.find(params[:id])
    puts @user.info
end

これで書くコードの量がだいぶ減る。


後から考えてみれば、Userはapp/models/user.rbでクラスとして定義されていてUserのオブジェクトであるのなら、Userクラスで定義されている関数はごく普通のOOP言語のように使える。Railsのように最初から結構複雑にいろいろなファイルを作られると案外気づかないものだ(笑)

tunemygcジェムはrakeタスクを妨害する可能性がある

HerokuでGCチューニングをやりたいと思っていたものの、ずっとheroku run rake db:seedをするときにエラーが発生し、うまくいかなかった。いろいろ試した。データベースのリセット、Herokuにコードを再度プッシュなど、けどどれもダメだった。


それで、諦めて、一個前のコミットに戻った。変わった内容はGemfileの中にtunemygcジェムがあるかどうかだけ。そしたら、不思議にもHeroku上でうまく使いたかったコマンドが使えるようになった。結果わかったことは、この特定のジェムに関しては、Heroku上にアドオンがあり、HerokuではGemfileではなく、そっちを使うことが望ましかったようだ。
よって、存在するジェムのうち、いくつかのジェムはHeroku上でrakeタスクを邪魔し、アプリケーションがうまくいかなくなる。そういう時はググってHeroku用のアドオンがないかを探してみよう!


***通常、クレジットカードの登録を要求されるが、心配はしなくてよい。これらのアドオンは無料なので請求されはしない!!***

まとめ

今日はtunemygcをどうやってHeroku上で使うかを発見できたので、結構うれしかった。想像以上に時間かかってしまったけど... 。
こっからは帰宅後、学校に戻ってからの話。何人かのコンピュータサイエンスの友達に久しぶりに会った。インターン始めて以来初めて会ったからインターンについて色々聞かれた。その中で、なぜ職場ではLinuxを使っているのか聞かれたがよくはわからなかった。中にはLinuxWindowsMacOSと違って、オープンソースで情報を提供会社に盗まれないからとかいう理由まで。とりま、明日マネージャーに聞いてみよう!


ご精読ありがとうございました。では、また次回まで✌



Day 12はこちら↓↓
programming-shop.hatenablog.com


Day 10はこちら↓↓
programming-shop.hatenablog.com

Railsでデバッグする方法

こんにちは、皆さん!

今回はRailsをローカルで使う際にデバッグする方法のまとめ。

  • トピック:
  1. どんなコードを入れるの?
  2. どうやって使うの?


以下の内容では、デフォルトで入っているが、Gemfilebyebugがインストールされていることを前提とする。

どんなコードを入れるの?

実はとても簡単!!
まず、デバッグを他の場所でやったことある人ならブレイクポイントという言葉を聞いたことがあると思う。プログラム実行中にそこに到達するとそこで一時停止するもの。Railsでは代わりに下記のコードを挿入する。

debugger

というコードをブレイクポイントとなるところに入れるだけ
例えば、users_controller.rbがあったとして、下記のようにdebuggerを挿入する

  def show
    @user = User.find(params[:id])
    debugger
  end

そうするとローカルサーバーで/users/1に到達したときにプログラムが止まり、コマンド上でRubyのコンソールが開かれる。

どうやって使うの?

その後、ターミナル内で開かれたコンソールで下記のように変数の名前を打ち込むと、その値を返してきてくれる。

(byebug) @user.name
"Example User"

ブレイクポイントから出るためにはCtrl+D。もし、他のブレイクポイントがあるなら、今度はそこまで飛ぶ。

まとめ

今回はすごく簡単なRailsでのデバッグの方法について。使い勝手がよく、gemも基本的には既にインストールされているからすぐに始めれる。これからちょっと詰まった時に使ってみましょう!!

ご精読ありがとうございました。では、また次回まで✌

Railsでリキャプチャを実行しよう!!

こんにちは、皆さん!

f:id:Programming_Shop:20180713093204p:plain
今回はロボット対策のリキャプチャをRailsで実行する方法についてまとめた。
リキャプチャはただロボット対策をするだけでなく、ウェブサイトをよりプロフェッショナルに見せてくれる。一石二鳥ってところだね!!
スターターのコードもあり、それをダウンロードしてこの記事通りにコードを書いていけばRailsでリキャプチャを実行する方法を学習できる。
完成版のコードはこちら↓↓
https://github.com/Eric1015/recaptcha-tutorial



難易度と対象者

  • 難易度:★★☆☆☆
  • 対象者:中級者

***詳細はこちら→難易度と対象者の設定について - Programming_Shopの日記***

必要条件:

  1. MVCを理解している
  2. Herokuにアプリをデプロイできる


トピック:


スターターのダウンロード

スターターのリンクはこちら↓↓
https://github.com/Eric1015/recaptcha-tutorial-starter


ターミナルからコマンドでダウンロードするなら下記のコマンド

git clone https://github.com/Eric1015/recaptcha-tutorial-starter.git


セットアップと確認

ダウンロードしたら、ローカルサーバーで開きましょう

rails s

では、ローカルのそのウェブサイトに行ってみましょう。お好みのブラウザ(chromeを勧める)を開いてlocalhost:3000をURLに入れてサイトをロード。
下のような画面になったら問題なし
f:id:Programming_Shop:20180721092409p:plain


次にHerokuアプリを作りましょう

heroku create

そして、コードをHerokuにプッシュする

git add -A
git commit -m "Setup completed"
git push heroku master

念のためにローカルの時と同じサイト画面になるか確認

gemのインストール

では、まずは必要なgemを追加しましょう。Gemfileを開いて下記のgemを追加

gem 'recaptcha', require: 'recaptcha/rails'

そしたら、コマンドからインストール

bundle install

では、次は実際にコードの中に入れましょう!

リキャプチャをHTMLに挿入する

Railsではリキャプチャ専用のタグが用意されており、これを挿入すればよいのだ。

<%= recaptcha_tags %>

これがそのタグ。そして、これを今のアプリケーションに入れると、app/views/users/new.html.erbファイルは次のようになる。

<h1>ユーザー登録</h1>
<% if flash[:alert] %>
    <div class="error">
    <% flash[:alert].each do |msg| %>
      <p id="error-message"><%= msg %></p>
    <% end %>
    </div>
<% end %>
<%= form_for(@user) do |f| %>
    <p>名前 (必須)</p>
    <%= f.text_field :name %>   
    <p>メール (必須)</p>
    <%= f.text_field :email %>
    <p>好きな動物</p>
    <%= f.text_field :animal %>
    <br>
    <%= recaptcha_tags %>
    <br>
    <%= f.submit 'Submit', id: 'button', class: 'btn btn-default' %>
<% end %>

では、Herokuにプッシュして、再びサイトを訪れてみましょう。
f:id:Programming_Shop:20180721162731p:plain
あれ?エラーが起きてる!!
そう、リキャプチャを使うためにはグーグル公式のサイトからキーを取得しなければならないのだ。

リキャプチャのキーを取得する

では、このサイトに行って、キーを取得しましょう!


そしたら、右上のこのボタンをクリック↓↓
f:id:Programming_Shop:20180721094634p:plain


その次の画面で下のように記入(Domainsには自分自身のHerokuアプリのURLを入れる)
f:id:Programming_Shop:20180721162917p:plain
Domainsの部分はこのAPIキーが使える箇所を制限してくれるもの
例えば、僕がilovecanada.caというウェブサイトを所有していたとしよう。それでここにそれを入れることでこのAPIキーはそのウェブサイトでしか使えなくなるのだ。これはいざAPIキーが他人に漏れた時のために乱用されないようにするためのもの。
しかし、今では自分のウェブサイトを指定しないとそもそもリキャプチャを使わせてくれないようだ。


そしたら、以下のように2つのキーが表示されるはずだ(僕のは隠したが)
f:id:Programming_Shop:20180721152630p:plain

キーを環境変数として設定する

ここで使う環境変数の設定の詳細はこちらの記事に↓↓
programming-shop.hatenablog.com

そしたら、以下のようにconfig/initializers/recaptcha.rbファイルを作成する

Recaptcha.configure do |config|
    config.site_key  = ENV['recaptcha_site_key']
    config.secret_key = ENV['recaptcha_secret_key']
  end

これでリキャプチャの2つのキーは環境変数からとるように設定したことになる。
わかったところで、これをまずはHerokuにプッシュ。
そしたら、Herokuに2つの環境変数を入れる。
下のyour_site_keyyour_secret_keyにそれぞれ自分のキーを入れて以下のコマンドを実行する

heroku config:set recaptcha_site_key=your_site_key
heroku config:set recaptcha_secret_key=your_secret_key

では、もう一度Herokuアプリを開いてみよう。次のようになっているはず
f:id:Programming_Shop:20180721164209p:plain
おめでとう!リキャプチャが正しく表示されました。


実践してみよう。名前とメールに適当に何か入れて"Submit"ボタンを押す。(これはリキャプチャやってないから拒否されるはず)
f:id:Programming_Shop:20180721164532p:plain
なんと!通ってしまった。
実はコードをつけ足さないとリキャプチャの判別をしてくれないのだ。

コントローラーにコードを入れる

では、コントローラーにコードを書いて、リキャプチャを正解できたか判別しましょう!

verify_recaptcha(model: @user)

このコードで@userの登録に関してリキャプチャを正解できたか判別する。この手法はform_for(@user)に少し似ていて@userのためのフォームみたいに@userのためのリキャプチャと考えると覚えやすい。


ではこのコードが何をするかなのだが、使用者がリキャプチャを正解できた場合、このコードはtrueを、正解してない場合はfalseを返してくる。なので、返ってきたのがtrueの時だけ何かすればよい。

if verify_recaptcha(model: @user)
   # your code here
end

つまりこんな感じになる。これを今のアプリケーションに入れるとなるとユーザーを作成するときにリキャプチャの判別をする。よって、コントローラーのcreate関数に上記のコードの形を入れる。
それで、やりたいことはリキャプチャを正解し尚且つ@userが有効であることを確認したい。そうすると下記のようになるはず。

  def create
    @user = User.new(user_params)
    if verify_recaptcha(model: @user) && @user.save
      redirect_to thanks_path
    else
      redirect_to root_path
      flash[:alert] = @user.errors.full_messages
    end
  end

では、実際に試してみよう!
先ほどと同じように名前とメールだけ記入して"Submit"ボタンを押す。そうすると...
f:id:Programming_Shop:20180722073622p:plain
今回はちゃんと拒否されました。では、リキャプチャを正解したら...
f:id:Programming_Shop:20180722082221p:plain
ちゃんと登録できました!!

言語設定を日本語にする

最後にリキャプチャを英語版から日本語版に変えましょう。

やり方は簡単。app/views/layouts/application.html.erbの中のheadタグ内に下記のを追加するだけ!

<script src='https://www.google.com/recaptcha/api.js?hl=ja'></script>

なお、他の言語に指定したいのならこのhlの後の値を変えるだけ
言語コードの一覧はこのサイトに→https://developers.google.com/recaptcha/docs/language

まとめ

以上でリキャプチャをRailsで実装するチュートリアル完了。今回は簡単な方に入るチュートリアルだと個人的には思う。でも、多くの人のアプリにとってセキュリティー強化と見栄えの良さをプラスしてくれるものだと踏んでいるので学んで損はないと思う。
こんな簡単になったのもオープンソースを作ってくれた方々とサービスを提供してくれているグーグルのおかげである。感謝してます!


もしも、何か質問や感想、次の依頼があるのならコメントまたはツイッターまでDMお願いします!!

Ruby on Rails開発のインターン (Day 10)

こんにちは、皆さん!

今日はまだ何をやるのかよくわからないが、思い当たる限り、メモリ使用量を落とすことかな。


  • トピック:
  1. 一つ一つのgemのメモリ使用量の確認方法
  2. GC チューニングでガベージコレクションを加速させる
  3. GCチューニングのセットアップ
  4. GCチューニングの実行


  • 疑問:


  • 問題:


  • 学習した内容:
  1. 普通の画像をsvgのようなベクター形式の画像に変えることでメモリ使用量を減らすことができる


  • 今後やりたいこと:


  • リソース:
  1. Rails: メモリ使用量を制限してHerokuのR14エラー修正&費用を節約した話(翻訳)
  2. TuneMyGC - optimal MRI Ruby 2.1+ Garbage Collection tuning


一つ一つのgemのメモリ使用量の確認方法

リソース:Rails: メモリ使用量を制限してHerokuのR14エラー修正&費用を節約した話(翻訳)


上級者になってくると、メモリの管理もし始める。その時にとても役に立つと個人的には思う。
やり方はシンプルで、以下のジェムをGemfileに追加する。

gem 'derailed_benchmarks', group: :development

で、下記のコマンドを実行する。

$ bundle exec derailed bundle:mem

僕の場合はそんなに多くのメモリを使用しているものはなかったけど、リソースの記事を書いた人は合計で100MBものメモリを節約できたのでGemfileをあまり軽視しないように(笑)

GC チューニングでガベージコレクションを加速させる

リソース: TuneMyGC - optimal MRI Ruby 2.1+ Garbage Collection tuning


ガベージコレクションをより多くさせる方法があって、メモリにかかわる環境変数を変更することで達成できる。この方法をGC チューニングという。
***必要でないのなら絶対にしないこと!!ガベージコレクションをしている間はアプリケーションを止めるのでより頻繁なガベージコレクションはアプリケーションの使いやすさに悪影響を与えるかも***


一番よくつかわれるのは下記の環境変数の変更。

RUBY_GC_HEAP_GROWTH_FACTOR

デフォルトで1.8の値が与えられているこの変数はヒープのサイズが元の1.8倍になったらガベージコレクションを始めるという意味で設定されている。もうわかった人もいると思うけど、この値を小さくすることで理論的にガベージコレクションを開始するのがより早くなるのだ。

GCチューニングのセットアップ

GCチューニングを使うためにはまず、下記のようにジェムをGemfileに追加。

gem 'tunemygc'

それで、bundle installした後、次のコマンドでトークンを手に入れる。

$ bundle exec tunemygc -r email@yourdomain.com

おそらく次のようにトークンが表示されるはず。

Application registered. Use RUBY_GC_TOKEN=08de9e8822c847244b31290cedfc1d51 in your environment.


GCチューニングの実行

そして、ようやく最後に環境変数をセットしてプログラムを実行するとき。
次のように、自分のトークンと環境変数を入れて、実行スタート!

$ RUBY_GC_TOKEN=08de9e8822c847244b31290cedfc1d51 RUBY_GC_TUNE=200 RUBY_GC_HEAP_GROWTH_FACTOR=1.1 bundle exec rails s

3番目の変数が先ほど出てきたもので、ガベージコレクションをもっと頻繁に実行させる。
そして、この変数の値を自由に変えて他の設定で実行することももちろんできる。

まとめ

メモリ使用量がある段階まで来た時にそれ以上は全然多くならないのを見てすっごい喜んだ。ついにメモリ使用量に関する問題に対抗する手段を見つけた!!
だが、残念なことにHerokuでやろうとするとエラーが発生する。これじゃあ、実際にウェブ上にプログラムをアップロードした際にどうなるのかわからない。明日はがんばってここを何とかしようと思う。


ご精読ありがとうございました。では、また次回まで✌



Day 11はこちら↓↓
programming-shop.hatenablog.com


Day 9はこちら↓↓
programming-shop.hatenablog.com

難易度と対象者の設定について

こんにちは、皆さん!

この記事では、僕のブログで紹介するチュートリアルの難易度と対象者の表記の詳細について説明していきます。

難易度について

難易度はそのチュートリアルにかかる時間と複雑さで決めていく。時間がかかればかかるほど、複雑であればあるほど、難易度は上がっていく。表記は5段階

  • 難易度1★☆☆☆☆: (とてもかんたん)移動時間に見ればわかるような内容
  • 難易度2★★☆☆☆: (かんたん)記事通りにやれば30分ほどで終わる内容
  • 難易度3★★★☆☆: (ふつう)記事通りにやれば1時間ほどで終わる内容
  • 難易度4★★★★☆: (むずかしい)自分で使えるくらいに理解もしようとすると3時間から1日はかかる内容
  • 難易度5★★★★★: (おに)記事を読んだだけじゃあ、まだちんぷんかんぷんで理解するのに数日かかる場合もある内容

おそらく難易度5のものはなかなかないかと思う...

対象者について

こちらはその特定のチュートリアルがどのくらいの知識レベルの人向けなのかを示してくれる。5段階に分けるが、もちろん下のレベルなら上の人はできるはずの内容となっている。

  • ド初心者: code>rails new myappが理解できてるレベル
  • 初心者: RailsのコマンドでMVCを構築できるレベル
  • 中級者: フォームを作り、ユーザーの情報をデータベースに保存できるレベル
  • 上級者: APIなども駆使し、ウェブアプリを作れるレベル
  • エキスパート: 実務経験豊富でRailsの使い方についてほぼ網羅しているレベル

できれば、ド初心者の方でも楽しめる内容も作りたいなと思っているところ...

まとめ

この形式で運営していくが、更新した方がいいと思う内容があれば随時更新していくのでよろしくお願いします。
質問のある方はコメント、ツイッターなどで連絡ください!!

Ruby on Rails開発のインターン (Day 9)

こんにちは、皆さん!

2回目の月曜日がやってきた。いい週末だった。ゆっくり休みをとって後はリラックスしてって感じ。
今週から勤務時間がもとの6時間に戻るから、楽になる。
今日はTwilioで利用したアプリケーションのテストと例の困ったメモリリークの続き。


  • トピック:
  1. Herokuのデータベースを確認する方法
  2. Memory Bloat vs Memory Leak


  • 疑問:


  • 問題:
  1. データベースがめちゃくちゃに (解決)


  • 学習した内容:
  1. heroku logs -tでライブのHerokuログが見れる
  2. Memory bloatたるものが存在する


  • 今後やりたいこと:
  1. Twilioについてもっと勉強する


  • リソース:
  1. Debugging memory bloat


Herokuのデータベースを確認する方法

最近、ローカルのRailsにおけるターミナルでたたくコマンドは前にheroku runをつければ、ほとんどHeroku上でも使える。こうやって、一つのコマンドの意味と本当に正しい使い方を知ると一層プログラミングが楽しくなるものだ。


で、これがどうやって利用できるかというと、データベースがめちゃくちゃになっちゃった僕のデータベースにはいったい何があるのか気になったのでローカルでよくやるRailsのコンソールを以下のようにHerokuで実行した。

heroku run rails c

その後、User.allでデータベースのUserの部分にアクセス。それで、空集合が返ってきたのでデータベースに,Userが何もないことを知った。


結局、以下のように以前に取っておいたバックアップでデータベースを復帰させた。(もちろんいくつかのデータは失った)

$ heroku pg:backups restore [id of backups]

僕は同じHerokuアプリで複数の異なるコードをテストしていたので、これからこれにって感じで移動したときにデータベースの内容が混ぜったのが原因。みなさんは絶対に混ぜないでね(笑)


新しいマイグレーションを追加したときなどはちゃんと以下のコマンドでテーブルを更新しよう。

$ heroku run rake db:migrate


Memory Bloat vs Memory Leak

Memory bloatはおそらくmemory leakほどしられていないでしょう。 簡単に言うと、メモリ関係の問題の一つなのだが、memory leakよりは安全といえるでしょう。
個人的にこのサイト(Debugging memory bloat)の以下の写真が一番2つの違いを理解しやすくしてくれるだろう。
f:id:Coding_Studio:20180717082511p:plain
見ての通り、memory bloatでは短い期間にメモリの使用量が急増している。しかし、そのメモリの使用量も最終的にはある一定の範囲内に落ち着く。これはメモリの使用量がどんどん上昇するだけではないことを示している。
一方で、memory leakは少しだが、ずっとメモリの使用量が上昇し続けている。よって、最終的にはメモリを使い切ってしまうのだ。


よって、memory bloat はmemory leakよりも安全であり、必ずしもプログラムをクラッシュさせるとは限らない。しかし、memory leakはそのまま放っておくとあっという間にプログラムをクラッシュさせる要因となりうる!!

まとめ

今日はまず、Twilioに関していくつかテストをした。その内容はサイトとスマホの間でのテキストのやり取りであり、このプログラムにプロフェッショナルな機能が備わっているように感じた。時間があれば、自分でも少し研究して応用できたらいいなと思った。
それと、memory leakだと思っていたものが単なるmemory bloatである可能性が高くなってきた。少し安心した。今のプログラムでそれのせいでクラッシュしない可能性があるからね。


ご精読ありがとうございました。では、また次回まで✌



Day 10はこちら↓↓
programming-shop.hatenablog.com


Day 8はこちら↓↓
programming-shop.hatenablog.com

Ruby on Rails開発のインターン (Day 8)

こんにちは、皆さん!

今日は急用があって、1時間遅刻し、さすがの優しいマネージャーも今日は一言ありましたね...次は気をつけます(笑)


今日の業務内容は引き続きメモリリークとその傍ら小さなバグの修正もって感じかな


  • トピック:
  1. Railsでは何がメモリリークを引き起こすか
  2. 実際にメモリリークを発生させてみた


  • 疑問:


  • 問題:
  1. プログラム内にメモリリークがある可能性がある


  • 学習した内容:
  1. コントローラー内の定数はメモリリークを引き起こす原因になる


  • 今後やりたいこと:


  • リソース:
  1. How I spent two weeks hunting a memory leak in Ruby


Railsでは何がメモリリークを引き起こすか

Railsでは、自動でガベージコレクションがされるためメモリリークが起きるのは結構まれな話。しかし、メモリリークを引き起こすケースがいくつかある。このウェブサイト (How I spent two weeks hunting a memory leak in Ruby) の書きのコードがその可能性を教えてくれた。

class MyController < ApplicationController
  FOO = []

  def index
    FOO << "haha"
  end
end

ここのFOOグローバル変数のように存在し、そのせいでindexページから離れてもガベージコレクションが起こらず、FOOはその分のメモリを取り続ける。よって、メモリリーク発生!!

実際にメモリリークを発生させてみた

というわけで、自分でも実際にやってみていろんなものの変化を見てみようと思った。それで下記のようにPagesコントローラー付きのRailsアプリケーションを作り、テストしてみた。

class PagesController < ApplicationController
    FOO = []
    def index
        1000000.times do 
            FOO << "hello"
        end
        @size = FOO.length
    end
end

まあ、わかっての通り、絶対やらないと思うけど、index ページを訪れるたびにFOOに1000000の"hello"を追加し、巨大な配列を構築する。


コードが書けたので、実践!!
1回実行した後の結果(ページ内の数字はFOOの長さ)
f:id:Coding_Studio:20180714105338p:plain


その後、5回ページをリフレッシュした結果
f:id:Coding_Studio:20180714105535p:plain
もう明らかに赤線で引っ張ったところが上昇していて、これがメモリの使用状況を表している。そして、原因はFOO配列がどんどん膨張し、同時になにもその存在をどける処理がないためである。


***僕みたいに馬鹿なことをする人はいないと思うけど、代わりになにもメモリを取らないputsFOOに"hello"を追加するところにおいたら、コンピューターがクラッシュして電源を切ることになった。文字をプリントすることは時に危険なのだ(笑)***

まとめ

今日はRailsにおいてのメモリリークとはどんな感じなのか見れたので良い経験になった。問題は依然として解決してないけど、後でまた解決に乗り出せば大丈夫。
まずは週末を楽しむぜ!!


ご精読ありがとうございました。では、また次回まで✌



Day 9はこちら↓↓
programming-shop.hatenablog.com


Day 7はこちら↓↓
programming-shop.hatenablog.com