Frontend ๐Ÿ“š/React

[React] TMDB API๋ฅผ ํ™œ์šฉํ•œ ๋„ทํ”Œ๋ฆญ์Šค ํด๋ก  ์ฝ”๋”ฉ - 7. ํ—ค๋” ๊ตฌํ˜„: ๋ฐ˜์‘ํ˜• ๋””์ž์ธ๊ณผ ๋กœ๊ทธ์ธ ์ƒํƒœ์— ๋”ฐ๋ฅธ ๋™์  ๋ฒ„ํŠผ

leejaejae 2024. 12. 9. 14:04

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


1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

์ด๋ฒˆ์— ๊ตฌํ˜„ํ•  ํ—ค๋”๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ 2๊ฐ€์ง€ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

  1. ๋ฐ˜์‘ํ˜• ๋””์ž์ธ: ํ™”๋ฉด ํฌ๊ธฐ์— ๋”ฐ๋ผ ๋ฉ”๋‰ด๋ฅผ ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด๋กœ ์ „ํ™˜ํ•ด ๋ชจ๋ฐ”์ผ์—์„œ๋„ ๊น”๋”ํ•˜๊ฒŒ ํ‘œ์‹œ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  2. ๋กœ๊ทธ์ธ ์ƒํƒœ์— ๋”ฐ๋ฅธ ๋™์  ๋ฒ„ํŠผ: ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•œ ์ƒํƒœ์— ๋”ฐ๋ผ ํ—ค๋”์— ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ์„ ๋™์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋ฉฐ, ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค.

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

2.1 ๋ฐ˜์‘ํ˜• ๋””์ž์ธ

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

2.2 ๋กœ๊ทธ์ธ ์ƒํƒœ์— ๋”ฐ๋ฅธ ๋ฒ„ํŠผ ํ‘œ์‹œ

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

2.3 ์Šคํฌ๋กค ์‹œ ์Šคํƒ€์ผ ๋ณ€๊ฒฝ

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


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

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

ํ—ค๋”์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ์ฃผ์š” ์ƒํƒœ๋Š” isScrolled์™€ isMenuOpen์ž…๋‹ˆ๋‹ค.

  • isScrolled: ํŽ˜์ด์ง€๊ฐ€ ์Šคํฌ๋กค๋˜์–ด ํ—ค๋”์˜ ๋ฐฐ๊ฒฝ์ƒ‰์„ ๋ณ€๊ฒฝํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
  • isMenuOpen: ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜์—ฌ ๋ฉ”๋‰ด๊ฐ€ ์—ด๋ฆฌ๊ฑฐ๋‚˜ ๋‹ซํžˆ๋Š” ๋™์ž‘์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
const [isScrolled, setIsScrolled] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);

3.2 ์Šคํฌ๋กค ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

์‚ฌ์šฉ์ž๊ฐ€ ํŽ˜์ด์ง€๋ฅผ ์Šคํฌ๋กคํ•  ๋•Œ, ํ—ค๋”์˜ ๋ฐฐ๊ฒฝ์ƒ‰์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋„๋ก handleScroll ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. scrollY ๊ฐ’์„ ํ™•์ธํ•˜์—ฌ ์Šคํฌ๋กค์ด 50px ์ด์ƒ์ผ ๋•Œ ํ—ค๋”์— scrolled ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

const handleScroll = () => setIsScrolled(window.scrollY > 50);

useEffect(() => {
  window.addEventListener("scroll", handleScroll);
  return () => window.removeEventListener("scroll", handleScroll);
}, []);

3.3 ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด ๋ฐ ๋„ค๋น„๊ฒŒ์ด์…˜ ์ฒ˜๋ฆฌ

๋ชจ๋ฐ”์ผ ํ™”๋ฉด์—์„œ ๋ฉ”๋‰ด๋ฅผ ์—ด๊ฑฐ๋‚˜ ๋‹ซ์„ ์ˆ˜ ์žˆ๋Š” ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”๋‰ด๊ฐ€ ์—ด๋ฆด ๋•Œ isMenuOpen ์ƒํƒœ๋ฅผ true๋กœ ์„ค์ •ํ•˜์—ฌ ๋ฉ”๋‰ด๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ , ๋‹ซ์„ ๋•Œ๋Š” false๋กœ ์„ค์ •ํ•˜์—ฌ ๋ฉ”๋‰ด๋ฅผ ์ˆจ๊น๋‹ˆ๋‹ค.

const handleMenuToggle = () => {
  setIsMenuOpen((prev) => !prev);
};

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

ํ—ค๋”์—๋Š” ๋กœ๊ทธ์ธ ์ƒํƒœ์— ๋”ฐ๋ฅธ ๋™์  ๋ฒ„ํŠผ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•œ ๊ฒฝ์šฐ isLoggedIn ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜์—ฌ ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ์„ ๋ณด์—ฌ์ฃผ๊ณ , ํด๋ฆญ ์‹œ ๋กœ๊ทธ์•„์›ƒ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ, ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•จ์„ ์œ ๋„ํ•˜๋Š” ๋™์ž‘์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const handleLoginLogout = () => {
  if (isLoggedIn) {
    localStorage.removeItem("isLoggedIn");
    onLogout();
    setIsMenuOpen(false);
  } else {
    localStorage.setItem("isLoggedIn", true);
  }
};

3.5 ๋ฉ”๋‰ด ํ•ญ๋ชฉ ํด๋ฆญ ์ฒ˜๋ฆฌ

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

const handleNavItemClick = (path) => {
  if (!isLoggedIn) {
    navigate("/signUp");
    return;
  }
  setIsMenuOpen(false);
  navigate(path);
};

3.6 ์Šคํƒ€์ผ๋ง

CSS๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ—ค๋”์˜ ์Šคํƒ€์ผ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. scrolled ํด๋ž˜์Šค๋Š” ์Šคํฌ๋กค ์‹œ ๋ฐฐ๊ฒฝ์ƒ‰์„ ๋ณ€๊ฒฝํ•˜๊ณ , ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด์™€ ๋กœ๊ทธ์ธ ์ƒํƒœ์— ๋”ฐ๋ฅธ ๋ฒ„ํŠผ์„ ์ž˜ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

/* ๊ธฐ๋ณธ ์Šคํƒ€์ผ */
.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  background-color: transparent;
  color: white;
  padding: 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  z-index: 1000;
  transition: background-color 0.3s ease, padding 0.3s ease;
}

/* ์Šคํฌ๋กค ํ›„ ํ—ค๋” ์Šคํƒ€์ผ */
.header.scrolled {
  background-color: rgba(0, 0, 0, 0.9);
  padding: 10px 20px;
}

/* ๋กœ๊ณ  ์Šคํƒ€์ผ */
.header .logo {
  font-size: 1.8rem;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 2px;
}

.header .logo a {
  color: white;
  text-decoration: none;
  transition: color 0.3s ease;
}

.header .logo a:hover {
  color: #e50914;
}

/* ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฉ”๋‰ด */
.header nav {
  display: flex;
  gap: 20px;
  align-items: center;
}

.header nav a {
  color: white;
  text-decoration: none;
  font-size: 1.1rem;
  transition: color 0.3s ease;
}

.header nav a:hover {
  color: #e50914;
}

.header nav button {
  background-color: #e50914;
  color: white;
  border: none;
  padding: 10px 20px;
  font-size: 1.1rem;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.header nav button:hover {
  background-color: #f40612;
}

/* ๋ฐ˜์‘ํ˜• ๋””์ž์ธ: ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด */
@media (max-width: 768px) {
  .header nav {
    display: none; /* ๊ธฐ๋ณธ์ ์œผ๋กœ ์ˆจ๊ธฐ๊ธฐ */
    flex-direction: column;
    position: absolute;
    top: 70px; /* ํ—ค๋” ๋ฐ”๋กœ ์•„๋ž˜๋กœ ์œ„์น˜ */
    right: 0;
    width: 100%; /* ์ „์ฒด ๋„ˆ๋น„๋ฅผ ์ฐจ์ง€ํ•˜๊ฒŒ */
    background-color: rgba(0, 0, 0, 0.9);
    padding: 20px;
    box-sizing: border-box;
  }

  .header nav.open {
    display: flex; /* ์—ด๋ ธ์„ ๋•Œ ํ‘œ์‹œ */
  }

  /* ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด ์•„์ด์ฝ˜ */
  .header .hamburger {
    display: block; /* ์ž‘์€ ํ™”๋ฉด์—์„œ๋งŒ ๋ณด์ด๋„๋ก ์„ค์ • */
    cursor: pointer;
    z-index: 1100;
  }

  .header .hamburger div {
    width: 30px;
    height: 3px;
    background-color: white;
    margin: 5px 0;
    transition: 0.3s;
  }

  /* ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด๊ฐ€ ์—ด๋ฆด ๋•Œ ์•„์ด์ฝ˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ */
  .header .hamburger.open div:nth-child(1) {
    transform: rotate(45deg);
    position: relative;
    top: 8px;
  }

  .header .hamburger.open div:nth-child(2) {
    opacity: 0;
  }

  .header .hamburger.open div:nth-child(3) {
    transform: rotate(-45deg);
    position: relative;
    top: -8px;
  }
}

/* ํฐ ํ™”๋ฉด์—์„œ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฉ”๋‰ด ๋ ˆ์ด์•„์›ƒ */
@media (min-width: 769px) {
  .header .hamburger {
    display: none; /* ํฐ ํ™”๋ฉด์—์„œ๋Š” ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด ์ˆจ๊ธฐ๊ธฐ */
  }
}

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

  • ๋กœ๊ทธ์ธ ์ƒํƒœ์—์„œ๋งŒ ๋ณด์ด๋Š” ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ

 

  • ํ–„๋ฒ„๊ฑฐ ๋ฉ”๋‰ด

๐Ÿ’ก ์ •๋ฆฌ

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