SOBI
는 소비내역이 달력에 표시되며 월별 소비금액을 그래프로 볼 수 있는 웹 어플리케이션입니다.
로컬에서 테스트 하실 경우 root 폴더에 .env 파일 생성 후
VITE_USER_ID=team3
숫자 바꾸면 다른 조 데이터도 볼 수 있어요
팀원 | 이 정우 |
이 시우 |
문 현수 |
문 대현 |
---|---|---|---|---|
담당 | 총괄 차트 |
소비내역 생성 소비내역 수정 소비내역 삭제 |
소비내역 불러오기 달력 |
소비내역 검색 검색 자동완성 |
- 소비 기록 작성 API Request:
POST /expenses
Content-Type: application/json
{
"amount": 100,
"userId": "user123",
"category": "food",
"date": "2023-07-04T10:30:00.000Z"
}
Response:
Status: 201 Created
{
"message": "Expense created successfully"
}
- 소비 품목 목록 API Request:
GET /categories?userId={userId}
Response: Status: 200 OK
['food', 'clothing', 'electronics'];
- 검색어에 해당하는 소비 항목 및 금액 조회 API Request:
GET /expenses/search?q={keyword}&userId={userId}
Response:
Status: 200 OK
[
{
"amount": 100,
"userId": "user123",
"category": "food",
"date": "2023-07-04T10:30:00.000Z"
},
{
"amount": 80,
"userId": "user456",
"category": "food",
"date": "2023-07-03T14:20:00.000Z"
}
]
- 일별, 주별, 월별 소비 조회 API Request:
GET /expenses/summary?period={period}&userId={userId}
period : daily, weekly, monthly
Response:
Status: 200 OK
[
{
"_id": "2023-07-04",
"totalAmount": 180
},
{
"_id": "2023-07-03",
"totalAmount": 80
}
]
- 소비 기록 수정 API Request:
PUT /expenses/123
Content-Type: application/json
{
"amount": 150,
"userId": "user123",
"category": "food",
"date": "2023-07-04T10:30:00.000Z"
}
Response:
Status: 200 OK
{
"message": "Expense updated successfully"
}
- 소비 기록 삭제 API Request:
DELETE / expenses / 123;
Response:
Status: 200 OK
{
"message": "Expense deleted successfully"
}
- 소비 기록 달력 호출 API Request:
GET /expenses/calendar?year=2023&month=7&userId={userId}
Response:
Status: 200 OK
{
"1": [
{
"amount": 100,
"userId": "user123",
"category": "food",
"date": "2023-07-01T10:30:00.000Z"
}
],
"4": [
{
"amount": 80,
"userId": "user456",
"category": "food",
"date": "2023-07-04T14:20:00.000Z"
}
]
}
- Ant Design의 tour 기능을 활용.
- 방문 여부를 로컬에 저장하여 첫방문시에만 활성화된다.
- 일소비 금액별로 색이 다르게 표시된다.
- 일소비 내역 횟수가 해당 날짜에 숫자로 표시된다.
- 우측 하단 + 버튼을 눌러 소비내역을 등록할 수 있다.
- 우측 상단 검색창을 통해 소비내역을 검색할 수 있다.
- Date picker로 날짜와 시간을 선택할 수 있다.
- 유효성 검사를 통과해야 소비내역을 등록할 수 있다.
- 유효성 검사 결과와 등록 결과 등을 팝업 메세지로 사용자에게 알려준다.
- 페이지를 이동할 수 있다.
- 좌측 하단에 Color picker로 사용자가 원하는 강조색을 선택할 수 있다.
- 리액트 내장 context api를 사용하여 강조색을 전역상태관리.
- 로컬 저장소에 색의 hex값을 저장하여 새로고침을 하거나 브라우저를 종료하여도 강조색이 유지된다.
- 월별 소비액을 그래프로 나타낸다.
- chartjs 라이브러리 사용.
- 통신 중에는 skeleton 로딩 ui를 보여준다.
- 기간을 선택할 수 있다.
- api 통신 성공 여부를 배너 메세지로 표시한다.
-
협업
-
Ant Design
- 리액트 컴포넌트 기반의 UI라이브러리.
- 몇가지 규약만 지켜주면 빠른시간내에 완성도 있고 일관성있는 UI를 만들 수 있다.
- 다양한 기능들을 최대한 사용해보고자 하였음.
- Tour, Table, Modal, Skeleton, Drawer, Date Picker, Color Picker, Autocomplete 등...
- 그러나 커스터마이징 제약이 있다는 점, CSS를 잘 다루는 못하는 초보자의 경우 antD 의존성이 크게 올라가므로 배우는 입장에서는 scss나 tailwind가 더 적절하다고 생각함.
-
ChartJS
- 공식 문서를 보면서 필요한 기능들을 적용하였다.
- 커스터마이징 항목이 매우 많은 것은 장점이자 단점.
-
Context API
- src/context/AccentColorContext.tsx
- src/hooks/useAccentColor.ts
- 외부 전역상태관리 라이브러리를 사용하지 않고 리액트 내장 기능을 사용.
- 재사용하지 않기 때문에 custom hook를 사용할 필요는 없다.
-
SPA에서 server state와 client state 동기화 문제
-
소비내역을 등록, 수정, 삭제가 발생할 때마다 서버로부터 최신의 데이터를 받아와야 함.
-
본 프로젝트에서는 togglefetch라는 변수를 useState로 선언하고 소비내역의 변경이 발생하는 handling 함수에서
setToggleFetch((prev) => !prev)
. 그리고 통신이 일어나는 useEffect의 dependency에 togglefetch를 넣어주었다.const [togglefetch, setToggleFetch] = useState(false); useEffect(() => { const getData = async () => { setLoading(true); try { const response = await getExpenses(year, month); setMonthlyExpenses(response); } catch (error) { console.log(error); } finally { setLoading(false); } }; getData(); }, [month, year, togglefetch]);
-
이 방법은 페이지전환이 발생하지 않는 SPA에서 server state와 client state 동기화 문제를 가장 쉽게(
새로고침) 해결할 수 있는 방법이다. -
다른 방법으로 get 요청 없이 client state를 setState로 업데이트 시켜주는 방법이 있다.
- 그러나 본프로젝트에서는 서버에서 _id가 생성되고 이 값으로 수정과 삭제를 진행하기 때문에 이 방법은 적절하지 않다.
- 만약 모든 데이터를 클라이언트에서 생성하는 경우 이러한 전략이 유효.
- 예를 들어 새로운 소비내역을 post 요청으로 db에 저장하고, 동시에
setState((prev) => [...prev, newData])
를 통해 화면을 재랜더링. - 이러한 경우 post요청만 실행되고 get요청은 실행되지 않아도 되므로 자원을 아낄 수 있다.
-
또 다른 방법으로
react-query
라이브러리를 사용하는 방법이 있다.
-
-
UTC시간, local 시간 문제
- 서버에서 요구하는 시간의 형태는
ISO 8601
이다. 2023-07-22T17:51:36.506Z
와 같이 표기가 되며 z는 UTC를 의미함.- UTC는 경도 0, 즉 그리니치 천문대를 기준으로 한 시간이며, 한국은 이 시간보다 9시간 빠르다.
- 글로벌한 서비스들의 등장으로 UTC시간을 사용하는 것이 중요하다.
- 본 프로젝트에서 월별 소비 내역을 get요청하면 다음과 같은 데이터가 온다.
{ "1": [ { "_id": "64b8efd86f8d76dd4f24b5a4", "amount": 34000, "userId": "team3", "category": "피자헛", "date": "2023-07-01T17:24:32.781Z", "__v": 0 } ], "21": [ { "_id": "64baa68fcedf8391c1aa38e3", "amount": 4500, "userId": "team3", "category": "커피", "date": "2023-07-21T15:38:46.175Z", "__v": 0 } ], ... }
- 해당 객체의 key값이 되는 "일(day)"은 UTC 시간을 기준으로 생성된 값이다. 그러다 보니 실제로는 22일 오전 4시에 입력한 값이 9시간 전인 21일에 담겨서 오는 문제가 발생한다.
- 서버코드를 손대지 못하는 상황에서 이러한 문제를 해결하기 위해 등록 및 수정시 시간 정보에 9시간을 더하여 서버에 전송하였다.
- 그리고 서버로부터 받은 시간은 클라이언트에서 get
UTC
...()을 하여 UTC시간(9시간 더해진 UTC시간)을 변환 없이 그대로 사용한다.
export default function formatDateAndTime(dateString: string) { const date = new Date(dateString); const year = date.getUTCFullYear(); const month = (date.getUTCMonth() + 1).toString().padStart(2, '0'); const day = date.getUTCDate().toString().padStart(2, '0'); const hours = date.getUTCHours().toString().padStart(2, '0'); const minutes = date.getUTCMinutes().toString().padStart(2, '0'); const formattedDate = { date: `${year}년 ${month}월 ${day}일`, time: `${hours}:${minutes}`, }; return formattedDate; }
- 서버에서 요구하는 시간의 형태는
- 형식 비슷한 기능의 모달(수정,등록)을 한가지 모달 폼 활용해 두가지 기능을 구현하는 부분에서 시행착오를 겪었지만, 코드의 효율을 높일수 있었습니다.
- 이슈를 활용한 프로젝트 관리를 처음 접해보아서 초반에 커밋단위, PR단위를 어느정도로 해야할지 감을 잡기 힘들었지만, 이번 경험을 통해 추후 진행될 프로젝트에서 이슈,프로젝트를 활용 할 수 있을것 같습니다.
- 주석 작성을 통해서 다시 한번 코드의 구조를 확실하게 이해할수 있었고, 이를 팀원들 간에 설명하는 시간을 통해 전체 프로젝트를 더 확실하게 이해할 수 있었습니다.
- React calendar 라이브러리 사용
- 이번 프로젝트에서 react-calendar라는 라이브러리를 사용했는데, 어느 요소에 어떤 기능이 들어있는지 잘 파악이 되지 않아서 어려웠습니다. 특히 생각한 기능이 있었는데, 생각한대로 하기에는 react-calendar을 사용하기엔 어려움이 있었습니다.
- 그렇다고 해서 만들어서 사용하는 것은 시간이 더 오래걸렸을 것 같다는 생각이 들었습니다.
- 다음 번에는 주어진 상황과 기획한 대로 구현할 수 있는 방법을 선택하면 좋을 것 같습니다.
- 프로젝트를 진행한 이후에 팀원들끼리 모여서 본인이 맡은 부분에 대해서 설명하는 시간을 가졌는데, 굉장히 유의미한 시간이었던 것 같습니다. 물론 프로젝트 규모가 크다면 이렇게 하는 것은 어렵겠지만, 이렇게 브리핑하면서 전반적인 프로젝트의 코드나 맡지 않은 파트에 대해서도 이해할 수 있어서 좋았던 것 같습니다.
- 프로젝트를 진행할 때, 타입스크립트에 대한 이해가 좀 낮아서 타입을 일단 any로 설정해놓고 진행한 적이 있는데, 타입스크립트에 대한 공부가 좀 더 필요하다고 생각했습니다.
- 검색 기능을 구현하면서 서버에 등록되어 있는 데이터를 불러오는 데 있어 어려움이 있었습니다. 검색 결과를 모달 화면에 표시하고 표시된 데이터를 클릭 시 클릭한 날짜에 해당하는 모든 데이터를 불러오는 과정에 어려움이 있었고 그 외에도 코드를 구현하는 데 있어 어려움이 있을 때마다 팀장님께서 도움을 주셔서 문제 해결을 하는데 많은 도움이 되었습니다. 이번 프로젝트를 진행하며 쌓은 경험이 추후 프로젝트를 진행할 때 많은 도움이 될 것 같습니다.
- 이번에 antd 라이브러리를 처음 사용해 보며 antd라는 유용한 라이브러리 경험을 하게 되어 추후 진행될 프로젝트에 유용하게 사용할 수 있을 것 같습니다.
- 프로젝트 마무리 정리를 하며 팀원들 간에 본인이 맡은 파트에 대해 설명하는 유익한 시간을 가졌습니다. 다른 팀원의 파트는 주석 처리로 설명을 잘 해 놓았다 하더라도 텍스트 만으로는 이해하기 어려운 부분이 있지만 이번 발표로 각자 맡은 파트를 설명하는 시간을 가져 각자의 코드를 이해하는데 많은 도움이 된 것 같습니다.