유효성 검사 코드를 미들웨어로 분리할지 여부
미들웨어로 빼는 게 순서상 맞고 효율적인 코드라고 판단. 최적화 시 회원가입 관련 유효성 코드는 미들웨어로 분리. 그러나 로그인은 service 단위에 구현하는 게 코드를 “덜” 쓰는 것 같아서 일단 내버려둠. 그 결과 통일성 저해됨. → 다음 프로젝트에서 처음부터 미들웨어로 분리하는 방법을 고려해볼 것. (= 유효성 검사가 들어가는 모든 코드에)
기획 요구사항대로, 회원가입 시 바로 로그인이 되도록 설정
데이터 구조
{
message: "Client 일반 로그인 성공",
data: { accessToken, refreshToken, user }
}
import z from "zod";
import { ErrorMessage } from "../constants/ErrorMessage";
// 일반 회원가입 개별 스키마
export const emailSchema = z.string().email().nonempty(ErrorMessage.NO_EMAIL);
export const passwordSchema = z
.string()
.min(8, ErrorMessage.PASSWORD_LENGTH_LIMIT)
.max(16, ErrorMessage.PASSWORD_LENGTH_LIMIT)
.regex(
/^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*(),.?":{}|<>])[A-Za-z\\d!@#$%^&*(),.?":{}|<>]{8,16}$/,
ErrorMessage.PASSWORD_REGEX,
)
.nonempty(ErrorMessage.NO_PASSWORD);
export const nameSchema = z
.string()
.max(4, ErrorMessage.NAME_LENGTH_LIMIT)
.nonempty(ErrorMessage.NO_NAME);
export const phoneSchema = z
.string()
.regex(/^\\d{9,11}$/, ErrorMessage.PHONE_REGEX)
.nonempty(ErrorMessage.NO_PHONE);
// 일반 회원가입 DTO 및 zod 유효성 검사
export const signUpSchema = z
.object({
name: nameSchema,
email: emailSchema,
phone: phoneSchema,
password: passwordSchema,
passwordConfirmation: z.string(),
})
.refine((data) => data.password === data.passwordConfirmation, {
message: ErrorMessage.PASSWORD_CONFIRMATION_NOT_MATCH,
path: ["passwordConfirmation"],
});
export type SignUpRequestDto = z.infer<typeof signUpSchema>;
// 일반 회원가입
async function signUp(req: Request<{}, {}, SignUpRequestDto>, res: Response, next: NextFunction) {
try {
const client = await authClientService.create(req.body);
res.status(201).json({ message: "Client 일반 회원가입 성공", data: client });
} catch (error) {
next(error);
}
}
async function loginWithLocal({ email, hashedPassword }: LoginDataLocal) {
const client = await authClientRepository.findByEmail(email);
if (!client || client.provider !== "LOCAL") {
throw new NotFoundError(ErrorMessage.USER_NOT_FOUND);
}
// 비밀번호 확인 유효성 검사
await verifyPassword(hashedPassword, client.hashedPassword!);
// 토큰 넣음
const accessToken = generateAccessToken({
userId: client.id,
email: client.email,
name: client.name!,
userType: client.userType,
isProfileCompleted: client?.isProfileCompleted,
});
const refreshToken = generateRefreshToken({
userId: client.id,
email: client.email,
name: client.name!,
userType: client.userType,
isProfileCompleted: client?.isProfileCompleted,
});
// 비밀번호와 전화번호 빼고 반환
const user = filterSensitiveUserData(client);
return { accessToken, refreshToken, user };
}