Skip to content

Latest commit

ย 

History

History
executable file
ยท
1050 lines (866 loc) ยท 53.1 KB

README.md

File metadata and controls

executable file
ยท
1050 lines (866 loc) ยท 53.1 KB

STAYINN ์Šคํ…Œ์ด์ธ

image

๐Ÿ“— ๋ชฉ์ฐจ

  1. ์ฐธ์—ฌ ์ธ์› ๋ฐ ๋‹ด๋‹น ๊ธฐ๋Šฅ
  2. ๊ธฐํš ๋ฐ ๋””์ž์ธ
  3. OVERVIEW
  4. ๊ธฐ์ˆ  ์Šคํƒ
  5. ํ˜‘์—… ํ”„๋กœ์„ธ์Šค
  6. ํŒ€์›๋ณ„ ๊ตฌํ˜„ ๊ธฐ๋Šฅ ๋ฐ ํšŒ๊ณ 
  7. ๋ฆฌํŒฉํ† ๋ง ์ดํ›„ ์ถ”๊ฐ€/๋ณ€๊ฒฝ ๋œ ๊ธฐ๋Šฅ

๐Ÿ‘ญ ์ฐธ์—ฌ ์ธ์› ๋ฐ ๋‹ด๋‹น ๊ธฐ๋Šฅ

FE ํŒ€

ํŒ€์žฅ | ๋‚จ๊ถ์ข…๋ฏผ ํŒ€์› | ๋ฐ•์„ฑํ›„ ํŒ€์› | ์„œ์ง€์ˆ˜ ํŒ€์› | ์žฅ๋ฌธ์šฉ ํŒ€์› | ์ •์ง„์ฃผ
@NamgungJongMin @HOOOO98 @jseo9732 @moonyah @jinjoo-jung
  • ์ดˆ๊ธฐ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ธํŒ…
  • ํšŒ์›๊ฐ€์ž… / ๋กœ๊ทธ์ธ
  • ์ˆ™๋ฐ• / ๊ฐ์‹ค ์ƒ์„ธํŽ˜์ด์ง€
  • ํด๋ผ์ด์–ธํŠธ ๋ฐฐํฌ
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํŽ˜์ด์ง€
  • ํ—ค๋” / ํ‘ธํ„ฐ ์ปดํฌ๋„ŒํŠธ
  • ๋ฉ”์ธ ํŽ˜์ด์ง€
  • ์นดํ…Œ๊ณ ๋ฆฌ ํŽ˜์ด์ง€
  • ์˜ˆ์•ฝ ๊ด€๋ จ ํŽ˜์ด์ง€
  • ์˜ˆ์•ฝ ์ฃผ๋ฌธ/๋‚ด์—ญ/์ƒ์„ธ

BEํŒ€

ํŒ€์žฅ | ๊น€์ง„ํ™ ํŒ€์› | ๊น€์ •ํ›ˆ ํŒ€์› | ๋ฐ•์ฐฌ์˜
@deepredk @Aleexender @cyPark95
  • ์œ ์ € ์ •๋ณด
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ
  • ์˜ˆ์•ฝ ์ฃผ๋ฌธ
  • ์ˆ™๋ฐ•์—…์†Œ

๐Ÿ“ƒ API ์„ค๊ณ„

โœ’ ๊ธฐํš ๋ฐ ๋””์ž์ธ

Figma image

๐ŸŽž OVERVIEW

์‹œ์—ฐ์˜์ƒ

๐Ÿ”๏ธ ๊ธฐ์ˆ  ์Šคํƒ

FE ๊ฐœ๋ฐœ

ํ˜‘์—… ํˆด

๋ฐฐํฌ

๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ ํ˜‘์—… ํ”„๋กœ์„ธ์Šค

1) ๋ถ„๋‹ดํ•œ ์—ญํ• ์— ๋”ฐ๋ผ ๊ตฌํ˜„ํ•  ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๊ฐœ๋ฐœ ๋‚ด์šฉ์„ ์ด์Šˆ๋กœ ๋“ฑ๋กํ•œ๋‹ค.

image

2) ๊ฐœ์ธ์ด ๊ฐœ๋ฐœํ•œ ๋‚ด์šฉ์€ ๋ฐ˜๋“œ์‹œ pr์„ ํ†ตํ•ด์„œ ํŒ€์›๋“ค์˜ ์ฝ”๋“œ๋ฆฌ๋ทฐ์™€ approval์„ ๊ฑฐ์นœ ๋’ค ์ ์šฉํ•œ๋‹ค.

image

3) Trello๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ฐฑ์—”๋“œ ํŒ€๊ณผ API์™€ ๊ด€๋ จ ๋‚ด์šฉ์„ ์ƒ์˜ํ•œ๋‹ค.

image

๐Ÿ’ป ํŒ€์›๋ณ„ ๊ตฌํ˜„ ๊ธฐ๋Šฅ ๋ฐ ํšŒ๊ณ 

๋‚จ๊ถ์ข…๋ฏผ

์ž‘์—… ๋‚ด์šฉ

  • ์ดˆ๊ธฐ ๊ฐœ๋ฐœํ™˜๊ฒฝ ์„ธํŒ… (์ ˆ๋Œ€ ๊ฒฝ๋กœ alias ์„ค์ • / eslint, prettier ์„ค์ • / ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ / api ์š”์ฒญ ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ์ฒด ์„ค์ •)
  • ๊ฒ€์ƒ‰์—”์ง„์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ Metadatas ์ž‘์„ฑ (robots, sitemap, favicon, title, description)
  • ๋กœ๊ทธ์ธ/ ํšŒ์›๊ฐ€์ž… input ๊ฐ’ validation
  • validation ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋””์ž์ธ ๋ณ€๊ฒฝ ๋ฐ ๋ฒ„ํŠผ ํ™œ์„ฑํ™” ์—ฌ๋ถ€ ๊ฒฐ์ •
  • ๊ฐ input ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„ ๋ฆฌ๋ Œ๋”๋ง
  • ๋ฐ˜๋ณต๋˜๋Š” react hooks -> custom hooks๋กœ ๋ถ„๋ฆฌ (useAuthInput, useButtonActivate)
  • ๋‹จ์œ„ ๋‹น ํ•œ๋ฒˆ์˜ ์š”์ฒญ๋งŒ์ด ๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก debounce๋ฅผ ํ•จ์ˆ˜์— ์ ์šฉ
๋กœ๊ทธ์ธ ํšŒ์›๊ฐ€์ž…
๋กœ๊ทธ์ธ (1) ํšŒ์›๊ฐ€์ž… (1)

๐Ÿ’ฅ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

  1. input ๊ฐ’์„ ์ฑ„์šด ํ›„ ๋ฒ„ํŠผ ํด๋ฆญ์„ ํ†ตํ•ด api ์š”์ฒญ์„ ํ•  ๋•Œ ๋”๋ธ”ํด๋ฆญ์ด๋‚˜ ๋‹จ์‹œ๊ฐ„์— ์—ฌ๋Ÿฌ๋ฒˆ์˜ ํด๋ฆญ์„ ํ•  ๊ฒฝ์šฐ ์—ฌ๋Ÿฌ๋ฒˆ์˜ ์š”์ฒญ์ด ๊ฐ€๋Šฅ ์ด์Šˆ๊ฐ€ ์žˆ์—ˆ๋‹ค. debounce๋ฅผ ์ ์šฉํ•˜์—ฌ ์˜๋„ํ•œ ๋™์ž‘์—์„œ ํ•œ๋ฒˆ์˜ ์š”์ฒญ๋งŒ์ด ๊ฐ€๋„๋ก ํ•ด๊ฒฐํ–ˆ๋‹ค.
const signup = debounce(
  async (
    email: InputType,
    password: InputType,
    nickname: InputType,
    phone: InputType
  ) => {
    try {
      const res = await authRequest.createUser({
        email: email.value,
        password: password.value,
        nickname: nickname.value,
        phone: phone.value,
      });
      console.log(res);

      if (res.status === 'SUCCESS') {
        router.replace('/auth/signin');
      } else {
        setSubmitError(res.errorMessage);
      }
    } catch (error) {
      console.log(error);
    }
  },
  200
);
  1. ๊ฐ input๋งˆ๋‹ค value๊ฐ’์˜ ๋ณ€ํ™”๋ฅผ ์ƒํƒœ๋กœ ์ €์žฅํ•˜๊ณ  ๋ Œ๋”๋งํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋ฐ˜๋ณต๋˜์—ˆ๊ณ  validation ๊นŒ์ง€ ํ•˜๋ ค๊ณ  ํ•˜๋‹ˆ ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ์ง€์ €๋ถ„ํ•ด์ง€๊ณ  ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๋–จ์–ด์กŒ๋‹ค. ๊ฐ input ๋ณ„ ๊ด€๋ฆฌ์™€ validation๊นŒ์ง€ ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๋Š” useAuthInput์ด๋ผ๋Š” custom hook์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ํ•ด๊ฒฐํ–ˆ๋‹ค.
const useAuthInput = (target: string, password?: InputType) => {
  const [input, setInput] = useState({
    value: '',
    validationPass: false,
  });

  const handleChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>): void => {
      if (target === 'email') {
        setInput({
          value: e.target.value,
          validationPass:
            /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/.test(
              e.target.value
            ),
        });
      }

      if (target === 'password') {
        setInput({
          value: e.target.value,
          validationPass: /^(?=.*[a-zA-Z])(?=.*[0-9]).{8,15}$/.test(
            e.target.value
          ),
        });
      }

      if (target === 'passwordConfirm') {
        if (password) {
          setInput({
            value: e.target.value,
            validationPass: e.target.value === password.value,
          });
        }
      }

      if (target === 'name') {
        setInput({
          value: e.target.value,
          validationPass: (input.validationPass =
            e.target.value.length >= 2 && e.target.value.length <= 10),
        });
      }

      if (target === 'contact') {
        setInput({
          value: e.target.value,
          validationPass: /^\d{2,3}-\d{3,4}-\d{4}$/.test(e.target.value),
        });
      }
    },
    [input, target, password]
  );

  return [input, handleChange, setInput];
};
  1. ๊ฐ input๊ฐ’ ์ž…๋ ฅ์‹œ ํ•ด๋‹น input๋งŒ์ด ๋ฆฌ๋ Œ๋”๋ง๋˜๊ฒŒ ํ•˜๋ ค๊ณ  ์ปดํฌ๋„ŒํŠธ๋ฅผ memo๋กœ ๋ฌถ์–ด์ฃผ์—ˆ์ง€๋งŒ ์˜๋„ํ•œ๋Œ€๋กœ ๊ฐ’์„ ์ž…๋ ฅํ•˜๋Š” input ๊ฐ’๋งŒ์ด ๋ฆฌ๋ Œ๋”๋ง๋˜์ง€ ์•Š์•˜๋‹ค. ์ปค์Šคํ…€ ํ›…์—์„œ ์ƒ์„ฑ๋˜๋Š” handleChange ํ•จ์ˆ˜๊ฐ€ ์—ฌ๋Ÿฌ๋ฒˆ ์ƒ์„ฑ๋˜๋ฉฐ ์ œ๋Œ€๋กœ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์ด ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ซ๊ณ  useCallback์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ปค์Šคํ…€ ํ›…์˜ ํ•จ์ˆ˜๋˜ํ•œ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ํ•ด์คŒ์œผ๋กœ์จ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.
// inputEmail.tsx
const InputEmail = memo(({ email, handleEmail }: EmailProps) => (
  <div className='relative my-5'>
    <label htmlFor='email' className='text-base leading-10'>
      ์ด๋ฉ”์ผ*
    </label>

    <input
      type='text'
      name='email'
      id='email'
      value={email.value}
      placeholder='์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.'
      onChange={handleEmail}
      required
      autoComplete='off'
      className='border-lightGray top-10 h-14 w-full rounded-[10px] border-2 p-4 text-base text-black'
    />

    <ValidationIcon input={email} />
    <ErrorMsg target='email' input={email} />
  </div>
));

// useAuthInput.ts
const handleChange = useCallback(
  (e: React.ChangeEvent<HTMLInputElement>): void => {
    if (target === 'email') {
      setInput({
        value: e.target.value,
        validationPass:
          /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/.test(
            e.target.value
          ),
      });
    }

    if (target === 'password') {
      setInput({
        value: e.target.value,
        validationPass: /^(?=.*[a-zA-Z])(?=.*[0-9]).{8,15}$/.test(
          e.target.value
        ),
      });
    }

    if (target === 'passwordConfirm') {
      if (password) {
        setInput({
          value: e.target.value,
          validationPass: e.target.value === password.value,
        });
      }
    }

    if (target === 'name') {
      setInput({
        value: e.target.value,
        validationPass: (input.validationPass =
          e.target.value.length >= 2 && e.target.value.length <= 10),
      });
    }

    if (target === 'contact') {
      setInput({
        value: e.target.value,
        validationPass: /^\d{2,3}-\d{3,4}-\d{4}$/.test(e.target.value),
      });
    }
  },
  [input, target, password]
);
  1. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋กœ๊ทธ์ธ์‹œ ๋ฐฑ๋‹จ์—์„œ set-cookie ํ•ด์ค€ ๊ฐ’์„ ์ฝ์–ด์˜ค์ง€ ๋ชปํ•˜๋Š” ์ด์Šˆ ๋ฐœ์ƒ. http only ์†์„ฑ์ด๋ผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ํ•ด๋‹น ์ฟ ํ‚ค์— ์ ‘๊ทผํ•  ์ˆ˜๊ฐ€ ์—†์—ˆ๋‹ค. ๋˜ํ•œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์™€ ๋‹ฌ๋ฆฌ ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ๋Š” set-cookieํ•ด์ค€ ์ฟ ํ‚ค๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์„ค์ •ํ•˜์—ฌ ์ฟ ํ‚ค๊ฐ’์„ ๊ฐ€๋กœ์ฑ„๊ฑฐ๋‚˜ http only๋ฅผ ํ•ด์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. set-cookie๋ฅผ ํ•ด์ค€๋‹ค๋ฉด ํ”„๋ก ํŠธ์—์„œ ์š”์ฒญ์— ์ฟ ํ‚ค๋ฅผ ์‹ฌ์–ด์ฃผ์ง€ ์•Š์•„๋„ ์•Œ์•„์„œ ๋‹ด์•„๊ฐˆ ๊ฒƒ์ด๋ผ ๊ธฐ๋Œ€ํ–ˆ๋˜ ๋ฐ”์™€ ๋‹ฌ๋ผ ์ด ๋‘ ๋ฐฉ์‹๋„ ์˜ฌ๋ฐ”๋ฅธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์ด ์•„๋‹ˆ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๊ณ , ์‹ค์ œ ํ˜„์—…์—์„œ next.js app router๋ฅผ ์“ธ ๋•Œ ์–ด๋–ค์‹์œผ๋กœ ์ฟ ํ‚ค ์ธ์ฆ์„ ํ•˜๋Š”์ง€ ํ”ผ๋“œ๋ฐฑ ๋•Œ ์—ฌ์ญค๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

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

ํšŒ๊ณ 

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

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

๋ฐ•์„ฑํ›„

์ž‘์—… ๋‚ด์šฉ

  • ๋ฒ„ํŠผ ํƒœ๊ทธ ์† ์ด๋ฏธ์ง€ ํƒœ๊ทธ vs ๋ฒ„ํŠผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ด๋ฏธ์ง€
// ๋ฒ„ํŠผ ํƒœ๊ทธ ์† ์ด๋ฏธ์ง€ ํƒœ๊ทธ
<button>
  <img/>
</button>
// ์ด ๋ฐฉ๋ฒ•์€ ๋ฒ„ํŠผ ์•ˆ์— ์ด๋ฏธ์ง€ ํƒœ๊ทธ๋ฅผ ์ง์ ‘ ํฌํ•จ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
// ๋ฒ„ํŠผ์€ ํ…์ŠคํŠธ ๋˜๋Š” ๋‹ค๋ฅธ ์ฝ˜ํ…์ธ ์™€ ํ•จ๊ป˜ ์ด๋ฏธ์ง€๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// ์ด ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜๋ฉด ์ด๋ฏธ์ง€์™€ ์†์„ฑ์„ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// ๋ฒ„ํŠผ์— ํ…์ŠคํŠธ์™€ ์ด๋ฏธ์ง€๋ฅผ ํ•จ๊ป˜ ํ‘œ์‹œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์ด ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

//   VS

// ๋ฒ„ํŠผํƒœ๊ทธ์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ด๋ฏธ์ง€
<button style={{'backGroundImage:'url(...)'}}/>
// ์ด ๋ฐฉ๋ฒ•์€ ๋ฒ„ํŠผ์— ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
// ๋ฒ„ํŠผ ํ…์ŠคํŠธ๋‚˜ ๋‹ค๋ฅธ ์ฝ˜ํ…์ธ ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฒ„ํŠผ ๋‚ด์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.
// ์ด ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜๋ฉด ๋ฐฐ๊ฒฝ์ด๋ฏธ์ง€์™€ ์Šคํƒ€์ผ ์†์„ฑ์„ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// ๋ฐ˜๋ฉด์— ๋ฒ„ํŠผ ์ „์ฒด๊ฐ€ ์ด๋ฏธ์ง€์—ฌ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์ด ๋” ์ ํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ธํ’‹ Placeholder vs label
<input placeholder="์ž…๋ ฅํ•ด์ฃผ์„ธ์š”"/>
//placeholder ์†์„ฑ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•  ๋‚ด์šฉ์— ๋Œ€ํ•œ ์˜ˆ์‹œ๋‚˜ ํžŒํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
// ํ•˜์ง€๋งŒ placeholder๋Š” ์‹œ๊ฐ์ ์ธ ํžŒํŠธ๋กœ๋งŒ ์ œ๊ณต๋˜๊ธฐ ๋•Œ๋ฌธ์—
// ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์‚ฌ์šฉ์ž ๋“ฑ์—๊ฒŒ๋Š” ์ถฉ๋ถ„ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

//    VS

<label for="test">ํ…Œ์ŠคํŠธ ์ธํ’‹</label>
<input/>
// ์œ„์˜ ์˜ˆ์‹œ์—์„œ for ์†์„ฑ์€ input ์š”์†Œ์˜ id ๊ฐ’๊ณผ ์ผ์น˜์‹œ์ผœ ์–ด๋–ค ์ž…๋ ฅ ํ•„๋“œ์™€ ๊ด€๋ จ์ด ์žˆ๋Š”์ง€ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
// ์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์‚ฌ์šฉ์ž ๋ฐ ์‹œ๊ฐ์  ๋””์ž์ธ๊ณผ ์ƒ๊ด€์—†์ด ๋ช…ํ™•ํ•œ ์„ค๋ช…์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋น„๋กœ๊ทธ์ธ ์‹œ ์˜ˆ์•ฝํ•˜๊ธฐ ๋กœ๊ทธ์ธ ์‹œ ์˜ˆ์•ฝํ•˜๊ธฐ
แ„‡แ…ตแ„…แ…ฉแ„€แ…ณแ„‹แ…ตแ†ซ แ„‹แ…จแ„‹แ…ฃแ†จแ„’แ…กแ„€แ…ต แ„…แ…ฉแ„€แ…ณแ„‹แ…ตแ†ซ แ„‹แ…จแ„‹แ…ฃแ†จ
๋น„๋กœ๊ทธ์ธ ์‹œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋กœ๊ทธ์ธ ์‹œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ
แ„‡แ…ตแ„…แ…ฉแ„€แ…ณแ„‹แ…ตแ†ซ แ„Œแ…กแ†ผแ„‡แ…กแ„€แ…ฎแ„‚แ…ต ezgif-2-5bdebff3e8
๊ฐ์‹ค ์˜ˆ์•ฝ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
แ„€แ…ขแ†จแ„‰แ…ตแ†ฏ แ„‹แ…ฒแ„’แ…ญแ„‰แ…ฅแ†ผ

๐Ÿ’ฅ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ CSS override
    rsuite? vercel์‚ฌ์—์„œ ์ œ์ž‘ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ์•กํŠธ์™€ ๋„ฅ์ŠคํŠธ์— ์ตœ์ ํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
    ์ƒํ™ฉ : ์ˆ™๋ฐ• ๋‚ ์งœ ์„ ํƒ์„ ์œ„ํ•ด DaterangePicker๋ฅผ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
    ๋ฌธ์ œ : ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ •๋…ํ•˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
    CSS ๋ชจ๋“ˆ์ด ๊ฐ™์ด ์„ค์น˜๊ฐ€ ๋˜์–ด ์‚ฌ์šฉํ–ˆ์œผ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ ํŽ˜์ด์ง€๋ฅผ ๋ฐฉ๋ฌธํ•˜๋ฉด CSS๊ฐ€ override๋˜์–ด ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋„ ๋ ˆ์ด์•„์›ƒ์ด ๊นจ์ง€๋Š” ํ˜„์ƒ์ด ์ผ์–ด๋‚ฌ์Šต๋‹ˆ๋‹ค.
    ํ•ด๊ฒฐ : ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‚ด์— ์ด๋ฏธ ํ•ด๊ฒฐ๋ฐฉ์•ˆ์ด ๋‚˜์™€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ 'rsuite/dist/rsuite.min.css'; ๋Œ€์‹  'rsuite/dist/rsuite-no-reset.min.css'; ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
    ๋Š๋‚€์  : ์‚ฌ์‹ค ์ด ๋ฌธ์ œ๋„ ์ง์ ‘ ํ•ด๊ฒฐํ•œ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฆฌํŒฉํ† ๋ง ์ดํ›„๋กœ ๋ฏธ๋ฃฌ ํ›„์— ์กฐ์› ๋ถ„์ด ์ฐพ์•„์ฃผ์‹  ํ•ด๊ฒฐ์ฑ…์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์•ž์œผ๋กœ๋Š” ์–ด๋–ค ๊ฒƒ์ด๋“  ๊ณต์‹๋ฌธ์„œ๋ฅผ ๊ผผ๊ผผํžˆ ๋ณด๊ณ  ์‚ฌ์šฉํ•ด์•ผ๊ฒ ์Šต๋‹ˆ๋‹ค.
    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-12-01 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 4 10 58

ํšŒ๊ณ 

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

์„œ์ง€์ˆ˜

์ž‘์—… ๋‚ด์šฉ

ํ—ค๋”, ํ‘ธํ„ฐ

  • ๊ฐ ํŽ˜์ด์ง€์— ๋งž๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปดํฌ๋„ŒํŠธํ™”

์žฅ๋ฐ”๊ตฌ๋‹ˆ

  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์กฐํšŒ
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด๊ธด ์ƒํ’ˆ ๋ฐ์ดํ„ฐ (์ด๋ฏธ์ง€, ์ƒํ’ˆ๋ช…, ์˜ต์…˜ ๋“ฑ)์— ๋”ฐ๋ฅธ ์ƒํ’ˆ๋ณ„ ๊ตฌ๋งค ๊ธˆ์•ก, ์ „์ฒด ์ฃผ๋ฌธ ํ•ฉ๊ณ„ ๊ธˆ์•ก ๋“ฑ์„ ํ™”๋ฉด์— ์ถœ๋ ฅ
  • ์ง€๋‚œ ์ฒดํฌ์ธ ๋‚ ์งœ, ์žฌ๊ณ  ์—†์Œ์œผ๋กœ ์ธํ•œ ์˜ˆ์•ฝ ๋งˆ๊ฐ ์ƒํ’ˆ ํ‘œ์‹œ
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ฐœ๋ณ„ ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ฒดํฌ ๋ฐ•์Šค๋ฅผ ํ†ตํ•ด ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  • ์˜ˆ์•ฝ ๋ถˆ๊ฐ€ ์ƒํ’ˆ ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  • ์˜ˆ์•ฝ ๋งˆ๊ฐ ์ƒํ’ˆ์„ ์ œ์™ธํ•œ ์ „์ฒด ์„ ํƒ / ํ•ด์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  • ์ฒดํฌ ๋ฐ•์Šค๋ฅผ ํ†ตํ•ด ๊ฒฐ์ œํ•  ์ƒํ’ˆ์„ ์„ ํƒ/์ œ์™ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ์—์„œ ์ฃผ๋ฌธํ•˜๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ, ์˜ˆ์•ฝ(์ฃผ๋ฌธ) ํŽ˜์ด์ง€๋กœ ์ด๋™
ํ—ค๋” ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ฐœ๋ณ„, ์„ ํƒ ์‚ญ์ œ
แ„’แ…ฆแ„ƒแ…ฅg แ„‰แ…กแ†จแ„Œแ…ฆg
์˜ˆ์•ฝ ๋ถˆ๊ฐ€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์‚ญ์ œ ์ „์ฒด ์„ ํƒ, ์„ ํƒ ํ•ญ๋ชฉ ์˜ˆ์•ฝ
แ„‹แ…จแ„‹แ…ฃแ†จ แ„†แ…กแ„€แ…กแ†ท แ„‰แ…กแ†จแ„Œแ…ฆg แ„‹แ…จแ„‹แ…ฃแ†จแ„’แ…กแ„€แ…ตg

๐Ÿ’ฅ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

  • ํ•„์š”ํ•œ ์œ„์น˜์—์„œ๋งŒ ํ‘ธํ„ฐ ํ‘œ์‹œ NextJS ์„œ๋ฒ„์—์„œ ํ‘ธํ„ฐ๊ฐ€ ํ•„์š”ํ•œ ํŽ˜์ด์ง€์ธ์ง€ ๊ตฌ๋ถ„ํ•œ ๋’ค์— ๋ Œ๋”๋ง์ด ๋˜๊ธฐ ์ „์— ํ‘ธํ„ฐ ์œ ๋ฌด๋ฅผ ํŒ๋‹จํ•˜์—ฌ ๋ณด์—ฌ ์ฃผ๊ณ  ์‹ถ์—ˆ๋Š”๋ฐ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ header, cookie (from next/header)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •๋ณด๋ฅผ ๋ฐ›์•„์™€๋„ ํŽ˜์ด์ง€๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋Š” ์›ํ•˜๋Š” ๊ฐ’์„ ์ฐพ์„ ์ˆ˜ ์—†์—ˆ๋‹ค. ํ”„๋กœ์ ํŠธ ๊ธฐํ•œ ๋•Œ๋ฌธ์— ํ•„์š”ํ•œ ํŽ˜์ด์ง€๋งˆ๋‹ค ํ‘ธํ„ฐ๋ฅผ ๋„ฃ์–ด์ฃผ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ž„์‹œ ํ•ด๊ฒฐํ–ˆ์ง€๋งŒ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์˜ ์ฐจ์ด์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์ดํ›„ ๋ฆฌํŽ™ํ† ๋ง ๊ณผ์ •์—์„œ ์•„์‰ฌ์› ๋˜ ๋ถ€๋ถ„์„ ๊ฐœ์„ ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.
  • ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์„ ํƒ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์˜ˆ์•ฝ ๋ถˆ๊ฐ€(์ฒดํฌ์ธ ๋‚ ์งœ๊ฐ€ ์ง€๋‚ฌ๊ฑฐ๋‚˜ ์˜ˆ์•ฝ ๊ฐ€๋Šฅํ•œ ๋ฐฉ์˜ ์ˆ˜๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ) ํ•ญ๋ชฉ์€ ์ฒดํฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ์ฒ˜๋ฆฌ, ์ „์ฒด ์„ ํƒ, ํ•„์š”ํ•œ ํ•ญ๋ชฉ๋งŒ ์„ ํƒ ํ›„ ์‚ญ์ œ, ๊ฐœ๋ณ„ ์‚ญ์ œ ๋“ฑ ๊ณ ๋ คํ•ด์•ผํ•  ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ๋งŽ์•„ ๋งŽ์€ ์–ด๋ ค์›€์ด ์žˆ์—ˆ๋‹ค.
    • Strict ๋ชจ๋“œ๋กœ ์ธํ•œ ์ „์ฒด ์„ ํƒ ๋ฐฐ์—ด์— ๊ฐ™์€ ์•„์ดํ…œ์ด ๋“ค์–ด๊ฐ€ ์‹ค์ œ ์„ ํƒํ•œ ์ˆ˜์˜ 2๋ฐฐ๊ฐ€ ์„ ํƒ ์ฒ˜๋ฆฌ๋˜๋Š” ์ด์Šˆ
    • ์ฒซ ๋ Œ๋”๋ง ์‹œ ์ „์ฒด ์„ ํƒ์ด ๋  ๋•Œ ๊ฐ checkbox์˜ onChange๊ฐ€ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ธ์‹๋˜์ง€ ์•Š์•„ ๊ฐ ํ•ญ๋ชฉ์ด ์ฒดํฌ๊ฐ€ ๋˜์—ˆ์„ ๋•Œ ๊ทธ์— ๋”ฐ๋ฅธ ๋ฐฐ์—ด ๊ฐ’์„ ๋ฐ”๊ฟ”์ค˜์•ผํ•˜๋Š” ์ด์Šˆ
  • ์ด ์™ธ์—๋„ ๋งŽ์€ ์ด์Šˆ๊ฐ€ ์žˆ์—ˆ์ง€๋งŒ useEffect์™€ useState ๋ฅผ ์ž˜ ๊ณ ๋ คํ•˜์—ฌ ํ•ด๊ฒฐํ•˜๋ฉด์„œ ๋‹ค์‹œ ๋ฆฌ์•กํŠธ์˜ ๋ผ์ดํ”„ ์‚ฌ์ดํด์„ ๊ณต๋ถ€ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

ํšŒ๊ณ 

์ด์ „ ํ† ์ด2 ํ”„๋กœ์ ํŠธ์—์„œ ์ต์ˆ™ํ–ˆ๋˜ ํŽ˜์ด์ง€ ๋ผ์šฐํ„ฐ๋ฅผ ์‚ฌ์šฉํ–ˆ์—ˆ๋Š”๋ฐ ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ app ๋ผ์šฐํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ app ๋ผ์šฐํ„ฐ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ  ์ด์ „์—๋Š” ๊ณ ๋ฏผํ•˜์ง€ ์•Š์•˜๋˜ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
๋ฐฑ์—”๋“œ์™€์˜ ํ˜‘์—…์„ ํ†ตํ•ด์„œ ๋งŽ์€ ๊ฐœ๋ฐœ์ด ์ง„ํ–‰๋˜๊ธฐ ์ „์— ๋น ๋ฅด๊ฒŒ ๋ฐ์ดํ„ฐ ํ˜•์‹์ด๋‚˜ api ๋ฌธ์„œ๋ฅผ ํ†ต์ผํ•œ ๋’ค์— ์ž‘์—…ํ•ด์•ผ์ง€ ํฐ ๋ฌธ์ œ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ  ๋ฌธ์ œ ํ•ด๊ฒฐ๋„ ์ˆ˜์›”ํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๊ณ  ๋ฌธ์„œํ™”์™€ ์†Œํ†ต์˜ ์ค‘์š”์„ฑ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ๋‹ด๋‹นํ•˜๋ฉด์„œ ๋””ํ…Œ์ผํ•œ ์ž‘์—…๋“ค์ด ๋งŽ์•„์„œ ์ƒํƒœ๊ด€๋ฆฌ๋‚˜ ๋ผ์ดํ”„ ์‚ฌ์ดํด์„ ๊ณต๋ถ€ํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๊ฒฝํ—˜์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด์„œ ์ปดํฌ๋„ŒํŠธ์˜ ๋ถ„๋ฆฌ ๋ฐ ์ปจ๋ฒค์…˜์„ ๋”ฐ๋ฅด๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค. ํŒ€์›๋“ค๊ณผ ๋Œ€๋ฉด์œผ๋กœ ์†Œํ†ตํ•˜์—ฌ ์›ํ• ํ•˜๊ฒŒ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งˆ๋ฌด๋ฆฌ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค!

์žฅ๋ฌธ์šฉ

์ž‘์—… ๋‚ด์šฉ

  • main Carousel : autoplay ์ ์šฉ
  • main Icon : ์ˆ™๋ฐ• ์—…์†Œ ์นดํ…Œ๊ณ ๋ฆฌ ๋ณ„ ๋ถ„๋ฅ˜ ์•„์ด์ฝ˜, ๋ถ„๋ฅ˜ ํŽ˜์ด์ง€์™€ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค.
  • main contents 01 โ†’ ์ง€์—ญ ๋ณ„ ํŽœ์…˜ ๋ณด์—ฌ์ฃผ๊ธฐ, API ์—ฐ๊ฒฐ
  • main contents 02 โ†’ ์ง€์—ญ ๋ณ„ ํ˜ธํ…” ๋ณด์—ฌ์ฃผ๊ธฐ, API ์—ฐ๊ฒฐ
  • main contents 03 โ†’ ์ง€์—ญ ๋ณ„ ์ „์ฒด ์ˆ™์†Œ ๋ณด์—ฌ์ฃผ๊ธฐ (๋ถ„๋ฅ˜ ํŽ˜์ด์ง€์™€ ์—ฐ๊ฒฐ)
  • ์นดํ…Œ๊ณ ๋ฆฌ ๋ณ„ ๋ถ„๋ฅ˜ ๋“œ๋กญ๋‹ค์šด
  • ์ง€์—ญ ๋ณ„ ๋ถ„๋ฅ˜ ๋“œ๋กญ๋‹ค์šด
  • ์ˆ™์†Œ ์นด๋“œ ์ œ์ž‘, infinite scroll ์ ์šฉ
  • ์ˆ™๋ฐ• ์—…์†Œ ๋ชฉ๋ก ์กฐํšŒ API ์—ฐ๊ฒฐ
  • ์ˆ™์†Œ๋“ค์€ ๊ฐ๊ฐ์˜ detail page์™€ ์—ฐ๊ฒฐ
๋ฉ”์ธํŽ˜์ด์ง€ carousel ๋ฉ”์ธํŽ˜์ด์ง€ contents
main main2
์นดํ…Œ๊ณ ๋ฆฌ ํŽ˜์ด์ง€ ์ƒ์„ธํŽ˜์ด์ง€๋กœ ์ด๋™
products link

๐Ÿ’ฅ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

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

  • ์ƒํ™ฉ : ์ฒ˜์Œ์—๋Š” URL์„ slug๋กœ ์„ค์ •ํ•˜์—ฌ ํ˜ธํ…”์„ ๋ถ„๋ฅ˜ํ•˜๋ ค๊ณ  ํ–ˆ์œผ๋‚˜, ์ด ๋ฐฉ์‹์ด ๋„ˆ๋ฌด ํ—ท๊ฐˆ๋ ค์„œ ๋กœ์ง์„ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.
  • ๋ฌธ์ œ : slug๋ฅผ ์‚ฌ์šฉํ•œ URL์€ category์™€ location์˜ ๋ช…์‹œ์„ฑ์ด ๋ถ€์กฑํ•ด ํ˜ผ๋ž€์„ ์ดˆ๋ž˜ํ•ด์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ์ •๋ณด๋ฅผ ์ •ํ™•ํžˆ ์‹๋ณ„ํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
  • ํ•ด๊ฒฐ : URL์„ product?category=&location=๋กœ ๋ช…์‹œ์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๊ฐ๊ฐ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ช…ํ™•ํžˆ ๋‚˜ํƒ€๋‚ด๊ฒŒ ๋˜์—ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์‰ฝ๊ฒŒ ํ•„ํ„ฐ๋งํ•˜๊ณ  ์›ํ•˜๋Š” ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ๊ฐœ์„ ์ด ๋˜์—ˆ๋‹ค.
  • ๋Š๋‚€ ์  : URL ๊ตฌ์กฐ์˜ ์ค‘์š”์„ฑ์„ ๊นจ๋‹ฌ์•˜๊ณ , ๋ช…ํ™•ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๊ฒฐ์ •์„ ๋‚ด๋ ธ๋‹ค.

ํšŒ๊ณ 

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

์ •์ง„์ฃผ

์ž‘์—… ๋‚ด์šฉ

  • ์ฃผ๋ฌธ ๊ฒฐ์ œ ํŽ˜์ด์ง€ , api ์—ฐ๊ฒฐ
    • ์ฃผ๋ฌธํ•  ์ˆ™์†Œ ์ •๋ณด ๊ฒฐ์ œ ํŽ˜์ด์ง€๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ
    • ์ด์šฉ์ž , ์˜ˆ์•ฝ์ž ์ •๋ณด ๋™์ผํ•˜๋ฉด ์˜ˆ์•ฝ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
    • ์ด์šฉ์ž ์ •๋ณด, ๊ฒฐ์ œ ๋ฐฉ์‹, ํ•„์ˆ˜ ์ฒดํฌ๋ฐ•์Šค ์„ ํƒ ํ›„ ๊ฒฐ์ œ ๊ฐ€๋Šฅ
  • ์ฃผ๋ฌธ ๋‚ด์—ญ ์ƒ์„ธ ํŽ˜์ด์ง€ , api ์—ฐ๊ฒฐ
    • ๊ฒฐ์ œ ์™„๋ฃŒ โ†’ ์ฃผ๋ฌธ ์™„๋ฃŒ ์ƒ์„ธ ํŽ˜์ด์ง€
    • ๊ฒฐ์ œ ๊ธˆ์•ก, ๊ฒฐ์ œ ์ˆ˜๋‹จ, ์ด์šฉ์ž, ์˜ˆ์•ฝ์ž ์ •๋ณด ๋ณด์—ฌ์ฃผ๊ธฐ
  • ์ฃผ๋ฌธ ๋‚ด์—ญ ๋ชฉ๋ก ํŽ˜์ด์ง€, api ์—ฐ๊ฒฐ
    • ์‚ฌ์šฉ์ž๊ฐ€ ์ˆ™์†Œ ๊ฒฐ์ œํ•œ ๋‚ ์งœ ๊ธฐ์ค€์œผ๋กœ ์ˆ™์†Œ ๋ชฉ๋ก ๋„์–ด์ฃผ๊ธฐ
    • ์ƒ์„ธ๋ณด๊ธฐ ํด๋ฆญ์‹œ ์ƒ์„ธ ํŽ˜์ด์ง€๋กœ ์ด๋™
์ˆ™์†Œ ์˜ˆ์•ฝ ์ •๋ณด ์กฐํšŒ & ๊ฒฐ์ œ ์™„๋ฃŒ ๊ฒฐ์ œ(์˜ˆ์•ฝ)ํ–ˆ๋˜ ์ˆ™์†Œ ๋ชฉ๋ก ์กฐํšŒ
แ„€แ…งแ†ฏแ„Œแ…ฆแ„‘แ…ฆแ„‹แ…ตแ„Œแ…ต แ„‹แ…จแ„‹แ…ฃแ†จ แ„‚แ…ขแ„‹แ…งแ†จ แ„†แ…ฉแ†จแ„…แ…ฉแ†จ
์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‹ด์€ ์ˆ™์†Œ 2๊ฐœ ๊ฒฐ์ œ
แ„€แ…งแ†ฏแ„Œแ…ฆแ„‘แ…ฆแ„‹แ…ตแ„Œแ…ต2

๐Ÿ’ฅ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

  1. Next.js SSR์„ ์‚ฌ์šฉํ•œ SEO๋ฅผ ์œ„ํ•œ Next.js๋ฅผ ์‚ฌ์šฉํ•œ ํ”„๋กœ์ ํŠธ๊ฐ€ ์ด๋ฒˆ์ด ์ฒ˜์Œ์ด๋ผ์„œ ๊ทธ๋Ÿฐ์ง€, ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ๋ Œ๋”๋ง์˜ ์ฐจ์ด์ ์ด๋‚˜ props์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹๋“ค์„ ํ—ค๋งธ๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค ๋ณด๋‹ˆ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ useState, useEffect๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋๊ณ  ๊ฒฐ๊ณผ โ€˜use clientโ€™๋ฅผ ์ž‘์„ฑํ•˜๋ผ๋Š” ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ๋งˆ์ฃผํ•˜๋ฉด์„œ ๋‹ค์‹œ ์„œ๋ฒ„์ปดํฌ๋„ŒํŠธ์™€ ํด๋ผ์ด์–ธํŠธ์˜ ์ฐจ์ด์ ์„ ์ œ๋Œ€๋กœ ๊ณต๋ถ€ํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ๊ฒ ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ lifecycle hooks๊ฐ™์€ ์ƒํ˜ธ์ž‘์šฉ์„ฑ์„ ํฌํ•จํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ผ๋ฉด ๊ทธ๊ฒƒ์„ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์„œ๋ฒ„์ปดํฌ๋„ŒํŠธ๋กœ ๊ด€๋ฆฌ๋ฅผ ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ next.js๋ฅผ ์™œ ์‚ฌ์šฉํ•˜๋Š”์ง€, ์–ด๋–ค ๋ถ€๋ถ„์—์„œ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”์ง€ ๋“ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด๋‚˜๊ฐ€๋ฉฐ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

  2. ChunkError แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-12-01 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 5 30 50

์ด ๋ถ€๋ถ„์€ ์™„์ „ํžˆ ํ•ด๊ฒฐํ•˜์ง€ ์•Š์€ ์ƒํƒœ์ด์ง€๋งŒ ๊ฒช์€ ๋ฌธ์ œ์ด๊ธฐ์— ์ ์—ˆ์Šต๋‹ˆ๋‹ค. ์ง์—ญํ•˜๋ฉด Chunk ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ค์ง€ ๋ชปํ•ด์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์บ์‹ฑ์ด ๋˜์—ˆ๊ฑฐ๋‚˜, ์ด์ „ ๋ฒ„์ „์˜ ํŽ˜์ด์ง€๊ฐ€ ๊ณ„์† ์—ด๋ ค์žˆ๋˜๊ฐ€ ํ•˜๋Š” ๋“ฑ์˜ ์ด๋ฅ˜๋กœ ์ธํ•ด ์ง€๊ธˆ์€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ด์ „ ์ €๋ฒˆ์˜ chunk ํŒŒ์ผ์„ ์š”์ฒญํ•˜๊ฒŒ ๋˜๋ฉด์„œ ChunkError๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ํ˜„์ƒ์ธ๋ฐ, ์ด ์—๋Ÿฌ๊ฐ€ ๊ณ„์† ๋œจ๋ฉด์„œ ํ™”๋ฉด์ด ๋ณด์ด์ง€ ์•Š๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์—ด ๋ฒˆ ์ค‘ ํ•œ๋ฒˆ๊ผด๋กœ ๋ฐœ์ƒํ•˜๊ณ  ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์€ ์ž˜ ์ž‘๋™์„ ํ•ด์„œ ์ •ํ™•ํ•œ ์—๋Ÿฌ ๋ฐœ์ƒ ์ด์œ ๋Š” ์ฐพ์ง€ ๋ชป ํ–ˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ์ด์œ ๋กœ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€ ๋Œ€์ถฉ ์ดํ•ด๋Š” ํ–ˆ์ง€๋งŒ ์ •ํ™•ํ•˜๊ฒŒ ํ•ด๊ฒฐ์„ ํ•œ ๊ฒƒ์€ ์•„๋‹ˆ๋ผ์„œ ๋” ์ฐพ์•„๋ณด๊ณ  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ๋‚˜๊ฐˆ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

ํšŒ๊ณ 

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

๐Ÿ›  ๋ฆฌํŒฉํ† ๋ง ์ดํ›„ ์ถ”๊ฐ€/๋ณ€๊ฒฝ ๋œ ๊ธฐ๋Šฅ

๋‚จ๊ถ์ข…๋ฏผ

1. ์œ ์ € ์ •๋ณด์™€ ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ (๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ / ํšŒ์› ํƒˆํ‡ด)

๋งˆ์ดํŽ˜์ด์ง€์— ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ๊ณผ ํšŒ์› ํƒˆํ‡ด ์š”์ฒญ์„ ํ•˜๋Š” ๋ฒ„ํŠผ๋“ค์„ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค. ํ•ด๋‹น ๋ฒ„ํŠผ๋“ค์„ ๋ˆ„๋ฅด๋ฉด ์š”์ฒญ ์—ฌ๋ถ€๋ฅผ ๋‹ค์‹œ ํ•œ๋ฒˆ ํ™•์ธํ•˜๋Š” ๋ชจ๋‹ฌ์ด ๋œจ๊ฒŒ ๋˜๊ณ  ํ•„์ˆ˜ input ๊ฐ’์„ ์ฑ„์šฐ๊ณ  validation์ด ์ถฉ์กฑ๋˜๋ฉด ํ™•์ธ ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™”๋˜์–ด ์š”์ฒญ๋“ค์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

๋งˆ์ดํŽ˜์ด์ง€ ๋ฒ„ํŠผ ์ถ”๊ฐ€ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ๋ชจ๋‹ฌ ํšŒ์› ํƒˆํ‡ด ๋ชจ๋‹ฌ
image image image

2. ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ํŽ˜์ด์ง€ ๋ Œ๋”๋ง ์ด์ „ ์„œ๋ฒ„ ์ธก์—์„œ ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋ฅผ ํŒ๋ณ„

๋กœ๊ทธ์ธ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ค„์ง€ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚ฌ์ง€์— ๋Œ€ํ•œ ์—ฌ๋ถ€๋ฅผ ํŒ๋ณ„ํ•˜๋Š” ๊ธฐ์กด ๋กœ์ง์€ ํ•ด๋‹น ํŽ˜์ด์ง€์—์„œ useEffect๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋ฅผ ํŒ๋ณ„ํ•˜๋Š” api์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ๊ทธ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ํŽ˜์ด์ง€๋ฅผ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚ค๋Š” ๋ฐฉ์‹์ด์—ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์˜ ๋ฌธ์ œ๋Š” ์ธ๊ฐ€์—ฌ๋ถ€๋ฅผ ํŒ๋ณ„ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„œ๋ฒ„์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์ ๊ณผ ํŽ˜์ด์ง€๊ฐ€ mount๋˜์–ด ๋ Œ๋”๋ง ํ•  ๋•Œ api์š”์ฒญ์ด ๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์ „์—๋„ ์ž ๊น ํŽ˜์ด์ง€๊ฐ€ ๋ณด์ด๋ฉฐ ๊นœ๋นก๊ฑฐ๋ฆฌ๋Š” ํ˜„์ƒ์ด ์ผ์–ด๋‚œ๋‹ค๋Š” ์ ์ด์—ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ•ด๋‹น ํŽ˜์ด์ง€์˜ ๋ Œ๋”๋ง ์ด์ „์— ์ธ์ฆ์—ฌ๋ถ€๋ฅผ ํŒ๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค.

์ด ๋•Œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ์ฟ ํ‚ค ์ด์Šˆ๊ฐ€ ์ƒ๊ฒผ๋Š”๋ฐ, ๋ฐฑ์—”๋“œ ๋‹จ์—์„œ Set-cookie๋ฅผ ํ•ด์ฃผ๋”๋ผ๋„ next์„œ๋ฒ„์—๋Š” ์ฟ ํ‚ค๋ผ๋Š” ๊ฐœ๋…์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— next์„œ๋ฒ„์—์„œ์˜ ์š”์ฒญ์—์„œ ์ฟ ํ‚ค๊ฐ€ ๋‹ด๊ฒจ๊ฐ€์ง€ ์•Š์•„ ์ธ์ฆ ์š”์ฒญ์„ ํ•  ์ˆ˜๊ฐ€ ์—†์—ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋กœ๊ทธ์ธ์‹œ ๋ธŒ๋ผ์šฐ์ €์— Set-cookie๋œ ํ† ํฐ๋“ค์„ ์ง์ ‘ next์„œ๋ฒ„์— ๋ณ€์ˆ˜๋กœ ๋ถˆ๋Ÿฌ์™€ header์˜ cookie๋กœ ์ง์ ‘ ๋„ฃ์–ด์„œ api ์š”์ฒญ์„ ํ•ด์ฃผ์–ด์•ผ ํ–ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด 'next-client-cookies/server'์˜ CookiesProvider๋ฅผ RootLayout์— ๊ฐ์‹ธ์ฃผ์–ด ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ฟ ํ‚ค๋“ค์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๊ฒŒํ•˜์˜€๋‹ค. ์ดํ›„์—” Set-cookieํ•œ ํ† ํฐ๋“ค์˜ ๊ฐ’์„ ๋กœ๊ทธ์ธ์‹œ ์ฝ์–ด์™€ CookiesProvider์— ๋”ฐ๋กœ set ํ•ด์ฃผ์–ด ๋ฏธ๋“ค์›จ์–ด์—์„œ ํ† ํฐ๋“ค์„ ์ฝ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜์˜€๊ณ , ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋กœ๊ทธ์•„์›ƒ ์‹œ์—๋Š” ๋”ฐ๋กœ CookiesProvider์˜ ํ† ํฐ ๊ฐ’๋“ค์„ remove ํ•ด์ฃผ๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

// src/middleware.ts

export async function needAuth(req: NextRequest) {
  const cookies = getCookies();
  const url = req.nextUrl.clone();
  url.pathname = '/auth/signin';

  try {
    const response = await authRequest.getUser(cookies?.get('accessToken'));

    if (response.status === 'SUCCESS') {
      return NextResponse.next();
    }
  } catch (error) {
    console.log('err: ', error);
    return NextResponse.redirect(url);
  }
}

export async function alreadyAuth(req: NextRequest) {
  const cookies = getCookies();
  const url = req.nextUrl.clone();
  url.pathname = '/';

  try {
    const response = await authRequest.getUser(cookies?.get('accessToken'));

    if (response.status === 'SUCCESS') {
      return NextResponse.redirect(url);
    }
  } catch (error) {
    console.log('err: ', error);
    return NextResponse.next();
  }
}

export function middleware(request: NextRequest) {
  // <user signed> redirect to '/' when access auth pages
  if (request.nextUrl.pathname.startsWith('/auth/signin')) {
    console.log('call middleware - /auth/signin');

    return alreadyAuth(request);
  }
  if (request.nextUrl.pathname.startsWith('/auth/signup')) {
    console.log('call middleware - /auth/signup');

    return alreadyAuth(request);
  }

  // <user not signed> redirect to '/auth/signin' when access pages required authentication
  if (request.nextUrl.pathname.startsWith('/mypage')) {
    console.log('call middleware - /mypage');

    return needAuth(request);
  }

  if (request.nextUrl.pathname.startsWith('/cart')) {
    console.log('call middleware - /cart');

    return needAuth(request);
  }

  if (request.nextUrl.pathname.startsWith('/reservation')) {
    console.log('call middleware - /reservation');

    return needAuth(request);
  }

  if (request.nextUrl.pathname.startsWith('/reservationConfirm')) {
    console.log('call middleware - /reservationConfirm');

    return needAuth(request);
  }
}

export const config = {
  matcher: [
    '/',
    '/mypage',
    '/cart',
    '/auth/:path*',
    '/reservation/:path*',
    '/reservationConfirm/:path*',
  ],
};

// src/app/layout.tsx

const RootLayout = ({ children }: AppLayout) => (
  <CookiesProvider>
    <html lang='ko' className='bg-background'>
      <body className='container mx-auto mb-24 max-w-3xl'>{children}</body>
    </html>
  </CookiesProvider>
);
๋ฐ•์„ฑํ›„

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์œ ์‚ฌ CSS pollyfill

  • ์ƒํ™ฉ : rsuite๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” daterangepicker๋Š” ํ•˜๋‚˜์˜ ๋ชจ๋‹ฌ์—์„œ ๋‘๊ฐœ์˜ ๋‹ฌ๋ ฅ์„ ์ œ๊ณตํ•˜์—ฌ ์‚ฌ์šฉ์ž๋กœ ํ•˜์—ฌ๊ธˆ ํ•œ๋ˆˆ์— ๊ธฐ๊ฐ„์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฌธ์ œ : ํ•˜์ง€๋งŒ ๋ฐ˜์‘ํ˜•์„ ์ œ๊ณตํ•˜์ง€ ์•Š์•„ ๋ชจ๋ฐ”์ผ์˜ ๊ฒฝ์šฐ ๋ทฐํฌํŠธ๋ฅผ ๋ฒ—์–ด๋‚˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์˜ฌํ•ด 2023๋…„ ๋ถ€ํ„ฐ ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ update๋ฅผ ์ค‘์ง€ํ•˜๋ฉด์„œ contribution๋„ ํ•  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค.
  • ํ•ด๊ฒฐ : ๊ทธ๋ž˜์„œ css module์„ ์‚ฌ์šฉํ•ด์„œ class๋กœ ์ง์ ‘ ๋ฐ˜์‘ํ˜•์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ ์ ์šฉ๋œ css๋Š” important๋ฅผ ์ ์šฉํ•˜์—ฌ ๋ฎ์–ด์”Œ์›Œ ์Šคํƒ€์ผ์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ

//styles/dateRangePicker.css

.rs-picker-daterange-calendar-group {
  height: 100% !important;
}
@media screen and (max-width: 768px) {
  .rs-picker-daterange-menu {
    height: auto;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .rs-picker-daterange-panel {
    text-align: center;
    height: 100%;
  }
  .rs-stack-item {
    width: fit-content;
  }
  .rs-picker-daterange-content {
    width: 100%;
  }
  .rs-picker-daterange-header {
    width: 100%;
  }
  .rs-calendar-header-title {
    font-weight: bold;
  }

  .rs-picker-daterange-calendar-group {
    height: 578px;
    display: flex;
    flex-direction: column;
    min-width: 270px !important;
  }
  .rs-clendar,
  .rs-picker-menu {
    margin: 0;
  }
}

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌcsspolyfill

์„œ์ง€์ˆ˜

1. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ชฉ๋ก ์‚ญ์ œ ํ™•์ธ ์ ˆ์ฐจ์—†์ด ๋ฐ”๋กœ ์‚ญ์ œ๋˜๋Š” ๋ฌธ์ œ

  • ์ƒํ™ฉ
    ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ชฉ๋ก์„ ์กฐํšŒํ•˜๋Š” ํŽ˜์ด์ง€์—์„œ '์˜ˆ์•ฝ๋ถˆ๊ฐ€์‚ญ์ œ', '์„ ํƒ์‚ญ์ œ', '๊ฐœ๋ณ„์‚ญ์ œ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํ•œ๋ฒˆ ๋” ํ™•์ธํ•˜๋Š” ์ ˆ์ฐจ์—†์ด ๋ฐ”๋กœ ์‚ญ์ œ๋˜๋Š” ์ƒํ™ฉ
  • ๋ฌธ์ œ
    ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์ถ”๊ฐ€ํ•œ ํ•ญ๋ชฉ์ด ํ™•์ธ ์ ˆ์ฐจ์—†์ด ๋ฐ”๋กœ ์‚ญ์ œ๋œ๋‹ค๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ๋–จ์–ด์ง€๋Š” ๊ฒƒ์œผ๋กœ ํŒ๋‹จ
  • ํ•ด๊ฒฐ
    ์‚ญ์ œ๋ฅผ ํ™•์ธํ•˜๋Š” ๋ชจ๋‹ฌ์„ ๋งŒ๋“ค์–ด์„œ ๋ฐ”๋กœ ์‚ญ์ œ๋˜์–ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ 
  • ์ฝ”๋“œ
    'use client';
    
     import { useEffect } from 'react';
     
     interface Props {
       title?: string;
       content?: string;
       cancel?: string;
       onCancelClick: VoidFunction;
       confirm?: string;
       onConfirmClick: VoidFunction;
     }
     
     const Modal = ({
       title = '์‚ญ์ œํ•˜์‹œ๊ฒ ์–ด์š”?',
       content,
       cancel = '์•„๋‹ˆ์š”',
       onCancelClick,
       confirm = '์‚ญ์ œํ•˜๊ธฐ',
       onConfirmClick,
     }: Props) => {
       useEffect(() => {
         document.body.style.overflow = 'hidden';
         return () => {
           document.body.style.overflow = 'unset';
         };
       }, []);
     
       return (
         <div className='fixed left-0 top-0 z-50 flex h-screen w-screen items-center justify-center bg-[rgba(0,0,0,0.5)]'>
           <div className='w-[18.5rem] rounded-2xl bg-white px-4 pb-2 pt-8'>
             <div className='mx-1 mb-4 text-center text-base font-bold text-black'>
               {title}
             </div>
             <div className='text-gray1 mx-1 mb-5 text-center text-sm'>
               {content}
             </div>
             <div className='flex items-center justify-center text-base'>
               <button
                 className='text-gray1 mx-1 h-10 w-full flex-1'
                 onClick={onCancelClick}
               >
                 {cancel}
               </button>
               <button
                 className='text-blue mx-1 h-10 flex-1 font-bold'
                 onClick={onConfirmClick}
               >
                 {confirm}
               </button>
             </div>
           </div>
         </div>
       );
     };
     
     export default Modal;
    const DeleteButton = ({ cartId }: Props) => {
    const [isShowModal, setIsShowModal] = useState(false);
    
    return (
        <>
          <button
            type='button'
            aria-label='์žฅ๋ฐ”๊ตฌ๋‹ˆ ์‚ญ์ œ'
            onClick={() => setIsShowModal(true)}
          >
            <HiMiniXMark className='text-gray1' />
          </button>
          {isShowModal && (
            <Modal
              content='์„ ํƒํ•˜์‹  ์ƒํ’ˆ์ด ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค'
              onCancelClick={() => setIsShowModal(false)}
              onConfirmClick={deleteCartItem}
            />
          )}
         {isShowToast && <Toast message={isShowToast} />}
       </>
      );
    };
     
    export default DeleteButton;
    ์‚ญ์ œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ๋ชจ๋‹ฌ์„ ํ‘œ์‹œํ•ด์ฃผ๊ณ  ํ•ญ๋ชฉ์„ ์‚ญ์ œํ• ์ง€ ํ•œ๋ฒˆ ๋” ํ™•์ธํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ๊ฑฐ์น˜๋„๋ก ๊ฐœ์„ 

2. api๋กœ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ์— ๋งž๋„๋ก ์ „์ฒ˜๋ฆฌํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด ํŒŒ์ผ์— ๋„ˆ๋ฌด ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” ๋ฌธ์ œ

  • ์ƒํ™ฉ
    api๋กœ ๋ฐ›์•„์˜จ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐ์ดํ„ฐ๊ฐ€ ํ™”๋ฉด์— ๋ณด์—ฌ์ค˜์•ผํ•˜๋Š” ํ˜•์‹๊ณผ ์ฐจ์ด๊ฐ€ ์žˆ์–ด ์ „์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผํ•˜๋Š”๋ฐ ๊ทธ ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๊ธธ์–ด ํ•œ ํŒŒ์ผ์— ๋„ˆ๋ฌด ๋งŽ์€ ๋‚ด์šฉ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ƒํ™ฉ
  • ๋ฌธ์ œ
    ํ•œ ํŒŒ์ผ์— ๋„ˆ๋ฌด ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์„œ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง€๊ณ  ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฝ์ง€ ์•Š์€ ๋ฌธ์ œ
  • ํ•ด๊ฒฐ
    ๋ฐ์ดํ„ฐ๋ฅผ ์ „์ฒ˜๋ฆฌํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ปค์Šคํ…€ํ›…์œผ๋กœ ๋ถ„๋ฆฌ
  • ์ฝ”๋“œ
     import { useEffect, useState } from 'react';
     
     import type { CartItemInfo, PreppedCartProduct } from '@/@types/cart.types';
     
     const useCartList = (apiCartList: CartItemInfo[]) => {
       const [preppedProductList, setPreppedProductList] = useState<
         PreppedCartProduct[]
       >([]);
     
       useEffect(() => {
         setPreppedProductList([]);
     
         apiCartList.map((item) => {
           setPreppedProductList((prevPreppedProductList) => {
             const existingIndex = prevPreppedProductList.findIndex(
               (prevPreppedProductItem) =>
                 prevPreppedProductItem.productId === item.product.productId
             );
     
             // ๋ฐฐ์—ด ์•ˆ์— ์ˆ™์†Œ๊ฐ€ ์กด์žฌํ•˜๋ฉด
             if (existingIndex !== -1) {
               return prevPreppedProductList.map((prevPreppedProductItem, index) => {
                 // ์ˆ™์†Œ ์•ˆ์— ๋ฐฉ๋งŒ ์ถ”๊ฐ€
                 if (index === existingIndex) {
                   return {
                     ...prevPreppedProductItem,
                     cartRoomList: [
                       ...prevPreppedProductItem.cartRoomList,
                       {
                         id: item.id,
                         roomId: item.product.roomId,
                         imageUrl: item.product.imageUrl,
                         roomName: item.product.roomName,
                         baseGuestCount: item.product.baseGuestCount,
                         maxGuestCount: item.product.maxGuestCount,
                         price: item.product.price,
                         checkInTime: item.product.checkInTime,
                         checkOutTime: item.product.checkOutTime,
                         stock: item.product.stock,
                         checkInDate: item.checkInDate,
                         checkOutDate: item.checkOutDate,
                         numberOfNights: item.numberOfNights,
                         guestCount: item.product.guestCount,
                       },
                     ],
                   };
                 }
                 return prevPreppedProductItem;
               });
             }
     
             // ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ์ˆ™์†Œ ๋ฐ ๋ฐฉ ์ถ”๊ฐ€
             return [
               ...prevPreppedProductList,
               {
                 productId: item.product.productId,
                 productName: item.product.productName,
                 address: item.product.address,
                 cartRoomList: [
                   {
                     id: item.id,
                     roomId: item.product.roomId,
                     imageUrl: item.product.imageUrl,
                     roomName: item.product.roomName,
                     baseGuestCount: item.product.baseGuestCount,
                     maxGuestCount: item.product.maxGuestCount,
                     price: item.product.price,
                     checkInTime: item.product.checkInTime,
                     checkOutTime: item.product.checkOutTime,
                     stock: item.product.stock,
                     checkInDate: item.checkInDate,
                     checkOutDate: item.checkOutDate,
                     numberOfNights: item.numberOfNights,
                     guestCount: item.product.guestCount,
                   },
                 ],
               },
             ];
           });
         });
       }, [apiCartList]);
     
       return preppedProductList;
     };
     
     export default useCartList;
์žฅ๋ฌธ์šฉ

๋ฆฌํŒฉํ† ๋ง ๋‚ด์šฉ

  1. ์ˆ™์†Œ ๊ฐœ์ˆ˜๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์— ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ์˜ ์›์ธ์€ API ๋ฌธ์„œ์—์„œ ํŽ˜์ด์ง€์˜ ๊ธฐ๋ณธ๊ฐ’์ด 1๋กœ ๋ช…์‹œ๋˜์–ด ์žˆ์–ด์„œ, ์ดˆ๊ธฐ์—๋Š” ํŽ˜์ด์ง€๊ฐ€ 1๋ถ€ํ„ฐ ์‹œ์ž‘ํ•œ๋‹ค๊ณ  ์˜คํ•ดํ•˜๊ณ  ์žˆ์—ˆ๋˜ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ํŽ˜์ด์ง€๊ฐ€ 0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค

  2. ๋ฉ”์ธ์บ๋Ÿฌ์…€ ์ฝ”๋“œ์—์„œ ์žˆ๋˜ any ํƒ€์ž…์„ ๋ช…์‹œ์ ์ธ TypeScript ํƒ€์ž…์œผ๋กœ ๋Œ€์ฒดํ•˜์˜€์Šต๋‹ˆ๋‹ค.

  3. ์ปดํฌ๋„ŒํŠธ main, products ํด๋”์— index ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

  4. ์ปดํฌ๋„ŒํŠธ์— section ํƒœ๊ทธ ๋„ฃ์–ด์„œ main ํŽ˜์ด์ง€ ์ฝ”๋“œ๋ฅผ ์ •๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

  5. ๋‚ ์งœ ๊ด€๋ จ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๋ถ€๋ถ„์„ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜ ์ ์šฉ์œผ๋กœ ๊ฐœ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

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

์ •์ง„์ฃผ

์ถ”๊ฐ€๊ธฐ๋Šฅ

  • ์˜ˆ์•ฝ ๋‚ด์—ญ ๋ชฉ๋ก ํ™•์ธ ๋ฌดํ•œ์Šคํฌ๋กค ์ ์šฉ
  • ์ฃผ๋ฌธ ๋‚ด์—ญ ๋ชฉ๋ก, ์ฃผ๋ฌธ ๋‚ด์—ญ ๋””ํ…Œ์ผ ํŽ˜์ด์ง€์—์—์„œ๋„ ๋ช‡ ๋ฐ•์ธ์ง€ ๋ณด์ด๊ฒŒ ๋ณ€๊ฒฝ

์ˆ˜์ •์‚ฌํ•ญ

  • ๋ฐœ๊ฒฌํ•˜์ง€ ๋ชป ํ•œ anyํƒ€์ž… ์ œ๊ฑฐ
  • ๋ถˆ๋ช…ํ™•ํ•œ ๋ณ€์ˆ˜, ํ•จ์ˆ˜๋ช… ์ˆ˜์ •
  • ๊ธฐ๊ฐ„ ์ตœ๊ทผ 6๊ฐœ์›” fix
  • ์ฃผ๋ฌธ ๋‚ด์—ญ ๋ชฉ๋ก์—์„œ CARD, CASH -> ์นด๋“œ, ๊ณ„์ขŒ์ด์ฒด ํ•œ๊ธ€๋กœ ๋ณด์ด๊ฒŒ ๋ณ€๊ฒฝ
  • ๋ฒ„ํŠผ ๋ˆ„๋ฅด๋ฉด ์ œ์ถœ๋˜๋Š” ๋ฒ„๊ทธ ๋ณ€๊ฒฝ

๋ฌดํ•œ ์Šคํฌ๋กค ์‹œ์—ฐ ์˜์ƒ

2023-12-15.2.37.30.mov