Frontend ๐Ÿ“š/React

[React] TMDB API๋ฅผ ํ™œ์šฉํ•œ ๋„ทํ”Œ๋ฆญ์Šค ํด๋ก  ์ฝ”๋”ฉ - 4. ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐ ๋กœ๊ทธ์ธ ๋ฐ ํšŒ์›๊ฐ€์ž… ํŒ์—… ๊ตฌํ˜„

leejaejae 2024. 12. 7. 00:38

๋กœ๊ทธ์ธ ๋ฐ ํšŒ์›๊ฐ€์ž… ํŒ์—… ๊ตฌํ˜„

์ด ํฌ์ŠคํŒ…์—์„œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ๋กœ๊ทธ์ธ ํŒ์—…์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ํŒ์—… ์•ˆ์—์„œ ๋กœ๊ทธ์ธ๊ณผ ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ์„ ๋ชจ๋‘ ์ง€์›ํ•˜๋ฉฐ, ๋กœ๊ทธ์ธ ์œ ์ง€ ๊ธฐ๋Šฅ๋„ ์ถ”๊ฐ€ํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ์ธ์ฆ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.


1. ์ฃผ์š” ๊ธฐ๋Šฅ

1.1 ๋กœ๊ทธ์ธ

๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅ๋œ ์‚ฌ์šฉ์ž ๋ชฉ๋ก๊ณผ ๋น„๊ตํ•˜์—ฌ ์ธ์ฆํ•ฉ๋‹ˆ๋‹ค. ์ธ์ฆ์— ์„ฑ๊ณตํ•˜๋ฉด isLoggedIn ๊ฐ’์„ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ๋œ ์ƒํƒœ๋กœ ์•ฑ์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

1.2 ํšŒ์›๊ฐ€์ž…

ํšŒ์›๊ฐ€์ž…์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ ํ•„๋“œ๋ฅผ ์ œ๊ณตํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ •ํ™•ํžˆ ์ž…๋ ฅํ–ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1.3 ๋กœ๊ทธ์ธ ์œ ์ง€ ๊ธฐ๋Šฅ

๋กœ๊ทธ์ธ ์œ ์ง€ ์ฒดํฌ๋ฐ•์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ์‹œ keepLogin ๊ฐ’์„ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅํ•˜๋ฉด, ์ดํ›„์— ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ๋กœ ๊ณ ์นจ์„ ํ•˜๊ฑฐ๋‚˜ ํŽ˜์ด์ง€๋ฅผ ๋ฒ—์–ด๋‚ฌ๋‹ค๊ฐ€ ๋Œ์•„์™€๋„ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


2. ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ด€๋ฆฌ

์‚ฌ์šฉ์ž ์ธ์ฆ์€ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์‚ฌ์šฉ์ž์˜ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ sessionStorage๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๊ณ , ๋กœ๊ทธ์ธ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ํ™”๋ฉด์„ ๋‹ค๋ฅด๊ฒŒ ํ‘œ์‹œํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

2.1 ๋กœ๊ทธ์ธ ์ƒํƒœ ๊ด€๋ฆฌ

์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•˜๋ฉด sessionStorage์— ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ , ๋กœ๊ทธ์•„์›ƒ ์‹œ ํ•ด๋‹น ์ •๋ณด๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ์ดํŠธ๋ฅผ ๋– ๋‚˜๋„ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// App.js
useEffect(() => {
  const loggedIn = sessionStorage.getItem("isLoggedIn") === "true";
  setIsLoggedIn(loggedIn);
  setIsPopupVisible(!loggedIn); // ๋กœ๊ทธ์•„์›ƒ ์ƒํƒœ๋ฉด ํŒ์—… ํ‘œ์‹œ
}, []);

2.2 ๋กœ๊ทธ์ธ ๋ฐ ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ

๋กœ๊ทธ์ธ ์‹œ sessionStorage์— ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ , ๋กœ๊ทธ์•„์›ƒ ์‹œ ํ•ด๋‹น ์ •๋ณด๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ์•„์›ƒ ํ›„ ํŒ์—…์„ ๋‹ค์‹œ ํ‘œ์‹œํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

// App.js
const handleLogin = () => {
  sessionStorage.setItem("isLoggedIn", true);
  setIsLoggedIn(true);
  setIsPopupVisible(false); // ํŒ์—… ๋‹ซ๊ธฐ
};

const handleLogout = () => {
  sessionStorage.removeItem("isLoggedIn");
  setIsLoggedIn(false);
  setIsPopupVisible(true); // ๋กœ๊ทธ์•„์›ƒ ํ›„ ํŒ์—… ๋‹ค์‹œ ํ‘œ์‹œ
};

2.3 ์‚ฌ์šฉ์ž ์ •๋ณด ์ดˆ๊ธฐํ™”

๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ๋นˆ ๋ฐฐ์—ด์„ ์ €์žฅํ•˜๋Š” ์ฝ”๋“œ๋„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. initializeUserStorage ํ•จ์ˆ˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ ์‹คํ–‰๋˜๋ฉฐ, "users"๋ผ๋Š” ํ‚ค๋กœ ๋นˆ ๋ฐฐ์—ด์„ ์ €์žฅํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ๊ธฐ๋ณธ๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

// utils/storage.js
export const initializeUserStorage = () => {
  if (!getFromLocalStorage("users")) {
    saveToLocalStorage("users", []);
  }
};

์ด ํ•จ์ˆ˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘๋  ๋•Œ ํ˜ธ์ถœ๋˜๋ฉฐ, ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ๋นˆ ๋ฐฐ์—ด์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ฒ˜์Œ ์‚ฌ์šฉํ•  ๋•Œ ๋นˆ ์‚ฌ์šฉ์ž ๋ชฉ๋ก์„ ์„ค์ •ํ•˜๊ณ , ์ดํ›„์— ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ์ €์žฅ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.


3. ์ฝ”๋“œ ์„ค๋ช…

3.1 ์ƒํƒœ ๊ด€๋ฆฌ

๋กœ๊ทธ์ธ ํŒ์—… ์ปดํฌ๋„ŒํŠธ๋Š” isSignUp, form, error, keepLogin ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

  • isSignUp: true์ผ ๊ฒฝ์šฐ ํšŒ์›๊ฐ€์ž… ํผ์„ ํ‘œ์‹œํ•˜๊ณ , false์ผ ๊ฒฝ์šฐ ๋กœ๊ทธ์ธ ํผ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
  • form: ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ email, password, confirmPassword ๊ฐ’์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • error: ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜์—ฌ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์‹คํŒจ ์‹œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜ค๋ฅ˜๋ฅผ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.
  • keepLogin: ๋กœ๊ทธ์ธ ์œ ์ง€ ์ฒดํฌ๋ฐ•์Šค ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
const [isSignUp, setIsSignUp] = useState(false); // true๋ฉด ํšŒ์›๊ฐ€์ž… ๋ชจ๋“œ
const [form, setForm] = useState({
  email: "",
  password: "",
  confirmPassword: "",
});
const [error, setError] = useState("");
const [keepLogin, setKeepLogin] = useState(false);

3.2 ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€ ์ดˆ๊ธฐํ™”

initializeUserStorage ํ•จ์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. users๋ผ๋Š” ํ‚ค๋กœ ์ €์žฅ๋œ ์‚ฌ์šฉ์ž ๋ชฉ๋ก์ด ์—†์œผ๋ฉด ๋นˆ ๋ฐฐ์—ด์„ ์ €์žฅํ•˜์—ฌ, ์ดํ›„ ๋กœ๊ทธ์ธ ๋ฐ ํšŒ์›๊ฐ€์ž…์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋“ฑ๋ก๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

useEffect(() => {
  initializeUserStorage();
}, []);

3.3 ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ

์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด, ์ž…๋ ฅํ•œ ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์—์„œ ๊ฐ€์ ธ์˜จ ์‚ฌ์šฉ์ž ๋ชฉ๋ก๊ณผ ๋น„๊ตํ•˜์—ฌ ์ธ์ฆํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ, isLoggedIn ๊ฐ’์„ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅํ•˜๊ณ , keepLogin ์ฒดํฌ๋ฐ•์Šค ์ƒํƒœ์— ๋”ฐ๋ผ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

const handleLogin = () => {
  const { email, password } = form;
  if (!email || !password) {
    setError("์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.");
    return;
  }

  const users = getFromLocalStorage("users");
  const user = users.find(
    (user) => user.email === email && user.password === password
  );

  if (!user) {
    setError("์•„์ด๋”” ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
    return;
  }

  saveToLocalStorage("isLoggedIn", true);
  if (keepLogin) {
    saveToLocalStorage("keepLogin", true);
  }

  alert("๋กœ๊ทธ์ธ ์„ฑ๊ณต!");
  onLogin();
  onClose();
};

3.4 ํšŒ์›๊ฐ€์ž… ์ฒ˜๋ฆฌ

ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ์—์„œ๋Š” ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ์„ ํ†ตํ•ด ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž๋ฅผ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์ด๋ฉ”์ผ์ด ์ด๋ฏธ ๋“ฑ๋ก๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

const handleSignUp = () => {
  const { email, password, confirmPassword } = form;
  if (!email || !password || !confirmPassword) {
    setError("๋ชจ๋“  ํ•„๋“œ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.");
    return;
  }
  if (password !== confirmPassword) {
    setError("๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
    return;
  }

  // ์œ ์ € ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
  const users = getFromLocalStorage("users");
  const isEmailUsed = users.some((user) => user.email === email);

  if (isEmailUsed) {
    setError("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค.");
    return;
  }

  // ์ƒˆ๋กœ์šด ์œ ์ € ์ถ”๊ฐ€
  const newUser = { email, password };
  saveToLocalStorage("users", [...users, newUser]);

  alert("ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต! ๋กœ๊ทธ์ธ ํ•ด์ฃผ์„ธ์š”.");
  setIsSignUp(false);
  setForm({ email: "", password: "", confirmPassword: "" });
};

3.5 ํ™”๋ฉด ์ „ํ™˜

์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ํŒ์—…์—์„œ ๋กœ๊ทธ์ธ ๋˜๋Š” ํšŒ์›๊ฐ€์ž…์„ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฒ„ํŠผ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. isSignUp ์ƒํƒœ์— ๋”ฐ๋ผ ๋กœ๊ทธ์ธ๊ณผ ํšŒ์›๊ฐ€์ž… ํ™”๋ฉด์„ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<button type="button" onClick={() => setIsSignUp(!isSignUp)}>
  {isSignUp ? "์ด๋ฏธ ๊ณ„์ •์ด ์žˆ๋‚˜์š”? ๋กœ๊ทธ์ธ" : "๊ณ„์ •์ด ์—†๋‚˜์š”? ํšŒ์›๊ฐ€์ž…"}
</button>

3.6 ์ „์ฒด ์ฝ”๋“œ

SignInPopup.js

//components/SignInPopup.js

import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import {
  saveToLocalStorage,
  getFromLocalStorage,
  initializeUserStorage,
} from "../utils/storage";
import "../styles/components/SignInPopup.css";

const SignInPopup = ({ onClose, onLogin }) => {
  const [isSignUp, setIsSignUp] = useState(false); // true๋ฉด ํšŒ์›๊ฐ€์ž… ๋ชจ๋“œ
  const [form, setForm] = useState({
    email: "",
    password: "",
    confirmPassword: "",
  });
  const [error, setError] = useState("");
  const [keepLogin, setKeepLogin] = useState(false);
  const navigate = useNavigate();

  useEffect(() => {
    initializeUserStorage();
  }, []);

  const handleInputChange = (e) => {
    setForm({ ...form, [e.target.name]: e.target.value });
  };

  const handleLogin = () => {
    const { email, password } = form;
    if (!email || !password) {
      setError("์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.");
      return;
    }

    const users = getFromLocalStorage("users");
    const user = users.find(
      (user) => user.email === email && user.password === password
    );

    if (!user) {
      setError("์•„์ด๋”” ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
      return;
    }

    saveToLocalStorage("isLoggedIn", true);
    if (keepLogin) {
      saveToLocalStorage("keepLogin", true);
    }

    alert("๋กœ๊ทธ์ธ ์„ฑ๊ณต!");
    onLogin();
    onClose();
  };

  const handleSignUp = () => {
    const { email, password, confirmPassword } = form;
    if (!email || !password || !confirmPassword) {
      setError("๋ชจ๋“  ํ•„๋“œ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.");
      return;
    }
    if (password !== confirmPassword) {
      setError("๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
      return;
    }

    // ์œ ์ € ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
    const users = getFromLocalStorage("users");
    const isEmailUsed = users.some((user) => user.email === email);

    if (isEmailUsed) {
      setError("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค.");
      return;
    }

    // ์ƒˆ๋กœ์šด ์œ ์ € ์ถ”๊ฐ€
    const newUser = { email, password };
    saveToLocalStorage("users", [...users, newUser]);

    alert("ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต! ๋กœ๊ทธ์ธ ํ•ด์ฃผ์„ธ์š”.");
    setIsSignUp(false);
    setForm({ email: "", password: "", confirmPassword: "" });
  };

  const handleSignUpRedirect = () => {
    navigate("/signUp");
    onClose();
  };

  return (
    <div className="popup-overlay">
      <div className="popup-content">
        <h1>{isSignUp ? "ํšŒ์›๊ฐ€์ž…" : "๋กœ๊ทธ์ธ"}</h1>
        <form>
          <input
            type="email"
            name="email"
            placeholder="์ด๋ฉ”์ผ"
            value={form.email}
            onChange={handleInputChange}
          />
          <input
            type="password"
            name="password"
            placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ"
            value={form.password}
            onChange={handleInputChange}
          />
          {isSignUp && (
            <input
              type="password"
              name="confirmPassword"
              placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ"
              value={form.confirmPassword}
              onChange={handleInputChange}
            />
          )}
          {error && <p className="error">{error}</p>}
          {isSignUp ? (
            <button type="button" onClick={handleSignUp}>
              ํšŒ์›๊ฐ€์ž…
            </button>
          ) : (
            <>
              <label>
                <input
                  type="checkbox"
                  checked={keepLogin}
                  onChange={() => setKeepLogin(!keepLogin)}
                />
                ๋กœ๊ทธ์ธ ์œ ์ง€
              </label>
              <button type="button" onClick={handleLogin}>
                ๋กœ๊ทธ์ธ
              </button>
            </>
          )}
        </form>
        <button type="button" onClick={() => setIsSignUp(!isSignUp)}>
          {isSignUp ? "์ด๋ฏธ ๊ณ„์ •์ด ์žˆ๋‚˜์š”? ๋กœ๊ทธ์ธ" : "๊ณ„์ •์ด ์—†๋‚˜์š”? ํšŒ์›๊ฐ€์ž…"}
        </button>
        <button className="signUp-btn" onClick={handleSignUpRedirect}>
          ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๋กœ ์ด๋™
        </button>
      </div>
    </div>
  );
};

export default SignInPopup;

๐Ÿš€ ๊ฒฐ๊ณผ ํ™”๋ฉด

  • ๋กœ๊ทธ์ธ ํŒ์—…

  • ํšŒ์›๊ฐ€์ž… ํŒ์—…

 

  • ์‚ฌ์šฉ์ž ์ธ์ฆ


๐Ÿ’ก ์ •๋ฆฌ

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋กœ๊ทธ์ธ ํŒ์—…์„ React๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ค˜์Šต๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ ๋ฐ ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ์„ ํ•˜๋‚˜์˜ ํŒ์—…์—์„œ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž๋Š” ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ณ , ๊ฐ„ํŽธํ•˜๊ฒŒ ํšŒ์›๊ฐ€์ž…์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.