2025. 1. 14. 02:19ㆍCalender Website
필수 입력, 선택 입력, 프로필 설정의 3단계로 회원가입 시스템을 구현해 보았다.
1. 필수 입력
우선 필수 입력 파트에서는 이메일. 비밀번호. 비밀번호 확인. 휴대폰 번호를 입력받고 유효성 검사를 거친 후 다음 필드인 선택 입력 파트로 넘어간다.
1-1. Email 중복확인
이메일을 입력 받은 후 이메일이 데이터베이스에 저장된 이메일과 중복되는지 확인한다.
<HTML>
<div class="form-group">
<label for="user-email">이메일</label>
<div class="email-form">
<input type="email" id="user-email" name="user-email" placeholder="Email" required>
<button type="button" id="confirm-email">중복 확인</button>
</div>
<p id="email-result"></p>
</div>
<Javascript>
우선 이메일 형식이 올바른지 검증한 후, 올바르지 않으면 경고 메세지를 emailResult에 띄우고 함수는 종료한다.
이메일 형식이 올바르면, AXIOS 통신을 통해 Flask로 전송한다. Flask에서 온 응답인 available에 따라 적절한 메세지를 emailResult에 띄운다.
document.getElementById('confirm-email').addEventListener('click', function(e) {
e.preventDefault();
const userEmail = document.getElementById('user-email').value;
const emailResult = document.getElementById('email-result');
// 이메일 형식 검증을 위한 정규표현식
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
if (!emailRegex.test(userEmail)) {
emailResult.textContent = '올바른 이메일 형식이 아닙니다';
emailResult.style.color = 'red';
return;
}
axios.post('/confirm_email', {
userEmail: userEmail
})
.then(function(response) {
if (response.data.available) {
emailResult.textContent = "사용 가능한 이메일입니다.";
emailResult.style.color = 'green';
}
else {
emailResult.textContent = "이미 등록된 이메일입니다.";
emailResult.style.color = 'red';
}
})
.catch(function(error) {
console.error('에러 발생: ', error);
});
});
<Flask>
Flask에서는 전달 받은 이메일을 User 테이블과 비교한 후, 일치하는 것이 없으면 available을 False로, 만약 일치하는 이메일이 존재한다면 True로 설정하여 Javascript로 응답을 보낸다.
@api.route('/confirm_email', methods=['POST'])
def confirm_email():
data = request.json
user_email = data.get('userEmail')
user = User.query.filter_by(email=user_email).first()
if user:
return jsonify({
'available' : False
})
else:
return jsonify({
'available' : True
})
<결과 화면>
1-2. 비밀번호 확인
비밀번호와 비밀번호 확인이 동일한지 확인한다.
<HTML>
<div class="form-group">
<label for="user-pwd">비밀번호</label>
<input type="password" name="user-pwd" placeholder="Password" required>
</div>
<div class="form-group">
<label for="user-repwd">비밀번호 확인</label>
<input type="password" name="user-repwd" placeholder="Comfirm password" required>
<p id="pwd-result"></p>
</div>
<Javascipt>
입력 필드에 입력이 들어올 때마다 checkPasswordMatch를 실행하여 비밀번호 일치 여부를 확인한다.
- 비밀번호 o, 비밀번호 확인 x => 메세지를 띄우지 않음
- 비밀번호 o, 비밀번호 확인 o, 두 입력값 일치o => 비밀번호 일치 메세지를 띄움
- 비밀번호 o, 비밀번호 확인 o, 두 입력값 불일치 => 비밀번호 불일치 메세지를 띄움
document.addEventListener('DOMContentLoaded', function() {
const userPwd = document.querySelector('input[name="user-pwd"]');
const userRepwd = document.querySelector('input[name="user-repwd"]');
const pwdResult = document.getElementById('pwd-result');
function checkPasswordMatch() {
if (userRepwd.value === '') {
pwdResult.textContent = '';
}
else if (userPwd.value === userRepwd.value) {
pwdResult.textContent = "비밀번호가 일치합니다.";
pwdResult.style.color = "green";
}
else {
pwdResult.textContent = "비밀번호가 일치하지 않습니다.";
pwdResult.style.color = "red";
}
}
userPwd.addEventListener('input', checkPasswordMatch);
userRepwd.addEventListener('input', checkPasswordMatch);
});
<결과 화면>
1-3. 휴대폰 번호 유효성 검사
휴대폰 번호 형식이 올바른지 검사한다.
<HTML>
<div class="form-group">
<label for="user-phone">휴대폰 번호</label>
<input type="tel" name="user-phone" placeholder="Phone number" required>
<p id="phone-result"></p>
</div>
<Javascript>
비밀번호 유효성 검사와 동일하게 입력이 들어올 때마다 checkPhoneValidate를 실행한다. 미리 검증용 정규표현식을 만든다음 통과하지 않으면 경고 메세지를 띄운다.
document.addEventListener('DOMContentLoaded', function() {
const phone = document.querySelector('input[name="user-phone"]');
const phoneResult = document.getElementById('phone-result');
function checkPhoneValidate() {
const phoneRegex = /^(01[016789]{1})[0-9]{3,4}[0-9]{4}$/;
if (phone.value === '') {
phoneResult.textContent = '';
}
else if (!phoneRegex.test(phone.value)) {
phoneResult.textContent = "휴대폰 번호를 다시 확인해주세요.";
phoneResult.style.color = "red";
}
else {
phoneResult.textContent = '사용 가능한 휴대폰 번호입니다.';
phoneResult.style.color = "green";
}
}
phone.addEventListener('input', checkPhoneValidate);
})
<결과 화면>
1-4. 모든 입력이 완료되었는지 확인
필수 입력에 모두 입력되었는지 확인하고 다음 파트인 선택 입력으로 넘어간다.
<HTML>
button에 validateAndShowstep 함수를 달아 다음으로 넘어가기 전에 함수를 실행시켜 입력 필드를 확인한다.
<div class="button-div">
<button type="button" id="comfirm-allfilled" class="step-button" onclick="validateAndShowstep(2)">다음</button>
</div>
<p id="warning-message" style="color: red; display: none">모든 필드를 입력해주세요.</p>
<Javascript>
required인 input들을 모두 불러와 forEach로 하나하나 확인한다. 만약 하나라도 입력이 되어있지 않다면 allFilled가 false로 변경되어 경고 메세지가 나타날 것이고, 모두 입력되었다면 showStep을 통해 선택 입력창으로 넘어간다.
function validateAndShowstep(step) {
const inputs = document.querySelectorAll('#step1 input[required]');
let allFilled = true;
inputs.forEach(input => {
if (input.value.trim() === '') {
allFilled = false;
}
});
if (allFilled) {
document.getElementById('warning-message').style.display = 'none';
showStep(step);
}
else {
document.getElementById('warning-message').style.display = 'block';
}
}
<결과 화면>
2. 선택 입력
2번째 단계인 선택 입력에서는 유저의 생년월일, 관심. 직장. 학교, 위치, 웹사이트를 입력받는다. 굳이 입력안해도 되며 따로 유효성 검사를 거칠 필요 없다.
<HTML>
<div class="step" id="step2">
<div class="form-group">
<label for="user-birth">생년월일</label>
<input type="date" max="9999-12-31" name="user-birth" class="user-birth" placeholder="Birthday">
</div>
<div class="form-group">
<label for="user-interest">관심</label>
<input type="text" name="user-interest" placeholder="Interest">
</div>
<div class="form-group">
<label for="user-work">직장, 학교</label>
<input type="text" name="user-work" placeholder="Work">
</div>
<div class="form-group">
<label for="user-location">위치</label>
<input type="text" name="user-location" placeholder="Location">
</div>
<div class="form-group">
<label for="user-website">웹사이트</label>
<input type="text" name="user-website" placeholder="Website">
</div>
<div class="button-div">
<button type="button" class="step-button" onclick="showStep(1)">이전</button>
<button type="button" class="step-button" onclick="showStep(3)">다음</button>
</div>
</div>
<결과 화면>
3. 프로필 설정
프로필 설정에서는 유저의 프로필 이미지, 이름, 설명을 입력받는다. 프로필 이미지를 데이터 베이스에 올리는 방법은 이전 포스팅에서 설명하였다. 이제 submit 버튼을 누르면 폼이 Flask에 제출되어 회원가입이 완료된다.
<HTML>
<div class="step" id="step3">
<div class="form-group">
<div class="image-div">
<img src="{{ url_for('static', filename='images/woman.png') }}" class="profile-image" id="profile-image">
</div>
<input type="file" id="image-input" name="user-image" style="display: none;" accept="image/*">
</div>
<div class="form-group">
<label for="user-name">이름</label>
<input type="text" name="user-name" placeholder="Name" required>
</div>
<div class="form-group">
<label for="user-description">설명</label>
<input type="text" name="user-description" placeholder="Description">
</div>
<div class="button-div">
<button type="button" class="step-button" onclick="showStep(2)">이전</button>
<button type="submit" class="submit-button">가입하기</button>
</div>
</div>
<Javascript>
FormData를 Flask에 AXIOS 통신을 통해 제출한다. 정상적으로 회원가입이 완료되면 환영메세지를 띄운다.
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('.signup-form').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
axios.post('/register', formData)
.then(function(response) {
document.querySelector('.form-container').style.display = 'none';
document.querySelector('.welcome-message').style.display = 'block';
document.getElementById('welcome-name').textContent = response.data.name;
})
.catch(function(error) {
console.error('Error: ', error);
});
});
});
<Flask>
입력폼에 있는 모든 데이터들을 User 객체의 인자로 전달해준다. 비밀번호는 보안을 위해 generate_password_hash로 해쉬값을 사용해준다.
db.sesson.add를 통해 테이블에 추가해주고 커밋하면 회원가입이 완료된다. user name을 응답으로 전달하여 환영 메세지에 유저 이름이 띄워지도록 해준다.
@api.route('/register', methods=['POST'])
def register():
user_email = request.form['user-email']
user_pwd = request.form['user-pwd']
hashed_password = generate_password_hash(user_pwd) # Hash 생성
user_phone = request.form['user-phone']
user_birth = request.form['user-birth']
if (user_birth == ''):
user_birth = None
user_interest = request.form['user-interest']
user_work = request.form['user-work']
user_location = request.form['user-location']
user_website = request.form['user-website']
user_image = request.files['user-image']
if user_image.filename == '':
db_file_path = ''
else:
file_name = secure_filename(user_image.filename)
file_path = os.path.join('app/static/images/profile', file_name)
user_image.save(file_path)
# db에 저장할 경로는 static 이후부터
db_file_path = f'images/profile/{file_name}'
user_name = request.form['user-name']
user_description = request.form['user-description']
new_user = User(username=user_name,
email=user_email,
password=hashed_password,
phone=user_phone,
birth=user_birth,
interest=user_interest,
work=user_work,
location=user_location,
website=user_website,
description=user_description,
image_path=db_file_path)
db.session.add(new_user)
db.session.commit()
return jsonify ({
'name': user_name
})
<결과 화면>
4. 가입 환영 메세지
가입 환영 메세지에는 유저의 이름을 함께 보여주고, 로그인 페이지로 안내한다.
<HTML>
<div class="welcome-message" style="display: none;">
<img src="{{ url_for('static', filename='images/logo_v2.png') }}" class="logo-picture">
<h1>안녕하세요, <span id="welcome-name"></span>님!</h1>
<p>회원가입이 성공적으로 완료되었습니다.</p>
<button id="goto-login">로그인 페이지로 이동</button>
</div>
<Javascript>
document.getElementById('goto-login').addEventListener('click', function() {
window.location.href = '/';
});
<결과 화면>
'Calender Website' 카테고리의 다른 글
[Flask] 회원 정보 수정, 탈퇴, 로그아웃 (0) | 2025.01.23 |
---|---|
[Flask] Flask-login을 사용한 로그인 시스템 구현 (0) | 2025.01.16 |
[Flask] Blueprint로 웹 애플리케이션 구조화 (0) | 2025.01.14 |
[Flask] 프로필 이미지 미리보기 및 데이터 베이스에 저장하기 (0) | 2025.01.14 |
투두리스트, 캘린더, 다이어리 웹사이트 개발 - Flask 개발 환경 준비 (0) | 2024.12.30 |