본문 바로가기

내일배움캠프

파이썬 팀과제 - 클래스 사용해보기!

과제 내용:

  1. Member 클래스와 Post 클래스를 정의하세요.
  2. Member 클래스에는 다음과 같은 속성을 가지고 있어야 합니다.
    • 회원 이름 (name)
    • 회원 아이디 (username)
    • 회원 비밀번호 (password)
  3. Member 클래스에는 다음과 같은 메소드를 가지고 있어야 합니다.
    • 회원 정보를 print해주는 display (회원이름과 아이디만 보여주고 비밀번호는 보여줘서는 안됩니다!)
  4. Post 클래스에는 다음과 같은 속성을 가지고 있어야 합니다.
    • 게시물 제목 (title)
    • 게시물 내용 (content)
    • 작성자 (author) : 회원의 username 이 저장되어야 함!
  5. 회원 인스턴스를 세개 이상 만들고 members 라는 빈리스트에 append를 써서 저장해주세요
    • members 리스트를 돌면서 회원들의 이름을 모두 프린트 해주세요
  6. 각각의 회원이 게시글을 세개 이상 작성하는 코드를 만들어주세요.(회원이 세명이명 총 9개 이상의 post 인스턴스가 만들어져야 합니다). 만들어진 게시글 인스턴스들은 posts 빈리스트에 append를 써서 저장해주세요
    • for 문을 돌면서 특정유저가 작성한 게시글의 제목을 모두 프린트 해주세요
    • for문을 돌면서 ‘특정 단어’가 content에 포함된 게시글의 제목을 모두 프린트 해주세요

 

추가 도전 과제:

  1. input을 이용하여 Member 인스턴스 만드는것을 사용자가 터미널에서 할 수 있게 해주세요.
  2. post도 터미널에서 생성할 수 있게 해주세요.
  3. (심화)비밀번호 해싱이 무엇인지 공부한 후 hashlib 라이브러리를 써서 회원 비밀번호를 해시화하여 저장하게 해주세요.

 

팀원들과 회의를 한 결과 이번 과제는 각자 연습해보기로 결정했다. 이유는 각자 심화강의의 진도가 달라서, 클래스를 처음 다루어보아서 연습목적으로, 분량이 많이 않아서 어떻게 나눠야할지 정하지 못했음 등이 있었다.

나도 마침 파이썬의 클래스에 대해서 더 공부해보고 싶었고 마침 좋은 기회라고 생각되어 혼자서 이번 과제를 수행해보기로 했다. 명목상 데드라인은 내일까지지만 최대한 오늘안에 끝내고 다른 공부를 해보자! 할게 너무 많다.

 

문제 1 ~ 4

class Member:
    def __init__(self, name, username, password):
        self.name = name
        self.username = username
        self.password = password

    def display(self):
        return f'이름: {self.name}, 닉네임: {self.username}'

class Post:
    def __init__(self, title, content, Member):
        self.title = title
        self.content = content
        self.author = Member.username

Member와 Post 클래스를 각각 만들고 기본적인 속성들과 메소드를 생성했다. 그리고 Post의 작성자는 Member의 username을 가지도록 한다.

 

문제 5

members = []

member_1 = Member('김철수', 'IronWater', '철수9999')
member_2 = Member('김영희', 'ZeroH', '영희8888')
member_3 = Member('바둑이', 'Chess', '바둑7777')

members.append(member_1)
members.append(member_2)
members.append(member_3)

for member in members:
    print(member.name)

 

members 리스트를 생성한 후 append를 이용해서 인스턴스들을 입력했다. 그리고 for반복문을 사용해서 각 인스턴스들의 name속성을 출력했다.

 

문제 6

posts = []

post_1 = Post('안녕하세요.', '가입했습니다. 잘부탁드립니다.', member_1)
post_2 = Post('처음 뵙겠습니다.', '첫 가입입니다.', member_2)
post_3 = Post('멍멍멍멍', '왈왈왈', member_3)
post_4 = Post('등산을 갔다왔습니다.', '경치가 좋네요.', member_1)
post_5 = Post('등산 부럽네요.', '저는 야근중입니다.', member_2)
post_6 = Post('멍멍멍', '으르렁', member_3)
post_7 = Post('잘 지내고 계신가요.', '처음 가입했을때가 엊그제같군요.', member_1)
post_8 = Post('안녕하세요.', '오래간만입니다.', member_2)
post_9 = Post('야옹야옹', '야옹', member_3)

posts.append(post_1)
posts.append(post_2)
posts.append(post_3)
posts.append(post_4)
posts.append(post_5)
posts.append(post_6)
posts.append(post_7)
posts.append(post_8)
posts.append(post_9)

for post in posts:
    if post.author == member_3.username:
        print(post.title)
        
for post in posts:
    if '가입' in post.content:
        print(post.title)

 

Posts 리스트를 생성한 후 append를 사용해서 인스턴스들을 입력했다. 그리고 for반복문을 이용해서 조건에 맞는 게시글들의 제목을 출력했다.

1번조건 - 바둑이가 작성한 게시글의 제목

2번조건 - 게시글 내용에 '가입'을 포함하고 있는 게시글의 제목

 

추가과제 1 - input을 이용하여 Member 인스턴스 만드는것을 사용자가 터미널에서 할 수 있게 해주세요.

members = []

member_1 = Member('김철수', 'IronWater', '철수9999')
member_2 = Member('김영희', 'ZeroH', '영희8888')
member_3 = Member('바둑이', 'Chess', '바둑7777')

member_4_name = input('이름을 입력하세요.')
member_4_username = input('닉네임을 입력하세요.')
member_4_password = input('비밀번호를 입력하세요.')

member_4 = Member(member_4_name, member_4_username, member_4_password)

members.append(member_1)
members.append(member_2)
members.append(member_3)
members.append(member_4)

for member in members:
    print(member.name)

출력확인

members를 출력하기 전에 새로운 member의 정보를 입력받고 함께 출력을 해주었다.

 

 

추가과제 2 - Post도 터미널에서 생성할 수 있게 해주세요.

posts = []

post_1 = Post('안녕하세요.', '가입했습니다. 잘부탁드립니다.', member_1)
post_2 = Post('처음 뵙겠습니다.', '첫 가입입니다.', member_2)
post_3 = Post('멍멍멍멍', '왈왈왈', member_3)
post_4 = Post('등산을 갔다왔습니다.', '경치가 좋네요.', member_1)
post_5 = Post('등산 부럽네요.', '저는 야근중입니다.', member_2)
post_6 = Post('멍멍멍', '으르렁', member_3)
post_7 = Post('잘 지내고 계신가요.', '처음 가입했을때가 엊그제같군요.', member_1)
post_8 = Post('안녕하세요.', '오래간만입니다.', member_2)
post_9 = Post('야옹야옹', '야옹', member_3)

post_0_title = input('제목을 입력하세요.')
post_0_content = input('내용을 입력하세요.')

post_0 = Post(post_0_title, post_0_content, member_4)

posts.append(post_1)
posts.append(post_2)
posts.append(post_3)
posts.append(post_4)
posts.append(post_5)
posts.append(post_6)
posts.append(post_7)
posts.append(post_8)
posts.append(post_9)
posts.append(post_0)

출력확인

 

1번과 마찬가지로 새로운 post를 생성했다.

사실 1번과 2번과제를 하면서 고민이 많았는데 클래스 추가를 이런식으로 해도 되는지였다. 코드를 보면 알겠지만 유효성검사도 제대로 되지 않았고 멤버추가도 단한번, 포스트 추가도 단한번 게다가 작성자는 고를 수도 없다. 하지만 그렇다고 다른 방법이 있는것도 아니고 동적 변수를 생성하는 것은 권장되지 않는다고 해서 이 이상 작성하지는 않았다. 이 이상으로 나아가는 것은 데이터베이스의 영역이라고 생각되기도 하고...

 

 

추가과제 3 - (심화)비밀번호 해싱이 무엇인지 공부한 후 hashlib 라이브러리를 써서 회원 비밀번호를 해시화하여 저장하게 해주세요.

해시란 데이터를 단방향으로 암호화하는 것이다. 즉 같은 값을 입력하면 같은 결과를 얻을 수 있지만 해시된 데이터를 역추적하는 것은 거의 불가능하다. 따라서 비밀번호를 저장하는데에는 매우 적절한 방식일 것이다. 그리고 해시 '함수'라고 지칭하는 것을 들어보니 특정 값을 입력해서 해싱된 값을 출력한다는 것이 더 직관적으로 이해가 되었다.

조금 더 찾아보니 해시함수의 종류도 아주 많았다. MD5, SHA-256, HAS-180, PBKDF2 등등... 나는 SHA-256를 사용해보려고 한다. 아직은 각 해시함수들이 어떤 특징을 가졌는지 잘 모르기때문에 그냥 임의로 선택을 했다...

 

import hashlib

password = "Hello World!"
hash_object = hashlib.sha256(password.encode())

# 해시 값을 16진수 문자열로 변환
hashed_password = hash_object.hexdigest()

print(f"원본 비밀번호: {password}")
print(f"해시된 비밀번호: {hashed_password}")

Hello World!의 해시값

 

처음 다루어 보는 함수였기 때문에 어떻게 작동을 하는지 가볍게 연습을 해보았다. 입력한 값이 해시 함수를 거쳐서 암호화 되어서 출력되는것같다.

 

import hashlib

password = "Hello World!"
hash_object = hashlib.sha256(password.encode())

print(f"원본 비밀번호: {password}")
print(f"해시된 비밀번호: {hash_object}")

 

한가지 모르겠는 점은 hashed_password = hash_object.hexdigest()부분을 삭제하면 값이 위와 같이 나온다는 것이었다. 확실히는 모르겠지만 써있는 것을 읽어보니  SHA-256로 해시를 한 오브젝트라는 뜻인것같다. 그럼 위의 메시지는 아마도 입력한 문자열을 해싱했다는 의미인것같다.
hashed_password = hash_object.hexdigest() 라는 구문을 추가로 작성한 것은 해시값을 16진수로 변환해서 해시한 문자열을 반환하기 위함일 것이다.

 

class Member:
    def __init__(self, name, username, password):
        self.name = name
        self.username = username
        hash_object = hashlib.sha256(password.encode())
        hashed_password = hash_object.hexdigest()
        self.password = hashed_password
members = []

member_1 = Member('김철수', 'IronWater', '철수9999')
member_2 = Member('김영희', 'ZeroH', '영희8888')
member_3 = Member('바둑이', 'Chess', '바둑7777')

member_4_name = input('이름을 입력하세요.')
member_4_username = input('닉네임을 입력하세요.')
member_4_password = input('비밀번호를 입력하세요.')

member_4 = Member(member_4_name, member_4_username, member_4_password)

members.append(member_1)
members.append(member_2)
members.append(member_3)
members.append(member_4)

해싱된 패스워드 출력확인

 

이제 본격적으로 적용해보자. Member클래스에서 password속성을 받는 부분에서 입력값을 해싱해주는 구문을 추가했다.

그리고 시험삼아서 예문을 입력하고 해싱된 패스워드들을 출력해보았고 내용은 차례대로 다음과 같았다.

 

# member_1의 해싱 비밀번호

6f64fd233e5e402af79e5b3767db42049a4baacc6b08ad18cfbfe1032cd4cc04

# member_2의 해싱 비밀번호
46b4b37e842ed8c07ccc1ef340c385f0388c335f2755ed5219858c5d7c02dd74

# member_3의 해싱 비밀번호
7df7b2911fd8b6871f8ff30eb84574bd9329884a84d41b949fe3ebc95daea0ef

# member_4의 해싱 비밀번호
7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069

 

그런데 위의 패스워드들에서 어디서 본듯한 모양이 보인다. 바로 member_4의 해싱 패스워드이다.

위의 예시를 본다면 내가 비밀번호를 Hello world!라고 입력을 했는데 그것에 대한 해시값을 16진수로 변환해서 출력한 것이다. 그리고 조금 더 위로 올라가서 내가 연습할때 입력했던 예문을 보자. member_4의 패스워드와 동일한 문자열을 입력했다. 즉, 입력값이 동일했기때문에 같은 값을 반환한 것을 알 수 있었다.

 

이것으로 마지막 과제가 끝이 났다. 아직 해시값에 대해 정확하게 안다라고 할 정도는 아니었지만 어느정도는 알게되었다.

다만 이번 과제는 특히나 이렇게 하는게 정말 맞나? 싶은 부분이 많았다. 과제 내용을 볼 때, append를 사용하라고 해서 같은 구문을 여러번 반복한 부분이라던지... 입력값을 리스트에 저장하는게 정말 맞나? 딕셔너리가 나은게 아닐까? 아니면 데이터베이스를 더 공부하고 시도해야하는 부분은 아닐까? 등등...

하지만 개선을 위해서 여러가지 정보를 찾아보다보니 문제들이 여럿 보였다. 반복문을 사용해서 인스턴스들을 연속해서 리스트에 입력할 경우 인스턴스의 변수명이 달라지기 때문에 인덱스값을 모른다면 원하는 값을 출력할 수 없다는 문제들이 있었다. 아마 그때문에 데이터베이스를 사용하는 것이 아닐까?

어떻게든 문제를 개선해보기 위해서 동적변수라는 것도 알아보았지만 입력값에 적용하는 것은 권장되지 않는 방식이라고 한다...

그래서 결국에는 조금 구려보이는 코드가 완성되었다. 여기서 더 세련되게 만드는 것은 백엔드만의 영역이 아니라고 생각했기 때문이다.