├── .gitignore ├── LICENSE ├── README.md ├── accounts ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── serializers.py ├── templates │ └── accounts │ │ ├── login.html │ │ ├── profile.html │ │ └── signup.html ├── templatetags │ ├── __init__.py │ └── gravatar.py ├── tests.py ├── urls.py └── views.py ├── assets ├── 1-main.png ├── 2-genre.png ├── 3-profile.png ├── 4-detail.png ├── 5-genre.png └── erd.png ├── backend ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── manage.py ├── movies ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20190516_0040.py │ └── __init__.py ├── models.py ├── serializers.py ├── templates │ └── movies │ │ ├── _comments.html │ │ ├── _genre-cards.html │ │ ├── detail.html │ │ ├── genre.html │ │ └── home.html ├── tests.py ├── urls.py └── views.py ├── requirements.txt ├── static ├── css │ └── app.css ├── data │ └── data.json ├── image │ ├── favicon.ico │ └── favicon.png └── js │ └── app.js └── templates ├── _footer.html ├── _nav.html └── base.html /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/vuejs,django,python,pycharm 3 | # Edit at https://www.gitignore.io/?templates=vuejs,django,python,pycharm 4 | 5 | ### Django ### 6 | *.log 7 | *.pot 8 | *.pyc 9 | __pycache__/ 10 | local_settings.py 11 | db.sqlite3 12 | media 13 | 14 | # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ 15 | # in your Git repository. Update and uncomment the following line accordingly. 16 | # /staticfiles/ 17 | 18 | ### Django.Python Stack ### 19 | # Byte-compiled / optimized / DLL files 20 | *.py[cod] 21 | *$py.class 22 | 23 | # C extensions 24 | *.so 25 | 26 | # Distribution / packaging 27 | .Python 28 | build/ 29 | develop-eggs/ 30 | dist/ 31 | downloads/ 32 | eggs/ 33 | .eggs/ 34 | lib/ 35 | lib64/ 36 | parts/ 37 | sdist/ 38 | var/ 39 | wheels/ 40 | pip-wheel-metadata/ 41 | share/python-wheels/ 42 | *.egg-info/ 43 | .installed.cfg 44 | *.egg 45 | MANIFEST 46 | 47 | # PyInstaller 48 | # Usually these files are written by a python script from a template 49 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 50 | *.manifest 51 | *.spec 52 | 53 | # Installer logs 54 | pip-log.txt 55 | pip-delete-this-directory.txt 56 | 57 | # Unit test / coverage reports 58 | htmlcov/ 59 | .tox/ 60 | .nox/ 61 | .coverage 62 | .coverage.* 63 | .cache 64 | nosetests.xml 65 | coverage.xml 66 | *.cover 67 | .hypothesis/ 68 | .pytest_cache/ 69 | 70 | # Translations 71 | *.mo 72 | 73 | # Django stuff: 74 | 75 | # Flask stuff: 76 | instance/ 77 | .webassets-cache 78 | 79 | # Scrapy stuff: 80 | .scrapy 81 | 82 | # Sphinx documentation 83 | docs/_build/ 84 | 85 | # PyBuilder 86 | target/ 87 | 88 | # Jupyter Notebook 89 | .ipynb_checkpoints 90 | 91 | # IPython 92 | profile_default/ 93 | ipython_config.py 94 | 95 | # pyenv 96 | .python-version 97 | 98 | # pipenv 99 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 100 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 101 | # having no cross-platform support, pipenv may install dependencies that don’t work, or not 102 | # install all needed dependencies. 103 | #Pipfile.lock 104 | 105 | # celery beat schedule file 106 | celerybeat-schedule 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | 120 | # Spyder project settings 121 | .spyderproject 122 | .spyproject 123 | 124 | # Rope project settings 125 | .ropeproject 126 | 127 | # mkdocs documentation 128 | /site 129 | 130 | # mypy 131 | .mypy_cache/ 132 | .dmypy.json 133 | dmypy.json 134 | 135 | # Pyre type checker 136 | .pyre/ 137 | 138 | ### PyCharm ### 139 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 140 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 141 | 142 | # User-specific stuff 143 | .idea/ 144 | 145 | # Gradle and Maven with auto-import 146 | # When using Gradle or Maven with auto-import, you should exclude module files, 147 | # since they will be recreated, and may cause churn. Uncomment if using 148 | # auto-import. 149 | # .idea/modules.xml 150 | # .idea/*.iml 151 | # .idea/modules 152 | 153 | # CMake 154 | cmake-build-*/ 155 | 156 | # Mongo Explorer plugin 157 | .idea/**/mongoSettings.xml 158 | 159 | # File-based project format 160 | *.iws 161 | 162 | # IntelliJ 163 | out/ 164 | 165 | # mpeltonen/sbt-idea plugin 166 | .idea_modules/ 167 | 168 | # JIRA plugin 169 | atlassian-ide-plugin.xml 170 | 171 | # Cursive Clojure plugin 172 | .idea/replstate.xml 173 | 174 | # Crashlytics plugin (for Android Studio and IntelliJ) 175 | com_crashlytics_export_strings.xml 176 | crashlytics.properties 177 | crashlytics-build.properties 178 | fabric.properties 179 | 180 | # Editor-based Rest Client 181 | .idea/httpRequests 182 | 183 | # Android studio 3.1+ serialized cache file 184 | .idea/caches/build_file_checksums.ser 185 | 186 | # JetBrains templates 187 | **___jb_tmp___ 188 | 189 | ### PyCharm Patch ### 190 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 191 | 192 | # *.iml 193 | # modules.xml 194 | # .idea/misc.xml 195 | # *.ipr 196 | 197 | # Sonarlint plugin 198 | .idea/sonarlint 199 | 200 | ### Python ### 201 | # Byte-compiled / optimized / DLL files 202 | 203 | # C extensions 204 | 205 | # Distribution / packaging 206 | 207 | # PyInstaller 208 | # Usually these files are written by a python script from a template 209 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 210 | 211 | # Installer logs 212 | 213 | # Unit test / coverage reports 214 | 215 | # Translations 216 | 217 | # Django stuff: 218 | 219 | # Flask stuff: 220 | 221 | # Scrapy stuff: 222 | 223 | # Sphinx documentation 224 | 225 | # PyBuilder 226 | 227 | # Jupyter Notebook 228 | 229 | # IPython 230 | 231 | # pyenv 232 | 233 | # pipenv 234 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 235 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 236 | # having no cross-platform support, pipenv may install dependencies that don’t work, or not 237 | # install all needed dependencies. 238 | 239 | # celery beat schedule file 240 | 241 | # SageMath parsed files 242 | 243 | # Environments 244 | 245 | # Spyder project settings 246 | 247 | # Rope project settings 248 | 249 | # mkdocs documentation 250 | 251 | # mypy 252 | 253 | # Pyre type checker 254 | 255 | ### Vuejs ### 256 | # Recommended template: Node.gitignore 257 | 258 | node_modules/ 259 | npm-debug.log 260 | yarn-error.log 261 | 262 | # End of https://www.gitignore.io/api/vuejs,django,python,pycharm 263 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Eunjeong Park 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎞 미음비읍 2 | 3 | SSAFY 마지막 프로젝트를 위한 영화 포털 사이트입니다. 4 | 5 | 6 |   7 | ## 0. 로컬 실행 방법 8 | 9 | pip로 프로젝트에 필요한 패키지를 설치합니다. 10 | 11 | ```bash 12 | > pip install -r requirements.txt 13 | ``` 14 | 15 | migrate를 한 뒤 Django 프로젝트를 실행합니다. 16 | 17 | ```bash 18 | > python manage.py migrate 19 | > python manage.py runserver 20 | ``` 21 | 22 |   23 | ## 1. 업무 분담 24 | 25 | 서지욱 : DB / 장고 REST API 설계, 장고 로직 구현, Vue 로직 구현 26 | 27 | 박은정 : 화면 설계 및 디자인, Vue 로직 구현, REST API 활용 28 | 29 |   30 | 31 | 32 | 33 | ## 2. 사용 기술 및 개발 계획 34 | 35 | ### 1) 사용 기술 36 | 37 | Django 템플릿에서 Vue CDN을 이용해 개발했습니다. 38 | 39 | #### Frontend 40 | 41 | - Vue : 2.6.10 42 | - Axios : 0.18.0 43 | - Bulma : 0.7.4 44 | - Font Awesome : 5.8.2 45 | 46 | #### Backend 47 | 48 | - Django : 2.1 49 | - Django REST framework : 3.9.4 50 | - Python : 3.6.7 51 | 52 |   53 | 54 | 55 | 56 | ### 2) 개발 계획 57 | 58 | * 진행 기간 : 2019.05.14 - 2019.05.17 59 | * 목표 : 영화 포털 사이트의 기본 기능을 빠르게 구현하자 60 | 61 |   62 | 63 | 64 | 65 | ## 3. DB 모델링 66 | 67 | ![장고 DB](./assets/erd.png) 68 | 69 | 70 | 71 |   72 | 73 | ## 4. 핵심 기능 74 | 75 | ### 1) 영화 추천 76 | 77 | - 메인 화면에서 관람객 수 1위 영화 추천 78 | - 프로필 페이지에서 팔로워들의 최애 영화 추천 79 | 80 | 81 | 82 | ### 2) 리뷰 작성 83 | 84 | * 로그인한 유저는 :star: 를 조작해 평점과 리뷰 등록 85 | * 내가 남긴 리뷰 수정, 삭제 기능 86 | 87 | 88 | 89 | ### 3) 영화 검색 90 | 91 | * 상단 Nav 바에 있는 Search bar에서 영화명으로 영화 검색 92 | * 메인 화면에서 장르 카드를 눌러 장르별 영화 검색 93 | 94 | 95 | 96 | ### 4) 유저 프로필 97 | 98 | * 내가 작성한 평점과 리뷰 확인 99 | * 내 팔로워들의 최애 영화 확인 100 | 101 | 102 | 103 | ### 5) UI/UX 104 | 105 | - Bulma 프레임워크를 이용해 플랫폼에 최적화된 디자인 구현 106 | - Single Page App으로 페이지 리로드 최소화 (ex. 장르별 영화) 107 | 108 | 109 |   110 | 111 | ## 5. 배포 112 | 113 | ~~[Demo](https://movie-ejolie.c9users.io/)~~ 114 | 115 | ~~Cloud9을 이용해 임시 배포했습니다.~~ 116 | 117 | 118 | 119 | ### 스크린샷 120 | 121 | ![메인 화면](./assets/1-main.png) 122 | 123 | ![메인 화면 - 장르](./assets/2-genre.png) 124 | 125 | ![장르 페이지](./assets/5-genre.png) 126 | 127 | ![영화 상세 정보](./assets/4-detail.png) 128 | 129 | ![유저 프로필](./assets/3-profile.png) 130 | 131 | -------------------------------------------------------------------------------- /accounts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/accounts/__init__.py -------------------------------------------------------------------------------- /accounts/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import User 3 | 4 | admin.site.register(User) 5 | -------------------------------------------------------------------------------- /accounts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountsConfig(AppConfig): 5 | name = 'accounts' 6 | -------------------------------------------------------------------------------- /accounts/forms.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.contrib.auth.forms import UserCreationForm 3 | 4 | 5 | class UserCustomCreationForm(UserCreationForm): 6 | class Meta: 7 | model = get_user_model() 8 | fields = ('email', 'username', ) 9 | -------------------------------------------------------------------------------- /accounts/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1 on 2019-05-15 07:28 2 | 3 | from django.conf import settings 4 | import django.contrib.auth.models 5 | import django.contrib.auth.validators 6 | from django.db import migrations, models 7 | import django.utils.timezone 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | ('auth', '0009_alter_user_last_name_max_length'), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='User', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('password', models.CharField(max_length=128, verbose_name='password')), 24 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 25 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 26 | ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), 27 | ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), 28 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 29 | ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), 30 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 31 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 32 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 33 | ('from_user', models.ManyToManyField(related_name='to_user', to=settings.AUTH_USER_MODEL)), 34 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), 35 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), 36 | ], 37 | options={ 38 | 'verbose_name': 'user', 39 | 'verbose_name_plural': 'users', 40 | 'abstract': False, 41 | }, 42 | managers=[ 43 | ('objects', django.contrib.auth.models.UserManager()), 44 | ], 45 | ), 46 | ] 47 | -------------------------------------------------------------------------------- /accounts/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/accounts/migrations/__init__.py -------------------------------------------------------------------------------- /accounts/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import AbstractUser 3 | from django.conf import settings 4 | 5 | 6 | class User(AbstractUser): 7 | from_user = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="to_user") 8 | 9 | def get_score(self): 10 | return round(self.ratings.aggregate(models.Avg('score'))['score__avg'], 2) 11 | 12 | def get_recommend(self): 13 | return self.ratings.order_by('-score').first() 14 | 15 | def __str__(self): 16 | return self.username 17 | -------------------------------------------------------------------------------- /accounts/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from .models import User 3 | from movies.serializers import RatingSerializer 4 | 5 | 6 | class UserSerializer(serializers.ModelSerializer): 7 | ratings = RatingSerializer(many=True, read_only=True) 8 | from_user = serializers.StringRelatedField(many=True) 9 | 10 | class Meta: 11 | model = User 12 | fields = ( 13 | 'id', 14 | 'username', 15 | 'email', 16 | 'first_name', 17 | 'last_name', 18 | 'ratings', 19 | 'from_user', 20 | ) 21 | -------------------------------------------------------------------------------- /accounts/templates/accounts/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load bulma_tags %} 3 | {% block app %} 4 |
5 |
6 |

7 |
8 |
9 |
10 | {% csrf_token %} 11 | {{ form | bulma }} 12 |
13 | 16 |
17 |
18 |
19 |
20 |
21 |
22 | {% endblock %} 23 | {% block script %} 24 | 32 | {% endblock %} -------------------------------------------------------------------------------- /accounts/templates/accounts/profile.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% load gravatar %} 4 | {% block app %} 5 |
6 |
7 |
8 |
9 |
10 |

11 | {{ person.username }}'s gravatar 15 |

16 |
17 |
18 |
19 |

20 | 27 | {{ person.username }} 28 | 29 | {% if user != person %} 30 | 32 | 35 | {% endif %} 36 |

37 |
38 |
39 | 59 |
60 |
61 |
62 | 63 | {% if user == person %} 64 |
65 |
66 |
70 |

팔로워 추천

71 |
72 |
73 | {% if user.from_user.count == 0 %} 74 |
  • 새로운 유저를 팔로우 해보세요!
  • 75 | {% endif %} 76 | {% for follower in user.from_user.all %} 77 | {% if follower.get_recommend %} 78 |
  • 79 | {{ follower.username }} 님의 최애 영화 81 |
  • 82 | 99 | {% endif %} 100 | {% endfor %} 101 |
    102 |
    103 |
    104 | {% endif %} 105 |
    106 |
    107 |
    111 |

    작성한 리뷰

    112 |
    113 | {% if person.ratings.all %} 114 |
  • 등록한 평점 : {{ person.ratings.all.count }} 개
  • 115 |
  • 평균 평점 : {{ person.get_score }} / 5.0 점
  • 116 | 117 |
    118 | {% for rating in person.ratings.all reversed %} 119 |
    120 | 124 |
    125 | 126 | {% for i in 1|rjust:rating.score %} 127 | 128 | {% endfor %} 129 |
    130 | {{ rating.comment }} 131 |
    132 |
    133 | {% endfor %} 134 |
    135 | 136 | {% else %} 137 |
  • 아직 등록된 평점이 없습니다. 평점을 남겨주세요!
  • 138 | {% endif %} 139 |
    140 |
    141 |
    142 | {% endblock %} 143 | 144 | {% block script %} 145 | 171 | {% endblock %} -------------------------------------------------------------------------------- /accounts/templates/accounts/signup.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load bulma_tags %} 3 | {% block app %} 4 |
    5 |
    6 |

    7 |
    8 |
    9 |
    10 | {% csrf_token %} 11 | {{ form | bulma }} 12 |
    13 | 16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 | {% endblock %} 23 | {% block script %} 24 | 32 | {% endblock %} -------------------------------------------------------------------------------- /accounts/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/accounts/templatetags/__init__.py -------------------------------------------------------------------------------- /accounts/templatetags/gravatar.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | from urllib.parse import urlencode 3 | from django import template 4 | from django.utils.safestring import mark_safe 5 | 6 | register = template.Library() 7 | 8 | 9 | # return only the URL of the gravatar 10 | # TEMPLATE USE: {{ email|gravatar_url:150 }} 11 | @register.filter 12 | def gravatar_url(email, size=40): 13 | default = 'retro' 14 | return "https://www.gravatar.com/avatar/%s?%s" % ( 15 | hashlib.md5(email.lower().encode('utf-8')).hexdigest(), urlencode({'d': default, 's': str(size)})) 16 | 17 | 18 | # return an image tag with the gravatar 19 | # TEMPLATE USE: {{ email|gravatar:150 }} 20 | @register.filter 21 | def gravatar(email, size=40): 22 | url = gravatar_url(email, size) 23 | return mark_safe('' % (url, size, size)) 24 | -------------------------------------------------------------------------------- /accounts/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /accounts/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | app_name = "accounts" 5 | urlpatterns = [ 6 | path('login/', views.login, name="login"), 7 | path('logout/', views.logout, name="logout"), 8 | path('signup/', views.signup, name="signup"), 9 | path('/', views.profile_view, name="profile_view"), 10 | path('/follow/', views.profile_follow, name="profile_follow"), 11 | path('api//', views.profile_detail, name="profile_detail"), 12 | ] 13 | -------------------------------------------------------------------------------- /accounts/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect, get_object_or_404 2 | from django.contrib.auth import get_user_model 3 | from django.contrib.auth import login as auth_login, logout as auth_logout 4 | from django.contrib.auth.forms import AuthenticationForm 5 | from django.contrib.auth.decorators import login_required 6 | from .forms import UserCustomCreationForm 7 | from .serializers import UserSerializer 8 | from rest_framework.decorators import api_view 9 | from rest_framework.response import Response 10 | from django.http import JsonResponse 11 | 12 | 13 | def login(request): 14 | if request.user.is_authenticated: 15 | return redirect('movies:index') 16 | if request.method == 'POST': 17 | form = AuthenticationForm(request, request.POST) 18 | if form.is_valid(): 19 | auth_login(request, form.get_user()) 20 | return redirect(request.GET.get('next') or 'movies:index') 21 | else: 22 | form = AuthenticationForm() 23 | return render(request, 'accounts/login.html', {'form': form}) 24 | 25 | 26 | @login_required 27 | def logout(request): 28 | auth_logout(request) 29 | return redirect('movies:index') 30 | 31 | 32 | def signup(request): 33 | if request.user.is_authenticated: 34 | return redirect('movies:index') 35 | if request.method == 'POST': 36 | user_form = UserCustomCreationForm(request.POST) 37 | if user_form.is_valid(): 38 | user = user_form.save() 39 | auth_login(request, user) 40 | return redirect('movies:index') 41 | else: 42 | user_form = UserCustomCreationForm() 43 | return render(request, 'accounts/signup.html', {'form': user_form}) 44 | 45 | 46 | def profile_view(request, username): 47 | user = get_object_or_404(get_user_model(), username=username) 48 | return render(request, 'accounts/profile.html', {'person': user}) 49 | 50 | 51 | @api_view(["GET"]) 52 | def profile_detail(request, username): 53 | user = get_object_or_404(get_user_model(), username=username) 54 | serializer = UserSerializer(user) 55 | return Response(serializer.data) 56 | 57 | 58 | @login_required 59 | def profile_follow(request, username): 60 | user = get_object_or_404(get_user_model(), username=username) 61 | is_follow = False 62 | if user in request.user.from_user.all(): 63 | request.user.from_user.remove(user) 64 | elif user != request.user: 65 | request.user.from_user.add(user) 66 | is_follow = True 67 | return JsonResponse({'is_follow': is_follow}, json_dumps_params={'ensure_ascii': True}) 68 | -------------------------------------------------------------------------------- /assets/1-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/assets/1-main.png -------------------------------------------------------------------------------- /assets/2-genre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/assets/2-genre.png -------------------------------------------------------------------------------- /assets/3-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/assets/3-profile.png -------------------------------------------------------------------------------- /assets/4-detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/assets/4-detail.png -------------------------------------------------------------------------------- /assets/5-genre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/assets/5-genre.png -------------------------------------------------------------------------------- /assets/erd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/assets/erd.png -------------------------------------------------------------------------------- /backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/backend/__init__.py -------------------------------------------------------------------------------- /backend/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for backend project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.1/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'px7y9=a50x%y29s5s85s!o2&ak5v)i9s6r6egkfr+tmwp^9p$b' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ['*'] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'rest_framework', 35 | 'bulma', 36 | 'django_extensions', 37 | 'django.contrib.humanize', 38 | 'django.contrib.admin', 39 | 'django.contrib.auth', 40 | 'django.contrib.contenttypes', 41 | 'django.contrib.sessions', 42 | 'django.contrib.messages', 43 | 'django.contrib.staticfiles', 44 | 'movies', 45 | 'accounts', 46 | ] 47 | 48 | MIDDLEWARE = [ 49 | 'django.middleware.security.SecurityMiddleware', 50 | 'django.contrib.sessions.middleware.SessionMiddleware', 51 | 'django.middleware.common.CommonMiddleware', 52 | 'django.middleware.csrf.CsrfViewMiddleware', 53 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 54 | 'django.contrib.messages.middleware.MessageMiddleware', 55 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 56 | ] 57 | 58 | ROOT_URLCONF = 'backend.urls' 59 | 60 | TEMPLATES = [ 61 | { 62 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 63 | 'DIRS': [BASE_DIR, 'templates'], 64 | 'APP_DIRS': True, 65 | 'OPTIONS': { 66 | 'context_processors': [ 67 | 'django.template.context_processors.debug', 68 | 'django.template.context_processors.request', 69 | 'django.contrib.auth.context_processors.auth', 70 | 'django.contrib.messages.context_processors.messages', 71 | ], 72 | }, 73 | }, 74 | ] 75 | 76 | WSGI_APPLICATION = 'backend.wsgi.application' 77 | 78 | 79 | # Database 80 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases 81 | 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.sqlite3', 85 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 86 | } 87 | } 88 | 89 | 90 | # Password validation 91 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators 92 | 93 | AUTH_PASSWORD_VALIDATORS = [ 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 99 | }, 100 | { 101 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 102 | }, 103 | { 104 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 105 | }, 106 | ] 107 | 108 | 109 | # Internationalization 110 | # https://docs.djangoproject.com/en/2.1/topics/i18n/ 111 | 112 | LANGUAGE_CODE = 'ko-kr' 113 | 114 | TIME_ZONE = 'Asia/Seoul' 115 | 116 | USE_I18N = True 117 | 118 | USE_L10N = True 119 | 120 | USE_TZ = True 121 | 122 | 123 | # Static files (CSS, JavaScript, Images) 124 | # https://docs.djangoproject.com/en/2.1/howto/static-files/ 125 | 126 | STATIC_URL = '/static/' 127 | STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] 128 | 129 | AUTH_USER_MODEL = "accounts.User" 130 | -------------------------------------------------------------------------------- /backend/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | 4 | urlpatterns = [ 5 | path('', include("movies.urls")), 6 | path('accounts/', include("accounts.urls")), 7 | path('admin/', admin.site.urls), 8 | ] 9 | -------------------------------------------------------------------------------- /backend/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for backend project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == '__main__': 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /movies/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/movies/__init__.py -------------------------------------------------------------------------------- /movies/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Genre, Movie, Rating 3 | 4 | admin.site.register(Genre) 5 | admin.site.register(Movie) 6 | admin.site.register(Rating) 7 | -------------------------------------------------------------------------------- /movies/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MoviesConfig(AppConfig): 5 | name = 'movies' 6 | -------------------------------------------------------------------------------- /movies/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1 on 2019-05-15 07:28 2 | 3 | import datetime 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Genre', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('type', models.CharField(max_length=150)), 23 | ], 24 | ), 25 | migrations.CreateModel( 26 | name='Movie', 27 | fields=[ 28 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 29 | ('title', models.CharField(max_length=150)), 30 | ('title_eng', models.CharField(blank=True, default='', max_length=150)), 31 | ('title_org', models.CharField(blank=True, default='', max_length=150)), 32 | ('audience', models.IntegerField(default=0)), 33 | ('release', models.DateField(blank=True, default=datetime.datetime.now)), 34 | ('thumb_url', models.URLField(blank=True, default='')), 35 | ('poster_url', models.URLField(blank=True, default='')), 36 | ('running_time', models.IntegerField(default=0)), 37 | ('director', models.CharField(blank=True, default='', max_length=45)), 38 | ('film_rating', models.CharField(blank=True, default='', max_length=45)), 39 | ('actor1', models.CharField(blank=True, default='', max_length=45)), 40 | ('actor2', models.CharField(blank=True, default='', max_length=45)), 41 | ('actor3', models.CharField(blank=True, default='', max_length=45)), 42 | ('genres', models.ManyToManyField(related_name='movies', to='movies.Genre')), 43 | ], 44 | ), 45 | migrations.CreateModel( 46 | name='Rating', 47 | fields=[ 48 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 49 | ('comment', models.TextField()), 50 | ('score', models.IntegerField()), 51 | ('movie', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ratings', to='movies.Movie')), 52 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ratings', to=settings.AUTH_USER_MODEL)), 53 | ], 54 | ), 55 | ] 56 | -------------------------------------------------------------------------------- /movies/migrations/0002_auto_20190516_0040.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1 on 2019-05-15 15:40 2 | 3 | import django.core.validators 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('movies', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='rating', 16 | name='score', 17 | field=models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(5)]), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /movies/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/movies/migrations/__init__.py -------------------------------------------------------------------------------- /movies/models.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from django.db import models 3 | from django.contrib.auth import get_user_model 4 | from django.core.validators import MinValueValidator, MaxValueValidator 5 | 6 | 7 | class Genre(models.Model): 8 | type = models.CharField(max_length=150) 9 | 10 | def __str__(self): 11 | return self.type 12 | 13 | 14 | class Movie(models.Model): 15 | genres = models.ManyToManyField(Genre, related_name='movies') 16 | title = models.CharField(max_length=150) 17 | title_eng = models.CharField(max_length=150, default="", blank=True) 18 | title_org = models.CharField(max_length=150, default="", blank=True) 19 | audience = models.IntegerField(default=0) 20 | release = models.DateField(default=datetime.now, blank=True) 21 | thumb_url = models.URLField(max_length=200, default="", blank=True) 22 | poster_url = models.URLField(max_length=200, default="", blank=True) 23 | running_time = models.IntegerField(default=0) 24 | director = models.CharField(max_length=45, default="", blank=True) 25 | film_rating = models.CharField(max_length=45, default="", blank=True) 26 | actor1 = models.CharField(max_length=45, default="", blank=True) 27 | actor2 = models.CharField(max_length=45, default="", blank=True) 28 | actor3 = models.CharField(max_length=45, default="", blank=True) 29 | 30 | def __str__(self): 31 | return self.title 32 | 33 | @classmethod 34 | def max_audience(cls): 35 | return cls.objects.order_by('-audience')[0] 36 | 37 | 38 | class Rating(models.Model): 39 | movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='ratings') 40 | user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='ratings') 41 | comment = models.TextField() 42 | score = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(5)]) 43 | 44 | def username(self): 45 | return self.user.username 46 | 47 | def __str__(self): 48 | return f'{self.movie} | {self.score} | {self.comment}' 49 | -------------------------------------------------------------------------------- /movies/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from .models import Genre, Movie, Rating 3 | 4 | 5 | class RatingSerializer(serializers.ModelSerializer): 6 | username = serializers.ReadOnlyField() 7 | 8 | class Meta: 9 | model = Rating 10 | fields = "__all__" 11 | 12 | 13 | class MovieSerializer(serializers.ModelSerializer): 14 | ratings = RatingSerializer(many=True, read_only=True) 15 | genres = serializers.StringRelatedField(many=True) 16 | 17 | class Meta: 18 | model = Movie 19 | fields = "__all__" 20 | 21 | 22 | class GenreSerializer(serializers.ModelSerializer): 23 | movies = MovieSerializer(many=True, read_only=True) 24 | 25 | class Meta: 26 | model = Genre 27 | fields = "__all__" 28 | -------------------------------------------------------------------------------- /movies/templates/movies/_comments.html: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
    4 |

    아직 등록된 평점이 없습니다. 평점을 남겨주세요!

    5 |
    6 |
    7 |
    8 | [[ rating.username ]] 9 | 10 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 |
    31 |

    [[ rating.comment ]]

    32 |
    33 |
    34 |
    35 |
    36 | {% if user.is_active %} 37 | 38 |
    39 |
    40 |
    41 |

    42 | 43 |

    44 | 60 |
    61 |

    62 |
    63 |
    64 |

    65 | 66 |

    67 |
    68 | 94 |
    95 |
    96 | {% endif %} -------------------------------------------------------------------------------- /movies/templates/movies/_genre-cards.html: -------------------------------------------------------------------------------- 1 |
    2 | 25 |
    -------------------------------------------------------------------------------- /movies/templates/movies/detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% load humanize %} 4 | {% block app %} 5 |
    6 |
    7 | 8 |
    9 |
    10 |
    11 | 12 |
    13 | 14 |
    15 | {% for genre in movie.genres.all %} 16 | 17 | {{ genre.type }} 18 | 19 | {% endfor %} 20 |
    21 |

    {{ movie.title }}

    22 |
    23 | {{ movie.release }} 개봉 24 | • {{ movie.film_rating }} 25 | • {{ movie.audience | intcomma }}명 26 |
    27 |

    감독 : {{ movie.director }}

    28 |

    29 | 배우 : {{ movie.actor1 }} 30 | {% if movie.actor2 %} 31 | , {{ movie.actor2 }} 32 | {% if movie.actor3 %} 33 | , {{ movie.actor3 }} 34 | {% endif %} 35 | {% endif %} 36 |

    37 |
    38 |
    39 |
    40 |
    41 |
    42 | 43 |
    44 |
    45 | {% include 'movies/_comments.html' %} 46 |
    47 |
    48 |
    49 |
    50 | {% endblock %} 51 | 52 | {% block script %} 53 | 148 | {% endblock %} -------------------------------------------------------------------------------- /movies/templates/movies/genre.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% load humanize %} 4 | {% block app %} 5 |
    6 |
    7 |
    8 |
    9 |
    10 | 11 | 12 | [[ genre.type ]] 13 | 14 | 15 |
    16 |

    17 |
    18 |
    19 |
    20 |
    21 | 55 |
    56 |
    57 | {% endblock %} 58 | 59 | {% block script %} 60 | 94 | {% endblock %} -------------------------------------------------------------------------------- /movies/templates/movies/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% load humanize %} 4 | {% block app %} 5 | 6 | 53 |
    54 |
    55 | 56 |

    62 |

    63 |
    64 | {% include 'movies/_genre-cards.html' %} 65 |
    66 | {% endblock %} 67 | 68 | {% block script %} 69 | 96 | {% endblock %} -------------------------------------------------------------------------------- /movies/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /movies/urls.py: -------------------------------------------------------------------------------- 1 | from . import views 2 | from django.urls import path 3 | 4 | app_name = "movies" 5 | urlpatterns = [ 6 | path('', views.index, name="index"), 7 | path('genres/', views.genres_view, name="genres_view"), 8 | path('movies//', views.movies_view, name="movies_view"), 9 | path('api/genres/', views.genres_list, name="genres_list"), 10 | path('api/genres//', views.genres_detail, name="genres_detail"), 11 | path('api/movies/', views.movies_list, name="movies_list"), 12 | path('api/movies//', views.movies_detail, name="movies_detail"), 13 | path('api/movies//ratings/', views.ratings_list, name="ratings_list"), 14 | path('api/ratings//', views.ratings_detail, name="ratings_detail"), 15 | path('load/', views.load), 16 | ] 17 | -------------------------------------------------------------------------------- /movies/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, get_object_or_404 2 | from django.conf import settings 3 | from rest_framework import status 4 | from rest_framework.decorators import api_view 5 | from rest_framework.response import Response 6 | from .models import Genre, Movie, Rating 7 | from .serializers import GenreSerializer, MovieSerializer, RatingSerializer 8 | import json 9 | import os 10 | 11 | 12 | def index(request): 13 | movie_of_week = Movie.max_audience() 14 | return render(request, 'movies/home.html', {'movie': movie_of_week}) 15 | 16 | 17 | def movies_view(request, pk): 18 | movie = get_object_or_404(Movie, pk=pk) 19 | return render(request, 'movies/detail.html', {'movie': movie}) 20 | 21 | 22 | def genres_view(request): 23 | genre = get_object_or_404(Genre, type=request.GET.get('type')) 24 | return render(request, 'movies/genre.html', {'genre': genre}) 25 | 26 | 27 | @api_view(["GET"]) 28 | def genres_list(request): 29 | genres = Genre.objects.all() 30 | serializer = GenreSerializer(genres, many=True) 31 | return Response(serializer.data) 32 | 33 | 34 | @api_view(["GET"]) 35 | def genres_detail(request, pk): 36 | genre = get_object_or_404(Genre, pk=pk) 37 | serializer = GenreSerializer(genre) 38 | return Response(serializer.data) 39 | 40 | 41 | @api_view(["GET"]) 42 | def movies_list(request): 43 | movies = Movie.objects.all() 44 | serializer = MovieSerializer(movies, many=True) 45 | return Response(serializer.data) 46 | 47 | 48 | @api_view(["GET"]) 49 | def movies_detail(request, pk): 50 | movie = get_object_or_404(Movie, pk=pk) 51 | serializer = MovieSerializer(movie) 52 | return Response(serializer.data) 53 | 54 | 55 | @api_view(["GET", "POST"]) 56 | def ratings_list(request, pk): 57 | movie = get_object_or_404(Movie, pk=pk) 58 | if request.method == "GET": 59 | ratings = movie.ratings.all() 60 | serializer = RatingSerializer(ratings, many=True) 61 | return Response(serializer.data) 62 | else: 63 | if request.user.is_authenticated: 64 | request.data["movie"] = movie.id 65 | request.data["user"] = request.user.id 66 | serializer = RatingSerializer(data=request.data) 67 | if serializer.is_valid(raise_exception=True): 68 | serializer.save() 69 | return Response(serializer.data, status=status.HTTP_201_CREATED) 70 | else: 71 | return Response(status=status.HTTP_401_UNAUTHORIZED) 72 | 73 | 74 | @api_view(["GET", "DELETE", "PUT"]) 75 | def ratings_detail(request, pk): 76 | rating = get_object_or_404(Rating, pk=pk) 77 | if request.method == 'GET': 78 | serializer = RatingSerializer(rating) 79 | return Response(serializer.data) 80 | else: 81 | if request.user == rating.user: 82 | request.data["movie"] = rating.movie.id 83 | request.data["user"] = request.user.id 84 | if request.method == 'PUT': 85 | serializer = RatingSerializer(rating, data=request.data) 86 | if serializer.is_valid(raise_exception=True): 87 | serializer.save() 88 | return Response(serializer.data, status=status.HTTP_202_ACCEPTED) 89 | elif request.method == 'DELETE': 90 | rating.delete() 91 | return Response({"message": "삭제되었습니다."}, status=status.HTTP_204_NO_CONTENT) 92 | else: 93 | return Response(status=status.HTTP_401_UNAUTHORIZED) 94 | 95 | 96 | def load(request): 97 | data = [] 98 | json_data = os.path.join(settings.BASE_DIR, 'static', "data/data.json") 99 | with open(json_data, encoding='UTF-8') as f: 100 | data = json.load(f) 101 | for rec in data: 102 | # 'genre': rec['genre'].split('/'), 103 | context = { 104 | 'id': rec['id'], 105 | 'title': rec['title'], 106 | 'title_eng': rec['title_eng'], 107 | 'title_org': rec['title_org'], 108 | 'audience': rec['audience'], 109 | 'release': rec['release'], 110 | 'thumb_url': rec['thumb_url'], 111 | 'poster_url': rec['poster_url'], 112 | 'running_time': rec['running_time'], 113 | 'director': rec['director'], 114 | 'film_rating': rec['film_rating'], 115 | 'actor1': rec['actor1'], 116 | 'actor2': rec['actor2'], 117 | 'actor3': rec['actor3'], 118 | } 119 | movie = Movie.objects.create(**context) 120 | for genre_name in rec['genre'].split('/'): 121 | obj, created = Genre.objects.get_or_create(type=genre_name) 122 | movie.genres.add(obj) 123 | movie.save() 124 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | backcall==0.1.0 2 | colorama==0.4.1 3 | decorator==4.4.0 4 | Django==2.1.11 5 | django-bulma==0.5.6 6 | django-extensions==2.1.6 7 | djangorestframework==3.9.4 8 | ipython==7.5.0 9 | ipython-genutils==0.2.0 10 | jedi==0.13.3 11 | parso==0.4.0 12 | pickleshare==0.7.5 13 | prompt-toolkit==2.0.9 14 | Pygments==2.4.0 15 | pytz==2019.1 16 | six==1.12.0 17 | traitlets==4.3.2 18 | wcwidth==0.1.7 19 | -------------------------------------------------------------------------------- /static/css/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #0a0a0a; 3 | } 4 | 5 | #app { 6 | min-height: 100vh; 7 | padding: 0; 8 | margin: 0; 9 | margin-top: 5vh; 10 | background-color: #0a0a0a; 11 | } 12 | 13 | /* Nav */ 14 | 15 | #nav .menu-list { 16 | position: absolute; 17 | top: 9.6px; 18 | right: 0; 19 | width: 12vw; 20 | } 21 | 22 | .search-movie { 23 | background-color: white; 24 | margin: 1px; 25 | } 26 | 27 | .navbar-brand .navbar-item { 28 | font-family: "Gugi", cursvie; 29 | } 30 | 31 | .navbar-end .navbar-item { 32 | font-family: "Noto Sans KR", sans-serif; 33 | font-weight: 400; 34 | } 35 | 36 | /* Home */ 37 | .hero-title { 38 | font-family: "Gugi", cursive; 39 | color: #fff; 40 | } 41 | 42 | /* Movie Card */ 43 | .media-content > .title { 44 | color: #000; 45 | } 46 | 47 | .movie-card { 48 | font-family: "Noto Sans KR", sans-serif; 49 | border-radius: 10px; 50 | cursor: pointer; 51 | 52 | box-shadow: 0 15px 15px rgba(255, 255, 255, 0.15); 53 | -webkit-transition: all 250ms cubic-bezier(0.02, 0.01, 0.47, 1); 54 | -moz-transition: all 250ms cubic-bezier(0.02, 0.01, 0.47, 1); 55 | -o-transition: all 250ms cubic-bezier(0.02, 0.01, 0.47, 1); 56 | transition: all 250ms cubic-bezier(0.02, 0.01, 0.47, 1); 57 | } 58 | 59 | .movie-card:hover { 60 | box-shadow: 0 20px 20px rgba(255, 255, 255, 0.2); 61 | transform: translate(0, -10px); 62 | transition-delay: 0s !important; 63 | } 64 | 65 | .movie-card:hover .movie-title { 66 | text-shadow: 0 0 5px rgba(0, 0, 0, 0.3); 67 | } 68 | 69 | 70 | 71 | /* Genre Cards */ 72 | h1.title.genre-title.is-size-4 { 73 | font-family: "Gugi", cursive; 74 | color: #fff; 75 | } 76 | 77 | .highlight { 78 | display: inline; 79 | background: #000000a3; 80 | color: #fff; 81 | padding: 0.25em; 82 | } 83 | 84 | article.media.notification { 85 | border-radius: 10px; 86 | cursor: pointer; 87 | 88 | box-shadow: 0 10px 10px rgba(255, 255, 255, 0.1); 89 | -webkit-transition: all 250ms cubic-bezier(0.02, 0.01, 0.47, 1); 90 | -moz-transition: all 250ms cubic-bezier(0.02, 0.01, 0.47, 1); 91 | -o-transition: all 250ms cubic-bezier(0.02, 0.01, 0.47, 1); 92 | transition: all 250ms cubic-bezier(0.02, 0.01, 0.47, 1); 93 | } 94 | 95 | article.media.notification:hover { 96 | box-shadow: 0 15px 15px rgba(255, 255, 255, 0.1); 97 | transform: translate(0, -10px); 98 | transition-delay: 0s !important; 99 | } 100 | 101 | article.media.notification:hover h1.title.genre-title.is-size-4 { 102 | color: #000; 103 | } 104 | 105 | article.media.notification:hover .highlight { 106 | display: inline; 107 | background: #f5f5f582; 108 | color: #000; 109 | padding: 0.25em; 110 | } 111 | 112 | 113 | /* Detail */ 114 | .movie-detail .title { 115 | font-family: "Noto Sans KR", sans-serif; 116 | font-weight: 700; 117 | color: #fff; 118 | } 119 | 120 | .movie-description { 121 | font-family: "Noto Sans KR", sans-serif; 122 | font-weight: 300; 123 | color: #fff; 124 | } 125 | 126 | .gradient-box { 127 | border: 10px solid; 128 | border-image-source: linear-gradient(179deg, rgba(109, 19, 132, 1) 18.4%, rgba(245, 135, 75, 1) 108.3%); 129 | border-image-slice: 1; 130 | } 131 | 132 | .tags a:hover { 133 | text-decoration: none; 134 | } 135 | 136 | /* Comments */ 137 | .comments .content { 138 | font-family: "Noto Sans KR", sans-serif; 139 | font-weight: 300; 140 | color: #fff; 141 | } 142 | 143 | .star-rating { 144 | margin: 0; 145 | } 146 | 147 | /* Star rating */ 148 | .star-rating__star { 149 | display: inline-block; 150 | padding: 1px; 151 | vertical-align: middle; 152 | line-height: 1; 153 | font-size: 1.3em; 154 | color: #ABABAB; 155 | transition: color .2s ease-out; 156 | margin: 0; 157 | } 158 | 159 | .star-rating__checkbox { 160 | position: absolute; 161 | overflow: hidden; 162 | clip: rect(0 0 0 0); 163 | height: 1px; 164 | width: 1px; 165 | margin: 0; 166 | padding: 0; 167 | border: 0; 168 | } 169 | 170 | .star-rating:hover { 171 | cursor: pointer; 172 | } 173 | 174 | .star-rating .is-selected { 175 | color: #FFD700; 176 | } 177 | 178 | .star-rating .is-disabled:hover { 179 | cursor: default; 180 | } 181 | 182 | 183 | /* Signup */ 184 | .signup * { 185 | font-family: "Noto Sans KR", sans-serif; 186 | font-weight: 400; 187 | } 188 | 189 | .signup .title { 190 | font-family: 'Gugi', cursive; 191 | font-weight: bold; 192 | color: #fff; 193 | } 194 | 195 | .signup .label { 196 | color: #fff; 197 | } 198 | 199 | p.help { 200 | color: #fff; 201 | } 202 | 203 | #app > section > div > div > div > form > div:nth-child(4) > div > ul { 204 | color: #fff; 205 | font-size: 12px; 206 | } 207 | 208 | /* Login */ 209 | .login { 210 | font-family: "Noto Sans KR", sans-serif; 211 | font-weight: 400; 212 | } 213 | 214 | .login .title { 215 | font-family: 'Gugi', cursive; 216 | font-weight: bold; 217 | color: #fff; 218 | } 219 | 220 | .login .label { 221 | color: #fff; 222 | font-weight: 400; 223 | } 224 | 225 | /* Footer */ 226 | .footer p { 227 | font-family: "Noto Sans KR", sans-serif; 228 | font-weight: 300; 229 | } -------------------------------------------------------------------------------- /static/data/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 20195743, 4 | "genre": "애니메이션/판타지", 5 | "title": "신데렐라:마법 반지의 비밀", 6 | "title_eng": "Cinderella and The Secret Prince", 7 | "title_org": "", 8 | "audience": 281705, 9 | "release": "2019-03-06", 10 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1819/181959_P11_114544.jpg", 11 | "poster_url": "https://movie-phinf.pstatic.net/20190122_170/1548125137403TMFAP_JPEG/movie_image.jpg", 12 | "running_time": 87, 13 | "director": "린 사우더랜드", 14 | "film_rating": "전체관람가", 15 | "actor1": "사문영", 16 | "actor2": "김혜성", 17 | "actor3": "남도형" 18 | }, 19 | { 20 | "id": 20176103, 21 | "genre": "범죄", 22 | "title": "돈", 23 | "title_eng": "Money", 24 | "title_org": "", 25 | "audience": 3388756, 26 | "release": "2019-04-24", 27 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1815/181559_P04_111412.jpg", 28 | "poster_url": "https://movie-phinf.pstatic.net/20181219_162/1545185650147bHGa4_JPEG/movie_image.jpg", 29 | "running_time": 115, 30 | "director": "박누리", 31 | "film_rating": "15세이상관람가", 32 | "actor1": "류준열", 33 | "actor2": "유지태", 34 | "actor3": "조우진" 35 | }, 36 | { 37 | "id": 20197617, 38 | "genre": "애니메이션/어드벤처/판타지", 39 | "title": "정글북: 마법 원정대", 40 | "title_eng": "Savva", 41 | "title_org": "", 42 | "audience": 22534, 43 | "release": "2019-03-20", 44 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1490/149022_P12_110503.jpg", 45 | "poster_url": "https://movie-phinf.pstatic.net/20190313_16/1552442701021fC5IC_JPEG/movie_image.jpg", 46 | "running_time": 80, 47 | "director": "막심 파디브", 48 | "film_rating": "전체관람가", 49 | "actor1": "", 50 | "actor2": "", 51 | "actor3": "" 52 | }, 53 | { 54 | "id": 20199736, 55 | "genre": "애니메이션/어드벤처/코미디", 56 | "title": "프렌즈: 둥지탈출", 57 | "title_eng": "Manou the Swift", 58 | "title_org": "", 59 | "audience": 10178, 60 | "release": "2019-05-01", 61 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1842/184253_P10_155905.jpg", 62 | "poster_url": "https://movie-phinf.pstatic.net/20190419_139/1555657143756VspOd_JPEG/movie_image.jpg", 63 | "running_time": 87, 64 | "director": "크리스찬 하스", 65 | "film_rating": "전체관람가", 66 | "actor1": "", 67 | "actor2": "", 68 | "actor3": "" 69 | }, 70 | { 71 | "id": 20198982, 72 | "genre": "드라마", 73 | "title": "크게 될 놈", 74 | "title_eng": "", 75 | "title_org": "", 76 | "audience": 90320, 77 | "release": "2019-04-24", 78 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1598/159845_P12_102707.jpg", 79 | "poster_url": "https://movie-phinf.pstatic.net/20190325_23/15534772166529TroP_JPEG/movie_image.jpg", 80 | "running_time": 108, 81 | "director": "강지은", 82 | "film_rating": "15세이상관람가", 83 | "actor1": "김해숙", 84 | "actor2": "손호준", 85 | "actor3": "남보라" 86 | }, 87 | { 88 | "id": 20020222, 89 | "genre": "드라마", 90 | "title": "해리포터와 비밀의 방", 91 | "title_eng": "Harry Potter And The Chamber Of Secrets", 92 | "title_org": "", 93 | "audience": 79301, 94 | "release": "2019-02-20", 95 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/0339/C3930-06.jpg", 96 | "poster_url": "https://movie-phinf.pstatic.net/20111222_244/1324530400723IXN6V_JPEG/movie_image.jpg", 97 | "running_time": 161, 98 | "director": "크리스 콜럼버스", 99 | "film_rating": "전체관람가", 100 | "actor1": "다니엘 래드클리프", 101 | "actor2": "엠마 왓슨", 102 | "actor3": "루퍼트 그린트" 103 | }, 104 | { 105 | "id": 20196244, 106 | "genre": "액션", 107 | "title": "샤잠!", 108 | "title_eng": "SHAZAM!", 109 | "title_org": "", 110 | "audience": 636971, 111 | "release": "2019-04-10", 112 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1406/140649_P11_111034.jpg", 113 | "poster_url": "https://movie-phinf.pstatic.net/20190322_198/1553220632606va06v_JPEG/movie_image.jpg", 114 | "running_time": 131, 115 | "director": "데이비드 F. 샌드버그", 116 | "film_rating": "12세이상관람가", 117 | "actor1": "제커리 리바이", 118 | "actor2": "애셔 앤젤", 119 | "actor3": "잭 딜런 그레이져" 120 | }, 121 | { 122 | "id": 20184082, 123 | "genre": "코미디/액션", 124 | "title": "걸캅스", 125 | "title_eng": "Miss & Mrs. Cops", 126 | "title_org": "", 127 | "audience": 591974, 128 | "release": "2019-05-08", 129 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1740/174065_P44_174038.jpg", 130 | "poster_url": "https://movie-phinf.pstatic.net/20190418_272/155557683107045leS_JPEG/movie_image.jpg", 131 | "running_time": 107, 132 | "director": "정다원", 133 | "film_rating": "15세이상관람가", 134 | "actor1": "라미란", 135 | "actor2": "이성경", 136 | "actor3": "윤상현" 137 | }, 138 | { 139 | "id": 20198603, 140 | "genre": "애니메이션/액션/판타지", 141 | "title": "페이트 스테이 나이트 헤븐즈필 제2장 로스트 버터플라이", 142 | "title_eng": "Fate/stay night [Heaven's Feel] II.lost butterfly", 143 | "title_org": "", 144 | "audience": 24340, 145 | "release": "2019-03-20", 146 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1692/169263_P01_094955.jpg", 147 | "poster_url": "https://movie-phinf.pstatic.net/20190306_199/15518333925794IRYd_JPEG/movie_image.jpg", 148 | "running_time": 117, 149 | "director": "스도 토모노리", 150 | "film_rating": "15세이상관람가", 151 | "actor1": "스기야마 노리아키", 152 | "actor2": "시타야 노리코", 153 | "actor3": "카미야 히로시" 154 | }, 155 | { 156 | "id": 20195915, 157 | "genre": "공포(호러)/코미디", 158 | "title": "해피 데스데이 2 유", 159 | "title_eng": "Happy Death Day 2 U", 160 | "title_org": "", 161 | "audience": 392397, 162 | "release": "2019-02-20", 163 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1814/181411_P02_101349.jpg", 164 | "poster_url": "https://movie-phinf.pstatic.net/20190121_116/1548033223659mzXL5_JPEG/movie_image.jpg", 165 | "running_time": 100, 166 | "director": "크리스토퍼 랜던", 167 | "film_rating": "15세이상관람가", 168 | "actor1": "제시카 로테", 169 | "actor2": "이스라엘 브로우사드", 170 | "actor3": "" 171 | }, 172 | { 173 | "id": 20197283, 174 | "genre": "액션/공포(호러)/스릴러", 175 | "title": "이스케이프 룸", 176 | "title_eng": "The Escape Room", 177 | "title_org": "", 178 | "audience": 544462, 179 | "release": "2019-03-20", 180 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1804/180425_P19_175210.jpg", 181 | "poster_url": "https://movie-phinf.pstatic.net/20190308_234/1552035128139wsiTd_JPEG/movie_image.jpg", 182 | "running_time": 99, 183 | "director": "애덤 로비텔", 184 | "film_rating": "15세이상관람가", 185 | "actor1": "테일러 러셀", 186 | "actor2": "로건 밀러", 187 | "actor3": "제이 엘리스" 188 | }, 189 | { 190 | "id": 20197846, 191 | "genre": "범죄/드라마", 192 | "title": "악질경찰", 193 | "title_eng": "Jo Pil-ho: The Dawning Rage", 194 | "title_org": "", 195 | "audience": 254451, 196 | "release": "2019-03-27", 197 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1535/153580_P24_103637.jpg", 198 | "poster_url": "https://movie-phinf.pstatic.net/20190312_100/1552354595583gzgz1_JPEG/movie_image.jpg", 199 | "running_time": 127, 200 | "director": "이정범", 201 | "film_rating": "청소년관람불가", 202 | "actor1": "이선균", 203 | "actor2": "전소니", 204 | "actor3": "박해준" 205 | }, 206 | { 207 | "id": 20060347, 208 | "genre": "드라마/판타지/전쟁", 209 | "title": "판의 미로 - 오필리아와 세 개의 열쇠", 210 | "title_eng": "Pan's Labyrinth", 211 | "title_org": "El Laberinto Del Fauno", 212 | "audience": 482423, 213 | "release": "2019-05-01", 214 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/0493/49336_P59_105053.jpg", 215 | "poster_url": "https://movie-phinf.pstatic.net/20190409_174/1554774642995SWGiz_JPEG/movie_image.jpg", 216 | "running_time": 118, 217 | "director": "길예르모 델 토로", 218 | "film_rating": "15세이상관람가", 219 | "actor1": "이바나 바쿠에로", 220 | "actor2": "더그 존스", 221 | "actor3": "세르지 로페즈" 222 | }, 223 | { 224 | "id": 20182501, 225 | "genre": "공포(호러)/스릴러", 226 | "title": "왓칭", 227 | "title_eng": "Watching", 228 | "title_org": "", 229 | "audience": 46139, 230 | "release": "2019-04-17", 231 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1736/173667_P27_101947.jpg", 232 | "poster_url": "https://movie-phinf.pstatic.net/20190326_187/1553563141974kMLat_JPEG/movie_image.jpg", 233 | "running_time": 97, 234 | "director": "김성기", 235 | "film_rating": "15세이상관람가", 236 | "actor1": "강예원", 237 | "actor2": "이학주", 238 | "actor3": "주석태" 239 | }, 240 | { 241 | "id": 20198146, 242 | "genre": "드라마", 243 | "title": "콜레트", 244 | "title_eng": "COLETTE", 245 | "title_org": "", 246 | "audience": 35223, 247 | "release": "2019-04-03", 248 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1715/171533_P29_143351.jpg", 249 | "poster_url": "https://movie-phinf.pstatic.net/20190327_10/1553664829865BN4HO_JPEG/movie_image.jpg", 250 | "running_time": 112, 251 | "director": "워시 웨스트모어랜드", 252 | "film_rating": "15세이상관람가", 253 | "actor1": "키이라 나이틀리", 254 | "actor2": "도미닉 웨스트", 255 | "actor3": "엘리너 톰린슨" 256 | }, 257 | { 258 | "id": 20175407, 259 | "genre": "스릴러", 260 | "title": "우상", 261 | "title_eng": "Idol", 262 | "title_org": "", 263 | "audience": 175481, 264 | "release": "2019-03-27", 265 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1619/161984_P61_144815.jpg", 266 | "poster_url": "https://movie-phinf.pstatic.net/20190306_44/1551851285506NYfPC_JPEG/movie_image.jpg", 267 | "running_time": 143, 268 | "director": "이수진", 269 | "film_rating": "15세이상관람가", 270 | "actor1": "한석규", 271 | "actor2": "설경구", 272 | "actor3": "천우희" 273 | }, 274 | { 275 | "id": 20162003, 276 | "genre": "애니메이션/미스터리", 277 | "title": "명탐정 코난:전율의 악보", 278 | "title_eng": "Detective Conan: Full Score of Fear", 279 | "title_org": "", 280 | "audience": 83376, 281 | "release": "2019-02-13", 282 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/0482/48233_P05_140218.jpg", 283 | "poster_url": "https://movie-phinf.pstatic.net/20190104_172/1546578136456YlUhI_JPEG/movie_image.jpg", 284 | "running_time": 115, 285 | "director": "야마모토 야스이치로", 286 | "film_rating": "12세이상관람가", 287 | "actor1": "타카야마 미나미", 288 | "actor2": "야마자키 와카나", 289 | "actor3": "카미야 아키라" 290 | }, 291 | { 292 | "id": 20188941, 293 | "genre": "범죄/액션", 294 | "title": "악인전", 295 | "title_eng": "The Gangster, The Cop, The Devil", 296 | "title_org": "", 297 | "audience": 21280, 298 | "release": "2019-05-08", 299 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1779/177967_P30_105028.jpg", 300 | "poster_url": "https://movie-phinf.pstatic.net/20190426_228/15562434221140zLbL_JPEG/movie_image.jpg", 301 | "running_time": 109, 302 | "director": "이원태", 303 | "film_rating": "청소년관람불가", 304 | "actor1": "마동석", 305 | "actor2": "김무열", 306 | "actor3": "김성규" 307 | }, 308 | { 309 | "id": 20185406, 310 | "genre": "액션/어드벤처/판타지", 311 | "title": "헬보이", 312 | "title_eng": "Hellboy", 313 | "title_org": "", 314 | "audience": 311910, 315 | "release": "2019-04-17", 316 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1642/164200_P20_112727.jpg", 317 | "poster_url": "https://movie-phinf.pstatic.net/20190404_205/155434484507891fje_JPEG/movie_image.jpg", 318 | "running_time": 120, 319 | "director": "닐 마샬", 320 | "film_rating": "청소년관람불가", 321 | "actor1": "데이빗 하버", 322 | "actor2": "밀라 요보비치", 323 | "actor3": "이안 맥쉐인" 324 | }, 325 | { 326 | "id": 20185352, 327 | "genre": "공포(호러)/스릴러", 328 | "title": "어스", 329 | "title_eng": "Us", 330 | "title_org": "", 331 | "audience": 1450903, 332 | "release": "2019-04-10", 333 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1814/181414_P24_174349.jpg", 334 | "poster_url": "https://movie-phinf.pstatic.net/20190329_232/1553849027701K0DBy_JPEG/movie_image.jpg", 335 | "running_time": 116, 336 | "director": "조던 필", 337 | "film_rating": "15세이상관람가", 338 | "actor1": "루피타 뇽", 339 | "actor2": "윈스턴 듀크", 340 | "actor3": "엘리자베스 모스" 341 | }, 342 | { 343 | "id": 20197483, 344 | "genre": "다큐멘터리", 345 | "title": "1919 유관순", 346 | "title_eng": "", 347 | "title_org": "", 348 | "audience": 46858, 349 | "release": "2019-03-27", 350 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1824/182407_P00_105405.jpg", 351 | "poster_url": "https://movie-phinf.pstatic.net/20190222_78/1550800424424qWbEp_JPEG/movie_image.jpg", 352 | "running_time": 79, 353 | "director": "신상민", 354 | "film_rating": "12세이상관람가", 355 | "actor1": "이새봄", 356 | "actor2": "양윤희", 357 | "actor3": "김나니" 358 | }, 359 | { 360 | "id": 20187981, 361 | "genre": "드라마", 362 | "title": "배심원들", 363 | "title_eng": "Juror 8", 364 | "title_org": "", 365 | "audience": 23716, 366 | "release": "2019-05-08", 367 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1774/177483_P85_164427.jpg", 368 | "poster_url": "https://movie-phinf.pstatic.net/20190513_73/1557733465293c0tYc_JPEG/movie_image.jpg", 369 | "running_time": 113, 370 | "director": "홍승완", 371 | "film_rating": "12세이상관람가", 372 | "actor1": "문소리", 373 | "actor2": "박형식", 374 | "actor3": "백수장" 375 | }, 376 | { 377 | "id": 20197423, 378 | "genre": "공포(호러)/스릴러", 379 | "title": "공포의 묘지", 380 | "title_eng": "Pet Sematary", 381 | "title_org": "", 382 | "audience": 87134, 383 | "release": "2019-04-10", 384 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1814/181419_P21_142511.jpg", 385 | "poster_url": "https://movie-phinf.pstatic.net/20190410_263/1554873909936kYTuV_JPEG/movie_image.jpg", 386 | "running_time": 100, 387 | "director": "케빈 콜쉬", 388 | "film_rating": "15세이상관람가", 389 | "actor1": "제이슨 클락", 390 | "actor2": "존 리스고", 391 | "actor3": "에이미 세이메츠" 392 | }, 393 | { 394 | "id": 20180868, 395 | "genre": "범죄/액션", 396 | "title": "뺑반", 397 | "title_eng": "Hit-and-Run Squad", 398 | "title_org": "", 399 | "audience": 1821147, 400 | "release": "2019-02-13", 401 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1641/164173_P45_171744.jpg", 402 | "poster_url": "https://movie-phinf.pstatic.net/20190115_40/15475402622236yHgr_JPEG/movie_image.jpg", 403 | "running_time": 133, 404 | "director": "한준희", 405 | "film_rating": "15세이상관람가", 406 | "actor1": "공효진", 407 | "actor2": "류준열", 408 | "actor3": "조정석" 409 | }, 410 | { 411 | "id": 20166301, 412 | "genre": "가족/멜로/로맨스/코미디", 413 | "title": "썬키스 패밀리", 414 | "title_eng": "Sunkist Family", 415 | "title_org": "", 416 | "audience": 26086, 417 | "release": "2019-03-27", 418 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1526/152624_P34_161443.jpg", 419 | "poster_url": "https://movie-phinf.pstatic.net/20190313_154/15524612817421mgYP_JPEG/movie_image.jpg", 420 | "running_time": 105, 421 | "director": "김지혜", 422 | "film_rating": "15세이상관람가", 423 | "actor1": "박희순", 424 | "actor2": "진경", 425 | "actor3": "황우슬혜" 426 | }, 427 | { 428 | "id": 20185881, 429 | "genre": "드라마", 430 | "title": "증인", 431 | "title_eng": "Innocent Witness", 432 | "title_org": "", 433 | "audience": 2519150, 434 | "release": "2019-03-13", 435 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1773/177374_P100_112832.jpg", 436 | "poster_url": "https://movie-phinf.pstatic.net/20190226_194/15511480897441tE6t_JPEG/movie_image.jpg", 437 | "running_time": 129, 438 | "director": "이한", 439 | "film_rating": "12세이상관람가", 440 | "actor1": "정우성", 441 | "actor2": "김향기", 442 | "actor3": "이규형" 443 | }, 444 | { 445 | "id": 20189463, 446 | "genre": "애니메이션/어드벤처/코미디/가족", 447 | "title": "주먹왕 랄프 2: 인터넷 속으로", 448 | "title_eng": "Ralph Breaks the Internet", 449 | "title_org": "", 450 | "audience": 1754950, 451 | "release": "2019-01-30", 452 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1526/152632_P19_104759.jpg", 453 | "poster_url": "https://movie-phinf.pstatic.net/20181204_287/1543888062606NHMh7_JPEG/movie_image.jpg", 454 | "running_time": 112, 455 | "director": "리치 무어", 456 | "film_rating": "전체관람가", 457 | "actor1": "존 C. 라일리", 458 | "actor2": "사라 실버맨", 459 | "actor3": "맨디 무어" 460 | }, 461 | { 462 | "id": 20184888, 463 | "genre": "판타지/뮤지컬", 464 | "title": "메리 포핀스 리턴즈", 465 | "title_eng": "Mary Poppins Returns", 466 | "title_org": "", 467 | "audience": 199519, 468 | "release": "2019-02-20", 469 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1511/151151_P24_105029.jpg", 470 | "poster_url": "https://movie-phinf.pstatic.net/20190114_278/1547430628262QsUaV_JPEG/movie_image.jpg", 471 | "running_time": 130, 472 | "director": "롭 마샬", 473 | "film_rating": "전체관람가", 474 | "actor1": "에밀리 블런트", 475 | "actor2": "린-마누엘 미란다", 476 | "actor3": "벤 위쇼" 477 | }, 478 | { 479 | "id": 20195791, 480 | "genre": "멜로/로맨스/코미디", 481 | "title": "어쩌다, 결혼", 482 | "title_eng": "Trade Your Love", 483 | "title_org": "", 484 | "audience": 70827, 485 | "release": "2019-02-27", 486 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1676/167629_P17_105046.jpg", 487 | "poster_url": "https://movie-phinf.pstatic.net/20190122_192/1548121844125KdJDJ_JPEG/movie_image.jpg", 488 | "running_time": 87, 489 | "director": "박수진", 490 | "film_rating": "12세이상관람가", 491 | "actor1": "김동욱", 492 | "actor2": "고성희", 493 | "actor3": "황보라" 494 | }, 495 | { 496 | "id": 20188282, 497 | "genre": "액션/어드벤처/멜로/로맨스/SF/스릴러", 498 | "title": "알리타: 배틀 엔젤", 499 | "title_eng": "Alita: Battle Angel", 500 | "title_org": "", 501 | "audience": 1946206, 502 | "release": "2019-02-27", 503 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/0667/66728_P38_102726.jpg", 504 | "poster_url": "https://movie-phinf.pstatic.net/20190128_22/1548638810964Go9Wy_JPEG/movie_image.jpg", 505 | "running_time": 121, 506 | "director": "로버트 로드리게즈", 507 | "film_rating": "12세이상관람가", 508 | "actor1": "크리스토프 왈츠", 509 | "actor2": "에드 스크레인", 510 | "actor3": "" 511 | }, 512 | { 513 | "id": 20183096, 514 | "genre": "드라마", 515 | "title": "생일", 516 | "title_eng": "Birthday", 517 | "title_org": "", 518 | "audience": 1191115, 519 | "release": "2019-05-01", 520 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1740/174050_P32_172314.jpg", 521 | "poster_url": "https://movie-phinf.pstatic.net/20190329_223/1553847778414CTAcF_JPEG/movie_image.jpg", 522 | "running_time": 120, 523 | "director": "이종언", 524 | "film_rating": "전체관람가", 525 | "actor1": "설경구", 526 | "actor2": "전도연", 527 | "actor3": "김보민" 528 | }, 529 | { 530 | "id": 20184896, 531 | "genre": "드라마", 532 | "title": "가버나움", 533 | "title_eng": "Capharnaum", 534 | "title_org": "Capernaum", 535 | "audience": 87787, 536 | "release": "2019-02-06", 537 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1748/174830_P17_104205.jpg", 538 | "poster_url": "https://movie-phinf.pstatic.net/20190109_149/1546998123676c6LjJ_JPEG/movie_image.jpg", 539 | "running_time": 126, 540 | "director": "나딘 라바키", 541 | "film_rating": "15세이상관람가", 542 | "actor1": "자인 알 라피아", 543 | "actor2": "요르다노스 시프로우", 544 | "actor3": "보루와티프 트레저 반콜" 545 | }, 546 | { 547 | "id": 20198403, 548 | "genre": "드라마/멜로/로맨스", 549 | "title": "파이브 피트", 550 | "title_eng": "Five Feet Apart", 551 | "title_org": "", 552 | "audience": 67395, 553 | "release": "2019-04-10", 554 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1819/181912_P10_135327.jpg", 555 | "poster_url": "https://movie-phinf.pstatic.net/20190405_72/155444000594589QMA_JPEG/movie_image.jpg", 556 | "running_time": 116, 557 | "director": "저스틴 밸도니", 558 | "film_rating": "15세이상관람가", 559 | "actor1": "콜 스프로즈", 560 | "actor2": "헤일리 루 리차드슨", 561 | "actor3": "클레어 폴라니" 562 | }, 563 | { 564 | "id": 20185478, 565 | "genre": "애니메이션", 566 | "title": "극장판 헬로카봇: 옴파로스 섬의 비밀", 567 | "title_eng": "Hello Carbot The Movie: The Secret of Omphalos Island", 568 | "title_org": "", 569 | "audience": 558144, 570 | "release": "2019-02-13", 571 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1815/181554_P23_112202.jpg", 572 | "poster_url": "https://movie-phinf.pstatic.net/20190207_107/1549506120933tsuBH_JPEG/movie_image.jpg", 573 | "running_time": 78, 574 | "director": "김진철", 575 | "film_rating": "전체관람가", 576 | "actor1": "이지현", 577 | "actor2": "김용준", 578 | "actor3": "양정화" 579 | }, 580 | { 581 | "id": 20197092, 582 | "genre": "멜로/로맨스", 583 | "title": "로망", 584 | "title_eng": "Romang", 585 | "title_org": "", 586 | "audience": 46938, 587 | "release": "2019-04-03", 588 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1823/182348_P02_174720.jpg", 589 | "poster_url": "https://movie-phinf.pstatic.net/20190306_13/1551862037635qnE0r_JPEG/movie_image.jpg", 590 | "running_time": 111, 591 | "director": "이창근", 592 | "film_rating": "전체관람가", 593 | "actor1": "이순재", 594 | "actor2": "정영숙", 595 | "actor3": "조한철" 596 | }, 597 | { 598 | "id": 20196025, 599 | "genre": "가족/판타지", 600 | "title": "덤보", 601 | "title_eng": "Dumbo", 602 | "title_org": "", 603 | "audience": 296253, 604 | "release": "2019-04-03", 605 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1637/163787_P33_174808.jpg", 606 | "poster_url": "https://movie-phinf.pstatic.net/20190328_112/1553762886182QHDpG_JPEG/movie_image.jpg", 607 | "running_time": 111, 608 | "director": "팀 버튼", 609 | "film_rating": "전체관람가", 610 | "actor1": "콜린 파렐", 611 | "actor2": "마이클 키튼", 612 | "actor3": "대니 드비토" 613 | }, 614 | { 615 | "id": 20185576, 616 | "genre": "액션", 617 | "title": "콜드 체이싱", 618 | "title_eng": "Cold Pursuit", 619 | "title_org": "", 620 | "audience": 78303, 621 | "release": "2019-02-20", 622 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1814/181409_P30_100656.jpg", 623 | "poster_url": "https://movie-phinf.pstatic.net/20190225_25/1551056804750UglaU_JPEG/movie_image.jpg", 624 | "running_time": 118, 625 | "director": "한스 페터 몰란트", 626 | "film_rating": "15세이상관람가", 627 | "actor1": "리암 니슨", 628 | "actor2": "톰 베이트먼", 629 | "actor3": "윌리엄 포사이스" 630 | }, 631 | { 632 | "id": 20186261, 633 | "genre": "애니메이션", 634 | "title": "드래곤 길들이기 3", 635 | "title_eng": "How to Train Your Dragon 3", 636 | "title_org": "How to Train Your Dragon: The Hidden World", 637 | "audience": 1498759, 638 | "release": "2019-02-27", 639 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1091/109193_P21_103935.jpg", 640 | "poster_url": "https://movie-phinf.pstatic.net/20190131_168/1548898773611USlT7_JPEG/movie_image.jpg", 641 | "running_time": 104, 642 | "director": "딘 데블로이스", 643 | "film_rating": "전체관람가", 644 | "actor1": "제이 바루첼", 645 | "actor2": "아메리카 페레라", 646 | "actor3": "케이트 블란쳇" 647 | }, 648 | { 649 | "id": 20177282, 650 | "genre": "애니메이션/어드벤처/가족", 651 | "title": "리노", 652 | "title_eng": "Lino - Adventure of Seven Lives", 653 | "title_org": "Lino: Uma Aventura de Sete Vidas", 654 | "audience": 16489, 655 | "release": "2019-03-06", 656 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1709/170953_P08_140411.jpg", 657 | "poster_url": "https://movie-phinf.pstatic.net/20190221_158/1550725449727GgQaK_JPEG/movie_image.jpg", 658 | "running_time": 88, 659 | "director": "라파엘 리바스", 660 | "film_rating": "전체관람가", 661 | "actor1": "", 662 | "actor2": "", 663 | "actor3": "" 664 | }, 665 | { 666 | "id": 20183454, 667 | "genre": "공포(호러)", 668 | "title": "요로나의 저주", 669 | "title_eng": "The Curse of La Llorona", 670 | "title_org": "", 671 | "audience": 197127, 672 | "release": "2019-04-24", 673 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1803/180399_P00_154546.jpg", 674 | "poster_url": "https://movie-phinf.pstatic.net/20190403_179/1554273944526iPln5_JPEG/movie_image.jpg", 675 | "running_time": 93, 676 | "director": "마이클 차베즈", 677 | "film_rating": "15세이상관람가", 678 | "actor1": "린다 카델리니", 679 | "actor2": "패트리시아 벨라즈퀘즈", 680 | "actor3": "레이몬드 크루즈" 681 | }, 682 | { 683 | "id": 20197147, 684 | "genre": "애니메이션", 685 | "title": "어글리 돌", 686 | "title_eng": "UglyDolls", 687 | "title_org": "UGLYDOLLS", 688 | "audience": 50816, 689 | "release": "2019-05-08", 690 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1820/182012_P08_135215.jpg", 691 | "poster_url": "https://movie-phinf.pstatic.net/20190426_71/1556254332881AWqUn_JPEG/movie_image.jpg", 692 | "running_time": 87, 693 | "director": "캘리 애스버리", 694 | "film_rating": "전체관람가", 695 | "actor1": "켈리 클락슨", 696 | "actor2": "닉 조나스", 697 | "actor3": "자넬 모네" 698 | }, 699 | { 700 | "id": 20198844, 701 | "genre": "액션/드라마/스릴러", 702 | "title": "아이 엠 마더", 703 | "title_eng": "Peppermint", 704 | "title_org": "", 705 | "audience": 83953, 706 | "release": "2019-04-17", 707 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1830/183063_P01_142452.jpg", 708 | "poster_url": "https://movie-phinf.pstatic.net/20190131_194/1548912288294lPeOb_JPEG/movie_image.jpg", 709 | "running_time": 101, 710 | "director": "피에르 모렐", 711 | "film_rating": "15세이상관람가", 712 | "actor1": "제니퍼 가너", 713 | "actor2": "존 갤러거 주니어", 714 | "actor3": "" 715 | }, 716 | { 717 | "id": 20180937, 718 | "genre": "드라마", 719 | "title": "더 페이버릿: 여왕의 여자", 720 | "title_eng": "THE FAVOURITE", 721 | "title_org": "", 722 | "audience": 133508, 723 | "release": "2019-03-06", 724 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1489/148909_P29_164758.jpg", 725 | "poster_url": "https://movie-phinf.pstatic.net/20190214_257/1550130475283pkbtF_JPEG/movie_image.jpg", 726 | "running_time": 119, 727 | "director": "요르고스 란티모스", 728 | "film_rating": "15세이상관람가", 729 | "actor1": "올리비아 콜맨", 730 | "actor2": "엠마 스톤", 731 | "actor3": "레이첼 와이즈" 732 | }, 733 | { 734 | "id": 20169323, 735 | "genre": "다큐멘터리", 736 | "title": "안도 타다오", 737 | "title_eng": "Samurai Architect Tadao Ando", 738 | "title_org": "", 739 | "audience": 25541, 740 | "release": "2019-05-08", 741 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1488/148863_P10_094956.jpg", 742 | "poster_url": "https://movie-phinf.pstatic.net/20190329_257/1553820591251NTNwg_JPEG/movie_image.jpg", 743 | "running_time": 73, 744 | "director": "미즈노 시게노리", 745 | "film_rating": "전체관람가", 746 | "actor1": "", 747 | "actor2": "", 748 | "actor3": "" 749 | }, 750 | { 751 | "id": 20183773, 752 | "genre": "멜로/로맨스", 753 | "title": "장난스런 키스", 754 | "title_eng": "Fall in Love at First Kiss", 755 | "title_org": "一吻定情", 756 | "audience": 390548, 757 | "release": "2019-04-10", 758 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1803/180381_P17_103533.jpg", 759 | "poster_url": "https://movie-phinf.pstatic.net/20190305_257/1551749723722A0Gw5_JPEG/movie_image.jpg", 760 | "running_time": 122, 761 | "director": "프랭키 첸", 762 | "film_rating": "12세이상관람가", 763 | "actor1": "왕대륙", 764 | "actor2": "임윤", 765 | "actor3": "" 766 | }, 767 | { 768 | "id": 20196723, 769 | "genre": "애니메이션/어드벤처/코미디", 770 | "title": "숲속왕국의 꿀벌 여왕", 771 | "title_eng": "Tall Tales", 772 | "title_org": "", 773 | "audience": 18305, 774 | "release": "2019-03-13", 775 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1822/182206_P12_121114.jpg", 776 | "poster_url": "https://movie-phinf.pstatic.net/20190222_80/1550805069992i1MXH_JPEG/movie_image.jpg", 777 | "running_time": 87, 778 | "director": "아르나드 부롱", 779 | "film_rating": "전체관람가", 780 | "actor1": "최원형", 781 | "actor2": "장은숙", 782 | "actor3": "안종덕" 783 | }, 784 | { 785 | "id": 20196601, 786 | "genre": "드라마", 787 | "title": "항거:유관순 이야기", 788 | "title_eng": "A Resistance", 789 | "title_org": "", 790 | "audience": 1143978, 791 | "release": "2019-03-20", 792 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1823/182360_P11_140223.jpg", 793 | "poster_url": "https://movie-phinf.pstatic.net/20190207_119/1549515732449nv5tM_JPEG/movie_image.jpg", 794 | "running_time": 105, 795 | "director": "조민호", 796 | "film_rating": "12세이상관람가", 797 | "actor1": "고아성", 798 | "actor2": "김새벽", 799 | "actor3": "김예은" 800 | }, 801 | { 802 | "id": 20177539, 803 | "genre": "미스터리/스릴러", 804 | "title": "사바하", 805 | "title_eng": "SVAHA : THE SIXTH FINGER", 806 | "title_org": "", 807 | "audience": 2389760, 808 | "release": "2019-03-13", 809 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1670/167099_P13_101134.jpg", 810 | "poster_url": "https://movie-phinf.pstatic.net/20190128_284/1548637887932Dwfkj_JPEG/movie_image.jpg", 811 | "running_time": 122, 812 | "director": "장재현", 813 | "film_rating": "15세이상관람가", 814 | "actor1": "이정재", 815 | "actor2": "박정민", 816 | "actor3": "이재인" 817 | }, 818 | { 819 | "id": 20185124, 820 | "genre": "공연", 821 | "title": "러브 유어셀프 인 서울", 822 | "title_eng": "LOVE YOURSELF IN SEOUL", 823 | "title_org": "", 824 | "audience": 342366, 825 | "release": "2019-02-06", 826 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1802/180220_P01_102406.jpg", 827 | "poster_url": "https://movie-phinf.pstatic.net/20181214_60/15447506226141Uiod_JPEG/movie_image.jpg", 828 | "running_time": 111, 829 | "director": "", 830 | "film_rating": "전체관람가", 831 | "actor1": "김남준", 832 | "actor2": "김석진", 833 | "actor3": "민윤기" 834 | }, 835 | { 836 | "id": 19990220, 837 | "genre": "멜로/로맨스/코미디", 838 | "title": "노팅 힐", 839 | "title_eng": "Notting Hill", 840 | "title_org": "Notting Hill", 841 | "audience": 49958, 842 | "release": "2019-04-24", 843 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/0248/24830_P18_133358.jpg", 844 | "poster_url": "https://movie-phinf.pstatic.net/20190325_31/1553488426511DoXdd_JPEG/movie_image.jpg", 845 | "running_time": 124, 846 | "director": "로저 미첼", 847 | "film_rating": "12세관람가", 848 | "actor1": "줄리아 로버츠", 849 | "actor2": "휴 그랜트", 850 | "actor3": "" 851 | }, 852 | { 853 | "id": 20198183, 854 | "genre": "애니메이션/가족", 855 | "title": "뽀로로 극장판 보물섬 대모험", 856 | "title_eng": "Treasure Island Adventure", 857 | "title_org": "", 858 | "audience": 721423, 859 | "release": "2019-05-08", 860 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1831/183136_P10_134628.jpg", 861 | "poster_url": "https://movie-phinf.pstatic.net/20190329_266/15538347866861YXl3_JPEG/movie_image.jpg", 862 | "running_time": 79, 863 | "director": "김현호", 864 | "film_rating": "전체관람가", 865 | "actor1": "이선", 866 | "actor2": "이미자", 867 | "actor3": "김현지" 868 | }, 869 | { 870 | "id": 20197356, 871 | "genre": "드라마", 872 | "title": "라스트 미션", 873 | "title_eng": "The Mule", 874 | "title_org": "", 875 | "audience": 93476, 876 | "release": "2019-03-20", 877 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1799/179974_P01_153621.jpg", 878 | "poster_url": "https://movie-phinf.pstatic.net/20190211_224/1549866975787EcmzO_JPEG/movie_image.jpg", 879 | "running_time": 116, 880 | "director": "클린트 이스트우드", 881 | "film_rating": "15세이상관람가", 882 | "actor1": "클린트 이스트우드", 883 | "actor2": "타이사 파미가", 884 | "actor3": "브래들리 쿠퍼" 885 | }, 886 | { 887 | "id": 20176442, 888 | "genre": "드라마", 889 | "title": "자전차왕 엄복동", 890 | "title_eng": "Race to Freedom : Um Bok Dong", 891 | "title_org": "", 892 | "audience": 168884, 893 | "release": "2019-03-06", 894 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1590/159070_P13_114738.jpg", 895 | "poster_url": "https://movie-phinf.pstatic.net/20190201_136/1548989242154HiJy8_JPEG/movie_image.jpg", 896 | "running_time": 116, 897 | "director": "김유성", 898 | "film_rating": "12세이상관람가", 899 | "actor1": "정지훈", 900 | "actor2": "강소라", 901 | "actor3": "이범수" 902 | }, 903 | { 904 | "id": 20182530, 905 | "genre": "코미디", 906 | "title": "극한직업", 907 | "title_eng": "Extreme Job", 908 | "title_org": "", 909 | "audience": 16243233, 910 | "release": "2019-03-13", 911 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1676/167651_P106_141030.jpg", 912 | "poster_url": "https://movie-phinf.pstatic.net/20190116_206/1547615429111dINWj_JPEG/movie_image.jpg", 913 | "running_time": 111, 914 | "director": "이병헌", 915 | "film_rating": "15세이상관람가", 916 | "actor1": "류승룡", 917 | "actor2": "이하늬", 918 | "actor3": "진선규" 919 | }, 920 | { 921 | "id": 20181877, 922 | "genre": "액션/어드벤처/SF", 923 | "title": "캡틴 마블", 924 | "title_eng": "Captain Marvel", 925 | "title_org": "", 926 | "audience": 5796057, 927 | "release": "2019-05-01", 928 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1326/132623_P22_133853.jpg", 929 | "poster_url": "https://movie-phinf.pstatic.net/20190225_72/1551069530582h2huX_JPEG/movie_image.jpg", 930 | "running_time": 123, 931 | "director": "애너 보든", 932 | "film_rating": "12세이상관람가", 933 | "actor1": "브리 라슨", 934 | "actor2": "사무엘 L. 잭슨", 935 | "actor3": "벤 멘델슨" 936 | }, 937 | { 938 | "id": 20176251, 939 | "genre": "판타지/코미디", 940 | "title": "내안의 그놈", 941 | "title_eng": "The Dude in Me", 942 | "title_org": "", 943 | "audience": 1910802, 944 | "release": "2019-01-30", 945 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1641/164172_P08_114039.jpg", 946 | "poster_url": "https://movie-phinf.pstatic.net/20181204_172/1543891228332kV3d1_JPEG/movie_image.jpg", 947 | "running_time": 122, 948 | "director": "강효진", 949 | "film_rating": "15세이상관람가", 950 | "actor1": "박성웅", 951 | "actor2": "진영", 952 | "actor3": "라미란" 953 | }, 954 | { 955 | "id": 20185444, 956 | "genre": "애니메이션/액션/어드벤처", 957 | "title": "레고 무비2", 958 | "title_eng": "The Lego Movie 2: The Second Part", 959 | "title_org": "", 960 | "audience": 71030, 961 | "release": "2019-02-06", 962 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1254/125438_P06_114959.jpg", 963 | "poster_url": "https://movie-phinf.pstatic.net/20190201_289/1548989381126kvXTK_JPEG/movie_image.jpg", 964 | "running_time": 106, 965 | "director": "마이크 미첼", 966 | "film_rating": "전체관람가", 967 | "actor1": "크리스 프랫", 968 | "actor2": "엘리자베스 뱅크스", 969 | "actor3": "알리슨 브리" 970 | }, 971 | { 972 | "id": 20184574, 973 | "genre": "드라마", 974 | "title": "그린 북", 975 | "title_eng": "Green Book", 976 | "title_org": "", 977 | "audience": 409871, 978 | "release": "2019-03-20", 979 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1715/171539_P26_135622.jpg", 980 | "poster_url": "https://movie-phinf.pstatic.net/20190115_228/1547528180168jgEP7_JPEG/movie_image.jpg", 981 | "running_time": 130, 982 | "director": "피터 패럴리", 983 | "film_rating": "12세이상관람가", 984 | "actor1": "비고 모텐슨", 985 | "actor2": "마허샬라 알리", 986 | "actor3": "" 987 | }, 988 | { 989 | "id": 20180825, 990 | "genre": "드라마", 991 | "title": "미성년", 992 | "title_eng": "Another Child", 993 | "title_org": "", 994 | "audience": 279305, 995 | "release": "2019-04-24", 996 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1717/171785_P84_181225.jpg", 997 | "poster_url": "https://movie-phinf.pstatic.net/20190411_137/1554973943268Tu8fN_JPEG/movie_image.jpg", 998 | "running_time": 95, 999 | "director": "김윤석", 1000 | "film_rating": "15세이상관람가", 1001 | "actor1": "염정아", 1002 | "actor2": "김소진", 1003 | "actor3": "김혜준" 1004 | }, 1005 | { 1006 | "id": 20185784, 1007 | "genre": "드라마/코미디", 1008 | "title": "나의 특별한 형제", 1009 | "title_eng": "Inseparable Bros", 1010 | "title_org": "", 1011 | "audience": 1275433, 1012 | "release": "2019-05-08", 1013 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1753/175366_P31_101140.jpg", 1014 | "poster_url": "https://movie-phinf.pstatic.net/20190404_182/1554340290133Khnb5_JPEG/movie_image.jpg", 1015 | "running_time": 113, 1016 | "director": "육상효", 1017 | "film_rating": "12세이상관람가", 1018 | "actor1": "신하균", 1019 | "actor2": "이광수", 1020 | "actor3": "이솜" 1021 | }, 1022 | { 1023 | "id": 20185384, 1024 | "genre": "스릴러", 1025 | "title": "호텔 뭄바이", 1026 | "title_eng": "Hotel Mumbai", 1027 | "title_org": "", 1028 | "audience": 37029, 1029 | "release": "2019-05-08", 1030 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1650/165022_P15_153454.jpg", 1031 | "poster_url": "https://movie-phinf.pstatic.net/20190415_275/1555310081472MmN8D_JPEG/movie_image.jpg", 1032 | "running_time": 122, 1033 | "director": "앤소니 마라스", 1034 | "film_rating": "15세이상관람가", 1035 | "actor1": "데브 파텔", 1036 | "actor2": "아미 해머", 1037 | "actor3": "나자닌 보니아디" 1038 | }, 1039 | { 1040 | "id": 20184105, 1041 | "genre": "드라마", 1042 | "title": "말모이", 1043 | "title_eng": "MAL·MO·E: The Secret Mission", 1044 | "title_org": "", 1045 | "audience": 2848736, 1046 | "release": "2019-02-06", 1047 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1676/167699_P40_175859.jpg", 1048 | "poster_url": "https://movie-phinf.pstatic.net/20181227_80/1545901137289EGbWK_JPEG/movie_image.jpg", 1049 | "running_time": 135, 1050 | "director": "엄유나", 1051 | "film_rating": "12세이상관람가", 1052 | "actor1": "유해진", 1053 | "actor2": "윤계상", 1054 | "actor3": "김홍파" 1055 | }, 1056 | { 1057 | "id": 20198598, 1058 | "genre": "액션/어드벤처/코미디", 1059 | "title": "명탐정 피카츄", 1060 | "title_eng": "Pokemon Detective Pikachu", 1061 | "title_org": "", 1062 | "audience": 453864, 1063 | "release": "2019-05-08", 1064 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1676/167657_P03_163928.jpg", 1065 | "poster_url": "https://movie-phinf.pstatic.net/20190424_53/1556091565705XDpwg_JPEG/movie_image.jpg", 1066 | "running_time": 104, 1067 | "director": "롭 레터맨", 1068 | "film_rating": "전체관람가", 1069 | "actor1": "라이언 레이놀즈", 1070 | "actor2": "저스티스 스미스", 1071 | "actor3": "캐서린 뉴튼" 1072 | }, 1073 | { 1074 | "id": 20184889, 1075 | "genre": "액션/SF", 1076 | "title": "어벤져스: 엔드게임", 1077 | "title_eng": "Avengers: Endgame", 1078 | "title_org": "", 1079 | "audience": 12799656, 1080 | "release": "2019-05-08", 1081 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1369/136900_P57_104126.jpg", 1082 | "poster_url": "https://movie-phinf.pstatic.net/20190417_250/1555465284425i6WQE_JPEG/movie_image.jpg", 1083 | "running_time": 180, 1084 | "director": "안소니 루소", 1085 | "film_rating": "12세이상관람가", 1086 | "actor1": "로버트 다우니 주니어", 1087 | "actor2": "크리스 에반스", 1088 | "actor3": "마크 러팔로" 1089 | }, 1090 | { 1091 | "id": 20198104, 1092 | "genre": "드라마", 1093 | "title": "바이스", 1094 | "title_eng": "VICE", 1095 | "title_org": "", 1096 | "audience": 131698, 1097 | "release": "2019-04-24", 1098 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1680/168045_P15_100136.jpg", 1099 | "poster_url": "https://movie-phinf.pstatic.net/20190322_181/1553216483584eF8l2_JPEG/movie_image.jpg", 1100 | "running_time": 132, 1101 | "director": "아담 맥케이", 1102 | "film_rating": "15세이상관람가", 1103 | "actor1": "크리스찬 베일", 1104 | "actor2": "에이미 아담스", 1105 | "actor3": "스티브 카렐" 1106 | }, 1107 | { 1108 | "id": 20174169, 1109 | "genre": "코미디/가족", 1110 | "title": "기묘한 가족", 1111 | "title_eng": "THE ODD FAMILY : ZOMBIE ON SALE", 1112 | "title_org": "", 1113 | "audience": 216254, 1114 | "release": "2019-02-13", 1115 | "thumb_url": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1690/169078_P66_101231.jpg", 1116 | "poster_url": "https://movie-phinf.pstatic.net/20190213_142/1550020349326Pk2m3_JPEG/movie_image.jpg", 1117 | "running_time": 111, 1118 | "director": "이민재", 1119 | "film_rating": "12세이상관람가", 1120 | "actor1": "정재영", 1121 | "actor2": "김남길", 1122 | "actor3": "엄지원" 1123 | } 1124 | ] -------------------------------------------------------------------------------- /static/image/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/static/image/favicon.ico -------------------------------------------------------------------------------- /static/image/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/static/image/favicon.png -------------------------------------------------------------------------------- /static/js/app.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejolie/miumbieub/6cfa5d02378c216e9cf031c26d2752f029b6c010/static/js/app.js -------------------------------------------------------------------------------- /templates/_footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/_nav.html: -------------------------------------------------------------------------------- 1 | 2 | 63 | 96 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | {% load static %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | ㅁㅂ 27 | 28 | 29 | {% include '_nav.html' %} 30 |
    31 | {% block app %}{% endblock %} 32 |
    33 | {% include '_footer.html' %} 34 | {% block script %} 35 | {% endblock %} 36 | 37 | 38 | --------------------------------------------------------------------------------