• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    미해결

drf의 업로드 업데이트 관련 여러가지 질문입니다

22.02.15 15:10 작성 조회수 307

0

안녕하세요 강사님.

강사님 강의를 토대로 학습하며 개인적으로 진행하고있던 프로젝트에서 문제점이 발생하여 이렇게 질문드립니다.

현재 상황은 다음과 같은 모델이 존재하고, 리액트에서 formdata객체에 모든 데이터를 append해서 post요청을 통해

중첩된 시리얼라이저를 활용한 modelviewset에 create함수를 호출하는 형태로 코드를 작성했습니다.

 

1)최초에 portfolio 모델을 구성하고 관계를 설정할때, portfolioupload 모델은 유저가 동적으로 필드를 채워나가기 때문에 하나의 컬럼에 총 5가지 필드가 만들어지게 제작하게 되었습니다.(이미지, 텍스트, 비디오, 임베드, 그리드 등의 5가지 선택지가 있고 유저는 그중 한가지를 초이스 하여 필드를 채워나가는 형식/behance의 포트폴리오 업로드 형식을 벤치마킹하려고 노력했습니다) 그렇기 때문에 현재와 같은 portfolio 모델을 구성하게 되었는데, 보다 더 나은 방법이 있었을지 궁금합니다.

2)portfolio 모델과 같이 여러 가지 관계를 형성하고 있는 데이터를 불러와서 리액트에서 사용하려다 보니, 중첩된 시리얼라이저가 필수인것 같아 다음과 같은 시리얼라이저를 작성했습니다. 그러다 보니 query 수가 많아져서 요청이 길어지는 성능 이슈가 발생했습니다. 현재는 django auto prefetch라는 라이브러리를 통해 개선했지만, 저런 데이터구조를 불러올 때 더 좋은 방법이 있을지 궁금합니다.

3)create 함수를 구현할 때 중첩된 객체를 받아서 현재의 코드보다 조금 더 간결하게 구현해 보려고 했었습니다. 따라서 리액트에서 아래와 같은 함수를 통해 중첩된 object를 보내봤지만 drf에서 멀티미디어 콘텐츠를 읽어내지 못했습니다. 아마도 콘텐츠 타입이 aplication/json 때문일 거라 생각했기 때문에 parser를 바꿔보는 등 여러 가지 방법을 시도했고, 모든 게 다 실패해서 현재와 같이 formdata 객체에 모든 데이터를 append 해서 보내는 형태가 되었습니다. 이 과정에서 portfolio 모델에 post 요청을 보내는 데이터의 구조와 drf에서의 데이터 처리가 어떤 방향으로 가는 게 지금보다 더 좋은 생각이었는지, 혹은 중첩된 object를 drf에서 받아서 처리할 방법이 있는지 궁금합니다. 

function formData(formData, filesIgnore = []) {
  let data = new FormData();

  let files = filesIgnore;

  Object.entries(formData).forEach(([key, value]) => {
    if (typeof value === 'object' && !files.includes(key)) {
      data.append(key, JSON.stringify(value) || null);
    } else if (files.includes(key)) {
      data.append(key, value[0] || null);
    } else {
      data.append(key, value || null);
    }
  })
  return data;
}

4) portfolio 모델에서의 update를 구현하려고 보니, react에서 기존 데이터를 보여주기 위해 portfolioserializer를 통해 데이터를 불러오고 유저가 진입한 upload url에 pk가 포함되었는지를 검사해서 미리 input 필드에 데이터를 채워주는 형태의 방법을 사용했습니다. 이런 상황에서 불려와진 image는 storage의 url 형태였고, 유저가 이미지의 순서 변경이나 삭제를 할 경우 추적할 방법이 없어서 데이터의 변경이 안되는 이슈가 발생했습니다. 위 상황에서 저는 get 요청을 할 때 멀티미디어 콘텐츠가 불려와져야 할까? / put 요청으로 보내는 serializer에 해당 정보가 담겨있어야 할까? 라는 고민을 했고 첫 번째 방법은 방법 자체를 찾을 수 없었고, 두번 째 경우는 해당 정보를 어떻게 담아서 어떻게 처리할지 감이 잡히질 않아서 질문을 드리게 되었습니다. 사실 접근 방법이 이게 맞는지조차 의문이 들기에 보통 이런 혼합 데이터의 게시물 수정을 어떻게 구현하시는지 궁금합니다.

5) 업로드 시에 가로 1920px 이상의 이미지들의 resize가 필요해서 찾아보니 백엔드에서 업로드 시에 실행하는 게 보편적이라는 답변을 받았는데, 업로드의 속도를 줄이려면 업로드할 이미지들의 미리 보기 기능에 사용된 image onload 함수에 포함하는 게 더 좋은 방법이 아닌가에대해 고민하게 되었습니다. 어떤 접근 방법이 더 나은 방향인지 궁금합니다.

6)해당 프로젝트는 강의에서 설명 해주신 azure를 통해 배포 / 테스트 중입니다. 포트폴리오 같은 데이터를 업로드할 때 아무래도 이미지, 비디오 등의 데이터량이 많기 때문에 간혹 업로드 용량이 상당해지는 경우가 생기게 되었습니다. 이와 같은 경우에 용량의 총합이 100mb가 넘어가게 되면 데이터 업로드가 안되는 현상이 발생했고, 로컬에서는 정상작동하기에 아마도 서버의 문제일 것 이라고 예상하게 되었는데, 서버가 작아서 발생하는 문제인 건지 아니면 어떤 다른 문제점 혹은 제한을 풀 수 있는 명령어가 존재하는지 궁금합니다.

강사님 강의를 토대로 약 9개월 정도 혼자서 진행한 프로젝트입니다. 상당히 많은 시행착오가 있었고 그때마다 구글링과 강의 다시 보기를 통해 해결해왔었는데, 혼자서 적지 않은 기간을 하다 보니 어떠한 코드 작성에 있어서 구현이 되고안되고 보다는 접근 방법이나 이게 정말 맞는지? 라는 문제가 여러가지 발생했습니다. 그런 부분들이 혼자서는 해결되지 않아 이렇게 질문드립니다. 만약 설명이 부족하거나 질문들에 이해가 안 되시는 점 있으시면 바로바로 추가하겠습니다.

답변 1

답변을 작성해보세요.

1

안녕하세요. :-)

혼자 개발을 진행하시느라 고생이 많으십니다.

1. 하나의 Row에 5가지 타입이 있다는 말씀이시죠? 이와 유사한 라이브러리로 django-wagtail 이 있습니다. 모델링에는 정답은 없습니다만, django-wagtail 의 모델링을 참고해보시면 어떠실까 싶네요.

2. 장고에서는 N+1 문제를 해결하기 위해 select_related, prefetch_related 기능을 제공해주고 있습니다. 수행되는 SQL 쿼리를 확인해보신 후에 N+1 문제가 맞다면 그렇게 해결해보실 수 있구요. 아래의 <요기요> 개발자 분의 영상도 참고해보시면 좋습니다. 그리고 변화가 자주 일어나지 않는 조회 요청의 경우에는 캐시(memcached, redis)를 적절히 사용하여 성능 향상을 시켜보실 수 있습니다. 정확한 분석이 우선되어야할 것입니다.

https://www.youtube.com/watch?v=EZgLfDrUlrk

3. application/json 타입은 json 문자열로서 전달하는 것이니 파일 내용을 담을 수 없습니다. 파일 전송에는 multipart/form-data 타입이어야만 전달이 가능합니다. JSON 전송을 해보고자 하신다면, multipart/form-data 타입으로서 하나의 필드에 json 문자열을 담아서 전송을 해볼 수도 있지 않을까요?

4. 설명이 잘 이해가 가질 않습니다만 // 조회 시에는 지정된 Serializer 내역대로 응답을 받게 되겠죠. 그 응답에서 image/file은 url로서 받게 될 것이구요. // 수정할 대상의 Resource URL을 지정해서 put 혹은 patch 요청을 보내어 변경을 원하시는 필드만 변경하실 수 있지 않을까요?

5. 보통 백엔드 단에서는 여러가지 라이브러리 사용이 자유로운 데 반해, 웹 프론트엔드에서는 제약이 많습니다. 구닥다리 IE 지원까지 한다면 제약은 더 심해지요. // 무엇이든 정답은 없습니다. 프론트에서 구현하시는 것이 더 적합하다고 판단이 되시면 그렇게 하시면 됩니다.

6. 데이터 업로드가 안 될 때 어느 부분에서 어떤 오류가 발생하는 지 먼저 확인해보세요. **오류 메세지를 정확히 보셔야겠죠**. 그러면 어느 단에서의 오류인지 확인해볼 수 있을 듯 합니다. 그 후에 해당 부분의 전송 limit을 확인해보는 순서로 체크하셔야할 듯 싶습니다. // 로컬에서는 이 이슈가 없고, 실서비스에서 발생한다면, 흠. 같은 도커 이미지로 테스트해보신 거죠? 같은 도커 이미지로 테스트해보셨다면, uwsgi 옵션 이슈도 아닐테고. 그럼 azure app service plan을 상위 plan으로 잠시 생성하셔서 테스트해보시면 어떨까요?

---

이렇게 개발하시면서 겪으시는 다양한 이슈들을 꼭 블로깅을 하시고, 그리고 해결과정도 같이 정리해서 포스팅하시길 권해드립니다. 혹시 잘 해결이 안 되고 계시더라도, 그 진행과정/사고의 과정이 중요한 것이니깐요. 꼭 포스팅하세요.

화이팅입니다. :-)

 

주원님의 프로필

주원

질문자

2022.02.16

모든 질문이 굉장히 애매했던 거 같은데, 이렇게 빠르게 답변해 주셔서 정말 감사드려요

다른 부분은 대부분 이해가 되었는데 3번, 4번, 6번 조금 더 궁금해서 다시 질문드립니다

 

 

3) multipart/form-data 타입으로서 하나의 필드에 json 문자열을 담아서 전송하라는 말씀은 object를 만들어서 JSON.stringify를 통해 append 하라는 말씀이신 걸까요?

이게 맞다면, 이렇게 했을 때 장고에서 추가적인 작업 없이 멀티미디어 콘텐츠를 읽을 수 있나요?

 

4) 이 경우는 제가 구현조차 하지 못한 부분인데 꼭 해보고 싶어서 조금 더 자세히 설명드려볼게요

현재 업로드 페이지의 기능 중에 이미지 혹은 비디오를 업로드 할 경우 미리 보기를 보여주고, 해당 리소스들의 순서 변경, 삭제 등을 지원하고 있습니다.

그에 따라서 사용자가 input 필드를 생성할 수 있고, input list의 index를 formdata key에 담아서 post 요청 하는 방식입니다. 이렇다 보니 사용자가 기존 게시물의 업데이트 화면에 진입했을 때 해당하는 이미지 혹은 비디오의 url들을 조회해서 detail 페이지를 구현하는 것 처럼 실제 파일 값은 존재하지 않는 상태로 사용자에게 보여주고 있습니다.(보통 react에서 update를 이런 형태로 구현 하는게 맞는 걸까요?)

이 시점에서 문제가 발생하게 되는데, 사용자가 업데이트 화면에 진입 후 미리 보기 형태로 존재하는 이미지, 비디오를 삭제 혹은 순서 변경했을 때 file의 값이 존재하지 않으니 put 혹은 patch 요청을 보내도 어떻게 변경되었는지 체크해서 db에 저장하는 로직을 작성할 수가 없었습니다.

이런 상황일때 Resource Url을 지정하라는 말씀이 url값을 drf로 넘겨서 기존의 값과 일치하는지 혹은 변경되었는지 체크해보란 말씀이셨던 걸까요? (이해를 돕기위해 react코드 첨부합니다.)

 

*input 필드 추가하는 로직

*onsave 로직

 

6) 현재 배포환경에서 로그를 확인해보니

```

Access to XMLHttpRequest at '' from origin '' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

//

2.8381b7c0.chunk.js:2          POST  net::ERR_FAILED 502

```

위와 같은 오류를 발견했습니다 cors 관련 오류라는 것 같은데 배포환경에서 모든 데이터 요청과 같은 url로 100mb 미만의 데이터 업로드는 아무런 문제가 없는 상황입니다. 이게 어디서 발생한 문제인지 혹시 알 수 있을까요?

 

마지막으로 formdata를 post요청할때 react단에서 formdata에 append한 순서대로 drf에서 조회할 수도 있나요? request.data로 접근해보면 항상 text와같은 작은파일이 먼저 배치가 되고 video같은 큰 파일이 맨 나중에 배치가 되는거같던데, 이런 현상을 formdata의 순서대로 request.data를 통일할수 있는 방법이 있을까요?(현재는 이러한 현상때문에 key에 index를 포함해서 정렬하는 로직을 쓰고있었습니다.)

 

또 적다보니 질문이 많아졌는데 죄송합니다.. 제가 지식이 아직 많이 부족해서 설명드린 것들이 잘 전달되었을까 걱정인데, 확인해 보시고 코멘트 주시면 추가적으로 더 말씀드리겠습니다.

 

3. 클라이언트에서 서버로의 전달을 확인하는 것이 우선이구요. 서버로 전달이 된다면 서버 단에서 읽을 수 있습니다. 언어/프레임워크에 상관없이요. // 웹프론트엔드에서의 다수 필드를 json으로 직렬화(stringify)하면 하나의 문자열이 될테구요. 이를 하나의 form 필드로서 전달을 할 수 있습니다. 그러면 장고 단에서는 하나의 필드로서만 인지할 테니, 관련 API 뷰에서 Serializer 인스턴스 생성할 때, 적절히 deserializer하여 넘겨줄 필요가 있을 듯 합니다.

4. 문제를 보다 간단하게 만드는 것도 한 가지 방법입니다. 순서변경 등은 일단 파일을 업로드하고 나서 가능토록 해볼 수 있지 않을까요. 일단 업로드부터 수행하는 거죠.

5. CORS 이슈였군요. 이는 브라우저 보안기능이구요. 해당 js 요청을 받는 서버 단에서 세팅을 해주시면 됩니다. 지금은 Azure Storage Account가 되겠네요. 애저포털에서 해당 Storage account 로 가보시면 Resource sharing (CORS) 항목이 있습니다. 아래 문서를 참고하시어, 문서의 XML에 명시된 대로 애저포털에서 지정하시면 CORS 이슈를 해결하실 수 있습니다.

https://docs.microsoft.com/ko-kr/rest/api/storageservices/Cross-Origin-Resource-Sharing--CORS--Support-for-the-Azure-Storage-Services?redirectedfrom=MSDN#enabling-cors-for-azure-storage

화이팅입니다. :-)

주원님의 프로필

주원

질문자

2022.02.16

감사합니다 많은 도움되었어요

더 생각해보고 수정하면서 또 궁금한 점 생기면 말씀드리겠습니다!