월별
총 예산을 설정합니다.카테고리
별 예산을 설계(=추천)하여 사용자의 과다 지출을 방지합니다.지출
을 금액
, 카테고리
등을 지정하여 등록 합니다. 언제든지 수정 및 삭제 할 수 있습니다.월별
설정한 예산을 기준으로 오늘 소비 가능한 지출
을 알려줍니다.지출
을 카테고리
별로 안내받습니다.지난 달 대비
, 지난 요일 대비
, 다른 유저 대비
등 여러 기준 카테고리 별
지출 통계를 확인 할 수 있습니다.계정명
, 패스워드
입력하여 회원가입계정
, 비밀번호
로 로그시 JWT
가 발급됩니다.JWT
가 항시 포함되며, JWT
유효성을 검증합니다.예산
을 설정합니다. 예산은 카테고리
를 필수로 지정합니다.
식비
: 40만원, 교통
: 20만원카테고리 별 예산 설정에 어려움이 있는 사용자를 위해 예산 비율 추천 기능이 존재합니다.
카테고리
지정 없이 총액 (ex. 100만원) 을 입력하면, 카테고리
별 예산을 자동 생성합니다.
기존 요구사항(유저들의 평균으로 추천)과 다르게 스타일을 추천하고 스타일에 따른 정해진 비율을 선택하는 것으로 수정했습니다.
유저들이 서비스 접근 시 쉽게 설정이 가능하도록 4가지 스타일을 추천합니다.
지출 일시
, 지출 금액
, 카테고리
와 메모
를 입력하여 생성합니다
생성
, 수정
, 읽기(상세)
, 읽기(목록)
, 삭제
, 합계제외
할 수 있습니다.생성한 유저
만 위 권한을 가집니다.읽기(목록)
은 아래 기능을 가지고 있습니다.
기간
으로 조회 합니다.지출 합계
, 카테고리 별 지출 합계
를 같이 반환합니다.카테고리
만 조회.최소
, 최대
금액으로 조회.
합계제외
처리한 지출은 목록에 포함되지만, 모든 지출 합계
에서 제외됩니다.설정한 월별
예산을 만족하기 위해 오늘 지출 가능한 금액을 총액
과 카테고리 별 금액
으로 제공합니다.
고려사항 1. 앞선 일자에서 과다 소비하였다 해서 오늘 예산을 극히 줄이는것이 아니라, 이후 일자에 부담을 분배한다.
고려사항 2. 기간 전체 예산을 초과 하더라도 0원
또는 음수
의 예산을 추천받지 않아야 한다.
최소 금액
을 자유롭게 설정하세요.유저의 상황에 맞는 1 문장의 멘트
노출.
15333원 과 같은 값이라면 백원 단위 반올림 등으로 사용자 친화적이게 변환
선택 구현 기능) 매일 08:00 시 알림 발송
총액
과 카테고리 별 금액
을 알려줍니다.월별
설정한 예산 기준 카테고리 별
통계 제공
적정 금액
: 오늘 기준 사용했으면 적절했을 금액지출 금액
: 오늘 기준 사용한 금액위험도
: 카테고리 별 적정 금액, 지출금액의 차이를 위험도로 나타내며 %(퍼센테이지) 입니다.
지난 달
대비 총액
, 카테고리 별
소비율.
식비
지난달 대비 150%지난 요일
대비 소비율
월요일
이라면 지난 월요일
에 소비한 모든 기록 대비 소비율월요일
평소 대비 80%다른 유저
대비 소비율
유저
가 예산 대비 사용한 평균 비율 대비 나의 소비율다른 사용자
대비 120%가상환경: venv
언어 및 프레임워크:
데이터 베이스:
(전제) python >= 3.11
과 mysql >= 8.0
은 설치되어 있습니다.
python -m venv venv
source venv/Scripts/activate
pip install -r requirements.txt
manage.py
가 있는 위치에서 모델 migration을 해줍니다.python manage.py migrate
[참고]
python manage.py makemigrations
: 아직 데이터베이스에 적용되지 않음, 데이터베이스 스키마 변경사항을 기록하는 용python manage.py migrate
: 위의 명령어에서 생성된 마이그레이션 파일들을 데이터베이스에 적용
(지금은 두번째 명령어만 작성하는게 맞습니다. 변경사항 없이 DB에 적용하기 위함이기 때문입니다.)manage.py
가 있는 위치에서 서버를 실행합니다.python manage.py runserver
POST /api/auth/signup
Body Parameter | Type | Description |
---|---|---|
username |
string |
Required. |
password |
string |
Required. |
EX)
{
"username": "user1",
"password": "devpassword1"
}
HTTP 201 Created
Allow: POST, OPTIONS
Content-Type: application/json
Vary: Accept
{
"username": "user1"
}
POST /api/auth/jwt-login
Body Parameter | Type | Description |
---|---|---|
username |
string |
Required. |
password |
string |
Required. |
EX)
{
"username": "user1",
"password": "devpassword1"
}
HTTP 200 OK
Allow: POST, OPTIONS
Content-Type: application/json
Vary: Accept
{
"username": "user1",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpVXCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjk5NDM2NTA3LCJpYXQiOjE2OTk0MzI5MDcsImp0aSI6Ijc4Y2E1NzI3NDZkMDQzYzA4ZWZlNWM3NGNjMDFkNDNiIiwidXNlcl9pZCI6MX0.W-z5wAg0zNJWlaLA6mb0xEMPeEdOqenKeKrCsenWCNs"
}
GET /api/budgets/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. 'Bearer eyJhbGciOiJIU...' |
Content-Type |
string |
Required. application/json |
{
"message": "get categories",
"data": [
{
"id": 1,
"name": "undefined"
},
{
"id": 2,
"name": "쇼핑"
},
{
"id": 3,
"name": "교통"
},
{
"id": 4,
"name": "주거비"
},
{
"id": 5,
"name": "취미"
},
{
"id": 6,
"name": "교육"
},
{
"id": 7,
"name": "병원"
},
{
"id": 8,
"name": "식비"
},
{
"id": 9,
"name": "저축/투자"
}
]
}
POST /api/budgets/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
Body Parameter | Type | Description |
---|---|---|
category |
integer |
Required. |
amount |
integer |
Required. |
{
"budget_list": [
{
"category": 2,
"amount": 30
},
{
"category": 3,
"amount": 50
},
{
"category": 4,
"amount": 20
}
]
}
{
"message": "budget create success!",
"setup_user": 2,
"setup_user_total": 100,
"data": [
10,
11,
12
]
}
1개의 레코드만 수정합니다.
PATCH /api/budgets/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
Body Parameter | Type | Description |
---|---|---|
category |
integer |
Required. |
amount |
integer |
Required. |
{
"category": 2,
"amount": 30
}
{
"message": "success!",
"data": "Budget[1] - category쇼핑 is changed.(30)"
}
GET /api/budgets/rec/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
{
"message": "recommend styles",
"data": [
"식비위주",
"쇼핑위주",
"취미위주",
"저축/투자위주"
]
}
POST /api/budgets/rec/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
Body Parameter | Type | Description |
---|---|---|
total |
integer |
Required. |
style |
string |
Required. |
{
"total": 1200000,
"style": "식비위주"
}
{
"message": "suceess!",
"setup_user": 2,
"setup_user_total": 1200000,
"data": [
41,
42,
43,
44,
45
]
}
예산 수정 스타일 지정 변경이기 때문에 해당되지 않는 카테고리는 0으로 수정하고, 없었던 budget 카테고리 행은 생성합니다.
PATCH /api/budgets/rec/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
Body Parameter | Type | Description |
---|---|---|
total |
integer |
Required. |
style |
string |
Required. |
{
"total": 1000000,
"style": "쇼핑위주"
}
{
"message": "style change success",
"data": "쇼핑위주"
}
유저가 소비한 항목들에 대한 모든 정보와 전체/카테고리별 소비금액 정보를 반환
GET /api/expenditures/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
{
"message": "success!",
"data": [
{
"id": 1,
"amount": 22000,
"appropriate_amount": 10000,
"memo": "어디어디서 썼습니다",
"is_exept": false,
"created_at": "2023-11-15T09:39:37.523988",
"updated_at": "2023-11-15T10:23:46.735049",
"user": 2,
"category": 3
},
{
"id": 2,
"amount": 25000,
"appropriate_amount": 10000,
"memo": "어디어sdfsdfsdf디서 썼습니다",
"is_exept": false,
"created_at": "2023-11-15T09:39:52.412771",
"updated_at": "2023-11-15T09:39:52.412771",
"user": 2,
"category": 4
},
{
"id": 3,
"amount": 68000,
"appropriate_amount": 10000,
"memo": "간만에 뭘좀 샀는데 뭘샀을까?",
"is_exept": false,
"created_at": "2023-11-15T10:30:42.755083",
"updated_at": "2023-11-15T10:30:42.755083",
"user": 2,
"category": 5
},
{
"id": 4,
"amount": 68000,
"appropriate_amount": 10000,
"memo": "간만에 뭘좀 샀는데 뭘샀을까?",
"is_exept": false,
"created_at": "2023-11-16T04:30:05.493767",
"updated_at": "2023-11-16T04:30:05.494782",
"user": 2,
"category": 5
},
{
"id": 5,
"amount": 68000,
"appropriate_amount": 10000,
"memo": "간만에 뭘좀 샀는데 뭘샀을까?",
"is_exept": false,
"created_at": "2023-11-16T04:30:30.164717",
"updated_at": "2023-11-16T04:30:30.165718",
"user": 2,
"category": 5
},
{
"id": 6,
"amount": 15000,
"appropriate_amount": 10000,
"memo": "호롤로",
"is_exept": false,
"created_at": "2023-11-16T06:01:07.085904",
"updated_at": "2023-11-16T06:01:07.085904",
"user": 2,
"category": 2
},
{
"id": 7,
"amount": 27000,
"appropriate_amount": 10000,
"memo": "호롤로2",
"is_exept": false,
"created_at": "2023-11-16T06:01:18.473358",
"updated_at": "2023-11-16T06:01:18.473358",
"user": 2,
"category": 2
},
{
"id": 8,
"amount": 33300,
"appropriate_amount": 10000,
"memo": "33호호호홓ㅎㅎ3332",
"is_exept": false,
"created_at": "2023-11-16T06:01:31.638764",
"updated_at": "2023-11-16T06:01:31.638764",
"user": 2,
"category": 3
},
{
"id": 9,
"amount": 2300,
"appropriate_amount": 10000,
"memo": "기타소비1",
"is_exept": false,
"created_at": "2023-11-19T16:23:22.261079",
"updated_at": "2023-11-19T16:23:22.261079",
"user": 2,
"category": 1
},
{
"id": 10,
"amount": 4400,
"appropriate_amount": 10000,
"memo": "오늘의소비 카테고리2",
"is_exept": false,
"created_at": "2023-11-19T16:23:40.198193",
"updated_at": "2023-11-19T16:23:40.198193",
"user": 2,
"category": 2
},
{
"id": 11,
"amount": 12000,
"appropriate_amount": 10000,
"memo": "오늘의소비 카테고리2",
"is_exept": false,
"created_at": "2023-11-19T16:23:50.713640",
"updated_at": "2023-11-19T16:23:50.713640",
"user": 2,
"category": 2
},
{
"id": 12,
"amount": 52000,
"appropriate_amount": 10000,
"memo": "오늘의소비 카테고리2",
"is_exept": false,
"created_at": "2023-11-19T16:23:55.347926",
"updated_at": "2023-11-19T16:23:55.348941",
"user": 2,
"category": 2
},
{
"id": 13,
"amount": 24000,
"appropriate_amount": 10000,
"memo": "오늘의소비 카테고리3",
"is_exept": false,
"created_at": "2023-11-19T16:24:05.103259",
"updated_at": "2023-11-19T16:24:05.104258",
"user": 2,
"category": 3
},
{
"id": 14,
"amount": 28900,
"appropriate_amount": 10000,
"memo": "오늘의소비 카테고리5",
"is_exept": false,
"created_at": "2023-11-19T16:24:23.814410",
"updated_at": "2023-11-19T16:24:23.814410",
"user": 2,
"category": 5
},
{
"id": 15,
"amount": 37580,
"appropriate_amount": 10000,
"memo": "오늘의소비 카테고리5",
"is_exept": false,
"created_at": "2023-11-19T16:24:34.861078",
"updated_at": "2023-11-19T16:24:34.861078",
"user": 2,
"category": 7
},
{
"id": 16,
"amount": 4600,
"appropriate_amount": 10000,
"memo": "오늘의소비 카테고리5",
"is_exept": false,
"created_at": "2023-11-19T16:24:44.307684",
"updated_at": "2023-11-19T16:24:44.307684",
"user": 2,
"category": 5
}
],
"static_data": {
"category_group": [
{
"undefined": {
"count": 1,
"sum": 2300
}
},
{
"쇼핑": {
"count": 5,
"sum": 110400
}
},
{
"교통": {
"count": 3,
"sum": 79300
}
},
{
"주거비": {
"count": 1,
"sum": 25000
}
},
{
"취미": {
"count": 5,
"sum": 237500
}
},
{
"교육": {
"count": 0,
"sum": 0
}
},
{
"병원": {
"count": 1,
"sum": 37580
}
},
{
"식비": {
"count": 0,
"sum": 0
}
},
{
"저축/투자": {
"count": 0,
"sum": 0
}
}
],
"total_sum": 492080
}
}
POST /api/expenditures/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
Body Parameter | Type | Description |
---|---|---|
category |
integer |
Required. |
amount |
integer |
Required. |
memo |
string |
Required. |
{
"category": 3,
"amount": 200,
"memo": "카테고리덧ㄷ러ㅣㄴ소비소빗"
}
{
"message": "success!",
"data": {
"category": 3,
"amount": 200,
"memo": "카테고리덧ㄷ러ㅣㄴ소비소빗",
"user": 2,
"appropriate_amount": 10000
}
}
1개 지출에 대한 모든 내용을 확인하는 API
GET /api/expenditures/<int:expenditure_id>/
GET /api/expenditures/15/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
{
"message": "get sucess!",
"data": {
"id": 15,
"amount": 37580,
"appropriate_amount": 10000,
"memo": "오늘의소비 카테고리5",
"is_exept": false,
"created_at": "2023-11-19T16:24:34.861078",
"updated_at": "2023-11-19T16:24:34.861078",
"user": 2,
"category": 7
}
}
PATCH /api/expenditures/<int:expenditure_id>/
GET /api/expenditures/15/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
Body Parameter | Type | Description |
---|---|---|
category |
integer |
Required. |
amount |
integer |
Required. |
memo |
string |
Required. |
{
"category": 3,
"amount": 22000,
"memo": "어디어디서 썼습니다",
"appropriate_amount": 10000,
"is_exept": false
}
{
"message": "update sucess!"
}
DELETE /api/expenditures/<int:expenditure_id>/
DELETE /api/expenditures/15/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
{
"message": "delete sucess!",
"data": {
"id": 15,
"amount": 22000,
"appropriate_amount": 10000,
"memo": "어디어디서 썼습니다",
"is_exept": false,
"created_at": "2023-11-19T16:24:34.861078",
"updated_at": "2023-11-19T23:38:12.157585",
"user": 2,
"category": 3
}
}
이번 달 지출과 예산 내용을 조합해서 오늘 지출 내용을 추천해줍니다.
GET /api/expenditures/rec/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
{
"message": "today(2023-11-19)'s message: you have to save your money!",
"data": {
"today_recommand": 168530,
"period": "2023-11-01 00:00:00 ~ 2023-11-19 00:00:00",
"쇼핑": 25600,
"교통": 3700,
"주거비": 14500,
"취미": 3330,
"식비": 20800,
"저축/투자": 0
}
}
소비 내용에 대한 비율과 카테고리별 소비 비용을 알려줍니다. 예산에 없는 범위에 소비 내용이 있을 경우 -1로 처리했습니다.
GET /api/expenditures/noti/
Parameter | Type | Description |
---|---|---|
Authorization |
string |
Required. Bearer eyJhbGciOiJIU... |
Content-Type |
string |
Required. application/json |
{
"message": "success!",
"data": {
"total": 76,
"undefined": -1,
"쇼핑": 267,
"교통": 654,
"주거비": 0,
"취미": 1006,
"교육": -1,
"병원": -1,
"식비": 0,
"저축/투자": -1,
"unit": "percent"
},
"recommend_data": {
"today_recommand": 168530,
"period": "2023-11-01 00:00:00 ~ 2023-11-19 00:00:00",
"쇼핑": 25600,
"교통": 3700,
"주거비": 14500,
"취미": 3330,
"식비": 20800,
"저축/투자": 0
}
}
APScheduler
를 사용해서 지출알림과 지출추천 기능을 주기적으로 실행하도록 기능 추가 예정(~11/24)RESTful API 설계
모델링
시작 기준일
과 총예산
은 지속적으로 확인해야 하고 고정적인 값이기 때문에 User
모델에 필드를 추가하여 확인하도록 결정했습니다.서비스 사용자 관점에서
Scheduler 설계
이름 | github주소 |
---|---|
유진수 | https://github.com/YuJinsoo |