Skip to content

๐Ÿ”ฑ ์‹ค์‹œ๊ฐ„ ์ฃผ๊ฐ€ ์ •๋ณด ๋ฐ ์˜ˆ์ธก ๐Ÿ”ฑ

License

Notifications You must be signed in to change notification settings

YongBeomKim/stock_analyzer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Stock Analyzer

  • ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ ์ฃผ์‹์‹œ์žฅ๋ณ„ ์ข…๋ชฉ์˜ ์‹œ์„ธ ์กฐํšŒ ๋ฐ ๋ถ„์„
  • ๋ˆ„์ ๋œ ๋ฐ์ดํ„ฐ์™€ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฃผ๊ฐ€ ์˜ˆ์ธก.

์œ„์˜ ๋‘ ๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ์ค‘์ ์œผ๋กœ ํ•˜๋Š” ์›น ์‚ฌ์ดํŠธ๋ฅผ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ๋  ์Šคํƒ

  • Django & DRF (Django Rest Framework)

๋ฐฑ์—”๋“œ์„œ๋ฒ„๋กœ์จ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • React.js & material ui

ํ”„๋ก ํŠธ์—”๋“œ๋กœ์จ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • PyKrx

์ฃผ๊ฐ€ ์ •๋ณด ์ˆ˜์ง‘ ๋ชจ๋“ˆ๋กœ์จ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • MongoDB

MongoDB๋ฅผ ์ฑ„ํƒํ•œ ์ด์œ ๋Š” ์Šคํ‚ค๋งˆ์˜ ์ œ์•ฝ์„ ๋œ ๋ฐ›๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค.

  • Sklearn (Regression)

์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ํšŒ๊ท€ ๋ถ„์„์„ ํ†ตํ•ด ์˜ˆ์ธก.
Linear Regression > Ridge Regression > Lasso Regression
์ „์ฒด์ ์ธ ์„œ๋น„์Šค๊ฐ€ ๊ตฌํ˜„์ด ๋˜๋ฉด ๋‹ค๋ฅธ ๋ชจ๋ธ์„ ๋„์ž…ํ•˜๋ฉฐ ์ •ํ™•๋„๋ฅผ ๋†’์—ฌ๋‚˜๊ฐˆ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ์ฃผ๊ฐ€์— ์˜ํ–ฅ์œผ ์ฃผ๋Š” ๋…๋ฆฝ๋ณ€์ˆ˜(feature)๋“ค์„ ์ฐพ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • Matplotlib

๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™” ๋ชจ๋“ˆ.

Case Rule

ํด๋ž˜์Šค๋ช… : Upper Camel case
๋ฉ”์†Œ๋“œ, ์†์„ฑ : Snake case

๋ชจ๋“ˆ ๊ด€๋ฆฌ

๋ชจ๋“ˆ ๊ด€๋ฆฌ๋Š” requirements.txt๋ฅผ ํ†ตํ•ด ์ด๋ฃจ์–ด์ง„๋‹ค.

# ํŒจํ‚ค์ง€ ๋ชฉ๋ก๊ณผ ๋ฒ„์ „ ํ…์ŠคํŠธ๋กœ ์ €์žฅํ•˜๊ธฐ
pip freeze > requirements.txt

# ํ…์ŠคํŠธ ํŒŒ์ผ์— ์žˆ๋Š” ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๊ธฐ
pip install -r requirements.txt

๊ณ„ํš ๋ฐ ์ง„ํ–‰๊ณผ์ •

1. ๋ฐฑ์—”๋“œ์„œ๋ฒ„ ๊ตฌ์ถ•

1-1. ์•ฑ ์„ค๊ณ„

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

1-2 ๋ชจ๋ธ ์„ค๊ณ„

  • ์‚ฌ์šฉ์ž ์ •๋ณด ๋ชจ๋ธ (์ฆ๊ฒจ์ฐพ๊ธฐ ์ข…๋ชฉ ํฌํ•จ)
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)

1-3. REST API ๊ตฌํ˜„ (DRF ์ ์šฉ)

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)

  1. ๊ธฐ์กด์˜ native django ๋ฐฉ์‹๋Œ€๋กœ ๊ฐœ๋ฐœ์„ ํ•œ๋‹ค๋ฉด ํ”„๋ก ํŠธ ๋ถ€๋ถ„์€ ๋ฐฑ์—”๋“œ๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ณ  django template์— ๊ฐœ๋ฐœ์„ ํ•ด์•ผํ•  ๊ฒƒ์ด๋‹ค. ์ด๋Ÿด ๊ฒฝ์šฐ, ๋ฐฑ์—”๋“œ์™€ ํ”„๋ก ํŠธ์˜ ์™„์ „ํ•œ ๋ถ„๋ฆฌ๊ฐ€ ์–ด๋ ต๋‹ค. ๊ทธ๋ž˜์„œ DRF๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด rest api๊ฐ€ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— django ๋ฐฑ์—”๋“œ์™€ react ํ”„๋ก ํŠธ๊ฐ€ ๋ถ„๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
  2. ์žฌ์‚ฌ์šฉ์„ฑ์ด ์ข‹์•„์ง„๋‹ค. 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๋ฅผ ์ž‘์„ฑํ•  ์ฐจ๋ก€์ด๋‹ค.

(์ฐธ๊ณ  : https://ssungkang.tistory.com/entry/Django-APIView-Mixins-generics-APIView-ViewSet%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90)

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

1-4. URL Patterns

ํ˜„์žฌ 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์„ ๊ฐ€์ง„๋‹ค.

2. MongoDB ์—ฐ๋™

  1. MongoDB ๊ฐ€์ž… -> ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ ์ดํ›„ ํ•˜๋‹จ์˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ์ธ์ฆ๋ฐฉ์‹, DB ์ ‘๊ทผ๊ถŒํ•œ ๋“ฑ์„ ์„ค์ •ํ•˜์—ฌ Database user๋ฅผ ์ƒ์„ฑ

db1

db2

  1. ์™ผ์ชฝ ๋ฉ”๋‰ด์— Network Access์— ๋“ค์–ด๊ฐ„ ๋’ค DB์— ์ ‘๊ทผ์„ ํ—ˆ์šฉํ•  IP๋ฅผ ๋“ฑ๋ก

db3

dbdbdb

**(์Šคํ‚ต) Django ๋ชจ๋ธ์„ migrationํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๋ฏ€๋กœ ์Šคํ‚ต

db4

  1. ์ฒซ ํŽ˜์ด์ง€๋กœ ๋Œ์•„์™€์„œ ํ˜„์žฌ Cluster์˜ Connection string์„ ๋ณต์‚ฌ

db5

db6

  1. connection string์œผ๋กœ mongoDB ์—ฐ๋™์„ ์œ„ํ•œ ๋ชจ๋“ˆ๋“ค์„ ์„ค์น˜
$ pip install dnspython
$ pip install djongo
  1. ์žฅ๊ณ  ํ”„๋กœ์ ํŠธ ๋‚ด 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",   # ์ธ์ฆ๋ฐฉ์‹
        },
    }
}
  1. migrate ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ๋ชจ๋ธ์˜ ๋ณ€๋™์‚ฌํ•ญ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ์šฉ

db9

  1. ์ฒซ ํŽ˜์ด์ง€์˜ collections๋ฅผ ๋ˆŒ๋Ÿฌ DB๊ฐ€ ์ž˜ ์—ฐ๋™๋˜์—ˆ๋Š”์ง€ ํ™•์ธ

db8

2-1. settings.py ๊ธฐ๋ฐ€์ •๋ณด ๊ด€๋ฆฌ

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")

3. PyKrx๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘

3-1 ์ฃผ์‹ ์ข…๋ชฉ ์ฝ”๋“œ ์ˆ˜์ง‘

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() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์ฃผ์‹์‹œ์žฅ๊ณผ ์ข…๋ชฉ๋ช…์„ ์ž…๋ ฅํ•˜๋ฉด ํ•ด๋‹น ์ข…๋ชฉ ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

3-2 ์ฃผ์‹ ์ข…๋ชฉ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘

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() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์˜ค๋Š˜๋‚ ์งœ๋กœ๋ถ€ํ„ฐ ์›ํ•˜๋Š” ๋‚  ์ด์ „๊นŒ์ง€์˜ ์ฃผ๊ฐ€ ์ •๋ณด๋ฅผ ๋ฐ์ดํ„ฐ ํ”„๋ ˆ์ž„์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

image ์‚ผ์„ฑ์ „์ž์˜ ์ฃผ๊ฐ€๋ฅผ ์ผ์ •ํ•œ ์ฃผ๊ธฐ(10์ดˆ)๋กœ ์ˆ˜์ง‘ํ•˜๋Š” ๋ชจ์Šต

3-3 DRF๋ฅผ ํ™œ์šฉํ•œ ๋ฐ์ดํ„ฐ ์‚ฝ์ž… (CREATE)

๋ฐ์ดํ„ฐ ์‚ฝ์ž…์„ ์œ„ํ•ด ์šฐ์„  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)

image
image
์„ฑ๊ณต์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฝ์ž…๋œ ๋ชจ์Šต

  • ์ฐธ๊ณ 
    CSRF๋Š” ๋ฌด์—‡์ธ๊ฐ€? CSRF๋Š” Cross-site request forgery์˜ ์•ฝ์ž๋กœ '์‚ฌ์ดํŠธ ๊ฐ„ ์š”์ฒญ ์œ„์กฐ'๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ์‚ฌ์ดํŠธ ๊ฐ„ ์Šคํฌ๋ฆฝํŒ…(XSS) ๊ณต๊ฒฉ์€ ๊ณต๊ฒฉ์ž๊ฐ€ ์›น ์‚ฌ์ดํŠธ์— ์•…์˜์ ์ธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ต๋ฌ˜ํ•˜๊ฒŒ ๋ผ์›Œ๋„ฃ์–ด ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ์›น์‚ฌ์ดํŠธ๋ฅผ ์‹ ์šฉํ•˜๋Š” ์ ์„ ๋…ธ๋ฆฐ ๊ฒƒ์ด๋ผ๋ฉด, CSRF๋Š” ๊ทธ์™€ ๋ฐ˜๋Œ€๋กœ ํŠน์ • ์›น์‚ฌ์ดํŠธ๊ฐ€ ์‚ฌ์šฉ์ž์˜ ์›น ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์‹ ์šฉํ•˜๋Š” ์ƒํƒœ๋ฅผ ๋…ธ๋ฆฐ ๊ฒƒ์ด๋‹ค. ์ผ๋‹จ ์‚ฌ์šฉ์ž๊ฐ€ ์›น์‚ฌ์ดํŠธ์— ๋กœ๊ทธ์ธํ•œ ์ƒํƒœ์—์„œ ์‚ฌ์ดํŠธ๊ฐ„ ์š”์ฒญ ์œ„์กฐ ๊ณต๊ฒฉ ์ฝ”๋“œ๊ฐ€ ์‚ฝ์ž…๋œ ํŽ˜์ด์ง€๋ฅผ ์—ด๋ฉด, ๊ณต๊ฒฉ ๋Œ€์ƒ์ด ๋˜๋Š” ์›น์‚ฌ์ดํŠธ๋Š” ์œ„์กฐ๋œ ๊ณต๊ฒฉ ๋ช…๋ น์ด ๋ฏฟ์„ ์ˆ˜ ์žˆ๋Š” ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋ฐœ์†ก๋œ ๊ฒƒ์œผ๋กœ ํŒ๋‹จํ•˜๊ฒŒ ๋˜์–ด ๊ณต๊ฒฉ์— ๋…ธ์ถœ๋œ๋‹ค. CSRF ํ† ํฐ์ด ์กด์žฌํ•˜๋Š” ์ด์œ ๋Š” CSRF ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๊ณ ์œ ํ•œ ๊ฐ’์„ ๋Œ€์กฐํ•˜์—ฌ ์‚ฌ์šฉ์ž์™€ ๋Œ€์กฐํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค.

4. ํ”„๋ก ํŠธ์—”๋“œ ๋ทฐ ๊ตฌ์ถ• (React.js)

4-1 ๋ทฐ ์„ค๊ณ„

4-1-1 ์‹ค์‹œ๊ฐ„ ์ฃผ๊ฐ€ ์ •๋ณด ์กฐํšŒ์•ฑ์˜ ๋ทฐ ์„ค๊ณ„
  • ์ฃผ์‹์‹œ์žฅ ์„ ํƒ ๋ทฐ (๋ฌดํ•œ์Šคํฌ๋กคํ˜•)
    ๊ฐ ์ฃผ์‹์‹œ์žฅ๋ณ„ ๊ทธ๋ž˜ํ”„๋ฅผ ๋„์šฐ๋ฉฐ, ํ•ด๋‹น ์ฃผ์‹์‹œ์žฅ(์ฝ”์Šคํ”ผ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
๊ฐ ์ฃผ์‹์‹œ์žฅ์„ ํ‘œํ˜„ํ•  ์นด๋“œ ์„ค๊ณ„์ค‘
์Šคํฌ๋ฆฐ์ƒท 2020-12-29 ์˜คํ›„ 10 46 30

2021.02.24
image ์ฐธ๊ณ  : 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์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ํ…Œ์ŠคํŠธ๋กœ ๋“ฑ๋ก์ผ์ž๋งŒ ์ฟผ๋ฆฌํ•ด๋ณด์•˜๋‹ค.
image

  • ์ข…๋ชฉ๋ณ„ ์ฃผ๊ฐ€ ์ •๋ณด ์กฐํšŒ ๋ทฐ (List)
    ํ˜„์žฌ ์‹œ์  ํ•ด๋‹น ์ฃผ์‹์‹œ์žฅ์˜ ์—ฌ๋Ÿฌ ์ข…๋ชฉ์˜ ์ฃผ๊ฐ€๋ฅผ ์กฐํšŒ ๊ฐ€๋Šฅ.
    ํ•ด๋‹น ์ข…๋ชฉ์— ํ•˜์ดํผ๋งํฌ๋ฅผ ๋‹ฌ์•„ ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ ๋ทฐ๋กœ ๋„˜์–ด๊ฐˆ ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

  • ํ•ด๋‹น ์ข…๋ชฉ ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ ๋ทฐ (Detail)
    ํ•ด๋‹น ์ข…๋ชฉ์˜ ์ฃผ๊ฐ€๋ฅผ ๊ธฐ๊ฐ„๋ณ„๋กœ ์กฐํšŒ ๊ฐ€๋Šฅ (๊ทธ๋ž˜ํ”„ ์ ์šฉ)
    e.g) 1๋…„์ „, 6๊ฐœ์›”์ „, 3๊ฐœ์›”์ „, 1๊ฐœ์›”์ „

- ๊ธฐ๋Šฅ ํŒจ๋„
๋ชจ๋“  ๋ทฐ์˜ ์šฐ์ธก์— ํŒจ๋„๋กœ์จ ์กด์žฌ. ํ•ด๋‹น ํŒจ๋„์—๋Š” ๊ธฐ๋Šฅ(Util)๋“ค์ด ์†ํ•ด์žˆ๋‹ค.
๊น”๋”ํ•œ ๋””์ž์ธ์„ ์œ„ํ•ด ๋ฌดํ•œ์Šคํฌ๋กคํ˜• ๋ฆฌ์ŠคํŠธ์— ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ๋‹ค. (๊ธฐ๋Šฅ์˜ ํ™•์žฅ ๊ณ ๋ ค)

- ์ฆ๊ฒจ์ฐพ๊ธฐ ๊ด€๋ฆฌ
๊ธฐ๋Šฅ ํŒจ๋„์— ์†ํ•ด์žˆ๋Š” ๊ธฐ๋Šฅ.
์‚ฌ์šฉ์ž๊ฐ€ ๋“ฑ๋กํ•ด๋‘” ์ข…๋ชฉ์˜ ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ ๋ทฐ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

- ์ข…๋ชฉ๋ณ„ ์ฃผ๊ฐ€ ๋น„๊ต
๊ธฐ๋Šฅ ํŒจ๋„์— ์†ํ•ด์žˆ๋Š” ๊ธฐ๋Šฅ.
์ข…๋ชฉ๋ณ„ ์ฃผ๊ฐ€ ๋น„๊ต ๋ทฐ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

- ์ข…๋ชฉ๋ณ„ ์ฃผ๊ฐ€ ๋น„๊ต ๋ทฐ
์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ์ข…๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๊ทธ๋ž˜ํ”„๋ฅผ ๊ฒน์ณ์„œ ๊ทธ๋ฆผ

4-1-2 ์ฃผ๊ฐ€ ์ •๋ณด ์˜ˆ์ธก์•ฑ์˜ ๋ทฐ ์„ค๊ณ„
  • ํ•ด๋‹น ์ข…๋ชฉ ์ฃผ๊ฐ€ ์˜ˆ์ธก ๋ทฐ
    ํ•ด๋‹น ์ข…๋ชฉ์˜ ์˜ˆ์ธก์ฃผ๊ฐ€๋ฅผ ๊ทธ๋ž˜ํ”„๋กœ ์‹œ๊ฐํ™”
    ์ข…๋ชฉ๋ณ„๋กœ ๋ทฐ๊ฐ€ ๋™์ผํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋˜๋ฏ€๋กœ ํ…œํ”Œ๋ฆฟ ์ ์šฉ

5. Regression ์ ์šฉ

์ฐธ๊ณ  : https://steemit.com/kr/@phuzion7/volume
pandas_datareader์˜ get_data_yahoo()๋ฅผ ํ†ตํ•ด ๊ฐ ์ฃผ์‹์ข…๋ชฉ๋ณ„ ์ •๋ณด๋ฅผ ๋ฐ์ดํ„ฐ ํ”„๋ ˆ์ž„ ํ˜•ํƒœ๋กœ ๊ฐ€์ ธ์™”๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์—ฌ๊ธฐ์„œ volume์˜ ์˜๋ฏธ๋Š” ๋‹ค์Œ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ–ˆ๋‹ค. ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด, volume์€ ์ผ์ •๊ธฐ๊ฐ„์˜ ๊ฑฐ๋ž˜๋Ÿ‰์„ ์˜๋ฏธํ•œ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์ฃผ๊ฐ€๊ฐ€ ์ƒ์Šนํ•  ๋•Œ์—๋Š” ๊ฑฐ๋ž˜๋Ÿ‰์ด ์ฆ๊ฐ€ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค. (๊ฑฐ๋ž˜๋ฅผ ์‹œ๋„ํ•˜๋Š” ์„ธ๋ ฅ๋“ค์— ์˜ํ•ด) ํฌ๊ฒŒ ์ƒ์Šน ๋ณผ๋ฅจ, ํ•˜๋ฝ ๋ณผ๋ฅจ, U๋ณผ๋ฅจ, ๋”๋ณผ๋ฅจ, ๋žœ๋ค๋ณผ๋ฅจ์œผ๋กœ ๋‚˜๋‰œ๋‹ค. ์ด์ค‘ ๋ณผ๋ฅจ์˜ ์ƒ์Šน๊ณผ ํ•˜๋ฝ์€ ์„ ํ˜•ํšŒ๊ท€๋ฅผ ํ†ตํ•ด ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค.

6. ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”

7. ๋‹ค๋ฅธ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ ์ ์šฉ ๋ฐ ํ…Œ์ŠคํŠธ

About

๐Ÿ”ฑ ์‹ค์‹œ๊ฐ„ ์ฃผ๊ฐ€ ์ •๋ณด ๋ฐ ์˜ˆ์ธก ๐Ÿ”ฑ

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages