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
25
7k
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
リアーキテクトと開発生産性について
yosuke_furukawa
PRO
21
7.8k
JavaScript Server Runtime History
yosuke_furukawa
PRO
8
2.7k
tc39 x jsconf.jp Panel Discussion
yosuke_furukawa
PRO
0
820
フロントエンドの開発生産性とは
yosuke_furukawa
PRO
16
9.4k
7 principles for rich web apps And how next.js achieves these principles
yosuke_furukawa
PRO
6
2k
Deep Dive International Conference
yosuke_furukawa
PRO
0
95
フロントエンドのDXと今後
yosuke_furukawa
PRO
6
3.6k
フロントエンドリアーキテクトの話
yosuke_furukawa
PRO
18
8.8k
new_urlparser.pdf
yosuke_furukawa
PRO
1
420
Other Decks in Programming
See All in Programming
PHPカンファレンス関西2024でLTとスタッフした
ohmori_yusuke
2
120
OpCode目線で眺める PHPコードのカバレッジ
o0h
PRO
2
470
Learning PHP and Static Analysis with PHP Parser
inouehi
1
220
BuefyのMaintainerを引き継いだ件
kikuomax
0
330
PHPerKaigi 2024〜10年以上動いているレガシーなバッチシステムを Kubernetes(Amazon EKS) に移行する取り組み〜
tshinowpub
1
170
【KMC春合宿2024】実装視点で見るNeural Radiance Fields
runningoutrate
0
130
導入から5年が経って見えた Datadog APM 運用の課題
bgpat
1
480
オレオレkaggle開発環境に Formatter/Linter入れてみた
stgkrt
0
350
PHPerライフをChrome拡張開発でちょっと便利に / PR TIMES x DMM.com
meihei3
0
190
Docker ハンズオン / docker-hands-on
suzukihoge
48
15k
25 Years of the JCP Program
ivargrimstad
0
1k
PHP で読む楽しいコアダンプ
sji
0
220
Featured
See All Featured
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
226
16k
Designing the Hi-DPI Web
ddemaree
275
33k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
24
2.2k
Product Roadmaps are Hard
iamctodd
43
9.6k
Atom: Resistance is Futile
akmur
258
25k
Robots, Beer and Maslow
schacon
PRO
154
7.9k
Code Reviewing Like a Champion
maltzj
512
39k
Fireside Chat
paigeccino
19
2.5k
Statistics for Hackers
jakevdp
789
220k
Being A Developer After 40
akosma
56
580k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
11
1.4k
Designing for humans not robots
tammielis
247
25k
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