1주차 소셜로그인의 늪에서 헤어나온 후
2주차에 나를 고통 속으로 몰아넣었던 바로 그.... 크리에이터 센터 클래스 생성/편집 기능...!
우리 팀은 정규 클래스 생성이 아닌, 원포인트 클래스를 생성하는 기능을 구현했고, 너무 후회했다....
위 이미지는 클래스 101의 정규 클래스 생성 화면인데, 모든 항목을 작성해야 저장하고 다음으로 넘어갈 수 있다.
반면에, 위 이미지는 원포인트 클래스 작성 화면이다. 사진을 보면 알 수 있듯이, 하나의 항목만 저장해도 저장이 되고, 한 항목만 작성이 되어있는 채로 저장되어 이후에 로그아웃하고 재로그인해도 계속 편집할 수 있는 편리한 기능이다.
그러나 나는 조금 고통스러웠다.
개발자 도구의 네트워크 탭에서 <저장하기>를 눌렀을 때 발생하는 일들을 확인해보았고, 클래스 101은 graphQL을 사용한다는 것을 알 수 있었다. graphQL을 사용하면 이렇게 하나의 API로도 한 항목씩 개별적으로 저장하는 것이 가능하다.
그러나 나는 아직 graphQL을 공부하지 않았기 때문에, 다른 방법을 찾아보아야 했고, 일단은 모든 저장하는 기능을 각각의 API로 만들었다.
import json, boto3, uuid
from django.http import JsonResponse
from django.views import View
from django.db import transaction
from core.utils import Authorize
from products.models import *
from users.models import *
from quest101.settings import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_STORAGE_BUCKET_NAME, AWS_REGION
class MyCourse(View):
@Authorize
def get(self,request):
print(request)
if not User.objects.get(id=request.user.id).is_creator:
return JsonResponse({"message": "INVALID_CREATOR"}, status = 401)
courses = Course.objects.filter(user=request.user)
data = [{
'name' : course.name,
'thumbnail_image_url' : course.thumbnail_image_url,
} for course in courses]
return JsonResponse({"Course_list" : data}, status=200)
@Authorize
def delete(self, request):
user = request.user
data = json.loads(request.body)
course = Course.objects.get(id = data['course_id'], user = user)
if not Course.objects.filter(id = data['course_id'], user = user).exists():
return JsonResponse({"message" : "NOT_EXIST"}, status=400)
course.delete()
return JsonResponse({"message" : "DELETE_SUCCESS"}, status=204)
class NewCourseCreate(View):
@Authorize
def post(self,request):
user = User.objects.get(id=request.user.id)
with transaction.atomic():
Course.objects.create(user = request.user, is_course_created = False)
if not user.is_creator:
user.is_creator = True
user.save()
return JsonResponse({"message": "SUCCESS"}, status = 201)
class CourseCreate(View):
@Authorize
def get(self,request, course_id):
course = Course.objects.get(id=course_id)
user = User.objects.get(id=request.user.id)
social_accounts = SocialAccount.objects.filter(user=request.user)
if not User.objects.get(id=request.user.id).is_creator:
return JsonResponse({"message": "INVALID_CREATOR"}, status = 401)
course_create_info = {
'name' : course.name,
'thumbnail_image_url' : course.thumbnail_image_url,
'description' : course.description,
'user' : course.user.id,
'sub_category' : course.sub_category,
'level' : course.level,
'is_course_created' : False,
'user_name' : user.name,
'user_profile_image' : user.profile_image,
'user_phone_number' : user.phone_number,
'user_description' : user.description,
'social_account' : [{'channel' :social_account.channel,
'url' : social_account.url}
for social_account in social_accounts]
}
return JsonResponse({"course_create_info" : course_create_info}, status=200)
@Authorize
def patch(self,request, course_id):
course = Course.objects.get(id=course_id)
course.is_course_created = True
course.save()
return JsonResponse({"message" : "SUCCESS"}, status=201)
class CourseNameCreate(View):
@Authorize
def patch(self,request):
try:
data = json.loads(request.body)
course = Course.objects.get(id=data['course_id'])
course.name = data['name']
course.save()
return JsonResponse({"message" : "SUCCESS"}, status=201)
except KeyError:
return JsonResponse({"message" : "KEY_ERROR"}, status=400)
class CategoryCreate(View):
@Authorize
def patch(self,request):
try:
data = json.loads(request.body)
course = Course.objects.get(id=data['course_id'])
course.subcategory = SubCategory.objects.get(name=data['sub_category'])
course.save()
return JsonResponse({"message" : "SUCCESS"}, status=201)
except KeyError:
return JsonResponse({"message" : "KEY_ERROR"}, status=400)
class LevelCreate(View):
@Authorize
def patch(self,request):
try:
data = json.loads(request.body)
course = Course.objects.get(id=data['course_id'])
course.level = Level.objects.get(level=data['level'])
course.save()
return JsonResponse({"message" : "SUCCESS"}, status=201)
except KeyError:
return JsonResponse({"message" : "KEY_ERROR"}, status=400)
class CourseDetailCreate(View):
@Authorize
def patch(self,request):
try:
data = json.loads(request.body)
course = Course.objects.get(id=data['course_id'])
course.description = data['description']
course.save()
return JsonResponse({"message" : "SUCCESS"}, status=201)
except KeyError:
return JsonResponse({"message" : "KEY_ERROR"}, status=400)
def s3_auth():
s3 = boto3.client(
's3',
aws_access_key_id = AWS_ACCESS_KEY_ID,
aws_secret_access_key = AWS_SECRET_ACCESS_KEY
)
return s3
def put_objects(s3,image):
unique_key = str(uuid.uuid4())
s3.put_object(
Bucket = AWS_STORAGE_BUCKET_NAME,
Key = unique_key,
Body = image.file.read(),
ContentType = image.content_type
)
return unique_key
class CreateCourseImage(View):
@Authorize
def post(self,request):
try:
s3 = s3_auth()
thumbnail_image = request.FILES.__getitem__('thumbnail_image')
detail_images = request.FILES.getlist('detail_image')
unique_key = put_objects(s3,thumbnail_image)
course = Course.objects.create(
user = request.user,
thumbnail_image_url = '%s.s3.%s.amazonaws.com/%s' % (AWS_STORAGE_BUCKET_NAME, AWS_REGION, unique_key),
is_course_created = False
)
for detail_image in detail_images:
unique_key = put_objects(s3, detail_image)
Media.objects.create(
url = '%s.s3.%s.amazonaws.com/%s' % (AWS_STORAGE_BUCKET_NAME, AWS_REGION, unique_key),
type = 'image',
course = course
)
return JsonResponse({"MESSAGE":"SUCCESS"},status=201)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"},status=400)
class CreatorImage(View):
@Authorize
def patch(self,request):
try:
data = json.loads(request.body)
user = User.objects.get(id=request.user.id)
user.profile_image = data['profile_image']
user.save()
return JsonResponse({"MESSAGE":"SUCCESS"},status=201)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"},status=400)
class CreatorName(View):
@Authorize
def patch(self,request):
try:
data = json.loads(request.body)
user = User.objects.get(id=request.user.id)
user.name = data['name']
user.save()
return JsonResponse({"MESSAGE":"SUCCESS"},status=201)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"},status=400)
class CreatorDetail(View):
@Authorize
def patch(self,request):
try:
data = json.loads(request.body)
user = User.objects.get(id=request.user.id)
user.description = data['description']
user.save()
return JsonResponse({"MESSAGE":"SUCCESS"},status=201)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"},status=400)
class CreatorPhoneNumber(View):
@Authorize
def patch(self,request):
try:
data = json.loads(request.body)
user = User.objects.get(id=request.user.id)
user.phone_number = data['phone_number']
user.save()
return JsonResponse({"MESSAGE":"SUCCESS"},status=201)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"},status=400)
class SocialAccountCreate(View):
@Authorize
def post(self,request):
try:
data = json.loads(request.body)
if not SocialAccount.objects.filter(user=request.user, channel=data['channel']).exists():
SocialAccount.objects.create(
channel = data['channel'],
url = data['url'],
user = request.user
)
else:
social = SocialAccount.objects.get(channel = data['channel'], user = request.user)
social.url = data['url']
social.save()
return JsonResponse({"MESSAGE":"SUCCESS"},status=201)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"},status=400)
from django.urls import path
from .views import *
urlpatterns = [
path('/mycourse', MyCourse.as_view()),
path('/new', NewCourseCreate.as_view()),
path('/<int:course_id>', CourseCreate.as_view()),
path('/coursename', CourseNameCreate.as_view()),
path('/category', CategoryCreate.as_view()),
path('/level', LevelCreate.as_view()),
path('/coursedetail', CourseDetailCreate.as_view()),
path('/createcourseimage', CreateCourseImage.as_view()),
path('/creatorimage', CreatorImage.as_view()),
path('/creatorname', CreatorName.as_view()),
path('/creatordetail', CreatorDetail.as_view()),
path('/creatorphonenumber', CreatorPhoneNumber.as_view()),
path('/socialaccount', SocialAccountCreate.as_view())
]
일단 이렇게 작성을 해보았지만 역시 이렇게 해서는 안된다는 생각이 들었다.
리뷰를 받고 1차 수정된 API는 다음과 같다.
urlpatterns = [
path('', CoursesView.as_view()),
path('/<int:course_id>', CourseView.as_view()),
]
class ImageHandler:
def __init__(self, client, bucket, region):
self.client = client
self.bucket = bucket
self.region = region
def upload_file(self, file):
unique_key = str(uuid.uuid4())
s3.put_object(
Bucket = slef.bucket,
Key = unique_key,
Body = file.file.read(),
ContentType = file.content_type
)
return '%s.s3.%s.amazonaws.com/%s' % (self.bucket, self.region, unique_key)
def upload_files(self, files):
return [self.upload_file(file) for file in files]
boto3_client = boto3.client(
's3',
aws_access_key_id = AWS_ACCESS_KEY_ID,
aws_secret_access_key = AWS_SECRET_ACCESS_KEY
)
image_handler = ImageHandler(boto3_client, AWS_STORAGE_BUCKET, AWS_REGION)
class CoursesView(View):
@login_required
def get(self, request):
courses = user.course_set.all()
results = [{
'name' : course.name,
'thumbnail_image_url' : course.thumbnail_image_url,
'description' : course.description,
'user' : course.user.id,
'sub_category' : course.sub_category,
'level' : course.level,
'is_course_created' : False,
'user_name' : user.name,
'user_profile_image' : user.profile_image,
'user_phone_number' : user.phone_number,
'user_description' : user.description,
'social_account' : [{
'channel' :social_account.channel,
'url' : social_account.url
} for social_account in course.user.social_accounts_set.all()]
} for course in courses]
return JsonResponse({"results" : results}, status=200)
def put(self, request, course_id):
data = json.loads(request.body)
user = request.user
course = Course.objects.get(id=course_id, user_id=user.id)
course.name = data["name"]
course.description = data['description']
course.subcategory = SubCategory.objects.get(name=data['sub_category'])
course.level = Level.objects.get(level=data['level'])
user.profile_image = data['profile_image']
user.name = data['name']
user.description = data['description']
user.phone_number = data['phone_number']
social_account, created = SocialAccount.objects.get_or_create(channel = data['channel'], user = request.user)
social_account.url = data['url']
course.save()
user.save()
@login_required
@transaction.atomic()
def post(self,request):
course = Course.objects.create(user = request.user, is_course_created = False)
if not user.is_creator:
user.is_creator = True
user.save()
return JsonResponse({"courseId": course.id}, status = 201)
class CourseView(View):
@login_required
def get(self, request, course_id):
course = Course.objects.get(id = course_id, user_id = request.user.id)
result = {
'name' : course.name,
'thumbnail_image_url' : course.thumbnail_image_url,
'description' : course.description,
'user' : course.user.id,
'sub_category' : course.sub_category,
'level' : course.level,
'is_course_created' : False,
'user_name' : course.user.name,
'user_profile_image' : course.user.profile_image,
'user_phone_number' : course.user.phone_number,
'user_description' : course.user.description,
'social_account' : [{
'channel' : social_account.channel,
'url' : social_account.url
} for social_account in course.user.social_accounts_set.all()]
}
return JsonResponse({"results" : results}, status=200)
@transaction.atomic()
def post(self,request):
try:
course = Course.objects.create(
user = request.user,
thumbnail_image_url = image_handler.upload_file(request.FILES.__getitem__('thumbnail_image'))
)
urls = image_handler.upload_files(request.FILES.getlist('detail_image'))
Media.objects.bulk_create([Media(type = 'image', course = course, url = url) for url in urls])
return JsonResponse({"MESSAGE":"SUCCESS"},status=201)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"},status=400)
어떤 정보가 저장이 되든 모든 정보를 매번 다 함께 업데이트 해주는 방식으로 클래스 정보 저장 API를 하나로 통합했고, 엔드포인트의 수도 2개로 줄어들었다.
이렇게 수정이 된 API로 프론트엔드와 이틀에 걸쳐 통신을 시도하며 코드 수정에 수정을 거듭했다.
수정을 거듭하다가 프론트엔드 팀원분께서 갑자기 모든 데이터를 FormData로 보내주신다고 하셨다.
기존의 API는 이미지를 업로드하는 기능만 FormData로 받도록 되어있었기 때문에, 모든 데이터를 FormData 형식으로 받도록 수정을 했다.
프론트와 통신 후 수정된 API는 아래와 같다.
import json, boto3, uuid
from enum import Enum
from django.http import JsonResponse
from django.views import View
from django.db import transaction
from requests.sessions import Request
from core.utils import Authorize
from products.models import Course, CourseStat, CourseStatus, SubCategory, Level, Media
from users.models import User, SocialAccount
from quest101.settings import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_STORAGE_BUCKET_NAME, AWS_REGION
class CourseStatusEnum(Enum):
PENDING = "클래스 준비"
RUNNING = "클래스 운영"
REVIEWING = "클래스 검토중"
class ImageHandler:
def __init__(self, client, bucket, region):
self.client = client
self.bucket = bucket
self.region = region
def upload_file(self, file):
unique_key = str(uuid.uuid4())
self.client.put_object(
Bucket = self.bucket,
Key = unique_key,
Body = file.file.read(),
ContentType = file.content_type
)
return '%s.s3.%s.amazonaws.com/%s' % (self.bucket, self.region, unique_key)
def upload_files(self, files):
return [self.upload_file(file) for file in files]
boto3_client = boto3.client(
's3',
aws_access_key_id = AWS_ACCESS_KEY_ID,
aws_secret_access_key = AWS_SECRET_ACCESS_KEY
)
image_handler = ImageHandler(boto3_client, AWS_STORAGE_BUCKET_NAME, AWS_REGION)
class CoursesView(View):
@Authorize
def get(self, request):
courses = request.user.course_set.all()
results = [{
'id' : course.id,
'name' : course.name if course.name != None else "제목을 생성해주세요",
'thumbnail_image_url' : course.thumbnail_image_url,
'detail_media' : [{'type' : image.type,
'url':image.url} for image in course.media_set.all()],
'description' : course.description,
'sub_category' : course.sub_category.name if course.sub_category != None else None,
'category' : course.sub_category.category.name
if course.sub_category != None
else None,
"healthStat" : 0 if not CourseStat.objects.filter(course=course, stat__name="체력").exists() else CourseStat.objects.get(course=course, stat__name="체력").score,
"intellectStat" : 0 if not CourseStat.objects.filter(course=course, stat__name="지능").exists() else CourseStat.objects.get(course=course, stat__name="지능").score,
"charmStat" : 0 if not CourseStat.objects.filter(course=course, stat__name="매력").exists() else CourseStat.objects.get(course=course, stat__name="매력").score,
"artStat" : 0 if not CourseStat.objects.filter(course=course, stat__name="예술").exists() else CourseStat.objects.get(course=course, stat__name="예술").score,
'status' : course.course_status.status,
'level' : course.level.level if course.level != None else None ,
'user_name' : request.user.name,
'user_profile_image' : request.user.profile_image,
'user_phone_number' : request.user.phone_number,
'user_description' : request.user.description,
'social_account' : [{
'channel' :social_account.channel,
'url' : social_account.url
} for social_account in course.user.socialaccount_set.all()]
} for course in courses]
return JsonResponse({"results" : results}, status=200)
@Authorize
def post(self, request):
try:
formdata = request.POST
print(formdata)
Course.objects.filter(id=formdata['class_id'], user=request.user).update(**course_dict)
user = request.user
course.name = formdata['course_name']
course.description = formdata['course_description']
course.sub_category = SubCategory.objects.get(name=formdata['sub_category']) if formdata['sub_category'] != 'null' else None
course.level = Level.objects.get(level=formdata['level']) if formdata['level'] != "0" else None
course.coursestatus = formdata['status']
user.profile_image = User.objects.get(id=request.user.id).profile_image
user.name = formdata['user_name']
user.description = formdata['user_description']
user.phone_number = formdata['user_phone_number']
social_account, created = SocialAccount.objects.get_or_create(channel = formdata['channel'], user = request.user)
social_account.url = formdata['url']
course.save()
user.save()
stats_set = {
"체력" : int(formdata.get('healthStat', 0)),
"지능" : int(formdata.get('intellectStat', 0)),
"매력" : int(formdata.get('charmStat', 0)),
"예술" : int(formdata.get('artStat', 0))
}
[course.coursestat_set.filter(stat__name=key) for key in stats_set]
[course.coursestat_set.filter(stat__name=key).update(score=stats_set[key]) for key in stats_set]
return JsonResponse({"message": "SUCCESS"}, status = 201)
except Course.DoesNotExist:
return JsonResponse({"message": "INVALID_COURSE"}, status = 404)
@Authorize
@transaction.atomic()
def delete(self, request):
try:
user = request.user
data = json.loads(request.body)
course = Course.objects.get(id = data['course_id'], user = user)
course_stats = CourseStat.objects.filter(course=course)
if not Course.objects.filter(id = data['course_id'], user = user).exists():
return JsonResponse({"message" : "NOT_EXIST"}, status=400)
course.delete()
[course_stat.delete() for course_stat in course_stats]
return JsonResponse({"message" : "DELETE_SUCCESS"}, status=200)
except Course.DoesNotExist:
return JsonResponse({"message" : "INVALID_COURSE"}, status=404)
@Authorize
def put(self,request):
course = Course.objects.create(user = request.user,
course_status=CourseStatus.objects.get(status=CourseStatusEnum.PENDING.value))
if not request.user.is_creator:
request.user.is_creator = True
request.user.save()
return JsonResponse({"courseId": course.id}, status = 201)
class CourseView(View):
@Authorize
def get(self, request, course_id):
course = Course.objects.get(id = course_id, user_id = request.user.id)
results = {
'name' : course.name if course.name != None else "제목을 생성해주세요",
'thumbnail_image_url' : course.thumbnail_image_url,
'detail_media' : [{'type' : image.type, 'url':image.url} for image in course.media_set.all()],
'description' : course.description,
'sub_category' : course.sub_category.name if course.sub_category != None else None,
'category' : course.sub_category.category.name if course.sub_category != None else None,
'level' : course.level.level if course.level != None else None ,
'is_course_created' : False,
'user_name' : course.user.name,
'user_profile_image' : course.user.profile_image,
'user_phone_number' : course.user.phone_number,
'user_description' : course.user.description,
"healthStat" : 0 if not CourseStat.objects.filter(course=course, stat__name="체력").exists() else CourseStat.objects.get(course=course, stat__name="체력").score,
"intellectStat" : 0 if not CourseStat.objects.filter(course=course, stat__name="지능").exists() else CourseStat.objects.get(course=course, stat__name="지능").score,
"charmStat" : 0 if not CourseStat.objects.filter(course=course, stat__name="매력").exists() else CourseStat.objects.get(course=course, stat__name="매력").score,
"artStat" : 0 if not CourseStat.objects.filter(course=course, stat__name="예술").exists() else CourseStat.objects.get(course=course, stat__name="예술").score,
'social_account' : [{
'channel' : social_account.channel,
'url' : social_account.url
} for social_account in course.user.socialaccount_set.all()]
}
return JsonResponse({"results" : results}, status=200)
@Authorize
@transaction.atomic()
def post(self,request, course_id):
try:
course = Course.objects.get(user=request.user, id=course_id)
urls = image_handler.upload_files(request.FILES.getlist('detail_image_url'))
course.thumbnail_image_url = image_handler.upload_file(request.FILES.__getitem__('thumbnail_image_url'))
course.save()
Media.objects.bulk_create([Media(type = 'image', course = course, url = url) for url in urls])
return JsonResponse({"MESSAGE":"SUCCESS"},status=201)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"},status=400)
프론트와 통신을 하며 일단 에러없이 기능이 정상적으로 작동되는 API를 구현하려다보니 코드가 다소 지저분해졌지만 발표 전 리팩토링을 통해 코드를 다듬고 마무리하려고 했다.
그러나 아주 큰 복병이 있었다.
발표 전날 오후 4시, 최종 머지를 2시간 앞두고 프론트와 최종 통신을 시도했는데, 모든 정보를 다 한번씩 저장하고 나가기를 한 다음 다시 내 클래스로 들어오면 한 가지 정보만 저장이 되어있었다.
매번 처음부터 끝까지 모든 정보를 업데이트하는 방식의 백엔드API와
매번 처음부터 끝까지 모든 정보를 보내주는 프론트엔드의 데이터 형태에 문제가 있었다.
일단 프론트엔드에서는, 사용자가 한 항목의 저장 버튼을 누를 때마다 해당 페이지에 있는 모든 정보를 백엔드로 보내주었으나, 사용자가 방금 저장한 그 해당 항목에 대한 정보를 제외한 다른 정보는 모두 null값으로 백엔드에게 보내주었다.
그렇게 되면, 이전에 저장되어있던 항목의 정보도, 다른 정보를 저장하게 될 때는 다시 null값으로 백엔드에게 전송이 되기 때문에 매번 한 가지 정보만을 저장할 수 밖에 없었다.
이 방법을 해결하기 위해서는, 프론트엔드에서 그 화면에서 저장되어 있던 정보를 재저장하여 저장되어있는 모든 항목의 정보를 제외한 값만 null값으로 백엔드에 보내주어야 했다.
그런데 이 로직을 프론트가 전부 다 수정하기에는 시간이 너무 부족했고,
사실상 지금의 백엔드 API도, 매번 한번에 모든 정보를 업데이트하는 것은 비효율적인 방법이라는 멘토님의 피드백이 있어 백엔드 API도 다시 수정해보기로 했다.
[23/Dec/2021 16:54:35] "OPTIONS /creators/60 HTTP/1.1" 200 0
<MultiValueDict: {'thumbnail_image_url': [<InMemoryUploadedFile: sumaid-pal-singh-bakshi-74_dhkCgpWI-unsplash.jpg (image/jpeg)>], 'detail_image_url': [<InMemoryUploadedFile: warren-umoh-YmTIxQbQo4I-unsplash.jpg (image/jpeg)>, <InMemoryUploadedFile: ferhat-deniz-fors-YOCDD-D4oOM-unsplash.jpg (image/jpeg)>]}>
[23/Dec/2021 16:54:49] "POST /creators/60 HTTP/1.1" 201 22
<QueryDict: {'category': ['공예'], 'sub_category': ['실 공예'], 'undefined': ['undefined'], 'class_id': ['60']}>
일단 프론트엔드에서는, 모든 정보를 백엔드로 다 주는 대신, 위와 같이 필요한 정보 (사용자가 저장하기를 누른 항목의 정보)만 백엔드로 보내주는 형식으로 수정이 되었다.
그런데 백엔드에서는 이 문제를 해결하려면 아예 데이터베이스의 컬럼명부터 다 뒤집어엎어야하는 상황이었다.
그래서 일단은 시간이 촉박했기 때문에, 내 크리에이터 센터 페이지의 API들은 git에 merge를 시키지 않고 독립적으로 서버를 연결해서 진행하기로 했다... 눈물...
@Authorize
def post(self, request):
try:
formdata = request.POST
course_dict = {}
user_dict = {}
for key, value in formdata.items():
# print(key, value)
if 'course' in key:
course_dict[key] = value
if 'user' in key:
user_dict[key] = value
Course.objects.filter(id=class_dict['class_id'], user=request.user).update(**course_dict)
User.objects.filter(id=request.user.id).update(**user_dict)
일단은 이런 방식으로 수정해서 시연 영상도 찍고 발표도 마쳤지만,
백엔드 다른 팀원분들의 데이터베이스와 내 데이터베이스의 컬럼명이 모두 다르고, (저 방식을 위해 데이터베이스 컬럼명을 다 뒤엎었다,,,) 여전히 최적화된 코드는 아닌듯싶어 곧 리팩토링을 진행할 예정이다!
크리에이터 센터 API들을 작성하면서, s3 이미지 업로드 하는 방법과 FormData를 받아 처리하는 방법을 배웠다. 나중에 시간이 된다면 그 부분도 따로 블로깅을 해보고 싶다.