motion 프로젝트를 조금씩 수정 하면서, 이번에는 유효성 검사를 진행하기 위해 유튜브 부분의 코드를 다시 살펴봤다.
이미지 첨부 부분의 유효성 검사는 이미지 파일인 경우에만 올릴 수 있어서 문제가 없었는데, 유튜브 링크의 유효성 검사는 조금 까다로웠고, 원하는 동작을 만들기 위해 총 3번의 시행착오가 있었는데 그 과정을 기록하고자 한다.
먼저 주어진 링크가 유효한 YouTube 링크인지를 판별하기 위한 코드가 필요해, 검색을 통해 작성했다.
const isValidYouTubeLink = (link: string): boolean => {
// 유효한 YouTube 도메인 패턴
const youtubeDomainPattern = /^(https?:\/\/)?(www\.)?youtube\.com(\/.*)?$/;
const shortLinkPattern = /^(https?:\/\/)?(www\.)?youtu\.be(\/.*)?$/;
// 정규식 패턴을 이용하여 링크를 검증
if (youtubeDomainPattern.test(link) || shortLinkPattern.test(link)) {
// 도메인 패턴 또는 짧은 링크 패턴에 일치하면 유효한 링크로 판단
return true;
}
return false;
};
// 테스트
console.log(isValidYouTubeLink("https://www.youtube.com/watch?v=NpEaa2P7qZI")); // true
console.log(isValidYouTubeLink("https://www.google.com")); // false
이제 이 함수를 이용해서
를 하려고 한다.
위 과정으로 올바르지 않은 유튜브 링크일 때 바로 placeholder 유튜브가 뜨도록 만들었는데, 막상 완성하고 나니 UX가 좋지 못하다는 느낌을 받았다.
const isValidYouTubeLink = (link: string): boolean => {
// 유효한 YouTube 도메인 패턴
const youtubeDomainPattern = /^(https?:\/\/)?(www\.)?youtube\.com(\/.*)?$/;
const shortLinkPattern = /^(https?:\/\/)?(www\.)?youtu\.be(\/.*)?$/;
// 정규식 패턴을 이용하여 링크를 검증
if (youtubeDomainPattern.test(link) || shortLinkPattern.test(link)) {
// 도메인 패턴 또는 짧은 링크 패턴에 일치하면 유효한 링크로 판단
return true;
}
return false;
};
const handleVideoChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (isValidYouTubeLink(event.target.value)) {
setVideoUrlState(event.target.value);
} else setVideoUrlState("https://youtu.be/NpEaa2P7qZI"); // ** 해당 부분
};
// ...
// 등록 버튼을 누르면 실행되는 함수. 입력값을 dispatch로 보냄
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newPost = {
id: Date.now(),
title,
content,
imageUrl: imageFile,
videoUrl: videoUrlState,
task: taskState,
category,
};
console.log("newPost 객체", newPost);
if (imageFile || videoUrlState) {
dispatch(setImage(imageFile));
dispatch(setPreviewImage(imageFile));
dispatch(addPost(newPost));
setTitle("");
setContent("");
} else {
dispatch(addPost(newPost));
setTitle("");
setContent("");
}
console.log("taskState :", taskState);
console.log("category :", category);
onClose();
};
// .. 아래로 리턴문
{category === "video" && (
<>
<label>
<input
placeholder="YouTube video URL"
type="text"
value={videoUrlState}
onChange={handleVideoChange}
/>
</label>
<YouTubeIframe url={videoUrlState} />
</>
)}
이렇게 작성하면 ‘올바르지 않은 유튜브 링크’일 때 바로 placeholder 유튜브가 떠서 사용자 입장에서 뜬금없이 느껴질 수도 있을 것 같다.
차라리 보편적인 방법으로 ‘올바르지 않은 링크입니다. 링크를 다시 확인해 주세요.’ 라는 문구가 아래에 뜨는 것이 좋을 것 같아 그렇게 수정하기로 했다.
<p>
태그로 경고 문구 띄우기올바르지 않은 유튜브 링크인지 구분하기 위해 [invalidLink, setInvalidLink] = useState(false) 를 설정하고,
handleVideoChange 함수 안에 올바르지 않은 링크가 들어와서 false값이 리턴되면
invalidLink 상태를 true로 변경,
아래 리턴문에서 invalidLink가 true이면 <p>올바르지 않은 링크입니다. 링크를 다시 확인해 주세요.</p>
문구를 추가한다
const Modal = () => {
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [imageFile, setImageFile] = useState("");
const [videoUrlState, setVideoUrlState] = useState("");
const [taskState, setTaskState] = useState(false);
const [invalidLink, setInvalidLink] = useState(false);
// ...
const isValidYouTubeLink = (link: string): boolean => {
// 유효한 YouTube 도메인 패턴
const youtubeDomainPattern = /^(https?:\/\/)?(www\.)?youtube\.com(\/.*)?$/;
const shortLinkPattern = /^(https?:\/\/)?(www\.)?youtu\.be(\/.*)?$/;
// 정규식 패턴을 이용하여 링크를 검증
if (youtubeDomainPattern.test(link) || shortLinkPattern.test(link)) {
// 도메인 패턴 또는 짧은 링크 패턴에 일치하면 유효한 링크로 판단
return true;
}
return false;
};
const handleVideoChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (isValidYouTubeLink(event.target.value)) {
setInvalidLink(false);
setVideoUrlState(event.target.value); // 수정해야 할 부분**
} else setInvalidLink(true);
};
// ...
{category === "video" && (
<>
<label>
<input
placeholder="YouTube video URL"
type="text"
value={videoUrlState} // 수정해야 할 부분**
// 특정한 값을 넣는 것이 아닌 이상 value는 필요하지 않다. ex) id 기억하기 -> value값 기억하는 경우
onChange={handleVideoChange}
/>
{invalidLink ? (
<p className="text-xs text-red-200">
올바르지 않은 링크입니다. 링크를 다시 확인해 주세요.
</p>
) : null}
</label>
<YouTubeIframe url={videoUrlState} />
</>
)}
그런데 이렇게 입력하니 옳은 링크를 입력한 뒤 백스페이스로 링크 지우기를 시도했을 때
아래 gif 처럼 youtu.be 까지만 지워지고, 그 이후로 지워지지 않는 문제가 발생했다.
앗, 큰일이당
원인은 위 코드에서 주석 표기한 두 곳인
앞서 잘못 쓴 코드에서는 gif 사진처럼 유튜브 링크를 옳게 넣었을 때
인 상태,
백스페이스로 지우기를 시도했을 때 (유튜브 링크가 옳지 않을 때)는
그래서 유튜브 링크가 옳지 않을 때도, 입력값에 따라 input 안의 내용이 변경되려면
handleVideoChange를 통해 setVideoUrlState(event.target.value)가 작동되어야 하고
input 태그 안의 value를 지워야 한다
input 태그 안의 value는 특정한 값을 넣는 것이 아닌 이상 추가할 필요가 없었다.
예를 들면 로그인 화면에서 “아이디 기억하기” 처럼 value 값을 기억해야 하는 경우에 쓰이기 때문에
지금 같은 경우 value가 필요하지 않으므로 input 안의 value를 지워준다
const isValidYouTubeLink = (link: string): boolean => {
// 유효한 YouTube 도메인 패턴
const youtubeDomainPattern = /^(https?:\/\/)?(www\.)?youtube\.com(\/.*)?$/;
const shortLinkPattern = /^(https?:\/\/)?(www\.)?youtu\.be(\/.*)?$/;
// 정규식 패턴을 이용하여 링크를 검증
if (youtubeDomainPattern.test(link) || shortLinkPattern.test(link)) {
// 도메인 패턴 또는 짧은 링크 패턴에 일치하면 유효한 링크로 판단
return true;
}
return false;
};
const handleVideoChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (isValidYouTubeLink(event.target.value)) {
setInvalidLink(false);
// ** setVideoUrlState(event.target.value) : 해당 부분을 지워준다
} else setInvalidLink(true);
// ** 계속 input에 넣는 값을 업데이트 할 수 있게 밖으로 빼낸다
setVideoUrlState(event.target.value);
};
// .. 아래로 리턴문
{category === "video" && (
<>
<label>
<input
placeholder="YouTube video URL"
type="text"
// value={videoUrlState}
// 특정한 값을 넣는 것이 아닌 이상 value는 필요하지 않다. ex) 아이디 기억하기 -> value 값 기억하는 경우
onChange={handleVideoChange}
/>
{invalidLink ? (
<p className="text-xs text-red-200">
올바르지 않은 링크입니다. 링크를 다시 확인해 주세요.
</p>
) : null}
</label>
<YouTubeIframe url={videoUrlState} />
</>
)}
위 코드처럼 수정했더니 의도한 대로 유효성 검사와 함께 백스페이스가 잘 작동하는 것을 확인할 수 있었다 :)