Upgrade to Pro — share decks privately, control downloads, hide ads and more …

BFF/SSRの話

 BFF/SSRの話

Mercari Web / Frontend meetupで話した BFF/SSR の話です。

D76231a2114896dfcc7b79ac69558b79?s=128

Yosuke Furukawa
PRO

February 19, 2018
Tweet

Transcript

  1. SSR / BFF ʹ͍ͭͯ Mercari Web / Frontend meetup #1

    2018/02/19
  2. Twitter: @yosuke_furukawa Github: yosuke-furukawa

  3. /PEFֶԂ࣌ݶ໨΍ΔͷͰօ͞Μ དྷͯͶʂʂʂ

  4. ࠷ۙBFFʹ͍ͭͯͷ࿩Λ ͱ͋ΔϝσΟΞʹॻ͍ͯΔ

  5. ͓ͦΒۙ͘೔ެ։༧ఆ ʢక੾ʹؒʹ߹͑͹ʣ

  6. ࠓ೔ͷ࿩ • BFFೖ໳ • SSRೖ໳ • BFF/SSR࣮ફྫs

  7. Α͋͘Δٙ໰ • BFFͬͯͳΜͷͨΊʹ͍Δͷʁ • SSRͬͯSEOෆཁͳΒཁΒͳ͍Μ͡Όͳ͍ʁ • BFF/SSRͬͯ೉͘͠ͳ͍ͷʁ

  8. BFFೖ໳

  9. BFFͱ͸Կ͔ • Backends For Frontends • ϑϩϯτΤϯυͷͨΊͷόοΫΤϯυ(αʔό) ͷ͜ͱ ͜͜ʂ

  10. Q. ͳΜͰBFF͕ඞཁͳͷ͔

  11. Q. ͳΜͰBFF͕ඞཁͳͷ͔ A. 2ͭ͋Δ 1. ૊৫తͳ࿩ 2. ੑೳతͳ࿩

  12. Q. ͳΜͰBFF͕ඞཁͳͷ͔ A. 2ͭ͋Δ 1. ૊৫తͳ࿩ 2. ੑೳతͳ࿩ (SSRͷ࿩)

  13. BFFͷ૊৫తͳ࿩ • ͜Ε·ͰͷWebΞϓϦέʔγϣϯ • ҰຕؠͰͰ͖ͨΞϓϦέʔγϣϯ • HTMLςϯϓϨʔτͰදࣔΛߏங • ߋ৽͸FormͰ

  14. BFFͷ૊৫తͳ࿩ • ঃʑʹͦΕ͚ͩͰ͸ճΒͳ͘ͳ͖͍ͬͯͯΔ • Ajax௨৴ͰΠϯλϥΫςΟϒʹ • Ϟʔμϧ΍υϥοάΛۦ࢖ͨ͠ϦονͳΠϯ λϑΣʔεʹ

  15. BFFͷ૊৫తͳ࿩ • ঃʑʹͦΕ͚ͩͰ͸ճΒͳ͘ͳ͖͍ͬͯͯΔ • Ajax௨৴ͰΠϯλϥΫςΟϒʹ • Ϟʔμϧ΍υϥοάΛۦ࢖ͨ͠ϦονͳΠϯ λϑΣʔεʹ

  16. ͦΕΛಥ͖ਐΊͨઌʹ͋Δ ҉ࠇͷUI • JSP(Java ͷ HTMLςϯϓϨʔτ)͔ΒJavaScriptΛݺ ͼग़͢ʂʂʂ • JavaScriptͰDOMͷதͰΰϦΰϦͷϩδοΫΛॻ ͘ʂʂʂ

    • Ajax௨৴தʹDOMͰΠϯδέʔλग़͍͔ͨ͠Βͬͯ શ෦ͷૢ࡞ͰAjax௨৴தʹΠϯδέʔλग़͢ʂʂʂ
  17. ͦΕΛಥ͖ਐΊͨઌʹ͋Δ ҉ࠇͷUI • JSP(Java ͷ HTMLςϯϓϨʔτ)͔ΒJavaScriptΛ ݺͼग़͢ʂʂʂ • JavaScriptͰDOMͷதͰϩδοΫΛॻ͘ʂʂʂ •

    Ajax௨৴தʹDOMͰΠϯδέʔλग़͍͔ͨ͠Βͬ ͯશ෦ͷૢ࡞ͰAjax௨৴தʹΠϯδέʔλग़ ͢ʂʂʂ=>ফ͑ͳ͘ͳΔΠϯδέʔλ΁
  18. ͦΕΛಥ͖ਐΊͨઌʹ͋Δ ҉ࠇͷUI • JSP(Java ͷ HTMLςϯϓϨʔτ)͔ΒJavaScriptΛ ݺͼग़͢ʂʂʂ • JavaScriptͰDOMͷதͰϩδοΫΛॻ͘ʂʂʂ •

    Ajax௨৴தʹDOMͰΠϯδέʔλग़͍͔ͨ͠ Βͬͯશ෦ͷૢ࡞ͰAjax௨৴தʹΠϯδέʔλग़ ͢ʂʂʂ
  19. BFFͷ૊৫తͳ࿩ • ΋͏͍͍Ճݮʹ GUI (ϖʔδ) Λ࡞ΔॴͱσʔλΛ؅ ཧ͢Δॴ͸෼͚͍ͨɻ • WebΞϓϦέʔγϣϯϑϨʔϜϫʔΫ͔ͩΒͱ͍ͬ ͯԿͰ΋΍ΔͷͰ͸ͳ͘ɺݖݶΛ෼཭͍ͨ͠ɻ

    • σʔλΛ؅ཧ͢Δॴ͸όοΫΤϯυͰ࣮ࢪ͍ͨ͠ • GUI(ϖʔδ)Λ࡞Δॴ͸ϑϩϯτΤϯυͰ࣮ࢪ͍ͨ͠
  20. BFFͷ૊৫తͳ࿩

  21. BFFͷ૊৫తͳ࿩ όοΫΤϯυ ΤϯδχΞ͸σʔλ ͷૢ࡞ɾ؅ཧʹूத ͢Δɻ

  22. BFFͷ૊৫తͳ࿩ ϑϩϯτΤϯυ ΤϯδχΞ͸(6* ͷߏஙʹઐ೦͢Δɻ ओͳ࢓ࣄ͸6*69ͷ ޲্

  23. BFFͷྺ࢙తͳ࿩ • ࠷ۙ "BFF" ͱ͍͏໊લ͕͍͚ͭͨͩͰಛʹ৽͍͠࿩Ͱ͸ ͳ͍ɻ • ੲͷΤϯλʔϓϥΠζ࣌୅͸͜͏͍͏ΞʔΩςΫνϟ͕ଟ ͔ͬͨ (๻΋લલ৬Ͱ͸ͦ͏ͩͬͨ)

    • HTML͕ࠓ·ͰԿ΋͠ͳͯ͘΋ϒϥ΢β͕উखʹGUIΛද ݱͯͨ͠ͷʹ๻Β͕SPAͱ͔ͰGUIΛ࠶ఆ͔ٛͩͨ͠͠Β ΋͏Ұ౓໊લ͕͍ͭͨ(ؾ͕͢Δ)ɻ
  24. BFFͷྺ࢙తͳ࿩ • Chapter 5: Re-Architecting ϞϊϦεతͳΞʔΩςΫνϟͰ͸νʔϜ ͱͯ͠εέʔϧ͠ͳ͍ͱࢥͬͨΒɺϑϩ ϯτΤϯυͱόοΫΤϯυʹ෼͚Δɺ ΑΓίϯςΫετΛҙࣝͯ͠ϚΠΫϩ αʔϏεԽ͢ΔͷΛݕ౼͢Δ

  25. BFFͷྺ࢙తͳ࿩ • Chapter 5: Re-Architecting ϞϊϦεతͳΞʔΩςΫνϟͰ͸νʔϜ ͱͯ͠εέʔϧ͠ͳ͍ͱࢥͬͨΒɺϑϩ ϯτΤϯυͱόοΫΤϯυʹ෼͚Δɺ ΑΓίϯςΫετΛҙࣝͯ͠ϚΠΫϩ αʔϏεԽ͢ΔͷΛݕ౼͢Δ

    ϑϩϯτΤϯυͱόοΫΤϯυʹ෼͚Δɺ ϚΠΫϩαʔϏεΛҙࣝ͢Δ
  26. BFFͷྺ࢙తͳ࿩ • ྺ࢙తʹ͸ΞʔΩςΫνϟ͸ૄ݁߹ͱີ݁߹Λ ܁Γฦͯ͠༳Ε͍ͯΔɻ • લ·Ͱ͸ີ݁߹͕ͩͬͨɺ࠷ۙ͸ૄ݁߹ͷ͕ྑ ͘ͳ͖ͬͯͨɻ • ࠓޙ͸Ͳ͏ͳΔ͔Θ͔Βͳ͍ɺ·ͨີ݁߹ʹ໭ Δ͔΋͠Εͳ͍͠ɺૄ݁߹ͷ··͔΋͠Εͳ͍ɻ

  27. SSRೖ໳

  28. SSRͱ͸ • Server Side Rendering ͷུ • Client Side Ͱ

    HTML ΛϨϯμϦϯά͢Δ͜ͱ ΛClient Side RenderingͱݺͿɻͦΕʹର͠ ͯಉ͜͡ͱΛ Server Side Ͱ΋࣮ࢪ͢Δ͜ͱΛ Server Side Rendering ͱݺͿɻ
  29. Server Side Rendering 4FSWFS 1BHF3FRVFTU $PNQPOFOUT 5FNQMBUFT 'FUDI%BUB %#"1* 3FOEFS)5.-

    (FUQBHFQBSUT
  30. Server Side Rendering 4FSWFS 1BHF3FRVFTU $PNQPOFOUT 5FNQMBUFT 'FUDI%BUB %#"1* 3FOEFS)5.-

    $MJFOU4JEF 3FOEFSFS $PNQPOFOUT 5FNQMBUFT 3FOEFS)5.-XJUITBNF DMJFOUTFSWFSSFOEFSMPHJD 4FSWFS4JEF 3FOEFSFS (FUQBHFQBSUT
  31. Why we need Server Side Rendering?

  32. SEO

  33. SEO

  34. SEO Ϋϩʔϥ͕+4Λ࣮ߦͰ͖Δͱ͸ ͍͑ɺͦͷ+4͕ͪΌΜͱಈ͘อ ূ͸ͳ͍ɻ

  35. SEO͸ॏཁɺͰ΋ຊ୊͸࣍

  36. First View Performance

  37. ͦ΋ͦ΋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
  38. First View Performance • SSR ʹ͓͚Δ First View ͷվળͱ͸ɺ͜ͷ First

    Meaningful Paint ·ͰΛࢦ͢ɻ /BWJHBUJPO4UBSU 'JSTU1BJOU 'JSTU$POUFOUGVM 1BJOU Loading.. 'JSTU.FBOJOHGVM 1BJOU
  39. First View Performance • First Meaningful PaintҎ߱ͷվળ͸Service Worker΍HTTP2ͳͲผͳ΍Γํ͕͋Δɻ 'JSTU.FBOJOHGVM 1BJOU

    7JTVBMMZSFBEZ *NBHF 5JNF5P *OUFSBDUJWF +4MPBEFE *NBHF 'VMMZ-PBEFE BMMSFTPVSDFTBSF MPBEFE *NBHF
  40. Client Side Rendering ͷΈͰͷಈ͖

  41. Client Side Rendering ͷΈͷ ৔߹ • First Meaningful Paint ͱ

    Time To Interact ͷ λΠϛϯά͕΄ͱΜͲಉ͡ɻ 'JSTU.FBOJOHGVM1BJOU 5JNF5P*OUFSBDUJWF +4MPBEFE 'JSTU$POUFOUGVM 1BJOU Loading..
  42. Client Side Rendering ͷΈͷ ৔߹ • ݴ͍׵͑ΔͱɺFirst Contentful Paint͔ΒFirst Meaningful

    Paint ·Ͱ͕௕͘ͳΔɻ 'JSTU.FBOJOHGVM1BJOU 5JNF5P*OUFSBDUJWF +4MPBEFE 'JSTU$POUFOUGVM 1BJOU Loading..
  43. Client Side Rendering ͷΈͷ ৔߹ • ݴ͍׵͑ΔͱɺFirst Contentful Paint͔ΒFirst Meaningful

    Paint ·Ͱ͕௕͘ͳΔɻ 'JSTU.FBOJOHGVM1BJOU 5JNF5P*OUFSBDUJWF +4MPBEFE 'JSTU$POUFOUGVM 1BJOU Loading.. Ϣʔβʔ͸ݟ͔͑ͯΒૢ࡞͢Δ 'JSTU.FBOJOHGVM1BJOU͕ग़͔ͯΒ*OUFSBDU ͢Δ ͷͰɺ͜ͷ$MJFOU4JEF3FOEFSJOH͚ͩͰ͸࣌ؒతʹແବ͕ଟ͍ɻ
  44. ͦ͜Ͱ Server Side Rendering

  45. /BWJHBUJPO4UBSU 'JSTU1BJOU 'JSTU$POUFOUGVM 1BJOU Loading.. 'JSTU.FBOJOHGVM 1BJOU ࠷ॳ͔ΒσʔλΛຒΊࠐΜͩঢ়ଶͰ)5.-ʹ͢Δ ʢ4FSWFS4JEF3FOEFSJOHʣ

  46. ʮBFFΛಋೖ͢Δʯͱ͍͏࣌ ʹʮSSRʯ·Ͱηοτͷํ͕
 ϏδωεతͳϝϦοτ(ੑೳɺ SEO)΋આ໌͠΍͍͢ɻ

  47. BFF/SSR࣮ફྫ

  48. BFF/SSR࣮ફྫ • ฐࣾͷϘΠϥʔϓϨʔτΛߏங͠ɺͦΕΛల։͢ΔܗͰ֤։ ൃ͔Β࢖ͬͯ΋Β͍ͬͯΔ • bookingtable(ϨετϥϯݕࡧαΠτ)Ͱͷ࣮ફ (2016/11~) • raico(SNS)Ͱͷ࣮ફ (2016/12

    ~) • coshigoto(ΞϯέʔτΞϓϦ)Ͱͷ࣮ફ (2017/08 ~) • AirShift(εέδϡʔϧ؅ཧΞϓϦ)Ͱͷ࣮ફ (2017/09 ~)
  49. BFF/SSR࣮ફྫ • ฐࣾͷϘΠϥʔϓϨʔτΛߏங͠ɺͦΕΛల։͢ΔܗͰ֤։ ൃ͔Β࢖ͬͯ΋Β͍ͬͯΔ • bookingtable(ϨετϥϯݕࡧαΠτ)Ͱͷ࣮ફ (2016/11~) • raico(SNS)Ͱͷ࣮ફ (2016/12

    ~) • coshigoto(ΞϯέʔτΞϓϦ)Ͱͷ࣮ફ (2017/08 ~) • AirShift(εέδϡʔϧ؅ཧΞϓϦ)Ͱͷ࣮ફ (2017/09 ~) ࣮ફճ਺ଟ਺ɺࠓޙ΋͍͔ͭ͘࠾༻༧ఆ
  50. ฐࣾϘΠϥʔϓϨʔτ

  51. 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
  52. 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 ͱͯ͠։ൃத
  53. 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 ۙ೔ެ։͢Δʂʂʂʂ ͔΋
  54. ฐࣾϘΠϥʔϓϨʔτ ͪΐͬͱ঺հ (React෼͔Δਓ޲͚)

  55. ιʔείʔυϨΠΞ΢τ . !"" 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 ϑΥϧμ
  56. 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> ); });
  57. 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
  58. 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
  59. 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
  60. 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͢Δ࢓૊Έ
  61. Α͋͘Δٙ໰ • BFFͬͯͳΜͷͨΊʹ͍Δͷʁ
 => ݖݶͷ෼཭ͱ։ൃͷ͠΍͢͞ • SSRͬͯSEOෆཁͳΒཁΒͳ͍Μ͡Όͳ͍ʁ
 => No, First

    Meaningful PaintΛ࠷దԽ͢ΔͨΊʹ͸ඞཁ • BFF/SSRͬͯ೉͘͠ͳ͍ͷʁ
 => Partially Yes, ࠷ॳͷઃܭ͸೉͍͠ɻઃܭతͳ೉͠͞Λ৐Γӽ͑ Ε͹࣮૷͸೉͘͠ͳ͍ɻ͞Βʹ࠷ۙ͸Next.jsͱ͔Nuxt.jsͱ͔ग़ͯ Δ͔Βঃʑʹ೉͘͠ͳ͘ͳ͖ͬͯͯΔ͔΋ɻ
  62. Thank you