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

Hatena Engineer Seminar #14 GraphQL編

Hatena Engineer Seminar #14 GraphQL編

サブタイトル: 〜ユーザーも開発者も快適なサービスであるために〜

魔法のiらんどでは、クライアント・サーバー間のインターフェースとしてGraphQLを採用しました。この技術選択は、はてなの技術スタックのモダン化を進め、ユーザーと開発に携わるエンジニア双方の体験が快適であることを目指したものです。この発表では、魔法のiらんどリニューアルを成功に導いたGraphQLの強みと、月間3億PVをさばくためのパフォーマンス面の検討について紹介します。

Yuu Igarashi

July 15, 2020
Tweet

More Decks by Yuu Igarashi

Other Decks in Programming

Transcript

  1. GraphQLฤ
    ʙϢʔβʔ΋։ൃऀ΋շదͳαʔϏεͰ͋ΔͨΊʹʙ
    Hatena Engineer Seminar #14
    2020/07/15 id:yigarashi

    View Slide

  2. ࣗݾ঺հ
    • id:yigarashi
    • ৽ଔ2೥໨
    • WebΞϓϦέʔγϣϯΤϯδχΞ
    • ڵຯ
    • ։ൃϓϩηεɺϓϩδΣΫτ؅ཧ
    • ϓϩάϥϛϯάݴޠཧ࿦

    View Slide

  3. ຐ๏ͷiΒΜͲ

    View Slide

  4. γεςϜͷ֓؍
    /FYUKT
    αʔόʔαΠυϨϯμϦϯά
    (SBQI2-"1*
    σʔλϕʔε

    View Slide

  5. γεςϜͷ֓؍
    /FYUKT
    αʔόʔαΠυϨϯμϦϯά
    (SBQI2-"1*
    σʔλϕʔε
    ͍·͔Β͜͜ͷ࿩Λ͠·͢

    View Slide

  6. GraphQLฤ - ໨࣍
    • GraphQLͱ͸
    • ͳͥGraphQLΛ࠾༻͔ͨ͠
    • GraphQLͷԸܙ
    • ݄ؒ3ԯPV΁ͷඋ͑

    View Slide

  7. GraphQLͱ͸

    View Slide

  8. GraphQLͱ͸
    "1*ͷͨΊͷΫΤϦݴޠͱΫΤϦʹԠͨ͡σʔλΛฦ͢ϥϯλΠϜ

    View Slide

  9. GraphQLͱ͸
    "1*ͷͨΊͷΫΤϦݴޠͱΫΤϦʹԠͨ͡σʔλΛฦ͢ϥϯλΠϜ
    ΫϥΠΞϯτ
    αʔόʔ
    ʢϥϯλΠϜʣ

    View Slide

  10. GraphQLͱ͸
    {
    me {
    name
    interests
    }
    }
    ΫΤϦ
    "1*ͷͨΊͷΫΤϦݴޠͱΫΤϦʹԠͨ͡σʔλΛฦ͢ϥϯλΠϜ
    ΫϥΠΞϯτ
    αʔόʔ
    ʢϥϯλΠϜʣ

    View Slide

  11. GraphQLͱ͸
    {
    me {
    name
    interests
    }
    }
    ΫΤϦ
    {
    "me": {
    "name": "yigarashi",
    "interests": [
    "։ൃϓϩηε",
    "ϓϩάϥϛϯάݴޠཧ࿦",
    ]
    }
    }
    ฦΓ஋ʢ+40/ʣ
    "1*ͷͨΊͷΫΤϦݴޠͱΫΤϦʹԠͨ͡σʔλΛฦ͢ϥϯλΠϜ
    ΫϥΠΞϯτ
    αʔόʔ
    ʢϥϯλΠϜʣ

    View Slide

  12. GraphQLͱ͸
    {
    me {
    name
    interests
    }
    }
    ΫΤϦ
    {
    "me": {
    "name": "yigarashi",
    "interests": [
    "։ൃϓϩηε",
    "ϓϩάϥϛϯάݴޠཧ࿦",
    ]
    }
    }
    ฦΓ஋ʢ+40/ʣ
    ΫΤϦͱಉ͡ܗͷ
    +40/͕ฦ͞ΕΔ
    "1*ͷͨΊͷΫΤϦݴޠͱΫΤϦʹԠͨ͡σʔλΛฦ͢ϥϯλΠϜ
    ΫϥΠΞϯτ
    αʔόʔ
    ʢϥϯλΠϜʣ

    View Slide

  13. ॏཁͳ֓೦(1) - εΩʔϚ
    ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ

    View Slide

  14. ॏཁͳ֓೦(1) - εΩʔϚ
    ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ
    αʔόʔ
    ʢϥϯλΠϜʣ
    ΫϥΠΞϯτ

    View Slide

  15. ॏཁͳ֓೦(1) - εΩʔϚ
    ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ
    type Query { me: Me }
    type Me {
    name: String
    interests: List[String]
    }
    εΩʔϚͷྫ
    αʔόʔ
    ʢϥϯλΠϜʣ
    ΫϥΠΞϯτ

    View Slide

  16. ॏཁͳ֓೦(1) - εΩʔϚ
    ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ
    type Query { me: Me }
    type Me {
    name: String
    interests: List[String]
    }
    εΩʔϚͷྫ
    αʔόʔ
    ʢϥϯλΠϜʣ
    ΫϥΠΞϯτ
    {
    me {
    name
    interests
    }
    }

    View Slide

  17. ॏཁͳ֓೦(1) - εΩʔϚ
    ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ
    type Query { me: Me }
    type Me {
    name: String
    interests: List[String]
    }
    εΩʔϚͷྫ
    αʔόʔ
    ʢϥϯλΠϜʣ
    ΫϥΠΞϯτ
    {
    me {
    name
    interests
    }
    }

    View Slide

  18. type Query { me: Me }
    type Me {
    name: String
    interests: List[String]
    }
    ॏཁͳ֓೦(1) - εΩʔϚ
    ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ
    εΩʔϚͷྫ
    αʔόʔ
    ʢϥϯλΠϜʣ
    ΫϥΠΞϯτ

    View Slide

  19. type Query { me: Me }
    type Me {
    name: String
    interests: List[String]
    }
    ॏཁͳ֓೦(1) - εΩʔϚ
    ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ
    εΩʔϚͷྫ
    αʔόʔ
    ʢϥϯλΠϜʣ
    ΫϥΠΞϯτ
    {
    me {
    name
    interests
    age
    }
    }

    View Slide

  20. type Query { me: Me }
    type Me {
    name: String
    interests: List[String]
    }
    ॏཁͳ֓೦(1) - εΩʔϚ
    ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ
    εΩʔϚͷྫ
    αʔόʔ
    ʢϥϯλΠϜʣ
    ΫϥΠΞϯτ
    {
    me {
    name
    interests
    age
    }
    }

    View Slide

  21. ॏཁͳ֓೦(2) - Ϧκϧό
    ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺

    View Slide

  22. ॏཁͳ֓೦(2) - Ϧκϧό
    ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺
    {
    me {
    name
    interests
    }
    }

    View Slide

  23. ॏཁͳ֓೦(2) - Ϧκϧό
    ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺
    {
    me {
    name
    interests
    }
    }
    యܕతͳ࣮ߦ

    View Slide

  24. ॏཁͳ֓೦(2) - Ϧκϧό
    ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺
    {
    me {
    name
    interests
    }
    }
    • meΛܭࢉ͢ΔϦκϧόΛ࣮ߦ
    • DB͔ΒmeʹରԠ͢ΔΦϒδΣΫτMeΛऔಘ
    యܕతͳ࣮ߦ

    View Slide

  25. ॏཁͳ֓೦(2) - Ϧκϧό
    ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺
    {
    me {
    name
    interests
    }
    }
    • meΛܭࢉ͢ΔϦκϧόΛ࣮ߦ
    • DB͔ΒmeʹରԠ͢ΔΦϒδΣΫτMeΛऔಘ
    • nameΛܭࢉ͢ΔϦκϧόΛ࣮ߦ
    • Me.nameΛฦ͢
    యܕతͳ࣮ߦ

    View Slide

  26. ॏཁͳ֓೦(2) - Ϧκϧό
    ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺
    {
    me {
    name
    interests
    }
    }
    • meΛܭࢉ͢ΔϦκϧόΛ࣮ߦ
    • DB͔ΒmeʹରԠ͢ΔΦϒδΣΫτMeΛऔಘ
    • nameΛܭࢉ͢ΔϦκϧόΛ࣮ߦ
    • Me.nameΛฦ͢
    • interestsΛܭࢉ͢ΔϦκϧόΛ࣮ߦ
    • Me.interests͕idͷྻʹͳ͍ͬͯΔͷͰDB͔Βऔಘ͢Δ
    యܕతͳ࣮ߦ

    View Slide

  27. ॏཁͳ֓೦(2) - Ϧκϧό
    ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺
    {
    me {
    name
    interests
    }
    }
    • meΛܭࢉ͢ΔϦκϧόΛ࣮ߦ
    • DB͔ΒmeʹରԠ͢ΔΦϒδΣΫτMeΛऔಘ
    • nameΛܭࢉ͢ΔϦκϧόΛ࣮ߦ
    • Me.nameΛฦ͢
    • interestsΛܭࢉ͢ΔϦκϧόΛ࣮ߦ
    • Me.interests͕idͷྻʹͳ͍ͬͯΔͷͰDB͔Βऔಘ͢Δ
    • ࠷ޙʹ׬੒ͨ͠஋ΛJSONʹͯ͠ฦ͢
    యܕతͳ࣮ߦ

    View Slide

  28. ͳͥGraphQLΛ࠾༻͔ͨ͠

    View Slide

  29. Ϣʔβʔମݧɾ։ൃޮ཰Λ޲্͢Δ
    • Ϣʔβʔମݧ
    • SPA(Single Page Application) + APIͷߏ੒ʹ͢Δ͜ͱͰɺϞμϯͳ
    ΞϓϦతͳମݧΛ࣮૷͠΍͘͢͢Δ
    • ։ൃޮ཰
    • ΫΤϦݴޠ͕ڧྗͰը໘ͷมߋʹڧ͍
    • Ϟμϯͳٕज़Λ࠾༻͢Δ͜ͱͰϞνϕʔγϣϯ޲্
    • ͔ͦ͠͠Ε͚ͩͰ͸ͳ͍……

    View Slide

  30. ΧΫϤϜ

    View Slide

  31. ຐ๏ͷiΒΜͲϦχϡʔΞϧͷϙΠϯτ
    • ΧΫϤϜͷίΞϩδοΫΛ࢖ͬͯAPIΛ࣮૷͢Δ
    • ྫ: ࡞඼ʹؔ͢ΔCRUDૢ࡞
    • ൓ରʹɺຐ๏ͷiΒΜͲͷͨΊʹ࣮૷ͨ͠APIΛΧΫϤϜͰ΋࢖͑ΔΑ
    ͏ʹ͓ͯ͘͠
    ΧΫϤϜͱίʔυϕʔεΛڞ༗͠ɺ։ൃɾӡ༻ίετͷ࡟ݮΛૂ͏

    View Slide

  32. ঢ়گΛ෼ੳ͢Δ
    • খઆΛॻ͘ɺಡΉɺڞ༗͢Δͱ͍͏େے͸͔֬ʹࣅ͍ͯΔ
    • ࡉ෦͕ͪΐͬͱͣͭҧ͏
    • ϑΟʔϧυͷ༗ແ
    • ຐ๏ͷiΒΜͲʹ͋Δ͕ΧΫϤϜʹ͸ͳ͍ɺٯ΋વΓ
    • ஋Λฦͨ͢Ίͷॲཧͷҧ͍

    View Slide

  33. REST API ͸ͭΒ͍
    ίΞϩδοΫΛ࢖ͬͯJSON
    Λฦ͢෦෼ͷංେԽɾΧΦ
    εԽ͕༧૝͞Εͨ

    View Slide

  34. REST API ͸ͭΒ͍ sub example_handler {
    my $common_field = get_common_field;
    if (is_kakuyomu) {
    $common_field =
    kakuyomu_specific_operation($common_field);
    }
    my $kakuyomu_field = undef;
    if (is_kakuyomu) {
    $kakuyomu_field = get_kakuyomu_field;
    }
    ...
    }
    ίΞϩδοΫΛ࢖ͬͯJSON
    Λฦ͢෦෼ͷංେԽɾΧΦ
    εԽ͕༧૝͞Εͨ

    View Slide

  35. REST API ͸ͭΒ͍ sub example_handler {
    my $common_field = get_common_field;
    if (is_kakuyomu) {
    $common_field =
    kakuyomu_specific_operation($common_field);
    }
    my $kakuyomu_field = undef;
    if (is_kakuyomu) {
    $kakuyomu_field = get_kakuyomu_field;
    }
    ...
    }
    ίΞϩδοΫΛ࢖ͬͯJSON
    Λฦ͢෦෼ͷංେԽɾΧΦ
    εԽ͕༧૝͞Εͨ
    ڞ௨ͷϑΟʔϧυ͕ͩΧΫϤϜͰ
    ͸ಛघͳՃ޻͕ඞཁ

    View Slide

  36. REST API ͸ͭΒ͍ sub example_handler {
    my $common_field = get_common_field;
    if (is_kakuyomu) {
    $common_field =
    kakuyomu_specific_operation($common_field);
    }
    my $kakuyomu_field = undef;
    if (is_kakuyomu) {
    $kakuyomu_field = get_kakuyomu_field;
    }
    ...
    }
    ίΞϩδοΫΛ࢖ͬͯJSON
    Λฦ͢෦෼ͷංେԽɾΧΦ
    εԽ͕༧૝͞Εͨ
    ڞ௨ͷϑΟʔϧυ͕ͩΧΫϤϜͰ
    ͸ಛघͳՃ޻͕ඞཁ
    ΧΫϤϜͷΈଘࡏ͢ΔϑΟʔϧυ

    View Slide

  37. GraphQLͰղܾ
    ϑΟʔϧυ͝ͱʹϦκϧ
    όΛఆٛ͢ΔͷͰɺॲཧ
    ͷৼΓ෼͚Λॊೈ͔ͭ៉
    ྷʹߦ͏͜ͱ͕Ͱ͖Δ

    View Slide

  38. GraphQLͰղܾ
    GraphQL::Type::Object->new(
    name => 'Example',
    fields => +{
    common_field => is_kakuyomu ?
    kakuyomu_resolver :
    maho_resolver,
    (is_kakuyomu ? (
    kakuyomu_field => kakuyomu_field_resolver
    ) : ())
    ...
    }
    )
    ϑΟʔϧυ͝ͱʹϦκϧ
    όΛఆٛ͢ΔͷͰɺॲཧ
    ͷৼΓ෼͚Λॊೈ͔ͭ៉
    ྷʹߦ͏͜ͱ͕Ͱ͖Δ

    View Slide

  39. GraphQLͰղܾ
    GraphQL::Type::Object->new(
    name => 'Example',
    fields => +{
    common_field => is_kakuyomu ?
    kakuyomu_resolver :
    maho_resolver,
    (is_kakuyomu ? (
    kakuyomu_field => kakuyomu_field_resolver
    ) : ())
    ...
    }
    )
    ϑΟʔϧυ͝ͱʹϦκϧ
    όΛఆٛ͢ΔͷͰɺॲཧ
    ͷৼΓ෼͚Λॊೈ͔ͭ៉
    ྷʹߦ͏͜ͱ͕Ͱ͖Δ
    ݺͼग़͢ϦκϧόΛৼΓ෼͚Δͩ
    ͚Ͱྑ͍

    View Slide

  40. GraphQLͰղܾ
    GraphQL::Type::Object->new(
    name => 'Example',
    fields => +{
    common_field => is_kakuyomu ?
    kakuyomu_resolver :
    maho_resolver,
    (is_kakuyomu ? (
    kakuyomu_field => kakuyomu_field_resolver
    ) : ())
    ...
    }
    )
    ϑΟʔϧυ͝ͱʹϦκϧ
    όΛఆٛ͢ΔͷͰɺॲཧ
    ͷৼΓ෼͚Λॊೈ͔ͭ៉
    ྷʹߦ͏͜ͱ͕Ͱ͖Δ
    ݺͼग़͢ϦκϧόΛৼΓ෼͚Δͩ
    ͚Ͱྑ͍
    αʔϏε͝ͱʹඞཁͳϑΟʔϧυ
    ͚ͩ࿐ग़͢Δ

    View Slide

  41. ࠾༻ཧ༝ͷ·ͱΊ
    • SPA + API ʹΑΔϞμϯͳϢʔβʔମݧͷ࣮ݱ
    • ։ൃޮ཰ͷ޲্
    • ΧΫϤϜͱͷίʔυϕʔεͷڞ༗
    ҎԼͷͭͷधཁΛ͏·͘ຬͨ͢ͷ͕(SBQI2-ͩͬͨ

    View Slide

  42. GraphQLͷԸܙ

    View Slide

  43. Ըܙ(1) - ϑϩϯτΤϯυ։ൃମݧͷ޲্
    • ύϫϑϧͳΫΤϦݴޠʹΑΔॊೈͳσʔλऔಘ
    • Τϐιʔυ --> ࡞඼ --> ࡞ऀͱ͍ͬͨωετͨ͠σʔλ΋αʔόʔ
    ͷมߋͳ͠ʹ؆୯ʹऔಘͰ͖Δ
    • Apollo ClientͷԸܙ
    • Reactͱ࿈ܞ͢ΔGraphQLΫϥΠΞϯτͷσϑΝΫτ
    • ߥ࡟Γͳ෦෼΋͋Δ͕ϞμϯͳUIΛ࡞ΔͨΊͷػೳ͕๛෋

    View Slide

  44. Ըܙ(2) - API։ൃͷޮ཰Խ
    • ࠷ॳʹશͯͷը໘Λਫ਼ࠪͯ͠1000ߦ͍ۙεΩʔϚΛઃܭͨ͠
    • εΩʔϚϑΝʔετͳ։ൃ
    • εΩʔϚઃܭΛ௨ͯ͠ϓϩμΫτ΁ͷཧղ͕ਂ·ͬͨ
    • ΤϯδχΞ8ਓͷେॴଳ͕͏·͘ػೳ͢Δ͖͔͚ͬʹ
    • εΩʔϚத৺ͷίϛϡχέʔγϣϯͰԿΛ࡞Δ͔ͷೝ͕ࣝἧͬͨ
    • type ΍ mutation ୯Ґͷ෼୲Ͱߴ౓ͳฒྻ։ൃ͕Մೳʹ

    View Slide

  45. Ըܙ(3) - ։ൃϞνϕʔγϣϯͷ޲্
    • ϦϦʔεޙͷ;Γ͔͑ΓͰʮϞνϕʔγϣϯΛߴ͘อͯͨʯͱͷ੠
    • ϕςϥϯ΋஌Βͳ͍͜ͱ͕ͨ͘͞Μ͋Γܹࢗతͳ೔ʑ
    • ແҋʹ৽ٕज़Λ࢖͏ͷ͸ةݥ͕ͩɺద੾ʹऔΓೖΕΔͷ͸ॏཁ
    • ྲྀߦ͍ͬͯΔٕज़͸৘ใ΋ଟ͍

    View Slide

  46. Ըܙ(4) - ΧΫϤϜͱͷίʔυϕʔεڞ༗
    • ೉͍͠෦෼΋ଟ͕͋ͬͨ֓͘Ͷ៉ྷʹ࣮૷Ͱ͖ͨ
    • ॲཧΛ෼͚͍ͨ৔߹ͷΈαʔϏε͝ͱͷϦκϧόΛఆٛ
    • αʔϏε͝ͱʹεΩʔϚ͕͋ΔͷͰϑΟʔϧυͷࠩ෼΋໌Β͔

    View Slide

  47. ݄ؒ3ԯPV΁ͷඋ͑

    View Slide

  48. ݄ؒ3ԯPVͷڪා
    • ࣄલͷݟੵ΋Γ͸ϐʔΫ࣌Ͱຖඵ350ϦΫΤετ
    • طଘαʔϏεͷϦϓϨΠε
    • ϦϦʔεͨ͠௚ޙ͔ΒେྔͷΞΫηε͕΍ͬͯ͘Δ
    • ҆શ͔ͭద੾ʹඋ͑Δඞཁ͕͋Δ
    • Πϯϑϥඅ༻͸༗ݶ

    View Slide

  49. ࡞ઓ
    • N+1໰୊ͷରࡦͳͲجຊతͳ͜ͱ͸΍Δ
    • Ωϟογϡ͕೉͍͠ͷΛͲ͏͢Δ͔
    • ΤϯυϙΠϯτ͕ /graphql ʹू໿͞Εͯ͠·͏
    • ΞϓϦέʔγϣϯϨΠϠʔͰͷΩϟογϡ͸࣮૷ίετ͕ߴ͍
    • ຊ౰ʹΩϟογϡ͕ඞཁ͔ෛՙࢼݧΛߦͬͯ໌Β͔ʹ͢Δ
    • ਪଌ͢ΔͳɺܭଌͤΑ

    View Slide

  50. ෛՙࢼݧ - 1ճ໨ͷ݁Ռ
    • Ωϟογϡͱؔ܎ͳ͘ϨΠςϯγ͕ҟৗʹѱ͍
    • ͔͠΋ϨεϙϯεαΠζʹൺྫͯ͠ϨΠςϯγ͕ѱԽ͍ͯ͠Δ…
    • ϓϩϑΝΠϦϯάͯ͠ௐࠪ
    • ࣮ߦ࣌ͷܕνΣοΫϥΠϒϥϦ͕ɺ֤ϑΟʔϧυͷϦκϧόݺͼग़
    ͠͝ͱʹݺ͹Ε͍ͯͨͷ͕ݪҼ
    • production؀ڥͰܕνΣοΫΛແޮʹ͢ΔύονΛ౰ͯͯ࠶τϥΠ

    View Slide

  51. ෛՙࢼݧ - 2ճ໨ͷ݁Ռ
    • ര଎
    • ฏۉϨΠςϯγ͸100msۙ͘
    • ݱ࣮తͳΠϯϑϥͰຊ൪૝ఆΛେ্͖͘ճΔෛՙʹ଱͑ΒΕͨ
    • ݁࿦: Ωϟογϡͳ͠ͰϦϦʔεޙͷෛՙʹ଱͑ΒΕΔ
    • ϦϦʔεޙɺෛՙͷ໰୊ͰαʔϏεμ΢ϯ͢Δ͜ͱ͸ͳ͔ͬͨ
    • ܭଌʹΑͬͯඞཁे෼ͳରࡦΛߦ͑ͨ

    View Slide

  52. ·ͱΊ

    View Slide

  53. • ຐ๏ͷiΒΜͲͰ͸ GraphQL Λ࠾༻͠·ͨ͠ɹ
    • ϞμϯͳϢʔβʔମݧͷ࣮ݱʹߩݙ
    • ։ൃޮ཰ɾ։ൃϞνϕʔγϣϯ͕޲্
    • ΧΫϤϜͱͷϩδοΫڞ༗ͱ͍͏೉͍͠ཁٻʹϚον
    • ύϑΥʔϚϯε໘΋ཱ྆
    • ͸ͯͳͱGraphQL
    • ϋϚΔͱ͜ΖͰ࠾༻͞ΕΔ͜ͱ͕૿͖͑ͯͨ
    • ͜Ε͔Β΋ϓϥΫςΟεͷ஝ੵɾڞ༗ΛਐΊ͍͖ͯ·͢ʂ

    View Slide