LEFログ:学習記録ノート

leflog: 学習の記録をどんどんアップしていきます

Urawa.rb(浦和Ruby)の第一回に参加しました!💎

urawarb.connpass.com

【オフライン開催】Urawa.rb #1 - connpass

Urawa.rb #1に参加しました

こちらのUrawa.rb(浦和Ruby)に参加しました。

記念すべき第一回に立ち会うことができて良かったです!!

ボードゲームもとても楽しかったです。

お話ししてくださった皆様、ありがとうございました。✨

Photos

お洒落な建物

コバトンボードゲーム

どうぶつタワーバトル(物理)

みんな大好きRubyメソッドかるた

素晴らしいイベントをありがとうございました!

世界一わかりやすいWebSocketのサンプルコード(とその解説)

差し込む側がプラグで、差し込まれる側がソケット(豆知識)

はじめに

こんにちは! これはフィヨルドブートキャンプ Part 1 Advent Calendar 2023 - Adventarの4日目の記事です。 昨日のエントリーは、suzuka_horiさんの「「FBCビブリオバトル」で発表した感想📚 - すずかのプログラミング勉強記」でした!

本日のエントリーでは、WebSocketについて具体的なコードを交えながら世界一わかりやすく解説していきたいと思います!

WebSocketって何?

WebSocketとは、サーバーとクライアント(ブラウザ)との間で双方向通信を行うための通信プロトコルです。

どんなところで使えるの?

一番わかりやすい例では、チャットアプリが挙げられます。

相手が送ったメッセージをリアルタイムに眺めるためには、クライアントA → サーバー → クライアントBへと即座に送る必要があります。

WebSocketなら双方向通信が可能なので、この機能を簡単に実装できます。

また、多人数対応のオンラインゲームを作るときにも役立ちます。

Node.js+Socket.IOで作る、通信対戦ができるHTML5ゲームシステムの作り方 - Yahoo! JAPAN Tech Blog

何が便利なの?

この技術を使うことで、サーバーから送信されたメッセージをクライアントがいつでも受け取れるようになります(逆もしかり)。

HTTPの場合、クライアントがサーバーに「自分宛てのメッセージが届いていませんか?」と一定間隔で問い合わせる必要があります。この方法だと、リアルタイム性が失われてしまいます。通信ごとに、一回一回コネクションが切れています。

WebSocketの場合、接続を切るまでずっとコネクションが繋がったままです。専用の軽量なプロトコルを用いて、安定した通信を維持できます。

どうやって使うの?

ここからが本題です。

WebSocketという名称を聞いたことがあっても、実際にどんなふうにコードを書けば良いのか迷ってしまうかもしれません。

そのため本記事では、自分が作成した「世界一わかりやすいWebSocketのサンプルコード」を用いつつ、そのコードを解説していきます。この記事とリポジトリのコードを読めば、基本をガッチリ押さえられるはずです。

リポジトリ

github.com

lef237/simple-websocket-typescript: シンプルなWebSocketのTypeScriptコード

こちらのリポジトリを用意しました。

このコードを用いつつ、どのような流れでメッセージが送受信されているか解説します。

デモ

まず、上記のコードを実行しましょう。実際に動く様子を見ることで、その後の解説が分かりやすくなるはずです。

実行方法はREADMEに書きました。📝

本当は試しに動かして頂きたいですが「手元で動かすのはちょっと面倒……」という方向けに、録画した動画を添付致します。

youtu.be

https://youtu.be/j9_ARlt6X2w?si=IpVTTzuazrSY6V18

左のウィンドウがサーバーで、右の2つのウィンドウがクライアントです。

aaa, bbbといった文字列がリアルタイムに送受信されたのをご確認頂けたと思います。

クライアントのコード

まず、クライアント側のコードから説明していきます。

リポジトリにはNodeとBunのディレクトリがありますが、Nodeのほうで解説していきます。

simple-websocket-typescript/node-websocket/client.ts at main · lef237/simple-websocket-typescript

http://ではなくws://を使おう

WebSocketの通信にはws://またはwss://から始まるURLスキーマが必要です。

それぞれhttp://ws://, https://wss://に対応している感じです。

今回はローカル環境内での通信のため、ws://のほうを使うことにします。
(本番環境ではwss://を使いましょう!)

const url = "ws://localhost:8080";

イベントハンドラ

先ほどのurlを使ってwsインスタンスを生成しています。

const ws = new WebSocket(url);

このwsインスタンスに対してサーバーからイベントが送られます。

下記の4つのイベントごとに、クライアントで処理を実装できます。

  • onopen → WebSocketコネクションが確立したときに実行するよ
  • onmessage → サーバーからデータを受け取ったときに実行するよ。受け取ったデータの値を確認できるよ
  • onerror → 何か問題が起こったときに実行するよ
  • onclose →WebSocketコネクションが閉じたときに実行するよ

ちなみに自分はonmessageのような書き方(イベントハンドラープロパティ)にしましたが、addEventListener()メソッドを使うこともできます。

ws.addEventListener("message", (e) => {
  console.log(`Received: ${e.data}`);
  promptInput();
});

この書き方でも問題なく動きます!

詳しくはMDN Web Docsを参照ください。

WebSocket: message イベント - Web API | MDN

インスタンスメソッド

今度はクライアントからサーバーへと指示を送るときです。

具体的には次の2つのメソッドが使えます。

  • send() → サーバーへとメッセージを送りたいとき
  • close() → WebSocketコネクションを切断したいとき

リポジトリのコードでは、次のようになっています。

function promptInput() {
  rl.question("Enter message to send: ", (message) => {
    if (message === "exit") {
      // 'exit'と入力した場合、プログラムを終了
      rl.close();
      ws.close();
    } else {
      ws.send(message);
    }
  });
}

ws.onmessage = (e) => {
  console.log(`Received: ${e.data}`);
  promptInput();
};

まず、onmessageでサーバーからのデータを受け取っています。そして、データを受け取ったらpromptInput()関数を呼んでいます。

promptInput()関数では、readlineを使ってターミナルでの入力を受け付けています。

question()関数では、エンターキーを押したときにコールバック関数(アロー関数の部分)が呼び出されて、直前に入力した文字がmessage変数へと渡されます。

そして、このmessage変数(入力された文字列)が"exit"のとき、WebSocketのコネクションを切断します。それ以外のときは、そのmessage変数に格納された文字列をサーバーへと送るようになっています。

一見複雑に見えますが、単純にws.send(message);でサーバーへとメッセージを送ってるよ〜と理解すると分かりやすいと思います。

サーバーのコード

次にサーバー側のコードを解説していきます。こちらもNodeで解説します。

simple-websocket-typescript/node-websocket/server.ts at main · lef237/simple-websocket-typescript

サーバー側の詳しい書き方については、wsライブラリのREADMEをご参考ください。

websockets/ws: Simple to use, blazing fast and thoroughly tested WebSocket client and server for Node.js

シンプルサーバー

こちらのリンク先のSimple serverを元に説明します。

import { WebSocketServer } from 'ws';

const server = new WebSocketServer({ port: 8080 });

server.on('connection', function connection(ws) {
  ws.on('error', console.error);

  ws.on('message', function message(data) {
    console.log('received: %s', data);
  });

  ws.send('something');
});

このコードのように、onsendを使うだけでほぼ目的は達成できます。

server.on()を使って、第一引数に"connection"、第二引数にコールバック関数を渡しています。これで、サーバーへのコネクションが確立されたとき、クライアント側から送られたメッセージの処理をおこなえます。

あとはws.on()を使って、messageが送られたときの処理を実装します。

この中でconsole.log()を使ってログを出力したり、send()を使ってクライアント側にメッセージを送ったりします。

  ws.on('message', function message(data) {
    ws.send(`Client said: ${data}`)
  });

このように書けば、サーバーが受け取ったデータをそのままクライアントに投げ返すことができます。

これで、双方向通信が可能になりました!

ブロードキャスト機能

しかし、ちょっとだけ物足りないです。

WebSocketを使えば、一つのサーバーに対して複数のクライアントが接続できます。

複数のクライアントを接続できるのだから、クライアントAが送信したメッセージを、クライアントBも受け取れるようにしたいです。

【現在】

  • 複数のクライアントが存在する → クライアントAが送信 → サーバーが受け取ってクライアントAに投げ返す → クライアントAだけがキャッチする

【理想】

  • 複数のクライアントが存在する → クライアントAが送信 → サーバーが受け取って全てのクライアントに送信する → クライアントAにもクライアントBにもクライアントCにも届く → 複数のクライアントが同時に動く

理想形のほうが、実際のチャットアプリに近いです。

この挙動を実現するために、サーバー接続時に生成されたws(WebSocketのインスタンス)を、Setを使って保存しましょう。

そして、それらのインスタンスの集まりをclientsとし、forEach()関数でその全てにメッセージを送ればOKです。

const clients = new Set<WebSocket>(); // 接続中のクライアントを格納するSet

server.on("connection", (ws) => {

  clients.add(ws); // クライアントを追加

  ws.on("message", (message) => {
    // すべてのクライアントにメッセージをブロードキャスト
    clients.forEach((client) => {
      client.send(`Client said: ${message}`);
    });
  });
});

クライアントにNode.jsを使わずにブラウザを使う場合

まずターミナルでサーバー側のコードを動かします。

それからChromeのDevToolsでコンソールを開きます*1

ブラウザ上でこのように打つと

サーバーに届きます

ブラウザから送信する場合、Node.jsとは違ってクライアント側でWebSocketのライブラリを用意する必要はありません。

Setを使わない方法

ちなみにライブラリを使った場合、Setを使わなくてもBroadcastすることはできます。

https://github.com/websockets/ws#server-broadcast

今回はBunのコードとなるべく形を揃えるため、Setを使って実装しました。
(Bunの場合はpub/subで実現できるようです)

応用的な使い方

今回は文字列を送受信しているだけですが、オブジェクトやJSONを使うことでもっと複雑なdataを受け渡すこともできます。

例えばオンラインゲームで、キャラクターに対して指示を出すdataをイメージしてみましょう。

  const data = JSON.stringify({
    target: "lefzou",
    action: "move",
    direction: "right",
    ...
  });

サーバーを介して複数のクライアントがこのdataを送受信すれば、お互いのブラウザに描画されたキャラクターをリアルタイムに動かせるでしょう。

まとめ

WebSocketの簡単な使い方について今回ご紹介致しました。

必要最小限の機能を満たすように、コードをなるべくシンプルに保つように気をつけました。記事の説明で分からないところがあっても、GitHub上のコードを読めば、大方のところは理解できるようになっていると思います。

Bunで実装したコードもリポジトリに上げています。そちらについても解説しようか迷ったのですが、この記事の主旨から外れそうなので今回は断念しました。ぜひNodeのコードと見比べてみてください。

参考記事

MDN Web Docs以外では、JavaScript Infoさんの記事がオススメです。

https://ja.javascript.info/websocket

明日のアドベントカレンダー

明日のエントリーは Sochi419 さんの記事です! 楽しみ✨

*1:ブラウザを右クリックして「検証(Inspect)」を選択します

【ゆるく使うNext.js】App Routerは全部use client;にすればReactの機能を気軽に使えるので個人開発にオススメ

※かなり短いゆるめの記事です。

はじめに

この記事の着想はmasakinihirotaさんのツイートからアイデアを得たものです! ありがとうございます!

TL;DR

Next.jsは「Reactのプラスα」的な使い方をすれば、個人開発の規模感でも使いやすい。

経緯

個人開発の用途でNext.jsに触れているのですが、バージョンが上がるに従ってどんどん新しい機能が増えていて、キャッチアップに困っていました。

具体的には、ねりけしさんのこのツイートと同じ悩みを抱えていました。

ねりけし @nerikeshi_k
個人開発物Next.jsで作ってるけど日和ってpage routerにしてしまっている。個人開発物ならばapp routerを試すべきでは とも思いつつ、でも自分のモチベは限りある資産(?)だから、技術的な挑戦はリリースまで辿り着けそうな上限に絞る必要があると信じているので、日和ってしまってる(二回言う)

https://twitter.com/nerikeshi_k/status/1715357927383716284

App Routerを使うかPage Routerのままにするか、Server ComponentsとClient Componentsをどのように使い分けるか、Server Actionsを導入するか……覚えることがたくさんあって混乱していました。

Reactについては「ちょっとデキる」ですが、Next.jsについては「何もわからん……」という状態でした。*1

任天堂横井軍平さんの名言に「枯れた技術の水平思考」という言葉がありますが、これは個人開発にも当てはまります。ある程度落ち着いている技術のほうが、素早く開発できます。

技術的挑戦はとても大事です。しかし、「一つのプロダクトを自分一人で最後まで作り切る」という目標の達成においては、ある程度落ち着いた技術のほうが開発しやすいのも事実です。

そんな中、偶然見つけたのがmasakinihirotaさんのツイートでした。

masakinihirota @masakinihirota
Next.jsで高性能にする意味がないなら page routerのままでいいと思う。最先端の機能が使いたい、高度なことに挑戦したいというのならApp routerを使えばいいと思う。もしくはrootに'use clinet' ディレクティブ置いて全部クライアントにすればいいんじゃないかな?
App routerのディレクトリでのルーティングのほうが直感的で使いやすい。 App routerは、真面目に使おうと思うとかなり高い壁になるとおもう。サーバークライアントはもちろんmiddlewareとかも絡めて考えなきゃいけなくなるからね。

https://x.com/masakinihirota/status/1710969004972703751?s=20

このツイートは自分にとってコロンブスの卵のようなツイートでした。

このおかげで、自作サービスにおけるNext.jsの開発方針を決定できました。

開発戦略

戦略はとてもシンプルです。

まずはClient Componentsのみでアプリを作ってみます。

つまり、App Routerの状態でトップレベルのコンポーネント(root)にuse client;をつけて、全てのコンポーネントをいったんClient Componentsにします。

開発を進めていけば、自然と静的に表示したい箇所が増えてくるはず。

そこから徐々にServer Componentsの領域を増やしていけばOK!

データの取得方法も、useSWRを使ってまずはCSR(クライアントサイドレンダリング)から始めてみるのがオススメです。*2

まとめ

すごく単純なフローですが、このことに気がつくまで色々悩んでかなりの時間を費やしました。

ここから導き出せる一般法則は「新しい技術に触れるとき、まずは自分の詳しい領域のみを使って開発しよう」ということです。そして基本をガッチリと固めてから、徐々にエッジな技術を取り入れてみるのが良さそうです。

最近Rustについて調べていたときに @sadnessOjisan さんのこのツイートを見掛けました。

統合開発環境 @sadnessOjisan
Rust, 正直なところウェブサーバーを書くための言語としてならかなりイージー側の選択肢だと思うんだよな。所有権とかライフタイム無視して書いてもお釣りが来ると思うし、実はそんなに勉強しなくても書けると思う。DB周りの扱いはクセあるけど、それはどの言語も同じでしょとも思う。知らんけど。

https://x.com/sadnessOjisan/status/1726408490649006312?s=20

このツイートも慧眼だと思いました。つまり、所有権やライフタイムをすぐに使いこなせなかったとしても(Rustの全ての機能を使いこなせなかったとしても)、まずは自分の分かる範囲で使うだけでもメリットを享受できるということです。

新しい技術やスピードの速い技術に触れるとき、ついつい全てを理解してから触れてしまいがちですが、そんなに気負うことはなくて、まずは自分が理解しやすい範囲で使ってみる、というのが良いのだと分かりました。*3

話を戻すと、まずはCSR的な用途のみで使うことで最新のNext.jsでも安心して使えるのがこの開発方針のポイントです。

個人開発でNext.jsを使いたい方のご参考になれば幸いです。

ちょっと追記(2023-11-28)

Next.js周りのStackについて

以前、ブログ記事に「T3 Stackを使って開発をしてやるぜ!」みたいな内容のことを書いたのですが、これはこれで色々なデメリットが有ることが分かりました。

というのも、T3 StackはPage RouterとAPI Routesを前提にして作られています。*4

それぞれのライブラリがPage Routerと密結合している印象を受けました。自分はT3 Stackを使って途中まで開発していたのですが、Page RouterからApp Routerへの移行があまりに複雑で、素のNext.jsを使ってゼロから作り直すことにしました。

またtRPCはAPI Routesが前提になった作りになっていて、Route Handlersでは実装できません。

そして最新のNext.js公式は、API RoutesではなくRoute Handlersを推奨しています。

Good to know: If you are using the App Router, you can use Server Components or Route Handlers instead of API Routes.

API Routes can't be used with static exports. However, Route Handlers in the App Router can.

そのため、T3 Stackを使った状態でNext.jsのバージョンを上げたとき、最新の機能に合わせてコードを書き換えるのが難しいです。*5

これはNext.jsの技術の進展が速いことが理由です。つい先日まではAPI Routesを新機能として勧められていた気がするのに、いつの間にかRoute Handlersが推奨になっていました。

Next.jsの技術の進化は素晴らしいと思いますし、なんだかんだとても使い勝手は良いです。ただ、最新の機能をキャッチアップしすぎると、それだけで時間が溶けてしまい、肝心のプロダクト開発・機能追加に着手できないという問題があります。最近の個人開発のトレンドを見ても「Next.jsのApp Routerを諦める選択」は割りと多く見られる気がします。

つまり、何が言いたいかというと「ある時点でのNext.jsを前提にした技術スタックの塊」を使ってガチガチに構築すると、アップグレードが大変になるということです。

App RouterやServer Components, Server Actionsへの移行をスムーズにするには、外部のライブラリを使いすぎないという選択肢もアリだと思います。自分はそうするつもりです。

そのため、Next.jsの進化がもうちょっと落ち着くまで*6App RouterでClient Componentsを使うCSRを中心にした薄めのフロントエンドにしたほうが色々と困らないのではないかと思いました。Reactの基本的な機能に絞って使ったほうが、後々の開発で助かる気もします。*7

余談ですが、ReactやNext.jsがどのような歴史を辿ってきたのかについては、この記事に詳しくまとめられていてとてもオススメです。

zenn.dev

Next.jsから学ぶWebレンダリング ~React誕生以前からApp Router with RSCまでの流れ~

*1:ViteでのReact開発や、Deno/FreshのPreactでの開発はしたことがありましたが、それでもNext.jsはよく分からない状態でした。Island Architectureより難しい気がします

*2:Next.jsの場合、CSRのデータ取得でuseEffectかuseSWRを使いますが、後者の方をオススメします。詳細はこちら → https://nextjs.org/docs/pages/building-your-application/rendering/client-side-rendering

*3:まずは理解しやすいところから始めて、徐々に知見を広げていく

*4:T3 StackでApp Routerを使うオプションが最近導入されましたが、まだExperimentalです。

*5:Page RouterやAPI Routesを最新のNext.jsでも使えるため、あえてそのままにすることもできますが、個人的にはなんとなく居心地が悪いです……

*6:Server Componentsの機能はReactが将来実装する機能の先取りなのでガンガン使っても大丈夫だとは思います → https://ja.react.dev/reference/react/use-server また、使いこなせるかは別としてServer Actionsも個人的には大歓迎です。

*7:もちろん、これは個人開発における考えです。フロントエンド専門のチームがいる場合は、色んなStackをどんどん積極的に取り入れるのも悪くないと思います。

SupabaseとPrismaとNextAuth(Auth.js)を同時に使うときはConnection poolingの設定もしよう

コネクションプーリング(イメージ図)

TL;DR

  • リモートのSupabaseとNextAuthを連携するには、Prismaでコネクションプーリングの設定をする必要があるよ
  • そのためにはPrismaにコードを付け足して、環境変数を設定する必要があるよ
  • ローカルのSupabaseに接続するときはこの設定は必要ないよ

経緯

最近Next.jsとPrismaとSupabaseとNextAuth(Auth.js)*1を組み合わせて認証機能を実装しようとしていたのですが、困ったことがありました。

それは、ローカルで立ち上げたアプリでは認証できるのに、リモート(本番環境)で立ち上げたアプリでは認証がうまくいかないという問題です。

リモートで認証しようとすると、リダイレクトの画面で「別のアカウントで試してね」という表示が出てきてログインできません。

開発環境としては次のようになっていました。

  • ローカルで立ち上げたNext.jsアプリ → ローカルのSupabaseに繋げる
  • リモートで立ち上げたNext.jsアプリ → リモートのSupabaseに繋げる

ネットで検索してもあまり情報がなかったので、今回ブログにまとめてみました。

問題箇所の特定

問題箇所を特定するために、次のステップを踏みました。

NextAuthでGoogle認証ができるように設定していたところ、ローカル環境では問題なく認証できるのに、本番環境では認証できないという問題に遭遇する。

↓

初めはGoogle側で自分が何か設定ミスをしているのかもしれないと考えていたが、本番環境ではDiscordの認証までできなくなっていることを確認する。

↓

NextAuthまたはVercelまたはSupabaseの問題であると絞り込む。

↓

更にローカルではエラーが再現しなかったため、VercelかSupabaseかの問題であることまでを絞り込む。

↓

VercelおよびSupabaseのログを読み、データベースとの接続が怪しいと当たりをつける。

↓

ローカル環境でもエラーを再現しようと試みる。

↓

ローカル上のNext.jsのenvファイルの環境変数を変更し、ローカルからリモートのSupabaseに接続を変えたところエラーが再現する。

↓

Vercel側の問題ではなく、Supabase側の問題だと特定する。

↓

SupabaseとPrismaのドキュメントを読んでコネクションプーリングの設定をしたところ、無事に本番環境でもGoogle認証ができるようになる。

これで、無事にリモート(Vercel上のNext.js)でも認証機能を実装できました。

なぜコネクションプーリングの設定が必要なのか?

ここからは推測ですが、Prismaの機能をフルで使うためにはこの設定が必要なのだと思われます。

Using Prisma with Supabase | Prisma Docs

We strongly recommend using pgbouncer in addition to DIRECT_URL. You will gain the great developer experience of the Prisma CLI while also allowing for connections to be pooled regardless of deployment strategy. While this is not strictly necessary for every app, serverless solutions will inevitably require connection pooling.

Prisma schema API (Reference) | Prisma Docs

Connection URL for direct connection to the database. If you use a connection pooler URL in the url argument (for example, if you use the Data Proxy or pgBouncer), Prisma CLI commands that require a direct connection to the database use the URL in the directUrl argument. The directUrl property is supported by Prisma Studio from version 5.1.0 upwards.

Supabaseのログを見ると、「pgBouncerがうまく機能していない」というような内容でした。

そしてPrismaは「pgBouncerを使うためにはdirectUrlを使ってデータベースに直接接続する必要がある」とドキュメントに書いています。

逆にローカル環境同士でうまく動いたのが謎ですが、おそらくローカルで動くSupabaseとリモートで動いているSupabaseで中身がちょっと違うのだと思います。

コネクションプーリングは(すごく簡略化して説明すると)「ずっと接続を維持しておくための仕組み」なので、リモートでは接続が切れやすい設定になっているんじゃないのかな〜と思いました。

まとめ

ローカルでは問題なく動くのに、リモートでは動かない事象に遭遇してとても不思議でした。

エラーの発生箇所を徐々に絞り込むことで解決できたので良かったです。

問題を解決するためには、確実に動いている地点を確かめるのが大事だと改めて理解しました。

また、BaaS(Backend as a Service)を使う場合には、リモートとローカルで環境を分けると設定が色々大変なことが分かりました。

(Supabaseを使った開発フローについてもそのうち記事を作りたいと思います。)

設定方法については次のリンク先の記事をご参考ください。

参考記事

PrismaとSupabaseの公式ドキュメントです。

記事に従ってConnection Poolingの設定までおこなえば無事に動きます。Vercel側にも環境変数を設定してあげましょう。

*1:最近NextAuthからAuth.jsに名前が変わったらしい

【感想】『ノンデザイナーズ・デザインブック』―― 良いデザインは、素早く情報を伝えられる

読書のきっかけ

皆さんはデザインが得意ですか? 自分は苦手です。最近だとTailwind CSSを使ってWebページにデザインをつけているのですが、なかなか難しいです。

というのも、プログラムのコードとは違って「正解」がないように思えるからです。

しかし、プロの方のデザインを見ていると、素人の自分とは明確な差異があります。その差異には理由があるはずです。

その理由を探るため、この『ノンデザイナーズ・デザインブック』を読んで確かめてみることにしました。

4つの大原則

この本には4つの原則が述べられています。

どういうことかというと、関連する項目が近くにあって、ページ内の配置が揃っていて、同じパターンが繰り返されていて、違いがハッキリしていることが、良いデザインの基本だと述べられています。

と言われても、この文章を読んだだけではピンと来ないかもしれません。

この本はカラー刷りになっていて、良い例と悪い例のデザインの実例が紹介されています。文章をあまり読まなくても、該当のデザインをパッと見ただけで、どれだけの効果があるか分かります。百聞は一見に如かず、です。

悪いデザインってどういうこと?

一言でいうと、ユーザが分かりにくいデザインは良くないです。

上の4原則を守っていないデザインは、その画面が何を伝えたいかが分からず、ユーザを混乱させます。

例えばWebアプリで考えてみましょう。そのアプリでは、サービス名と操作ボタンが重要だとします。

それにもかかわらず、ボタンの大きさがバラバラだったり、どこにあるか分からなかったら困るでしょう。また、サービス名が目立たないフォントだったら、名前を覚えてもらえないかもしれません。

逆に良いデザインとは?

Webアプリにおける良いデザインは何か考えてみましょう。それは、一瞬で使い方が分かるデザインです。

説明書きをほとんど読まず、アプリに関する知識が皆無でも、迷わずサクサク操作できる。これは間違いなく良いデザインです。

画面が心地よくて、思わずサービスに登録しちゃった! ここまでいけば最高のデザインでしょう。

読み取るのに時間が掛かっちゃダメ

私達はあらゆる情報が次々に流れていく情報社会を生きています。そのため、1つの画面を長時間見ることはできません。

このブログですら、目次をさらっと読まれているだけかもしれません。最初から最後まで読み通している人はいないでしょう。

しかし、こうして重要な所を目立たせるだけで、少なくともそこだけは注意を向けてもらえます。

太字で目立っている部分を読むだけで、内容が推測できる。

こうすれば斜め読みするユーザにも情報を伝えられます。また、通読してくれるユーザも情報処理がしやすくなるでしょう。

目立つ部分と目立たない部分のコントラストを設けることは重要なのです。

デザインが良いだけで競合に勝てるかも?

これは本書には書いていない私見なのですが、デザインの良いWebアプリは、それだけで競合に勝てる要素を持っている気がします。

というのも、今まさに自分が使っているこの「はてなブログ」は、あらゆる面でUI/UXが優れています。

例えば、この文章を執筆してる編集画面は、何をどう操作すれば良いのかが一目瞭然です。他のブログサービスも使ったことがありますが、自分がはてなブログを使っているのは、この操作性と明瞭さにあります。

また、自分が大好きなサービスにZennがあります。Zennのデザインは本当に素晴らしくて、記事投稿系のサービスでは1つの到達点だと思います。

上に挙げた4大原則を、Zennは全部活用しています。また、細部の使い心地も良いです。

このように、長く生き残っているサービス、そして人気のあるサービスは、良いデザインであることが多いです。

良いデザインであれば、繰り返し使われ、ユーザの滞在時間も長くなるでしょう。悪いデザインであれば、どんなにサービスが良くても、長期間にはユーザが少しずつ減っていくかもしれません。

素晴らしいスライド

この『ノンデザイナーズ・デザインブック』についてGoogleで調べていたところ、とても素晴らしいスライドを発見したのでご共有します。

speakerdeck.com

read-a-non-designers-designbook - Speaker Deck

本の内容を、別の文献も用いつつ、科学的根拠から裏付けています。

それだけでなく、このスライド自体がとても良いデザインになっています。次に発表スライドを作るとき、参考にしたいと思います!✨

※ちなみにフィヨブー関係者なら全員必読だと思います。理由は秘密です。

まとめ

以前、とある飲み会でプロのデザイナーさんとお話ししたところ「UI/UXには正解があるんだよ」と仰っていました。

この本を読んだことで、その「正解」が何か分かった気がします。

つまり、ユーザにとって親切であること。言い換えれば、素早く明快に情報が伝わるデザインこそが1つの正解だということです。

そのための方法論を学ぶことができるのが、この『ノンデザイナーズ・デザインブック』でした。

デザインを学ぶ第一歩として、この本をオススメします。今まで無意識に捉えていたデザインの良し悪しを、具体的な事例とともに身につけられると思います。🎨

Reject on Rails2023で登壇しました!

Reject on Railsとは?

gotanda-rb.connpass.com

Reject on Rails2023 - connpass

このイベントのことです!

Gotanda.rbが主催していて、Kaigi on Railsで発表できなかった内容を発表できるイベントでした。

当日のツイートまとめはこちら → Reject on Rails2023 #rejectonrails - Togetter

登壇を申し込んだ経緯

今年、自作サービスをリリースしたのですが、その内容についてオフラインで発表する機会がなかなか見つかりませんでした。

「あ、そうだ。Kaigi on Railsにプロポーザルを出そう!」と決心したときには、8月2日。もう申し込みが終わっていました。

何かしらRailsに関係するイベントで発表したいな〜と思ってConnpassを探していると、Reject on Railsを見つけました。そこには「Kaigi on Railsで登壇したかったけれど、申し込みが間に合わなかった内容」も歓迎と書かれていました。

こりゃもう発表するっきゃない!ということで申し込みました。

発表内容

Rails 7でReactとHotwireを同時に使って個人開発して得た知見』というタイトルで発表しました。

登壇時に使ったスライドはこちらになります。

speakerdeck.com

Rails 7でReactとHotwireを同時に使って個人開発して得た知見 - Speaker Deck

写真

会場はこのような雰囲気でした(人が入る前の様子)。軽食やお菓子、特製ビールもあって良かったです!✨

会場のスクリーンとテーブル

感想

とても楽しかったです! 前日から緊張してずっと心臓がドキドキしていたのですが、無事に発表を終えることができました。

個人的にとても嬉しかったのは、お集まり頂いた方からリアクションを貰えたことです。スライドの途中で拍手があがったり、内容について感想を頂けて、オフラインで参加することの醍醐味を感じることができました。

他の方の発表も勉強になりました。RubyRailsに関して、まだまだ分からないことがたくさんあるんだな〜と改めて思いました。

プログラマーとしてこれからも精進し、より高い技術力を身につけていきたいです。

お礼

登壇した方向けに、タイミーさんからコースターを貰いました。大切に使います🍹

会場で分からなかったことに関して、管理者の堀井 雄太さん(@yutadayo)やhiroteaさん(@nifuchi222222)が丁寧に教えてくださって、とても助かりました。🙏

改めて、発表を聞いてくださった皆様、ありがとうございました!

追記(2023-10-23)

発表スライドがはてなブックマーク人気エントリーに掲載されていました。ブックマークしてくださった皆様、ありがとうございます!!

(t_wadaさんの記事の隣で嬉しいです🦁)

はてなブックマーク - 人気エントリー - テクノロジー - 2023年10月18日

はてなブックマーク人気エントリーのテクノロジー