Giter VIP home page Giter VIP logo

kyubnb-next's Introduction

Airbnb clone with Next.js 13.4 ๐Ÿš

์‹คํ–‰๋ฐฉ๋ฒ•

npm install

npm run dev

๋งŒ๋“œ๋Š” ๊ณผ์ •

1. GNB ๋งŒ๋“ค๊ธฐ

๊ฑฐ์˜๋‹ค use client๋กœ ์„ ์–ธํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค์—ˆ๋‹ค. onClick, useState๋“ฑ์ด ์žˆ์œผ๋ฉด ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“œ๋Š”๊ฑด ์•Œ๊ฒ ๋Š”๋ฐ ๊ทธ๊ฑฐ ์—†๋Š” ๊ฒƒ๋“ค์€ ์™œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค๊นŒ?

2. Modal ๋งŒ๋“ค๊ธฐ

Modal์˜ ์ƒํƒœ๋Š” ์ปค์Šคํ…€ ํ›… + zustand์˜ ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ๋กœ ์—ด์—ˆ๋‹ค ๋‹ซ์•˜๋‹ค๋ฅผ ํ•œ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ชจ๋‹ฌ์ด ์—ด๋ฆฌ๋Š” ๊ฒƒ์„ ํ•ธ๋“ค๋ง ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์˜ˆ์‹œ์ฝ”๋“œ : ์ปค์Šคํ…€ํ›… + zustand

import { create } from 'zustand';

interface RegisterModalStore {
  isOpen: boolean;
  onOpen: () => void;
  onClose: () => void;
}

const useRegisterModal = create<RegisterModalStore>((set) => ({
  isOpen: true,
  onOpen: () => set({ isOpen: true }),
  onClose: () => set({ isOpen: false }),
}));

export default useRegisterModal;

3. ํ˜„์žฌ๊นŒ์ง€์˜ ์ตœ์ƒ๋‹จ Layout.tsx

return (
    <html lang='en'>
      <body className={font.className}>
        <ClientOnly>
          <ToasterProvider />
          <RegisterModal />
          {/* <Modal actionLabel='Submit' title='hello world' isOpen={true} /> */}
          <NavBar />
        </ClientOnly>
        {children}
      </body>
    </html>
  • ClientOnly ๋ž˜ํผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ•˜์ด๋“œ๋ ˆ์ด์…˜ ์—๋Ÿฌ๊ฐ€ ๋‚˜์ง€ ์•Š๊ฒŒํ–ˆ๋‹ค. ์˜ค์ง ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ๋žœ๋”๋ง
  • react-hot-toast๋ฅผ ์„ค์น˜ํ•ด์„œ ClientOnly ๋ž˜ํผ ์ปดํฌ๋„ŒํŠธ์•ˆ์—์„œ Provider๋กœ ์„ ์–ธํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

4. prisma ์…‹ํŒ…

prisma ์„ค์น˜

npm install -D prisma

prisma ์‹œ์ž‘

npx prisma init

5. Auth ๊ตฌํ˜„ (๋กœ๊ทธ์ธ, ํšŒ์›๊ฐ€์ž… with Next-auth) ๊ณผ์ •

  1. Prisma๋ฅผ ์‚ฌ์šฉํ•ด์„œ DB Schema๋ฅผ ๋งŒ๋“ ๋‹ค. (/prisma/schema.prisma ์ฐธ๊ณ )
  2. next-auth๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์–ด์ฉ”์ˆ˜ ์—†์ด pages ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ ๋‹ค. (์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์–ด์ฉ” ์ˆ˜ ์—†์—ˆ์Œ)
  3. /page/api/auth/[...nextauth].ts์— next-auth ๊ด€๋ จ ๋กœ์ง์„ ๋„ฃ์–ด์ค€๋‹ค. (์ด๊ฒŒ ๋ณต์žกํ•จ.... ๐Ÿฅฒ)
  4. /app/api/register/route.ts์— POST์š”์ฒญ์ธ ํšŒ์›๊ฐ€์ž… ๋กœ์ง์„ ๋„ฃ๋Š”๋‹ค (DB์— ์‹ ๊ทœ ์œ ์ € ์ƒ์„ฑ with prisma), ์‹ค์ œ ํšŒ์›๊ฐ€์ž… API๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •.
  5. Signup๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ Login์€ credentials๋ผ๋Š” ๊ฒƒ์„ ์‚ฌ์šฉ([...nextauth]์— ์„ ์–ธ๋˜์–ด์žˆ๋Š”)ํ•ด์„œ ๋กœ๊ทธ์ธ ๋กœ์ง ๋ฐ onSumbitํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
  6. ServerComponent์˜ ํŠน์„ฑ์„ ์ด์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ ํ•œ ์œ ์ €์ •๋ณด๋ฅผ DB์—์„œ ๋‹ค์ด๋ ‰ํŠธ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค. (์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์ด๋‹ˆ๊น ๊ฐ€๋Šฅํ•œ์ !!) ์ด๋ ‡๊ฒŒ ๋˜๋ฉด API ์ฝœ์„ ์•ˆํ•ด๋„๋œ๋‹ค๋Š” ์ด์ ์ด ์žˆ๋‹ค!!
  7. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ€์ ธ์˜จ currentUser๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์— props๋กœ ๋„˜๊ฒจ์ค€๋‹ค! ๐Ÿฅฐ
  8. ๊ฐ€์ ธ์˜จ ์œ ์ €์ •๋ณด๋กœ ๋กœ๊ทธ์ธ์‹œ ๋ณด์—ฌ์ค„ ๋ฉ”๋‰ด๋“ค์„ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง ํ•˜์—ฌ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.
  9. ๋กœ๊ทธ์•„์›ƒ์€ next-auth์˜ signOut ํ•จ์ˆ˜๋งŒ ํ˜ธ์ถœํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋กœ๊ทธ์•„์›ƒ์ด ๋œ๋‹ค. (์—„์ฒญ ํŽธํ•จ.)

6. ์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ ๊ตฌํ˜„

  1. ์นดํ…Œ๊ณ ๋ฆฌ ๋ฒ„ํŠผ(beach, windMills, Modern)์„ ํด๋ฆญํ–ˆ์„๋•Œ, url ์ฐฝ์— url?category='๋ญ์‹œ๊ธฐ' ์ด๋Ÿฐ์‹์œผ๋กœ ํŽ˜์ด์ง€๊ฐ€ ์ด๋™(`router.push`` ์‚ฌ์šฉ)๋˜๋ก ๋งŒ๋“ ๋‹ค.
  2. ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ ๋ฒ„ํŠผ์„ ํ•œ๋ฒˆ ๋” ๋ˆ„๋ฅด๊ฒŒ ๋˜๋ฉด query String์„ ์ง€์šด๋‹ค.
  3. ์นดํ…Œ๊ณ ๋ฆฌ ๋ฒ„ํŠผ์˜ label๊ณผ url queryString์˜ category๊ฐ’๊ณผ ๊ฐ™์€์ง€ ๋น„๊ตํ•ด์„œ props๋กœ ๋„˜๊ธฐ๋Š” ๋ฐฉ์‹

1๋ฒˆ, 3๋ฒˆ ๊ตฌํ˜„

const handleClick = useCallback(() => {
  let currentQuery = {};

  if (params) {
    currentQuery = qs.parse(params.toString());
  }

  console.log('ํ˜„์žฌ ์ฟผ๋ฆฌ', currentQuery);

  const updateQuery: any = {
    ...currentQuery,
    category: label,
  };
  // ํ˜„์žฌ ์ฟผ๋ฆฌ ์ŠคํŠธ๋ง์˜ category ๊ฐ’๊ณผ ๋ˆ„๋ฅธ label์ด ๊ฐ™์œผ๋ฉด ์ฟผ๋ฆฌ ์‚ญ์ œ
  if (params?.get('category') === label) {
    delete updateQuery.category;
  }

  // ์‹ค์ œ๋กœ ์ด๋™์‹œํ‚ฌ ์ฟผ๋ฆฌ ์ƒ์„ฑ
  const url = qs.stringifyUrl(
    {
      url: '/',
      query: updateQuery,
    },
    { skipNull: true }
  );
  // ์‹ค์ œ๋กœ ์ด๋™์‹œํ‚ด
  router.push(url);
}, [label, params, router]);

7. ์˜ˆ์•ฝ Form ๋ชจ๋‹ฌ ๋งŒ๋“ค๊ธฐ RentModal (์‚ด์ง hardํ•จ)

  1. ์ปจ์…‰์€ ์šฐ๋ฆฌ๊ฐ€ ํšŒ์›๊ฐ€์ž…ํ•˜๋Š” ํ™”๋ฉด์ฒ˜๋Ÿผ step1 ~ step6 ๊นŒ์ง€ ๋„˜๊ธฐ๋ฉด์„œ ๋งˆ์ง€๋ง‰์— ์ œ์ถœํ•˜๋Š” ๋ฐฉ์‹
  2. step1 ~ step6๊นŒ์ง€์˜ ํ™”๋ฉด์ด ๋ฐ”๋€Œ๊ฒŒ ํ•˜๋Š”๊ฒƒ์€ bodyContent๋ฅผ let์œผ๋กœ ์„ ์–ธํ•œ ํ›„ ์žฌํ• ๋‹น ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ™”๋ฉด์„ ๋ณ€๊ฒฝํ•ด์คŒ + Enum
enum STEPS {
  CATEGORY = 0,
  LOCATION = 1,
  INFO = 2,
  IMAGES = 3,
  DESCRIPTION = 4,
  PRICE = 5,
}
  1. form์˜ state๋Š” react-hook-form์„ ์‚ฌ์šฉํ•œ๋‹ค.
  2. Step1์€ <CategoryInput/>์— map์„ ์ด์šฉํ•˜์—ฌ, ๋ฆฌ์ŠคํŒ… ํ•ด์ค€๋‹ค. ์—ฌ๊ธฐ์„œ react-hook-form ์‚ฌ์šฉ๋ฒ•์„ ์ตํ˜€์•ผํ• ๋“ฏ ํ•˜๋‹ค.
  3. Step2๋Š” react-select์™€ world-countries๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ, ์ „ ์„ธ๊ณ„ ๋‚˜๋ผ๋“ค์„ select ui๋กœ ๋ณด์—ฌ์ค€๋‹ค.
  4. useCountries๋ผ๋Š” ์ปค์Šคํ…€ ํ›…์„ ๋งŒ๋“ค์–ด, ํ•ด๋‹น ๋‚˜๋ผ๋“ค์„ ๊ฐ€์ ธ์˜ค๋Š” Select UI๋ฅผ ๋งŒ๋“ฌ
    ์˜ˆ์‹œ์ฝ”๋“œ)
import countries from 'world-countries';

const formattedCountries = countries.map((country) => ({
  value: country.cca2,
  label: country.name.common,
  flag: country.flag,
  latlng: country.latlng,
  region: country.region,
}));

const useCountries = () => {
  const getAll = () => formattedCountries;

  const getByValue = (value: string) => {
    return formattedCountries.find((item) => item.value === value);
  };

  return {
    getAll,
    getByValue,
  };
};

export default useCountries;

โญ๏ธ ์ œ์ผ ์ค‘์š”ํ•œ๊ฑด react-hook-form์œผ๋กœ form ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , ๋’ค๋กœ๊ฐ”๋‹ค ์•ž์œผ๋กœ ๊ฐ”๋‹คํ•˜๋”๋ผ๋„ state๊ฐ€ ์œ ์ง€๋˜๋Š”๊ฒƒ!

7.1 leaflet์ด๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง€๋„ ๋„์šฐ๊ธฐ

  1. ์ง€๋„๋ฅผ next.js์˜ dynamic import๋กœ ๊ฐ€์ ธ์˜ค๋Š”๊ฒŒ ์œ ์˜ํ•  ์‚ฌํ•ญ!

8. ์œ„ 7๋ฒˆ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์„ค์น˜ํ•œ ํŒจ์บ์ง€

npm install query-string
npm install leaflet
npm install @types/leaflet
npm install react-leaflet
npm install world-countries
npm install react-select

kyubnb-next's People

Contributors

kokyusik91 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.