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.2k
tc39 x jsconf.jp Panel Discussion 2024
yosuke_furukawa
PRO
0
170
Removing Corepack
yosuke_furukawa
PRO
9
1.3k
JavaScript Runtime とはなにか
yosuke_furukawa
PRO
15
2.5k
Strip Types と Storage
yosuke_furukawa
PRO
4
350
Module Harmony について
yosuke_furukawa
PRO
3
1.6k
LTのやり方
yosuke_furukawa
PRO
16
2.2k
AppRouter Panel Talk
yosuke_furukawa
PRO
3
720
Node.js v22 で変わること
yosuke_furukawa
PRO
13
5.4k
Other Decks in Programming
See All in Programming
103 Early Hints
sugi_0000
1
220
HTTP compression in PHP and Symfony apps
dunglas
2
1.7k
RWC 2024 DICOM & ISO/IEC 2022
m_seki
0
200
선언형 UI에서의 상태관리
l2hyunwoo
0
140
これが俺の”自分戦略” プロセスを楽しんでいこう! - Developers CAREER Boost 2024
niftycorp
PRO
0
190
Fibonacci Function Gallery - Part 1
philipschwarz
PRO
0
200
Effective Signals in Angular 19+: Rules and Helpers @ngbe2024
manfredsteyer
PRO
0
130
KubeCon + CloudNativeCon NA 2024 Overviewat Kubernetes Meetup Tokyo #68 / amsy810_k8sjp68
masayaaoyama
0
250
Mermaid x AST x 生成AI = コードとドキュメントの完全同期への道
shibuyamizuho
0
160
Monixと常駐プログラムの勘どころ / Scalaわいわい勉強会 #4
stoneream
0
270
複雑な仕様に立ち向かうアーキテクチャ
myohei
0
170
第5回日本眼科AI学会総会_AIコンテスト_3位解法
neilsaw
0
170
Featured
See All Featured
Product Roadmaps are Hard
iamctodd
PRO
49
11k
Build The Right Thing And Hit Your Dates
maggiecrowley
33
2.4k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
6.9k
Code Review Best Practice
trishagee
65
17k
Building Adaptive Systems
keathley
38
2.3k
Designing on Purpose - Digital PM Summit 2013
jponch
116
7k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.5k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
111
49k
Measuring & Analyzing Core Web Vitals
bluesmoon
4
170
Into the Great Unknown - MozCon
thekraken
33
1.5k
Fireside Chat
paigeccino
34
3.1k
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