Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Django의 배신(주니어 개발자의 Django 삽질기)

13.239 Aufrufe

Veröffentlicht am

Dev Django Korea 2018에서 발표한 자료입니다.
주니어 개발자가 Django로 개발하며 삽질한 내용을 담았습니다.

Veröffentlicht in: Software
  • Login to see the comments

Django의 배신(주니어 개발자의 Django 삽질기)

  1. 1. Dev Django 2018 KOREA START Django의 배신 주니어 개발자의 Django 삽질기 김은향
  2. 2. DJANGO의 배신 주니어 개발자의 Django 삽질기 또 삽질하냐 각본, 연출: 김은향
  3. 3. 1. 장고와 나 DevDjango Korea목차 목차 2. ORM뒤에 숨겨진 블랙홀 3. 트랜잭션 적용하다 celery에게 뒷통수 맞다! 4. 헌집 줄게, 새집 다오 5. 삽질기는 계속된다
  4. 4. 제1장. 장고와 나
  5. 5. 1. 장고와 나 Who am I? 개발한지 이제 1년 된 뽀시래기 주니어 개발자 스스로 개발자라는게 아직도 신기함 만나는 개발자 분들마다 존경스러워요..! 내가 DevDjango에서 발표라니..!(쏘리질러!) DevDjango Korea
  6. 6. 개발은.. 어쩌다 시작하셨나요? 디자인 하다가 화나서!!!!!!!!!(분노) 1. 장고와 나 DevDjango Korea
  7. 7. 이런..어쩌다 그런.. 개발자님.. 1픽셀만 옮겨주세요.. 아..아니 그 테두리좀 없.. 익스플로러에서 왜 이렇게 보여요.. 아..아니..그게아니라.. 쭈글쭈글 1. 장고와 나1. 장고와 나 DevDjango Korea
  8. 8. 그래서 어떤 선택을..? 나도 한 때 (프포자)공대생이었는데!!!! 까짓거 내가 개발해보겠어!!! 1. 장고와 나 DevDjango Korea
  9. 9. 산 넘어 더 큰 산 Dd Java산 JSP산 Servlet산 Spring산 아..나는 뼛속까지 문과생..프포자.. 게시판 하나 만드는 게 이렇게 힘들다니... 1. 장고와 나 DevDjango Korea
  10. 10. 내 눈!!!!!! 1. 장고와 나 DevDjango Korea
  11. 11. JAVA가 문제가 아니에요.. 코드만 보면 급격히 떨어지는 내 인내심이 문제 1. 장고와 나 DevDjango Korea
  12. 12. 그러다 우연히 만나게 된 그 이름하여..! 1. 장고와 나 DevDjango Korea
  13. 13. 엄청나게 친절한 django girls 튜토리얼 이거해본다고 뭐가 나오겠어.. 따라하긴 쉽네..한글로 옴청 친절하고.. 1. 장고와 나 DevDjango Korea
  14. 14. !!! 뭐지.. 이 간지나는..블로그는..? 세상에나 내가 지금 블로그 만든겨?! 1. 장고와 나 DevDjango Korea
  15. 15. Django, 너는 내 운명 쉽고 재밌는데 게다가 python..! 자, Django 개발자가 되어 봅시다! 1. 장고와 나 DevDjango Korea
  16. 16. Django하려 하면 이런 일들이?!1 운 간절함 스터디에 합격하셨습니다! 1. 장고와 나 DevDjango Korea
  17. 17. Django하려 하면 이런 일들이?!2 운 인복 Django 프로젝트 팀에 합류했습니다!! 1. 장고와 나 DevDjango Korea
  18. 18. Django하려 하면 이런 일들이?!3 Django 인턴개발자로 취업했습니다!! Django 원주민 팀장님을 만났습니다!!! 운 인복 1. 장고와 나 장고장고 DevDjango Korea
  19. 19. 그리하여... 주니어 백엔드 개발자가 되었습니다! 근자감 살어리 살어리랏다 장고랑 살어리랏다~~ 1. 장고와 나 DevDjango Korea
  20. 20. 시간은 흘러흘러~ 약 1년 후... 1. 장고와 나 DevDjango Korea
  21. 21. Django에게 배신당하다..! Django… 누가 쉽댔냐.... 과거의 나..원망한다..1. 장고와 나 DevDjango Korea
  22. 22. Django로 개발을 하면 할수록.. 나는 정말 근자감 천재였구나..! 컴퓨터도 몰라.. 파이썬도 몰라.. 네트워크도 몰라.. 그런데 Django도 몰라..! 1. 장고와 나 DevDjango Korea
  23. 23. 그래서 제 사연은요.. 정말 (쉽다고, 재밌다고) 철썩같이 믿었는데... 세 번이나 배신당했어요... 먼저 ORM..그 녀석이..하... … 1. 장고와 나 ps. 이제 막 Django하시는 개발자분들도 배신당하지 말아요 함께!! DevDjango Korea
  24. 24. 제2장. ORM뒤에 숨겨진 블랙홀!
  25. 25. 2. ORM뒤에 숨겨진 블랙홀 Django 개발자가 된 lv1 야도란 ORM을 만나서 진화-@@@! 쿼리를 낚다가.. DevDjango Korea
  26. 26. SQL 그게 뭐야? :) SELECT FROM JOIN GROUB BY WHEN ON WHERE INSERT 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  27. 27. 얍! 얍! 얍! OH! ORM 찬양! OH! 포스트 다 가져오기 포스트 1개 가져오기 내가 쓴 포스트 가져오기 Post.objects.all() Post.objects.get(id=1) Post.objects.filter(username=“hyang”) 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  28. 28. 실제 상황 1 USER: 2. ORM뒤에 숨겨진 블랙홀 저희 머신러닝 모델의 여러 버전들마다 실행된 것들에서 8월 1일에서 8월 20일까지 실행된 것 들 중 error없이 실행된 active한 것들만 모아서 응답시간의 평균 값, 최대값 그리고 모델에 설정한 스레숄드 값보다 빠르게 응답한 개수를 보여주는 화려한 대시보드 만들어주세요 ^^ “ “ DevDjango Korea
  29. 29. ????????????? 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  30. 30. SQL 강제 공부 돌입 SELECT FROM JOIN GROUB BY WHEN ON WHERE 얘네 가져올꺼야 이럴 때만 얘네들만 묶어서 저기서부터 가져와서 교집합 차집합 합집합.. 이런 것들만 2. ORM뒤에 숨겨진 블랙홀 잘못했어요.. 살려주세요.. DevDjango Korea
  31. 31. 드디어 쿼리문 완성!! SELECT AVG(run.response_time) as "avg_response_time" , Max(run.response_time) as "max_response_time" , SUM(1) as num_runs , SUM(case when run.response_time > ml_model.slow_run_threshold_in_milliseconds then 1 else 0 end) as num_fast_runs FROM run JOIN version on version.id = run.version_id JOIN ml_model ml_model on run. ml_model_id = ml_model.id WHERE run.requested_at >= '2018-08-01'::timestamp with time zone AND run.requested_at < '2018-08-20'::timestamp with time zone AND run.is_active = True AND version.id in (versions_ids) 2. ORM뒤에 숨겨진 블랙홀 짜잔! DevDjango Korea
  32. 32. 쿼리도 쓰고 대단해!! 2. ORM뒤에 숨겨진 블랙홀 근데.. 어떻게 썼더라.. DevDjango Korea
  33. 33. 머리에 쥐나요... 도와줘요.. ORM... 착하게 개발할게요... 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  34. 34. ORM 응급실 ORM에대한 희망을 잃지 말아요~ 도큐먼트에는 다 있으니까~ 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  35. 35. 필요한 데이터 뽑기! 2. ORM뒤에 숨겨진 블랙홀 저희 머신러닝 모델의 여러 버전마다 실행된 것들에서 8월 1일에서 8월 20일까지 실행된 것 들 중 error없이 실행된 active한 것들만 모아서 응답시간의 평균 값, 최대값 그리고 저희 모델에 설정한 스레숄드 값보다 빠르게 응답한 개수를 보여주는 화려한 대쉬보드 만들어주세요 ^^ 저 조건들 다 filter에 넣고 한 번에 가져올 수 있으면 좋겠다.. “ “ DevDjango Korea
  36. 36. 가져올 수 있음!! 2. ORM뒤에 숨겨진 블랙홀 run_qs = Run.objects.filter( version__id__in=version_ids, ml_model__id__in=version_ids, is_active=True, requested_at__range=(start_date, end_date) ) Filter에 ForeignKey도 조건으로 막 넣고! list에 포함되는지는 in! 범위는 range! DevDjango Korea
  37. 37. 그렇게 모은 쿼리셋! 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  38. 38. 표로 보면 이렇게 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  39. 39. 평균? 최대값? 2. ORM뒤에 숨겨진 블랙홀 저희 머신러닝 모델의 여러 버전마다 실행된 것들에서 8월 1일에서 8월 20일까지 실행된 것 들 중 error없이 실행된 active한 것들만 모아서 응답시간의 평균 값, 최대값 그리고 저희 모델에 설정한 스레숄드 값보다 빠르게 응답한 개수를 보여주는 화려한 대쉬보드 만들어주세요 ^^ “ “ DevDjango Korea
  40. 40. 평균? 최대값? response_time 열만 모아서 계산하면 되는데.. 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  41. 41. Aggregation!!! Aggregate를 사용해서 한 컬럼에 전체에 대해 계산할 수 있어요! 2. ORM뒤에 숨겨진 블랙홀 run_qs.aggregate(Avg('response_time'), Max('response_time')) DevDjango Korea
  42. 42. Level up!!! run.response_time > ml_model.threshold인 run의 총개수??? 2. ORM뒤에 숨겨진 블랙홀 저희 머신러닝 모델의 여러 버전마다 실행된 것들에서 8월 1일에서 8월 20일까지 실행된 것 들 중 error없이 실행된 active한 것들만 모아서 응답시간의 평균 값, 최대값 그리고 저희 모델에 설정한 스레숄드 값보다 빠르게 응답한 개수를 보여주는 화려한 대쉬보드 만들어주세요 ^^ “ “ DevDjango Korea
  43. 43. 조건문은?? Case와 When을 쓰기! 2. ORM뒤에 숨겨진 블랙홀 fast_runs=Count( Case( When(response_time__gt=F('ml_model_threshold'), then=1), output_field=IntegerField(), ) ) DevDjango Korea
  44. 44. Annotation!!! 특정 기준으로 값을 묶어 새로운 컬럼만들때는 annotation! 2. ORM뒤에 숨겨진 블랙홀 run_qs.annotate( fast_runs=Count( Case( When(response_time__gt=F('ml_model_threshold'), then=1), output_field=IntegerField(), ) ) ).aggregate(Sum('fast_runs')) DevDjango Korea
  45. 45. Aggregation!!! 그렇게 만든 컬럼을 다시 합쳐서 계산하면 끝! 2. ORM뒤에 숨겨진 블랙홀 run_qs.annotate( fast_runs=Count( Case( When(response_time__gt=F('ml_model_threshold'), then=1), output_field=IntegerField(), ) ) ).aggregate(Sum('fast_runs')) DevDjango Korea
  46. 46. 2. ORM뒤에 숨겨진 블랙홀 해냈다!!! DevDjango Korea SQL ORM
  47. 47. SQL 이렇게 커넥션도 직접 쓰고..막 쿼리쓰면서 교집합 합집합하고 난리났는데.. 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  48. 48. ORM run_qs = Run.objects.filter( version__id__in=version_ids, ml_model__id__in=version_ids, is_active=True, requested_at__range=(start_date, end_date) ) run_qs.aggregate(Avg('response_time'), Max('response_time')) run_qs.mannotate( fast_runs=Count(Case( When(response_time__gt=F(‘ml_model__threshold'), then=1), output_field=IntegerField(), )) ).aggregate(Sum('fast_runs')) ORM으로 할 수 있다니 대단해!!! 이제 ORM으로만 돌려봐야지!!! 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  49. 49. 실전 상황 2 Run.objects.all() 1초.. 2초.. 345초 .. … 서버가 터졌습니다!!!!! 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  50. 50. … 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  51. 51. ORM 쉽다며... 믿었던 ORM으로 만든 지뢰밭.. 어디서 터지고 왜 터지는지 이해할 수가 없어요.. 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  52. 52. ORM의 비밀..?! 세상에나, Run.objects.all()에서 터진게 아니었다? 여기서 터졌다고 해요 2. ORM뒤에 숨겨진 블랙홀 runs = Run.objects.all() for run in runs: print(run.name) WHY?!!!! DevDjango Korea
  53. 53. 왜??? 사실 ORM은 매우 매우 게을러서(?) ORM아 그렇게 놀기만 하다가 서버가 터지면 어쩌려고 그러니 2. ORM뒤에 숨겨진 블랙홀 공부나해 ORM아 그렇게 놀기만 하다가 서버가 터지면 어쩌려고 그러니 DevDjango Korea
  54. 54. 왜??? 진짜진짜 필요할 때까지는 DB에서 레코드를 가져오지 않는다고 해요. … 무념무상 2. ORM뒤에 숨겨진 블랙홀 runs = Run.objects.all() for run in runs: print(run.name) DevDjango Korea
  55. 55. 왜??? 가져오려면 이렇게 쿼리셋을 순회해야.. 더 이상 미룰수 없음.. 2. ORM뒤에 숨겨진 블랙홀 가져 올게runs = Run.objects.all() for run in runs: print(run.name) DevDjango Korea
  56. 56. 심지어 저장해둠 나중에 또 쓸까봐 한 번 가져오면 캐시로 저장해둔다고해요 runs = Run.objects.all() for run in runs: print(run.name) for run in runs: print(run.name) 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea 이미 있음
  57. 57. 그럼 실제로 쿼리를 어떻게 날리는걸까? Django-debug-toolbar를 실행시켜봅니다. 오오 어디서 쿼리를 어떻게 날렸는지 다 알려줘요!! 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  58. 58. 그런데..! 중복되는 쿼리가 굉장이 많다...! 코딩할 때 뭐든 중복은 안 좋은 거랬어..! 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  59. 59. 검색검색 쿼리 최적화하려면 데이터 set을 “필요한 만큼만” “웬만하면 한 번에!” 쏙쏙 가져와야한대요 인터넷에서 모은 꿀 팁들 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  60. 60. 먼저, 한 번에 가져오기! ForeignKey는 select_related! ManyToManyField는 prefatch_related! 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  61. 61. 예를들면, class Post(models.Model): author = models.ForeignKey(Author) tag = models.ManyToManyField(Tag) 2. ORM뒤에 숨겨진 블랙홀 author을 ForeinKey tag를 ManyToMany로가진 Post 모델!! DevDjango Korea
  62. 62. ForeignKey는 select_related! post = Post.objects.get(id=1) author = post.author 2. ORM뒤에 숨겨진 블랙홀 post가져올 때 한 번 DevDjango Korea
  63. 63. ForeignKey는 select_related! post = Post.objects.get(id=1) author = post.author 2. ORM뒤에 숨겨진 블랙홀 author가져올 때 한 번 이렇게 가져오면 DB에 두번이나 갔다와야함 정색 DevDjango Korea
  64. 64. 한 번에 가져오기! select_related로 한 번에 가져오기! 방긋 post = Post.objects.select_related('author').get(id=1) author = post.author author값도 같이 가져와! 2. ORM뒤에 숨겨진 블랙홀 굿굿 DevDjango Korea
  65. 65. ManyToManyField는 prefatch_related! posts = Post.objects.all() for post in posts: for tag in post.tag_set.all(): print(tag) 2. ORM뒤에 숨겨진 블랙홀 post 하나 돌 때마다 DevDjango Korea
  66. 66. ManyToManyField는 prefatch_related! posts = Post.objects.all() for post in posts: for tag in post.tag_set.all(): print(tag) 2. ORM뒤에 숨겨진 블랙홀 tag_set 가져오기..? posts 백개면 백번..천개면 천번.. … DevDjango Korea
  67. 67. posts = Post.objects.all().prfetch_related(‘tag_set') for post in posts: for tag in post.tag_set.all(): print(tag) 두 번으로 줄이기 감격 post다 가져온다음 tag_set도 다 가져와! post.objects.all() self.tag_set.all)() 2. ORM뒤에 숨겨진 블랙홀 최고! DevDjango Korea ManyToManyField는 prefatch_related!
  68. 68. 필요한 것만 가져오기! 아무 생각 없이 다가져오던 시절.. 메모리따위 고려하지 않겠어!! posts = Post.objects.all() for post in posts: print(post.name) 메모리 ㅠ ㅠ 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  69. 69. 필요한 것만 가져오기! 어떻게든 values, values_list로 줄여요… ORM 무서워.. posts = Post.objects.values('name') for post in posts: print(post.name) 2. ORM뒤에 숨겨진 블랙홀 메모리 ^ ^ DevDjango Korea
  70. 70. 있는지 없는지 확인하는데 posts 다 가져오면 낭비니까! posts = Post.objects.all() If posts: print(posts) 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea 필요한 것만 가져오기!
  71. 71. 전부 vs 하나 가져오기 posts = Post.objects.all() If posts.exists(): print(posts) 하나만 있는지 확인! 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea 필요한 것만 가져오기!
  72. 72. 꿀팁을 알았으니.. 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea 엄청나게 써먹을테야!! 지뢰밭 다 없애버리겠어!!
  73. 73. 그 밖에도 무긍무진한 ORM의 블랙홀 틈틈히 정복하기로합니다! 오 알 ㅇ ㅔ ㅁ 어려워.. 2. ORM뒤에 숨겨진 블랙홀 DevDjango Korea
  74. 74. 제3장. Transaction적용하다 celery에게 뒷통수 맞다!
  75. 75. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 ORM에게 호되게 당한 기분은? 역시 공부를 해야겠어요..! Django책은 디자인도 간지나군요!! DevDjango Korea
  76. 76. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 7장을 읽던 중.. Transaction???? DevDjango Korea
  77. 77. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 Transaction 감 잡았다! 요놈! 대충 감이 오는군 그니까 전부 없던 일로 쳐준다는거 아녀!! DevDjango Korea
  78. 78. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 0 아니면 1 쿼리 다 성공하던지, 다 없던 일로 하던지 쿼리 쿼리 쿼리 실패하면 원상복구 DevDjango Korea
  79. 79. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 두리번 두리번 어디 써먹을 데 없나요? 저 좋은거 득템했는데.. DevDjango Korea
  80. 80. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 발견!!! 저 create() 함수에서 부모객체, 자식객체 둘 다 생성하군! version 자식객체 생성! ml_model 부모객체 생성! def create(self, request, *args, **kwargs): ml_model = Mlmodel.objects.create( title=request.data.get('title', None) ) tasks.build(ml_model.id) version = Version.objects.create( parent=ml_model ) DevDjango Korea
  81. 81. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 상상중... 자식 생성 도중 실패하면? 그럼 이미 만들어진 부모 객체는?? DevDjango Korea version 자식객체 생성 실패! ml_model 부모객체 생성! def create(self, request, *args, **kwargs): ml_model = Mlmodel.objects.create( title=request.data.get('title', None) ) tasks.build(ml_model.id) version = Version.objects.create( parent=ml_model )
  82. 82. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 테스트 해보자!! 일만 아니면 왜이렇게 신이 날까 ~~ DevDjango Korea
  83. 83. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 테스트 작성중... 실패하면 부모객체=Mlmodel 남아있는거야!!! (실패하길 바라는 자) def test_create_version_failed(self): … self.assertEqual(Version.objects.count(), 0) self.assertEqual(Mlmodel.objects.count(), 0) DevDjango Korea
  84. 84. def test_create_version_failed(self): … self.assertEqual(Version.objects.count(), 0) self.assertEqual(Mlmodel.objects.count(), 0) 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 돌리고돌리고 통과! DevDjango Korea Version의 총 개수 == 0 !!
  85. 85. def test_create_version_failed(self): … self.assertEqual(Version.objects.count(), 0) self.assertEqual(Mlmodel.objects.count(), 0) 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 과연 결과는?! 실패! DevDjango Korea Mlmodel의 총 개수 == 1 !!
  86. 86. DjangoCon3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 테스트 성공하도록 바꿔보자! 함수 전체에 @transaction을 적용!! @transaction.atomic def create(self, request, *args, **kwargs): ml_model = Mlmodel.objects.create( title=request.data.get('title', None) ) tasks.build(ml_model.id) version = Version.objects.create( parent=ml_model )
  87. 87. def test_create_version_failed(self): … self.assertEqual(Version.objects.count(), 0) self.assertEqual(Mlmodel.objects.count(), 0) 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 테스트 통과! 통과! 통과! PASSED!!! DevDjango Korea
  88. 88. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 세상 사람들! 제가 문제를 찾아서해결했어요!!! 똑똑한 척. 유능한 척. 문제를 제기합니다 으쓱으쓱 DevDjango Korea
  89. 89. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 얼른 스테이징 환경에 적용해 봅시다! +++ @transaction.atomic CODE DevDjango Korea
  90. 90. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 create()! 얍! Version.objects.create() 1초 2초 345초... … DevDjango Korea
  91. 91. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 create()! 얍! Version.objects.create() 1초 2초 345초... … Fail!!!!!! DevDjango Korea
  92. 92. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 … /// … 과거의 나야 나대지말란말이야!! 나도 같이 트랜젝션시켜줘!! DevDjango Korea
  93. 93. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 일 벌리고 수습하기 어디서 터졌나요.. 제발 알려줘요 코딩신내림이라도.. DevDjango Korea @transaction.atomic def create(self, request, *args, **kwargs): ml_model = Mlmodel.objects.create( title=request.data.get('title', None) ) tasks.build(ml_model.id) version = Version.objects.create( parent=ml_model )
  94. 94. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 가만…?! 그렇구나!! celery 테스크가 있었지!! DevDjango Korea @transaction.atomic def create(self, request, *args, **kwargs): ml_model = Mlmodel.objects.create( title=request.data.get('title', None) ) tasks.build(ml_model.id) version = Version.objects.create( parent=ml_model )
  95. 95. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 flower를 보니까.. DevDjango Korea DoesNotExist???!!! ml_model이 없다??
  96. 96. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션이 어떻게 작동하길래? DevDjango Korea (반성) 이번엔 제대로 공부하고 수습해야겠어요.. @transaction.atomic def create(self, request, *args, **kwargs): ml_model = Mlmodel.objects.create( title=request.data.get('title', None) ) tasks.build(ml_model.id) version = Version.objects.create( parent=ml_model )
  97. 97. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하기 전에는.. Django ml_model = Mlmodel.objects.create() DB 성공! DevDjango Korea
  98. 98. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하기 전에는.. Django DB ml_model row commit! DevDjango Korea ml_model = Mlmodel.objects.create()
  99. 99. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하기 전에는.. Django DB version = Version.objects.create() 성공! DevDjango Korea ml_model row ml_model = Mlmodel.objects.create()
  100. 100. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하기 전에는.. Django DB Version row commit! DevDjango Korea version = Version.objects.create() ml_model row ml_model = Mlmodel.objects.create()
  101. 101. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하면.. Django DB DevDjango Korea ml_model = Mlmodel.objects.create() 성공!
  102. 102. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하면.. Django DB DevDjango Korea version = Version.objects.create() ml_model = Mlmodel.objects.create() 성공!
  103. 103. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하면.. Django DB commit! DevDjango Korea Version rowversion = Version.objects.create() ml_model row ml_model = Mlmodel.objects.create()
  104. 104. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 Celery task가 추가되면? Django Celery DB DevDjango Korea
  105. 105. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하기 전에는.. Django Celery ml_model= Mlmodel.objects.create() 성공! DB DevDjango Korea
  106. 106. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하기 전에는.. Django Celery DB commit! DevDjango Korea ml_model= Mlmodel.objects.create() ml_model row
  107. 107. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하기 전에는.. Django Celery DB DevDjango Korea ml_model= Mlmodel.objects.create() ml_model row ml_model get!
  108. 108. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하고는.. Django Celery DB DevDjango Korea ml_model= Mlmodel.objects.create() 성공!
  109. 109. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하고는.. Django Celery DB ??? DevDjango Korea ml_model = Mlmodel.objects.create() version = Version.objects.create() 성공!
  110. 110. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 트랜잭션을 적용하고는.. Django Celery DB DoesNotExist!!! commit! 늦었어!!! DevDjango Korea ml_model = Mlmodel.objects.create() version = Version.objects.create() ml_model row version row
  111. 111. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 해결방안 찾기 지구에서 누군가는 .. 나와 똑같은 고민을 했지! 검색검색! DevDjango Korea
  112. 112. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 갓 구글... 영어로 답 나온거 실화냐.. DevDjango Korea
  113. 113. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 역시 Django!! Django에서 이런 때를 대비해!! on_commit이라는 훅을 제공한대요 on_commit transaction끝난 뒤 호출됩니다!! DevDjango Korea
  114. 114. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 Celery 기다려! on_commit 훅을 사용해서 커밋이 다 끝난뒤 셀러리가 task를 하도록 설정!! transaction.on_commit(lambda: celery_task.delay(‘version_id’)) DevDjango Korea
  115. 115. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 그리하여 celery가 commit을 기다린 후 Django Celery DB commit! 기다려! DevDjango Korea ml_model = Mlmodel.objects.create() version = Version.objects.create() ml_model row version row
  116. 116. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 모델을 가져오도록 적용할 수 있었다고.. Django Celery DB ml_model row ml_model!!! version row get! DevDjango Korea ml_model = Mlmodel.objects.create() version = Version.objects.create()
  117. 117. 3. 트랜젝션 적용하다 Celery에게 뒷통수 맞다 삽질했지만! 혼자 문제만들고 삽질하고...그래도 해결하고보니 완전 뿌듯해!!!! DevDjango Korea
  118. 118. 제4장. Django야 Django야 헌 집줄게 새 집다오
  119. 119. 4. 헌집줄게 새집다오 옆자리 Frontend 개발자님의… 21살 쩌는 프론트개발자 특징: 엄~청나게 위대한 위장 DevDjango Korea 배고파요헤헤 계속배고파요
  120. 120. BIG 프로젝트…!!! 현재 서비스의 간지나는 NEW 프론트 만들기!!! 두둥!! 힘들겠당ㅋㅋ 비 희 4. 헌집줄게 새집다오 DevDjango Korea
  121. 121. (반전)남 일인줄 알았나요? 멍.... 4. 헌집줄게 새집다오 DevDjango Korea
  122. 122. DRF의 배신? 분명히 백엔드는 DRF로 돌아가서 api화 다되어있는데 내가 할 일이 뭐가 있단 말이오.. 4. 헌집줄게 새집다오 DevDjango Korea
  123. 123. 궁금해??? Django가 하는 일이 엄청나게 많기 때문이지..! 4. 헌집줄게 새집다오 DevDjango Korea
  124. 124. 그냥 Django Url View Template Django Form 4. 헌집줄게 새집다오 DevDjango Korea
  125. 125. DRF + Template Django와 DRF의 혼합물 Url DRF View Template Django DRF Serializer Form 4. 헌집줄게 새집다오 DevDjango Korea
  126. 126. FormForm DRF + Ccccccustom + Template 우리 플랫폼은 ... 어마어마한 커스트마이징 Url DRF View Template Django DRF Serializer FormForm 4. 헌집줄게 새집다오 DevDjango Korea
  127. 127. FormForm 목표는?? 흠..뭔가 이상한데?? Url DRF View NEW FRONT Django DRF Serializer FormForm 4. 헌집줄게 새집다오 DevDjango Korea
  128. 128. FormForm 엄청나게 뚱뚱한 Form의 존재.. 저 Form은 어떻게…??? Url DRF View NEW FRONT Django DRF Serializer FormForm 4. 헌집줄게 새집다오 DevDjango Korea
  129. 129. FormForm api에 집어넣어야.. Url DRF View NEW FRONT Django DRF Serializer FormForm 흡수!!!!!! 4. 헌집줄게 새집다오 DevDjango Korea
  130. 130. 현실도피 저 많은 커스터마이징을 내가 다 api화 해야할리가 없어.. 4. 헌집줄게 새집다오 DevDjango Korea
  131. 131. Form이 하던 Validation Form이 하던 Validation은 Serializer에게 PASS DRF Serializer DevDjango Korea4. 헌집줄게 새집다오
  132. 132. 떠넘기기 Permission은 api따주고 프론트에게 넘기기! DevDjango Korea4. 헌집줄게 새집다오 {                 "permissions":[                    "Can execute this ml_model”,                  "Can retrieve this ml_model”               ]            } 옛다! 이제 프론트에서 처리해 NEW FRONT
  133. 133. (복수?)백엔드 번역해주기 문서화...해둘걸..(눈물) 은향님 이건 어떻게 처리해야해요? 은향님 이건 권한 설정 어때요? 은향님 이 폼 보여줘야해요? 은향님… … 4. 헌집줄게 새집다오 DevDjango Korea
  134. 134. 병행 대표님 스타트업은 lean해야하니꼬북! 필터 만들어주세요! 라이브러리 지원해주세요! 버전 비교하게 해주세요! … 새로운 요구사항 + 뉴 프론트 지원 병행! 유저 4. 헌집줄게 새집다오 DevDjango Korea
  135. 135. 이건 마치.. 새 기능 만들기 4. 헌집줄게 새집다오 DevDjango Korea
  136. 136. 이건 마치.. 새 기능 만들기 새 기능 프론트 다시 만들기 4. 헌집줄게 새집다오 DevDjango Korea
  137. 137. 이건 마치.. 새 기능 만들기 새 기능 프론트 다시 만들기 또 새로운 기능 만들기 4. 헌집줄게 새집다오 DevDjango Korea
  138. 138. 이건 마치.. 새 기능 만들기 새 기능 프론트 다시 만들기 또 새로운 기능 만들기 또 새로운 기능 프론트 만들기 4. 헌집줄게 새집다오 DevDjango Korea
  139. 139. 새 기능 만들기 새 기능 프론트 다시 만들기 또 새로운 기능 만들기 또 새로운 기능 프론트 만들기 무한한 꼬리잡기.. 4. 헌집줄게 새집다오 에헤라디야~ 잡히나봐라~ DevDjango Korea
  140. 140. 시간이 흘러 흘러~~ 그렇게 무려 3개월이 지나고.. 드디어..드디어..! 뉴 프론트 합쳐서 올려봅시다..! 4. 헌집줄게 새집다오 DevDjango Korea
  141. 141. CSRF!!!! CSRF DevDjango Korea4. 헌집줄게 새집다오
  142. 142. 뭐만하면 CSRF!!! 4. 헌집줄게 새집다오 고마해!! DevDjango Korea
  143. 143. 도대체 csrf??crfs??crsf??그게 뭔데!!!!! 너무 많이 쳐봐서 자동완성됨 4. 헌집줄게 새집다오 DevDjango Korea
  144. 144. 1초 집중력 사이트 간 요청 우 ㅣ 조 ㄴ ㅡㄴ... 보안..보안은 1도 모른다능... 4. 헌집줄게 새집다오 DevDjango Korea
  145. 145. Stack Overflow 복붙(찡긋) 해결 됐으니..끝!! 4. 헌집줄게 새집다오 DevDjango Korea
  146. 146. 머리채를 잡힌다.. flask로 만들던 서비스에서도.. 4. 헌집줄게 새집다오 DevDjango Korea
  147. 147. CSRF.. 아아아아알아내고 말것이야!!!!! 4. 헌집줄게 새집다오 DevDjango Korea
  148. 148. 상상 유저가 브라우저에서 우리 서비스에 로그인하고 인터넷을 떠돌아다니다 User1 반가워요! 4. 헌집줄게 새집다오 DevDjango Korea sessionid: r37046e1944d532e3dc 2adf0d5c483fc (주의! session id를 사용하여 인증할 때의 예를 든 것이에요!) 쿠키
  149. 149. 4. 헌집줄게 새집다오 DevDjango Korea 어머나! 피싱사이트에 접속! 풰이크북.com Save 상상
  150. 150. 4. 헌집줄게 새집다오 DevDjango Korea 풰이크북.com 홀라당 덫에 걸려 CSRF공격 코드가 삽입된 버튼 click!!! Save 나는. 유노윤호다.! 상상
  151. 151. 4. 헌집줄게 새집다오 DevDjango Korea 풰이크북.com Save 나는. 유노윤호다.! <form action=“http://knowru.com/ user/reset_password/“ method=“POST”> <input type=“hidden” name=“password” value=“1234"> </form> 폼에는 우리 서비스에 비밀번호를 리셋하는 코드가..! 상상
  152. 152. 4. 헌집줄게 새집다오 DevDjango Korea 브라우저에 저장된 쿠키와 함께 우리 서버로 요청을 보냈어요! POST http://knowru.com/user/ reset_password/ {password: ‘1234’} sessionid: r37046e1944d532e3dc2adf0d5c483fc ㅋㅋㅋ 상상
  153. 153. 4. 헌집줄게 새집다오 DevDjango Korea 발급해준 sessionid가 일치하니 비밀번호 리셋 성공! User sessionid 인증성공! 200 OK! ?! 상상
  154. 154. 그렇게 비밀번호는 해킹됐고 해커가 우리 서비스를 탈탈털고... 고객 서비스도 탈탈털고.... 메일이 쇄도하고.. 4. 헌집줄게 새집다오 고객님 #$#$@#!@#!!!!@#!1 DevDjango Korea 상상…?!
  155. 155. 파멸,, csrf.. django.. 주니어 개발자.. 안녕…;; 4. 헌집줄게 새집다오 DevDjango Korea
  156. 156. 의욕 200% 당..당장 해결하겠습니다! CSRF!!! 4. 헌집줄게 새집다오 DevDjango Korea
  157. 157. 그래서 Django에서는 csrf를 어떻게 방어하나요? CSRF문서에 친절하게 적혀있었다..! 4. 헌집줄게 새집다오 DevDjango Korea
  158. 158. Django와 CSRF User Django Server 4. 헌집줄게 새집다오 DevDjango Korea 유저가 로그인을 요청했어요 GET: 로그인!!
  159. 159. Django와 CSRF User Django Server 4. 헌집줄게 새집다오 DevDjango Korea 서버가 csrftoken을 쿠키에 저장하래요 GET: 로그인!! csrftoken: r37046e1944d532e3dc2adf0d5c483fc
  160. 160. Django와 CSRF User Django Server 4. 헌집줄게 새집다오 DevDjango Korea 이제 모든 요청에 csrftoken값이 쿠키에 포함되어 전송되요. 유저가 Form 페이지를 요청했어요 GET: Form페이지 ‘csrf토큰’가지고있쿠키
  161. 161. Django와 CSRF User Django Server 4. 헌집줄게 새집다오 DevDjango Korea From에 hidden input을 넣어서 보냅니다! 바로 csrfmiddlewaretoken! <form method="post"> <input type="hidden" name="csrfmiddlewaretoken" value="mTGKXN4BWSBd6wavFtK SzBr..."> </form>
  162. 162. Django와 CSRF User Django Server 4. 헌집줄게 새집다오 DevDjango Korea <form method="post"> <input type="hidden" name="csrfmiddlewaretoken" value="mTGKXN4BWSBd6wavFtK SzBr..."> </form> from에 넣던 {% csrf_token %}이 저 input값이 되는거였어요! {% csrf_token %}
  163. 163. Django와 CSRF User Django Server 4. 헌집줄게 새집다오 DevDjango Korea ‘csrf토큰’가지고있쿠키 POST: From 제출 (+csrfmiddlewaretoken) 이제 다시 쿠키와함께 Form을 제출합니다.
  164. 164. Django와 CSRF User Django Server 4. 헌집줄게 새집다오 DevDjango Korea 서버가 두 값을 비교검증합니다! csrftoken: r37046e1944d532e3dc2adf0d5c 483fc csrftmiddlewaretoken: mTGKXN4BWSBd6wavFtKSzBr…
  165. 165. Django와 CSRF User Django Server 4. 헌집줄게 새집다오 DevDjango Korea 검증이 성공하면 유저의 요청을 수행합니다! 201 Created!!
  166. 166. User Django Server 4. 헌집줄게 새집다오 DevDjango Korea POST: http://knowru.com/user/ reset_password/ 우리서비스에 유저 비밀번호 reset 요청! 만약 CSRF공격이라면
  167. 167. User Django Server 4. 헌집줄게 새집다오 DevDjango Korea csrfmiddlewaretoken이 없거나 틀린 값이겠네!!! csrftoken: r37046e1944d532e3dc2adf0d5c 483fc ??? 만약 CSRF공격이라면
  168. 168. Forbidden!! 4. 헌집줄게 새집다오 이번엔 반가운 403!! DevDjango Korea
  169. 169. 그럼 Ajax는? 뭐지...이 엄청난 코드는.... DevDjango Korea // using jQuery function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function(xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } }); 갖다쓰렴 ^^ 4. 헌집줄게 새집다오
  170. 170. 그럼 Ajax는? 유저가 ajax로 전송할 Form 제출 버튼을 클릭하면 DevDjango Korea Submit Ajax로 데이터 전송! 4. 헌집줄게 새집다오
  171. 171. 그럼 Ajax는? csrftoken값을 해독해서 Request header에 저장! DevDjango Korea Request Header { X-CSRFToken: csrftoken } csrftoken: r37046e1944d532e3dc2adf0d5c483fc 4. 헌집줄게 새집다오
  172. 172. User Django Server 4. 헌집줄게 새집다오 DevDjango Korea POST: From 제출 (+X-CSRFToken) X-CSRFToken를 포함한 헤더와 폼제출! 그럼 Ajax는?
  173. 173. Django Server 4. 헌집줄게 새집다오 DevDjango Korea 그럼 서버가 X-CSRFToken으로 온 값을 검증하고 그럼 Ajax는? csrftoken: r37046e1944d532e3dc2adf0d5c 483fc X-CSRFToken
  174. 174. Django와 CSRF User Django Server 4. 헌집줄게 새집다오 DevDjango Korea 검증이 성공하면 유저의 요청을 수행합니다! 201 Created!!
  175. 175. 그렇다면 Angular는!!! 4. 헌집줄게 새집다오 DevDjango Korea
  176. 176. 그렇다면 Angular는!!! 4. 헌집줄게 새집다오 DevDjango Korea $httpProvider.defaults.xsrfCookieName = 'csrftoken'; $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
  177. 177. 그렇다면 Angular는!!! 4. 헌집줄게 새집다오 DevDjango Korea Angular와 같은 프론트엔드 프레임워크들은 대부분 CSRF 대응을 지원하기 때문에!
  178. 178. 그렇다면 Angular는!!! 4. 헌집줄게 새집다오 DevDjango Korea $httpProvider.defaults.xsrfCookieName = 'csrftoken'; 이렇게 쿠키로오는 csrf토큰값이 무엇인지 $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; Request보낼때 헤더에 포함할 값은 무엇인지만 알려주면! csrftoken: r37046e1944d532
  179. 179. Django Server 4. 헌집줄게 새집다오 DevDjango Korea 서버가 헤더에 포함된 토큰값을 검증할 수 있어요! csrftoken: r37046e1944d532e3dc2adf0d5c 483fc X-CSRFToken 그렇다면 Angular는!!!
  180. 180. 선 해결 후 이해 이렇게 우여곡절 끝에 뉴 프론트를 맞이하게됩니다!!! 간지폭팔!!! 4. 헌집줄게 새집다오 DevDjango Korea
  181. 181. 비하인드 스토리 그 뒤로도 사실 또다른 몹들을 맞이하다 드디어 뉴 프론트를 서비스에 올렸어요 Lv10 npm Lv20 China aws Lv100000 NGINX 4. 헌집줄게 새집다오 DevDjango Korea
  182. 182. 비하인드 스토리 NGINX설정이 안 먹어요 + 올리고보니 이런저런데서 또 터짐 4. 헌집줄게 새집다오 DevDjango Korea
  183. 183. 비하인드 스토리 단호박 접읍시다꼬북! 4. 헌집줄게 새집다오 DevDjango Korea
  184. 184. 비하인드 스토리 진-짜!!! 할 수 있어요!!! 4. 헌집줄게 새집다오 DevDjango Korea 흔들림
  185. 185. 비하인드 스토리 24시간 안에 완성합시다!!! 4. 헌집줄게 새집다오 DevDjango Korea
  186. 186. 비하인드 스토리 엄청난 집중력으로... 모든 에러를 해결!!!! 이런 모습 처음이야..! 4. 헌집줄게 새집다오 DevDjango Korea
  187. 187. 비하인드 스토리 성공적으로 릴리즈하고 감격의 눈물을 흘렸다고.. 4. 헌집줄게 새집다오 DevDjango Korea
  188. 188. 제5장. 삽질기는 계속된다-!
  189. 189. 5. 삽질기는 계속된다! 그저께는 네트워크 공부해야겠다..! AWS 설정하다가..https적용시키다.. Vpn… Security Groups… Load Balencers.. SSL..TLS.. DevDjango Korea
  190. 190. 어제는 아냐..컴퓨터 기초부터..공부해야겠어..! 서버설정하다.. 5. 삽질기는 계속된다! DevDjango Korea
  191. 191. 오늘은 Python이나 제대로해야지.. 컴퓨터부셨다 다시 조립해볼까..(화난거아님) … 5. 삽질기는 계속된다! DevDjango Korea
  192. 192. 그래도 사소한거에 유닛테스트..사방에서 터졌군 5. 삽질기는 계속된다! DevDjango Korea
  193. 193. 햄볶!! 나 혼자 사방의 유닛테스트를 해결하디니.. (감격) 5. 삽질기는 계속된다! DevDjango Korea
  194. 194. 어서와 Django는 처음이지? 낮은 진입장벽 덕분에 Djang언덕길을 발견했다! 5. 삽질기는 계속된다! DevDjango Korea
  195. 195. 엄청 높은 산이었다! 비록 배신당하긴 했지만 겁나 높음 ㅋㅋㅋ 5. 삽질기는 계속된다! DevDjango Korea
  196. 196. 왜 해야 하는지 몰랐던 공부를 프포자의 최강간지 모니터받침대!!! 5. 삽질기는 계속된다! DevDjango Korea
  197. 197. 직접 찾아(즐겁게?)하게되고 내가 전공 책을 필요해서 직접 사읽고있다니.. (이북 최고) 5. 삽질기는 계속된다! DevDjango Korea
  198. 198. 고통받다.. 외않되... 5. 삽질기는 계속된다! DevDjango Korea
  199. 199. 느끼는 희열에 중독되고 있어요!!(?) 됐다!!!!!됐어!!!!(근데 왜되지?) 5. 삽질기는 계속된다! DevDjango Korea
  200. 200. 삽질기는 계속!! 오늘도 내일도 모레도.. 함께해요! ^^ 5. 삽질기는 계속된다! DevDjango Korea
  201. 201. DjangoCon섹션네임 감사합니다! :) DevDjango Korea
  202. 202. DjangoCon섹션네임 도움주신 분들! 파이님! 너굴님! 프로그라피 운명들 ㅇㅇㅇㅇㅇㅇㅇ! 프로그라피 Django팀, 재규님, 성환님! 노루팀! Javlon! 선우! 코알못 고향친구들! 날카로우면서 따뜻하고도 섬세한 리뷰 정말정말 감사해요. 최고. DevDjango Korea

×