Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
モダンフロントエンド 開発研修
Search
Recruit
PRO
August 05, 2026
Technology
20
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
モダンフロントエンド 開発研修
2026年度リクルート エンジニアコース新人研修の講義資料です。
Recruit
PRO
August 05, 2026
More Decks by Recruit
See All by Recruit
つくって納得、つかって実感! 大規模言語モデルことはじめ ver2.0
recruitengineers
PRO
0
20
ブラウザ研修 2026
recruitengineers
PRO
0
21
事業価値と Engineering 2026年度版
recruitengineers
PRO
0
100
トヨタ⽣産⽅式(TPS)⼊⾨
recruitengineers
PRO
0
15
JavaScript 研修 (2026)
recruitengineers
PRO
0
10
TypeScript入門 2026
recruitengineers
PRO
0
14
Webアクセシビリティ入門 2026
recruitengineers
PRO
0
16
攻撃と防御で学ぶAI時代のプロダクトセキュリティ演習
recruitengineers
PRO
0
21
モバイルアプリ開発概論2026
recruitengineers
PRO
0
38
Other Decks in Technology
See All in Technology
AIエージェントに財布を渡す日 ― 承認付き"買い物エージェント"を作って実演
yama3133
1
110
PLaMoを毎日の開発で使い育てていく
pfn
PRO
0
180
plamo-3-translateの開発
pfn
PRO
0
260
Master Dataグループ紹介資料
sansan33
PRO
1
4.8k
AI驚き屋発見器
yama3133
2
400
クラウドセキュリティ入門 ~安全なクラウド利用のための基礎知識~
lhazy
8
7.1k
[MIRU26] To What Extent Does MLLM-as-a-Judge Exhibit Cross-Model Preference Bias?
keio_smilab
PRO
0
190
データ組織の転換期 一足飛びしない段階的戦略
leveragestech
PRO
0
140
【CEDEC2026】次世代デジタルカードゲームのサーバー設計と運用 〜『Shadowverse: Worlds Beyond』の舞台裏~
cygames
PRO
0
280
BigQuery を検索ソースとした AI Agent の作り方って 〇〇 通りあんねん
satohjohn
0
150
個人OSSが、机の上から世界に広がるまでの話
shinyasaita
1
310
Data Hubグループ 紹介資料
sansan33
PRO
0
3.1k
Featured
See All Featured
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
230
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
270
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
600
WCS-LA-2024
lcolladotor
0
780
Between Models and Reality
mayunak
4
380
Chasing Engaging Ingredients in Design
codingconduct
0
250
Code Reviewing Like a Champion
maltzj
528
40k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Designing Powerful Visuals for Engaging Learning
tmiket
1
470
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
190
Optimizing for Happiness
mojombo
378
71k
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
340
Transcript
モダンフロントエンド 開発研修 フロントエンド界隈の当たり前を知る
Profile // profile.jsonc { "name": " ", "alias": ["akfm_sato", "
"specialty": " ", "assignment": " ", "tags": ["Next.js", "UX"], "sns": { "X": "akfm_sato", "Zenn": "akfm", }, } 佐藤 昭文 あっきー"], フロントエンド 住まい
今日のテーマ : モダンフロントエンド開発 エンジニア市場における当たり前を知っておくことは、常に重要 今日の目標: フロントエンドにおけるモダンな技術の知見や感覚を養うこと ハードスキル TypeScript, Lint, Formatter,
Storybook Next.js ソフトスキル チームで助け合う技術
Agenda 実案件同様チームメンバー同士教え教わりながら進めましょう タイムテーブル 内容 10:15~10:45 はじめに 10:45~12:00 Storybook 12:00~13:00 休憩
13:00~15:00 Next.js 15:00~17:00 17:00~18:00 課題(ブログ開発) 振り返り
研修の前提知識 これまでの研修で以下の基礎理解を習得している前提としています ブラウザやHTTP HTML/CSS/JS React TypeScript Node.js
本研修での心掛け 以下を意識して研修に臨んでください。 教え教わる技術 モダンなチーム開発において最も大事なことは、教え教わる技術です チームメンバー同士、教え教わりながら進めましょう 理解や体験の目的意識 本研修は競争でも課題でもありません 深く理解することも大事、ある程度の理解で進むことも大事です
⚠️理解のために使用し、作業のために使わないこと AIツールについて ✅ 相談 ✅ 理解 ❌ 作業
はじめに 我々はなぜモダンな技術を学ばなければならないのか
リクルートとモダンな技術 リクルートのエンジニアは、モダンな技術を当たり前に知ってるものとみなされる リクルートにおいてレガシーとモダンはどちらも重要 ビジネスを支えるレガシーな技術 進化を支えるモダンな技術 リクルートのエンジニアは、モダンな技術ができて当たり前に思われてる 特定の技術を求められるより、「いい感じにしてほしい」の方が多い 「いい感じ」にするには、レガシーとモダン双方の深い理解が必要
リクルートのエンジニアリング 社外から見たリクルートを想像してみよう リクルートは非常に大きな社会的影響力を持っている 一人一人がその事を常に意識し、正々堂々人に誇れる仕事をすることが大事 リクルートのエンジニアは… 常に学び 技術に強く精通 人のために技術を使う
プログラマーの誓い https://qiita.com/diskshima/items/588c423977f42626910d 害を及ぼすようなコードは書きません。 2. 自分が書くコードがもっとも良いものであることを誓います。振る舞いや構造が悪いコードをリリースしま せん。 3. リリースごとにコードのすべてが意図通りに動くことの、速く、確実で、再現可能な証明も併せて作りま す。 4.
他の人の進みを妨げないよう、コマ目に、小さいリリースを行います。 5. 常にコードを躊躇なく、常に改善していきます。間違ってもコードを悪くしません。 6. 自分や他の人の生産性を最も高く保つようにします。生産性を下げるようなことはしません。 7. 他の人が自分をカバーできるよう、また、自分が他の人をカバーできるように保ちます。 8. 規模と正確性において正直な見積りを出します。確信のない約束はしません。 9. 常に学び、自分の技術を磨き続けます。 1.
リクルートのエンジニアであるということは 大きな責任が伴う
モダンな技術 =守りの技術 モダンな技術は、大きな責任を果たすための「守り」の手段 予期せぬ修正を防ぐ 長期的な保守性の向上 自動化による属人性の排除 高いパフォーマンス 高いセキュリティ
リクルートのエンジニアは モダンな技術を学び扱えなければならない
モダンフロントエンド開発 と を中心にモダンフロントエンドの開発スタイルを学ぶ Storybook Next.js 題材 を使ったコンポーネント駆動開発 Tailwind CSS, shadcn/uiを使ったコンポーネントの構築
Next.jsを使ったアプリケーション開発 全体を通して利用する技術 Storybook TypeScript Lint Formatter 時間の都合で省略する技術 AIツールの活用 テスト(Vitest/Playwright)
Storybook コンポーネント駆動開発とスタイリング
復習 : React ここからは事前課題をこなした前提で進めます が開発した、コンポーネント指向のフレームワーク JSXというJSの拡張記法でコンポーネントを宣言できる コンポーネントは必要に応じて再実行(再レンダリング)され、常に値が最新の状態に保たれる Meta function Accordion({
children }: { children: React.ReactNode }) { const [isOpen, setIsOpen] = useState(false); } return ( <div> <button onClick={() => setIsOpen(!isOpen)}>toggle</button> {isOpen && <div>{children}</div>} </div> );
コンポーネント指向とは コンポーネント: の構成部品 UI 公式ドキュメント - 隅から隅までコンポーネント ほとんどの React アプリでは隅から隅までコンポーネントが使われます。つまり、ボタンのような再利用可能なところでのみ使うので
はなく、サイドバーやリスト、最終的にはページ本体といった大きなパーツのためにも使うのです。コンポーネントは、1 回しか使わな いような UI コードやマークアップであっても、それらを整理するための有用な手段です。
コンポーネントの見た目、どうやって確認する?
Storybook コンポーネント単位で見た目や使い方を確認することができる https://storybook.js.org/
Storybook コンポーネント単位で見た目や使い方を確認することができる // button.stories.tsx import type { Meta, StoryObj }
from "@storybook/react"; import { Button } from "./Button"; const meta: Meta<typeof Button> = { component: Button, }; export default meta; type Story = StoryObj<typeof Button>; export const Primary: Story = { args: { primary: true, label: "Button", }, };
Storybook コンポーネント単位で見た目や使い方を確認することができる どのフレームワークでもコンポーネント指向は必須 コンポーネント中心に開発する上で、Storybookは必須 リクルート社内でReactを採用してる場合、Storybookも採用してるケースが多い
課題 : Storybookの起動 研修の をセットアップして、 を起動してみましょう Workspace Storybook 研修リポジトリのREADMEに従ってセットアップ 2.
packages/ui のREADMEに従ってStorybookを起動 1.
Tailwind CSS, shadcn/ui @repo/ui のコンポーネントは tailwind CSS と shadcn/ui を利用している
Tailwind CSS な フレームワーク 現在のCSS情勢はTailwind人気が非常に高い Utility 1st CSS shadcn/ui コンポーネントライブラリ 一強まで言わずとも非常に人気
課題: コンポーネントの追加 を追加し、 Checkbox のStoryを作成 Storyはどんなパターンがあるといいだろう🤔 2. Cardを追加し、 Card のStoryを作成
見やすいStoryにするには、どんな children がいいだろう🤔 3. その他好きにコンポーネントを導入+Storyを作成してみましょう 1. Checkbox
Next.js & React Server Components
とは Next.js Next.jsはReactのフレームワーク React=ライブラリ+仕様 Next.js=React系フレームワーク 現在最も人気 Legacy: Pages Router Modern:
App Router
はもう使わないべき? Q: Pages Router 結論: はい(一応現役だけど) 機能開発は停滞しており、実質的非推奨 将来的にApp Routerへの移行が必須になる https://nextjs.org/blog/next-13-4#is-the-pages-router-going-away
App Router Next.jsの新しいアプリケーション構築の仕組み app ディレクトリ配下に配置する React Server Componentsをサポートしてる デフォルトでServer Components
Server -> Clientの2層構造 強力なCache その他諸々新機能(多くて混乱するので割愛)
React Server Components React Server Components(RSC)はアーキテクチャ名 Server Component: サーバー側でのみレンダリングされるComponent Client
Component: 従来からあるクライアント・サーバーどちらでもレンダリング可能なComponent データとUIをまとめてComponentとして考える設計思想に基づく
のモチベーション RSC 従来からあったReactの問題を解決したかった デフォルトでより良いパフォーマンスの達成 ハイドレーション処理・バンドルサイズを減らせる 低速なクライアント<->サーバーの通信を減らせる サーバー間通信の方が高速でセキュア バックエンドへのフルアクセス より簡単に、Componentが必要なデータを取得できるようになった 中間層(gSSP,
tRPC, GraphQL, API Routesなど)の実装や設計が不要に(詳細は後述) 参考: https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md#motivation
と との違い RSC SSR 従来からあるSSR(Server Side Rendering)と混乱しやすいが、Server Componentsは全く異なる概念 SSR: Client
Componentsをサーバーサイドでレンダリングすること RSC: Reactの新たなアーキテクチャ名称 Client Components: 従来からあるComponent Server Components: サーバーサイドでのみレンダリングされるComponent
Server Components サーバーでのみ実行されるComponent Component自体をasyncにすることが可能(= fetch を直接的に扱える) useState などの一部hooksは利用できない Client Components/Server
Componentsどちらも含めることができる export async function Products() { // await const res = await fetch("https://dummyjson.com/products"); const product = await res.json(); 今までできなかったが、直接 } できる return ( <div> <pre> <code>product: {JSON.stringify(product)}</code> </pre> <Counter /> {/* Counter: Client Component */} </div> );
Client Components 従来からあるComponent "use client" はサーバーから見た時のクライアントサイドの入り口 import されるモジュールは、全てClientモジュールとなる "use client"
import { useState } from "react"; import { Child } from "@/components/Child"; export function Counter() { const [count, setCount] = useState(0) // Client Component } のみで利用可能 return ( <div> <p>count: >{count}</p> <button onClick={() => setCount(prev => prev + 1)}>increment</button> <Child /> </div> )
Client Components 従来からあるComponent children (などのprops)を除き、Server Componentsを含むことはできない "use client"; import {
useState } from "react"; export function Accordion({ children, }: { children: React.ReactNode; // Server Components }) { const [isOpen, setIsOpen] = useState(false); } return ( <div> <button>toggle</button> <div>{children}</div> </div> ); も可!
Server Action formの action からサーバー側の関数を実行できる "use server" はクライアントから見た時のサーバーへの入り口 // todo-create.ts
"use server"; async function createTodo(formData: FormData) { const title = formData.get("title"); // ...API POST DB ... } へ したり に保存するなど // page.tsx export default function Page() { return ( <form action={createTodo}> <input type="text" name="title" /> <button type="submit">Create</button> </form> ); }
その他多くの機能 これらの詳細は で手を動かしながら short tutorial Nested Layout Error/Loading UI dynamic
route revalidate parallel route intercepting route PPR etc…
課題: Helloにアクセスして、 world localhost:3000 のルートディレクトリより # Git $ cd apps/learn-next
$ pnpm dev のページが表示されることを確認しましょう Hello world
課題: Nested layout を修正して共通のヘッダーを作成しましょう layout.tsx // app/layout.tsx export default function
RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="ja"> <body> <header>all page's header</header> {children} </body> </html> ); }
課題 : New page ディレクトリを作成して新しいページを作成しましょう // app/page.tsx import Link from
"next/link"; export default function Page() { return ( <> <h1>Hello, Next.js!</h1> <Link href="/products">/products</Link> </> ); }
課題 : New page ディレクトリを作成して新しいページを作成しましょう // app/products/page.tsx export default function
Page() { return <h1>Hello, Products page!</h1>; }
課題 : New page ディレクトリを作成して新しいページを作成しましょう // app/products/layout.tsx export default function
RootLayout({ children, }: { children: React.ReactNode; }) { return ( <> {children} <footer>products page's footer</footer> </> ); }
課題データを : Server Components + fetch して表示しましょう dummy fetch //
app/products/page.tsx import { Suspense } from "react"; export default function Page() { return ( <> <h1>Product Page</h1> <Suspense fallback={<div>Loading...</div>}> <Posts /> </Suspense> </> ); } async function Posts() { const res = await fetch("https://dummyjson.com/products"); const product = await res.json(); return ( <pre> <code>{JSON.stringify(product, null, 2)}</code> </pre>
課題: でエラーが起きた時の Error UI は、 で定義しましょう page.tsx で強制的にエラーを起こすことで確認できます page.tsx UI
error.tsx // app/products/error.tsx "use client"; export default function Error({ error, reset, }: { error: Error & { digest?: string }; reset: () => void; }) { return ( <div> <h2>Error!</h2> <button onClick={() => reset()}>Try again</button> <pre>{error.message}</pre> </div> ); }
課題: を使うために Client Components + useState を追加しましょう useState "use client"
// app/components/counter.tsx "use client"; import { useState } from "react"; export function Counter() { const [count, setCount] = useState(0); } return ( <div> <p>count: {count}</p> <button type="button" onClick={() => setCount(count + 1)}> increment </button> </div> );
課題: を使うために Client Components + useState を追加しましょう useState "use client"
// app/products/page.tsx import { Counter } from "app/components/counter"; import { Suspense } from "react"; export default function Page() { return ( <> <h1>Product Page</h1> <Counter /> <Suspense fallback={<div>Loading...</div>}> <Posts /> </Suspense> </> ); } // ...
課題からサーバー側の関数を実行しましょう : Server Actions form // app/products/create-post.ts "use server"; export
async function createPost(data: FormData) { // skip validation const title = data.get("title"); console.log(`action called with title: "${title}"`); // ...API POST DB ... } へ したり に保存するなど
課題からサーバー側の関数を実行しましょう : Server Actions form // app/products/page.tsx import { Counter
} from "app/components/counter"; import { Suspense } from "react"; import { createPost } from "./create-post"; export default function Page() { return ( <> <h1>Product Page</h1> <Counter /> <form action={createPost}> title: <input type="text" name="title" /> <button type="submit">create post</button> </form> <Suspense fallback={<div>Loading...</div>}> <Posts /> </Suspense> </> ); } // ...
課題 : Dynamic route 動的な のページを作成しましょう URL // app/products/type.tsx export
type Product = { id: number; title: string; description: string; };
課題 : Dynamic route 動的な のページを作成しましょう URL // app/products/page.tsx import
Link from "next/link"; import type { Product } from "./type"; // ... async function Posts() { const res = await fetch("https://dummyjson.com/products"); const product: { products: Product[] } = await res.json(); return ( <> {product.products.map((product) => ( <div key={product.id}> <Link href={`/products/${product.id}`}>{product.title}</Link> </div> ))} </> ); }
課題 : Dynamic route 動的な のページを作成しましょう URL // app/products/[id]/page.tsx import
{ Suspense } from "react"; import type { Product } from "../type"; export default async function Page({ params, }: { params: Promise<{ id: string }>; }) { const { id } = await params; } return ( <Suspense fallback={<div>Loading...</div>}> <ProductDescription id={id} /> </Suspense> ); // ... (次のページへ)
課題 : Dynamic route 動的な のページを作成しましょう URL // app/products/[id]/page.tsx //
... (前のページより) async function ProductDescription({ id }: { id: string }) { const res = await fetch(`https://dummyjson.com/products/${id}`); const product: Product = await res.json(); } return ( <div> <h1>Product {product.title}</h1> <p>{product.description}</p> </div> );
課題 : use cache キャッシュしたいコンポーネントや関数には を追加しましょう "use cache" をファイルや関数の先頭に記述することで、キャッシュを有効化できる 有効期限は
cacheLife() で設定 import { cacheLife } from "next/cache"; async function Posts() { "use cache"; cacheLife("minutes"); } // ... "use cache"
課題 : use cache キャッシュしたいコンポーネントや関数には を追加しましょう cacheTag('tag-name') でキャッシュにタグを付与でき、Server Functions内の updateTag('tagname')
でタグに紐づくキャッシュをrevalidateできる import { cacheTag } from "next/cache"; async function Posts() { "use cache"; cacheTag("products"); } // ... "use cache"
課題 : use cache キャッシュしたいコンポーネントや関数には を追加しましょう cacheTag('tag-name') でキャッシュにタグを付与でき、Server Functions内の updateTag('tagname')
でタグに紐づくキャッシュをrevalidateできる "use server"; import { updateTag } from "next/cache"; export default async function revalidatePost() { // ... } updateTag("posts"); "use cache"
実践課題 ブログアプリケーションの開発
課題 : ブログアプリケーション 今日学んだことを活かして、ブログアプリケーションを実装しましょう 前提 ディレクトリに作りかけのブログがあります APIの仕様は mock-server/openapi.json を参照してください 要件は非常に抽象的なので、周りや講師に相談しながらよしなに仕様を決めてください
要件 1. 一覧を10件ごとに表示するようにして下さい 2. 詳細ページを実装して下さい 3. 記事の新規作成機能を実装して下さい 4. 記事の更新ページを作ってください 5. 記事の削除機能を実装して下さい 6. formにvalidation機能を追加して下さい apps/blog-app
課題ヒント 不明点は、Next.js公式ドキュメントやNext.jsの考え方を参考にして下さい 詰まったら近くの同期や講師に相談しましょう
知見の共有 組織人としてのエンジニアの責務
アウトプットの重要性 インプットは忘れるが、アウトプットはなかなか忘れない 情報の整理 自分が得た知見を体系的に整理できる まずは対話的に伝えるだけでもいい 情報の永続化 1ヶ月後の自分は他人 技術記事は自分の資産 知識の永続化と他人への共有が容易になる 今日は時間もないので、チームごとに得た知見を共有してみましょう
課題 : 知見の共有 今日の研修で得た学び・知見・感想を共有しよう 目的: 自分の知見を共有すること、相手から知見を得ること チームでまとまってください 1人2~3分程度で今日得た知見・疑問・感想を共有してください 全員話し終えたら、気になった話題について深ぼって教えあったり議論したりしましょう
まとめ モダンフロントエンド開発研修のまとめ
今日のテーマ(再掲) : モダンフロントエンド開発 エンジニア市場における当たり前を知っておくことは、常に重要 今日の目標: フロントエンドにおけるモダンな技術の知見や感覚を養うこと ハードスキル TypeScript, Lint, Formatter,
Storybook Next.js ソフトスキル 教え教わる技術
研修で触れられなかったこと 今日研修で扱えなかったものの、現場では普通に使うモダンな技術 単体テスト: Vitest+react-testing-libraryなどを AIツール: CursorやGitHub Copilot、Claude Codeなど UI/UX: モダンなUXやパフォーマンスチューニング
品質保証: 体系化されたテスト、開発プロセス
皆さんの今後のご活躍を楽しみにしています
End