Pro Yearly is on sale from $80 to $50! »

GMOペパボの Rails & Vue.js プロダクト開発の現場 / Rails Developers Meetup 2017

0f9d23f2361217bf88e5704141109d9c?s=47 kymmtchan
December 09, 2017

GMOペパボの Rails & Vue.js プロダクト開発の現場 / Rails Developers Meetup 2017

Rails Developers Meetup 2017での発表資料です。ペパボにおける新規プロダクト開発の流れと、スキーマファースト開発を紹介しています。

0f9d23f2361217bf88e5704141109d9c?s=128

kymmtchan

December 09, 2017
Tweet

Transcript

  1. !LFODIBOGFBU!LZNNU(.01FQBCP *OD 3BJMT%FWFMPQFST.FFUVQ (.0ϖύϘͷ3BJMT7VFKT ϓϩμΫτ։ൃͷݱ৔

  2. νʔϑςΫχΧϧϦʔυ &$ࣄۀ෦ ∁ڮ݈Ұ!LFODIBO ΤϯδχΞ &$ࣄۀ෦ΧϥʔϛʔϦϐʔτνʔϜ ࢁຊߒฏ!LZNNU

  3. None
  4. ܅΋ϖύϘͰಇ͔ͳ͍͔ʁ ࠷৽ͷ࠾༻৘ใΛνΣοΫˠ !QC@SFDSVJU

  5. ໨࣍ wαʔϏεͷ֓ཁͱΞʔΩςΫνϟ wϓϩδΣΫτͷ্ཱͪ͛ͱ೔ʑͷ։ൃ wεΩʔϚϑΝʔετ։ൃͷݱ৔

  6. ໨࣍ wαʔϏεͷ֓ཁͱΞʔΩςΫνϟ wϓϩδΣΫτͷ্ཱͪ͛ͱ೔ʑͷ։ൃ wεΩʔϚϑΝʔετ։ൃͷݱ৔

  7. IUUQTDPMPSNFSFQFBUKQ

  8. wఆظൢചʹಛԽͨ͠&$αʔϏε wϓϩμΫτΦʔφʔɺσΟϨΫλɺσβΠφɺ
 ΤϯδχΞ͕νʔϜʹͳͬͯ։ൃ w3VCZPO3BJMT7VFKTΛ)FSPLVͰӡ༻

  9. શମײ "EEPO ΦϒδΣΫτ ετϨʔδ ߦಈϩάू໿ɾ෼ੳ $%/ ։ൃɾίϛϡχέʔγϣϯ
 πʔϧ Ϣʔβʔ ։ൃνʔϜ

  10. ։ൃνʔϜ ϓϩμΫτ Φʔφʔ σΟϨΫλ ࣄۀ෦෦௕ 10νʔϜ ΤϯδχΞ σβΠφ 2"νʔϜ σβΠϯઓུ

    νʔϜ Πϯϑϥ άϧʔϓ
  11. ໨࣍ wαʔϏεͷ֓ཁͱΞʔΩςΫνϟ wϓϩδΣΫτͷ্ཱͪ͛ͱ೔ʑͷ։ൃ wεΩʔϚϑΝʔετ։ൃͷݱ৔

  12. ϓϩδΣΫτͷ্ཱͪ͛ wΠϯηϓγϣϯσοΩ wυϥοΧʔ෩ΤΫααΠζ wྨࣅαʔϏεͷϞσϦϯά wౡࠜ߹॓

  13. ΠϯηϓγϣϯσοΩ wʮΞδϟΠϧαϜϥΠʯͰ঺հ͞Ε͍ͯΔϓϩδΣΫ τͷ্ཱͪ͛࣌ʹ༗ޮͳϓϥΫςΟεͰ͋Δ wݸͷ࣭໰ʹνʔϜͰ౴͑Λग़͢͜ͱͰɺϓϩδΣ ΫτɾϓϩμΫτͷํ޲ੑΛ߹ҙ͢Δ wࠓճ͸൒೔º̎೔Ͱݸͷ࣭໰ʹ௅ઓͨ͠

  14. ΦϑΟεͷҰ֯Λઐ༻εϖʔεʹͯࣾ͠಺߹॓

  15. υϥοΧʔ෩ΤΫααΠζ wΠϯηϓγϣϯσοΩಉ༷ʮΞδϟΠϧαϜϥΠʯͰ ঺հ͞Ε͍ͯΔνʔϜϏϧσΟϯάͷϓϥΫςΟε wͭͷ࣭໰ʹ౴͑Δ͜ͱͰɺ͓ޓ͍ͷಘҙ෼໺΍Ձ஋ ؍Λڞ༗͠ɺϝϯόʔͷ૬ޓཧղʹޮՌ͕͋Δ wৄ͘͠͸ϖύϘςοΫϒϩάΛࢀর wIUUQTUFDIQFQBCPDPNUIFESVDLFSFYFSDJTF

  16. ྨࣅαʔϏεͷϞσϦϯά ։ൃʹೖΔલʹɺྨࣅͷαʔϏε Λ࢖ͬͯΈͯɺ಺෦ߏ଄Λ૝૾Ͱ ϞσϦϯάͯ͠ΈΔɻ ࣄۀυϝΠϯͷޠኮΛऩूͨ͠Γɺ Ϟσϧؒͷؔ࿈Λ࡞ΔલʹΠϝʔ δͰ͖Δɻ

  17. দߐ߹॓ 3VCZͷ੟஍ɺদߐͰ։ൃ ߹॓Λ࣮ࢪɻ

  18. ೔ʑͷ։ൃ wिؒεϓϦϯτͷεΫϥϜ։ൃ wϢʔβςετ w Նͷ ࣗ༝ݚڀ

  19. िؒεϓϦϯτͷεΫϥϜ ͬΆ͍ ։ൃ w໦༵࢝·ΓͷिؒεϓϦϯτ wਫ༵༦ํʮ;Γ͔͑Γ ,15" ʯͱʮϓϥϯχϯάʯ wʮ;Γ͔͑ΓʯͷલʹσϞλΠϜ wՐ༵༦ํʹʮόοΫϩάϦϑΝΠϯϝϯτʯ wλεΫ؅ཧ͸1JWPUBM5SBDLFS෺ཧΧϯόϯ

  20. Ϣʔβςετ wϦϦʔεલ͸ࣾ಺ͷϝϯόʔʹɺϦϦʔεޙ͸ࣾ֎ͷ λʔήοτϢʔβʹΞϓϦέʔγϣϯΛ৮ͬͯ΋Β͍ ϑΟʔυόοΫΛಘΔ wૢ࡞ΛಈըʢϦΞϧλΠϜPS࿥ը Ͱݟͳ͕ΒɺϢʔ βͷൃݴͱؾ͕͍ͭͨ఺Λॻ͖ग़͠Ϛοϐϯά͢Δ w5IBOLTσβΠϯઓུνʔϜ

  21. ࿥ը΍தܧΛΈͳ͕Β
 ؾʹͳͬͨͱ͜ΖΛॻ͖ग़͢ தܧ͸(PPHMF)BOHPVUͷը໘ڞ༗ ϚΠΫ

  22. Ϛοϐϯάͨ͠΋ͷ͔Βಉ͡΋ͷΛચ͍ग़͠ ༏ઌॱҐΛ෇͚ͯܭըʹམͱ͠ࠐΉ

  23. Նͷ ࣗ༝ݚڀ ٕज़తͳෆ҆ཁૉ΍νϟ Ϩϯδ͍ͨ͠τϐοΫΛ ࣗ༝ʹݚڀ͠ɺͦͷ݁Ռ ΛJTTVFͰڞ༗͢Δ

  24. ໨࣍ wαʔϏεͷ֓ཁͱΞʔΩςΫνϟ wϓϩδΣΫτͷ্ཱͪ͛ͱ೔ʑͷ։ൃ wεΩʔϚϑΝʔετ։ൃͷݱ৔

  25. ΧϥʔϛʔϦϐʔτͷߏ੒ "1*αʔόʔ ϑϩϯτΤϯυαʔόʔ ΫϥΠΞϯτ ॳճΞΫηε࣌ +40/Λฦ͢8FC"1* +40/Λฦ͢8FC"1* ϒϥ΢β্Ͱ41"͕ಈ࡞

  26. ΧϥʔϛʔϦϐʔτͷߏ੒ "1*αʔόʔ ϑϩϯτΤϯυαʔόʔ ΫϥΠΞϯτ ॳճΞΫηε࣌ +40/Λฦ͢8FC"1* +40/Λฦ͢8FC"1* ϒϥ΢β্Ͱ41"͕ಈ࡞

  27. ΧϥʔϛʔϦϐʔτͷߏ੒ "1*αʔόʔ ϑϩϯτΤϯυαʔόʔ ΫϥΠΞϯτ +40/Λฦ͢8FC"1* +40/Λฦ͢8FC"1* ϒϥ΢β্Ͱ41"͕ಈ࡞ w ॳظදࣔΛߴ଎ʹ͍ͨ͠Ұ෦ϖʔδͰ4FSWFS4JEF3FOEFSJOH 443

     w ҎԼʮ৽نϓϩδΣΫτ΁ͷ7VFKTY41"Y443ͷಋೖʯ͕ৄ͍͠
 https://www.slideshare.net/TsuchiKazu/vuejs-x-spa-x-ssr-79213417 ॳճΞΫηε࣌
  28. ॳճΞΫηε࣌ ΧϥʔϛʔϦϐʔτͷߏ੒ "1*αʔόʔ ϑϩϯτΤϯυαʔόʔ ΫϥΠΞϯτ +40/Λฦ͢8FC"1* +40/Λฦ͢8FC"1* ϒϥ΢β্Ͱ41"͕ಈ࡞ w3BJMT"1*ϞʔυΛ༻͍ͨ8FC"1* w41"͔Βར༻͢Δ͜ͱΛ૝ఆͨ͠"1*

    w4JEFLJRͱTJEFLJRTDIFEVMFSͰඇಉظॲཧͱఆظόον࣮ߦ
  29. ΧϥʔϛʔϦϐʔτͷߏ੒ "1*αʔόʔ ϑϩϯτΤϯυαʔόʔ ΫϥΠΞϯτ +40/Λฦ͢8FC"1* +40/Λฦ͢8FC"1* ϒϥ΢β্Ͱ41"͕ಈ࡞ ॳճΞΫηε࣌

  30. ΧϥʔϛʔϦϐʔτͷ։ൃϑϩʔ APIఆٛ νʔϜ ϨϏϡʔ ։ൃͱ ςετ ݁߹

  31. ΧϥʔϛʔϦϐʔτͷ։ൃϑϩʔ APIఆٛ νʔϜ ϨϏϡʔ ։ൃͱ ςετ ݁߹ ˡى఺͸ίί

  32. "1*ఆٛΛ׆༻ͨ͠։ൃ w"1*ఆٛΛ༻͍ͨϑϩϯτΤϯυͱόοΫΤϯυฒߦ ։ൃ wεΩʔϚϑΝʔετ։ൃ w4QFDJpDBUJPO'JSTU%FWFMPQNFOU
 SFGεΩʔϚϑΝʔετ։ൃͷεεϝPOLOJOKB
 https://blog.onk.ninja/2017/09/21/schema_first_development

  33. "1*ఆٛͱ͸ w8FC"1*ʢҎ߱"1*ͱ͢Δʣͷ࢓༷ΛطఆͷܗࣜͰॻ͘͜ͱ wΤϯυϙΠϯτɺϦΫΤετɺεςʔλείʔυɺϨεϙϯε PUT /shipments/:id path: id body: { "shipment":

    { "status": "active", "delivery_on": "2017-12-09" } } status: 200, 404, 422 body: { "shipment": { "status": "active", "delivery_on": "2017-12-09", "created_at": "2017-12-09T00:00:00", "updated_at": "2017-12-09T00:00:00" } } ϦΫΤετ Ϩεϙϯε
  34. "1*ఆٛΛ׆༻ͨ͠։ൃͷϝϦοτ w։ൃϝϯόʔؒͰϦΫΤετ΍Ϩεϙϯεͷܗࣜʹ
 ର͢ΔೝࣝͷζϨ͕ݮΔ w"1*ఆٛࣗମΛύʔεͯ͠ɺ͍Ζ͍Ζ׆༻Ͱ͖Δ wΤίγεςϜ͕ӫ͍͑ͯΔͱศར wεϜʔζͳϑϩϯτΤϯυͱόοΫΤϯυฒߦ։ൃ

  35. 0QFO"1* wΧϥʔϛʔϦϐʔτͷ"1*ఆٛͷܗࣜͱͯ͠0QFO"1*ΛબΜͩ w0QFO"1*ͱ͸ w"1*ఆٛΛॻͨ͘Ίͷ਺͋Δܗࣜͷͻͱͭ wଞʹ͸"1*#MVFQSJOU΍3".-ͳͲ w+40/͔:".-Ͱॻ͘ w೥ݱࡏɺ"1*࢓༷هड़ϑΥʔϚοτͷओྲྀͱͳ͍ͬͯΔ
 SFG.VMF4PGU+PJOTUIF0QFO"1**OJUJBUJWF5IF&OEPGUIF"1*4QFD8BST
 https://swagger.io/blog/mulesoft-joins-the-openapi-initiative/

  36. 0QFO"1*υΩϡϝϯτͷྫ # definitions/shipment.yml definitions: Shipment: type: object properties: id: type:

    integer example: 1 status: type: string enum: - unshipped - shipped - canceled # paths/shipment.yml paths: /shipments/{shipmentId}: get: parameters: - shipmentId: in: path name: shipmentId type: integer required: true responses: '200': schema: type: object properties: shipment: $ref: '#/definitions/Shipment' ˡΤϯυϙΠϯτఆٛ ˣϦιʔεఆٛ ΄΅+40/4DIFNB
  37. 0QFO"1*υΩϡϝϯτͷྫ # definitions/shipment.yml definitions: Shipment: type: object properties: id: type:

    integer example: 1 status: type: string enum: - unshipped - shipped - canceled # paths/shipment.yml paths: /shipments/{shipmentId}: get: parameters: - shipmentId: in: path name: shipmentId type: integer required: true responses: '200': schema: type: object properties: shipment: $ref: '#/definitions/Shipment' ˡΤϯυϙΠϯτఆٛ ˣϦιʔεఆٛ ΄΅+40/4DIFNB
  38. 0QFO"1*υΩϡϝϯτͷྫ # definitions/shipment.yml definitions: Shipment: type: object properties: id: type:

    integer example: 1 status: type: string enum: - unshipped - shipped - canceled # paths/shipment.yml paths: /shipments/{shipmentId}: get: parameters: - shipmentId: in: path name: shipmentId type: integer required: true responses: '200': schema: type: object properties: shipment: $ref: '#/definitions/Shipment' ˡΤϯυϙΠϯτఆٛ ˣϦιʔεఆٛ ΄΅+40/4DIFNB ͜ΕΛຕͷ+40/ PS:".- ʹॻ͘
  39. 0QFO"1*ͱ4XBHHFS w0QFO"1*JTGPSNFSMZLOPXOBT4XBHHFS w4NBSUCFBSࣾ։ൃͷ4XBHHFSΛ(PPHMF΍.JDSPTPGU ໊͕Λ࿈ͶΔ0QFO"1**OJUJBUJWFʹҠ؅ w4XBHHFS0QFO"1* w͜ͷ໊࢒Ͱ0QFO"1*༻ͷπʔϧ͸4XBHHFSdͱ
 ͍͏໊લ

  40. 0QFO"1*υΩϡϝϯτͷ࡞੒ wϦιʔεఆٛΤϯυϙΠϯτఆٛ͝ͱʹ:".-
 ϑΝΠϧΛ෼͚Δ # paths/user.yml paths: /admin/users # ... #

    index.yml swagger: '2.0' # ... paths: /users: $import: ./paths/users.yml definitions: Shipment: $import: ./definitions/shipment.yml # definitions/shipment.yml definitinos: Shipment: # ...
  41. 0QFO"1*υΩϡϝϯτͷ࡞੒ w3BJMTϓϩδΣΫτ಺ʹ0QFO"1*υΩϡϝϯτ഑ஔ . !"" app !"" bin !"" db !""

    doc # ഑ԼʹϦιʔε/ΤϯυϙΠϯτ͝ͱʹ֨ೲ !"" lib !"" public !"" spec #"" ... doc #"" spec #"" v1 !"" definitions # Ϧιʔεఆٛ $ !"" shipment.yml $ #"" ... !"" index.yml # Ϧιʔε/ΤϯυϙΠϯτ·ͱΊ༻ #"" paths # ΤϯυϙΠϯτఆٛ !"" admin $ !"" users.yml $ !"" ...
  42. 0QFO"1*υΩϡϝϯτͷ࡞੒ w:BNM3FG3FTPMWFSΛ࢖ͬͯϚʔδͯ͠ຕͷ+40/ TXBHHFSKTPO Խ
 https://github.com/Joe-noh/yaml_ref_resolver # swagger_json_generator.rb require 'yaml_ref_resolver' resolver

    = YamlRefResolver.new(key: '$import') json = resolver.resolve!('./index.yml') File.open('./public/swagger.json', 'w') { |f| f.write(json) } # index.yml swagger: '2.0' # ... paths: /admin/users: $import: ./paths/admin/users.yml definitions: Shipment: $import: ./definitions/shipment.yml
  43. 4XBHHFS&EJUPSΛิॿతʹ࢖ͬͯ TZOUBYFSSPSΛආ͚Δ

  44. None
  45. ΧϥʔϛʔϦϐʔτͷ։ൃϑϩʔ APIఆٛ νʔϜ ϨϏϡʔ ։ൃͱ ςετ ݁߹

  46. "1*ఆٛͱνʔϜϨϏϡʔ w:".-ͰΤϯυϙΠϯτఆٛͱؔ࿈͢ΔϦιʔεఆٛ Λॻ͘ wνʔϜϝϯόʔ͸୭Ͱ΋ॻ͘

  47. "1*ఆٛͱνʔϜϨϏϡʔ w"1*ఆٛΛॻ͍ͯ1VMM3FRVFTUΛ࡞ͬͨΒνʔϜͰϨϏϡʔ͢Δ w໋໊ɺϦιʔεͷ಺༰ɺύϥϝʔλɺϨεϙϯεͳͲΛݟΔ w0,ͳΒ"1*ఆ͚ٛͩϚʔδʂ

  48. ΧϥʔϛʔϦϐʔτͷ։ൃϑϩʔ APIఆٛ νʔϜ ϨϏϡʔ ։ൃͱ ςετ ݁߹

  49. "1*ఆٛͱ։ൃ wϑϩϯτΤϯυ w4XBHHFS$PEFHFOͰ0QFO"1*υΩϡϝϯτ͔Β
 ελϒαʔόΛੜ੒͢Δ
 0QFO"1*ͷϨεϙϯεྫఆٛΛ΋ͱʹ4XBHHFS$PEFHFOͰελϒαʔόΛੜ੒͢Δ
 https://qiita.com/kymmt90/items/6a254669cd6c7f2b3cf w"1*ͷ࣮૷Λ଴ͨͣʹฒߦ։ൃͰ͖Δ

  50. "1*ఆٛͱ։ൃ wόοΫΤϯυ w0QFO"1*υΩϡϝϯτͱͷ੔߹ੑΛऔΓͳ͕Β࣮૷ ͢Δඞཁ͕͋Δ

  51. "1*ఆٛͱ࣮"1*ͷ੔߹ੑ w0QFO"1*υΩϡϝϯτ্ͷ"1*ఆٛͱ࣮"1*ͷڍಈͷ ੔߹ੑΛͱΓɺϨεϙϯεͷܗࣜͷζϨΛආ͚Δ wϑϩϯτΤϯυ͸0QFO"1*υΩϡϝϯτΛਖ਼ͱͯ͠ ฒߦ։ൃ͍ͯ͠ΔͨΊ w"1*ఆٛͱ࣮ϨεϙϯεΛࣗಈͰಥ߹Ͱ͖Δͱศར

  52. ࣗಈͰ੔߹ੑΛνΣοΫ w34QFDͷ3FRVFTU4QFD࣮ߦ࣌ͷϨεϙϯε+40/ͱ0QFO"1*ʹ
 ॻ͍ͨϨεϙϯεͷ+40/4DIFNBͷ੔߹ੑΛνΣοΫ͍ͨ͠ wࣗಈͰϨεϙϯεΛόϦσʔγϣϯ͢ΔHFNΛ࡞ͬͯಋೖͨ͠
 4DIFNB$POGPSNJTUhttps://github.com/kymmt90/schema_conformist w$PNNJUUFF3BJMTͷassert_schema_conformϝιουΛར༻
 https://github.com/willnet/committee-rails wνΣοΫ͠๨ΕΛͳ͘͢

  53. ϨεϙϯεόϦσʔγϣϯͷ࣮ߦྫ # public/swagger.json # GET /users/:id͸ҎԼͷJSONΛฦ͢ͱ͍͏࢓༷ definitinos: User: type: object

    properties: email: type: string status: type: string enum: - active - suspended # ... # spec/requests/users_spec.rb # Θ͟ͱemailΛؚΊͳ͍࣮૷ʹରͯ͠Request SpecΛ࣮ߦ RSpec.describe 'Users', type: :request do describe 'GET /users/:id' do it 'returns a valid response' do get "/users/#{user.id}" # ... end end end
  54. ϨεϙϯεόϦσʔγϣϯͷ࣮ߦྫ # OpenAPIυΩϡϝϯτதͷJSON Schemaͱ࣮ϨεϙϯεͷJSON͕ζϨͨͱ͖ʢemailΛ࣮ϨεϙϯεʹؚΊΒΕ͍ͯͳ͍ͱ͖ʣ 1) Users GET /users/:id Failure/Error: assert_schema_conform

    Committee::InvalidResponse: Invalid response. #: failed schema #/properties//users/{userId}/properties/GET: "email" wasn't supplied. HFNΛೖΕ͓͚ͯͩ͘Ͱ3FRVFTU4QFDͷ࣮ߦ࣌ʹࣗಈͰ
 0QFO"1*υΩϡϝϯτͱ࣮ϨεϙϯεͷҰகΛνΣοΫͰ͖Δ
  55. ੔߹ੑνΣοΫͷ͘͠Έ wϨεϙϯεΛ"1*ఆٛதͷ+40/4DIFNBͰ
 όϦσʔγϣϯ͢ΔͨΊʹɺ$PNNJUUFF3BJMTͷ assert_schema_conformΛͲ͔͜Ͱ࣮ߦ͢Δ wͲ͕͍͍͔͜ʁ

  56. module ActionDispatch module Integration #:nodoc: module RequestHelpers def get(path, **args)

    process(:get, path, **args) end # ... end class Session include TestProcess, RequestHelpers, Assertions def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil) # ... 3FRVFTU4QFDͷ)551ϦΫΤετൃߦϝιουʢget postͳͲʣͷ
 ࣮ମͰ͋ΔActionDispatch::Integration::Session#process
  57. # lib/schema_conformist/ process_with_assertion.rb module SchemaConformist module ProcessWithAssertion # ... def

    process(*args) super *args # ... assert_schema_conform end end end )551Ϩεϙϯεऔಘ௚ޙʹassert_schema_conformΛ
 ࣮ߦ͢ΔϝιουΛ࣋ͭϞδϡʔϧΛprependͯ͠ಈ࡞Λॻ͖׵͑✌ # lib/action_dispatch/integration.rb module ActionDispatch module Integration class Session prepend SchemaConformist::ProcessWithAssertion end end end
  58. ΧϥʔϛʔϦϐʔτͷ։ൃϑϩʔ APIఆٛ νʔϜ ϨϏϡʔ ։ൃͱ ςετ ݁߹

  59. "1*ఆٛͱ݁߹ w͋Δ"1*ʹ͍ͭͯϑϩϯτΤϯυͱόοΫΤϯυͷฒߦ։ൃ͕׬ྃ͢ Δͨͼʹ݁߹ wपͰͻͱͭͷΤϯυϙΠϯτʹ͍ͭͯͷػೳ͕Ͱ͖Δ w࠷ऴతʹɺݸͷΤϯυϙΠϯτΛ࣋ͭ"1*ͱɺͦΕΛར༻ͨ͠41" ͔ΒͳΔ8FCΞϓϦέʔγϣϯΛɺΤϯδχΞਓº͔݄ڧͰ։ൃ Ͱ͖ͨ

  60. ·ͱΊ

  61. ໨࣍ wαʔϏεͷ֓ཁͱΞʔΩςΫνϟ wϓϩδΣΫτͷΩοΫΦϑͱ೔ʑͷ։ൃϓϩηε wΧϥʔϛʔϦϐʔτʹ͓͚ΔεΩʔϚϑΝʔετ։ൃ

  62. ·ͱΊ wϓϩμΫτΛνʔϜͰ։ൃ͍ͯͨ͘͠Ίͷ
 ϓϥΫςΟεΛ঺հ͠·ͨ͠ w3BJMT 7VFKTͳ৽ن&$ϓϩμΫτͷ։ൃͰ
 εΩʔϚϑΝʔετ։ൃͨ͠ࣄྫΛ঺հ͠·ͨ͠ w࠙਌ձͳͲͰ͓࿩͠͠·͠ΐ͏