✅- 이메일 드롭다운 선택 탭 내용을 이메일 주소에 반영
1. 드롭다운에서 특정 이메일을 선택하면 : input에 내가 쓴 이메일 + 드롭다운에 선택한 이메일
둘을 합쳐서 formData의 email로 보내고싶어.
드롭다운에서 '직접 입력' 선택시 : input에 내가 쓴 이메일이 그대로 formData로 전달되기
'직접입력' 선택했는데 @랑 .com .net 등의 이메일 형식에 맞는 input값이 들어오지 않았다면
'이메일 형식이 올바르지 않습니다' 라고 에러 메시지를 보여주고싶어.
->
input type에 email이라고 전달하기
register안에 pattern으로 이메일 형식을 지정해두고, 여기에 맞지 않으면 해당 에러 메시지를 커스텀해서 출력할 수 있다! 우왕…
// LabelInputSet.tsx 일부
<input
id={id}
autoComplete={id}
{...register(id, {
required: `${labelText}을(를) 입력해 주세요.`,
pattern:
type === "email" // 이메일 형식 검증
? {
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
message: "이메일 형식이 올바르지 않습니다.",
}
: undefined,
})}
placeholder={placeholder}
type={type}
className={styles.input}
style={{ width }}
/>
const { setValue, watch } = useFormContext();
// ✅ react-hook-form을 활용하여 이메일 값 관리
const email = watch("email");
// ✅ 현재 입력된 이메일 값 가져오기
…
<EmailDropdown
email={email}
setEmail={(updatedEmail) =>
setValue("email", updatedEmail)}
/>
setValue를 활용해서 드롭다운에서 선택하면 updateEmail로 수정되도록 하는 로직으로 만들면 됐다!
🛠️ 문제 : 이메일 드롭다운이랑 LabelInputSet.tsx랑 별개의 컴포넌트로 되어있다보니까 유저가 작성한 이메일이랑 드롭다운에 선택된 이메일형식이랑 합치는게 어려웠다.
그래서 둘다 같은 email을 바라보게 하려면 useFormContext가 필요했다.
FindUserInfoProcessContents.tsx에서 CommonForm으로 감싸주고 여기 컴포넌트 안에서 useFormContext 불러와서 setValue하니까 자꾸 setValue가 null이라고 ts에러가 뜨는거야.
에러 : Uncaught TypeError: Cannot destructure property 'setValue' of 'useFormContext(...)' as it is null.
원인 : useForm()을 호출하는 컴포넌트가 부모에 있어야지 FindUserInfoProcessContents.tsx
여기서 context를 쓸수 있는거였어.
부모에서 provider가 감싸주지 않는거나 마찬가지였던거야.
해결 : CommonForm을 상위 컴포넌트인 FindUserInfoPage.tsx
여기서 감싸주니까 해결됐음!
🛠️ 문제 : 이메일 형식이 맞지 않는 경우 toastify처럼 말풍선으로 에러 메시지가 뜸 -> 에러메시지 보여주는 p태그 작성해놓은 곳에서 뜨게 만들고싶다.
해결
<form>
에 noValidate 추가
🔍 noValidate를 추가하면 브라우저의 기본 검증이 비활성화되고, react-hook-form에서만 오류를 처리할 수 있어!
문제 : 이메일 드롭다운 계속 선택하면 이메일 뒤에 @이메일 이 계속 중첩해서 쌓임
->
const handleDomainSelect = (domain: string) => {
setSelectedDomain(domain);
setIsDropdownOpen(false);
if (domain === "직접 입력") {
setEmail(email); // 직접 입력 선택 시 기존 이메일 유지
} else {
// setEmail(email ? `${email}@${domain}` : "");
// 선택한 도메인과 결합하여 이메일 설정
// 기존 email에서 '@'가 포함된 경우 앞부분만 추출
const [localPart] = email.split("@");
setEmail(localPart ? `${localPart}@${domain}` : "");
}
};
-> 이메일 형식이 계속 올바르지 않대
-> 제출 함수 문제인지, novalidate때문인지…
gpt는 useForm({mode:onChange})로 바꾸고
LabelInputSet에서도
const inputValue = watch(id); // 입력값 변경 감지
써서
{errors[id] && inputValue && (
<p className={styles.errorMessage}>{errors[id]?.message}</p> )}
이렇게 리팩토링할것을 추천해줌
그래서 계속 이것저것 만져봤는데
계속 이메일 형식이 올바르지 않대ㅜㅜ
-> 해결
근데 novalidate 없애고 보니까
내가 계속 이메일주소를 한글로 쓰고있었는데 그게 문제였음….
영어로 쓰도록 정규식이 되어있는거였음….
아직 이메일 형식 유효성 검증해주는 백엔드 api가 안나와서 LabelInputSet에서 pattern으로 임시 검증을 해주고 있었다.
gpt로 이메일 형식 검증 정규식을 가져와서 활용하고 있었는데, 정규식에서 이메일 형식을 어떤 형식으로 걸러내고 있는지에 대한 이해 없이 가져와서 생긴 문제였다...
{ Controller ) from react-hook-form
const { control } = useFormContext()
select, option 태그로 리팩토링해보려고 했으나,
css적용이 원활하지 않아서 div 드롭다운으로 그대로 적용
Controller의 render 안에서 드롭다운을 보여주고 있는데,
드롭다운 선택하면 onClick 안에서 field.onChange(domain)으로 해서
• selectedDomain과 동기화하고, field.onChange를 통해 선택된 값을 React Hook Form과 연동하는 방식으로 충분히 구현이 가능합니다.
현재 코드에서는 field.onChange(domain)이 이미 구현되어 있으므로 올바르게 작동할 것입니다. 🐾
(selectedDomain)을 field.value와 동기화해야 합니다. 현재는 field.onChange(domain)을 호출하여 값을 업데이트하고 있으므로, 이 부분은 이미 처리되었습니다.
이메일 형식 검증을 input값으로만 하고 있음
-> ‘직접 입력’ 선택시에는 이게 맞는데,
드롭다운 항목에서 다른 이메일 형식을 선택한 경우에는 ‘@도메인’ 합친 이메일을 보내서 검증하도록 변경 필요
드롭다운 진짜 어렵네…ㅠㅠ
드롭다운 선택시
field.onChange(field.value)
드롭다운 기본으로 보여지는 부분 {field.value || “도메인 선택”}
(문제)
❌1. 1차 고민
-> input defaultValue를 localPart로 지정해두면 되지 않을까?!
(안됨)
전송 클릭할때마다 계속 @뒷주소가 계속 붙음
이메일주소 써놓고 드롭다운 클릭해도 이메일 형식 바로 검증하지 않음
❌
-> 이메일 형식 검증 주석해버림
☑️
그래, 딱 필요한것만 해놓자 이메일 형식 검증 이런거 하지말자
딱 지금 해야되는게
이메일 보내는거, 이메일 받는거지
기본!! : 입력한 이메일 주소 + 드롭다운 합쳐서 보내기만 하면 돼
드롭다운 클릭했을때 계속 연달아 붙는거 방지해야해.
(드롭다운 클릭시 value에 @뒷주소 있으면 이번에 선택한 다음 도메인으로 바꿔줘야해)
input에 빈값’ 또는 도메인 선택’에 두고 전송하면 전송 안되게 만들어야돼
(빈값으로 전송하면 undefined@undefiend 되고있음)
(지금은 @ 하나만 붙고있음)
✅- ‘전송’클릭시 드롭다운 항목을 붙이지 말고, 드롭다운 선택시 바로 그냥 setValue해버리자
기본!! : 유저정보중 이메일을 가져온다.
@뒷주소가 드롭다운에 있을때 : 드롭다운의 해당 도메인을 선택시켜놓는다, 앞주소는 풀주소 넣어놓기
@뒷주소가 드롭다운에 없을때 : 드롭다운을 ‘직접 입력’으로 선택시켜놓는다,
앞주소는 풀주소
수정 후 제출할때 : 수정된 내용이 있으면 업데이트,
없으면 그대로 둔다.
와 오늘 하루종일 이거만하네
( 이메일 input에 보여지는 값이랑 useForm에서 관리하는 value랑 달라야하는데… 드롭다운 클릭하면 setValue로 input에 입력했던 값을 아예 바꿔버리니까 input에는 합쳐진 이메일이 보여지고 있었다. )
-> 로직 계획
useForm에
input에 입력한 값을 보내는게 아니라
input에 입력한 value값에 특정 텍스트를 추가한 새로운 newInputValue를 useState로 정해놓고 이 useState값을 useForm에 보낼수 있어?
( 0 ) 구현 예제
import React, { useState } from "react";
import { useForm } from "react-hook-form";
interface FormValues {
inputValue: string;
}
const ExampleForm: React.FC = () => {
const { register, handleSubmit } = useForm<FormValues>();
const [newInputValue, setNewInputValue] = useState("");
const onSubmit = (data: FormValues) => {
const modifiedData = {
...data,
inputValue: newInputValue, // useState로 관리하는 값 전달
};
console.log("제출된 데이터:", modifiedData);
};
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
setNewInputValue(`${value} - 추가 텍스트`); // 입력값에 특정 텍스트 추가
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register("inputValue")} // useForm으로 등록
onChange={handleInputChange} // 값 변경 시 useState 업데이트
/>
<button type="submit">제출</button>
</form>
);
};
export default ExampleForm;
이메일주소 input value 그대로 두고,
const [ mergedEmail, setMergedEmail ] = useState(“”)
이렇게 지정해둔 mergedEmail을
onSubmit할때 보내는 방법은 어때?!
-> 공통컴포넌트 안쓰면서,
input에 onChange 함수 보내주기
or
드롭다운 클릭시
1. getValues(“email”)
2. setMergedEmail( 이메일 + 클릭한 도메인 )
이 다음 onSubmit 함수에서
const modifiedData = {
...data, inputValue: newInputValue, // useState로 관리하는 값 전달 };
console.log("제출된 데이터:", modifiedData);
}
이렇게 처리하는거지. 이 modifiedData를 서버로 전달하면 되는거지!!!
드롭다운 클릭하는건 얼마든지 계속 클릭해도 되는데,
전송 눌렀을때는 최종 드롭다운항목과 이메일 input value를 mergedEmail로 넣어서 합친 다음,
modifiedData로 만들어서 그걸 전송하는거야!
// FindUserInfoPage.tsx
const methods = useForm<FindUserMethodsType>();
const { watch } = methods;
const emailInputValue = watch("email");
const emailDomain = watch("emailDomain");
// 인증번호 요청 (임시)
const handleRequestVerificationCode = (data: any) => {
if (!emailDomain) {
// FIXME - 백엔드 api 참고하여 이메일 형식 에러 처리
alert("이메일을 입력해주세요");
return;
}
if (emailDomain !== "직접 입력") {
const modifiedData = {
...data,
email: `${emailInputValue}@${emailDomain}`,
};
console.log("인증번호 요청 1:", modifiedData);
setIsCodeSent(true);
return;
}
console.log("인증번호 요청 2:", data);
setIsCodeSent(true);
};
// 인증번호 검증 (임시)
const handleVerifyCode = (formData: any) => {
// 이름, 전화번호, 인증번호를 한번에 제출하는 로직
// -> FIXME : 백엔드 api 참고 후 인증번호만 제출할지 확인 필요
console.log("인증번호 검증 및 아이디 찾기:", formData);
if (`/userInfo/${url}` === ROUTE_PATHS.FIND_USER_ID) {
navigate(ROUTE_PATHS.FIND_USER_ID_RESULT);
} else {
navigate(ROUTE_PATHS.CHANGE_PASSWORD);
}
};
// form submit 함수 (임시)
const handleSubmitTest = (formData: any) => {
const cleanedFormData = { ...formData };
if (selectedTab === "email") {
delete cleanedFormData.phone; // 이메일 선택 시 phone 필드 제거
} else if (selectedTab === "phone") {
delete cleanedFormData.email; // 휴대폰 선택 시 email 필드 제거
}
if (!isCodeSent) {
handleRequestVerificationCode(cleanedFormData); // 인증번호 요청
} else {
handleVerifyCode(cleanedFormData); // 인증번호 검증
}
};
드롭다운 항목 클릭할때마다 input값에 도메인 붙게 하면 클릭때마다 계속 주소에 도메인 붙게됨
-> 드롭다운 클릭은 계속 할수 있어도, ‘인증번호 전송’눌렀을때만 마지막 드롭다운 항목이 이메일 주소에 붙어서 최종 전송되도록 로직을 만들면 됐다!
form태그의 handleSubmit 함수에서는
-> ‘인증번호 전송’ 시 함수 실행,
‘완료’시 함수 실행
📝 최종 정리
이메일 드롭다운에서 계속 해결하지 못하고 있던 부분들 중 가장 어려웠던 부분을 정리해보자.
이메일 input에 값을 입력하고 드롭다운 중 '직접 입력'이 아닌 항목을 선택하고 제출버튼을 누르면 백엔드에 보내는 값은 '이메일 input값' + '이메일 도메인 드롭다운 항목'이 합쳐진 텍스트를 보내되, input창에 보여지는 텍스트는 여전히 이메일 input값만 보이도록 하고 싶었다.
그런데 제출 시 setValue로 이메일+도메인 값을 바꿔버리니까 input텍스트창에 합쳐진 이메일이 그대로 보여졌다. 내가 원한건 그게 아니었는데...
그렇다고 react-hook-form을 사용하는 input 옵션에 defaultValue를 사용한다해도 어쨌든 value 자체가 바뀌는 순간 바뀐 텍스트가 보여버리는걸..
input에 보여지는 텍스트랑 value랑 다르게 만드는 방법은 없으니까 해결 방법이 없다고 생각했다.
사실 form태그의 handleSubmit 함수 원리를 제대로 파악하지 못해서 생긴 문제였던 것 같다. handleSubmit안에 들어가는 함수에 자동으로 인자로 들어오는 data는 백엔드로 보낼때 수정이 아예 불가하다고 착각했다.
[ 해결 방법 ]
1. mergedEmail을 변수로 놓고, 제출 버튼 클릭 시 이메일 + 도메인으로 합쳐서 mergedEmail에 set해준다.
2. handleSubmit으로 보내는 함수 안에서의 data를 바꿔준다.
const modifiedData = {...data, email: mergedEmail}
이런식으로.
3. 화면 렌더링시 가져온 이메일을 useEffect에서 @기준으로 분리하고, 앞의 값만 setValue로 input 텍스트로 넣어버리기!!