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.8k
tc39 x jsconf.jp Panel Discussion 2024
yosuke_furukawa
PRO
0
190
Removing Corepack
yosuke_furukawa
PRO
9
1.5k
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.4k
AppRouter Panel Talk
yosuke_furukawa
PRO
3
750
Node.js v22 で変わること
yosuke_furukawa
PRO
13
5.8k
Other Decks in Programming
See All in Programming
Serverless Rust: Your Low-Risk Entry Point to Rust in Production (and the benefits are huge)
lmammino
1
150
Ruby on cygwin 2025-02
fd0
0
180
Bedrock Agentsレスポンス解析によるAgentのOps
licux
3
910
Honoをフロントエンドで使う 3つのやり方
yusukebe
7
3.5k
AIプログラミング雑キャッチアップ
yuheinakasaka
17
4k
「個人開発マネタイズ大全」が教えてくれたこと
bani24884
1
130
Djangoアプリケーション 運用のリアル 〜問題発生から可視化、最適化への道〜 #pyconshizu
kashewnuts
1
260
Ça bouge du côté des animations CSS !
goetter
2
140
PEPCは何を変えようとしていたのか
ken7253
2
140
『GO』アプリ データ基盤のログ収集システムコスト削減
mot_techtalk
0
140
なぜイベント駆動が必要なのか - CQRS/ESで解く複雑系システムの課題 -
j5ik2o
14
4.6k
もう少しテストを書きたいんじゃ〜 #phpstudy
o0h
PRO
16
3.5k
Featured
See All Featured
The Pragmatic Product Professional
lauravandoore
32
6.4k
Statistics for Hackers
jakevdp
797
220k
A better future with KSS
kneath
238
17k
[RailsConf 2023] Rails as a piece of cake
palkan
53
5.3k
Being A Developer After 40
akosma
89
590k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5.2k
Build The Right Thing And Hit Your Dates
maggiecrowley
34
2.5k
Reflections from 52 weeks, 52 projects
jeffersonlam
348
20k
Optimizing for Happiness
mojombo
376
70k
Visualization
eitanlees
146
15k
Rails Girls Zürich Keynote
gr2m
94
13k
Scaling GitHub
holman
459
140k
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