- 投稿日:2021-12-24T16:05:10+09:00
Vue.jsでWYSIWYGエディタコンポーネントを作ろう
慶應のアドベントカレンダーの24日目として書かしてもらいました!誘ってくださったSさん、ありがとうございます。 https://adventar.org/calendars/6279 Vue.js で WYSIWYG エディタコンポーネントを作ろう オリジナルのCMSを作りたい時に、inputやtextareaのような使い心地で入力フィールドを作ることができないかを考えていました。コンポーネントを作る上での目標は、 v-modelを適用するだけでHTMLの文字列データと同期できること です。あまり時間がないので、軽めの内容になってしまいましたが、ご容赦ください。 今回は JSFiddle(https://jsfiddle.net/) にコードを書いていきますので、適宜単一ファイルのコンポーネントに置き換えて使ってください。 出来上がりイメージ 一番上のフィールドがcontenteditableがtrueになっている要素です。ここで文字を打ち替えたりできます。選択範囲(Range)を指定してspanとかで包む関数を作れば、MSの Sway みたいにできます。 二つ目が生のHTMLをそのまま編集できるフィールドで、最後がv-htmlで吐き出された結果です。 上二つを編集すれば、全て同期されていますので、他のフィールドも同時に変更されます。 ソースコード div#appに親のコンポーネントをmountして、その中にwysiwygコンポーネントを入れているというのが、基本的な構造です。SPA等であれば、そのページにinputタグのようにwysiwygをimportして使うという状況を想定しています。 とりあえず(ミニマルな)最終的なコードを掲載します。 <div id="app"> <div class="container"> <h2>WYSIWYG</h2> <wysiwyg v-model="html" ref="wysiwygcomp" /> </div> <div class="container"> <h2>raw HTML</h2> <textarea type="text" v-model="html" class="raw-text-editor"></textarea> </div> <div class="container"> <h2>v-html</h2> <div v-html="html"></div> </div> </div> const wysiwyg = Vue.extend({ template: ` <div class="wysiwyg-editor" contenteditable="true" ref="wysiwyg" @input="sync" ></div>`, props: ['value'], model: { prop: 'value', event: 'change', }, watch: { value(v) { this.sync2innerhtml() } }, mounted() { this.sync2innerhtml() }, methods: { sync(ev) { this.$emit('change',ev.target.innerHTML) }, sync2innerhtml() { this.$refs.wysiwyg.innerHTML = this.value } } }) new Vue({ el: "#app", components: { wysiwyg }, data: { html: `Hello, <span style="color: blue;">World</span>` }, methods: { sync(e) { // sync2wysiwygから読んでも一つ前が同期されてしまう this.$refs.wysiwygcomp.sync(e.target.value) } } }) 説明 小分けにしてなるべく頑張って説明します!!(あんまり説明することもない気がしますが!!) v-modelの適用 ↓この辺は、「自作コンポーネントにv-modelを使う」みたいなことを調べてばすぐ検索で出てくるので割愛します。 props: ['value'], model: { prop: 'value', event: 'change' }, templateで at input を使っている理由 template: ` <div class="wysiwyg-editor" contenteditable="true" ref="wysiwyg" @input="sync" ></div>`, 関数でinnerHTMLを変えることもあるわけだし、@changeができたら楽だなとは思っていたんですけど、innerHTMLを編集してもchangeが発火されないんですよね。Mutation Observerとか使って検知できそうですが、なんかうまくいかなかったので、inputでいいやとなりました。ごめんなさい。@inputはちゃんと動いてくれます。 watchしている理由 親のデータ(html)を書き換えていても、innerHTMLは勝手に変わってはくれません。変更を検知して、innerHTMLを変更して初めてWYSIWYGエディタのような振る舞いをしてくれるのです。 $emitでデータを変更しよう 以下のように親のデータと同期するために$emitを使っています。 sync(ev) { this.$emit('change',ev.target.innerHTML) }, 実は this.value = hoge とかしてしまうと "Avoid mutating a prop directly" と怒られてしまいます。 mutateしないで、イベントで変更しなければならないです。理由はエラーの中に書いているので、気になる人はエラーを起こしてみてはいかがでしょうか!! this.value.splice(適当) とかもmutateするので、さけた方が良いのかもしれないですね。 僕だったら deep copy してからspliceして、変更後のやつを$emitに渡します。 自作v-modelでは親にデータの変更を通知するときは、$emitを使いましょう! 最後 もし自分でリッチなメモアプリとかを作ってみたいとか、CMSを作りたい方がいれば、参考になればなと思っています。 また、個人的にはCMS開発はVue(JS)の良い勉強になったので、是非はお勧めしたいです!!
- 投稿日:2021-12-24T15:45:16+09:00
Vue.js2で作ったサイトがIE11で表示されなくなった調査の軌跡
前提 pakcage.jsonにbrowserslistは指定してあって、ある時点まではIE11でも動いていた。 エラーの原因を特定 こちらの記事と似たような事象だと思ったが。 キャプチャのようにエラーがバンドルされて1つになったJS内で起きていたためどこに原因があるのか全く分からなかった。 そこでまずはこちらの記事を参考に読み込んでいるモジュール単位にビルド後のファイルを分割するようにした。 vue.config.js configureWebpack: { optimization: { runtimeChunk: 'single', splitChunks: { chunks: 'all', maxInitialRequests: Infinity, minSize: 0, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name(module) { // get the name. E.g. node_modules/packageName/not/this/part.js // or node_modules/packageName const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1]; // npm package names are URL-safe, but some servers don't like @ symbols return `npm.${packageName.replace('@', '')}`; } } } } } } 自作のVue.jsコンポーネントの構文エラー 上記の対応を行うと、Minifyされていないソースコード位置にジャンプできるようになったので読んでみると、自作のチェックボックスのコンポーネントを読み込むところでエラーになっていた。 こちらの記事を参考にstrictモードを切ってみようとするもうまくいかず。 こちらの投稿にチェックボックスでv-modelとcheckedを一緒に使ってるとエラーになると書かれていたので試しに消してみるとエラー箇所が変わった。 IE11非対応のライブラリのトランスパイル コンポーネントのエラーが解消したところで別のエラーが出るようになった。 node_modules配下にインストールされているライブラリはデフォルトではトランスパイルの対象とならないため、IE11非対応のライブラリの場合エラーになるとのこと。 バンドルを抑制したことでライブラリごとのjsファイルが生成されるようになり、どのライブラリでエラーが出ているのか確認できたので、これを指定。 今回はpackage.jsonに書かれているライブラリとpackage-lock.jsonに書かれている依存ライブラリにエラーがあった。 vue.config.js }, transpileDependencies: ['library1', 'library2'], configureWebpack: { この2つの対処をしたところ無事表示されるようになった(早くIEなくなってくれ…)。
- 投稿日:2021-12-24T11:31:08+09:00
Vue.js(Nuxt.js)で使用するref属性とは何か
概要 Nuxt.jsについて調べているとたまにrefという属性を見かけることがあります。 そんなref属性について自分なりに理解して備忘録としてアウトプットします。 ref属性とは ref属性とは、特定のHTML要素を参照するために使用するものです。 使用例 例えば、ページのマウント時にinputタグにフォーカスを当てたい場合 <!-- Component.vue --> <template> <input ref='sample' /> </template> <script> export default { methods: { inputFocus() { this.$ref.sample.focus() // "this.$ref.参照名"でref属性を定義した要素を取得できます } }, mounted() { this.inputFocus } } </script> ref属性を使用することで、特定の要素を取得して操作することができるようになる。 また、子コンポーネントタグにref属性を指定することで 親コンポーネントから操作することもできる。 <!-- index.vue --> <template> <Component ref='inputComponent' /> </template> <script> export default { mounted() { this.$ref.inputComponent.inputFocus // 子コンポーネントであるComponent.vueのinputFocus()メソッドを実行している } } </script> 参考文献 公式ドキュメント
- 投稿日:2021-12-24T11:24:10+09:00
【アウトプット】地域を限定的にしたフードロス削減サービスを作った
1.はじめに はじめまして!! 茶木(ちゃき)と申します。本名です。 宜しくお願い致します! 【Twitterアカウント】 https://twitter.com/chaki01288 現在は転職の為にウェブカツにてLaravelやVue.js、AWSを学んでいます。 アウトプットでWEBサービスやWordpressのテーマ販売サイトを作成しています。 今回はその一環としてフードロス削減サービスを作りました。 私事で恐縮ですが 自分自身、趣味が筋トレ、ランニングで6年以上続けているのですが、それらの努力が 無に帰す程お酒が大好きでコロナ禍で家でずっと飲んでました。 最近は少し落ち着いてきたのでたまに近所の居酒屋で飲んだりします。 (オミクロンでまた自粛中) 酔いも相まって、そこで働いてるおじいさんやおばあさんと仲良くなったりすると 何か出来る事はないかと思っちゃうんですよね。 2.目的 目的は前述した通りフードロス削減です。 なぜそのサービスを作ろうかと思ったか、その背景は以下の通りです。 ・世界のフードロス 突然ですが皆さんは日本の食品廃棄物てどのくらいあるか知ってますか? なんと、 年間2,550万tに及びます。 これは世界3位です。 ちなみに1位は中国、2位がアメリカです。 そして、その食品廃棄物の4分の1がフードロスなんです。 話しを戻しますが、正直世界がどうとかはあまり興味なくて、自分の手の届く範囲だけで 良いから地域で助け合い出来ないかなって思いまして、自分の好きな居酒屋さんや飲食店の 経費削減(フードロス)に貢献したいと思い作りました。 ・個人経営の居酒屋あるある 個人経営の居酒屋や飲食店で結構見かけると思うんですが、お客さんとの距離が やっぱり近いんですよね。 そしてフランチャイズとかではないから融通も利く。 例えば「こんなメニュー欲しい」とか「あんな飲み物作って」とか、本社を通したりするわけではないので フットワークも軽く出来ちゃうわけです。 それをリクエストしてくれたお客さんが毎日通ってくれて、毎回それを注文してくれるなら 良いと思います。 しかし現実問題そんな事はあまりなくて、メニュー数だけが無駄に増えていきます。 そしてメニューがあるって事はそれを作るための材料も抱えておかないといけない。 たまに顔出して頼んでくれるもんだからメニューを無くしたりもできない。 そうなると原価率だけ上がって、客数も単価も上がらないと利益が出ません。 そういった背景からこのサービスを作りました。 3.サービス概要 1.食材を登録または検索 新規登録するとまずは食材を登録、または食材を探す画面が表示されます。 お知らせ等もこの画面で見ます。 2.地域を限定して表示 登録してある食材はユーザーの登録した住所の近隣のお店の物しか表示されません。 このサービスのコンセプトが「手の届く範囲で良いから助け合う」だからです。 3.気になる食材があれば「いいね」 気になる食材があれば「気になるボタン」を押します。 相手ユーザーに通知され、気になるリストに登録されます。 4.その中からメッセージを送る相手を選ぶ 誰でも投稿者に自由にメッセージを送ることが出来ても相手からしたら煩わしいと思ったので メッセージでやりとりする相手は「いいね」を押したユーザーの中からか、食材の登録者自身が決める仕様にしました。 5.メッセージ等はPUSH通知でお知らせ PUSHERを使用しているので サイトにいる限りはリロードしなくても相手からのメッセージが届いた時や、気になるボタンを押してもらった時に通知が届きます。 参考:PUSHERの参考記事はこちら 5.賞味期限前日で自動削除 当然ですが賞味期限の切れた食材が表示されているとまずいので 賞味期限前日になったらその食材は自動で削除されます。 それに紐づくlikesテーブル,messagesテーブルも削除されます。 プロジェクト内のstorageフォルダ内の実際の画像ファイルも削除されるので プロジェクト内の肥大化を防ぎます。 食材を登録する際も賞味期限が当日のものは登録できません。 6.成約済みから3日経過で自動削除 メッセージを通して食材を渡すユーザーが決まったら、食材を登録したユーザーが「成約済み」ボタンを押します。 こちらも上記同様、成約済みのデータがいつまであってもパフォーマンスが落ちるので3日経つと削除されます。 4.こだわったポイント 1.登録した食材を使った人気レシピ表示 楽天レシピAPIと連携させて、登録した食材のおススメレシピTOP4を 表示させています。 ただ単に食材が登録されているよりも、その食材を使ってどんな料理を作るれるか。という体験をユーザーに想起させる事でフードロス削減に繋がりやすいかもしれないと考えました。 本当はクラシルかクックパッドのAPIがあればそちらを利用したかったです。 2.非同期処理 動的セレクトボックス 今までだとDBのカテゴリー情報等を展開する時は変数の中にDBから取得した配列を入れて、「foreach」で展開。というのが 当たり前でしたが、今回は楽天レシピの膨大なカテゴリーの中からユーザーがカテゴリーを選択します。その中から該当のものを選ぶのは大変なので大カテゴリーを選んだら中カテゴリーは動的に絞られる様にしました。 ページングやタブの切り替え ページングやタブもvueの非同期処理でDBの情報を展開しました。 タブを切り替えてもページ情報の保持が少し大変でした。 住所検索 こちらも入力した郵便番号をvueで取得してaxiosでPHP側に渡し、DB内の全国の住所情報と照らし合わせて 住所を自動入力できるようにしました。 3.食材は直接会いに行って受け取る カード決済機能とか実装しようと思ったんですが、このサービスのもう一つの狙いで「地域のコミュニティの活性化」があります。地域を限定したのもこれが理由で、コンセプトである「手の届く範囲で良いから助け合う」これを実現させる為にFace to Faceは不可欠だと思いました。 それゆえ自分の住んでる場所から歩いて行ける距離のお店しか表示されません。 それによる「ついで消費」も発生すれば良いかな。と 4.その他 食材の画像を登録する際は自動でリサイズしてstorageフォルダに保存したり テーブルを削除したらちゃんとプロジェクト内のimageファイルも削除等 とにかくプロジェクト内の肥大化を防ぐのとファイル一つ一つの軽量化を 心掛けました。 使用言語 Laravel5.8 JavaScript(Jquery,Vue.js) 外部サービスやAPI 楽天レシピ・・・レシピ取得 PUSHER・・・リアルタイム通知 vuejs-pagenate・・・ページネーション Intervention Image・・・画像リサイズ、圧縮 Croppie・・・プロフィール画像切り抜き登録 maatwebsite・・・CSVデータをDBにエクスポート ER図 今後実装したい事 まだ開発環境なのですが、デプロイするタイミングで追加したい機能です。 ソーシャルログイン機能 メールリマインダー サイトを開いていなくてもPUSH通知 課題 収益化 無料で公開しているうちはまだまだ3流、4流だと思うので今後どの様にして 収益化させるかが課題。 数ヵ月後に月額利用料(Larevel cashieを使ったサブスク決済等)、追加機能利用で料金発生、広告スペース設置 等々? そもそもターゲットが厳しいかもしれないのでポジショニングをずらさないといけないかもしれません。 個人経営の飲食店がターゲットだとそんなにお金持っていないだろうし。。。 色々考えないといけないですがまずは世に出すのが大切だと思ってます。 終わりに 最後まで読んで誠に頂きありがとうございました! 本番環境に移せたらまた記事を更新したいなと思います。 ご指摘等ありましたら、バシバシお願い致します。
- 投稿日:2021-12-24T11:22:58+09:00
Vue.js関連のメモ
この記事は、Vue.jsに関する全くの個人用メモとして残すものです。随時追記! 1. Vue-cliでプレジェクトを作るときのメモ ☞ まだインストールしていなかったら。。。 npm install -g vue-cli プロジェクトを作るとき。xxxは自分のプロジェクト名。 vue init webpack xxx 自分のプロジェクトに移動した後に、 npm run dev
- 投稿日:2021-12-24T09:40:01+09:00
Firebaseを使い始めるために必要な手順
Firebaseを使う時に初期化する方法でつまづいたのでメモです。 ①npm install firebaseをする。 ②src/config/firebase-config.tsファイルを作成し、以下のようにコードをかく。 src/config/firebase-config.ts import { getStorage } from "firebase/storage"; import { getFirestore } from "firebase/firestore"; import firebase from "firebase/compat/app"; export const firebaseConfig = { apiKey: "process.env.VUE_APP_API_KEY", authDomain: "process.env.VUE_APP_AUTH_DOMAIN", projectId: "process.env.VUE_APP_PROJECT_ID", storageBucket: "process.env.VUE_APP_STORAGE_BUCKET", messagingSenderId: "process.env.VUE_APP_MESSAGING_SENDING_ID", appId: "process.env.VUE_APP_APP_ID" }; //Firebase.initializeApp()を何度も呼び出すことを防ぐために以下のコードで対応 export const firebaseApp = !firebase.apps.length ? firebase.initializeApp(firebaseConfig) : firebase.app(); export const storage = getStorage(firebaseApp); export const db = getFirestore(); ③Firebase構成オブジェクト情報を隠しファイルに記載する。 Firebase構成オブジェクト情報は隠しておきたい情報なので、.envファイルに以下のように書いておき、上のファイルから読み取れるようにしておく。また、VueCLI3.x 4.xで環境変数を扱う場合は .env ファイルに VUE_APP_ の接頭語をつけるということなのでVUE_APPも忘れずにつけておく。 .env VUE_APP_API_KEY = "自分のapiKey" VUE_APP_AUTH_DOMAIN = "自分のauthDomain" VUE_APP_PROJECT_ID= "自分のprojectId" VUE_APP_STORAGE_BUCKET = "自分のstorageBucket" VUE_APP_MESSAGING_SENDING_ID = "自分のmessagingSenderId" APP_ID = "自分のappId" ④gitにも上げないようにするため、.gitigonreファイルにも以下のように記載する。 .gitignore ./.env. ⑤必要なものをimportする。 Firebase JavaScript SDK (v9.0.0)が2021年8月25日にリリースされたということで、前のバージョンであるv8系のコードとの互換性の問題がある。v8系のコードをv9でも使える様にするためには、基本的にimport時のpathにcompatを加えるだけで使えるとのこと。 Firebaseのサービス共通で必要なのは以下のコード。 exaple.vue import firebase from "firebase/compat/app"; import "firebase/compat/auth"; import { firebaseConfig } from "@/config/firebase-config"; storageを使用する時には、 const storage = getStorage(firebaseApp); Firestoreを利用する時には、 const db = getFirestore(); などの定義が必要。 それぞれの変数の使い方については公式ドキュメントを参照。 また、firebase/storageやfirebase/firestoreなどのモジュールから適宜必要なメソッドをインポートする。 終わりに 公式ドキュメントの方法だとうまくいかず、手探りで解決策を見つけました。 今のところ動いていますが、より良い方法がありましたらぜひ教えていただきたいです!
- 投稿日:2021-12-24T01:28:52+09:00
我不屈抗体注射施行開発 -開発初心者な私が1ヶ月でウェブサービスを作るまで-
我ワクチン注射にも屈さず開発を施行する ※この記事は「木更津高専アドカレ2021 1本目」の記念すべき25日目の記事です! 前→ 木更津高専と雄峰寮、サバイバル術 by @chizuchizu 「フォッフウデイタイォッフォッ Merry X-"SMASS"!」 はじめまして、NIT Kisarazu C 情報工学科2年のサンタ、とすあっぷと申します。 ほぼ初めての記事なので、至らぬ点が多数あるかと存じますが、お手柔らかにご指摘ください。 全くクリスマスにふさわしくないので、クリスマスに相応しい記事を見たい方は、こちらをご覧ください。 ちなみに今日ワクチン打ちました。2回目です。 タイトルはそういうことです。 何を書くんだい? 色々書きたいことはあるのですが、今回は「開発初心者な私がたった1ヶ月でウェブサービス(笑)を展開するまで」を書かせていただきたいと思います。 学寮のことはみんな書いてるからいいでしょ もくじ 採用篇 ~私、どうなっちゃうのー!?~ 開発篇 その1 ~うわっ…俺の技術力、ゴミ過ぎ?!~ | With Rust 開発篇 その2 ~C#こそ至高…え?!Webも書かなきゃなんですか?!~ | With C#(ASP.NET), JavaScript(Vue.js) 開発篇 その3 ~開発の刄 無限議論編~ | With Team 1. 採用篇 そもそもなんでこんな事態になったのか、ご説明致します。 「第4回高専防災コンテスト 2ndステージ」に採択された おしり ーーー ⓃⒾⓉ 無責任ですね。ちゃんとご説明致します。 私が所属(笑)している木更津高専 情報工学科2年チーム Trialでは、「避難所の運営を紙媒体からデジタル媒体へ」というアイデアを年始から練り始め、様々なコンテストへ応募して参りました。 最初はRFIDタグを用いたものから、次第にユーザーへ寄り添ったサービスへ。 最終的に考え出されたのはナウでヤングな"QRコード"を用いたシステムでした。 それが第4回 高専防災コンテストで採択された「避難所運営をサポートするITシステム Shelter Management And Support System 【SMASS】」です。 このシステムのウリは「アプリのダウンロードがいらない点」等ですが、このご時世、何をどう考えてもアプリがないと辛いですよね。めちゃんこデメリットだと思います。 2. 開発篇 その1 このシステムを作成するにあたって、サーバーサイド、クライアントサイドの作成、そしてデータベース設計が必要になります。 私は「どうせ私しかAPIサーバー書けんのやろうな」とサーバーサイド開発、チームメイトが他を作成してくれることになりました。 開発言語はAll Rust。DB設計者の希望です。 私はすでに嫌な予感がしていましたが、まあどうにかなるだろうと開発を開始しました。 (なお、この中でRustを書ける人は誰一人としていません。) <- ここ重要 さて、採用されたのは10月中旬。開発完了予定までは2ヶ月半あります。 最初は全員ゴリゴリ書いていました。最初は。 1ヶ月ほど経ち、それぞれ気づきます。 「これ無理くね?」と。 そらそうなんですよね。私はC#er、開発メンバーはJavarとPythonerです。 私がSAN値チェックに失敗し、発狂をキメ、開発言語は私のプレゼンテーションによりC#に変更になりました。 それが試験直前だというのは、また別の話。 3. 開発篇 その2 C#に変わっても開発パートは変わらず。 そこで私気づきます。 「サーバーサイドってもしかしてフルスタックやらんとあかん?」 と。(そのとおりなのは、また別の話。) 生粋のVSerである私は、VS2022の新機能、VueとC#を同時に開発できそうな機能を用いて開発を開始しました。 私「Vueの勉強からか…(試験直前)」 チームメイト「俺ら触ったこと無い言語やから、がんば」 何度でも言いますが、アプリケーションにしたほうが絶対都合がいいですよね。 ということで環境構築の方法です。 今回の環境は以下です。 OS: Windows 11 Dev(22494.1000) CPU: AMD Ryzen 7 5700U RAM: 16GB (DDR4-3200) ROM: WDのNVMeみたいなやつの512GBのやつだと思う GPU: Radeon Graphicsくん(専用512MB) 使用するソフトウェア等 IDE: Visual Studio Community 2022 v17.0.2 (以下VS) データベース: MySQL v8.0.27 .NETフレームワーク: .NET 6 JS環境: Node.js v14.18.1 あと Vue v2.6.14 (待機時間を除いて)人によっては3分で出来る環境構築〜! VSをDLする! 入れる! 終わり! やったあ! (詳しい入れ方は調べてください) Vueで書き進めていくうちに様々な困難が…というかそもそも知らないのでそんなこと?って思われるかもしれない壁にぶち当たっては砕け散ってます。 あまりにも動かなくて環境消し飛ばしを15回くらいやってるので、環境再構築RTAは世界新だと思います。多分。(適当) 折角なので私が困ったことも書こうと思います。 1. CORSポリシーに超引っかかった バチバチに引っかかりました。 同じlocalhostでもポートが違うとダメなんですね。 ということでプロキシ機能を使いました。 vue.config.js module.exports = { devServer: { // ここから -- proxy: { '/api/': { target: 'https://localhost:5001' } }, // -- ここまで } } これで/api/にリクエストを投げると、https://localhost:5001で待機しているバックエンドにリクエストが飛びます。やったね! 2. Visual Studioで、「クライアントサイドライブラリ」を追加しても追加されへん 追加されへんのよね。 開発ディレクトリでコンソール開いて、npm install hogehoge --saveってしたらimportできました。 なんでか知ってる人教えてください。 3. ビルドが一個しか出来へん。何? ここに書いてあって素敵。 4. 開発が滞ってます 知りません。頑張ってください。 4. 開発篇 その3 無限議論してます。 メンターさんとのMTGは月1回。 他にも毎日Microsoft TeamsやGitHubなどを用いて、様々な情報共有をしています。 POSTがPASTでBOOSTしたり。(?) Issueたてたり夕焼け見たり。 開発?知らない子ですね。 今度、地域の学校で検証させていただく予定です。楽しみ。 最後に 投げやりな記事でごめんなさい。許してください。 あと開発頑張ります。 最後までご覧頂きありがとうございました。