회원가입 페이지 validation을 끝냈으니, 이제 login페이지 validation을 코드를 작성해 줄 시간이다.
먼저 action.ts 페이지를 생성 후, validation 관련 코드를 작성하면 된다.
'use server';
import {
PASSWORD_MIN_LENGTH,
PASSWORD_REGEX,
PASSWORD_REGEX_ERROR,
} from '@/lib/constants';
import { z } from 'zod';
const formSchema = z.object({
email: z.string().email().toLowerCase(),
password: z
.string({
required_error: 'Password is required',
})
.min(PASSWORD_MIN_LENGTH)
.regex(PASSWORD_REGEX, PASSWORD_REGEX_ERROR),
});
export async function login(prevState: any, formData: FormData) {
const data = {
email: formData.get('email'),
password: formData.get('password'),
};
const result = formSchema.safeParse(data);
if (!result.success) {
return result.error.flatten();
} else {
console.log(result.data);
}
}
로그인 검증을 할 때에는 email, password 이 두개만 사용하게 된다.
자주 사용할 것을을 constats.ts 파일로 상수로 따로 보관을 해두었다.
이를 불러와서 사용하면 된다!
이전에 회원가입때 했던 것과 거의 유사하기 때문에 설명은 생략하도록 하겠다.
이렇게 action.ts에 정의한 것을 가지고 <Login/>에서 useFormState() 를 가지고 사용하면 된다!

그럼 이렇게 error가 발생했을 때, return result.error.flatten()으로 에러메시지가 UI에 출력되는 것을 확인할 수 있다!

이제 smsLogin() validation을 해볼 시간이다.
먼저 phone number를 검증한 후, Verificaiton code를 검증할 것이다.
그러기 때문에 다른 login, account과 같이 이 둘을 같은 formSchema에 z.object() 로 둘을 같이 둘 필요가 없어진다.
이를 주의하고 <smsLogin/> 를 작성해보자
먼저, phoneSchema ,tokenSchema 를 작성해보려고 한다.
const phoneSchema = z.string().trim().refine(validator.isMobilePhone);
const tokenSchema = z.coerce.number().min(100000).max(999999);
여기서 처음 보는 것이 나왔다 --> validator
validator는 JavaScript와 Node.js에서 입력 값을 검증할 때 자주 사용하는 라이브러리이다.
이 라이브러리는 다양한 유형의 데이터(이메일, URL, IP 주소, 전화번호 등)를 검증하는 데 유용한 여러 가지 유효성 검사 함수를 제공하게 된다.
위에서 사용한 validator.isMobilePhone() 함수는 문자열이 유효한 휴대전화 번호인지 확인하게 된다.
여기서 우리는 token은 보통 숫자로 이루어진다.
어디선가 인증번호를 받으면 그 인증번호는 6자리로 받게 되면서, 우리도 input 입력창에 숫자 6자리를 입력하게 된다.
그런데, 여기서 input에 입력되는 숫자는 --> string으로 변경되게 된다.
JavaScript에서 일반적으로 HTML form이나 다른 UI 요소에서 입력된 데이터는 모두 문자열(string) 형태로 저장된다. 예를 들어, 사용자가 폼에서 숫자를 입력하더라도, 이 값은 JavaScript에서 문자열로 처리되게 된다.
그렇기 때문에 유저가 입력한 string을 number으로 변환하려고 시도해야 함 -> coerce()를 사용하면 됨 (강제를 의미함)
여기서 첫번째 console은 input에 입력된 token의 타입을 그대로 출력하는 것이고,
두번째 console은 input에 입력된 token의 값을 위에 작성한 tokenSchema를 통해 validation을 끝난 후 타입을 출력하는 것이다. ( coerce로 타입 변환 시도 후 )
첫번째는 string이 두번째는 number이 출력되는 것을 확인할 수 있다.
사용자가 입력한 token이 string으로 변경되는 것을 coerce.number() 로 number 타입으로 타입 변환을 시도한 것이다!
SMS Login 은
1. 휴대폰 번호 validation 실시 (휴대폰 번호가 맞는지)
2. 맞다면, 인증번호 인증하기
대충 이런식으로 이루어진다.
휴대폰 번호를 입력 후, 버튼을 누르고, 만약 휴대폰 번호의 형식 (위에서 정의한 것들)이 맞다면,

이후, 인증 번호를 부르면, 메인 화면으로 이동을 할 수 있도록 해두었다.
먼저 살펴봐야 할 부분이 있다.
const [state, dispatch] = useFormState(smsLogin, initialState);
useFormState()의 두번째 인자에 null을 주로 사용하는데,
null을 전달함으로써 기본 동작을 유지하고 추가적인 설정을 하지 않는게 대부분이다.
즉, 두번째 인자는 초기 상태를 설정하는 것이다.
여기서는 initialState를 지정해 두었다.
token의 값이 false, error 는 undefinded 로 지정해 두었다.
initial state는 이 함수를 최초 호출할 때의 prevState의 값이 된다.
즉, 내가 form 함수를 호출하면, token은 false로, error는 undefinded를 prevState로 넘겨주는 것이다.
고로, 페이지가 처음 render 되면, state.token의 값이 false가 된다.
이를 통해서 {state.token?} 를 이용해서 input을 숨기는데 그걸 사용할 수 있다는 것을 의미한다.
여기서 prevState.token이 false라는 의미는, 이 action을 처음 호출했다는 뜻임
initialState 가 prevState로 온거기 때문
prevState.token이 false라면, 유저가 전화번호만 입력했다는 의미임.
여기서 중요한 것은 무엇이 return 이 되던간에, state가 된다
결론은 페이지가 처음 렌더링 되었을 때에는 initialState -> prevState로 전달
다음 호출 될 때는 action.ts에 정의한 return 값이 state로 들어가게 된다.
즉, token : true를 return 하면, state의 token도 true가 된다.
전화번호 인증 성공 -> token : true를 return을 하게되는데, 이게 state로 들어감
그리고 다시 SMSLogin을 호출하게 되면 전에 return 했던 값이 prevState가 될 것임 --> 함수가 이전에 return 했던 값이 그 다음번 함수를 다시 호출 했을 때의 prevState로 들어가는 것이다.
sms login 페이지에서 전화번호를 입력하고 Verify 버튼을 누르면 => action을 처음으로 호출하게 되는 것.
action을 처음 호출하게 되면, initialState가 여기 prevState로 전달하게 된다. (token : false로 정의한 것을 전달시켜줌 )
!prevState.token--> 즉, initialState에서 넘겨온 token이 false인지를 체크하는 것
여기서 전화번호가 올바르다면, token이 true가 되면서 token 입력하는 input이 보이게 되고, 전화번호가 올바르지 않다면, token이 false를 유지하면서 input이 보이지 않게 된다.
token을 입력하고 Vertify를 입력하게 되면, 두번째 호출하는 것인데 즉 form을 두번째로 submit 하게 되는 것--> prevState는 token 이 true가 될 것임
간단하게 설명하면 이런 형태로 이루어지는 것이다!