원티드 3주차 기업 과제 : Deer Corporation Assignment Project ✅ 디어코퍼레이션 기업 과제입니다.
- Team 소개
- 과제 내용
- 기술 환경 및 tools
- 모델링 ERD
- API 명세서
- 기능 구현 추가설명
- 설치 및 실행 방법
이름 | 담당 기능 | 블로그 |
---|---|---|
공통 | 초기환경 설정, DB 모델링, postman api 문서 작성, README.md 작성, 배포, UnitTest | X |
유동헌 | 킥보드 대여 기능 | |
하예준 | 유저 인증 기능, 서비스 지역 생성 기능 | |
송치헌 | 킥보드 반납 및 요금 정책에 따른 요금 계산 기능 | |
오지윤(팀장) | 할인/벌금 수정 기능, 지역에 추가되는 할인/벌금 수정 기능 | |
손희정 | 할인/벌금 추가 기능, 지역에 추가되는 할인/벌금 추가 기능 |
디어는 사용자의 요금을 계산하기 위해 다양한 상황을 고려합니다.
- README 작성
- 프로젝트 빌드, 자세한 실행 방법 명시
- 구현 방법과 이유에 대한 간략한 설명
- 완료된 시스템이 배포된 서버의 주소
Swagger
나Postman
을 통한 API 테스트할때 필요한 상세 방법- 해당 과제를 진행하면서 회고 내용 블로그 포스팅
Swagger
나Postman
을 이용하여 API 테스트 가능하도록 구현
-
지역별로 다양한 요금제 적용 기능
-
다양한 할인/벌금 조건 추가 기능
-
킥보드 고장시 1분 이내 요금 청구되지 않는 기능
-
확장성을 고려한 시스템 설계 및 구현
-
새로운 할인이나 벌금 조건이 쉽게 추가될 수 있는 기능
✔️ REST API 기능
-
유저 Auth
- 회원가입 API
- 로그인 API
-
할인 / 벌금 CRUD
- 할인 조건 추가 API
- 벌금 조건 추가 API
- 할인 조건 변경 API
- 벌금 조건 변경 API
-
킥보드 대여 및 반납
- 킥보드 대여 API
- 킥보드 반납 요금 정책에 따른 요금 계산 API
-
위치 정보 CRUD
- 서비스 지역 생성 API
- 지역에 추가되는 할인 / 벌금 추가 API
- 지역에 추가되는 할인 / 벌금 삭제 API
API URL : http://3.38.118.39:8000
- Back-End:
Python 3.9.7
,Django 3.2.9
,GDAL 3.0.2
- Deploy: AWS
EC2
,RDS
- ETC:
Git
,Github
,Postman
Aquerytool URL
Password : 70pd61
├── areas
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── charges
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── config
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── CONVENTION.md
├── core
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── utils.py
│ ├── validation.py
│ └── views.py
├── manage.py
├── my_settings.py
├── PULL_REQUEST_TEMPLATE.md
├── README.md
├── requirements.txt
├── users
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
└── vehicles
├── admin.py
├── apps.py
├── models.py
├── tests.py
├── urls.py
└── views.py
[회원가입]
- 유저 인증 처리를 위해 회원가입 API
- 유저의 이름과 이메일, 휴대폰 번호를 요청 본문에 담으면 가입된다.
- Method: POST
http://3.38.118.39:8000/users/signup
- parameter : request_body
{
"name": "관리자2",
"email": "hayejun10@server.co.kr",
"phone_number": "010-2222-3333"
}
- response
{
"message": "SUCCUESS"
}
[로그인]
- 유저의 이메일과 휴대폰 번호를 통해서 User Auth 검증 한다.
- 로그인 성공 시, user_id 정보를 담은 access_token을 반환한다.
- Method: POST
http://3.38.118.39:8000/users/signin
- parameter : request_body
{
"email": "hayejun1013@naver.com",
"phone_number": "010-2723-4713"
}
- response
{
"message": "Success",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiOWNlZjllMDItMTA2ZC00MjQ4LWIyMTItMzUyMjc1OTBlYzFjIn0.8ubSxYVGeyGAjgGkb1DxCsc9uw2i27ihhksFBhlJ3Mg"
}
[할인 조건 추가]
- 관리자가 할인 조건을 추가하기 위해 요청한다.
- 벌금 퍼센트와 벌금 코드를 body에 담아 요청합니다.
- 관리자 외 유저가 추가할 시 에러를 반환한다.
- 할인 코드형식(D-P-1)이 다를 시 에러를 반환한다.
- 이미 존재하는 할인시 에러를 반환한다.
- Method: POST
http://3.38.118.39:8000/charges/discount
- header : Bearer token
- parameter : request_body
{
"code": "D-P-1",
"number": 30,
"description": "주차존 반납"
}
- response
{
"message": "SUCCESS"
}
[벌금 조건 추가]
- 관리자가 벌금 조건을 추가하기 위해 요청한다.
- 벌금 퍼센트와 벌금요금 코드를 body에 담아 요청합니다
- 관리자 외 유저가 추가할 시 에러를 반환한다.
- 벌금 코드형식(D-P-1)이 다를 시 에러를 반환한다.
- 이미 존재하는 벌금시 에러를 반환한다.
- Method: POST
http://3.38.118.39:8000/charges/penalty
- header : Bearer token
- parameter : request_body
{
"code": "P-A-1",
"number": 6000,
"description": "서비스 지역 이탈 요금 부과"
}
- response
{
"message": "SUCCESS"
}
[할인 조건 변경]
- 관리자만이 할인 조건 변경할 수 있다.
- 패스 파라미터로 변경할 할인의 코드를 보낸다.
- 할인 조건에 대한 금액과 설명을 body에 담아 요청한다.
- 관리자 외 유저가 수정 시 에러를 반환한다.
- 할인 조건에 대한 id가 없을 시 에러를 반환한다.
- Method: PUT
http://3.38.118.39:8000/charges/discount/D-P-1
- header : Bearer token
- parameter : request_body
- path parameter : 요금code
{
"number" : 20,
"description" : "주차존 반납"
}
- response
{
"message": "SUCCESS"
}
[벌금 조건 변경]
- 관리자만이 벌금 조건 변경할 수 있다.
- 패스 파라미터로 변경할 벌금의 코드를 보낸다.
- 벌금 조건에 대한 금액과 설명을 body에 담아 요청한다.
- 관리자 외 유저가 수정 시 에러를 반환한다.
- 벌금 조건에 대한 id가 없을 시 에러를 반환한다.
- Method: PUT
http://3.38.118.39:8000/charges/penalty/P-A-1
- header : Bearer token
- parameter : request_body
- path parameter : 요금code
{
"number" : 10000,
"description" : "서비스 지역 이탈 요금 부과"
}
- response
{
"message": "SUCCESS"
}
- 패스 파라미터로 킥보드 아이디를 받아 킥보드 대여를 시작한다.
- 대여가 됨과 동시에 유저 아이디를 통해 사용자를 확인한다.
- 만약 킥보드가 이미 사용 중일 경우 에러를 발생한다.
- 만약 킥보드 아이디가 잘 못 전달되면 에러를 발생한다.
- Method: POST
http://3.38.118.39:8000/vehicles/lend
- header : Bearer token
- parameter : request_body
{
"deer_name": "씽씽이"
}
- response
{
"message": "SUCCESS"
}
- 킥보드를 반납할 시 요금 계산 하는 API
- 킥보드를 반납할 경우, Path Parameter에 vehicle_id를 받는다.
- 킥보드 반납 지점에 대해 end_lat(위도), end_lng(경도)를 Payload Body로 값을 전달 받고, API 호출시 반납 시간이 자동으로 저장된다.
- 반납 지점이 지역을 이탈하여 주차하면, 거리에 비례하여 요금을 계산한다.
- 주차 금지 구역에 반납하면 일정 금액 벌금을 추가한다.
- 파킹존에 반납하면, 일정 금액 할인이 적용되고 이용 시간에 따라 금액이 책정된다.
- Method: PATCH
http://3.38.118.39:8000/vehicles/return/50cc196d-3f0b-4b2c-9052-c61af5c35505
- header : Bearer token
- parameter : request_body
{
"end_lat": 37.507649215664735,
"end_lng": 126.88256263732909
}
- response
{
"message": "UPDATED"
}
# DB에 생성된 할인/벌금 Object
"D-P-1" - 파킹존 주차 할인
"P-A-1" - 지역 이탈 벌금
"P-P-1" - 금지 구역에 주차
- 서비스 지역 데이터를 추가할 수 있는 API
- 추가하고자 하는 지역의 이름을 name으로 전달한다.
- boundary에 이중 리스트 형식의 위도, 경도 데이터를 보내면 polygon 데이터로 바뀌어서 서비스 지역 영역이 저장된다.
- Polygon 형태의 데이터로 저장하기 위해서는 첫 번째로 들어온 위도, 경도와 마지막으로 들어온 위도, 경도 데이터가 같아야 한다.
- 서비스 지역 영역의 중심 Point 정보는 center로 보낸다.
- 서비스 지역을 생성하면서 해당 지역의 할인 / 벌금 조건을 연결 시킬 수 있다.
- code에 리스트로 할인 / 벌금 객체의 ID를 전달한다.
- Method: POST
http://3.38.118.39:8000/areas/service/b1d96ffe56cd441bb12e789199f419de
- header : Bearer token
- parameter : request_body
{
"name": "신도림_1",
"boundary": [
[
126.88183307647704,
37.515121399458664
],
[
126.87754154205322,
37.51101945039516
],
[
126.87490224838255,
37.50712153887425
],
[
126.87981605529784,
37.50303617298528
],
[
126.89022302627565,
37.51027052249435
],
[
126.88659667968751,
37.512534304312624
],
[
126.88183307647704,
37.515121399458664
]
],
"center": [
26.8819327474983,
37.5091695751726
],
"code": [
"D-P-1",
"P-A-1",
"P-P-1"
]
}
- response
{
"message": "신도림_1 has created!"
}
- 관리자가 지역에 할인 / 벌금을 추가하기 위해 요청한다.
- 관리자 외 유저가 추가 시 에러를 반환한다.
- 할인 / 벌금에 대한 id가 없을 시 에러를 반환한다.
- 존재하지 않은 코드시 에러를 반환한다.
- 존재하지 않은 지역시 에러를 반환한다.
- Method: POST
http://3.38.118.39:8000/areas/service
- header : Bearer token
- parameter : request_body
- query parameter : 지역 name
{
"code": "D-P-1",
"region": "신도림_1"
}
- response
{
"message": "SUCCESS"
}
- 관리자가 지역에 할인 / 벌금을 삭제하기 위해 요청한다.
- 관리자 외 유저가 삭제시 에러를 반환한다.
- 할인 / 벌금에 대한 id가 없을 시 에러를 반환한다.
- 존재하지 않은 코드시 에러를 반환한다.
- 존재하지 않은 지역시 에러를 반환한다.
- Method: DELETE
http://3.38.118.39:8000/areas/service
- header : Bearer token
- parameter : request_body
- query parameter : 지역 name
{
"code": "D-P-1",
"region": "신도림_1"
}
- response
{
"message": "SUCCESS"
}
core.debuggers.py
에 다음과 같이 작성한다.
from functools import wraps
from time import perf_counter as prf_cnt #코드 실행시간 측정
from django.db import connection, reset_queries
from django.conf import settings
def query_debugger(func):
@wraps(func)
def wrapper(*args, **kwargs):
reset_queries()
number_of_start_queries = len(connection.queries)
start = prf_cnt()
result = func(*args, **kwargs)
end = prf_cnt()
number_of_end_queries = len(connection.queries)
print("========================================")
print(f"function : {func.__name__}")
print(f"number of queries : {number_of_end_queries - number_of_start_queries}")
print(f"finished in : {(end - start):.2f}s")
return result
return wrapper
이 모듈을 view
의 각 메서드에 데코레이터로 연결하면 해당 메서드에서 평가되는 쿼리의 개수를 다음과 같이 확인할 수 있다.
query_debugger
를 사용해서 쿼리를 최적화하는데 전략을 잘 구성할 수 있다.
geojson.io
에서 원하는 지역에 도형(polygon
) 및 점(point
)을 생성하여 geojson
형식의 파일을 생성하고 unit test에서 geojson
파일을 읽어서 각 feature
를 생성하였다.
haversine 라이브러리를 이용하여 WGS84 좌표계 시스템으로 되어있는 두 좌표간 거리를 계산해 주었고, 지역을 벗어난 킥보드가 해당 지역으로부터 얼마나 떨어져 있는지 계산하기 위해 epsg 5174 좌표로 변환한뒤 킥보드 반납 지점(Point)과 지역(Polygon)의 거리를 계산해 주었다.
- 해당 프로젝트를 clone하고, 프로젝트로 들어간다.
https://github.com/wanted-InfinityLoop/deer-InfinityLoop.git .
cd deer
- 가상환경으로 miniconda를 설치한다. Go
conda create -n deer python=3.9
conda actvate deer
- 가상환경 생성 후, requirements.txt를 설치한다.
pip install -r requirements.txt
Django==3.2.9
django-cors-headers==3.10.0
gunicorn==20.1.0
mysqlclient==2.1.0
PyMySQL==1.0.2
bcrypt==3.2.0
PyJWT==2.3.0
- migrate 후 로컬 서버 가동
python manage.py makemigrations
python manage.py migrate
python manage.py runserver