graphQL ๋ง›์„ ๋ณด์ž๐Ÿ‘ป

katanazeroยท2020๋…„ 5์›” 11์ผ
0

tool

๋ชฉ๋ก ๋ณด๊ธฐ
6/7

์š”์ฆ˜ ์ฑ„์šฉ๊ณต๊ณ ๋ฅผ ๋ณด๋ฉด, ๊ธฐ์ˆ ์Šคํƒ(skills)์— graphQL ์ด๋ผ๋Š” ํ‚ค์›Œ๋“œ๋ฅผ ๋ณด์•˜์„ ๊ฑฐ๋‹ค.
์ผ๋‹จ, graphQL ์ด ๋ญ”์ง€๋ถ€ํ„ฐ ์•Œ์•„๋ณด์ž!

graphQL

  • ํŽ˜์ด์Šค๋ถ์—์„œ ์ œ์ž‘ํ•œ ์งˆ์˜์–ด(graph query language)
  • REST API ์— ํ•œ๊ณ„๋ฅผ ๊ทน๋ณตํ•˜๊ณ ์ž ์ œ์ž‘
  • over-fetching, under-fetching ์„ ๊ฐœ์„ ํ•˜๊ณ ์ž ์ œ์ž‘

    over-fetching : API ์กฐํšŒ๋ฅผ ํ•˜๋ฉด, ํ•„์š”์—†๋Š” ๋ฐ์ดํ„ฐ๊นŒ์ง€ ๊ฐ€์ง€๊ณ ์˜ด
    under-fetching : API ์กฐํšŒ๋ฅผ ํ•˜๋ฉด, ์ถฉ๋ถ„ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์–ด์„œ ๋‹ค๋ฅธ API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํ•ฉํ•จ

  • graphQL ์€ ํ†ต์ƒ 1๊ฐœ์˜ endpoint ๋งŒ ์กด์žฌ (๋ฐ์ดํ„ฐ๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ query ์ž‘์„ฑํ•˜์—ฌ ์กฐํšŒ)

graphQL ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ? ํ”„๋ ˆ์ž„์›Œํฌ?

  • NO! ๐Ÿ˜ 
  • graphQL ์€ REST ์™€ ๊ฐ™์€ API ๋””์ž์ธ์— ๋Œ€ํ•œ ๊ฐœ๋… ๋ฐ ์‚ฌ์ƒ์ด๋‹ค. (REST ๋Š” ROA ๋ฅผ ๋”ฐ๋ฅธ๋‹ค)
  • ๊ทธ๋ž˜์„œ graphQL ๊ตฌํ˜„์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.(graphql-yoga, graphql-express ๋“ฑ)

graphQL ์ด๋ž‘ ๊ธฐ์กด REST ๋ž‘ ๋ฌด์—‡์ด ๋‹ค๋ฅธ๊ฐ€?

  • endpoint
  • REST ๋Š” http method ์— ๋”ฐ๋ผ URI ๊ฐ€ ์กด์žฌํ–ˆ๋˜ ๋ฐ˜๋ฉด์—, graphQL ์€ post ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉ(query, mutation)

graphQL ์Šคํ‚ค๋งˆ, ๋ฆฌ์กธ๋ฒ„ ์ƒ์„ฑ


// ์Šคํ‚ค๋งˆ

type Query {
  info: String!
  feed: [Link!]!
  person: [Person!]!
}

type Mutation {
  createLink(url: String!, description: String!): Link!

  updateLink(targetId: ID!, url: String, description: String): Link

  deleteLink(targetId: ID!): LinkResult

  createPerson(userName: String, userPassword: String): Person
}

type LinkResult {
    msg: String!
}

type Link {
    id: ID!
    description: String!
    url: String!
 }

type Person {
    user_id : ID!
    user_name : String
    user_password : String
}


// resolver

const resolvers = {

    // ์กฐํšŒ(R)
    Query: {
        info: () => `info ๋ฅผ ์กฐํšŒํ•˜๋‹ค?`,
        feed: () => links,
        person: async () => {
            const result = await new Promise(((resolve, reject) => {
                const query = `SELECT * FROM person`;
                db.all(query, [], (err, rows) => {

                    if (err) {
                        console.log(err);
                        reject({err});
                        return false;
                    }

                    resolve([...rows]);
                });

            }));

            if (result.hasOwnProperty('err')) {
                return [];
            } else {
                return [...result];
            }
        }
    },

    // ์ƒ์„ฑ, ๋ณ€๊ฒฝ, ์‚ญ์ œ(CUD)
    Mutation: {
        createLink: (parent, args) => {
            const link = {
                id: `link-${idCount++}`,
                description: args.description,
                url: args.url,
            };
            links.push(link);
            return link;
        },

        updateLink: (parent, {targetId, description, url}) => {
            const targetLink = links.find(link => link.id === targetId);
            targetLink.description = description ? description : '';
            targetLink.url = url ? url : '';
            return targetLink;
        },

        deleteLink: (parent, {targetId}) => {
            const targetLinkIndex = links.findIndex(link => link.id === targetId);
            if (targetLinkIndex != -1) {
                links.splice(targetLinkIndex, 1);
                return {
                    msg: 'success'
                }
            } else {
                return {
                    msg: 'undefined'
                }
            }

        },

        createPerson: async (parent, {userName, userPassword}) => {
            // https://www.sqlitetutorial.net/sqlite-nodejs/insert/

            const result = await new Promise((resolve, reject) => {
                const query = `INSERT INTO person (user_name, user_password) VALUES (?,?)`;
                const selectQuery = `SELECT * FROM person WHERE user_id=?`;
                db.run(query, [userName, userPassword], function (err) {
                    // success ์‹œ, this ์— lastID, changes ์†์„ฑ์ด ์žˆ๋‹ค.
                    if (err) {
                        console.log(`err : ${err}`);
                        reject({err});
                        return false;
                    }

                    db.get(selectQuery, [this.lastID], function (err, row) {
                        if (err) {
                            console.log(`err : ${err}`);
                            reject({err});
                            return false;
                        }

                        resolve({...row});
                    });
                });

            });

            if (result.hasOwnProperty('err')) {
                return {
                    user_id: -1,
                    user_name: 'error',
                    user_password: ''
                }
            } else {
                return {...result};
            }
        }
    },

    // Link: {
    //     id: parent => parent.id,
    //     description: parent => parent.description,
    //     url: parent => parent.url
    // }
};
  • ์Šคํ‚ค๋งˆ๋Š” ์Šค์นผ๋ผํƒ€์ž…๊ณผ ๊ฐ์ฒดํƒ€์ž… ํ•„๋“œ๋ฅผ ์ •ํ•œ๋‹ค.
  • ๋ฆฌ์กธ๋ฒ„๋Š” ์Šคํ‚ค๋งˆ๊ฐ€ ์‹ค์ œ ์–ด๋–ค๋™์ž‘์„ ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•œ๋‹ค.

์šฉ์–ด์ •์˜

  • Query : ์ฝ๊ธฐ์ž‘์—…์„ ํ•˜๋Š” GraphQL๋ฌธ์„ ์˜๋ฏธํ•œ๋‹ค.(R)
  • field : query์— ์žˆ๋Š” ๊ฐ’(์†์„ฑ)
  • Mutation : ๋ฐ์ดํ„ฐ ์ˆ˜์ •์ž‘์—…์„ ํ•˜๋Š” GraphQL๋ฌธ์„ ์˜๋ฏธํ•œ๋‹ค.(CUD)
  • schema : Query์™€ Mutation์˜ retrun type๊ณผ arguments type ๋ฐ custom type, custom interface, enum type ๋“ฑ๊ณผ input value์˜ default๊ฐ’ ๋“ฑ์„ ์ •์˜ํ•œ ์ฝ”๋“œ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
  • resolver : schema์—์„œ ์ •์˜๋œ Query์™€ Mutation์˜ ๊ตฌ์กฐ์— ๋งž์ถ”์–ด retrun type๊ณผ arguments type์— ๋งž์ถ”์–ด ์„ค์ •ํ•œ ์ฝ”๋“œ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

์˜ˆ์ œ์ฝ”๋“œ

profile
developer life started : 2016.06.07 ~ ํ”ํ•œ Front-end ๊ฐœ๋ฐœ์ž

0๊ฐœ์˜ ๋Œ“๊ธ€