[개인 포트폴리오] [클라이밍 커뮤니티 SNS] 2. 코드 분석 <layout.js>

2024. 12. 20. 10:11웹개발 포트폴리오

728x90
반응형

 

import { Outlet, Link, useNavigate } from "react-router-dom";
import styled from "styled-components";
import { auth } from "../firebase";
import { useState, useEffect } from "react";
import { onAuthStateChanged } from "firebase/auth";
import { signOut } from "firebase/auth";

const Wrapper = styled.div`
  @media (max-width: 600px) {
    grid-template-rows: auto 1fr auto; /* 헤더, 메인, 푸터 */
    grid-template-columns: 1fr; /* 단일 열 구조 */
  }

  display: grid;
  height: 100vh;
  width: 100vw;
  overflow-y: hidden;
  position: relative; /* 콘텐츠가 배경 위에 위치하도록 설정 */
  z-index: 1;
`;

const Background = styled.div`
  position: fixed;
  top: 0;
  left: 0;
  width: 20vw;
  height: 100vh;
  background-image: url('/sidebar.jpg');
  background-size: cover;
  background-position: center;
  z-index: -1;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5); /* 50% 어두운 효과 */
    z-index: 1;
  }
`;

const Logo = styled.img`
  width: 15vw;
  margin-bottom: 2vh;

  @media (max-width: 600px) {
    display: none;
  }
`;

const Sidebar = styled.div`
  display: flex;
  flex-direction: column;
  padding: 20px;
  width: 15vw;
  height: 100vh;
  position: fixed;
  background-color: #403B36;
  color: white;

  @media (max-width: 600px) {
    width: 10vw;
    font-size: 0.64rem;
  }
`;

const NavLink = styled(Link)`
  margin-bottom: 1.5rem;
  display: flex;
  align-items: center;
  text-decoration: none;
  color: white;

  img {
    width: 2rem;
    margin-right: 1rem;

    @media (max-width: 600px) {
      align-items: center;
      width: 2rem;
      margin: 0 auto;

      
      &:hover {
        img {
            border-color: white;
        }
      }
    }
  }

  span {
    @media (max-width: 600px) {
      display: none; /* 모바일 버전에서는 숨기기 */
    }
  }
`;

export default function Layout() {
  const navigate = useNavigate();
  const [userId, setUserId] = useState(null);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, async (user) => {
      if (user) {
        setUserId(user.uid);
        // 로그인된 상태에서 로그인 또는 회원가입 페이지 접근 시 로그아웃 처리
        if (window.location.pathname === "/login" || window.location.pathname === "/create-account") {
          try {
            await signOut(auth); // 로그아웃
            alert("로그아웃 되었습니다.");
            navigate("/login"); // 로그인 페이지로 리다이렉트
          } catch (error) {
            console.error("로그아웃 중 오류 발생:", error);
          }
        }
      } else {
        setUserId(null);
      }
    });

    return () => unsubscribe();
  }, [navigate]);

  const handleLogout = async (e) => {
    e.preventDefault(); // NavLink의 기본 동작 중단
    const confirmLogout = window.confirm("정말 로그아웃 하시겠습니까?");
    if (!confirmLogout) return; // 취소를 누르면 함수 종료
    try {
      await signOut(auth);
      alert("로그아웃 되었습니다.");
      navigate("/login"); // 로그아웃 후 로그인 페이지로 이동
    } catch (error) {
      console.error("로그아웃 중 오류 발생:", error);
    }
  };

  return (
    <Wrapper>
      <Sidebar>
      <Background />
      <Logo src="/logo.png" alt="Logo" />
      <NavLink to="/">
          <img src="/home.png" alt="Home Icon" />
          <span>홈</span>
        </NavLink>
        <NavLink to="/posting">
          <img src="/posting.png" alt="Post Icon" />
          <span>포스팅</span>
        </NavLink>
        {userId && (
          <NavLink to={`/profile/${userId}`}>
            <img src="/profile.png" alt="Profile Icon" />
            <span>프로필</span>
          </NavLink>
        )}
        <NavLink to="/login" onClick={handleLogout}>
          <img src="/logout.png" alt="Logout Icon" />
          <span>로그아웃</span>
        </NavLink>
        <NavLink to="/developer">
          <img src="/contect.png" alt="Developer Icon" />
          <span>개발자 컨텍</span>
        </NavLink>
      </Sidebar>
      <Outlet />
    </Wrapper>
  );
}

 

useState : 

상태 관리를 위한 훅

데이터를 저장하고 업데이트 하는데 사용

const [현재 상태 값, 업데이트 할 상태 값] = useState(초기값);

 

useEffect :

사이드 이펙트를 처리하기 위한 훅

 

useEffect(() => {

// 실행할 동작

return () => {

// 정리(clean-up) 동작 (선택 사항) };

},

[dependencyArray]);

 

의존성 배열 : 배열에 포함된 값이 변경될 때만 useEffect가 실행, 생략하면 매 렌더링마다 실행, 비우면 마운트 시 한 번만 실행

728x90
반응형