AWSのプライベートサブネットとパブリックサブネットの違い

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

Amazonが提供しているAWSでは、サーバやDBなどを配置する箱として「サブネット」と呼ばれるものがあります。 そしてサブネットは、「プライベートサブネット」と「パブリックサブネット」の2種類が存在します。

今回は、その違いについて説明していきます。

サブネットとは

AWSには、「サブネット」と、それを入れるための「VPC」と呼ばれるものがあります。

公式では、以下のように説明されています。

docs.aws.amazon.com

VPC の基本

Virtual Private Cloud (VPC) は、AWS アカウント専用の仮想ネットワークです。


サブネットの基本

サブネットは、VPC の IP アドレスの範囲です。

知らない人にとってはおそらく「なんのこっちゃ」だと思いますが、言ってみれば、「VPC」は「サブネット」を入れるための箱で、「サブネット」はサーバやDBなど、具体的なAWSサービスを入れるための箱です。

例えばEC2を作成する時、「VPC」、及び「サブネット」を作成し、その内部にEC2を建てるようにすることで、そのまま作成するよりもセキュアにEC2を作成することができます。

1つのVPCには複数のサブネットを配置することができます。

そしてこのサブネットですが、「プライベートサブネット」というものと「パブリックサブネット」というものがあります。

プライベートサブネットとパブリックサブネットの違い

この「プライベートサブネット」と「パブリックサブネット」には明確な役割の違いがあり、「インターネットから直接アクセスさせたくないもの」はプライベートサブネットに、「インターネットから直接アクセスさせたいもの」はパブリックサブネットに入れます。

例えば、外部からアクセスする必要のないバッチサーバなどはプライベートサブネットに、SSHなどで外部からログインしたい踏み台サーバなどはパブリックサブネットに配置する、といった具合です。

Webサービスであれば、多くの場合パブリックサブネットに配置するのは踏み台サーバやユーザアクセス用のALBくらいのもので、他はすべてプライベートサブネットに置くので、ほとんどプライベートサブネットばかり使うことになると思われます。

では、このインターネットからのアクセスの差はどこから生まれているのでしょうか?

NATゲートウェイとインターネットゲートウェイ

実はプライベートサブネットとパブリックサブネットの違いは、インターネットとの接続に「NATゲートウェイ(NAT GW)」を使っているか「インターネットゲートウェイ(IGW)」を使っているか、というものです。

パブリックサブネットで使われるIGWは、サブネット内のサーバ等に付与されるプライベートなIPアドレスグローバルIPアドレスを、1 : 1で紐付けることができます。

結果、インターネット側からパブリックサブネット内に存在するサーバ等を認識することができるのです。

一方でプライベートサブネットで使われるNAT GWは、NAT GW自体が持つグローバルなIPアドレスに、複数のプライベートIPアドレスを紐付けます。 つまり、グローバルなIPアドレスとプライベートなIPアドレスの対応が 1 : n となり、1つのグローバルIPアドレスで複数のサービスがインターネットの情報にアクセスできるようになるのですが、逆にインターネット側からは、個別のIPアドレスを識別することができません。

結果、インターネット側からはプライベートサブネット内に存在するサーバ等を認識することができず、インターネットから直接アクセスできないのです。

(ちなみにNAT GW自体はグローバルIPアドレスと 1 : 1 で紐付いている必要があるので、パブリックサブネット内に存在しています。)

この仕組みを利用することで、よりセキュアにAWSサービスを扱えるようになるというわけです。

最後に

ざっくりとプライベートサブネットとパブリックサブネットの差を紹介してみました。

クラウドでインフラを作成する時は、セキュリティに気をつけないと思わぬ攻撃を受けて取り返しのつかないことになるので、きっちりと考えて使っていきましょう。

5月度iXIT Tech MTGを開催しました!

こんにちは、iXITの渡邊です。
先日、iXIT Tech MTGを行いました。

iXIT Tech MTGとは、iXITのエンジニアが月一で、一堂に会するMTGです!
このMTGでは、メンバーの発信力を高める取り組みとして、昨年度からメンバーが持ち回りでLT発表を行っています。

5月度 LT紹介

5月のLT担当メンバーは秋山さんと武見さんでした。

LTのスライドを一部ご紹介させて頂きます。

武見さん:インターフェース(ハードウェアの方)を学ぼうぜ!

 

普段何となく使っている電子機器に差しているケーブル類、改めて見返すと色んな端子の種類があるんですね!

パラレル接続のインターフェースは最近中々見かけなくなりましたから、実物の写真と見比べながらの説明がとても分かりやすかったです。

会社から貸与されているノートパソコンにUSBCが付いてるのを最近気づいてamazonでUSBPDとtypeC買ったらめっちゃ電源軽くなってウッキウキ!

みんなにもおすすめ!

個人的に、これが一番ウキウキ情報でした。出社する時PCの電源ケーブルが地味にかさばるので私も試してみたいです!

 

秋山さん:古い環境でComposer用のライブラリを使いたい

前提条件

ライブラリを使いたい環境のPHPのバージョンが、 Composerの使用条件以上であること(5.3.2以上)

環境は案件の状況もあり、おいそれと動かせるものではないので、自身の案件でライブラリが動くかどうかちょっと手元で試してみたい時、ローカル環境だけで済む作業の効率化をしたい時には便利ですね!

以上、LTの紹介でした!

また、今回からLTの司会進行も技術部メンバーが交代で回していく試みをはじめました。
ファシリテーション力もメキメキ鍛えられますね…!

5月の司会トップバッターは池田さんです!
昨年度のエキサイトHD社内カンファレンス『excite×iXIT TechCon』でもバックエンドセッションの司会を務めた頼れる先輩です!

 

6月もiXIT Tech MTGでのLTを紹介予定です!次回もお楽しみに!

Docker ComposeでGraceful Shutdownのための時間をかせぐ方法

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

前回、ECS FargateとGraceful Shutdownに関する記事を書きました。

tech.excite.co.jp

今回はこの調査をしている時に直面した、Docker Composeで実行したコンテナの終了時、Graceful Shutdownが完了する前に強制停止されてしまう場合がある件について説明していきます。

Docker ComposeとGraceful Shutdown

上記の記事で書いたとおり、コンテナで動いているアプリケーションを安全に終了させるためにはGraceful Shutdownをすることが必要です。

すなわち、例えばWebサーバであれば、「コンテナ停止時に即時終了するのではなく、さばいているアクセスがすべて完了してから停止する」というような感じです。

Docker Composeでも、適切な終了シグナルを送信するようにすればちゃんとGraceful Shutdownを開始してくれるのですが、実は場合によっては途中でGraceful Shutdownが打ち切られ、強制終了されてしまう場合があります。

Docker Composeと stop_grace_period

Docker Composeには stop_grace_period という設定項目があります。

docs.docker.com

stop_grace_period specifies how long the Compose implementation MUST wait when attempting to stop a container if it doesn’t handle SIGTERM (or whichever stop signal has been specified with stop_signal), before sending SIGKILL. Specified as a duration.

Default value is 10 seconds for the container to exit before sending SIGKILL.

この項目は、「終了コードを送った後にこの項目に設定した時間が経過したら、Graceful Shutdownが完了していなくても強制終了する」というもので、デフォルトは10秒になっています。

すなわち、この項目に何も設定していない場合、Graceful Shutdownに10秒以上掛かってしまうアプリケーションでは、10秒の時点で強制終了されてしまうということです。

もし10秒以上時間がかかる可能性があるのであれば、適切な値をここに設定しましょう。

ECS Fargateと stopTimeout

実はこれに似たような設定が、ECS Fargateのタスク定義にも存在します。

docs.aws.amazon.com

stopTimeout

コンテナが正常に終了しなかった場合にコンテナが強制終了されるまでの待機時間 (秒)。

上記の通り、 stopTimeout という設定項目がそれに該当します。

ECS Fargateの場合はデフォルトが30秒となっていますが、こちらも必要に応じて適切な値に変更するのが良いでしょう。

{
    "family": "***",
    ...
    "containerDefinitions": [
        {
            "name": "***",
            "stopTimeout": 60,
            ...
        }
    ]
}

最後に

今回は、Graceful Shutdownを気にし始めると躓くかもしれないタイムアウトの設定値について説明しました。

Graceful Shutdownは、適切に設定しないとユーザが不利益を被る結果になることもあるので注意していきましょう。

プロジェクト/リポジトリごとにJVMのバージョンを切り替える

エキサイト株式会社メディア事業部エンジニアの佐々木です。

メディア事業部では、最近JVMを利用した開発が増えてきており、プロジェクトやリポジトリごとにバージョンが変わったりが起きています。これをなるべく自動で切り替える方法をご紹介します。

前提

メディア事業部では、SDKMANを使用して、Javaをインストールしております。Windows/Mac/Linuxで統一したコマンドを使用できるので管理が楽です。

設定ファイルの作成

設定ファイルを作成します。

$ sdk env init

コマンドを実行すると、下記ファイルが出来上がります。

# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.2-tem

sdkman_auto_envというパラメータを有効にすると、ディレクトリ遷移時に自動的に切り替わります。下記コマンド実行すると書き換わります。

$ sed -i -e 's/sdkman_auto_env=false/sdkman_auto_env=true/g' ~/.sdkman/etc/config

切替の確認

ディレクトリを移動すると、下記のようなメッセージがでます。

Using java version 17.0.2-tem in this shell.

正常に切り替わりました。

マシンインストールされていないJVMバージョンが指定されている場合

マシンにないJVMが指定されている場合は、インストールを促すようになります。

インストールされていないJVMバージョンを.sdkmanrcに記載します。

# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=18.0.1-amzn

記載後に、ディレクトリを移動すると下記のように警告をだしてくれます。

Stop! Candidate version is not installed.

Tip: Run the following to install this version

$ sdk install java 18.0.1-amzn

警告をだしてくれたので、インストールします。警告にあるコマンドを叩いてもいいんですが、コピペが面倒なので、.sdkmanrcに指定されているものをインストールするショートカットコマンドを実行します。

$ sdk env install

Downloading: java 18.0.1-amzn

In progress...

######################################################################## 100.0%

Repackaging Java 18.0.1-amzn...

Done repackaging...
Cleaning up residual files...

Installing: java 18.0.1-amzn
Done installing!


Using java version 18.0.1-amzn in this shell.

こんな感じでインストールが完了します。

まとめ

プロジェクトが多くなってくると環境問題がでてくるかとおもいます。Dockerを使うのも手ですが、こういった切替がほぼ自動でできるのは便利かと思います。

最後に

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。

カジュアル面談はこちらになります! meety.net

就業型インターンの募集もしております!

www.wantedly.com

募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com

ECS Fargate Spotでは、STOPSIGNALを変更できない

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

今回は、ECS Fargate SpotでGraceful Shutdownをしたい時に引っかかる可能性のある、STOPSIGNALに関する注意点を説明します。

ECS Fargate Spotとは

ECS Fargateは、AWSの提供するマネージドなコンテナ実行サービスです。

公式では、以下のように説明されています。

AWS Fargate は、サーバーレスで従量制料金のコンピューティングエンジンであり、サーバーを管理することなくアプリケーションの構築に集中することができます。

そしてECS Fargate Spotは、AWSの空きキャパシティをECS Fargateで使用するプランで、AWS側の都合で停止される可能性がある代わりに、通常に比べて大幅に安い料金となっています。

その料金の安さから、可能であればSpotを使いたいところですが、「AWS側の都合で停止される可能性がある」点を考慮する必要があります。

Graceful Shutdown

AWS側の都合で停止されても問題ないようにするには、停止時にGraceful Shutdownをすれば良いでしょう。


* 2022年8月8日追記

なお、ALBと接続してWebアプリケーション等として使用している場合は、Graceful Shutdownによる対応では問題が起きる場合があります。

詳細は以下にまとめてあります。

tech.excite.co.jp


Graceful Shutdownとは、端的に言えば「安全な停止」のことで、例えばWebサーバで言えば、「停止命令が来てもすべてのアクセスの処理が完了するまでは停止せず、完了後に停止するようにする」などです。

多くのサービスの場合、 SIGTERM というシグナルがコンテナに送られるとGraceful Shutdownが行われます。 また、ECS Fargate Spotも、停止の際はこの SIGTERM が送られるので、基本的には勝手にGraceful Shutdownが行われます。

ただし、たまに例外があります。

有名なところで行くと Nginx で、 Nginx をGraceful Shutdownするには SIGQUIT が必要となり、 SIGTERM では強制停止されてしまうのです。

では、ECS Fargate Spotで停止シグナルを変更するにはどうすれば良いでしょうか?

ECS Fargate Spotでの停止シグナル変更方法は存在しない

2022年5月23日現在、AWSのブログには、以下のような記載があります。

タスクが停止すると、ECS はそのタスク内の各コンテナに停止シグナルを送信します。デフォルトの停止シグナルは SIGTERM ですが、これは Dockerfile に STOPSIGNAL ディレクティブを追加することによってオーバーライドできます。この停止シグナルは、シャットダウンの命令をアプリケーションに通知します。

これに従うなら、Dockerfileに STOPSIGNAL を追記すれば停止シグナルは変更できそうですが、 実際はできません

このブログは日本語訳なのですが、原文の英語版を見てみると、以下のようになっています。

When a task is stopped, ECS sends each container in that task a stop signal. Today, ECS always sends a SIGTERM, but in the future you will be able to override this by adding the STOPSIGNAL directive in your Dockerfile and/or task definition. The stop signal informs your application that it is time to begin shutting down.

まとめると、「現在はECSでは常にSIGTERMが送られるが、 将来的には DockerfileのSTOPSIGNALやtask definitionあたりで変更できるようになる」となっています。

実際はこちらが正しく、現在はECSからは常に SIGTERM が送られてしまうのです。

では、 SIGTERM 以外のシグナルでGraceful Shutdownするアプリケーションでは、どのようにGraceful Shutdownをすればよいのでしょうか。

ECS Fargate Spotで、SIGTERM以外のGraceful Shutdownをする方法

実は、「コンテナが受け取ったシグナルを別のシグナルに変換する」というライブラリが存在します。

github.com

現状では、こういったライブラリを使用して、 SIGTERM を受け取ったら任意のシグナルに変換するようにすると良いでしょう。

そして将来的にAWS側でシグナルの変更に対応したら、そちらに寄せるのが良いかと思います。

最後に

Graceful Shutdownは、直接アプリケーションの動作に関わらないので見過ごされることもあるかもしれませんが、ちゃんと設定しないとユーザ側にエラーを発生させてしまう要因になり得ます。 注意していきましょう。

なお今回は停止シグナルに注目していますが、上記で上げたAWSのブログにも書いてあるとおり、ALBと接続している場合はそちらからの登録解除設定も考える必要があります。 ご注意ください。

Controllerでもinterfaceをimplementsすることができる

こんばんは。エキサイト株式会社の中尾です。

あまり需要がないかもしれないですが、Controllerでもinterfaceをimplementsすることができます。

ただ普通にimplementsすることは、どんなクラスでもできますが、@RequestMapping@GetMapping()@RequestParam()も使えます。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RequestMapping("hello")
public interface HelloController {
    /**
     * helloを返すエンドポイント
     *
     * @param hello
     * @return
     */
    @GetMapping()
    String hello(@RequestParam(name = "hello") String hello);
}
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloControllerImpl implements HelloController {
    @Override
    public String hello(String hello) {
        return hello + "\n";
    }
}

気をつけて欲しいのは@RestControllerは実装クラスにつけるということです。

まぁ、swaggerでも良いのですが

EnumをExtensionを使用し拡張することでStringを取得できるようにする方法

エキサイト株式会社の奥田です。 本日はTabViewを作成する際にEnumに紐付いたStringを取得する場合があったため、その方法について記述します。

なぜEnumに紐付いたStringを定義する必要があったか?

今回のケースではTabViewを作成する際にTabの名称をEnumに定義したTypeに紐付けたものにしたかったからです。 Enumの要素一つ、一つに対して紐づくものであり、取り回しがしやすいと感じて、今回のような実装にしました。

定義

それではまずEnum側の定義をしていきます。

enum SampleCategory {
  article,
  user,
}

extension SampleCategoryExt on SampleCategory {
  String get name {
    switch (this) {
      case SearchCategory.article:
        return '記事';
      case SearchCategory.user:
        return 'ユーザー';
    }
  }
}

定義したStringを呼び出す際は下記のように定義します。

final article = SearchCategory.article.name;
final user = SearchCategory.user.name;

print('${article}'); //'記事'
print('${user}'); //'ユーザー'

このように定義したEnumからextensionを使用し拡張することでStringを取得することができました。

まとめ

今回はStringを取得するシンプルな定義にしましたが、widgetやintなどを定義することができます。使用する場面に応じて使い分けることができるので、柔軟に使用することができそうです。 次回は本格的にTabViewの実装部分の記事を書いていこうと思います。

最後に

弊社では絶賛採用強化中です。もしご興味がある方がいましたら下記リンクよりアクセスいただけると幸いです。(カジュアルからもOKです)

www.wantedly.com

【世界初開催!】Config Watch Party参加してきた!【後編】

こんにちは!21卒デザイナーの山崎です! 2022年5月11日に開催されたfigma初のオフラインイベント「Config Watch Party」レポの後編になります!

↓会場の様子を紹介した前編や日本から登壇したスピーカーセッションを紹介した中編はこちらです↓

tech.excite.co.jp

tech.excite.co.jp

後編ではLT会の様子を紹介したいと思います!

LT会はConfig Watch Party参加者しか見れなかったと思うので、オンラインでConfigを視聴していた方はぜひチェックしてみてください👏

LT会「とっておきFigmaの活用策をシェアしよう!」

選りすぐりのFigmaヘビーユーザー数組が、とっておきのFigmaの使い方をシェアしてくれるLT会です!

ざっくりLT会内容をまとめてみました!

「みんな大好き!ショートカット!!!」

株式会社ベーシック・Brianさんが発表してくれたのはFigmaの便利なショートカットです!

shift+1

「Zoom to Fit」はデザイン全体を見渡せる機能。よくFigma内で「あのデザインどこにいったっけ…?」っと迷子になるのでこの機能は大変助かります…!👏

⌘+shift+C

「Copy as PNG」はPNGで勝手に書き出してくれる機能です!

Figmaで作ったデザインをSlackで共有するためにわざわざ書き出して…という作業をする方におすすめのショートカットになります🎉

ちなみに⌘+shift+Cを使えばこのはてなブログにも⌘+Vで画像貼り付けを行うことができます!すごすぎる…(Macと端末同期していればiPadにも貼り付けることができるそうです!)

⌘+Click

こちらは「Deep select」という「グループレイヤーの層に阻まれて一発でクリックできない…」というオブジェクトを一発でクリックできる機能です!

⌘+Click」を押した後に/」を押すと一つ上の親要素を選択できる機能も…!

詳しくはBrianさんのプレゼンをご覧ください👏

www.figma.com

プラグイン紹介「Pitchdeck」

登壇していただいたnarumiさんはFigmaプラグインについてお話ししてくれました! 「Pitchdeck」という「Figmaで作ったスライドにアニメーションが付けられる」プラグインをご紹介いただきました! 一つ一つのフレームにもアニメーションが付けられる便利なプラグインで、全く知りませんでした…!

www.figma.com

「figjamのサービス立案段階での活用方法」

Takako Watabeさんがお話ししてくれたのはサービス立案段階というアーリーフェーズでのFigjamの活用方法です!

週末に2時間ほど友人デザイナーとアプリ開発しているwatabeさんが、2時間という制約がある中でどのようにスピード感失わずにブレスト→ペルソナ・リサーチ→プロトタイプ制作をFigjamで制作したかをお話ししてくれました!

www.figma.com

Figmaを使ったロゴ作りのメリット」

Qiita株式会社でデザイナーをしている綿貫佳祐さんが発表してくれたのはFigmaでのロゴ制作についてです!

Figmaでのロゴ作りのメリットは以下の4点でした!

  1. 整数座標へのスナップの容易さ
  2. 1つのアンカーポイントから複数のパスが生やせる
  3. スムースコーナーが簡単に作れる
  4. リファレンスとの行ったり来たりが楽

ロゴを印刷する場合もあるので中々作る機会がないんですが、Webのみでの使用だったらFigmaでも作ってみたいなと思いました!

qiita.com

終わりに

いかがだったでしょうか?

最後に、エキサイトではデザイナー、フロントエンジニア、バックエンドエンジニア、アプリエンジニアを絶賛募集しております!

興味があれば連絡いただければと思います🙇‍♀️

それではまた!

www.wantedly.com

「第二回テクデザ総会」を開催しました!

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

2022年5月13日に、第二回目となる「テクデザ総会」を開催しました!

第二回テクデザ総会

ちなみに、第一回目の様子はこちらになります。

tech.excite.co.jp

テクデザ総会

テクデザ総会は、事業部間など、普段は接することがない距離感のエンジニア・デザイナーの間でのコミュニケーション活性化を目的として開催されています。

幸いにも前回が参加者から好評だったため、半年経って再度開催することとなりました。

今回は内容を第一回と一部変更しており、

  • 各事業部トピックス発表
  • 新卒発表
  • ブレイクアウトセッション
  • ディスカッション

という構成にしました。

各事業部トピックス発表

前回と同じく、各事業部の部長陣等に協力していただき、前回のテクデザ総会から今回までの間にあったトピックスを発表していただきました。 案外知らないこともたくさんあり、盛り上がりました。

新卒発表

前回との大きな違いの一つはここで、4月に入社した新卒エンジニアの皆さんに、技術研修で行った講義やハンズオン、研修として開発した検証端末の貸し出しツールの発表をしていただきました。 研修内容は、実際に講師やメンターとして関わっていない人はほとんど知る機会がなかったため、新卒の方の紹介も兼ねた良い時間になったと思います。

ブレイクアウトセッション

こちらは前回と同じく、Zoomのブレイクアウトルームの機能を使って参加者を四名程度のグループに分け、話し合ってもらいました。

全部で二回行い、一回目は10分程度で軽く自己紹介などをしていただき、二回目は「もっとコミュニケーションを活性化するにはどうすればよいか」という内容で20分議論していただきました。

ディスカッション

前回との大きな違いの二つ目がここで、二回目のブレイクアウトセッションである「もっとコミュニケーションを活性化するにはどうすればよいか」という議論について、全体から四チームを選出して、その代表の方々に実際にディスカッションを行ってもらいました。

各チームで出た案の内容は多種多様で、今後のコミュニケーション活性化に繋がりそうなとても良い機会になりました。

最後に

二回目となったこの総会も、ありがたいことに好評をいただきました。

もちろん単に実行するだけではなくこれを活かしていろいろな施策を行っていきたいですが、それと同時に、半年に一回のスパンで総会も続けていきたいところです。

【世界初開催!】Config Watch Party参加してきた!【中編】

こんにちは!2年目デザイナーのSaaS事業部のかじもとです!

2022年5月11日に開催されたfigma初のオフラインイベント「Config Watch Party」に同期デザイナーの山﨑さんと参加したので、そのレポートをお届けします😉

中編では、日本から登壇した4名のスピーカーのセッションについてさっくりご紹介します。

会場の雰囲気やグッズについては前編記事をご覧ください〜!

tech.excite.co.jp

①インハウスデザイナーが非デザイナーとスムーズに仕事をする秘訣

Watch Party最初のスピーカーは、楽天グループ株式会社でUXデザイナーをされている木原 朝美さんによるセッション。

スライドもFigmaのコミュニティにアップされていたのでぜひご覧ください!

www.figma.com

楽天フリマアプリのラクマを担当されているインハウスデザイナーでもあり、普段はプロジェクトのキックオフからリリース前のテストまで担当なさるそうです。

このワークフローの中で非デザイナーとのコミュニケーションがあり、これまでであれば、各フローに関わる人が個別のフォーマットでファイルを作成しているため、個別にコミュニケーションを取らざるを得ませんでした。
しかし、複数人でFigmaを運用したことで、早い段階でのファイル共有やフィードバックが可能になり、爆速コミュニケーションを取れるようになり驚いた、と体験談を語っていました🙌

さらにVariant機能を使うことで、Figma上にノンデザイナー向けのデザインツールを作成したことや、Figma実用ならではのおすすめ機能なども紹介いただき、弊社デザインでも取り入れたくなるTipsが多く素敵でした✨

プラグインによるFigmaのハックとワークフローの拡張

2人目スピーカーの谷 拓樹さんはUbie株式会社でデザインエンジニアをしており、欲しいと思ったプラグインを自ら作ってしまうほどのFigma愛😳
今回のセッションでもプラグインについて丁寧に話していただきました〜!
www.figma.com

私自身も業務でプラグインを使いますが、全く知らなかったものやおすすめのプラグインも多数ご紹介いただきました🥳

個人的に「ためになった〜!」と思ったTipsはコードの読み方。FigmaのPagesやアートボード・Frameそれぞれにもきちんと名前がつけられており、それをうまく使いこなせばハックした状態「ツールとして使うだけじゃないFigma」にもできちゃうんですね…🤭
この辺はエンジニアならでは…な視点ですね💃

Figmaのスライドにdemoの動画や、コードのサンプルなどのリンクがあるので、気になった方はぜひ見てみてください…!
(弊社エンジニアもぜひみて欲しい🥺そして教えていただきたい🥺🥺)

③開発者に愛されるFigmaのデザインの作り方

実装という視点でセッションいただいたのは、株式会社カケハシでフロントエンドとデザインを担当されている関 憲也さん。
普段はUIを作りそのままフロントまでされるとのことで、開発者目線で「デザインの意図を汲み取りやすく」「メンテナブルなデザイン」のススメを語っていただきました。

こちらもCommunityにスライドが共有されていたのでぜひご覧ください✨(日本語版も作っていただいているようです…神!)

www.figma.com

デザイナーとエンジニアの間で「意図」をうまく伝えられないと、思ってもいなかったものにつながってしまいます。私自身もFigma上でUIを作ってエンジニアに渡す工程を経験しましたが、意図が伝わらなかったり不備が多く困らせてしまった経験があります…
そういった意図の伝え漏れ・伝言ミスを防ぐためにも、意図を伝えるためのTipsをご紹介いただきました👏

特に印象に残ったのは、「デザインとコードを同期させる」こと。そもそもFigmaはコードの概念と似た部分が多く、作り方に気をつければコードのようなレイヤーを組めてしまうのです…!さらに、コードの概念に沿った作りにすることで、デザイナーの考えていた意図をエンジニアが分かる共通の言語に変換できるので、ミスを減らせるようになります。

また、エンジニア界隈では有名(?)な、負の遺産清算する「負債デー」も教えていただきました。これは事業部でも取り入れたい所存…😭

④複数ブランドのための一つのデザインシステム

Watch Party最後のスピーカーは、GMOペパボ株式会社でシニアデザイナーの福嶋 瞭さん。SUZURIやminneといった複数のブランドを、共通のデザインシステムで管理しており、そのメリット・デメリットや、具体的な設計手法についてセッションくださいました!🎉

www.figma.com

ペパボさんでは、ブランドの世界観を表現する手法としてデザインシステムを活用しており、デザインシステムのトークンを差し替えることで各ブランドを表現しつつ、共通のデザインシステムを成り立たせているそうです。

「複数ブランド」と聞き、エキサイトも複数事業・複数サービスを抱えているため、共感の頷きが止まりません…😭
かなりシステマティックなので下準備が必要ですが、こんな解決方法もあるのか…!と勉強になりました。

また、共通基盤のデザインシステムを作る上で重要なこととして「意味を先に決めること」を挙げていました。見た目としては同じボタンを、色違いでYes/Noの2種類を作った場合、色の種類が少ないブランドでコードにすると同じ意味となってしまいます。それを回避するためにも、きちんと意味を定義しておくのがコツになるそうです。

セッションを聞いてみての感想

ここまで読んでみた方はお気づきかもしれませんが、スピーカーのうち3名は技術にまつわるお話をしていました…!デザインを組み立てたりコミュニケーションを深めるツールでありながら、開発者に渡すためのバトンにもなるFigma。強すぎる…👏👏

アップデートによりさらに進化していて、これからもプロジェクトの中で大活躍してくれそうですね!私自身もコードなど分からずでしたが、これを機にちょっとづつ勉強してみようと思います👩‍💻

終わりに

初のオフラインイベントで大ボリュームだったので、少し長くなってしまいました💦
後編では、ここで書ききれなかったWatch Partyの内容をお伝えします🥰

tech.excite.co.jp

最後に、エキサイトではデザイナー、フロントエンジニア、バックエンドエンジニア、アプリエンジニアを絶賛募集しております!

興味があればぜひぜひ連絡ください〜〜!🙇

www.wantedly.com

【グラフィックデザイン】TDC展2022に行ってきた!

こんにちは!21卒の山﨑です!

今年4月に開催された「TDC展2022」に行ってきたので、そのレポをまとめました!

TDC展って何?

佐藤卓佐藤可士和など日本を代表するデザイナーが所属する「東京タイプディレクターズクラブ(通称TDC)」が主催のグラフィックデザインの国際賞「東京TDC賞2022」の成果を見る事ができる展覧会です。

tokyotypedirectorsclub.org

TDC展の様子

TDC展はggg(ギンザ・グラフィック・ギャラリー)で開催されています!

国内1788作品、海外1856作品、あわせて3644作品の応募から11の受賞作品、77のノミネート作品を含む514作品が入選し、今年秋に出版されるデザイン年鑑に掲載されます。

ネット越しに見るよりも実物を見たほうが、印刷の細かな違いを見ることができて最高です👏

「ベースを白・メインの赤の面積を広く使って青を差し入れに入れるとポップで爽やかな感じになるな〜」とか「漢字ってこんなかっこいい使い方できるんだ」など勉強になる事が沢山ありました😊

いい作品をインプットしないと業務のアウトプット領域が広がらないので、これからも展示会には積極的に足を運びたいと思います!

終わりに

最後に、エキサイトではデザイナー、フロントエンジニア、バックエンドエンジニア、アプリエンジニアを絶賛募集しております!

興味があれば連絡いただければと思います🙇‍♀️

それではまた!

www.wantedly.com

【世界初開催!】Config Watch Party参加してきた!【前編】

こんにちは!21卒デザイナーの山﨑です!

今回は世界初Figmaコミュニティイベント「Config Watch Party」に21卒デザイナー・鍜治本と参加したので、開場のレポを前編・中編・後編に分けてお伝えします!

前編では会場の様子・グッズなどをざっくりご紹介します👏

カンファレンスの様子

会場は品川の「WHAT CAFE」で開催されました!

開催日は平日だったのですが、開場前から多くの人が集まりFigmaの盛り上がりを改めて実感しました…

検温・消毒の後に自分の名札を書き、係の方からFigmaグッズを受け取り入場します!

室内は椅子のみとテーブル席で分かれていて、テーブル席ではPCで作業しやすくなっております👏(テーブル席の写真を撮り忘れていました)

Configを聴きながらメモをとったりしていたので、とてもありがたいです…

Config Watch Partyの間にLT会やFigmaの中の人に質問するコーナーもあり、とても充実+学びになった時間でした!

他社のFigmaデザインガイドライン運用なども聞く事ができ、エキサイトで今後Figmaどのように運用していくかの指針において、とても参考になりました!(詳細は中編・後編にて!)

新機能発表

Figmaの新機能発表もアツすぎます👏

↓今回発表された新機能はこちら↓

  1. ダークモード
  2. 改善されたオートレイアウト
  3. コンポーネントプロパティ
  4. スポットライト
  5. FigJamの新しいウィジェット(JIRA/ASANA/Github)
  6. 可変 (Variable)フォント
  7. スプリングアニメーション
  8. 個別のストローク

個人的にAuto layoutのオブジェクト重ねる機能と「可変 (Variable)フォント」でフォントの微調整が可能になったのが嬉しいです…😭

Figmaの中の人に聞く!」コーナー

オフライン会場では「Figmaの中の人」である山下さん・川延さんに質問できるコーナーが…!

今回質問と回答をいくつかピックアップしてみました👏

【質問】今後個人プランは有料になる?

【回答】その予定はありません!

 

【質問】デザイントークンの置き換え機能は考えていますか?ブランドが複数あって同じコンポーネントを使いたい場合、プラグインを使えばできるけど、公式でできる?

【回答】考え中です!できるようになるかも…?

 

【質問】FigmaファイルのiPad対応はどうなる?

【回答】FigmaはReadのみだけど、Figjamは対応しているよ!なるべく早く頑張る予定だけど、仕組みが複雑なのでまだかかるかも…しばしお待ちください!

 

【質問】ブランチ機能って本当に必要なの?実際どのくらい使われてる?

【回答】ユーザーの意見的には多くて、会社カルチャーによりけりだけど使ってもらえてます!元々はマイクロソフトのような大きい企業からのリクエストで、デザインシステムの管理が複雑な場面で活用してもらうために搭載したよ。 ブランチ機能は、ブランチを作ったことによって自由に編集できる部分に強みがあると考えているよ

 

【質問】コンポーネントのプロパティ機能ってvariantとの使い分けはどうするの?

【回答】平行に使えるようになっていくし、両方使っていって欲しい機能だよ。 例えばボタンをコンポーネントにしたときに、variantだと何十種類も作っていたけど、アイコンの有無なんかをプロパティ機能で変えて使うイメージかな

 

【質問】Figmaのオフラインイベントはどうなりそう?

【回答】もっとやりたいしやろうと思う!今日のイベントもこんなに来てもらってるし、会社に持ち帰って議論します!

かわいいグッズや美味しそうなご飯も!

続いてはカンファレンスを彩るグッズ、ランチをご紹介します!

可愛すぎるConfigグッズ

今回「Config Watch Party」に当選した100名限定で配布されたConfigグッズはこちらです!

ピンバッチ・ステッカー・帽子など非常に豪華なラインナップでした!すごすぎる…😭

ご飯

飲み物やご飯もご用意していただきました!

スパイスチキン・エビ・ヴィーガンメニューと3種類ほどメニューがあり、多様性に配慮されたご飯でした👏

最後に

登壇者の発表、LT会など盛りだくさんなイベントでした!

詳しい内容については中編・後編でお伝えする予定です😊

↓中編↓

tech.excite.co.jp

↓後編↓

tech.excite.co.jp

最後に、エキサイトではデザイナー、フロントエンジニア、バックエンドエンジニア、アプリエンジニアを絶賛募集しております!

興味があれば連絡いただければと思います🙇‍♀️

それではまた!

www.wantedly.com

DateTimeFormatでは、JSONで受け取った日付をうまく取得できない場合があるという話

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

Javaで日付形式の文字列を受け取る場合、 DateTimeFormat を使うことが多いと思います。 ですが実は、JSONで日付を受け取ろうとするとうまく行かない場合があります。

今回は、JSONで日付を受け取るにはどうするべきかを説明します。

JSON以外から、Javaで日付を受け取る方法

まずはGETリクエストで日付を受け取ることを考えてみます。

package sample;

import lombok.RequiredArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;

@RequiredArgsConstructor
@RestController
@RequestMapping("sample")
public class SampleController {

    @GetMapping
    public String sample(@ModelAttribute SampleRequestModel sampleRequestModel) {
        return sampleRequestModel.datetime.toString();
    }

    public record SampleRequestModel(
            @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime datetime
    ) { }
}

このコードのように DateTimeFormat を使い、

http://localhost/sample?datetime=2022-01-01 00:00:01

このアクセスをすれば、

2022-01-01T00:00:01

こんな感じのレスポンスが返ってくるため、問題なく文字列の日付を受け取れていることがわかります。

JSONから、Javaで日付を受け取る方法

一方で、例えばどこかのAPIへアクセスし、レスポンスとして受け取ったJSONに日付が混ざっていた場合を考えましょう。

package sample;

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.LocalDateTime;

@Data
public class SampleResponseModel {
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    LocalDateTime datetime;
}

こんなモデルで、

{
    "datetime": "2015-02-24 15:00:00"
}

このJSONを受け取ることを想定します。

先程のGETリクエストを考えると問題なさそうですが、実は実行すると

JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String \"2015-02-24 15:00:00\"

こんなエラーが起きてしまいます。

どうやら今回の日付のような、間にスペースが入っている日付は、JSONから受け取ろうとすると DateTimeFormat を使った場合エラーになってしまうようなのです。

ではどうするかというと、 JsonFormat を使えば問題ありません。

package sample;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.time.LocalDateTime;

@Data
public class SampleResponseModel {
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    LocalDateTime datetime;
}

これで、無事受け取れるようになります。

終わりに

受け取る形式によって、同じデータでも微妙に必要な処理が変わってくる場合があります。 注意しましょう。

【大ピンチ】FigmaのTeamを削除した時の復活方法

こんにちは。21卒デザイナーの山崎です。

今回は「FigmaのTeamを削除した時の復活方法」について書きたいと思います。

FigmaのTeamって何?

Figmaには「Team」「Project」「File」と大きく3つの階層があります。 基本Teamは各サービスごとに作られています。

Teamは重要なものなので、そのTeamのadmin権限を持っていないと削除できません。

削除方法

  1. Teamを右クリック
  2. 「Delete team」を選択
  3. 記入画面にTeam名を入力
  4. 削除

Teamsを削除した場合の復活方法

Teamの復活させ方は、Teamを削除するときの文言に記載されています。 「この操作を取り消すには、28日以内に電子メールに記載された指示に従ってください。」

Figmaからきたメールの赤枠の部分をクリックすると、削除したTeamsが復活します!

終わりに

最後に、エキサイトではデザイナー、フロントエンジニア、バックエンドエンジニア、アプリエンジニアを絶賛募集しております!

興味があれば連絡いただければと思います🙇‍♀️

それではまた!

www.wantedly.com

Spring Bootにおける、おすすめRedis設定方法

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

以前こちらの記事で、Spring BootのRedisキャッシュで List.of などを使う方法について説明しました。

tech.excite.co.jp

今回はそれも含んだ、Redisキャッシュのおすすめ設定方法を紹介します。

なお実は、弊社の中尾さんが以下のブログで同様におすすめ設定方法を紹介してくれています。

tech.excite.co.jp

今回は、これに更に詳しい説明を加えたり、一部設定を変えたものとなります。

Redisキャッシュのおすすめ設定方法

結論から言うと、以下のような設定になります。

キャッシュキー一覧

package sample;

import lombok.Getter;

import java.util.concurrent.TimeUnit;

public enum CacheKeyType {
    SAMPLE_1(SAMPLE_1_KEY, TimeUnit.MINUTES.toSeconds(1)),
    SAMPLE_2(SAMPLE_2_KEY, TimeUnit.MINUTES.toSeconds(1)),
    SAMPLE_3(SAMPLE_3_KEY, TimeUnit.MINUTES.toSeconds(1)),

    @Getter
    private final String key;

    @Getter
    private final Long ttl;

    CacheKeyType(String cacheName, Long ttl) {
        this.key = cacheName;
        this.ttl = ttl;
    }

    public static final String SAMPLE_1_KEY = "sample1Key";
    public static final String SAMPLE_2_KEY = "sample2Key";
    public static final String SAMPLE_3_KEY = "sample3Key";
}

使いやすいよう、キー名とTTLをまとめてEnumで管理しています。

Redis設定

package sample;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.lettuce.core.ReadFrom;
import sample.CacheKeyType;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.Arrays;
import java.util.stream.Collectors;

@Configuration
@EnableCaching
@RequiredArgsConstructor
public class RedisConfig {
    private final RedisProperties redisProperties;

    /**
     * Redis用のプロパティ
     * ここでは、application.ymlからデータをとってくるようにしています
     * @param primary プライマリエンドポイントのプロパティ
     * @param reader リーダーエンドポイントのプロパティ
     * @param database データベース番号
     */
    @ConfigurationProperties(prefix = "spring.redis")
    public record RedisProperties(Endpoint primary, Endpoint reader, Integer database) {

        /**
         * Redisエンドポイント用のプロパティ
         * @param host ホスト名
         * @param port ポート番号
         */
        public record Endpoint(String host, Integer port) { }
    }

    /**
     * カスタマイズした GenericJackson2JsonRedisSerializer を取得する
     * - List.of 等の Unmodifiable collection をキャッシュで使用可能にする
     * - LocalDateTime 等を、各プロパティでの Serializer/Deserializer の指定なしにキャッシュで使用可能にする
     *
     * <p>
     * NOTE: GenericJackson2JsonRedisSerializer.registerNullValueSerializer(objectMapper, null) について
     * disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) の設定に必要だが、
     * DefaultTyping.EVERYTHING では一部処理を変更する必要があり、現状上記設定はしないのでここでは設定しない
     * spring-data-redis が ver.2.7 以降であれば設定されているようなので、アップデートすれば設定しても問題ない
     * </p>
     *
     * @see <a href="https://github.com/spring-projects/spring-data-redis/pull/2237">Switch Jackson default mapping default from NON_FINAL to EVERYTHING</a>
     *
     * @return カスタマイズした GenericJackson2JsonRedisSerializer
     */
    private GenericJackson2JsonRedisSerializer serializer() {
        ObjectMapper objectMapper = new ObjectMapper()
                .registerModule(new JavaTimeModule()) // これを設定することで、これ以外の設定なしに日付系データをキャッシュできるようになります
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // キャッシュ時の日付データのフォーマットを見やすくします。少なくとも LocalDateTime においては、なくても機能としては問題ありません
                .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) // キャッシュ側に、Javaモデルでは存在しないプロパティがある(Javaモデル側のプロパティを減らしてデプロイしたなど)場合でもエラーが起きないようにします
                .activateDefaultTyping(
                        LaissezFaireSubTypeValidator.instance,
                        ObjectMapper.DefaultTyping.EVERYTHING, // finalなモデルでもキャッシュ時に型情報がRedisに保存されるようにします。これにより、 `List.of` 等もキャッシュに使用できるようになります
                        JsonTypeInfo.As.PROPERTY
                );

        // GenericJackson2JsonRedisSerializer.registerNullValueSerializer(objectMapper, null);

        return new GenericJackson2JsonRedisSerializer(objectMapper);
    }

    /**
     * Redis接続用設定のFactory
     * Primary / Secondary の接続設定をします
     * @return Lettuce接続用設定のFactory
     */
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
                .readFrom(ReadFrom.REPLICA_PREFERRED)
                .build();

        RedisStaticMasterReplicaConfiguration serverConfig = new RedisStaticMasterReplicaConfiguration(
                redisProperties.primary().host(),
                redisProperties.primary().port()
        );
        serverConfig.addNode(redisProperties.reader().host(), redisProperties.reader().port());
        serverConfig.setDatabase(redisProperties.database());

        return new LettuceConnectionFactory(serverConfig, clientConfig);
    }

    /**
     * キャッシュ用のReactiveRedisTemplate設定を追加
     * キャッシュのキー・バリューのシリアライズ方法を指定します
     * @param factory ConnectionFactory
     * @return キャッシュに使用するReactiveRedisTemplate
     */
    @Bean
    public ReactiveRedisTemplate<String, Object> reactiveRedisTemplate(LettuceConnectionFactory factory) {
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        RedisSerializationContext.RedisSerializationContextBuilder<String, Object> builder = RedisSerializationContext
                .newSerializationContext(keySerializer);
        RedisSerializationContext<String, Object> context = builder.value(this.serializer()).build(); // バリュー側で、カスタムしたシリアライザを使用するようにします

        return new ReactiveRedisTemplate<>(factory, context);
    }

    /**
     * Redisでのキャッシュ時、カスタムタイプを使って保存できるようにBuilder設定を追加
     * 先に設定したキャッシュのキー・TTL一覧を使ってキャッシュするようにします
     */
    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
        return builder -> {
            var map = Arrays.stream(CacheKeyType.values())
                    .collect(
                            Collectors.toMap(
                                    CacheKeyType::getKey,
                                    e -> RedisCacheConfiguration
                                            .defaultCacheConfig()
                                            .prefixCacheNameWith("prefix:") // profile名などを持ってくると、環境ごとにprefixを付けられます
                                            .entryTtl(Duration.ofSeconds(e.getTtl()))
                                            .disableCachingNullValues()
                                            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
                                            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(this.serializer())) // バリュー側で、カスタムしたシリアライザを使用するようにします
                            )
                    );
            builder.withInitialCacheConfigurations(map);
        };
    }
}

設定です。 細かくはコメントで書いてあります。

使用方法

public class SampleImpl implements Sample {

    @Override
    @Cacheable(cacheNames = CacheKeyType.SAMPLE_1_KEY)
    public String getSample() {
        // ....
    }

使用したいメソッドに @Cacheable を付け、キャッシュ名として先に作成していたキー名を指定します。 これにより、そのキー名と抱き合わせられているTTLでキャッシュしてくれます。

終わりに

キャッシュ周りはよく使うものなので、良い感じの設定にしておきたいものです。

今の所この設定では、(キーが増えると管理が大変になること以外は)特に不便は無いので、よければ参考にしてもらえれば幸いです。