instaclone- User

wj·2023년 6월 12일
0
  • instaclone 새로운 데이터베이스 추가

  • npx prisma init 으로 새로운 프리즈마 폴더, .env 생성

  • .env에서 model User 생성 및 id, userName, password 등 필드 추가

  • typeDefs.js 정의 시 gql import해주고 export default 작성
    (gql에선 password 물어볼 필요가 없다!)

  • where필터 사용 시, OR은 [] 조건이 담긴 배열 필요

  • promise문은 보장되지 않는 사항을 의미함.바로 실행되지 않고,
    데이터 베이스에서 정보를 확인한 후, 정보와 함께 돌아옴

  • .prisma 파일에 부여하는 속성 등(@unique) 으로 db에러를 사전 방지
    findFirst : 조건에 맞는 첫 번째 결과를 return
    prisma client의 결과로 promise를 return하므로 async, await를 사용

- CreatAccount

import bycrypt from "bcrypt";
import Jwt from "jsonwebtoken";
import client from "../client";

export default {
    Mutation: {
        createAccount: async (_, {
            firstName,
            lastName,
            userName,
            email,
            password
        }) => {
            try {
                // check if username or email are already on DB.
                const existingUser = await client.user.findFirst({
                    where: {
                        OR: [{
                                userName,
                            },
                            {
                                email,
                            },
                        ],
                    },
                });
                if (existingUser) {
                    throw new Error("This username/password is already taken.");
                };
                // hash password
                const uglyPassword = await bycrypt.hash(password, 10);
                // save and return the user
                return client.user.create({
                    data: {
                        userName,
                        email,
                        firstName,
                        lastName,
                        password: uglyPassword,
                    }
                })
            } catch (e) {
                return e;
            }
        },
    },
};
  • existingUser 존재여부 확인 시, throw문으로 에러 다루거나,
    await, async 사용할 땐 try-catch문 활용해서 에러 다루는 게 좋음

- SeeProfile

import client from "../client";

export default {
    Query: {
        seeProfile: (_, {
                userName
            }) =>
            client.user.findUnique({
                where: {
                    userName,
                },
            }),
    },
}
  • findUnique()메서드는 @unique한 필드만 찾음

  • fineFirst()메서드

  • bycrypt.compare: hashing된 패스워드와 비교

  • bycrypt.hash: 일반 패스워드를 암호화

  • jsonWebtoken(JWT)는 sign을 필수로 하며 payload, secretOrPrivatekey를 가짐. 서버에서 서명을 하는 개념(서명하지 않으면 진짜 토큰인지 확인이 불가)

  • token의 목적은 타인이 변경할 수 없는 정보를 통해 유저가 누군지 확인하는 것 (보안이 필요한 정보나 비밀을 담는 게 아님!)

- Login

export default {
    Mutation: {
        login: async (_, {
            userName,
            password
        }) => {
            //find user with args.username
            const user = await client.user.findFirst({
                where: {
                    userName,
                }
            })
            if (!user) {
                return {
                    ok: false,
                    error: "User not found",
                };
            }
            //check password with args.password
            const passwordOk = await bycrypt.compare(password, user.password);
            if (!passwordOk) {
                return {
                    ok: false,
                    error: "Incorrent password",
                };
            }
            //issue a token and sent it to the user
            const token = await Jwt.sign({
                id: user.id
            }, process.env.SECRET_KEY);
            return {
                ok: true,
                token,
            };

        },
    },
};
  • 폴더 구조를 각 resolver의 이름으로 나눠서
    users폴더 안에 createAccount, editProfile, login, seeProfile로 divide해서 리팩토링. 각 resolver안에 resolvers.js(mutation이랑 queries합침), typeDefs.js 생성

생성 후, schema.js에 있는 load형식도 바꿔준다.

import {
    loadFilesSync
} from "@graphql-tools/load-files";
import {
    mergeResolvers,
    mergeTypeDefs
} from "@graphql-tools/merge";
import {
    makeExecutableSchema
} from "apollo-server";

//loadfilesync 후 merge
//loadFileSync: default export를 가져온다. export를 하지 않으면 error 발생.
//anyfolder/anyfilename.typeDefs.js
const loadedTypes = loadFilesSync(`${__dirname}/**/*.typeDefs.js`);
//pattern language (glob)
const loadedResolvers = loadFilesSync(`${__dirname}/**/*.resolvers.js`);

const typeDefs = mergeTypeDefs(loadedTypes);
const resolvers = mergeResolvers(loadedResolvers);

const schema = makeExecutableSchema({
    typeDefs,
    resolvers
})

export default schema;

- EditProfile

//editProfile.typeDefs.js

import {
    gql
} from "apollo-server";

export default gql `
    type EditProfileResult {
        ok: Boolean!
        error: String
    }
    type Mutation {
        editProfile(
            firstName: String 
            lastName: String 
            userName: String 
            email: String 
            password: String
        ): EditProfileResult!
    }
`;

//editProfile.resolvers.js

import bycript from "../../client";
import client from "../../client";

export default {
    Mutation: {
        editProfile: async (_, {
            firstName,
            lastName,
            userName,
            email,
            password: newPassword,

        }) => {
            let uglyPassword = null;
            if (newPassword) {
                uglyPassword = await bycript.hash(newPassword, 10);
            }
            const updatedUser = await client.user.update({
                where: {
                    id: 1,
                },
                data: {
                    firstName,
                    lastName,
                    userName,
                    email,
                    ...(uglyPassword && {
                        password: uglyPassword
                    }),
                },
            });
            if (updatedUser.id) {
                return {
                    ok: true,
                }
            } else {
                return {
                    ok: false,
                    error: "Could not update profile",
                }
            }
        },
    },
};
  • ...(uglyPassword && { password: uglyPassword }),
    spread 연산자를 사용해서 uglyPassword가 null이 아닌 true일 때에만
    hash된 암호가 update되도록 작성

- 토큰 활용을 위한 jwt import시, { Jwt }로 작성하면 value 안 읽힘. 괄호 없애기

  • login-> token 발행, sign token, user는 token저장 후 전송, verify token(토큰이 변경되지 않았고, 내가 발행했다는 것을 확인(SECRET_KEY를 통해 확인), token해독

  • token을 argument로 활용하게 되면 mutation마다 token을 작성해줘야하고, await를 작성해줘야 해서 번거로움.
    그대신 http headers에 작성하게 되면 request마다 token이 자동으로 생성되기 때문에 효율적이다.

profile
tistory로 옮겼습니다

0개의 댓글