Velog의 로그인 화면을 보면, 모달 팝업 형태로 만들면 될 것 같다.
내가 구성한 로그인 모달 팝업이다.
처음으로 svg 파일을 직접 넣어서 사용했다.
그 동안 대부분 아이콘들을 svg 파일을 직접 사용하는 것이 아니라, FontAwesome에서 받아와서 사용했기 때문에.. img 태그를 사용한 것은 이번이 처음이었다.
그런데 자꾸 엑스박스로 이미지가 깨지는 오류가 있었다.
처음에는 파일 경로를 잘못 지정한 줄 알았으나, 그건 아니었고.. 그러면 svg 파일을 읽지 못하는 것인가 싶어 PNG 파일로 변환해서 사용했으나, 여전히 엑스박스...
구글링 해본 결과 이미지 경로에 require() 함수를 사용하면 해결 된다고 한다.
<img
src={require("../images/loginImage.png")}
alt="Login Image"
className="image"
/>
require() 함수를 붙여도 엑스박스가 뜬다면 default를 붙이면 해결된다.
require() 함수를 사용하면 해결되는 이유는 img 태그의 소스로 이미지를 직접 보내주는 것이 아니라, require() 함수에서 return 된 객체가 보내지기 때문이다.
하단의 회원가입 버튼을 클릭하면 모달 창이 새로 띄워지는 것이 아니라 내용만 변하는 것을 볼 수 있었다.
처음에는 로그인 전용 모달, 회원가입 모달을 따로 만들어서 버튼을 누를 때 마다 해당 모달이 켜지고 꺼지는 방식으로 만들려고 했었다.
근데, 생각해보니까 굳이...? 라는 생각이 들더라, 그래서 그냥 내부 텍스트만 변경해주기로 코드를 짰다.
const onClickRegisterButton = () => {
if (modalState === "login") {
setModalState("register");
modalTags.title.current.innerText = "회원가입";
modalTags.method.current.innerText = "이메일로 회원가입";
modalTags.mainButton.current.innerText = "회원가입";
modalTags.subTitle.current.innerText = "소셜 계정으로 회원가입";
modalTags.message.current.innerText = "계정이 이미 있으신가요?";
modalTags.messageButton.current.innerText = "로그인";
} else if (modalState === "register") {
setModalState("login");
modalTags.title.current.innerText = "로그인";
modalTags.method.current.innerText = "이메일로 로그인";
modalTags.mainButton.current.innerText = "로그인";
modalTags.subTitle.current.innerText = "소셜 계정으로 로그인";
modalTags.message.current.innerText = "아직 회원이 아니신가요?";
modalTags.messageButton.current.innerText = "회원가입";
modalTags.userIDInputRef.current.style.backgroundColor = "white";
modalTags.userIDInputRef.current.style.color = "black";
modalTags.userIDInputRef.current.style.fontWeight = "400";
modalTags.userIDInputRef.current.value = "";
modalTags.userIDInputRef.current.removeAttribute("readOnly");
modalTags.mainButton.current.style.display = "";
console.log(modalTags.userIDInputRef.current);
}
};
modalState를 상태로 설정하여, 현재 modal 이 로그인 화면인지, 회원가입 화면인지 파악하고, 그에 맞는 텍스트로 설정한다.
좀 무식한 방법인 것 같긴 한데.. 더 나은 방법을 생각해내지 못했다. 😅
정상적인 이메일로 판단되면 위와 같이 이메일 input이 하나의 div 태그로 변하는 것 같다.
이 부분에서 조금은 애를 먹었던 것 같다.
일단 css-module을 이용하면 classList.toggle()이 정상적으로 동작하지 않았다.
새로운 클래스가 등록돼도 좀 이상하게 동작하는 것 같아서.. header.js 는 그냥 css 로 작성하기로 결정했다.
hide 클래스를 지정하여 정상적인 이메일이 제출되면 input과 button에 hide 클래스를 지정하여 안보이게 숨기고, 성공 메세지의 hide 클래스를 제거하여 보여지게 하는 방법으로 코드를 짰다.
그런데.. 이상하게 Link 태그가 hide 가 안되더라.. 이유를 모르겠다. 지금 다시 구현하려고 하니까 또 된다. 나한테 왜 그러는거야...
아무튼 그래서 처음 구상했던 새로운 성공 메세지 div를 만들지 않고, 기존의 input과 button을 이용해서 성공메세지를 띄우기로 했다.
modalTags.userIDInputRef.current.style.backgroundColor =
"var(--velog-white-green)";
modalTags.userIDInputRef.current.style.color = "var(--velog-green)";
modalTags.userIDInputRef.current.style.fontWeight = "600";
modalTags.userIDInputRef.current.value ="✔️ 회원가입 링크가 이메일로 전송되었습니다.";
modalTags.userIDInputRef.current.readOnly = "true";
modalTags.mainButton.current.style.display = "none";
위 코드 처럼 input 태그의 backgroundColor 수동으로 지정하고, button은 display: "none"으로 감춰버렸다.
그렇게 만들어진 내 jelog 회원가입 화면이다.
꽤나 그럴싸 하게 만든 것 같다... 껍데기 뿐이지만..ㅎㅎ
지금까지는 껍데기만 만들었다면, 이제는 제대로 동작하는 로그인 기능을 만들어야지 않겠나..
일단, 아직 DB가 없기 때문에.. 리액트의 useState() 를 사용해서 계정을 등록하는 방식으로 구현해봤다.
setAccountList([
...accountList,
{ id: userID.split("@")[0], account: userID },
]);
위 코드와 같이 input으로 들어오는 userID는 전제 이메일이 들어오기 때문에, @ 앞으로 id를 지정하고, account는 전제 이메일을 지정해서 accountList에 넣어주었다.
Regular Expression // RegExp
정규 표현식은 특정한 패턴의 문자열을 찾거나 매치하는데 사용되는 문자열의 집합이다.
정규 표현식은 다양한 문자와 메타 문자들로 구성되고, 각 문자와 메타 문자들은 특정한 의미를 갖는다.정규 표현식에서 자주 사용되는 문자와 메타 문자의 예시
.
: 어떤 문자와 일치한다. 예를 들어,/a.c/
는 "abc", "aac", "a1c" 등과 일치한다.*
: 바로 앞에 있는 패턴이 0회 이상 반복되는 문자열과 일치한다. 예를 들어,/ab*c/
는 "ac", "abc", "abbc", "abbbc" 등과 일치한다.+
: 바로 앞에 있는 패턴이 1회 이상 반복되는 문자열과 일치한다. 예를 들어,/ab+c/
는 "abc", "abbc", "abbbc" 등과 일치하지만 "ac"와 일치하지 않는다.?
: 바로 앞에 있는 패턴이 0회 또는 1회 반복되는 문자열과 일치한다. 예를 들어,/ab?c/
는 "ac"와 "abc"와 일치하지만 "abbc"는 일치하지 않는다.[]
: 괄호 안에 있는 문자 중 하나와 일치한다. 예를 들어,/[abc]/
는 "a", "b", "c"와 일치한다.[^]
: 괄호 안에 있는 문자를 제외한 다른 문자와 일치한다. 예를들어,[^abc]
는 "d", "e", "f"와 일치한다.()
: 괄호 안에 있는 패턴을 그룹으로 묶는다. 예를 들어,/(ab)+c/
는 "abc", "ababc"와 일치한다.
const foundResult = accountList.find((item) => item.account === userID);
if (foundResult) {
setUserID(userID.split("@")[0]);
setShowModal(false);
setIsLogin(true);
} else {
modalTags.alertPopUP.current.style.display = "flex";
showAlertPopUp();
}
array.find() 함수를 사용해서 accountList에 사용자가 입력한 계정이 존재하는지 확인하고, 존재하면 로그인, 그렇지 않으면 경고창을 띄운다.
Array.prototype.find()
JavaScript의 배열 메서드 중 하나로, 배열에서 주어진 조건을 만족하는 첫 번째 요소를 찾아 반환한다.
조건을 만족하는 요소가 없을 경우 'undefined'를 반환한다.
벨로그의 경우 로그인이나 회원가입에서 잘못된 입력이 있을 경우, 우측 상단에 알림이 띄워진다.
이 부분은 어떻게.. 구현할까 생각하다. modal 태그에 넣기로 결정했다.
평상시에는 hide 클래스로 보여지지 않지만, 잘못된 입력이 제출되면 hide 클래스를 삭제하여 사용자에게 노출되도록 하는 방법으로 코드를 짰다.
else {
console.log("올바른 이메일 형식이 아닙니다.");
modalTags.alertPopUP.current.style.display = "flex";
showAlertPopUp();
}
====================================================================
const showAlertPopUp = () => {
setTimeout(() => {
modalTags.alertPopUP.current.style.display = "none";
}, 2000);
};
약 2초정도 알림 팝업이 보여지고 사라지기 때문에, setTimeout 함수를 사용하여 2초가 지나면 팝업이 보이지 않도록 속성을 수정한다.
그렇게 만들어진 나의 결과물이다. 아직 애니메이션은 없지만, 그래도 동작은 완벽하게 하는 모습이다.
로그인 구현을 완료하고.. 기존에 포스트 페이지 URL이 그냥 /post/index 였던 것을, /post/userID/index 로 수정해야 그 게시글을 누가 작성했는지, 권한을 판단할 수 있으니.. 포스팅 URL을 수정했다.
<Link
to={`/post/${item.userID}/${item.id}`}
key={index}
className="postBox"
>
<div className="innerBox">
<div className="thumbnail">THUMBNAIL</div>
<span className="title">{item.title}</span>
<span className="content">{item.content}</span>
</div>
<span className="date">{item.date}</span>
</Link>
==========================================================
<Route
path="/post/:userID/:id"
element=
{ <PostPage
postList={postList}
setPostList={setPostList}
userID={userID}
/>
}
/>
위와 같이 Link와 Route 태그를 수정하였다.
정상적으로 URL이 사용자 ID에 맞게 설정된 것을 볼 수 있다.
이제 앞으로 할 것은, 게시글의 작성자와 현재 로그인된 사용자를 서로 비교하여 같으면 게시글을 수정, 삭제 할 수 있게하는 기능, 그리고 사용자마다 like 버튼은 한 번만 가능 하도록 하는 기능을 구현 해 보면 좋을 것 같다.
포스트에 이미지를 첨부하는 기능도 구현해야하고.. 생각보다 벨로그에 기능이 꽤나 많았구나...