Day 11

TIL - Django

๐Ÿ“‹ย ๊ณต๋ถ€ ๋‚ด์šฉ

Django ์„ค์น˜ ๋ฐ ๊ฐœ๋ฐœํ™˜๊ฒฝ ์„ค์ •

Python virtual environment (venv)

์„œ๋กœ ๋‹ค๋ฅธ ํ™˜๊ฒฝ์—์„œ ๊ฐœ๋ฐœ๋˜๋Š” ์—ฌ๋Ÿฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ, ํ™˜๊ฒฝ์„ ์ฒดํฌํ•˜๊ณ  ๋ณ€๊ฒฝํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€ ๋ฐ ํ”„๋กœ์ ํŠธ๊ฐ„์˜ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•œ๋‹ค.
์ด์™€ ๊ฐ™์€ ๋ฌธ์ œ์ ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ํ”„๋กœ์ ํŠธ๋“ค์„ ๊ฐ ๋ชฉ์ ์— ๋งž๊ฒŒ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด, ํ”„๋กœ์ ํŠธ๋งˆ๋‹ค ๊ฐ€์ƒํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.

  • ๊ฐ€์ƒํ™˜๊ฒฝ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
1
$ python -m venv {project-name}
  • ํ™œ์„ฑํ™”
1
$ source project-name/bin/activate
  • ๋น„ํ™œ์„ฑํ™”
1
$ deactivate

Django

๊ฐ€์ƒํ™˜๊ฒฝ์ด ํ™œ์„ฑํ™” ๋œ ์ƒํƒœ์—์„œ ์„ค์น˜

1
$ pip install django

Django ํ™œ์šฉ

Django : ํŒŒ์ด์ฌ์œผ๋กœ ์ œ์ž‘๋œ ์›น ํ”„๋ ˆ์ž„์›Œํฌ

ํ”„๋กœ์ ํŠธ

ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ ๋ฐ ์„œ๋ฒ„ ์‹คํ–‰

1
2
$ django-admin startproject mysite
$ python3 manage.py runserver

(python์œผ๋กœ ์ž…๋ ฅํ•˜๋Š” ๊ฒฝ์šฐ ์‹คํ–‰์ด ๋˜์ง€ ์•Š์„ ๋•Œ๊ฐ€ ๋งŽ์•„ python3 ์‚ฌ์šฉ)

App & Url

  • ์ƒˆ๋กœ์šด ์•ฑ polls์™€ index, some_url ํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  url ์—ฐ๊ฒฐ
1
$ python3 manage.py startapp polls
  • polls/views.py
1
2
3
4
5
6
7
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world.")

def some_url(request):
    return HttpResponse("Some ulr์„ ๊ตฌํ˜„ํ•ด ๋ดค์Šต๋‹ˆ๋‹ค.")
  • mysite/urls.py
1
2
3
4
5
6
7
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("polls/", include('polls.urls'))
]
  • polls/urls.py
1
2
3
4
5
6
7
from django.urls import path
from . import views

urlpatterns = [
    path('',views.index, name='index')
    path('some_url',views.some_url)
]

RDB Relational Database

๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ ํ–‰๊ณผ ์—ด๋กœ ์ด๋ฃจ์–ด์ง„ ํ…Œ์ด๋ธ”์˜ ํ˜•ํƒœ๋กœ ๊ตฌ์„ฑํ•˜๊ณ , ํ…Œ์ด๋ธ” ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

  • ํ…Œ์ด๋ธ” table

    ํ–‰๊ณผ ์—ด๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š” ๋ฐ์ดํ„ฐ์˜ ์ง‘ํ•ฉ

  • ์—ด column

    ํ…Œ์ด๋ธ”์˜ ํ•„๋“œ(field)

    • primary key
      ํ…Œ์ด๋ธ”์˜ ๊ฐ ํ–‰(row)์„ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ์—ด(column)
    • forien key
      ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์˜ primary key๋ฅผ ์ฐธ์กฐํ•˜๋Š” ์—ด(column)์œผ๋กœ, ๋‘ ํ…Œ์ด๋ธ” ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Œ
  • ํ–‰ row

    ํ…Œ์ด๋ธ”์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ๋ ˆ์ฝ”๋“œ(Record)

Model

Django์—์„œ RDB์™€ ์—ฐ๋™์„ ๋‹ด๋‹นํ•˜๋Š” ํด๋ž˜์Šค๋กœ, ๊ฐ ๋ชจ๋ธ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ…Œ์ด๋ธ”(Table)์— ํ•ด๋‹นํ•˜๋ฉฐ ํ•„๋“œ(Field)๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

  • ๋ชจ๋ธ ํ•„๋“œ (model field)
    • ๋‹ค์–‘ํ•œ ํƒ€์ž…์˜ ํ•„๋“œ์™€ ์˜ต์…˜์ด ์กด์žฌํ•œ๋‹ค. ์ž์„ธํ•œ ์„ค๋ช…์€ ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ .
    • Django Model Fields
  • mysite/settings.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
...
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polls.apps.PollsConfig', #์ƒˆ๋กœ ์ƒ์„ฑํ•œ app์„ settings.py์— ์ถ”๊ฐ€
]
...
  • polls/models.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from django.db import models

# polls app์— ๋ชจ๋ธ ๋‘ ๊ฐœ๋ฅผ ๋งŒ๋“ค๊ณ  ๊ฐ๊ฐ์˜ field๋ฅผ ์„ค์ •

# ๋”ฐ๋กœ primary key(=pk)๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ, ๋ชจ๋ธ์ด ์ƒ์„ฑ๋  ๋•Œ ์ฃผ์–ด์ง€๋Š” id์™€ ๊ฐ™์€ ๊ฐ’์„ ๊ฐ–๊ฒŒ ๋จ
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

# foreign key๋กœ Question ๋ชจ๋ธ์˜ pk๋ฅผ ์ฐธ์กฐ
class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
  • migration ํŒŒ์ผ ์ƒ์„ฑ
1
$ python3 manage.py makemigrations polls
  • migration์œผ๋กœ ์‹คํ–‰๋  SQL ๋ฌธ์žฅ ์‚ดํŽด๋ณด๊ธฐ
1
$ python3 manage.py sqlmigrate polls 0001
  • migration ์‹คํ–‰ํ•˜๊ธฐ
1
$ python3 manage.py migrate
  • migration ๋กค๋ฐฑ
1
2
#0001 ๋ฒ„์ „์œผ๋กœ ๋กค๋ฐฑ
$ python3 manage.py migrate polls 0001

Admin

๊ด€๋ฆฌ์ž ๊ณ„์ •์„ ์ƒ์„ฑํ•˜๊ณ  ์›น ๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ†ตํ•ด ์‚ฌ์ดํŠธ์— ์ ‘์†ํ•˜์—ฌ
User, App ๊ทธ๋ฆฌ๊ณ  Model ๋“ฑ์„ ์ƒ์„ฑ, ์ˆ˜์ • ๋ฐ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Admin ์ƒ์„ฑ
1
$ python manage.py createsuperuser
  • polls/admin.py
1
2
3
4
5
6
from django.contrib import admin
from .models import * # polls/models.py ์—์„œ ์ •์˜ํ•œ ๋ชจ๋ธ์„ ๊ฐ€์ ธ์˜จ๋‹ค.

#admin site์— ์ƒ์„ฑํ•œ ๋ชจ๋ธ ๋“ฑ๋ก
admin.site.register(Question)
admin.site.register(Choice)
  • polls/models.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    # field ์ถœ๋ ฅ์„ ์œ„ํ•œ ํ•จ์ˆ˜
    def __str__(self):
        return f'์ œ๋ชฉ: {self.question_text}, ๋‚ ์งœ: {self.pub_date}'

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

Model Method

  • polls/modes.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from django.utils import timezone
import datetime

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

    def __str__(self):
        if self.was_published_recently():
            new_badge = 'NEW!!!'
        else:
            new_badge = ''
        return f'{new_badge} ์ œ๋ชฉ: {self.question_text}, ๋‚ ์งœ: {self.pub_date}'

QuerySet

List of model objects
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ณ , ํ•„ํ„ฐ๋งํ•˜๊ฑฐ๋‚˜ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ์Œ

(Django Shell์—์„œ ์ง„ํ–‰)

  • Django Shell ์‹คํ–‰
1
$ python manage.py shell

Read model object

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# ๋ชจ๋ธ ๊ฐ€์ ธ์˜ค๊ธฐ
>>> from polls.models import *

#๋ชจ๋“  Question,Choice ์˜ค๋ธŒ์ ํŠธ ์ถœ๋ ฅ
>>> Question.objects.all()
>>> Choice.objects.all()

#์ฒซ๋ฒˆ์งธ Choice ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์™€ ํ•„๋“œ ์ถœ๋ ฅ
>>> choice = Choice.objects.all()[0]
>>> choice.id
>>> choice.choice_text
>>> choice.votes

#์ฒซ๋ฒˆ์งธ Choice์™€ ์—ฐ๊ฒฐ๋œ Question์„ ๊ฐ€์ ธ์™€ ํ•„๋“œ ์ถœ๋ ฅ
>>> q = choice.question
>>> choice.question.pub_date
>>> choice.question.id

#ํ•ด๋‹น Question๊ณผ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” ๋ชจ๋“  Choice ๊ฐ€์ ธ์˜ค๊ธฐ
>>> q.choice_set.all()

CRUD model objects (Records)

(CRUD : Create, Read, Update & Delete)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
>>> from polls.models import *

# ์ƒˆ๋กœ์šด Question ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋‚ด์šฉ๊ณผ ํ•จ๊ป˜ ์ƒ์„ฑํ•˜๊ณ  'q1'์ด๋ผ๋Š” ๋ณ€์ˆ˜์— ์ €์žฅ
>>> q1 = Question(question_text = "์ปคํ”ผ vs ๋…น์ฐจ")

# 'q1' ์ƒ์„ฑ์‹œ๊ฐ์„ ์„ค์ •
>>> from django.utils import timezone
>>> q1.pub_date = timezone.now()

# 'q1'์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๊ธฐ
>>> q1.save()

# ๊ฐ™์€ ๊ณผ์ •์„ ํ†ตํ•ด q2๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ
>>> q2 = Question(question_text = "abc")
>>> q2.pub_date = timezone.now()
>>> q2.save()

# .create method๋กœ Choice object๋ฅผ ์ƒ์„ฑํ•˜์—ฌ q2์™€ ์—ฐ๊ฒฐ
>>> q2.choice_set.create(choice_text = "b")
# Choice object๋ฅผ ์ƒ์„ฑํ•  ๋•Œ question field์— q2๊ฐ’์„ ๋„ฃ์–ด ์—ฐ๊ฒฐ
>>> choice_c = Choice(choice_text='c', question=q3)
#์ƒˆ๋กœ์šด Choice ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๊ธฐ
>>> choice_c.save()
# q2์™€ ์—ฐ๊ฒฐ๋œ Choice ์˜ค๋ธŒ์ ํŠธ๋“ค์„ ํ™•์ธ
>>> q2.choice_set.all()
<QuerySet [<Choice: b>, <Choice: c>]>

# ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์œผ๋กœ ์ƒ์„ฑ๋œ ์˜ค๋ธŒ์ ํŠธ ๊ฐ€์ ธ์™€ ์‚ญ์ œํ•˜๊ธฐ
>>> q = Question.objects.last()
>>> q.delete()

Filter model objects

  • QuerySet method get()
1
2
3
4
5
6
# ์กฐ๊ฑด์— ๋งž๋Š” ์˜ค๋ธŒ์ ํŠธ ํ•„ํ„ฐ๋ง
>>> Question.objects.get(id=1)
>>> Question.objects.get(question_text__startswith='ํœด๊ฐ€๋ฅผ')
# ์˜ค๋ธŒ์ ํŠธ ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•„ํ„ฐ๋ง์„ ์ง„ํ–‰ํ•  ๊ฒฝ์šฐ ์—๋Ÿฌ ๋ฐœ์ƒ
>>> Question.objects.get(pub_date__year=2023)
# get() returned more than one Question
  • QuerySet method filter() & exclude()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# ์—ฌ๋Ÿฌ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•„ํ„ฐ๋ง ๊ฐ€๋Šฅ
>>> Question.objects.filter(pub_date__year=2023)
<QuerySet [<Question: ์ œ๋ชฉ: ํœด๊ฐ€๋ฅผ ์–ด๋””์„œ ๋ณด๋‚ด๊ณ  ์‹ถ์œผ์„ธ์š”?, ๋‚ ์งœ: 2023-02-05 18:52:59+00:00>, <Question: ์ œ๋ชฉ: ๊ฐ€์žฅ ์ข‹์•„ํ•˜๋Š” ๋””์ €ํŠธ๋Š”?, ๋‚ ์งœ: 2023-02-05 18:53:27+00:00>, ...]>
>>> Question.objects.filter(pub_date__year=2023).count()
2

# ๊ด€๊ณ„๊ธฐ๋ฐ˜์˜ ํ•„ํ„ฐ๋ง
# foreign key๋กœ ์ง€์ •๋œ Model์˜ ํ•„๋“œ๊ฐ’์œผ๋กœ ํ•„ํ„ฐ๋ง ๊ฐ€๋Šฅ
>>> Choice.objects.filter(question__question_text__startswith='ํœด๊ฐ€')
>>> Choice.objects.exclude(question__question_text__startswith='ํœด๊ฐ€')
  • SQL query
1
2
3
4
5
6
7
8
9
# example 1
>>> print(Question.objects.filter(pub_date__year=2023).query)
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."pub_date" BETWEEN 2023-01-01 00:00:00 AND 2023-12-31 23:59:59.999999

# example 2
# pk == primary key = id
>>> q = Question.objects.get(pk=1)
>>> print(q.choice_set.all().query)
SELECT "polls_choice"."id", "polls_choice"."question_id", "polls_choice"."choice_text", "polls_choice"."votes" FROM "polls_choice" WHERE "polls_choice"."question_id" = 1
  • Field lookups

QuerySet methods filter(), exclude() & get() ์—์„œ keyword arguments๋กœ ์ง€์ •๋จ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# startswith
>>> Question.objects.filter(question_text__startswith='ํœด๊ฐ€๋ฅผ')

# contains
>>> Question.objects.filter(question_text__contains='ํœด๊ฐ€')

# gt, gte, lt, lte
>>> Choice.objects.filter(votes__gt=0)

# ์ •๊ทœํ‘œํ˜„์‹
>>> print(Question.objects.filter(question_text__regex=r'^ํœด๊ฐ€.*์–ด๋””').query)
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date", "polls_question"."owner_id" FROM "polls_question" WHERE "polls_question"."question_text" REGEXP ^ํœด๊ฐ€.*์–ด๋””

๋‚ ์งœ์™€ ์‹œ๊ฐ„ date & time information

  • datetime
    • date : ๋‚ ์งœ๋ฅผ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค(Year, Month, Day)
    • time : ์‹œ๊ฐ„์„ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค(Hour, Minute, Second, Microsecond)
    • datetime : ๋‚ ์งœ์™€ ์‹œ๊ฐ„์„ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค (date ํด๋ž˜์Šค์™€ time ํด๋ž˜์Šค์˜ ์†์„ฑ์„ ๋ชจ๋‘ ๊ฐ€์ง)
    • timedelta : ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์„ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค
1
2
3
4
5
6
7
8
9
from datetime import datetime, timedelta

now = datetime.now()

# one hour / one day / one week / one year later
hour_after = now + timedelta(hours=1)
day_after = now + timedelta(days=1)
week_after = now + timedelta(days=7) # weeks=1
year_after = now + timedelta(days=365)
  • timezone
    • UTC ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๋ฉฐ, ๊ธฐ๋ณธ๊ฐ’์€ UTC+0
1
2
3
4
from django.utils import timezone

now = timezone.now()
# datetime.datetime(2023, 10, 28, 6, 25, 23, tzinfo=datetime.timezone.utc)

๐Ÿ‘€ย CHECK

(์–ด๋ ต๊ฑฐ๋‚˜ ์ƒˆ๋กญ๊ฒŒ ์•Œ๊ฒŒ ๋œ ๊ฒƒ ๋“ฑ ๋‹ค์‹œ ํ™•์ธํ•  ๊ฒƒ๋“ค)

  • vscode ์„ธํŒ…

  • ๋ฌธ์ž์—ด ๋ณด๊ฐ„ String Interpolation

    • %, .format, f
    1
    2
    
    def __str__(self):
        return f"์ œ๋ชฉ: {self.question_text}, ๋‚ ์งœ: {self.pub_date}"
    
  • ์—๋Ÿฌ๋…ธํŠธ

    1. ์‚ฌ์šฉ์ค‘์ธ Port
    1
    
    Error: That port is already in use.
    
    • Ctrl + z ๋กœ ์ข…๋ฃŒํ•ด์„œ ์„œ๋ฒ„๊ฐ€ ์ œ๋Œ€๋กœ ์ข…๋ฃŒ๋˜์ง€ ์•Š์•„ ์ƒ๊ธฐ๋Š” ์—๋Ÿฌ (Ctrl + c ๋กœ ์ข…๋ฃŒํ•ด์•ผ ํ•จ)
    • ํ•ด๋‹น ํฌํŠธ์—์„œ ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋Š” ์„œ๋ฒ„ ํ™•์ธ
      1
      
      lsof -i:{port number}
      
    • ํ•ด๋‹น ํ”„๋กœ์„ธ์Šค kill
      1
      
      kill -9 {port number}
      
  • MVC in Django : MTV

โ— ๋Š๋‚€ ์ 

์˜ค๋Š˜์€ Django ํ”„๋ ˆ์ž„์›Œํฌ์˜ ์†Œ๊ฐœ ๋ฐ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ๊ณผ ๊ตฌ์กฐ์— ๋Œ€ํ•ด ๋ฐฐ์› ๋‹ค. ๊ฐ„๋‹จํ•œ ๊ฐœ๋… ๋ฐ ์†Œ๊ฐœ์˜€๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋ ต๋‹ค๊ธฐ ๋ณด๋‹ค๋Š” ์ •๋ฆฌํ•˜๊ณ  ๊ธฐ์–ตํ•ด์•ผ ํ•  ๋‚ด์šฉ์ด ๋งŽ์•„ ๊ณต๋ถ€ํ•˜๊ณ  ์ •๋ฆฌํ•˜๋Š” ๋ฐ ์˜ค๋žœ ์‹œ๊ฐ„์ด ๋“ค์—ˆ๋‹ค.

์˜ค๋Š˜ ๊ณต๋ถ€ํ•œ ๊ธฐ๋ณธ์ ์ธ ๊ฐœ๋…์„ ์ž˜ ์ •๋ฆฌํ•˜๊ณ ์‹ถ์–ด์„œ ๊ฐ•์˜๋ฅผ ๋“ค์€ ์‹œ๊ฐ„๋งŒํผ TIL์„ ์ ์—ˆ๋‹ค. 100% ๋งŒ์กฑํ•˜์ง„ ๋ชปํ•ด๋„ ๋‚ด๊ฐ€ ์ •๋ฆฌํ•œ ๋‚ด์šฉ๊ณผ ๊ณต์‹ document๋ฅผ ํ†ตํ•ด ๋”ฐ๋กœ ์ฐพ์•„๋ณธ ๋‚ด์šฉ๋“ค์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ์–ด์„œ ๋ฟŒ๋“ฏํ•˜๋‹ค. ์˜ค๋Š˜ ์ •๋ฆฌํ•˜๋Š”๊ฒŒ ์ •๋ง ํž˜๋“ค์—ˆ์ง€๋งŒ ์ด ๊ฒฝํ—˜ ๋•๋ถ„์— ์ดํ›„ ๊ฐ•์˜๋“ค์„ ์ž˜ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„๊ฑฐ๋ผ๊ณ  ๋ณธ๋‹ค.

๋ชจ๋ฅด๋Š” ๋‚ด์šฉ์„ ์ฐพ์•„๋ณด๊ณ  ์ ์šฉํ•˜๋Š”๋ด ์ž์‹ ์ด ์žˆ์ง€๋งŒ ๋‚จ๋“ค์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ •๋ฆฌํ•˜๋Š”๋ด ์ •๋ง ์ž์‹ ์ด ์—†๋‹ค. (๋ง๋กœ ์„ค๋ช…ํ•˜๋Š”๊ฒƒ๋„ ๋น„์Šทํ•œ ํŽธ์ด๋‹ค.) ๊ทธ๋ž˜๋„ TIL์„ ์ ๊ธฐ ์ „๋ณด๋‹ค๋Š” ํ›จ์”ฌ ๋‚˜์•„์กŒ๋‹ค๋Š” ๊ฑธ ์Šค์Šค๋กœ๋„ ์ฒด๊ฐ์ค‘์ด๋‹ค. ์ •๋ฆฌํ•˜๋Š”๋ฐ ๋“ค์ธ ์‹œ๊ฐ„๋“ค์ด ๊ฑฐ๋ฆ„์ด ๋˜์–ด ๋‚˜์ค‘์—๋Š” ‘์ž˜ ์ดํ•ด๋˜๋Š” ๊ธ€’, ‘์ž˜ ์„ค๋ช…ํ•œ ๊ธ€’ ์ด๋ผ๋Š” ์ข‹์€ ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜ํ™•ํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ!!

Hugo๋กœ ๋งŒ๋“ฆ
Jimmy์˜ Stack ํ…Œ๋งˆ ์‚ฌ์šฉ ์ค‘