もふもふ技術部

https://tech.mof-mof.co.jp

IT技術系mofmofメディア

フィード

記事のアイキャッチ画像
【Rails・Next.js】技術研修で初モノレポ構成でタスク管理アプリを作ってみたレポート
もふもふ技術部
こんにちは。鳥井です!株式会社mofmofに入社して2ヶ月が立とうとしています。mofmofでは実際にアプリを作るという技術研修があり、入社してから技術研修に集中して取り組んでいました。個人的には技術部分が初めて触ったところが多くて忙しくも楽しい技術研修になったと思います。今回はmofmofの技術研修レポートとしてどんなものを作ったか、技術研修を通して学んだことについて紹介します!概要タスクを管理するTodo Appです。WEBブラウザで確認したTodo Appの画面機能は下記の内容で一通り実装しました。一覧表示機能詳細表示機能ユーザー登録機能ログイン機能タスク一覧情報のCSVデータ出力技術スタックについて技術スタックについては以下の通りです。DockercomposeRuby on Railsdevice-auth-token使用RubyGraphQLclient: Apollo Clientバックグラウンド処理系SidekiqフロントエンドNext.jsApp RouterTypeScriptTailwind CSSインフラHerokuDBPostgreSQL技術研修内で行ったこと 実装はもちろん担当していましたが技術研修では技術定例があり、そのファシリテーションも担当していました。 技術定例とは週に1回案件に関わっているメンバー同士で行うものです。(技術研修では週1)実際に案件を担当したという流れで進捗と、今後の進め方について話を行いました。実は個人的にファシリテーションをやるのは緊張しており正直に言いすぎて、何か失言したのではないかとずっとヒヤヒヤしていましたね…。今回は技術研修でしたが実際のファシリテーションを担当する時は技術研修で経験したことを活かしていきたい、と思いました。技術研修を通してわかったことについてフロントエンド部分実装が弱点私はフロントエンド業務を行なってきたことがないことも関係しており、そこが今苦手な分野なんだと感じました。今後はバックエンドとフロントエンドどちらも担当することが増えるのでフロントエンドの方も個人で勉強を進めていきたいところです。非同期処理の実装方法今まで個人で実装してきたものも同期処理が主な実装だったので今回初めて非同期処理の実装を行いました。Next.jsの書き方がわからないことから結構苦戦しましたが…。基礎はつかめた
5ヶ月前
記事のアイキャッチ画像
Raycast活用テックトーク
もふもふ技術部
こんにちは、岩井です!テックトーク記事化シリーズ、今回は「Raycastずっと使ってるけどあんまり使いこなしてないので、おすすめの使い方紹介」回です!この記事に辿り着く方はみなさんご存知かなと思いますが、Raycastは強化版Spotlightみたいなやつです。www.raycast.comこの記事はテックトークに登場した機能たちをTips集的にまとめてみようと思います。Raycast vs Spotlightランチャーとしての機能はそもそも標準でSpotlightがあるからSpotlightでいいんじゃないかという話が出ました。いやしかし、Raycastには標準でクリップボードの履歴機能が使えたり、(真偽は定かではないですが)Spotlightより検索が早そうな気がするなど、利点があります。ホットキー設定や拡張の利用、AI連携など、単なるランチャーを超えたアプリです。システム設定システム設定がいじれるのが割と便利です。拡張機能にはなりますが、Set Audio Deviceというものを入れると音声の入出力デバイスも簡単に変更できます。AI機能の活用プロンプトをimportしてショートカットから呼べるようにしておくと、選択中の文字列をプロンプトに埋め込んでAIに渡す、みたいなことができるようです。zenn.dev計算とか変換とか日付: 2 days agoで2日前の日付を表示できるタイムゾーン変換: now to utcでUTCの時刻が表示できる通貨換算: $100と入力すると日本円でいくらかわかるなど!細かい機能たちQuicklinkshttps://google.com/search?q={argument} というものを使っていました。スニペットSearch Emojiスクリプトの実行Confettiホットキー設定Raycast Note(Extension) Authy以上自由にスクリプト書けるしAIにプロンプト渡しての処理もできるし、無限の拡張性を持っていることがわかりました(知らなかった)
6ヶ月前
記事のアイキャッチ画像
mofmof推奨課題管理ツールはPivotal TrackerからLinearに変更になりました
もふもふ技術部
こんにちは。出口です。表題の通り、mofmofが推奨する課題管理ツールをPivotal TrackerからLinearに変更することになりました。mofmofでは以前までPivotal Trackerを推奨していました。www.mof-mof.co.jpwww.mof-mof.co.jpですが、先日サービスが終了してしまいました。数ヶ月かけて移行先を調査・検討した結果、一度はNotionを使うことに決まったのですが、実際に使い始めると色々と問題があり、Linearを使うことになりました。この記事では、Pivotal Trackerからの移行の紆余曲折、Linearにした理由などを語りたいと思います。Pivotal Trackerのどこが良かったのかこれまで、Pivotal Trackerを推奨してきたわけですが、改めてどこが良かったのかをまとめておきます。開発スピードが可視化されるこれがPivotal Trackerを使う最大のメリットだと思います。開発スピード ≒ ベロシティ = 完成したプロダクトバックログアイテムのサイズの合計これが自動的に計算され画面に表示されます。ベロシティが10の時また、ベロシティに合わせて、登録されているストーリーが自動的にスプリントに割り当てられるようになっています。1スプリントに割り当てられるストーリーのストーリーポイント合計がベロシティの値に収まるように自動調整されているこれによって、いつ何ができそうかが分かるようになっているので、ビジネス面での計画も立てやすいです。意外とこれができるツールが少ないです。優先度が並び順で分かりやすいPivotal Trackerは登録されているユーザーストーリーを上から順番にエンジニアが着手していけばよい作りになっています。よくある優先度という項目はないので、優先順位がクライアントにもエンジニアにも分かりやすいです。(アップデートでオプションで優先度を追加できるようにはなりました。)優先度という項目があった場合、よくあるのが全部優先度が「高」だったり、「中」で、結局どれが優先なのか分からないという問題があります。それがなくなるのも良い点だと思います。サービス終了してしまったのですが、以上が良かったところです。候補をどのようにして選んだのか移行先を選定する際に、気にしていたこともまとめておきます。P
6ヶ月前
記事のアイキャッチ画像
ベロシティは提供価値の量?作業量?見積もりテックトーク
もふもふ技術部
mofmofでは毎週金曜日の昼に開発手法やトレンドの技術、案件の困りごとなどについて雑談をするテックトークの時間を設けています!今回のテーマは「ユーザーストーリーのポイント付けについて」。ユーザー価値が直接見えにくいタスク が存在します。話したこと(読み飛ばしても大丈夫です)ストーリーのポイント付けに悩んだ 調査の扱いについて ベロシティをなんの指標にするか バグのポイント付けについて 顧客のスタンスと開発スタイルの相性 という内容でした。以下、o3くんがいい感じにまとめてくれた表を掲載します!背景と論点整理論点主な意見代表的な発言 調査ストーリーにポイントを付けるか 案件による。調査が実装の20%以上ならポイント付与もアリ。 「結局これ結構時間取ってるしポイント付けたい ってことがある」 リファクタリングやパフォーマンス改善は? “価値”ではなく“作業量”として測りたいニーズが強い。 「リファクタリングはChoreにしちゃうとベロシティが下がる…」 バグにポイントを付けるか 新規機能由来バグなら機能内に吸収。保守案件ならバグ単体に付与。 「既存バグを直すだけならポイント付けたくなる」 ベロシティを何に使うか ①機能開発の計画精度②顧客への進捗説明用途で変わる。 「次の目標設定に使いたい」 価値ベース vs. 作業量ベース ― ふたつのベロシティ価値ベース作業量ベース 目的 ユーザー/ビジネス価値の提供速度を測る 工数消化ペースを測り、納期・稼働を説明 含めるタスク “ユーザーストーリー”のみ(調査・バグは原則 0pt) 調査・バグ・リファクタリングにもポイントをつける 向いている案件 開発した価値がビジネスの成果に直結する開発自社サービス、長期プロダクト、顧客がアジャイルを理解している、など 作ることが目的となっている案件受託開発、納期固定、顧客が開発リソースを求めている場合、など メリット 提供価値にフォーカスできる 顧客と“いつ終わる?”を会話しやすい デメリット 調査・バグが多いスプリントは数値のブレが大きい スクラムを採用するか?から考えるべきかも どうしたらいい?結論: 案件ごとに話し合って決めましょう契約前の打ち合わせやキックオフなどで、開発スタイルに関する意識・関わり方についての認識をそろえておくと良さそうです。観点は以下の通り。顧客は“価値”と“作
7ヶ月前
記事のアイキャッチ画像
社内あれこれ調査の結果
もふもふ技術部
採用資料があまりにも古かったので更新しようと思い、社内のあれこれを調査するアンケートを実施してみました。全部で11名の回答です。Mac or Windows10割Macでした!業務で必要な時にだけWindowsも使うというメンバーもいました。mofmofの業務は私物と会社支給の選択肢があり、会社支給の場合はその時点でのそこそこ良いスペックのものを用意してもらえます。めちゃくちゃ盛るということはしないですが、業務で不便を感じない程度にはカスタマイズしてくれる感じです。業務で使ったことのある技術各技術を何%のメンバーが使ったことあるか、って表記です。四捨五入してます。Rails: 100%React, Next.js: 91%Node.js, Express, NestJS: 82%jQuery: 73%Vue.js, Nuxt: 55%Python: 36%React Native: 27%Swift: 9%Kotlin: 9%Flutter: 9%元々RailsでのMVP開発を得意としていた会社なのでRailsは100%でした。2018年、2019年頃はVueの案件が多かったのですが、現在ではReactの方が多いのは時代の変遷を感じますね。また、サーバサイドをJavaScript(TypeScript)で書くことが増えているのも時代感があります。モバイルはもっと多いかなと思っていたんですが、意外と少ない結果となりました。(個人開発を含めたらもっといそう)個人開発プロダクト数mofmof全体では約61個でした!多い人で10個前後です。4,5,6個程度作ったという人が大半でした。長期間運用するとなると個数は増やしづらいので一概に数が多けりゃいいというものでもないですが、これからも年末年始やGWを経るたびに増えていくことでしょう。楽しみです。使ってるエディタVS Code: 6Cursor: 5RubyMine・IntelliJ IDEA: 2Neovim: 2Xcode: 1VS Code・Cursorが強い!これらが優勢で、一部Neovimユーザーもいるという結果でした。IntelliJはAI時代に少し乗り遅れている印象があり、ユーザー2名はCursorに移行中です。好きなツールブラウザ、メモ、ユーティリティなど。自由記述で募りました。まずは複数票を獲得したツールです
8ヶ月前
記事のアイキャッチ画像
もふもふ技術部まとめ2024
もふもふ技術部
こんにちは。出口です。2024年もそろそろ終わりということで、今年もふもふ技術部に投稿した記事をまとめようと思います。2024年に投稿された記事は以下のとおりです。全部で47件でした。www.mof-mof.co.jp2024年投稿技術記事、表示回数TOP102024年に投稿された記事の中から表示回数の上位10記事は以下のとおりです。※表示回数は、Google Analyticsの値を参照しています。1. 既存プロジェクトでPrettier・ESLintをBiomeに移行してみたwww.mof-mof.co.jp1位は、Biomeの記事でした。ちょうど世間的にもBiomeへの移行が進んできていたタイミングで出した記事だったのも影響してそうです。2. 技術ブログをNuxt + Netlify + Contentfulから、はてなブログ for DevBlogに移行しましたwww.mof-mof.co.jp2位は、はてなブログへの移行記事でした。2024年2月にNuxt, Contentfulで作られていたブログシステムから、はてなブログ for DevBlogに著者情報を残して移行したのが好評だったみたいです。3. Rails7 に Tailwind CSS を導入するwww.mof-mof.co.jp3位以降は、Railsの記事が多数ランクイン。4. Rails7 に React + TypeScript を導入するwww.mof-mof.co.jp5. Rails7 + PostgreSQL + esbuild を docker-compose で環境構築するwww.mof-mof.co.jp6. FlutterでiOSアプリのビルドとデプロイを自動化した話www.mof-mof.co.jp7. 【Rails7】任意のviewsからJSファイルを個別に読み込むwww.mof-mof.co.jp8. 【Rails7】特定のviewsで部分的にReactを使うwww.mof-mof.co.jp9. Rails7 + MySQL を docker-compose で環境構築するwww.mof-mof.co.jp10. Rails7 + PostgreSQL + importmap を docker-compose で環境構築するwww.mof-mof.co.jpその他の
1年前
記事のアイキャッチ画像
テックトーク管理用Slack Botでテックトーク運営を自動化した
もふもふ技術部
こんにちは。出口です。先日紹介したテックトーク用のSlack Botを作りました。テックトークについては以下の記事を参照してください。www.mof-mof.co.jpSlack Botのアーキテクチャは以下の通りです。メインはGoogle Apps Script(GAS)です。TSで書いたものをVite + RollupでJSにトランスパイルしてclaspを使ってGASに反映する形にしました。vite.config.jsの内容は次の通りです。import path from "node:path";import rollupPluginGas from "rollup-plugin-google-apps-script";import { defineConfig } from "vite";export default defineConfig({ plugins: [ rollupPluginGas({ manifest: { copy: true, }, }), ], build: { rollupOptions: { input: "src/index.ts", output: { dir: "dist", entryFileNames: "index.js", format: "iife", }, }, minify: false, // trueにすると関数名が消えるのでfalse必須 }, resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, },});rollup-plugin-google-apps-scriptを使っています。github.comトラブルシューティングNotionのデータベースからデータが取得できないデータを取得するには、Notionのインテグレーションとデータベースを接続する必要があります。インテグレーションはワークスペースのオーナーでないと作成出来ないので注意が必要です。定期実行の設定が思った感じに出来ないGASはトリガーを設定して定期実行が可能ですが、設定の最小単位が1時間なので、そのままだと決まった時間に定期実行することは出来ません。// 毎週金曜日9時〜10時のどこかで実行されるScriptApp.newTrigger('notifyTechTalk
1年前
記事のアイキャッチ画像
React Router v7、Prisma、Vitestでテストを書き始める準備
もふもふ技術部
Remixを触ってみたいと思っていたら、React Router v7に統合されてしまいました。そんなReact Routerでプロジェクトを作成し、PrismaとVitestを導入してテストを書き始める準備について書きました。React Routerでプロジェクト作成docker composeでDBを用意Prisma導入スキーマ定義開発用とテスト用のDBをそれぞれ作成SeedingVitest導入vite.config.tsの設定tsconfig.jsonの設定テストを書いてみるさいごにReact Routerでプロジェクト作成プロジェクトの作成は以下を実行します。> npx create-react-router@latest my-react-router-appNeed to install the following packages:[email protected] to proceed? (y) create-react-router v7.0.2 ◼ Directory: Using my-react-router-app as project directory ◼ Using default template See https://github.com/remix-run/react-router-templates for more ✔ Template copied git Initialize a new git repository? Yes deps Install dependencies with npm? No ◼ Skipping install step. Remember to install dependencies after setup with npm install. ✔ Git initialized done That's it! Enter your project directory using cd ./my-react-router-app Check out README.md for development and deploy instructions. Join the community at https://rmx.as/discorddocker composeで
1年前
記事のアイキャッチ画像
mofmof式テックトークでエンジニアの成長・コミュニケーションを活性化する
もふもふ技術部
こんにちは。出口です。今回は、mofmofで毎週開催している「テックトーク」という取り組みについてです。エンジニアが気になっている技術的なトピックについて気軽に話し合える場として、大切にしている取り組みの1つです。コロナ禍以前はオンサイトで実施していましたが、コロナ禍以降フルリモートになってからはオンラインで開催しています。mofmof式テックトークとは?なぜテックトークを始めたのかmofmof式テックトークの特徴的な運営方式柔軟な参加形態聞き専での参加もOK実況スレッドを活用して後から内容が分かる仕組みテーマ設定の工夫毎月のネタ出し会で、みんなで話したい内容を決める定期的なテーマを決めない雑談会も実施深い話題については別途、場を設ける運営負担を最小限にファシリテーターは当日決定テックトークBotを作成し、自動化テックトークを通じて得られる価値個人の成長チーム、組織の成長おわりにmofmof式テックトークとは?mofmof式テックトークは、毎週金曜日のお昼に45分間開催している技術的な雑談の場です。以下のような特徴があります。完全自由参加制(聞き専OK)カジュアルな雰囲気重視毎月のネタ出し会で約1ヶ月分の予定を決定ネタはNotionにて管理Slackにて都度実況スレッドを立ち上げファシリテーターは当日決定事前に予定を決めておくことで、興味があるネタに参加してもらいやすくする狙いがあります。また、実況スレッドを作ることで、聞き専の人やお昼時にマイクをオンにしづらい人も話に参加しやすくしています。ファシリテーターを当日決めるという制約を設けることで、よりカジュアルになるよう工夫しています。なぜテックトークを始めたのかエンジニアにとって、技術的な知見を共有し合える場というのは非常に重要です。コロナ禍前は昼食中やちょっとしたタイミングで隣のエンジニアと雑談する機会がありましたが、コロナ禍以降フルリモートに移行してからは雑談の機会が極端に減ってしまいました。フルリモートになって出てきた課題に次のようなものがあります。このあたりはリモート化が進んでいるところだと皆同じだと思います。案件が異なるエンジニア同士で技術的な知見を共有する機会が少ない気になっている技術について気軽に話せる場が欲しいSlackだけではなかなか深い技術的な会話が難しいこれらの課題を解決するため、「カジュアル
1年前
記事のアイキャッチ画像
Unityで重力を設定した球体の惑星の上を歩き回る
もふもふ技術部
地球上に立っている人が剛速球を投げて、地球を1回転して戻って来て自分でキャッチするための条件を確かめるためにUnityの物理エンジンでシミュレーションしようと思ったので、まずは自由に動き回れる惑星を実装してみた。ドラゴンボールの界王星みたいな感じ。オブジェクトの配置まず適当なプロジェクトを作り、直径30mほどの球を配置する。Hierarchy - 3D Object - SpherePositionは全て0Rotationは全て0Scale X: 30, Y:30, Z:30オブジェクトにPlanetという名前をつけておく。目印用に適当にCubeをおいておく。Hierarchy - 3D Object - CubePosition X:0, Y:17, Z:0Rotationは全て0Scaleは全て1ComponentにRigidBody, Box Colliderをセット。独自の重力を実装するためUse Gravityをオフにしておく。次に操作するプレイヤーを作り、カメラを追従させる。だいぶ昔にやったことあるので、この辺のポストを参照。www.mof-mof.co.jpHierarchy - CreateEmptyオブジェクトにPlayerという名前をつける。Position X:0, Y:16, Z:-4Rotationは全て0Scaleは全て1こちらもRigidBody, Box Colliderをセット。Use Gravityをオフ。Hierarchyビューから、Main CameraをドラッグしてPlayerの下に入れておく。重力の実装Use Gravityをオフにしているのでこのままだと浮いた状態になっている。そこで、Planetの中心に重力点があるようにスクリプトで実装する。Player - Inspector - Add Component - New Script - Planet GravityPlayerGravity.csusing System.Collections;using System.Collections.Generic;using UnityEngine;public class PlanetGravity : MonoBehaviour{ [SerializeField] private Transform sphereCe
1年前
記事のアイキャッチ画像
【Rails】Gemを使わないFinderオブジェクトを使った検索
もふもふ技術部
Railsで検索を実装するとき、どのように実装しますか?ransack などのGemを使っていますか?事前準備1. モデルを作る$ rails g model post20241117000000_posts.rbclass CreatePosts < ActiveRecord::Migration[7.1] def change create_table :posts do |t| t.references :category t.string :name t.string :content t.timestamps end endend20241118000000_categories.rbclass CreateCategories < ActiveRecord::Migration[7.1] def change create_table :categories do |t| t.string :name t.timestamps end endend2. マスターデータを用意する以下のようなカテゴリーを用意します。 ID カテゴリー名 1 野菜 2 果物 3 お肉 4 穀物 実装する1. Formオブジェクトを作るapp/forms/search_posts_form.rbclass SearchPostsForm include ActiveModel::Model include ActiveModel::Attributes attribute :keyword, :string attribute :category, :string, default: :noneend2. Finderオブジェクトを作るapp/finders/base_finder.rbclass BaseFinder def initialize(q) @q = q end private def like_search_condition(words) words.map{ |word| ("%#{ActiveRecord::Base.sanitize_sql_like(word)}%") } end def split_freewords(keyword) keyword.split(/\,| | |\、|,/) endendapp/finders/posts_finde
1年前
記事のアイキャッチ画像
【Rails/GitHub】お問い合わせフォームからの問い合わせ内容を自動でissue化!
もふもふ技術部
HPでもWebサービスでもお問い合わせフォームは必須な機能だと思います。事前準備GemをインストールするGemfilegem 'octokit'GitHubでアクセストークンを作成するgithub.comここからアクセストークンを作成します。Issues の項目だけ、 Read and write を許可します。作成したアクセストークンは1度しか表示されないので、忘れずコピーしましょうアクセストークンをcredentialsに追加するrails credentials:edit -e development を実行し、以下のようにアクセストークンを追加します。github: access_token: github_~~~~実装するルーティングを設定するconfig/routes.rbresources :inquiries, only: [:new, :create, :index]Formを実装する今回はDBに保存しないため、Formオブジェクトを使用します。app/forms/create_inquiry_form.rbclass CreateInquiryForm include ActiveModel::Model include ActiveModel::Attributes attribute :email, :string attribute :name, :string attribute :title, :string attribute :body, :string def save return false if invalid? ActiveRecord::Base.transaction do client = Octokit::Client.new(access_token: Rails.application.credentials.dig(:github, :access_token)) client.create_issue( "kanahebi/inquiry", # issueを作成するリポジトリを指定します。 title, issue_body, labels: [ "未対応", "お問い合わせ" ], assignees: [ "kanahebi" # 担当者をアサインしたい場合はここにGitHubのアカウント名を指定しま
1年前
記事のアイキャッチ画像
【Rails】ActiveHashでマスターデータを管理する
もふもふ技術部
DBに保存するほどでもないマスターデータをどのように管理するか悩んだことはありませんか?ActiveHash を使ってモデルライクにマスターデータを管理します。github.com事前準備GemをインストールするGemfilegem 'active_hash'マスターデータを用意する今回はカテゴリーを管理します。 ID カテゴリー名 1 野菜 2 果物 3 お肉 4 穀物 ActiveHash のモデルに直書きする場合app/models/category.rbclass Category < ActiveHash::Base self.data = [ { id: 1, name: "野菜"}, { id: 2, name: "果物"}, { id: 3, name: "お肉"}, { id: 4, name: "穀物"}, ]endyamlファイルで管理する場合app/models/category.rbclass Category < ActiveYaml::Base set_root_path Rails.root.join('config', 'active_yamls') set_filename "categories"endconfig/active_yamls/categories.yml- id: 1 name: 野菜- id: 2 name: 果物- id: 3 name: お肉- id: 4 name: 穀物使い方モデルを操作するのと同じように使うことができます。Category.all#=> [#<Category:0x0000ffff7b547d80 @attributes={:id=>1, :name=>"野菜"}>, #<Category:0x0000ffff7b547a10 @attributes={:id=>2, :name=>"果物"}>, #<Category:0x0000ffff7b547880 @attributes={:id=>3, :name=>"お肉"}>, #<Category:0x0000ffff7b5476f0 @attributes={:id=>4, :name=>"穀物"}>]Category.first#=> #<Category:0x0000ffff7b547d80 @attributes={:id=>
1年前
記事のアイキャッチ画像
【Rails I18n】localeのYAMLファイルで式展開する
もふもふ技術部
Railsで多言語化する時、yamlファイルに日本語訳や英語訳を追加していくと思います。事前準備config/application.rb に以下の設定を追加します。config.i18n.default_locale = :jaconfig.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]使ってみる1.yamlファイルを追加するconfig/locales/views/ja.ymlja: welcom: "ようこそ %{user_name} さん"式展開したい箇所を %{} で囲みます。user_name にしました。2. viewsで使うapp/views/top.html.erb<%= t('welcom', user_name: "もふもふ") %>yamlに設定した名前をキーに、値を指定します。ようこそ もふもふ さん と表示されます!ログインユーザー(current_user)の名前を指定したい場合は以下のようにします。<%= t('welcom', user_name: current_user.name) %>終わり色々な所で使える便利技なので、ここぞの時に使ってみたいです!
1年前
記事のアイキャッチ画像
【Rails I18n】URLで言語切り替えする
もふもふ技術部
アクセスしたURLによってlocaleを切り替える機能を意外と実装したことがなかったので、今回は3つの方法でlocaleを切り替えてみたいと思います。事前準備複数の言語を使用できるように設定するconfig/application.rb に以下の設定を追加します。config.i18n.available_locales = %i(ja en)config.i18n.enforce_available_locales = trueconfig.i18n.default_locale = :jaconfig.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]localeファイルを設置するconfig/locales/ja.yml や config/locales/en.yml など必要なファイルを設置します。URLのパスで言語を判断する場合www.hohoge.com/ja/docs や、www.hohoge.com/en/docs のようにURLのパスで判断する場合の実装方法です。www.hohoge.com/docs のように言語が指定されていない場合はデフォルトの言語を使用します。1. ルーティングを設定するconfig/routes.rbRails.application.routes.draw do scope '(:locale)', locale: /#{I18n.available_locales.map(&:to_s).join('|')}/ do ## 省略 endend全てのルーティングを scope '(:locale)', locale: /#{I18n.available_locales.map(&:to_s).join('|')}/ do ブロックで囲むようにしましょう!2. コントローラーに設定を追加するapp/controllers/application_controller.rbclass ApplicationController < ActionController::Base around_action :switch_locale private def switch_locale(&action) I18
1年前
記事のアイキャッチ画像
ECSコンテナの立ち上げをやってみた
もふもふ技術部
はじめに今回は、Railsの基本スタックのインフラ構築をAWSで行うことに挑戦しました。アプリをAWS上でデプロイするにあたり、ECS(Elastic Container Service)を初めて利用してみました。Railsの基本スタックをAWSで簡単に構築すること、特にDockerとECSを用いたコンテナ化、SidekiqやElastiCacheの導入も含めて実践しました。この記事では、その過程や得られた学びを共有します。デプロイの目的Railsの基本スタックをAWSで構築AWS上で、Railsアプリの本番環境を簡単にセットアップできるようになりたい。Sidekiqの本番利用を経験するこれまでHerokuを使っていたため、AWSでのSidekiqのセットアップに挑戦。DockerでのデプロイDockerで環境構築したアプリを効率的にデプロイできる環境を構築したい。ECSの利用経験を積むこれまで使ったことのないECSを学びたかったため、今回の機会に挑戦してみました。ElastiCacheの利用ElastiCacheを利用したことがなかったため、試してみることにしました。デプロイするアプリの構成フレームワーク: Rails 7Rubyバージョン: 3.2.4データベース: PostgreSQLバックグラウンドジョブ管理: Sidekiqキャッシュ: Redisサーバーレスコンテナ: ECS Fargateアプリの機能デプロイしたアプリは、シンプルな管理機能を持つもので、以下の機能を備えています。管理者ログインユーザー一覧の表示メール送信SidekiqダッシュボードRedisデータのビューアこのアプリを選んだ理由は以下のとおりです。バックグラウンド処理が視覚的に動作していることを確認したかった点Redisのデータを直接確認してみることが面白いと感じた点基礎の基礎の機能は動かしたかった点アプリ画像デプロイ方法今回は、CloudFormationやTerraformを使用せずに、AWS CLIを活用してインフラを構築するスクリプトを作成しました。スクリプトはChatGPTと相談しながら、AWSリソースの構築をスクリプト化することで、手動操作を減らし、デプロイの手間を軽減しました。詰まったポイントECSのタスク概念タスクの管理方法に戸惑い、タスクが起動したままの状態や管理方法
1年前
記事のアイキャッチ画像
IBM Qiskitでベル状態の量子回路を作る
もふもふ技術部
どうも、量子の人を目指して頑張ってます原田です。普段は量子の理論の方を学習しているので、コードを書くのは初だったのですが、予想以上に難しかった。というか、コードを書く=アプリケーションを開発するという認識しかなかったので、ビットを直接いじるというのはなかなか新鮮だった。今回は以下ベル状態と呼ばれる4つの回路を作ってみます。今のところまだベル状態が意味するものはいまいち分かっていないけど、2つの量子ビットがもつれている基底ベクトルというような理解。環境Google Colaboratoryで動かすとローカルで環境を構築しなくていいのでとても楽。colab.research.google.comクラウド上の機械学習の実行環境というような説明をされているのを目にするが、とりあえずPythonが動いてくれる環境で、別に機械学習に関わらず使える。アプリケーションのコードを書いていくというより、小さいコードのブロックを実行していて、その結果を記録していくようなドキュメントとコーディングをセットで出来る環境って感じ。まあいわゆるJupyter NotebookのGoogle版。まずは必要なパッケージのインストールとインポートをしておく。ざっとコピペなので、今回必要ないのも混ざっているかもしれん。!pip install qiskit>=1!pip install pylatexenc!pip install qiskit-aer>=0.15import numpy as npfrom qiskit_aer import Aer, AerSimulatorfrom qiskit_aer.noise import NoiseModel, pauli_errorfrom qiskit import QuantumRegister, ClassicalRegister, QuantumCircuitfrom qiskit.visualization import plot_histogramfrom qiskit import user_configfrom qiskit.quantum_info import partial_tracefrom IPython.display import Javascriptsv_sim = Aer.get_backend('statevector_
1年前
記事のアイキャッチ画像
【Rails】よく使う・知っていると便利なmigrateコマンド
もふもふ技術部
今回はよく使う・知っていると便利なmigrateコマンドを紹介します!マイグレートするrails db:migrate特定の環境のみマイグレートする開発環境の場合(デフォルトなので明示的に指定することはあまりないと思います)rails db:migrate RAILS_ENV=developmentテスト環境の場合rails db:migrate RAILS_ENV=test本番環境の場合rails db:migrate RAILS_ENV=productionマイグレーションの状態を確認するrails db:migrate:status以下のように出力されます。 Status Migration ID Migration Name-------------------------------------------------- up 2024100100000 Create users up 2024100200000 Create groups up 2024100300000 Create posts up 2024100400000 Create notices up 2024100500000 Create animals特定のバージョンだけダウンするrails db:migrate:down VERSION=2024100300000特定のバージョンだけアップするrails db:migrate:up VERSION=2024100300000特定のバージョンまでマイグレートするrails db:migrate VERSION=20240100300000ロールバックするrails db:rollback2つ以上のバージョンをロールバックするrails db:rollback STEP=3ロールバックして、マイグレートするrails db:migrate:redo2つ以上のバージョンをロールバックして、マイグレートするrails db:migrate:redo STEP=3DBを作成するrails db:createDBを削除するrails db:dropシードデータを投入するrails db:seedDBをセットアップするDBの作成、スキーマの読み込み、シードデータの投入が行われます。rails db:setupDBをリセットするDBを削除してからセ
1年前
記事のアイキャッチ画像
はてなブログのSEO効果を高めるために各記事に著者プロフィールとJSON-LDへの情報追加をやってみた
もふもふ技術部
こんにちは。出口です。今回は、はてなブログのSEO効果を高めるための施策を行ったので、その辺りの話です。大きく分けて2つの対応を行いました。記事下への著者情報の追加JSON-LDへの著者情報等の追加どちらもJavaScriptでの対応になります。記事下への著者情報の追加まず1つ目、記事下への著者情報の追加です。はてなブログには、元々著者情報が表示されています。これは、はてなプロフィールをベースにしているので、はてなID、表示名, はてなプロフィールへのリンクぐらいしか表示されません。これを次のように改善しました。画像のようなものが記事の一番下に表示されているはずです。こちらはJavaScriptを使って表示しています。次のようなコードです。const profiles = [ { hatenaId: 'deg84', name: '出口 達也', title: 'エンジニアリングコーチ', photo: 'https://www.mof-mof.co.jp/images/about/about_deguchi_update2_detail.jpg', aboutPageUrl: 'https://www.mof-mof.co.jp/member/tatsuya-deguchi', profile: '大阪のIT系専門学校を卒業後、東京のWEB制作会社に入社し、フロントエンドやバックエンドの開発経験を経て、退職までディレクターに従事。\nその後独立し、月額制とアジャイル開発をベースにしたサービス開発を行う。\n当時、担当していたサービス開発の増員依頼をきっかけに以前より親交のあった原田さんに相談し、以降、共に開発を行う。\n会社の成長に合わせて、エンジニアの技術力低下に懸念を抱いた小畑さんからの依頼により、エンジニアリングコーチとしてmofmofにジョイン。\n現在は、1on1の実施や開発プロセスの改善など、チームの開発生産性向上に取り組んでいる。', githubUserName: 'deg84', twitterUserName: 'deg84' }];function createProfileCard(profile) { const container = document.querySelector(".entry-footer-section.track
1年前
記事のアイキャッチ画像
【Rails】I18nを使った日本語化
もふもふ技術部
Railsで日本語化をするとき、「あれどうやるんだっけ」と意外と忘れてしまうこともあると思います。事前準備config/application.rb に以下の設定を追加します。config.i18n.default_locale = :jaconfig.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]ファイルを設置する雛形ファイルを持ってくる日本語の雛形ファイルは以下を使うのがおおすすめです。config/locales/ja.yml として設置しましょう。github.comモデルの日本語ファイルを別で設置するconfig/locales/ja.yml に追記していく形でもよいですが、モデル数やカラム数が増えていくと管理が大変になるので別のファイルにしましょう。config/locales/models/ja.yml または config/locales/models/user/ja.ymlja: activerecord: attributes: user: name: 名前 email: メールアドレスFormオブジェクトを使用している場合Formオブジェクトを使用している場合は、activerecord ではなく、 activemodel を使用することに注意しましょう。app/forms/sign_in_form.rb を以下のように定義している場合class SignInForm include ActiveModel::Model include ActiveModel::Attributes attribute :email, :string attribute :password, :stringendconfig/locales/forms/ja.yml または config/locales/forms/sign_in_form/ja.ymlja: activemodel: attributes: sign_in_form: email: メールアドレス password: パスワードenumを使用している場合以下の記事を参考にしてみてください。【日本語対応あり】Railsでenumを使う - もふもふ技術部終わり雛形ファイルの
1年前
記事のアイキャッチ画像
【日本語対応あり】Railsでenumを使う
もふもふ技術部
ユーザーの権限を管理する際などに、enumを使うことがあると思います。下準備1. Gemをインストールする日本語対応のためのGemです。gem "enum_help"2. モデルを作る$ rails g model user202401001000000_users.rbclass CreateUsers < ActiveRecord::Migration[7.1] def change create_table :users do |t| t.string :name t.integer :role, default: 1 t.timestamps end endendapp/models/user.rbgeneral を通常ユーザーに admin を管理者とします。class User < ApplicationRecord enum role: { general: 1, admin: 2 }end使ってみる1. 保存するuser = User.create(role: 1)user.role# => "general"数値で指定する以外にも、先ほど設定したキーで指定することもできます。admin = User.create(role: "admin")admin.role# => "admin"2. スコープenumを設定すると、スコープが使用できるようになります。通常ユーザーの一覧を取得するUser.general通常ユーザー以外の一覧を取得するUser.not_general3. 日本語対応冒頭でインストールした enum_help を使って日本語対応もできます。config/locales/enums/ja.ymlja: enums: user: role: general: 通常ユーザー admin: 管理者user = User.firstuser.role_i18n# => 通常ユーザー4. セレクトボックスで使う日本語対応を応用して、セレクトボックスで使用するハッシュを作成することができます。~~省略~~<%= f.select :role, User.roles_i18n.invert %>~~省略~~終わりGemを使用すれば日本語化も簡単に使うことができてとても便利ですね!
1年前
記事のアイキャッチ画像
Rails7 に Bootstrap を導入する
もふもふ技術部
以前Rails7でTailwind CSSの導入をしましたが、今回はBootstrapを導入してみます。プロジェクト作成時に導入する場合1. rails new mofmof -j esbuild --css bootstrap でプロジェクトを作成する2. 以下を package.json に追加する"scripts": { "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets", "build:css:compile": "sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules", "build:css:prefix": "postcss ./app/assets/builds/application.css --use=autoprefixer --output=./app/assets/builds/application.css", "build:css": "yarn build:css:compile && yarn build:css:prefix", "watch:css": "nodemon --watch ./app/assets/stylesheets/ --ext scss --exec \"yarn build:css\""}既存のプロジェクトに途中から導入する場合1. 以下を Gemfile に追加gem "cssbundling-rails"2. bundle install を実行3. bin/rails css:install:bootstrap 実行4. 以下を package.json の scripts に追加する"scripts": { "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds -
1年前
記事のアイキャッチ画像
【Rails】Stripeで決済したサブスクを解約する
もふもふ技術部
Stripe決済第四弾です。www.mof-mof.co.jp実装ルーティングを追加します。config/routes.rbresources :cancel, only: [:create]viewsに追記します。app/viewa/plans/index.html.erb<%= button_to cancel_index_path, data: { turbo: false } do %> 解約<% end %>コントローラーを作成します。app/controllers/cancel_controller.rbclass CancelController < ApplicationController def create subscriptions = Stripe::Subscription.list(status: 'active', customer: current_user.customer_id) if subscriptions.blank? redirect_to plans_url, status: :see_other and return end subscription = Stripe::Subscription.cancel(subscriptions.first.id) if subscription.status == "canceled" current_user.update(plan_id: 1) end redirect_to plans_url, status: :see_other and return endend以下のようにStripeのAPIを使用して、契約中のサブスクデータを取得しています。Payment にも決済情報を保存していますが、有効なサブスクデータを探すという意味合いでもAPIを使用してみました。Stripe::Subscription.list(status: 'active', customer: current_user.customer_id)もし、サービス内で複数のサブスクを契約できるようにしている場合は、以下のように price_id も用いて絞り込みをする必要があります。Stripe::Subscription.list(status: 'active', customer: cur
1年前
記事のアイキャッチ画像
【Rails】Stripe決済で複数のユーザープランを実装する
もふもふ技術部
Stripe決済第三弾です。www.mof-mof.co.jp決済を実装する今回用意するプランスタンダードプラン¥100/月¥800/買い切りプレミアムプラン¥1,000/月¥8,000/買い切り料金を作成するこちら で料金を作成します。 www.mof-mof.co.jp商品カタログページを見て、以下のようになっていればOKです。商品カタログページプランのマスターデータを作る各料金の price_~~~ を保存する場所がないので、作成していきます。ActiveHash を使います。Gemをインストールします。Gemfilegem "active_hash"PlanとPlanPriceを作成します。app/models/plan.rbclass Plan < ActiveHash::Base include ActiveHash::Associations has_many :plan_prices self.data = [ { id: 1, code: 'free', name: "無料会員"}, { id: 2, code: 'standard', name: "スタンダード会員"}, { id: 3, code: 'premium', name: "プレミアム会員"} ]endapp/models/plan_price.rbclass PlanPrice < ActiveHash::Base include ActiveHash::Associations belongs_to :plan self.data = [ { id: 1, plan_id: 2, price_id: "price_~~~~", mode: "subscription" }, # サブスクのスタンダードプラン { id: 2, plan_id: 2, price_id: "price_~~~~", mode: "payment" }, # 買い切りのスタンダードプラン { id: 3, plan_id: 3, price_id: "price_~~~~", mode: "subscription" }, # サブスクのプレミアムプラン { id: 4, plan_id: 3, price_id: "price_~~~~", mode: "payment" }, # 買い切りのプレミ
1年前
記事のアイキャッチ画像
【Rails】Stripe決済で買い切りプレミアムプランを実装する
もふもふ技術部
Stripe決済第二弾です。www.mof-mof.co.jp決済を実装する料金を作成するこちら で料金を作成します。 前回作成したプレミアムプラン「料金」の右側にある「+」を押し、以下の画像のように入力して登録します。料金追加画面料金を追加することができました。画面右上にある 「price_~~~」は後で使用するのでコピーしておきましょう。料金の詳細プレミアムプランへの動線を作るルーティングを追加します。config/routes.rbresources :plans, only: [:index]コントローラーを作成します。app/controllers/plans_controller.rbclass PlansController < ApplicationController def index; endendviewsを作成します。app/viewa/plans/index.html.erb<%= button_to checkouts_path, data: { turbo: false } do %> プレミアムプラン(¥1,000)に申し込む<% end %>Stripeで決済するルーティングを追加します。config/routes.rbresources :checkouts, only: [:create]コントローラーを作成します。app/controllers/checkouts_controller.rbclass CheckoutsController < ApplicationController def create create_customer if current_user.customer_id.blank? price_id = 'price_~~~' #先ほどコピーした値 session = Stripe::Checkout::Session.create( customer: current_user.customer_id, mode: "payment", payment_method_types: ["card"], line_items: [ { quantity: 1, price: price_id } ], success_url: root_url, cancel_url: plans_url ) re
1年前
記事のアイキャッチ画像
【Rails】Stripeでサブスク決済
もふもふ技術部
近年、サブスクのサービスが増えましたね。下準備Gemをインストールします。Gemfilegem "stripe"Stripeのアカウントを作成します。stripe.com開発環境で動作確認するだけであれば、アカウントを作成するだけですぐに使えます!環境変数を追加します。https://dashboard.stripe.com/test/apikeys からAPIキーを持ってきます。credentials に保存しましょう。$ rails credentials:edit -e development エディタが開いたら以下を参考にAPIキーを設定します。stripe: publishable_key: pk_test_~~~~ secret_key: sk_test_~~~~次に、 config/initializers/stripe.rb を作成し、以下の内容で保存します。Stripe.api_key = Rails.application.credentials.dig(:stripe, :secret_key)Stripe.api_version = '2024-06-20'※ APIのバージョン情報は こちら で確認できます。ユーザー認証を作っておきましょう。www.mof-mof.co.jpこちらの記事などを参考に、ユーザー認証を準備します。usersテーブルにカラムを足します。$ bundle exec rails g migration stripe_to_usersdb/20240910000000_stripe_to_users.rbclass StripeToUsers < ActiveRecord::Migration[7.1] def change add_column :users, :premium, :boolean, default: false add_column :users, :customer_id, :string endend※ customer_id はStripeの顧客IDを保存するために使用します。Paymentモデルを追加します。$ bundle exec rails g model paymentdb/20240910000000_create_payments.rbclass CreatePayments <
1年前
記事のアイキャッチ画像
スクラムを改めて基本から理解するためにPSM Ⅰを取得しました
もふもふ技術部
PSMとはなぜ受験しようと思ったかよかったことスクラムの基本が身につけられたスクラムガイド最新版のキャッチアップができた複数スクラムチームの運用について知ることができたPSM Ⅱに向けて最後にPSMとはPSM (Professional Scrum Master)は、Scrum.orgによって提供される認定資格です。この資格は、スクラムのフレームワークを理解し、実践するために必要な基礎知識を持つことを証明するものです。他にもスクラム認定資格といえばCSM、LSMがありますが、研修が必須であり、受講料も高額です。PSMは他のスクラム認定資格と比べて受験費用が安く、研修も必須ではないので受験のハードルは低いです。試験は英語での出題となりますが、Google翻訳を使用してもよいと案内されているので安心してください。www.scrum.orgなぜ受験しようと思ったかmofmofではスクラムをベースにアレンジ(専任のスクラムマスターを置かない、デイリースクラムはやらない等)された"モジャイル開発"で開発を行っています。アレンジは開発者自身が自律的にプロジェクトを進行できる前提として行われていますが、プロジェクトがなんとなく上手く進んでいない気がした時に、スクラムマスターだったらやるべきことってなんだっけ?と考えることがありました。スクラムの進め方は一通り知っていたけど、スクラムマスターの役割や責任範囲などはよくわかっていないことに気づき、改めてベースとなっているスクラムをしっかりと理解するため、スクラム認定資格を受験することにしました。よかったことスクラムの基本が身につけられたスクラムガイドや解説ブログを読むことで曖昧になっていたスクラムの知識を整理することができました。スクラムガイド最新版のキャッチアップができた2022年入社時、SCRUM BOOT CAMP THE BOOKを読んでスクラムのキャッチアップを行ったのですが、もととなっているスクラムガイドは増補改訂時点で最新の2017年版でした。スクラムガイドは2020年11月にアップデートされており、その差分について今回の受験を通して把握することができました。複数スクラムチームの運用について知ることができた複数スクラムチームでの開発は経験・知識ともになかったので、スケーリングした際のスクラムについて知る良い機会になりまし
1年前
記事のアイキャッチ画像
dockerのRuby環境をHerokuと同じUbuntuで動くものにする
もふもふ技術部
Dockerの公式が出しているRubyのdocker imageではdebianやapline linuxが使用されています。mofmofではHerokuで本番環境を作っているアプリも多いのですが、HerokuではUbuntuが使われています。そのため、ローカルで使用しているdockerでもUbuntuを使ってHerokuと環境を合わせたいねという話が開発チームの中で出ました。開発しているアプリでHeroku-20のstackからHeroku-22にアップグレード対応するものがあったため、このタイミングでDockerのRubyをUbuntu上で動くものにしてみました。rubylang/rubyのdocker imagerubylang/rubyrubylang/ruby:3.1.6-jammyこちらが今回採用したdocker imageです。Heroku-22のstackはUbuntu 22.04で動作しており、こちらのdocker imageも同様にUbuntu 22.04で動作しているため、Heroku-22のstackで動作している環境と同じ環境で動かせるということになります。変更する上でのポイントbundle install時のインストール先ディレクトリの権限設定ruby:3.1.6今まではこちらのようなDocker公式のdocker imageを使っていたのですが、こちらを使った場合のDockerfileでは下記のように、dockerというUSERを作って、bundle installはUSER dockerが実行する形にしていました。RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudoWORKDIR /usr/src/appCOPY Gemfile* ./RUN chown docker:docker -R .USER dockerRUN bundle install --jobs 4今回も同様の形にしてdocker compose buildを実行したところ、 > [app stage-1 10/12] RUN bundle install --jobs 4:1.917 Fetching gem metadata from https://ruby
1年前
記事のアイキャッチ画像
【Rails7】JS不要!Hotwireで無限スクロールを実装する
もふもふ技術部
Hotwireを使ってみる第三弾です!下準備1. Gemをインストールするgem "kaminari"2. モデルを作る$ rails g model post20240813000000_posts.rbclass CreatePosts < ActiveRecord::Migration[7.1] def change create_table :posts do |t| t.string :name t.timestamps end endend実装する1. ルーティングを設定するconfig/routes.rbresources :posts, only: [:index]2. コントローラーの実装app/controllers/posts_controller.rbclass PostsController < ApplicationController def index posts = Post.all if params[:page].present? @posts = posts.page(params[:page]).per(5) render turbo_stream: turbo_stream.replace("posts", partial: "posts") else @posts = posts.page(1).per(5) render :index end endend3. viewsの実装app/views/posts/index.html.erb<%= render 'posts' %>app/views/posts/_posts.html.erb<% @posts.each do |post| %> <div><%= post.name %></div><% end %><% if @posts.next_page %> <%= turbo_frame_tag "posts", loading: :lazy, src: posts_path(page: @posts.next_page) %><% end %>終わりこれで実装完了です!!loading: :lazy で遅延読み込みをすることにより、無限スクロールを実現しています。JSを使わずに無限スクロールが実装できるなんて、数年前の自分が知ったらビックリして腰が抜けると思い
1年前
記事のアイキャッチ画像
【Rails7】Hotwireで検索を実装する
もふもふ技術部
Hotwireを使ってみる第二弾です!下準備1. モデルを作る$ rails g model post20240813000000_posts.rbclass CreatePosts < ActiveRecord::Migration[7.1] def change create_table :posts do |t| t.string :name t.timestamps end endend2. Gemをインストールする(ページネーションを使う場合)gem "kaminari"実装する1. ルーティングを設定するconfig/routes.rbresources :posts, only: [:index]2. コントローラーの実装今回は name カラムをLIKE検索してみます。app/controllers/posts_controller.rbclass PostsController < ApplicationController def index @posts = Post.where("name LIKE ?", "%#{search_params[:name]}%") end private def search_params params.fetch(:q, {}).permit(:name) endend3. viewsの実装app/views/posts/index.html.erb<%= form_with scope: :q, url: posts_path, method: :get, data: { turbo_frame: "posts" } do |f| %> <div> <%= f.text_field :name %> <%= f.submit "検索" %> </div><% end %><%= turbo_frame_tag 'posts' do %> <% @posts.each do |post| %> <div><%= post.name %></div> <% end %><% end %>これだけで、実装は終わりです。<%= turbo_frame_tag 'posts' do %> ブロックの中身だけ更新されます。ページネーションを置きたい場合ページネーションを置きたい場合も少しの修正だけで実装できます。1.
1年前