게시판에서 등록과 수정은 항상 세트다. 통신 부분만 빼면 퍼블리싱 코드는 같다. 따라서 재활용해야 제맛이다. 그런데 등록은 정상 작동하는데, 수정 버튼이 눌려지지 않는다.
문제
수정 시 서버에서 데이터를 불러와 input들에 정보를 띄우는 것은 문제 없다. 다만 수정하고 수정 버튼을 누르면 아무 반응이 없다. form은 다음과 같다.
<form
onSubmit={
isEdit ? handleSubmit(onClickUpdate) : handleSubmit(onClickCreate)
}
>
....
<S.SubmitBtn>
<span>{isEdit ? "수정하기" : "등록하기"}</span>
</S.SubmitBtn>
</form>
수정 상태냐, 아니냐(isEdit)에 따라 onSubmit가 보내는 함수가 달라지게 수정했다. vscode나 콘솔 어디를 봐도 오류도 나타지않아 당황스러운 부분이었다. 등록은 되는데 수정이 안된다니.
시도 1
혹시 onClickUpdate함수를 잘못 작성했나 싶어 처음부터 다시 코드를 확인했다. 혹시 통신 부분에서 잘못 코딩한게 있을까 싶었다.
const onClickUpdate = async (data: any) => {
const { info, ...rest } = data;
try {
let imgUrl = "";
if (imgFiles) {
const resultImgs = await uploadImages({
variables: {
images: imgFiles,
},
});
imgUrl = resultImgs.data.uploadImages.map((el: any) => el.imageUrl);
} else {
imgUrl = fetchProduct?.fetchProduct.images;
}
const result = await updateProduct({
variables: {
productId: String(router.query.useditemId),
updateProductInput: { ...rest, images: imgUrl },
updateProductDetailInput: { ...info },
},
});
SuccessModal("상품 수정 완료!");
void router.push(`/product/${String(result.data?.updateProduct.id)}`);
} catch (error) {
if (error instanceof Error) ErrorModal(error.message);
}
};
로직상에도 오타도 잘못된 건 없었다. 이미지 잘들어오고, data도 잘 들어온다. 당황스러웠다. console.log를 몇번이고 여러곳이고 찍어봐도 문제가 없었다.
시도 2
내가 useForm의 handleSubmit을 잘못 사용했나 싶어 react-hook-form DOCS를 살폈다.
기본 사용법을 다시 보았고, 예시를 보았음에도 잘못 작성했거나, 오타를 낸 것 없었다. 콘솔로도, useForm도 문제가 아니면 무엇인가. 등록 버튼은 작동한 다면 수정 버튼에 관련된 부분에서만 문제가 있다는 것인데... 시도1에 쓴 onClickUpdate 함수를 빼고 모든 코드를 공유하는 데 이것만 되지 않는다는 점은 나를 당황하게 했다.
시도 3
콘솔로 정보는 들어오는 것을 확인한 만큼 통신은 되는가 싶어 network 탭을 살폈다. 일단 빨간 줄은 없었다. 통신 자체는 성공하는 듯했다. 오류가 있다면, try catch 썼으니 사실 콘솔이든 모달이든 오류가 떠야하는 게 맞으니까.
시도 4
그러다 불현듯 떠올랐다. 혹시, useForm에서 제공하는 fomState.error에 뭔가가 찍히지 않을까 싶었다. 그래서 모든 이름을 콘솔에 다 찍었다. 그랬더니 아래와 같은 오류를 내뱉었다!
"info.option6 must be a `string` type, but the final value was: `null`.
If "null" is intended as an empty value be sure to mark the schema as `.nullable()`"
이 오류를 발견하자마자 너무나 반가웠다. 오류가 뜬다는 건 그 부분을 해결하면 끝난다는 의미니까. 아무튼, 오류 내용을 살펴보니, info.option6의 타입은 문자여야하는데 null이 들어왔다는 것. 그러니 schema에 .nullable()을 넣으란다. 결론부터 말하면 resolver 시 검증 단계에서 일어난 문제였고, string이 아니라 null도 입력 받을 수도 있다고 알려주면 끝나는 오류였다.
export const updateSchema = yup.object({
...
info: yup.object({
type: yup.string(),
option1: yup.string(),
option2: yup.string(),
option3: yup.string(),
option4: yup.string(),
option5: yup.string().nullable(),
option6: yup.string().nullable(),
option7: yup.string().nullable(),
...
});
왜?
여기서 하나 의문이 들었다. 등록은 잘 되는데, 수정에서만 오류가 날까? 이 원인의 발달은 백엔드였다. 처음 등록 시 옵션 값을 입력 받고, 값이 없다면 null을 기본값으로 넣도록 테이블을 설계했단다. 따라서 수정 페이지에 들어왔을 때 서버에서 기존 값을 다 불러온 후, 화면 상엔 존재하지 않더라도 option5 이후 모든 값은 null을 불러온 상태였던 것.
yup은 자기 역할을 철저히 움직였을 뿐인 셈이다. null은 스트링이 아니니 yup과 useForm은 그 다음을 진행시키지 않았다. 따라서 백엔드에서 설계한대로 yup에서 null도 받아들일 수 있도록 .nullable()을 추가했다.
마무리
함수를 잘못 짰거나, 통신을 잘못 했거나... 둘 중 하나에서 오류가 날 것이라 믿어 의심치 않았던 나에게 오류는 검증에서도 충분히 날 수 있다는 것을 알게 해주었다. 그리고 react-hook-form을 사용한다면 완전히 끝나기 전까진 formstate를 통해 오류 핸들링도 동시에 진행해야 한다는 것을 인지했다. 콘솔도, vscode도, 네트워크 탭에서도 나타지않은 오류라니... 나를 매우 당황시켰고, 시간도 많이 뺐았겼다. 다만, 여러 시도 끝에 찾아서 해결했으니 뿌듯함은 이루 말할 수 없다.
'FE > React' 카테고리의 다른 글
coustom hook보다 일반 function이 나을 수도 있다. (0) | 2023.01.19 |
---|---|
memoization(feat. useMemo, useCallback) (0) | 2022.10.26 |
Curring (0) | 2022.10.26 |
Optimistic-UI (0) | 2022.10.25 |
Shallow Routing (0) | 2022.10.25 |