• [6/6 ~ 6/12] ์ฝ”๋“œ์Šค์ฟผ๋“œ ๋งˆ์Šคํ„ฐ์ฆˆ 22์ฃผ์ฐจ ํšŒ๊ณ 

    2022. 6. 19.

    by. ์ฉธ

    ๐Ÿ’ฆ  ์—์–ด๋น„์—”๋น„ ํ”„๋กœ์ ํŠธ ๋งˆ์ง€๋ง‰ ์ฃผ์ฐจ!

     

    ์–ด์ฉŒ๋‹ค ๋ณด๋‹ˆ ํ•œ์ฃผ์”ฉ ๋ฐ€๋ ค์„œ ์“ฐ๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™์€๋ฐ ์ผ์ฃผ์ผ ์ง€๋‚˜์„œ ์“ฐ๋ ค๊ณ  ํ•˜๋‹ˆ๊นŒ ์ž˜ ๊ธฐ์–ต์ด ์•ˆ ๋‚œ๋‹ค ( ๏ฝฅแด—๏ฝฅฬฅฬฅฬฅ )

    ๋‹ค์Œ ํšŒ๊ณ ๋ถ€ํ„ด ๋ฐ€๋ฆฌ์ง€ ๋ง๊ณ  ์จ์•ผ์ง€ ใ… ใ… 

     

     

    ๐ŸŽ‰  ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ ์ ์šฉ

    const SearchBar = ({ size = SEARCH_BAR_SIZE.LARGE }: SearchBarPropsTypes) => {
      ...
      return (
        <S.Container {...{ size }} onClick={toggleIsActive}>
          <PeriodProvider>
          	<PeriodArea {...{ size }} />
          </PeriodProvider>
          <PriceProvider>
          	<PriceArea {...{ size }} />
          </PriceProvider>
          <PersonnelProvider>
          	<PersonnelArea {...{ size }} />
          </PersonnelProvider>
        </S.Container>
      );
    };

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

     

    interface WithProviderPropsType<T> {
      Component: FC<T>;
      Provider: FC<ProviderTypes>;
    }
    
    interface ProviderType {
      children: JSX.Element;
    }
    
    const WithProvider = <T,>({ Component, Provider }: WithProviderPropsTypes<T>) => {
      return (props: T) => {
        return (
          <Provider>
            <Component {...props} />
          </Provider>
        );
      };
    };

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

     

     

    ๐Ÿ–‹  useContext ์ปค์Šคํ…€ ํ›…

    // 1.
    export const PriceContext = createContext<PriceContextType>({} as PriceContextType);
    
    // 2.
    export const PriceContext = createContext<PriceContextType | null>(null);

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

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

     

    export const usePersonnelState = (
      type?: string,
    ): PersonnelStateWithType | PersonnelContextTypes => {
      const personnelState = useContext<PersonnelContextTypes | null>(PersonnelContext);
      if (!personnelState) throw new Error('PersonnelProvider is not found');
    
      const {
        numberOfAdults,
        setNumberOfAdults,
        numberOfChildren,
        setNumberOfChildren,
        numberOfBabies,
        setNumberOfBabies,
      } = personnelState;
    
      switch (type) {
        case PERSONNEL_TYPE.ADULT:
          return [numberOfAdults, setNumberOfAdults];
        case PERSONNEL_TYPE.CHILD:
          return [numberOfChildren, setNumberOfChildren];
        case PERSONNEL_TYPE.BABY:
          return [numberOfBabies, setNumberOfBabies];
        default:
          return personnelState;
      }
    };

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

     

     

    ๐Ÿ–ฅ  ๋ชจ๋‹ฌ์ฐฝ ๊ตฌ์กฐ ๋ณ€๊ฒฝํ•˜๊ธฐ

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

     

    const handleModalClick = (event: MouseEvent) => {
      for (const element of event.composedPath()) {
        if (element === modalRef.current) return;
      }
      setIsModalOpen(false);
    };
    
    useEffect(() => {
      window.addEventListener('click', handleModalClick, true);
    
      return () => {
        window.removeEventListener('click', handleModalClick, true);
      };
    }, [modalRef]);

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

    path์—๋Š” ํ•ด๋‹น ์š”์†Œ์—์„œ๋ถ€ํ„ฐ ์ตœ์ƒ์œ„ ์š”์†Œ์ธ window ๊ฐ์ฒด๊นŒ์ง€๊ฐ€ ๋ฐฐ์—ด๋กœ ๋‹ด๊ฒจ์žˆ์—ˆ๋‹ค. ์ด ์†์„ฑ์„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ composedPath()๋กœ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ์—ˆ๋Š”๋ฐ, ์ด ๋ฐฐ์—ด์„ ๋Œ๋ฉด์„œ ๋ชจ๋‹ฌ ์˜์—ญ์˜ ref ๊ฐ์ฒด์™€ ์ผ์น˜ํ•˜๋Š” ์š”์†Œ๊ฐ€ ์žˆ๋Š”์ง€๋ฅผ ํ™•์ธํ•˜์—ฌ ๋ชจ๋‹ฌ ๋ฐ–์—์„œ ํด๋ฆญ์ด ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ์— ์ฐฝ์„ ๋‹ซ์•„์ฃผ๋„๋ก ํ–ˆ๋‹ค. 

    ๊ทธ๋Ÿฐ๋ฐ ์ด๋ ‡๊ฒŒ ํ•˜๋‹ˆ ๊ฒ€์ƒ‰๋ฐ” ์˜์—ญ์„ ์•„๋ฌด๋ฆฌ ํด๋ฆญํ•ด๋„ ๋ชจ๋‹ฌ์ฐฝ์ด ์—ด๋ฆฌ์ง€ ์•Š์•˜๋‹ค. ๋ชจ๋‹ฌ์„ ์—ด๊ธฐ ์œ„ํ•ด ๊ฐ ์˜์—ญ์— ๋“ฑ๋กํ•ด๋‘” ํด๋ฆญ ์ด๋ฒคํŠธ์™€ useModal ํ›…์—์„œ ๋“ฑ๋กํ•ด๋‘” ๋ชจ๋‹ฌ์„ ๋‹ซ๊ธฐ ์œ„ํ•œ ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ๊ฐ™์ด ๋ฐœ์ƒํ•ด์„œ ๊ทธ๋Ÿฐ ๊ฒŒ ์•„๋‹๊นŒ ์‹ถ์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ window์— ๊ฑด ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์˜ useCapture ์˜ต์…˜์„ true๋กœ ์ง€์ •ํ•ด์ฃผ๋‹ˆ ๋ชจ๋‹ฌ์ด ์ž˜ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. 

     

     

    ๐Ÿ“‘  ๊ทธ ์™ธ ์ž์ž˜ํ•œ ๊ณ ๋ฏผ๋“ค

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

    const useCanvas = ({ canvasWidth, canvasHeight, animate }: useCanvasTypes) => {
      const canvasRef = useRef<HTMLCanvasElement>(null);
    
      useEffect(() => {
        const canvas = canvasRef.current;
        const ctx = canvas?.getContext('2d');
    
        const setCanvas = () => {
          const devicePixelRatio = window.devicePixelRatio ?? 1;
          if (canvas && ctx) {
            canvas.style.width = `${canvasWidth}px`;
            canvas.style.height = `${canvasHeight}px`;
    
            canvas.width = canvasWidth * devicePixelRatio;
            canvas.height = canvasHeight * devicePixelRatio;
    
            ctx.scale(devicePixelRatio, devicePixelRatio);
          }
        };
        setCanvas();
        ctx && animate(ctx);
      });
    
      return canvasRef;
    };

    ๋˜ ์บ”๋ฒ„์Šค๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์บ”๋ฒ„์Šค์™€ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์บ”๋ฒ„์Šค๋ฅผ ๊ทธ๋ฆฌ๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ›์•„ ์‹คํ–‰์‹œ์ผœ์ฃผ๋Š” useCanvas ํ›…์„ ์‚ฌ์šฉํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์Šฌ๋ผ์ด๋”์—์„œ ์ง€์ •ํ•œ ๊ฐ’์˜ ๋ฒ”์œ„์— ๋”ฐ๋ผ ์ฐจํŠธ์—์„œ ํ•ด๋‹น ์˜์—ญ์„ ๊ฐ•์กฐํ•ด์„œ ๊ทธ๋ ค์ฃผ๋Š” ๋ถ€๋ถ„์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์บ”๋ฒ„์Šค๋ฅผ ๊ทธ๋ฆฌ๋Š” animate ํ•จ์ˆ˜๋ฅผ ์Šฌ๋ผ์ด๋” ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰์‹œ์ผœ์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ, useEffect ๋‚ด์—์„œ ์บ”๋ฒ„์Šค๋ฅผ ์„ธํŒ…ํ•˜๊ณ  ref ๊ฐ์ฒด๋งŒ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ๋Š” ํ›…์˜ ๊ตฌ์กฐ์ƒ animate ๋ถ€๋ถ„๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜๊ฐ€ ์—†์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ฐ์ฐํ•˜์ง€๋งŒ ์ผ๋‹จ์€ useEffect์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ์‚ญ์ œํ–ˆ๋Š”๋ฐ, ๋ฆฌ๋ทฐ์—์„œ '๋งค๋ฒˆ ๋ฆฌ๋ Œ๋”๋ง ๋˜๋Š” ๊ฒƒ์„ ์˜ˆ์ƒํ•˜๊ณ  ๋งŒ๋“œ์‹ ๊ฑฐ์ฃ ?' ๋ผ๊ณ  ๋”ฑ ์ด ๋ถ€๋ถ„์„ ์–ธ๊ธ‰ํ•ด์ฃผ์…จ๋‹ค..ใ…Žใ…Ž ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์บ”๋ฒ„์Šค๋ฅผ ๊ฐ€์ ธ์™€์„œ ์ปจํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ํฌ๊ธฐ๋ฅผ ์„ธํŒ…ํ•ด์ฃผ๋Š” ๋ถ€๋ถ„๊นŒ์ง€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋งค๋ฒˆ ์‹คํ–‰๋˜๋Š”๋ฐ, ์–ด๋–ป๊ฒŒ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ• ์ง€ ์ž˜ ์ƒ๊ฐ์ด ๋‚˜์ง€ ์•Š์•„์„œ ์•„์ง ๊ฐœ์„ ํ•˜์ง€ ๋ชปํ–ˆ๋‹ค. ์ข€ ๋” ๊ณ ๋ฏผ์ด ํ•„์š”ํ•  ๊ฒƒ ๊ฐ™๋‹ค. ๐Ÿฅฒ

     

     

    ๐Ÿ’จ  ์—์–ด๋น„์—”๋น„ ํ”„๋กœ์ ํŠธ ๋!

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

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

    ๋Œ“๊ธ€