프로젝트 작업사항으로는 배송지 조회, 생성, 수정, 삭제 그리고 상품 검색 및 상세조회 부분을 맡아 작업하였다.
주문 시작과 동시에 해당 유저에 대한 기본 배송지 정보를 반환하고
회원, 비회원 모두 주문 시작이 가능하니 유연하게 배송지 정보 값을 받아 처리해 주어야했다.
#PRESENTATION LAYER
data = dict()
if 'account_id' in g:
data['account_id'] = g.account_id
if 'permission_type_id' in g:
data['permission_type_id'] = g.permission_type_id
connection = get_connection(self.database)
destination_detail = self.service.destination_service
비회원 유저도 주문을 시작할수 있기때문에 토큰값이 항상 들어온다고 가정한다면 비회원일 경우 keyError 가 발생하기때문에 g 객체를 유연하게 받아오는 방법을 선택하였다.
# BUSINESS LAYER
if permission_type_id not in data and 'account_id' not in data:
return '로그인한 회원만 배송정보를 조회할수 있습니다'
if not data['permission_type_id'] == 3:
raise InvalidUser('허가되지 않은 유저입니다')
한 계층에서 일관된 처리를 해주기 위해 service에서 예외를 raise 해주기로 결정하였다. 이렇게 처리된 데이터는 business layer 에서 회원이 아닐경우 데이터를 조회할수 없음으로 예외처리를 해준다. 만약 토큰값이 들어온 경우 권한체크를하여 예외처리도 같이 해주었다.
배송지를 생성할때 고려해볼 사항이 몇가지 있다. 첫번째로, 오직 회원만이 배송지를 생성할 수 있고, 두번째로 회원당 생성할수 있는 배송지 정보는 5개로 제한해야하며, 5개중 무조건 한가지는 기본배송지 값으로 표시되어 있어야 했다.
# Business Layer
if not data['permission_type_id'] == 3:
raise InvalidUser('오직 회원만이 배송지를 생성할 수 있습니다.')
모든 비지니스 로직은 service layer에서 구성하였다. 오직 회원만이 배송지 생성을 할수 있음으로 회원인지 판단해주는 로직을 구현하였다.
# Business Layer
data_length = self.destination_dao.destination_check_length(connection, data['user_id'])
if data_length >= 5:
raise DataLimitExceeded('최대 입력할 수 있는 배송지 개수를 초과했습니다.')
배송지의 개수는 유저당 오직 1개만 가질 수 있기때문에 데이터베이스의 존재하는 배송지의 개수를 확인하는 로직을 구현하였다.
# Business Layer
default_location_flag = self.destination_dao.check_default_location_by_user(connection, data['user_id'])
if not default_location_flag:
data['default_location'] = 1
else:
data['default_location'] = 0
만약 회원이고, 존재하는 배송지도 5개 미만이라면 데이터에 존재하는 기본배송지 여부에 따라 유연하게 기본배송지 값을 선언해준다.
check_default_location_by_user 메서드는 기본 배송지 유무를 확인해 truthy falsy 값으로 반환해 주는 역할을 한다.
# Business Layer
return self.destination_dao.create_destination_dao(connection, data)
모든 조건이 만족되었다면 배송지 생성을 한다.
배송지 수정 API 또한 생성과 마찬가지고 데이터의 개수, 기본배송지 여부에 따라서 로직을 짜주어야한다. 주요사항으로는 기본배송지 값이 수정될 경우를 고려해야했는데 이는 크게 두가지 가능성으로 볼 수 있다.
생성때와 마찬가지로 회원만이 배송지 정보를 수정할 수 있기때문에 로직을 만들어준다. 모든 로직은 Layered Pattern에 의해서 Business Layer 에 구현해준다.
# Business Layer
if not data['permission_type_id'] == 3:
raise InvalidUser('오직 회원만이 배송지를 생성할 수 있습니다.')
배송지 수정의 주요사항을 분기처리해주는 로직을 구현했다. 기본배송지(default_location) 값을 설정해 주려는건지 아니면 해제하는 값인지를 판별해야한다.
먼저 default_location = 0 값이 들어온 경우를 처리해준다. check_default_location 메서드를 사용해 truthy falsy 값을 flag 변수에 할당 받는다.
{
"destination_id": "55",
"recipient": "뮤즈씨",
.
.
.
"default_location":"0"
}
if data['default_location'] == "0":
flag = self.destination_dao.check_default_location_flag(connection, data)
default_location = 1 을 default_location = 0 으로 수정하는 상황
기본 배송지가 해제되는 상황임으로 데이터베이스에 존재하는 값중 최초로 입력된 값이 기본배송지로 자동으로 변경해주고 수정을 진행하여야합니다.
# 최초로 입력된 배송지 정보를 찾아 기본배송지로 바꾸어준다.
self.destination_dao.update_default_location_true(connection, data)
# 배송지 정보 수정
return self.destination_dao.update_destination_info_dao(connection, data)
default_location = 0 을 default_location = 0 으로 수정하는 상황
이 경우는 기본배송지값이 영향을 받지 않기때문에 바로 수정을 진행하면 된다.
# 배송지 정보 수정
return self.destination_dao.update_destination_info_dao(connection, data)
{
"destination_id": "55",
"recipient": "뮤즈씨",
.
.
.
"default_location":"1"
}
기본배송지를 설정하는 값이 들어오는 경우
# 존재하는 모든 데이터의 기본배송지 정보를 0으로 바꿔준다.
self.destination_dao.update_default_location_false(connection, data)
# 배송지 수정 진행
return self.destination_dao.update_destination_info_dao(connection, data)
배송지 데이터중 기본배송지값을 가진 데이터는 오직 1개만 존재할 수 있기때문에 기본배송지를 설정하는 값이 들어온다면 기존에 존재하는 모든 데이터의 기본 배송지를 0으로 만들어주야한다.
기본배송지는 1개가 존재해야하기 때문에 기본배송지가 지워지더라도 남아있는 배송지 데이터중 최초로 입력된 배송지 정보가 기본배송지로 설정되어야한다.
if not data['permission_type_id'] == 3:
raise InvalidUser('오직 회원만이 배송지를 삭제할 수 있습니다.')
# 삭제 진행
self.destination_dao.delete_destination_dao(connection, data)
default_location_flag = self.destination_dao.check_default_location(connection, data)
if not default_location_flag:
self.destination_dao.update_default_location_true(connecton, data)
회원 비회원 모두 상품 상세 페이지를 조회할수 있다. 하지만 데코레이터로 회원, 비회원에 따라 달라지는 북마크(하트) 상태를 표시해주어야 한다.
상품과 관련된 다수의 이미지와, 색상, 그리고 사이즈 데이터를 반환해 주어야했지만 쿼리문에서 Left join을 사용할경우 상품명의 중복이 발생였고, Inner join을 사용할 경우 오직 한개의 이미지, 색상 그리고 사이즈가 반환되는 결과가 있었다.
회원, 비회원 모두 상세 페이지를 조회할수 있게 만들어주기 위해 쿼리를 3개로 분단한뒤 조합하는 방법을 선택했다.
비회원일경우 query + query_end
회원일 경우 query + query_with_account + query_end
query = """
SELECT
product.id AS product_id
.
.
.
bookmark.bookmark_count
"""
query_end= """
FROM
product.id AS product_id
.
.
.
INNER JOIN bookmark_volumes AS bookmark
ON bookmark.product_id = product.id
WHERE
product.id = %(product_id)s
AND product.is_deleted =0
"""
query_with_account= """
, EXISTS(
SELECT
id
FROM
bookmarks
WHERE
account_id = %(account_id)s
AND product_id = %(product_id)s
AND is_deleted = 0
) AS is_bookmarked
"""
INNER JOIN / LEFT JOIN 을 사용할 수 없기때문에 쿼리를 나누어서 DAO로 내보내고 서비스에서 조합해서 반환하는 방법으로 문제해결을 했다.
images = self.product_dao.get_product_images_dao(connection, data)
sizes = self.product_dao.get_product_sizes_dao(connection, data)
colors = self.product_dao.get_product_colours_dao(connection, data)
products = self.product_dao.get_product_detail_dat(connnection, data)
product['images'] = images
product['sizes'] = sizes
product['colours'] = colours
return product
쿼리스트링으로 입력받은 키워드를 '상품이름', '셀러이름' 과 겹치는 데이터를 모두 반환해주어야 했고 최소 20개의 데이터를 보여주는것으로 하며, 정렬은 추천순, 판매량순, 최신순을 기준으로 삼아야한다.
@validate_params(
Params('q', GET, str, required=True),
Params('limit', GET, str, required=True, rule=[NumberRule()],
Params('sort_type', GET, str, required=True, rule=[SortTypeRule()])
)
검색 api가 실행되면 무조건 모두 받아야되는 값임으로 flask-request-validator 를 사용하였다.
class SortTypeRule(AbstractRule):
def validate(self, value):
sort_type = [1, 2, 3]
errors = []
if value not in sort_type:
errors.append('1~3 값만 받습니다')
return value, errors
sort_type = {
'1': '북마크추천순',
'2': '판매순',
'3': '최신순'
}
.
.
ORDER BY
(CASE WHEN %(sort_type)s=1 THEN bookmark_count END) DESC
, (CASE WHEN %(sort_type)s=2 THEN sales_count END) DESC
, (CASE WHEN %(sort_type)s=3 THEN product.id END) DESC
LIMIT%(limit)s;