React 앱에서 Joi를 활용한 폼 유효성 검사 구현 방법
2024-10-07 14:06:48React 앱에서 폼 유효성 검사를 구현하기
웹 애플리케이션에서 사용자 입력을 처리할 때 가장 중요한 요소는 폼의 보안성과 유효성 검사입니다. 이를 통해 사용자의 데이터를 보호하고 악성 스크립트로부터 시스템을 안전하게 지킬 수 있습니다. 이번 포스팅에서는 React와 Joi를 사용하여 안전한 로그인 폼을 구축하고 유효성 검사를 구현하는 방법에 대해 알아보겠습니다.
프로젝트 설정
우선 우리가 만들려는 프로젝트는 Vite를 활용한 React 애플리케이션입니다. Vite는 빠른 개발 환경 구성을 돕는 도구입니다. React Bootstrap과 Bootstrap을 사용하여 폼을 스타일링할 예정이며, Joi를 사용하여 폼 입력 내용을 유효성 검사할 것입니다.
1. Vite로 React 프로젝트 생성
먼저 Vite를 사용해서 새로운 React 프로젝트를 생성합니다.
npm create vite@latest react-form -- --template react
생성된 프로젝트 디렉토리로 이동합니다.
cd react-form
2. 프로젝트에 필요한 패키지 설치
프로젝트에 필요한 의존성을 설치합니다.
npm install
추가로 필요한 패키지를 설치합니다. 여기서는 Joi와 Bootstrap 관련 패키지를 포함합니다.
npm install joi-browser@latest react-bootstrap bootstrap
코드 편집기(e.g., VSCode)에서 프로젝트를 엽니다.
code .
개발 서버를 시작하여 프로젝트를 실행합니다.
npm run dev
로그인 폼 제작
이제 본격적으로 로그인 폼을 만들어보겠습니다. src 폴더 내에 components 폴더를 생성하고, 그 안에 SecureForm.jsx 파일을 만듭니다. 폼은 Bootstrap의 HTML 폼 요소를 활용하여 구축할 것입니다.
Bootstrap을 활용한 폼 구현
Bootstrap 문서의 폼 섹션을 참고하여 간단한 로그인 폼을 작성합니다. 아래 예제는 기본적인 Bootstrap 폼을 사용합니다.
function SecureForm() {
return (
<form>
<div className="mb-3">
<label htmlFor="exampleInputEmail1" className="form-label">
Email address
</label>
<input
type="email"
className="form-control"
id="exampleInputEmail1"
aria-describedby="emailHelp"
/>
<div id="emailHelp" className="form-text">
We'll never share your email with anyone else.
</div>
</div>
<div className="mb-3">
<label htmlFor="exampleInputPassword1" className="form-label">
Password
</label>
<input
type="password"
className="form-control"
id="exampleInputPassword1"
/>
</div>
<button type="submit" className="btn btn-primary">
Submit
</button>
</form>
);
}
export default SecureForm;
상태 관리 추가
폼 제출에 필요한 상태 관리를 설정합니다. useState를 사용하여 이메일과 패스워드를 저장할 상태 변수를 만듭니다.
import { useState } from "react";
function SecureForm() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState({});
const handleSubmit = (e) => {
e.preventDefault();
// submit function here
};
return <form onSubmit={handleSubmit}>{/* form inputs 여기에서 계속 */}</form>;
}
export default SecureForm;
폼 입력값 유효성 검사
폼 제출 전에 폼 값을 검사하여 유효한지 확인합니다. 처음에는 간단한 검증 함수를 구현하고, 이후 Joi로 강화된 유효성 검사를 수행합니다.
기본 유효성 검사 함수
입력값이 비어있는지 확인하는 기본 검증 함수를 작성합니다.
import { useState } from "react";
function SecureForm() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState({});
const validateInput = (input) => {
if (input.name === "username") {
if (username.length === 0) return "Username is required!";
}
if (input.name === "password") {
if (password.length === 0) return "Password is required!";
}
};
const handleUsernameChange = (e) => {
const input = e.target;
setUsername(input.value);
const inputErrors = { ...error };
const errorMessage = validateInput(input);
if (errorMessage) {
inputErrors.username = errorMessage;
} else {
delete inputErrors.username;
}
setError(inputErrors);
};
const handlePasswordChange = (e) => {
const input = e.target;
setPassword(input.value);
const inputErrors = { ...error };
const errorMessage = validateInput(input);
if (errorMessage) {
inputErrors.password = errorMessage;
} else {
delete inputErrors.password;
}
setError(inputErrors);
};
const validateForm = () => {
const errors = {};
if (username.length === 0) {
errors.username = "Username is required!";
}
if (password.length === 0) {
errors.password = "Password is required!";
}
setError(errors);
return Object.keys(errors).length === 0 ? null : errors;
};
const handleSubmit = (e) => {
e.preventDefault();
const errors = validateForm();
if (errors) return;
// submit the form to the endpoint
console.log("Form submitted successfully");
};
return (
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label htmlFor="exampleInputEmail1" className="form-label">
Email address
</label>
<input
type="email"
className="form-control"
id="exampleInputEmail1"
aria-describedby="emailHelp"
value={username}
onChange={handleUsernameChange}
/>
{error.username && (
<span className="text-danger">{error.username}</span>
)}
<div id="emailHelp" className="form-text">
We'll never share your email with anyone else.
</div>
</div>
<div className="mb-3">
<label htmlFor="exampleInputPassword1" className="form-label">
Password
</label>
<input
type="password"
className="form-control"
id="exampleInputPassword1"
value={password}
onChange={handlePasswordChange}
/>
{error.password && (
<span className="text-danger">{error.password}</span>
)}
</div>
<button type="submit" className="btn btn-primary">
Submit
</button>
</form>
);
}
export default SecureForm;
Joi를 사용한 향상된 유효성 검사
기본적인 폼 유효성 검사가 완료된 후, Joi를 이용하여 더욱 정교하게 유효성 검사를 수행할 수 있습니다. Joi는 JavaScript 객체의 유효성 검사를 간편하게 할 수 있는 라이브러리입니다.
Joi 설정 및 유효성 검사
Joi를 사용하여 폼 입력값을 검사하기 위한 스키마를 설정합니다. Joi는 다양한 타입의 유효성 검사 기능을 제공하므로, 원하는 조건을 쉽게 추가할 수 있습니다.
import Joi from "joi-browser";
const schema = {
username: Joi.string()
.email({ minDomainSegments: 2 })
.required()
.label("Email"),
password: Joi.string().min(5).required().label("Password"),
};
function validateInput(input) {
const obj = { [input.name]: input.value };
const subSchema = { [input.name]: schema[input.name] };
const { error } = Joi.validate(obj, subSchema);
return error ? error.details[0].message : null;
}
// 나머지 코드 유지
전체 폼 유효성 검사
폼 제출 시 전체 입력값을 검증하는 과정도 추가합니다. Joi로 작성한 스키마를 사용하여 모든 입력값을 검사합니다.
function validateForm() {
const options = { abortEarly: false };
const { error } = Joi.validate({ username, password }, schema, options);
if (!error) return null;
const errors = {};
for (let item of error.details) {
errors[item.path[0]] = item.message;
}
setError(errors);
return errors;
}
// 나머지 제출 함수 및 코드 유지
마무리 및 참고 자료
이제 React와 Joi를 사용하여 안전한 사용자 입력 폼을 구축하는 방법을 배웠습니다. 이러한 방식으로 유효성 검사를 자동화하면 개발 시간을 단축하고, 보다 안전한 애플리케이션을 작성할 수 있습니다.
추가 참고 자료
위의 링크들을 통해 더 깊이 있는 내용을 학습하여 프로젝트에 더욱 완성도 높은 UX/UI를 구현할 수 있습니다.