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를 사용
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;
}
},
},
};
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의 목적은 타인이 변경할 수 없는 정보를 통해 유저가 누군지 확인하는 것 (보안이 필요한 정보나 비밀을 담는 게 아님!)
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.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",
}
}
},
},
};
- 토큰 활용을 위한 jwt import시, { Jwt }로 작성하면 value 안 읽힘. 괄호 없애기
login-> token 발행, sign token, user는 token저장 후 전송, verify token(토큰이 변경되지 않았고, 내가 발행했다는 것을 확인(SECRET_KEY를 통해 확인), token해독
token을 argument로 활용하게 되면 mutation마다 token을 작성해줘야하고, await를 작성해줘야 해서 번거로움.
그대신 http headers에 작성하게 되면 request마다 token이 자동으로 생성되기 때문에 효율적이다.