πŸ”₯μ„Έμ…˜μŠ€ν† λ¦¬μ§€ 훅을 μ΄μš©ν•œ μžλ™ μ €μž₯ 및 μ΄μ–΄μ„œ μž‘μ„±ν•˜κΈ°

λ°•λ―Όμš°Β·2024λ…„ 1μ›” 5일
2
post-thumbnail

μ˜¬ν•΄λ„ μ•„μ’Œμ’Œ ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•˜λ©° κ³„νš μ΄μ–΄μ„œ μž‘μ„±ν•˜κΈ° κΈ°λŠ₯을 λ‹΄λ‹Ήν–ˆκ³ , 이λ₯Ό useSessionStorage 훅을 μ •μ˜ν•΄ κ΅¬ν˜„ν•˜λŠ” 과정을 담은 κΈ€μž…λ‹ˆλ‹€.


πŸ“Œ κ³„νš μž‘μ„± νŽ˜μ΄μ§€

κ³„νš μž‘μ„± νŽ˜μ΄μ§€λŠ” κ³„νšμ„ μž‘μ„±ν•  수 μžˆλŠ” νŽ˜μ΄μ§€μž…λ‹ˆλ‹€. μœ„μ˜ νŒŒλž€μƒ‰ λ°•μŠ€μ—μ„œ 보듯이, κ³„νš μž‘μ„± 과정은 총 4λ‹¨κ³„λ‘œ κ΅¬μ„±λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. /create λΌλŠ” url을 κ°€μ§€λŠ” ν•˜λ‚˜μ˜ νŽ˜μ΄μ§€ μ•ˆμ— 총 4개의 단계가 진행될 수 있고, 각 단계λ₯Ό λ‚˜νƒ€λ‚΄λŠ” μ»΄ν¬λ„ŒνŠΈκ°€ ν•œ νŽ˜μ΄μ§€ μ•ˆμ—μ„œ κ΅μ²΄λ˜λ©΄μ„œ λ Œλ”λ§λ˜λŠ” κ΅¬μ‘°μž…λ‹ˆλ‹€.

μœ μ €λŠ” 이 νŽ˜μ΄μ§€μ—μ„œ κ³„νšμ„ μž‘μ„±ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ κ·Έ 단계가 4λ‹¨κ³„λ‚˜ λ˜λŠ” 만큼, μœ μ €κ°€ 쀑간에 μ‹€μˆ˜ λ˜λŠ” 고의둜 νŽ˜μ΄μ§€λ₯Ό μ΄νƒˆν•  수 있기 λ•Œλ¬Έμ—, λ‹€μ‹œ μž‘μ„± νŽ˜μ΄μ§€λ‘œ λŒμ•„μ™”μ„ λ•Œ 이전 dataλ₯Ό μ‚¬μš©ν•΄ κ³„νšμ„ μ΄μ–΄μ„œ μž‘μ„±ν•  수 μžˆλŠ” κΈ°λŠ₯이 ν•„μš”ν•˜λ‹€κ³  μƒκ°ν–ˆμŠ΅λ‹ˆλ‹€.


πŸ“Œ μ„Έμ…˜ μŠ€ν† λ¦¬μ§€μ™€ useSesseionStorage hook

κ³„νš μ΄μ–΄μ„œ μž‘μ„±ν•˜κΈ° κΈ°λŠ₯을 κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄μ„œλŠ”, μœ μ €κ°€ 이전에 μž‘μ„±ν•˜λ˜ dataλ₯Ό μ–΄λ”˜κ°€μ— μ €μž₯ν•΄μ•Όν–ˆμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ•Όμ§€λ§Œ μœ μ €κ°€ κ³„νšμ„ μ΄μ–΄μ„œ μž‘μ„±ν•˜λ €κ³  ν•  λ•Œ, 이전 dataλ₯Ό 가져와 μ‚¬μš©ν•  수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

저희 νŒ€μ΄ μ„ νƒν•œ μ €μž₯μ†ŒλŠ” μ„Έμ…˜ μŠ€ν† λ¦¬μ§€μž…λ‹ˆλ‹€. 사싀 데이터λ₯Ό μ„œλ²„μ— μ €μž₯ν•  μˆ˜λ„, 둜컬 μŠ€ν† λ¦¬μ§€μ— μ €μž₯ν•  μˆ˜λ„ μžˆμ§€λ§Œ μ„Έμ…˜ μŠ€ν† λ¦¬μ§€λ₯Ό μ„ νƒν•œ μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • μœ μ €κ°€ κ³„νš μž‘μ„± νŽ˜μ΄μ§€μ—μ„œ μž‘μ„±μ„ ν•˜λ‹€κ°€ μ‹€μˆ˜λ‘œ λ‹€λ₯Έ νŽ˜μ΄μ§€λ‘œ μ΄λ™ν–ˆμ„ λ•Œ, λ‹€μ‹œ μž‘μ„± νŽ˜μ΄μ§€λ‘œ λŒμ•„μ™€μ„œ μ΄μ–΄μ„œ μž‘μ„±ν•  수 μžˆλ„λ‘ ν•˜κ³  μ‹Άμ—ˆμŠ΅λ‹ˆλ‹€.
  • μœ„μ˜ μƒν™©λ§Œ λ§Œμ‘±μ‹œμΌœμ£Όλ©΄ λ˜κ² λ‹€κ³  μƒκ°ν–ˆκΈ°μ—, λΈŒλΌμš°μ €λ₯Ό μ•„μ˜ˆ λ‹«κ±°λ‚˜ λ‹€λ₯Έ νƒ­μ—μ„œ κ³„νš μž‘μ„± νŽ˜μ΄μ§€μ— μ ‘κ·Όν–ˆμ„ λ•Œ dataλ₯Ό μœ μ§€μ‹œμΌœμ€„ ν•„μš”λŠ” μ—†λ‹€κ³  μƒκ°ν–ˆμŠ΅λ‹ˆλ‹€.

κ·Έλž˜μ„œ μ»΄ν¬λ„ŒνŠΈ λ‚΄μ˜ μƒνƒœλ“€μ„ μ„Έμ…˜ μŠ€ν† λ¦¬μ§€μ—λ„ μ €μž₯ν•˜κ³  μ»΄ν¬λ„ŒνŠΈμ˜ μƒνƒœκ°€ 변경될 λ•Œλ§ˆλ‹€ 이λ₯Ό μ„Έμ…˜ μŠ€ν† λ¦¬μ§€μ™€ λ™κΈ°ν™”μ‹œμΌœμ£ΌκΈ° μœ„ν•΄, useSessionStorage 훅을 μ •μ˜ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

interface SessionStorageProps<T> {
  key: string;
  initialValue: T;
  setSessionValueAtFirst?: boolean; 
}

export const useSessionStorage = <T>({ // T: μ €μž₯ν•˜λ €λŠ” data의 type
  key,
  initialValue,
  setSessionValueAtFirst = false,
}: SessionStorageProps<T>) => {
  const [getItem, setStoredItem] = useState<T>(() => {
    if (typeof window !== 'undefined') {
      const item = sessionStorage.getItem(key);
      if (item) {
        // sessionStorage에 값이 μžˆλ‹€λ©΄ κ·Έ 값을 μ‚¬μš©
        return JSON.parse(item);
      } else {
        // session에 값이 없을 λ•Œ μ΄ˆκΈ°κ°’μœΌλ‘œ μ‚¬μš©
        if (setSessionValueAtFirst) {
          // μ΄ˆκΈ°κ°’μ„ sessionStorage에 μ €μž₯
          sessionStorage.setItem(key, JSON.stringify(initialValue));
        }
        return initialValue;
  });

  const setItem = (value: T) => {
    setStoredItem(value); // 1. 자체 state λ³€κ²½   
    sessionStorage.setItem(key, JSON.stringify(value)); // 2. μ„Έμ…˜ μŠ€ν† λ¦¬μ§€ λ‚΄ data λ³€κ²½
  };

  return [getItem, setItem] as const;
};

useSessionStorage hook을 κ°„λ‹¨νžˆ μ„€λͺ…ν•˜μžλ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • state의 μ΄ˆκΈ°κ°’

    • μ„Έμ…˜μŠ€ν† λ¦¬μ§€μ— ν•΄λ‹Ή key에 λŒ€ν•œ dataκ°€ 이미 μ‘΄μž¬ν•œλ‹€λ©΄, κ·Έ 값을 μ΄ˆκΈ°κ°’μœΌλ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€.

      μ΄λŠ” μœ μ €κ°€ νŽ˜μ΄μ§€λ₯Ό μ΄νƒˆν•œ ν›„, λ‹€μ‹œ νŽ˜μ΄μ§€μ— μ ‘κ·Όν–ˆμ„ λ•Œ μ„Έμ…˜μŠ€ν† λ¦¬μ§€μ— λ‚¨μ•„μžˆλ˜ dataλ₯Ό 계속 μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•˜κΈ° μœ„ν•¨μž…λ‹ˆλ‹€.

    • μ„Έμ…˜μŠ€ν† λ¦¬μ§€μ— ν•΄λ‹Ή ket에 λŒ€ν•œ dataκ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄, 인자둜 받은 initialValueλ₯Ό μ΄ˆκΈ°κ°’μœΌλ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€.

  • state에 μ ‘κ·Ό

    useSessionStorage hook λ‚΄ 자체 state 값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

  • state의 λ³€κ²½

    1. useSessionStorage hook λ‚΄ 자체 stateλ₯Ό λ³€κ²½μ‹œμΌœμ£Όκ³ 

    2. μ„Έμ…˜μŠ€ν† λ¦¬μ§€μ˜ data도 λ³€κ²½μ‹œμΌœμ€λ‹ˆλ‹€.

      => 이λ₯Ό 톡해 μ»΄ν¬λ„ŒνŠΈ λ‚΄ state와 μ„Έμ…˜μŠ€ν† λ¦¬μ§€ λ‚΄ dataκ°€ 항상 같은 값을 μœ μ§€ν•˜λŠ” 것을 보μž₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


κ²°κ΅­ 이λ₯Ό μ •λ¦¬ν•˜λ©΄, useState hook을 톡해 μ»΄ν¬λ„ŒνŠΈμ˜ stateλ₯Ό κ΄€λ¦¬ν•˜λŠ” 방법과 거의 λ™μΌν•˜μ§€λ§Œ, stateκ°€ μ„Έμ…˜ μŠ€ν† λ¦¬μ§€μ—λ„ μ €μž₯λ˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

const [number, setNumber] = useState(1);

λ‹€μŒκ³Ό 같이 useState hook을 μ‚¬μš©ν•΄ μƒνƒœλ₯Ό κ΄€λ¦¬ν•œλ‹€λ©΄,

const [μ›ν•˜λŠ” getter 이름, μ›ν•˜λŠ” setter 이름] = useSessionStorage({key, 1});

μ΄λ ‡κ²Œ λΉ„μŠ·ν•œ ν˜•μ‹μœΌλ‘œ useSessionStorage hook을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.



πŸ“Œ μžλ™μ €μž₯ 및 μ΄μ–΄μ„œ μž‘μ„±ν•˜κΈ° 적용 μ˜ˆμ‹œ

μ‹€μ œ μ½”λ“œ μƒμ—μ„œ μœ„ hook을 μ‚¬μš©ν•΄ μ–΄λ–»κ²Œ μžλ™μ €μž₯ 및 μ΄μ–΄μ„œ μž‘μ„±ν•˜κΈ° κΈ°λŠ₯을 κ΅¬ν˜„ν–ˆλŠ”μ§€ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

μžλ™ μ €μž₯

μœ μ €κ°€ 처음 κ³„νš μž‘μ„± νŽ˜μ΄μ§€μ— μ ‘κ·Όν•˜κ²Œ 되면, 1단계인 κ³„νš μ•„μ΄μ½˜ μ •ν•˜κΈ° 단계에 μ§„μž…ν•˜κ²Œ λ©λ‹ˆλ‹€. 그러면 μœ„ μ΄λ―Έμ§€μ˜ μ΄ˆλ‘μƒ‰ λ°•μŠ€μ— ν•΄λ‹Ήν•˜λŠ” κ³„νš μ•„μ΄μ½˜ μ»΄ν¬λ„ŒνŠΈμ—μ„œ useSessionStorage hook을 μ΄μš©ν•΄ κ³„νš μ•„μ΄μ½˜ dataλ₯Ό κ΄€λ¦¬ν•©λ‹ˆλ‹€.


CreatePlanIcon.tsx

const [iconNumber, setIconNumber] = useSessionStorage<number | null>({
  key: SESSION_STORAGE_KEY.STEP_1,
  initialValue: null,
});

1단계에 ν•΄λ‹Ήν•˜λŠ” key인 μƒμˆ˜ SESSION_STORAGE_KEY.STEP_1λ₯Ό 인자둜 전달해주고, 맨 μ²˜μŒμ—λŠ” μ„ νƒλœ μ•„μ΄μ½˜μ΄ μ—†κΈ° λ•Œλ¬Έμ— μ΄ˆκΈ°κ°’μ€ null둜 μ „λ‹¬ν•΄μ€λ‹ˆλ‹€.

그리고 이 hook을 톡해 λ°˜ν™˜ν•œ getter, setter 역할을 ν•˜λŠ” iconNumber와 setIconNumberλ₯Ό 각각 ν•„μš”ν•œ 뢀뢄에 μ—°κ²°ν•΄μ€λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같이 μ•„μ΄μ½˜ 선택 λͺ¨λ‹¬μ—μ„œ μœ μ €κ°€ νŠΉμ • μ•„μ΄μ½˜μ„ ν΄λ¦­ν–ˆμ„ λ•Œ, μ»΄ν¬λ„ŒνŠΈ λ‚΄μ˜ κ³„νš μ•„μ΄μ½˜ dataκ°€ λ³€κ²½λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€. λ”°λΌμ„œ, μ•„μ΄μ½˜ 선택 λͺ¨λ‹¬ μ»΄ν¬λ„ŒνŠΈμ— setIconNumber λ©”μ„œλ“œλ₯Ό 인자둜 λ„˜κ²¨μ£Όκ³ , μ•„μ΄μ½˜ 선택 λͺ¨λ‹¬ μ»΄ν¬λ„ŒνŠΈ λ‚΄ 각 μ΄λ―Έμ§€μ˜ 클릭 λ¦¬μŠ€λ„ˆμ— setIconNumber λ©”μ„œλ“œλ₯Ό λ“±λ‘ν•΄μ€λ‹ˆλ‹€.

CreatePlanIcon.tsx

<Modal>
<ModalSelectIcon
  setIconNumber={setIconNumber}
  closeModal={() => {
    setIsSelectIconModalOpen(false); // μ•„μ΄μ½˜ 선택 λͺ¨λ‹¬ μ»΄ν¬λ„ŒνŠΈμ— setIconNumber λ©”μ„œλ“œλ₯Ό 인자둜 λ„˜κ²¨μ€Œ
  }}
/>
</Modal>

ModalSelectIcon.tsx

<Image
	src={`/animal/${planIcons[iconNumber]}.png`}
	width={40}
	height={40}
	alt="example plan icon"
	className={classNames('select-icon-modal__icon-image')}
	onClick={() => { // 클릭 λ¦¬μŠ€λ„ˆμ— setIconNumber λ©”μ„œλ“œλ₯Ό 등둝
  		setIconNumber(iconNumber);
  		closeModal();
	}}
/>

λ”°λΌμ„œ, μ•„μ΄μ½˜μ΄ ν΄λ¦­λ˜μ—ˆμ„ λ•Œ μ•„μ΄μ½˜ dataλŠ” 클릭된 μ•„μ΄μ½˜μ— ν•΄λ‹Ήν•˜λŠ” number둜 λ³€κ²½λ˜κ³ , μ„Έμ…˜ μŠ€ν† λ¦¬μ§€μ˜ 정보도 λ™μ‹œμ— λ³€κ²½λ˜λŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. 즉, μ»΄ν¬λ„ŒνŠΈ λ‚΄ stateκ°€ μ„Έμ…˜ μŠ€ν† λ¦¬μ§€μ—λ„ μžλ™μœΌλ‘œ μ €μž₯λ˜λŠ” κ²ƒμž…λ‹ˆλ‹€.


μ΄μ–΄μ„œ μž‘μ„±ν•˜κΈ°

λ˜ν•œ, μ•„μ΄μ½˜μ„ μ„ νƒν•œ ν›„ λ‹€λ₯Έ νŽ˜μ΄μ§€λ‘œ κ°”λ‹€κ°€ λ‹€μ‹œ κ³„νš μž‘μ„± νŽ˜μ΄μ§€λ‘œ 왔을 λ•Œ 이전 dataλ₯Ό μ‚¬μš©ν•΄ μ΄μ–΄μ„œ μž‘μ„±ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

κ³„νš μž‘μ„± νŽ˜μ΄μ§€μ— λ‹€μ‹œ μ ‘κ·Όν•œλ‹€λ©΄, κ³„νš μ•„μ΄μ½˜ μž‘μ„± μ»΄ν¬λ„ŒνŠΈκ°€ λ‹€μ‹œ mount 되고, μ»΄ν¬λ„ŒνŠΈ λ‚΄ stateλŠ” λ‹€μ‹œ μ΄ˆκΈ°ν™”λ  κ²ƒμž…λ‹ˆλ‹€. 즉 μ•„λž˜μ˜ μ½”λ“œκ°€ λ‹€μ‹œ 싀행될 κ²ƒμž…λ‹ˆλ‹€.

const [iconNumber, setIconNumber] = useSessionStorage<number | null>({
  key: SESSION_STORAGE_KEY.STEP_1,
  initialValue: null,
});

ν•˜μ§€λ§Œ μ§€κΈˆμ€ 이미 이전에 μ‚¬μš©ν•œ dataκ°€ μ„Έμ…˜μŠ€ν† λ¦¬μ§€μ— λ‚¨μ•„μžˆκΈ° λ•Œλ¬Έμ—, inivialValue둜 μ „λ‹¬λœ null이 μ•„λ‹ˆλΌ μ„Έμ…˜ μŠ€ν† λ¦¬μ§€μ˜ 값을 μ΄ˆκΈ°κ°’μœΌλ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€. λ”°λΌμ„œ λ‹€μŒκ³Ό 같이 μ΄μ–΄μ„œ μž‘μ„±ν•˜κΈ°κ°€ κ°€λŠ₯ν•©λ‹ˆλ‹€.

profile
κΎΈμ€€νžˆ, 깊게

0개의 λŒ“κΈ€