Redisのスローログを確認する方法とKEYSではなくSCANを使った方がいい理由

こんにちはしばたにえんです 今回は、Redisのスローログを確認する方法とKEYSではなくSCANを使った方がいい理由について紹介します。

スローログを確認する手順

1. redis-cliでredisに接続する

redis-cli -h sample-redis-host

2. スローログを取得するクエリを実行する

 slowlog get 128

128 は取得するスローログの件数

1) 1) (integer) 6
   2) (integer) 1683509330
   3) (integer) 17108
   4) 1) "GET"
        2) "0"
        4) "sampleKey"
...

こんな感じで表示されます。

見方としては

1) 自動採番されたID
2) クエリ実行時のUnixタイムスタンプ
3) クエリ実行にかかった時間(マイクロ秒)
4) クエリとそのパラメータ

となってます。

Redisの注意点

Redisはシングルスレッドで動いているため、スロークエリがあった場合に他のリクエストも全てスロークエリの実行が終わるまでレスポンスが返せなくなってしまいます。
なので、当前ですが、クエリを実行する場合は細心の注意を

KEYSは使わない方がいい

データが入っているか確認したいときにKEYSを実行してデータを取得したいなんてことがありますが、おすすめできません。
KEYSは指定の条件のキーを全て見に行きます。この処理が終わるまで他の処理は待たないといけません。
代わりにSCANを使いましょう。

SCANの概要

SCANはカーソルベースで繰り返し実行することでキーを取得します。-- COUNTオプションの件数分(指定しなければ10件まで)データを取得し終えると処理を止めてキーと次のカーソルを返してくれます。
次のカーソルが0になったとき全てのキーの検出の完了となります。

SCANを実行する

SCAN 0

# プレフィックス指定で取得したい場合
SCAN 0 MATCH "sample*"

0番目の要素から取得します。

SCAN 0 MATCH "sample*"
1) "53"
2) 1) "sample:da1f1052aef0582e8f5472df91a74c0f"
   2) "sample:ae6d55e35b80643d45fe0351de523068"
   3) "sample:db93635af5fdc8366f666cc55d18cc3f"

こんな感じで表示されます。

見方としては

1) 次の呼び出しで使う新しいカーソル
2) キーの配列

となります。

次の呼び出しで使う新しいカーソルが0になるまで実行しましょう

SCAN 53 MATCH "sample*"
1) "0"
2) 1) "sample:ba18d60898fecb5c8fa68c425f61b45a"

これでsampleに前方一致するキーは

sample:da1f1052aef0582e8f5472df91a74c0f
sample:ae6d55e35b80643d45fe0351de523068
sample:db93635af5fdc8366f666cc55d18cc3f
sample:ba18d60898fecb5c8fa68c425f61b45a

の4つということがわかります。

参考

mogile.web.fc2.com mogile.web.fc2.com

MyBatis Generatorで出力されるMapper&Modelにsuffixをつけてみる

お久しぶりです。中尾です。

長いことブログを書いていませんでしたが、少しずつ書こうと思います。

今回は MyBatis Generatorで出力されるドメインオブジェクト名にsuffixをつけてみます。

MyBatis GeneratorはTableからドメインオブジェクトを作成する機能なのですが、ドメインオブジェクト名はデフォルトでTable名に設定されます。

変更するにはdomainObjectRenamingRuleを設定します。

<domainObjectRenamingRule searchString="^(.*)$" replaceString="$0Dto" />

mybatis.org

domainObjectRenamingRuleは内部では java.util.regex.Matcher.replaceAll を使っているため、正規表現でTable名を先頭から末尾までを取得し、suffixに文字列を結合します。 上記の例だと以下のようになります。

Table名 ドメインオブジェクト名 domainObjectRenamingRuleを使用
test_user TestUser.java TestUserDto.java

このように、Dtoが末尾に付けられました。

suffixでもprefixでもどちらも付けられます。

ドメインオブジェクト名を変更したいと考えている人は参考にしていただけると幸いです。

Alpine Linuxで、URLに対するヘルスチェックを行う方法

こんにちは。 エキサイト株式会社の三浦です。

コンテナでの開発をする際、できるだけサイズを減らすため、Alpine Linux(以降Alpine)を使っている方も多いと思います。

ですがAlpineは、サイズを減らすためにデフォルトでは curl コマンドが入っていません。 そのため、例えばAPIのヘルスチェックのための curl を使ったエンドポイントへのアクセスは、少なくともデフォルトそのままでは出来ないということになります。

今回は、 Alpineを使っている場合の、APIのエンドポイントへのヘルスチェックのためのアクセス方法を説明します。

方法1:curlをインストールする

1つめの方法は、シンプルに curl をインストールするというものです。

FROM ***-alpine

RUN apk update \
    && apk add --upgrade curl

こうすれば、あとは通常通り curl でヘルスチェック可能です。

curl -f http://localhost/health/path || exit 1

方法2:wgetを使用する

2つめは、 wget を使用する方法です。

Alpineは、 curl はデフォルトでは入っていませんが wget は入っているので、それを活用します。

wget --quiet --spider http://localhost/health/path || exit 1

-q -O オプションを使う方法もあるようです。

特に curl を他で使う必要性が無いのであれば、デフォルトで入っているこの wget を使った方法の方が、手間がかからず良いのではないでしょうか。

最後に

Alpineなど、コンテナサイズが小さくなるように作ってあるものは、その性質上普段使いしているコマンドが無いこともあります。

今回のように代替案を見つけたり、どうしようもない場合は自分でインストールしましょう。

「伝わるFigma」を考えてみた

こんにちは。内定者アルバイトデザイナーの齋藤です。

今回は、UIデザインをさせていただく中で気がついた、デザイナーからエンジニアさんに「伝わるFigma」にするにはどうしたらよいのか、自分なりに考えてみましたので自戒も含めて共有します。

超超新米ですので間違っていることを言っていたらご指摘ください🙇

構造と仕組みがわかること

例えばボタンのデザインを検討してたとしましょう。そこで以下の2つのようなものをFigmaで作成したとします。

見た目としてはどちらも全く一緒なのですが、実は左側がオートレイアウト使用していないもので右側は使用しているものになります。

「見た目としては同じだからいいんじゃね?」と思うかもしれませんが、使用していない場合色々と弊害が生じる恐れがあります。

もう少し、詳しく見てみましょう。

オートレイアウトなしのボタン
こちらは、オートレイアウトなしのボタンのインスペクトです。

widthheightの値を見るとわかる通り、固定値で設定されています。これだと、大きさが固定のボタンを必要としていると誤解されてしまうかもしれません。

また、ボタン内のアイコンとテキストの位置もAbsoluteになってしまいます。よって、汎用的でない使い勝手の悪いボタンが出来上がることになります。

次に、オートレイアウトありの場合を見てみましょう。

オートレイアウトありのボタン
ご覧の通り、オートレイアウトを使用することで構造と仕組みが分かりやすくなりました。

今回の場合では「display: flex;でボタン内要素を横並びにして、align-items: center;で要素を上下中央付近に揃えて、gap: 4px;だから要素と要素の間隔は4pxになって....」と言った具合に、デザインからコードへの脳内変換が容易くなります。

また、paddingが設定されていることから、テキストの分量によって大きさが変化する仕様であることが分かり、汎用性のあるボタンが出来上がりました。

このように、オートレイアウトの有無で「構造と仕組み」が伝わりやすくなります。

状態の変化パターンが見えること

ボタンを筆頭に、Hoverをはじめとした状態(見た目)の変化が生じる場合が多くあります。 そのため、どのような変化パターンがあるのかもFigma内で定義した方が良いと考えます。

その際に有用なのが、バリアンツ(Variants)です。 バリアンツとは、「同要素・別状態」のコンポーネントをグループ化して、コンポーネントを使用した際に簡単に変化させることができる機能です。(ex. ボタンのHover時と通常時の切り替え)

例えば以下のように、通常時とHover時で背景色を変化するボタンを作成する場合を考えてみます。

この際、通常時のボタンのコンポーネントを作成すると同時に、Hover時のコンポーネントも作成してバリアンツとしてグループ化を行います。 プロパティとして「Hover」を作成して、通常時のものを「False」、Hover時のものを「True」にします。

バリアンツのプロパティ

この設定をすると、当該ボタンコンポーネントが使用されている箇所をフォーカスした際に、HoverのON/OFFスイッチが出現し、変化させることができます。

HoverのONとOFF

こうすることで、一目で状態の変化パターンを把握することができます。

再利用性と再現性があること

最後に、カラーやタイポグラフィーなどデザイン内で共通するものの扱いについてです。

以下のように同じボタンが複数箇所あったとしましょう。

同じボタンが複数箇所にある
AとBのボタンは見た目も構成要素も全く同じなのですが、カラーを見てみると・・・

AとBのFillの値

カラーコードが異なっているため、同じ色に見えても若干色が変わっているのです。こうなると、全く同じものなのに意図せずカラーが異なっていると判断されて、再利用できずに同じようなものを2回作ることになってしまいます。

パーツを作るごとに色を手打ちで、特にカラーピッカーで指定しているとこのような事態が起こりやすくなります。(※今回のようなボタンの場合は大抵コンポーネント化するため、毎回毎回色指定をすることは考えられにくいですが・・・)

このような事態を避けるために、スタイル(Styles)を使用することがおすすめです。 この機能は、色やテキスト、シャドウなどのエフェクトまで、同じデザイン内で再利用される共通要素を定義することができるものです。

カラースタイルの作成

スタイルを設定することで、同じ色を使用したい場合にワンクリックで適用することができます。

スタイルで定義した色を適用

こうすることで、同じ色だけど若干カラーコードが異なるという事態を避け、エンジニアさんが:rootなどで共通要素を再現しやすくすることにもなると考えます。

まとめ

今回は、チームで開発する際にデザイナーからエンジニアさんに「伝わるFigma」とは何かを考察し特に重要であろう、

  • 構造と仕組みがわかること
  • 状態の変化パターンが見えること
  • 再利用性と再現性があること

を自分なりに考えてみました。他にも大切なことはたくさんあると思います。

デザイナーとエンジニアさんの齟齬を無くし、円滑に物事を進める一助となれば幸いです。 「もっと良い方法あるよ」や「これは違うのでは」というご指摘も大歓迎ですので、是非教えてください!!

ご精読ありがとうございました。

PHPとJavaでmd5の値を同じにする方法

エキサイトのエンジニア岩藤です。

PHPJavamd5の値を同じにする方法を紹介します。

sample.php

<?php
$md5 = md5("エキサイト");
echo $md5 . "\n";
PHP sample.php
2d56c309fdea63868f7397ebceba1867

Sample.java(サンプルはSpring Shellです)

@ShellMethod(value = "文字をmd5で暗号化する md5 text", key = "md5" , group = "md5")
public String md5(String text) {
    return DigestUtils.md5Hex(text);
}
shell:>md5 エキサイト
2d56c309fdea63868f7397ebceba1867

ちなみに方法論として上記にいきつく前に、最初は下記の方法でJava側は変換してたんですが、うまくいかず。 色々頑張ってしまったんですが、普通にシンプルにできたんですね、という事で今回メモとして残します。

最初うまくいかなかった書き方

try {
    String key = this.articleCode + "-" + this.articleId;
    byte[] md5Bytes = MessageDigest.getInstance("MD5").digest(key.getBytes(StandardCharsets.UTF_8)); 
    return new BigInteger(1, md5Bytes).toString(16);
} catch (NoSuchAlgorithmException e) {
    throw new RuntimeException("get token error");
}

上記だと先頭0の場合に、Java側で先頭0が削られてしまい一致せずではまりました。

PHP側 032e3f03122aa5162dd4a5351539b3ea
Java側 32e3f03122aa5162dd4a5351539b3ea

Nginx経由でNext.jsの開発環境にアクセスした際のブラウザコンソールエラーを回避する

こんにちは。 エキサイト株式会社の三浦です。

フロントエンドを構築するとき、皆さんはどんな構成にするでしょうか。

手段の一つとして、「Nginx + Next.js」のように、アプリケーション本体である「Next.js」と、そのアプリケーションとクライアント(例:ブラウザ)のプロキシをさせる「Nginx」を併用する、という方法を採用している方も多いのではないでしょうか。

本番環境でこの構成を取る以上、ローカル環境での開発時も同等の構成になっていることが望ましいですが、実はこの「Nginx + Next.js」という構成でローカル開発しようとすると、ブラウザのコンソールエラーが発生することがあります。

今回は、「Nginx + Next.js」の構成にしたときに、ブラウザのコンソールエラーを回避する方法を紹介します。

Next.jsでの開発方法

Next.jsは、JavaScriptやTypeScriptのフレームワークの一つで、フロントエンドのためのフレームワークとして人気なものの一つです。

開発方法も簡単で、公式ページに従ってインストール後、以下のコマンドを実行することでローカルで実際のページが確認できるようになります。

# npmを使用する場合
npm run dev

単に現在のコードを元にページが表示されるだけでなく、コード側を変更することで自動的に変更内容がページ側にも反映される(ホットリロードと言います)ため、ビルドし直しなどの手間がなく、スピーディな開発を行うことが出来ます。

ローカルでの「Nginx + Next.js」の構成の問題点と解決法

Nginxを併用して開発する場合は、Next.jsが発行するURLをNginx経由でアクセスするようにすればよい…と思いきや、単純にそうすると以下のようなエラーがブラウザのコンソールに出てしまうはずです。

ブラウザのコンソールエラー

それも、1件だけではなく、時間経過で増えていってしまいます。

一方で、ページ自体はエラー画面ではなくそのまま表示されるはずです。

これは一体何なのでしょうか。

実はこれは、ホットリロードのための仕組みが関係しています。

Next.jsはホットリロードを実現するため、ブラウザとアプリケーション間でWebSocket通信をしています。

直にNext.jsが発行するURLを使う場合は問題ありませんが、Nginxを経由するようにし、かつNginxがWebSocket通信を許容していない場合、上記のようなエラーが出てしまうのです。

このエラーを解消するためには、以下の設定を追加します。

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

この設定によりWebSocket通信がNginxでも許容されるようになるので、エラーが起きなくなります。

最後に

コンソールではエラーが起きるものの、ページとしては一見問題なく表示されるので、気持ち悪いものの解決が後回しになっている場合もあるのではないでしょうか。

理由さえわかってしまえば解決方法自体は非常に簡単なので、もしお困りの方がいたら試してみても良いかもしれません。

AWS SUMMIT TOKYO の 1日目の突撃レポート!!

こんにちは、エキサイト新卒2年目、趣味は自宅鯖(オンプレ)のNOGU(@NOGU_D626🐤)です。

本日はそう!AWS SUMMIT TOKYO の 1日目に行ってきたので熱が冷めないうちにレポートとして記事を書きたいと思います!!

そもそもAWS Summit Tokyo とは何かという人に向けて説明をすると、+150 を超える AWS セッション・事例セッション・パートナーセッションに加え、AWSAWS パートナーによるソリューション展示、事例展示、ハンズオンなど +180 を超えるコンテンツとAWS 導入&技術相談、Developer のためのスペシャルコンテンツなどの初心者でも気軽に参加することのできる日本最大の AWS を学ぶイベントです。

もちろん学生の方も参加することは可能なイベントとなっています。

そして今回のAWS Summit2023(2023年4月20日、4月21日)は4年ぶりに幕張メッセでリアルイベント開催でした!

早期来場特典としてイベント当日に会場受付を 10 時までに完了するとお弁当と交換できるクーポンをプレゼント があり(両日とも先着 2,000 名)。 さらにオリジナルクッションを両日先着 2,800 名にプレゼントがあります。☜これが意外と大事だったりします

セッションについて

今回自分が聞いてきたセッションは以下になります。

また各セッションでは録音・録画などは禁止されていますが、写真などの静止画については許可されておりその際に撮影した写真を掲載しています。

セッション名 時間
今踏み出す、変革への一歩 4月20日(木) 10:30-12:00(90分)
オンラインマルチプレイゲームにおけるゲームサーバーホスティングの選択肢とその選び方 4月20日(木) 14:20-15:00(40分)
AWS の徹底活用で ”守りの IT” から ”攻めの IT” そして "DX” へ。 4月20日(木) 15:40-16:10(30分)
国内監視カメラシェア No.1 の i-PRO 様と実現した IoT カメラサービス「i-PRO Remo.」アジャイル開発の軌跡と経営効果 4月20日(木) 16:20-17:00(40分)
サスティナブルな Web3 事業を実現するためのサービス設計と支える技術 4月20日(木) 17:20-17:50(30分)

各セッションについてざっくりとまとめていきます。

今踏み出す、変革への一歩

今踏み出す、変革への一歩 のセッションでは アマゾン ウェブ サービス ジャパン合同会社 代表執行役員社長 長崎氏の挨拶から始まり、変革のチャンスの事例として オランダのビール醸造会社であるHeinekenサプライチェーンのスマート化事例、フランスに基盤を置く電気事業者・ガス事業者である ENGIEのデータ統合基盤のAWS活用事例について話していました。

Heinekenの事業事例の説明の中にあった 成功の鍵は大胆に考え小さく始める
間違いを恐れずに進めるというワードが記憶に残っています。

AWSが掲げるミッションとしている"お客様との対話で生まれるAWSサービス"ではAI・機械学習についての話があり、Amazonが展開している通販サービスの検索システムや物流拠点の配送システム、スマートスピーカーであるAlexaなどの製品で実際に自社としてAI開発に入れつつ、自分達の業務でも実際に生かされているAIを民主化していき普及に向けた機械学習広範で成熟したAI の提供を目指し頑張っていると説明していました。

KDDI株式会社の代表取締役社長 CEO髙橋氏 に加え、株式会社インテグリティ・ヘルスケア代表取締役社長園田氏の企業でのAWSの活用事例とともにデータ駆動型社会のあり方や医療現場でのウェアラブル端末とデータの可視化の重要性に加え患者の医療の質の向上に関する説明がありました。

オンラインマルチプレイゲームにおけるゲームサーバーホスティングの選択肢とその選び方

オンラインマルチプレイゲームにおけるゲームサーバーホスティングの選択肢とその選び方のセッションでは実際にAWS基盤にゲームサーバーホスティングをする事例とともに、AWSで提供しているマネージドサービスであるAmazon GameLiftの説明がありました。 またAmazon GameLiftとAmazon GameLift FleetIQをどのように使い分けるかといった事例案などもセッションの中で話されていました。

AWS の徹底活用で ”守りの IT” から ”攻めの IT” そして "DX” へ

AWS の徹底活用で ”守りの IT” から ”攻めの IT” そして "DX” へのセッションでは、日本気象株式会社デジタルイノベーション推進室 室長 大野氏によるオンプレからAWS利用の実際の背景など。ここで印象深かったのは サービスを理解して使い倒す・浸透させることに注力することが結果として社内のAWS利用を促進させたという説明が自分の中でインパクトが強かったです。 またAWS移行後に新規で作ったサービスとしてFRANの説明がありました。

スライドの通り正統派お天気予報とは一線を画すサービスでびっくりしました。

国内監視カメラシェア No.1 の i-PRO 様と実現した IoT カメラサービス「i-PRO Remo.」アジャイル開発の軌跡と経営効果

国内監視カメラシェア No.1 の i-PRO 様と実現した IoT カメラサービス「i-PRO Remo.」アジャイル開発の軌跡と経営効果のセッションでは、富士ソフト株式会社システムインテグレーション事業本部 ネットサービス事業部 第2技術部 第4グループ主任 / クラウドxDevOps エヴァンジェリスト大槻 氏とi-PRO株式会社 moduca事業部 SVP シニアバイスプレジデント高橋氏による、AI/IoT 活用に取組まれ IoT カメラサービス「i-PRO remo.」の説明に加え、富士ソフトとi-PRO株式会社が取り組んだAWSを活用したアジャイル開発による組織面、プロセス面の取組みと開発効率 2.5 倍向上や 3 割のコスト削減効果といった点について話されていました。

このセッションの終盤では誰でも1台から購入でき、AWSの機能を使うことでエッジAIデバイスとして誰もが気軽に始められるi-PRO moducaというIoTカメラの新製品についての説明がありました。

サスティナブルな Web3 事業を実現するためのサービス設計と支える技術

サスティナブルな Web3 事業を実現するためのサービス設計と支える技術のセッションでは、株式会社スクウェア・エニックスブロックチェーン・エンタテインメント事業部 事業部長 畑氏による、スクウェア・エニックスが取り組む 2 つの NFT プロジェクト「資産性ミリオンアーサー」「SYMBIOGENESIS」を例に実際のAWSアーキテクチャまた、サービスの仕組みについての説明などがありました。

資産性ミリオンアーサーについてはこのセッションの始まる数時間前に2ndシーズンとしてゲームコンテンツの追加のためのリリースがあったという説明がありました。

各展示ブースでいただいたノベルティー

シールを見るとついつい回収しにいってしまいます。

これで1日目のAWS SUMMIT TOKYO についての体験レポートは終わります。 今回参加できなかった方もコロナが終息してきた関係もありオフライン開催のイベントが戻りつつあります。AWSが開催するイベントとしては直近でAWS Dev Day 2023 Tokyoっといった6月21日 ~ 23日 で開催決定しているものなどがあります。詳しくは公式ページなどでお調べください。

第4回テクデザBeer Bashを開催しました

こんにちは。 エキサイト株式会社の三浦です。

去る4月21日、第4回目となる「テクデザBeer Bash」を開催しましたので、そのレポートを書いていきます!

なお第3回のレポートは以下になりますので、ぜひ合わせてお読みください。

tech.excite.co.jp

テクデザBeer Bashとは

テクデザBeer Bashは、社内のエンジニア・デザイナーの交流を深めるためのイベントです。

軽食・お酒やソフトドリンクを片手に部署問わず参加者同士で話したり、登壇者によるコンテンツ発表を一緒に聞いたりして関係性を深めてもらい、日々の業務でも気軽に話し合えるようになってもらうことを目的としています。

テクデザBeer Bash

今回は感染症による行動制限が開けてからの初となるBeer Bashということで、30人超がオフラインで集まるとてもにぎやかなものになりました!

(*感染症対策をした上での開催となっています)

軽食

軽食

飲み物

当日の様子

今回は、新卒一年目が入社してから初となるBeer Bashということでもあるのと、最近の自動生成AIの急激な認知度向上を鑑みて、「23年新卒自己紹介」と「AI自動生成について」という2つの登壇コンテンツを用意しました。

23年新卒自己紹介

23年新卒自己紹介

23年新卒自己紹介では、今年度に入社した新卒にそれぞれ自己紹介をしてもらいました。

出身地や趣味、配属先はどこなのか、今年の目標、好きなプログラミング言語・ツールなど、各々自由に発表してもらいました。

また、あらかじめ運営チームより聞いていた「初任給で買いたいものは?」「エンジニア就職を決めたきっかけは?」などにも答えていただき、大いに盛り上がりました。

AI自動生成について

AI自動生成について

ChatGPTを始め、ここ数ヶ月で自動生成AIの話題が急激に増えました。

そこで今回は社内の有識者にお願いし、自動生成AIについてデモを交えながら説明をしてもらうこととしました。

「そもそもAIとは」という基礎的なところから始まり、「法律的には大丈夫か」、「実際に使ってみるとどんな出力が出るのか」、そして「機械学習のロジック的な基礎知識」などを、初心者でもわかりやすく説明してもらいました。

「わかりやすかった」、「ただ触るだけでは知ることのない部分を聞くことが出来た」など、とても好評でした!

フリートーク

登壇コンテンツ後は、フリートークを楽しんでもらいました。

中一回の席替えをはさみつつ、他部署の人や入社したての新卒など、普段話すことのない人たちと交流する良い機会になりました。

最後に

ようやく行動制限も開けたため、オフラインだからこそできる交流を今後はより活用できるようになりました。

オンラインが悪いわけではありませんが、オンラインが得意なこと・オフラインが得意なことはそれぞれあるので、こういったコミュニケーションの活性化のためにぜひ今後もBeer Bashを盛り上げていきたいと思います!

WebJarsを使ってJavaScriptライブラリをSpring Bootプロジェクトにインストールする方法

こんにちは。 エキサイト株式会社の三浦です。

Spring BootでWebページ等を作るとき、ちょっとしたJavaScriptのライブラリを入れたいときがあると思います。

ですが、「ちょっとしたライブラリのためだけに npm install 等をできるようにするのは面倒…」と思う方も多いのではないでしょうか。

今回はそういった方に朗報な、WebJarsを使ったJavaScriptライブラリのインストール方法を紹介します。

WebJarsとは

WebJarsは、 build.gradle 等のJavaのパッケージ管理の仕組みからJavaScriptCSSライブラリをインストールできるよう管理してくれているサービスです。

www.webjars.org

WebJars are client-side web libraries (e.g. jQuery & Bootstrap) packaged into JAR (Java Archive) files.

仕組みとしては、build.gradle 等のパッケージ管理システムがWebJars経由でライブラリをダウンロードしてくれ、それをHTML上で使用できる、というものになっています。

実際に使ってみましょう。

WebJarsを使用してAlpine.jsを使ってみる

今回は、Alpine.jsというJavaScriptライブラリを使ってみます。

WebJarsの検索画面から、使いたいライブラリを検索

まずはWebJarsの検索画面から、使いたいライブラリを検索します。

使いたいライブラリを検索

build.gradleに追加

使っているパッケージ管理システムに、検索結果を貼り付けます。

なお、WebJarsの画面上では

compile 'org.webjars.npm:alpinejs:3.12.0'

となっていますが、Gradleでは compile は現在非推奨となっているため、 implementation を使用します。

implementation 'org.webjars.npm:alpinejs:3.12.0'

HTML上で使用する

最後に、HTML上で使用します。

今回はThymeleafを使っているので、以下のような記述になります。

<head>
    <meta charset="UTF-8">
    <title>Sample Page</title>

    <script th:src="@{/webjars/alpinejs/3.12.0/dist/cdn.min.js}" defer></script>
</head>

なお、今回 /webjars/alpinejs/3.12.0/dist/cdn.min.js をパスとして使用していますが、これは公式サイトでのCDN経由でのダウンロード方法を確認したり、実際にダウンロードされた結果を見たりすることで導き出すことができます。

公式サイトで言及されているCDN経由のダウンロード方法

<html>
<head>
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body>
    <h1 x-data="{ message: 'I ❤️ Alpine' }" x-text="message"></h1>
</body>
</html>

WebJars経由でダウンロードされたファイル一覧

WebJars経由でダウンロードされたファイル一覧

これで、 build.gradle だけでAlpine.jsが使えるようになりました!

最後に

JavaScript側の規模が大きくなってくるのであれば、最終的には npm 等のJavaScript側のパッケージ管理システムを使ったほうがいい場合もあるでしょうが、あくまでHTMLにちょっとライブラリを追加したい、くらいの規模感であれば、今回の様にWebJarsを使ってJava側のパッケージ管理システムのみで完結できるのは非常に有用と言えます。

多くのライブラリがWebJarsに登録されているので、ユースケースにマッチしているのであればぜひ使ってみてください!

JMeterの使い方

しばたにえんです。
今回は負荷テストのため、JMeterを試してみたので使い方をまとめました。
意外とかなり簡単で、トータル10分程度で試せるかと思います。

JMeterのダウンロード(Macの場合)

公式ドキュメントApache JMeter - Apache JMeter™からzipファイルをダウンロードしてbinディレクトリーからshを叩いてとか色々ありますが

Macの場合はhomebrewにあるため

brew install jmeter

この一発でjmeterが実行できるようになります。

JMeterの起動

ターミナルから

$ jmeter

を実行してください、そうするとGUIが立ち上がります。

Thread Groupを作成

Thread Groupでは何人のユーザーが何秒間に何回リクエストするかを設定します。
Test Planを右クリックして
Add -> Threads(Users) -> Thread Group をクリックして作成します。

Thread Groupの設定を編集します。

以下を適宜修正してください。

Number of Thread(users): ユーザーの人数に設定
Ramp-up period(seconds): 何秒に1回リクエストを投げるかの設定
Loop Count: 何回実行させるか

HTTP Requestを作成

HTTPでのリクエストの設定をします。
Thread Groupを右クリックして
Add -> Sampler -> HTTP Request をクリックして作成します。

HTTP Requestの設定を編集します。

以下を適宜修正してください。

protocol: http/httpsのプロトコルの設定
Server Name or IP: サーバーネームかIP
Port Number: ポート番号

View Results Tree

View Result Treeではリクエスト結果を表示してくれます。

HTTP Requestを右クリックして

Add -> Listener -> View Results Treeをクリックして作成します。

設定は以上で最後になります。
command + save で保存してください。

JMeterを実行

緑のスタートボタンを押して実行します。

あとはView Results Treeから実行結果が見ることができます。

Google Analytics Data API for PHPを使う

エキサイト株式会社の武藤です。

GA3(ユニバーサルアナリティクス、UA)の終了が2023年7月1日となっており、いよいよ期日が迫ってきました。 引き続きGAを利用するためには、GA4への変更が必要になります。

私が担当しているサービスでは、バッチ処理PHPで実装しています。 一部バッチでは、GA3 APIを利用してランキングの集計を行っています。

今回は、PHP実装におけるGA4 APIへの切り替えについて説明します。

support.google.com

Google Analytics Data API

Google Analytics Data APIは、GA4のデータを取得するためのAPIです。 developers.google.com

以下に公式ドキュメントの内容を引用します。

developers.google.com

Why do I need to migrate

If your application needs to access data in a Google Analytics 4 property, it is necessary to update the code to use the Data API v1, since the Reporting API v4 can only access properties created with Universal Analytics.

GA3までは、Google Analytics Reporting APIからデータを取得できました。 Reporting APIではGA4のデータを取得できないため、Data APIへの移行が必要になります。

Google Analytics Reporting API は現在v4まであります。このv4表記がGA4に対応していそうに見えますが、実際にはできないので注意が必要です。

GA4 APIを利用するための準備

まずは、Google Coud PlatformのコンソールからGA4 APIを有効化します。

GCPコンソールからGA4 APIを有効化

PHPでGA4 APIを操作するために、クライアントライブラリを使用します。 クライアントライブラリの操作には、認証情報が必要となりますので、サービスアカウントと認証情報を作成します。

サービスアカウントと認証情報の作成

最後に、サービスアカウントがGAにアクセスするための設定をします。

作成した認証情報はに下記の形式のサービスアカウントのメールアドレスが含まれています。

[サービスアカウント名]@[GCP プロジェクト名].iam.gserviceaccount.com

このメールアドレスをGAにアクセス可能なアカウントとして追加します。 GAの管理ページにあるアカウントのアクセス管理から追加します。

GAの管理画面からサービスアカウントのメールアドレスを追加

Google Analytics Data for PHP を使った実装

PHPからGoogle Analytics Data用のクライアントライブラリを使って、GA4のデータを取得します。

PHP 7.0系での動作を確認しました。

github.com

公式ページに則り、compsoerからライブラリをインストールします。

$ composer require google/analytics-data

先述したように、クライアントライブラリの利用にはサービスアカウントの認証情報を使います。 環境変数 GOOGLE_APPLICATION_CREDENTIALS に認証情報のファイルパスを設定しておくと、クライアントライブラリが自動で読み込んでくれます。

また、bcmathを実行環境にインストールします。クライアントライブラリの内部で使われているライブラリがbcmathパッケージに依存しているためです。

github.com

以下に、サンプルコードを紹介します。 下記は特定のURLパスの条件にマッチするPV数を取得する実装です。

<?php

require_once 'vendor/autoload.php';

use Google\Analytics\Data\V1beta\BetaAnalyticsDataClient;
use Google\Analytics\Data\V1beta\DateRange;
use Google\Analytics\Data\V1beta\Dimension;
use Google\Analytics\Data\V1beta\Filter;
use Google\Analytics\Data\V1beta\Filter\StringFilter;
use Google\Analytics\Data\V1beta\Filter\StringFilter\MatchType;
use Google\Analytics\Data\V1beta\FilterExpression;
use Google\Analytics\Data\V1beta\Metric;
use Google\ApiCore\ApiException;

/**
 * GA プロパティー
 */
$gaProperty = '0123456789';
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . '/path/to/credentials.json');
$client = new BetaAnalyticsDataClient();

$response = null;
try {
    $response = $client->runReport([
        'property' => 'properties/' . $gaProperty,
        'dateRanges' => [
            new DateRange([
                'start_date' => '7daysAgo',
                'end_date' => 'yesterday',
            ]),
        ],
        'dimensions' => [
            new Dimension([
                'name' => 'pagePath',
            ]),
        ],
        'dimensionFilter' =>
            new FilterExpression([
                'filter' => new Filter([
                    'field_name' => 'pagePath',
                    'string_filter' => new StringFilter([
                        'match_type' => MatchType::PARTIAL_REGEXP,
                        'value' => '/hogehoge/[^\?]+'
                    ]),
                ]),
            ]),
        'metrics' => [
            new Metric([
                'name' => 'screenPageViews',
            ]),
        ],
    ]);
} catch (ApiException $e) {
    // 例外処理
} finally {
    foreach ($response->getRows() as $row) {
        foreach ($row->getDimensionValues() as $dimensionValue) {
            print 'pagePath : ' . $dimensionValue->getValue() . PHP_EOL;
        }

        foreach ($row->getMetricValues() as $metricValue) {
            print 'screenPageViews : ' . $metricValue->getValue() . PHP_EOL;
        }
    }
}

出力結果のサンプルです。

pagePath : /hogehoge/20230403/
screenPageViews : 4651
pagePath : /hogehoge/20230403/
screenPageViews : 1526
pagePath : /hogehoge/20230320/
screenPageViews : 299
pagePath : /hogehoge/20230327/
screenPageViews : 287
pagePath : /hogehoge/20210412/
screenPageViews : 255

コードとしては、パラメータを付与して、APIにリクエストしています。 リクエストパラメータは以下になります。

  • dataRanges : 取得期間
    • 過去7日間
  • dimemsions : ディメンション
    • URLパス
  • dimensionFilter : ディメンションフィルター
    • URLパスに対して、正規表現(/hogehoge/[^\?]+)で部分マッチ
  • metrics : メトリクス
    • PV数

取得したいディメンションやメトリクスの設定は、下記を参考にしました。

developers.google.com

dimensionsFilterで正規表現を扱う際には、用途に応じたMatchTypeを設定します。

developers.google.com

終わりに

GA4のデータを取得するため、Google Analytics Data APIPHPのクライアントライブラリ経由で利用する方法を紹介しました。

GA3からGA4へ移行するにあたってクライアントライブラリが変更になりますが、仕様自体は大きくは変わっていないような印象を受けました。

どなたかの参考になれば幸いです。

IntelliJでTypeScriptの型補完などが効かなかったときは、とりあえずキャッシュを消して再起動してみよう

こんにちは。 エキサイト株式会社の三浦です。

今回は、表題のとおりですが、とりあえずIntelliJで不具合があったときはキャッシュを消して再起動してみると直るかもしれない、という話になります。

順を追って話していきます。

何が起きたか

以下のコードは、 Next.jsのデフォルトコードです。

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';

type Data = {
  name: string;
};

export default function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
  res.status(200).json({ name: 'John Doe' });
}

このコードにアクセスがきたら、ステータスコードは200を返しつつボディとして

{ name: 'John Doe' }

を返す、というシンプルなものです。

TypeScriptで作られたものであり、例えば res という引数は NextApiResponse<Data> と厳密に型が指定されているためコード補完が効くはずなのですが、IntelliJでこのコードを開いたとき、なぜか補完が効かないようになっていました。

  // resの型である NextApiResponse に存在するプロパティを参照しようとしても、コード補完されない
  res.status(200).json({ name: 'John Doe' });

色々調べてみてもIntelliJ側での対応策は見つけられず、またNext.jsのデフォルトコードなのでコード自体が間違っている可能性も低い状況でした。

そしていよいよ手詰まりになり、「再起動してみるか…」とキャッシュ等を削除して再起動した結果、補完が効くようになりました

キャッシュの破棄画面

結論

IntelliJはそれなりにキャッシュを使っていたりするので、場合によってはそれが悪さをする場合があります。

なにかおかしな挙動があったら、とりあえず最初に「キャッシュを削除して再起動」を試してみると良いかもしれません。

ローカルにダウンロードしたnpmでnpmコマンドを実行する方法

こんにちは。 エキサイト株式会社の三浦です。

JavaScriptやTypeScriptでプロジェクトを作る上で、 npm は必須なツールの一つと言えるでしょう。

PCにグローバルにダウンロードして使うことも出来ますが、場合によっては特定のローカルなディレクトリにダウンロードして使いたくなることもあるかもしれません。

今回は、ローカルなディレクトリに npm をダウンロードして npm コマンドを使う方法を紹介します。

npmとは

npm は、JavaScriptやTypeScriptで使われるパッケージ管理ツールです。

様々なライブラリについて、種類やバージョンを指定してダウンロードできるようにしてくれるもので、プロジェクトを管理していく上で必須なツールと言えます。

また、単にライブラリをダウンロードするだけでなく、任意のコマンドを実行させることも出来ます。

類似のツールとして yarn というものも存在します。

npm の使い方は簡単です。

例えば、 sample-library というライブラリをダウンロードしたい場合は以下のようにコマンドを実行します。

npm install sample-library

package.json ファイルという、ダウンロードするライブラリ等をまとめたファイルが存在すれば、以下のコマンドだけですべてのライブラリをダウンロードしてくれます。

npm install

この npm は現在でも開発が進められており、定期的にバージョンアップしています。

そのため、場合によっては、「このプロジェクトでは特定のバージョンの npm を使いたい」という場面も出てくるでしょう。

そういった状況だと、単にPCにグローバルに npm をダウンロードするだけでは、PC全体で npm のバージョンが固定されてしまうので問題があります。

これを解決するためには、以下の方法が考えられます。

  1. npm のバージョンを管理してくれるツール」を使うことで、プロジェクトごとに npm のバージョンを切り替える
  2. ローカルのディレクトリに npm をダウンロードして、それを使用する

今回は、後者の方法について説明します。

ローカルにダウンロードしたnpmでnpmコマンドを実行する方法

さて、実は npm コマンドは、 npm というものさえあれば実行できるというものではありません。

npm コマンドを実行する本体である npm ファイルは実はJavaScriptで出来ているため、それを実行するための環境が必要になるのです。

それを実現してくれるのが node です。

nodejs.org

上記のダウンロードページに書いてあるように、そもそも npm はダウンロード時に node に同梱されています。

npm をダウンロードする過程で、 node もダウンロードできるようになっているのです。

先程、 npm コマンドは以下の方法で実行できると説明しました。

npm install

これだけを見ると node が必要そうには見えませんが、これは裏側で npmnode を自動的に探し、発見して使ってくれているのです。

実態としては、以下のようになっています。

// node を使って npm を実行し、その npm で install を行う
node npm install

それを踏まえると、ローカルにダウンロードした npm を使って npm コマンドを実行する方法は以下が考えられます。

node npm install というコマンドを実行するようにする

先程説明したように、実態としては npm コマンドは以下のように実行されています。

node npm install

これをシンプルにそのまま使います。

つまり、 npmnode/path/to/bin というディレクトリ配下にダウンロードされている場合、以下のように実行するのです。

/path/to/bin/node /path/to/bin/npm install

これで、ローカルにダウンロードした npmnode で、 npm コマンドを実行することが出来ます。

ただし、これだと問題が起きる場合もあります。

npm コマンドは、単純にライブラリのインストールだけでなく様々なことを実行することが出来ます。

例えば、 npm コマンドを通して別のJavaScriptファイルを実行することもできるのですが、その実行したいJavaScriptにも node が必要な場合、そちらは node を見つけることが出来ずエラーになってしまう場合があるのです。

その場合は、次の方法で解決することが出来ます。

node のパスを通す

そもそも node を実行する必要があるJavaScriptファイルは、最初に自動的に node を探し、発見したらそれを使ってくれるようになっています。

通常ローカルにダウンロードされた node はその探し場所に含まれていないために発見されないのですが、逆に言えば探し場所に含ませさえすれば、自動的に発見・使用してくれるようになるのです。

以下のコマンドで、その探し場所に追加できます。

// /path/to/bin というディレクトリ内に node が入っている場合
export PATH=/path/to/bin:$PATH

こうすれば、後は以下のコマンドで問題ありません。

// npm も node と同じディレクトリにあるなら、パスを指定する必要はない
npm install

最後に

JavaScriptやTypeScriptは、今やフロントエンド・バックエンド問わず選択肢として上げられることの多い言語の一つであり、そのため npm の出番も少なくありません。

今回の記事が役に立てば幸いです。

JavaでISO 8601の計算を考える

はじめに

エキサイトで内定者インターンをしている岡崎です。 今日はISO 8601の日付の計算を考えたときにハマったので、その時に学んだことについてまとめていきます。

ISO 8601とは

ISO 8601とは、日付と時刻についてのISO(スイスのジェネーブに本部をおく非政府機関)の国際規格の一種です。 シンプルでわかりやすいため、APIなどで利用されることが多いです。

ISO 8601の年と週と曜日の数え方には以下のような特徴があります。

  • 週において、その週が1年の中の何番目かを指す
  • 1月の最初の木曜日を含む週をその年の第1週として定義
  • 第1週は01、年末の最終週は52、53と表記
  • 週の開始曜日は月曜で、終わりは日曜

実際のコード

日付から年、月、週、曜日を求める方法

実際にJavaで年の何週目か、を実装してみます。

LocalDate today = LocalDate.now();
Integer todayWeek = today.get(WeekFields.ISO.weekOfMonth());
System.out.println(todayWeek);

ちなみに、LocalDateでは週の方かに年、月、曜日も簡単に求められる関数が存在します。 実際のコードは以下です。

// 年を求める
Integer year = today.getYear();
// 月を求める
Integer month = today.getMonthValue();
// 曜日を求める
Integer yobi = today.getDayOfWeek().getValue();

年、週、曜日から日付を求める方法

さて。ここまでは日付から年、月、週(ISO 8601)、曜日を求める方法を示しました。 それでは、反対にISO 8601で年、週、曜日から日付を求めることを考えてみます。

デフォルトの関数で用意されていれば便利ですが、もちろん存在はしていません。 計算式を考えます。

私が考えたのは以下の方法です。

与えられた年の1月1日に、与えられた週から1を引いたものを足し、さらに与えられた曜日から1月1日の曜日の差の日を足す、というものでした。

言葉だとわかりづらいので、実装してみたものを示します。

public LocalDate getDate(Integer year, Integer week, Integer yobi) {
    return LocalDate.of(year, 1, 1)
    .plusWeeks(week - 1)
    .plusDays(yobi - LocalDate.of(year, 1, 1).getDayOfWeek().getValue());
}

ここでうまくいくと思っていたんですけど、計算してみると、実は年によっては正解で、年によっては合っていないことがありました。

ISO 8601 の注意点とその解決方法

なんでそんなことになるのかというと、上記の計算式では与えられた年の1月1日は必ず第1週として考えていたからでした。

しかし、これは間違いです。 なぜならISO 8601では、その年の第1週が、1月の最初の木曜日を含む週と定義されるからです。

実際、1月1日の曜日がどんな風になっているのか試してみました。 その実装は以下です。

class Main{
  public static void main(String args[]){

    List<Integer> years = Arrays.asList(2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023);

    for (Integer year : years) {
        Integer week = LocalDate.of(year, 1, 1)
                .get(WeekFields.ISO.weekOfYear());
        
        if (week.equals(0)) {
                System.out.println("weekが0から始まる年: " + year);
        } 

        if (week.equals(1)) {
           System.out.println("weekが1から始まる年: " + year);
        }
    }
  }
}

この結果を載せます。

weekが0から始まる年: 2011
weekが0から始まる年: 2012
weekが1から始まる年: 2013
weekが1から始まる年: 2014
weekが1から始まる年: 2015
weekが0から始まる年: 2016
weekが0から始まる年: 2017
weekが1から始まる年: 2018
weekが1から始まる年: 2019
weekが1から始まる年: 2020
weekが0から始まる年: 2021
weekが0から始まる年: 2022
weekが0から始まる年: 2023

つまり、これを考慮しなければ、正しい日付の計算ができないということですね。 実際にコードを修正してみます。

public LocalDate getDate(Integer year, Integer week, Integer yobi) {
    LocalDate startDate = LocalDate.of(year, 1, 1);

    return startDate
    .plusWeeks(startDate.get(WeekFields.ISO.weekOfYear()).equals(0)
        ? week : week - 1)
    .plusDays(yobi - LocalDate.of(year, 1, 1).getDayOfWeek().getValue());
}

これで正しく計算されるようになりました。

まとめ

ISO 8601で何週目かを表すときに学んだ話をまとめました。 日付の計算を考えることは大変なので、そもそもこのようなことを考えないような設計にしておくことが大切だと思いました。

ここまで読んでいただきありがとうございます。

1ヶ月の就業型インターンで学んだこと

こんにちは、エキサイトで就業型インターンに参加させて頂いているひがしです!

私は美術大学でデザイン学科を専攻しており、UI/UXデザインを中心に学んでいます。

今回はインターン中に行った業務「サムネイル制作」で学んだことを振り返りたいと思います。

<目次>


☆ワークフロー

  1. サムネイルの市場調査・ターゲット理解
  2. サムネイルの制作
  3. デザイナーの方からのレビューを元に改善を重ねる

1. サムネイルの市場調査・ターゲット理解

制作を始める前に、既存サムネイルの市場調査を行いました。自分が素敵なデザインだなと思ったサムネイルを集め、どうしてそう思ったのかを言語化したり、デザインを構造化して考えました。その結果、それぞれのデザインの共通点や良い点を見つけることでサムネイルデザインへの理解を深めることができました。また、実際に制作をしていく時に参考になり、自分のデザインの知識も増えたと思います。

2. サムネイルの制作

サムネイルの市場調査・ターゲット理解から得た気づきを元にcanvaを使ってサムネイル制作に取り組みました。 今回は役員向けの「KUROTENのノウハウウェビナー」と飲食事業の経理部長向けの「KUROTENプロダクトウェビナー」の2点のセミナー用サムネイルの新規制作を行いました。ここでは私が制作を進める上で気を付けたことや学んだことを共有します。

* とにかく手を動かしながらアイデア発散
まずデザインの配置パターンを複数制作しました。私はデザインをするときに細かいことに注力しすぎてしまい時間が足りなくなってしまうことが多いのですが、今回は期日が決まっていたため細かい部分まで考えるのではなく、まずは配置のみを先に考えることで複数のデザインパターンを制作し、その後に気に入ったパターンの細かい調整をする作業フローにしました。

* 配色パターンは寒色、暖色に限らず複数考えてみる
同じデザインだとしても、配色によって大きく印象が変わるため配色選びはとても重要です。私は一度制作した物のイメージをガラリと変更するのは苦手なのですが、寒色系、暖色系合わせて配色パターンを12個作成することを目標に制作しました。 難しかったですが、セミナーの内容やターゲットのことを考えながら制作している時間はとても楽しかったです。

3. デザイナーの方からのレビューを元に改善を重ねる

自分が制作したサムネイルデザインをSlackでデザイナーの方に共有し、レビューを頂き改善を重ねました。ここではレビューを貰い自分が新しく学んだことについて共有します。

* デザインの推しポイントを言語化することの大切さ
今回複数のパターンを制作したのですが、人によって素敵に感じるポイントは違います。複数のパターンの中で私が何故このデザインにしたのか、何故この配色にしたのかの意図を説明することで納得してもらったり、より密なレビューを頂くことができ改善しやすくなりました。

4. まとめ

実際に自分の制作したものが世の中に出る体験や、デザイナーの方に自分の制作物を細かくレビューしてもらう体験は初めてだったのでとても学びになりました。特に、製作時にはあまり気が付かなかった細かい字間の調整や目線誘導を意識した配置、要素を置く位置についてなどの細かい具体的なレビューを頂くことができました。まだまだ知識もデザイン力も未熟ですが、残りのインターン生活を楽しんで、色々なことを吸収していきたいです!