카카오페이증권 과제
기간 : ~ 2022.09.29 23:59까지
요구사항
- 서비스 제공에 필요한 RESTful API 를 구현합니다.
- 실시간 데이터의 변동을 위해 데이터가 랜덤으로 변경되는 API를 추가로 개발해 주시기 바랍니다.
- Application이 로딩될 때 기본 데이터가 DB에 적재하도록 합니다. (주식 종목에 대한 정보는 첨부된 데이터를 참고하시면 됩니다.)
- 데이터 테이블 구조는 효율적인 방식으로 스스로 설계해 주시면 됩니다.
- 각 기능 및 제약사항에 대한 단위테스트를 작성합니다.
제약사항
- 설계 내용과 이유, 핵심 문제해결 전략 및 분석한 내용을 작성하여 “readme.md” 파일에 첨부 해주세요.
- 개발은 SpringBoot 와 JAVA를 사용해서 구현하시기 바랍니다.
- API의 HTTP Method들은 자유롭게 선택하시면 됩니다. (GET, POST, DELETE, PUT, PATCH)
- 에러응답, 에러코드는 자유롭게 정의해주세요.
- 단위 테스트를 작성하세요.
평가항목
- 프로젝트 구성방법 및 관련된 시스템 아키텍쳐 설계방법이 적절한가?
- 요구사항을 잘 이해하고 구현하였는가?
- 작성한 어플리케이션 코드의 가독성이 좋고 의도가 명확한가?
- 작성한 테스트코드는 적절한 범위의 테스트를 수행하고 있는가?
- 어플리케이션은 다량의 트래픽에도 무리가 없도록 효율적으로 작성되었는가?
- JDK 11
- Spring Boot 2.5.3
- STS-4-4.16.0.RELEASE
- JPA
- H2
- Redis
- Maven
- Junit
상태코드 | 싱태 | 설명 |
---|---|---|
200 |
성공 | 정상 응답 |
201 |
성공 | 정상적으로 생성 |
400 |
실패 | 잘못된 요청 |
404 |
실패 | 리소스를 찾을 수 없음 |
500 |
실패 | 시스템 에러 |
code | 설명 | HTTP상태코드 |
---|---|---|
000 |
예상치 못한 오류가 발생하였습니다. | 500 |
101 |
서버 내부에서 처리 중에 에러가 발생했습니다. | 400 |
102 |
파라미터 확인부탁드립니다. | 400 |
103 |
서비스 점검 중입니다. 공지사항을 확인해주세요. | 400 |
104 |
헤더 정보 확인부탁드립니다. | 400 |
HTTP/1.1 400 Bad Request
{
"code": 101,
"msg":"서버 내부에서 처리 중에 에러가 발생했습니다."
}
- 이 API는 카카오페이증권 모든 주제 Top5를 조회하는 API입니다.
- 주문 조회 API를 사용해 개별 주문의 상세 정보를 조회합니다. 앱 어드민 키를 헤더에 담아 POST로 요청합니다. 아래 두 가지 예제 중 어느 방법을 사용해도 결과는 같습니다. 요청이 성공하면 응답은 바디에 JSON 객체로 주문 상세 정보를 포함합니다.
Name | Type | Descrption |
---|---|---|
viewALot | ViewALot[] | 많이 본 상세 |
riseALot | RiseALot[] | 많이 오른 상세 |
dropALot | DropALot[] | 많이 내린 상세 |
volumeHigh | VolumeHigh[] | 많이 보유한 상세 |
Name | Type | Descrption |
---|---|---|
code | String | 상품코드 |
codeNm | String | 상품코드명 |
rank | BigDecimal | 순위 |
price | BigDecimal | 가격 |
percent | Double | 백분율 |
curl -v -X GET "http://localhost:8080/api/rank"
HTTP/1.1 200 OK
Content-type: application/json;charset=UTF-8
{
"dropALot": [
{
"code": "010140",
"codeName": "삼성중공업",
"rank": 1.0,
"price": 5063.00,
"percent": -15.4800
},
{
"code": "003410",
"codeName": "쌍용C&E",
"rank": 2.0,
"price": 6218.00,
"percent": -11.5600
},
{
"code": "006800",
"codeName": "미래에셋증권",
"rank": 3.0,
"price": 6169.00,
"percent": -8.3400
},
{
"code": "024110",
"codeName": "기업은행",
"rank": 4.0,
"price": 9006.00,
"percent": -7.0600
},
{
"code": "034220",
"codeName": "LG디스플레이",
"rank": 5.0,
"price": 15524.00,
"percent": -4.4700
}
],
"riseALot": [
{
"code": "008560",
"codeName": "메리츠증권",
"rank": 1.0,
"price": 5614.00,
"percent": 13.8700
},
{
"code": "028670",
"codeName": "팬오션",
"rank": 2.0,
"price": 6179.00,
"percent": 11.9300
},
{
"code": "018880",
"codeName": "한온시스템",
"rank": 3.0,
"price": 11192.00,
"percent": 5.5800
},
{
"code": "316140",
"codeName": "우리금융지주",
"rank": 4.0,
"price": 12723.00,
"percent": 4.2800
},
{
"code": "015760",
"codeName": "한국전력",
"rank": 5.0,
"price": 22295.00,
"percent": 3.4500
}
],
"viewALot": [
{
"code": "007070",
"codeName": "GS리테일",
"rank": 1.0,
"price": 24854.00,
"percent": -2.9200
},
{
"code": "259960",
"codeName": "크래프톤",
"rank": 2.0,
"price": 257137.00,
"percent": -0.3400
},
{
"code": "017670",
"codeName": "SK텔레콤",
"rank": 3.0,
"price": 51944.00,
"percent": 1.4500
},
{
"code": "096770",
"codeName": "SK이노베이션",
"rank": 4.0,
"price": 209936.00,
"percent": -0.2700
},
{
"code": "047810",
"codeName": "한국항공우주",
"rank": 5.0,
"price": 56536.00,
"percent": 0.7700
}
],
"volumeHigh": [
{
"code": "055550",
"codeName": "신한지주",
"rank": 1.0,
"price": 36084.00,
"percent": 1.6400
},
{
"code": "066570",
"codeName": "LG전자",
"rank": 2.0,
"price": 102508.00,
"percent": 0.4900
},
{
"code": "047810",
"codeName": "한국항공우주",
"rank": 3.0,
"price": 56536.00,
"percent": 0.7700
},
{
"code": "105560",
"codeName": "KB금융",
"rank": 4.0,
"price": 50214.00,
"percent": -0.9600
},
{
"code": "139480",
"codeName": "이마트",
"rank": 5.0,
"price": 105463.00,
"percent": 0.4400
}
]
}
- 이 API는 카카오페이증권 주제별 최대 Top100을 조회하는 API입니다.
- 주문 조회 API를 사용해 개별 주문의 상세 정보를 조회합니다. 앱 어드민 키를 헤더에 담아 POST로 요청합니다. 아래 두 가지 예제 중 어느 방법을 사용해도 결과는 같습니다. 요청이 성공하면 응답은 바디에 JSON 객체로 주문 상세 정보를 포함합니다.
Name | Type | Descrption |
---|---|---|
id | int | 0 = 많이 본, 1 = 많이 오른, 2 = 많이 내린, 3 = 많이 보유한 |
paging | int | default = 20, max = 100 |
Name | Type | Descrption |
---|---|---|
viewALot | ViewALot[] | 많이 본 상세 |
riseALot | RiseALot[] | 많이 오른 상세 |
dropALot | DropALot[] | 많이 내린 상세 |
volumeHigh | VolumeHigh[] | 많이 보유한 상세 |
Name | Type | Descrption |
---|---|---|
code | String | 상품코드 |
codeNm | String | 상품코드명 |
rank | BigDecimal | 순위 |
price | BigDecimal | 가격 |
percent | Double | 백분율 |
curl -v -X GET "http://localhost:8080/api/rank/{id}"
HTTP/1.1 200 OK
Content-type: application/json;charset=UTF-8
{
"riseALot": [
{
"code": "008560",
"codeName": "메리츠증권",
"rank": 1.0,
"price": 5614.00,
"percent": 13.8700
},
{
"code": "028670",
"codeName": "팬오션",
"rank": 2.0,
"price": 6179.00,
"percent": 11.9300
},
{
"code": "018880",
"codeName": "한온시스템",
"rank": 3.0,
"price": 11192.00,
"percent": 5.5800
},
{
"code": "316140",
"codeName": "우리금융지주",
"rank": 4.0,
"price": 12723.00,
"percent": 4.2800
},
{
"code": "015760",
"codeName": "한국전력",
"rank": 5.0,
"price": 22295.00,
"percent": 3.4500
},
{
"code": "026960",
"codeName": "동서",
"rank": 6.0,
"price": 26514.00,
"percent": 3.3600
},
{
"code": "371460",
"codeName": "TIGER 차이나전기차SOLACTIVE",
"rank": 7.0,
"price": 17138.00,
"percent": 3.2400
},
{
"code": "138040",
"codeName": "메리츠금융지주",
"rank": 8.0,
"price": 30696.00,
"percent": 2.1400
},
{
"code": "032640",
"codeName": "LG유플러스",
"rank": 9.0,
"price": 12569.00,
"percent": 1.7700
},
{
"code": "006360",
"codeName": "GS건설",
"rank": 10.0,
"price": 32058.00,
"percent": 1.7700
},
{
"code": "055550",
"codeName": "신한지주",
"rank": 11.0,
"price": 36084.00,
"percent": 1.6400
},
{
"code": "005830",
"codeName": "DB손해보험",
"rank": 12.0,
"price": 63345.00,
"percent": 1.5100
},
{
"code": "004990",
"codeName": "롯데지주",
"rank": 13.0,
"price": 39468.00,
"percent": 1.4600
},
{
"code": "017670",
"codeName": "SK텔레콤",
"rank": 14.0,
"price": 51944.00,
"percent": 1.4500
},
{
"code": "036460",
"codeName": "한국가스공사",
"rank": 15.0,
"price": 42288.00,
"percent": 1.4100
},
{
"code": "086790",
"codeName": "하나금융지주",
"rank": 16.0,
"price": 39037.00,
"percent": 1.3900
},
{
"code": "030200",
"codeName": "KT",
"rank": 17.0,
"price": 38521.00,
"percent": 1.3700
},
{
"code": "000060",
"codeName": "메리츠화재",
"rank": 18.0,
"price": 38649.00,
"percent": 1.3000
},
{
"code": "008770",
"codeName": "호텔신라",
"rank": 19.0,
"price": 73136.00,
"percent": 1.2900
},
{
"code": "047050",
"codeName": "포스코인터내셔널",
"rank": 20.0,
"price": 25445.00,
"percent": 1.1700
}
]
}
- 이 API는 카카오페이증권 모든 주제를 랜덤하게 변경하는 API입니다.
- 주문 조회 API를 사용해 개별 주문의 상세 정보를 조회합니다. 앱 어드민 키를 헤더에 담아 POST로 요청합니다. 아래 두 가지 예제 중 어느 방법을 사용해도 결과는 같습니다. 요청이 성공하면 응답은 바디에 JSON 객체로 주문 상세 정보를 포함합니다.
Name | Type | Descrption |
---|---|---|
code | String | 코드 |
message | String | 메시지 |
curl -v -X POST "http://localhost:8080/api/randomRank"
HTTP/1.1 200 OK
Content-type: application/json;charset=UTF-8
{
"code": 201,
"message": "success"
}
- 요구사항에는 없는 내용이지만 현재 상용 뿌리기 서비스에는 있는 뿌릴 인원수 최대값을 채팅방 사용자 수 - 1(본인) 보다 크지 않도록 구현. 불필요하게 뿌릴 인원수를 많이 설정하여 요청자 지갑으로 환불하는 로직을 호출해야하기 때문에 서비스 과부하 가능성 있기 때문(프론트에서 막아도 됨)
- 뿌리기 토큰을 랜덤 3자리로 생성할 때 random() 대신randomAlphanumeric() 으로 생성하여 인코딩 오류 방지, 또한 DB 데이터와 중복 체크 필수, 또한 다른 곳에서도 사용할 확장성 고려하여 공통 함수로 분리
- 중복될 경우나 내부 오류시 지정해놓은 횟수만큼 반복 생성하는데, 현재는 3회로 지정하였지만 추후 뿌릴 인원(count)에 맞춰서 유동적으로 가져가는 것도 고민중.
- 돈이 오고가는 거래이므로 로직 중에 오류나면 생성한 레코드 롤백하는 @Transaction 어노테이션 사용
- 뿌린 금액이 DB에 오름차순으로 저장되어 있기 때문에 뿌린 금액 받기 기능 호출시 한번 더 랜덤 정렬 시행하여 '랜덤'기능 충실
- KP_TB_RAIN_MONEY의 FK를 KP_TB_ROOM_USER의 복합키로 할지, 각각의 PK로 할지 고민하다가 복합키로 결정하였으나 조회문이 복잡해지는 단점 생.
- TOKEN 필드의 데이터크기를 3자리로 잡았는데, 추후 확장성을 고려해 더 넉넉하게 잡았어도 좋았겠다는 생각. 서비스가 확장되면 인원이 늘어나고 3자리수로 유니크한 문자가 한계가 있기 때문
- PK Java에서는 Long인데 데이터 타입 Unsigned int로 할지, bigint로 할지 고민했다가 Java에서는 기본적으로 unsigned type을 지원하지 않아서 방법은 있겠으나 시간을 뺏길 것 같아서 bigint 사용