SpringBoot 2.1.4
JDK8
JPA
sqlite-jdbc
lombok
jbcrypt
JWT
- com.demo.taxiApi
- common : exception Handler 및 유틸성 클래스
- controller :
- CallController(/api/call/*) : 배차목록조회, 택시 배차 요청, 기사 배차 기능수행 Controller
- AuthController(/api/auth/*) : 회원가입, 로그인, 토큰 갱신 기능수행 Controller
- domain : JPA Entity 클래스(Call, User)
- exception : 커스텀 예외처리 클래스 및 예외처리 코드
- interceptor : Header에 Authorization 확인 및 검증 interceptor 클래스
- mapper : JPA Entity 클래스와 Model간의 컨버팅 클래스
- model : Model 클래스
- repository : JPA repository 클래스
- service : service 클래스
- resources : property파일
- test : 테스트 수행 클래스
- JWT로 API의 인증을 관리하는 구조로 구현하였습니다.
- 회원가입시 password는 jbcrypt해시로 암호화되어 저장.
- 회원가입/로그인 성공시 email과 userType(Role)정보가 포함되어있는 토큰(JWT) 출력.
- 토큰 만료일은 60분으로 만료시 API접근이 불가하며 사전에 토큰갱신을 통해 새로운 만료일의 토큰으로 갱신필요.
- 발급받은 토큰을 Header에 추가하여 호출하여야 인증 완료.
- 택시 배차 요청은 승객(PASSENGER)만, 기사 배차는 기사(DRIVER)만 접근 가능하도록 userType(Role)을 구분하였습니다.
- 목록 조회는 승객/기사 모두 가능
- 기사배차의 경우 한번 할당이 되면 추가 기사 할당을 할 수 없도록 하였습니다.
- 택시 배차 요청의 경우 한명의 승객이 추가로 배차 요청을 할수도 있다고 판단하여 여러건의 배차 요청을 제한하지는 않았습니다.
// 빌드 수행
cd taxiApi
mvn package (또는 ./mvnw package)
// 서버 기동(9876 port)
java -jar target/taxiApi-0.0.1-SNAPSHOT.jar
name | key | type | desc |
---|---|---|---|
id | primary key | bigint | ID |
varchar(255) | 이메일 | ||
password | varchar(255) | 비밀번호 | |
user_type | varchar(255) | 유저 타입 (PASSENGER / DRIVER) |
name | key | type | desc |
---|---|---|---|
id | primary key | bigint | ID |
passenger_id | foreign key | bigint | 승객ID |
driver_id | foreign key | bigint | 기사ID |
address | varchar(255) | 요청 주소 | |
status | varchar(255) | 배차 상태 (REQUESTED / ASSIGNED) | |
request_date | timestamp | 배차 요청 시간 | |
assign_date | timestamp | 배차 완료 시간 |
Swagger UI 참고 (서버기동후 실행) http://localhost:9876/swagger-ui.html
회원가입 수행
curl -X POST \
http://localhost:9876/api/auth/signup \
-H 'Content-Type: application/json' \
-H 'cache-control: no-cache' \
-d '{
"email": "[email protected]",
"password": "test1234",
"userType": "PASSENGER"
}'
Method : POST
Auth required : NO
Body constraints
{
"email": "[email format]",
"userType": "[PASSENGER or DRIVER]"
}
Body example 3개의 필수 입력 프로퍼티로 구성
{
"email": "[email protected]",
"password": "test1234",
"userType": "PASSENGER"
}
Code : 200 OK
{
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyVHlwZSI6IlBBU1NFTkdFUiIsImV4cCI6MTU1NTMzMTI1MiwiZW1haWwiOiJzdmxhZGFjb0BkLmNvbSIsImRlc2MiOiLthYzsiqTtirjsmqkgRGVtb1RheGlBcGnsl5DshJwg67Cc7ZaJIn0.LuYtm-gdFIeAAzA0ABRHmH3sOxKp5ennT_uUqnzGvPw"
}
Condition : 이메일 형식이 잘못 되었을때.
Code : 400 Bad Request
Content :
{
"code": "INVALID_EMAIL_FORMAT",
"message": "잘못된 이메일 형식입니다."
}
Condition : 3개의 필수 입력중 한개라도 누락 되었을때.
Code : 400 Bad Request
Content :
{
"code": "SIGNUP_REQUIRED_EMAIL",
"message": "등록할 이메일을 입력해 주세요."
}
// or
{
"code": "SIGNUP_REQUIRED_PASSWORD",
"message": "등록할 패스워드를 입력해 주세요."
}
// or
{
"code": "SIGNUP_REQUIRED_USERTYPE",
"message": "유저의 타입(승객/기사)을 입력해 주세요."
}
Condition : 이미 등록된 이메일일때.
Code : 400 Bad Request
Content :
{
"code": "SIGNUP_EXIST_EMAIL",
"message": "이미 등록된 이메일 입니다."
}
로그인 수행
curl -X POST \
http://localhost:9876/api/auth/signin \
-H 'Content-Type: application/json' \
-H 'cache-control: no-cache' \
-d '{
"email": "[email protected]",
"password": "test1234"
}'
Method : POST
Auth required : NO
Body constraints
{
"email": "[email format]"
}
Body example 2개의 필수 입력 프로퍼티로 구성
{
"email": "[email protected]",
"password": "test1234"
}
Code : 200 OK
{
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyVHlwZSI6IlBBU1NFTkdFUiIsImV4cCI6MTU1NTMzMTI1MiwiZW1haWwiOiJzdmxhZGFjb0BkLmNvbSIsImRlc2MiOiLthYzsiqTtirjsmqkgRGVtb1RheGlBcGnsl5DshJwg67Cc7ZaJIn0.LuYtm-gdFIeAAzA0ABRHmH3sOxKp5ennT_uUqnzGvPw"
}
Condition : 가입한 이메일이 없거나 비밀번호가 일치하지 않을때.
Code : 400 Bad Request
Content :
{
"code": "NOT_FOUND_USER",
"message": "유저정보가 존재하지 않습니다."
}
토큰 갱신(재발급)
curl -X POST \
http://localhost:9876/api/auth/refresh \
-H 'Authorization: Bearer JWT_TOKEN' \
-H 'Content-Type: application/json' \
-H 'cache-control: no-cache'
// SAMPLE_JWT_TOKEN
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidGVzdFVzZXIiLCJleHAiOjE4Njg5NDYzMjcsImRlc2MiOiLthYzsiqTtirjsmqkgRGVtb0FwaeyXkOyEnCDrsJztlokifQ.r8RLJgRVvUyf1TecScqJMQPF_JMTF0vYFHDkE9_uPjI
Method : POST
Auth required : YES
Code : 200 OK
{
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyVHlwZSI6IlBBU1NFTkdFUiIsImV4cCI6MTU1NTMzMTI1MiwiZW1haWwiOiJzdmxhZGFjb0BkLmNvbSIsImRlc2MiOiLthYzsiqTtirjsmqkgRGVtb1RheGlBcGnsl5DshJwg67Cc7ZaJIn0.LuYtm-gdFIeAAzA0ABRHmH3sOxKp5ennT_uUqnzGvPw"
}
Condition : 헤더(Authorization)에 JWT_TOKEN이 없거나 만료 변조된 토큰일 경우
Code : 400 Bad Request
Content :
{
"code": "UNAUTHORIZED_REQUEST",
"message": "허가되지않은 요청입니다."
}
전체 배차 목록 조회
curl -X GET \
http://localhost:9876/api/call/list \
-H 'Authorization: Bearer JWT_TOKEN' \
-H 'Content-Type: application/json' \
-H 'cache-control: no-cache'
// SAMPLE_JWT_TOKEN
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidGVzdFVzZXIiLCJleHAiOjE4Njg5NDYzMjcsImRlc2MiOiLthYzsiqTtirjsmqkgRGVtb0FwaeyXkOyEnCDrsJztlokifQ.r8RLJgRVvUyf1TecScqJMQPF_JMTF0vYFHDkE9_uPjI
Method : GET
Auth required : YES
Parameter constraints
page=[default value 0]
size=[default value 50]
sort=[default value 'id, DESC']
Parameter example 3개의 선택 입력 파라메터로 구성
page=0&size=50&sort=id, DESC
Code : 200 OK
[
{
"id": 6,
"passenger": {
"email": "[email protected]",
"userType": "PASSENGER"
},
"address": "서울시 도봉구 XX동",
"status": "REQUESTED",
"requestDate": "2019-04-15T12:17:34.236+0000"
},
{
"id": 4,
"passenger": {
"email": "[email protected]",
"userType": "PASSENGER"
},
"driver": {
"email": "[email protected]",
"userType": "DRIVER"
},
"address": "수원시 XX동",
"status": "ASSIGNED",
"requestDate": "2019-04-15T12:17:32.070+0000",
"assignDate": "2019-04-15T12:18:44.446+0000"
}
]
Condition : 헤더(Authorization)에 JWT_TOKEN이 없거나 만료 변조된 토큰일 경우
Code : 400 Bad Request
Content :
{
"code": "UNAUTHORIZED_REQUEST",
"message": "허가되지않은 요청입니다."
}
택시 배차 요청
curl -X POST \
http://localhost:9876/api/call/request \
-H 'Authorization: Bearer JWT_TOKEN' \
-H 'Content-Type: application/json' \
-H 'cache-control: no-cache' \
-d '{
"address": "서울시 중구 을지로 XX"
}'
// SAMPLE_JWT_TOKEN
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidGVzdFVzZXIiLCJleHAiOjE4Njg5NDYzMjcsImRlc2MiOiLthYzsiqTtirjsmqkgRGVtb0FwaeyXkOyEnCDrsJztlokifQ.r8RLJgRVvUyf1TecScqJMQPF_JMTF0vYFHDkE9_uPjI
Method : POST
Auth required : YES
Body constraints
{
"address": "[100자 이하]"
}
Body example 1개의 필수 입력 프로퍼티로 구성
{
"address": "서울시 중구 을지로 XX"
}
Code : 200 OK
{
"code": "SUCCESS",
"message": "요청이 정상적으로 완료 되었습니다."
}
Condition : Token상의 UserType이 PASSENGER가 아닐 경우
Code : 400 Bad Request
Content :
{
"code": "REQUEST_ONLY_PASSENGER",
"message": "승객만 요청할 수 있는 기능 입니다."
}
Condition : 필수 입력값이 누락되었거나 100자를 초과하였을 경우
Code : 400 Bad Request
Content :
{
"code": "ERROR_ADDRESS",
"message": "주소값이 없거나 100자를 넘었습니다."
}
기사 배차
curl -X POST \
http://localhost:9876/api/call/assign \
-H 'Authorization: Bearer JWT_TOKEN' \
-H 'Content-Type: application/json' \
-H 'cache-control: no-cache' \
-d '{
"id": 10
}'
// SAMPLE_JWT_TOKEN
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidGVzdFVzZXIiLCJleHAiOjE4Njg5NDYzMjcsImRlc2MiOiLthYzsiqTtirjsmqkgRGVtb0FwaeyXkOyEnCDrsJztlokifQ.r8RLJgRVvUyf1TecScqJMQPF_JMTF0vYFHDkE9_uPjI
Method : POST
Auth required : YES
Body constraints
{
"id": "[NUMBER format]"
}
Body example 1개의 필수 입력 프로퍼티로 구성
{
"id": 10
}
Code : 200 OK
{
"code": "SUCCESS",
"message": "요청이 정상적으로 완료 되었습니다."
}
Condition : Token상의 UserType이 DRIVER가 아닐 경우
Code : 400 Bad Request
Content :
{
"code": "REQUEST_ONLY_DRIVER",
"message": "기사만 요청할 수 있는 기능 입니다."
}
Condition : 필수 입력값이 누락되었을 경우
Code : 400 Bad Request
Content :
{
"code": "ERROR_ID",
"message": "ID값이 없습니다."
}
Condition : 존재하지 않는 ID일 경우
Code : 400 Bad Request
Content :
{
"code": "NOT_FOUND_CALL_ID",
"message": "존재 하지 않는 ID 입니다."
}
Condition : 이미 할당된 배차일 경우
Code : 400 Bad Request
Content :
{
"code": "ALREADY_ASSIGNED",
"message": "이미 할당된 배차입니다."
}