이번 주는 일정이 많아 한 게 많이 없어 월요일 더 빠르게 느껴진다. 늦은 시간에 작성하는 회고, 화요일이 되기전에 얼른 써내려가보자
하.. 인증.. 이녀석.. 정말...
하나 완성하면 다른 곳에서 문제가 생기고 하나 완성하면 다른 곳에서 문제가 터졌다. 설계를 제대로 하고 들어가지 않아 문제가 된 거 같았다.
const query = `
query currentUser {
currentUser {
id
username
email
profile {
id
thumbnail
display_name
short_bio
profile_links
}
user_meta {
id
email_notification
email_promotion
}
}
}
`;
검증을 진행했을때 부가적인 데이터가 있어야 FE에 전달해줄 수 있지 않을까 싶어서 Velog API 호출 시에 얻을 수 있는 모든 data를 요청하였다.
코드 리뷰 중 id 정도만 받아와도 검증의 역할을 할 수 있지 않겠냐는 의견이 나왔다. 일리가..있다!
애초에 API가 하나로 들어올걸 고려해서 만들었지만 API가 나누어 들어온다면 쿼리문을 나누기 유리했다. 검증용과 최초 등록용으로 나누기로 하였고 미들웨어의 시작점은 아래와 같다.
* Velog 사용자 인증을 위한 미들웨어 모음
* @property {Function} login - 사용자의 전체 정보를 조회하는 인증 미들웨어
* @property {Function} verify - 기본 사용자 정보만 조회하는 인증 미들웨어
*/
export const authMiddleware = {
login: verifyBearerTokens(QUERIES.LOGIN),
verify: verifyBearerTokens(QUERIES.VERIFY),
};
GraphQL 쿼리를 하나의 객체로 두어 관리할 수 있게 하였다. 추후 대쉬보드에 보일 요소가 추려지면 login의 쿼리만 수정하면 되니 유지보수 측면에서 잘 된 거 같다.
/**
* 요청에서 토큰을 추출하는 함수
* @param req - Express Request 객체
* @returns 추출된 토큰 객체
* @description 다음 순서로 토큰을 확인합니다:
* 1. 요청 본문 (req.body) - 신규 로그인
* 2. 요청 헤더 - API 호출
* 3. 쿠키 - 웹 클라이언트
*/
const extractTokens = (req: Request): { accessToken: string | undefined; refreshToken: string | undefined } => {
const accessToken = req.body.accessToken || req.headers['access_token'] || req.cookies['access_token'];
const refreshToken = req.body.refreshToken || req.headers['refresh_token'] || req.cookies['refresh_token'];
return { accessToken, refreshToken };
};
🤔 로그인 form으로 온다면 body에 토큰이 있을 거라고 생각하고 짠 로직이지만 FE 코드를 보았을 때 header.cookie에 전송하는 걸로 확인이 되어 기준님과 상의 후 수정이 필요할 거 같다.
🥹 이 부분은 아직 미완성이라 조금 더 고민이 필요하다
Velog 에서 검증이 완료된 유저는 DB에서 조회 후 분기에 따라 나뉘게 된다.
// 토큰 복호화 처리
private async decryptTokens(refreshToken: string) {
return {
decryptedRefreshToken: this.aesEncryption.decrypt(refreshToken),
};
}
원래는 DB의 refresh_token을 복호화 하여 client에서 보낸 token의 uuid와 대조해보려고 계획했지만, 이미 Velog API에서 검증을 하고 들어온터라 당장 DB저장에는 필요가 없다는 판단이 들었다. 후에 토큰을 가져와서 통계 데이터 접근에 필요할 수 있으므로 그냥 두었다.
// 토큰 암호화 처리
private async encryptTokens(tokens: { accessToken: string; refreshToken: string }) {
return {
encryptedAccessToken: this.aesEncryption.encrypt(tokens.accessToken),
encryptedRefreshToken: this.aesEncryption.encrypt(tokens.refreshToken),
};
}
클라이언트에서 받은 new_token을 각각 암호화를 진행해준다.
const { encryptedAccessToken, encryptedRefreshToken } = await this.encryptTokens(newTokens);
await this.userRepository.updateTokens(user.uuid, {
accessToken: encryptedAccessToken,
refreshToken: encryptedRefreshToken,
});
암호화한 new_token을 해당하는 user의 old_token을 DB에 업데이트 해준다.
const { encryptedAccessToken, encryptedRefreshToken } = await this.encryptTokens(newTokens);
// TODO: DB 저장 로직
각 new_token을 암호화하여 user를 생성한다.
오늘은 winston과 error-handling 에 시간을 잡아먹어 인증 로직에는 시간을 많이 할애 못했다.
아마 내일도 일정이 많아 늦은 저녁에 완성 후, user data tracking 전용 api 를 진행할 거 같다.
근데 user data tracking 전용 api 정확히 뭐였더라..
하온님 ㅋㅋㅋ 많이 바쁜 나날을 보내시고 계시는 군요.. 무엇이 가장 바쁘신가요?! "이번 주는 잘한 점이 없다!" 라뇨 ㅎㅎㅎㅎ
"tracking 전용 api" 은 말 그대로 유저의 행동을 추적해보려고 수집 전용, insert & select only 데이터를 쌓아보자는 취지로 만들어진 data & api 였습니다.
예를 보자면 로그인 이후 대시보드에서 가장 먼저 누른 버튼이 뭔가? 를 추적하기 용이하게 api 있으면 좋지 않을까?
버튼 하나를 누르고, api 하나를 호출할 때 마다 logging 이 쌓이겠지만, 오히려 한 유저의 생애주기처럼 api 호출을 계속 한다면? 어떨까? 에서 시작된 아이디어 였어요!