RETURNING INTO句を使って更新結果を出力する in Oracle

サムネイル

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

今回は、OracleのRETURNING INTO句について紹介します。

環境情報

はじめに

OracleでUpdate文を実行すると、3 row affected と更新された件数が結果として返されます。

SQL > UPDATE test_users SET note = '成年' WHERE age >= 20
3 row affected

しかし、これでは件数しか分かりません。実際の更新対象者を知るにはSelect文を実行する必要があります。

SQL > SELECT * FROM test_users WHERE age >= 20
 3 rows retrieved
+--+----+---+-----+
|ID|NAME|AGE|NOTE |
+--+----+---+-----+
|1 |花子  |20 |成年 |
|2 |一郎  |25 |成年 |
|4 |null |23 |成年 |
+--+----+---+-----+

また、nullになっているNAMEの値を更新する場合は、更新後には該当のデータが無くなるため、Select文で確認することができません。

SQL > UPDATE test_users SET name = '名無し' WHERE name IS NULL
1 row affected

SQL > SELECT * FROM test_users WHERE name IS NULL
0 rows retrieved

このような場合に、RETURNING INTO句を利用することができます。

RETURNING INTO句の利用方法

RETURNING INTO句では更新結果を格納するための変数を指定することができます。 その変数をDBMS_OUTPUTで出力することで、Select文を実行せずに結果を知ることができるようになります。

SQL > DECLARE
            TYPE result_item IS RECORD (
                                           id test_users.id%TYPE,
                                           note test_users.note%TYPE
                                       );
            TYPE result IS TABLE OF result_item;
            output result;
        BEGIN
            UPDATE test_users
            SET
                note = '成年'
            WHERE age >= 20
                RETURNING id, note BULK COLLECT INTO output;
        
            FOR I IN output.FIRST..output.LAST LOOP
                    DBMS_OUTPUT.PUT_LINE('id:' || output(i).id || ' note: ' || output(i).note );
                END LOOP;
        END;

completed in 67 ms
id:1 note: 成年
id:2 note: 成年
id:4 note: 成年

実行したPL/SQLを説明していきたいと思います。

TYPE result_item IS RECORD (                            
                               id test_users.id%TYPE,   
                               note test_users.note%TYPE
                           );                           
TYPE result IS TABLE OF result_item;                    
output result;                                          

上記ではRETURNING INTO句で格納するための変数の型を定義しています。 今回は複数件格納されるので、変数( output )にはコレクション型( result )を定義しています。

UPDATE test_users                                 
SET                                               
    note = '成年'                                   
WHERE age > 20                                    
    RETURNING id, note BULK COLLECT INTO output;  

RETURNING INTO句で変数( output )に格納しています。 コレクション型に格納するため、BULK COLLECT句を使用しています。

FOR I IN output.FIRST..output.LAST LOOP                                                 
        DBMS_OUTPUT.PUT_LINE('id:' || output(i).id || ' note: ' || output(i).note );    
    END LOOP;                                                                           

RETURNING INTO句で格納された変数( output )を展開して1行ずつDBMS_OUTPUT.PUT_LINEで出力しています。

RETURNING INTO句についてより知りたい場合はこちらを御覧ください。

docs.oracle.com

tips:DataGripでDBMS_OUTPUT.PUT_LINEを出力する方法

SET serveroutput ON; を実行してDBMS_OUTPUT.PUT_LINEを有効化するのが一般的ですが、 PhpStorm Database Toolでは、Data Sources > Properties > Options から設定します。

PhpStorm Database Toolの設定画像

最後に

RETURNING INTO句を使うことでSelect文を実行せずに更新結果を知ることができました。🚀

Oracle Database 23cでRETURNING句の機能が拡張され、更新前後の値が1度に取得できるようになったそうです。 どこかの機会で使ってみたいと思います。

Oracle Database 23c SQL新機能 - Speaker Deck

【インターン】デザイナーインターンで学んだこと

こんにちは! エキサイトでインターンをさせて頂いている戸嶋です!

大学ではメディア社会の分野を勉強しています。大学とは別でWebデザインのスクールに通い、デザインとプログラミングの勉強を行いました。そこでデザインに興味を持ち、実際のデザイナーの仕事についてもっと知りたいと思い参加を決めました。

今回は任せていただいた業務、お悩み相談の新規カテゴリ追加(デザインからコーディング、プレリリースまで)を通じて学んだことを共有いたします。

ワークフロー

デザイン

  • ページデザイン

  • 広告バナー作成

コーディング

  • お金の相談ページの作成

  • 入口リンクの設定

  • OGPの追加

  • プログラム一覧用の画像をリサイズして登録

  • お金の入口追加(SP/PC)

  • ステージ環境アップ

この中でも特に勉強になった広告バナー作成コーディングについて共有させていただきます。

広告バナー作成

バナー制作では、今までのカテゴリのバナーにトンマナを合わせたものを作りました。 自分の作ったバナーを他のデザイナーの方に見てもらい、フィードバックを頂きながら修正を重ねて作成しました。

デザイン気付きのまとめ

英語や数字、「!」は日本語と比べて幅が狭くなりやすいので文字間隔を調節する。

このような細かいところまで意識してデザインをすることが大切だと学びました! メンターデザイナーさんの丁寧なアドバイスのおかげで、数を重ねていくうちに自然と細部まで意識してデザインすることができるようになりました。

イラストの色調節や編集はIllustratorで行うことが出来る。

今までイラストが自分のイメージと異なる際、使用を諦めていましたがIllustratorで細かい編集を行うことができることを学びました。 これからデザインの表現の幅が広がりそうです!

今回新たに追加されるお金カテゴリの基本色を紫に決定したため、イラストが映えるようIllustratorで色を黄色と白に変更しました。

また「新」の文字を付け足し、人物も新たに作成しました。人物を付け足す際、髪型を類似イラストから引っ張ってきたのですが、編集が大変でした!

文書のなかでどこが伝えたい部分なのかを考え、強弱をつけ表現する。

バナー広告はターゲットに「興味を持ってクリックしてもらう」「目を留めてもらう」この2つが大きな目的になると思います。

一番伝えたい情報は何か、要素に順番をつけ、優先順位が高いものはきちんと強調することが大切だと感じました。

今回のインターンでは、さまざまな強調の仕方を学ぶことができました!

【強調の仕方】

  • 色を変える(目立たせたい文字は補色に近い色を使う)

  • 文字の大きさを変える

  • 「・」のあしらいを文字の上につける

  • イラストで何のバナーかイメージつきやすいようにする

デザインレビュー

制作したバナーは、エキサイトのデザイナーに皆さんからフィードバックをいただきました。レビューをもらうことで自分だけでは気付かなかった文字の太さの違和感やあしらいの付け方がわかり、大変勉強になりました。

↓デザインレビューをもとに修正を行いバナーが完成しました。

コーディング

自分でデザインしたページをPHPstormを用いて実装しました。 自分でWebサイトのコードを書く経験はありましたが、企業のコードを触る機会は初めてで、とても勉強になりました。

また、コードレビューでは自分だけでは気付かなかったミスに気付く事もありました。 コードレビュー後に修正を行い、自分が作った新しいページ入り口がプレリリースで表示されたときは、とてもうれしかったです。

figmaデザイン

プレリリースページ(サイトのスクリーンショット画面を載せています)

学んだこと
  • 半角、余計な余白はエラーになってしまうためpush前に細部まで確認

  • ファイルの場所を探しすときはサイトの検証から見る

  • Github pushの方法

git branch(自分が今なんのブランチにいるか確認)
git checkout master(masterに居なかったら一旦masterに戻る)
git branch xxxxxxxx(masterにいる状態で作業ブランチ作る)
git checkout xxxxxxxx(xxxxxxxxの作業ブランチに移動)
〜作業〜
git status(変更を加えたファイル一覧を表示)
git add .../..../..../(更新したいファイルを追加)
git status(追加漏れがないかもう一度git status)
git commit -m"[modify]更新内容"(コミットする)
git push origin xxxxxxxx(作業ブランチをpush)
〜git hub開いて更新内容を登録〜

最後に

メンターのデザイナーの方にサポートしてもらいながら、自分がデザインしたページをプレリリースまで行うことができました。

実際に自分の制作したものがサイトに載っている様子を見てとても嬉しかったです。また、デザイナーの方々に成果物をレビューしていただき、修正を行うことで、自分のデザインの表現の幅をグッと増やすことができました。

約2ヶ月という短い間でしたが、たくさんのことを吸収し、成長することができました! 環境構築を手伝ってくださったエンジニアの方々、アドバイスをくださったデザイナーの方々、ありがとうございました!

htmxでデータ送信時、親form内のすべてのデータが送信される

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

皆さんは、htmxをご存知でしょうか?(HTMLのtypoではありません!)

htmx.org

htmxはHTML用のライブラリの一つで、HTMLページ上での動的なデータ通信やそれによる表示の置き換えなどを簡単に行えるようにしてくれるものです。

エキサイト TechBlogでも以前紹介しているので、詳しくはぜひ公式サイトやブログを御覧ください!

tech.excite.co.jp

今回はこのhtmxでデータを送信する際、親form内のすべてのデータが送信されるということを紹介していきます。

htmxとは

htmxはHTML用ライブラリの1つで、HTMLページ上での動的なデータ通信や通信結果によるページのパーツの置き換えなど、自分でやろうとすると少々面倒な処理を簡単に行えるようにしてくれるものです。

詳しくはぜひこちらを御覧ください!

htmx.org

tech.excite.co.jp

ここでは簡単に、公式のサンプルに少し手を加えたものをSpring Boot / Thymeleafで試してみます。

<script src="https://unpkg.com/htmx.org@1.9.6"></script>

<!-- have a button POST a click via AJAX -->
<button hx-post="/sample" hx-swap="none">
    Click Me
</button>
@Controller
@RequestMapping
public class SampleController {
    /**
     * サンプル用ページを表示する
     * 
     * @return サンプル用ページのテンプレート
     */
    @GetMapping("sample")
    public String showSamplePage() {
        return "sample";
    }

    /**
     * サンプル用ページから、POSTアクセスを受け取る
     * 
     * @param param リクエストのパラメータ
     */
    @PostMapping("sample")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void sample(@RequestParam Map<String, String> param) {
        System.out.println(param);
    }
}

このようなコードを書くと、下記のようなページが表示されます。

サンプルページ

ボタンをクリックすると、開発者ツール等で通信が発生したことが確認できます。

このとき、画面遷移は発生せず、リクエストの送信のみが行われています。

sampleへのリクエス

Spring Boot側のコンソールからも、アクセスが来たことが確認できます。

なお今回は特にデータを送っていないため、リクエストの中身は空です。

受け取ったリクエス

このように、非常に簡単に動的なデータ通信ができました。

formを使ってデータ通信を行う

上記の例は非常に単純なものであり、実際はformを使った通信が多いでしょう。

今度はformを使ったサンプルを考えてみます。

要件は以下です。

  • テキストフィールドで、入力するごとにリクエストを送信してほしい
  • CSRF等の攻撃を回避するため、リクエスト送信時はトークンも一緒に送りたい

これを解決するコードは、以下のようになります。

<script src="https://unpkg.com/htmx.org@1.9.6"></script>

<!-- formでデータを送信 -->
<form hx-post="/sample"
      hx-swap="none"
      hx-trigger="input changed from:#sample_text"
>
    <!-- トークンを追加 -->
    <input type="hidden" name="token" value="トークン"/>

    <!-- テキストフィールドを追加 -->
    <label>
        サンプルテキストフィールド
        <input id="sample_text" type="text" name="sample_text" />
    </label>
</form>

サンプルページ

今回は要件に沿って、buttonタグは取り除き、かわりにformタグ、トークン用のhiddenなinputタグ、およびテキストフィールド用のinputタグを追加しています。

また、テキストフィールドへの入力ごとにリクエストを送るため、formタグに以下の属性をつけています。

hx-trigger="input changed from:#sample_text"

ほぼ読んだままですが、

  1. sample_textをIDに持つタグが、
  2. 入力されると、
  3. それをトリガーとしてリクエストを送信する

という内容です。

formタグ内にあるhiddenなinputタグとテキストフィールド用のタグの両方のデータを一緒に送るため、テキストフィールド用のタグではなくformタグの方にこの属性を付けています。

結果、 1111 を入力すると以下のようになりました。

開発者ツール

sampleへのリクエス

Spring Bootのコンソール

受け取ったリクエス

これで無事、要件である

  • テキストフィールドで、入力するごとにリクエストを送信してほしい
  • CSRF等の攻撃を回避するため、リクエスト送信時はトークンも一緒に送りたい

に沿ったページを作ることができました。

ですがこちら、formタグにテキストフィールドのタグのトリガーを書いており、記述が少々煩雑になってしまっています。

できればテキストフィールドのタグ自体にトリガーを書きたいところですが、hiddenなinputタグのデータも一緒に送るにはしょうがないのでしょうか?

データ送信時、親form内のすべてのデータが送信される

実はそんな事はありません。

よくhtmxのドキュメントを読んでみると、以下のように記述されています。

htmx.org

if the element causes a non-GET request, the values of all the inputs of the nearest enclosing form will be included.

つまり、「データ送信時、親form内のすべてのデータが送信される」ということです。

実際に試してみます。

<script src="https://unpkg.com/htmx.org@1.9.6"></script>

<form>
    <input type="hidden" name="token" value="トークン"/>

    <label>
        サンプルテキストフィールド
        <input id="sample_text"
               type="text"
               name="sample_text"

               hx-post="/sample"
               hx-swap="none"
               hx-trigger="input changed"
        />
    </label>
</form>

テキストフィールド側に、送信先やイベントのトリガー情報を書いています。

なお、自身がトリガーの対象のタグであるため、 from の記述は不要になりました。

ページの表示は変わりません。

サンプルページ

では、 1111 と入力してみましょう。

開発者ツール

sampleへのリクエス

Spring Bootのコンソール

受け取ったリクエス

先程のformの属性に書き込んだときと同じく、tokenのデータまで一緒に送られることが確認できました!

最後に

htmxは基本的な機能だけでも便利なライブラリですが、ドキュメントをよく見ると細かいところでも便利な機能があることがわかります。

今回紹介した機能だけでも、知っているのとそうでないのとではコードの複雑さ等がかなり変わってくるでしょう。

ぜひ読み込んで、使いこなしていきましょう!

TablePlusでPostgreSQLの「Sequence」を表示する方法

ご無沙汰しています。
taanatsuです。

今回は、Exciteで愛用されている「TablePlus」で、
PostgreSQLの「Sequence」を表示する方法をご紹介します。

バージョン

taanatsuは「Version 5.5.1 (510)」で確認できました。

Sequenceを表示する方法

TablePlusの左側ツリービューにある、設定から、
「Show sequence section」をONにします。

以上です。


これで更に便利にTable Plusを使うことができますね。
それでは、また次回!

Spring Session3系から、Redis接続時にNO_OP設定がデフォルトで不要になった

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

Java / Spring Bootでセッションを扱う場合、Spring Sessionを使うことが多いのではないでしょうか。

その際、データ保存先にAWSなどのクラウド上のRedisを指定した場合には、以前はNO_OP設定というものが必要でした。

それがSpring Session3系からはデフォルト状態では不要になったので、今回はその説明をしていきます。

Spring Sessionとは

Spring Sessionを使うことで、Spring Bootで簡単にセッションを扱えるようになります。

使い方も簡単で、例えば依存管理にGradleを使い、データ保存先をRedisにするのであれば、以下の設定をするだけです。

build.gradle

// 別途、Spring Boot本体は必要です
implementation "org.springframework.session:spring-session-data-redis"

application.yml

spring:
  data:
    redis:
      host: # 接続先のRedisのホスト
      port: # 接続先のRedisのポート

もちろんローカルやオンプレで作成したRedisだけではなく、AWSのElastiCacheなどで作成できるマネージドなRedisも使うことができるのですが、その際注意点があります。

それは、NO_OP設定です。

Spring SessionとNO_OP設定

Spring Sessionには、セッションのデータが削除されたり期限切れになった時などにイベントを発火してくれる機能があります。

docs.spring.io

この機能を使うためには、Redisに対して config というコマンドを使って設定を行う必要があるのですが、例えばAWS上のRedisなどではこの config コマンドは使えないため、そのままだとエラーになってしまいます。

そのため以前は、NO_OP設定というものをSpring Sessionに対して設定し、上記の設定が行われないようにする必要がありました。

@Bean
ConfigureRedisAction configureRedisAction() {
    return ConfigureRedisAction.NO_OP;
}

Spring Session3系とNO_OP設定

しかし実は、それはSpring Session2系のお話です。

3系からは、ガラリと変わりました。

github.com

github.com

Spring Session3系から、イベント発火等を行わない RedisSessionRepository というものがデフォルトで使われるようになりました。

その結果、NO_OP設定はデフォルト設定では不要になりました!

なお、もしイベント発火等が必要な場合は、 RedisIndexedSessionRepository というものを使う必要があります。 こちらを使う場合は、これまでどおりNO_OP設定が必要です。

最後に

シンプルにRedisでセッションを扱うだけなら、イベント発火等は使う必要はありません。

初めて、あるいは久しぶりにSpring Sessionを使う人にとってはちょっとしたハマりポイントだと思うので、一見シンプルなことですが結構ありがたい変更だと思います。

使いやすくなったSpring Sessionを、ぜひ使っていきましょう!

【Vue2からReactへの移行Tips】 React Hooksについて

こんにちは、新人エンジニアの久々江です。

本稿では、Vue.jsのv2系+Options APIからReactへの移行のTips、特にReact Hooksに関するものを紹介します。

筆者が担当するプロダクトでは、Vue2からReactへリプレイスを進めています。 筆者自身もチームメンバーもReactの業務経験は無く、実装を進めるにあたり様々な疑問が生まれました。特に弊チームメンバーのようなVue2経験者にとってReact Hooksは特異な概念でした。

本稿は、チームメンバーからのReact Hooksとは何か?、どのように使うのか?といった疑問に対する回答としてまとめましたが、他にもVue2からReactの移行を検討している方、移行を始めた方の一助になれば幸いです。

なお、Vue.jsのバージョンは2.7.10、Reactのバージョンは18.2.0、useSWRのバージョンは2.2.2を使用しています。

TL;DR

  • React HooksはVue2のOptions/Data、Options/Lifecycle Hooksに相当するものである
  • React Hooksを使う際の留意点

    • ループ中や条件式中にuseXXX()は不可
    • Custom Hookの命名useXXX
    • Reactにおける副作用は以下を指す
      • データの取得や変更
      • DOM操作
      • イベントリスナーの設定
      • タイマーのセット
      • state, propsの変化による処理実行
  • React HooksとVue2のOptions/Data、Options/Lifecycle Hooksの対応

    • data ↔︎const, useState, useRef
    • mounted↔︎ useEffect, useSWR
    • watch↔︎ useEffect(() => {}, [watchData])
    • computed↔︎ const data = () => { return culcData }

VueのOptions/Data、Options/Lifecycle Hooksとは

Options/Data、Options/Lifecycle Hooksは、Vue2経験者であれば馴染み深いAPIです。 この二つのAPIコンポーネントの作成、更新、破棄などに関わり、Vue2でのコンポーネントに欠かすことができないものです。

React Hooksとは

React Hooksは、React Component内で状態管理や副作用を行うための関数群です。 Vue2のdatamountedなどのOptions/Data、Options/Lifecycle Hooksに相当します。

React組み込みのReact Hooksがいくつかあり、それを元に作成された関数をCustom Hookと言います。 本稿では以下の組み込みのHooksについて紹介します。

Custom Hookにはサードパーティ製のものがしばしば利用されます。 本稿では、特にfetchに関する状態管理とfetch dataのキャッシュを担うuseSWRを紹介します。

 参考:swr.vercel.app

React Hooksを利用する際の留意点

Vue2からReactにリプレイスする際に留意する点はいくつもありますが、チームメンバーからの質問を元にReact Hooksに関しては以下の3点を挙げます。

  • React Hooksはコンポーネントのトップレベルで宣言しなければならない
  • ある関数においてReact Hooksを利用する場合、その関数はCustom Hookとなり、関数名はuseXXXとしなければならない
  • Reactの副作用

React Hooksはコンポーネントのトップレベルで宣言しなければならない

React HooksとOptions/Data、Options/Lifecycle Hooksは対応するものですが、Vue2では気にする必要の無かった点です。 React Hooksは、コンポーネント内のトップレベルでしか呼び出すことができません。これは、ReactがHooksの呼び出し順序に依存してHooksの状態を管理しているためです。条件式やループによりHooksを呼び出しの順番が変動すると前回と今回で参照する状態が異なりエラーを起こす可能性があります。 例えば、初回レンダリングではuseStateuseStateuseEffect、再レンダリング時に何らかの条件の変化によりuseStateuseEffectと呼び出された場合、useEffectは前回の状態をとしてuseStateを参照してしまいます。

以下の公式ドキュメントの例が分かりやすいので、是非ご覧ください。

ja.legacy.reactjs.org

内部実装に基づいた解説は以下にまとめていただいています。

qiita.com

ある関数においてReact Hooksを利用する場合、その関数はCustom Hookとなり、関数名はuseXXXとしなければならない

React Componentを除く、React Hooksを利用する関数は、Custom Hookとなります。

Custom Hookを作成する際には、その関数名を必ずuseで始める必要があるとされています。例えば、状態管理のためのCustom Hookは、関数名をuseCustomStateのように命名することが推奨されています。この命名規則に従うことで、その関数がReact Hooksであることを識別しやすくなります。

Custom Hooksも使用する際には、組み込みのReact Hooksと同じように、トップレベルで呼び出す必要があります。

Reactにおける副作用

Reactにおける「副作用(Side Effect)」とは、コンポーネントレンダリングサイクル外で発生する処理や影響のことを指します。主に以下のような場面で副作用が発生します。

  • データの取得や変更
  • DOM操作
  • イベントリスナーの設定
  • タイマーのセット
  • state, propsの変化による処理実行

React HooksとVue2のOptions/Data、Options/Lifecycle Hooksの対応

前項で述べた3種のReact HooksとCustom Hookを用いてVue2の実装を実現します。

Vue2でコンポーネントを作成する際、主に以下の手順が必要になります。

  • 管理するを値をdataに定義する
  • レンダリング直後に実行する処理をmountedに定義する
  • 何らかの値を監視し、変更を検知した際に実行する処理をwatchに定義する
  • 依存する値が変更されるたびに再計算される値をcomputedに定義する

次にそれらの手順をVue2で実装する例とReactで実装する例を示します。

管理するを値をdataに定義する

Vue2

Vue2ではコンポーネント内で共通して利用できる値を以下のように定義します。

data

値の宣言

    data () {
      return {
        isShown: false,
      }
    }

値の更新:

mounted () {
  this.isShown = true
}

React

Reactにおけるコンポーネント内で共通して利用できる値の管理方法は、以下の3種類があります。

  • 更新は不可能である(const)
  • 更新可能であり、値の更新による再レンダリングがトリガーされている(useState)
  • 更新可能であり、値の更新による再レンダリングがトリガーされていない(useRef)

それぞれの実装を以下に示します。

const

定数として利用、更新はしない

const isShown = true
useState

DOMの値を動的に変更したい時

// 定義
const [isShown, setIsShown] = useState<boolean>(false)

// 更新
setIsShown(true)
useRef

DOMの値を変更する必要はないが、何らかのイベントにより変数を変更したい時

// 定義
const isShown = useRef<boolean>(false)

// 更新
isShown.current = true

レンダリング直後に実行する処理をmountedに定義する

Vue2

Vue2ではレンダリング直後に実行する処理をmountedに定義します。以下の例のように外部APIの取得に使用されることがあります。

なお、外部APIの取得をレンダリング前(beforeMount)に行った場合、ページアクセスしてからAPI取得にかかる時間分のレンダリングされない状態が続きます。何も表示されない状態を短くするため、レンダリング直後に外部APIを取得しています。

mounted
  data() {
    return {
      data: null,
      loading: true,
      error: null,
    };
  },
  methods: {
    async fetchData() {
      try {
        const response = await fetch('https://api.example.com/data');
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        this.data = await response.json();
      } catch (err) {
        this.error = err.message;
      } finally {
        this.loading = false;
      }
    }
  },
  mounted() {
    this.fetchData();
  }

React

Reactではレンダリング直後に実行する処理をuseEffectに定義します。 データ取得であれば、useSWRも有用です。

useEffect
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
  const fetchData = async () => {
    try {
      const response = await fetch('https://api.example.com/data')
      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`)
      }
      const result = await response.json()
      setData(result)
    } catch (err) {
      setError(err.message)
    } finally {
      setLoading(false)
    }
  }

  fetchData()
}, [])
useSWR
const fetcher = async (url) => {
  const response = await fetch(url)
  if (!response.ok) {
    throw new Error(`HTTP error! Status: ${response.status}`)
  }
  return response.json()
};

const { data, error, isLoading } = useSWR('https://api.example.com/data', fetcher)

何らかの値を監視し、変更を検知した際に実行する処理をwatchに定義する

Vue2

Vue2において何らかの値を監視し、変更を検知した際に何らかの処理を実行する場合、watchを利用します。

watch
data: {
  message: 'Hello Vue!'
},
watch: {
  message: function (value) {
    console.log('message changed from', value)
  }
}

React

Reactにおいて値の変更を検知し、処理を実行する際にもuseEffectを利用します。 ただし、第二引数に変更検知の対象となる変数をまとめた依存配列を宣言します。

useEffect
const [message, setMessage] = useState('Hello React!')

useEffect(() => {
  console.log('message changed to', message)
}, [message])

依存する値が変更される度に再計算される値をcomputedに定義する

Vue2

Vue2ではcomputedを用いることで依存する値の変更に伴い、その値も再計算されます。

compouted
data: {
  firstName: 'John',
},
computed: {
  fullName: function() {
    return this.firstName + ' Doe'
  }
}

React

Reactにおいて値の再計算にはいくつか手法がありますが、useStateで管理している値が変更された場合、依存する値も再計算されます。

const [firstName, setFirstName] = useState('John')

const fullName = firstName + ' Doe'

まとめ

本稿ではVue2での開発経験者を対象にReact HooksのTipsをまとめました。React HooksはVue2のOptions/Data、Options/Lifecycle Hooksに相当するものであり、Vue2での実装をReactで代替可能であること、いくつかのルールがあることがわかりました。

至らぬ点があれば、ご指摘いただけますと幸いです。

参考文献

FlutterKaigi 2023に登壇します

こんにちは。エキサイト株式会社 Androidエンジニアの克です。 この度はFlutterKaigi 2023に登壇させていただくことになりました。

登壇内容

2023年11月10日 (金) 16:30 ~ 17:10(40分)

fortee.jp

内容は漫画ビューアのモバイルアプリをFlutterで開発する方法となります。

よくある機能を実装する際にいくつか工夫が必要な場面があったため、それらについてもお話しできればと思います。

  • Edge-to-edgeでの全画面表示
  • UIの表示切替
  • 画像表示および拡大表示
  • 暗号化された画像の表示
  • ページ対応のスライダー
  • 見開きページの実現
    • ImageProviderの拡張
    • Isolateを用いた処理の並列化
    • 画像の連結

FlutterKaigi 2023について

開催日 : 2023年11月10日 (金)

flutterkaigi.jp

採用情報

エキサイトではエンジニアを随時募集しています。 興味がありましたらお気軽にご連絡いただければ幸いです。

www.wantedly.com

AWS SAA-C03 合格体験記

はじめに

エキサイト株式会社 エンジニアの岡(https://twitter.com/oka000111 )です。

2023/7/30に受けたAWS SAA-C03 に合格したのでその体験記を綴ろうかと思います。

なぜ受験したか

そもそも自分の経歴を話すと、機械学習エンジニアを前職5年ほどやっていて、キャリアのリスク分散のため、バックエンドの知識を得たいと思い、Webエンジニアとして会社に転職してきました。

そのため、自分のキャリアが機械学習orWebエンジニアとなるのか、もしくは両方なのか、決めかねている状況でした。

そのなかでインフラというのは、どの分野に進んだとしても、影響するスコープが広く、その影響度合いも大きいと感じています。というのもインフラが適切に構築されないとウェブアプリケーション機械学習モデルも適切には動きません。そこでAWSの知識を勉強しようと思うようになりました。

AWSの知識を勉強しようとしたときに、色々な目標があると思うのですが、SAA-C03は資格試験にありがちな、「資格試験を取ることが目的になり、実務の役に立たない」ということが無い、実務に即した試験だと感じています。

例えば以下の問題

ある企業のセキュリティチームは、クラウドに保存されているすべてのデータを、オンプレミスに保存され た暗号化キーを使用して、保管時は常に暗号化する必要があります。
これらの要件を満たす暗号化オプションはどれですか。 (2 つ選択)
A) Amazon S3 で管理された暗号キー (SSE-S3) でサーバー側の暗号化を使用する。
B) AWS KMS で管理された暗号化キー (SSE-KMS) でサーバー側の暗号化を使用する。
C) 顧客提供の暗号化キー (SSE-C) でサーバー側の暗号化を使用する。
D) クライアント側の暗号化を使用して、保存時の暗号化を行う。
E) Amazon S3 イベントによって呼び出される AWS Lambda 関数を使用し、顧客のキーを使用してデータを 暗号化する。

(参考 ) https://d1.awsstatic.com/ja_JP/training-and-certification/docs-sa-assoc/AWS-Certified-Solutions-Architect-Associate_Sample-Questions.pdf

答えは

C) , D) なのですが(のはず..)、このようにある状況を想定した問題となっており、
これであれば実務に役に立つ、というのがおわかりいただけるかと思います。
そのためSAAの(実務にちゃんと役に立つ)知識を得たい、と思っている自分にはうってつけの試験だと感じ、受験することにしました。

試験準備

前提知識

  • lambdaやEC2といった用語は知っている程度

やったこと

  • techstock
    techstockはかなり人気があるAWSGCP認定試験の問題集が乗っているサイトです https://techstock.jp/ #1~#170だけchapterがあり、各chapterに7問あるので 170*7=1190問の問題を解くことができます。
    (問題は後のchapterほうが難易度が高くなっていくように感じました)
    自分は色々調べた中で #80~#170が試験難易度に近いという情報があったので、
    そこの問題を分からない問題がなくなるまで周回して解きました。

  • udemy(これだけでOK! AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座)
    こちらも人気があるUdemyの講座です
    【SAA-C03版】これだけでOK! AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座 | Udemy
    自分は動画を見るのはかなり時間あたりのパフォーマンスが悪いと思ったので講義資料のpdfを全結合し、
    techstockでわからない用語が出てきたときにpdfをctrl+Fして調べつつ覚えていきました

時間

1,2ヶ月の勉強期間の中で100時間ほど(単に知識を勉強するだけでなく、「なぜ」の部分も深掘って勉強したため結構かかった印象です)勉強しました

費用

AWS試験費用 16500円

techstock 4280円

udemy 1800円

計 22580円

結果と所感

803/1000で合格することができました。

所感としては、サービスの開発初期でないと触れないような、サブネットやVPC周りの知識やベストプラクティス集が頭の中にひととおり入ったような感じで、実務に役に立つような知識が頭の中に入った感覚があります。
インフラが本業ではないですが、自分の状況下に於いて、自分は勉強してよかったと感じました。

SaaS事業部で就業型インターンを経験し学んだこと

はじめに

エキサイト株式会社で一ヶ月間、就業型インターンシップ Booost!!!に参加させていただきました北野です。 今回はインターンシップで取り組んだことや学びを紹介いたします。

自己紹介

私は機械学習の研究をしている修士1年の大学院生です。 エンジニアとしての経験は浅く、大学院生になってからweb開発の勉強を始めました。 具体的には、TypeScript(Next.js)やFirebaseを用いて身近な問題を解決する簡単なアプリを開発しています。

やったこと

私はSaaS事業部の配属となり、KUROTEN - 管理会計がラク速になる経営管理クラウドの開発に参加させていただきました。 KUROTENはフロントエンドにVue2(Nuxt.js)を使用していましたが、Vue2のサポートが終了するのでReact移行(Next.jsは使用しない)を進めている途中です。 そこで、私は実際にReact移行の業務をさせていただきました。具体的には、「初期設定」の画面を担当させていただきました。

学び

インターンで学んだことをまとめていきます。

働き方について

今回のインターンはリモートで参加しました。そこで感じたこととしては、「リモートでの開発は効率が良い」ということです。 オフラインで業務したことはないので比較はできませんが、少なくとも自分が考えていたよりはずっと効率が良いものだと感じました。

リモートワークでは主にTandemというオンラインコミュニケーションツールを用いました。 メンターの方は自分と同じ(Tandemアプリ内の)ルームにいることが多いので、業務でどうしても分からないことがあるときなどはすぐに質問することができました。 また、会議などではオフラインで集まっていても、画面の共有のためにgoogle meetを用いるということもあるらしいです。

このように、エンジニアの働き方を体験でき、自身がエンジニアとして働くイメージが明確になりました。

業務レベルのフロントエンド開発

デザインが決まっていること

業務でwebアプリケーションを開発する際には、エンジニアだけでなくデザイナーさんもいます。(いないこともあるかと思いますが)

デザイナーさんが作成してくださったfigmaを見てそれの通りに画面を実装するのは、個人開発と大きく異なる部分です。 私は個人開発の経験からフロントエンドは自由度が高くてセンスがいるなあと感じていましたが、実際の開発(画面の実装)に自由度はありませんでした。 いかに忠実に画面を再現できるかが問われる職種であると感じました。

逆に、汎用的な関数、コンポーネントを作る、またそれらのレンダリングのロジックを組むという部分にはそれなりに自由度があり、フロントエンドエンジニアの腕が試される部分だと感じました。

Reactの学び

業務に参加してたくさんのことを学びました。

まずは、汎用コンポーネントについて。 先述の通り、デザイナーさんが画面のデザイン、コンポーネントのデザインまで作成しておられます。 この実装は私が参加した時点でもう済んでいて、私は汎用コンポーネント使用して画面を実装していくだけでしたが、画面の実装の際、コンポーネントのpropsやロジックを確認するためにコードを見にいくことが多かったです。そのコードが本当に勉強になりました。「汎用」なので色んなケースで使用することを考えつつも、共通化するところは共通化していてすごいなと感じました。自分よりできる方がいて、その方に質問をできる環境って魅力的だなと思います。

次に、デバッグについて。 私は多分ほかのインターン生よりもデバッグをしている時間が多かったと思います...。 しかし、このおかげでプリントデバッグに慣れることができましたし、その中でReactの仕組みなどについても理解が深まりました。 例えば、SWRなどを用いてフォームに挿入する初期値をフェッチするとき、その値は初回レンダリングの際にundefinedなので、フェッチの完了にフックしてフォームに初期値を入れ直さなければなりません(こうやって書くと当たり前ですが)。 このようなReactの挙動をデバッグをしていくなかで、理解していくことができたと思います。

また、スタイルに関するデバッグもたくさんしました。CSSに関してもデベロッパーツールなどでデバッグをしながら理解を深めることができました。

gitについて

今回のインターンでgitの操作に慣れることができたと感じています。 私はプルリクエストを出すときに、マージする対象のブランチを間違えてしまい、レビューされ、それに気づかずにマージするという失敗をしてしまいました。

しかし、このミスのおかげでrevert, rebase, resetなどのコマンドについて勉強して学びが広がりました。 また、チーム開発ならではの複雑なブランチをGUIで確認し、適切なコマンドを実行して解決できたことでgitの操作に自信が付き、 これからミスをしても一人で直せると感じるようにもなりました。

オフラインイベントに参加した話

インターンの途中でオフラインイベントが開催され、参加しました。 こちらでは、エンジニアの方々からの技術的な内容の紹介や、エンジニアの方、他のインターン生との交流ができ、とても有意義な時間となりました!

参加した感想としては、社員の皆さんとても仲が良くて楽しい雰囲気が感じれました。 技術的な話に付いていけない時もありましたが、いつか分かるようになりたいと感じ、モチベーションが上がりました🔥

また、他のインターン生とも業務の話や、技術的な経験の話をお互いにし合って、とても刺激的で楽しい一日になりました!

※ 以下は4月に開催されたもののレポートです tech.excite.co.jp

最後に

今回のインターンでは、個人で勉強しているだけでは得難い経験をさせていただきました。 これからもエンジニアになるためにますます努力したいと思います!!

メンターの方々、SaaS事業部の皆さん、人事の皆さん、ほかの部署の皆さんも本当にありがとうございました!

Web系未経験の学生が就業型インターンで学んだこと

はじめに

こんにちは、エキサイト株式会社で1ヶ月の就業型インターンに参加させていただいている久米です。

今回は、8月の1ヶ月間で学んだことについてご紹介します。

自己紹介

情報系大学院に通っている修士1年です。

大学院では深層強化学習の研究をやっていたりします。趣味で統計学の勉強していて、統計検定準1級を持っていたりします。技術的なことでは、基本的にPythonを触っているのでWeb系の言語はほぼ触ったことないですし、実務経験などもありません。

本稿では、1ヶ月の流れについて、また何を学んだのかについて述べようと思います。

やったこと

私は、SaaS・DX事業部のKUROTENに配属されました。元々事前面談でGo言語を触ってみたいと言っていたこともあってKUROTENのバックエンド側の開発に携わらせていただきました。

主なタスクとしては、一つのエンドポイントをMVCからクリーンアーキテクチャに移行するといったものです。

この記事によると、元々KUROTENはMVCを採用していました。現在はMVCからクリーンアーキテクチャに移行中とのことです。

今回のインターンでは、Go言語を用いて一つのエンドポイントをMVCからクリーンアーキテクチャに移行するタスクを実際にプルリクを出し修正するところまで行いました。

インターンの流れ

ここからは章別に学んだことや為になったこと、苦労したことなどについて語っていこうと思います。

環境構築編

最初にやらなければならなかったのが、環境構築です。KUROTENの開発をするためにまずはローカル環境でKUROTENを立ち上げる必要がありました。

大きなサービスを一つ立ち上げるということで、さまざまなツールをインストールしたり、コマンドを打ったりしていました。分からないことも多く、調べて分からなかったことはメンターさんに質問したりして解決していきました。 その甲斐あって、比較的早く環境構築が完了でき、メンターさんには頭が上がりません。

自分で考え抜くことはとても重要なことですが、それでも分からない場合はメンターさんに質問し、解決を仰ぐことが重要だと感じました。

クリーンアーキテクチャ

環境構築が完了してから実際にタスクを割り振っていただきました。それが、ある一つのエンドポイントをMVCからクリーンアーキテクチャに移行するというタスクです。

自分の中ではGo言語も全くの素人なのに新しいアーキテクチャの勉強もしなければいけないということで頭はパニックになってました。 こちらの記事を元にクリーンアーキテクチャについて学びました。

ここで、サービスを運用していく上でアーキテクチャは非常に重要なんだなと思いました。アーキテクチャについて考えることで新しい機能の追加や修正、メンテナンスなどがしやすくなるためです。

実装編

クリーンアーキテクチャの概要を理解したところで、実際のコードを書くところに移ります。しかし、私はバックエンド未経験、Go言語未経験なため、まずはGo言語の書き方について勉強しました。

ここで私はGo言語の勉強をネットの記事ではなく実際のコードを読みながら学習していきました。KUROTENのコードを読んで思ったことは、やはりコードでは命名規則がきっちり決まっており、変数名も用語集にまとまっていました。また、コードも綺麗だったため読みやすく、理解しやすかったです。やはり、チーム開発では他の人が読みやすく改修しやすいように書くことが重要なんだと思いました。

自分のコードと会社のコードの相違点として、例えば、個人で開発しているときは人数を表す変数を"num"や"number"と書きがちですが、KUROTENでは"numberOfPeople"など一目見て人数だと分かる変数名を使用していました。具体的に下記に今回私が書いたコードの一部の載せます。(※許可はいただきました)

if _, ok := totalNumberOfPeopleMap[averageKey]; !ok {  
    totalNumberOfPeopleMap[averageKey] = nil  
}

上記のコードは生成したtotalNumberOfPeopleMapに対してnilを格納しているコードです。ここでaverageKeyとは文字列の"average" が格納されています。個人で書くとtotalNumberOfPeopleMap["average"]と直で書いてしまうところ、きちんと変数を使っていることが分かります。

for _, numberOfPeople := range t.numbersOfPeople {  
    strYearMonth := strconv.Itoa(int(numberOfPeople.YearMonth))  
  
    if totalNumberOfPeopleMap[strYearMonth] == nil {  
       totalNumberOfPeopleMap[strYearMonth] = getRoundValues(numberOfPeople.NumberOfPeople.Float64)  
       continue  
  }  
    if totalNumberOfPeople, ok := totalNumberOfPeopleMap[strYearMonth].(float64); ok {  
       totalNumberOfPeopleMap[strYearMonth] = getRoundValues(totalNumberOfPeople + numberOfPeople.NumberOfPeople.Float64)  
    }  
}

上記のコードは生成したtotalNumberOfPeopleMapの年月に対して何も入っていなかったら値を入れ、値が入っていたら合計していくコードです。値を格納するコードなんかは横に長くなってしまっていますが、読むのが苦になったりはしないなと思いました。

私の中で長めの変数名を使うことは可読性が低くなることや書きにくくなることに繋がると思い避けてきましたが、全然そんなことはなく、むしろ変数名を見ることでどういう値が格納されているのかが一目で分かり、コードの理解にかかる時間を大幅に短縮することができました。 個人で開発をしていると変数の命名などおざなりになってしまいがちですが、長く開発していくことやデバッグすることを考えると最初に命名規則などをしっかり決めておくことが重要だと感じました。

実際にコードを書いてみると既存のコードを活用できる場面もありました。しかし、書いて勉強しているわけではないので考えていた実装が実はGo言語では出来なかったり、コードの書き方があやふやでエラーを連発してしまったりしました。

しかし、1ヶ月触れていると自然と書けるようになる場面も少しずつ増えてきたので、インターン終了後も継続して個人・チーム開発を続けていきたいなと思います。

メンターさんが口癖のように「Go言語の良いところをたくさん知ってもらって今後も書いていってほしい」と仰っていて、案の定、そのシンプルな書き方や言語思想などを学んでいく過程でGo言語を書くのが好きになりました。ありがとうございます。

プルリク編

実装が済み次第、各層に分けてプルリクを出しました。チーム開発をしたのもほぼ初めてだったためプルリクを出すのも緊張しました。 皆さんに自分のコードをレビューしていただきて、その都度修正を行いました。

自分では大丈夫だと思っていてもレビューしてもらうとミスが見つかったので、客観的な視点というのは重要なんだなと強く感じました。

おまけ:面談編

今回のインターンではSaaS・DX事業部や他事業部の方、人事の方やビジネス職の方、同期の皆さんなど、様々な方とお話させていただきました。普段の業務や就活の話であったり、技術的な話、趣味の話などもお話できました。 総じて、エキサイトの方々は皆さん優しくて面白く、お話してて楽しかったです。

まとめ

この1ヶ月間、Web系未経験の私が実際のサービスの業務に携わらせていただきました。環境構築から実際の実装、プルリクまで一通り学ぶことができました。しかし、概念を理解しても実際にそれをコードに落とし込むことは難しく、助けていただく場面が多かったです。今回のインターンで学んだことを糧にして、今後も成長し続けていきたいと思っています。

最後に

メンターさんをはじめ、業種問わずさまざまな方達に良くしていただき、たくさんのことを学ぶことができました。社員さんのおかげで非常に充実した時間を過ごすことができました。 1ヶ月間、ありがとうございました。

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

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

9月15日に、社内交流イベントである「テクデザBeer Bash」の第6回目を開催しました!

今回はそのレポートを書いていきます。

なお前回の様子は以下のブログにまとめてありますので、よければ御覧ください。

tech.excite.co.jp

テクデザBeer Bashとは

テクデザBeer Bashは、主に社内のエンジニアやデザイナー同士のコミュニケーション活性化のための催し物です。

同じチーム内の人はもちろん、業務ではあまり関わることがない他部署の人たちとも、お酒や軽食などを食べながら話したり、コンテンツ発表を一緒に見ることで交流を深めてもらって、普段の業務でもフランクに話せるようになることを目的としています。

また今回はそれに加え、夏季インターンで来てくれていた学生の方々にも多数参加していただきました!

社内の雰囲気を知ってもらえていたら幸いです。

当日の様子

当日は、登壇発表として

  • コードからのドキュメント自動生成と、ドキュメントからのコード自動生成の方法
  • 生成系AIをSaaS系サービスに導入した際の知見

の2つを行ってもらい、その後フリートークタイムとしました。

コードからのドキュメント自動生成と、ドキュメントからのコード自動生成の方法

APIからドキュメントを自動生成する方法と、自動生成されたドキュメントからリクエスト用のコードを自動生成する方法について発表してもらいました。

自動生成によって、ドキュメントを常に最新に保ちつつ、人手を減らしてコードを作る方法をデモを交えて発表してもらいました。

生成系AIをSaaS系サービスに導入した際の知見

実際に生成系AIを導入した2つのSaaS系サービスについて、それぞれの担当者に知見を発表してもらいました。

導入してよかった点・大変だった点や、その反響や展望について赤裸々に話してもらい、とても盛り上がりました!

最後に

今回は学生の方々が沢山いらしたこともあり、かなり大所帯でした! その分いつもよりワイワイと活気のある会になったと思います。

社内の参加者だけでも同等以上の活気のある会になるよう、今後も盛り上げていきます!

エキサイトブログにおけるマイグレーションの実施例

はじめに

エキサイト株式会社 バックエンドエンジニアの山縣(@zsp2088dev)です。 普段の業務では、エキサイトブログの新機能開発や運用に取り組んでいます。

先日、エキサイトブログのブログテーマ機能について内部的に大きな変更を行いました。 具体的には、ブログテーマ機能と依存しているトラックバック機能を切り離すために、新テーブルへのマイグレーションを実施しました。

本記事では、マイグレーションを実施した背景や実際に行ったことについて紹介します。

ブログテーマ機能とトラックバック機能

エキサイトブログには、エキサイトブログ編集部が提供するテーマについて、ブロガーが自由に投稿できるブログテーマ機能といったものがあります。 テーマ一覧ページには、数多くのテーマに対する様々な視点や熱量の高い記事が集まり、新しい発見や学びを得られます。 このテーマに対して、投稿していただいた記事の中からエキサイトブログ編集部の記事内で紹介することもあります。 この機能は、当初から提供しており、多くのブロガーにご利用いただいております。

ブログテーマに投稿した記事一覧

ここで、ブログテーマ機能と関係の深いトラックバック機能についても紹介します。 トラックバック機能は、「ある記事に対する記事を作成し、相手にも作成した記事を知らせることのできる」機能です。 馴染みのあるもので言えば、X(旧Twitter)の引用リポストのような機能です。

技術的な側面について着目すると、ブログテーマの機能は、このトラックバック機能を使用して実装していました。 テーマ投稿用ユーザーに対してトラックバックをすることで、テーマ投稿した記事として扱うような実装となっています。

2023年1月20日より、利用者数が減少していることを理由に、一般ブロガー間での新規のトラックバックを停止しています。 テーマ投稿については、テーマ投稿用ユーザーへのトラックバックのみを許可することで引き続きテーマ投稿をできるようにしていました。

blog.excite.co.jp

ブログテーマ機能の詳細

続いて、トラックバック機能を使用してテーマ投稿した記事を取得する手順について説明します。 テーマ投稿した記事を取得する仕組みは、以下の図のとおりです。

  1. スキーマAから、トラックバック機能を使用してテーマ投稿した記事URL(https://[ブログURL].exblog.jp/[記事ID])を取得する
  2. アプリケーション内で、正規表現を使用してブログURLと記事IDを抽出する
  3. スキーマBから、2で抽出したブログURLと記事IDをもとに記事情報を取得

この方式では、以下の3つの問題がありました。

  • トラックバック機能とブログテーマ機能に依存関係がある
  • キャッシュが効いていないときのページ応答速度が遅い
  • トラックバックという言葉に馴染みがない

これらを踏まえた上で、テーマ投稿した記事を管理しやすくするためのテーブルを作成し、書き込みと参照先を切り替えることにしました。

マイグレーションツールの作成

新テーブルへのマイグレーションをするにあたり、「スキーマをまたぐ」「データの加工」が必要であることを考慮した結果、 一度アプリケーションに落とし込んでマイグレーションをするのがよいと考えました。 そこで、マイグレーションツールを、普段の開発でも使用しているJava17 / SpringBoot (ShellComponent) / JdbcTemplateを使用して作成することに決めました。

トラックバックテーブルには、一般ブロガー間でのトラックバックを含めた数十万のレコードが格納されています。 その中から、テーマ投稿に関係するレコードのみを抽出したり、削除済みの記事や退会済みブロガーの記事などをマイグレーションの対象外としました。 本当に必要なレコードのみをマイグレーション対象とすることで、稼働中のDBへの負荷を最小限の負荷に留めるようにしました。

ここで、実際に作成したコードの一部について紹介します。 データをまとめて追加する際、基本的にはJdbcTemplateのbatchUpdateを使用してまとめてデータの追加をしつつ、 重複エラー時はupdateを使用して1件ずつ追加するようにしました。 これにより、大量のデータを効率よく追加することができるようになります。 *1

以下にサンプルコードを示します。

public void bulkInsert(List<Item> items) {
    int BULK_INSERT_SIZE = 1000;

    // [1, 2, 3, 4, 5] → [[1, 2], [3, 4], [5]] のように分割する
    List<ArrayList<Item>> splitItems = IntStream
            .range(0, (items.size() + BULK_INSERT_SIZE - 1) / BULK_INSERT_SIZE)
            .mapToObj(i -> new ArrayList<>(items.subList(i * BULK_INSERT_SIZE, Math.min((i + 1) * BULK_INSERT_SIZE, items.size()))))
            .toList();

    splitItems.forEach(this::bulkInsertSplitItems);
}

private void bulkInsertSplitItems(List<Item> items) {
    try {
        jdbcTemplate.batchUpdate("""
                INSERT INTO sample_item (
                    value
                ) VALUES (?)
                """, items, items.size(), (ps, item) -> ps.setString(1, item.getValue())
        );
    } catch (Exception e) {
        items.forEach(this::insert);
    }
}

private void insert(Item item) {
    try {
        jdbcTemplate.update("""
                INSERT INTO sample_item (
                    value
                ) VALUES (?)
                """, item.getValue());
    } catch (Exception ignored) {

    }
}

マイグレーションの実施

マイグレーション前の初期の状態は以下のとおりです。

このとき、テーマ投稿した記事の書き込みと参照は旧テーブルを向いています。

続いて、旧テーブルへの参照と書き込みは行いつつ、新テーブルにも書き込みを行うようにします。 このような処理をダブルライト方式と呼んでいます。

ダブルライトを確認後、作成したマイグレーションツールを実行します。 マイグレーションツールで重複エラーを考慮しているのは、Step2以降でデータが追加されているからです。

エキサイトブログでは、本番環境とステージング環境で同じデータベースを使用しています。 ステージング環境で、テーマ投稿ページを見て、旧テーブルを参照している本番環境と同じ結果になることを確認します。 このとき、一部データの抜けている箇所があったため、データ移行ツールを修正し再度実行し、同じ結果になることを確認しました。

最後に、ダブルライトを停止して新テーブルを参照、書き込みをするようにしました。

おわりに

本記事では、エキサイトブログのブログテーマ機能からトラックバック機能を切り離すために、マイグレーションに取り組んだことについて紹介しました。 マイグレーションツールの作成からマイグレーションの実施、ダブルライトの停止まで、1ヶ月弱で取り組むことができました。 エキサイトブログでは、引き続き既存機能の改修や新機能の開発などに取り組んでいきます。

採用アナウンス

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しています。 また、長期インターンも歓迎しています。

カジュアル面談からもOKです。少しでもご興味がございましたら、お気軽にご連絡頂ければ幸いです。

▼ 募集職種一覧 ▼ recruit.jobcan.jp

*1:AWS Dev Day 2023 1日目のZOZO社のセッションを参考に実装しました。

老舗プラットフォーム事業での10日間の就業型エンジニアインターン

始めに

初めまして,エキサイト株式会社による就業型インターンシップ「Booost!!! Excite Internship 2023」

info.excite.co.jp

に参加した金子と申します.本稿では,インターンを行う中で得た知識や経験,それから得た学びや抱いた感想をご紹介できればと思います.

自己紹介

現在大学院に通う博士課程2年の学生で,大学院では物理学を専攻しております.研究では物理シミュレーションを行っており,普段はC++Pythonを用いてシミュレーションプログラム開発やデータの解析をしています.

1年ほど前からWebアプリ開発の世界に興味を持ち,PHPフレームワークの一つ,Laravelを使った開発を学びました.その後もハッカソンへの参加や地元のスタートアップでの長期のエンジニアインターンシップを経験する中で,ReactやVueといったフロントエンドライブラリ,フレームワークDjango,Springbootといったフレームワークを用いた開発も経験してきました.

チーム開発や実務の開発経験を積む中で,BtoCのサービスも経験しエンジニアとしての働き方や業務内容の解像度を上げたいと思い,本インターンシップに応募した次第です.

業務内容

以下,私が10日間で経験した業務を紹介します.

サービスとその背景知識

具体的な業務内容を紹介する前に,担当することになったサービスや業務に関する背景知識を述べます.開発はアジャイル的に進められ,明確な仕様は開発と共に固めていきました(とはいえほとんどは仕様の確認に留まりましたが).仕様を固めるには内容に応じたドメイン知識が求められ,それは業務を進める中で学習していきました.

エキサイト電話占い

私が配属されたのは「Life&Wellness事業部」「エキサイト電話占い」というサービスでした.エキサイト電話占いは,エキサイト株式会社が契約する所属占い師と占いを希望する利用者を繋ぎ,通話を介して占いを行えるようにするサービスです.

管理ツール

このサービスには,利用者や占い師が利用するページの他に,それらの情報を管理する管理ツールが用意されています.管理ツールを利用することで非エンジニア職や業務委託を行っている方々も,データベースを直接覗かずに必要な業務を行えます.管理ツールは業務を円滑に行う上で必要な機能と言えるでしょう.

販売促進機能

エキサイト電話占いは販売促進機能の追加を検討しています.販売促進機能はマーケティング戦略的に重要で,この機能によって新たな施策を打ち出しやすくなります.この機能は,マーケティングの事例から確実な効果が見込まれており,また,エキサイト電話占いの今後の成長を支え,現在抱える課題も解決することも期待されています.

エキサイト電話占いに販売促進機能を追加する開発と並行して,その管理ツールにも販売促進機能の管理機能を追加するための開発が行われていました.

私はその管理ツールに,ユーザーが保有する販売促進機能の概要や詳細を確認するための項目やページを追加する業務を担当しました.

実際にやったこと

管理ツールの開発

開発内容は,既存のAPIを利用して取得したデータを加工してページに表示するというものでした.APIの構造例は以下のようになります.

Api
├── Src
│   └── Resource
│       └── App
│           └── Generation
│               └── SellPromotion
│                   └── User
│                       ├── Promotion1.php
│                       └── Promotion2.php
└── Tests
    └── Feature
        └── Generation
            └── SellPromotion
                └── User
                    ├── Promotion1Test.php
                    └── Promotion2Test.php

{api_domain}/Generation/SellPromotion/User/Promotion1もしくは{api_domain}/Generation/SellPromotion/User/Promotion2といったエンドポイントを適切なメソッド・パラメータで叩くことでデータベースからデータを取得できるようになっていました.

私が実装したのは,いわゆるMVC(Model, View, Controller)モデルのような形でAPIから取得したデータを適切に加工してページに表示するというものでした.私が修正もしくは新規作成を行ったのは以下のようなファイル群です.

Tool
├── App
│   ├── Resource
│   │    └── Generation
│   │        └── SellPromotion
│   │            └── User
│   │                ├── Promotion1.php
│   │                └── Promotion2.php
│   └── View
│       └── Pages
│          └── User
│             ├── Detail
│             │   └── index.tpl
│             └── Promotion
│                 ├── Promotion1
│                 │   └── index.tpl
│                 └── Promotion2
│                     └── index.tpl
└── Controller
        └── User
           ├── Detail
           │   └── index.php
           └── Promotion
               ├── Promotion1
               │   └── index.php
               └── Promotion2
                   └── index.php

MVCにそれぞれ,M=Resource, V=View, C=Controller以下のファイルが対応しています.これらを開発することによって利用者の詳細画面に対応する販促推進機能の要約情報を表示し,その画面から具体的な販促推進機能の情報を閲覧できるようにしました.

要件の確認

開発はアジャイル的に行われていて,私が担当したチケットが作成されて時点では明確に要件が固まっていませんでした.したがって,開発中はチームでコミュニケーションを取り,場合によっては仕様変更を行う必要があります.私が担当したチケットの一つもAPI側の仕様変更が必要であり,今回は社員の方にAPIの修正を行っていただきました.*1

インターン期間中に,単に質問するだけでなく仕様変更につながる問いかけをできたのは自信に繋がりました.

学んだこと

以上の業務を通して学んだことや考えたことを以下にまとめます.

老舗BtoCサービスとしての特徴

短期間のインターンでしたが,事業部の昼会(事業部全体のミーティング)に毎週出席させてもらいました.そこでは普段聞くことのないマーケティングの単語(ロイヤル,ライト,ARPPU等)を使って日毎の売上を分析し,事業戦略を話し合っていました.私が以前参加したことのあるBtoB向けのスタートアップのミーティングでは,大口顧客の維持を質的に議論したり,契約数や商談数を重視していたりしたので,事業領域やそのフェーズによる運営方針の違いを感じました.

また,エキサイト電話占いは16年以上続くサービスということもあって,参考にしたソースコードの中には10年前に作られたファイルもありました.そのため,コーディング規約が途中で変更されている様子も伺えました.といってもDockerを用いたコンテナ技術の導入やCI/CD,コードフォーマッタなど開発を支援する技術は積極的に取り入れていて,古い技術と新しい技術を融合させる取り組みが行われていました.これまでもフルリプレースや部分的なリプレースを繰り返してきたという話もあり,長期に続くプロダクトを継続的に成長させることの醍醐味を知れました.

トラブルシューティング

インターン期間中に,ローカルで開発中のサービスにログインできなくなるというトラブルが発生しました.自分なりにDockerコンテナを立ち上げ直したり,シークレットモードでキャッシュをリセットしてログインを試みるなど試してみましたが解決せず,メンターにヘルプを求めることになりました.

メンターの方によるトラブルシューティングで印象的だったのは,ソースコードを追って原因特定を行ったことです.私も普段の新しくページを開発している時は,デバッグの際にソースコードを丁寧に追うことはありました.しかし,正しく動いていたはずのページの中身の挙動を追うことはあまりしてきませんでした.如何なる時もソースコードを丁寧に追うことで問題の理解が深まるのだと思い,今後の開発で心がけたいと思いました.

最後に

インターンシップ参加に当たって,個人的な事情を汲んで*2,人事部やLife&Wellness事業部の皆様には少しイレギュラーなスケジュールを組んでいただきました.周りのインターン生の働き方を見てもかなり柔軟かつ,配属も各インターン生の要望や相性が反映されていて,個々の学生が求める形でエキサイト株式会社,ひいてはWeb業界でエンジニアとして働くイメージを得られるインターンシップだと感じました.

社内のイベント「Beer Bash」にもご招待いただき,当日は本社オフィスでの業務も経験させていただきました.メンターのお二人には気軽に毎日のように質問させていただき,必要なドメイン知識を教えていただきました.入社後をイメージしながら業務を行えた気がします.また,CTOや事業部の上長,新卒採用の方との1on1をする機会も多く,ざっくばらんに考えを伝える文化を感じられました.自分の考えを話す中で,自分が目指すエンジニア像も見えてきました.

非常に充実した日々でした.短い期間でしたが,エキサイト株式会社の皆様ありがとうございました!

*1:開発の進め方によっては私が巻き取って修正を行うこともあるようですが,時間的な制約から今回は社員の方に修正をお願いしました.

*2:本来は9月頭から1ヶ月かけて行うインターンシップを,9月中旬から参加させていただきました.

エキサイト就業型インターンBooost!!!に参加した感想

こんにちは!エキサイト株式会社でインターンをしている上原と申します。 今回は就業型インターンシップに参加した上で学んだことや感じたことについて紹介したいと思います。

自己紹介

私は現在、情報系の大学院に在学中の修士1年の学生で、分散人工知能について研究しています。 技術的な面では個人開発とチーム開発含め、WebアプリからiOSアプリ、インタラクティブアートまで様々なものを開発してきました。

やったこと

今回のインターンシップではメディア事業部のエキサイトブログというサービスの開発に参加しました。 具体的にはブログの一ヶ月のPV数が一定数を超えた時、希望しているユーザに対してメールを送信するという部分を実装しました。 また、送信するメールの文章を変更できる管理画面を実装しました。

学んだこと

インターンシップを通じて学んだことはたくさんありますが、今回はその中で4つ紹介したいと思います。

サーバの負荷や処理速度を気にする

今回、開発に携わらせていただいたエキサイトブログは大量のアクセス数を誇る大規模なサービスです。 最適化されていないコードを書いてしまうとサーバに負荷がかかったり、結果を表示するのに時間がかかってしまい、安定したサービスを提供することが出来ません。 そのため、自分が書いているコードがサーバに負荷や時間がかかる処理かどうか常に意識する必要があります。

具体的な例として、SQLの実行計画があります。 エキサイトブログではDBとしてPostgreSQLを使用しています。 PostgreSQLではEXPLAINコマンドとANALYZEオプションを使うことで、実行計画と呼ばれるDBの内部的な処理手順を見ることができ、 実際の処理にかかる時間やどこがボトルネックになっているかを調べることが出来ます。 これに基づいて、より最適なSQLクエリに変更したり、必要に応じてインデックスを作成することで、負荷や処理時間を減らすことが出来ます。

今回のインターンを通じて、どういった部分に注目する必要があるのか、どうやったら負荷を減らすことができるのか学ぶことが出来ました。 普段の個人開発では大規模なアクセスが来ることは無いため、あまり気にしていませんでしたが、これから開発する上で意識していきたいと思いました。

読みやすいコードを書く

チーム開発する上で分かりやすい変数名をつけたり、チームのコーディング規則を守ることは重要です。

//分かりづらい変数名
private List<BlogNoticeUser> u;
//分かりやすい変数名
private List<BlogNoticeUser> blogNoticeActiveUserList;

変数名が少し長くなっても問題ないので、なるべく他の人にとって分かりやすい名前を命名することが大切です。 また、ソースコードはなるべく分かりやすく簡潔にまとめることも必要です。 しかし、シンプルで短いコードが必ずしも他の人にとって分かりやすいコードとは限らない場合があります。

例えばStream APIにおけるラムダ式とメソッド参照です。

//メソッド参照
result = users.stream().filter(mailLog::isAlreadySent);
//ラムダ式
result = users.stream().filter(user -> mailLog.isAlreadySent(user));

一見するとメソッド参照はシンプルで短くなっているので、分かりやすくなっている感じがします。 しかし、どのような変数が渡されているのか分かりづらいため、読み手に負担がかかってしまいます。 こういった場合は少し文字数が増えますがしっかりと記述することが必要だということがわかりました。

tech.excite.co.jp

今回の開発を通じて、シンプルで短いコードが必ずしも他の人にとって分かりやすいコードでは無いことを実感しました。 また、IDEのコード整形や警告が必ずしも最適解では無いことが分かりました。

コメントを残す

実際の開発において、ドキュメントが残されているものは稀なので、システムの動作について知りたいときは直接、コードを読む必要があります。 コメントを意識して残すことで、プルリクエストでレビューをして頂く時や、コードを読む必要がある時に解読したり、コードを書いた人に聞く手間を減らすことが出来ます。

例えばSQLではクエリにハイフン2つをコメント文の先頭に入力することで残すことが出来ます。 また、COMMENT ON [TABLE or COLUMN] [テーブル名] is [コメント文]でテーブルやカラムに対してコメントを残すことができ、 TablePlusを使用してデータベースを覗くと、以下のようにコメントが表示されます。

TablePlus上のテーブル

普段のチーム開発ではコメントを残すことはどうしても後回しにしがちですが、コードレビューや後々のチームメンバーや自分にとって、とても重要だと感じました。

テストをする

個人開発ではプログラムが完成したらそのまま本番環境にデプロイし、バグがあればその都度、修正するということをおこなっていました。 しかし、多くのユーザを抱えているサービスではテストをせずにそのままリリースしてしまうと、未知のバグによってサービスが動作しなくなる可能性があります。 その結果、修正するまでの期間の収益の減少やユーザ離れにつながってしまいます。 そのため、本番環境に載せる前に入念にチェックする必要があります。

今回の開発を通じて、テストの大切さや、テスト環境の仕組み、テスト環境にデプロイする方法について学ぶ事ができました。 テスト環境の仕組みは便利だったので、自分の個人開発やチーム開発に導入してみたいと思いました。

まとめ

今回のインターンを通じて、実際の業務ではどのように開発が進められているのか体感することが出来ました。 また、普段の開発では気にしなかった部分や気づかなかった部分を知ることが出来ました。 技術力だけではなく、チーム開発や知識も含め、様々なことについて学ぶことが出来ました。 この貴重な経験を活かして次に繋げていきたいです!

最後に

メンターさんをはじめ、様々な社員さんのおかげで、学びのある充実した期間を過ごすことが出来ました。 あっという間でしたが、一ヶ月間ありがとうございました!

【インターン】「バナー広告制作」で学んだこと

こんにちは!エキサイトで就業型インターンをさせて頂いてる佐藤です! 今回は、インターンの中で初めての業務である「バナー広告制作」を通じて学んだことを共有したいと思います。

自己紹介

千葉県在住の大学3年生です。大学ではデザインを専攻しており、まだ専門的に1つの分野を学ぶというよりかは幅広くデザインについて学んでいます。 大学では学ぶことができない、実際に働くという経験をしたいと思い、インターンに参加させていただきました。

エキサイトのインターン以外での技術の経験

現在、大学と大学周辺の商業施設でハロウィンを盛り上げるプロジェクトを行なっています。そこでは地域の回遊性を高めるという目的があり、デザイン科で仕組みやゴールのデザインを考えています。このプロジェクトでは地域の人たちがどうしたら回遊してくれるのかや、子供達がメインなので、どう楽しませてあげるのかを考えています。

インターンでやったこと

インターンで初めて頂いたお仕事は「KUROTEN」のFacebook用バナー広告作成でした。

バナー広告市場調査

まず初めにバナー広告の市場調査を行いました。今回は「ターゲットは会社の代表になるので、あまりポップすぎない感じでFORCASのブランドイメージみたいなしっかりしてる感じ」という条件がありました。そこから「toB向け」「会計」「会計管理」などのキーワードの広告を調べ、条件に当てはまりそうなものを集め、グルーピングや共通点・明確な違いを言語化、調査から得た気づきをまとめました。また調査結果を整理し、社内wikiのConfluenceを制作しました。

競合他社とエキサイトが制作しているサムネイルの特徴の比較

共通点・明確な違いをまとめる

感想

このワークを通してバナー広告のあしらいや特徴について知ることができ、これから制作するにあたりどのような要素を入れれば良いか整理することができました。

実際にバナー広告制作

市場調査でまとめた内容、条件から自分なりにバナー広告を作成してみました。初めはFORCASのブランドイメージを「シンプル」「白・黒・グレー」の要素で制作していました。

ビジネス側からのフィードバック

実際にはLPのカラーにあったSaaSっぽいライトなトーンのものとかもパターンも用意しておくことが大切であるなと感じました。想像してた雰囲気と異なる可能性もあるからです。

デザイナーの方々にレビューを頂きBUを重ねる

制作物をデザイナーの方々にレビューしていただき、改善を重ねていきました。そこから学んだ共有します。

◯◯らしさとはどの要素のことを言っているのかを考える

実際に自分なりに作ったバナー広告を見てもらい頂いたレビューとしては条件にあるFORCASのブランドイメージがどの要素なのかを考える必要があるということです。その要素が「色合い」なのか「文字の大きさ・フォント」なのか「余白の取り方・レイアウト」なのか、それを明確にすることで、依頼してきた方のイメージに応えられるからです。

スピード感を意識する

実際に大学で課題をやるのと、業務で作業をするのでは全く異なり、最初は与えられたタスクが間に合わないこともありました。1人で業務をしているわけではないので、スピード感が遅いとついていくことが難しいからです。

いろんなパターンを制作し、デザインの引き出しを増やす

初めは雰囲気が1つに固まっておりパターンがありませんでした。依頼してきた側がどのようなイメージを持っているのかわからないので色合いや雰囲気のパターンを何個も用意しておくことで、依頼に応えられることを学びました。

言語化を意識する

レビューを頂く際に、どういった経緯でそのデザインを考えたのか言語化できていないとうまく伝えることができず、レビューももらうことができないからです。また、デザイナー以外にも伝える機会があるので、言語化はこれから意識していこうと感じました。

実際にバナー広告を配信した結果

配信した中で比較すると実際の画面が見えているもの(黄色の枠)がクリック率が高いことがわかりました。文字が大きく、少ない文字数のものが一番多くクリックされていました。画面の写真が大きなっていることで、実際に使用するときのイメージが湧くからではないかと考えました。

最後に

実際にデザイナーの方にレビューを頂く機会は大学ではないので、とても良い経験となりました。自分に足りない能力や伸ばしていける能力を知ることもできました。あと数回でインターンは終わってしまいますが、最後まで楽しんで、より多くのことを学んでいきたいと思います。そしてこれからの大学生活や就活に活かしていきたいと思います。