エキサイト株式会社メディア・プラットフォーム事業部エンジニアの佐々木です。
2024/10/27(日)の JJUG CCC 2024 Fall に20分枠
で登壇させていただきます。
今回は、SpringBoot x MyBatis x TestContainerでSQLテストを行う
といった内容で、お話させていただきます。
よろしくお願い致します。
エキサイト株式会社メディア・プラットフォーム事業部エンジニアの佐々木です。
2024/10/27(日)の JJUG CCC 2024 Fall に20分枠
で登壇させていただきます。
今回は、SpringBoot x MyBatis x TestContainerでSQLテストを行う
といった内容で、お話させていただきます。
よろしくお願い致します。
こんにちは、エキサイト株式会社の岩藤です。
JavaのSpring Bootを使用して、PostgreSQLデータベースのinet型やcidr型にIPアドレスを"124.24.32.2"の形式でINSERTすると、データ型の不一致によるエラーが発生することがあります。
本記事では、このエラーを回避するための解決策について解説します。
openjdk 21.0.2 2024-01-16 LTS OpenJDK Runtime Environment Corretto-21.0.2.13.1 (build 21.0.2+13-LTS) OpenJDK 64-Bit Server VM Corretto-21.0.2.13.1 (build 21.0.2+13-LTS, mixed mode, sharing)
Spring Boot
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.2.1)
Gradle
Build time: 2023-11-29 14:08:57 UTC Revision: 28aca86a7180baa17117e0e5ba01d8ea9feca598 Kotlin: 1.9.20 Groovy: 3.0.17 Ant: Apache Ant(TM) version 1.10.13 compiled on January 4 2023 JVM: 21.0.2 (Amazon.com Inc. 21.0.2+13-LTS) OS: Mac OS X 14.7 aarch64
PostgreSQL 15.4 (Debian 15.4-2.pgdg120+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
PostgreSQLでIPアドレスを扱う型は下記の3種類があります。
データ型 | 説明 | 例 |
---|---|---|
cidr型 | ネットワークアドレスを表現するデータ型。IPアドレスとサブネットマスクを含む。 | 192.168.100.0/24 |
inet型 | ホストやネットワークのIPアドレスを格納するデータ型。IPv4やIPv6に対応している。 | 192.168.100.15 または 192.168.100.15/24 |
macaddr型 | MACアドレス(ネットワーク機器の物理アドレス)を格納するデータ型。 | 08:00:2b:01:02:03 |
今回は、ネットワークのIPアドレスを格納したく、サブネットマスクなしで242.168.100.15のように登録したいので、inet型を採用しました。
CREATE文
CREATE TABLE ip_address ( id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, ip_address INET NOT NULL, created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(), PRIMARY KEY (id) ); COMMENT ON TABLE ip_address IS 'IPアドレス管理'; COMMENT ON COLUMN ip_address.id IS 'ID'; COMMENT ON COLUMN ip_address.ip_address IS 'IPアドレス'; COMMENT ON COLUMN ip_address.created_at IS '作成日時'; CREATE INDEX ip_address_index ON ip_address (ip_address);
作成したテーブルに手動でINSERTしてみます。
INSERT INTO ip_address (id, ip_address) VALUES (1, '18.177.66.153'); Query 1 OK: INSERT 0 1, 1 row affected
手動でINSERTした場合は、エラーにならずにINSERTされました。
なお、CIDR型の場合は、同様のINSERT文を実行した際に自動で /32 のサブネットマスクが付与されます。
18.177.66.153/32
id = 1L, ip = "18.177.66.153"
@Override public void insertIpAddress(final Long id, final String ip) { IpAddress ipAddress = new IpAddress(); ipAddress.setIpAddress(ip); ipAddressMapper.insertSelective(ipAddress); }
実行結果
Error updating database. Cause: org.postgresql.util.PSQLException: ERROR: column "ip_address" is of type inet but expression is of type character varying ヒント: You will need to rewrite or cast the expression.
挿入しようとしている値が文字列型(character varying)であるため、型の不一致によるエラーが発生しました。
ip_address_insert.ftl id = 1L, ip = "18.177.66.153"
/* IPアドレスを登録 */ INSERT INTO ip_address (id, ip_address) VALUES (<@p name="id" />, <@p name="ipAddress" />);
実行結果
Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: column "ip_address" is of type inet but expression is of type character varying ヒント: You will need to rewrite or cast the expression.
こちらでも同様のエラーが発生しました。
CIDR型の場合も同様のエラーが確認できました。
INET型とCIDR型の両方において、プログラムを通じて実行した際に、文字列型でIPアドレスを指定すると型の不一致によりINSERTが失敗する事がわかりました。
INSERT文を下記の内容に変更します。
変更箇所は、VALUESのipAddress後ろに::inetが指定されている部分です。 これは型キャスト演算子と言います。
/* IPアドレスを登録 */ INSERT INTO ip_address (id, ip_address) VALUES (<@p name="id" />, <@p name="ipAddress" />::inet);
実行結果 inet型にキャストした事で型が一致してINSERTが成功しました!
CIDR型の場合は::cidrという指定になります。
MyBatisでは以下のように、あらかじめ定義された型エイリアスを使用することが一般的です。
しかし、PostgreSQLのinet型に直接マッピングする場合、MyBatisはデフォルトでこのデータ型をサポートしていないため、独自に型エイリアスを定義する必要があります。
また、PostgreSQLのinet型に関連するJDBCドライバも、inet型をその他のデータ型として扱っています。
データ型 | 対応するJavaクラス | SQL型 |
---|---|---|
inet, cidr, macaddr | org.postgresql.util.PGobject | java.sql.Types.OTHER |
下記参照 JDBCドライバのドキュメント
つまり、MyBatisの型マッピングと、JDBCドライバ固有の処理を組み合わせて対応する必要があり、少し複雑な対応が必要そうで断念しました。
以上となります、同様のエラーでお困りの方は参考にいただけますと幸いです。
こんにちは。
taanatsuです。
今回はLocal LLMを使ってみたくなったので、手元のMacでサクッと動かしてみます。
ChatGPTやClaudeなどの生成系AIを手元のLocal環境で動かせるようにしたものです。
マシンスペックに左右はされますが、課金が不要なのは魅力的ですね。
$ python3 -m venv venv $ source venv/bin/activate
今回はMeta社のllamaを使います。
$ pip install llama-cpp-python
ひとまず main.py
として保存します。
from llama_cpp import Llama llm = Llama( model_path="Llama-3-ELYZA-JP-8B-q4_k_m.gguf", # ダウンロード済みのModelファイルまでのパス chat_format="llama-3", # モデルとの対話形式を指定するパラメータ n_ctx=2048, # モデルが一度に処理できるトークン(単語や文字の単位)の最大数 ) response = llm.create_chat_completion( messages=[ { "role": "system", "content": "常に日本語で回答してください。", }, { "role": "user", "content": "エキサイト株式会社のサービスについて教えて", }, ], max_tokens=1024, ) print(response["choices"][0]["message"]["content"])
コードは下記サイト様を参考にしました。
以下ページからダウンロードできます。(自己責任でお願いします)
必要なのは Llama-3-ELYZA-JP-8B-q4_k_m.gguf
ファイルのみです。
main.pyと同じ階層においてください。
$ python main.py
実行結果
エキサイト株式会社は、日本のIT企業で、1997年に設立されました。同社は、インターネット関連サービスを中心に展開し、以下のようなサービスを提供しています。 1. ポータルサイト「エキサイト」: ニュース、天気、占い、求人、旅行、金融、など多数のコンテンツを提供する総合ポータルサイトです。 2. メールサービス「エキサイトメール」: フリーメールの提供をしています。@excite.co.jpのドメインでメールアドレスを取得することができます。 3. Webホスティング「エキサイトカラーホスティング」: Webサイトやブログを運営するために必要なWebサーバー、データベース、メールサーバーなどを提供するWebホスティングサービスです。 4. モバイルサービス「エキサイトモバイル」: モバイル向けのサービスで、メールやカレンダーなどを、パソコンと同期することができます。 5. 掲示板「エキサイト掲示板」: フォーラムやブログを提供し、ユーザー同士が情報交換やコミュニケーションを取ることができる場を提供しています。 6. オークション「エキサイトオークション」: オークション形式で商品を販売することができるサービスです。 7. 写真共有「エキサイトフォト」: 写真を共有することができるサービスで、アルバムの作成や共有、公開ができます。 8. ブログサービス「エキサイトブログ」: ブログの作成や運営をサポートするサービスで、簡単にブログを始めることができます。 9. アフィリエイト「エキサイトアフィリエイト」: 広告主の商品やサービスを紹介し、売上や成約に応じて報酬を得ることができるアフィリエイトプログラムです。 以上のように、エキサイト株式会社は、多様なインターネットサービスを提供し、ユーザーが便利に、楽しくインターネットを利用できる環境を整えています。
自分は中途入社なので知らないサービスもありますが、「エキサイト掲示板」は過去にあったのかはたまた終わってしまったサービスなのか……?
他にもちょこちょこ知らないサービスありますね。
若干情報が古い or 間違いがある気がしますが、それっぽく生成されていますね。
文章の要約などには結構使えそうです。
まだまだAIは注目されていますね。
GPUマシンがなくともサクッとCPUで動かせる、しかもlocal環境で動かせるLLMも出てきて、今後の動向にも注目していきたいですね。
それではまた次回。
こんにちは。エキサイトでデザイナーをしている齋藤です。
最近、JSの記述なしにAJAXやSPA風の振る舞いが実装できるhtmxに触れる機会がありました。
今回は、htmxで別のHTMLファイルにある要素を埋め込む方法をご紹介します。
コンポーネントと同じ要領で、/parts
にある「パーツA」を呼び出してindex.html
に埋め込みたいとします。
実現したいことをhtmxでどう実装するかを順を追ってお示しします。
htmxが使用できる状態を前提とします。htmxはCDNでも提供されており、気軽に試すことが可能です。
<script src="https://unpkg.com/htmx.org@2.0.3" integrity="sha384-0895/pl2MU10Hqc6jd4RvrthNlDiE9U1tWmX7WRESftEDRosgxNsQG/Ze9YMRzHq" crossorigin="anonymous"></script>
また、index.html
と/parts
のbody
内はそれぞれ以下の状態とします。
index.html
... <body> <section> <h1>坊っちゃん</h1> <!-- ここにパーツを埋め込みたい --> </section> </body> ...
/parts
(厳密には/parts/index.html
)... <body> <div id="parts-a"> <p>親譲りの無鉄砲で小供の時から損ばかりしている。</p> </div> </body> ...
#parts-a
が埋め込みたい要素となります。
htmxでは独自の属性を記述して実装します。
今回は、
hx-get
: 他ファイルのHTMLを取得するhx-select
: 取得したHTML内の要素を指定するhx-target
: 取得した要素の埋め込み先を指定するhx-trigger
: 発火のきっかけを指定するを呼び出しているindex.html
内に記述して/parts
にある要素を埋め込みます。
完成形は以下の通りです。
... <body> <section> <h1>坊っちゃん</h1> <div hx-get="/parts" hx-select="#parts-a" hx-target="this" hx-trigger="load" ></div> </section> </body> ...
まず、/parts
の内容を使用するためhx-get
でリクエストをします。レスポンスのうち、使用したい部分は#parts-a
ですので、hx-select
で#parts-a
を指定します。
呼び出された要素の埋め込み先は、htmxの属性を記述しているタグ内ですので、hx-target
はthis
とします。
これらの処理はページロード時に行われるようにするためhx-trigger
はload
とします。
これで/parts
の#parts-a
の呼び出しと埋め込みは完了です。
今回は、htmxで別のHTMLファイルにある要素を埋め込む方法をご紹介しました。
素のHTMLに近く、ビルド不要なhtmxはデザイナーとの共創といった点でも開発能率向上に資するものであると思います。
皆さまもぜひお試しください。
こんにちは。エキサイトでデザイナーをしている齋藤です。
今回はAlpine.jsの公式プラグインを使用して、ドラッグ操作による並び替えを実現する方法をご紹介します。
Alpine.jsでドラッグ操作による並び替えを実現するためには、公式に提供されているSort Pluginを使用します。
Alpine.js本体と同様にscript
タグを使用してCDNから読み込むことが可能です。(npmも使用できます)
その際、Alpine.js本体を読み込むscript
タグの前に記述する必要がありますのでご注意ください。
<!-- Alpine.js本体を読み込むscriptより前に記述する --> <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/sort@3.14.1/dist/cdn.min.js"></script>
まずは基本形をお示しします。
ぜひCodePenに触れてみてください。
See the Pen Untitled by AyumuSaito (@ayumusaito-excite) on CodePen.
<div x-data> <ul x-sort> <li x-sort:item>アイテム1</li> <li x-sort:item>アイテム2</li> <li x-sort:item>アイテム3</li> </ul> </div>
並び替えを適用したいアイテム群のラッパーにx-sort
ディレクティブを、個々のアイテムはx-sort:item
ディレクティブを付与すると、ドラッグ操作による並び替えが可能になります。
x-sort:item
にIDを振ると、x-sort
内でアイテムIDは$item
、並び順は$position
(始まりは0番目)で取得できます。
※CodePen内でアイテムを動かすと、ブラウザのアラートダイアログで動かしたアイテムのIDと並び順が確認できます。
See the Pen Alpine.jsのSort Pluginで並び替えを実現(ハンドラー) by AyumuSaito (@ayumusaito-excite) on CodePen.
<div x-data> <ul x-sort="alert('アイテム' + $item + 'を' + $position + '番目に移動しました')"> <li x-sort:item="1">アイテム1</li> <li x-sort:item="2">アイテム2</li> <li x-sort:item="3">アイテム3</li> </ul> </div>
以下のようにすると、x-data
内でも並び替えられたアイテムのIDと並び順を扱うことができます。
<div x-data="{ handle: (item, position) => {alert('アイテム' + item + 'を' + position + '番目に移動しました')} }"> <ul x-sort="handle"> <li x-sort:item="1">アイテム1</li> <li x-sort:item="2">アイテム2</li> <li x-sort:item="3">アイテム3</li> </ul> </div>
今回はAlpine.jsの公式プラグインを使用して、ドラッグ操作による並び替えを実現する方法をご紹介しました。
公式ドキュメントでは、他にもグルーピングやドラック可能領域の設定の方法などが紹介されていますので、ぜひ目を通してみてください。
Alpine.jsを使用されている方の一助となれば幸いです。ご精読ありがとうございました。
こんにちは、エキサイト株式会社でアプリエンジニアをしている岡島です。今回はListViewを入れ子にする場合や、他のウィジェットと組み合わせる際に、注意すべきshrinkWrapプロパティの使用とSliversウィジェットについて取り上げたいと思います。
shrinkWrapプロパティは、子ウィジェットの高さに合わせて動的にListViewの高さを設定するものです。
デフォルトでは、falseになっていて、ListViewに高さの制限がない場合は、ListViewは最大サイズまで拡大されます。
上記のように、shrinkWrap: trueの場合はListView表示時にレンダリングの仕組みが違います。
ListView.builderは通常、画面外のウィジェットを構築しないようになっていますが、 shrinkWrap: trueを設定すると、画面外のウィジェットも含めてすべてのウィジェットが構築されてしまいます。
そのため、リストに多数のウィジェットがある場合や、アニメーションを含む場合、パフォーマンスが著しく低下する可能性があります。
詳しくは↓こちらの動画で詳しく説明されているの ぜひご覧ください。 https://youtu.be/LUqDNnv_dh0?feature=shared
shrinkWrapを使用する代わりに、Sliversウィジェットを活用することで、効率的なリスト表示を実現できます。Sliversウィジェットは、スクロール可能な領域を小さな部分に分割し、必要な部分のみを描画することでパフォーマンスを向上させます。
つまり、Sliverウィジェットを使用することで、現在の表示領域にあるウィジェットのみがレンダリングされるようになり、パフォーマンスの向上に繋がるのです。
↓サンプルコードを書いてみたので、画面描画の違いを確認してみてください。 https://dartpad.dev/embed-dart.html?id=6ac4d9e48d18129402c4ec6749731b8a
今回は、ListViewのshrinkWrapプロパティの真偽値によって変わるレンダリングの仕組みを見てみました。適切に影響を理解することで、より良い実装を考えることができます。この情報が皆様のアプリ開発のお役に立てれば幸いです。ご精読いただき、ありがとうございました。
2024年9月から1ヶ月間、エキサイト株式会社のSaaS事業部で就業型インターンシップ「Booost!!」に参加させていただきました。インターンを通して経験したことや学んだことを紹介します。
理学系の大学院の修士1年で、電離圏についての研究を行っています。大学3年生のときにプログラミングに出会い、ソフトウェア開発に興味を持ちました。現在はフロントエンドの開発を主に行っています。Firebaseが好きでよく使っています。今回のインターンシップではチーム開発とエンジニアの働き方を実際に体験したいと思い、参加させていただきました。
私のこれまでの開発経験をまとめたいと思います。
Python: 2年
TypeScript: 半年
SaaS事業FanGrowthの開発に携わりました。 FanGrowthでは、以前までフロントエンドをVue.jsを用いて作られていましたが、現在Reactの移行を進めています。
今回のインターンではReactへの移行業務をさせていただきました。 具体的な開発内容としては、共通コンポーネントの作成、API連携、画面作成等の機能を実装しました。
全てフルリモートで業務を行いました。
以下のスケジュールで稼働しました
(日によっては、ミーティングや面談等もありました)
開発では、JIRAを用いてタスクごとにチケットが発行されており、割り当てられたチケットの開発を行いました。 チケット発行→開発→レビューというフローを繰り返して行いました。 ミーティングでは、エンジニア内の進捗報告会の参加や、事業全体に関わる話し合いにも参加させていただきました。
人事、メンター、上長の方々と面談をさせていただくこともありました。
これまで個人開発をメインに行ってきたため、他の開発者が書いたコードに触れる機会が少なかったですが、インターンを通して実務のコードに触れる経験をすることが出来ました。他人の書いたコードを理解して新しい機能を実装することが想像以上に難しかったです。
また、機能を実装する際、UIについてもデザイナーの方が作成したデザインを基に実装する必要がありました。個人開発では、どうやってお洒落なUIを作るかという意識でコーディングをしていましたが、実務では「デザイン通りに実装する」ことが求められました。CSSの細部まで理解していなかったため、デザイン通りに実装することは想像以上に大変でした。
開発のルールに柔軟に対応することも難しかったです。
例)開発のルール
特に、印象的だったのが共通コンポーネントの粒度とCSSの2点です。
1点目の共通コンポーネントについてですが、これまで個人開発でボタンやフォームなどを共通化する程度で、基本的にその場でUIを記述していました。しかし、FanGrowthではボタンやフォームはもちろん、Flex
やCard
, Grid
といったレイアウト要素までコンポーネント化されており、とても新鮮に感じました。
2点目のCSSに関しては、main.css
に色などのスタイルを定義して、そこからインポートしてスタイルを適応していました。個人開発では、その場でスタイルを記述していたため、ページごとのデザインの統一感が課題になっていました。しかし、この方法を取り入れることで、統一感のあるUIを実現できることを学びました。
また、触れたことのない技術(Storybook, Radix, SWR等)が多くあり、それぞれキャッチアップする必要がありました。個人開発では技術ブログや生成AIに頼りがちでしたが、インターンでは公式ドキュメントから情報を得るように心がけ、新しい技術のキャッチアップを行いました。特に、Storybookは実際に画面に描画させる必要がなく、Storybookのサイト上に描画をしてくれるので、共通コンポーネントを作成する際には非常に便利なライブラリだと感じました。個人開発でも是非導入してみたいです。
業務ではエンジニア社員の方々と近い内容で日々業務を行いました。 Tandemというチャットツールが導入されており、リモートでもコミュニケーションを常にとれる環境がありました。 リモートではありましたが、チーム全体で作業している雰囲気を味わうことが出来ました。
業務を通して印象に残ったのは、タスク管理能力が想像以上に求められる点です。開発タスクに加え、ミーティングや面談、コードレビューなど、多岐にわたるタスクがあります。リモートワークという特性上、これらのタスクを自分で管理し、適切に優先順位をつける必要がありました。
また、開発中に分からない点に数時間費やしてしまうことが何度かありましたが、質問することで即座に解決するケースもありました。自分1人で解決できるかどうか判断するためにも、常に理解度を意識することが重要だと感じました。理解度に応じて取るべき行動(公式ドキュメントを読む、ソースコードを読む、質問する等)を適切に選択することが大切です。 質問をする際には、課題と解決したい点を言語化して、質問させていただく方の時間を無駄にしないように心がけました。
インターンシップを通してタスク管理能力と質問力はエンジニアとして重要な要素であると実感し、今後も意識していきたいと思いました。
メンター、同じチーム、人事の方々に支えられながら1ヶ月間を過ごすことが出来ました。 社員のみなさんが優しく接してくださり、最後まで楽しく業務を行えました。 技術的な質問があった際に、相談できる環境があり心強かったです。 インターンを通して学んだことを今後のキャリアに活かしていきたいと思います。 本当にありがとうございました!
こんにちは! 2024年9月9日〜9月30日まで約1ヶ月程度、メディア事業部で就業型インターンシップに参加させて頂いた渡邉です。ここでは今回のインターンシップの内容やインターンを通して学んだことなどを書いていこうと思います。インターンを検討している方などの参考になれば幸いです!
私は情報学を専攻している修士1年生で、自然言語処理の研究室に所属しています。研究ではChatGPTなどの生成AIを使用してテキストベースの人狼ゲームをプレイすることが可能なエージェントの作成を行っているほか、作成したエージェントの精度を評価するために他の大学の人が作成した人狼エージェントと対戦を行うための大会の主催などをしています。
ここでは自分が実際にインターンを行った形式について書こうと思います。あくまでも自分の場合なので参考程度にはなりますが、フルリモートでの勤務なのでとても働きやすく、就職後にリモートで働く場合の感覚を掴みやすかったです。インターンの開始期間については調整が利きやすく、私の場合は9月2日から参加することができましたが、他の用事があったため9月9日からの参加にさせて頂きました。勤務時間も調整が行いやすく、開始時間・終了時間の調整や中抜けなども自分の用事に合わせて行いやすかったです。
インターンの業務として、エキサイトブログで今後リリース予定であるPDFを発行する新機能の追加の業務に携わらせていただきました。
主にバックエンドを担当させて頂いたのですが、PDF作成のための技術調査から始まり、実際に調査した技術が使用できるかエンドポイント立てて確認、DBから値を取得してPDFに適応させることやPDF発行に伴い保存する必要のあるデータを保存するためのテーブルの作成などを行いました。 技術調査からapiの開発、DBのテーブル設計やコードレビューを受けるなど1つの機能開発の一連の流れを経験させて頂けてとても勉強になる1ヶ月間でした!
就業型インターンであるため実際のプロダクトの機能開発をすることができ、多くの人が使用する機能を自分で開発することができたのでとてもやりがいがありました。
研究やアルバイトでLaravelやJava、GitHubを使用したことはありましたが、それ以外のフレームワークや開発ツールはインターンで初めて使用しました。今まで触れたことのないフレームワークなどに初めは不安でしたが、分からない部分を丁寧に教えていただけたので特に問題はありませんでした。
インターン期間中にオフラインで開催されるイベント「BeerBash」に招待して頂きました! 私は遠方から参加することもあり、有り難いことにイベント当日はオフィスで業務をさせて頂いてからイベントに参加する形になりました。BeerBashではカンファレンス参加レポや各事業部ごとにインターン生からの質問に答える対談があり、とても興味深いお話を聞くことができました。また、フリータイムには様々な方とお話しさせて頂くことができ、とても貴重な体験となりました。 私が参加した回のBeerBashについて記事が書かれているようなので、そちらを参考にして頂くとイメージしやすいかもしれません。
インターンを初めて間もない頃は処理を簡潔に書くことの意識はしていましたが、書いた後に無駄にクエリを発行していないかなどを確認する意識がなく、コードレビューを頂いて無駄にクエリを発行していたことに気がつきました。クエリの発行は多いほど負荷がかかり実行時間が遅くなってしまい良いことがないため、可能であれば減らした方が良いです。これはクエリの発行に限りませんが無駄な処理がある場合はコードも冗長になってしまうため、今後の読み手のためにも処理をまとめることはできないか考えることは重要であることを改めて学ぶことができました。
先ほどの話と繋がりますが、これもコメントを頂いて意識しようと思った点です。PDFに必要なデータを取得するために既存のクエリにINNER JOIN
で新たにテーブルの結合をしようとしました。以下はその例ですが、新しくhogehoge
テーブルを内部結合するためにFROM
句の末尾にINNER JOIN hogehoge
を追加したクエリを作成しました。
SELECT * FROM hoge INNER JOIN fuga USING(id) LEFT OUTER JOIN piyo USING (id) INNER JOIN hogehoge USING(id) WHERE ...
今回私が行ったタスクの場合、クエリの順番としてはこれでも欲しい情報を得ることはできましたが、INNER JOIN
とLEFT OUTER JOIN
が混ざってしまって読み手にとって理解しずらいクエリになってしまいました。既存のクエリに追加する場合に安易に末尾に追加するのではなく、以下の例のようにINNER JOIN
とLEFT OUTER JOIN
を分けて書くことで理解しやすい形式にし、レビューなどをしやすいコードを書くことを意識したいと感じました。
SELECT * FROM hoge INNER JOIN fuga USING(id) INNER JOIN hogehoge USING(id) LEFT OUTER JOIN piyo USING (id) WHERE ...
また、今回のタスクでは問題ありませんでしたが、内部結合や外部結合は順番によって結果が変わるのでしっかりと考えて変更を行うようにしようと思いました。
またしても可読性についてで、レビューを頂き意識しようと感じた点です。PDFに値を埋め込む際に、input
タグから値を取得できたかどうかで代入する値を変更する処理を書く場面があり、その際に私は以下のようなコードを書きました。
$value = [ "id" => $_POST["id"], "name" => $_POST['input'] ]; if(!isset(value["name"])){ $value["name"] = $_POST['another_input']; }
上記のコードは$value["name"]
が色々な箇所で値が変更されていて、$value["name"]
の値に注目したい場合にどこで変更されたのか追うためにコードをしっかり見る必要があり大変です。そのため、以下のコードのように三項演算子など使用して値を変更する箇所を1つの場所にまとめ、変数の値を追いやすいコードにしたいと思いました。
$name= isset($_POST['input']) ? $_POST['input'] : $_POST['another_input']; $value = [ "id" => $_POST["id"], "name" =>$name ];
ここで示した例はPHPでしたが、apiを実装したSpring Boot(Java)ではfinal
修飾子が存在するため、値が変更されない場合はfinal
修飾子をつけることで可読性を向上させることを自然に意識できるようにしたいと感じました。
エキサイトブログは長年続いていることもあり運用を行いやすくするための指針がとても興味深かったです。実際の例として、新たに記録したい情報が出てきた場合に安易に既存のテーブルにカラムを追加せずに新しいテーブルを作成する理由がとても勉強になりました。カラムを追加する方針を採用すると既存のデータはそのカラムにNULLなどの値を入れることとなり、無意味なデータを大量に保管する上にデータを追い辛くなることを実際に大量にNULLがあるテーブルを見て実感しました。
約1ヶ月間のインターンシップの中で新機能の開発に一から携われるという非常に貴重な体験をすることができ、インターンを始める前よりも技術的にも成長することができたと感じています。開発を通し、チーム開発を行う上で気をつけるべき点などを学ぶことができたので、今後はこれを活かせるように意識して開発をしていきたいです!開発だけでなく、イベントを通してエキサイトの雰囲気を知ることができたので、非常に有意義な体験ができたと感じています。他ではなかなかできない貴重な経験ができるので、インターンに参加するか迷っている人には是非進むことをお勧めします。
初めて触る技術が多くチーム開発の経験も少ないので至らぬ点も多々あったと思いますが、メンターの方をはじめとする多くの方にサポート頂くことでインターンシップをやり切ることができました。この場を借りて感謝申し上げます。
こんにちは、エキサイトでエンジニアをしている吉川です。 先日9/13(金)に社内イベントの「テクデザBeer Bash」を開催したので、運営視点でレポートを書いていきます。
Beer BashとはBeer(ビール)+ Bash(にぎやかなパーティー)を合わせた造語で、真面目な部分を残しつつ、カジュアルな雰囲気で交流を行うイベントです。
年に3、4回社内カフェスペースで開催しており、同じチーム内の人はもちろん、業務ではあまり関わることがない他部署の人たちとも繋がる場になっています。
今回は以下の2つをメインコンテンツとし、後半はフリートークの時間にしました。
PHPerKaigiなどの社外カンファレンスにご参加されたエンジニアから、参加したきっかけや社外コミュニティの雰囲気のついてお話ししていただきました。 後半ではご自身が運営されたコミュニティのお話しもあり、コミュニティ運営の楽しさや難しさを伝えていただきました。
プラットフォーム事業チーム、ブロードバンド事業チームの各エンジニアとマネージャーによるパネルディスカッションを行いました。 若手の活躍の様子や、チームごと空気感の違いが現れており、盛り上がるコンテンツになりました。
今回から採用・広報活動の一環で、インターンなどの学生さんや選考中の方を積極的にご招待するようになりました。 メインコンテンツも社内の雰囲気を感じ取ってもらえるように工夫しており、参加されたインターン生の方々からもご好評をいただきました。
また今まで食事は各座席に配膳していたのですが、試験的にバイキング形式にしたところ、フリートークでの参加者の流動が良くなり多くの交流を産むことができました。 参加後のアンケートでは、8割以上の社員が普段関わりの薄い方やインターン生と交流できたと回答いただき、手応えがあったと感じています。
一方でテクデザ対談の時間配分については反省点です。各チーム15分で3〜5つトークテーマを準備していましたが、実際やると1、2個目で15分を超えてしまい、「もっと聞きたかった。でも全体としては長かった。」という感想が出てしまいました。タイムキープは今後もっと意識していきます。
最後まで読んでいただき、ありがとうございました。 テクデザBeer Bashは社内イベントではありますが、少しでもイベントの雰囲気が伝わっていたら幸いです。 次回はついに2桁に突入します。3年弱続けてきた企画ですが、だんだんと社内文化として定着してきたところがあるので、引き続き頑張ろうと思います!!
エキサイトではフロントエンドエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。 長期インターンも歓迎していますので、興味があればご連絡ください。 募集職種一覧はこちらになります!(カジュアルからもOK)
2024年9月の1ヶ月間エキサイト株式会社が開催した「Booost!!! Excite Internship 2024」で経験したこと、感じたことをこの記事を通して紹介したいと思います。
私は商学部に在籍している大学3年生です。大学内の授業ではプログラミングやコンピューターサイエンスの科目がほぼ無いと言って過言ではありません。そのため、私は「Recursion」という学習サービスを利用し、ソフトウェア開発やコンピューターサイエンスを学んできました。個人での開発物として、複数のバックエンド及びフロントエンドプロジェクトがあります。扱ってきた言語、技術は習熟度の差はありますが、JavaScript、TypeScript、PHP、Go、Python、Reactなどです。卒業後はエンジニアとして働くことを目指しています。
ブロードバンド事業への参加となりました。ここは「BBエキサイト」「エキサイトモバイル」などを展開している事業部です。
「メール本文テスト機能」の実装を担当しました。この機能は開発者向けとなっておりユーザーは使用できないものとなっております。機能面としましては、セレクトタブから確認したいメールのタイトルを選択し、その後対応したメール本文が画面上に表示されます。また「メール送信機能」も実装されており、任意のメールアドレス先まで送信することができます。
今回の実装では、PHPのフレームワークであるLaravelでバックエンド部分を構築し、フロントエンド部分をVue.jsで実装しました。私はPHP及びJavaScriptでの開発経験はありますが、フレームワークを使用しての開発は今回が初めてとなります。
私がインターンシップ参加前から事業部内で
「メールテスト機能があればいいよね」
との意見があり、今回私が担当することとなりました。
この機能の利用目的として
があげられます。
Controller.php、Index.vue、メールテンプレートに対応するconfigファイルを新たに作成しました。そして下記の順序で開発を進めました。
1ヶ月という短い期間でしたが、実際にエンジニアとして働くことがどのようなものかを経験することができました。また、今回は全日程リモートでの参加となりましたがメンターの方にサポートしていただき開発に取り組むことができました。加えて、今回の開発とは直接関係ない部分であっても、エンジニアとして必要な考えや意識しておいた方がいいことを随時教えていただき、とても有意義な期間でした。
メンターさんや人事の方にサポートしていただきながら、素晴らしいインターンシップ期間を過ごすことができました。1ヶ月間至らないところが多々ある中で、さまざまな経験をさせていただきありがとうございます。
こんにちは!9月初旬から1ヶ月の間、エキサイト株式会社のLife&Wellness事業部で、就業型インターンシップ「Booost!!!」に参加させていただいた須藤です。今回は、インターンシップでの取り組みや学んだことを紹介します。
私は大学院で、異常検知分野の研究を行っています。これまでWebアプリケーションの開発の経験は、趣味の範囲で簡単なものを作成した程度であり、Webアプリの本格的な開発や保守運用の経験はなく、今回のインターンシップが初めての挑戦となりました。このインターンシップに参加した目標は、就業型の実務インターンを通して、チーム開発の経験を積み、キャリアイメージや、エンジニアとして働く解像度を上げることです。その中で技術的な知識も加え、エンジニアとして求められることを学びたいと考えていました。
Life&Wellness事業部は、「エキサイト電話占い」という占いサービスを提供しています。私は、「エキサイト電話占い」で使用されているPHPフレームワークBEAR.Saturdayを、最近の主流フレームワークであるLaravelに移植する業務に取り組みました。その中で、アンケートページやそのアンケート回答の感謝を表示するページの移植を行いました。私は、Ruby on Railsの経験が少しあったものの、PHPやLaravelには今回初めて触れました。そのため、最初はロジックを含まない静的なページの表示から始めさせていただき、徐々に段階を踏みながら、より複雑なタスクを割り振っていただきました。また、毎回朝会で、進捗の報告や今日の目標の確認をする時間があり、タスク目標を明確にすることができました。さらに、業務の過程で、丁寧なPRレビューやアドバイスに加え、どのようにサービスが動いているのかというような詳細な補足説明もいただき、全体像を掴みながら、順調に慣れることができました。
このインターンを通して、数多くのことを学ぶことができましたが、その中でも特に印象に残ったことや、実務でのチーム開発を経験したからこそ得られた知見を、コーディング面とチーム開発/コミュニケーション面でまとめようと思います。
このインターンで初めて、PHPやLaravelに触れたということもあり、アーキテクチャの理解が大変でした。その中でも、アンケートの回答データをAPIを使用して送信をするまでに、データ処理のプロセスを把握することが一苦労でした。ディレクトリ構成が決められていて、回答データに関するファイルがたくさんあるということもあり、まずはsampleファイルを参照しながら、データがどのように処理されていくのかを、以下のように自然言語で簡単に書き出してみました。
書き出してみたことで、データ処理の流れを自分の中で明確にでき、開発がスムーズに進められた気がします。とりあえずコードを書き進めてみるのではなく、方針や全体像を把握することの重要性に気づきました。
私の中でかなり躓いたところが、テストケースの作成でした。テストケースを作成するにあたり、モックの理解が非常に重要でした。 例えば以下のコードは、URLのパラメータにidがセットされている場合に、idを保存するuseCaseのhandle処理が呼び出されているかどうかを検証するテストです。
public function test_パラメータにidが指定されているときに、idの保存処理handleが呼び出されていること(): void { // 1.準備 $id = 'test_id'; $this->mockAuthentication(dummyUserId: self::TEST_USER_ID); $this->mock(EnqueteIndexGetUseCaseInterface::class, function ($mock) use ($id) { //3. 検証 $mock->shouldReceive('handle') ->once() ->with($id); }); //2. 実行 $response = $this->withUnencryptedCookies([ApiConstant::COOKIE_KEY => 'test_value'])->get( uri: route(EnqueteIndexGetController::ROUTE_NAME, [EnqueteIndexGetController::GET_PARAMETER__ID => $id]), ); }
ここで、shouldReceive('handle')は、「handle()が呼び出されることを期待する」ということを定義します。同様に、once()は、「handle()が1回呼び出されることを期待する」ということを表しています。本来は、useCaseをモックして、どのような動作が望ましいのかを定義してから、URLのGETメソッドを実行します。このようにすることで、実行時(2.実行)にuseCaseが呼び出され、検証が行われます。*1
私は最初、URLをGETしてから、モックしていました。(//3.検証と//2.実行の順番を反対にしていました。)これでは、最初にURLのGETが実行され、このタイミングでuseCaseも呼び出されますが、この時点ではまだモックしていない(期待することを定義していない)ので、その後にモックをしてテストをしても、1回もhandle()が呼び出されないという判定になってしまいます。プロセスの順序の意味や、内部でどのような処理が行われているかを意識しながらコードを書くことの重要性を、改めて実感しました。
部署内ではコーディング規則があり、linterやformatterにより、保守運用等を伴う変更容易性や可読性を担保しています。加えて、フレームワーク移行のプロジェクトでは、ディレクトリ構成も決められていました。例えば、ビジネスロジックを処理するクラスが明確に分けられていることで、Laravelにも依存しない設計になっていました。また、Bladeファイル(テンプレートエンジン)においてViewModelを活用することで、HTMLで利用可能な変数を渡し、Bladeファイルの修正が必要になるケースを抑えることができることを学びました。実際にPRの修正等で、コードを直す場面がたくさんありましたが、このようなコーディング規則やディレクトリ構成により、変更の容易性を実感しました。
例えば、
// TODO: 以下はサンプルのため移植前に削除する $this->app->singleton(SampleUseCaseInterface::class, SampleUseCase::class);
新たなコードをこのコメント文の下に追加すると、新たに追加した箇所も削除対象扱いされかねません。チーム開発をする上では、このような箇所にも注意する必要があるということを学びました。
インターンを通して、メンターの方々への質問の仕方については課題を感じました。インターンを振り返ると、特に最初の頃は、質問の粒度が細かく、自分でデバッグしたり、他のコードを注意深く確認する前に質問をしてしまったり、「これで合ってるのかな」と心配になることがあって、すぐに確認してしまっていました。また、タスクにも期限が存在するので、自分で調べて解決できるのか、聞いた方が良いのか、という判断が困難でした。
特にアンケートページの回答を入力する処理では、入力データがどのように処理されるのかが複雑に感じ、わからないことが特に多かったので、疑問点や解決したいことをメモ等にまず書き出し、わからないことを定期的に整理することを心がけていました。その中で他のコードを参照したり、調べたりして、疑問点が生じた場合に、聞くようにしていました。その中で、質問を重ねていくうちに、どのように調べれば自分が知りたいことを理解できるのか、デバッグを使用した解決方法についても学ぶことができました。このインターンシップで学んだ質問の粒度や解決能力を、これから様々な場面で、意識していきたいです。
メンターの方々をはじめ、インターンシップで関わってくださった皆様に、この場を借りてお礼申し上げます。開発だけでなく、人事や上長と面談をさせていただいたり、エキサイト社内イベント「BeerBash」にもご招待いただき、社員の方と交流し、会社の雰囲気を肌で感じることができました。技術的な知識はもちろん、チーム開発で求められることを学び、自分の中で新たな課題点や発見もあり、エキサイトでのキャリアイメージやエンジニアとしての解像度を十分に上げることができました。また、最初は「自分に開発ができるのだろうか」というような不安感もありましたが、自信を持つこともできました。
約1ヶ月という短い期間でしたが、充実したインターンシップとなりました。エキサイト株式会社の皆様ありがとうございました!
*1:shouldHaveReceived()を使用する場合は順番が反対になるそうです。
エキサイト株式会社エンジニア佐々木です。Web業界ですと複数プロジェクトやリポジトリがあることがほとんどだと思いますが、CLIで作業するときに環境変数に頼りたくなるときがあります。それをディレクトリ単位で制御してくれるdirenv
をご紹介します。
公式にインストール手順がありますので、こちらを参考にします。
direnv/docs/installation.md at master · direnv/direnv · GitHub
Macだと下記のコマンドになります。
## brew経由 brew install direnv ## バイナリインストール curl -sfL https://direnv.net/install.sh | bash
インストール後に、.bashrc
や .zshrc
に下記の記述する
## bash eval "$(direnv hook bash)" ## zsh eval "$(direnv hook zsh)"
対象ディレクトリに.envrcを設置し、必要な環境変数を記載しておく
cd ${対象ディレクトリ} touch .envrc vi .envrc """ export TAG=latest export AWS_PROFILE=xxxx """ ※環境変数 でシークレット情報を扱っている場合は.gitignoreに記載して、コミットしないように注意
記載したら、direnvに許可する必要があるので、下記のコマンドを投入します。
direnv allow
これで設定が完了です。
対象ディレクトリに入ると、.envrc
が実行されているのがわかります。
cd ${対象ディレクトリ} direnv: loading ~/${対象ディレクトリ} direnv: export +AWS_PROFILE +TAG ~XPC_SERVICE_NAME
複数人で開発作業をしているときに環境変数で制御する必要があるときにREADME.mdに書いたり、オンボーディングのときに周知したりで結構面倒なので、これで統一できるなら割と便利になります。
エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。
募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com
いつものtaanatsuです。
今回は、Laravelのアップグレードって大変だなーと思っていたらrector-laravelなるものを発見しました。
github.com
なので少し触ってみようと思います。
rector-laravelはrectorの拡張で、 rectorとはPHPのコードをアップグレードし、リファクタリングしてくれるもののようです。
なので、Laravelのアップグレードを補佐してくれるツールですね。
composerでプロジェクトに追加します。
このときに--dev
をつけて、本番には適応されないようにします。
$ composer require --dev driftingly/rector-laravel
rector.php
をプロジェクト内に作成します。
中身としては以下です。
<?php declare(strict_types=1); use Rector\Config\RectorConfig; use RectorLaravel\Set\LaravelLevelSetList; return static function (RectorConfig $rectorConfig): void { $rectorConfig->sets([ LaravelLevelSetList::UP_TO_LARAVEL_110, // Laravel11にアップデートするのでこの定数 ]); };
以下のようなコマンドを実行します。
$ vendor/bin/rector process ディレクトリ名
例えばappディレクトリ配下に適応する場合は以下になります。
$ vendor/bin/rector process app
実行すると解析が行われ、自動的にリファクタしてくれます。
vendor/bin/rector process app 79/79 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% 1 file with changes =================== 1) app/Models/User.php:39 ---------- begin diff ---------- @@ @@ /** * The attributes that should be cast. * - * @var array<string, string> + * @return array<string, string> */ - protected $casts = [ - 'email_verified_at' => 'datetime', - ]; + protected function casts(): array + { + return [ + 'email_verified_at' => 'datetime', + ]; + } } ----------- end diff ----------- Applied rules: * AddArgumentDefaultValueRector * ModelCastsPropertyToCastsMethodRector * StringToClassConstantRector [OK] 1 file has been changed by Rector
今回のPHPのコードはLaravelを利用しておりますが、
極力Laravelへの依存度を少なくする方針でコード書いているため、リファクタの範囲は既存のUser.phpだけでした。
SourceTreeで見てみると、リファクタが自動で実行され差分が出ていることもわかります。
このツールがどのくらい業務に耐えうるかはまだまだわかりませんが、少しずつ触っていこうと思いますので、また続報がありましたら更新いたします。
それでは今回はこのへんで。
こんにちは、エキサイトでアプリエンジニアをしている岡島です。 今回は、ListViewなどスクロールができるウィジェットのphysicsついてまとめていこうと思います。
Flutterには、スクロール可能なウィジェットがいくつか用意されています。主なものには以下があります:
これらのウィジェットは、physicsプロパティを持っており、このプロパティを通じてスクロールの挙動をカスタマイズすることができます。
physicsプロパティは、スクロールの動作や効果を制御しています。physicsには以下のような種類があるのでそれぞれの特徴について紹介していきます。
AlwaysScrollableScrollPhysicsは、リストがスクロール可能な要素の数にかかわらず、常にスクロール可能にする設定です。、リスト項目が少なくても、スクロール可能な状態が維持されます。
iOSではBouncingScrollPhysicsの挙動、AndroidではClampingScrollPhysicsの挙動となります。
AlwaysScrollableScrollPhysics class - widgets library - Dart API
デフォルト | AlwaysScrollableScrollPhysics |
---|---|
BouncingScrollPhysicsは、iOS風のバウンス効果を持つスクロール動作を提供します。リストの先頭や末尾に到達すると、バウンドするようなスクロールです。
BouncingScrollPhysics class - widgets library - Dart API
iOS | Android |
---|---|
ClampingScrollPhysicsはAndroidのスクロール動作に近く、リストの先頭や末尾に到達した際に、バウンドせずにスクロールが停止する挙動です。
ClampingScrollPhysics class - widgets library - Dart API
iOS | Android |
---|---|
NeverScrollableScrollPhysicsは、スクロールを完全に無効化します。
NeverScrollableScrollPhysics class - widgets library - Dart API
iOS | Android |
---|---|
RangeMaintainingScrollPhysicsは、スクロール範囲が増減しても、自動的にスクロール位置を調整します。 リストの内容が動的に変化する場合に役に立ちます。
RangeMaintainingScrollPhysics class - widgets library - Dart API
今回はスクロールができるウィジェットのphysicsについてまとめてみました。状況に応じて使い分けることや組み合わせることで、ユーザー体験の向上につながるかと思います。読んでいただきありがとうございました!
こんにちは。エキサイトでデザイナーをしている齋藤です。
今回は、Tailwind CSSで連続する要素で交互に適用されるスタイリングをする方法として、odd:
&even:
Modifierをご紹介します。
冒頭、連続する要素で交互に適用されるスタイリングをしたい場面を整理します。
よくある例の一つに、以下のような行の背景色が交互に変わるテーブルがあります。
従来のCSSでは、疑似要素である:nth-child()
を使用することで実現できました。
引数に奇数番目の場合はodd
、偶数番目の場合はeven
を指定すると、連続する要素で交互に適用されるスタイルを作成できます。
tr: { &:nth-child(even) { background: #EEEEEE; // 偶数番目の兄弟要素の背景色が#EEEEEEとなる } }
Tailwind CSSで:nth-child(odd)
または:nth-child(even)
を表現するには、Modifierであるodd:
とeven:
を使用します。
先の例をTailwind CSSでスタイリングすると以下のようになります。
<tr class="even:bg-[#EEEEEE]">...</tr>
Tailwind CSSでは、他にも:first-child
や:nth-last-child()
といった疑似要素を表現するためのModifierが用意されています。(詳しくは公式ドキュメントをご覧ください)
今回は、Tailwind CSSで連続する要素で交互に適用されるスタイリングをする方法として、odd:
&even:
Modifierをご紹介しました。
Tailwind CSSを使用している方の一助となれば幸いです。
ご精読ありがとうございました。