GraphQL๊ณผ Apollo ์—ฌ์„ฏ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - GraphQL Moduleํ™”์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•„์š” ๐Ÿ˜€

2023. 11. 20. 15:05ใ†Back-End ์ž‘์—…์‹ค/๊ธฐํƒ€ ์ฐธ๊ณ  ์ž๋ฃŒ

728x90
๋ฐ˜์‘ํ˜•

 

 

์นด์นด์˜คํŽ˜์ด | ๋งˆ์Œ ๋†“๊ณ  ๊ธˆ์œตํ•˜๋‹ค

์—ฌ๊ธฐ๋ฅผ ๋ˆŒ๋Ÿฌ ๋งํฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

qr.kakaopay.com

 

 

 

 

 

 

GraphQL๊ณผ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ์›น ์„œ๋น„์Šค:์„ค๊ณ„๋ถ€ํ„ฐ ๊ฐœ๋ฐœ·๋ฐฐํฌ๊นŒ์ง€ ๋”ฐ๋ผ ํ•˜๋ฉฐ ์™„์„ฑํ•˜๋Š” ์›น ํ’€

COUPANG

www.coupang.com

"์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค."

 




๐Ÿ—‚ ๋ชฉ์ฐจ

โœ… GraphQL๊ณผ Apollo ์ฒซ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - ๊ฐœ๋… ์ตํžˆ๊ธฐ
โœ… GraphQL๊ณผ Apollo ๋‘๋ฒˆ์งธ ์ด์•ผ๊ธฐ - REST API๋ž€?
โœ… GraphQL๊ณผ Apollo ์„ธ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - GraphQL์˜ ์ •๋ณด ์ฃผ๊ณ  ๋ฐ›๋Š” ๋ฐฉ์‹

โœ… GraphQL๊ณผ Apollo ๋„ค๋ฒˆ์งธ ์ด์•ผ๊ธฐ - Apollo๋ž€?
โœ… GraphQL๊ณผ Apollo ๋‹ค์„ฏ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - GraphQL์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•ด ๋ณด์•„์š” ๐Ÿ˜€
โœ… GraphQL๊ณผ Apollo ์—ฌ์„ฏ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - GraphQL Moduleํ™”์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•„์š” ๐Ÿ˜€
โœ… GraphQL๊ณผ Apollo ์ผ๊ณฑ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - GraphQL Data Type์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•„์š” ๐Ÿ˜€
โœ… GraphQL๊ณผ Apollo ์—ฌ๋Ÿ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - GraphQL Union๊ณผ Interface ๊ทธ๋ฆฌ๊ณ  ์ธ์ž์™€ ์ธํ’‹ ํƒ€์ž…์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•„์š” ๐Ÿ˜€
โœ… GraphQL๊ณผ Apollo ์•„ํ™‰๋ฒˆ์งธ ์ด์•ผ๊ธฐ - Java + Spring Boot์—์„œ GraphQL ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ - ์‹ค์Šต ํ™˜๊ฒฝ ๊ตฌ์„ฑ
โœ… GraphQL๊ณผ Apollo ์—ด๋ฒˆ์งธ ์ด์•ผ๊ธฐ - Java + Spring Boot์—์„œ GraphQL ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ - ์‹ค์Šต ํ•ด๋ณด๊ธฐ
โœ… GraphQL๊ณผ Apollo ์—ด ํ•œ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - TypeScript + Nest.js์—์„œ GraphQL ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ - ์‹ค์Šต ํ™˜๊ฒฝ ๊ตฌ์„ฑ
โœ… 
GraphQL๊ณผ Apollo ์—ด ๋‘๋ฒˆ์งธ ์ด์•ผ๊ธฐ - TypeScript + Nest.js์—์„œ GraphQL ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ - ์‹ค์Šต ํ™˜๊ฒฝ ํ…Œ์ŠคํŠธ
โœ… GraphQL๊ณผ Apollo ์—ด ์„ธ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - TypeScript + Nest์—์„œ GraphQL ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ - ์‹ค์Šต ํ•ด๋ณด๊ธฐ
โœ… 
GraphQL๊ณผ Apollo ์—ด ๋„ค๋ฒˆ์งธ ์ด์•ผ๊ธฐ - React์™€ Apollo Client
โœ… GraphQL๊ณผ Apollo ์—ด๋‹ค์„ฏ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - React์™€ Apollo Client - Query์™€ Mutation ์‚ฌ์šฉํ•˜์—ฌ ์›น ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ
โœ… GraphQL๊ณผ Apollo ์—ด ์—ฌ์„ฏ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - Kotlin + Spring Boot์—์„œ GraphQL ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ - ์‹ค์Šต ํ™˜๊ฒฝ ๊ตฌ์„ฑ
โœ… GraphQL๊ณผ Apollo ์—ด ์ผ๊ณฑ๋ฒˆ์งธ ์ด์•ผ๊ธฐ - Kotlin + Spring Boot์—์„œ GraphQL ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ - ์‹ค์Šต ํ•ด๋ณด๊ธฐ


๐Ÿค” ๋‚ด๊ฐ€ ๋งŒ๋‚œ ๋ฌธ์ œ

โš ๏ธ [Nest.js] TypeORM Table ๊ด€๊ณ„๊ฐ€ ๋งบ์–ด์กŒ์„ ๋•Œ, Seeding (feat. Migration)
โš ๏ธ [Spring Boot 3.0] Could not resolve org.springframework.boot:spring-boot-gradle-plugin
โš ๏ธ [Spring Boot 3] Spring Doc(Swagger) White Label Error


๐Ÿ“‹ ๋ถ€๋ก

๐Ÿ” [Nest.js] ์ดˆ๊ธฐ ํ™˜๊ฒฝ ๊ตฌ์„ฑ (feat. TypeORM, QueryBuilder, GraphQL, Apollo)
๐Ÿ” [SOLID][Nest.js][Java + Spring] Interface๋ฅผ ํ™œ์šฉํ•œ ๊ฒฐํ•ฉ๋„ ๋ถ„๋ฆฌ (Interface๋ฅผ ์ด์šฉํ•œ Dependency Injection - DI)

 

 

 

 

 

๐Ÿš€ GraphQL๊ณผ Apollo ์—ฌ์„ฏ๋ฒˆ์งธ ์ด์•ผ๊ธฐ

    ๐Ÿ”ฝ GraphQL Moduleํ™”์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•„์š” ๐Ÿ˜€

        ๐Ÿ“ฆ ๊ฐœ์š”

์ง€๋‚œ ๋‹ค์„ฏ๋ฒˆ์งธ ์ด์•ผ๊ธฐ์—์„œ GraphQL์„ ์ด์šฉํ•˜์—ฌ ์„œ๋ฒ„๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ, ๊ธฐ๋ณธ์ ์ธ Query์™€ Mutation์„ ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑํ•ด์•ผ ํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์–ด์š”.

์ด๋ฒˆ ์—ฌ์„ฏ๋ฒˆ์งธ ์ด์•ผ๊ธฐ์—์„œ๋Š” GraphQL์„ ์ด์šฉํ•˜์—ฌ ์„œ๋ฒ„ ๊ตฌ์„ฑ ์‹œ ๋ณด๋‹ค ์ž์„ธํ•œ ๊ธฐ๋Šฅ๊ณผ ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•ด ๋ณด๋ ค๊ณ  ํ•ด์š”.

 

728x90

 

 

 

 

 

    ๐Ÿ”ฝ  ์„œ๋ฒ„ ๊ตฌ์„ฑ ์š”์†Œ ๋ชจ๋“ˆํ™”

        ๐Ÿ“ฆ ์‚ดํŽด๋ณด๊ธฐ

const database = require('./database');
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
  type Query {
    teams: [Team],
    team(id: Int): Team,
    equipments: [Equipment],
    supplies: [Supply]
  }
  
  type Mutation {
    insertEquipment (
        id: String,
        used_by: String,
        count: Int,
        new_or_used: String
    ): Equipment
    
    updateEquipment (
        id: String,
        used_by: String,
        count: Int,
        new_or_used: String
    ): Equipment
    
    deleteEquipment(id: String): Equipment 
  }
   
  type Team {
    id: Int,
    manager: String,
    office: String,
    extension_number: String,
    mascot: String,
    cleaning_duty: String,
    project: String,
    supplies: [Supply]
  }
  
  type Equipment {
    id: String
    used_by: String
    count: Int
    new_or_used: String
  }
  
  type Supply {
    id: String,
    team: Int
  }
`;
const resolvers = {
    Query: {
        teams: () => database.teams.map((team) => {
            team.supplies = database.supplies.filter((supply) => {
                return supply.team === team.id;
            });

            return team;
        }),

        team: (parent, args, context, info) =>
            database.teams.filter((team) => {
                return team.id === args.id;
            }) [0],

        equipments: () => database.equipments,
        supplies: () => database.supplies
    },

    Mutation: {
        insertEquipment: (parent, args, context, info) => {
            database.equipments.push(args);
            return args;
        },

        updateEquipment: (parent, args, context, info) => {
            return database.equipments.filter((equipment) => {
                return equipment.id === args.id;
            }).map((equipment) => {
                Object.assign(equipment, args);
                return equipment;
            }) [0];
        },

        deleteEquipment: (parent, args, context, info) => {
            const deleted = database.equipments.filter((equipment) => {
                return equipment.id === args.id
            })[0]

            database.equipments = database.equipments.filter((equipment) => {
                return equipment.id !== args.id
            })

            return deleted
        }
    }
}

const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
    console.log(`๐Ÿš€ ์ฃผ๋‹ˆ์˜ Apollo ์‹ค์Šต Server ์ค€๋น„ ์™„๋ฃŒ! ${url}`);
})

 

์œ„์˜ ์ฝ”๋“œ๋Š” ์ง€๋‚œ ์‹œ๊ฐ„๊นŒ์ง€ ์‹ค์Šตํ•ด ๋ณด์•˜๋˜ index.js์—์š”.
๋ณด์‹œ๋‹ค์‹œํ”ผ GraphQL์˜ ๋ชจ๋“  Type ์ฆ‰, Schema์™€ Resolver๊ฐ€ ๋ชจ๋‘ ํ•œ ํŒŒ์ผ์— ์žˆ๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์–ด์š”.

๊ฐ„๋‹จํ•œ ์‹ค์Šต์ด๋ผ๋ฉด ํฌ๊ฒŒ ๋ฌธ์ œ๋  ๊ฑด ์—†์ง€๋งŒ, ์‹ค์ œ ์„œ๋น„์Šค๋ฅผ ๊ตฌ๋™ํ•˜๋Š” ์„œ๋ฒ„๋ผ๋ฉด ์ด์ฒ˜๋Ÿผ ๊ตฌ์„ฑํ•˜๋ฉด ์œ ์ง€๋ณด์ˆ˜ ํ•˜๊ธฐ ๋งค์šฐ ํž˜๋“ค๊ฒŒ ๋ ๊ฑฐ์—์š”.

๊ทธ๋ž˜์„œ ์ด๋ฒˆ์—๋Š” ์ด ๋‚ด์šฉ๋“ค์„ ์ ์ ˆํ•˜๊ฒŒ ๋‚˜๋ˆ„์–ด Moduleํ™” ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•ด ๋ณด๋ ค๊ณ  ํ•ด์š”.

์‹ค์Šต์—์„œ ์‚ฌ์šฉํ•  ์ฝ”๋“œ๋Š” ์ด ๊ณณ์— ์žˆ์–ด์š”.

 

3-1-server-modularized · master · Channel Yalco / yalco-inflearn-graphql-apollo · GitLab

GitLab.com

gitlab.com

 

ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๊ธฐ๋ณธ์ ์ธ Moduleํ™”๊ฐ€ ์ด๋ฏธ ์ง„ํ–‰๋œ ํ”„๋กœ์ ํŠธ์—์š”.

index.js

๋ฐ˜์‘ํ˜•


์ตœ์ดˆ ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์˜ index.js๋ฅผ ์‚ดํŽด ๋ณผ๊ฒŒ์š”.

์ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” 18๋ฒˆ์งธ ์ค„์— TypeDef์™€ Resolver๋ฅผ ๋ฐ›์•„ new ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ์ž์˜ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„ server ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋ผ์š”.
 

const typeDefs = gql`
  type Query {
    teams: [Team],
    team(id: Int): Team,
    equipments: [Equipment],
    supplies: [Supply]
  }
  
  type Mutation {
    insertEquipment (
        id: String,
        used_by: String,
        count: Int,
        new_or_used: String
    ): Equipment
    
    updateEquipment (
        id: String,
        used_by: String,
        count: Int,
        new_or_used: String
    ): Equipment
    
    deleteEquipment(id: String): Equipment 
  }
   
  type Team {
    id: Int,
    manager: String,
    office: String,
    extension_number: String,
    mascot: String,
    cleaning_duty: String,
    project: String,
    supplies: [Supply]
  }
  
  type Equipment {
    id: String
    used_by: String
    count: Int
    new_or_used: String
  }
  
  type Supply {
    id: String,
    team: Int
  }
`;


์ง€๋‚œ ์‹œ๊ฐ„์— ์‹ค์Šตํ–ˆ๋˜ index.js๋ฅผ ๋ณด๋ฉด ์œ„์™€ ๊ฐ™์ด typeDef ๋ณ€์ˆ˜๊ฐ€ ์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑ๋œ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ ,

const resolvers = {
    Query: {
        teams: () => database.teams.map((team) => {
            team.supplies = database.supplies.filter((supply) => {
                return supply.team === team.id;
            });

            return team;
        }),

        team: (parent, args, context, info) =>
            database.teams.filter((team) => {
                return team.id === args.id;
            }) [0],

        equipments: () => database.equipments,
        supplies: () => database.supplies
    },

    Mutation: {
        insertEquipment: (parent, args, context, info) => {
            database.equipments.push(args);
            return args;
        },

        updateEquipment: (parent, args, context, info) => {
            return database.equipments.filter((equipment) => {
                return equipment.id === args.id;
            }).map((equipment) => {
                Object.assign(equipment, args);
                return equipment;
            }) [0];
        },

        deleteEquipment: (parent, args, context, info) => {
            const deleted = database.equipments.filter((equipment) => {
                return equipment.id === args.id
            })[0]

            database.equipments = database.equipments.filter((equipment) => {
                return equipment.id !== args.id
            })

            return deleted
        }
    }
}


resolver๋Š” ์œ„์™€ ๊ฐ™์ด ๋งŒ๋“ค์–ด์ง„๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ด๋ ‡๊ฒŒ ๋งŒ๋“  ๊ฒƒ์€ ์„œ๋ฒ„ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ๋•Œ, new ์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•˜์—ฌ ์ƒ์„ฑ์ž ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ๋„ฃ์–ด์ฃผ๊ณ , ์„œ๋ฒ„ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋ผ์š”.

์ถœ์ฒ˜: https://www.apollographql.com/docs/apollo-server/api/apollo-server/


Apollo ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด ์œ„์™€ ๊ฐ™์ด Server์— ๋Œ€ํ•œ ๋ฉ”๋‰ด์–ผ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ , constructor(์ƒ์„ฑ์ž)์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ถœ์ฒ˜: https://www.apollographql.com/docs/apollo-server/api/apollo-server/


ํ•ด๋‹น ๋ฉ”๋‰ด์–ผ์„ ๋ณด๋ฉด ์œ„์™€ ๊ฐ™์ด typeDefs์™€ resolvers๋ฅผ ํ•„์ˆ˜ Option(์˜ต์…˜)์œผ๋กœ ๋ฐ›์•„์•ผ ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

๋จผ์ € typeDefs ์„ค๋ช…์„ ํ™•์ธํ•ด๋ณด๋ฉด ๋‹จ์ˆ˜์˜ DocumentNode ๋˜๋Š” Array<DocumentNode>๋ผ๊ณ  ๋˜์–ด ์žˆ๋Š”๋ฐ, 

const typeDefs = gql`
  type Query {
    teams: [Team],
    team(id: Int): Team,
    equipments: [Equipment],
    supplies: [Supply]
  }
  
  type Mutation {
    insertEquipment (
        id: String,
        used_by: String,
        count: Int,
        new_or_used: String
    ): Equipment
    
    updateEquipment (
        id: String,
        used_by: String,
        count: Int,
        new_or_used: String
    ): Equipment
    
    deleteEquipment(id: String): Equipment 
  }
   
  type Team {
    id: Int,
    manager: String,
    office: String,
    extension_number: String,
    mascot: String,
    cleaning_duty: String,
    project: String,
    supplies: [Supply]
  }
  
  type Equipment {
    id: String
    used_by: String
    count: Int
    new_or_used: String
  }
  
  type Supply {
    id: String,
    team: Int
  }
`;


์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑํ•œ ๊ฒƒ์ด ๋‹จ์ˆ˜ํ˜•์ธ DocumentNode ํ˜•ํƒœ์ด๊ณ , ์ด๋ฅผ ๋ฐฐ์—ด๋กœ๋„ ๋งŒ๋“ค์–ด์ค„ ์ˆ˜ ์žˆ๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

Resolvers ์—ญ์‹œ ๋งˆ์ฐฌ๊ฐ€์ง€์—์š”.
Object ์ฆ‰, ๊ฐ์ฒด ํ•˜๋‚˜ ํ˜น์€ Array (๋ฐฐ์—ด)์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋‚˜์™€ ์žˆ์–ด์š”.


์ตœ์ดˆ ํ”„๋กœ์ ํŠธ์˜ typedefs-resolvers Directory(๋””๋ ‰ํ„ฐ๋ฆฌ)๋ฅผ ์‚ดํŽด๋ณด๋ฉด _queries.js, _mutaions.js, equipments.js๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด ํ”„๋กœ์ ํŠธ๋Š” equipments์— ๋Œ€ํ•œ Query์™€ Mutaion๋งŒ ์ž‘์„ฑ๋œ ํ”„๋กœ์ ํŠธ์—์š”.

equipments.js


์œ„ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด ๋ณผ๊ฒŒ์š”.
equipment์— ๋Œ€ํ•œ Schema(์Šคํ‚ค๋งˆ)๊ฐ€ 4 ~ 11๋ฒˆ์งธ ์ค„์— ๋ช…์‹œ๋˜์–ด typeDefs ๋ณ€์ˆ˜์— ๋“ค์–ด๊ฐ„ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

12 ~ 19๋ฒˆ์งธ ์ค„์—๋Š” resolvers ๋ณ€์ˆ˜์— ์ด ๋‘ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” Query์™€ Mutaion์ด ํ•œ ๊ฐ์ฒด๋กœ ์„ ์–ธ๋œ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

๊ทธ๋ฆฌ๊ณ , typeDefs์™€ resovers ๋ณ€์ˆ˜๋ฅผ 21 ~ 24๋ฒˆ์งธ ์ค„์— Module๋กœ exports(๋‚ด๋ณด๋‚ด๊ธฐ) ํ•˜๊ณ  ์žˆ์–ด์š”.

index.js


index.js์˜ 6๋ฒˆ์งธ ์ค„์— ๋ณด๋ฉด equipments.js 21๋ฒˆ์งธ ์ค„์— ๋‚ด๋ณด๋‚ด๊ธฐํ•œ Module์„ 6๋ฒˆ์งธ ์ค„์—์„œ ๋ถˆ๋Ÿฌ์™€ equipments ์ƒ์ˆ˜ ๋ณ€์ˆ˜์— ๋„ฃ๊ณ  ์žˆ์–ด์š”.

index.js


๊ทธ๋Ÿฐ ๋’ค 11๋ฒˆ์งธ ์ค„์— ํ•ด๋‹น ๋ณ€์ˆ˜์—์„œ typeDefs์— ๋Œ€ํ•œ ๊ฐ’์„ 8๋ฒˆ์งธ ์ค„์— ์„ ์–ธ๋œ typeDefs ์ƒ์ˆ˜ ๋ฐฐ์—ด ๋ณ€์ˆ˜์— ๋‹ด๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด์—์š”.

๊ทธ๋ฆฌ๊ณ , 14๋ฒˆ์งธ ์ค„ resolvers ์ƒ์ˆ˜ ๋ฐฐ์—ด ๋ณ€์ˆ˜์—๋Š” equipments.js 21๋ฒˆ์งธ ์ค„์— ๋‚ด๋ณด๋‚ด๊ธฐํ•œ Module ์ค‘ resolvers๋ฅผ ๋ถˆ๋Ÿฌ์™€ ๋‹ด๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด์—์š”.

๊ณต์‹ ๋ฌธ์„œ์—์„œ ํ™•์ธํ–ˆ๋“ฏ ๋ฐฐ์—ด๋กœ ๋„ฃ์–ด์ค€ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.



_queries.js


์œ„ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด ๋ณด๋ฉด equipments์— Root Query Type์ด ๋“ค์–ด๊ฐ€๊ณ ,

_mutaion.js


Mutation์—๋Š” Root Mutaion Type๋“ค์ด Document Node๋กœ ์ง€์ •๋˜์–ด ์žˆ๊ณ , ์ด๋“ค ์—ญ์‹œ Module๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ ๋˜์–ด์ ธ์„œ 

 

index.js


์ด ๊ณณ์— 4๋ฒˆ์งธ ์ค„๊ณผ 5๋ฒˆ์งธ ์ค„์— ๊ฐ๊ฐ ์ƒ์ˆ˜ ๋ณ€์ˆ˜์— ๋‹ด์•„์ค€๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

ํ•ด๋‹น ํ”„๋กœ์ ํŠธ๋ฅผ ๊ตฌ๋™ํ•˜๊ธฐ ์œ„ํ•ด ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ๋จผ์ € ํ„ฐ๋ฏธ๋„์— ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

npm install


๊ทธ๋Ÿฐ ๋’ค ์•„๋ž˜ ๋ช…๋ น์–ด๋กœ ๊ธฐ๋™ ์‹œ์ผœ์ค„๊ฒŒ์š”.

npm start

 

Playgroud


http://localhost:4000/ ์„ ๋ธŒ๋ผ์šฐ์ €์— ์ž…๋ ฅํ•˜๋ฉด ์œ„์™€ ๊ฐ™์ด Playgroud๊ฐ€ ์—ด๋ฆฌ๊ฒŒ ๋˜๊ณ , ์œ„์™€ ๊ฐ™์ด Query๋ฅผ ๋‚ ๋ ค๋ณด๋ฉด ์ •์ƒ์ ์œผ๋กœ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ›๋Š”๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

Playgroud


๋˜ํ•œ, Playgroud์—์„œ ์˜ค๋ฅธ์ชฝ์— SCHEMA Tab์„ ํด๋ฆญํ•˜๋ฉด ์œ„์™€ ๊ฐ™์ด ์Šคํ‚ค๋งˆ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ ,

Playgroud


DOCS Tab์„ ํ†ตํ•ด ๊ด€๋ จ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜๋„ ์žˆ์–ด์š”.

๊ณต๋ถ€๋ฅผ ํ•˜๋ฉด์„œ ๋Š๋‚€์ ์€ Postman ๋ณด๋‹ค๋Š” Swagger์™€ ์œ ์‚ฌํ•˜๋‹ค๊ณ  ๋Š๊ปด์กŒ์–ด์š”.

 

 

        ๐Ÿ“ฆ dbWorks.js ์‚ดํŽด๋ณด๊ธฐ

const database = require('./database.js')

const dataFiltered = (which, args) => {
    let result = database[which].filter((item) => {
        // ์กฐ๊ฑด์ธ์ž๊ฐ€ ์—†๊ฑฐ๋‚˜, ํŽ˜์ด์ง• ๊ด€๋ จ ์ธ์ž๊ฑฐ๋‚˜
        // ๋ชจ๋“  ์š”์†Œ๊ฐ€ ์•„์ดํ…œ๊ณผ ๋ชจ๋‘ ์ผ์น˜ํ•˜๋ฉด ํ†ต๊ณผ
        return !args || Object.keys(args).reduce((a, b) => {
            return a && (
                ['page', 'per_page'].includes(b) ||
                item[b] == args[b]
            )
        }, true)
    })

    // ํŽ˜์ด์ง•
    if (args.page && args.per_page) {
        result = result.slice(
            (args.page - 1) * args.per_page, 
            args.page * args.per_page)
    }

    return result
}

const dbWorks = {
    deleteItem: (which, args) => {
        const deleted = database[which].filter((item) => {
            return item.id == args.id
        })[0]
        database[which] = database[which].filter((item) => {
            return item.id != args.id
        })
        return deleted
    },

    getTeams: (args) => dataFiltered('teams', args)
        .map((team) => {
            team.members = dbWorks.getPeople({team: team.id})
            return team
        }),
    postTeam: (args) => {
        const newTeam = {
            id: database.teams.map((team) => {
                return Number(team.id)
            }).reduce((a, b) => {
                return Math.max(a, b)
            }, 0) + 1,
            ...args.input
        }
        database.teams.push(newTeam)
        return newTeam
    },
    editTeam: (args) => {
        return database.teams.filter((team) => {
            return team.id == args.id
        }).map((team) => {
            Object.assign(team, args.input)
            return team 
        })[0]
    },

    getPeople: (args) => dataFiltered('people', args) 
        .map((person) => {
            person.tools = [
                ...dbWorks.getEquipments({used_by: person.role}),
                ...dbWorks.getSoftwares({used_by: person.role})
            ]
            person.givens = [
                ...dbWorks.getEquipments({used_by: person.role}),
                ...dbWorks.getSupplies({team: person.team})
            ]
            return person
        }),
    postPerson: (args) => {
        const newPerson = {
            id: database.people.map((person) => {
                return Number(person.id)
            }).reduce((a, b) => {
                return Math.max(a, b)
            }, 0) + 1,
            ...args.input
        }
        database.people.push(newPerson)
        return newPerson
    },
    editPerson: (args) => {
        return database.people.filter((person) => {
            return person.id == args.id
        }).map((person) => {
            Object.assign(person, args.input)
            return person 
        })[0]
    },

    getRoles: (args) => dataFiltered('roles', args)
        .map((role) => {
            role.members = dbWorks.getPeople({role: role.id})
            role.equipments = dbWorks.getEquipments({used_by: role.id})
            role.softwares = dbWorks.getSoftwares({used_by: role.id})
            return role
        }),

    getEquipments: (args) => dataFiltered('equipments', args),
    postEquipment: (args) => {
        database.equipments.push(args)
        return args
    },
    increaseEquipment: (args) =>{
        return database.equipments.filter((equipment) => {
            return equipment.id == args.id
        }).map((equipment) => {
            equipment.count += 1 
            return equipment
        })[0]

    },

    getSoftwares: (args) => dataFiltered('softwares', args),

    getSupplies: (args) => dataFiltered('supplies', args),
}

module.exports = dbWorks


dbWorks.js๋Š” ์œ„์™€ ๊ฐ™์ด ์ด๋ค„์ ธ ์žˆ๋Š”๋ฐ, ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๋ ค๊ณ  ํ•ด์š”.
์ผ๋‹จ ํ•ด๋‹น ์ฝ”๋“œ๋Š” Resolver์— ์‚ฌ์šฉํ•  ๊ธฐ๋Šฅ๋“ค์„ Moduleํ™” ํ•˜๋Š” ์ฝ”๋“œ์—์š”.

GraphQL์„ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์‚ฌ์šฉํ•  Resolver๋ฅผ ์—ฌ๋Ÿฌ๊ฐ€์ง€ Mock ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ๋Šฅ์„ ํ•จ์ˆ˜๋“ค๋กœ ๊ตฌํ˜„ํ•ด ๋†“์€ ๊ฒƒ์ด์—์š”.
์ด ๋‚ด์šฉ์€ ์‹ค์Šต์„ ์œ„ํ•œ ๋‚ด์šฉ์ด๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋‹ค๋ฃจ๋Š” ์ฝ”๋“œ๋“ค์ด ๋“ค์–ด๊ฐ€์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์žŠ์œผ๋ฉด ์•ˆ๋˜๋Š” ๊ฒƒ์ด์—์š”.




 

 

        ๐Ÿ“ฆ supply Module ์ถ”๊ฐ€

ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์œผ๋‹ˆ ์‹ค์ œ Module์„ ๊ตฌํ˜„ํ•ด ๋ณผ๊ฒŒ์š”.
equipments.js๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“  ๊ฒƒ์ฒ˜๋Ÿผ supplies.js ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ๋ณผ๊ฑฐ์—์š”.

supplies.js


์œ„์™€ ๊ฐ™์ด ๋งŒ๋“ค์–ด ์ค„๊ฑฐ์—์š”.

supplies.js

 

const { gql } = require('apollo-server')
const dbWorks = require('../dbWorks')

const typeDefs = gql`
    type Supply {
        id: String
        team: Int
    }
`
const resolvers = {
    Query: {
        supplies: (parent, args) => dbWorks.getSupplies(args),
    },
    Mutation: {
        deleteSupply: (parent, args) => dbWorks.deleteItem('supplies', args),
    }
}

module.exports = {
    typeDefs: typeDefs,
    resolvers: resolvers
}


์ฝ”๋“œ๋Š” ์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด ์ฃผ์—ˆ์–ด์š”.

์ด์ œ ์œ„ ์ฝ”๋“œ์—์„œ ์ž‘์„ฑํ•œ resolver๋ฅผ Root Query Type๊ณผ Root Mutaion Type์—๋„ ์ž‘์„ฑํ•ด ์ฃผ์–ด์•ผ ํ•ด์š”.

_queries.js


์œ„์™€ ๊ฐ™์ด 6๋ฒˆ์งธ ์ค„์— Root Query Type์— supplies๋Š” Supply ๊ฐ์ฒด ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ์„ ์–ธํ•ด ์ฃผ์—ˆ์–ด์š”.


์—ฌ๊ธฐ๊นŒ์ง€๋งŒ ํ•˜๋ฉด ํ„ฐ๋ฏธ๋„์€ ๋˜ ๋‚œ๋ฆฌ๊ฐ€ ๋‚  ๊ฑฐ์—์š”.
์ด์œ ๋Š” supplies.js File์ด Import ๋˜์ง€ ์•Š์•„ ์ •๋ณด๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ด์—์š”.

_mutaions.js


์ผ๋‹จ, ์œ„์™€ ๊ฐ™์ด 6๋ฒˆ์งธ ์ค„์— Root Mutaion Type์— Supply๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•ด ์ฃผ์—ˆ์–ด์š”.


index.js


index.js์— ์œ„์™€ ๊ฐ™์ด 7๋ฒˆ์งธ ์ค„์— supplies๋ฅผ ๋ฐ›์•„์˜ค๋„๋ก ์ฒ˜๋ฆฌํ•˜๊ณ , typeDefs์™€ resolvers๋ฅผ ๋ฐ›์•„ ๋ฐฐ์—ด์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.


์ด๋ ‡๊ฒŒ ํ•ด ์ฃผ๋ฉด ํ„ฐ๋ฏธ๋„์€ ๋‹ค์‹œ ํ‰ํ™”๋ฅผ ์ฐพ์€ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

Playgroud - SCHEMA

 

Playgroud - DOCS


Playgroud์—์„œ ํ™•์ธํ•˜๋ฉด ์œ„์™€ ๊ฐ™์ด ์œ„์—์„œ ์ถ”๊ฐ€ํ•œ ๋‚ด์šฉ์ด ํ™•์ธ๋˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.



Query๋ฅผ ๋‚ ๋ ค๋ณด๋‹ˆ ์ •์ƒ์ ์œผ๋กœ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ˜ํ™˜ ๋ฐ›๋Š”๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

 

 

 

GraphQL๊ณผ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ์›น ์„œ๋น„์Šค:์„ค๊ณ„๋ถ€ํ„ฐ ๊ฐœ๋ฐœ·๋ฐฐํฌ๊นŒ์ง€ ๋”ฐ๋ผ ํ•˜๋ฉฐ ์™„์„ฑํ•˜๋Š” ์›น ํ’€

COUPANG

www.coupang.com

"์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค."

 

 

 

๐Ÿง ์ฐธ๊ณ  ์ž๋ฃŒ

ํ•ด๋‹น ๋‚ด์šฉ์€ ์•„๋ž˜ ๊ฐ•์˜๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž„์„ ์•Œ๋ ค ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

 

[๋ฌด๋ฃŒ] ์–„ํŒํ•œ GraphQL๊ณผ Apollo - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

โšก ์งง๊ณ  ๊ตต์€ ์ „์ฒด 90๋ถ„ ๊ฐ•์ขŒ! ์‚ฌ์ดํŠธ์˜ ์ฝ”๋“œ๋“ค์„ ๋ณต๋ถ™ํ•˜๋ฉฐ ๋น ๋ฅด๊ฒŒ GraphQL์„ ๋ฐฐ์šฐ๊ณ  ์•„ํด๋กœ ์‚ฌ์šฉ๋ฒ•์„ ์ตํžˆ์„ธ์š”., โฑ ์—ฌ๋Ÿฌ๋ถ„์˜ ์‹œ๊ฐ„์€ ์†Œ์ค‘ํ•ฉ๋‹ˆ๋‹ค. [์‚ฌ์ง„] ๐ŸŽข  GraphQL์ด ๋ญ”๊ฐ€์š”? ์„œ๋น„์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š”

www.inflearn.com

 

 

 

 

 

์นด์นด์˜คํŽ˜์ด | ๋งˆ์Œ ๋†“๊ณ  ๊ธˆ์œตํ•˜๋‹ค

์—ฌ๊ธฐ๋ฅผ ๋ˆŒ๋Ÿฌ ๋งํฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

qr.kakaopay.com

 

 

 

 

728x90
๋ฐ˜์‘ํ˜•