외주 개발을 하면서, input값이 비어 있거나 원하는(유효한) 형식이 아닐 경우 에러메시지를 띄우고, 저장을 못하게 막는 기능을 구현했다. react-use-form
과 같은 라이브러리를 사용하기엔 이미 작성된 코드에 끼워넣기가 굉장히 복잡했고, 프로젝트가 무거워지는 것을 원하지 않았기 때문에 커스텀해서 만들어 보기로 했다.
StudentModify
ㄴ index.js
ㄴ StudentModifyContainer.js
ㄴ StudentModifyPresenter.js
StudentModify는 사진의 모달을 구현한 코드이다. 컨벤션에 따라 Container&Presenter 패턴의 디렉토리 구조를 구성했다.
새 학생 등록 로직은 간략히 정리하자면 다음과 같다.
Presenter
에서 입력받은 Input값을 저장하기 버튼을 눌러서 Container
의 handleSubmit
에 전달한다. // StudentModifyPresenter.js
<SubmitButton onClick={handleSubmit}> 저장하기 </SubmitButton>
Container
의 handleSubmit
에서는newStudent
라는 새 객체에 담아 DB에 저장한다.// StudentModifyContainer.js
const handleSubmit = () => {
const newStudent = {
name: inputName.current.value,
school: inputSchool.current.value,
phone: inputPhone.current.value,
parents: inputParents.current.value,
category,
grade,
subjects,
officeHours
};
// 이 밑에서는 api를 호출해 DB에 newStudent를 저장해준다.
}
유효하지 않은 값이 들어왔을 때 각각 다른 에러메시지를 출력해주고 싶다.
예를 들면
이렇게 원하는 경우마다 다른 에러메시지를 출력해 주기 위해 errors
라는 객체 state를 정의하고 여기다가 담을 것이다.
// StudentModifyContainer.js
import React, { useState } from 'react';
const [errors, setErrors] = useState(new Object());
StudentModifyContianer.js
안에 있는 validate
가 하는 일은 다음과 같다.
validate
에서 newStudent
를 key별로 검사한다.errors
에 넣어준다. StudentModifyPresenter
로부터 newStudent
객체를 받는다. errors
가 비어있다면, 모든 key가 유효하다는 뜻이므로 handleSubmit
을 호출하여 새 학생을 DB에 저장한다. 설계에서 생각해 둔 대로 구현해본다.
// StudentModifyContainer.js
const validate = () => {
const temp_errors = {};
const regPhone = /^01([0|1|6|7|8|9])-?([0-9]{3,4})-?([0-9]{4})$/;
const newStudent = {
name: inputName.current.value,
school: inputSchool.current.value,
phone: inputPhone.current.value,
parents: inputParents.current.value,
category,
grade,
subjects,
officeHours
};
// newStudent의 모든 key에 대해서
for (var key in newStudent){
// newStudent[key], 즉 key에 해당하는 value가 없다면
if(newStudent[key].length === 0 || newStudent[key] === undefined){
if (key === 'name') {
temp_errors[key] = '학생 이름이 없습니다.';
} else if (key === 'school') {
temp_errors[key] = '학교가 없습니다.';
} else if (key === 'phone') {
temp_errors[key] = '학생 연락처가 없습니다.';
} else if (key === 'parents') {
temp_errors[key] = '부모님 연락처가 없습니다.';
} else if (key === 'category') {
temp_errors[key] = '계열이 없습니다.';
} else if (key === 'grade') {
temp_errors[key] = '학년이 없습니다.';
} else if (key === 'subjects') {
temp_errors[key] = '수강과목이 없습니다.';
} else if (key === 'officeHours') {
temp_errors[key] = '등원일이 없습니다.';
}
}
}
}
유효하지 않은 값을 찾을 때마다 state를 업데이트할 필요는 없으므로,temp_errors
라는 일반 변수에 저장했다가 마지막에 한 번만 state를 업데이트 할 것이다. 전화번호를 검사할 정규식을 정의했다.
Presenter
로부터 넘어온 input값으로 newStudent
를 구성하고, newStudent
의 key를 반복문으로 돌며 input값이 없는 경우를 확인했다.
이어서 for문 안에서 3개의 if문을 추가한다.
if (key === 'subjects') {
for (var i = 0; i < newStudent[key].length; i++) {
if (!newStudent[key][i].teacher) {
temp_errors[key] = newStudent[key][i].name + '과목의 선호강사가 없습니다.';
}
}
}
// 전화번호가 입력되었을 때 형식 검사
if (key === 'phone' && newStudent[key].length != 0 && regPhone.test(newStudent[key]) === false) {
temp_errors[key] = '학생 연락처가 형식에 맞지 않습니다.';
}
if (key === 'parents' && newStudent[key].length != 0 && regPhone.test(newStudent[key]) === false) {
temp_errors[key] = '부모님 연락처가 형식에 맞지 않습니다.';
}
if문의 조건으로 newStudent[key].length != 0
코드를 쓴 이유는, phone에 대해서 두 가지 에러가 있을 수 있기 때문이다. 전화번호가 입력되지 않은 경우 / 전화번호 형식이 잘못된 경우이다. 한 가지 에러메시지만 출력되기를 원하기 때문에 입력된 이후에 정규식 검사를 했다(이렇게 하지 않으면 전화번호 필드에 아무것도 입력하지 않았는데 전화번호 형식이 틀렸다는 에러메시지가 뜨더라).
마지막으로, for문의 바깥에서 2개의 if문을 추가한다.
// 에러가 없으면 handleSubmit
if (Object.keys(temp_errors).length === 0 && temp_errors.constructor === Object) {
handleSubmit(newStudent); // newStudent를 DB에 등록
} else {
setErrors({ ...temp_errors });
}
temp_errors
가 빈 객체인지를 검사하고, 비어 있다면 에러가 없이 모두 유효한 값이라는 뜻이므로 DB에 저장한다. 그렇지 않다면 errors
state에 temp_errors
를 넣어준다. 이 errors
는 Presenter
에 전달되어 에러메시지를 출력해 줄 예정이다.
우선 validate, errors
를 StudentModifyPresenter.js
에 props로 전달해야 한다.
// StudentModifyContainer.js
// 윗부분 코드 생략
// ...
return ( <StudentModifyPresenter validate={validate} errors={errors}/> )
export default StudentModifyContainer;
당연히 그냥 쓰면 안되고 이렇게 보낸 값을 받아줘야 validate, errors
를 presenter
에서 사용할 수 있다. 그리고 저장하기 버튼을 눌렀을 때 validate
를 호출하게끔 만들어준다. 그 다음 에러메시지를 저장하기 버튼 위에 출력해줄 것이다. 삼항 연산자를 이용해 errors
가 비어 있다면 null을, 비어 있지 않다면 errors
의 key를 map()
반복문으로 돌면서 에러 내용을 출력해준다.
// StudentModifyPresenter.js
const StudentModifyPresenter = ({validate, errors}) => {
return (
// html 코드
// input도 여기 있겠죠?
{ Object.keys(errors).length === 0
? null
: Object.keys(errors).map((key, index)=>(
<ModalFormError> <i class="fas fa-exclamation-circle"></i> {errors[key]} </ModalFormError>
))
}
<SubmitButton onClick={validate}> 저장하기 </SubmitButton>
)
}
저장하기 버튼을 눌렀을 때 에러메시지가 잘 출력되는 것을 확인할 수 있다.
전화번호가 없을 땐 없다는 메시지를, 형식에 맞지 않는 값이 들어왔을 때는 형식에 맞지 않는다는 메시지를 잘 출력해낸다. 모든 필드에 유효한 값을 입력하면 저장도 잘 된다.
리액트를 이번 프로젝트에 처음 사용해봐서 컨벤션을 잘 모르기 때문에, 좋게 만든 코드인지는 확신이 없다. 그래도 누군가에겐 도움이 되기를 바라며 (적어도 내가 오래 기억하는 데에 도움이 될 것이다), 그리고 더 좋은 방법이 있거나 잘못된 부분이 있으면 댓글로 공유 부탁드리며 포스트를 마무리 한다.
서윗한 서위치 쓰셈 ㅎㅎ