[React Native] 로그인 화면 구현 (프론트엔드) - 4. 로그인 Validation
2022. 5. 23. 02:55ㆍReact Native/Intermediate
1. 로그인을 위한 Validation Framework 작성
로그인의 버튼 만들기 전 선행되어야 하는 게 로그인 검증 validation이다.
로그인 검증 프로세스를 위한 파일을 하나 만들 건데 /app/utils/forms 에 validationRules.js 를 만들자. 이 파일에서 검증 항목과 검증 방법을 구현할 것이다. 파일의 반환은 true or false 인데 문제가 없다면 true, 하나라도 문제가 있다면 false가 반환될 것이다.
그리고 authForm.js에서는 반환된 값을 이용해 버튼을 동작시킬지 오류를 발생시킬지 구현할 것이다.
// /app/utils/form/validationRules.js
const validation = (value, rules, form) => {
let valid = true;
return valid;
};
export default validation;
// /app/components/auth/authForm.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow strict-local
*/
import {useNavigation} from '@react-navigation/native';
import React, {useState} from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
Button,
Platform,
} from 'react-native';
import Input from '../../utils/forms/input';
import validationRules from '../../utils/forms/validationRules';
const AuthForm = ({goWithoutLogin}) => {
const [type, setType] = useState('로그인'); // 로그인 / 등록
const [action, setAction] = useState('로그인'); // 로그인 / 등록
const [actionMode, setActionMode] = useState('회원가입'); // 회원가입 / 로그인 화면으로
const [hasErrors, setHasErrors] = useState(false);
const [form, setForm] = useState({
email: {
value: '',
type: 'textInput',
rules: {
isRequired: true,
isEmail: true,
},
valid: false,
},
password: {
value: '',
type: 'textInput',
rules: {
isRequired: true,
minLength: 6,
},
valid: false,
},
confirmPassword: {
value: '',
type: 'textInput',
rules: {
confirmPassword: 'password',
},
valid: false,
},
});
// state = {
// type: 'Login',
// action: 'Login',
// actionMode: '새로 등록할게요~',
// hasErrors: false,
// form: {
// email: {
// value: '',
// type: 'textInputRevised',
// rules: {},
// valid: false,
// },
// password: {
// value: '',
// type: 'textInput',
// rules: {},
// valid: false,
// },
// confirmPassword: {
// value: '',
// type: 'textInput',
// rules: {},
// valid: false,
// },
// },
// };
updateInput = (name, value) => {
setHasErrors(false);
let formCopy = form;
formCopy[name].value = value;
let rules = formCopy[name].rules;
let valid = validationRules(value, rules, formCopy);
formCopy[name].valid = valid;
setForm(form => {
return {...formCopy};
});
// setForm({
// form: formCopy,
// });
console.warn(form);
};
confirmPassword = () => {
return type != '로그인' ? (
<Input
value={form.confirmPassword.value}
type={form.confirmPassword.type}
secureTextEntry={true}
placeholder="비밀번호 재입력"
placeholderTextColor={'#ddd'}
onChangeText={value => updateInput('confirmPassword', value)}
/>
) : null;
};
formHasErrors = () => {
return hasErrors ? (
<View style={styles.errorContainer}>
<Text style={styles.errorLabel}>
앗! 로그인 정보를 다시 확인해주세요~
</Text>
</View>
) : null;
};
changeForm = () => {
type === '로그인'
? (setType('등록'), setAction('등록'), setActionMode('등록'))
: (setType('로그인'), setAction('로그인'), setActionMode('로그인'));
};
return (
<View>
<Input
value={form.email.value}
type={form.email.type}
autoCapitalize={'none'}
keyboardType={'email-address'}
placeholder="이메일 주소"
placeholderTextColor={'#ddd'}
onChangeText={value => updateInput('email', value)}
/>
<Input
value={form.password.value}
type={form.password.type}
secureTextEntry={true}
placeholder="비밀번호"
placeholderTextColor={'#ddd'}
onChangeText={value => updateInput('password', value)}
/>
{confirmPassword()}
{formHasErrors()}
<View style={{marginTop: 40}}>
<View style={styles.button}>
<Button title={action} color="#48567" />
</View>
<View style={styles.button}>
<Button title={actionMode} color="#48567" onPress={changeForm} />
</View>
<View style={styles.button}>
<Button
title="비회원 로그인"
color="#48567"
onPress={() => goWithoutLogin()}
/>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
errorContainer: {
marginBottom: 10,
marginTop: 30,
padding: 20,
backgroundColor: '#ee3344',
},
errorLabel: {
color: '#fff',
fontSize: 15,
fontWeight: 'bold',
textAlignVertical: 'center',
textAlign: 'center',
},
button: {
...Platform.select({
ios: {
marginTop: 15,
},
android: {
marginTop: 15,
marginBottom: 10,
},
}),
},
});
export default AuthForm;
2. Validation Rule 함수 구현
항목 하나하나에 대해서 유효성 검사하는 함수를 구현해보자
이메일 검사
test, toLocaleLowerCase는 js 내장 함수들이다.
test - 주어진 문자열이 정규표현식을 만족하는지 판별, string 값만 가능해서 String()으로 강제 형변환 시켜줌
debugger를 이용해서 확인해보자
그런데 원래 CMD + d를 이용해서 디버거를 열면 되는데 현재 이 키가 안 먹는다.. CMD + control + z 를 이용해서 디버거를 선택하자. 이것보다 더 좋은 방법! 그냥 f4 눌러서 react native debugger를 실행하면 된다. 참고로 이전에 미리 설치해둬서 존재하는 거라 안 깔려있다면 basic에 있는 부분을 참고하자.
완성된 validation 파일
// /app/components/utils/forms/validationRules.js
const validation = (value, rules, form) => {
let valid = true;
for (let rule in rules) {
switch (rule) {
case 'isRequired':
valid = valid && validateRequired(value);
// console.log(valid);
break;
case 'isEmail':
valid = valid && validateEmail(value);
// console.log(valid);
break;
case 'minLength':
valid = valid && validateMinLength(value, rules[rule]);
// console.log(valid);
break;
case 'confirmPassword':
valid =
valid &&
validateConfirmPassword(value, form[rules.confirmPassword].value);
console.log(valid);
break;
default:
valid = true;
}
}
return valid;
};
const validateConfirmPassword = (confirmPassword, password) => {
return confirmPassword === password;
};
const validateMinLength = (value, ruleValue) => {
if (value.length >= ruleValue) {
return true;
}
return false;
};
const validateEmail = value => {
const expression =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return expression.test(String(value).toLocaleLowerCase());
};
const validateRequired = value => {
if (value !== '') {
return true;
}
return false;
};
export default validation;
참고로 changeForm 부분 화면에서 실수가 있어 수정했다.
// /app/components/auth/authForm.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow strict-local
*/
import {useNavigation} from '@react-navigation/native';
import React, {useState} from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
Button,
Platform,
} from 'react-native';
import Input from '../../utils/forms/input';
import validationRules from '../../utils/forms/validationRules';
const AuthForm = ({goWithoutLogin}) => {
const [type, setType] = useState('로그인'); // 로그인 / 등록
const [action, setAction] = useState('로그인'); // 로그인 / 등록
const [actionMode, setActionMode] = useState('회원가입'); // 회원가입 / 로그인 화면으로
const [hasErrors, setHasErrors] = useState(false);
const [form, setForm] = useState({
email: {
value: '',
type: 'textInput',
rules: {
isRequired: true,
isEmail: true,
},
valid: false,
},
password: {
value: '',
type: 'textInput',
rules: {
isRequired: true,
minLength: 6,
},
valid: false,
},
confirmPassword: {
value: '',
type: 'textInput',
rules: {
confirmPassword: 'password',
},
valid: false,
},
});
// state = {
// type: 'Login',
// action: 'Login',
// actionMode: '새로 등록할게요~',
// hasErrors: false,
// form: {
// email: {
// value: '',
// type: 'textInputRevised',
// rules: {},
// valid: false,
// },
// password: {
// value: '',
// type: 'textInput',
// rules: {},
// valid: false,
// },
// confirmPassword: {
// value: '',
// type: 'textInput',
// rules: {},
// valid: false,
// },
// },
// };
updateInput = (name, value) => {
setHasErrors(false);
let formCopy = form;
formCopy[name].value = value;
let rules = formCopy[name].rules;
let valid = validationRules(value, rules, formCopy);
formCopy[name].valid = valid;
setForm(form => {
return {...formCopy};
});
// setForm({
// form: formCopy,
// });
// console.warn(form);
};
confirmPassword = () => {
return type != '로그인' ? (
<Input
value={form.confirmPassword.value}
type={form.confirmPassword.type}
secureTextEntry={true}
placeholder="비밀번호 재입력"
placeholderTextColor={'#ddd'}
onChangeText={value => updateInput('confirmPassword', value)}
/>
) : null;
};
formHasErrors = () => {
return hasErrors ? (
<View style={styles.errorContainer}>
<Text style={styles.errorLabel}>
앗! 로그인 정보를 다시 확인해주세요~
</Text>
</View>
) : null;
};
changeForm = () => {
type === '로그인'
? (setType('등록'), setAction('등록'), setActionMode('로그인 화면으로'))
: (setType('로그인'), setAction('로그인'), setActionMode('회원가입'));
};
return (
<View>
<Input
value={form.email.value}
type={form.email.type}
autoCapitalize={'none'}
keyboardType={'email-address'}
placeholder="이메일 주소"
placeholderTextColor={'#ddd'}
onChangeText={value => updateInput('email', value)}
/>
<Input
value={form.password.value}
type={form.password.type}
secureTextEntry={true}
placeholder="비밀번호"
placeholderTextColor={'#ddd'}
onChangeText={value => updateInput('password', value)}
/>
{confirmPassword()}
{formHasErrors()}
<View style={{marginTop: 40}}>
<View style={styles.button}>
<Button title={action} color="#48567" />
</View>
<View style={styles.button}>
<Button title={actionMode} color="#48567" onPress={changeForm} />
</View>
<View style={styles.button}>
<Button
title="비회원 로그인"
color="#48567"
onPress={() => goWithoutLogin()}
/>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
errorContainer: {
marginBottom: 10,
marginTop: 30,
padding: 20,
backgroundColor: '#ee3344',
},
errorLabel: {
color: '#fff',
fontSize: 15,
fontWeight: 'bold',
textAlignVertical: 'center',
textAlign: 'center',
},
button: {
...Platform.select({
ios: {
marginTop: 15,
},
android: {
marginTop: 15,
marginBottom: 10,
},
}),
},
});
export default AuthForm;
참고 자료
iOS/Android 앱 개발을 위한 실전 React Native - Intermediate - 인프런 | 강의
React Native 기반 모바일 앱 개발을 위한 중급 강의입니다. 프론트엔드의 심화내용 학습 뿐만 아니라 Firebase 기반의 백엔드 내용까지 함께 배우면서, 서버 연동/ 로그인/ 데이터 송수신/ 공공API 활
www.inflearn.com
'React Native > Intermediate' 카테고리의 다른 글
[React Native] 로그인 화면 구현 (프론트엔드) - 6. Redux를 통한 로그인 로직과 흐름 검증 (0) | 2022.05.23 |
---|---|
[React Native] 로그인 화면 구현 (프론트엔드) - 5. 로그인/등록 버튼 이벤트 생성 (0) | 2022.05.23 |
[React Native] 로그인 화면 구현 (프론트엔드) - 3. 로그인, 회원가입 화면 버튼 및 이벤트 생성 (0) | 2022.05.23 |
[React Native] 로그인 화면 구현 (프론트엔드) - 2. TextIput 이벤트 핸들러 (0) | 2022.05.22 |
[React Native] 로그인 화면 구현 (프론트엔드) - 1. 화면 준비 및 재사용 컴포넌트 작성 (0) | 2022.05.21 |