[DRF] 이미지를 S3에 올리고 DB에 주소 올리기

송민준·2023년 4월 3일
0

출그니 (캡스톤)

목록 보기
3/6

기능 설명

유저가 프로필 이미지를 설정하기 위해 이미지 파일을 django 서버에 업데이트 요청했을 때 S3에 올린 다음 올린 URL을 User 테이블의 user_profile_image에 업데이트합니다. 나중에 유저가 프로필 정보를 요구할 때 User 테이블에 질의하면 user_profile_image에서 URL을 얻을 수 있으므로 모바일에서는 그 URL을 가지고 이미지에 접근할 수 있습니다.

구현

In AWS

  • IAM 사용자 생성 서버에서 S3에 생성 요청을 할 수 있도록 IAM 사용자에 정책을 연결하고 생성했습니다.

생성 후 서버에서 S3에 접근하기 위해 액세스키를 받았습니다.
  • S3 버킷 생성 S3 버킷은 “capstone28subway”라는 이름으로 생성했습니다.

In Django Server

  • settings.py
    ...
    AWS_ACCESS_KEY_ID = secrets["AWS_ACCESS_KEY_ID"]
    AWS_SECRET_ACCESS_KEY = secrets["AWS_SECRET_ACCESS_KEY"]
    AWS_STORAGE_BUCKET_NAME = 'capstone28subway'
    AWS_S3_REGION_NAME = 'ap-northeast-2'
    ...
    다른 파일에서 필요한 AWS 관련 데이터입니다.
    • AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: IAM 사용자의 액세스키 입니다.
    • AWS_STORAGE_BUCKET_NAME: 생성한 버킷 이름입니다.
    • AWS_S3_REGION_NAME: 버킷의 리전입니다.
  • Serializer
    class UserProfileUploadSerializer(serializers.ModelSerializer):
        class Meta:
            model = User
            fields = ["user_nickname", "user_profile_image"]
        def update(self, instance, validated_data):
            instance.user_profile_image = validated_data["user_profile_image"]
            instance.save()
            return instance
  • View
    class UserUploadImageView(APIView):
        def post(self, request):
            image = request.FILES["image"]
            user_nickname = request.data["user_nickname"]
            data = {}
            if User.objects.filter(user_nickname=user_nickname):
                user = User.objects.get(user_nickname=user_nickname)
                url = S3ImageUploader(image).upload()
                serializer = UserSerializer(user,{"user_nickname":user_nickname, "user_profile_image":url})
                if serializer.is_valid():
                    serializer.save()
                    data["url"] = url
                    return Response(data)
                else:
                    data["error"] = "Wrong Request"
                    return Response(data, status=status.HTTP_400_BAD_REQUEST)
            else:
                data["error"] = "User is not exist"
                return Response(data, status=status.HTTP_400_BAD_REQUEST)
    유저 닉네임을 받아서 유저 인스턴스를 찾고 S3에 이미지를 업로드한 후에 user_profile_image을 수정합니다.
  • S3ImageUploader
    class S3ImageUploader:
        def __init__(self, file):
            self.file = file
    
        def upload(self):
            s3_client = boto3.client(
                's3',
                aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
                aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
                region_name=settings.AWS_S3_REGION_NAME
            )
            i = str(uuid.uuid4())
            response = s3_client.upload_fileobj(self.file, settings.AWS_STORAGE_BUCKET_NAME, i)
            return f'https://{settings.AWS_STORAGE_BUCKET_NAME}.s3.{settings.AWS_S3_REGION_NAME}.amazonaws.com/{i}'
    IAM 사용자의 엑세스 키를 통해서 S3_client를 받은 후에 고유한 이름값을 붙여 S3에 업로드합니다. 그리고 리턴 값은 이미지 파일에 접근할 수 있는 URL입니다.

개선해야할 점

  • 업데이트 request에서 body에 들어갈 요소는 user_nickname과 image. 그리고 헤더에 jwt 인증 토큰이 있습니다. 여기서 보안 문제가 있습니다. 만약 토큰은 그대로 보내되 user_nickname을 변경해서 요청한다면 다른 유저의 프로필 이미지를 변경시킬 수 있습니다.
    • 이는 jwt 인증 토큰의 payload 부분에 user_nickname을 저장하여 해결하려고 합니다. 업데이트 요청이 왔을 때 jwt의 user_nickname를 가지고 유저를 식별하면 될 것 같습니다.
  • S3의 권한 문제가 있습니다. 모바일에서 URL을 가지고 이미지에 접근하는 방법은 권한이 퍼블릭으로 설정된 S3의 파일 경로를 통해 접근하는식입니다. 누구든 이 경로를 사용할 수 있다는 점이 리소스 낭비로 이루어질 수 있습니다.
  • 이미 등록되었던 이미지는 지워야합니다. 리소스의 낭비로 이루어질 수 있습니다.
profile
개발자

0개의 댓글