Skip to content

wanted-10th-team4/pre-onboarding-10th-2-4

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

33 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Best Search - 2์ฃผ์ฐจ ๊ณผ์ œ

๋ชฉ์ฐจ

  • ํ”„๋กœ์ ํŠธ์†Œ๊ฐœ
  • ๊ธฐ๋Šฅ์†Œ๊ฐœ
  • ์‹คํ–‰
  • ๊ฐœ๋ฐœํ™˜๊ฒฝ
  • ๊ธฐ์ˆ ์Šคํƒ
  • ํ”„๋กœ์ ํŠธ ํŒŒ์ผ ๊ตฌ์กฐ
  • ํŒ€์†Œ๊ฐœ

ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

์ €ํฌ ํŒ€์˜ ํ”„๋กœ์ ํŠธ๋Š” Best Search์ž…๋‹ˆ๋‹ค.

์‚ฌ์ดํŠธ์˜ ๊ฒ€์ƒ‰ ์˜์—ญ์„ ํด๋ก ํ•˜์—ฌ ๊ฒ€์ƒ‰์ฐฝ์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰์ฐฝ์— ๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ–ˆ์„ ๋•Œ ํ•ด๋‹นํ•˜๋Š” ๊ฒ€์ƒ‰์–ด๋ฅผ ์ถ”์ฒœํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ํ”„๋กœ์ ํŠธ๋Š” 10๋ช…์˜ ํŒ€์› ๋ชจ๋‘ ํ•จ๊ป˜ ์ง„ํ–‰ํ•˜์˜€์œผ๋ฉฐ, ๊ฐ์ž ๊ณผ์ œ๋ฅผ ์ˆ˜ํ–‰ํ•œ ํ›„ ๊ฒฐ๊ณผ๋ฌผ์„ ํ†ตํ•ด์„œ ํŒ€์›๋“ค๋ผ๋ฆฌ ํ† ๋ก ํ•˜์—ฌ ๊ฐ€์žฅ ์ ์ ˆํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ๋ฐฉ์‹์„ ๋„์ถœํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Best Practice์ธ ์ด์œ 

๐Ÿ“ API ํ˜ธ์ถœ๋ณ„๋กœ ๋กœ์ปฌ ์บ์‹ฑ ๊ตฌํ˜„

์บ์‹œ์Šคํ† ๋ฆฌ์ง€

  • API ํ˜ธ์ถœ ์‹œ Cache Storage๋ฅผ ํ™•์ธํ•˜์—ฌ ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋‹ค๋ฉด API ํ˜ธ์ถœ์„ ํ•˜์ง€ ์•Š๊ณ  ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • expire time์„ 24์‹œ๊ฐ„์œผ๋กœ ์„ค์ •ํ•˜์—ฌ expire time์ด ์ง€๋‚˜๋ฉด Cache Storage์—์„œ ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์šฐ๊ณ  ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • Local Storage๋Š” ์ตœ๋Œ€ 5MB ์ €์žฅ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ Cache Storage๋Š” ํฌ๋กฌ์˜ ๊ฒฝ์šฐ ๋””์Šคํฌ ์ตœ๋Œ€ ์šฉ๋Ÿ‰์˜ 80% ๊นŒ์ง€ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Cache Storage ๋น„๋™๊ธฐ ๋ฐฉ์‹์ด๊ธฐ ๋•Œ๋ฌธ์— Local Storage ๋ณด๋‹ค ๋ถ€ํ•˜๊ฐ€ ์ ์Šต๋‹ˆ๋‹ค. Local Storage๋Š” String ํƒ€์ž…๋งŒ ์ง€์›ํ•˜์ง€๋งŒ Cache Storage๋Š” ๋‹ค์–‘ํ•œ ํƒ€์ž…์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

import { SetCacheProps } from '@type/props';
export const getCacheByKey = async (key: string) => {
const cache = await caches.open('cache');
const cachedResponse = await cache.match(key);
if (cachedResponse) {
const cachedData = await cachedResponse.json();
if (cachedData.expiry > new Date().getTime()) return cachedData.value;
cache.delete(key);
}
return null;
};
export const setCacheByExpireTime = async ({ key, value, expireTime = 0 }: SetCacheProps) => {
const cache = await caches.open('cache');
const item = {
value,
expiry: new Date().getTime() + expireTime,
};
const response = new Response(JSON.stringify(item));
console.log(response);
await cache.put(key, response);
};

import { GetSearchResultsResponse, SearchParam } from '@type/data';
import { getCacheByKey, setCacheByExpireTime } from '@utils/cache';
import { CACHE_EXPIRE_TIME } from '@utils/constant';
import axios from 'axios';
const PROXY = window.location.hostname === 'localhost' ? '' : '/proxy';
const getSearchResults = async (param: SearchParam): Promise<GetSearchResultsResponse> => {
const cacheItem = await getCacheByKey(param.name);
if (cacheItem) return cacheItem;
console.info('calling api');
const response = await axios.get(`${PROXY}${process.env.REACT_APP_API_URI}`, { params: param });
setCacheByExpireTime({ key: param.name, value: response.data, expireTime: CACHE_EXPIRE_TIME });
return response.data;
};
export default getSearchResults;



๐Ÿ“ ๊ฒ€์ƒ‰์–ด ์ž…๋ ฅ๋งˆ๋‹ค API๊ฐ€ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋„๋ก API ํ˜ธ์ถœ ํšŸ์ˆ˜ ์ค„์ด๊ธฐ

๋””๋ฐ”์šด์‹ฑ

  • useDebounce custom hook์„ ์ƒ์„ฑํ•˜์—ฌ ๊ฒ€์ƒ‰์–ด์— debounce๋ฅผ ์ ์šฉํ•˜์˜€๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒ€์ƒ‰์–ด ์ž…๋ ฅ์„ ์‹œ์ž‘ํ•œ ํ›„ 500ms๋™์•ˆ ์ž…๋ ฅ์ด ์—†์œผ๋ฉด API๊ฐ€ ํ˜ธ์ถœ๋˜๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

import { useRef } from 'react';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const useDebounce = <T extends any[]>(callback: (...args: T) => void, timeout: number) => {
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
return (...args: T) => {
if (timer.current) clearTimeout(timer.current);
timer.current = setTimeout(() => {
callback(...args);
timer.current = null;
}, timeout);
};
};
export default useDebounce;

const getDebounceResult = useDebounce(async text => {
try {
const data = await getSearchResults({ name: text });
setRecommendationList(data);
} catch (error) {
console.error('err: ', error);
}
}, DEBOUNCE_LIMIT_TIME);
useEffect(() => {
setSelectedIndex(-1);
if (inputText.length === 0) setRecommendationList([]);
if (inputText.length > 0) getDebounceResult(inputText);
}, [inputText]);



๐Ÿ“ ํ‚ค๋ณด๋“œ๋งŒ์œผ๋กœ ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด๋“ค๋กœ ์ด๋™ ๊ฐ€๋Šฅํ•˜๋„๋ก ๊ตฌํ˜„

ํ‚ค๋ณด๋“œ์ด๋™

  • ๊ฒ€์ƒ‰์ฐฝ์—์„œ onKeyDown ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ event.key ๊ฐ’์ด ArrowDown, ArrowUp์ผ ๊ฒฝ์šฐ selectedIndex๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ฒŒ ํ–ˆ๊ณ  ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด์˜ index์™€ selectedIndex๊ฐ€ ๊ฐ™์„ ๋•Œ background-color๊ฐ€ ๋ณ€๊ฒฝ๋˜๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

const getUpIndex = (prevIndex: number) => {
if (prevIndex <= 0) return maxIndex;
return prevIndex - 1;
};
const getDownIndex = (prevIndex: number) => {
if (prevIndex >= maxIndex) return 0;
return prevIndex + 1;
};
const onKeyDownItem = (key: string) => {
if (key === ARROW_UP) setSelectedIndex(prevIndex => getUpIndex(prevIndex));
if (key === ARROW_DOWN) setSelectedIndex(prevIndex => getDownIndex(prevIndex));
};
const onKeyDownInputText = (e: React.KeyboardEvent<HTMLInputElement>) => {
if ([ARROW_UP, ARROW_DOWN].includes(e.key)) {
e.preventDefault();
if (e.nativeEvent.isComposing === false) {
onKeyDownItem(e.key);
}
}
};


๊ธฐ๋Šฅ์†Œ๊ฐœ

๋ฐฐํฌ ๋งํฌ: Best Search

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

install

npm install

start

npm start

๊ฐœ๋ฐœํ™˜๊ฒฝ

  • ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ : Chrome browser
  • IDE : Visual Studio Code 1.71.0 (Universal)
  • ์ธํ”„๋ผ : Vercel
  • ์ฃผ์š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    • React : 18.2.0
    • TypeScript : 4.9.5
    • React-router-dom : 6.10.0
    • Styled-components : 5.3.10,

๊ธฐ์ˆ ์Šคํƒ

Environment

config

Language

Development

deploy

ํ”„๋กœ์ ํŠธ ํŒŒ์ผ ๊ตฌ์กฐ

๐Ÿ“ฆBestToDo
โ”œโ”€โ”€ .github
โ”‚   โ””โ”€โ”€ ISSUE_TEMPLATE
โ”œโ”€โ”€ .husky
โ”œโ”€โ”€ ๐Ÿ“‚public
โ””โ”€โ”€ ๐Ÿ“‚src
    โ”œโ”€โ”€ ๐Ÿ“‚api
    โ”œโ”€โ”€ ๐Ÿ“‚components
    โ”‚   โ””โ”€โ”€ ๐Ÿ“‚common
    โ”‚   โ””โ”€โ”€ ๐Ÿ“‚Landing
    โ”‚   โ””โ”€โ”€ ๐Ÿ“‚Recommendation
    โ”‚   โ””โ”€โ”€ ๐Ÿ“‚SearchInput
    โ”œโ”€โ”€ ๐Ÿ“‚hooks
    โ”œโ”€โ”€ ๐Ÿ“‚pages
    โ”‚   โ””โ”€โ”€ ๐Ÿ“‚LandingPage
    โ”œโ”€โ”€ ๐Ÿ“‚types
    โ””โ”€โ”€ ๐Ÿ“‚utils

ํŒ€์†Œ๊ฐœ

wet6123 1two13 plumpsqrl9744 tkdgh3050 senasoon
wet6123์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„ 1two13์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„ plumpsqrl9744์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„ tkdgh3050์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„ senasoon์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„
5thwin yminj1029 Leeseunghwan7305 JKyEun chyerin802
5thwin์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„ yminj1029์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„ Leeseunghwan7305์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„ JKyEun์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„ chyerin802์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages