TIL (2020.06.15)

Awesome·2020년 6월 15일
0

TIL

목록 보기
14/46

자료형 (List, Tuple, Dictionary, Hash)

List

리스트와 배열(Array)은 사실 서로 조금 다르다. stackoverflow 참조

리스트에 대해 먼저 알아보자.

  • 순차적으로 데이터를 저장한다.
  • 삽입이 순서대로 된다.
  • 수정 가능하다.
  • 요소의 중복이 가능하다.
  • 메모리를 미리 할당한다.

위의 그림처럼 데이터를 제거할 때, 빈 자리를 앞으로 한 칸씩 채운다. 이러한 과정으로 인하여 상대적으로 다른 데이터 구조에 비해 느릴 수 있다.

메모리를 미리 할당하기 때문에 리스트의 크기가 커지면, 위와 같이 Resizing 한다. 마찬가지로 속도가 다른 데이터 구조에 비해 오래 걸린다.

Tuple

  • 리스트와 거의 비슷하지만 가장 큰 차이점은 수정이 불가능하다는 점이다.
  • 리스트보다 빠르다.
  • 메모리 부담이 적다.

Dictionary

  • Key : Value 로 구성됨

위에서 보이는 것처럼, key 값을 해쉬함수라는 것을 통해서 변환시킨다. 그리고 bucket 이라는 공간에 저장한다. hash 는 중복 값을 허용하지 않으므로, 각 key 마다 다른 hash 값을 갖는다.

Set

  • list나 tuple과 유사한 형태이지만 순서가 없다.
  • dictionary와 마찬가지로 hash값을 이용하여 데이터를 저장한다.
  • 따라서 순서가 없고, 인덱스도 없다.
  • 또한, 중복 값이 없다.
  • 속도는 list에 비해서 빠르고, 수정이 가능하다.

Hash

Hash는 단방향 암호화이다. 가령 ABC 라는 값이 hash함수를 통해 1x2ot5k29 라는 값으로 암호화 되었다고 가정해보자. 그럼 반대로 1x2ot5k29 를 hash함수로 넣으면 ABC가 나올까?
단방향 암호화이기 때문에 불가능하다는 것이다.

또한 중복을 허용하지 않는다. 즉, ABC는 hash 함수를 백 만번 돌려도 항상 hash 값은 1x2ot5k29 이다. 웹 서비스에서 각각의 클라이언트에게 private 한 정보를 제공하기 위해서는 로그인을 통한 인증 절차가 필수적이다. 이 때, 계정을 암호화하는 방식으로 hash 가 사용된다.

그러나 문제점도 있다. 앞서 언급되었듯이 각각의 hash 값은 반드시 일대일로 매칭되는 key가 있다. 따라서 수 많은 hash 값들을 모아서 분석하면 원래의 key 값을 찾아낼 수 있는 것이다. 이를 분석해 놓은 테이블을 Rainbow Table 이라고 한다.

허접한 개발 뉴비 입장에서는 hash 값이 무섭게 나와서 이거 쩐다 싶은데, 그것마저도 뚫어내는 대단한 괴짜들이 많은가보다. 이러니 내 개인정보가 파키스탄이나 산둥성에 가있는거 아니겠나.

각설하고, 그럼 뭐 hash 보완책이 없나? 생각이 든다. 당연히 있다.

바로 salt 를 추가하는 것이다. salt 는 말그대로 소금이다. 양념 한 번 더 친다고 보면 된다. 원래의 값에 hash처리 된 salt를 덧붙인 상태에서 hash 함수에 넣는다. salt는 완전 난수이기 때문에 hash 이전의 값을 추측하는 것이 아예 불가능하다.

또 다른 괴짜들이 언젠가는 찾아낼 수도 있겠지만 일단 현재는 셀 수 없는 경우의 수를 추적하는 일은 현실적으로 불가능에 가깝다고 한다.

보안 같은 분야는 나랑은 전혀 관계없는 일이라고 생각했었는데, 개발에는 관계가 있는 일과 없는 일이 나누어져 있는 것은 아닌 것 같다.

그 밖에 알게 된 것들

로그인과 쿠키, 세션

Django 에서 models.py의 필드를 정의할 때, 다른 테이블을 참조하기 위한 ForeignKey를 설정한다. 이 때, 역참조를 위한 related_name을 설정할 수 있다.

# user.models.py
from django.db import models

class User(models.Model):

    """ User Model Definition """
    name=models.CharField(max_length=50)
    password=models.CharField(max_length=100)
    
    class Meta:
        db_table = "users"
        
    def __str__(self):
        return self.name
    
# comment.models.py
from django.db import models

class Comment(models.Model):

    """ Comment Model Definition """
    user = models.ForeignKey("User.user", on_delete=models.CASCADE, related_name="comment")
    comment = models.CharField(max_length=300)
    
    class Meta:
        db_table = "comments"
    
    def __str__(self):
        return self.comment

위와 같은 예제가 있다. 회원 정보를 담은 users 테이블이 있고, comments 라는 댓글 내용을 담은 테이블이 있다. comments 테이블에 ForeignKey를 설정했으므로, users 테이블을 참조한다.
즉, 어떠한 댓글이 남겨졌을 때 이 댓글을 남긴 사람의 user_id를 알 수 있다.

comment : "첫 번째 댓글!" // user_id : 1

여기까지는 큰 어려움은 없다. comments 테이블에서는 언제든지 댓글에 대한 user_id를 확인할 수 있다.

그럼 users 테이블에서 comments를 참조하기 위해서는 어떻게 할까?
이게 바로 역참조다. ForeignKey를 가지고 있는 테이블이 아니라 참조를 당하는 테이블에서 참조하는 테이블을 바라보는 것이다. 이때, related_name 이 매우 유용하다.

# python shell
from user.models import User
u = User.objects.get(id=1)   # 인스턴스를 생성한다.
u.comment.all()  # 인스턴스에 related_name을 manager로 사용하여 역참조 값에 접근한다.

related_name 은 ForeignKey가 적용된 테이블이 아니라 참조를 당하는 테이블 입장에서 사용하는 속성이다. 따라서, related_name = "user" 라고 했다면, u.user.all() 과 같이 작성해야 한다. 근데 이러면 의미상 너무 어색하다.

user(A) 가 남긴 comment 를 전부 찾기 위한 것인데, user의 user값을 가저와라(?) 뭐 이런 식으로 보이기 때문에 의미 전달이 매우 불분명해진다.

related_name은 테이블 간의 관계가 many-to-one 든 many-to-many 든 상관 없이 역참조를 쉽게 표현하기 위한 속성값으로 볼 수 있다. 테이블 간의 관계가 복잡해질수록 코드도 가독성이 떨어지고 어려워진다. 특히 협업을 할 때일수록 내 코드가 다른 사람이 봐도 읽기 쉬운 코드가 되려면, 관계를 잘 정의할 필요가 있다. 그런 점에서 매우 유용할 것으로 보인다.

** 물론 related_name 을 설정하지 않아도 역참조는 가능하다. 기본 default로 class명_set 형태로 related_name이 생성되기 때문이다. 하지만 related_name을 사용하는 이유는 코드에 대한 가독성을 높이기 위함이다. 따라서 가능하면 related_name을 적용해주는 것이 좋을 것 같다.

profile
keep calm and carry on

0개의 댓글