TECH PLAY

株式会社マイナビ デジタルテクノロジー戦略本部

株式会社マイナビ デジタルテクノロジー戦略本部 の技術ブログ

240

はじめに みなさん、こんにちは。 オペレーションデザイン統括本部オペレーションデザイン統括部セールステック推進部SFA・CRM推進課のU.Kです。 先日、2024年12月19日に新宿ミライナオフィスのTOWNHOLLで「なぜなにオペデザ発表会」が開催されました。 このイベントはオペレーションデザイン統括本部(通称オペデザ)について、みんなで理解を深めるための機会+年末の懇親会+クリスマスパーティとして開催されました。 ここでは、発表内容を主に開催レポートとしてみなさんにその内容を共有させていただければと思います。 オペレーションデザイン統括本部とは 発表の目的 この発表会の目的は、「オペレーションデザイン統括本部の業務とシステムについて、みんなが一つのチームとして理解する」ことです。 マイナビは事業部の数も業務領域も担当システムを利用する社員も膨大です。 そのために私も入社時に自部署のことを理解するのでやっとで、いつも近くにいて同じミッションを背負っているはずなのに、広大すぎるがゆえになかなか掴み切れないような感覚があったのを覚えています。 今回のイベントはそのような感覚の解消をすべく、新入社員にとっては、オペレーションデザイン統括本部内のつながりを相互理解すること、また長く在籍している社員にとっては、マイナビ全体のオペレーションデザインを相互理解することを目的としました。 発表の概要 発表会は下記の流れで進行しました。 はじめに:オペレーションデザイン統括本部の目的と事業について セールステック推進部MA推進課 セールステック推進部SFA・CRM推進課 ビジネスシステム部2課 ERPシステム1部2課 ERPシステム1部1課 オペレーション開発部開発課 ERPシステム2部 総評 1.セールステック推進部MA推進課:toB向けのマーケティングツールでの顧客獲得 2.セールステック推進部SFA・CRM推進課:SFA・CRMでの商談顧客管理 3.ビジネスシステム部2課:受注後の掲載業務システム 4.ERPシステム1部2課:ERPでの請求入金管理 5.ERPシステム1部1課:予算管理システムについて 6.オペレーション開発部開発課:iPaaS・RPAによる業務の自動化について 7.ERPシステム2部:今後のマイナビのオペレーションシステムの展望 8.総評 期待以上の結果・感想 なんと、オンライン・オフライン合わせて100名を超える方に参加者いただきました! オペレーションデザイン統括本部以外からもたくさんの方の参加があり発表者ながらびっくりしていました。 その後の懇親会では、システム・部門・統括本部に関わらず交流が活発に行われました。 発表会をきっかけに多くの社員が互いの業務について知識を深めることができました。 まとめと今後の展望 今回の「なぜなにオペデザ発表会」は、みんながオペレーションデザイン統括本部の業務とシステムについて理解を深める非常に有意義な機会となりました。 各部門がどのような役割を果たし、どのようにして業務を遂行しているのかを知ることで、統括本部内の連携がさらに強化できると感じることができたのと同時に、そのためのきっかけとつながりを今回の発表会で得ることができたと感じています。 私自身、これまで関わりのなかった方々と多く関わる機会を得て、入社時に感じた「いつも近くにいて同じミッションを背負っているはずなのに、広大すぎるがゆえになかなか掴み切れない」感覚を拭うような良い刺激を受けることができました。 社員一人ひとりが、自分の業務だけでなく、他部門の業務についても理解を深めることで、マイナビ全体の成長に寄与することができると感じられる、そんな一日になったのではないかと思います。
マイナビジョブサーチWebチームのT.Yです。 今回は、Google Chromeの拡張機能の、React Developer Toolsで出来ることについてお話しします。 React Developer Toolsとは Chrome DevToolsでReactアプリケーションのデバックができるツールです。 React Developer Toolsでは、以下のことを確認することができます。 コンポーネントの構造 コンポーネントに渡されているpropsやstateの値の確認、値の更新 レンダリングしたコンポーネントをハイライトで表示 パフォーマンスの計測、レンダリングした回数の計測 ...など これから4つの機能について紹介していきたいと思います。 インストール react-developer-tools インストール後に、Chrome DevToolsのナビバーに、「Components *」と「Profiler *」タブが表示されます。 Components コンポーネントの構造の確認や、コンポーネントに渡されているprops、stateの値の確認、値を更新することができます。 サンプルコード 【コード】app/page.tsx "use client"import { CSSProperties, useState } from "react";import { CountUp } from "@/components/CountUp";const style: CSSProperties = { display: "flex", flexDirection: "column", alignItems: "center", gap: "3rem",}export default function Home() { const [isShow, setIsShow] = useState<boolean>(false); return ( <div style={{ ...style }}> <div> <CountUp isShow={isShow} styleProps={style} /> </div> </div > ); 【コード】components/CountUp/index.tsx "use client"import { useState } from "react"import { CSSProperties } from "react";type Props = { isShow: boolean, styleProps: CSSProperties}export const CountUp: React.FC<Props> = ({ isShow, styleProps }) => { const [count, setCount] = useState(0) return ( <div style={{ ...styleProps, outline: "1px solid red" }}> <p>count : {count}</p> <button onClick={() => setCount(count + 1)}>COUNT UP</button> <p>⇩⇩⇩countが10になったら下にテキストが表示⇩⇩⇩</p> {count === 10 && <p>10回クリックされました</p>} {isShow && <p>propsのisShowがtrueの時に表示</p>} </div> )} 使い方 Chrome DevToolsを開き、黄色枠で囲んでいる「components」を選択すると、ReactのDOM構造が表示される 緑枠で囲んでいるボタンを選択した状態で、CountUpコンポーネント(赤枠)を選択すると、親からコンポーネントに渡っているpropsやstateが確認できる COUNTUPボタンをクリックしてみると、動画画面の右側に表示されているstateの値も変更される  Screen Recording 2024-12-05 at 15.04.35.mov 動画画面の右側に表示されているpropsの値、stateの値を直接変更することも可能で、変更した値に応じて表示が切り替わる  Screen Recording 2024-12-05 at 15.10.27.mov 「components」では、画面上でprops、stateの値の確認、値の変更ができるので、わざわざソース上で値を変更したり、ボタンを実際にクリックする必要もなくデバッグすることができます。 Profiler 再レンダリングしたコンポーネントをハイライトしてくれるので、レンダリングされた箇所を確認することが出来ます。また、コンポーネントが表示するまでにかかった時間や、コンポーネントがレンダリングされた回数も確認できます。 サンプルコード 【コード】app/page.tsx "use client"import { CSSProperties, useState } from "react";import { CountUp } from "@/components/CountUp";const style: CSSProperties = { display: "flex", flexDirection: "column", alignItems: "center", gap: "3rem",}export default function Home() { const [reRender, setReRender] = useState<boolean>(false); return ( <div style={{ ...style }}> <button onClick={() => setReRender(!reRender)}>stateを更新する</button> <div> <CountUp styleProps={style} /> </div> </div > );} 【コード】components/CountUp/index.tsx "use client"import { useState } from "react"import { CSSProperties } from "react";type Props = { styleProps: CSSProperties}export const CountUp: React.FC<Props> = ({ styleProps }) => { const [count, setCount] = useState(0) return ( <div style={{ ...styleProps }}> <p>count : {count}</p> <button onClick={() => setCount(count + 1)}>COUNT UP</button> </div> )} レンダリングしたコンポーネントをハイライトする方法 「Profiler」タブを選択し、歯車マークを押し、「General」タブの、黄色枠で囲んだ箇所にチェックをつける 画面を操作して、state、propsが更新されると、再レンダリングされたコンポーネントがハイライトされる  Screen Recording 2024-12-05 at 17.23.03.mov 「COUNT UP」ボタンをクリックした際は、CountUpコンポーネントのみがレンダリングされたが、親コンポーネントでstate管理している「stateを更新する」ボタンをクリックすると、CountUp子コンポーネントも再レンダリングされていることが確認できる 今回はやらないが、CountUp子コンポーネントをmemo化すると、ハイライトされない結果になる  Screen Recording 2024-12-05 at 17.32.50.mov このように、ハイライトをもとに、意図したレンダリングになっているかを確認することができます。 パフォーマンスの計測、レンダリングした回数の計測 丸いボタンをクリックすることでレコーディングが開始される レコーディング中にボタンをクリックしたり、そのほか操作をする レコーディングボタンを再度クリックし停止すると、レコーディング中のデータを確認することができる  Screen Recording 2024-12-05 at 19.07.08.mov 上記の動画で「COUNT UP」ボタンを5回クリックした結果、意図通りにCountUpコンポーネントが5回レンダリングされていること、CountUpコンポーネントがそれぞれのレンダリングにかかった時間も確認できる Screen Recording 2024-12-05 at 19.09.00.mov 「Profiler」タブを選択し、歯車マークを押し、「Profiler」タブの、黄色枠で囲んだ箇所にチェックをつけることで、レンダリングされた要因の説明が表示できる 「Profiler」を活用すれば、パフォーマンス向上につながる手がかりや、ボトルネックの原因を見つけることができます。 まとめ この記事では「React Developer Tools」を利用することで、Chrome DevTools上でpropsやstateのデバックが出来ること、レンダリングの可視化やパフォーマンスを確認できることを紹介しました。 他にも、今回紹介した機能以外に出来ることはありますが、まだ有効に活用できていないので、使いこなせるようにしていきたいです。 最後まで読んでいただき、ありがとうございました。
マイナビジョブサーチ WebチームのK.Kです。 今回は、Next.jsの画像最適化のお話をしたいと思います。 先日インフラ担当の方からこんな指摘をされました。 「Acceptヘッダーを転送していないから、画像の最適化が活かされていない。」 最初よくわからなくて「?」となったので、これをきっかけにNext.jsの画像最適化について調べてみました。 Next.jsの画像最適化とは? Next.jsではimgタグ、または、next/imageのImageコンポーネントを使用して画像を描画します。 この内、next/imageを使用すると、画像の最適化を行うことができます。 「リサイズ」「フォーマット変換」 などを行うことで、ページ読み込み速度が向上するなどといった恩恵が得られます。 リサイズ リサイズの機能では、表示するディスプレイの大きさに合わせて、 画像のサイズを最適化 してくれます。 例えばジョブサーチ上のこの画像、以下のように記載されています。 srcset属性に複数のURL が並んでいることがわかります。 これらが、画面サイズごとの画像のパスを示しています。 PC https://jobsearch.mynavi.jp/_next/image?url=%2Fapi%2FserveImage%3Furl%3Dhttps%253A%252F%252Fmynavi-agent.jp%252Fjobsearch%252Fimg%252Fjobimg%252Fjob1_01%252F02.webp&w=3840&q=75 SP(よりさらに少し狭めた場合) https://jobsearch.mynavi.jp/_next/image?url=%2Fapi%2FserveImage%3Furl%3Dhttps%253A%252F%252Fmynavi-agent.jp%252Fjobsearch%252Fimg%252Fjobimg%252Fjob1_01%252F02.webp&w=640&q=75 PC だと「690 × 398」SPだと「640 × 369」の画像が表示に使用されます。 容量を比べると23KB vs 18KBと、SPの方が小さくなっています。 特にSPは通信環境によってページ表示が遅くなりがちなので、容量が小さくなるのは嬉しいですね! 元画像がもっと大きい場合は、より大きな差になるかと思います。 画面サイズごとの画像のパスは、next/imageを使用すると、自動で生成されます。 これらの中から 最適な画像をブラウザ上に表示 してくれます。 フォーマット変換 フォーマット変換では、ブラウザが対応している画像フォーマットに合わせて、 画像フォーマットを最適化 してくれます。 どういったフォーマットに変換するかは設定で変更することができ、デフォルトではWebPに変換される設定になっています。 module.exports = { images: { formats: ['image/webp'], }, } WebPは、JPEGやPNGより 圧縮率の高いフォーマット です。 他にAVIFも指定できますが、こちらはより圧縮率が高い代わりに、エンコードに時間がかかるようです。 さて、ジョブサーチの こちらの画像 を取り上げます。 この画像、元はJPEGとして保存されています。 これをジョブサーチ上で開くと、JPEGで表示されます。 WebPじゃないの? Chromeの最新版だから対応しているはずだけど? そう、これが冒頭で書いた、今回指摘された内容です。 「ブラウザが対応している画像フォーマットに合わせて」画像フォーマットを最適化 ブラウザが対応している画像フォーマット、これは何で判断しているのでしょうか? それが、 Acceptヘッダー なんです。 ↓ Acceptヘッダーとは? HTTP の Accept リクエストヘッダーは、クライアントが理解できるコンテンツタイプを MIME タイプで伝えます。 参照元: https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Accept 実際にChromeでリクエストヘッダーのAcceptを見ると 「image/webp」の記載 があります。 Next.jsではこの値を見て、WebPで返すかどうかを判断しているわけですね。 公式ドキュメントにも以下の記載がありました。 Good to know: ~ 省略 ~ If you self-host with a Proxy/CDN in front of Next.js, you must configure the Proxy to forward the Accept header. 参照元: https://nextjs.org/docs/app/api-reference/components/image#formats ジョブサーチでは、 Next.jsの動作環境にAcceptヘッダーを転送していなかった ため、WebPのブラウザがフォーマットに対応しているか判定できず、そのままのJPEGフォーマットで返していたようです。 それでは、実際にAcceptヘッダーを転送して、フォーマットの変換をするとどうなるのか、検証環境で動作させてみました。 すると、 元画像(JPEG, 2000×1333):120KB Acceptヘッダーなし(JPEG, 916×611):41KB Acceptヘッダーあり(WebP, 916×611):20KB ということで、 画像の容量が半分 になりました!(圧縮されすぎでは?) 見た目上は変化がわからないのに、これはすごいですね! 最後に next/imageで画像の最適化をすることで、ページの読み込みが早くなったり、他にも視覚的な安定性が得られたりといったメリットがあります。 SEOにおいてもメリットがあるので、せっかくNext.jsを使用しているなら、動作しているのかちゃんと確認しないとなと思いました。 参考 https://nextjs.org/docs/app/building-your-application/optimizing/images https://nextjs.org/docs/app/api-reference/components/image#formats https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Accept
はじめに マイナビジョブサーチのWebチームのT.Yです。 みなさんは何のエディタを使っていますか? 私はVsCodeを使っているのですが、もう少し使いやすくならないかな〜、見やすくならないかな〜、と思ったことはないでしょうか? 思ったことがあるそんなあなたに、悩みを解決してくれるかもしれない設定をいくつか紹介します。 タブを多段表示する 設定:ワークベンチ>エディターの管理:Wrap Tabsにチェック setting.json:"workbench.editor.wrapTabs": true 新規タブを常に新しいタブとして表示 設定:ワークベンチ>エディターの管理:Enable Previewのチェックを解除 setting.json: "workbench.editor.enablePreview": false ファイルのインデントの表示幅を変更 設定:ワークベンチ>外観>Tree:Indent(デフォルト8) setting.json: "workbench.tree.indent": 20 ファイルのインデントにガイド線を表示 設定:ワークベンチ>外観>Tree:Render Indent Guides setting.json: "workbench.tree.renderIndentGuides": "always" ファイルのインデントのガイド線の色を変更 設定:ワークベンチ>外観> Color Customizations(setting.jsonで変更) setting.json:"workbench.colorCustomizations": { "tree.indentGuidesStroke": "#555555" // Default is #555555. }, フォルダを常に階層で表示する 設定:機能>エクスプローラー>Compact Foldersのチェックを解除 setting.json: "explorer.compactFolders": false 行番号の色と、カーソル行の行番号の色を変更 設定:ワークベンチ>外観> Color Customizations(setting.jsonで変更) setting.json:"workbench.colorCustomizations": { "editorLineNumber.foreground": "#7bff00", // 行番号 "editorLineNumber.activeForeground": "#ff0000", // カーソル行の行番号 }, 最後に いかがでしたでしょうか? 他にもたくさんの設定があるので、他の設定も色々と試してみてより良い開発環境にしてみてください。
今回はAPIのモックを用いたユニットテストとE2Eテストについて実際のコードを使いながら紹介しようと思います。 モックを用いたユニットテストの概要 モックとは まずモックとは何かについて説明します。 テストしたい関数が他のクラスに依存していることはよくあると思います。 例えば、SNSのとあるユーザーの投稿を取得するAPIのサービスクラスは投稿が公開か未公開か確認のために投稿のレポジトリクラスに依存し、またユーザーの存在を確認するためにユーザーのレポジトリクラスにも依存しています。 この状況下において、モックを使わずにUTを実装すると、ユーザーのレポジトリクラスのUTが失敗した場合、サービスクラスのUTも失敗しているということになります。そのため、原因特定に時間がかかります。 モックを使って実装すると、依存しているクラスや関数が想定通りの挙動をするように設定できるため、ユーザーのレポジトリクラスのUTが失敗した場合でも、サービスクラスのUTは成功します。そのため、瞬時にユーザーのレポジトリクラスのみでバグが生じていることがわかります。 つまり、モックとはUTの責任範囲を明確にし、 UTを実装しやすくする存在です。 依存注入 ただ、注意しなくてはいけないのは UTの対象関数の内、UTが制御できるのは対象関数の呼び方のみであるということです。 つまり、モックを使ってUTを制御するためには、モックするクラスを関数またはクラスの引数に設定する必要があります。 そのため、対象関数またはクラスの引数はクラスを注入できるように実装する必要があります。 これを 依存注入 と呼びます。 モックを用いたUTの実例 モックや依存注入について説明が終わったため、実際のコードを使って説明したいと思います。 今回はSNSのとあるユーザーの投稿を取得するAPIとそのUTコードを実装しました。 コントローラー コントローラーのコードは以下のようになっています。 get_post_info 関数の引数にサービスクラスを依存注入しています。 / controllers / get_post_controller . py < code >from dependency_injector . wiring import Provide , inject from fastapi import APIRouter , Depends from app . api_schemas . get_post_schema import ( GetPostRequest , GetPostResponse , GetPostSchema ) from app . core . container import Container from app . services . get_post_service import GetPostService router = APIRouter () @ router . get ( " /posts/ {post_id} " ) @ inject def get_post_info ( get_post_request : GetPostRequest = Depends (), service : GetPostService = Depends ( Provide [ Container . get_post_service ]), ) -> GetPostResponse : if post := service . get_post_info ( get_post_request . post_id , get_post_request . user_id ): return GetPostResponse ( result =True , post = GetPostSchema ( title = post . title , description = post . description ), ) return GetPostResponse ( result =False , post =None ) </ code > これに対するUTコードは以下のようになっています。 mock_get_post_service 関数でサービスのモックを作成し、各テストケースで利用しています。 コントローラーの get_post_info 関数の中で使うサービスのメソッドの返り値をモックで設定することで関数内の条件分岐を制御しています。 / tests / controllers / test_get_post_controller . pyfrom unittest . mock import MagicMock import pytest import requests from app . api_schemas . get_post_schema import ( GetPostRequest , GetPostResponse , GetPostSchema ) from app . controllers . get_post_controller import get_post_info from app . models . post import PostTable from app . models . user import UserTable from tests . base_test import BaseTest @ pytest . fixture () def mock_get_post_service (): return MagicMock () def test_get_post_succeeds ( mock_get_post_service ): mock_get_post_service . get_post_info . return_value = ( PostTable . test_public_post_by_user1_data () ) request = GetPostRequest ( post_id = 1 , user_id = 1 ) response = get_post_info ( get_post_request = request , service = mock_get_post_service ) assert response == GetPostResponse ( result =True , post = GetPostSchema ( title = PostTable . test_public_post_by_user1_data (). title , description = PostTable . test_public_post_by_user1_data (). description , ), ) def test_get_post_fails ( mock_get_post_service ): mock_get_post_service . get_post_info . return_value = None request = GetPostRequest ( post_id = 1 , user_id = 1 ) response = get_post_info ( get_post_request = request , service = mock_get_post_service ) assert response == GetPostResponse ( result =False , post =None ) サービス サービスのコードは以下のようになっています。 サービスはクラスにまとめているため、クラスの __init__ 関数で依存するユーザーレポジトリクラスと投稿レポジトリクラスを注入しています。 これによってクラス内の関数のインスタンスから依存先を利用できるようになっています。 / services / get_post_service . py < code >from app . models . post import PostTable from app . repositories . post_repository import PostRepository from app . repositories . user_repository import UserRepository class GetPostService : def __init__ ( self , post_repository : PostRepository , user_repository : UserRepository ): self . post_repository = post_repository self . user_repository = user_repository def get_post_info ( self , post_id , user_id ) -> PostTable : if not self . user_repository . get_user ( user_id ): return None post = self . post_repository . get_post ( post_id ) if not post or not self . __is_visible ( post , user_id ): return None return post def __is_visible ( self , post : PostTable , user_id ) -> bool : if post . user_id == user_id : return True elif not post . is_private : return True else : return False </ code > これに対するUTコードは以下のようになっています。 get_post_service 関数で依存するユーザーレポジトリクラスと投稿レポジトリクラスをモックしています。 サービスの get_post_info 関数の中で使うレポジトリのメソッドの返り値をモックで設定することで関数内の条件分岐を制御しています。 tests / services / test_get_post_service . py < code >from datetime import datetime from unittest . mock import MagicMock import pytest from app . models . post import PostTable from app . models . user import UserTable from app . services . get_post_service import GetPostService @ pytest . fixture () def get_post_service (): return GetPostService ( post_repository = MagicMock (), user_repository = MagicMock ()) def test_non_existing_user ( get_post_service ): non_existing_user_id = 1 get_post_service . user_repository . get_user . return_value = None assert get_post_service . get_post_info ( 1 , non_existing_user_id ) == None def test_non_existing_post ( get_post_service ): non_existing_post_id = 1 get_post_service . user_repository . get_user . return_value = ( UserTable . test_not_login_user1_data () ) get_post_service . post_repository . get_post . return_value = None assert ( get_post_service . get_post_info ( non_existing_post_id , UserTable . test_not_login_user1_data (). id ) == None ) def test_get_private_post_from_non_author ( get_post_service ): get_post_service . user_repository . get_user . return_value = ( UserTable . test_not_login_user1_data () ) get_post_service . post_repository . get_post . return_value = ( PostTable . test_private_post_by_user1_data () ) assert ( get_post_service . get_post_info ( UserTable . test_not_login_user1_data (). id , PostTable . test_private_post_by_user1_data (). id , ) == None ) def test_get_private_post_from_author ( get_post_service ): get_post_service . user_repository . get_user . return_value = ( UserTable . test_not_login_user1_data () ) get_post_service . post_repository . get_post . return_value = ( PostTable . test_private_post_by_user1_data () ) assert ( get_post_service . get_post_info ( PostTable . test_private_post_by_user1_data (). id , UserTable . test_not_login_user1_data (). id , ) == get_post_service . post_repository . get_post . return_value ) def test_get_public_post_from_non_author ( get_post_service ): get_post_service . user_repository . get_user . return_value = ( UserTable . test_login_user2_data () ) get_post_service . post_repository . get_post . return_value = ( PostTable . test_public_post_by_user1_data () ) assert ( get_post_service . get_post_info ( PostTable . test_public_post_by_user1_data (). id , UserTable . test_login_user2_data (). id , ) == get_post_service . post_repository . get_post . return_value ) </ code > レポジトリ レポジトリのコードは以下のようになっています。 レポジトリはクラスにまとめているため、クラスの __init__ 関数で依存するDBを注入しています。 DBを注入することで開発環境のDBとは別のDBにデータを入れることができるため、開発環境のDBに影響を与えずに済みます。 これによってクラス内の関数のインスタンスから依存先を利用できるようになっています。 / repositories / post_repository . py < code >from pydantic import BaseModel from sqlalchemy . orm import Session from app . models . post import PostTable from app . models . user import UserTable class PostRepository : def __init__ ( self , db : Session ): self . db = db def get_post ( self , post_id ) -> PostTable : return ( self . db . query ( PostTable ) . join ( UserTable , UserTable . id == PostTable . user_id ) . filter ( PostTable . id == post_id ) . first () ) </ code > これに対するUTコードは以下のようになっています。 レポジトリはAPIの最奥層であるため、何もモックせずに実際にテスト用DBにデータを入れた上でUTを書いています。 テスト用DBとアプリ用DBの切り替えは base_test.py で行っていますが、ここでは省略します。 / tests / repositories / test_post_repository . py < code >from app . helpers . helper import get_datetime_now_db_format from app . models . post import PostTable from app . models . user import UserTable from app . repositories . post_repository import PostRepository from tests . base_test import BaseTest class TestPostRepository ( BaseTest ): @ classmethod def _initialize_repository ( cls ): cls . post_repository = PostRepository ( cls . db ) @ classmethod def _insert_data ( cls ): cls . db . add_all ( [ PostTable . test_public_post_by_user1_data (), UserTable . test_not_login_user1_data (), ] ) cls . db . commit () @ classmethod def test_get_existing_post ( cls ): response = cls . post_repository . get_post ( PostTable . test_public_post_by_user1_data (). id ) assert response . id == PostTable . test_public_post_by_user1_data (). id assert response . title == PostTable . test_public_post_by_user1_data (). title assert ( response . description == PostTable . test_public_post_by_user1_data (). description ) assert response . user_id == PostTable . test_public_post_by_user1_data (). user_id assert ( response . is_private == PostTable . test_public_post_by_user1_data (). is_private ) @ classmethod def test_get_non_existing_post ( cls ): non_existing_post_id = 2 response = cls . post_repository . get_post ( non_existing_post_id ) assert response == None </ code > E2Eテスト E2Eテストとはシステム全体をテストするものです。 E2Eテストを実行することで関数間の値の受け渡しが正常であることを担保し、UTのみではカバーできないところをカバーし、バグが発生する可能性を下げることができます。 このレポジトリはAPIしか作成していないため、フロントエンドの挙動までは確認しません。 ここでは特定のパスにリクエストが来てからレスポンスが返されるまでの一連の動作を確認します。 そのため、ここでも実際にデータを入れます。 / tests / controllers / test_get_post_controller . py < code >from unittest . mock import MagicMock import pytest import requests from app . api_schemas . get_post_schema import ( GetPostRequest , GetPostResponse , GetPostSchema ) from app . controllers . get_post_controller import get_post_info from app . models . post import PostTable from app . models . user import UserTable from tests . base_test import BaseTest class TestGetPostController ( BaseTest ): @ classmethod def _insert_data ( cls ): cls . db . add_all ( [ UserTable . test_not_login_user1_data (), PostTable . test_public_post_by_user1_data (), ] ) cls . db . commit () @ classmethod def test_e2e ( cls ): post_id = str ( PostTable . test_public_post_by_user1_data (). id ) user_id = str ( UserTable . test_not_login_user1_data (). id ) response = cls . client . get ( " /posts/ " + post_id , params = { " post_id " : post_id , " user_id " : user_id } ) assert response . status_code == 200 assert response . json () == { " result " : True , " post " : { " title " : PostTable . test_public_post_by_user1_data (). title , " description " : PostTable . test_public_post_by_user1_data (). description , }, } </ code > 最後に APIのモックを用いたユニットテストは、自分が書いたコードが仕様を正しく反映していることを迅速に確認するための非常に効率的な手法です。 一定期間が経過して仕様を忘れてしまった場合でも、仕様がコードとして明確に表現されているため、再確認が容易になります。 また、E2Eテストはユニットテストだけでは見落としがちな、全体の動作を確認するのに非常に有効です。 これにより、実際の動作環境での問題を早期に発見し、修正することができます。 ぜひ、これらのテスト手法を取り入れて、効率的にAPI開発を進めていただければと思います。 今後も、テストの重要性を意識しながら、より良いソフトウェアを作り上げていきましょう。
今回は、GoogleChromeの拡張機能をサクッと作ってみようと思います。 概要 Google Chromeの拡張機能を作成する際には、主に3つの重要要素があります。 ポップアップ(popup) バックグラウンドスクリプト(background) コンテントスクリプト(content script) ポップアップ ポップアップは、ユーザーがChromeの拡張機能アイコンをクリックしたときに表示されるものです。 参考: DeepL コンテントスクリプト コンテントスクリプトは、ユーザーが閲覧しているウェブページに直接挿入されるJavaScriptです。ページのDOMを操作したり、ページからデータを取得したりするために使用されます。 参考: Dimentions (青い線 と 19px*214pxのツールチップ) バックグラウンドスクリプト バックグラウンドスクリプトは、拡張機能の背後で動作し、イベント駆動型で特定のイベントが発生したときにのみ起動します。バックグラウンドスクリプトは、APIへのアクセスやデータの管理、他のスクリプトとの通信を担当します。 コンテントスクリプトを発火させる時にも使用していたりします。 必要なファイル この後のチュートリアルでは、 chrome-extension-v3-starter  を参考にして作成していきます。 このリポジトリのファイルを参考に細くしてみると大体こんな感じです。 my-chrome-extension/├── manifest.json // ---------------- 拡張機能の概要やファイル定義を書く├── logo/ // ------------------------ アイコン画像│ └── (ロゴ画像ファイルなど)├── popup/ // ----------------------- ポップアップを書く│ ├── index.html│ └── style.css├── foreground.js // ---------------- コンテンツスクリプトを書く└── service-worker.js // ------------ バックグラウンドスクリプトを書く チュートリアル (スクリーンショット撮れる拡張機能) ポップアップボタンまたは、ショートカットキーでスクリーンショットを取れる拡張機能を作成してみようと思います。  画面収録 2024-12-03 9.55.40.mov ※開発のための準備に関しては割愛します。( こちら を参照してください。) ①コマンドを実行して、スクリーンショットのバイナリをコンソールに出力させる manifest.json にコマンドを追加する バックグラウンドスクリプト (service-worker.js) にて、画面キャプチャを書く manifest.json には、コマンドを追加するのと、スクリーンショットをするために、permissionを更新しています。①~③で更新が必要な箇所をまとめて追加していきます。気になる人は、 公式ドキュメント などを参照してみてください。 manifest.json { "manifest_version": 3, "name": "Chrome Extension v3 Starter", "description": "A minimal example of a chrome extension using manifest v3", "version": "0.0.1", "icons": { "16": "logo/logo-16.png", "48": "logo/logo-48.png", "128": "logo/logo-128.png" }, "options_page": "settings/settings.html", "action": { "default_title": "Chrome Addon v3 Starter", "default_popup": "popup/popup.html" }, "permissions": ["tabs", "commands", "activeTab"], // update permission "host_permissions": ["*://*/*"], "background": { "service_worker": "service-worker.js" }, "content_scripts": [ { "js": ["foreground.js"], "matches": ["https://*/*"] // update url pattern } ], // ↓ add commands "commands": { "take-screenshot": { "suggested_key": { "default": "Ctrl+Shift+F", "mac": "Command+Shift+F" }, "description": "Take a screenshot of the current page" } // ↑ add commands }} バックグラウンドスクリプト(service-worker.js)も修正します。manifest.json で定義した、"take-screenshot"を確認して、スクリーンショットを行い、バイナリを出力しています。 service-worker.js // add ↓ chrome.commands.onCommand.addListener((command) => { if (command === "take-screenshot") { chrome.tabs.captureVisibleTab(null, {}, (image) => { console.log(image); }); } }); // add ↑ こんな感じになります。 ※コマンドがうまく反映されない人は、 ショートカットの設定 から設定してみてください。 ② コンテンツスクリプトを使用して、開いているWebサイトで、スクリーンショットを表示させる コンテンツスクリプトにて、messageAPIを用いて、スクリーンショットを発火させる。 発火したスクリーンショットをWebサイトにレンダリングする。 先ほど、バックグラウンドスクリプトの、take-screenshotコマンド実行時の関数を修正します。 内容としては、スクリーンショットを行い、そのデータをsendMessageで渡そうとしています。 service-worker.js // update ↓ chrome.commands.onCommand.addListener(async (command) => { if (command === "take-screenshot") { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); chrome.tabs.captureVisibleTab(tab.windowId, { format: "png" }, (dataUrl) => { if (chrome.runtime.lastError) { console.error("Error capturing screenshot:", chrome.runtime.lastError); return; } console.log("Screenshot captured, sending data URL"); chrome.tabs.sendMessage(tab.id, { type: "screenshot", dataUrl }); }); } }); // update ↑ sendMessageで渡ってきたものを検知して、コンテンツスクリプト(foreground.js)にて、イベントを発火させています。 表示しているWebサイトの左下に撮ったスクリーンショットを同じものをレンダリングしています。 foreground.js // add ↓ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { console.log("Received message in foreground script", message); if (message.type === "screenshot") { const img = document.createElement("img"); img.src = message.dataUrl; img.onload = () => console.log("Image loaded successfully"); img.onerror = (e) => console.error("Error loading image:", e); img.style.position = "fixed"; img.style.bottom = "10px"; img.style.left = "10px"; img.style.width = "400px"; img.style.height = "300px"; img.style.zIndex = "10000"; img.style.border = "2px solid #000"; document.body.appendChild(img); } }); // add ↑ コマンドを実行すると、下記のよう左側に表示されます。 ③ポップアップから実装したスクリーンショットの機能を発火させる ポップアップのHTMLとJSを修正 バックグラウンドスクリプトで、スクリーンショットの機能を発火させるように設定 ポップアップ用のHTMLを更新します。JSのインポートとボタンの追加を行っています。 popup/popup.html <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <link rel="stylesheet" href="popup.css"> <title>Chrome Addon v3: popup</title> <script src="/popup/popup.js"></script> <!-- add import --> </head><body> <button id="take-screenshot">キャプチャする</button> <!-- add button --> </body></html> 新しくJSを追加します。追加したボタンにクリックイベントを付与して、take-screenshot のメッセージを送るように設定します。 popup/popup.js // ↓ add click event document.addEventListener('DOMContentLoaded', function () { document.getElementById('take-screenshot').addEventListener('click', () => { console.log('take-screenshot'); chrome.runtime.sendMessage({ name: 'take-screenshot' }); });}); バックグラウンドスクリプトで、新しく関数を追加します。take-screenshot のメッセージを感知して、スクリーンショット機能を発火させています。(中身の処理は②で書いた関数とほとんど同じ) service-worker.js // ↓ add chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => { if (request.name === "take-screenshot") { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); chrome.tabs.captureVisibleTab(tab.windowId, { format: "png" }, (dataUrl) => { if (chrome.runtime.lastError) { console.error("Error capturing screenshot:", chrome.runtime.lastError.message); return; } console.log("Screenshot captured, sending data URL"); chrome.tabs.sendMessage(tab.id, { type: "screenshot", dataUrl }); }); } }); // ↑ add chrome.commands.onCommand.addListener(async (command) => { ... こんな感じで実行すると、ポップアップからもスクリーンショット機能を発火することができるようになります。 画面収録 2024-12-03 9.55.40.mov チュートリアルは以上です。 まとめ 概念を理解すると意外と簡単に作れるので、今後業務効率化できそうなところがあれば、自作してみようと思います。 最後まで見ていただきありがとうございました🙇
はじめに 皆さんこんにちは! ラスベガスで開催された AWS の re:Invent にマイナビからも 4 名のエンジニアで現地参戦してきました! re:Invent がどういったイベントなのか、Keynote から SWAG、認定者ラウンジまで、実際に参加してみたレポートをご紹介します。 AWS re:Invent とは? AWS re:Invent  は Amazon Web Service(AWS)社が年に一度開催する、AWS 最大規模の技術カンファレンスです。2012 年から開催されており、今年で 13 回目の開催となります。 多くの参加者が注目する Keynote(基調講演)から、2,000 以上の技術セッションへの参加、Expo(展示会)などさまざまなプログラムが用意されており、毎年イベント中に多くの新サービスや、新機能が発表されます。 イベント概要 開催日程:12 月 2 日(月)- 12 月 6 日(木)の 5 日間 開催地 :アメリカ ネバダ州 ラスベガス 参加者 :現地参加者:約 60,000 名(うち日本からの参加者:約 2,000 名) 参加費用:$2,099 会場 引用: https://reinvent.awsevents.com/experience/plan-your-trip/ キャンパス(会場)はラスベガスの中心地のホテルで、全部で 6 つあります。 会場のホテルがかなり大きいため、ホテル間の移動は無料のシャトルバスまたはモノレールを使います。 基本的にはシャトルバス、人が多く集まるイベントが開催されるタイミングでの移動はモノレールでの移動をオススメします。 プログラム ここからは re:Invent で実際に体験したプログラムを紹介していきます。 Keynote(基調講演) EXPO(展示会) 技術セッション SWAG AWS 認定者ラウンジ Keynote(基調講演) Keynote とは、AWS のトップリーダが今年注目の新サービスや新機能についての発表を行う講演で、開催期間中は毎日行われます。 オンラインでの視聴も可能で、動画配信もされています。 Keynote現地参加レポート 今回はその中でも最注目だった Day2 の Keynote、AWS の CEO である Matt Garman の講演に参加しました。 Matt Garman は EC2 の最初のプロダクトマネージャーで、24 年の 6 月に AWS の新 CEO に就任したため、今回が初の Keynoteとなります。 朝 8:00 から始まる講演でしたが、会場に入りたかったので1 時間半ほど前から並びました。 メンバーと 6:00 に集合で MGM からローカルバスに乗り(シャトルバスが出ていない時間)移動。 6:45 に講演会場の Venetian に到着して待ち列へ。 7:00 には入場規制されていたので、開始 1 時間前には並んでいないと会場には入れないようです。 早朝とは思えない人の多さ 7:15 すぎにメイン会場へ。ここもとにかく広かったです。 運よく先頭の方に並んでいたので、会場の真ん中のあたりに着席できました。 ノリノリの DJ がいる会場 講演開始の 8:00 になるとオープニングムービーが流れた後、AWS の CEO である Matt Garman 氏が登場し、会場の盛り上がりも最高潮に! 登場の瞬間を激写することに成功 この後 3 時間ほどかけて、たくさんの新サービスや新機能が発表され、発表のたびに拍手喝采が起こっていました。 Amazon EC2 Trn2 インスタンス/ Trn2 UltraServers Amazon S3 関連 Amazon S3 Tables Amazon S3 Metadata Amazon Aurora DSQL Amazon DynamoDB global tables Amazon EKS Auto Mode Amazon EventBridge と AWS StepFunctions のプライベート接続 Amazon Nova Amazon Bedrock 関連アップデート Amazon Q 関連アップデート etc... Amazon の CEO 兼社長である Andy Jassy も登壇 早朝から 3 時間の講演はかなりハードでしたが、AWS のスケールの大きさや影響力を肌で感じることができました。 EXPO(展示会) EXPO とはいわゆる企業展示会場のことで、AWS のエキスパートやパートナー企業の方と直接お話ししたり、最新の情報収集をすることができます。 この会場もとにかく広いです。日本の企業もちらほら見かけました。すべてのブースを回ろうと思うと数時間かかるのではないでしょうか... EXPO は Day 2 の 16 時からの開場で、初日はフードやドリンクの提供もあり、ちょっとしたパーティーのようでした。 EXPO 会場の様子 企業ブースがたくさん 気になる企業でサービスについて詳しく話を聞くのもよし、いつもお世話になっている企業のブースに行ってグッズをもらうのもよしです。 頂いたぬいぐるみ自慢 技術セッション イベント中には3,000 以上の技術セッションが開催されます。 セッションタイプや対象業種/業界、レベルが分かれており、予約または事前に並んで(Walk-Up Seats)参加します。 セッションタイプ 主要なセッションについて説明します。 Breakout Session 1 時間の講義形式で特定のトピックについて深掘りする。のちにオンデマンドで動画配信あり Workshop 2 時間の実践型セッション。個人やグループでのハンズオン Chalk Talk 10~15 分の講義の後、45~50 分の質疑応答を行う。少人数での開催 Builders Session 1 時間の小グループセッション。AWS エキスパートとのハンズオン GameDay ゲーム形式のセッション。 レベル 100 レベル(入門)から 400 レベル(エキスパート)まで難易度が分かれているので、自分のスキルレベルや興味に合わせてセッションを選択できます。 参加してみて セッション選びが難しい かなりの数なので、開催場所やスケジュールを考慮しながら自分に合ったセッションを選択するのが難しかったです。 事前にセッションカタログの絞り込み機能を活用しつつ、タイトルだけではなく詳細まで確認して、よくよく検討しておけば良かったなぁと反省しました。 予約なしでも意外といける 人気そうなセッションでも、30〜45 分前に並んでいれば予約なしで入れました。 予約いっぱいでも、直前に席が空いて予約できたりもしたので、諦めずにチェックするのも一つの手です! 予約すべきセッション Breakout Session は配信があるので、それ以外のグループワークや質疑応答ができるセッションに参加するのがおすすめです。 特に GameDay のようなゲーム形式のセッションは、すぐ予約が埋まってしまうので、参加したい場合はセッション予約開始時にすぐ予約するのが良いです。(私はぼけっとしていて予約できなかった) SWAG SWAGとは企業のオリジナルグッズのことです。 re:Invent 参加者全員がもらえる AWS のグッズから、EXPO に出展している企業のグッズまで様々なアイテムが手に入ります。 AWS の認定を取得している人だけがもらえるグッズもありました。 SWAG 受け取り会場 戦利品のご紹介 企業ごとに特色がありますね。 AWS パーカーや T シャツはもらってそのまま着ている人が結構いました。 持って帰るのが大変な量 認定者ラウンジ 認定者ラウンジとは、AWS 認定資格取得者だけが入ることのできるラウンジです。 資格を 1 つでも保有していると入ることができます。 入場するためには、受付で Credly での認定バッジを提示する必要があるので、あらかじめ準備しておくのが良いと思います。 ラウンジの中には作業スペースがあり、ドリンクや軽食が提供されています。 私がステッカーをもらったり撮影したりと喜んでいる隣で、みなさん黙々と作業されてました。 照明がオシャレ 広々とした作業スペースで黙々と作業する人々 おわりに AWS re:Invent 2024 参加レポート、いかがだったでしょうか。 なんとなくでも雰囲気を感じ取っていただけたら幸いです。 私自身、かなり開発や語学学習のモチベーションが上がりました。 興味のある方はぜひ来年参加してみてください! 参加メンバーと会場にあった大きな黒板の前でパシャリ 📷✨ マイナビロゴはどこにあるでしょう?👀 イベントURL: AWS re:Invent 2025 | December 1 – 5, 2025
バケットくんとは バケットくん(正確な日本語訳不明・英語: Buckets)は、S3のマスコットキャラクターです。 AWS Storage Blog の下部に紹介文が記載されています。 re:Inventに参加しないと知りえない情報を集めてみました。 身体的特徴 バケットくんが羽織っているダウンにはS3テーブルとS3メタデータという今回発表された機能のワッペンのようなものが付いており、AWS社内で予め準備されていたことが伺えます。 後頭部に存在する取っ手のような部分を動かそうとしている人を見かけましたが、動かせず固定されていました。触られたことに気づいたようでしたが、バケットくんには首が存在しないため後ろを確認するのに少しあたふたしている様子でした。 生息域 毎年開催されるre:Inventのイベント会場内に出現されることが知られています。今回マイナビのエンジニアはEXPO会場・KeyNoteの会場近くにいるところを目撃しました。また、Wynnホテルなど、会場内の他の場所で目撃したという情報も耳にしました。 EXPOのAWSブースの方に、バケットくんに会ったことがあるか聞いたところ、今回は3日間ブースに立っているが見たことが無いと言っていました。バケットくんに会いたい方は、能動的に探すことが求められそうです。 バケットくんの周りには必ず人間が2名程度付き添い、一緒に行動をしていました。 性格 気さくな性格。女性と撮影する際は膝をつくことも。 撮影用にダンスもしてくれます。大きめの上半身とは裏腹に、意外と身軽です。 関連情報 マイナビで入手することはできませんでしたが、re:Invent2日目までは、バケットくんに会うとステッカーをもらえたらしいです。そのため、ステッカーを集めたい人は早めにバケットくんを探し始めたほうがよいです。 EXPOのAWSブースでは、バケットくんの亜種?のステッカーが貰えました。 最後に マイナビ社内向けに本記事を公開したところ、なんとお世話になっているAWSの担当者さまに「 AWS社内に展開したい 」とお声がけをいただきました!バケットくんは、多くの人に愛されるキャラクターのようです。
はじめに 皆さん、こんにちは!デジタルテクノロジー戦略本部(以下デジ戦)のT.Uです。2024年11月にAI・ディープラーニングに関する資格「JDLA Deep Learning for GENERAL(通称:G検定)」を受験しましたので、今回は学習方法や受験の内容についてお伝えできればと思います。少しでも参考になれば嬉しいです。 資格取得にあたり、マイナビのデジ戦社員は、会社の制度を利用することで勉強~受験までを実質無料でできます。(マイナビ&デジ戦最高!) 資格・試験の概要 資格・試験名 JDLA Deep Learning for GENERAL(G検定) 資格・試験の説明 G検定とは、一般社団法人日本ディープラーニング協会(JDLA)が実施する、AI・ディープラーニングの活⽤リテラシー習得のための検定試験です。(公式ページより) 試験日 2024年11月09日(土) 試験の形式 CBT(自宅受験可)形式 120分160問 受験に際して 2024年第6回の試験からシラバスと問題数に変更があります。 ネット上には変更前の情報が多いためご注意ください! 資格取得の背景 マイナビバイトではGoogleのDeep learningやAIの技術を取り入れようという取り組みを進めています。 そのため技術の背景や歴史を知ることで、より業務理解を深めることを目的に取得しました。 学習方法 使用した教材 Udemy デジ戦の社員が無料で利用できるオンライン学習ツールです! 「【全200問の模擬試験付き】G検定に合格するための集中講義!人工知能(AI)について体系的に学ぶ(初心者向け)」 公式テキスト 「深層学習教科書 ディープラーニング G検定(ジェネラリスト)公式テキスト 第3版」 公式問題集 「徹底攻略ディープラーニングG検定ジェネラリスト問題集 第3版」 オンライン問題集 「 Study-AI 」 学習期間 約3か月(30時間程度) 学習スケジュール 最初の1か月半で公式本、Udemyをざっと見る。 とりあえず見る。何となく見る。興味を持って見る。何はともあれ見る。 残りの1か月半で問題集をとりあえず解いてみる。 間違った箇所は「×」、何となくしか分からなかった箇所は「△」を付けておき、その部分は解説→(それでも意味が分からない場合は)公式本を読む。 土日など時間を取れるときに模擬問題をやってみる。 私は問題集の模擬問題をやりましたが、Udemy、Study-AIでも良いと思います。 まずは時間を気にせず解いてみる→時間を計って解く。 時間以内に解けるように頑張る。 試験は120分で160問(単純計算で1問当たり45秒)なので、時間配分や120分160問の恐ろしさを身体で理解する。 学び・気づき 「実質無料」で受験可能!?とは 結論、 勉強はデジ戦の「書籍購入補助制度」で購入した書籍とUdemy 受験は自宅なので交通費不要 受験料はマイナビの「IT・ビジネス資格取得支援」制度を利用する ことで、資格取得に関わる費用が基本的にかからなくなります! 資格取得支援制度に関しては、合否に関わらず受験資格ごとに1回は支給されるため、安心してチャレンジできますね。 p.s. 今回紹介しているオンライン問題集のStudy-AIについても、現在はβ版ということで、無料で利用することができました。(会員登録は必要) 勉強に関しては範囲が広く、量が多いです。(公式本は448ページ、Udemyも13時間くらいあります。) 全てを真剣に調べながら進めると試験に間に合わないため、「こういうものなんだ」程度で一旦納得し、興味がある部分だけ詳しく調べるという進め方をしました。 試験に関しては、PCを使った自宅受験ができます。 試験内容は、どれだけすぐ解ける問題に回答して、次に行けるかが合格のカギだと思います。 問題文がかなり長いものもあるので、読解力も併せて必要な気がします。 かくいう私も全て解き終わった時には、残り時間が15秒でした。(見直しほぼできず ..> <.. ) 一応ですが、全く分からない問題は「A」といったルールを事前に決めておきました。 結果についてはその場では分からず、受験後3~4週間でメールが届きます。 私の場合は、2024年11月25日(月)に合否メールが届きました。 メールには「合否結果」「総受験者数と合格者数」「各分野別の得点率」等が記載されています。 合格証やオープンバッチなどの発行は、もう少しかかるようです。(2024/11/26時点) 資格取得支援制度で必要になる受験料の領収書に関しては、合否メールとは別で届きます。 ▼2024/12/10 追記 合格証書も無事届きました。 さいごに 今後もし取得を目指している方がいれば、 内容も多く大変な資格かと思いますが、合格率は高い資格ですので合格目指して頑張ってください!
はじめに デジタルテクノロジー戦略本部とは 2022年10月、事業部門ごとに点在していたITとデジタルマーケティングの担当部署を1つに統合する形で『デジタルテクノロジー戦略本部(以下、デジ戦)』が誕生しました。 それまではドメインごとにサービスの開発・運用・マーケティングを行うという縦割りの構造で事業を展開していましたが、それだけでは予測不能と言われるこれからの時代に対応することは困難です。そこで、データやシステム、人材などアセットの最適化を図り、より大きなスケールでイノベーションを推し進めていく組織が作られました。 デジコネとは デジコネとは、デジ戦に所属するメンバーの中で、 「他のチームの人が何をしているのか分からない」 「もっと他チームのマーケターと交流を図りたい・・・」 といった悩みを持った方や、組織自体への課題に対し、マーケター間で交流を図る機会の1つとして発足したプロジェクトです! 詳しくはこちらの記事をご確認ください! ###card_post_id=1068### 内容:第6回開催内容に関して テーマ:「マイナビが競合に打ち勝つためのマーケターの心得とは?」 登壇者:ノバセル株式会社 田部 正樹様  ノバセル株式会社 は、「マーケティングの民主化」をビジョンに掲げ、「指名検索」を重要指標とし、当社が独自に開発した「ノバセル for TV」や「ノバセル for デジタル」などの効果分析ツールや、第三者目線で評価・改善を行う「オーディットサービス」など、オンオフ横断で多様なマーケティングサービスを展開しています。 マーケティングの定義 マーケティングの基礎的な考え方や重要なポイントをご紹介いただきました マーケティングとは、商品を売ることではなく、売れる仕組みを作ること。 具体的には、プロダクト、価格、場所、プロモーションの4つの要素(4P)を組み合わせて、顧客に選ばれるための戦略を立てることを指す。 マーケティングの目的は顧客に選ばれて当たり前の状態を作ることであり、これがマーケティングの本質 だと解説いただきました。 また、他社にはない独自の特徴(POD: Point of Difference)を見つけ、それを強調することが重要であることや、カテゴリー内でのポジショニング(カテゴリーエントリーポイント)を確立することが重要であるという内容に関しても講義いただきました。 グループワーク 勉強会後半では、自分たちのサービスに関するマーケティング戦略を考えるワークショップに取り組みました。各グループは、サービスの存在理由(Why)、対象顧客(Who)、強み(What)、価値(Value)等を明確にするためのフレームワークを使用しました。 田部様より、複数サービスにおけるマーケティング戦略の貴重なフィードバックをいただき、大変有意義なワークショップとなりました。 感想 事後アンケートではこんな感想をいただきました! 「再認識させられること、ハッとさせられること、どちらも盛り沢山で有意義でした」 「事業が成長する過程や、自社の訴求ポイントを整理するための手法を学ぶことができた」 「同じサービスのメンバーで、今改めてマーケティングの話を聞き、ブランドについて話し合う。とても貴重な機会になりました」 感想にもある通り、改めてフレームワークを活用し、サービス内のメンバーと時間とって話し合うことで再認識できたことや気づきを得ることができたという意見が多かったです。今後も皆様の業務に役立つよう、デジコネの開催を進めてまいります。 過去回はこちらから! 第1回 「Z世代の心を掴むマーケティングとは?」 第2回 「Meta/マーケ担当が抑える最新トレンドとは?」  第3回「ADKマーケティング・ソリューションズ/明日から認知マーケターになれる?TVCMプランニングのすべて」 第4回「株式会社イー・エージェンシー/データの力を引き出す!GA・Looker Studio・BigQueryの基礎知識」 第5回「株式会社FaberCompany/LP大解剖!CVR改善!LPのPDCAの回し方
はじめに お疲れ様です。 Webマーケティング課のY.Dと申します。 専門はGA4などのマーケティング分析基盤を中心に仕事しています。 約一年ほど、マイナビが出資しているインドネシアの「企業×フリーランサー」のマッチングサービス「Sribu」に対して、私含めたデジ戦プロモーション部隊の3人でマーケティングサポートを行って参りました。 https://www.sribu.com/en ★買収時のニュースなど https://www.nikkei.com/compass/content/PRTKDB000001540_000002955/preview https://www.mynavi.jp/news/2021/12/post_32688.html サポート内容 Sribuマーケティング支援の詳細内容は以下です。 ①週一の定例会議 Sribu×グローバル経営×デジ戦で会議を行います。 ここではマイナビインドネシアが通訳となり英語と日本語が入り混じる会議となっています。(英語勉強しろ) ②広告戦略の立案 Sribuではオンライン広告でGoogle広告とMeta広告を運用しており、適切なキャンペーン構造や投資金額を立案します。Sribu側CMOと打ち合わせをしながら最適な広告効果を狙います。 ③計測設定 GA4やGTMなどの計測に関与するツールに関して技術的な支援をします。マイナビでも起きている数値が取れない・間違っているみたいな問題の解消をめざします。 なぜ出張したのか?【目的など】 オンラインでの会話を重ねてきた本件でしたが以下の理由で今回の話へとつながりました。 つまり、「直接会話をするため」ということです。 これは外国人だからというわけではないと思うのですが、実際に会ったこともない人・リアルでの交流がない人からの意見は基本素直に受け取ってもらえません。 人に話を聞いてもらうためには、まずは信頼関係を構築しコミュニケーションを深めることが最優先であると今回学びました。 出張場所(ジャカルタ,Sribuオフィスなど) 羽田空港からジャカルタまでは片道7時間半で、赤道直下の南国でした。日本よりも直射日光が鋭く、冷房の効きもより強いものでした。穏やかな時間がゆったり流れている雰囲気でした! ★渋滞 なんと・・・インドネシアには 信号がありません。 あまりも交通量が多く、雰囲気で横切ったり、合流したりしています。 ★辛い 食べ物がすべて辛いです。下記は適当に入った街のごはん屋さん。 伝統的な「ナシゴレン」などもスパイスの効いた激辛料理です。(個人的には) ※水道水が飲めない(というかアブナイ)ため氷などには注意が必要 ★Sribuオフィス ベンチャーキャピタルを感じるオフィスでした。 出張スケジュール 全体で3泊4日の出張スケジュールは以下です。 10月30日(水) 移動日なので割愛。 10月31日:木曜日 Ryan(CEO)挨拶:9:00-9:30 Alex(CMO)挨拶:9:30-10:00 マーケチームとのMTG①:10:00-12:00 自己紹介、質疑応答、問題点の整理、解決策のディスカッション [宿題] 自己紹介スライド(簡易)+Sribuへの質問を準備 一緒にランチ:12:00-13:00 各自手を動かす作業時間(都度質問対応・モニタリング):13:00-15:00 Staffinc訪問(webマーケチームとの情報交換):16:00-17:00 Sribuではこんなことやっているという共有 Staffincはどんな感じで運用しているのかのヒアリング 11月1日:金曜日 マーケチームとのMTG②:9:00-10:00 昨日の振り返り、今日のアクション明確化 各自手を動かす(都度質問対応・モニタリング):10:00-12:00 一緒にランチ:12:00-13:00 マーケチームとのMTG③:13:00-15:00 改めての問題整理、解決策のディスカッション、帰国後のそれぞれのアクション明確化 CEO,CMOへの成果報告:15:00-16:00 マイナビインドネシア訪問:16:30-17:30 11月2日(土) 帰宅日なので割愛。 出張での成果 課題の再発見 :直接の対話を通じて、Sribuの計測や広告キャンペーン設計の不整合、予算配分の課題など、遠隔では把握しにくかった運用上の問題を特定できました。 集中した支援による意思決定への貢献 :2日間にわたりデジ戦側の知見を活用し、P-MAXキャンペーンや計測精度の改善策を提案し、Sribu側の意思決定プロセスに貢献できました。 戦略転換の合意形成 :繁忙期戦略として、CPA悪化を一時的に許容してCV数の最大化を目指す方向へ転換する方針を共有し、獲得広告の予算の合意を得ました。 基本的には初日にヒアリングを行い、2日目に最終成果としてデジ戦チームからの提案を行いました。 以下の資料が最終的なアウトプットと言えます。 ①ヒアリングまとめ ②改善提案(※抜粋) 上記とても真面目に書いておりますが、一番は「会話ができたこと」が大きいです。 最終日には集合写真を撮りました。言葉の壁はありましたが、お互いの信頼関係を築く機会となったと感じております。 感じたこと 今回の出張は3人で行きましたので、それぞれの感想を載せておきます! 日本の基準よりも細かい設定確認などがされておらず、現地で会話しながら事実確認をしないと現状を把握することが難しい。特に計測に関しては基礎的なパラメータの整理など日本では共通ルールとなっている部分も個人(企業)独自のルールとなっていたりして会話しながらすり合わせないと修正案を出せないと感じた。 広告配信において、これまで改善インパクトが大きいであろう提案したものの、内容のすべてが反映されているわけではなかった。今回の出張の間に、各キャンペーンの配信目的や意図、改善アクションにつなげる際にどこが障害になっているのかなど、現地で直接コミュニケーションをとることで、どのように提案したら納得してもらえるか考えることができた。 デジ戦管轄のサービスは既にサービスとして成り立っているものがほとんどだと思いますが、今回の支援はまだ小さなサービスをいかに成長させるかという視点で、WEB広告施策も本当にゼロからの取り組みとなったので、戦略的マーケティングの重要性を改めて知るいい機会になりました。たった「10万円」の投資でサービスを大きく改善できる可能性を持っているということ、マーケティングサイドの目標未達が致命傷になり得ることなど、マーケターとして常に感じなくてはいけない「責任」の重さを実感しました。 まとめ 海外でビジネスの経験ができたことは非常に視野が広がる有難い経験でした。 また、英語勉強を本格的にしないとまずいなと言う感覚になりました。AIでの同時通訳も技術的にはありますが、よりリアルタイムでラフにコミュニケーションを取ることが如何に重要か気づきを得られたと思っております。 グローバル経営企画×デジ戦との連携強化は全社にも大きなメリットがあると思います。 今回の出張をきっかけで新たな東南アジア企業のマーケティング支援の話も複数上がったとのことで、こういった案件が各所で起こっている状態が起きればデジ戦グローバル化も現実になるかも・・・? 読んでいただきありがとうございました。
はじめに デジタルテクノロジー戦略本部とは 2022年10月、事業部門ごとに点在していたITとデジタルマーケティングの担当部署を1つに統合する形で『デジタルテクノロジー戦略本部(以下、デジ戦)』が誕生しました。 それまではドメインごとにサービスの開発・運用・マーケティングを行うという縦割りの構造で事業を展開していましたが、それだけでは予測不能と言われるこれからの時代に対応することは困難です。そこで、データやシステム、人材などアセットの最適化を図り、より大きなスケールでイノベーションを推し進めていく組織が作られました。 デジコネとは デジコネとは、デジ戦に所属するメンバーの中で、 「他のチームの人が何をしているのか分からない」 「もっと他チームのマーケターと交流を図りたい・・・」 といった悩みを持った方や、組織自体への課題に対し、マーケター間で交流を図る機会の1つとして発足したプロジェクトです! 詳しくはこちらの記事をご確認ください! ###card_post_id=1068### 内容:第5回開催内容に関して テーマ:「LPのPDCAの回し方」 登壇者:株式会社FaberCompany 岩本 庸佑様   ============ 株式会社FaberCompanyにてCROコンサルタントをされている岩本様をお招きし、LP制作にお けるPDCAサイクルの回し方について詳しくお話しいただきました。 Web広告におけるLPの重要性 LPはユーザーの検討フェーズに合わせた訴求やストーリー設計を行い、ターゲットを絞ったコンバージョン率最適化(CRO)を実現するための重要なページです。ここではFaberCompany様のサイトを例に、WEBサイトとLPの使い分けについて詳しく解説いただきました。 LP検証と分析の基本的な流れ 月並みですがPDCAを継続的に回すことが基本! 今回は②~③を重点的に解説いただきました。 現状把握から打ち手設計 気づきの抽出 現状把握のための手法を詳しくご紹介いただきました。下記から1つ又は複数の手法で「気づき(課題要素)」を抽出します。 数値的なアプローチ:GA探索レポートによる把握 簡易ユーザーテスト:身近な人にCV手前までを試して貰い、横から見る ヒートマップ分析:スクロール(ページの精読率)状況、クリック箇所 差分分析:競合他社との差分分析からテスト要素抽出 打ち手の設計 抽出した気づきを施策まで落とし込むプロセスをご紹介いただきました。最初はこのプロセスを丁寧に実施することが推奨とのことでした。 ヒートマップ分析を実践! マイナビサービスのLPを、ヒートマップを活用して上記プロセスを実践しました。短時間ではありましたが、多くの施策案が出され、岩本様から貴重なフィードバックもいただき、大変有意義なワークショップとなりました。 ▼実際にワークの中で出た気づき~打ち手 感想 事後アンケートではこんな感想をいただきました! 「まさにLP制作における成果について、課内で苦戦している最中だったのでとても参考になりました。」 「終了後同じチームになった方と各サービスのLP状況などをお聞きできて参考になりました。」 感想にもある通り、各サービスのLP運用状況を知るきっかけとなり、具体的な代理店へのオーダーにつながったというお話も伺いました!今後も皆様の業務に役立つよう、デジコネの改善を進めてまいります。 第1回 「Z世代の心を掴むマーケティングとは?」 第2回 「Meta/マーケ担当が抑える最新トレンドとは?」  第3回「ADKマーケティング・ソリューションズ/明日から認知マーケターになれる?TVCMプランニングのすべて」 第4回「株式会社イー・エージェンシー/データの力を引き出す!GA・Looker Studio・BigQueryの基礎知識」
はじめに はじめまして!23卒のエンジニアのR.Aといいます。 現在は主に社内システムのデバイス管理ツールの開発を行なっています。 現在はバックエンド側はGoを用いてレイヤードアーキテクチャとDDD(ドメイン駆動設計)に沿って開発を進めています。 開発していく中でレイヤードアーキテクチャやクリーンアーキテクチャなどは綺麗に書けるもののコードの量が多く、時間が取られてしまうという問題点がありました。 その問題を解決するために定型的なコードは自動生成したほうが業務効率化につながると思いコードを生成できるようにしました。 使ったライブラリとしては以下のものになります。 jennifer 概要 自動生成の概要としては以下のようになります。 プロジェクトのdomain配下に対象のentity構造体を作成しておく generator配下のmodelディレクトリに対象のmodelの構造体を配置する(sqlboilerだとスキーマからmodelが自動生成されるの楽です) make generate-backend-base-code Entity=”任意のエンティティ名” Model=”任意のモデル名” というコマンドを実行 interfase, di, repository, usecaseのコードが自動的に生成される レコードの新規作成関数、更新関数が自動生成される 以下が動画になります。 0:04くらいにコマンドを実行しています。 自動生成の中身の説明 各フォルダにソースコードを生成してくれる素となるものが入っています。 generator ┣ di ┃ ┗ gen_di.go // di生成 ┣ handler ┃ ┗ gen_handler.go // handler生成 ┣ models ┃ ┣ admin.go // 生成したいモデル ┣ repository ┃ ┣ gen_repo_impl.go // レポジトリ実体生成 ┃ ┗ gen_repo_interface.go // レポジトリインターフェース生成 ┣ usecase ┃ ┗ gen_usease.go // ユースケース生成 ┗ generator.go // main関数 コード生成の部分を全て書くと長くなってしまうので生成するときに実行されるmain関数と、ユースケースの生成部分について書きます。 generator.go(main関数) 以下のmain関数を実行することで各フォルダの関数を実行し、生成するという形をとっています。 func main() { // コマンドライン引数を定義 entity := flag.String("e", "", "Entity name") model := flag.String("m", "", "Model name") // コマンドライン引数を解析 flag.Parse() // エンティティ名を取得 e := *entity m := *model fileName := toSnakeCase(e) // handler作成 genhandler.GenHandler(e, fileName) // di作成 gendi.GenDi(e, fileName) // usecase作成 genusecase.GenUsecase(e, fileName) // repositoryinterface 作成 genrepository.GenRepoInterface(e, fileName) // repositoryimpl作成 genrepository.GenRepoImpl(e, m, fileName) } gen_usease.go (ユースケース生成) ここでモデル名とエンティティ名を使い、ユースケースのコードを生成しています。 難しそうに見えますがコードの文字を羅列していく感覚に近いので、ライブラリの仕様を理解してしまえばそこまで難しくはないです。 package genusecase import ( "strings" "github.com/dave/jennifer/jen" ) func GenUsecase(e string, fileName string) { // Usecase interface E := strings.Title(e) // エンティティ名の先頭を大文字に abbrUsecase := strings.ToLower(string(e[0])) + "u" abbrRepository := strings.ToLower(string(e[0])) + "r" f := jen.NewFile("usecase") f.ImportName("main/domain/entity", "entity") f.ImportName("main/domain/repository", "repository") f.Type().Id(E+"UseCase").Interface( jen.Id("Add"+E).Params(jen.Id(e).Op("*").Qual("main/domain/entity", E)).Error(), jen.Id("Update"+E).Params(jen.Id(e).Op("*").Qual("main/domain/entity", E)).Error(), ) f.Type().Id(E + "UseCaseImpl").Struct( jen.Id(E+"Repository").Qual("main/domain/repository", E+"Repository"), ) f.Func().Id("New"+E+"UseCaseImpl").Params(jen.Id(abbrRepository).Qual("main/domain/repository", E+"Repository")).Id(E + "UseCase").Block( jen.Return(jen.Op("&").Id(E+"UseCaseImpl").Values(jen.Dict{ jen.Id(E + "Repository"): jen.Id(abbrRepository), })), ) f.Line() f.Func().Params(jen.Id(abbrUsecase).Id(E + "UseCaseImpl")).Id("Add"+E).Params(jen.Id(e).Op("*").Qual("main/domain/entity", E)).Error().Block( jen.List(jen.Id("err")).Op(":=").Id(abbrUsecase + "." + E + "Repository").Dot("Add" + E).Call(jen.Id(e)), jen.If(jen.Id("err").Op("!=").Nil()).Block( jen.Return(jen.Id("err")), ), jen.Return(jen.Nil()), ) f.Line() f.Func().Params(jen.Id(abbrUsecase).Id(E + "UseCaseImpl")).Id("Update"+E).Params(jen.Id(e).Op("*").Qual("main/domain/entity", E)).Error().Block( jen.List(jen.Id("err")).Op(":=").Id(abbrUsecase + "." + E + "Repository").Dot("Update" + E).Call(jen.Id(e)), jen.If(jen.Id("err").Op("!=").Nil()).Block( jen.Return(jen.Id("err")), ), jen.Return(jen.Nil()), ) // Save to file f.Save("../application/usecase/" + fileName + ".go") } コマンド実行後に生成されるコードは以下のものになります。 package usecase import ( "main/domain/entity" "main/domain/repository" ) type MemberUseCase interface { AddMember(Member *entity.Member) error UpdateMember(Member *entity.Member) error } type MemberUseCaseImpl struct { MemberRepository repository.MemberRepository } func NewMemberUseCaseImpl(mr repository.MemberRepository) MemberUseCase { return &MemberUseCaseImpl{MemberRepository: mr} } func (mu MemberUseCaseImpl) AddMember(Member *entity.Member) error { err := mu.MemberRepository.AddMember(Member) if err != nil { return err } return nil } func (mu MemberUseCaseImpl) UpdateMember(Member *entity.Member) error { err := mu.MemberRepository.UpdateMember(Member) if err != nil { return err } return nil } 工夫した点 インフラ層でエンティティからDBモデルに変換するための関数が必要でした(sqlboilerのmodel特有の型があるため) この関数の中にはフィールド30個ほど存在するものがあり書くのが大変であったため、この関数を生成できるようにしました。 該当の関数は以下のような変換関数です。 func ToDbModelMember(mri *entity.Member) *models.Member { return &models.Member{ Email: mri.Email, EntryTime: mri.EntryTime, Kana: mri.Kana, MemberID: mri.MemberID, Name: mri.Name, Post: mri.Post, UpdateTime: mri.UpdateTime, } } 上記のコードを生成するために以下の手順を立てました。 ASTを使いmodelの構造体の型情報をループさせentityと比較する 比較した上でentityをどの型に変換したらいいか分岐させる 上記の情報をもとに関数を生成する この手順をもとに以下のコードを作成しました。 models, _ := FindModelStruct(fileName, modelName) // ここでmodelを取得 dict := make(map[string]jen.Code) // ここでフィールド名をkey 変換するentityをvalueとして作成 pk := "none" for _, field := range models.Fields.List { // modelの構造体をループさせます name := field.Names[0] if strings.Contains(name.Name, "ID") && strings.Contains(name.Name, E) { pk = name.Name // 主キー取得 } // ここでtypeごとに変換関数を記述させるようにしています // sqlboilerで使われているnull.stringなどを変換できるようにしています switch typeName := fmt.Sprintf("%s", field.Type); typeName { case "&{null String}": dict[name.Name] = jen.Qual("github.com/volatiletech/null", "StringFrom").Call(jen.Id(abbrRepository + "." + name.Name)) case "&{null Int}": dict[name.Name] = jen.Qual("github.com/volatiletech/null", "IntFrom").Call(jen.Id(abbrRepository + "." + name.Name)) case "&{null Time}": dict[name.Name] = jen.Qual("github.com/volatiletech/null", "TimeFrom").Call(jen.Id(abbrRepository + "." + name.Name)) default: // その他の型の場合の処理 dict[name.Name] = jen.Id(abbrRepository + "." + name.Name) } } stmt := jen.Dict{} for k, v := range dict { stmt[jen.Id(k)] = v } // model関数生成 f.Func().Id("ToDbModel" + E).Params(jen.Id(abbrRepository).Op("*").Id("entity." + E)).Op("*").Qual("main/infrastructure/postgres/sqlboiler", modelName).Block( jen.Return( jen.Op("&").Qual("main/infrastructure/postgres/sqlboiler", modelName).Values(stmt), ), ) model構造体取得関数 上記のFindModelStruct(modelの構造体を取得関数)について解説していきます。 ファイルをパースして Goの抽象構文木(AST) という標準ライブラリを使用し、取得しています。 func FindModelStruct(fileName, modelName string) (*ast.StructType, error) { fset := token.NewFileSet() node, err := parser.ParseFile(fset, "./models/"+fileName+".go", nil, parser.ParseComments) if err != nil { log.Println(err) return nil, err } var model *ast.StructType ast.Inspect(node, func(n ast.Node) bool { switch t := n.(type) { case *ast.TypeSpec: if t.Name.Name == modelName { if x, ok := t.Type.(*ast.StructType); ok { // modelの構造体を取得 model = x return false // 構造体が見つかったら探索を終了します } } } return true }) if model == nil { return nil, fmt.Errorf("%s struct not found", modelName) } return model, nil } 今後と課題 新規追加と更新処理しかないのでCRUD処理全てを生成できるようにしたい また、不要なものが生成されてしまう場合があるのでカスタムして必要なものだけ生成できた方がいい プロジェクトに応じて関数名など微妙に変えないといけないので、プロジェクトに応じた対応が必要 統一化するなどをした方がいい entityからmodelに変換する関数部分がsqlboilerに依存してしまっている、また今回のようにDBモデルとエンティティが似ているものにしか使用できない DB駆動設計となっているので改良が必要 最後に 自動生成を自ら提案してプロジェクトの業務効率化に貢献できたことは大きな経験となりました。 自主的に行動して、開発スピードを上げていくような工夫をこれからも続けていきたいです。 参考にした記事 https://qiita.com/nghrsss/items/e6c9c95db19640f0f654 https://qiita.com/po3rin/items/a19d96d29284108ad442
Dreamforceについて Dreamforceとは、Salesforce社が毎年開催している世界最大のカンファレンスイベントになります。 世界中のSalesforceユーザが集まり、基調講演やセッションからインサイトや成功を共有し、業界の最新イノベーションやテクノロジーの未来を学ぶことができる場となっています。 1. Dreamforce2024 Dreamforce2024は、9/17日~9/19日の3日間、サンフランシスコで開催されました。 世界中から45,000人以上、日本からは700人以上の顧客・パートナーが現地参加されたそうです。 今年のDreamforceのテーマは、 『Humans with Agents drive customer success together』 (人とAIエージェントが共同して顧客の成功を支援する) と掲げ、「AIの第3の波」についてメインテーマとして取り上げられました。 WEBページ: https://www.salesforce.com/dreamforce/ 2. AIのイノベーション(第3の波) Dreamforce2024では、「AIの第3の波」が取り上げられました。 ここでは、AIのイノベーションを紹介させていただきます。 第1の波では「Chatbot」でAIが質問に対してルールベースで回答することができるようになりました。 第2の波では「Copilot」でAIベースで処理をすることができるようになりました。例えば、AIがメール作成や要約をし、人の業務をアシストすることができるようになりました。 そして、第3の波では「Agentforce」というAIプラットフォームが発表されました。こちらは、目的に対して計画を作成し、オーケストレーション、実行することができます。第2の波では、アシストすることまででしたが、第3の波でアクションを実行することまでできるようになり、人のように業務をこなすことができるようになりました。 マイナビでは、現在「Copilot for Microsoft365」など議事録や資料作成に利用していますが、「AIの第3の波」を導入することでさらに業務を効率化できるとともに新しいところに目を向けられる時代がくると感じました。 3. Agentforce 「Agentforce」とは、自律型AIエージェントとなり、営業・サービス・マーケティング・コマースなど、ビジネスのあらゆる領域で活用できるAIプラットフォーム。 簡単に言うと、Salesforceに蓄積されたデータをもとにAIが人のように業務をこなすエージェントとなるプラットフォームのようです。 また、Salesforceの新しいアーキテクチャが同心円の図で示されました。 これまでSalesforceが掲げてきた「Customer 360」の基盤としてData Cloudが位置づけられ、その外側の顧客接点のレイヤーにAgentforceが配置されています。 この新しいアーキテクチャは、Data CloudがSalesforceプラットフォームの心臓部として機能し、Salesforceに蓄積されたデータを集約し管理します。そのデータを利用し、各「Agentforce」が顧客との関係性に直接反映させる役割を担うことを示して紹介されていました。 4. SalesAgentのご紹介 「Agentforce」は、営業・サービス・マーケティング・コマースなど、ビジネスのあらゆる領域で活用できるAIプラットフォームになります。 ここでは、営業組織のための「Sales Agent」を紹介させていただきます。 「Sales Agent」とは、営業組織のための「Agentforce」になります。 「Sales Agent」には、「Einstein SDR」と「Einstein Sales Coach」のエージェント機能があります。 ①Einstein SDR Einstein SDR Agentは、見込み顧客と自然言語で自律的に対話し、問い合わせへの対応や営業担当者とのミーティングを設定することができます。 今後、見込み顧客や一度契約いただいたお客様に対して、Agentにより問い合わせ対応をすることできると営業担当者に時間ができますね。 ②Einstein Sales Coach Einstein Sales Coach Agentは、お客様役として、ヒアリング、プレゼン、交渉時のシミュレーションを行い、営業担当者と自律的なロールプレイを実行することができます。また、実際に商談に参加し、商談進行中にアドバイスをすることもできます。 この機能を使うことで、営業担当者全体の商談スキルも向上し、その結果商談成功率もあがることへ繋がると思いました。 5.(おまけ)Waymo Dreamforce2024では、「Agentforce」が「AIの第3の波」と紹介されていましたが、サンフランシスコの街中にも「AIの第3の波」がありましたので紹介させていただきます。 サンフランシスコでは、Waymoという完全無人の自動運転タクシーが走っていました。 Waymoは、現在 アリゾナ州(フェニックス)、カリフォルニア州(ロサンゼルス/サンフランシスコ)にて自動運転タクシー「Waymo One」を商業運用しているそうです。 「AIが人のように業務をこなす」まさしくこういうことなんだなと実感しました。 WEBページ: https://waymo.com/ 6. まとめ Dreamforce2024に参加し、これからは人とAIが共に働く時代がやってくることを実感しました。 その第一歩がSalesforceの「Agentforce」になるかもしれません。 また、今後のビジネスの中心は、AIによりデータ駆動型の意思決定がされる方向性であるそうです。 そのために、まずデータを整理すること、データの繋がり作ることが最低限必要と感じました。 今後のAI共同時代に向けて、データ活用を意識し業務を遂行していきたいと思います!
自己紹介 ゼネラルエージェント事業本部IT_WEB領域にて、求人企業の採用支援、求職者の転職支援を担当している佐藤です。 担当企業 :WEB事業会社(マイナビも担当しています) 担当求職者:WEBエンジニア、WEBエンジニアを目指している方 メイン業務は企業の採用支援ですが、現在は求職者支援もおこなっております。普段はCTOやVPoE含め、現場の方々とコミュニケーションを取っておりますため、(可能な範囲での)生の情報をご共有できます。 想定する読者 現在WEBエンジニアとして活躍されている方や、WEBエンジニアを目指し自己研鑽などをされている方を想定しています。 マイナビなどWEB事業会社に興味はあるけど、何から始めるべきかわからない… 今の実力でどういう会社に転職できるかわからない… 転職は考えていないけど、今の環境が今後に活きるのかわからない… 今後のために情報収集したいという方に、少しでも参考になる情報をお渡しできればと思っています。 はじめに WEB事業会社は、面接の数日前に対策をして合格するほど簡単な会社ではないです。普段の業務から意識や行動を変えていく必要があると捉えているため、1人でも多くのエンジニアの方に、WEB事業会社で活躍するエンジニアになるために、「持つべき意識」「取るべき行動」をお伝えできればと思っております。 ※本記事では技術的な部分ではなく、スタンスやマインド部分に注目し、まとめています。 本記事のまとめ(特徴5選) プロダクト志向の重要性 手段とやりがいの理解 3方向の視点(自分、仲間、ユーザー) 自責 プロダクトは仮説の集合体(課題定義力と課題解決力) まずは転職市場を理解するために、よくある転職理由から見ていきます 人それぞれ、理由は多くありますが、まとめると下記の理由が多いです。 スキル(キャリア)アップ 現在の仕事の成長機会が限られている 人間関係のストレスや職場環境の問題 現職の待遇(給与、福利厚生)に不満がある ワークライフバランスの改善を希望 業界や職種の変化に興味を持った 企業の将来性に不安を感じる 自分のスキルや専門知識をより活かせる仕事を探している 長期的な目標と現職のミスマッチ 地理的な要因や家族の事情による転居 よく聞く内容ですね。 この理由だけでWEB企業で合格が出るか、お見送りになるか、が判断されるわけではないです。ここから一歩踏み込んだところに、重要な理由が隠れています。 【例】スキル(キャリア)アップしたいという求職者 ⇒ なぜスキル(キャリア)アップしたいのか? 【求職者A】 今の環境では古い技術を用いた開発しかできず、新しい言語を用いた開発にチャレンジすることが難しい状況です。環境を変え、新しい言語を用いた開発環境に身を置きスキルアップしていきたい etc… 【求職者B】 自身が扱える技術の幅を広げ、よりユーザーに価値を届けるための技術の選択肢を増やしていきたい。新しい技術が必ずしも正解ではないが、新しい技術を学び理解しておくことで、今後プロダクトの成長や、自組織の効率化に貢献できる気付きにつながるかもしれない etc… WEB事業会社で活躍する人(WEB事業会社で内定が出る人)かどうか、求職者AとBの違い、面接官がみているポイントを整理していきます。 プロダクト志向と技術志向 プロダクト志向とは? 他者(ユーザー)視点で物事を考えており、技術を手段として利用し「何をつくればユーザーに価値を届けられるか」「なぜこの技術を選択したのか」を考えられることが多く、プロダクト重視の志向性 技術志向とは? 自分視点で物事を考えており、技術力が上がれば良いという考えで「何をつくればユーザーに価値を届けられるか」「なぜこの技術を選択したのか」を考えられないことが多く、技術重視の志向性 志向性を理解すること エンジニアとして、どちらが良いか悪いか、の答えはないです。どちらも良いと思います。ただWEB事業会社を目指すのであれば、「プロダクト志向」を持っていることが重要です。 2つの志向性どちらも持っているエンジニアもいたりしますし、技術志向でもWEB事業会社で活躍されているエンジニアもいたりします。繰り返しになりますが、どちらが良い悪いではなく、エンジニアとしてどうなりたいか、目的や将来の理想像によって異なるものでもあるので、まずこの志向性を理解することが重要です。 手段とやりがい 技術をどう捉えているか? WEB事業会社のエンジニアの多くは技術を「手段」として捉えていて、WEB事業会社以外のエンジニアは「目的」と捉えていることが多いです。 日本のIT環境がそうさせてしまっている可能性も高いですが、クライアントワークをしている人だと開発環境が限定的であることが多く、とある1つの開発環境に強いエンジニアとしてキャリアを積まれることが多いです。単価も上がり、会社も同等の開発環境の案件を集中させるケースも多いので、異なる開発環境を目指したり、手元のスキルを向上させることに意識が向いてしまうのは仕方がないことなのかもしれません。 エンジニアとして感じるやりがいにも違いがある WEB事業会社のエンジニアがユーザー貢献、サービスグロースなどをやりがいに感じている一方で、WEB事業会社以外で働くエンジニアは「技術向上」がやりがいになっていることが多いと感じます。※それが決して悪いわけではなく、希望する環境、将来の理想像につなげればいいだけです。 「なんでこの会社がこのサービスを作ろうとしているのか気になる」 「なんでこの設計になったのか気になる、他にも選択肢はあったのに、なんでだろう」 「リリースしたあと、そのサービスやシステムがどう使われているのか気になる」 といった理由から、WEB事業会社を志望されるエンジニアも多くいます。やりがいが「技術向上」以外に向いていると、上記のような発想になることが多いと感じています。 3方向の視点(自分、仲間、ユーザー) 自分視点、仲間視点、ユーザー視点、と3つに分けていますがWEB事業会社にいるエンジニアはこの3つの視点を持ち、うまく使いこなしていることが多いと感じます。よく利己的、利他的、とも表現されたりしますが… 自分視点  =利己的 仲間視点  =利他的 ユーザー視点=利他的 と捉えるとわかりやすいかもしれません。 WEB事業会社のエンジニアは自分視点(利己的)も持っていますが、仲間視点(利他的)、ユーザー視点(利他的)を存分に活用し、仕事していることが多いです。※自分視点は言葉の通りですので、割愛します。 仲間視点とは? この部分のコード、このままだとわかりづらいからもっとシンプルにしよう、ここに躓くかもしれないからコメント残しておこう、など、将来自分以外の、ただ自分に関係している仲間が仕事をするときのことを考えられる視点。 ユーザー視点とは? このプロダクトにどんな機能を追加したら、よりユーザーは使いやすいと思ってくれるか、ユーザーの利便性が上がるか、またはこのプロダクトからどの機能を排除すれば、よりシンプルにユーザーにとってわかりやすく使ってもらえるか、を考えられる視点。 ※普段はユーザー視点、仲間視点を合わせて他者視点と言ったりしていますが、ここではわかりやすく分けてます。あまり普段の会話の中で「仲間視点」は使わないですが、理解を深めるために記載しています。 自責か他責か 自責:自分で自分のあやまちを責めること 他責:自分以外の人や状況に責任があると考えること ここに関しては、エンジニアだけでなく全社会人共通して言えることだと思いますが、他責よりも自責で物事を捉えられる人であることが重要です。過剰過ぎる自責は不要ですが、ひとつひとつの行動に対し、成功も失敗も振り返りが大事です。 他責の人は自分は悪くないと思い考え方や行動を変えようとしない(=成長がない) 自責の人はどんなことからも改善点を見付け、考え方や行動を変えようとする(=成長がある) ので、ここは受ける企業がどこであろうが、意識すべきポイントになります。 (余談)プロダクトは仮説の集合体 有名な話ですが、「プロダクトは仮説の集合体」と言われています。またWEB事業会社では、仮説志向も重要視されていて… 課題定義力 × 課題解決力 この2つの能力が備わっていることも重要だと言われています。ここは長くなってしまうので、ご面談の機会をいただけた際にお伝えします。 最後に WEB企業への入社難易度は高いため、転職を考え始めた前後数ヶ月だけ頑張ってもあまり意味がありません。普段の生活から変えていく必要があります。 マイナビ含め、将来WEB事業会社で働いていくために今すべきことが知りたい 今の自分でWEB事業会社に受かるのか、受からないなら何が足りないのか、 など、気になる方はぜひお問い合わせください。 https://mynavi-agent.jp ※私の思いとしては、1人でも多くのエンジニアがWEB事業会社で活躍する世界をつくることです。面接対策をして、その時だけ良く見えるようにして、WEB企業に入れるエンジニアを増やそうとは考えていないです。ので、転職すべきではない方に求人紹介はいたしません。
はじめに こんにちは! 株式会社マイナビで内製開発をしているA.Kです! アメリカのラスベガスで開催された AWS re:Invent2023 に参加してきました 今回はre:Inventで開催されていたExpoに行ってきましたのでそのことについて書こうと思います Expoとは 世界各国の企業のサービスの展示会場です 各社がブースをもっていてサービスの紹介をしていたり質問をうけつけたりしています 主に5つのエリアがありました infrastructure Solutions Zone Data Zone Security Zone Developer Solutions Zone AWS for Industries Pavilion 会場はどんな感じ? 会場自体は初日の夕方からWelcome receptionという形でオープンされます オープン初日は入り口前で多くの人が待っていました オープン初日の様子 それ以降は朝10時ごろにオープンしていました 中に入るとこんな感じで各企業がブースを開いています 各企業のブースではこんな感じでサービスの紹介をしたり質問をうけつけたりしています それ以外にもイベントを開催したりセッションが開かれたりしています 企業のブースによってはレースのようなアクティビティが開かれたりしています こういったブースも数多くありました 企業のブース以外は下記のようなものがありました AWS資格の学習用ブース Jamのブース Gameday用のブース 会場にはドリンクや軽食が置かれており食べたり飲んだりしながらブースを回っている人が多くいました 夕方になるとアルコールの提供もされます 企業ブースではSwagを配っておりそれを目的にExpoを回るひとも多くいるみたいです 今回自分がもらったSwagはこんな感じです(Expo以外もあります) 個人的にDockerのマウスパッドはうれしかったです まわってみた企業ブース Expoは歩き回ったりアクティビティに参加するだけでも面白いのですが現地で企業の人から直接お話を聞けるのもre:Inventに参加する醍醐味の一つです せっかくなのでいくつかまわってみました どの企業もブースには多くの人がいて話し合いが行われています 自分は普段はWebアプリの開発をおこなっているのでDeveloperエリアを主にまわっていました Postman PostmanはなじみのあるサービスでしたがPostman Flowsの紹介をしていたので話を聞いてみました Postman Flowsはこんな感じでAPIを複数組み合わせて作るワークフローを視覚化された形で作成できます シナリオベースのテストをおこないたいとき活用できそうです ビジュアライズされてることでワークフローがわかりやすく開発効率が上がるだけでなく人に説明するときも便利です デフォルトではオフらしいので各自でオンにする必要があります 参考記事 Postman Flowsとは Postman Flows ドキュメント LaunchDarkly feature flagを利用して本番コードを管理するサービスになります feature flagとはコードにfeature flagと呼ばれるフラグを埋め込みその真偽値で表示の有無を変えたり特定のユーザー郡に対しての限定リリースに対応したりします launchDarkly上でフラグを作成しsdkを通じてそのフラグの真偽値を取得しコード上で利用できます その値はGUI上で切り替えることができます 簡単にユーザーに対しての表示を切り替えることができるのでABテストなどのユースケースで効きそうです 参考記事 LaunchDarklyとは LaunchDarkly ドキュメント rootly 障害発生時のアラートに関連したサービスになります 障害が発生すると専用のチャンネルが作成されzoomのリンクやJiraのチケットが作成されます Slack上でアサインなども可能です 会場では利用方法などをデモなども交えて見せてもらいました slack上でほとんど作業がおこなえるので作業効率が上がりそうです 参考記事 rootlyとは Pinecone ベクトルデータベースのサービスです 機械学習などでは文章や画像などのデータをベクトルに変換したデータを取り扱うためのデータベースをベクトルデータベースといいます このデータベースを用いた検索では、ユーザーの意図なども加味した検索だったりマルチモーダル検索などがあります ExpoではPineconeの無料枠のチケットをもらうことができました こういったのもExpoならではです 参考記事 Pineconeとは その他 それ以外には有名なサービスの企業も多くありいくつかまわりました github redis docker Vercel etc.. おわりに 今回はExpoについてまとめました re:Inventでセッションに参加するのも良い経験となりますがExpoで実際に現地でサービスなどに触らせもらったりすることもre:Inventならではの経験となりました Expoを回っていると自分の知らない普段の開発経験を向上させるサービスが数多くありました オンラインでもそういったサービスを得ることはできますが素通りしてしまいがちです Expoではオンラインとは違いその場でどんなサービスやどんな特徴があるのかをデモや説明ともに知ることができるので得るものが多くありました 読んでいただきありがとうございます
4年ぶりに対面で行われ、約3000人ものエンジニアが集結した Developers Summit 2024 にて、「マイナビの全社データ基盤のモダナイズ」をテーマに弊社社員が登壇をしてきました! 講演内容 マイナビ、そして私たちが所属するデジタルテクノロジー戦略本部の説明をした後、講演テーマでもある『全社データ基盤のモダナイズ』についてお話しさせていただきました。 オンプレで構築していたデータ基盤をどのようにしてAWSにクラウドシフトしたのか、導入前後の変化や技術選定のポイント、反省点について等、講演内容は登壇資料にもまとめておりますので、ぜひご確認いただければと思います! 講演資料 画像をクリックしてご覧ください! 登壇後記 マイナビではデータ民主化について取り組んでいます。 今回はそのデータ民主化のために必要不可欠なデータ基盤のモダナイズについてお話させていただきました。 オンプレ更改が起因ではありましたが、TCO削減や働くエンジニアの負荷軽減も含めた技術刷新ができ、そのおかげで新たなことに挑戦しやすくなりました。 今まで運用・保守に割かれていた時間が浮いた分、新しいことに取り組めています。 限られた時間で伝えきれないことも多かったですが、マイナビの取り組みが多くの方の参考になれば嬉しいです。 発表の中でお話できなかったところで少し技術的なお話をしますと、Snowflake接続のところでちょっと苦労した話があります。 より安全な接続をということでIP制限をしているのですが、PC接続では環境変数で適用するわけにもいかず、各ツール毎にbatを作ってプロキシを通すような対応をしました。 また、SnowflakeはIdPにAzureADを利用していたのですが、Power BIからの接続で外部OAuthセキュリティ結合が必要だったのですがここでも苦労しました。 ALTER SECURITY INTEGRATIONでexternal_oauth_any_role_mode = ‘ENABLE’を設定し デフォルトロールを設定しておかないといけないというのは盲点でしたのでPower BIからSnowflake接続を検討されている方のために記述を残しておきたいと思います。 最後に、マイナビではエンジニアを募集しておりますので、ご興味がある方はぜひお応募いただけますと幸いです!
はじめに サイトのローンチ時にサクッと負荷テストを行いたいといった依頼が多々あるのですが、0から負荷テストの環境構築をしたり、テスト会社へ依頼をすると時間や費用の負担が多くなります… AWSが提供している負荷テストソリューション「Distributed Load Testing on AWS」ではサクッと負荷テストの環境構築ができるとのことで、実際に試してみました。 Distributed Load Testing on AWS について AWS での分散負荷テストは、大規模な負荷時のソフトウェアアプリケーションテストを自動化して、リリース前に>潜在的な性能上の問題を特定するのに役立ちます。このソリューションは、一定のペースでトランザクションレコードを生成する数多くの接続ユーザーを作成およびシミュレートします。サーバーをプロビジョニングする必要はありません。また、このソリューションでは、複数の AWS リージョンにまたがってテストを実行することができます。 AWS公式ドキュメント: https://aws.amazon.com/jp/solutions/implementations/distributed-load-testing-on-aws/ 構成 上記は公式ドキュメントより引用したDistributed Load Testing全体の構成図です。 frontendはシナリオを作成するWebコンソール、backend/regionのリソースは負荷テストを実行するAPIと実行環境になっています。 公式ドキュメントページ内にCloudFormationテンプレートのソースコードのリンクもあるので、ソースコードをカスタマイズして実行環境の構成を変更することも可能です。 ※実際にDistributed Load Testing を利用してテストを実施した案件ではIP制限をしていたため、NAT Gatewayを実行環境の構成に追加し、アクセス元IPの固定化を行いました。 特徴 AWS Fargateコンテナを使用した負荷テスト実行環境 JMeterスクリプトを作成することで、アプリケーションテストのカスタマイズが可能 スケジュール機能を利用した負荷テストの自動化が可能 Web コンソールからテストデータのライブ表示が可能 コスト Distributed Load Testingで使用するAWSサービスの利用料が掛かります。 負荷テストの要件によりコストは変動しますが、サーバーレス構成のため比較的安価かと思います。 コストに関する詳しいドキュメントは こちら 今回のゴール 「Distributed Load Testing on AWS」の実行環境構築および、負荷テストの実施 ※本記事での負荷テスト対象は、CloudFront+S3で作成した検証用の静的ページとします Distributed Load Testingのデプロイ 公式ドキュメント の「AWSコンソールで起動する」ボタンを押下します。 CloudFormationのコンソール画面に遷移するため、画面に沿って処理を進めます。 テンプレートソースが指定されていることを確認して「次へ」を押下 スタック名、コンソールにアクセスするユーザー名、メールアドレスが必須項目のため、入力して「次へ」を押下 デプロイするVPCを変更したい場合やCIDR Blockを変更したい場合は、その他の項目も入力してください。 スタックオプションの設定は特に変更をせず「次へ」を押下 確認画面ではThe following resource(s) require capabilities: [AWS::IAM::Role]のチェックボックスにチェックを入れて「送信」を押下し、デプロイ完了を待ちます。 10分前後でデプロイが完了しました!デプロイ時に入力したメールアドレスにWebコンソールのログイン情報が届いているので、コンソールにログインします。 メール コンソールログイン画面 ダッシュボード Distributed Load Testingが利用できるようになりました! テストシナリオの作成・実行 コンソール画面上部の「CREATE TEST」を押下し、シナリオ作成画面に遷移します。 以下のように負荷テストの設定を行っていきます。 General Settings ここでは同時接続数やテスト時間など、負荷テストに関する一般的な設定をしていきます。 Task CountとConcurrencyの積が同時接続数の最大値となるため、今回は同時接続数最大値を5×200=1000に設定しています。 Ramp Upは5分、Hold Forは10分にし、5分かけて同時接続数の最大値まで到達させその状態を10分維持するように設定しました。 項目 説明 Task Count 起動するコンテナ数 Concurrency タスク当たりの同時接続数(推奨上限は200) Ramp Up 同時接続数に達するまでの時間 Hold For 同時接続数の保持時間 Run Now 予約実行をしない場合はこちらを選択 Run on Schedule テストのスケジュール設定 Scenario テストのシナリオ設定を行います。 今回はTopページへ負荷をかけるシンプルなテストのためSingle HTTP Endpointを選択し、エンドポイントを入力しました。 項目 説明 Test Type(Single HTTP Endpoint) 特定ページに負荷をかけるようなシンプルなテストであればこちらを選択 Test Type(JMeter) 複雑なシナリオを設定する場合はこちらを選択(別途JMeterのシナリオ作成が必要) シナリオを入力し画面右下の「RUN NOW」を押下するとテストが実行され、詳細画面に切り替わります。実行ステータスがRUNNINGとなっているため、このまま完了を待ちます。 ステータスがCOMPLETEとなれば、負荷テストは完了です! テスト結果 テストが完了するとSUMMARYが表示され、レスポンスタイム、レイテンシー、コネクションタイムの平均や、カウント数(トータル・成功・失敗)を確認できます。 より詳細の情報を確認したい場合は、CloudWatch Logsなどで確認可能です。 まとめ 実際に使ってみての所感は下記の通りです。とりあえず特定ページの負荷耐性だけ確認しておきたい!という場合には丁度良いのではないかなと感じました。 メリット 環境構築がとにかく簡単 Webコンソールの操作も分かりやすい 特定ページに負荷をかけるだけといった単純なシナリオであれば手軽に実施できる デメリット 複雑なシナリオを作成する場合は、JMeterでのシナリオ作成が必要 同時接続数最大値に達するまでのカウントもSUMMARYに含まれているため、整合性に欠ける? 長時間に渡って高負荷をかける場合は、コストに注意 Distributed Load Testingのリソースだけでなく、テストを行うサイトの側の通信量(見落としがち)の考慮も必要です
はじめに 皆さん、初めまして! WEBアプリケーション開発系のエンジニアとして採用された、2024年新卒入社のK.Iです。 私は学生時代は文系の出身でプログラミングとは無縁の学生生活でした。 そこから、とあることをきっかけにプログラミングやアプリ開発に興味を持ち、学習を続けてきた結果、ご縁あってマイナビに入社しました。 今回は、そんな僕が学生時代にWEB系のエンジニアとしてキャリアをスタートさせるために学んできたことを僭越ながら皆さんに共有させていただこうと思います。 現在WEB系エンジニアとしての就職を考えている方や、これからエンジニアを目指していく学生の方にとって少しでもヒントとなるように、なるべく再現性の高い内容に絞ってお伝えしていくので是非最後まで目を通していただけますと幸いです。 (※注意) 今回紹介する内容はあくまで個人の経験による内容を多く含むものとなるので、必ずしもすべての人に等しくあてはまる内容とは限らないということをあらかじご理解いただきたいと思います。 一個人がエンジニアになるまでにどんなことを考え、何を実践してきたのかということを事実としてとらえていただけると記事の意図が伝わるかと思います。 学習のゴールを設定する 学んだことを紹介する前に、私がエンジニアとして就職を検討する際に行った「ゴール設定」をお伝えさせていただきます。 私がエンジニアとして就職するにあたって、まずはじめに行ったこととしては、 「技術をどこまで学習したら就職活動を開始するか」 ということを先に定めるようにしました。 目的にもよるかと思いますが、私の場合、WEB系エンジニアとしての就職がメインだったので下記のようなゴール設定で学習を本格的にスタートさせました。 「企業に見せる成果物(WEBアプリケーション)を1つ作成する」 私の場合、文系でしたので企業にとっては「プログラミングやWEBに関して何の経験もない人」と判断されることは当然の事実としてありました。 そのため、 ある程度、技術的な興味関心がある この業界で長く続けていける素養がある ことを示すために上記のような目標を設定しました。 学んできたこと ここから本題の私がWEB系エンジニアとして内定をもらうまでに学んできた技術的な内容に関してお伝えさせていただきます。 先に結論から述べさせていただくと、、 コンピュータサイエンス Linux HTML, CSS JavaScript Ruby RDB, SQL Git, Github Ruby on Rails 上記の順番で学習を継続してきました。 全体として、約3か月程度の学習期間を設けました。 そののちに、就職活動をスタートさせ無事、エンジニアとして就職することができました。 ここからは、上記の内容に関して実際にどんなことを学んできたかを深堀りしていきたいと思います。 コンピュータサイエンス はじめに、コンピュータサイエンスの基礎的な内容に関して学び始めました。 現在学生の方ですでに情報の分野を専攻しているorいた方は、このパートをスキップしてしまったも問題ないかもしれません。 私の場合、情報専攻ではなかったので下記のような内容を中心にコンピュータの基礎的な内容を一通り学びました。 n進数 デジタルデータ(ビットとバイトなど) CPU・メモリ ハードウェア・ソフトウェア データベース ネットワーク セキュリティ 実際に利用した参考文献も記載しておきます。 エンジニアとして実務にかかわる上で、上記のような内容を知っているのといないのでは、理解に差が生まれるので情報系以外の方は改めて学習しておくことをお勧めします。 参考 基本情報技術者試験 対策用書籍 Linux Linuxは、Windows, Macと同様にOSの一種で、アプリケーションのサーバーのOSとして利用されることが多いので、エンジニアとして業務を行っていく上では必須の概念になります。 主にCLI(Command Line Interface)でのコマンド操作を扱うことになるため、Linuxの基礎的な概念と併せてよくつかわれる基本コマンドを押さえておくと、今後の作業がスムーズになります。 参考 https://linuc.org/textbooks/linux/ HTML, CSS HTMLはWEBページを表示するための骨組みのようなもので、CSSはHTMLに色を付けたり大きさを変えたり等の装飾を加えるための技術になります。 HTML, CSSの学習を通じて、WEBサイトが表示される基本的な仕組みを学習しました。 基礎的な内容に関しては、オンラインの学習教材( Progate , ドットインストール )を使って一通り学習し、その後は他のWEBサイトを模写する形でアウトプットを行いました。 模写に使用したおすすめサイト: https://code-jump.com/ 参考 https://prog-8.com/courses/html https://dotinstall.com/lessons/basic_html_tags_v3 JavaScript JavaScriptはWEBアプリケーション開発でよく用いられるプログラミング言語で、フロントエンドと呼ばれるユーザーの目に触れる部分の仕組みを実装することができます。 HTML, CSS同様、基礎的なインプットに関してはオンラインの学習教材を使用しました。 アウトプットとして、先述のHTML, CSSで模写したサイトに動き(ユーザーのアクションに対するイベント処理等)をつけながら改良したり、アルゴリズムの問題をJSを使って解きながら文法を身に着けていきました。 参考 https://prog-8.com/courses/es6 https://dotinstall.com/lessons/basic_javascript_v6 Ruby RubyはWEBアプリケーションのバックエンドと呼ばれる裏側の仕組みを構築できるバックエンド言語になります。 フロントエンドの言語であるJavaScriptと合わせて、一連のWEBアプリケーションを構築するために、バックエンド言語のRubyを学習しました。 Rubyを選んだ理由としては、 文法が簡単で、初学者でも学びやすいため。 日本語のリファレンス等も豊富で調べやすいため。(日本人が開発した言語のため?) フレームワークやライブラリが豊富でWEBアプリケーションを開発しやすいため。 また、日本の企業では比較的Ruby, Railsを採用している企業が多く、求人数も多かったたことも選択した理由の一つです。 ここでも、オンラインの学習教材メインで使用して学習を進めました。 アウトプットに関しては後述のRuby on Railsでまとめて行いました。 参考 https://prog-8.com/courses/ruby https://dotinstall.com/lessons/basic_ruby_grammar RDB, SQL 続いて、データベースの学習です。 RDB(Relational Database)は現在のWEBアプリケーション開発で主流のデータベースになります。 主な特徴として、テーブルと呼ばれるデータをExcelの表のような形式で管理し、それぞれのテーブル間の関係性を定義していきます。 また、RDBに格納されたデータに対して問い合わせを行い、データの取得・削除・追加・更新等の操作を行うための命令を記述する言語がSQL(Structured Query Language)になります。 いずれもWEBアプリケーションのバックエンドを実装する上では必須の知識となります。 なお、RDBを管理する仕組みにはいくつか種類があります。 MySQL PostgreSQL OracleSQL 主流かつ代表的なものとしてはMySQL, PostgreSQLになりますので、その辺りを学習しておけば問題ないかと思います。 学習方法に関しては、私の場合、SQLは書籍を使用し、RDBに関してはオンライン学習教材使用して学習を行いました。 RDB(RDBMS)の種類や役割、基本的なSQL文の文法が理解できていれば問題ないかと思います。 参考 RDB SQLおすすめ書籍 Git, Github Gitはプログラミングコードの変更履歴を管理するためのツールになります。 実務では複数人のチーム単位で行うことが一般的なのでその際に、ファイルの変更や最新版のファイルの管理等を円滑に行うためにGitがほぼ確実に使われます。 まずは Github のアカウントを作成し、そこに学習で使用したソースコードを挙げておくようにすると、Gitの練習になるのでお勧めです。 Git, Githubの学習に関しては実際の動きを確認できる方がよりイメージしやすいかと思いますので、動画で行うのがおすすめです。(参考資料の中にも動画教材を挙げています。) ただし、Gitを学習する際の注意点として、動画等でインプットしたら必ずアプトプットまでセットで行うことをお勧めします。 概念・ルールを理解することより「操作できる」ことが重要になるため、教材で学んだらそれをご自身の環境でも実践してみる形で並行して進めていくことをお勧めします。 参考 YouTube Udemy Ruby on Rails Ruby on RailsはWEBアプリケーションの開発を効率的に行えるようにするためのRuby言語のフレームワークになります。 要するに、Rubyでアプリ開発する際に必要となる機能をはじめから準備してくれている便利ツールです。 学習の流れとしては、、 Railsで開発する全体像を把握する チュートリアル等を使用して実践する になります。 1の全体像に関してはオンライン学習教材の Progate の講座等を活用して、Railsがどんなもので、どんな流れで開発してくのかをざっくり理解するとよいでしょう。 次のステップとして、Railsで記述するコードの内容やGemの使い方等を詳細に理解しながら、チュートリアルを進めていくことをお勧めします。 環境構築でいろいろなソフトをインストールしたりバージョンを合わせたりする必要があり、思った以上に苦労した経験があるので、一つ一つの手順を把握しつつ進めることをお勧めします。 まずは、見よう見まねで手順通りに動かして、徐々に理解できて来たら自分なりに改良しながら理解を深めるような流れで行うと良いかと思います。 Railsを学ぶ上での代表的な教材として Railsチュートリアル があります。 こちらの内容が大まかに理解できれば成果物のアプリケーションを作成する上で困ることはほとんどありません。 Railsの学習まで一通り終えることができたら、チュートリアルで完成したアプリの延長線上で独自のテーマを設定して自作のアプリケーションの作成に取り掛かりましょう。 テーマは~クローン等ありきたりなものではなく、身の回りで感じた悩み等を解決できるような独創性の感じられるテーマで作成しましょう。 ちなみに筆者は、コロナ禍で自宅にいる時間に料理を通じてつながりを増やしたいという発想から、SNS×レシピサイトのようなテーマで作成しました。(なぜこういう発想に至ったのかに関してはあまり覚えていません(笑)) 参考 Progate Railsチュートリアル 学習のポイント 最後に学習のポイントについていくつかお伝えしようと思います。 5, 6割理解できたら進める 内容が理解できず何度も何度も反復してしまうという方も中にはいるかもしれません。 私自身も文系で何の知識もないところからのスタートだったので、非常に気持ちは理解できますが、学習の目的は、「成果物を完成させること」なので、内容をはじめのうちから内容を100%近く完璧に理解しておく必要はありません。 実践していく中で理解できてくることも多いと思いますし、そこに何時間もとらわれてしまうのは非効率になってしまっていることの方が多いと思います。 なので、「やった内容がざっくり頭に残っている」ぐらいの状態でさっさと先に進むようにすると挫折することなくスムーズに基礎学習を終えることができます。 アウトプットを重視する 経験上、書籍や教材でインプットしているだけでは、用語や一定の知識は多少身に付きますが本質的な理解につながらず、いつまでたってもプログラミングができるようにはならないと思います。 そのため、インプットを行ったら併せて必ずアプトプットも一緒にするべきだと思います。 割合としては 7:3 (アウトプット:インプット) ぐらいがちょうどよいかと思います。 先述の「5, 6割理解できたら進める」の話ともつながりますが、半分以上内容を理解できたらさっさと実践してみるとより効率的に身に着けられると思います。 何よりも、そっちの方が楽しいですよね! ググりまくる エンジニアは実務において、実装方法に関して調べる場面が多々あります。 特に実装したことのない機能や、新しい技術を使用して実装するときなどはその割合が多くなります。 初学者であれば、なおさらわからないことや知らない知識・用語が多く出てくるかと思います。 ただ、安心してください。 そういった悩みはほとんど全てどこかの誰かしらが解消してくれています。 世界中の誰も遭遇したことのない目新しいエラーを引き当てる方が難しくなっています。 そのため、現在であればChat GPTなどの生成AI等の力も借りられるので、それらも活用しつつ自力で解決して、自力で実装する力をつけることがエンジニアとしてジョインしてからの苦労を減らすことにもつながると思います。 なので、わからなくてもとにかくググりまくって解決する癖をつけておくとよいかと思います。 投稿 文系・未経験からWEB系エンジニアになるために学んできたこと は マイナビエンジニアブログ に最初に表示されました。
はじめに 皆さん、こんにちは!デジ戦のT.Tです。 就職に伴い、上京する方はとても多いと思います。土地勘が無い中での住まい探しは不安だと思いますし、私も実際そうでした。 そこで今回は、私や同期の家探し状況や入社後の生活をお伝えし、少しでも参考にしていただけたらと思います。 経歴 静岡県出身。2024年よりUI/UXデザイナーとして入社。 大学では美術系の学部に在籍し、その中でもUI/UXデザインを中心に広くデザインについて学ぶ。 入社に伴い勤務地である新宿から電車で30分ほどの埼玉県に転居。同時に初めての一人暮らしを始める。 部屋選びで注意することってある? 私の場合は周辺環境を特に重視していました。中でも重視していたのはこの3つです。 虫の出にくさ 2階以上 日当たりの良さ(北向きは絶対に避けていました。) 付近に公園などの自然が無いところ 徒歩圏内にスーパーがあるかどうか 駅からの近さ 最寄駅から徒歩15分圏内で探していました。 初めての一人暮らしだったため、部屋選びに関する知識は全くありませんでした。大学時代から一人暮らしをしている友人から助言をもらい、確認すべき項目をまとめたチェックリストを作成してから不動産屋に向かった記憶があります。 人気のエリアってあるの? 基本的には東京在住の方が多い印象です。 路線でいうと、私の体感ですが小田急線沿いと埼京線沿いが特に多いイメージです。小田急線沿いでいうと、特に登戸にはなぜか関西出身の同期が集中しています。埼京線沿いも多いですが、こちらは結構最寄り駅はばらけている印象でした。 他にも埼玉県や神奈川県、千葉県といった県外から通われている方もいらっしゃいます。県外でいうと埼玉県が特に多い印象です。私も埼玉県から出社しています! 家賃って実際どれくらい? 家賃は、管理費込みで9万円ほどです。同期にも聞いてみたところ、6万円~9万円台と意外と幅広い結果が得られました。 私は部屋探しにあたってかなりこだわり(虫の出にくさや水回りなど・・・)があったためこの価格でしたが、選り好みしなければある程度家賃を抑えることも可能だと思います。 部屋の広さ リビングが6畳半くらいです。同期にも聞いてみたところ、大体5.5~7畳台くらいが多い印象でした。 ベッド、デスク、棚を置くとある程度面積は埋まってしまいますが、一人で住むことや掃除のしやすさなどを考えると個人的には丁度良い広さだと感じています。決して広いとは言えないですが、この「私の城」感をとても気に入っています! レイアウト 1K、風呂・トイレ別の独立洗面台付きの部屋です。 部屋決めにあたって風呂・トイレ別と独立洗面台だけは絶対に譲れないポイントだったため、とても気に入っています。 ドアや窓の配置や部屋自体の形は家具のレイアウトやその後の住みやすさにも大きく関わってくるので、間取り図や内見でチェックしてみてください! 定時後の生活 基本そのまま家に直帰していますが、週1,2回ほど同期と夜ご飯を食べに行きます。私が勤務している新宿のミライナタワーオフィスでは付近に美味しいご飯屋さんや有名チェーン店が多く、同期のおすすめのお店に連れて行ってもらうことが多いです。 同期として研修やインターンシップなどの話もしますが、同世代の友達としての会話もできる貴重な機会だと思っています。 休日の過ごし方 休日は、沢山寝たり平日出来なかった家事をやったりと、ゆっくりとした時間を過ごしています。 同期の方々と遊びに行くことも多いです。最近は一緒に服を買ったり美味しいものを食べに行ったりしました。 新社会人と初めての一人暮らし、不安はあった? 私は実家から大学に通っていたため、社会人になると共に一人暮らしをスタートさせました。 家事にかなり苦手意識があり、一人暮らしが決まった段階からかなり不安を感じていたのを覚えています。 そんな私でも、今のところ生活に困らない程度にしっかり一人暮らしできていると思います! 「この時間に洗濯だけでもやろう」など最低限のタスクを決めて、自分の余力を見てプラスワンの家事もやる、といった方式で家事をやっています。 また現段階では週1~2回テレワークがあるため、本来通勤に使う時間で家事を片付けたりと時間の活用を工夫しています。 最後に 一人暮らしの醍醐味はなんといってもこの自由さだと思います。 一人暮らしに不安を感じている方もいるかと思いますが、一緒にこの自由を楽しみましょう!