리스트와 배열(Array)은 사실 서로 조금 다르다. stackoverflow 참조
리스트에 대해 먼저 알아보자.
위의 그림처럼 데이터를 제거할 때, 빈 자리를 앞으로 한 칸씩 채운다. 이러한 과정으로 인하여 상대적으로 다른 데이터 구조에 비해 느릴 수 있다.
메모리를 미리 할당하기 때문에 리스트의 크기가 커지면, 위와 같이 Resizing 한다. 마찬가지로 속도가 다른 데이터 구조에 비해 오래 걸린다.
위에서 보이는 것처럼, key 값을 해쉬함수라는 것을 통해서 변환시킨다. 그리고 bucket 이라는 공간에 저장한다. hash 는 중복 값을 허용하지 않으므로, 각 key 마다 다른 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을 적용해주는 것이 좋을 것 같다.