본문 바로가기
취대넓얕

[기술면접] 18일차 문답 | TDD, DRF 기본기

by 째깍단 2023. 8. 11.

💡 테스트코드를 작성하는 이유는 무엇이며 어떤 장점이 있습니까?

 

 작성 이유와 장점

  1. 코드의 로직을 이해해야 작성이 용이하기때문에 작성하는 것만으로도 코드의 구조를 이해하는데에 도움이 된다
  2. 구현한 기능의 보이지 않는 오류를 테스트코드를 통해 검증하고 찾아내어 보완할 수 있다
  3. 코드 변경 시, 변경 부분으로 인한 영향도를 쉽게 파악할 수 있다
  4. 코드 리팩토링 시 기능 구현이 동일하게 되었다는 판단을 내릴 수 있다
  5. 구현한 코드의 정확도를 확인할 수 있다
  6. 프론트엔드 작업자가 백엔드 작업에서의 오류를 쉽게 확인할 수 있게 해주어 협업에 용이하다 

참고: https://yozm.wishket.com/magazine/detail/1964/

 

 

 

 

💡 테스트코드에서 setup 함수와 setupclass의 차이는 무엇입니까?

 

실행 순서 지속성, 내용 면에서 차이가 있습니다.

 

setUp은 각 테스트 메서드가 실행되기 전에 호출되며

setUpClass는 테스트 케이스 클래스가 처음 시작될 때 한번만 호출됩니다

 

지속성에서 setUp은 각 테스트 메서드가 실행될 때 호출되고 메서드가 끝나면 초기화되므로 각 테스트 메서드가 독립적 환경에서 실행되게 합니다. setUpClass는 테스트 케이스 클래스가 끝나기 전까지 유지되기 때문에 모든 테스트 메서드에 공통적으로 필요한 작업을 수행할때 유용합니다

 

작성 내용면에서

setUp은 테스트 메서드마다 롤백하므로 테스트 중 내용에 변화가 있을 수 있는 데이터를 작성하며,

setUpClass는 처음 한번만 호출하므로 내용에 변화가 없는 데이터를 작성합니다. (롤백 여부 때문)

 

 

 

 

 

💡 TDD 설명하시오

Test-driven development

개발 방식, 방법론 중 하나로 개발(코드 작성)전 테스트 코드를 먼저 작성하는 것

  • 케이스 단위로 테스트를 작성하는데, 기능 구현 전에 미리 설계를 바탕으로 작은 단위의 테스트케이스를 작성한다!
    작성 후에 이를 통과하는 코드를 추가하는 작업을 반복한다.
  • 작성 시에 예외 케이스를 생각해야 한다.
    테스트 실행 : 직접적으로 함수를 불러 호출을 하거나 커맨드 라인 등을 통해 간접 호출
  • 행위의 결과 확인: pass / fail로 터미널에 나타남
  • 케이스 자동화

 

 

👍 장점 :

설계자의 관점에서의 개발이 가능하다 = 협업에 용이

요구사항이나 목표를 점검할 수 있고, 사용자 입장에서 코드를 작성할 수 있다

테스트 통과율이 높아진다

오버 엔지니어링을 방지한다

 

👎 단점 : 생산성이 떨어짐

 

 

 

 

 

💡 DRF Q기능을 통해서 쿼리하는 것은 어떤 장점이 있습니까?

`django.db.models.Q`

Q는 쿼리 그자체를 객체로 다룰수 있게 하는 기능으로, 데이터베이스 관련 작업에 사용할 수 있는 SQL 조건문이다.

Django 에서 사용하는 filter(), get()같은 ORM 옵션을 Q()를 사용하여 동일하게 적용 시킬 수 있는데,

AND외 에도 OR, XOR, NOT의 연산을 처리할 수 있다.

 

Q를 사용하면 다양한 조건문을 결합할 수 있어 코드의 가독성이 향상되고 쿼리한 코드의 재사용성이 높아져 중복코드가 감소한다.

 

  • AND : Q(A) & Q(B)
  • OR : Q(A) | Q (B)
  • XOR : Q(A) ^ Q(B)
  • NOT : ~Q()

** XOR : 배타적 논리합. 입력값 2개가 동일하면 출력 값 0, 다르면 1을 반환하는 논리게이트, (0 1 | 1 0 이면 True, 0 0 | 1 1 이면 False)

예시

 

from django.db.models.query_utils import Q
from rest_framework.views import APIView

class UserView(APIView):
	def get(self, request):
    	#취미 중 산책이 있거나, 나이가 19살보다 많고, 김씨인 사람만 필터
    	query = Q(hobby__name="산책") | Q(age__gt=19, user__name__startwith="김")
        user_profile_list = UserProfileModel.objects.filter(query)
        
        #Q를 사용하지 않았을때
        user_profile_list = UserProfileModel.objects.filter(hobby__name = "산책", age__gt=19, user__name__startwith="김")

 

여러 개의 filter는 SQL에서 AND 조건을 추가하는 것과 같다. filter의 여러 파라미터도 SQL쿼리의 AND로 처리된다!

# AND는 꼭 Q로 처리할 필요는 없다. Django ORM에서 아래 2개가 동일하다. 
Product.objects.filter(Q(category='A') & Q(sub_category='AB'))
Product.objects.filter(category='A', sub_category='AB')

 

 

** 추가 F()

F는 모델의 필드, annotate된 열의 값. 데이터의 연산에 해당하는 쿼리를 만들어낸다

F() 객체를 만나면 연산자 오버라이딩을 통해 SQL 쿼리를 수행한다

Django는 데이터베이스 정보를 메모리로 가져와 처리하는데, 여러 요청이 동시에 하나의 객체로 접근을 한다면 문제가 발생한다. F()는 이러한 요청을 데이터베이스 단위로 처리하면서 문제를 해결한다

 

 

Django의 Q와 F, 보다 복잡한 활용 예시 : https://powerlichen.github.io/posts/django-orm/

Django Query Expression

https://velog.io/@qlgks1/Django-Query-Expressions-F-Func-Aggregate-Value-Subquery-그리고-and-or-not

https://velog.io/@qlgks1/Django-Query-Expressions-2-annotate-Aggregate-Value-Subquery

Django XOR, 논리적 연산 수행

https://stackoverflow.com/questions/14711203/perform-a-logical-exclusive-or-on-a-django-q-object

 

 

 

 

 

💡 Serializer에서 어떻게 객체를 validate하게 됩니까?


is_valid를 호출하며 데이터 수집을 하고, serializer객체에서 필드 유효성 검사를 실행합니다.

이후 is_valid가 호출한 validate함수 혹은 커스텀 validate함수로 사용자 정의 유효성 검사를 진행한 후,

객체 전체의 유효성 검사를 순차적으로 실행합니다.

이 과정에서 유효한 데이터인지 여부를 반환하는데, 에러가 발생할 경우 serializer.errors를 호출하여 오류 메세지를 점검할 수 있습니다.

 

serializer = Userserializer()
serializer.is_valid(raise_exception=True)

참고 : https://hyeo-noo.tistory.com/324

 

 

 

💡 Serializer에서 create, update 함수는 어떤 기능을 수행합니까?

 

  • create는 새로운 객체를 생성하는 기능을 수행하고 update는 기존 객체를 업데이트하는 기능을 수행합니다.

 

  • DRF의 ViewSet에서의 create()update() 메서드는 ViewSet 클래스 내부에서 사용되어 HTTP 요청을 처리하고, 새로운 리소스를 생성하거나 기존 리소스를 업데이트합니다. Serializer 클래스에서의 create()update() 메서드는 Serializer 클래스의 serializer.save() 호출 시점에서 사용되어 데이터베이스에 객체를 생성하거나 업데이트합니다.

 

 

 

 

💡 Serializer에서 Custom Validation을 하는 이유는 무엇입니까?

custom validation을 하는 이유는 상황에 따라 유효성 검사를 다양하게 활용하기 위함입니다.

 

 

custom validation을 하는 경우

  1. 데이터 일관성 유지 → serializer 모델과의 일치성을 유지
  2. 보안을 유지 → 신뢰할 수 없는 소스라는 가정 하에 데이터 자체를 변환 및 검증하는 과정을 가짐
  3. 비즈니스로직의 로직과 규칙 적용 → DB저장, 중요 정보는 특정 비즈니스 로직과 규칙을 적용해야하는 경우가 있음
  4. 복잡한 검증 로직 처리 → 간단한 필드 유효성 검사 이상의 다양한 로직에 대한 검사가 가능함
  5. 사용자 정의 에러메시지 전송 → (프론트엔드와의 협의 필요), 일반화된 오류 메시지를 대체

 

 

** Custom Validation의 보안관련

  • 입력 데이터의 검증
  • 비즈니스 로직을 적용한 유효성 검사
  • 권한과 인가 검사
  • 데이터 일관성, 정합성 유지 = 데이터 무결성 보장
  • 취약점 방어(XSS(Cross-Site Scripting)나 SQL Injection과 같은 공격에 취약하지 않도록 데이터를 검증)

 

 

 

💡 Serailizer Custom validate함수를 통해 오류 케이스를 어떻게 처리합니까?

Django에서 모델 필드의 값에 대한 사용자 정의 유효성 검사를 수행할 때 Custom validation을 하여

필요한 validator 메소드를 만들거나, 좀 더 많은 기능을 검증할 수 있습니다.

 

이러한 validator함수로 오류 케이스를 처리할 경우 raise exception을 일으킵니다. 필드 유효성 검사를 위해 설정한 검증 절차를 통과하지 못할 경우 raise ValueError, InvaildToken, serializer.ValidationError 등을 발생시킬 수 있습니다.

 

이렇게 발생시킨 오류를 적절히 처리함으로써 어플리케이션의 안정성을 높이고 사용자에게 명확한 오류 메시지를 제공할 수 있습니다.

오류 처리는 사용자 경험을 개선하고 디버깅을 용이하게 하며, 데이터의 일관성을 유지하기 위해 매우 중요합니다.

 

 

 

✦ 내장된 Error 관련 소스 코드

더보기

**builtins.pyi, BaseException에서 기본적으로 내장된 Error를 확인할 수 있다

class BaseException:
    args: tuple[Any, ...]
    __cause__: BaseException | None
    __context__: BaseException | None
    __suppress_context__: bool
    __traceback__: TracebackType | None
    def __init__(self, *args: object) -> None: ...
    def __setstate__(self, __state: dict[str, Any] | None) -> None: ...
    def with_traceback(self, __tb: TracebackType | None) -> Self: ...
    if sys.version_info >= (3, 11):
        # only present after add_note() is called
        __notes__: list[str]
        def add_note(self, __note: str) -> None: ...

class Exception(BaseException): ...
class InvalidToken(AuthenticationFailed):
    status_code = status.HTTP_401_UNAUTHORIZED
    default_detail = _("Token is invalid or expired")
    default_code = "token_not_valid"

DRF 팩에서는 exceptions.py에서 내장 Error를 확인 가능