[TIL] 엘리스 SW 엔지니어 트랙 Day 076
글 작성자: 망고좋아
반응형
📖 오늘 배운 내용 - 2022.02.12
- 그래프 페이지 mock 데이터 연결
- json 서버 연결해서 axios 사용
- 리액트 라이프 사이클
- 타입스크립트의 필요성
📝 axios 결정 이유
- axios는 json을 자동으로 적용해서 response 객체를 바로 반환하지만 fetch는 promise자체를 반환해서 json으로 변환을 해야 한다는 차이점이 있다.
- axios는 data를 바로 전달하지만 fetch는 body로 json.stringify()를 통해서 서버가 이해할 수 있도록 문자열 파싱을 해야 한다.
📝 리액트 라이프 사이클 공부
- 추후 추가
📝 로딩 창 :: suspense 사용
import React, { useCallback, useEffect, useState, Suspense } from "react";
import { Container } from "@/components/Container/style";
import DateController from "@/components/DateController";
import * as api from "@/api";
import LoadingModal from "@/components/LoadingModal";
import MonthYearBtn from "./MonthYearBtn";
import PieChartComponent from "./PieChart";
import { DateControllerWrapper } from "./style";
const LineGraph = React.lazy(() => import("./LineGraph"));
function Graph() {
const monthButton = true;
const yearButton = false;
const ClickedMoth = true;
const toDay = new Date();
const [date, setDate] = useState(toDay);
const [clickButtonColor, setClickButtonColor] = useState(true);
const [checkMonth, setCheckMonth] = useState(false);
const [graphTitle, setGraphTitle] = useState("월간");
const [commitData, setCommitData] = useState([]);
const getCommitsPerMonth = async () => {
const year = date.toISOString().slice(0, 4);
const data = await api.getCommitsTotalPerMonth(year);
if (data.success) {
const commitPerYear = await data.commitPerYear;
const createData = commitPerYear.slice(1).map((commitCnt, index) => ({
name: `${date.toISOString().slice(0, 2)}.${index + 1}`,
commit: commitCnt,
}));
setCommitData(createData);
} else {
setCommitData([]);
}
};
useEffect(() => {
getCommitsPerMonth();
}, [date]);
const changeDate = (value) => {
const newDate = new Date(date.getFullYear() + value, date.getMonth());
setDate(newDate);
};
const clickLeft = () => {
if (date.getFullYear() - 2000 <= 0) return;
changeDate(-1);
};
const clickRight = () => {
if (toDay.getFullYear() - date.getFullYear() <= 0) return;
changeDate(1);
};
const goToday = () => {
setDate(toDay);
};
const handleMonthBtn = useCallback(() => {
if (monthButton) {
setClickButtonColor(monthButton);
setCheckMonth(false);
setGraphTitle(ClickedMoth ? "월간" : "년간");
}
}, [graphTitle]);
const handlYearBtn = useCallback(() => {
if (!yearButton) {
setClickButtonColor(yearButton);
setCheckMonth(true);
setGraphTitle(!ClickedMoth ? "월간" : "년간");
}
}, [graphTitle]);
return (
<Container>
<Suspense fallback={<LoadingModal />}>
<DateControllerWrapper>
{!checkMonth && (
<DateController
date={date}
clickLeft={clickLeft}
clickRight={clickRight}
goToday={goToday}
month={false}
/>
)}
</DateControllerWrapper>
<MonthYearBtn
isClick={clickButtonColor}
handlYearBtn={handlYearBtn}
handleMonthBtn={handleMonthBtn}
/>
<LineGraph graphTitle={graphTitle} commitData={commitData} />
<PieChartComponent />
</Suspense>
</Container>
);
}
export default Graph;
- 로딩 창 부분을 suspense로 구현했다.
- suspense의 신세계를 경험하고 팀원분들에게 공유했다. 그리고 적용하시던 중 페이지 내에서 비동기 요청 시 로딩 컴포넌트가 동작되지 않는다는 문제점을 말씀해주셨다.
suspense는 렌더링 시점에서만 감지해준다.
- 그래서 캘린더 내에서 비동기 요청을 하더라도 리 렌더링을 하지 않는다.
- 따라서 기존에 있는 isLoding을 완전히 대체할 수 없다. 첫 페이지 로딩은 suspense를 사용하고, 페이지 내에서 비동기 처리를 하는 부분은 isLoading을 사용해서 로딩 창을 보여주는 방식으로 가기로 했다.
- 추후 데이터 불러오기도 지원해준다고 하니까 지원해줄 때까지 저는 숨 참고 기다리겠...
- suspense를 공부하다 빠뜨린 내용이었는데.... 지식을 공유하고 모르는 정보를 얻을 수 있었다.
- 이것이 집단지성의 힘인가! 공유를 함으로써 나 또한 모르는 지식을 얻을 수 있어서 좋았다.
import React, { Suspense, lazy } from "react";
import { Routes, Route, BrowserRouter } from "react-router-dom";
import LoadingModal from "@/components/LoadingModal";
const Header = lazy(() => import("./components/Header"));
const Nav = lazy(() => import("./components/Nav"));
const Login = lazy(() => import("./pages/login"));
const Main = lazy(() => import("./pages/main"));
const MonthlyCalender = lazy(() => import("./pages/monthlyCalender"));
const RankPage = lazy(() => import("./pages/rank"));
const Graph = lazy(() => import("./pages/graph"));
const MyPage = lazy(() => import("./pages/myPage"));
const Setting = lazy(() => import("./pages/setting"));
const GoalSetting = lazy(() => import("./pages/goalSetting"));
const Badge = lazy(() => import("./pages/badge"));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<LoadingModal />}>
<Header />
<Nav />
<Routes>
<Route path="/" element={<Login />} />
<Route path="/main" element={<Main />} />
<Route path="/calender" element={<MonthlyCalender />} />
<Route path="/rank" element={<RankPage />} />
<Route path="/graph" element={<Graph />} />
<Route path="/mypage" element={<MyPage />} />
<Route path="/setting" element={<Setting />} />
<Route path="/goal" element={<GoalSetting />} />
<Route path="/badge" element={<Badge />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
export default App;
- 따라서 suspense는 앱 전체에 적용해줘서 페이지 이동 때마다 로딩 창을 한 번에 구현하기로 하고, 페이지 내에서 데이터를 요청하는 부분에서는 isLoading라는 상태를 만들어줘서 로딩 컴포넌트를 구현하기로 했다.
🛠 graph/index.js
import React, { useCallback, useState } from "react";
import { Container } from "@/components/Container/style";
import DateController from "@/components/DateController";
import MonthYearBtn from "./MonthYearBtn";
import PieChartComponent from "./PieChart";
import { DateControllerWrapper } from "./style";
const LineGraph = React.lazy(() => import("./LineGraph"));
function Graph() {
const monthButton = true;
const yearButton = false;
const ClickedMoth = true;
const toDay = new Date();
const [date, setDate] = useState(toDay);
const [clickButtonColor, setClickButtonColor] = useState(true);
const [checkMonth, setCheckMonth] = useState(false);
const [graphTitle, setGraphTitle] = useState("월간");
const changeDate = (value) => {
const newDate = new Date(date.getFullYear() + value, date.getMonth());
setDate(newDate);
};
const clickLeft = () => {
if (date.getFullYear() - 2000 <= 0) return;
changeDate(-1);
};
const clickRight = () => {
if (toDay.getFullYear() - date.getFullYear() <= 0) return;
changeDate(1);
};
const goToday = () => {
setDate(toDay);
};
const handleMonthBtn = useCallback(() => {
if (monthButton) {
setClickButtonColor(monthButton);
setCheckMonth(false);
setGraphTitle(ClickedMoth ? "월간" : "년간");
}
}, [graphTitle]);
const handlYearBtn = useCallback(() => {
if (!yearButton) {
setClickButtonColor(yearButton);
setCheckMonth(true);
setGraphTitle(!ClickedMoth ? "월간" : "년간");
}
}, [graphTitle]);
return (
<Container>
<DateControllerWrapper>
{!checkMonth && (
<DateController
date={date}
clickLeft={clickLeft}
clickRight={clickRight}
goToday={goToday}
month={false}
/>
)}
</DateControllerWrapper>
<MonthYearBtn
isClick={clickButtonColor}
handlYearBtn={handlYearBtn}
handleMonthBtn={handleMonthBtn}
/>
<LineGraph graphTitle={graphTitle} date={date} clickYear={checkMonth} />
<PieChartComponent />
</Container>
);
}
export default Graph;
🛠 LineGraph/index.js
import React, { useEffect, useState } from "react";
import LoadingModal from "@/components/LoadingModal";
import PropTypes from "prop-types";
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
} from "recharts";
import * as api from "@/api";
import * as LineGraphs from "./style";
function LineGraph({ graphTitle, date, clickYear }) {
const [commitData, setCommitData] = useState([]);
const [loading, setLoading] = useState(false);
if (clickYear) {
const getRecentThreeYearCommitsCount = async () => {
setLoading(true);
const data = await api.getRecentThreeYear();
if (data.success) {
const createData = data.recentThreeYear.map((it) => ({
name: it[0],
commit: it[1],
}));
setCommitData(createData);
} else {
setCommitData([]);
}
setLoading(false);
};
useEffect(() => {
getRecentThreeYearCommitsCount();
}, [clickYear]);
} else {
const getCommitsPerMonth = async () => {
setLoading(true);
const year = date.toISOString().slice(0, 4);
const data = await api.getCommitsTotalPerMonth(year);
if (data.success) {
const commitPerYear = await data.commitPerYear;
const createData = commitPerYear.slice(1).map((commitCnt, index) => ({
name: `${year.slice(2, 4)}.${index + 1}`,
commit: commitCnt,
}));
setCommitData(createData);
} else {
setCommitData([]);
}
setLoading(false);
};
useEffect(() => {
getCommitsPerMonth();
}, [date]);
}
return (
<LineGraphs.Container>
{loading ? (
<LoadingModal />
) : (
<>
{commitData.length ? (
<>
<LineGraphs.Title>{graphTitle} 커밋 추이</LineGraphs.Title>
<LineGraphs.Wrapper>
<LineChart width={350} height={280} data={commitData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Line
type="monotone"
dataKey="commit"
stroke="#6ABD8C"
activeDot={{ r: 2 }}
isAnimationActive={false}
/>
</LineChart>
</LineGraphs.Wrapper>
</>
) : (
<LineGraphs.NoData>데이터가 없습니다.</LineGraphs.NoData>
)}
</>
)}
</LineGraphs.Container>
);
}
LineGraph.propTypes = {
graphTitle: PropTypes.string,
date: PropTypes.instanceOf(Date).isRequired,
clickYear: PropTypes.bool.isRequired,
};
LineGraph.defaultProps = {
graphTitle: "월간" || "연간",
};
export default LineGraph;
📝 hasOwnProperty
- hasOwnProperty를 사용하면 객체가 어떤 key값을 가지고 있는지 확인할 수 있다.
const getUsersReposLanguage = async () => {
const res = await api.getReposLanguage();
const languageCountObj = {};
if (res.success) {
const reposLanguageArray = res.data;
reposLanguageArray.forEach((it) => {
if (
Object.prototype.hasOwnProperty.call(languageCountObj, it.language)
) {
languageCountObj[it.language] += 1;
} else {
languageCountObj[it.language] = 1;
}
});
const languageCountArray = [];
const language = Object.keys(languageCountObj);
const values = Object.values(languageCountObj);
for (let i = 0; i < language.length; i += 1) {
languageCountArray.push({
name: language[i],
value: values[i],
});
}
languageCountArray.sort((a, b) => b.count - a.count);
console.log(languageCountArray);
}
};
📝 hasOwnProperty eslint 오류 해결
💡 오늘 깨달은 것
- 예전에는 컴퓨터가 느려서 복잡한 연산은 백에서 처리했지만, 요즘은 컴퓨터가 좋아져서 데이터 원형을 프론트에 어떻게든 빨리 던지고 프론트에서 복잡한 연산을 수행해서 데이터를 뿌려준다.
- map, reduce는 브라우저마다 동작 방식이 다르기 때문에 사용자마다 다른 결과가 보일 수 있다. 이런 경우는 백에서 연산해서 프론트에 데이터를 넘겨주는 것이 좋다.
- 어제오늘부터 타입스크립트의 필요성을 드디어 느끼게 되었다!!! 이전에는 타입스크립트가 좋다 좋다 그러고 많은 기업에서 사용하고 있다 보니 기초만 살짝 배우고 직접적으로 필요성을 느끼지 못했다.
- 하지만 이번 리액트 프로젝트를 하면서 타입스크립트의 필요성을 절실히 느끼게 되었고 왜 많은 회사나 개발자들이 TS를 좋아하고 사용하는지 알 거 같다.
- 차량 이동 중 공부 및 아이디어를 얻기 위해 다른 팀원분들의 코드를 태블릿으로 읽었다. proptypes를 적용하기 전에는 받아오는 props를 뭐지? 자료형이 뭐지? 이러면서 props를 따라 올라가고 그랬다.
- 하지만 proptypes 적용 이후에는 이게 어떤 형태로 넘어와서 이렇게 뿌려주고 동작하는구나라는 것을 눈으로 코드가 읽히니까 이해하기 수월했다. 이러한 부분을 타입스크립트가 도와주니까 많은 개발자와 회사가 원하는구나....!!! 개발 시 실수도 줄여주고!!! 개발자를 위한 언어이다.
- 이전에 김병철 코치님이 새로운 기술이 나온 배경에는 필요와 이유가 있다면서 이유를 찾아가며 공부하고 익히라고 했는데 뒤늦게 격하게 공감하게 되었다.
- 이전까지는 취업을 위해 커리어를 위해 타입스크립트를 공부했지만, 이제는 내 필요에 의해 공부하게 될 테니 흥미가 생기고 재밌게 공부할 수 있을 거 같다!!
📌 참고
반응형
'프로그래밍 > Today I Learned' 카테고리의 다른 글
[TIL] 엘리스 SW 엔지니어 트랙 Day 078 (0) | 2022.02.21 |
---|---|
[TIL] 엘리스 SW 엔지니어 트랙 Day 077 (0) | 2022.02.20 |
[TIL] 엘리스 SW 엔지니어 트랙 Day 075 (0) | 2022.02.15 |
[TIL] 엘리스 SW 엔지니어 트랙 Day 074 (0) | 2022.02.15 |
[TIL] 엘리스 SW 엔지니어 트랙 Day 073 (0) | 2022.02.10 |
댓글
이 글 공유하기
다른 글
-
[TIL] 엘리스 SW 엔지니어 트랙 Day 078
[TIL] 엘리스 SW 엔지니어 트랙 Day 078
2022.02.21 -
[TIL] 엘리스 SW 엔지니어 트랙 Day 077
[TIL] 엘리스 SW 엔지니어 트랙 Day 077
2022.02.20 -
[TIL] 엘리스 SW 엔지니어 트랙 Day 075
[TIL] 엘리스 SW 엔지니어 트랙 Day 075
2022.02.15 -
[TIL] 엘리스 SW 엔지니어 트랙 Day 074
[TIL] 엘리스 SW 엔지니어 트랙 Day 074
2022.02.15