[프리온보딩 백엔드] 8퍼센트 기업 과제 회고👏

✔️과제 정보

✔️Project Review

#️⃣Project Review1 - 알게된 내용

#1 Transaction에 대한 처리

상황)
이번 과제에서 구현해야하는 입금/출금 API는 입금의 경우 요청 시 전달된 금액을 더한 값으로 계좌의 잔액 값을 업데이트해야 했고, 출금의 경우 계좌 잔액 조회 후 출금 가능한 경우에만 요청 시 전달된 금액을 뺀 값으로 업데이트를 위한 Database Transaction 처리가 필요했음

구현 방식)

  • 기존 코드 - Transaction 외부에서 계좌 조회 함
if not account.owner_id == user.id: # 현재 Transaction 밖에서 객체 조회함 
...(생략)
            with transaction.atomic():
                if deal_position_id == DealPositionId.DEPOSIT.value:
                    ...(생략)
                    

문제점 : 코드상에서는 계좌 조회와 Transaction 처리가 바로 이어져있는 것처럼 보이지만, 실제 서비스에서는 그 사이에 굉장히 많은 일이 발생할 수 있기 때문에 Transaction 수행 전에 해당 계좌 객체가 바뀔 수도 있음

  • 개선 코드 - Transaction 내에서 계좌 조회도 함께 처리하기 & select_for_update()로 lock 걸기
with transaction.atomic():
		account = Account.objects.get(id = account_id).select_for_update()	
		# 개선 부분 
    ...(생략)

#2 Enum을 객체처럼 사용하자! (클래스로 정의했다면)

  • 개선 코드
class DealPosition(Enum): # Enum 클래스 
    DEPOSIT  = 1
    WITHDRAW = 2

		def is_deposit(self):
				return self == DealPosition.DEPOSIT

		def is_withdraw(self):
				return self == DealPosition.WITHDRAW


deal_position = DealPosition(int(data['deal_position_id'])) # 객체로 선언해서 사용하자! 
...

if deal_position.is_deposit():
...

Deal2021.objects.create(
    account_id       = account_id,
    deal_position_id = deal_position.value, 
    amount           = amount,
    description      = description,
    balance          = account.balance
)

개선 사항: Enum으로 정의하였다면 열거형 상수로 접근하기보다 해당 Enum 클래스의 객체를 선언하여 접근하는 것이 더 적절함

#3 Functional Test

상황)

Functional Test는 E2E(End-to-End) 혹은 브라우저 테스트로 소프트웨어 내부 구조나 구현 방법을 고려하기보다 테스트 시나리오를 바탕으로 실제 사용자가 접하는 브라우저 테스트를 하는 것임 하지만 현재 프로젝트는 프론트 부분을 구현하지 않았으므로 해당 방향으로 테스트를 진행하기는 어렵다고 판단하였음

대신 사용자의 입장에서 구현한 기능(회원가입/로그인/계좌 및 거래 관련)들의 일련의 시나리오를 각각 구성하여 테스트를 진행하였음

구현 방식)

핵심 기능인 입금, 출금, 거래 내역 조회 시나리오는 다음과 같다.

  • 입금 시나리오 : 사용자 로그인 → 계좌 생성 → 입금 → 입금 거래 내역 조회 에 대한 테스트 시나리오를 작성 하였고, 현재 계좌의 잔액과 마지막 입금 내역의 잔액이 동일함을 확인하여 테스트를 통과하였다. (테스트 시나리오에서 진행한 입금이 마지막 거래 내역이라고 가정)
  • 출금 시나리오: 로그인 → 출금 → 출금 거래 내역 조회 에 대한 테스트 시나리오를 작성하였고, 현재 계좌의 잔액과 마지막 출금 내역의 잔액이 동일함을 확인하여 테스트를 통과하였다. (테스트 시나리오에서 진행한 출금이 마지막 거래 내역이라고 가정)
  • 거래 내역 조회 시나리오: 로그인 → 입금/출금 내역 생성 → 거래 내역 조회 에 대한 테스트 시나리오를 작성하였고, 생성한 거래 내역(2개)을 기반으로 '조회 기간'이 오늘이고, '최근 거래 내역이 위로'인 경우에 대한 응답으로 온 거래 내역 데이터가 총 2개이고, 현재 계좌의 잔액과 마지막 거래 내역의 잔액이 동일함을 확인하여 테스트를 통과하였다.

하지만 기업에서 원했던 것이 해당 내용이 맞는지 아직 확인해보지 않았고, 일단 질문을 남겼으니 답변을 기다려보자! 직접 8퍼센트에 물어볼까?!

#4 APITestCase 사용하여 테스트 코드 작성시 Token 인증하는 법

상황)

해당 프로젝트의 Authentication 방법으로 장고에서 제공해주는 기본 Token 인증 방식을 사용함

구현 방식)

 self.client.credentials(HTTP_AUTHORIZATION='token ' + token)  # 토큰 인증을 위한 설정

참고: https://stackoverflow.com/questions/50678609/how-to-add-authentication-token-in-header-of-apiclient-in-django-rest-framewo

#️⃣Project Review2 - 느낀 점 & 개선 점

#1 팀플의 장점 : 궁금한 부분에 대한 해결 및 프로젝트 방향성 잡기

이번과제에서 느낀 팀플의 큰 장점은 '궁금한 부분에 대한 해결 및 방향성 잡기' 였다. 과제 요구사항 중 입금/출금 API 부분이 있었는데, 맨 처음에 나혼자서 이해했을 때는 '인터넷 상에서 만을 생각해보면 출금&입금은 하나의 쌍으로 이루어져야 하는 것이 아닌지?'에 대한 의문이 들었었는데 팀 회의를 하며 '이번 과제 요구사항에서는 입금/출금 API만 존재하니 본인의 계좌로의 입금/출금만 가능하게 하는 것이다' 라는 결론을 내릴 수 있었다. 팀원들과의 회의를 통해 프로젝트의 방향성을 같이 결정할 수 있었던 점이 팀플의 장점이라는 생각이 들었다.

#2 ViewSet이 언제나 유용한 것은 아니다..!

이번에 구현할 때 accounts/{account_id}/tradelogs 라는 특정 계좌의 거래 내역을 조건에 따라 조회하는 API가 있었다. ViewSet을 view 로직을 작성하여서 router로 일단 accounts/ 가 매핑되기 때문에 다른 View를 생성하여 accounts/{account_id}/tradelogs을 사용할 수 없었고, 실질적으로 조회해야 하는 객체가 Account 가 아닌 TradeLog 여서 pagination과 filter를 TradeLog 객체에 적용하기 위해 ViewSet 코드 내의 설정을 사용하지 못하고 따로 직접 구현해야 했던 부분이 있었다. 지금 생각으로는 Viewset이 아니라 GenericView 혹은 다른 View 작성 방식으로 구현했다면 각각의 View에 대해 적절한 filter, pagination 등을 쉽게 적용할 수 있었을 것이라는 생각이 들었다.

If the generic views don't suit the needs of your API, you can drop down to using the regular APIView class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views.

--> DRF의 Generic views 공식문서 부분을 보면 해당 글이 있다. 이처럼 상황에 따라 적절한 구현 방식을 선택하는 것이 중요한 것 같다!

#3 팀플의 장점 : 객관적인 입장에서 코드 보기

기존에는 내가 직접 기능을 구현을 구현해야지만 의미있다고 생각했었는데 이번에 과제를 하면서 다른 분이 코드를 작성해주시고 나는 옆에서 같이 고민하고 틀리거나 놓친 부분을 알려주며 코드를 짰다. 옆에서 다른 분이 코드 짜는 것을 보니까 재미있었고, 내가 직접 작성할 때와는 다르게 객관적인 관점에서 작성되어지는 코드를 보며 전체적인 코드 구성을 보고 잘못된 부분을 찾아낼 수 있어 굉장히 의미있는 경험이었다. 😃

추가

#1 예외 처리에 대한 Status Code를 구체적으로 정해놓는게 코드 작성시에도 편한 것 같다 (같은 팀원이신 태우님의 API 제안을 보고 배움👍)

좋은 웹페이지 즐겨찾기