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
BFF/SSRの話
Search
Yosuke Furukawa
PRO
February 19, 2018
Programming
26
7.4k
BFF/SSRの話
Mercari Web / Frontend meetupで話した BFF/SSR の話です。
Yosuke Furukawa
PRO
February 19, 2018
Tweet
Share
More Decks by Yosuke Furukawa
See All by Yosuke Furukawa
Welcome JSConf.jp 2024
yosuke_furukawa
PRO
1
3.7k
tc39 x jsconf.jp Panel Discussion 2024
yosuke_furukawa
PRO
0
190
Removing Corepack
yosuke_furukawa
PRO
9
1.4k
JavaScript Runtime とはなにか
yosuke_furukawa
PRO
15
2.6k
Strip Types と Storage
yosuke_furukawa
PRO
4
370
Module Harmony について
yosuke_furukawa
PRO
3
1.6k
LTのやり方
yosuke_furukawa
PRO
16
2.3k
AppRouter Panel Talk
yosuke_furukawa
PRO
3
750
Node.js v22 で変わること
yosuke_furukawa
PRO
13
5.7k
Other Decks in Programming
See All in Programming
2024年のkintone API振り返りと2025年 / kintone API look back in 2024
tasshi
0
210
Spring gRPC について / About Spring gRPC
mackey0225
0
220
時計仕掛けのCompose
mkeeda
1
280
第3回 Snowflake 中部ユーザ会- dbt × Snowflake ハンズオン
hoto17296
4
360
iOSエンジニアから始める visionOS アプリ開発
nao_randd
3
120
Java Webフレームワークの現状 / java web framework at burikaigi
kishida
9
2.2k
Linux && Docker 研修/Linux && Docker training
forrep
23
4.5k
パスキーのすべて ── 導入・UX設計・実装の紹介 / 20250213 パスキー開発者の集い
kuralab
3
670
ISUCON14公式反省会LT: 社内ISUCONの話
astj
PRO
0
180
Flutter × Firebase Genkit で加速する生成 AI アプリ開発
coborinai
0
150
Bedrock Agentsレスポンス解析によるAgentのOps
licux
2
720
Introduction to kotlinx.rpc
arawn
0
630
Featured
See All Featured
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
Designing for humans not robots
tammielis
250
25k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
129
19k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Thoughts on Productivity
jonyablonski
69
4.5k
Automating Front-end Workflow
addyosmani
1367
200k
Docker and Python
trallard
44
3.3k
How to Ace a Technical Interview
jacobian
276
23k
Navigating Team Friction
lara
183
15k
Making Projects Easy
brettharned
116
6k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
29
4.6k
Transcript
SSR / BFF ʹ͍ͭͯ Mercari Web / Frontend meetup #1
2018/02/19
Twitter: @yosuke_furukawa Github: yosuke-furukawa
/PEFֶԂ࣌ݶΔͷͰօ͞Μ དྷͯͶʂʂʂ
࠷ۙBFFʹ͍ͭͯͷΛ ͱ͋ΔϝσΟΞʹॻ͍ͯΔ
͓ͦΒۙ͘ެ։༧ఆ ʢకʹؒʹ߹͑ʣ
ࠓͷ • BFFೖ • SSRೖ • BFF/SSR࣮ફྫs
Α͋͘Δٙ • BFFͬͯͳΜͷͨΊʹ͍Δͷʁ • SSRͬͯSEOෆཁͳΒཁΒͳ͍Μ͡Όͳ͍ʁ • BFF/SSRͬͯ͘͠ͳ͍ͷʁ
BFFೖ
BFFͱԿ͔ • Backends For Frontends • ϑϩϯτΤϯυͷͨΊͷόοΫΤϯυ(αʔό) ͷ͜ͱ ͜͜ʂ
Q. ͳΜͰBFF͕ඞཁͳͷ͔
Q. ͳΜͰBFF͕ඞཁͳͷ͔ A. 2ͭ͋Δ 1. ৫తͳ 2. ੑೳతͳ
Q. ͳΜͰBFF͕ඞཁͳͷ͔ A. 2ͭ͋Δ 1. ৫తͳ 2. ੑೳతͳ (SSRͷ)
BFFͷ৫తͳ • ͜Ε·ͰͷWebΞϓϦέʔγϣϯ • ҰຕؠͰͰ͖ͨΞϓϦέʔγϣϯ • HTMLςϯϓϨʔτͰදࣔΛߏங • ߋ৽FormͰ
BFFͷ৫తͳ • ঃʑʹͦΕ͚ͩͰճΒͳ͘ͳ͖͍ͬͯͯΔ • Ajax௨৴ͰΠϯλϥΫςΟϒʹ • ϞʔμϧυϥοάΛۦͨ͠ϦονͳΠϯ λϑΣʔεʹ
BFFͷ৫తͳ • ঃʑʹͦΕ͚ͩͰճΒͳ͘ͳ͖͍ͬͯͯΔ • Ajax௨৴ͰΠϯλϥΫςΟϒʹ • ϞʔμϧυϥοάΛۦͨ͠ϦονͳΠϯ λϑΣʔεʹ
ͦΕΛಥ͖ਐΊͨઌʹ͋Δ ҉ࠇͷUI • JSP(Java ͷ HTMLςϯϓϨʔτ)͔ΒJavaScriptΛݺ ͼग़͢ʂʂʂ • JavaScriptͰDOMͷதͰΰϦΰϦͷϩδοΫΛॻ ͘ʂʂʂ
• Ajax௨৴தʹDOMͰΠϯδέʔλग़͍͔ͨ͠Βͬͯ શ෦ͷૢ࡞ͰAjax௨৴தʹΠϯδέʔλग़͢ʂʂʂ
ͦΕΛಥ͖ਐΊͨઌʹ͋Δ ҉ࠇͷUI • JSP(Java ͷ HTMLςϯϓϨʔτ)͔ΒJavaScriptΛ ݺͼग़͢ʂʂʂ • JavaScriptͰDOMͷதͰϩδοΫΛॻ͘ʂʂʂ •
Ajax௨৴தʹDOMͰΠϯδέʔλग़͍͔ͨ͠Βͬ ͯશ෦ͷૢ࡞ͰAjax௨৴தʹΠϯδέʔλग़ ͢ʂʂʂ=>ফ͑ͳ͘ͳΔΠϯδέʔλ
ͦΕΛಥ͖ਐΊͨઌʹ͋Δ ҉ࠇͷUI • JSP(Java ͷ HTMLςϯϓϨʔτ)͔ΒJavaScriptΛ ݺͼग़͢ʂʂʂ • JavaScriptͰDOMͷதͰϩδοΫΛॻ͘ʂʂʂ •
Ajax௨৴தʹDOMͰΠϯδέʔλग़͍͔ͨ͠ Βͬͯશ෦ͷૢ࡞ͰAjax௨৴தʹΠϯδέʔλग़ ͢ʂʂʂ
BFFͷ৫తͳ • ͏͍͍Ճݮʹ GUI (ϖʔδ) Λ࡞ΔॴͱσʔλΛ ཧ͢Δॴ͚͍ͨɻ • WebΞϓϦέʔγϣϯϑϨʔϜϫʔΫ͔ͩΒͱ͍ͬ ͯԿͰΔͷͰͳ͘ɺݖݶΛ͍ͨ͠ɻ
• σʔλΛཧ͢ΔॴόοΫΤϯυͰ࣮ࢪ͍ͨ͠ • GUI(ϖʔδ)Λ࡞ΔॴϑϩϯτΤϯυͰ࣮ࢪ͍ͨ͠
BFFͷ৫తͳ
BFFͷ৫తͳ όοΫΤϯυ ΤϯδχΞσʔλ ͷૢ࡞ɾཧʹूத ͢Δɻ
BFFͷ৫తͳ ϑϩϯτΤϯυ ΤϯδχΞ(6* ͷߏஙʹઐ೦͢Δɻ ओͳࣄ6*69ͷ ্
BFFͷྺ࢙తͳ • ࠷ۙ "BFF" ͱ͍͏໊લ͕͍͚ͭͨͩͰಛʹ৽͍͠Ͱ ͳ͍ɻ • ੲͷΤϯλʔϓϥΠζ࣌͜͏͍͏ΞʔΩςΫνϟ͕ଟ ͔ͬͨ (લલ৬Ͱͦ͏ͩͬͨ)
• HTML͕ࠓ·ͰԿ͠ͳͯ͘ϒϥβ͕উखʹGUIΛද ݱͯͨ͠ͷʹΒ͕SPAͱ͔ͰGUIΛ࠶ఆ͔ٛͩͨ͠͠Β ͏Ұ໊લ͕͍ͭͨ(ؾ͕͢Δ)ɻ
BFFͷྺ࢙తͳ • Chapter 5: Re-Architecting ϞϊϦεతͳΞʔΩςΫνϟͰνʔϜ ͱͯ͠εέʔϧ͠ͳ͍ͱࢥͬͨΒɺϑϩ ϯτΤϯυͱόοΫΤϯυʹ͚Δɺ ΑΓίϯςΫετΛҙࣝͯ͠ϚΠΫϩ αʔϏεԽ͢ΔͷΛݕ౼͢Δ
BFFͷྺ࢙తͳ • Chapter 5: Re-Architecting ϞϊϦεతͳΞʔΩςΫνϟͰνʔϜ ͱͯ͠εέʔϧ͠ͳ͍ͱࢥͬͨΒɺϑϩ ϯτΤϯυͱόοΫΤϯυʹ͚Δɺ ΑΓίϯςΫετΛҙࣝͯ͠ϚΠΫϩ αʔϏεԽ͢ΔͷΛݕ౼͢Δ
ϑϩϯτΤϯυͱόοΫΤϯυʹ͚Δɺ ϚΠΫϩαʔϏεΛҙࣝ͢Δ
BFFͷྺ࢙తͳ • ྺ࢙తʹΞʔΩςΫνϟૄ݁߹ͱີ݁߹Λ ܁Γฦͯ͠༳Ε͍ͯΔɻ • લ·Ͱີ݁߹͕ͩͬͨɺ࠷ۙૄ݁߹ͷ͕ྑ ͘ͳ͖ͬͯͨɻ • ࠓޙͲ͏ͳΔ͔Θ͔Βͳ͍ɺ·ͨີ݁߹ʹ Δ͔͠Εͳ͍͠ɺૄ݁߹ͷ··͔͠Εͳ͍ɻ
SSRೖ
SSRͱ • Server Side Rendering ͷུ • Client Side Ͱ
HTML ΛϨϯμϦϯά͢Δ͜ͱ ΛClient Side RenderingͱݺͿɻͦΕʹର͠ ͯಉ͜͡ͱΛ Server Side Ͱ࣮ࢪ͢Δ͜ͱΛ Server Side Rendering ͱݺͿɻ
Server Side Rendering 4FSWFS 1BHF3FRVFTU $PNQPOFOUT 5FNQMBUFT 'FUDI%BUB %#"1* 3FOEFS)5.-
(FUQBHFQBSUT
Server Side Rendering 4FSWFS 1BHF3FRVFTU $PNQPOFOUT 5FNQMBUFT 'FUDI%BUB %#"1* 3FOEFS)5.-
$MJFOU4JEF 3FOEFSFS $PNQPOFOUT 5FNQMBUFT 3FOEFS)5.-XJUITBNF DMJFOUTFSWFSSFOEFSMPHJD 4FSWFS4JEF 3FOEFSFS (FUQBHFQBSUT
Why we need Server Side Rendering?
SEO
SEO
SEO Ϋϩʔϥ͕+4Λ࣮ߦͰ͖Δͱ ͍͑ɺͦͷ+4͕ͪΌΜͱಈ͘อ ূͳ͍ɻ
SEOॏཁɺͰຊ࣍
First View Performance
ͦͦFirst ViewͱҰޱʹ ݴͬͯछྨ͕͋Δ /BWJHBUJPO4UBSU 'JSTU1BJOU 'JSTU$POUFOUGVM 1BJOU Loading.. 'JSTU.FBOJOHGVM 1BJOU
'JSTU.FBOJOHGVM 1BJOU 7JTVBMMZSFBEZ *NBHF 5JNF5P *OUFSBDUJWF +4MPBEFE *NBHF 'VMMZ-PBEFE BMMSFTPVSDFTBSF MPBEFE *NBHF
First View Performance • SSR ʹ͓͚Δ First View ͷվળͱɺ͜ͷ First
Meaningful Paint ·ͰΛࢦ͢ɻ /BWJHBUJPO4UBSU 'JSTU1BJOU 'JSTU$POUFOUGVM 1BJOU Loading.. 'JSTU.FBOJOHGVM 1BJOU
First View Performance • First Meaningful PaintҎ߱ͷվળService WorkerHTTP2ͳͲผͳΓํ͕͋Δɻ 'JSTU.FBOJOHGVM 1BJOU
7JTVBMMZSFBEZ *NBHF 5JNF5P *OUFSBDUJWF +4MPBEFE *NBHF 'VMMZ-PBEFE BMMSFTPVSDFTBSF MPBEFE *NBHF
Client Side Rendering ͷΈͰͷಈ͖
Client Side Rendering ͷΈͷ ߹ • First Meaningful Paint ͱ
Time To Interact ͷ λΠϛϯά͕΄ͱΜͲಉ͡ɻ 'JSTU.FBOJOHGVM1BJOU 5JNF5P*OUFSBDUJWF +4MPBEFE 'JSTU$POUFOUGVM 1BJOU Loading..
Client Side Rendering ͷΈͷ ߹ • ݴ͍͑ΔͱɺFirst Contentful Paint͔ΒFirst Meaningful
Paint ·Ͱ͕͘ͳΔɻ 'JSTU.FBOJOHGVM1BJOU 5JNF5P*OUFSBDUJWF +4MPBEFE 'JSTU$POUFOUGVM 1BJOU Loading..
Client Side Rendering ͷΈͷ ߹ • ݴ͍͑ΔͱɺFirst Contentful Paint͔ΒFirst Meaningful
Paint ·Ͱ͕͘ͳΔɻ 'JSTU.FBOJOHGVM1BJOU 5JNF5P*OUFSBDUJWF +4MPBEFE 'JSTU$POUFOUGVM 1BJOU Loading.. Ϣʔβʔݟ͔͑ͯΒૢ࡞͢Δ 'JSTU.FBOJOHGVM1BJOU͕ग़͔ͯΒ*OUFSBDU ͢Δ ͷͰɺ͜ͷ$MJFOU4JEF3FOEFSJOH͚ͩͰ࣌ؒతʹແବ͕ଟ͍ɻ
ͦ͜Ͱ Server Side Rendering
/BWJHBUJPO4UBSU 'JSTU1BJOU 'JSTU$POUFOUGVM 1BJOU Loading.. 'JSTU.FBOJOHGVM 1BJOU ࠷ॳ͔ΒσʔλΛຒΊࠐΜͩঢ়ଶͰ)5.-ʹ͢Δ ʢ4FSWFS4JEF3FOEFSJOHʣ
ʮBFFΛಋೖ͢Δʯͱ͍͏࣌ ʹʮSSRʯ·Ͱηοτͷํ͕ ϏδωεతͳϝϦοτ(ੑೳɺ SEO)આ໌͍͢͠ɻ
BFF/SSR࣮ફྫ
BFF/SSR࣮ફྫ • ฐࣾͷϘΠϥʔϓϨʔτΛߏங͠ɺͦΕΛల։͢ΔܗͰ֤։ ൃ͔ΒͬͯΒ͍ͬͯΔ • bookingtable(ϨετϥϯݕࡧαΠτ)Ͱͷ࣮ફ (2016/11~) • raico(SNS)Ͱͷ࣮ફ (2016/12
~) • coshigoto(ΞϯέʔτΞϓϦ)Ͱͷ࣮ફ (2017/08 ~) • AirShift(εέδϡʔϧཧΞϓϦ)Ͱͷ࣮ફ (2017/09 ~)
BFF/SSR࣮ફྫ • ฐࣾͷϘΠϥʔϓϨʔτΛߏங͠ɺͦΕΛల։͢ΔܗͰ֤։ ൃ͔ΒͬͯΒ͍ͬͯΔ • bookingtable(ϨετϥϯݕࡧαΠτ)Ͱͷ࣮ફ (2016/11~) • raico(SNS)Ͱͷ࣮ફ (2016/12
~) • coshigoto(ΞϯέʔτΞϓϦ)Ͱͷ࣮ફ (2017/08 ~) • AirShift(εέδϡʔϧཧΞϓϦ)Ͱͷ࣮ફ (2017/09 ~) ࣮ફճଟɺࠓޙ͍͔ͭ͘࠾༻༧ఆ
ฐࣾϘΠϥʔϓϨʔτ
redux-protoͱݺΕΔϘΠ ϥʔϓϨʔτΛ࡞ͬͯΔ • View: React • State Management: Redux •
Component Management: Atomic Design • Redux Middleware: redux-effects, redux-async- loader, etc • SSR ready, Service Worker ready, AMP not yet
redux-protoͱݺΕΔϘΠ ϥʔϓϨʔτΛ࡞ͬͯΔ • View: React • State Management: Redux •
Component Management: Atomic Design • Redux Middleware: redux-effects, redux-async- loader, etc • SSR ready, Service Worker ready, AMP not yet ϦΫϧʔτͷ-JWJOH"QQ4UBOEBSE ͱͯ͠։ൃத
redux-protoͱݺΕΔϘΠ ϥʔϓϨʔτΛ࡞ͬͯΔ • View: React • State Management: Redux •
Component Management: Atomic Design • Redux Middleware: redux-effects, redux-async- loader, etc • SSR ready, Service Worker ready, AMP not yet ۙެ։͢Δʂʂʂʂ ͔
ฐࣾϘΠϥʔϓϨʔτ ͪΐͬͱհ (React͔Δਓ͚)
ιʔείʔυϨΠΞτ . !"" client // Client ͰͷΈ࣮ࢪ͢Δίʔυ܈ɺͪͳΈʹຊʹϑΝΠϧ͔͠ͳ͍ɻ # !"" analytics
// Google Analytics ͷղੳπʔϧ # $"" vendor // ֎෦ϕϯμʔͰClientͰͷΈ࣮ࢪ͢Δͷ !"" server // Server ͰͷΈ࣮ࢪ͢Δίʔυ܈ɻ # !"" components // SSRͷ࣌ͷίϯϙʔωϯτ # !"" configs // ίϯϑΟάϑΝΠϧ܈ # !"" libs // ·ͩnpmʹͳͬͯͳ͍ϥΠϒϥϦ܈ # !"" middlewares // SSRΛ࣮͍ͯ͠ΔίΞͷ෦ # $"" services // APIͷ·ͱΊΛߦ͍ͬͯΔαʔϏε !"" shared # !"" components // atoms, molecules, organisms ͱ֊ʹͳͬͯΔɺSSR/CSR྆ํͰ࣮ࢪ # !"" redux // actions/reducersͰ͚ͯͳͯ͘ɺducksܗࣜͷϞδϡʔϧʹͳͬͯΔ # !"" routes // ϧʔλʔͷఆٛɺ͜͜Ͱ Code Splitting ͷઃఆߦ͍ͬͯΔ # !"" utils // ࡶଟͳͷ # $"" validators // Validation܈ !"" sw // Service Worker ϑΥϧμ $"" webpack // Webpack ϑΥϧμ
Atom Component export default compose( onlyUpdateForPropTypes, // PropTypesʹॻ͍ͨͷ͕มߋ͞Ε͚ͨ࣌ͩrendering͢Δ setPropTypes({ //
PropTypesΛఆٛ loading: PropTypes.bool.isRequired, }), )(function Indicator(props) { const { loading } = props; if (!loading) { return null; } return ( <div className={local(‘loader’)}></div> ); });
Flowͷ߹ // @flow import React from 'react'; import { compose,
onlyUpdateForKeys } from 'recompose'; export default compose( onlyUpdateForKeys(), // FlowTypeͷ߹babel-pluginͰܕใΛύʔεͯ͠উ खʹshouldComponentUpdateΛ࣮ࢪ͢ΔϓϥάΠϯΛೖΕͯΔɻ )(function Foo(props: { foo: string, bar: number }) { const { foo, bar } = props; return ( <div>{foo}-{bar}</div> ); }); https://github.com/recruit-tech/babel-plugin-flow- onlyupdateforkeys
Flowͷ߹ // @flow import React from 'react'; import { compose,
onlyUpdateForKeys } from 'recompose'; export default compose( onlyUpdateForKeys(), // FlowTypeͷ߹babel-pluginͰܕใΛύʔεͯ͠উ खʹshouldComponentUpdateΛ࣮ࢪ͢ΔϓϥάΠϯΛೖΕͯΔɻ )(function Foo(props: { foo: string, bar: number }) { const { foo, bar } = props; return ( <div>{foo}-{bar}</div> ); }); ܕใΛݩʹνϡʔχϯά͢Δ Έ͕ೖͬͯΔ https://github.com/recruit-tech/babel-plugin-flow- onlyupdateforkeys
Code Splitting using SSR import createUniversalComponent from ‘./createUniversalComponent’; const chunkName
= ‘salon’; export function loadSalon(_, cb) { createUniversalComponent( () => import(/* webpackChunkName: “salon” */ ‘../components/organisms/Salon’), () => require.resolveWeak(‘../components/organisms/Salon’), chunkName, ).then((result) => cb(null, result), cb); } export function loadSalonForm(_, cb) { createUniversalComponent( () => import(/* webpackChunkName: “salon” */ ‘../components/organisms/SalonForm’), () => require.resolveWeak(‘../components/organisms/SalonForm’), chunkName, ).then((result) => cb(null, result), cb); } https://github.com/faceyspacey/react-universal- component
Code Splitting using SSR import createUniversalComponent from ‘./createUniversalComponent’; const chunkName
= ‘salon’; export function loadSalon(_, cb) { createUniversalComponent( () => import(/* webpackChunkName: “salon” */ ‘../components/organisms/Salon’), () => require.resolveWeak(‘../components/organisms/Salon’), chunkName, ).then((result) => cb(null, result), cb); } export function loadSalonForm(_, cb) { createUniversalComponent( () => import(/* webpackChunkName: “salon” */ ‘../components/organisms/SalonForm’), () => require.resolveWeak(‘../components/organisms/SalonForm’), chunkName, ).then((result) => cb(null, result), cb); } https://github.com/faceyspacey/react-universal- component $PEF4QMJUͭͭ͠443ͷ࣌ී ௨ʹ+4$44ΛGFUDI͢ΔΈ
Α͋͘Δٙ • BFFͬͯͳΜͷͨΊʹ͍Δͷʁ => ݖݶͷͱ։ൃͷ͢͠͞ • SSRͬͯSEOෆཁͳΒཁΒͳ͍Μ͡Όͳ͍ʁ => No, First
Meaningful PaintΛ࠷దԽ͢ΔͨΊʹඞཁ • BFF/SSRͬͯ͘͠ͳ͍ͷʁ => Partially Yes, ࠷ॳͷઃܭ͍͠ɻઃܭతͳ͠͞ΛΓӽ͑ Ε࣮͘͠ͳ͍ɻ͞Βʹ࠷ۙNext.jsͱ͔Nuxt.jsͱ͔ग़ͯ Δ͔Βঃʑʹ͘͠ͳ͘ͳ͖ͬͯͯΔ͔ɻ
Thank you