이번 달 초에 했던 토이 프로젝트에서 티커를 입력하면 주가를 조회할 수 있는 웹사이트를 만듦.
2주에 걸쳐 추가할 수 있는 부분이 뭐가 있을까 고민했고 업데이트를 완료했다.
먼저 업데이트 사항으로는
- 입력값 유효성 검사
- 데이터 로드와 렌더링 사이의 시간 틈 제거
- SNS 공유 기능 추가
- not found page
- loading spinner 추가
입력값 유효성 검사
먼저 유효성 검사를 하기 전에 수정한 코드가 있음.
const tickerInput = useRef();
tickerInput 값을 useState로 받아서 onChange로 만들어줬는데 이렇게 모든 입력마다 데이터를 업로드하는 현상이 발생해 로딩 시간 개선을 위해 ref로 받아왔다.
그리고 submit을 할 때 ref의 value를 읽어서 데이터를 로드하는 방식으로 수정.
const checkValidInput = (value) => {
if (value.trim().length === 0) {
alert("티커를 입력해주세요.");
return false;
} else if (!/^[a-zA-Z]+$/.test(value)) {
alert("올바른 티커를 입력해주세요.");
tickerInput.current.value = "";
return false;
}
return true;
};
다음 1차 유효성 검사로 빈 입력과 알파벳이 아닌 입력을 했을 때를 만들어주었다.
빈 입력으로 submit을 하면 입력해달라는 메시지
알파벳이 아닌 입력값을 submit 하면 올바른 티커를 입력해달라는 메시지를 alert하도록 만들었다.
그리고 얘를 통과하면 true를 리턴.
그 다음에 이제 데이터를 fetch 해야 함.
기존에는 주식 데이터와 뉴스 데이터 다른 함수로 불러와서 별개로 실행했는데 이 부분도 수정.
const fetchData = async (ticker) => {
setFetchLoading(true);
const res = await fetch(
`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${ticker}&apikey=${process.env.NEXT_PUBLIC_STOCK_API_KEY}`
);
const newData = await res.json();
console.log(newData["Global Quote"]);
if (
newData["Error Message"] ||
Object.keys(newData["Global Quote"]).length === 0
) {
setFetchLoading(false);
setFetchSuccess(false);
setNewsData([]);
return;
}
await fetchNewsData(ticker);
setFetchLoading(false);
setFetchSuccess(true);
return setData(Object.values(newData["Global Quote"]));
};
로딩인지 아닌지 하는 state는 일단 설명을 제끼고
먼저 주식에 대한 데이터를 로딩했을 때 데이터를 찾을 수 없으면
api에서 에러메시지나 빈 object를 출력하더라.
그래서 그 부분을 확인해서 데이터 fetch여부에 따라 fetchSuccess state를 변경하도록 코드를 바꾸었다.
{!fetchLoading && fetchSuccess && (
<Content
stockData={firstFetch ? startingData : data}
newsData={newsData}
/>
)}
{!fetchLoading && !fetchSuccess && (
<ErrorMessage errorType='no company found'></ErrorMessage>
)}
그리고 success 여부에 따라 content 컴포넌트를 표시할지 error message를 표시할지 조건부로 렌더링
자 정리하면 이렇다.
빈 입력값 or 알파벳 확인 -> 그다음 티커 확인해서
존재하는 기업이면 data를 fetch해서 컴포넌트 만들고
실패하면 error message 출력
데이터 로드와 렌더링 사이의 시간 틈 제거
위의 유효성 검사 부분과도 연계가 되는데
fetchStockData와 fetchNewsData를 스크립트로 작성하니
화면에는 주식 데이터가 먼저 나오고 조금 이따가 뉴스가 나왔다.
렌더링 시간 일치가 안 돼어 매우 불편...
그래서 유효성 검사 로직 부분 다시 보면
const fetchData = async (ticker) => {
...
const res = await fetch(
...
);
if (
newData["Error Message"] ||
Object.keys(newData["Global Quote"]).length === 0
) {
......
return;
}
await fetchNewsData(ticker);
...
return setData(Object.values(newData["Global Quote"]));
이런 식으로 await을 한 번 더 걸었다.
주식 데이터를 기다렸다가 받고 그 다음에 뉴스 데이터 받을 때까지 기다리고 setData로 데이터를 넣어주었다.
그렇게 되니깐 주식과 뉴스 데이터가 한 방에 setData로 들어가기 때문에 렌더링이 한 번에 되서 편-안.
SNS 공유 기능 추가
사실 대단한 거 있을 줄 알았는데 생각보다 별개 없었다.
https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fclipstock.vercel.app
https://twitter.com/intent/tweet?text=${sendText}&url=${sendUrl}
친절하게도 페이스북과 트위터는 이렇게 쉽게 공유할 수 있는 url을 만들어 놓았다.
자 그러면 뭐가 하나 빠진 느낌이 들지 않는가.
사실 하나는 아니고 인스타랑 카톡이 빠졌다.
인스타랑 카톡을 활용하는 디바이스는 대부분이 모바일이다.
모바일에서 활용할 수 있는 내장 공유 api를 찾았다.
바로 navigator.share
const sendUrl = "https://clipstock.vercel.app/";
const shareData = {
title: "미국주식 주가 조회",
url: sendUrl,
};
const shareLink = async () => {
try {
await navigator.share(shareData);
} catch (err) {
console.log(err);
}
};
이렇게 shareLink라는 함수를 만들어서 걸고
<a
className='flex cursor-pointer visible md:invisible ml-auto sm:ml-0 bg-slate-300 p-2 rounded-md'
onClick={shareLink}>
Share
</a>
onClick으로 걸면 끝난다.
이게 모바일에서 어떻게 보이냐면
share 버튼을 부르면 이렇게 뜬다.
그래서 카카오톡이나 문자나 인스타나 url을 보낼 수 있다.
너무나 유용한 걸 알아내서 흐뭇.
not found page
<section className='text-gray-600 body-font'>
<div className='container px-5 py-24 mx-auto'>
<div className='xl:w-1/2 lg:w-3/4 w-full mx-auto text-center'>
<p className='leading-relaxed text-xl sm:text-3xl'>
페이지를 찾을 수 없습니다.
</p>
<p className='text-lg mb-6'>인터넷 연결을 확인해보세요.</p>
<Link href='/'>
<a className='text-white bg-indigo-500 border-0 py-3 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg'>
다시 시도
</a>
</Link>
<br />
<span className='inline-block h-1 w-10 rounded bg-indigo-500 mt-8 mb-6'></span>
<h2 className='text-gray-900 font-medium title-font tracking-wider text-sm'>
<p className='text-gray-500'>문의사항</p>
<Link href='mailto:jasonsc@korea.ac.kr'>
<a>jasonsc@korea.ac.kr</a>
</Link>
</h2>
</div>
</div>
</section>
not found 404 page는 위와 같이 만들었다.
별거는 없고 인터넷 다시 확인하라는 메시지
다시 시도하면 홈으로 다시 접속하게 하고
문의사항으로 이메일은 남기고 mailto를 걸어 누르면 바로 이메일을 보낼 수 있게 만들었다.
loading spinner 추가
로딩 스피너는 데이터 로딩 중에 빈 화면으로만 뜨지 않게 하기 위해 추가했다.
fetchLoading이라는 state를 활용해서 이 state가 true면 로딩 스피너가 등장할 수 있게 했다.
그래서 업데이트를 완료한 데모는 이렇다.
기존에 있던 거랑 크게 차이는 느껴지지는 않을 수도 있지만
코드가 매우매우 바뀌었고 그 안에서 의외로 고생을 많이했다....ㅋㅋㅋㅋ
https://github.com/manu1307/clipstock
깃헙에다가 쑥스럽지만 readme 까지 써서 업로드를 완료했다.
기존에 있던 주소도 좀 드러워서 콕 집어주는 클립의 특성을 이용해 클립스탁이라는 이름으로 바꾸어주었다.
업데이트 하면서 배운 점
- state의 생명 주기에 대해 더 익숙해짐. 왜 안 변하지? 엄청 고민했던 시간이 길었는데 그게 다 함수 실행 과정 + state 생명주기가 꼬여서 그랬던 것이었다.
- async await에 대한 이해가 더 올라감. 보통 예제로 맨날 한 번만 하고 말았는데 스스로 코드 진행과정을 생각해서 await을 또 붙이는 연습.
- 기본 web api에서 share이라는 api를 활용하면 쉽게 공유를 할 수 있음. 이건 진짜 유용했다. 너무나 간단해서 발견하고 유레카를 외쳤음.
- state와 ref를 어떻게 활용해야할지 구분이 좀 됨. ref는 dom을 직접 다루는 거라 react에서 그렇게 권장하지는 않는다고 알고 있는데 그래도 활용하는 게 더 좋다고 생각이 들면 충분히 활용해봐도 좋을 듯.
- 페이스북, 트위터는 그냥 공유링크가 있음. 그거 그대로 복붙하고 내 웹사이트 url만 붙여주면 땡.
'웹 > 개인 프로젝트' 카테고리의 다른 글
HTML CSS JS로 크리스마스 카드 만들기 + 눈 내리는 효과 (0) | 2022.12.25 |
---|---|
포트폴리오 웹사이트 클론코딩 회고록 (0) | 2022.07.11 |
7월 1주차 스프린트 회고 - 미국주식 주가조회 웹사이트 (0) | 2022.07.10 |