TECH PLAY

ニフティ株式会社

ニフティ株式会社 の技術ブログ

517

はじめに こんにちは。宮永です。 友人が勉強し始めるというのでノリで受験してきました。申請書取り寄せから免許申請まで一連の体験談を書いていきます。 先に白状すると、8時間勉強で合格したのは私の事前知識が関係しています。勉強面以外の工程が他の資格と比べてかなり特殊だったので、これから受験する人の参考になればと思います。 ※2024年4月から受験申請等の手続きが色々変わったようです。受験会場が追加された・電子申請ができるようになったらしいですが、あくまで私の体験談として読んでいただければと思います。 衛生管理者とは 職場において労働者の健康障害を防止するために、一定の人数以上がいる事業場に対して有資格者を選任しなければならないと定められています。その資格が衛生管理者免許です。 法定の有害業務を扱う事業場も担当できる第一種衛生管理者と、有害業務を除く業務を扱う事業場を担当できる第二種衛生管理者があります。今回は第二種衛生管理者を受験しました。 色々条件はありますが、常時50人以上の労働者がいる事業場には少なくとも1人以上の衛生管理者がいるはずです。思ったより身近な資格です。 受験資格 第一種・第二種共に受験資格が決まっています。 労働衛生の実務に従事した経験 が必要で、必要な年数は学歴によって違います。私は「大卒後、1年以上の実務」という受験資格を利用しました。ほかにも10パターン以上ありますが、どれも実務経験が必要です( HP参照 )。 受験申請時に 労働衛生の実務に従事した経験 があることの証明書を事業者に書いてもらい、それを提出します。つまり自分で適当に経歴があったことにはできません。 労働衛生の実務 にどのような業務が当たるかは 既定の証明書 に書いてあります。該当するか分からない場合は事前に試験センターへ問い合わせると良いそうです。 受験申請書取り寄せ 衛生管理者試験は受験申し込みが 郵送申請のみ で、既定の受験申請書を郵送で取り寄せる必要があります。請求方法の案内に従って取り寄せます。封筒に必要部数や住所のメモ、切手を貼った返送用封筒を入れて送ります。 令和とは思えないアナログスタイル です。 投函から1週間で受験申請書が届きました。冊子になっており、申請書・事業者証明書・受験料払込用紙・受験申請と試験当日の注意事項などがすべてまとまっています。HPに書いていない情報もすべてまとまっているので、受験を完全に決めてない場合でも受験申請書を先に取り寄せてしまう方が良いと思います。 1通のみ取り寄せにすると書き損じや再受験するときに再度取り寄せる必要が出てきます。面倒なので私は2通取り寄せました。 ※ブログ執筆時に協会HPを見たところオンライン申請が追加されたようです。どちらにしろ郵送が必要なようですが、少しは便利になったみたいです。 受験申請(必要証明書の準備) 先述の通り受験資格が決まっており、受験資格を満たすことの証明書を受験申請時に添付する必要があります。私の場合は 大学の卒業証明書 労働衛生の実務に従事した経験についての事業者証明書 を準備しました。私は大学→大学院→就職という経歴で、大学院在籍中にアルバイトをしていたので、大学院在籍中のアルバイト経験で受験資格を満たせます。元アルバイト先に頼み込んで事業者証明書を書いてもらいました。 申請書は冊子に例があるのでそれに従って書きます。受験会場は選択肢がなく、関東在住であれば千葉にある関東安全衛生技術センターです(2024年4月から東京試験場が追加されたようです)。月5~6回ほど試験が行われています。HPに 試験の申し込み状況が見れるページ があるので、先に確認したほうが良いです。 受験料払込用紙の控えと顔写真を受験申請書に貼り、郵送します。 投函から1週間で受験票が届きました。 勉強方法 第二種衛生管理者の試験科目は 労働衛生(有害業務に係るもの以外のもの) 関係法令(有害業務に係るもの以外のもの) 労働生理 の3科目です。各科目40%以上かつ合計60%以上で合格です。 第一種衛生管理者は上記に加えて 労働衛生(有害業務に係るもの) 関係法令(有害業務に係るもの) が追加されます。科目ごとの問題数が若干違いますが、科目の範囲は変わりません。 また、第二種衛生管理者を持っている人が第一種衛生管理者を受験する際は、差分の科目のみ合格すればよいという制度もあります。完全上位互換です。 先述の通り、第一種と第二種は科目数の違いだけなので、テキストは第一種衛生管理者のものを選びました。第二種に比べて第一種のほうがテキストが充実しているためです。 まずは要点がまとまっているテキストを使って試験範囲全体を拾っていきました。範囲は広いですが試験に出るポイントは9割決まっているので、テキストが大事と言っているポイントを確実に覚えていきます。 ある程度知識が付いたら過去問周回に入りました。月に3~4回行われている試験という事もあり、半分以上が過去問から出題されます。なので、過去問をひたすら繰り返して完ぺきにしました。 総勉強時間は約8時間でした。というのも、つい数年前まで塾講師のアルバイトをしており、中学生で習う理科の内容を完ぺきに覚えています。突然中学校の定期テストを渡されても満点を取る自信があります。 出題範囲には中学生の理科で扱うような内容も多く、追加で覚えなくて良い内容が多かったため、短時間の勉強で合格できました。消化酵素の名前や役割・心臓の部屋の名前などを憶えている方は比較的簡単に勉強できるかと思います。 試験会場へのアクセス 試験会場は多くなく、関東在住であれば千葉にある関東安全衛生技術センターに行くことになります。 …この関東安全衛生技術センター、 お世辞にも便利な場所とは言えない 所にあります。東京駅から約1時間ほどの所にあるJR五井駅から、試験日のみ運行している乗り合いバスで20分です。 私は面倒だったので車で行ってしまいました。無料駐車場はありますが受験者に対して少ないです。平日でも 試験開始1時間前には満車 になって止められない人が出てきていました。車で行く際はかなり早めに行くことをおすすめします。 また、会場の周りにはコンビニ等が一切ないです。食べ物飲み物は持参したほうが良いです。 試験 試験は学校の教室を広くしたような部屋でした。いわゆる「小学校みたいな机と椅子」で、荷物を置くスペースが少なかったです。空調はちゃんとしており、暑くも寒くもない環境でした。 一部屋に100人ほどおり、年齢層は30~40代が多く、男女比はバラバラでした。 カンニングについて厳しめで、消しゴムの裏、受験票、電卓に書き込みが無いかしっかりチェックされました。試験開始前と退出時のダブルチェックです。疑わしいものは持って行かないほうが無難です。 試験時間は3時間あるのですが、私は20分で解き終わりました。過去問周回していれば見たことある問題ばかり出てくるので一瞬で終わります。1時間経過すると途中退出できるので、何度も解きなおしをして途中退出します。半分以上の人が1時間経過時点で途中退出するので、時間に追われる試験ではないです。 帰る際に、 免許発行の必要書類を持って帰る必要があります 。受付のような場所に大量に置いてあるので取って帰ります。忘れると郵送で取り寄せる必要があるそうなので、合格した自信がなくても持って帰ったほうが良いです。 結果発表・免許申請 試験から1週間後、HPで合格者の受験番号一覧が掲載されます。その数日後に合格通知書が郵送されます。 試験時に持って帰ってきた免許申請用紙を記入し、収入印紙を貼り、合格通知書の原本を同封して郵送します。 2週間後に免許が届きました。これで晴れて衛生管理者と名乗ることができます。 かかった費用 受験料 8800円 他手数料(免許申請など)2000円 切手代 2192円 テキスト代 3190円 交通費 約5000円 合計 約20000円 母数の大きい試験なので、受験料やテキストは良心的な値段です。証明書取り寄せ等いろんな場面で郵送をつかうので、家の切手がものすごい勢いでなくなりました。 まとめ 特に意味もなく受験した資格の割に準備や申請が大変だったのでブログにしてみました。受験者の多い試験なので、この体験談が誰かの助けになれば幸いです。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
ニフティのN1! Machine Learning Product Engineer 中村です。 最近はRustを書いていて、TerraformとRustの組み合わせでの知見がネット上にないなと思ったので書き残します。 TerraformでLambdaのデプロイを完了させる 業務利用からプライベート開発に至るまで、自分自身はLambdaのデプロイは多くをTerraformで完結させています。 Terraformにアプリケーションコードを含めたくないという声があるのは知っているのですが、Lambdaはちょっとしたコードを書くことも多く、特にプライベートの開発などにおいてはLambda1個をデプロイするためにCI/CDを整備したりするのは大袈裟すぎるなと考えています。そこで、自分自身はLambdaのアプリケーションコードもインフラの一部だと考えて、terraform applyのみでデプロイまで完結させるようにしています。 今回はその手順を記します。 ここからの手順は以下のレポジトリにコードを残していますので、こちらも参照してください。 inakam/cargo-lambda-terraform-sample Contribute to inakam/cargo-lambda-terraform-sample development by creating an account on GitHub. github.com Cargo LambdaでRustコードを準備する Rustのコード管理にはCargo Lambdaを用います。 cargo-lambda/cargo-lambda: Cargo Lambda is a Cargo subcommand to help you work with AWS Lambda. Cargo Lambda is a Cargo subcommand to help you work with AWS Lambda. – cargo-lambda/cargo-lambda github.com LambdaにRustコードをデプロイするためには、クロスコンパイルなどで適切な設定をする必要があるのですが、cargo-lambdaはそれらの設定をやりやすくしてくれます。 今回はTerraformでデプロイするので、実はcargo-lambdaを使う必要性は薄い(自前でRustのコンパイル設定などを調整すればそれでもデプロイ可能)のですが、プロジェクトのセッティングなどでも手間が少なくなるので、cargo-lambdaを使用します。 cargo lambdaのインストール Macの場合は以下でcargo-lambdaをインストールします。 brew tap cargo-lambda/cargo-lambda brew install cargo-lambda そのほかのシステムにおいては、以下を参考にインストールしてください https://www.cargo-lambda.info/guide/installation.html cargo lambda new cargo-lambdaを使ってLambda関数のテンプレートを作成します。 cargo lambda new --http rust-lambda 今回はAWS Lambda Function URLsを使いたいので、HTTP functionを統合するようにしてテンプレートを作成します。(lambda_httpが使える状態でテンプレートが作成されます) ビルドしてみる 作成されたフォルダに移動して、プロジェクトをビルドしてみましょう。 cd rust-lambda cargo lambda build --release --arm64 コードのコンパイルが無事に完了すれば、コードの準備は完了です。 Finished release [optimized] target(s) in 30.26s TerraformでLambdaをデプロイする準備をする 次にTerraformを整備していきます。今回想定しているフォルダ構成は以下のようになります。 . ├── rust-lambda │   ├── Cargo.lock │   ├── Cargo.toml │   ├── src │   │   └── main.rs │   └── target └── terraform ├── lambda.tf ├── lambda_archive ├── modules │   └── lambda_rust_module │   ├── main.tf │   ├── outputs.tf │   └── variables.tf ├── outputs.tf └── variables.tf 先ほどcargo-lambdaで作成したrust-lambdaフォルダと同じ階層にterraformフォルダを作成し、そこから terraform apply を行うことでデプロイできるようにします。 Terraformの設定 それではterraformフォルダ内にファイルを作成し、インフラの設定を行なっていきます。 以下のようにTerraformを設定します。S3 Remote Stateなどが必要であれば適宜設定を追加します。AWSのプロファイルはローカルマシンなどに設定されているプロファイル名を指定してください。 terraform/variables.tf provider "aws" { region = "ap-northeast-1" shared_credentials_files = ["~/.aws/credentials"] profile = "[AWSのプロファイル名]" } terraform { required_version = ">= 1.0.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } variable "name" { default = "rust-lambda" } Lambdaのモジュールを作成する RustでLambdaにデプロイするためのモジュールを作成します。このようにモジュールを作成しておくことで、Lambdaごと分離したい・Lambdaを別の用途で増やしたいという場合に手軽に増やすことができて便利です。 modulesというフォルダの中にlambda_rust_moduleフォルダを作成し、以下の記述をします。 terraform/modules/lambda_rust_module/variables.tf variable "function_name" { description = "The name of the Lambda function." type = string } variable "role" { description = "The ARN of the IAM role to be used by Lambda function." type = string } variable "environment_variables" { description = "A map of environment variables to pass to the Lambda function." type = map(string) default = {} } variable "rust_src_path" { description = "The path to the Lambda function's Rust project." type = string } variable "cargo_lambda_env_name" { description = "name in cargo lambda new [name]" type = string } variable "lambda_zip_local_path" { description = "The path where the Lambda function's zip archive will be saved." type = string } terraform/modules/lambda_rust_module/main.tf output "lambda_function_url" { value = aws_lambda_function_url.this.function_url } terraform/modules/lambda_rust_module/variables.tf # Lambdaの定義 resource "aws_lambda_function" "this" { function_name = var.function_name filename = data.archive_file.this.output_path source_code_hash = data.archive_file.this.output_base64sha256 role = var.role architectures = ["arm64"] handler = "bootstrap" runtime = "provided.al2" timeout = 30 environment { variables = var.environment_variables } } resource "aws_lambda_function_url" "this" { function_name = aws_lambda_function.this.function_name authorization_type = "NONE" } # ローカル環境でcargo-lambdaでビルドするための設定 # ファイルのsha256ハッシュを全て連結させ、連結した文字列からsha512ハッシュを計算し、差分があればビルドする resource "null_resource" "rust_build" { triggers = { code_diff = sha512(join("", [ for file in fileset(var.rust_src_path, "**/*.rs") : filesha256("${var.rust_src_path}/${file}") ])) } provisioner "local-exec" { working_dir = var.rust_src_path command = "cargo lambda build --release --arm64" } } # バイナリをzip化 data "archive_file" "this" { type = "zip" source_file = "${var.rust_src_path}/target/lambda/${var.cargo_lambda_env_name}/bootstrap" output_path = var.lambda_zip_local_path depends_on = [ null_resource.rust_build ] } このTerraformコードの中では、ローカル環境でビルドを行い、バイナリをzip化し、Function URLs付きのLambda関数としてデプロイするという構成が含まれています。 ファイルについてハッシュを計算することで、差分があった時のみデプロイするということを可能にしています。 Lambdaを設定する モジュールが完成したらLambdaを作成します。 terraform/lambda.tf module "api_lambda" { source = "./modules/lambda_rust_module" function_name = "${var.name}-api" role = aws_iam_role.lambda_iam_role.arn rust_src_path = "../rust-lambda" cargo_lambda_env_name = "rust-lambda" lambda_zip_local_path = "../lambda_archive/api.zip" environment_variables = { RUST_BACKTRACE = 1 } } Lambdaに付与するIAM権限も同時に作成します。Lambdaから使うサービスが増えてきたら、ここの権限を追加するといいでしょう。 terraform/iam.tf # lambda用Roleの設定 resource "aws_iam_role" "lambda_iam_role" { name = "${var.name}-iam-role" assume_role_policy = <<POLICY { "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Principal": { "Service": "lambda.amazonaws.com" }, "Effect": "Allow", "Sid": "" } ] } POLICY } # lambda用Policyの作成 resource "aws_iam_role_policy" "lambda_access_policy" { name = "${var.name}-access-policy" role = aws_iam_role.lambda_iam_role.id policy = <<POLICY { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:CreateLogGroup", "logs:PutLogEvents" ], "Resource": "*" } ] } POLICY } 実際にデプロイを行う terraformフォルダ内から実際にデプロイしてみましょう。 $ terraform init $ terraform apply 最後に出てくるURLにアクセスして、Hello worldが表示されると完成です。 Apply complete! Resources: 5 added, 0 changed, 0 destroyed. Outputs: api_lambda_function_url = "<https://4wrzwdaows4h3z7vcdc2xlodou0wogpb.lambda-url.ap-northeast-1.on.aws/>" WebAPIを作成するためのLambdaテンプレート Rustを使用したLambdaの場合に比較的需要があるのはWebAPIの作成だと思います。しかし、Lambdaを利用した場合にはactix-web, axum, Rocketなどのフレームワークをそのまま適用することができません。また、lambda_httpを使ったサンプルがWeb上に少なく、構築に苦労するケースが多いです。(苦労しました) そこで以下のようにmain.rsを記述することで、Lambda上で比較的扱いやすくWebAPIを構築できるという例を載せておきます。 rust-lambda/main.rs use std::future::Future; use std::pin::Pin; use lambda_http::{Body, lambda_runtime::Error, Request, RequestExt, Response, run, service_fn}; use serde_json::{json, Value}; type LambdaResult = Result<Response<String>, Error>; fn build_response(status_code: u16, message: Value) -> LambdaResult { let response = Response::builder() .status(status_code) .header("Content-Type", "application/json") .body(message.to_string()) .map_err(Box::new)?; Ok(response) } async fn echo_query(event: Request) -> LambdaResult { let params = event.query_string_parameters(); let name = params.first("name").unwrap_or("world"); let message = json!({ "message": format!("Hello, {}!", name) }); build_response(200, message) } async fn echo_body(event: Request) -> LambdaResult { let body = event.body(); let body_str = match body { Body::Text(text) => text, _ => return build_response(400, json!({ "error": "Invalid request body" })), }; let data: Value = serde_json::from_str(body_str)?; let response = json!({ "message": "Received POST request", "data": data }); build_response(200, response) } async fn not_found() -> LambdaResult { build_response(404, json!({ "error": "Not Found" })) } async fn hello_world() -> LambdaResult { build_response(200, json!({ "message": "Hello, world!" })) } fn route_request(event: Request) -> Pin<Box<dyn Future<Output = LambdaResult> + Send>> { Box::pin(async move { match (event.method().as_str(), event.uri().path()) { ("GET", "/hello") => hello_world().await, ("GET", "/echo") => echo_query(event).await, ("POST", "/echo") => echo_body(event).await, _ => not_found().await, } }) } #[tokio::main] async fn main() -> Result<(), Error> { run(service_fn(route_request)).await } rust-lambda/Cargo.toml [package] name = "rust-lambda" version = "0.1.0" edition = "2021" [dependencies] lambda_http = "0.11.1" tokio = { version = "1", features = ["macros"] } serde_json = "1.0.115" serde = { version = "1.0.197", features = ["derive"] } また、axumはlambda_httpと統合して利用できるようなので、こちらを利用してもいいかもしれません。(この記事のレビュー中に社内で教わりました…!) aws-lambda-rust-runtime/examples/http-axum/src/main.rs at main · awslabs/aws-lambda-rust-runtime A Rust runtime for AWS Lambda. Contribute to awslabs/aws-lambda-rust-runtime development by creating an account on GitHub. github.com まとめ 今回はcargo-lambdaとTerraformを用いてRustでLambdaの環境を手軽に構築する方法について解説しました。 勉強がてらRustに触れてみたのですが、AWSサービスとの組み合わせにおいてもRustの速度は非常に高速で、学習コストは高いというデメリットはありますが、高速に動作すると言う何者にも変え難いメリットを得ることができます。(特に従量課金型のLambdaという環境においては高速に動作するというのは金銭的にもメリットがあると言えると思います) みなさんも良きRustライフをお送りください。
基幹システムグループ N1! オートメーションスペシャリストの南川です。 今回は、Docker Compose v1 ( docker-compose コマンド) が GitHub Actions の Ubuntu と Windows のイメージで使えなくなった件について説明します。 背景 2024年4月3日の朝に、以前紹介したDockerイメージのビルド失敗を通知するGitHub Actionsのワークフローが失敗しているのを確認しました。 GitHub Actions で Docker イメージを定期的にビルドし、失敗したら Slack に通知させる その時の実行結果のログ (一部マスク済み) は以下の通りです。 Run docker-compose build docker-compose build shell: /usr/bin/bash -e {0} env: ACTION_URL: https://github.com/***/***/actions/runs/*** /home/runner/work/_temp/********-****-****-****-************.sh: line 1: docker-compose: command not found Error: Process completed with exit code 127. どうやら、 docker-compose コマンドが見つからないようです。 原因 Ubuntu と Windows イメージから Docker Compose v1 が削除されたのが原因でした。 2024年4月1日から 3~4 日間 かけて削除されるとのことです。 https://github.com/actions/runner-images/issues/9557 ちなみに、この Issue は GitHub ホステッドランナーに プリインストール済みのソフトウェア一覧ページ から見つけました。 For the overall list of included tools for each runner operating system, see the  Available Images  documentation the runner images repository. https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#preinstalled-software 対応 以下のページを参照し、Docker Compose v1 から v2 に移行する必要があります。 https://docs.docker.com/compose/migrate/ 今回のケースでは、 docker-compose build コマンドを docker compose build コマンドに書き換えるだけで動くようになりました。 修正前 - name: define run url run: echo "ACTION_URL=https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" >> $GITHUB_ENV - name: docker-compose build run: docker-compose build 修正後 - name: define run url run: echo "ACTION_URL=https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" >> $GITHUB_ENV - name: docker compose build run: docker compose build 「 path:.github/workflows AND ("docker-compose build" OR "docker-compose pull") 」で検索すると、修正が必要なワークフローファイルを確認できます。 https://github.com/actions/runner-images/issues/9557#issuecomment-2033320632 https://docs.github.com/ja/search-github/github-code-search/understanding-github-code-search-syntax まとめ 今回の問題は、 Docker Compose v1 が非推奨になってからも docker-compose コマンドを使い続けていたのが原因でした。 今回、すぐに気づけた要因は 以前に作成した定期的にビルドするワークフロー のおかげであり、興味のある方は是非導入してみてください(宣伝) みなさんも EOL に気を付けながら、ツールを使っていきましょう。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
前回の記事では、ニフティのオプションチームで働く社員に、業務内容やチーム環境についてインタビューを行いました。 今回はインタビューの後編をお届けします。前編はこちらの記事をご覧ください。 【インタビュー】ニフティのオプションサービスを開発しているチームに仕事について聞いてみました!【オプション前編】 オプションチームではどんなスキルが伸ばせますか? T.Mさん 幅広くやるのでフルスタックエンジニアを目指せます。中でも好きな分野を伸ばしてくというスタンスなので更に各々の得意分野で尖っていけますね。私はクリーンアーキテクチャーやドメイン駆動設計について外部発信したりもしています。 たけろいど さん Svelteで外部向けのイベントに登壇したり、UI/UX勉強会を主催したりしています。 H.Sさん @nifty ADクリーナーのインフラは私が作りました。インフラのことなら相談に乗ります。あとはいま勉強しているKubernetesについて社内向けの勉強会を開いていますね。 T.Mさん 専門分野を社内外に発信できる人材が育っていってます。あとはチームの体制がスクラムなのでスクラムの知識がつくし、サービスの立上げ>開発>運用>グロース>サービス終了までの全工程を経験できますね。フロントエンド、バックエンド、インフラ、それぞれ得意分野があるエンジニアがいて、どのスキルを伸ばすにしても目標になる人が揃っています。 とーますさん サーバーサイドをPythonで作っているところは珍しいと思いますね。世の中javaが主流なので。Flask、マイクロフレームワークを身に着けていきたい人にはとてもいいと思います。 チームで取り組んでいることを教えてください。 T.Mさん スクラム体制を採用しているのですが、スプリントは1週間で回していて、コミュニケーションの頻度を意識的に上げています。1週間単位でプロダクトオーナーがやりたいことが変わったりするのでしっかりキャッチしたいんです。 とーますさん 前の職場では振り返りの習慣があまりなかったので、レトロスペクティブが新鮮でした。自分たちの仕事効率化にもつながるしニフティの仕事のしやすさにもつながっていると感じます。 今後の展望を教えてください。 T.Mさん 新しいサービスのリリースに向けて開発を進めています。並行して既存製品の改善も行うので、しばらくは2軸で動いていく予定です。 H.Sさん 基盤の刷新もありますね。 T.Mさん この先1年はスケジュールが埋まっています!楽しみです。 皆さんについて教えてください。 休日はどう過ごしていますか? T.Mさん たけろいどさんは料理ですよね! たけろいど さん 料理はかなり好きで、休日に関わらずやっていますね。最近お弁当をつくるようになりました。 H.Sさん えらすぎでしょ…。 たけろいど さん パスタにはまっていて、僕のパスタは世界一うまいです。 T.Mさん レトロスペクティブの時にパスタの写真見せてもらうんですが、マジでうまそうです。 ▲たけろいどさんのパスタ H.Sさん マイクラやパルワールドなんかのゲームもやりますが、最近は勉強してます。いまはKubernetesがアツいです。家にk8sのクラスタがあります。毎日1時間やってるコツコツやってる感じです。 たけろいど さん …僕も毎日料理してます!笑 とーますさん 私はセキュリティソフトが好きなんですが笑 中でもいわゆるエンドポイントセキュリティが好きで。プライベートでセキュリティソフトを全部使って検証してみたり、第三者機関のレポートを読んだりしています。転職先にニフティを選んだのはプロダクトにエンドポイントセキュリティがあり、そこが魅力を感じたからです。 T.Mさん とーますさんにはこれから商品知識をいっぱい身に着けてもらって、外部エンジニアとの交流会にもいってほしいです。 いま楽しみなことはなんですか? たけろいど さん 秒速5センチメートルのリバイバル上映です! H.Sさん CKAを受けるのが楽しみですね。 とーますさん セキュリティソフト以外の趣味のライブ遠征です。あとは会社のテニス部に入っていて、部活での交流が楽しみですね。 T.Mさん 海外出張が控えているので、ローマのゴハンが楽しみです。 今回はニフティのオプション開発チームのインタビューの様子をお届けしました。ニフティでは、さまざまなプロダクトへ挑戦するエンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトよりお気軽にご連絡ください! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する .is-style-rounded + .has-text-color{ font-size:95%; }
自己紹介 T.Mさん 新卒5年目で、サブリーダーをやってます。入社時からオプションサービスの開発運用を担当していて、現在は常時安全セキュリティ24や@ nifty セキュア・プライバシーなどのセキュリティサービスを中心に、おしゃべりなども担当しています H.Sさん 同じく新卒5年目で、@nifty ADクリーナーのバックエンド、インフラ中心に開発を行っています。オプションチームには2023年3月に配属されました。前のチームでは課金システムチームの開発を担当していました。勉強が趣味で今は CKA 取得に向けて毎日コツコツ勉強しています。 たけろいどさん こんにちは、たけろいどです。フロントエンドとたけのこの里が好きな新卒3年目です。とくにSvelteというフレームワークが好きで業務・趣味問わずよく使います。好きじゃないものはきのこの山です。対戦ヨロ とーますさん 2023年10月に入社したばかりのキャリア社員です。ニフティに入社する前は金融系のシステム開発に関わっていました。前の職場ではコーディングを外注していましたが今は自らコードを書く機会を得てエンジニアらしい仕事ができる環境になったと思います。またBtoBからBtoCへの転向という面もあり自分にとって大きな変化でした。 ニフティの「オプションサービス」とは? ニフティの「オプションサービス」とはなんですか? T.Mさん 光回線サービスをニフティのメインサービスと捉え、それに付属するサービスなのでオプションサービスと呼ばれています。 常時安全セキュリティ24、@nifty ADクリーナー、@nifty まかせて365などたくさんのサービスがあります。(オプションサービスの紹介ページは こちら )オプションサービスを通じ、お客様に通信回線に留まらない付加価値をお届けしたいという想いでサービスのブラッシュアップに日々取り組んでいます。 ニフティのオプションチームについて教えてください オプションサービスすべてをこのチームで担当しているんですか? T.Mさん このチームではオプションサービスの中でもセキュリティ系のサービスを担当しています。他のチームとしては入会システムチーム、サービスインフラチーム、メールチーム、光電話や回線従属系のものは回線チームがあります。 たけろいど さん オプションに関わっているチーム、改めて聞くと多いですね! T.Mさん オプションサービスチームは、オプションサービスを専業でやっている唯一のチームです。最近は回線とは別にオプションサービスを単体として販売することもあるので、オプションという枠を超えてメインのサービスとして提供していたりもしますね。常時安全セキュリティ24、@nifty ADクリーナーがそれにあたります。 このチームで立上げしたサービスはありますか? T.Mさん @nifty ADクリーナーについては立上げからやりました。 とーますさん 会社の売上にも貢献できている、チームの看板サービスと言えますね。 開発スタイルや環境について教えてください。 たけろいど さん バックエンドはPython(Flask)で、フロントはSvelteかJinja2を使っています。元はphpでしたが思い切って刷新しました。コードについてもリファクタリングが必要だったので、環境変更に伴い書き換えました。 T.Mさん フロントの刷新についてはたけろいど君がグイグイ引っ張ってくれました。 たけろいど さん そう、フロントエンドは自分が引っ張りました。 Jinja2という話が出ていましたが、UI/UXの面でユーザー体験が下がると考えてチームを説得して押し切りました笑。 T.Mさん フレームワーク等々、たけろいど君に質問攻めをしてこれはやってくれそうだなと思って任せました! たけろいど さん まだ2年目だったのですが、フロントエンドに詳しい人が自分しかいなかったので押し切ることができました笑。 ベースはAPIもあったので土壌はとてもよくやりやすかったので先達に感謝です。無事モダンな環境を実現できて良かったです。 どんな運用があるか教えてください。 T.Mさん オプションチームにおける運用とはサーバー保守等のいわゆる運用だけではなく、お客様の困りごとを解消するための改善行動もあります。 たけろいど さん 店頭でお客様からご相談があった場合、開発チームに対応依頼がくることがあります。その場で解決できた時はとても嬉しいですね。 T.Mさん 使い方のサポートであったり、申し込み処理をしたつもりができていなかった場合のリカバーなどですね。お客様と直接お話しすることで改善に繋げられることもあります。 たけろいど さん 運用はどうしても時間がかかってしまうので大変ですね。でも、とてもやりがいがあります。 今のチーム体制になるまでの変遷を教えてください。 T.Mさん 元は企画と開発がワンチームでやっていたんです。自分が1,2年目のころは部署も一緒でした。その後、組織改編によって企画・開発に別れはしましたが、そのころの経緯もあってサービスについての意見交換も活発ですしフィードバックを受ける機会も多いですね。スクラム体制を採用しているのですが、いまも毎朝デイリースクラムで話をしてます。 H.Sさん 直接会話する機会の他に、Slackでも密にやりとりしています。仕様の確認なども気軽にできる関係なのでとてもいいですね。 とーますさん 企画あっての開発なのでつながりを大切にしています。企画の方で契約しているベンダーの方とも関わりを持つように心がけています。 T.Mさん 商品を販売するバイヤーに商品の売り方を提案する場にも、企画と開発一緒になって臨んだりしています。取り扱っているプロダクトのベンダーの拠点があるヨーロッパにいって、現地のエンジニアと意見交換したりもします。 たけろいど さん 近々また海外に行きますよね。次はどこでしたっけ。 T.Mさん ローマです。今回は企画と僕とで行って、現地の開発のエンジニアやセールスと意見交換をする予定です。ちなみに昨年はオランダへいきました。英語はぜんぜんなんですが笑、なんとかなります! たけろいど さん 英語はオンラインゲームでちょっと使うくらいですね笑。 T.Mさん 開発チームの話に戻りますが、実は僕とたけろいど君の二人体制になっちゃった時期があって。そこにH.S君がヒーロー的に他のチームからやってきたんです。 H.Sさん ヒーローかぁ笑。 当時、自分は4年目でキャリアを見直したかったんです。社内公募制度という希望したチームに異動できる仕組みがあるのでそれを利用しました。前にいたチームでは課金システムのバックエンドとインフラ周りを担当しており、4年の中でいろんな経験をさせていただいたので知識はかなりついていたのは確かですね。 とーますさん 異動してきて特に違和感とかはなかったですか? H.Sさん なかったです。全然違うスキルセットが必要でしたが、元のチームは今のチームとの接点もありましたし、言語は同じでPythonだったので全く問題ありませんでした。 とーますさん 私も前職でPythonを使っていたのですぐに馴染めましたね。 たけろいど さん とーますさんって前職ではどんなことをされていたんでしたっけ? とーますさん 金融系です。前職では開発はほぼ委託していたので、エンジニアとしてプロダクトに関わる機会があまりなかったですね。基本的にBtoBになるのですが、BtoBだと納品後のサービスの姿が見えないことが多いんです。ニフティでは内製開発ですし、BtoCなのでプロダクトに対するフィードバックが聞けたりや売上高を追うことができるのでやりがいを感じます。 後編に続きます! 今回はニフティのオプション開発チームのインタビューの様子をお届けしました。続きは後編の記事をご覧ください。 【インタビュー】ニフティのオプションサービス開発エンジニアってどんな人?【オプション後編】 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに  Gemini AI  事前準備  3分でAIアプリを作りましょう  詳しい使い方 DIの導入  試してみよう! はじめに はじめまして。ニフティ株式会社のLinです。 台湾出身のモバイルアプリエンジニアとして、社内で「マイ ニフティ」の Android および iOS 版の開発を担当しております。 2023年末に、GoogleがGemini APIをリリースしたことで、モバイルアプリでもAIサービスとの連携が容易になりました。 今回はGemini AIアプリの作り方をご紹介いたします。  Gemini AI Gemini AIはGoogleが開発したマルチモーダルAIモデルで、2023年12月6日(米国時間)に初リリースされました。 マルチモーダルのため、テキストだけでなく、動画や画像、音声など様々な種類のデータを入力、出力として扱えることができます。 モデルは下記3種類があります: Gemini Ultra 非常に複雑で幅広いタスクに対して最も高いパフォーマンスを提供する、Gemini最大のモデル。 Gemini Pro 強力なパフォーマンスを維持しつつ、コストやレイテンシがバランスよく最適化された、中規模のモデル。 Gemini Nano デバイス上で実行用に設計された、最も効率的なモデル。 低メモリデバイス用にNano-1(18億パラメータ)、高メモリデバイス用にNano-2(32.5億パラメータ)が提供されます。 Gemini AIのモデル種類 2024年現在、 Gemini Pro は1分間に60回のリクエストまで無料です。今回はGemini ProのAPIを使いましょう。  事前準備 APIキー Android Studio Preview Jellyfishから Gemini API Starter が追加されました。  3分でAIアプリを作りましょう Android Studio Preview(Jellyfish)を起動して、 Gemini API Starter を選択します。 プロジェクト情報を入力します。 先ほど作ったGemini APIキーを入力します。 入力したAPIキーは local.properties での apikey に保存され、 BuildConfig を経由して使われています。 ビルドが完了すると、デフォルトアプリが作成されました。 SDKの依存関係は、以下のようにモジュール(アプリレベル)のGradle設定ファイル(例: <project>/<app-module>/build.gradle.kts )に設定されています。 ... dependencies { ... // Gemini AI implementation("com.google.ai.client.generativeai:generativeai:0.1.2") }  詳しい使い方 Gemini AIを呼び出すには、モデルとプロンプトの設置が必要です。 モデルは下記2種類を使えます: gemini-pro:文字のみの入力。 startChat 経由でチャット機能を実装できます。 history でチャット履歴の設置と確認が可能です。 gemini-pro-vision:画像と文字の入力。 画像の入力は必須です。 文字のみの入力に対してはエラーが発生します。 画像のフォーマットはBitmapとなります。 また、temperatureなどのコンフィグ設定と 安全性設定 もできます: // 安全性設定 val harassment = SafetySetting(HarmCategory.HARASSMENT, BlockThreshold.ONLY_HIGH) val hateSpeech = SafetySetting(HarmCategory.HATE_SPEECH, BlockThreshold.MEDIUM_AND_ABOVE) // コンフィグ設定 val config = generationConfig { temperature = 0.99f // 生成するテキストのランダム性を制御 topK = 50 // 生成に使用するトークンの累積確率を制御 topP = 0.99f // 生成に使用するトップkトークンを制御 } プロンプトについては、モデルによって内容が異なります。 gemini-proの場合は以下の例をご参考にしてください: // モデルを設置します val generativeModel = GenerativeModel( // 文字入力のみ modelName = "gemini-pro", apiKey = BuildConfig.apiKey ) val viewModel = GeminiViewModel(generativeModel) // ViewModelでプロンプトを設定します class GeminiViewModel( private val generativeModel: GenerativeModel ) : ViewModel() { private val _uiState: MutableStateFlow<UiState> = MutableStateFlow(UiState.Initial) val uiState: StateFlow<UiState> = _uiState.asStateFlow() fun respond(inputText: String) { _uiState.value = UiState.Loading // プロンプトを設置します // 文字(必須) val prompt = inputText viewModelScope.launch { try { val response = generativeModel.generateContent(prompt) response.text?.let { outputContent -> _uiState.value = UiState.Success(outputContent) } } catch (e: Exception) { _uiState.value = UiState.Error(e.localizedMessage ?: "") } } } } gemini-pro-visionの場合は以下の例をご参考にしてください: // モデルを設置します val generativeModel = GenerativeModel( // 画像+文字入力 modelName = "gemini-pro-vision", apiKey = BuildConfig.apiKey ) val viewModel = GeminiViewModel(generativeModel) // ViewModelでプロンプトを設定します class GeminiViewModel( private val generativeModel: GenerativeModel ) : ViewModel() { private val _uiState: MutableStateFlow<UiState> = MutableStateFlow(UiState.Initial) val uiState: StateFlow<UiState> = _uiState.asStateFlow() fun respond(inputText: String, inputImageList: List<Bitmap>) { _uiState.value = UiState.Loading // プロンプトを設置します val prompt = content { // 画像(必須) inputImageList.forEach { image -> image(image = image) } // 文字 text(text = inputText) } viewModelScope.launch { try { val response = generativeModel.generateContent(prompt) response.text?.let { outputContent -> _uiState.value = UiState.Success(outputContent) } } catch (e: Exception) { _uiState.value = UiState.Error(e.localizedMessage ?: "") } } } } DIの導入 HiltなどのDI(依存性注入)ライブラリも導入したい場合、以下の例をご参考にしてください: // GeminiModule.kt @Module @InstallIn(SingletonComponent::class) object GeminiModule { // 必要によるモデルのコンフィグ設定と安全性設定を追加します private val harassment = SafetySetting(HarmCategory.HARASSMENT, BlockThreshold.ONLY_HIGH) private val hateSpeech = SafetySetting(HarmCategory.HATE_SPEECH, BlockThreshold.MEDIUM_AND_ABOVE) private val config = generationConfig { temperature = 0.95f topK = 50 topP = 0.9f } @Provides @Singleton @GeminiPro fun provideGemini(): GenerativeModel { return GenerativeModel( modelName = "gemini-pro", apiKey = BuildConfig.apiKey, safetySettings = listOf( harassment, hateSpeech ), generationConfig = config ) } @Provides @Singleton @GeminiProVision fun provideGeminiVision(): GenerativeModel { return GenerativeModel( modelName = "gemini-pro-vision", apiKey = BuildConfig.apiKey, safetySettings = listOf( harassment, hateSpeech, SafetySetting(HarmCategory.DANGEROUS_CONTENT, BlockThreshold.ONLY_HIGH), SafetySetting(HarmCategory.SEXUALLY_EXPLICIT, BlockThreshold.MEDIUM_AND_ABOVE) ) ) } @Provides @Singleton fun provideGeminiRepository( @GeminiPro geminiProModel: GenerativeModel, @GeminiProVision geminiProVisionModel: GenerativeModel ): GeminiRepository = GeminiRepositoryImpl(geminiProModel, geminiProVisionModel) @Provides fun provideApplicationContext(application: Application): Context { return application.applicationContext } } @Qualifier @Retention(AnnotationRetention.BINARY) annotation class GeminiPro @Qualifier @Retention(AnnotationRetention.BINARY) annotation class GeminiProVision // GeminiRepository.kt interface GeminiRepository { fun getGeminiProModel(): GenerativeModel fun getGeminiProVisionModel(): GenerativeModel } // GeminiRepositoryImpl.kt class GeminiRepositoryImpl @Inject constructor( @GeminiPro private val geminiProModel: GenerativeModel, @GeminiProVision private val geminiProVisionModel: GenerativeModel ) : GeminiRepository { override fun getGeminiProModel(): GenerativeModel = geminiProModel override fun getGeminiProVisionModel(): GenerativeModel = geminiProVisionModel } // GeminiViewModel.kt @HiltViewModel class GeminiViewModel @Inject constructor( geminiRepository: GeminiRepository ) : ViewModel() { // 文字のみのモデル private val geminiProModel = geminiRepository.getGeminiProModel() private val chat = geminiProModel.startChat(history = emptyList()) // チャット機能 // 画像+文字のモデル private val geminiProVisionModel = geminiRepository.getGeminiProModel() ... } これで、ViewModelで一度GeminiRepositoryを注入すると、2つのモデルを利用できるようになりました。  試してみよう! メッセージ画面を追加すると、以下のようなアプリを作れます。 下記サンプルは自由にクローンして試せます。 サンプル 今後のモバイルアプリでは、ますますAIサービスが導入されます。 ぜひAI系のアプリ作りを体験しましょう! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは、会員システムグループの渡邊です。 社内にNotionが導入されて2年以上立ちました。 最近は何でもNotionで管理すればいいじゃんという気持ちになってきていて、試しにDB設計をNotionで行ってみたのでその感想を書いていきます。 範囲 今回はDB設計におけるエンティティ抽出とデータモデリング、ER図作成までをNotionだけで行います。 エンティティ抽出とモデリング NotionはMermaid記法が使えるため、こちらを使ってエンティティ抽出を行うことが可能です。 しかし、いきなりコードベースで図を作成するのは難易度が高いため、今回はdraw.ioを使ってエンティティ抽出とデータモデリングを行っていきます。 draw.io for Notion という拡張機能を使うことで、SVGファイルをNotionに貼り付けるだけで直接draw.ioの操作を行うことができます。 エンティティ抽出 今回はユーザーが記事投稿をし、そこにコメントを投稿できるサービスを実装することを想定して設計していきます。 データモデリング 次に各テーブルの関係性を紐づけていきます。 これもNotion上で操作できるので楽ですね。 ER図作成 ER図を作る前にテーブル定義をしていきます。 各テーブルの属性情報をNotionのデータベースで管理していきます。 実際に作ったものが以下になります。テーブルでグループ化するとかなり見やすくなりますね。 続いてこちらをMermaid記法を使ってER図化していきます。 ここで一工夫してMermaid記法に合う形に整えていきます。 Mermaidコピー用のビューを作成し、型と論理名をMermaid記法に合わせるための関数を作成します。 今回は桁数を制限しているのでこちらを型と合わせて表示できるようにするため、新規に 型(Mermaid用) というプロパティを作成し、以下の関数を設定します。 if(!empty(prop("桁数")), ((prop("型") + "(") + format(prop("桁数"))) + ")", prop("型")) 続いて論理名をダブルクォーテーションで囲ってエラーが出ないようにするため、新規に 論理名(Mermaid用) というプロパティを作成し、以下の関数を設定します。 ("\"" + prop("論理名")) + "\"" 完成したものが以下になります。 最後に先ほど作成したテーブルの内容をコードブロックに貼り付けていきます。 貼り付けはUSER,POST,COMMENTの中にコピペするだけです。 erDiagram USER { INT comment_id PK "コメントID" INT post_id FK "PostID" TEXT(10) user_id FK "ユーザーID" TIMESTAMP comment_at "コメント日時" TEXT content "内容" } POST { INT post_id PK "PostID" TEXT(10) user_id FK "ユーザーID" TIMESTAMP post_at "投稿日時" TEXT content "内容" TEXT title "タイトル" } COMMENT { TEXT(10) user_id PK "ユーザーID" TIMESTAMP last_login_at "最終ログイン日時" TIMESTAMP created_at "作成日時" TEXT(256) email_address "メールアドレス" TEXT password "パスワード" } USER ||--o{ POST : "Creates" POST ||--o{ COMMENT : "Has" USER ||--o{ COMMENT : "Writes" これで完成です。 テーブル構成が変わる場合は、Notionのデータベースを修正してコードブロックにコピペし直すだけでER図の更新ができるようになるので非常に便利です! 終わりに 今回はNotionでDB設計を行ってみました。 DB設計の肝であるテーブル定義とER図がNotionで作成、管理できるのは非常に便利だなと思いました。 しかし、一つ問題点がありましてmermaidの仕様上、エンティティの位置調整が自動で行われるため、エンティティが多くなれば多くなるほど複雑なER図になってしまいます。そのため、複雑なシステムの場合は図を分けるなどの工夫が必要になってきます。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
 こんにちは。ニフティ株式会社、新卒2年目の小林です。  近日、Javaプロジェクトをインフラから作ることに着手しており、Mavenを使用したパッケージのビルドを行うことになりました。その際に得た便利なPオプションについて共有します。 変更内容  結論から述べますと、以下の2項目により実現可能です。maven 3.5.2 を使用しています。 ビルド時コマンドの追加  &env はpom.xmlで設定したidを入力します。(例:hoge1, local, develop, production) maven clean package -P &env pom.xmlの変更  <profiles><profile>タグにより、各環境(id)ごとに変化する定数について設定することが出来ます(<properties>)。また、<activation>, <activeByDefault>で指定されたprofileを、Pオプションが設定されていなかった場合の初期値に設定することが出来ます。( 公式 ) <profiles> <profile> <id>hoge1</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <fuga1>/webappファイルが存在する相対パス/</fuga1> <fuga2>/あるファイルが存在する相対パス/</fuga2>    ... </properties> </profile> <profile> <id>hoge2</id> ... <profiles>  そののち、<properties>で設定したタグ名を共通した箇所で使用することで、環境ごとの設定を反映させることが出来ます。  例えば、warとしてパッケージ化する際のwebappDirectoryのパス指定( 参考 )を以下のように変数化することが出来ます。 <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>~~~</version> <configuration> <webappDirectory>${fuga1}/directory</webappDirectory> </configuration> </plugin> </plugins> </build> 例えば、 -P hoge1 と指定されていれば、 ${fuga1}には hoge1のprofileで設定されたfuga1が指定されます。 ユースケース 様々な環境における構成図とprofileの例を紹介します。含まれているファイルの拡張子は、読み込む形式に従って適切に変更してください。 ただ、<id>タグと、<properties>タグの中身を変更するのみです。 ローカル環境 設定ファイルの中に、Dockerで作成されたネットワーク内のWiremockを使用するコードがあった場合です。 <profile> <id>local</id> <properties> <fuga1>/ローカル環境用の設定ファイルが存在する相対パス/</fuga1> <fuga2>/ローカル環境用のあるファイルが存在する相対パス/</fuga2>    ... </properties> </profile> 開発環境 設定ファイルの中に、開発用の外部APIを使用するコードがあった場合です。 本番環境も構成は同じになります。 <profile> <id>develop</id> <properties> <fuga1>/開発環境用の設定ファイルが存在する相対パス/</fuga1> <fuga2>/開発環境用のあるファイルが存在する相対パス/</fuga2>    ... </properties> </profile> 本番環境(商用) <profile> <id>production</id> <properties> <fuga1>/本番環境用の設定ファイルが存在する相対パス/</fuga1> <fuga2>/本番環境用のあるファイルが存在する相対パス/</fuga2>    ... </properties> </profile> <profiles>まとめ 以上3環境の設定をpom.xmlに記載した際の<profiles>を以下に記述します。 <activeByDefault>が指定されているため、Pオプションを設定しない場合はlocalのprofileが呼び出されます。 <profiles> <profile> <id>local</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <fuga1>/ローカル環境用の設定ファイルが存在する相対パス/</fuga1> <fuga2>/ローカル環境用のあるファイルが存在する相対パス/</fuga2>   </properties> </profile> <profile> <id>develop</id> <properties> <fuga1>/開発環境用の設定ファイルが存在する相対パス/</fuga1> <fuga2>/開発環境用のあるファイルが存在する相対パス/</fuga2>   </properties> </profile> <profile> <id>production</id> <properties> <fuga1>/本番環境用の設定ファイルが存在する相対パス/</fuga1> <fuga2>/本番環境用のあるファイルが存在する相対パス/</fuga2> </properties> </profile> </profiles> 終わりに  今回はMavenのビルド時における、便利なPオプションについてまとめました。コマンドの追加とpom.xmlの変更のみで、環境ごとに読み込むファイルを変更することが出来ました。Javaの勉強をしていくにつれて、他にも便利な機能が存在すると思うので、環境に合わせた最適なオプションを設定出来るように日々研鑽していきます。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
こんにちは! CSSを使っていると、時々うまくスタイルが適用されないことがありますよね。 私もフロントエンドエンジニアとして、日々JavaScriptなどのプログラミング言語で開発を行っていますが、CSSをうまく使いこなせずに困ってしまうことがよくあります。 プログラミング言語には詳しいけれども、HTML/CSSについてはあまり詳しくないまま使ってしまっているエンジニアも少なくないでしょう。 そこで今回は、私のようなエンジニアでも美しいCSSを書きたいと思い、CSSの詳細度について学びました。その学んだ内容を、説明しながら個人的にも良いアウトプットにつなげたいと考え、解説していきます。 詳細度とは? MDNの説明だとこんな感じに書かれています。 詳細度  (Specificity) は、ある要素に最も関連性の高い  CSS 宣言 を決定するためにブラウザーが使用するアルゴリズムで、これによって、その要素に使用するプロパティ値が決定されます。詳細度のアルゴリズムは、 CSS セレクター の重みを計算し、競合する CSS 宣言の中からどのルールを要素に適用するかを決定します。 詳細度 – CSS: カスケーティングスタイルシート | MDN ある要素に対して、競合する複数のCSS宣言があったときに、どのルールを適用するかを決めるために用いられるアルゴリズムのことのようです。 そのアルゴリズムでは、競合するCSS宣言から最も関連性の高いCSS宣言を決め、適用します。 詳細度の計算方法 では、そのアルゴリズムではどうやって詳細度を計算し、関連性の高いCSS宣言を決定しているのでしょう? 基本的にはID, クラス, 要素の3種類のセレクタに対応するID, CLASS, TYPEという3つの分類に分けて計算します。( ID – CLASS – TYPE と記述するようです) セレクタの3つの分類 ID IDセレクタが該当します。 #hoge みたいな感じ。 一致するセレクタに含まれるIDごとに、 1 – 0 – 0 を追加します。 CLASS クラス .fuga や、属性 [type=”radio”] 、擬似クラス :hover などのセレクタが該当します。 それぞれで、 0 – 1 – 0 を追加します。 TYPE 要素 h1 , h2 , p など、擬似要素 ::before などのセレクタが該当します。 それぞれで 0 – 0 – 1 を追加します。 上記の3種類の分類を左から右の順番で比較し、数値が大きいほうのCSS宣言を適用します。 例を示すと以下のようになります。 #myElement input.myClass { color: red; } /* 1-1-1 - ID列が一番大きいのでこのCSS宣言が適用される */ input[type="password"]:required { color: blue; } /* 0-2-1 */ html body main input { color: green; } /* 0-0-4 */ 上記のセレクタが全て同じ input 要素を対象とした場合、IDで一番大きい値を示している color: red; が適用されます。 #myElement { color: yellow; /* 1-0-0 */ } #myApp [id="myElement"] { color: green; /* 1-1-0 - CLASS列がより大きいのでこのCSS宣言が適用される */ } 上記の例では、ID列が同じ値になっています。その場合はCLASS列の優劣で判定します。 CLASS列がより大きい、 color: green; が適用されます。 示してきた2つの例より、ID → CLASS → TYPEの順に数の大小を判定し、詳細度の大きい値のCSS宣言を適用していることがわかります。 3つの例外 擬似クラスの例外 擬似クラスはCLASS列として計算されますが、 :is() , :not() , :has() は詳細度の計算で、擬似クラスとして見なされず、CLASS列として計算されません。ただし、括弧内の引数は詳細度の計算に利用されます。 p { /* 0-0-1 */ } :is(p) { /* 0-0-1 */ } h2:nth-last-of-type(n + 2) { /* 0-1-1 */ } h2:has(~ h2) { /* 0-0-2 */ } div.outer p { /* 0-1-2 */ } div:not(.inner) p { /* 0-1-2 */ } :where() も擬似クラスの例外の一つで、CLASS列として計算されません。 また、 :is() , :not() , :has() とも違い、括弧内の引数も詳細度の計算に利用されません。 :where(#defaultTheme) a { /* 0-0-1 */ color: red; } footer a { /* 0-0-2 */ color: blue; } インラインスタイル HTMLの各要素にはstyle属性を使用してインラインスタイルを追加することができます。 <p style="color: red;">これは赤になります。</p> 通常のCSS宣言を常に上書きすることができるので、最も高い詳細度を持つと考えることができます。 インラインスタイルのみ例外として、 1 – 0 – 0 – 0 というID列よりも高い詳細度を保有すると考えても問題ないです。 !important !important を使用することで、詳細度を無視しインラインスタイルをも凌駕してスタイルを適用することができる、最強のCSS宣言になります。 <p class="yellow" style="color: red;">これは青になります。</p> p { color: blue !important; } .yellow { color: yellow; } MDNにも記述されていますが、 !important を使用して詳細度を上書きすることは 悪しき習慣 です。 !important を使うことでどんなスタイルでも上書きすることができます。 しかし、 !important をさらに上書きすることは難しく、重ねて !important を使うしかなくなります。 CSSのいたる所に !important が付与されてしまい、新しくスタイルを適用しようにもなかなか反映されない。。。保守性の欠片もないCSSになりかねないのです。。。 !important を使用する際は本当に必要なのか確認してから付与する必要があります。 また、なぜ必要なのかはコメントとして残しておきましょう。 セレクタの用途 各セレクタとその詳細度について説明してきましたが、そもそも色々あるセレクタはどう使い分ければいいのでしょうか? idセレクタ、クラスセレクタ、要素セレクタにわけて説明していきます。 要素セレクタ HTML要素名で指定する要素セレクタは指定した要素全てが対象となるためCSSのリセットや初期設定に利用します。 span { background-color: yellow; } p { color: blue; } a { color: red; } クラスセレクタ クラスセレクタはピリオド . でクラスを指定し、クラスが適用されている全ての要素にスタイルを適用します。 クラスセレクタは以下の理由から一番よく使われるセレクタだと個人的には考えています。 作成者自身がスタイルの適用範囲を決めることができる 他のクラスを要素に割り当てることで、上書きも容易 Webサイトを作成していて、何らかのスタイルを適用したい時にまず一番最初に試すのが、クラスセレクタです。 .notebox { border: 4px solid #666; padding: .5em; } .notebox.warning { border-color: orange; font-weight: bold; } .notebox.danger { border-color: red; font-weight: bold; } IDセレクタ # でIDを指定します。IDセレクタはそのidが設定されている要素を選択します。 IDはドキュメントの中で一度しか使用できず、非常に特別なセレクタとなります。 なので前述した詳細度も一番大きい値になります。 IDセレクタに関しては、他のセレクタを上書きすることができます。 しかし、IDセレクタに適用されたスタイルをさらに上書きすることは、より高い詳細度を適用する必要があるので非常に難しくなります。 なので可能ならIDセレクタは使用せずに、クラスセレクタを使用するほうが良さそうです。 どうしてもIDセレクタを使わざるを得ない場合のみ、この方法を使用しますが基本的には使いません。 #one { background-color: yellow; } h1#heading { color: rebeccapurple; } おわりに 今回は詳細度の話でした。 CSSはまだまだ奥深いので、色々勉強していきたいです。 エンジニアは割とプログラミング言語を重視しがちですが、HTMLやCSSも気に掛ける必要があるかなと思います。 「モダンで保守性の高いJavaScriptで書かれたソースコードだけど、CSSは汚い」みたいなことが意外とありますよね。綺麗なCSSを書けるようにしていきたいです! 参考 詳細度 – CSS: カスケーティングスタイルシート | MDN 要素・クラス・IDによるセレクター – ウェブ開発を学ぶ | MDN ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは。ニフティ株式会社の並木です。 今回は「Amazon API Gateway」と「AWS Lambda」を使ってAPIを作る方法についてご紹介いたします。 背景 以前「 Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた 」で紹介した方法で、RSSフィードの更新をトリガーにしたX(旧Twitter)への自動投稿機能を作成したのですが、上記記事の「④Twitterに文章を投稿したい」で紹介した機能が2023年9月に使用できなくなってしまいました。(詳細: https://help.zapier.com/hc/en-us/articles/18657531069965 ) 別の方法で自動投稿機能を復活できないかという話になり、前から使用していた「Zapier」を流用しつつ、新たに「Amazon API Gateway」「AWS Lambda」「X API」を使用して、自動投稿を実現することにしました。 構成図は以下の通りです。 構成図の【1】【2】については、「 Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた 」の処理をそのまま流用します。 【5】の手順は「 LambdaでX APIを呼び出してみた 」に記載しています。 【3】【6】については別途記事を執筆予定です。 「【4】API GatewayからLambdaを呼び出す」が今回の説明範囲になります。 「Amazon API Gateway」とは AWSのサービスの一つで、これを利用することで簡単にAPIを作成することができます。 API Gatewayが窓口となり、受け取ったリクエストを他のAWSサービス(今回はLambda)に渡して処理を行います。 APIの作成と同時に、APIキーを作成することもできます。(今回、ZapierからAPI Gatewayを呼び出すような構成にしたのは、APIキーによる認証をしたかったというのも理由の一つです) 「Amazon API Gateway」から「AWS Lambda」を呼び出す Lambda関数は既に作成済みであることを前提として説明します。 Lambda関数の作成方法については、以前投稿した「 LambdaでX APIを呼び出してみた 」を参照してください。 API Gatewayの作成 作成したLambda関数の「関数の概要」の「トリガーを追加」を押下します。 以下の内容でトリガーを追加します。 「API Gateway」を選択 Intent:Create a new API API type:REST API Security:API Key これでAPI Gatewayが作成されました。同時にAPIキーも作成されています。 このAPIキーを知っている人のみが、今回作成するAPIを利用できます。 APIキーは、ZapierからAPIを呼び出す時に使用します。(構成図の【3】で使用します) 作成したAPIキーは、コントロールパネルの左メニューの「APIキー」から確認できます。 メソッドの作成 作成したAPI Gatewayのリソースでメソッドを作成していきます。 「メソッドタイプ:ANY」のメソッドがデフォルトで作成されていますが、こちらは不要なので削除します。 以下の内容でメソッドを作成します。 メソッドタイプ:POST 統合タイプ:Lambda関数 Lambda プロキシ統合:無効 Lambda 関数:作成したLambda関数を選択 「API キーは必須です」にチェックを付ける Lambdaプロキシ統合とは、APIのリクエスト時の様々な情報をLambda関数のeventパラメータに渡してくれる機能です。(詳細: プロキシ統合のための Lambda 関数の入力形式 ) Lambdaプロキシ統合を有効にすると、APIのリクエストパラメータとLambda関数のeventパラメータのキーをマッピングしなくて済むため便利なのですが、「 LambdaでX APIを呼び出してみた 」で実装したLambda関数の修正(eventパラメータから値を取り出す辺りの処理の修正)が必要となってしまうため、今回はLambdaプロキシ統合を無効にして進めたいと思います。 統合リクエストの編集 統合リクエストのマッピングテンプレートを編集していきます。 統合リクエストとは、API Gatewayからのリクエストを受け取り、必要に応じて変換してバックエンド(今回はLambda)に渡すものです。 統合リクエストのマッピングテンプレートを編集することで、APIのリクエストパラメータとLambda関数のeventパラメータのキーをマッピングすることができます。 統合リクエストの「編集」ボタンを押下します。 マッピングテンプレートに以下を入力します。 コンテンツタイプ:application/json テンプレート本文:{“text”: $input.json(‘$.hoge’)} 統合リクエストのマッピングテンプレートに上記を記載することで、JSON形式でAPIのリクエストパラメータを受け取れるようになります。 curlコマンドで表現すると以下のような形です。 $ curl https://aaa/bbb/ccc -X POST -H "Content-Type: application/json" -d '{"hoge":"値"}' 「hoge」で受け取った値が「 LambdaでX APIを呼び出してみた 」のLambdaのコードの39行目の「t ext = event['text'] 」に渡るようなイメージになります。 統合レスポンスの編集 統合レスポンスとは、バックエンド(今回はLambda)からのレスポンスを操作するものです。 今回はLambda関数からのレスポンスをそのままAPIのレスポンスとして使用するため、マッピングテンプレートを削除します。 統合レスポンスの「編集」ボタンを押下します。 マッピングテンプレートの「削除」ボタンを押下します。 統合レスポンスのマッピングテンプレートを削除することで、「 LambdaでX APIを呼び出してみた 」のLambdaのコードの51~60行目のreturnの内容が、APIのレスポンスとしてそのまま返却されるようになります。 APIのデプロイとテスト API Gatewayの設定が完了したので、「APIをデプロイ」ボタンを押下してAPIをデプロイしてみます。(Lambdaについてもデプロイが実施されていることを確認してください) デプロイが完了したら、テストをしてみます。 リクエスト本文にJSON形式でリクエストパラメータを入力し、テストを実行します。 Xに文章が投稿できていれば成功です! おわりに 統合リクエストと統合レスポンスの設定周りがよく分からず苦戦しました。 上記のテスト機能を活用し、設定を少しずつ修正しながらテストを繰り返し実施することで、やりたいことを実現することができました。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
概要・はじめに こんにちは。2023年9月にエンジニアとして中途入社した細野です。 社内では、会員システムグループでニフティポイントクラブの開発を担当しています。 この記事では、ニフティへ入社して感じたギャップや、会社や配属先のチームの特徴などについて紹介させていただきます。 簡単な自己紹介 前職では、SIerエンジニアとして約2年半勤務しておりました。 React、Rails、AWSの技術を中心に、複数案件の開発に携わらせていただき、その中で、案件内の開発リーダーや、新人エンジニアの育成などを担当していました。 ニフティへの入社の決め手は、IT転職サイトのカジュアル面談を通して、ニフティのサービスや社員に魅力を感じた為です。カジュアル面談後、ご縁があって、昨年ニフティへ中途入社しました。 ニフティポイントクラブの簡単な紹介 「ニフティポイントクラブ」は、運営実績20年以上、累計会員数330万人以上のポイントサイトで、普段のネットでのお買い物をニフティポイントクラブ経由で利用することでポイントを貯めることができ、貯めたポイントは現金や電子マネー、インターネット回線使用料に交換することで、お客さまの生活をもっとお得にできるサービスです。 詳細については、 「ニフティポイントまるわかりガイド」 を良ければご覧ください。 開発環境/技術 ニフティポイントクラブでは以下の技術を使用しています。 クラウド:AWS、ニフクラ 仮想化環境:Vagrant、Docker DB:Oracle、PostgreSQL OS:CentOS 言語:Ruby、TypeScript フレームワーク:Ruby on Rails、Vue.js(Vue3) 構成管理:Terraform、Itamae バージョン管理:Git、GitHub、GitLab チームでは、フロントエンド担当やバックエンド担当などで分業せず、チーム全体で臨機応変に開発しています。 ニフティで働いてみて感じたギャップ [1] 想像以上にスクラム開発が中心であること 今まではウォーターフォール寄りの開発が多かった事もあり、ニフティでは、スクラム開発の文化がとても根付いているように感じました。 前職でも開発スケジュールの作成、定例MTG(進捗共有・振り返り)などは実施していましたが、スクラム開発で言うところのベロシティ(チームの平均的な速度や作業量の指標)を測ったり、チーム全員で計画を立てるという活動などは、あまり取り入れられていなかったように思います。 ニフティでは、 「スクラムガイド」 を参考に、積極的にスクラム開発を導入している為、スプリントプランニング、スプリントレビュー、レトロスペクティブなど、各スクラムイベントを通して、チーム全員でスクラム開発をしているという実感が強いです。 [2] 社内イベントが多く、学びや交流の場が多いこと ニフティでは、社内交流会や勉強会が活発に開催されています。私も、DDD(ドメイン駆動開発)ギルドに入り、社内勉強会の運営に携わらせていただきました。 私たちのチームでは、DDDの書籍出版や勉強会等で有名な外部講師の方を招き、ドメインの設計やコーディングの配信する勉強会を開催しました。こちらは後日ブログにアップされる予定です。 他チームのイベントの例としては、AWS、Azure、GCPの3つのクラウドサービスについてハンズオン形式の勉強会や、社内システムのドメイン勉強会など、定期的に様々なイベントが企画されています。 また、学びの場だけでなく、チームや全社の交流会イベントも定期的に開催されていたりします。勿論強制参加とかではありません。 [3] たくさんのシステムの開発・運用を経験できること 配属直後にオリエンテーションと一緒にオンボーディング資料を共有頂いたのですが、最初は社内用語を含めた大量のドメイン知識と、システムアーキテクチャの複雑さに対して、理解が全然追いつきませんでした。 社内用語は、Notion QAを使用したり、チームメンバーから教えて頂くことで、自然と慣れていきました。 システムの理解については、まだ全体的に理解が浅いと感じています。 レガシーシステムでは良くある話なのかも知れませんが、ブラックボックス化されてしまっている仕様や処理が所々あり、 「エンジニアに与える認知負荷の高さ」 が開発チームの大きな課題の1つだと捉えています。 現在は、DDD(ドメイン駆動開発)やDaC(Document as Code)などのプラクティスを通して、チーム全体で積極的にアイディアを出しながら、改善活動に取り込み始めています。 ポイント開発チームの主な作業 ポイント開発チームでは、主に以下に関する作業を行ってます。 [1] 事業開発 ビジネスサイド側の依頼内容に沿った開発行います。依頼内容には、1人月を超えるような規模の新規機能の開発や既存機能の改修を進めていきます。 開発物としては、ユーザー向けの機能追加が多い印象です。 [2] システム部開発 こちらはシステムの課題に沿った開発を行います。例としては、システムのモダナイゼーションやコストカットの為の開発などがあげられます。 DB移行やライブラリのバージョンアップ、サーバーの棚卸など内部的にシステムを作り替えたり、コストカット施策の立案〜開発までを進めていきます。 [3] 運用保守 基本的には1人月の範囲で収まる規模の既存機能の改修やパッチ適用の他、問い合わせに依頼が上がった場合の既存機能の調査対応などを行います。 おわりに この記事では、私がニフティへ中途入社して感じたギャップや会社やチームの特徴などについて紹介させて頂きました。 最初は覚える事のボリュームの多さや、チームメンバーの活躍する姿を目の当たりにして「本当にやっていけるかな?」と何度も思ったりしました。 ただそんな中でも、周りの方に支えられ、少しずつチームに貢献出来るようになってきているように感じます。仕事において「何をやるか」も大切ですが、「誰とやるか」はそれ以上に大切な要素だと言えるのではないでしょうか。 そしてニフティには、知識、経験共に「この人と一緒に働きたい」と思えるような人が多いように感じました。 現場の技術的負債も多く、大変だと感じる面もありますが、システムのモダナイゼーションを進める事で得られる経験は、とても良い経験になると思っています。 私自身もいち早く皆さんのお手本社員になれるように、今後もスキルを磨き続けていきたいと思います。 もしニフティの開発に興味頂いた方は、以下リンクよりお気軽にご連絡ください。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに こんにちは。たかたかと申します。 私は23卒で入社したのですが、社会人一年目ももうすぐ終わりそうな時期に差し掛かっており、時が経つのは早いなぁと思うばかりです。(年齢的には若い筈なのに) さて、私はジョブローテーション最後の配属先である、サービスインフラチームで活動をしていました。 ここでは、ニフティのシングルサインオンや会員管理データベースなど、ニフティのサービスを裏から支えるシステムの運用に携わりました。( こちら に詳しく書かれている記事があるので、興味がある方は参考にしてみてください) その中で特定の条件にマッチした会員データを抽出する機会があり、状況に応じてSQLを考えたりする必要がありました。 私は情報系の勉強はしてきたつもりではいましたが、SQL自体あまり触った経験がなく、この際ある程度マスターしてしまおうということで学習をしたりしていました。 こんな感じの事やってました〜というのを、内容を会社的に問題にならないように改変した上で問題形式にしてみたので、皆さんも考えてみてください。 では問題です 問題1 同じ誕生日の人が身近にいると親近感が湧く方ですか?一種の仲間意識が芽生えたりしますか? ということで、1980-01-01以降の日付で、誕生日毎にカウントを出力するSQLを書いてください。 出力例 birthday | count ------------+------- 1988-03-15 | 4 1990-08-25 | 1 1995-07-09 | 1 1985-04-12 | 1 テーブル例 person_id name birthday 1 takeshi 1985-04-12 2 yumi 1990-08-25 3 hiroshi 1975-11-30 4 sachiko 1988-03-15 5 sacchan 1988-03-15 6 hamako 1988-03-15 7 momoko 1988-03-15 8 kenji 1995-07-09 persons テーブル 解答例 SELECT birthday, COUNT(*) as count FROM persons WHERE birthday >= '1980-01-01' GROUP BY birthday; 余談 COUNT(*)とCOUNT(Primary Key)とCOUNT(1)のどれが一番処理速度が早いかという議論がたまに行われていたりします。調べてみたところ、Primary Keyが良いだったり、どれもそんなに変わらないよと言われていたりします。ケースバイケースかもしれませんが、どれが一番最適なのか考えてみると良いかもしれませんね。 問題2 私はtakeshiさんの誕生日を盛大に祝いたいと考えています。 しかし私はtakeshiさんの食べ物の好みを知らず、このままだと喜んでいただけないでしょう。 そこであなたにはtakeshiさんの好きな食べ物を抽出するSQLの作成をお願いしたいです。 出力例 person_name | favorite_food -------------+--------------- takeshi | Ramen takeshi | Pizza テーブル例 person_id name birthday 1 takeshi 1985-04-12 2 yumi 1990-08-25 3 hiroshi 1975-11-30 4 sachiko 1988-03-15 5 sacchan 1988-03-15 6 hamako 1988-03-15 7 momoko 1988-03-15 8 kenji 1995-07-09 persons テーブル food_id food_name 1 りんご 2 ぶどう 3 カレー 4 おにぎり foods テーブル food_id person_id 1 1 3 2 2 3 1 4 favorite_foods テーブル 解答例 SELECT p.name as person_name, f.food_name as favorite_food FROM persons p INNER JOIN favorite_foods ff ON p.person_id = ff.person_id INNER JOIN foods f ON ff.food_id = f.food_id WHERE p.name = 'takeshi'; 余談 join句辺りは昔の私には何が何だかよく分からない文法でしたね。 left joinやright joinの内容を理解できず頭を抱えていました。 問題3 会社名と学校名をまとめて表示したいのに、別々のテーブルに保存されています。 schoolsテーブルとcompaniesテーブルのnameを合体させたデータを抽出するSQLを作成してください。 出力例 name ----------- xxcompany wwschool wwcompany xxschool bbschool aaschool ooschool aacompany テーブル例 school_id school_name 1 ooschool 2 xxschool 3 wwschool 4 aaschool 5 bbschool schools テーブル company_id company_name 1 xxcompany 2 wwcompany 3 aacompany companies テーブル 解答例 SELECT school_name as name FROM schools UNION SELECT company_name as name FROM companies; 余談 union句の存在を知った時、こんなこともできるのかと衝撃が走りましたね。 最後に いかがでしたでしょうか? SQLは、抽出方法ならまだしも、速度や可読性などを考え始めるとまた奥の深い世界なのかなぁと思います。 それでは、またどこかで会いましょう! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 Tech TalkやMeetUpも開催しております! こちらもお気軽にご応募ください! Event – NIFTY engineering connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
参加登録はこちらから (connpass)スクラムマスターによるチーム改善LT! ニフティのスクラムトーク vol 2 イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! テーマ NIFTY Tech Talkでスクラムにテーマを絞った「ニフティのスクラムトーク」シリーズが始まりました。 今回はVol 2となります。 チーム改善に取り組んだこと かんばんの戦略 認知負荷 過去のニフティのスクラムトークはこちら スクラムマスターの技を磨く! ニフティのスクラムトーク vol 1 / 動画 ( Youtube ) 開催概要 日程:3月26日(火)19:00〜20:00 会場:ニフティ株式会社 オンライン参加の方 YouTube Liveで配信 こんな方におすすめ スクラムマスターの経験がある方 チームの改善に興味がある方 タイムテーブル 時間 コンテンツ 18:30 – 19:00 開場 19:00 – 19:10 オープニング 19:10 – 19:30 LT1: チーム力を高めるスクラム実践法:カンバン公開と課題攻略について 19:30 – 19:50 LT2: スクラムチームと認知負荷 19:50 – 20:00 クロージング 登壇者プロフィール 添野 翔太(登壇者) ニフティ株式会社 会員システムグループ 第1開発チーム 新卒入社7年目。@niftyトップページやニフティゲームズの開発・運用を担当し、日々スクラムマスターとして奮闘しています。また最近はマルチクラウドを題材にした社内研修の講師や運営も務めています。 清水 利音(登壇者) ニフティ株式会社 基幹システムグループ サービスインフラチーム 新卒入社7年目。シングルサインオンシステムのスクラムマスターとして刷新・開発・運用などの活動をしています。 ニフティグループでは一緒に働く仲間を募集中です 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティエンジニアのX(旧Twitter)アカウント NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 NIFTYDevelopers アンチハラスメントポリシー 私たちは下記のような事柄に関わらずすべての参加者にとって安全で歓迎されるような場を作ることに努めます。 社会的あるいは法的な性、性自認、性表現(外見の性)、性指向 年齢、障がい、容姿、体格 人種、民族、宗教(無宗教を含む) 技術の選択 そして下記のようなハラスメント行為をいかなる形であっても決して許容しません。 不適切な画像、動画、録音の再生(性的な画像など) 発表や他のイベントに対する妨害行為 これらに限らない性的嫌がらせ 登壇者、主催スタッフもこのポリシーの対象となります。 ハラスメント行為をやめるように指示された場合、直ちに従うことが求められます。ルールを守らない参加者は、主催者の判断により、退場処分や今後のイベントに聴講者、登壇者、スタッフとして関わることを禁止します。 もしハラスメントを受けていると感じたり、他の誰かがハラスメントされていることに気がついた場合、または他に何かお困りのことがあれば、すぐにご連絡ください。 ※本文章はKotlinFest Code of Conductとして公開された文章( https://github.com/KotlinFest/KotlinFest2018/blob/master/CODE-OF-CONDUCT.md )を元に派生しています。 ※本文章はCreative Commons Zero ライセンス( https://creativecommons.org/publicdomain/zero/1.0/ ) で公開されています。
NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 ニフティグループの社員が業務を通じて学んだことを発信しています! スクラムにテーマを絞ったシリーズとして、「ニフティのスクラムトーク」を開催しています。 隔月での開催を予定しています。 connpassでNIFTYグループのメンバー登録をしていただくと、次回以降のイベント情報を受け取ることができます。 connpass: NIFTY Developers NIFTY Tech Talk ニフティのスクラムトーク 【2024/01/30】 スクラムマスターの技を磨く! ニフティのスクラムトーク vol 1 【2024/03/26】 スクラムマスターによるチーム改善LT! ニフティのスクラムトーク vol 2 【2024/05/28】 TBD ニフティのスクラムトーク vol 3 【2024/07/23】 TBD ニフティのスクラムトーク vol 4 【2024/09/24】 TBD ニフティのスクラムトーク vol 5 【2024/11/26】 TBD ニフティのスクラムトーク vol 6
はじめに まだギリギリ新卒1年目の中井改めstatiolakeです。エンジニアブログでは Rust関連の投稿 をさせていただいたりしていますが、改めましてRust大好きマンです。よろしくお願いします。 さて、実は私もこのたびのエンジニア定例合宿に参加させていただき、チーム†skyscraper†としてオリンピックを盛り上げるサービスを作るなどしました。チーム内では私はバックエンド開発グループ(総勢1名)に所属していました。せっかくなので、記憶が新しいうちに(といっても一ヶ月は経ってしまったのですが)どういう感じだったのかをつらつらと書き残しておこうかと思います。 エンジニア定例合宿ってなんぞ? †skyscraper†って誰? という方は、以下の投稿を先にご覧ください! エンジニア定例とは: https://engineering.nifty.co.jp/blog/25125 †skyscraper†の制作物: https://engineering.nifty.co.jp/blog/25226 チームの開発体制について 改めて、私たちのチームの開発物の構成はこのようになっていました。 フロントエンドにSvelteを採用し、バックエンドにはRustを採用しています。Rust側がAPIを提供し、フロントからはそれを呼び出してJSONでレスポンスを受けるという構造です。 ふたを開けてみると、他のチームではこのように明確にフロントエンドとバックエンドを分離しているところはなかったように思います。確かにツールや言語も増えますし、APIのインターフェースなど、短期的には考えることが少し増えますよね。そういう意味では、この構成でハッカソンを走りきれたことには達成感もあります。ただ、開発中も完成しない危機感を感じることは全くありませんでした。それはフロントエンドチームの技術力の高さと、実際に走りきるための色々な工夫があったからこそだと思っています。 フロントエンドチームの技術力の高さ まず最初に断っておきたいのは、これから書き残す諸々の工夫がしっかり効果を発揮したのはフロントエンドチームの技術力の高さゆえです。バックエンド側として私が決めたAPIの仕様をいい感じにエスパーして勝手に使ってくれましたし、APIの新しい実装が出来上がったときもすぐチェックしてもらえました。とにかく余裕をもって作業を進めてくれましたおかげで、バックエンド側も次へ次へとスムーズに進んでいきました。これめちゃくちゃやりやすかったです。ありがとうございます。 走り切るための工夫 さて、 チーム側の参加記 では、次の点を開発における工夫に挙げさせてもらいました。 フロントエンドとバックエンドを効率よく完全に分業する。 フロントエンド側のUXにかなりの比重を置き、粗の少ないユーザー体験を作る。 「最低限の機能」を十分小さく定義し、動作する状態をキープしながら拡張を続ける。 この記事では、1つ目の「効率よく完全に分業する」点、そして3つ目の「動作する状態をキープしながら拡張を続ける」点について、自分が考えたことだったり貢献した部分だったりを書き残そうと思います。2つ目の部分は走り切るための工夫というよりはクオリティのための工夫で、少しでも理想形に近い完成度でゴールするためという意味合いが強いためです。 ちなみに、ここでいう最低限の機能というのは「デモで動作が破綻しない程度にとりあえず動く状態のもの」という意味合いです。例えばデモでちゃんとしたものに見えるとか、はたまたお客様へのリリースに耐える品質であるとか、そういう「担保したい品質のボーダーライン」的な意味では考えていませんでした。むしろ完全に開発の都合側の概念で、短期間の開発で品質を最大化するために、1ステップ1ステップのギャップを最小化しようとした、という意味で捉えていただけると幸いです。 工夫1: 完成像の雰囲気と開発の流れを全員が完全に理解しておく これはまあ合宿が始まる前の話ですが、事前のアイデア出しの段階で3人の中の完成像を完全に一致させるように話し合いを重ねました。合わせて全体の開発の流れも全員で共有していました。以下はその話し合い中のメモです。 求める機能全体をいい感じの小さい機能に分割し、実装優先順位を決めて番号を振りました。当日は番号順にできるところまで進めるという方針を取りました。実際に進んだのは4まで、5がベータ版として実装されていたくらいです。でも、仮に最初から4まで一気に完成させようとしていたら多分うまくいかなかったと思います。見せられるものがなくて焦ってしまったり、進捗が合わなくてつなぎこみに失敗したりするからです。 そして、機能を分割した後に、各機能の具体的な内容をすり合わせていきました。遷移はこんな感じ、画面構成はこんな感じで、こういう機能は存在しないので考えなくて良くて、… などの内容です。そのおかげで、完全に分かれて作業してもAPIの機能に関する認識が一致し、つなぎこみに失敗することはありませんでした。 工夫2 重たい仕組みを必須要件から削除する 我々が普段見るWebサービスのほとんどは、だいたいログイン機能があり、何かしらデータを送信する機能があって、それを閲覧表示する機能があります。これらの機能のためにはデータを保存する仕組み、データベースが必ず必要になります。そしてログイン機能を実装するためには、ログイン画面やユーザー登録画面を実装する必要があるでしょう。 データベース、新しい画面、認証、ログイン処理、これらはサービスの実装の中でかなりのウェイトを占めてしまいます。私たちのように経験が浅いとなおさらです。これらを必須機能に要求してしまうと、いつまでたっても最初の機能するバージョンが完成しないということになりかねません。 今回私たちが取った戦略は、これらの重たい要素を極力排除することです。 私たちの画面にはログイン画面のような画面があります。しかし、これは厳密にはログイン画面ではありません。ユーザー登録がないからです。 入力された名前と秘密の言葉は、適当な文字列に加工され、これをシード値としてビンゴカードを生成します。生成されたビンゴカードを保存することすらしません。毎回生成し直します。このようにすると、利用者についてサーバー側で保存しておくべき情報は何もありません。 当初は、ビンゴカードのマス目をクリックして自分で開ける操作ができるようにするとか、ビンゴカードのマス目を自分で変更して自分好みのカードを作るなどの機能も想定されていました。これらはデータベースが必要になるという理由でコア機能としては見送りました。それくらい軽量化にはこだわったつもりです。 ここまで仕様を簡略化すると、フロントエンド側はこのログイン風画面とビンゴカードそのものの画面だけを作成すれば完成します。画面の遷移も「ボタンを押したらビンゴカードに移動する」の一言で済んでしまいます。残った時間をすべて、いちばん大切なビンゴカードの画面に費やすことができるようになります。 一方のバックエンド側も、データベースのテーブル設計・モデル設計をスキップし、接続周りの様々な面倒からも開放され、ビンゴカード生成ロジックに集中することができます。 ちなみに、最終的には一応データベースも利用しています。ある時点での試合結果の情報がないと、どのマスが空いているかを決定できないからです。一応ここでもデータベースを回避するために、デモ用に適当なAPIを生やしてインメモリでごまかすという逃げ道を考えてはいました。デモだけであればこれで十分で、小さく作る次の一手としてはこの歩幅でも良かったと思います。ただおかげさまで十分な時間的余裕があったので、順当にデータベースをセットアップすることができました。まあ案の定、色々と時間を溶かしてしまいましたね。でも焦りはありませんでした。常に見せられるものができている状況というのは本当に開発しやすいです。 工夫3: APIの仕様を何よりも一番最初に決め、モックを作る さて、いい感じに成果物の要件について思いを巡らせることができたら、次は実装です。スムーズな開発のため、APIのインターフェースとモックをなるべく早く決定しなくてはなりません。事前にある程度認識を固めてあるとはいえ、プロパティ名まで含めた具体的なデータ形式があったほうがフロントエンド側が気にすることが減ります。ここはスピードが重要です。 注意することは、後々の変更にも耐える、なるべく破壊的変更を起こさないインターフェースにすることです。そもそも最初から完璧なインターフェースが設計できればそれに越したことはないのかもしれませんが、短期の開発、アジャイル開発では、仕様を素早く確定してしまうことのほうがはるかに重要です。足りない情報はインクリメンタルに修正していけばよいからです。しかしだからといって、APIを修正するたびに既存のコードを書き換えなければいけなくなってしまっては大問題です。 例えば、私が最初に考えていたビンゴのマスのレスポンスの構造はこのようなものでした。 { "sport": "Archery", "event": "Mixed Team", "team": "JPN" } 競技、種目、チーム名、この3つでひとマスが表せるので、最初の機能としてはこれで十分です。しかし、もしかしたらそれぞれのプロパティに後々追加の情報を足したくなるかもしれません。そのようなとき、今まで文字列だったものがオブジェクトになってしまうと困ったことになります。 少し考えて、代わりに下のようなインターフェースで決定しました。 { "sport": { "name": "Archery" }, "event": { "name": "Mixed Team" }, "team": { "ioc": "JPN" } } 一見冗長なようですが、これで例えばスポーツ名の日本語名を足したくなっても name_ja のような項目を別に足すだけで済み、その時に既存のコードには影響を与えません。 実際、こうしておいてよかったです。開発中、チームの詳細情報を横に補足として表示するというアイデアが出て、最終的には次のようなレスポンスになったからです。 { "sport": { "name": "Archery" }, "event": { "name": "Mixed Team" }, "team": { "ioc": "JPN", "country_ja": "日本", "country_en": "Japan", "first_olympics": "1912", "remarks": "1956年冬・1960年夏 : GIA / 1960年冬 : JAP" } } アジャイルな開発では、次の機能に取り掛かるたびに必ずAPIの改修が発生します。その時に一度も破壊的変更を起こさないで済んだことも、私たちの開発がスムーズに進んだ理由だと思います。 工夫4: Rustで実装する あー待ってください。閉じないで。Rust大好きマンとして少しだけ語らせてください。 今回バックエンドにRustを選択したのにはいくつか理由があります。まず自分が好きだからというのが9割。そしてビンゴカードの生成処理のような重たい処理をリクエストごとに走らせる必要があったことが0.5割。バグを起こしづらいことが0.5割です。すなわち、上述の工夫を可能にし、スムーズな開発を裏で支えてくれたのがRustなわけです。 今回、データベースをなるべく排除した以上、ビンゴカードの生成処理を毎回走らせることが必要になってきます。いや、仮にデータベースに保存していたとしても、初回は必ずこの生成処理が走ってしまいます。そのビンゴカードに対してリーチやビンゴの判定も毎回行うことになります。そこをRustがネイティブバイナリの力を生かして最速で実行してくれました。本当に助かります。 また、Rustがバグを起こしづらいというのは、そこに非常にしっかりと型がついているからです。 たとえば、私が愛してやまないRustの機能の一つにenumがあります。Rustのenumは多くの他の言語のenumとは異なり、構造体的なものを一緒に持つことができます。tagged unionや代数データ型と呼ばれるものですね。今回のビンゴのセルもこのenumの力を多分に発揮しました。具体的には、セルの型は以下のように定義されています。 pub enum BingoCell { Free, Normal { sport: SportName, event: EventName, team: TeamIoc, opened_at: Option<i32>, }, } これの意味するところは、ビンゴのセルは中央の「Free」マスであるか通常のマスであるかの二通りがあるということです。これにより、Freeマスなのにスポーツやイベント名が設定されているとか、通常マスなのにそれらが設定されていないということはなくなります。 ここで嬉しいのは、コードの他の部分でビンゴのセルを扱うときは必ず条件分岐でどちらの値が入っているかを確認しなければならないことです。FreeかNormalかを確認することなく勝手にsportやeventにアクセスすることはコンパイル時エラーになります。 他にも細かい場所として、opened_atは、そのマスが何ターン目に開いたかを持ちます。もちろんまだ空いていない場合もありますから、これをOption<i32>型とすることで「空いていたらターン数、空いていなければNoneという特別な値」で表現できるようになります。そしてこれも、Noneでないかを確認せずに数字にアクセスすることはできません。プログラマーは、使う箇所で必ずNoneかもしれないことを意識させられることになります。 ちなみに、バグりにくいもう一つの理由として、テストが好きなところにかけるからというのもあります。Rustには組み込まれたテストシステムがあって、書いているファイルにそのままテストを書くことができます。今回もちょこっとだけですがテストを書いたりしました。普通ハッカソンでテストをかくのもなかなか難しいと思うのですが、強みが出ましたね。 リーチやビンゴの判定はビット演算を使ったちょっと複雑なロジックになっているので、以下のように、ちゃんとリーチやビンゴが判定できているかどうかをとりあえずチェックしています。全く網羅性はありませんが… // : // : // ここまで普通にBingoCard型の実装とかが書いてある // 唐突にこうやって追加すればその場でテストになってくれる #[cfg(test)] mod tests { use super::*; #[test] fn test_bingo_new() { let mut rng = rand::thread_rng(); let bingo = BingoCard::generate(&mut rng); println!("{:?}", bingo); } #[test] fn test_bingo_check() { let mut rng = rand::thread_rng(); let mut check = |order: &[(usize, usize)]| { let mut card = BingoCard::generate(&mut rng); assert!(!card.is_bingo()); let rest = &order[..order.len() - 2]; let reach_last = order[order.len() - 2]; let bingo_last = order[order.len() - 1]; for &(i, j) in rest { card.open(i, j, 0); assert!(!card.is_reach()); assert!(!card.is_bingo()); } card.open(reach_last.0, reach_last.1, 0); assert!(card.is_reach()); assert!(card.reach_last_cells().get(bingo_last.0, bingo_last.1)); assert!(!card.is_bingo()); card.open(bingo_last.0, bingo_last.1, 0); assert!(!card.is_reach()); assert!(card.is_bingo()); }; check(&[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4)]); check(&[(0, 3), (1, 3), (2, 3), (3, 3), (4, 3)]); check(&[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]); check(&[(0, 4), (1, 3), (2, 2), (3, 1), (4, 0)]); } } おわりに この記事では、今回のハッカソンでフロントエンドとバックエンドを完全に分離して作業を進めるために考えた工夫について書き残しました。全員が完全に成果物のイメージを持っていたことで、フロントとバックのすり合わせのための会話はほとんどなかったです。きれいに分割したことでお互いにコンフリクトを起こすことなく自分の責任範囲を実装できました。また、重たい仕組みを排除してできた時間で、フロントエンド側は一生懸命デザインに凝ってくれました。自分もそれに横から口出ししたりして、本当にクリエイティブな時間だったと思います。とてつもなく楽しく、貴重な時間でした。 ところで、振り返ってみると、これらの工夫はこの一年間のOJTを通じて培った知識でもあります。例えば、常に完成品を持っておいて小さな改善を続けるという開発は、ニフティで広く取り入れられているスクラム開発(アジャイル開発)の思想です。何としてもモックを先に作るぞという気概は、ニフティニュースの開発チームにお邪魔していたときに先輩方から学んだことです。サービスやAPIの設計も、色々なチームで設計に携わらせてもらった成果です。改めてこの一年間で多くのことを学んだなと思いました。 さて、もう1ヶ月もしないうちに2年目になるらしいです。これからもまだまだ新人として、たくさんのことを吸収していきたいと思います。一方、自分も先輩になるという側面もあります。来年度の新人に少しでもなにか良い影響を与えられるよう、精進していきます! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
 こんにちは。ニフティ株式会社、新卒2年目の小林です。  弊社では新人のスキルアップを目的として、エンジニア定例と呼称される勉強会が毎年行われています。その集大成として開催された3日間の開発合宿、通称「エンジニア定例合宿」に参加しました。  詳細は「 4年ぶりのリアルハッカソン合宿@ノジマ大磯スクウェアに行ってきました! 」 をご覧ください。  私たち新卒2年目は、前年、未曾有の災禍によりエンジニア定例合宿が開催出来なかったため、新卒1年目の方々と一緒に参加する形となりました  この記事では、「ニフティ社内全ブカツまとめサイト」というテーマに取り組んだ過程と結果について共有します。 メンバーについて 小林 普段の業務:@nifty光などの回線サービスの開発・運用 今回の担当:機能の実装やレイアウト、DB操作(CRUD)周り なにか一言:経験の浅い技術に着手出来て面白かった。ご飯が美味しかった。 柴田 普段の業務:「マイ ニフティ」(iOS, Android, バックエンド)の開発・運用 今回の担当:環境構築やTerraformを用いたIaC化 なにか一言:大磯おいしそ〜〜 テーマについて テーマ 「ニフティ社内全ブカツまとめサイト」 背景  ニフティ社内には「ブカツ」と呼ばれる制度があります。こちらは一般的に言うと部活動のようなもので、ニフティでは社内間コミュニケーションアップを目的として費用補助を頂きながら活動をすることが出来ます( 社内交流制度 )。  例えばフットサル部やボルダリング部などのアウトドア系のブカツから、ボードゲーム部やeスポーツ部などのインドア系のブカツが設立されています。  私たちもブカツに参加しているのですが、他のブカツ含めて「いつ行われているか分からない」や「気になってはいるが、ブカツに参加する際の手順が分からない」などの懸念が見受けられました。  そのため、円滑にブカツの管理が出来るように「ニフティ社内全ブカツまとめサイト」を作成することに決定しました。 成果物について WEBサイト  期間内では左端のようなまとめサイトを作成することが出来ました。  事前に作成しておいた記事一覧が最新5件で表示されるようになっており、題名やアイキャッチ画像、開催日時、いいね数、概要を記事ごとにこの画面では見ることが出来ます。他にもブカツ一覧や申込フォームがあります。  また、「記事の投稿」ボタンを押下すると、ブカツの選択を経て参加メンバーや活動記録(記事)を登録することが出来たり、左端のページより記事をクリックすると簡素にはなりますが活動内容の詳細なページが表示されます。 インフラ構成  インフラストラクチャについては画像の通りとなり、AWSのAmazon ECS(Fargate)やAmazon RDSを用いてWEBサイトの構築を完了させました。  また、Terraformを用いてIaC化を図ることで、運用負荷の削減やリソース全体の管理をより効率化することが出来ました。 3日間の過ごし方 1日目 簡単なハイレベルアーキテクチャをホワイトボードに5分程度で作成 要求定義 ブカツ一覧が見たい 説明が欲しい 活動報告が見たい クラウド上で運用したい … 役割分担 フロントエンド(Next.js × TailwindCSS) バックエンド (Next.js × Prisma) インフラストラクチャ (AWS × Terraform) Next.js のチュートリアルの実施 Devcontainerを含めたコンテナ上環境構築 2日目 DB設計 記事マスタとブカツマスタを作成 実装 ローカル環境にてDB接続を実現し、DB操作も実装 READ:記事一覧の取得 CREATE:記事の投稿 UPDATE:いいね機能 DELETE:実装せず 最新5件の記事を表示と、ブカツ紹介や活動報告ページへの遷移 TailwindCSSを用いたレイアウト修正 インフラ構築 Terraformを用いてAWSリソースを作成 WEBサイトへアクセスが出来ることを確認 Amazon RDSへの疎通を確認 遊び心で記事にいいね機能を追加 記事がいいねされた際は、Incoming Webhookを用いてSlack上に通知されるように 3日目 発表用資料作成 時間を鑑みて追加機能の実装は困難と判断 工夫したところ 現在のDBマスタからブカツ一覧を取得するように  DB設計を経て作成したブカツマスタを用いて、ブカツ一覧を一元管理できるようにしました。その結果、ハードコーディングを回避することが出来ました。 Terraformを用いたIaC化  「インフラ構築」にもある通り、AWSリソースの構築はTerraformにより実現しています。当初は「ローカル実行でもいいから実際に操作できたら嬉しい」という会話をしていましたが、2年勤務した中で学んだTerraformの経験を活かすため、挑戦することにしました。  まず、「一旦1から作成してみるか 」で積極的に行動できたことが素晴らしく、そしてエラー解消に数時間苦悩しつつも乗り越えたことで、とても良い経験が出来たと思います。 いいね機能とSlackへの通知機能の実装 荒ぶるSlack通知  2日目が終了する数十分前までは、Slackへの通知機能を実装しようという考えはありませんでした。  活動のモチベーションをあげることを目的としていいね機能を作成した後、「みんなが触るタイミングで通知機能あったら面白くない?笑」から始まり、Slackの機能であるIncoming Webhookを用いて実現しました。とても簡単に実装ができ、数分程度で完成させることが出来ました。 学んだこと 時間配分の難しさ  後悔していることの1つに、「もっと機能を実現できたのではないか」と考えています。  短期間でサービスとして完成させる上では、ある程度の「見た目」は必要だと思っており、「1機能が完成するとTailwindCSSを用いてレイアウトを作成し、またある1機能が完成すると…」の繰り返しで実装していきました。  この時点で想定以上の時間がレイアウトの作成に掛かったのは事実であるため、3日目にはレイアウトを考慮せずに機能の実装に時間を掛ければ良かったのかもしれないと思ってしまいました。  しかし、今回のエンジニア合宿については、皆さんに触って頂いた際に評判がとても良かったため、見た目にもこだわって良かったと嬉しさを感じています。この経験を通して、実際の業務については納品先が求めていることを明確化し、時間配分を見極めたいと思います。 ORMを用いたDB操作とサーバサイドとの連携  今までは、DB操作についてハンズオンや既存のコードを拡張することでのみ触れてきましたが、実際に0からサービスを作る過程でORMを用いてサーバサイドから操作が出来るように新規作成することは今まで行わなかったため、面白い体験になりました。  公式のドキュメントを参照しながら実装し、実現出来た時はとても嬉しかったです。 0からAWSリソースを作成する経験ができた  業務でAWSやTerraformを触ることはありますが、既存に構築されている環境への追加や修正に限っていました。今回ハッカソン形式で開催されたことで、よく使われるAmazon ECS, Amazon RDSの構成を0からAWS環境を構築する経験が出来たため、今後の業務にも活かしていきたいと思います。 まとめ  本記事を読んでいただきありがとうございました。  メンバーとして2人体制で行ってはいましたが、実用的に活かせるサービスを作れたと自負しています。もう少し時間があれば実際に運用が出来るサービスとして確立出来るかもしれません。  今回のエンジニア合宿では他の業務を大きく意識せず、特定のプロダクトの開発にのみ着手することが出来たこともあり、ある1つのサービスをインフラも併せて完成まで導けました。その経験はとても大きいものだと思っています。その反面、色々な業務を並行して遂行していく際のスケジュール管理についても同時に考慮することができたため、工数としての尺度として今回の体験と比較が出来ることを期待しています。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
0. はじめに 1. ブース 1-1. リアルニフくじブース 1-2. コードレビューブース 1-3. @nifty光 10ギガ体験ブース 1-4. フォトブース 1-5. アンカンファレンスブース 1-6. Ask the Speakersブース 1-7. キャリア相談ブース 2. オンライン配信の裏側 3. まとめ 0. はじめに こんにちは! ニフティでは、2023年11月18日(土)にテックカンファレンス「NIFTY Tech Day 2023」を初のハイブリッド開催で実施しました。 今回は、セッション以外で当日会場で実施されていたブースの様子や、オンライン配信の裏側をお届けします! NIFTY Tech Day 2023 セッション編のレポートは こちら アーカイブ動画の再生リストは こちら 発表資料は こちら 1. ブース 開催日に会場へご来場された方は既にご存知だと思いますが、開催当日は 7 ブースと大変多く、盛況でした! 今回は各ブースの様子をご紹介します 1-1. リアルニフくじブース ニフティでは、 ニフくじ というキャンペーンを実施しております。 ニフくじとは、@niftyトップページ(PC/スマホ)、会員向けアプリ「マイ ニフティ」で引けるくじで、ニフティポイントがゲットできるキャンペーンです。 そんなニフくじを NIFTY Tech Day 限定でリアルで引けるようにしちゃいました! 今回の景品はモバイルバッテリーやTech Day Tシャツ・Tech Day 靴下など盛り沢山でした。 どの景品も今回限りの限定品のため、とても貴重ですね。 リアルニフくじの景品の写真!左から靴下・モバイルバッテリー・Tシャツとなります。 私の推しはモバイルバッテリーです 1-2. コードレビューブース エンジニアの業務にはコードレビューが付き物! よくない実装を来場者の方にレビューしていただくコードレビューブースです。 今回レビューをしていただいたコードと解答は以下の記事から確認できます! NIFTY Tech Day 2023 【出題編】コードレビュー問題 〜このReactコードを改善せよ〜 NIFTY Tech Day 2023 【解答編】コードレビュー問題 〜このReactコードを改善せよ〜 コードを見ていただいてと思った場所には付箋でレビュー内容を書いていただきました。 コードレビューブースでレビューをする皆さん。 わいわいとしており盛況でした! コードレビューブースで掲載されていたコードはこちら。 付箋同士でも議論されていますね! 1-3. @nifty光 10ギガ体験ブース 3つ目のブースは弊社商品である @nifty光 10ギガを実際に体験していただけるブースです! こちらでは、1ギガ回線と10ギガ回線の比較が可能となっております。 私も会場事前準備の時に体験してみましたが、10ギガの速さには圧倒されました…! 10ギガ体験コーナーの実際の様子です! 右奥にあるモニターに映っているセッションを視聴しながら10ギガを体験できます。 1-4. フォトブース NIFTY Tech Day 2023 参加の記念に特製パネルと一緒に撮影ができるブースです! こちらのブースでは参加者はもちろんのこと、登壇者が記念に撮影をしておりました 開場の前にフォトブースの様子を撮影 こうして写真で見るとパネルの大きさを感じますね。 フォトブースの隣にあるサイネージでTech Day 2023 実行委員であるお二人のツーショット! サイネージもTech Day 2023 仕様となっておりました。 1-5. アンカンファレンスブース 当日の午前には、参加者自身でテーマを提案していただき議論していくアンカンファレンスブースが開かれていました! アンカンファレンスのテーマとして、以下内容がテーマとなっていました。 Goのエラーハンドリングについて Rustの言語仕様について Vim VS VSCode …その他いろいろ 「その他いろいろ」ではどのような内容が議論されていたのでしょうか…? 1-6. Ask the Speakersブース 当日はセッション内容の質問の場として、slidoをご用意しておりました。会場では直接会話ができるAsk the Speakerブースもございました! Ask the Speakersブースではスクラムのトークなど様々繰り広げられてました。 こういった直接的な交流ができるのも会場開催の良いところです! 1-7. キャリア相談ブース エンジニアお悩み相談室と称して、将来のキャリアだったり、悩んでいる内容をニフティのベテランエンジニアに何でも聞けちゃうブースです。 昼食のお弁当を食べながら会話をされているようでした! 2. オンライン配信の裏側 今回はハイブリッド開催! ということで、YouTubeにて配信をしていたのですが、今回は配信の様子もチラッとご紹介! こちらは2レーンに変更した後の1レーン配信を管轄している様子。もう1つのセッションでも同様の設備となっております。 前日の準備ではカメラ位置の微調整だったり各種機器の動作確認・リハーサルなど多岐に渡り対応されていました! 初のハイブリッド開催にも関わらず、予定通りに進行できたのも配信チームのおかげです。頭が上がりません。 別の視点からも 裏方の仕事、かっこいいですね! 3. まとめ そんなこんなで、NIFTY Tech Day 2023 のレポートは以上となります! これからも、技術イベントを開催していきますのでぜひご参加ください〜。 connpassに登録くださいますと、イベント開催通知をメールで受け取れますのでぜひ以下のURLよりご登録ください! ニフティ株式会社のconnpassはこちら: https://nifty.connpass.com/ ここまでお読みいただきありがとうございました! ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
イベント概要 NIFTY Tech Talkは、ニフティ株式会社の社員が主催するトークイベントです。 本イベントでは、ニフティグループの社員が業務を通じて学んだことを発信しています! TechTalk#17は「超入門 ここから始める開発環境」です。 開発を行うにあたり、まずは構築する開発環境。 どのような環境を整えればいいのかわからない、構築はできたけどちょっと使いにくかななどなど新人エンジニアにありがちな疑問や質問にお応えできればと思います。 今回はTech Talk初のオンライン、オフラインのハイブリッド開催となります。 Youtube配信だけでなくニフティ本社にお越しいただき参加することも出来ます。 概要 日程:3月14日(木)19:00〜20:00 参加方法 connpass にて登録をお願いいたします。 オフライン参加 ニフティ本社にて開催となります。 当日18:30開場いたしますので、エレベーターにて18階へお越しください。 受付時に名刺を1枚、もしくは身分確認が出来るもののご提示をお願いいたします。 懇親会等はございません。 オンライン参加 YouTube Liveにて配信いたします。 ※YouTube LiveのURLは決定後、参加者への情報欄に記載いたします。 こんな方におすすめ 新人エンジニア プログラミングに興味あるが、なにから手を付けていいかよくわからない方 今の開発環境をもう少し便利にするために参考にしたい方 タイムテーブル 時間 コンテンツ 18:30 開場 19:00 – 19:10 オープニング+会社紹介 19:10 – 19:25 LT1: dotfilesをつくるよ 19:25 – 19:40 LT2: フロントエンドを始める、その前に どうしていっぱいツールがあるの (仮) 19:40 – 19:55 LT3: ローカル環境を守れ、DevContainerのススメ(Python) 19:55 – 20:00 まとめ+クロージング 20:30 閉場 テーマ 超入門 ここから始める開発環境 登壇者プロフィール たけろいど(登壇者) ニフティ株式会社 会員システムグループ第二開発チーム こんにちは。たけろいどです。SvelteKitというフロントエンドフレームワークが大好きです。 最近はユニコーンオーバーロードというゲームをやっています 山田 良介(登壇者) ニフティ株式会社 会員システムグループ 第一開発チーム 三浦 拓実(登壇者) ニフティ株式会社 会員システムグループ第二開発チーム お客様向けセキュリティサービスのシステム開発担当です。 先月のTechTalkでは1on1について話しましたが、今回は開発者として話します ニフティグループでは一緒に働く仲間を募集中です 新卒採用、キャリア採用を実施しています。ぜひ リクルートサイト をご覧ください。 ニフティエンジニアが業務で学んだことやイベント情報を エンジニアブログ にて発信しています! ニフティエンジニアのTwitterアカウントを作りました NIFTY Tech Talkのことや、ニフティのエンジニアの活動を発信していきます。 Tweets by NIFTYDevelopers アンチハラスメントポリシー 私たちは下記のような事柄に関わらずすべての参加者にとって安全で歓迎されるような場を作ることに努めます。 社会的あるいは法的な性、性自認、性表現(外見の性)、性指向 年齢、障がい、容姿、体格 人種、民族、宗教(無宗教を含む) 技術の選択 そして下記のようなハラスメント行為をいかなる形であっても決して許容しません。 不適切な画像、動画、録音の再生(性的な画像など) 発表や他のイベントに対する妨害行為 これらに限らない性的嫌がらせ 登壇者、主催スタッフもこのポリシーの対象となります。 ハラスメント行為をやめるように指示された場合、直ちに従うことが求められます。ルールを守らない参加者は、主催者の判断により、退場処分や今後のイベントに聴講者、登壇者、スタッフとして関わることを禁止します。 もしハラスメントを受けていると感じたり、他の誰かがハラスメントされていることに気がついた場合、または他に何かお困りのことがあれば、すぐにご連絡ください。 ※本文章はKotlinFest Code of Conductとして公開された文章( https://github.com/KotlinFest/KotlinFest2018/blob/master/CODE-OF-CONDUCT.md )を元に派生しています。 ※本文章はCreative Commons Zero ライセンス( https://creativecommons.org/publicdomain/zero/1.0/ ) で公開されています。
はじめに こんにちは。ニフティ株式会社の並木です。 今回は「X API」を利用するための手順と、「AWS Lambda」から「X API」を呼び出す方法についてご紹介いたします。 背景 以前「 Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた 」で紹介した方法で、RSSフィードの更新をトリガーにしたX(旧Twitter)への自動投稿機能を作成したのですが、上記記事の「④Twitterに文章を投稿したい」で紹介した機能が2023年9月に使用できなくなってしまいました。(詳細: https://help.zapier.com/hc/en-us/articles/18657531069965 ) 別の方法で自動投稿機能を復活できないかという話になり、前から使用していた「Zapier」を流用しつつ、新たに「Amazon API Gateway」「AWS Lambda」「X API」を使用して、自動投稿を実現することにしました。 構成図は以下の通りです。 構成図の【1】【2】については、「 Zapierを使ってRSSフィードの更新をトリガーにしたTwitterへの自動投稿機能を作ってみた 」の処理をそのまま流用します。 【3】【4】【6】については別途記事を執筆予定です。 「【5】LambdaからX APIを呼び出して投稿」が今回の説明範囲になります。 「X API」とは 外部のアプリケーションから、Xの機能やデータを利用するためのAPIです。 Freeプラン(無料プラン)では毎月1500件の投稿をすることができます。 「X API」を利用できるようにする X Developer Platform にアクセスし、Freeプランの説明の下にある「Get started」ボタンを押下します。 先の画面へ進んでいくと、「X API」の使用用途を250文字以上で入力する画面が出てきます。(使用用途は英語で入力する必要があるので注意してください) 使用用途や規約同意の入力が完了すると、「Developer Portal」のページが表示されます。 「Projects & Apps」に、自動生成されたProjectとAppが表示されますので、App(下の画像の左メニューの赤枠部分)を押下します。 「Settings」が選択された状態で、そのまま下にスクロールしていくと「Set up」ボタンがあります。 こちらを押下すると「User authentication settings」画面に遷移します。 今回は「X API」を使った投稿(Write)がしたいので、「App permissions」で「Read and write」を選択します。 上記画面で「Save」を押下後、「Keys and tokens」を選択し、「API Key and Secret」と「Access Token and Secret」をそれぞれ生成します。(以下画像ではボタン名が「Regenerate」となっていますが、初めて生成する場合はボタン名が「Generate」となっています) 生成した「API Key and Secret」と「Access Token and Secret」は後で使用するためメモしておきます。 「AWS Lambda」から「X API」を呼び出す 「AWS Lambda」から「X API」を呼び出せるようにします。 こちらのAWSの公式記事 の「4-2. AWS Lambda 関数のひな形を作成する」を参考にしながら以下を実施します。 ランタイム:Pythonの最新バージョンを選択 「AWS CloudShell」を使用してPythonモジュール「requests_oauthlib」をインストール インストールしてきた「requests_oauthlib」をzipファイルにまとめ、Lambda関数にソースコードとしてアップロード 上記を実施した上で、Lambdaにコードを書いていきます。 import json import os from requests_oauthlib import OAuth1Session import boto3 import ast def lambda_handler(event, context): ################################# # Secrets Managerからキーを取得 ################################# secret_name = "シークレットの名前" region_name = "リージョン名" # Create a Secrets Manager client session = boto3.session.Session() client = session.client( service_name='secretsmanager', region_name=region_name ) get_secret_value_response = client.get_secret_value( SecretId=secret_name ) secret_data = get_secret_value_response['SecretString'] secret = ast.literal_eval(secret_data) consumer_key = secret['API_KEY'] client_secret = secret['API_KEY_SECRET'] access_token = secret['ACCESS_TOKEN'] access_token_secret = secret['ACCESS_TOKEN_SECRET'] ################################# # Xにポストする ################################# oauth = OAuth1Session(consumer_key, client_secret, access_token, access_token_secret) text = event['text'] payload = {'text': text} response = oauth.post( "https://api.twitter.com/2/tweets", json=payload, ) if response.status_code == 201: result = 'OK' else: result = 'NG' return { "statusCode": 200, "headers": { "Content-Type": "application/json" }, "body": { "result": result, "api_response": response.text } } Pythonモジュール「requests_oauthlib」の「OAuth1Session」を使用することで、認証周りの実装を簡単に行うことができます。 当初は、Zapierから直接「X API」を呼び出すことを検討していたのですが、ZapierにPythonの外部ライブラリを入れることができず、認証周りの実装が困難であることから、Lambdaで「OAuth1Session」を使用して実装することにしました。 OAuthセッションを作るのに、最初に生成した「API Key and Secret」と「Access Token and Secret」を使用します。 ハードコーディングを避けるため「AWS Secrets Manager」を使って管理・呼び出しをしています。 「AWS Secrets Manager」の使い方については、以前投稿した「 LambdaでSecrets Managerを使ってみた 」を参照してください。 X投稿が成功した場合は「OK」、失敗した場合は「NG」を返すようにしています。(SlackにX投稿結果を通知するのに使用します) おわりに LambdaでPythonモジュール「requests_oauthlib」を使用することで、簡単に「X API」を呼び出すことができました。 当初、Zapierだけで何とか実装できないかと試行錯誤し、認証周りの調査に時間をかけてしまったので、Lambdaで実装するという手段に早めに切り替えれば良かったと感じました。 実現したいことにあわせて、様々なサービスを組み合わせて柔軟に対応することの大切さを学びました。 参考記事 https://aws.amazon.com/jp/builders-flash/202201/aws-drill-twitter-bot-1/?awsf.filter-name=*all ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する
はじめに ニフティでは新人エンジニアの研修の一環として「エンジニア定例」と言う内製の研修が行われます。先日そのエンジニア定例の1つのエンジニア定例合宿というハッカソン合宿を行いました。本記事ではこのハッカソン合宿での制作物をご紹介します。 チームメンバー紹介 チーム名:欲望の沼 村山 普段の業務:OJT3期、メールシステム運用業務 高田 食を愛しているが、舌と体が肥えてしまっているのが悩み kiqkiq 外食とみかん 白岩 リサイクルショップで変なものを買ってきて修理する人。 制作物 概要 私たちのチームでは「スポーツイベントを盛り上げられるようなサービス」としてスポーツギャンブルサービスを開発しました。これは、サッカーやバスケットボールのような2チームごとに競うスポーツに対して、どちらのチームが勝つかを予想しポイントを賭けるスポーツベッティング形式のサービスで、スポーツ観戦とギャンブルを同時に楽しめるようなWebアプリケーションです。 主体となる機能はオッズの計算と払戻です。自他の賭けたポイントによってオッズを計算し、ユーザーの賭けたポイントと勝敗に応じてポイントが払い戻されます。 使い方 まずはログインして最初に遷移するTOP画面です。この画面では試合一覧が表示される他、各試合の投票画面へのリンクがあります。ユーザーはこの画面で試合一覧から試合を選び、投票画面に遷移することができます。 次に投票画面について説明します。投票画面では、両チームのオッズが表示され、どちらのチームに何ポイントかけるかをプルダウンから選択し、「投票する」ボタンからポイントを賭けることができます。また、オッズの計算は全体の賭けポイントによって算出され、ユーザーは一度賭けた試合にオッズの変動を見て追加でポイントをかけることができます。 試合が終了すると、的中したユーザーにはオッズを元に算出されたポイントが払い戻され、払い戻しポイントが画面に表示されます。 次はランキング画面についてです。ランキング画面では、保有ポイントのランキングを確認できます。ランキングの項目は2種類実装しており、上位から表示したランキングとログイン中のユーザーの周辺を表示するランキングです。 ヘッダー部分は、TOP画面、ランキング画面へのリンクがある他、ログイン・ログアウトボタンとに加え、ログイン中のユーザー名と保有ポイントが表示されます。 使用技術 主要なフレームワークはチーム4人のうち2人が経験のあるFastAPIを選び、データベースは一般的なDBとしてMySQLを選びました。マークアップの部分はテンプレートエンジンのjinjaを用いて実装し、これらのシステムをdockerを用いて、アプリケーション用のコンテナとDB用のコンテナとして構成しました。最後にこの構成全体をAmazon EC2で構築しました。 構成図 工夫したこと 工夫したことは大きく3つあります。まず1つ目は臨機応変にモブプロで進めたことです。開発初期の環境構築時や開発におけるのエラーやトラブルの解決をモブプロで進めることで、共通理解の元に作業を進めることができ、エラーやトラブルを素早く解決できたと思います。 2つ目はChatGPTをうまく使い効率よく進めたことです。オッズの計算などのアルゴリズムは考えて実装するようなものでないものはChatGPTを用いて大まかな実装方法などを検索しました。 3つ目は各々の得意不得意を生かした役割分担で開発したことです。主な役割はデザイン担当、開発担当、インフラ担当の3つで、チームメンバーのそれぞれが得意な分野を担当するように役割分担しました。また、上記で述べた通り、エラー時には適宜モブプロを入れ、必要に応じてメンバー全体で考えながら、各々の得意な分野で実装を行いました。 学んだこと・成長したこと CSS経験がない中でUIなどを作ることができた 私たちは4人ともフロントエンド側の経験が少なく、特にCSSについては最初はデザインができてもそれを実装する方法が全然わからないという状態でした。それでもなんとかフレームワークやAIを活用してある程度形にすることができ、終わる頃にはCSSについて少しだけわかるようになりました。 問題発生時に臨機応変に対応できるようになった 実質2日程度の非常に短い開発期間で動くものを完成させていく中では当然さまざまな問題が発生し、その場での対応を常に求められました。今回の経験を通して臨機応変に動く力が身についたと思います。 デプロイ方法を模索した 合宿2日目の22時頃、ローカル環境である程度動くものが完成し、AWSへのデプロイを行う段階になりました。一般的なデプロイ方法としては、GitHubのリポジトリをCodeCommitへミラーリングする方法や、CodePipelineを使用したCI/CD環境の構築が挙げられますが、実現するにはあまりにも時間が足りません。 そこで、とにかく早く、シンプルにデプロイするにはどうすればよいかを考え、以下の方法にたどり着きました。 Amazon Linux 2023のEC2インスタンス、セキュリティグループ、NATゲートウェイを作成 インスタンス内にDockerをインストール リポジトリをtarに圧縮 S3に圧縮したリポジトリを置く EC2インスタンスからS3にリポジトリを取りに行く インスタンス内でtarを解凍 コンテナを起動 絶対に賢くない方法ですが、どんな方法であれデプロイできれば勝ちです。 これで無事AWSへのデプロイが完了しました。 バグの解決策を考えた 今回ソース管理にはGitHubを利用しました。しかし、merge、push、merge、revert…なんてことをやっているうちに「ブランチ間にあるはずの差分が認識されずPRが作れない」という事態が発生しました。 当初は頑張って原因調査などもしていましたが、他のチームの助けも借りて事態の解決を優先し強制resetなどを行ってどうにか事なきを得ました。期間の短いハッカソンならではの経験だったと思います。 DBにinsertしたテストデータを画面で表示すると文字化けしてしまう、という問題が発生しました。機能に関係なく困る不具合だったためモブで対応し、SQLやDBMSについて知見を深めることができました。 フロント・バックで分担していたが、機能ごとに開発するように修正した 開発初日は、各ページの入出力を決めた上でフロントエンドとバックエンドでブランチを分けて進めていく体制でした。しかし短期の開発ではどうしてもブランチの管理が煩雑になり進めづらかったため、2日目からは機能ベースでの分担に切り替えました。結果としてこちらの方が進めやすかったため、早めの決断が下せたことはよかったと感じています。 最後に 短期間での開発で、当初は動くものが完成するかどうか不安でしたが、なんとか形にすることができました。 AWSへのデプロイは必須ではないと指示されていましたが、チーム内で話し合った結果、せっかくなのでやってみようという流れになり、デプロイまでたどり着くことができました。 また、ページ全体を少し胡散臭いデザインにしたことで、発表の際の反応がよかったです。ブログ上では伏せていますが、実際には大量の広告やコラージュ画像を貼り付けていました。 ニフティでは、 さまざまなプロダクトへ挑戦する エンジニアを絶賛募集中です! ご興味のある方は以下の採用サイトより お気軽にご連絡ください! ニフティ株式会社採用情報 カジュアル面談も随時受付中! ニフティに興味をお持ちの方は キャリア登録をぜひお願いいたします! キャリア登録 connpassでニフティグループに 参加いただくと イベントの お知らせが届きます! connpassで ニフティグループに参加する