[9X년생 개발자 모임 특강 #1] 게임 소프트웨어 개발자란 & 개발자의 9가지 기술 – 박종천(Nexon VP)

 

9x년생 개발자 모임(9XD)의 첫 특강 “XXD TO 9XD”가 성황리에 마쳤습니다.

넥슨의 플랫폼 VP Jongcheon Park님께서 강연을 해주셨는데요,

첫 세션에서는 ‘더 나은 신입 개발자가 되기 위해 알아야 할 것들’,
그리고 두 번째 세션에서는 ‘개발자가 팀/프로덕트 관리를 어떻게 효율적으로 할 수 있는지’에 관하여 이야기해주셨습니다.

참석하신 분들의 절반 이상이 뒤풀이에 함께 해주셨는데요,
선수 개발자 & 시작하는 개발자가 어우러져 자유로운 교류의 장이 되었습니다.

그럼, 다음 모임에서 뵙겠습니다 🙂

온오프믹스 모임 링크: http://onoffmix.com/event/70589
사진 링크: https://goo.gl/photos/u5yQf4tywrXKVyJr9

[Django Models 뜯어보기 #2] Making Queries

앞으로 예시들에 사용할 모델.
글 하나에 블로그 여러 개 중 하나가 연결되어있고, author는 m2m.

class Blog(models.Model): # 블로그
name = models.CharField(max_length=100)
tagline = models.TextField()

def __str__(self):
return self.name

class Author(models.Model): # 작가
name = models.CharField(max_length=50)
email = models.EmailField()

def __str__(self):
return self.name

class Entry(models.Model): # 글
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()

def __str__(self):
return self.headline

오브젝트 만들기

b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') # 모델 인스턴스화
b.save() # 디비에 저장. SQL의 INSERT를 호출한다. 이거 하기 전까지는 디비를 건들지 않는다.

create()는 create하고 save까지 한다.

오브젝트 바뀐거 저장

>>> b5.name = 'New name'
>>> b5.save()

save()하면 UPDATE SQL문을 날린다.

foreignkey 필드 저장

entry = Entry.objects.get(pk=1)
yoolmoo_blog = Blog.objects.get(name='유르무르 블로그')
entry.blog = yoolmoo_blog
entry.save

ManyToManyField 저장

jay = Author.objects.create(name='Jay')
entry.authors.add(jay)

josh = Author.objects.create(name='Josh')
yoolmoo = Author.objects.create(name='Yoolmoo')
entry.authors.add(josh, yoolmoo)

add로 추가한다.

오브젝트 가져오기 (Retrieving objects)

오브젝트를 retrieve하면, Manager를 통해 Queryset을 만든다. Filters는 쿼리를 좁힌다. 모든 모델들은 하나 이상의 Manager를 가진다. Manager는 기본으로 objects라는 이름.
Queryset은 디비에서 나온 오브젝트들의 컬렉션이다.
QuerySet == SELECT
filter == WHERE, LIMIT

>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
...
AttributeError: "Manager isn't accessible via Blog instances."

Managers는 모델 클래스에서만 접근 가능한다.

모든 오브젝트 가져오기

all_entries = Entry.objects.all()

Manager의 all()메서드를 쓴다. 디비 모든 오브젝트의 쿼리셋을 반환한다.

Filter로 오브젝트 걸러서 가져오기

Entry.objects.filter(pub_date__year=2006) # all() 안써도 된다.
Entry.objects.all().filter(pub_date__year=2006)

>>> Entry.objects.filter(
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.date.today()
... ).filter(
... pub_date__gte=datetime(2005, 1, 30)
... )

Queryset이 evaluated되기 전까진 장고는 쿼리를 돌리지 않는다.

get()으로 싱글 오브젝트 가져오기

filter()는 싱글 오브젝트를 반환하더라도 쿼리셋으로 반환한다.
하나 오브젝트를 가져오려면 get()을써라.

one_entry = Engry.objects.get(pk=1)

get 안에 filter처럼 쓰면 된다.
쿼리 반환값이 없을 때 get()은 DoesNotExist exception을 뱉는다. 반환값이 1 이상일때는 MultipleObjectsReturned.

주석(annotate)

Queryset의 각 오브젝트마다 annotate하기.

>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
# 첫 번째 블로그 이름
>>> q[0].name
'율무네 블로그'
# 첫 번째 블로그의 엔트리 숫자
>>> q[0].entry__count
10

>>> q = Blog.objects.annotate(number_of_entries=Count('entry'))
# 첫 번째 블로그의 엔트리 숫자. 지정한 이름으로 가져오기
>>> q[0].number_of_entries
42

쿼리셋 제한하기

파이썬 Array-slicing 문법을 사용해서 쿼리셋의 서브셋 가져오기.
SQL의 LIMIT, OFFSET과 비슷하다.

Entry.objects.all()[:5] # 앞에서부터 5개 오브젝트
Entry.objects.all()[5:10] # 6번부터 10번까지
Entry.objects.all()[-1] # 에러난다
Entry.objects.all()[:10:2] # 10번째까지 모든 오브젝트들의 2번째 오브젝트
Entry.objects.order_by('headline')[0]

Field lookups

field이름__lookuptype=value이렇게 쓰면 된다.

Entry.objects.filter(pub_date__lte='2006-01-01')
Entry.objects.filter(blog_id=4) # foreignkey일땐 _id 서픽스를 붙여서 primary key를 찾는다.

Entry.objects.get(headline__exact="개발자 생활백서") # headline이 '개발자 생활백서'인 것을 찾는다.

Blog.objects.get(id__exact=14) # Explicit form
Blog.objects.get(id=14) # __exact is implied. 위랑 같다.

Blog.objects.get(name__iexact="beatles blog") # 대소문자 구분 X

Entry.objects.get(headline__contains='Lennon') # 포함

Span relationships의 Lookups

JOIN을 자동으로 해준다.

Entry.objects.filter(blog__name='율무 블로그') # 블로그모델의 name필드가 `율무 블로그`

Reverse relationship도 참조 가능. 모델 이름을 소문자로 쓴다.

Blog.objects.filter(entry__headline__contains='율무') # 거꾸로 참조

# 두 조건 모두 만족
Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)

Spanning multi-valued relationships

M2M필드나 reverse foreignkey를 필터링할때, 두 가지 종류의 필터가 있다.
– Blog / Entry (one-to-many)
+ 엔트리 제목이 Lennon이면서 2008년에 발행된 블로그는 1개
+ 엔트리 제목이 Lennon이면서 2008년에 발행된 블로그를 찾고싶다.

Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008) # 엔트리 제목이 Lennon포함하면서 엔트리 발행일이 2008

Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008) # 엔트리 제목이 Lennon포함하는 블로그를 찾고, 거기서 엔트리 발행일이 2008인 블로그를 찾는다. 위의 쿼리셋과 다를 수 있다.

모델의 필드를 참조할 수 있는 필터

특정 모델 필드랑 다른 필드 값 비교할 때도 쓸 수 있다.

>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks')) # 커멘트가 핑백보다 수가 많은 Entry 가져오기
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks')) # 레이팅이 커멘트+핑백보다 적은 엔트리 가져오기
>>> Entry.objects.filter(authors__name=F('blog__name')) # 관계를 span하기 위해선 __를 쓴다.
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3)) # 발행일보다 3일 후 날짜보다 큰 수정일을 가진 엔트리를 가져와라

pk lookup shortcut

primary key를 좀 더 편하게 참조할 수 있게 pk라고 shortcut을 만듦

>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact

# pk가 1이나 4나 7인것
>>> Blog.objects.filter(pk__in=[1,4,7])

# pk가 14보다 큰 것
>>> Blog.objects.filter(pk__gt=14)

>>> Entry.objects.filter(blog__pk=3) # entry의 blog의 pk

쿼리셋 캐싱하기

>>> queryset = Entry.objects.all() # 재활용할거 할당해두기
>>> print([p.headline for p in queryset])
>>> print([p.pub_date for p in queryset])

>>> [entry for entry in queryset] # 데이터베이스 쿼리하기
>>> print queryset[5] # 캐시 쓰기
>>> print queryset[5] # 캐시 쓰기

Q 오브젝트로 복잡한 lookups 만들기

filter, exclude, get등에 쓸 수 있다.

from django.db.models import Q
Q(question__startswith='What')
Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) # OR연산
)

오브젝트 삭제

e.delete()

다중 오브젝트 한번에 업데이트 하기

# pub_date가 2007인 모든 엔트리 헤드라인 업데이트하기
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')

# 모든 엔트리 업데이트
>>> Entry.objects.all().update(blog=b)

update 메서드는 SQL문으로 바뀌어 바로 적용된다.

Related objects

다대일(One-to-many)

Forward
Foreignkey로 연결했으면 그냥 .으로 호출 가능

>>> e = Entry.objects.get(id=2)
>>> e.blog = some_blog
>>> e.save() #이거 해야 저장됨

>>> e = Entry.objects.get(id=2)
>>> print(e.blog) # e에서 블로그를 가져올 때 DB를 건드린다.
>>> print(e.blog) # 캐시 써서 디비를 건드리지 않는다.

>>> e = Entry.objects.select_related().get(id=2) #select_related()
>>> print(e.blog) # 캐시 써서 디비를 건드리지 않는다.
>>> print(e.blog) # 캐시 써서 디비를 건드리지 않는다.

Backward
모델이 ForeignKey를 갖고 있으면,

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # 거꾸로 연결되어있던 Entry를 소문자로 바꾸고, 뒤에 _set을 붙여주면 블로그랑 연결된 모든 entry의 쿼리셋 가져온다.

>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()

related_name파라미터를 ForeignKey만들때 쓰면 FOO_set을 오버라이드 할 수 있다.

# related_name 지정해두기
blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries')

# related_name으로 불러오기
>>> b = Blog.objects.get(id=1)
>>> b.entries.all()

다대다(Many-to-many)

e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains='John')

a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.

Refer

https://docs.djangoproject.com/en/1.9/topics/db/queries/

[특강] 패스트캠퍼스 ‘프론트엔드 개발자 생활백서’

스크린샷 2016-06-20 오후 10.29.27

S__13369355test

IT 실무교육 아카데미 패스트캠퍼스의 ‘프론트엔드 개발 캠프’에서
하루동안 ‘신입 개발자 생활백서’ 특강을 진행하고 왔습니다.
제가 개발을 시작했을 때에 도움이 되었던 모든 것들을 쏟아내고 왔습니다 🙂

필드에서 멋진 서비스로  다시 만나면 좋겠습니다.

[Django Models 뜯어보기 #1] Models

Django Models

필드(Fields)

from django.db import models

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)    
  • null: True면, DB의 컬럼에 NULL을 할당할 수 있게 된다. (DB의 NOT NULL)
  • blank: True면, 필드값 입력하지 않아도 된다. 디비의 제약과 관련 없음. 유효성 검증(validation)과 연관.

Verbose field names

ForeignKey, ManyToManyField, OneToOneField빼고 모든 필드는 첫 인자로(옵션) Verbose name을 받는다. 따로 지정하지 않으면 필드명으로 대신한다(언더스코어를 스페이스로 바꾸어서)

first_name = models.CharField("성", max_length=30)

ForeignKey, ManyToManyField, OneToOneField는 첫 인자가 모델 클래스이므로, verbose_name 키워드 인자를 줄 수 있다.

dogs = models.ManyToManyField(Dog, verbose_name='개 리스트')

관계(Relationships)

  1. Many-to-one
class Kind(models.Model):
    # 개 종류

class Dog(models.Model):
    kind = models.ForeignKey(Kind, on_delete=models.CASCADE) # 개마다 종류가 있음

  1. Many-to-many
class Food(models.Model):
    # 음식
    # 음식에 dogs가 있을 수도 있겠지만, Dog에 foods가 있는 것이 더 명확하므로 그렇게 한다.

class Dog(models.Model):
    foods = models.ManyToManyField(Food) # 개마다 먹을 수 있는 여러가지 음식. 복수 형태.

좀 더 복잡한 M2M관계를 표현하기 위해서는 별도의 모델을 만든다. (중간모델/intermediate model)

class Person(models.Model): #인간
    name = models.CharField

class Group(models.Model): #뮤직 그룹
    name = models.CharField
    # 그룹엔 멤버십으로 연결된 멤버들이 있다.
    members = models.ManyToManyField(Person, through='Membership') #멤버스는 멤버십을 통해 생긴다.

class Membership(models.Model): #뮤지컬 멤버십
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")

# 멤버십에 person, group 넣어서 만들면 그 그룹에 person이 들어가서 생긴다.
>>> m1 = Membership(person=ringo, group=beatles, 
...     date_joined=date(1962, 8, 16),
...     invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
[<Person: Ringo Starr>]
>>> ringo.group_set.all()
[<Group: The Beatles>]

>>> m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason="Wanted to form a band.")
>>> beatles.members.all()
[<Person: Ringo Starr>, <Person: Paul McCartney>]

# 아래 3개처럼 추가는 안된다.
>>> beatles.members.add(john)
>>> beatles.members.create(name="George Harrison")
>>> beatles.members = [john, paul, ringo, george]

# intermediate model로 만들었어도, 쿼리 아래처럼 평범하게 날릴 수 있다.
>>> Group.objects.filter(members__name__startswith='Paul')
[<Group: The Beatles>]

# intermediate model 만들때 attribute로도 쿼리 날릴 수 있다.
>>> Person.objects.filter(
...     group__name='The Beatles',
...     membership__date_joined__gt=date(1961,1,1))
  • 중간 모델을 직접 만들 때는, 관계를 가지는 두 모델에 대한 ForeignKey필드를 선언하고 추가적인 필드를 선언하면 된다.
  1. One-to-one
    다른 모델을 확장하여 새로운 모델을 만드는 경우 유용.
    가게라는 데이터베이스가 이미 있었는데, 맛집 데이터베이스를 추가적으로 만들게 되었다. 이 때 가게를 extend받아서 확장시킬 수 있다.

Meta 옵션

모델클래스 내부에 메타데이터를 추가할 수 있다.

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"] # 순서 정의
        verbose_name_plural = "oxen"

Model Attributes – objects

모델에서 가장 중요한 attribute은 Manager다. 모델 클래스 선언 기반하여 실제 디비에 대한 쿼리 인터페이스를 제공하며, 디비 레코드를 모델 객체로 인스턴스화 하는데 사용된다.
커스텀 Manager를 만들지 않으면, 기본은 ‘objects’라는 이름으로 잡힌다.

class Person(models.Model):
    people = models.Manager()

위와 같이 써주면 Person.objects는 AttributeError를 반환하지만,
Person.people.all()은 모든 Person오브젝트들을 반환한다.

모델에 메서드 추가하기

class Person(models.Model)
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    def baby_boomer_status(self):
        "사람의 베이비 부머 status를 보여준다."
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(1965, 1, 1):
            return "Baby boomer"
        else:
            return "Post-boomer"

    def _get_full_name(self):
        "사람의 풀네임을 반환한다."
        return '%s %s' % (self.first_name, self.last_name)
    full_name = property(_get_full_name)
  • __unicode__(): 해당 클래스의 유니코드 표현. (python2)
  • __str__(): python3에서 위와 같다. utf-8
  • get_absolute_url(): 오브젝트의 URL. 어드민이나 오브젝트에 url이 필요할 때 쓰인다.

미리 정의된 메서드 오버라이드

save()delete()를 오버라이드 할 경우가 많다.

class Blog(models.Model):
    name = models.CharField(max_length=100)

    def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs) # 원래 save() method 부르기
        do_something_else()

superclass method를 불러서 원래 기능을 호출한다.

모델 상속(Inheritance)

Abstract base classes

공통적 정보를 다수의 모델에 넣고 싶을 때 유용.

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True #abstract = True로 해준다.

class Student(CommonInfo): #여기에 상속
    home_group = models.CharField(max_length=5)

related_name이 겹치지 않도록 unique한 reverse_name을 적어준다.

from django.db import models

class Base(models.Model):
    m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related")

    class Meta:
        abstract = True

class ChildA(Base): # related_name: common_childa_related
    pass

class ChildB(Base): # related_name: common_childb_related
    pass

Multi-table inheritance

공통 부분의 데이터는 부모모델에 저장, 자식 모델의 데이터는 자식모델 테이블에 저장.

refer

https://docs.djangoproject.com/es/1.9/topics/db/models/
http://nukggul.tistory.com/17