diff --git a/next.config.ts b/next.config.ts index 9dbb260..ced4173 100644 --- a/next.config.ts +++ b/next.config.ts @@ -3,13 +3,11 @@ import type { NextConfig } from 'next'; const nextConfig: NextConfig = { // Docker 배포를 위한 standalone 모드 활성화 // 해당 설정은 프로덕션 빌드 시 필요한 파일만 .next/standalone 폴더에 복사됨. - images: { - domains: ['sprint-fe-project.s3.ap-northeast-2.amazonaws.com'], - }, output: 'standalone', // 외부 이미지 도메인 허용 images: { + domains: ['sprint-fe-project.s3.ap-northeast-2.amazonaws.com'], remotePatterns: [ { protocol: 'https', diff --git a/src/app/api/experiences/getExperiences.ts b/src/app/api/experiences/getExperiences.ts index 0eb0c8d..af5819f 100644 --- a/src/app/api/experiences/getExperiences.ts +++ b/src/app/api/experiences/getExperiences.ts @@ -14,8 +14,8 @@ interface ExperienceResponse { cursorId: number; } -const teamId = process.env.NEXT_PUBLIC_TEAM_ID; -const url = `/${teamId}/activities`; +const baseUrl = process.env.NEXT_PUBLIC_API_SERVER_URL; +const url = `${baseUrl}/activities`; const validSorts = ['price_asc', 'price_desc']; export const getExperiences = async ({ page, category, sort, keyword }: Params) => { diff --git a/src/app/api/experiences/getPopularExperiences.ts b/src/app/api/experiences/getPopularExperiences.ts index 40e20e4..7ad7638 100644 --- a/src/app/api/experiences/getPopularExperiences.ts +++ b/src/app/api/experiences/getPopularExperiences.ts @@ -7,8 +7,8 @@ interface ResponseData { activities: Experience[]; } -const teamId = process.env.NEXT_PUBLIC_TEAM_ID; -const url = `/${teamId}/activities`; +const baseUrl = process.env.NEXT_PUBLIC_API_SERVER_URL; +const url = `${baseUrl}/activities`; export const getPopularExperiences = async (): Promise => { const res = await instance.get(url, { diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 1e43591..666defe 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,19 +1,26 @@ 'use client'; import Link from 'next/link'; -import Image from 'next/image'; -import { useState } from 'react'; import IconLogo from '@assets/svg/logo'; import IconBell from '@assets/svg/bell'; +import useUserStore from '@/stores/authStore'; +import { useRouter } from 'next/navigation'; +import ProfileDropdown from '@/components/ProfileDropdown'; export default function Header() { - // 실제로는 로그인 여부를 전역 상태나 context로 받아와야 함 - // test하려면 로그인 상태를 true로 변경 - // 빌드 에러로 인해 setIsLoggedIn 변수 앞에 _를 붙여 의도적으로 사용하지 않음을 표시 - const [isLoggedIn, _setIsLoggedIn] = useState(false); + const router = useRouter(); + const user = useUserStore((state) => state.user); + const setUser = useUserStore((state) => state.setUser); + const isLoggedIn = !!user; + + // 로그아웃 처리 + const handleLogout = () => { + setUser(null); + router.push('/'); + }; return ( -
+
{/* 로고 */} {/* 우측 메뉴 */} -
+
{isLoggedIn ? ( <> {/* 알림 아이콘 */} @@ -32,20 +39,15 @@ export default function Header() { - {/* 세로 구분선 */} + {/* 구분선 */}
- {/* 유저 프로필 */} -
- 프로필 이미지 - 김보경 -
+ {/* 프로필 드롭다운 */} + ) : ( <> diff --git a/src/components/ProfileDropdown.tsx b/src/components/ProfileDropdown.tsx new file mode 100644 index 0000000..c30a456 --- /dev/null +++ b/src/components/ProfileDropdown.tsx @@ -0,0 +1,77 @@ +'use client'; + +import Image from 'next/image'; +import Link from 'next/link'; +import { useEffect, useRef, useState } from 'react'; +import { usePathname } from 'next/navigation'; +import ProfileDefaultIcon from '@assets/svg/profile-default'; + +import { ProfileDropdownProps } from '@/types/profileDropdownTypes'; + +export default function ProfileDropdown({ nickname, profileImageUrl, onLogout }: ProfileDropdownProps) { + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + const pathname = usePathname(); + + // 외부 클릭 시 드롭다운 닫기 + useEffect(() => { + const handleClickOutside = (e: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) { + setIsOpen(false); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + // 경로 변경 시 드롭다운 닫기 + useEffect(() => { + setIsOpen(false); + }, [pathname]); + + return ( +
+ {/* 프로필 영역 (드롭다운 토글) */} +
setIsOpen((prev) => !prev)} + > + {profileImageUrl ? ( + 프로필 이미지 + ) : ( +
+ +
+ )} + {nickname || '사용자'} +
+ + {/* 드롭다운 메뉴 */} + {isOpen && ( +
e.stopPropagation()} + > + + 마이페이지 + + +
+ )} +
+ ); +} diff --git a/src/types/profileDropdownTypes.ts b/src/types/profileDropdownTypes.ts new file mode 100644 index 0000000..6f888bf --- /dev/null +++ b/src/types/profileDropdownTypes.ts @@ -0,0 +1,5 @@ +export interface ProfileDropdownProps { + nickname: string; + profileImageUrl: string | null; + onLogout: () => void; +} \ No newline at end of file