본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.
기존 Joi의 message는 전부 직접 문자열을 작성했음
그런데 튜터님께서 문자열들을 객체화시켜서 관리하는 방법을 알려주심
생각보다 노가다였지만 이 방법을 사용하면 중복 문제도 해결되고 문자열을 바꾸더라도 하나씩 바꿔줄 필요가 없음
즉, 상수들을 중앙화해서 관리하는 것임
// src/constants/message.constant.js
export const MESSAGES = {
AUTH: {
COMMON: {
EMAIL: {
BASE: '이메일은 문자열이어야 합니다.',
EMAIL: '이메일의 형식이 올바르지 않습니다',
REQUIRED: '이메일을 입력해주세요.',
DUPLICATED: '이미 가입 된 사용자입니다.',
},
PASSWORD: {
BASE: '비밀번호는 문자열이어야 합니다.',
REQUIRED: '비밀번호를 입력해주세요.',
PATTERN: '비밀번호가 형식에 맞지 않습니다. (영문, 숫자, 특수문자 포함 6~15자)',
},
PASSWORD_CONFIRM: {
BASE: '비밀번호 확인은 문자열이어야 합니다.',
REQUIRED: '비밀번호 확인을 입력해주세요.',
PATTERN: '비밀번호 확인의 형식이 맞지 않습니다. (영문, 숫자, 특수문자 포함 6~15자)',
INCONSISTENT: '입력 한 두 비밀번호가 일치하지 않습니다.',
},
NAME: {
BASE: '이름은 문자열이어야 합니다.',
REQUIRED: '이름을 입력해주세요.',
},
AGE: {
BASE: '나이는 정수를 입력해주세요.',
REQUIRED: '나이를 입력해주세요.',
},
GENDER: {
BASE: '성별은 문자열이어야 합니다.',
ONLY: '성별은 [MALE, FEMALE] 중 하나여야 합니다.',
},
PROFILE_IMAGE: {
BASE: '프로필 사진은 문자열이어야 합니다.',
REQUIRED: '프로필 사진을 입력해주세요.',
},
UNAUTHORIZED: '인증 정보가 유효하지 않습니다.',
FORBIDDEN: '접근 권한이 없습니다.',
JWT: {
NO_TOKEN: '인증 정보가 없습니다.',
NOT_SUPPORTED_TYPE: '지원하지 않는 인증 방식입니다.',
EXPIRED: '인증 정보가 만료되었습니다.',
NO_USER: '인증 정보와 일치하는 사용자가 없습니다.',
INVALID: '인증 정보가 유효하지 않습니다.',
ETC: '비정상적인 요청입니다.',
DISCARDED_TOKEN: '폐기 된 인증 정보입니다.',
},
},
SIGN_UP: {
SUCCEED: '회원가입에 성공했습니다.',
},
SIGN_IN: {
SUCCEED: '로그인에 성공했습니다.',
},
SIGN_OUT: {
SUCCEED: '로그아웃 되었습니다.',
},
TOKEN_REFRESH: {
SUCCEED: '토큰 재발급에 성공했습니다.',
},
},
...
};
// src/schemas/joi.schema.js
// 로그인 유효성 검사
export const signInSchema = Joi.object({
email: Joi.string()
.email({ minDomainSegments: 2, tlds: { allow: ['com', 'net', 'kr'] } })
.required()
.messages({
'string.base': MESSAGES.AUTH.COMMON.EMAIL.BASE,
'string.empty': MESSAGES.AUTH.COMMON.EMAIL.REQUIRED,
'string.email': MESSAGES.AUTH.COMMON.EMAIL.EMAIL,
'any.required': MESSAGES.AUTH.COMMON.EMAIL.REQUIRED,
}),
password: Joi.string().required().pattern(new RegExp('^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{6,15}$')).messages({
'string.base': MESSAGES.AUTH.COMMON.PASSWORD.BASE,
'string.empty': MESSAGES.AUTH.COMMON.PASSWORD.REQUIRED,
'any.required': MESSAGES.AUTH.COMMON.PASSWORD.REQUIRED,
'string.pattern.base': MESSAGES.AUTH.COMMON.PASSWORD.PATTERN,
}),
});
기존에 파스칼 케이스, 카멜 케이스가 혼용되었던 양식을 변경함
테이블 구조 자체가 바뀌었기 때문에 기존에 있던 데이터는 다 날라감
// 변경 전
model Resumes {
resumeId Int @id @default(autoincrement()) @map("resumeId")
UserId Int @map("UserId") // Users 테이블을 참조하는 외래키
title String @map("title")
introduce String @map("introduce") @db.Text
state String @default("APPLY") @map("state")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
ResumeHistory ResumeHistories[] // 1개의 이력서에는 여러 개의 이력서 로그 기록이 존재 (1:N 관계 형성)
// Users 테이블과의 관계 설정
User Users @relation(fields: [UserId], references: [userId], onDelete: Cascade)
@@map("Resumes")
}
// 변경 후
model Resume {
resumeId Int @id @default(autoincrement()) @map("resume_id")
UserId Int @map("user_id") // User 테이블을 참조하는 외래키
title String @map("title")
introduce String @map("introduce") @db.Text
state String @default("APPLY") @map("state")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
ResumeHistory ResumeHistory[] // 1개의 이력서에는 여러 개의 이력서 로그 기록이 존재 (1:N 관계 형성)
// User 테이블과의 관계 설정
User User @relation(fields: [UserId], references: [userId], onDelete: Cascade)
@@map("resumes")
}
기존에는 where절
을 역할에 따라 컨트롤하기 위해서 where
의 AND
문법과 이중 삼항 연산을 이용해서 복잡하게 구현함
튜터님이 짠 코드를 보니 코드는 길어지지만 가독성이 엄청 좋았음
where절
에는 객체가 들어가기에 whereCondition
이라는 객체를 만들어서 조건문을 통해서 값을 결정함
그리고 sort
쿼리에 desc
, acs
둘 다 아닐 경우에 대해서 처리하지 않아서 추가함
// 변경 전
// src/routers/resumes.router.js
// 이력서 목록 조회 API
router.get('/resumes', authMiddleware, async (req, res) => {
// 사용자를 가져옴
const user = req.user;
// 정렬 조건을 req.query로 가져옴
const sortType = req.query.sort.toLowerCase();
// 필터링 조건을 가져옴
const stateFilter = req.query.status.toUpperCase();
const resumes = await prisma.resumes.findMany({
where: {
// AND 배열 연산을 통해서 필터링
AND: [user.role === 'RECRUITER' ? {} : { UserId: +user.userId }, stateFilter === '' ? {} : { state: stateFilter }],
},
select: {
resumeId: true,
User: { select: { name: true } },
title: true,
introduce: true,
state: true,
createdAt: true,
updatedAt: true,
},
orderBy: { createdAt: sortType },
});
return res.status(200).json({ status: 200, message: '이력서 목록 조회에 성공했습니다.', data: { resumes } });
});
// 변경 후
// src/routers/resumes.router.js
// 이력서 목록 조회 API
router.get('/', async (req, res) => {
// 사용자를 가져옴
const user = req.user;
// 정렬 조건을 req.query로 가져옴
let sortType = req.query.sort.toLowerCase();
if (sortType !== 'desc' || sortType !== 'asc') {
sortType = 'desc';
}
const whereCondition = {};
// 채용 담당자인 경우
if (user.role === USER_ROLE.RECRUITER) {
// 필터링 조건을 가져옴
const stateFilter = req.query.status.toUpperCase();
if (stateFilter) {
whereCondition.state = stateFilter;
}
}
// 채용 담당자가 아닌 경우
else {
whereCondition.UserId = user.userId;
}
let resumes = await prisma.resume.findMany({
where: whereCondition,
include: {
User: true,
},
orderBy: { createdAt: sortType },
});
resumes = resumes.map((resume) => {
return {
resumeId: resume.resumeId,
userName: resume.User.name,
title: resume.title,
introduce: resume.introduce,
state: resume.state,
createdAt: resume.createdAt,
updatedAt: resume.updatedAt,
};
});
return res.status(HTTP_STATUS.OK).json({ status: HTTP_STATUS.OK, message: MESSAGES.RESUMES.READ.LIST.SUCCEED, data: { resumes } });
});
드디어 팀프로젝트 발제가 되는 날
주말과 공휴일이 팀프로젝트 기간 중에 있기에 생각보다 시간이 촉박함
그렇기에 주말, 공휴일을 따지지 않고 작업을 진행할 필요가 있어 보임
일단 발제 문서는 나왔기에 간단하게 기능별로 역할을 나눠봄
계속해서 Node.js 강의와 예비군 개인 과제 등등으로 너무 바쁜 시간을 보냄
그래도 개인과제 해설이 끝나면 약간의 시간을 이용해서 SQL 강의 시청하기
해설 강의를 통해서 문자열이나 숫자 같은 상수들을 관리하는 법을 알게 되었음
기존에는 Joi에서 사용하는 message에 문자열을 직접 넣었음
튜터님께서 공통으로도 사용되는 문자열들을 관리하기 위해서 그러한 문자열들을 객체로 관리하도록 코드를 구현하는게 유지보수에 좋다고 했음
그리고 Prisma의 where절의 조건을 역할에 따라 다르게 적용하는 법 역시 생각도 못한 방법이었기에 굉장히 유용했음
기존에 데이터베이스에서 파스칼 케이스, 카멜 케이스를 혼용에서 사용함
그래서 해설 강의를 기반으로 데이터베이스를 싹 뜯어 고침
데이터가 날라가는 건 어쩔 수 없다고 생각하고 Prisma 스키마를 수정함
전부 수정 후 리눅스 서버에서도 git pull로 최신화 함
그러고 코드를 수정하니 위와 같은 에러가 발생함
분명 제대로 수정된 스키마로 최신화 했고, DB도 바뀐 컬럼으로 적용되었는데 똑같은 에러가 발생했음
로컬에서 진행했던 과정을 다시 살펴보니 정말 간단한 이유였음
바로 npx prisma db push
를 리눅스 서버에서 실행시켜주지 않아서 발생한 문제였음
근데 생각해보면 npx prisma db push
를 통해서 DB가 업데이트 되었는데 리눅스 서버에서도 이 과정이 필요한 걸까?
로컬 서버, 리눅스 서버 모두 AWS의 RDS에 연결되어 있는데?
인터넷과 Chat-GPT를 사용해서 이유를 찾아봤지만 뚜렷한 해답은 없었음
인터넷과 Chat-GPT에서 말하는 이유는 다음과 같음
결론, 같은 클라우드 DB를 사용해서 위와 같은 에러가 발생하면 리눅스 서버에서도 한 번 더 동기화 시켜주는 게 좋음
그리고 동기화 작업 시 npx prisma db push
보다는 npx prisma migrate
명령이 더 좋다고 함
(추가) 팀원분께서 비슷한 상황에 대해서 말씀해 주셨음
https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/generating-prisma-client
스키마 같은 경우 node_module 밑에 Prisma client에 있는데 여길 통해서 쿼리 작업이 진행됨
그런데 단지 pull를 통해서 최신을 받아오면 node_module은 받아오지 않기 때문에 쿼리 작업을 담당하는 Prisma client는 리눅스 서버에서 최신화되지 않는다는 이야기 같음