[Airbnb 클론코딩] 환경설정 및 Navbar UI(With. Youtube)
유튜브를 보던 중에 Airbnb 클론코딩을 하는 것을 보았다.
그래서 Next.js를 찍먹해보려고 한다.
환경설정
해당 영상에서는 Next.js 13, React, Tailwind, Prisma, MongoDB를 사용한다.
먼저 Next 프로젝트 폴더를 만들어준다.
npx create-next-app --typescript
이렇게 하면 기본 셋팅을 물어보는 것이 있는데 초기 설정 값으로 설정하면 된다.
프로젝트 폴더가 만들어지면 npm run dev를 통해 실행해주면서 localhost:3000으로 들어가면 된다.
Navbar
먼저 완성된 디렉터리 구조는 이러하다.
📦app ┣ 📂components ┃ ┣ 📂navbar ┃ ┃ ┣ 📜Logo.tsx ┃ ┃ ┣ 📜MenuItem.tsx ┃ ┃ ┣ 📜Navbar.tsx ┃ ┃ ┣ 📜Search.tsx ┃ ┃ ┗ 📜UserMenu.tsx ┃ ┣ 📜Avatar.tsx ┃ ┗ 📜Container.tsx ┣ 📜favicon.ico ┣ 📜globals.css ┣ 📜layout.tsx ┗ 📜page.tsx |
하나씩 알아보도록 하자.
layout.tsx
import { Nunito } from 'next/font/google'
import Navbar from './components/navbar/Navbar'
import './globals.css'
export const metadata = {
title: 'Airbnb',
description: 'Airbnb clone',
}
const font = Nunito({
subsets: ["latin"]
})
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={font.className}>
<Navbar />
{children}
</body>
</html>
)
}
폰트를 적용하는 것은 다른 포스트에서 확인하면 된다.
Navbar라는 컴포넌트를 만들었고 이를 넣어줬다.
Navbar Component
import Container from '../Container';
import Logo from './Logo';
import Search from './Search';
import UserMenu from './UserMenu';
const Navbar = () => {
return (
<div className='fixed w-full bg-white z-10 shadow-sm'>
<div className='py-4 border-b-[1px]'>
<Container>
<div className="
flex
flex-row
items-center
justify-between
gap-3
md:gap-0
">
<Logo />
<Search />
<UserMenu />
</div>
</Container>
</div>
</div>
);
};
export default Navbar;
Navbar 컴포넌트에는 로고, 검색, 유저관련 메뉴가 있다. 이도 다른 컴포넌트를 가져오는 것이다.
위의 컴포넌트들을 감싸주는 컨테이너 컴포넌트가 있다.
'use client';
interface ContainerProps {
children: React.ReactNode;
}
const Container: React.FC<ContainerProps> = ({ children }) => {
return (
<div className="
max-w-[2520px]
mx-auto
xl:px-20
md:px-10
sm:px-2
px-4
">
{children}
</div>
);
};
export default Container;
컨테이너를 이렇게 만들면 컨테이너 안에 있는 Logo, Searh, UserMenu가 children에 들어간다.
Logo
다음은 로고가 들어가야 하는데 이도 Logo Component를 만들어준다.
'use client';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
const Logo = () => {
const router = useRouter()
return (
<Image
alt="Logo"
className="hidden md:block cursor-pointer"
height="100"
width="100"
src="/images/logo.png"
/>
);
};
export default Logo;
Next.js에는 Image Component가 있는데 이를 통해 간편하게 이미지를 가져올 수 있다.
Image 컴포넌트를 import해주고 tailwind를 사용해서 CSS를 적용해주고 src 속성을 통해 이미지 path를 넣어주면 된다.
이미지들은 깃허브를 통해 다운받거나 airbnb사이트에서 가져오자.
Search
Search 컴포넌트를 만들기 전에 설치해야 하는 것이 있다.
npm install react-icons
react-icons를 설치하여 필요한 아이콘들을 가져올 것이다.
'use client';
import { BiSearch } from 'react-icons/bi';
const Search = () => {
return (
<div className="
border-[1px]
w-full
md:w-auto
py-2
rounded-full
shadow-sm
hover:shadow-md
transition
cursor-pointer
">
<div className="
flex
flex-row
items-center
justify-between
">
<div className="
text-sm
font-semibold
px-6
">
Anywhere
</div>
<div className="
hidden
sm:block
text-sm
font-semibold
px-6
border-x-[1px]
flex-1
text-center
">
Any Week
</div>
<div className="
text-sm
pl-6
pr-2
text-gray-600
flex
flex-row
items-center
gap-3
">
<div className="hidden sm:block">Add Guests</div>
<div className="
p-2
bg-rose-500
rounded-full
text-white
">
<BiSearch size={18} />
</div>
</div>
</div>
</div>
);
};
export default Search;
다른 CSS는 얘기하기 않고 아이콘에 대해서만 말하면 react-icons를 통해 필요한 아이콘 이름을 import해준다.
그리고 아이콘을 사용할 위치에 해당 아이콘을 넣으면 된다. 아이콘은 airbnb의 퍼스널 컬러로 배경색을 넣어준다.
UserMenu
마지막으로 UserMenu는 계정 프로필과 목록을 나타내는 컴포넌트이다.
'use client';
import { useCallback, useState } from 'react';
import { AiOutlineMenu } from 'react-icons/ai'
import Avatar from '../Avatar';
import MenuItem from './MenuItem';
const UserMenu = () => {
const [isOpen, setIsOpen] = useState(false)
const toggleOpen = useCallback(() => {
setIsOpen((value) => !value)
}, [])
return (
<div className="relative">
<div className="flex flex-row items-center gap-3">
<div
onClick={() => { }}
className="
hidden
md:block
text-sm
font-semibold
py-3
px-4
rounded-full
hover:bg-neutral-100
transition
cursor-pointer
"
>
Airbnb your home
</div>
<div
onClick={toggleOpen}
className="
p-4
md:py-1
md:px-2
border-[1px]
border-neutral-200
flex
flex-row
items-center
gap-3
rounded-full
cursor-pointer
hover:shadow-md
transition
"
>
<AiOutlineMenu />
<div className="hidden md:block">
<Avatar />
</div>
</div>
</div>
{isOpen && (
<div
className="
absolute
rounded-xl
shadow-md
w-[40vw]
md:w-3/4
bg-white
overflow-hidden
right-0
top-12
text-sm
"
>
<div className="flex flex-col cursor-pointer">
<>
<MenuItem
onClick={() => { }}
label="Login"
/>
</>
</div>
</div>
)}
</div>
);
};
export default UserMenu;
목록에 대한 것은 나중에 얘기하는 것으로 하고 계정 프로필만 말하면 드롭다운을 만들어주기 위해 useState를 사용한다.
isOpen과 setIsOpen으로 값을 boolean으로 해준다.
useCallback을 이용해 반복되는 toggleOpen 함수를 만들어주자.
Avatar 컴포넌트는 아까 로고를 만들 때과 마찬가지로 이미지를 넣으면 된다.
해당 아바타 이미지를 클릭하면 드롭다운을 열리게 한다.
isOpen이 true이면 로그인이 나오게 된다. MenuItem 컴포넌트도 이전의 Container 컴포넌트와 마찬가지이므로 따로 코드를 첨부하지는 않겠다.
마무리
완성된 Navbar는 이렇게 되어 있다.
유튜브를 보면서 처음으로 클론코딩을 했는데 내가 프론트를 할 때, 컴포넌트 구조와 확연히 다른 것을 느끼게 되었다..
코드도 한결 보기가 좋은 것 같다.
다음에는 Auth UI를 만들어 볼 것이고 깃허브를 통해 정리해놔야겠다.