wafflestudio/seminar-2020

Serializer가 반환하는 data를 취사선택해서 가져올 수 있나요?

Opened this issue · 4 comments

SeminarSerializer.data로 반환하는 값은 다음과 같습니다.

{
    "id": Seminar id,
    "name": Seminar name,
    "capacity": Seminar capacity,
    "count": Seminar count,
    "time": Seminar time,
    "online": Seminar online,
  ...
}

하지만 저는 이중 아래와 같은 데이터만 사용하고 싶습니다.

{
    "id": Seminar id,
    "name": Seminar name,
}

이에 제가 생각한 해결책은 다음과 같습니다.


1. 직접 dict 데이터를 후가공한다.

.data로 반환되는 dict 데이터를 dict method를 이용해 원하는 가공을 거칠 수 있을 것입니다.
해당 부분을 함수화시켜 추후에 재사용 할 수도 있을 것으로 보입니다.
다만 이것은 views.py 파일 안에서 가능한 방식으로, 추후 다른 곳에서 같은 형태의 data를 사용하고 싶을 때, 해당 함수를 재정의하거나 불러와야 한다는 단점이 있습니다.

2. 원하는 데이터만을 반환하는 method를 SeminarSerializer 안에 정의해둔다.

1번에서 이야기한 함수를 SeminarSerializer class 안에 정의해서 필요할 때 해당 함수를 호출하는 방식이 있습니다. 어디서든 해당 클래스를 사용하는 곳에서 불러올 수 있다는 장점이 있습니다.
하지만 many=True를 사용해 ListSerializer를 불러오게 되면 해당 method를 사용할 수가 없게 되어 불편함이 따릅니다.

3. MiniSeminarSerializer class를 정의한다.

원하는 fields 만을 골라서 만든 Serializer을 정의하여 사용합니다. 이는 many=True에도 유효하며, 어디서든 불러와서 사용할 수 있습니다. 그리고 적다 보니 생각난 가장 좋은점은, 기존 serializer을 상속받으면 기존 method들도 사용할 수 있습니다.


이슈를 다 적고 보니 3번 방식이 가장 깔끔하고 좋다고 생각이 되어, 저는 3번 방식을 채택했습니다.

다만 이렇게 Serializer을 하나 더 정의하는 방식이 좋은 방식인지 확신이 들지 않습니다. 기존의 Serializer만으로도 반환하는 데이터를 취사선택하는 더 좋은 방식이 있을까 궁금해서 이슈 올립니다.

3.의 방식 많이 사용하는 것으로 알고 있습니다! 저는 UserSerializer가 있다면, 그중 더 적은 field만을 포함한 SimpleUserSerializer 같은 naming을 많이 본 것 같네요! 더 나아가 read(serialize), write(deserialize) 용도를 구분하여 create, update할 때만 사용하는 UserWriteSerializer 같은 것도 따로 정의할 수도 있을 거구요. 말씀하신 것처럼 이런 비슷한 Serializer들을 정의할 때, 중복되는 코드가 많아지는 것처럼 느껴진다면 OOP의 상속 개념을 활용하면 되겠지요.

1.의 방식도 가능하겠지만, .data 로 serialize시킬 때, 정작 response에 필요도 없는 field 정보들을 가져오느라 DB에 불필요한 부하를 줍니다.(똑같은 row들을 select하더라도, 필요한 column만 집어서 가져오는 것이 더 효율적입니다) 심지어 그것이 SerializerMethodField 같은 것을 통해, join 등 기교를 부리는 query라면 더욱더 끔찍하겠지요. 이게 가장 치명적인 문제 중 하나입니다. 그렇기에 불필요한 data를 response로 줄 수 있다고 해서 다 주려고 해선 안 되는 것이기도 하구요.

2.의 방식도 가능하겠지만, 일반적인 DRF method와 완전 별개의 custom한 method를 정의하는 것은 DRF serializer가 제공하는 장점들을 충분히 활용할 수 없을 수 있습니다. 협업할 때 어려운 지점을 만들거나, 어쩌면 위험하기도 하구요.

그리고 하나의 ViewSet에서 다양한 serializer를 쓰고 싶을 때, 그냥 해당 serializer를 직접적으로 SimpleUserSerializer() 처럼 바로 갖다 쓰는 것보다, get_serializer()를 사용하는 것이 좋을 것입니다. 단순히 보기 예쁜 것이 아니라 context 등이 알아서 넘어갑니다. 그럼 하나의 viewset에서 동일한 get_serializer() 로 어떨 때는 저거, 어떨 때는 이거를 어떻게 가져오는가? 그 해답은 과제 2의 참고하면 좋을 것들에도 처음부터 링크를 붙여넣어두었던 https://www.django-rest-framework.org/api-guide/generic-views/#get_serializer_classself 에 있습니다. DRF permission을 예쁘게 활용하려 하고 계신다면, permission에 대해서도 매우 비슷한 녀석이 있습니다.

3.의 방식 많이 사용하는 것으로 알고 있습니다! 저는 UserSerializer가 있다면, 그중 더 적은 field만을 포함한 SimpleUserSerializer 같은 naming을 많이 본 것 같네요! 더 나아가 read(serialize), write(deserialize) 용도를 구분하여 create, update할 때만 사용하는 UserWriteSerializer 같은 것도 따로 정의할 수도 있을 거구요. 말씀하신 것처럼 이런 비슷한 Serializer들을 정의할 때, 중복되는 코드가 많아지는 것처럼 느껴진다면 OOP의 상속 개념을 활용하면 되겠지요.

1.의 방식도 가능하겠지만, .data 로 serialize시킬 때, 정작 response에 필요도 없는 field 정보들을 가져오느라 DB에 불필요한 부하를 줍니다.(똑같은 row들을 select하더라도, 필요한 column만 집어서 가져오는 것이 더 효율적입니다) 심지어 그것이 SerializerMethodField 같은 것을 통해, join 등 기교를 부리는 query라면 더욱더 끔찍하겠지요. 이게 가장 치명적인 문제 중 하나입니다. 그렇기에 불필요한 data를 response로 줄 수 있다고 해서 다 주려고 해선 안 되는 것이기도 하구요.

2.의 방식도 가능하겠지만, 일반적인 DRF method와 완전 별개의 custom한 method를 정의하는 것은 DRF serializer가 제공하는 장점들을 충분히 활용할 수 없을 수 있습니다. 협업할 때 어려운 지점을 만들거나, 어쩌면 위험하기도 하구요.

@davin111 여러 serializer를 동시에 이용하고자 할 떄 쓸 수 있는 방법은 뭐가 있을까요? User를 생성할 때 role에 따라서 여러가지 ProfileSerializer가 추가로 사용되는 경우처럼요. 저는 writewrapup 클래스를 새로 정의해서, 그 클래스가 여러 serializer를 인스턴스 변수로 가지도록 해서 구현했는데 혹시 다른 방법이 있는지, 장단점은 어떤지 궁금합니다! 상속도 고려해 보았지만, 다중상속 문제가 생길 것 같아서 배제했습니다.

@eldpswp99 흠, deserialize하는 경우의 질문이지요? 사실 이건 코드를 좀 더 봐야 잘 답변할 수 있을 거 같은데, serialize든 deserialize든 결국 그런 경우엔 그 두 개의 Serializer를 내부적으로 합쳐둔 Serializer를 정의해서 그냥 그걸 쓸 수도 있을 거 같고, 아니면 그냥 명시적으로 ProfileSerializer 같은 걸 view에 그대로 끌어다가 사용하는 것도 그리 나쁘진 않을 수도 있을 거 같네요.