[Wanted]_Week4-2_4주차 과제 피드백

hanseungjune·2023년 7월 22일
0

Wanted

목록 보기
19/21
post-thumbnail

가변변수 사용 지양하기

  • 반드시 가변성이 필요한 상황이 아닐 경우에는 불변하게 사용하는 것이 변수가 나타내고자 하는 의미가 명확해지고, 버그가 발생할 가능성이 적어짐. 그래서 const 사용하라는 거임
useEffect(() => {
    (async ()=> {
      if (!inputValue || checkEmptyText(inputValue)) return setSickList([]);

      const isValid = /^[\uAC00-\uD7A3|A-Z|a-z|\s]*$/.test(inputValue);

      if (isValid) {
        const data = getCachedItem(inputValue) || await getSickList(inputValue)
        const result = Array.isArray(data) ? data : [];
        setSickList([...result]);
        setCachedItem(inputValue, result);
      }
    })()
  }, [inputValue]);

찾는 값이 없음에 대한 상태는 boolean보다는 null 또는 undefined 활용

const getCachedItem = (key: string): T[] | undefined => cachedList[key].data

관심사 분리하기

  • sickList라는 검색어를 관리하는 state
  • caching된 값을 관리하는 useCacheData라는 로직
  • 그리고, 검색어에 따라서 질병명을 가져오고, 캐싱을 하는 로직이 모두 컴포넌트 내부에 있음
  • 관심사를 분리한다면
    1. 컴포넌트
      • 질병명에 따른 UI 처리
      • 사용자의 액션 처리(input 등)
    2. 추천 검색어 가져오기
    3. (선택사항) 캐싱처리
      • 추천 검색어를 가져오는 모듈에서 캐싱처리까지 한번에 할건지는 나머지 사항들에 따라서 결정
const Home = () => {
  const {sickList, getSickList, clearList} = useSickList();
  const [isFocused, setIsFocused] = useState<boolean>(false);
  const [inputValue, setInputValue] = useState<string>("");

  useEffect(() => {
      if (isCompletedWord(inputValue)) getSickList(inputValue)

      function isCompletedWord(value: string) {
        return /^[\uAC00-\uD7A3|A-Z|a-z|\s]*$/.test(value);
      }
  }, [inputValue, getSickList]);

	useEffect(() => {
		if (isFocused && isEmpty(inputValue)) clearList()
	},[isFocused, clearList])

  return (
    <main>
      <Title />
      <SearchBar setIsFocused={setIsFocused}>
        <SearchInput
          isFocused={isFocused}
          setIsFocused={setIsFocused}
          inputValue={inputValue}
          setInputValue={setInputValue}
        />
        {isFocused && (
          <SearchClear setInputValue={setInputValue} setData={setSickList} />
        )}
        <SearchButton inputValue={inputValue} />
        {isFocused && (
          <KeywordList
            inputValue={inputValue}
            data={sickList}
            recentKeys={getRecentKeys()}
          />
        )}
      </SearchBar>
    </main>
  );
};

export default Home;

확실히 함수나 클래스를 통해서 관심사를 분리하니까, 내부 코드가 뭔지 몰라도 어떤 기능을 하는지 쉽게 알 수 있어서 관심사를 분리하는 게 굉장히 좋은 것 같다.

HashTable을 이용한 조건에 맞는 함수 가져오기

  const handleSearchKeyDown: React.KeyboardEventHandler<HTMLInputElement> = useCallback(
    e => {
      const functionFor: { [key: typeof e.key]: () => void } = {
        ArrowUp: () => setCurrentIdx(prev => (prev >= 0 ? prev - 1 : prev)),
        ArrowDown: () => setCurrentIdx(prev => (prev < searchList.length - 1 ? prev + 1 : prev)),
      };

      e.preventDefault();
      functionFor[e.key];
    },
    [searchList],
  );

싱글톤 패턴

export const cache = (() => {
  const EXPIRATION_TIME = 60 * 60 * 1000; // 60 * 60 * 1000 = 1hour
  const SEARCH_KEY = 'searchCache';

  let cacheStorage: Cache | undefined;

  async function getCacheStorage() {
    if (cacheStorage === undefined) {
      cacheStorage = await caches.open(SEARCH_KEY);
    }

    return cacheStorage;
  }

  return {
    async set(url: string, data: unknown) {
      const cacheStorage = await getCacheStorage();
      const response = new Response(JSON.stringify({ data, expirationTime: Date.now() + EXPIRATION_TIME }));

      await cacheStorage.put(url, response);
    },
    async get(url: string) {
      const cacheStorage = await getCacheStorage();
      const cacheResponse = await cacheStorage.match(url);

      return cacheResponse;
    },
  };
})();

싱글턴 패턴은 디자인 패턴 중 하나로, 클래스의 인스턴스가 하나만 생성되어서, 그 인스턴스를 어디서든지 접근할 수 있도록 하는 패턴을 말합니다. 이 패턴은 전역 변수를 사용하는 것과 비슷하지만, 인스턴스가 필요한 시점에 생성되므로 리소스를 절약할 수 있습니다.

이 코드는 캐시 저장소에 대한 접근을 관리하는 싱글턴 객체를 생성합니다. cache는 자기 자신을 즉시 실행 함수로 감싸서 싱글턴 패턴을 구현하고 있습니다. 이 즉시 실행 함수는 처음 한 번만 실행되며, 그 결과는 cache에 저장되어 이후에도 계속 사용됩니다.

즉시 실행 함수가 반환하는 객체는 set 메서드와 get 메서드를 가지며, 이 두 메서드 모두 캐시 저장소를 사용합니다. 여기서 캐시 저장소는 getCacheStorage 함수를 통해 생성되거나 가져옵니다. 이 getCacheStorage 함수는 cacheStorage 변수에 캐시 저장소가 아직 할당되지 않았을 때만 캐시 저장소를 생성하고, 그렇지 않은 경우에는 기존에 생성된 캐시 저장소를 반환합니다. 따라서 cacheStorage에는 항상 하나의 캐시 저장소 인스턴스만이 저장되며, 이 인스턴스는 set 메서드와 get 메서드에서 사용됩니다.

이처럼, 싱글턴 패턴은 특정 클래스나 객체에 대해 하나의 인스턴스만 생성하고, 그 인스턴스를 전역적으로 사용할 수 있도록 하는 방식입니다. 이 패턴은 전역 상태 관리, 로깅, 캐시, 데이터베이스 연결 등과 같이 한 번만 생성되어 여러 곳에서 사용되어야 하는 인스턴스를 관리할 때 유용합니다.

useRef vs useState

export function SearchProvider({
  children,
  searchService,
  localSearchRepository
}) {
  const [keywords, setKeywords] = useState([]);
  const [userInput, setUserInput] = useState('');
  const timerID = useRef(null);

  const saveUserInput = (name) => {
    setUserInput(name);
    delaySearch(name);
  };

  const search = searchService.search.bind(searchService);

  const delaySearch = (name) => {
    if (timerID.current) {
      clearTimeout(timerID.current);
    }

    if (!name) {
      setKeywords([]);
      return;
    }

    timerID.current = setTimeout(() => {
      if (localSearchRepository.get(name)) {
        return setKeywords(localSearchRepository.get(name));
      }
      search(name)
        .then((result) => {
          setKeywords(result);
          localSearchRepository.save(name, result);
        })
        .catch((error) => {
          console.error('API 요청 에러:', error);
        });
    }, 1000);
  };

  return (
    <SearchContext.Provider value={{ keywords, userInput, saveUserInput }}>
      {children}
    </SearchContext.Provider>
  );
}

useState와 useRef는 React에서 상태를 관리하기 위한 Hook들이지만, 그들이 다루는 상태와 업데이트 방식에는 중요한 차이가 있습니다.

useState는 상태를 변경할 때마다 컴포넌트를 리렌더링합니다. 이는 상태 변경이 UI에 반영되어야 하는 경우에 매우 유용합니다. 하지만, 이 경우에는 timerID 값이 변하더라도 컴포넌트를 리렌더링할 필요가 없습니다.

반면 useRef는 참조 값을 생성하고 관리하는데 사용됩니다. useRef를 사용하여 생성된 객체의 .current 프로퍼티를 변경해도 컴포넌트는 리렌더링되지 않습니다. 이러한 특성으로 인해 useRef는 컴포넌트의 리렌더링과 상관 없이 값이 유지되어야 하는 경우에 주로 사용됩니다.

이 코드에서는 timerID를 사용하여 setTimeout의 반환값을 저장하고 있습니다. 이 timerID는 setTimeout으로 예약된 작업을 취소하기 위해 필요하며, 이 값이 변경되었다고 해서 컴포넌트를 다시 렌더링할 필요가 없습니다. 따라서 이 경우에는 useRef를 사용하는 것이 적절합니다.

profile
필요하다면 공부하는 개발자, 한승준

0개의 댓글