Nonamed Develog
[TIL][240906] Troubleshooting 2: 중복된 코드의 냄새 본문
1. 문제 인식: 중복된 코드의 냄새
처음에는 각각의 기능(회원가입, 로그인, 프로필 조회, 프로필 수정)에 맞춰 별도의 시리얼라이저를 만들었다. 아래는 그 첫 번째 코드이다.
class SignUpSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ['username', 'password', 'email', 'first_name', 'last_name', 'nickname', 'date_of_birth', 'gender', 'bio']
class SignInSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name']
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name', 'nickname', 'date_of_birth', 'gender', 'bio']
class ProfileUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'first_name', 'last_name', 'nickname', 'date_of_birth', 'gender', 'bio']
처음 코드를 작성할 때는 필드 목록을 각각 정의하는 것이 문제가 없어 보였지만, 시간이 지남에 따라 공통된 필드가 반복적으로 정의되고 있다는 사실이 눈에 들어왔다. 특히 username, email, first_name, last_name과 같은 필드들이 여러 시리얼라이저에서 반복되면서 코드가 중복되고 있었다.
중복된 코드는 유지보수가 어려워지고, 버그가 발생할 가능성을 높이는 “코드 냄새”의 전형적인 예였다.
2. 리팩토링의 필요성: 효율성을 위한 상속 도입
코드를 개선하기 위한 다음 단계는, 공통된 부분을 하나의 부모 클래스에 모으고 필요한 시리얼라이저에서 이를 상속받는 구조로 바꾸는 것이었다. 여기서 등장한 것이 MyBaseSerializer이다. 이 시리얼라이저는 모든 시리얼라이저가 상속받는 기본 시리얼라이저로, 공통 필드를 한 곳에 정의해 중복을 줄였다.
class MyBaseSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name'] # 공통 필드
class SignUpSerializer(MyBaseSerializer):
password = serializers.CharField(write_only=True)
class Meta(MyBaseSerializer.Meta):
fields = MyBaseSerializer.Meta.fields + ['password', 'nickname', 'date_of_birth', 'gender', 'bio']
class SignInSerializer(MyBaseSerializer):
class Meta(MyBaseSerializer.Meta):
fields = MyBaseSerializer.Meta.fields
class ProfileSerializer(MyBaseSerializer):
class Meta(MyBaseSerializer.Meta):
fields = MyBaseSerializer.Meta.fields + ['nickname', 'date_of_birth', 'gender', 'bio']
class ProfileUpdateSerializer(MyBaseSerializer):
class Meta(MyBaseSerializer.Meta):
fields = ['email', 'first_name', 'last_name', 'nickname', 'date_of_birth', 'gender', 'bio']
MyBaseSerializer 도입: username, email, first_name, last_name과 같은 공통 필드를 MyBaseSerializer에 정의하고, 나머지 시리얼라이저는 필요한 필드를 추가하는 식으로 설계가 간소화되었다. 이로써 코드 중복이 눈에 띄게 줄어들었다.
3. to_representation 메서드 고민
리팩토링 과정에서, 시리얼라이저에서 반환할 때 특정 필드를 동적으로 수정할 필요가 있을지 고민이 있었다. 예를 들어, 프로필 조회 시 닉네임을 대문자로 변환하거나 데이터를 좀 더 동적으로 처리하는 방법으로 to_representation을 활용할 수 있다고 생각했다.
def to_representation(self, instance):
representation = super().to_representation(instance)
if 'nickname' in representation:
representation['nickname'] = representation['nickname'].upper()
return representation
아이디어: to_representation 메서드를 사용하면 반환되는 데이터를 조정할 수 있다는 점에서 유용해 보였다. 하지만, 이 메서드를 실제로 적용하려고 보니 필요 이상의 복잡성이 추가될 것 같았다. 단순한 필드 변환을 위해 메서드를 오버라이드하는 것보다는, 간단한 필드 추가와 수정으로도 충분히 문제를 해결할 수 있었다.
4. 결론: 상속으로 문제 해결, to_representation은 결국 사용하지 않음
결국, 상속을 통한 코드 중복 제거가 주요 개선점이 되었으며, to_representation을 사용하지 않아도 요구 사항을 충분히 충족할 수 있었다. MyBaseSerializer를 통해 공통 필드를 효율적으로 관리할 수 있었고, 추가적인 커스터마이징 없이도 필요한 데이터를 충분히 처리할 수 있었다.
- 이전: 각 시리얼라이저에 중복된 필드들이 정의되어 유지보수와 관리가 어려웠다.
- 이후: 공통 필드를 MyBaseSerializer로 묶어 중복을 제거하고, 복잡성을 추가하지 않으면서도 효율적인 설계를 할 수 있었다. to_representation은 결국 사용하지 않았다.
이 리팩토링 과정은 코드를 간결하고 효율적으로 유지하면서도, 불필요한 복잡성을 피하는 것이 얼마나 중요한지 깨닫는 과정이었다.
'WHAT I LEARN > TIL' 카테고리의 다른 글
[TIL][240910] Django와 웹 크롤링 (2) | 2024.09.10 |
---|---|
[TIL][240906] Troubleshooting 3: url routing의 순서 (0) | 2024.09.09 |
[TIL][240905] RefreshToken 속 check_blacklist() (0) | 2024.09.05 |
[TIL][240904] Troubleshooting 1: validaors.py의 활용 (0) | 2024.09.04 |
[TIL][240903] RESTful API (1) | 2024.09.03 |