8강. 회원가입 과제

0. 학습 목표
→ 회원가입 정보를 JSON 파일에 저장하고, 비밀번호는 해시값으로 관리하는 과제를 이해합니다.
이번 과제에서 할 일
이번 과제에서는 기존 로그인 프로그램에 회원가입 View를 추가합니다.
사용자가 회원가입 화면에서 아이디, 비밀번호, 이름, 이메일, 전화번호를 입력하면 새 회원 정보가 member.json 파일에 저장되어야 합니다.
중요한 점은 비밀번호를 그대로 저장하지 않는 것입니다.
비밀번호는 password_hash 형태로 변환해서 저장해야 합니다.
| 구분 | 내용 |
| 핵심 개념 | 회원가입 정보를 파일에 저장하고, 비밀번호는 해시값으로 관리합니다. |
| 실습 준비 | PySide6, QTabWidget, QLineEdit, QPushButton, QLabel, JSON 파일, hashlib를 사용합니다. |
| 최종 목표 | 회원가입한 사용자 정보가 member.json에 저장되고, 가입한 ID/PW로 로그인할 수 있도록 구현합니다. |
| 제출 기한 | X월 X일 X요일까지 제출하세요. |

이번 과제의 핵심: 회원가입으로 새 회원을 추가하고, 비밀번호는 평문이 아니라 password_hash로 저장해야 합니다.
1. 왜 회원가입 기능이 필요할까?
→ 기존 로그인 프로그램은 미리 저장된 회원만 사용할 수 있었기 때문에 새 회원을 추가하는 기능이 필요합니다.
1.1 기존 프로그램의 한계
기존 프로그램에서는 회원 정보가 미리 준비되어 있었습니다.
예를 들어 member.json 파일에 test 회원이 이미 들어 있고, 사용자는 그 계정으로만 로그인할 수 있었습니다.
# 기존 member.json 예시
{
"id": "test",
"password_hash": "03ac674216f3e15c...",
"name": "홍길동",
"email": "test@test.com",
"phone": "010-1234-5678"
}
이 구조는 로그인과 파일 저장을 이해하기에는 좋습니다.
하지만 새로운 사용자가 직접 회원가입을 할 수 없습니다.
| 상황 | 문제점 |
| 회원 정보가 1명만 있음 | test 계정 외에는 로그인할 수 없습니다. |
| 회원가입 화면이 없음 | 새 사용자가 직접 계정을 만들 수 없습니다. |
| 여러 회원 목록이 없음 | 회원 여러 명을 관리하기 어렵습니다. |
1.2 이번 과제에서 확장할 구조

이번 과제에서는 회원 1명만 저장하는 구조를 여러 회원을 저장하는 구조로 확장합니다.
또한 QTabWidget에 회원가입 View를 추가합니다.
# 기존 구조
QTabWidget
├── 로그인 View
├── 메인화면 View
└── 프로필 View
# 새 구조
QTabWidget
├── 로그인 View
├── 회원가입 View
├── 메인화면 View
└── 프로필 View
회원가입 View에서 새 회원 정보를 입력하면 MemberModel모델이 회원가입 처리를 담당합니다.
MemberStorage저장소는 변경된 회원 목록을 member.json 파일에 저장합니다.
문제의 핵심: 미리 만들어진 회원만 로그인하는 구조에서, 사용자가 직접 회원가입하고 로그인할 수 있는 구조로 확장해야 합니다.
2. 회원가입 화면 구조 설계하기
→ 회원가입 View에 필요한 입력칸과 버튼을 정리합니다.
2.1 회원가입 View에 필요한 입력 요소
회원가입 View에는 새 회원 정보를 입력할 수 있는 입력칸이 필요합니다.
비밀번호는 실수 방지를 위해 한 번 더 입력받습니다.
| 입력 항목 | 설명 |
| 아이디 | 로그인할 때 사용할 회원 ID입니다. |
| 비밀번호 | 로그인할 때 사용할 비밀번호입니다. |
| 비밀번호 확인 | 비밀번호를 한 번 더 입력해 같은지 확인합니다. |
| 이름 | 회원 이름입니다. |
| 이메일 | 회원 이메일입니다. |
| 전화번호 | 회원 전화번호입니다. |
2.2 회원가입 View 구현 조건
회원가입 View에는 입력칸과 회원가입 버튼, 결과 메시지 라벨이 있어야 합니다.

| 요소 | 조건 |
| ID 입력칸 | QLineEdit으로 구현합니다. |
| PW 입력칸 | QLineEdit으로 구현하고 비밀번호처럼 보이도록 EchoMode를 설정합니다. |
| PW 확인 입력칸 | PW와 같은지 검사합니다. |
| 회원가입 버튼 | clicked.connect(...)로 회원가입 처리 함수와 연결합니다. |
| 메시지 라벨 | 회원가입 성공 또는 실패 이유를 출력합니다. |
핵심 확인: 회원가입 View는 입력을 받는 역할만 하고, 실제 회원가입 처리는 MemberModel에게 요청해야 합니다.
3. 회원가입 Model 기능 설계하기
→ MemberModel에 회원가입, 중복 검사, 로그인 검증 기능을 추가합니다.
3.1 MemberModel에 필요한 기능
이번 과제에서는 MemberModel이 회원가입, 로그인, 프로필 수정을 모두 관리해야 합니다.
단, 화면 코드를 Model 안에 넣으면 안 됩니다.
MemberModel은 데이터와 기능만 담당해야 합니다.
| 기능 | 설명 |
| register_member() | 새 회원 정보를 members 목록에 추가합니다. |
| is_duplicate_id() | 이미 존재하는 아이디인지 확인합니다. |
| check_login() | 여러 회원 중 ID와 password_hash가 일치하는 회원을 찾습니다. |
| get_member_info() | 현재 로그인한 회원 정보를 반환합니다. |
| update_profile() | 현재 로그인한 회원의 이름, 이메일, 전화번호를 수정합니다. |
3.2 회원가입 입력 검증 조건

회원가입 버튼을 눌렀을 때 바로 저장하면 안 됩니다.
먼저 입력값이 올바른지 확인해야 합니다.
| 검증 항목 | 조건 |
| 빈 값 검사 | ID, PW, PW 확인, 이름, 이메일, 전화번호가 비어 있으면 가입할 수 없습니다. |
| 비밀번호 확인 | PW와 PW 확인 값이 다르면 가입할 수 없습니다. |
| 아이디 중복 검사 | 이미 같은 ID가 있으면 가입할 수 없습니다. |
| 비밀번호 해시 저장 | PW는 그대로 저장하지 않고 password_hash로 저장해야 합니다. |
주의할 점: 비밀번호는 절대 "pw": "1234" 형태로 저장하지 않고, 반드시 "password_hash": "..." 형태로 저장해야 합니다.
4. member.json 파일 구조 설계하기
→ 회원 1명 저장 구조를 여러 회원 목록 저장 구조로 확장합니다.

4.1 기존 파일 구조
기존 구조는 회원 1명의 정보만 저장했습니다.
# 기존 member.json 구조
{
"id": "test",
"password_hash": "03ac674216f3e15c...",
"name": "홍길동",
"email": "test@test.com",
"phone": "010-1234-5678"
}
이 구조는 간단하지만 여러 회원을 저장하기 어렵습니다.
4.2 새 파일 구조
이번 과제에서는 여러 회원을 저장하기 위해 members 목록을 사용합니다.
# 새 member.json 구조
{
"members": [
{
"id": "test",
"password_hash": "03ac674216f3e15c...",
"name": "홍길동",
"email": "test@test.com",
"phone": "010-1234-5678"
},
{
"id": "kim",
"password_hash": "a8b3f2...",
"name": "김철수",
"email": "kim@test.com",
"phone": "010-9999-8888"
}
]
}
회원가입을 하면 새 회원 딕셔너리가 members 목록에 추가되어야 합니다.
로그인할 때는 members 목록 안에서 입력한 ID와 password_hash가 일치하는 회원을 찾아야 합니다.
| 항목 | 설명 |
| members | 여러 회원 정보를 담는 리스트입니다. |
| id | 로그인할 때 사용할 회원 아이디입니다. |
| password_hash | 비밀번호를 해시로 변환한 값입니다. |
| name, email, phone | 메인화면과 프로필 화면에 표시할 회원 정보입니다. |
핵심 확인: 회원가입 과제에서는 회원 정보를 하나의 딕셔너리가 아니라 members 리스트 안에 여러 개의 딕셔너리로 저장해야 합니다.
5. 회원가입부터 로그인까지 동작 흐름 살펴보기
→ 회원가입 성공, 파일 저장, 로그인 성공, 프로필 수정 흐름을 단계별로 정리합니다.

5.1 회원가입 성공 흐름
회원가입 버튼을 눌렀을 때 성공하면 다음 순서로 동작해야 합니다.
# 회원가입 성공 흐름
회원가입 View에서 정보 입력
↓
회원가입 버튼 클릭
↓
빈 값 검사
↓
비밀번호와 비밀번호 확인 비교
↓
아이디 중복 검사
↓
비밀번호를 password_hash로 변환
↓
members 목록에 새 회원 추가
↓
member.json 파일에 저장
↓
회원가입 성공 메시지 출력
↓
로그인 View 탭으로 이동
회원가입이 성공하면 로그인 View로 이동하도록 구현하면 사용자가 바로 로그인할 수 있습니다.
5.2 회원가입 실패 흐름
입력값이 올바르지 않으면 회원가입을 진행하지 않습니다.
실패 이유를 메시지로 출력하고 회원가입 View에 그대로 머무릅니다.
| 실패 상황 | 출력 메시지 예시 |
| 빈 값이 있음 | 모든 값을 입력하세요. |
| 비밀번호 확인 불일치 | 비밀번호가 서로 다릅니다. |
| 아이디 중복 | 이미 사용 중인 아이디입니다. |
5.3 가입한 회원으로 로그인하는 흐름
회원가입이 끝나면 가입한 ID와 PW로 로그인할 수 있어야 합니다.
# 가입한 회원 로그인 흐름
로그인 View에서 ID, PW 입력
↓
로그인 버튼 클릭
↓
입력한 PW를 hash_password()로 변환
↓
members 목록에서 같은 ID 찾기
↓
저장된 password_hash와 입력 PW 해시값 비교
↓
일치하면 로그인 성공
↓
현재 로그인한 회원 정보를 저장
↓
메인화면 View 탭으로 이동
로그인에 성공하면 현재 로그인한 회원 정보를 기준으로 메인화면과 프로필 화면이 갱신되어야 합니다.
5.4 프로필 수정 흐름
프로필 수정은 전체 회원이 아니라 현재 로그인한 회원에게만 적용되어야 합니다.
# 프로필 수정 흐름
프로필 View에서 이름, 이메일, 전화번호 수정
↓
수정하기 버튼 클릭
↓
현재 로그인한 회원 찾기
↓
해당 회원의 name, email, phone 수정
↓
member.json 파일에 저장
↓
메인화면 View와 프로필 View 갱신
중요: 여러 회원을 저장할 때는 프로필 수정이 모든 회원에게 적용되지 않도록, 현재 로그인한 회원만 찾아서 수정해야 합니다.
6. 구현 조건과 제출 전 체크리스트 정리하기
→ 과제에서 반드시 지켜야 할 조건과 제출 전 확인 항목을 정리합니다.
6.1 핵심 구현 조건
반드시 다음 조건을 지켜서 구현하세요.
| 번호 | 조건 |
| 1 | QTabWidget에 회원가입 View를 추가합니다. |
| 2 | 회원가입 View에서 ID, PW, PW 확인, 이름, 이메일, 전화번호를 입력받습니다. |
| 3 | 회원가입 시 빈 값 검사를 수행합니다. |
| 4 | PW와 PW 확인 값이 같은지 검사합니다. |
| 5 | 이미 존재하는 ID는 가입할 수 없도록 중복 검사를 합니다. |
| 6 | 비밀번호는 그대로 저장하지 않고 password_hash로 저장합니다. |
| 7 | member.json에는 members 리스트 구조로 여러 회원을 저장합니다. |
| 8 | 회원가입한 ID/PW로 로그인할 수 있어야 합니다. |
| 9 | 로그인 성공 시 메인화면과 프로필 화면에 현재 로그인한 회원 정보가 출력되어야 합니다. |
| 10 | 프로필 수정 시 현재 로그인한 회원 정보만 변경되어야 합니다. |
6.2 권장 프로젝트 구조
프로젝트 구조는 아래처럼 구성하는 것을 권장합니다.
# 권장 프로젝트 구조
signup_login_hash_project/
├── main.py
├── member_model.py
├── member_storage.py
├── password_utils.py
└── tab_window.py
| 파일 | 역할 |
| main.py | QApplication을 만들고 프로그램을 실행합니다. |
| member_model.py | 회원가입, 로그인 검사, 프로필 수정을 담당합니다. |
| member_storage.py | member.json 파일 저장과 불러오기를 담당합니다. |
| password_utils.py | 비밀번호를 password_hash로 변환하는 함수를 담당합니다. |
| tab_window.py | QTabWidget과 각 View 화면 구성을 담당합니다. |
6.3 제출 전 확인 사항
제출하기 전에 아래 항목을 직접 확인하세요.
| 확인 항목 | 완료 |
| 회원가입 View가 추가되었는가? | □ |
| ID, PW, PW 확인, 이름, 이메일, 전화번호 입력칸이 있는가? | □ |
| 빈 값이면 회원가입이 되지 않는가? | □ |
| PW와 PW 확인이 다르면 회원가입이 되지 않는가? | □ |
| 중복 ID이면 회원가입이 되지 않는가? | □ |
| 비밀번호가 password_hash로 저장되는가? | □ |
| member.json에 새 회원이 members 목록으로 추가되는가? | □ |
| 가입한 회원으로 로그인할 수 있는가? | □ |
| 로그인 후 메인화면과 프로필 화면에 해당 회원 정보가 보이는가? | □ |
| 프로필 수정 시 현재 로그인한 회원 정보만 수정되는가? | □ |
| View마다 회원 정보를 따로 저장하지 않고 하나의 MemberModel을 공유하는가? | □ |
6.4 선택 구현과 심화 구현
기본 구현을 완료했다면 아래 기능을 추가로 구현해 볼 수 있습니다.
| 구분 | 내용 |
| 선택 구현 | 회원가입 성공 후 입력칸 초기화, 로그인 탭 자동 이동, 오류 메시지 자세히 표시 |
| 심화 구현 | 로그아웃 기능, 회원 탈퇴 기능, 비밀번호 변경 기능, salt를 추가한 비밀번호 해시 저장 |
기억할 문장: 회원가입 기능은 새 회원을 추가하는 기능이고, 비밀번호는 그대로 저장하지 않고 password_hash로 저장해야 합니다.
참고. 공식 문서로 확인하기
→ QTabWidget, JSON 저장, hashlib 사용법은 공식 문서에서 확인할 수 있습니다.
참고 문서
이번 과제에서 사용하는 주요 개념은 아래 공식 문서에서 더 자세히 확인할 수 있습니다.
참고: 이번 과제에서는 학습용으로 SHA-256 해시를 사용할 수 있습니다. 실제 서비스에서는 salt와 bcrypt, scrypt, argon2 같은 비밀번호 저장 전용 방식을 검토해야 합니다.