drf 다중 이미지 full url 삽질 기록

Han07·2021년 4월 1일
2

삽질 기록

목록 보기
1/2
post-thumbnail

다중 이미지를 처리하는 도중에 url이 다 안나오는 이슈가 발생했다.

{
    "id": 73,
    "title": "hello!",
    "content": "hello_content",
    "date_created": "2021-03-31",
    "images": [
        {
            "image": "/media/diary/DRF_Imagefield_1_tw1aZsT.png"
        },
        {
            "image": "/media/diary/django_test_x73GRWC.png"
        }
    ]
}

링크 된 포스트를 보면 해결된 코드가 있다. 이 포스트에서는 내가 한 삽질을 기록할 생각이다.


1. 가장 처음 코드 - 동작은 잘 되지만 url이 짤려서 나옴

from rest_framework import serializers

from diary.models import DiaryImage, Diary


class DiaryImageSerializer(serializers.ModelSerializer):
    image = serializers.ImageField(use_url=True)

    class Meta:
        model = DiaryImage
        fields = ['image']


class DiarySerializer(serializers.ModelSerializer):
    images = serializers.SerializerMethodField()

    def get_images(self, obj):
        image = obj.diaryimage_set.all()
        return DiaryImageSerializer(instance=image, many=True).data

    class Meta:
        model = Diary
        fields = ['id', 'title', 'content', 'date_created', 'images']

    def create(self, validated_data):
        instance = Diary.objects.create(**validated_data)
        image_set = self.context['request'].FILES
        for image_data in image_set.getlist('image'):
            DiaryImage.objects.create(diary=instance, image=image_data)
        return instance

2. MEDIA_ROOT 바꾸기

settings.py에 있는 MEDIA_ROOT에 경로를 다 적어주었다.

MEDIA_ROOT='http://127.0.0.1:8000/media/'

결과: url은 다 나오지만 다른 곳으로 접속하게 되면 바꿔줘야 한다. 그리고 프론트와 연동하면 Not Found가 뜬다.


3. DiaryImageSerializer에 SerializerMethod()를 이용해 url 집어넣기

https://stackoverflow.com/questions/35522768/django-serializer-imagefield-to-get-full-url

위 사이트를 참고해서 serializer field를 커스텀 해주었다.


1. DiarySerializer에 추가하기

안된다. self에 request라는 애가 없다고 한다. ViewSet을 써서 그런가 하고 APIView로 바꿔주었다.

from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from diary.models import Diary
from diary.serializers import DiarySerializer, DiaryListSerializer


class DiaryView(APIView):

    permission_classes = (IsAuthenticated, )

    def post(self, request):
        serializer = DiarySerializer(data=request.data, context={"request": request})
        if serializer.is_valid():
            serializer.save(user=self.request.user)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class DiaryDetailView(APIView):

    permission_classes = (IsAuthenticated, )

    def get_object(self, pk):
        return get_object_or_404(Diary, pk=pk)

    def get(self, request, pk):
        diary = self.get_object(pk)
        serializer = DiarySerializer(diary)
        return Response(serializer.data, status=status.HTTP_200_OK)

음... 결과가 나오기는 했다.

{
    "id": 73,
    "title": "hello!",
    "content": "hello_content",
    "date_created": "2021-03-31",
    "images": [
        {
            "image": "/media/diary/DRF_Imagefield_1_tw1aZsT.png"
        },
        {
            "image": "/media/diary/django_test_x73GRWC.png"
        }
    ]
    "image_url": "http://127.0.0.1:8000/media/diary/django_test_x73GRWC.png"
}

다만 full url은 여전히 나오지 않는다.

SerializerMethod()는 필드를 커스텀 하는 것인데 이미지는 다이어리가 아닌 다이어리 시리얼라이저에 있는 필드이다. 그래서 아예 이미지 시리얼라이저에 저장할 때 full url을 쓰면 어떨까 생각해 보았다.


2. 저장할 때 full url 쓰기

결과: 나오기는 하는데 이상하다.

/media/http://127.0.0.1:/diary/8000/django_test_x73GRWC.png

🤔🤔🤔🤔

그래도 아까는 full url이 나왔으니 다이어리 시리얼라이저가 아닌 이미지 시리얼라이저에서 필드를 커스텀 해보기로 했다.

3. 이미지 시리얼라이저에서 필드 수정하기

당연히 안됐다. 사이트에 들어가보면
request = self.context.get['request']
이런 코드가 있다. self에서 가져오려면 view에서 이 시리얼라이저를 불러야 한다.


4. 그냥 모델에 집어넣기

다중 이미지로 검색하니

다 본 포스트다.


😢😢

그래서 검색을 바꿔보기로 했다. 다중 이미지를 사용하는 프로젝트 중에서 가장 흔한 것이 생각났다.

인스타그램!

인스타그램은 한 포스트에 총 10장의 사진을 넣을 수 있다. 그리고 django로 개발되었기 때문에 인스타그램 클론 코딩은 생각보다 많은 자료가 있었다. 먼저 구글링을 해봤더니 대부분 블로그 글이 나왔다. 코드를 보니

  1. 모델에 이미지 필드 하나 있음
  2. 이미지 없음

보통 이 둘로 나뉘어졌다. 다중 이미지를 쓰는 포스트는 찾기 힘들어 깃허브에서 검색해보기로 했다.

drf imagefield
drf image
django image
instagram
drf insta

등등으로 검색을 했고 예상대로 구글링보다 더 많은 결과가 나왔다(당연하다)

코드를 봐보니

  1. 모델에 이미지 필드 하나 있음
  2. 이미지 필드가 아닌 파일로 받아서 뷰에서 url을 처리해줌
  3. 이미지 없음

대부분 이런 상황이었다. 나는 시리얼라이저에서 최대한 이미지를 처리하고 싶었기 때문에 2번은 하지 않았다.

대부분의 코드가 모델에 이미지 필드 1개를 넣어두었다. 그것을 보고 나는 착각을 하기 시작했다...

🤓: 헉 이미지필드는 필드 하나로도 여러 개 받을 수 있나봐!

삽질을 하다 지친 나는 시도해보기로 하였다.

결과 - 오류 안남!

대박. 오류가 안났다. 하지만 이미지 여러개를 추가하면 마지막 이미지만 저장되었다.


5. OneToMany 관계 Serializer 추가하기

다중 이미지와 클론 코딩 등으로는 원하는 결과를 얻지 못했다. 시선을 돌려 1:N 관계라는 것에 집중하기 시작했다.

https://cjh5414.github.io/django-rest-framework-onetomany-serializer/

역시 이미지필드를 사용한 것이 있었다!

적용을 해보았으나 역시 full url이 안나와 포기했다..


6. 이미지 시리얼라이저에서 다이어리 데이터 받아오기

이번에는 오픈 채팅방에 질문을 해보았다.

내가 원하는 것과 현재 상황, 참고한 자료, 코드 사진 등을 첨부하였다. 답변으로 다이어리 시리얼라이저가 아닌 이미지 시리얼라이저에서 다이어리의 정보를 받아오면 안되냐는 의견을 받았다.
다이어리와 이미지는 1:N 관계이기 때문에 이미지 쪽에서도 다이어리를 참조할 수 있다는 것이다.

하지만 '이미지가 여러개 있는데 어떻게 묶어서 return 하지? 그리고 다이어리 정보를 매번 불러와야 하지 않을까?' 라는 생각이 들었다.

또한 오히려 이렇게 구현하면 로직이 복잡해질 것 같다는 생각이 들어 포기하였다.


7. 해결!!!!

선배나 오픈채팅방에 질문하였더니 해결이 되지 않았다. 조금 더 큰 곳을 찾아보기로 하였다.

오류를 해결하기 위해서 검색하면 StackOverflow, OKKY, DEV 등의 사이트를 많이 볼 수 있다.

위와 같이 개발에 관련된 오류나 궁금한 점 등을 자유롭게 공유하는 사이트를 개발 커뮤니티라고 한다. 나는 그 중 가장 많이 봤던 StackOverflow에 질문하기로 했다.

회원가입을 한 뒤, 구글 변역기와 파파고를 열심히 돌려 질문을 작성했다.

https://stackoverflow.com/questions/66883875/how-can-i-get-the-full-url-of-the-image-in-drf/66885678#66885678

comment로 아래 질문을 보고 해결해보라는 의견이 달렸다.

https://stackoverflow.com/questions/26379849/return-image-url-in-django-rest-framework/26385269#26385269

요약하자면 SerializerMethod()를 이용하라는 것이다. 혹시나 하는 마음에 해봤지만 역시나 안됐다...

1시간 뒤, 답변이 달렸다.

그냥 내가 했던 코드에 context=self.context 를 붙이라는 것이었다.

놀랍게도 성공했다...

영어를 해석해보면

FileField와 ImageField는 serializer에서 absolute url을 build하지 않으면 자동적으로 context의 request를 이용한다. 그렇기 때문에 너는 DiarySerializer에 DiaryImageSerializer의 context를 써줘야 한다.

그렇다고 한다. 이해가 잘 되지 않아 자료를 더 찾아보았다.

한글 번역 문서

공식 문서를 확인하니 하이퍼링크된 관계를 포함하는 시리얼라이저를 사용할 때 정규화된 url을 만들기 위해서 사용된다고 한다.


8. 느낀점

이번 프로젝트를 하며 처음 이미지 필드를 써보았다. 그래서인지 이미지 처리에 있어서 유독 오류가 많이 발생했었다. 사실 이 포스트에 쓴 이슈를 해결하기 위해 일주일을 넘는 시간을 날렸다. 느낀점을 적어보자면

1. 역시 질문이 최고다.

물론 아무 노력도 하지 않고 질문만 한다면 그건...🤔
하지만 내가 할 수 있는 만큼 하고도 해결을 못할 것 같으면 질문이 최고인 것 같다. 세상에는 아주아주 멋진 개발자들이 많이 있고, 그 중 많은 개발자들이 커뮤니티에서 활동중이다. 나 역시 생각치도 못했던 해결방법을 stackoverflow라는 개발자 커뮤니티에서 찾았다.

2. 포기는 최대한 하지 말자

사실 하는 도중에 그냥 이미지 개수를 4개로 제한하고 모델에 이미지 필드 4개를 만들자는 생각도 해봤다. 굳이 이렇게까지 OntToMany 방식으로 해야할까? 라는 생각도 했었다. 하지만 내가 생각하기에 가장 좋은 방법이 이 방법이었고, 모르는 지식을 배우는 과정이라고 생각하였다. 최대한 포기하지 않고 답을 찾다 보면 100%는 아니지만 해결책이 나온다고 생각한다.

3. 기본 개념이 중요하다.

사실 이번 이슈는 내 개념이 부족해서 일어난 것이다. DRF를 공부할 때 그냥 코드 짜기에만 급급하고 공식 문서를 제대로 읽지 못한 것이다. 실제로 공식 문서에 context만 검색해도 url 이야기가 나온다. 이번 일을 계기로 drf를 다시 공부해보자는 생각이 들었다.

1개의 댓글

comment-user-thumbnail
2022년 1월 18일

감사합니다!!! 덕분에 해결했어요ㅠㅠㅠㅠ

답글 달기