- ์ค์๊ฐ์ผ๋ก ๊ฐ ์ฃผ์์์ฅ๋ณ ์ข ๋ชฉ์ ์์ธ ์กฐํ ๋ฐ ๋ถ์
- ๋์ ๋ ๋ฐ์ดํฐ์ ๋จธ์ ๋ฌ๋ ๋ชจ๋ธ์ ๊ธฐ๋ฐ์ผ๋ก ์ฃผ๊ฐ ์์ธก.
์์ ๋ ๊ฐ์ง ๊ธฐ๋ฅ์ ์ค์ ์ผ๋ก ํ๋ ์น ์ฌ์ดํธ๋ฅผ ๋ชฉํ๋ก ํฉ๋๋ค.
- Django & DRF (Django Rest Framework)
๋ฐฑ์๋์๋ฒ๋ก์จ ์ฌ์ฉํฉ๋๋ค.
- React.js & material ui
ํ๋ก ํธ์๋๋ก์จ ์ฌ์ฉํฉ๋๋ค.
- PyKrx
์ฃผ๊ฐ ์ ๋ณด ์์ง ๋ชจ๋๋ก์จ ์ฌ์ฉ๋ฉ๋๋ค.
- MongoDB
MongoDB๋ฅผ ์ฑํํ ์ด์ ๋ ์คํค๋ง์ ์ ์ฝ์ ๋ ๋ฐ๊ธฐ ์ํจ์ ๋๋ค.
- Sklearn (Regression)
์ฃผ๊ฐ ๋ฐ์ดํฐ๋ฅผ ํ๊ท ๋ถ์์ ํตํด ์์ธก.
Linear Regression > Ridge Regression > Lasso Regression
์ ์ฒด์ ์ธ ์๋น์ค๊ฐ ๊ตฌํ์ด ๋๋ฉด ๋ค๋ฅธ ๋ชจ๋ธ์ ๋์ ํ๋ฉฐ ์ ํ๋๋ฅผ ๋์ฌ๋๊ฐ ์์ ์ ๋๋ค. ์ฃผ๊ฐ์ ์ํฅ์ผ ์ฃผ๋ ๋ ๋ฆฝ๋ณ์(feature)๋ค์ ์ฐพ์์ผ ํฉ๋๋ค.
- Matplotlib
๋ฐ์ดํฐ ์๊ฐํ ๋ชจ๋.
ํด๋์ค๋ช
: Upper Camel case
๋ฉ์๋, ์์ฑ : Snake case
๋ชจ๋ ๊ด๋ฆฌ๋ requirements.txt๋ฅผ ํตํด ์ด๋ฃจ์ด์ง๋ค.
# ํจํค์ง ๋ชฉ๋ก๊ณผ ๋ฒ์ ํ
์คํธ๋ก ์ ์ฅํ๊ธฐ
pip freeze > requirements.txt
# ํ
์คํธ ํ์ผ์ ์๋ ํจํค์ง๋ฅผ ์ค์นํ๊ธฐ
pip install -r requirements.txt
django ํ๋ก์ ํธ๋ ์ฌ๋ฌ app๋ค๋ก ๊ตฌ์ฑ๋์ด ์๋ค. ์ฆ, ํ๋ก์ ํธ๋ ํ๋์ ์น์ฌ์ดํธ์ด๊ณ , ๊ฐ ์ฑ๋ค์ ํ๋์ ์น์ฌ์ดํธ์ ์ํด์๋ ๊ธฐ๋ฅ๋ค์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค. e.g) ๊ฒ์ํ, ์ด๋ฉ์ผ, ๊ฒฐ์ ..
ํ์ฌ ์งํํ ์ฃผ์ ์๋ ๊ธฐ๋ฅ์ด ์ค์๊ฐ ์ฃผ๊ฐ ์ ๋ณด ์กฐํ, ์ฃผ๊ฐ ์ ๋ณด ์์ธก์ด ์๋ค. ๊ณ ๋ก ๋ ๊ฐ์ง์ ์ฑ์ ์์ฑํ ๊ฒ์ด๋ค.
python manage.py startapp stock_inquiry
python manage.py startapp stock_prediction
2020-12-04 : API ์๋ฒ๋ก ์ฌ์ฉํ rest_api ์ฑ๋ ์์ฑํ๋ค.
python manage.py startapp rest_api
- ์ฌ์ฉ์ ์ ๋ณด ๋ชจ๋ธ (์ฆ๊ฒจ์ฐพ๊ธฐ ์ข ๋ชฉ ํฌํจ)
class Post(models.Model):
user_name = models.CharField(max_length=200)
# bookmark_item_list = models.CharField(max_length=200) # ์ฐ๊ตฌ์ค..
def __str__(self):
return self.user_name
- ์ฃผ์์์ฅ ๋ชจ๋ธ
class StockMarket(models.Model):
stock_market_name = models.CharField(primary_key=True, max_length=200)
def __str__(self):
return self.stock_market_name
- ์ฃผ์์ข ๋ชฉ ๋ชฉ๋ก ๋ชจ๋ธ
class StockItemList(models.Model):
stock_market_name = models.ForeignKey(StockMarket, on_delete=models.CASCADE)
stock_item_name = models.CharField(primary_key=True, max_length=200)
stock_item_code = models.CharField(max_length=200)
def __str__(self):
return self.stock_item_name
- ์ฃผ์์ข ๋ชฉ ๋ชจ๋ธ
class StockItem(models.Model):
stock_item_name = models.ForeignKey(StockItemList, on_delete=models.CASCADE)
reg_date = models.DateField(default=timezone.now(), null=True)
high = models.FloatField(default=0.0)
low = models.FloatField(default=0.0)
open = models.FloatField(default=0.0)
close = models.FloatField(default=0.0)
volume = models.FloatField(default=0.0)
DRF๋ ์ ์ธ๊น? (์ฐธ๊ณ : https://medium.com/@whj2013123218/django-rest-api%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1%EA%B3%BC-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95-a95c6dd195fd)
- ๊ธฐ์กด์ native django ๋ฐฉ์๋๋ก ๊ฐ๋ฐ์ ํ๋ค๋ฉด ํ๋ก ํธ ๋ถ๋ถ์ ๋ฐฑ์๋๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ณ django template์ ๊ฐ๋ฐ์ ํด์ผํ ๊ฒ์ด๋ค. ์ด๋ด ๊ฒฝ์ฐ, ๋ฐฑ์๋์ ํ๋ก ํธ์ ์์ ํ ๋ถ๋ฆฌ๊ฐ ์ด๋ ต๋ค. ๊ทธ๋์ DRF๋ฅผ ์ฌ์ฉํ๋ฉด rest api๊ฐ ์ฌ์ฉ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ django ๋ฐฑ์๋์ react ํ๋ก ํธ๊ฐ ๋ถ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค.
- ์ฌ์ฌ์ฉ์ฑ์ด ์ข์์ง๋ค. view์์ ๋ฐ๋ก html๋ก ๋๊ธฐ๊ฒ ๋๋ฉด view์๋ ๋น์ทํ ๋ก์ง๋ ๋งค๋ฒ class-based view๋ก ์์ฑํด์ผ ํ๋ ๋นํจ์จ์ ์ธ ์ํฉ์ด ์ฐ์ถ๋๋ค. ๊ทธ๋ฌ๋ api๋ฅผ ์ ์ฉํ๋ฉด ํด๋น api๋ฅผ ์ฌ์ฌ์ฉํ ์ ์๋ค.
pip install djangorestframework
DRF๋ฅผ ์ค์นํ ํ settings.py์ ๋ค์๊ณผ ๊ฐ์ด ์ ์ด์ค๋ค.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'stock_inquiry',
'stock_prediction',
'rest_framework', # DRF๋ฅผ ์ฑ์ผ๋ก ๋ฑ๋ก
'rest_api' # api ์๋ฒ๋ก ์ฌ์ฉํ ์ฑ
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
rest_api์ฑ์ urls.py๋ฅผ ์์ฑํ ๋ค, ๋ฃจํธ urls.py์ ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ๋ก๋ฅผ ๋ผ์ฐํ ํ๋ค.
urlpatterns = [
...
path('rest_api/', include('rest_api.urls')),
]
๊ทธ ๋ค์ ํด๋น ๋ชจ๋ธ์ serializeํด์ผ ํ๋ค.
๊ทธ ์ด์ ๋ Django ORM์ Queryset์ Context๋ก์จ Django template์ผ๋ก ๋๊ฒจ์ง๋ฉฐ, HTML๋ก ๋ ๋๋ง๋์ด Response๋ก ๋ณด๋ด์ง๊ฒ ๋๋ค.
ํ์ง๋ง JSON์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด์ผ ํ๋ RESTful API๋ HTML๋ก ๋ ๋๋ง ๋๋ Django template๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ๊ทธ๋์ Queryset์ Nestedํ JSON
์ผ๋ก ๋งคํํ๋ ๊ณผ์ ์ ๊ฑฐ์ณ์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค. (Queryset >> Json : Serialize)
rest_api์ฑ์ serializers.py๋ฅผ ์์ฑํด๋ณด์.
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import StockUser
from .models import StockMarket
from .models import StockItemList
from .models import StockItem
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class StockUserSerializer(serializers.ModelSerializer):
class Meta:
model = StockUser
fields = '__all__'
class StockMarketSerializer(serializers.ModelSerializer):
class Meta:
model = StockMarket
fields = '__all__'
class StockItemListSerializer(serializers.ModelSerializer):
class Meta:
model = StockItemList
fields = '__all__'
class StockItemSerializer(serializers.ModelSerializer):
class Meta:
model = StockItem
fields = '__all__'
model๊ณผ serializer๋ฅผ ์์ฑ์์ผฐ์ผ๋ ์ด์ view๋ฅผ ์์ฑํ ์ฐจ๋ก์ด๋ค.
DRF View ์ข ๋ฅ
- CBV(Class-Based View, APIView ์์)
- FBV(Function-Based View, @api_view ๋ฐ์ฝ๋ ์ดํฐ ์ฌ์ฉ)
- Mixin(์์ฒญ๋ง๋ค serializer ์ ์ํ๋ ๊ฒ์ ์ต์ํ)
- generic APIView
- ViewSet
rest_api/views.py
from django.http import HttpResponse
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import StockUserSerializer
from .serializers import StockMarketSerializer
from .serializers import StockItemListSerializer
from .serializers import StockItemSerializer
from .models import StockUser
from .models import StockMarket
from .models import StockItemList
from .models import StockItem
# Create your views here.
class StockUserViewSet(viewsets.ModelViewSet):
queryset = StockUser.objects.all()
serializer_class = StockUserSerializer
class StockMarketViewSet(viewsets.ModelViewSet):
queryset = StockMarket.objects.all()
serializer_class = StockMarketSerializer
class StockItemListViewSet(viewsets.ModelViewSet):
queryset = StockItemList.objects.all()
serializer_class = StockItemListSerializer
def get_queryset(self):
stock_market_name = self.request.query_params.get('stock_market_name', None)
if stock_market_name is not None:
self.queryset = self.queryset.filter(stock_market_name=stock_market_name)
return self.queryset
class StockItemViewSet(viewsets.ModelViewSet):
queryset = StockItem.objects.all()
serializer_class = StockItemSerializer
def get_queryset(self):
stock_item_name = self.request.query_params.get('stock_item_name', None)
if stock_item_name is not None:
self.queryset = self.queryset.filter(stock_item_name=stock_item_name)
return self.queryset
url์ ๋ผ์ฐํ
์์ผ๋ณด์. viewset์ ๋ผ์ฐํ
ํ ๋์๋ CBV, FBV, Mixin, GenericAPIView์๋ ๋ค๋ฅด๊ฒ router๊ฐ์ฒด๋ก ๊ฐํธํ๊ฒ ๋ฑ๋กํ ์ ์๋ค.
rest_api/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import StockUserViewSet
from .views import StockMarketViewSet
from .views import StockItemListViewSet
from .views import StockItemViewSet
router_user = DefaultRouter()
router_user.register('user', StockUserViewSet)
router_market = DefaultRouter()
router_market.register('market', StockMarketViewSet)
router_item_list = DefaultRouter()
router_item_list.register('item_list', StockItemListViewSet)
router_item = DefaultRouter()
router_item.register('item', StockItemViewSet)
urlpatterns = [
path('auth/', include('rest_framework.urls', namespace='rest_framework')),
path('', include(router_user.urls)),
path('', include(router_market.urls)),
path('', include(router_item_list.urls)),
path('', include(router_item.urls)),
]
์๋ฒ๋ฅผ ์คํ์ํจ ํ, http://localhost:8000/rest_api/posts/์์ ์์ธํ ๋ด์ฉ์ ํ์ธํ ์ ์๋ค.
DRF์ ๊ด๋ จ๋ ๋ด์ฉ์ drf_tutorial์ ํ์ธํ๋ฉด ๋๋ค.
์์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฌ์ฉ์ด ๊ฐ๋ฅํ ์ด๋ค REST API๋ฅผ ๊ตฌํํ ๊น? ๊ฐ ํ ์ด๋ธ(๋ชจ๋ธ)์ CRUD API
ํ์ฌ DRF์ ViewSets๋ฅผ ํตํด ์์ฒญ CRUD URL์ ๊ตฌํํด๋์ ์ํ์ด๋ค. ๊ทธ๋ฌ๋ ์ฝ๋์ ์ถ์ํ ์์ค์ด ๋๋ฌด ๋์์ ๋ฌธ์๋ก ์ง์ ์ ๋ฆฌ๋ฅผ ํ๋ ค๊ณ ํ๋ค.
-
์ฌ์ฉ์ ๋ชฉ๋ก ์กฐํ [GET] rest_api/user/
-
์ฌ์ฉ์ ์ถ๊ฐ
[POST] rest_api/user/
{
"user_name": "Test"
}
-
ํน์ ์ฌ์ฉ์ ์กฐํ
[GET] rest_api/user/[id] -
ํน์ ์ฌ์ฉ์ ์ ๋ณด ์ ๋ฐ์ดํธ
[PUT] rest_api/user/[id]
{
"id": 10,
"user_name": "Test U"
}
- ํน์ ์ฌ์ฉ์ ์ ๊ฑฐ
[DELETE] rest_api/user/[id]
์ฌ์ฉ์ ์ด์ธ์ ๋ค๋ฅธ ๋ชจ๋ธ(market, item)๋ ๊ฐ์ URL Pattern์ ๊ฐ์ง๋ค.
- MongoDB ๊ฐ์ -> ํด๋ฌ์คํฐ ์์ฑ ์ดํ ํ๋จ์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์ธ์ฆ๋ฐฉ์, DB ์ ๊ทผ๊ถํ ๋ฑ์ ์ค์ ํ์ฌ Database user๋ฅผ ์์ฑ
- ์ผ์ชฝ ๋ฉ๋ด์ Network Access์ ๋ค์ด๊ฐ ๋ค DB์ ์ ๊ทผ์ ํ์ฉํ IP๋ฅผ ๋ฑ๋ก
**(์คํต) Django ๋ชจ๋ธ์ migrationํ๋ ๊ณผ์ ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์๋์ผ๋ก ์์ฑ๋๋ฏ๋ก ์คํต
- ์ฒซ ํ์ด์ง๋ก ๋์์์ ํ์ฌ Cluster์ Connection string์ ๋ณต์ฌ
- connection string์ผ๋ก mongoDB ์ฐ๋์ ์ํ ๋ชจ๋๋ค์ ์ค์น
$ pip install dnspython
$ pip install djongo
- ์ฅ๊ณ ํ๋ก์ ํธ ๋ด settings.py ์ mongoDB ์ฐ๋์ ์ํ ์ฝ๋ ์์ฑ
DATABASES = {
'default': {
'ENGINE': 'djongo',
"CLIENT": {
"name": "testDB", # Database ์ด๋ฆ์
๋๋ค.
"host": "mongodb+srv://admin:<password>@cluster0.lvfxw.mongodb.net/<dbname>?retryWrites=true&w=majority", # ๋ฐฉ๊ธ ๋ณต์ฌํ Connection string
"username": "admin", # Database username
"password": "admin", # Database user's password
"authMechanism": "SCRAM-SHA-1", # ์ธ์ฆ๋ฐฉ์
},
}
}
- migrate ๋ช ๋ น์ด๋ฅผ ํตํด ๋ชจ๋ธ์ ๋ณ๋์ฌํญ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฉ
- ์ฒซ ํ์ด์ง์ collections๋ฅผ ๋๋ฌ DB๊ฐ ์ ์ฐ๋๋์๋์ง ํ์ธ
settings.py ์๋ secret_key, DB ์ ๋ณด ๋ฑ ์ธ๋ถ์ ๋ ธ์ถ๋๋ฉด ์๋๋ ๊ธฐ๋ฐ์ ๋ณด๋ค์ ๋ด๊ณ ์๋ค.
SECRET_KEY์ ์ฉ๋ -- ์ถ์ฒ
- ์ํธํ๋ ๋ฐ์ดํฐ ์๋ช ์ ํฌํจํ๊ณ ์๋ค.
- ์ฌ์ฉ์ ์ธ์ , ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ์์ฒญ, ๋ฉ์์ง ๋ฑ์์ํ ๊ณ ์ ํ ํฐ์ ํฌํจํ๊ณ ์๋ค.
Django ์ฑ์๋ ์ํธํ ์๋ช ์ด ํ์ํ ๋ง์ ๊ฒ๋ค์ด ์์ผ๋ฉฐ 'SECRET_KEY' ์ค์ ์ด ๊ทธ ์ด์ ๋ผ๊ณ ๋ณผ ์ ์๋ค. ํด๋น ๊ธฐ๋ฐ ์ ๋ณด๋ค์ ๋ค์๊ณผ ๊ฐ์ด json ํ์ผ๋ก ๊ด๋ฆฌํ์ฌ ๋ฐ๋ก ํธ์ถํ๊ฒ๋ ๋ณ๊ฒฝํ๋ค.
- secrets.json
{
"SECRET_KEY": "your SECRET_KEY",
"DB_HOST" : "your DB Host",
"DB_NAME" : "your DB name",
"DB_USERNAME" : "your DB Username",
"DB_PASSWORD" : "your DB Password"
}
- settings.py
secret_file = os.path.join(BASE_DIR, 'secrets.json')
with open(secret_file) as f:
secrets = json.loads(f.read())
def get_secret(setting, secrets=secrets):
"""
secrets.json์ ํตํด ๊ฐ์ ๊ฐ์ ธ์จ๋ค.
"""
try:
return secrets[setting]
except KeyError:
error_msg = "secrets.json ํ์ผ์ {} ๊ฐ์ด ์กด์ฌํ์ง ์์ต๋๋ค.".format(setting)
raise ImproperlyConfigured(error_msg)
SECRET_KEY = get_secret("SECRET_KEY")
class StockItemCodeCollector:
def __init__(self):
self.markets = ['KOSPI', 'KOSDAQ', 'KONEX', 'ALL']
self.tickers = dict()
for market in self.markets:
self.tickers[market] = self.__collect_tickers(market)
@staticmethod
def __collect_tickers(market='KOSPI'):
"""
stock.get_market_ticker_list()
>> ['095570', '006840', '027410', '282330', '138930', ...]
stock.get_market_ticker_name(ticker)
>> SKํ์ด๋์ค
"""
tickers = dict()
today = datetime.today().strftime("%Y%m%d")
for ticker in stock.get_market_ticker_list(today, market):
ticker_name = stock.get_market_ticker_name(ticker)
tickers[ticker_name] = ticker
return tickers
def get_code(self, market, ticker_name):
return self.tickers[market][ticker_name]
StockItemCodeCollector ์ธ์คํด์ค๋ ๊ฐ ์ฃผ์์์ฅ(KOSPI, KOSDAQ, KONEX, ์ ์ฒด)๋ณ๋ก ๋์ ๋๋ฆฌ์ ๊ฐ ์ข ๋ชฉ์ ์ด๋ฆ๊ณผ ์ฝ๋๋ฅผ ๋ณด๊ดํ๊ณ ์๋ค. get_code() ๋ฉ์๋๋ฅผ ํตํด ์ฃผ์์์ฅ๊ณผ ์ข ๋ชฉ๋ช ์ ์ ๋ ฅํ๋ฉด ํด๋น ์ข ๋ชฉ ์ฝ๋๋ฅผ ๋ฐํํ๋ค.
class StockItemDataFrameCollector:
def __init__(self, code):
self.code = code
self.today = datetime.now()
self.today_str = self.today.strftime("%Y%m%d")
def get_dataframe_from_previous(self, days=1):
interval = timedelta(days)
previous = self.today - interval
previous_str = previous.strftime("%Y%m%d")
dataframe = stock.get_market_ohlcv_by_date(previous_str, self.today_str, self.code)
return dataframe
StockItemDataFrameCollector ์ธ์คํด์ค๋ ์์ฑ์์์ ์ด๋ค ์ข ๋ชฉ์ ์ ๋ณด๋ฅผ ์์งํ ์ง ์ ํ๋ค. get_dataframe_from_previous() ๋ฉ์๋๋ฅผ ํตํด ์ค๋๋ ์ง๋ก๋ถํฐ ์ํ๋ ๋ ์ด์ ๊น์ง์ ์ฃผ๊ฐ ์ ๋ณด๋ฅผ ๋ฐ์ดํฐ ํ๋ ์์ผ๋ก ๋ฐํํ๋ค.
์ผ์ฑ์ ์์ ์ฃผ๊ฐ๋ฅผ ์ผ์ ํ ์ฃผ๊ธฐ(10์ด)๋ก ์์งํ๋ ๋ชจ์ต
๋ฐ์ดํฐ ์ฝ์ ์ ์ํด ์ฐ์ django ๋ชจ๋ธ์ ํฌ๋งท์ ์ ํฉํ๋๋ก ๋ณํํ๋ ๊ณผ์ ์ ์งํํ๋ค.
class Converter:
@classmethod
def convert_to_json_for_item(cls, df: DataFrame, market: int, company: str):
json_list = list()
for index in df.index:
# ๊ฐ ๋ ์ง๋ณ ํ์ ๋์
๋๋ฆฌ๋ก ๊ฐ์ ธ์ค๋ ๊ณผ์
index_per_date: str = index.strftime('%Y-%m-%d') # index: TimeStamp
series_per_date: Series = df.loc[index_per_date]
json_str_per_date: str = series_per_date.to_json(force_ascii=False)
json_per_date: dict = json.loads(json_str_per_date)
# ์ฃผ์์์ฅ, ์ข
๋ชฉ, ๋ฑ๋ก์ผ ์ฝ์
json_per_date['stock_market'] = market
json_per_date['stock_item_name'] = company
json_per_date['reg_date'] = index_per_date
# ํ๊ธ๋ก ๋ ํค๋ฅผ ์๋ฌธ์ผ๋ก ๋ณ๊ฒฝ
json_per_date = cls.__convert_key_from_kor_to_eng_for_item(json_per_date)
json_list.append(json_per_date)
return json_list
@staticmethod
def __convert_key_from_kor_to_eng_for_item(json_per_date: dict):
replacements = {'์๊ฐ': 'open',
'์ข
๊ฐ': 'close',
'๊ณ ๊ฐ': 'high',
'์ ๊ฐ': 'low',
'๊ฑฐ๋๋': 'volume'}
for key_kor, key_eng in replacements.items():
json_per_date[key_eng] = json_per_date.pop(key_kor)
return json_per_date
๊ฐ๋จํ ์ค๋ช ํ์๋ฉด PyKrx๋ฅผ ํตํด ๊ฐ์ ธ์จ ๋ฐ์ดํฐํ๋ ์์ requests ๋ชจ๋์ ์ธ์๋ก ๋ณด๋ผ ์ ์๋๋ก ๋์ ๋๋ฆฌ๋ก ๋ณํํ๋ ์์ ์ด๋ค. ๊ทธ๋ฆฌ๊ณ requests ๋ชจ๋์ get ์์ฒญ์ ์ํ ํ์ง๋ง post์์ฒญ์ ํ์ 403 ์๋ฌ๊ฐ ๋ํ๋๋ ๊ฒ์ ์ ์ ์์๋ค. ์ค๋ฅ๋ด์ฉ์ ๋ค์๊ณผ ๊ฐ์๋ค.
{
"detail": "CSRF Failed: CSRF token missing or incorrect."
}
๊ฒฐ๋ก ๋ถํฐ ์ด์ผ๊ธฐ ํ๋ฉด ์ด์ ๊ฐ์ ์ค๋ฅ๊ฐ ๋๋ ์ด์ ๋ ๋ก๊ทธ์ธ ์ธ์ ์ด ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ฐ๋ฆฌ๋ ๋ก๊ทธ์ธ ์ธ์ ์ ํ์ฑํํ๊ธฐ ์ํ ํ ํฐ์ ๋จผ์ ์ค๋นํด์ผํ๋ค. (๋ก๊ทธ์ธ์ ์ํ ๋ฐ์ดํฐ๋ผ๊ณ ์ดํดํ๋ฉด ์ฝ๋ค.) ์ฐ์ , ํ ํฐ์ ํตํ ์ธ์ฆ๋ฐฉ์์ ์งํํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ settings.py์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํด์ฃผ์ด์ผ ํ๋ค.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
๋ค์์ผ๋ก๋ ๋ก๊ทธ์ธ ์ธ์ ์ ์์ฑํด์ผํ๋ค.
class BaseReq:
def __init__(self):
self.url = 'http://127.0.0.1:8000/rest_api/'
self.login_url = self.url + 'auth/login/'
# 2021.02.14.hsk : ์ถํ์ screts.json์ผ๋ก ํตํฉ
self.id = ...
self.password = ...
self.data = None
self.client, self.login_res = self._login(self.login_url, self.id, self.password)
@staticmethod
def _login(login_url, id, password):
client = requests.session()
client.get(login_url)
csrf_token = client.cookies['csrftoken']
login_data = {'Username': id, 'Password': password, 'csrfmiddlewaretoken': csrf_token}
login_res = client.post(login_url, data=login_data)
return client, login_res
...
๊ทธ๋ฆฌ๊ณ ์ง๊ธ๋ถํฐ๋ requests ๋ชจ๋์ด ์๋ self.client ๊ฐ์ฒด (์ฆ, ํ์ฌ ํ์ฑํ๋ ๋ก๊ทธ์ธ ์ธ์ )์ ํตํด์ ์์ฒญ์ ํด์ผํ๋ค.
class BaseCreate(BaseReq):
def __init__(self):
super().__init__()
@csrf_exempt
def send_post(self):
for datum in self.data:
res = self.client.post(self.url, json=datum) # self.client(ํ์ฌ ํ์ฑํ๋ ๋ก๊ทธ์ธ ์ธ์
)๋ฅผ ์ฌ์ฉํ ์์ฒญ
res = self._ret(res)
print(res, datum)
์ฑ๊ณต์ ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ์ฝ์
๋ ๋ชจ์ต
- ์ฐธ๊ณ
CSRF๋ ๋ฌด์์ธ๊ฐ? CSRF๋ Cross-site request forgery์ ์ฝ์๋ก '์ฌ์ดํธ ๊ฐ ์์ฒญ ์์กฐ'๋ฅผ ์๋ฏธํ๋ค. ์ฌ์ดํธ ๊ฐ ์คํฌ๋ฆฝํ (XSS) ๊ณต๊ฒฉ์ ๊ณต๊ฒฉ์๊ฐ ์น ์ฌ์ดํธ์ ์ ์์ ์ธ ์คํฌ๋ฆฝํธ๋ฅผ ๊ต๋ฌํ๊ฒ ๋ผ์๋ฃ์ด ์ฌ์ฉ์๊ฐ ํน์ ์น์ฌ์ดํธ๋ฅผ ์ ์ฉํ๋ ์ ์ ๋ ธ๋ฆฐ ๊ฒ์ด๋ผ๋ฉด, CSRF๋ ๊ทธ์ ๋ฐ๋๋ก ํน์ ์น์ฌ์ดํธ๊ฐ ์ฌ์ฉ์์ ์น ๋ธ๋ผ์ฐ์ ๋ฅผ ์ ์ฉํ๋ ์ํ๋ฅผ ๋ ธ๋ฆฐ ๊ฒ์ด๋ค. ์ผ๋จ ์ฌ์ฉ์๊ฐ ์น์ฌ์ดํธ์ ๋ก๊ทธ์ธํ ์ํ์์ ์ฌ์ดํธ๊ฐ ์์ฒญ ์์กฐ ๊ณต๊ฒฉ ์ฝ๋๊ฐ ์ฝ์ ๋ ํ์ด์ง๋ฅผ ์ด๋ฉด, ๊ณต๊ฒฉ ๋์์ด ๋๋ ์น์ฌ์ดํธ๋ ์์กฐ๋ ๊ณต๊ฒฉ ๋ช ๋ น์ด ๋ฏฟ์ ์ ์๋ ์ฌ์ฉ์๋ก๋ถํฐ ๋ฐ์ก๋ ๊ฒ์ผ๋ก ํ๋จํ๊ฒ ๋์ด ๊ณต๊ฒฉ์ ๋ ธ์ถ๋๋ค. CSRF ํ ํฐ์ด ์กด์ฌํ๋ ์ด์ ๋ CSRF ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด ๊ณ ์ ํ ๊ฐ์ ๋์กฐํ์ฌ ์ฌ์ฉ์์ ๋์กฐํ๊ธฐ ์ํจ์ด๋ค.
- ์ฃผ์์์ฅ ์ ํ ๋ทฐ (๋ฌดํ์คํฌ๋กคํ)
๊ฐ ์ฃผ์์์ฅ๋ณ ๊ทธ๋ํ๋ฅผ ๋์ฐ๋ฉฐ, ํด๋น ์ฃผ์์์ฅ(์ฝ์คํผ200, ์ฝ์ค๋ฅ150)์ ์ข ๋ชฉ๋ณ ์ฃผ๊ฐ ์ ๋ณด ์กฐํ ๋ทฐ๋ก ์ ๊ทผํ ์ ์๊ฒ ํด์ค๋ค.
๋ฌดํ์คํฌ๋กคํ์ ์ ํํ ์ด์ ๋ ์ฃผ์์์ฅ์ด ์ถ๊ฐ๋๋ ๊ฒฝ์ฐ๋ฅผ ๊ณ ๋ คํ ๊ฒ์ด๋ค.
import React, { Component } from 'react'
import CardStockMarket from '../card/cardStockMarket'
class ListStockMarket extends Component {
render() {
var markets = [];
var marketDatas = this.props.data;
var i = 0;
for (i = 0; i < marketDatas.length; i++){
marketData = marketDatas[i]
markets.push(
<li key = {marketData.id}>
<CardStockMarket name={marketData.name}></CardStockMarket>
</li>
);
}
return (
<nav>
<ul>
{markets}
</ul>
</nav>
);
}
}
export default ListStockMarket;
// ๋ฆฌ์คํธ์ ๊ฐ ํญ๋ชฉ์ ์ญํ ์ ํ๋ ์นด๋ ์ปดํฌ๋ํธ
import React, { Component } from 'react'
import './cardStockMarket.css'
import {Card, CardHeader, CardContent, CardActions, CardMedia} from '@material-ui/core'
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button'
class CardStockMarket extends Component {
render() {
var marketName = this.props.name;
return (
<Card className="root">
<CardHeader>
</CardHeader>
<CardContent>
<Typography className="content">I'm {marketName}. </Typography>
</CardContent>
<CardActions>
<Button size="small">GO TO</Button>
</CardActions>
</Card>
);
}
}
export default CardStockMarket;
2020.12.29
๊ฐ ์ฃผ์์์ฅ์ ํํํ ์นด๋ ์ค๊ณ์ค
2021.02.24
์ฐธ๊ณ : https://www.zerocho.com/category/React/post/579b5ec26958781500ed9955
mongodb๋ก๋ถํฐ ๊ฐ์ ธ์จ ์ฃผ์์์ฅ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก CardStockMarket ์ปดํฌ๋ํธ๋ฅผ ์ถ๋ ฅํ ๋ด์ฉ์ด๋ค. ์ฐ์ React ์ปดํฌ๋ํธ์ ๋ผ์ดํ์ฌ์ดํด์ ๋ํ ๊ฐ๋จํ ์ค๋ช
์ ํ์๋ฉด, ์ปดํฌ๋ํธ๊ฐ ์ต์ด๋ก ์คํ๋๋ฉด mount๋๋ค๊ณ ํํํ๋ค. ์ด ๋, context, defaultProps์ state๋ฅผ ์ ์ฅํ๋ค. ๊ทธ๋ฆฌ๊ณ componentWillMount ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค. ์ด๋๋ ์ปดํฌ๋ํธ๊ฐ DOM์ ๋ถ์ฐฉ๋๊ธฐ ์ ์ด๋ฏ๋ก state๋ props๋ฅผ ๋ฐ๊ฟ์ ์๋๋ค. render ๋ฉ์๋๋ฅผ ํตํด DOM์ ๋ถ์ฐฉ๋ ํ, componentDidMount ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค. componentDidMount ๋ฉ์๋๋ ์ปดํฌ๋ํธ๊ฐ ์ต์ด๋ก ์คํ๋๊ณ DOM์ ๋ถ์ฐฉ๋ ํ์ ์คํ๋๋ฏ๋ก ๊ฐ ์ปดํฌ๋ํธ๊ฐ ์์ฑ๋ ํ ์ต์ด ํ๋ฒ๋ง ํธ์ถ๋๋ค. ์ด๋๋ DOM์ ๋ถ์ฐฉ๋์ด ์๋ ์ํ์ด๊ธฐ ๋๋ฌธ์ AJAX์์ฒญ์ด๋ setTimeout, setInterval๊ณผ ๊ฐ์ ํ๋์ ํ๋ค.
๊ทธ๋ฆฌ๊ณ axios ๋ชจ๋์ ํตํด react๋ก๋ถํฐ django ์๋ฒ๋ฅผ ๊ฑฐ์ณ mongodb์ ์๋ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํด์๋ค. python์ requests ๋ชจ๋๊ณผ ๊ฐ๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค. ์์ง ์์ธ์ ํ์ ํ์ง ๋ชปํ์ง๋ง render์์ axios๋ฅผ ํตํด ์์ฒญํ ๊ฒฝ์ฐ 2๋ฒ์ฉ ์์ฒญ๋๋ ํ์์ด ๋ฐ์ํ๋ค. componentDidMount ๋ผ์ดํ์ฌ์ดํด์์ ์์ฒญํ์ ์ด์๊ฐ์ ๋ฌธ์ ๋ ํด๊ฒฐ๋ฌ๋ค.
// App.js
...
componentDidMount(){
console.log('im didmount!');
axios({
method : "get",
url : "http://127.0.0.1:8000/rest_api/market/"
})
.then(response => {
console.log(response);
let _markets = [];
response.data.forEach(element => {
let _id = element["id"];
let _name = element["stock_market_name"];
let market = <CardStockMarket name={_name}/>
_markets = _markets.concat(market);
});
console.log('hello', _markets);
this.setState({markets: _markets});
})
.catch(error => {
console.log("error", error);
})
}
setState๋ฅผ ํจ์ผ๋ก์จ render๋ฅผ ํ๋ฒ๋ ์ ๋ฐ์ํจ๋ค. ์ด๋ ์ฐ๋ฆฌ๊ฐ ์์ฒญํ ๋ฐ์ดํฐ๋ฅผ ํ ๋๋ก ์นด๋๋ฅผ ํ๋ฉด์ ์ถ๋ ฅํ๊ฒ ๋๋ค.
2021.03.07
// App.js
class App extends Component{
state = {
view_mode: 'market',
}
constructor(props){
super(props);
}
// shouldComponentUpdate(){
// }
render(){
let template = null;
if (this.state.view_mode == 'market') {
template = <ListStockMarket/>;
}
return (
<div className="App">
{template}
</div>
);
}
}
// listStockMarket.js
class ListStockMarket extends Component {
state = {
markets: []
}
constructor(props){
super(props);
}
render(){
return (
<div id="grid">
<ul className="stockMarkets">
{this.state.markets}
</ul>
</div>
);
}
componentDidMount(){
axios({
method : "get",
url : "http://127.0.0.1:8000/rest_api/market/"
})
.then(response => {
console.log(response);
let _markets = [];
response.data.forEach(element => {
let _id = element["id"];
let _name = element["stock_market_name"];
let market = <CardStockMarket name={_name}/>
_markets = _markets.concat(market);
});
this.setState({markets: _markets});
})
.catch(error => {
console.log("error", error);
})
}
}
App.js ์ App ํด๋์ค์ view_mode ์์ฑ์ ์ง๋ state๋ฅผ ์ธํ ํจ์ผ๋ก์จ ์ํฉ์๋ฐ๋ผ ๋์ ์ผ๋ก ๋ณด์ฌ์ง ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ ๊ฒ์ด๋ค. ๊ธฐ์กด์ ๋ก์ง์ listStockList.js๋ก ์ฎ๊ธฐ๋ฉด์ ๊ตฌ์กฐ๋ฅผ ๋ฆฌํฉํ ๋งํ๋ค.
2021.03.14
์ด๋ฒ ์๊ฐ์๋ ์ฃผ์์์ฅ์ ํด๋นํ๋ ์ฃผ์์ข
๋ชฉ๋ง ๊ฐ์ ธ์ค๋ ๊ฒ์ด ๋ชฉํ์ด๋ค. ๊ทธ๋ฌ๋ http://127.0.0.1:8000/rest_api/item/ url๋ก GET ์์ฒญ์ ๋ณด๋ด๋ฉด ์กฐ๊ฑด์ ๊ด๊ณ์์ด ์ข
๋ชฉ์ ์ ๋ถ ๊ฐ์ ธ์ค๋ ๋ถ์์ฌ๊ฐ ์ผ์ด๋๋ค. ๊ทธ๋์ ์ฐ๋ฆฌ๋ ํน์ ์กฐ๊ฑด์ ์ค์ ํด์ค ํ์๊ฐ ์๋ค.
class StockItemViewSet(viewsets.ModelViewSet):
queryset = StockItem.objects.all()
serializer_class = StockItemSerializer
def get_queryset(self):
reg_date = self.request.query_params.get('reg_date', None) # ์ฟผ๋ฆฌ์คํธ๋ง
if reg_date is not None:
self.queryset = self.queryset.filter(reg_date=reg_date)
return self.queryset
์ ์ฝ๋๋ viewset์ผ๋ก ๊ตฌํ๋ ์์ฒญ url์ ์ฟผ๋ฆฌ์คํธ๋ง์ ๋ฐ์ ๋ชจ๋ธ๋ก๋ถํฐ ํด๋นํ๋ ๋ฐ์ดํฐ๋ง queryset์ผ๋ก ๋ฐํํ๋ค. ํ
์คํธ๋ก ๋ฑ๋ก์ผ์๋ง ์ฟผ๋ฆฌํด๋ณด์๋ค.
-
์ข ๋ชฉ๋ณ ์ฃผ๊ฐ ์ ๋ณด ์กฐํ ๋ทฐ (List)
ํ์ฌ ์์ ํด๋น ์ฃผ์์์ฅ์ ์ฌ๋ฌ ์ข ๋ชฉ์ ์ฃผ๊ฐ๋ฅผ ์กฐํ ๊ฐ๋ฅ.
ํด๋น ์ข ๋ชฉ์ ํ์ดํผ๋งํฌ๋ฅผ ๋ฌ์ ์์ธ ์ ๋ณด ์กฐํ ๋ทฐ๋ก ๋์ด๊ฐ ์ ์๊ฒ ํ๋ค. -
ํด๋น ์ข ๋ชฉ ์์ธ ์ ๋ณด ์กฐํ ๋ทฐ (Detail)
ํด๋น ์ข ๋ชฉ์ ์ฃผ๊ฐ๋ฅผ ๊ธฐ๊ฐ๋ณ๋ก ์กฐํ ๊ฐ๋ฅ (๊ทธ๋ํ ์ ์ฉ)
e.g) 1๋ ์ , 6๊ฐ์์ , 3๊ฐ์์ , 1๊ฐ์์
- ๊ธฐ๋ฅ ํจ๋
๋ชจ๋ ๋ทฐ์ ์ฐ์ธก์ ํจ๋๋ก์จ ์กด์ฌ. ํด๋น ํจ๋์๋ ๊ธฐ๋ฅ(Util)๋ค์ด ์ํด์๋ค.
๊น๋ํ ๋์์ธ์ ์ํด ๋ฌดํ์คํฌ๋กคํ ๋ฆฌ์คํธ์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ค. (๊ธฐ๋ฅ์ ํ์ฅ ๊ณ ๋ ค)
- ์ฆ๊ฒจ์ฐพ๊ธฐ ๊ด๋ฆฌ
๊ธฐ๋ฅ ํจ๋์ ์ํด์๋ ๊ธฐ๋ฅ.
์ฌ์ฉ์๊ฐ ๋ฑ๋กํด๋ ์ข
๋ชฉ์ ์์ธ ์ ๋ณด ์กฐํ ๋ทฐ๋ก ์ด๋ํ ์ ์๊ฒ ํ๋ค.
- ์ข
๋ชฉ๋ณ ์ฃผ๊ฐ ๋น๊ต
๊ธฐ๋ฅ ํจ๋์ ์ํด์๋ ๊ธฐ๋ฅ.
์ข
๋ชฉ๋ณ ์ฃผ๊ฐ ๋น๊ต ๋ทฐ๋ก ์ด๋ํ ์ ์๊ฒ ํ๋ค.
- ์ข
๋ชฉ๋ณ ์ฃผ๊ฐ ๋น๊ต ๋ทฐ
์ฌ์ฉ์๊ฐ ์ํ๋ ์ข
๋ชฉ์ ์ถ๊ฐํ๋ฉด ๊ทธ๋ํ๋ฅผ ๊ฒน์ณ์ ๊ทธ๋ฆผ
- ํด๋น ์ข
๋ชฉ ์ฃผ๊ฐ ์์ธก ๋ทฐ
ํด๋น ์ข ๋ชฉ์ ์์ธก์ฃผ๊ฐ๋ฅผ ๊ทธ๋ํ๋ก ์๊ฐํ
์ข ๋ชฉ๋ณ๋ก ๋ทฐ๊ฐ ๋์ผํ ๊ฒ์ผ๋ก ์์๋๋ฏ๋ก ํ ํ๋ฆฟ ์ ์ฉ
์ฐธ๊ณ : https://steemit.com/kr/@phuzion7/volume
pandas_datareader์ get_data_yahoo()๋ฅผ ํตํด ๊ฐ ์ฃผ์์ข
๋ชฉ๋ณ ์ ๋ณด๋ฅผ ๋ฐ์ดํฐ ํ๋ ์ ํํ๋ก ๊ฐ์ ธ์๋ค. ๊ทธ๋ฆฌ๊ณ ์ฌ๊ธฐ์ volume์ ์๋ฏธ๋ ๋ค์ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ๋ค. ๊ฐ๋ตํ๊ฒ ์ค๋ช
ํ์๋ฉด, volume์ ์ผ์ ๊ธฐ๊ฐ์ ๊ฑฐ๋๋์ ์๋ฏธํ๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ฃผ๊ฐ๊ฐ ์์นํ ๋์๋ ๊ฑฐ๋๋์ด ์ฆ๊ฐํ๋ค๊ณ ํ๋ค. (๊ฑฐ๋๋ฅผ ์๋ํ๋ ์ธ๋ ฅ๋ค์ ์ํด) ํฌ๊ฒ ์์น ๋ณผ๋ฅจ, ํ๋ฝ ๋ณผ๋ฅจ, U๋ณผ๋ฅจ, ๋๋ณผ๋ฅจ, ๋๋ค๋ณผ๋ฅจ์ผ๋ก ๋๋๋ค. ์ด์ค ๋ณผ๋ฅจ์ ์์น๊ณผ ํ๋ฝ์ ์ ํํ๊ท๋ฅผ ํตํด ํ์
ํ ์ ์๋ค.