저번 게시물에서 설정하였던 user엔티티를 이용하여 회원가입 기능을 구현하겠습니다.
먼저 회원가입 페이지에 대한 화면 설계입니다.

위 화면 설계와 같은 방식을 이용하여 회원가입 페이지를 구성해 보도록 하겠습니다.
먼저 html content 부분입니다.
<h1>계정 만들기</h1>
<form action="/signup" method="POST" enctype="multipart/form-data">
<div class="form-group">
<label for="uid">아이디</label>
<input type="text" id="uid" name="uid" required>
<button type="button" class="check-btn" onclick="checkUsername()">중복 확인</button>
</div>
<div class="form-group">
<label for="password">비밀번호</label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-group">
<label for="name">이름</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">이메일</label>
<input type="email" id="email" name="email" required>
<button type="button" class="check-btn" onclick="verifyEmail()">본인 인증</button>
</div>
<div class="form-group">
<label for="birthdate">생년월일</label>
<input type="date" id="birthdate" name="birthdate" required>
</div>
<div class="form-group">
<label for="introduce">자기소개</label>
<input type="text" id="introduce" name="introduce" placeholder="한줄로 자기소개를 해주세요" required>
</div>
<div class="form-group" style="width: 635px">
<label style="width: 300px; float: left;">성별(성별을 선택해주세요)</label>
<div class="gender-buttons form-group">
<input type="radio" id="male" name="gender" value="남성" required>
<label for="male">남성</label>
<input type="radio" id="female" name="gender" value="여성" required>
<label for="female">여성</label>
</div>
</div>
<div style="width: 635px; margin-bottom: 30px">
<label>프로필 사진 추가 (최소 3장, 최대 6장)</label>
</div>
<div id="photo-section" class="profile-photo-section" style="margin-bottom:70px">
<div class="photo-box" onclick="document.getElementById('photoInput1').click()">
<img id="preview1" src="#" alt="프로필 사진" style="display:none;">
<div class="add-photo-button">+</div>
<input type="file" id="photoInput1" class="hidden-input" accept="image/*" onchange="previewImage(event, 1)">
</div>
</div>
<!-- 관심사 추가 및 재설정 버튼 섹션 -->
<div style="width: 635px; margin-bottom: 30px">
<label>관심사 추가</label>
</div>
<div class="interest-section">
<div id="interestButtonsContainer">
<!-- 관심사 선택 후 버튼들이 동적으로 추가됩니다. -->
<button type="button" id="addInterestBtn" onclick="openModal()">+ 관심사 추가</button>
</div>
<button type="button" id="resetInterestBtn" onclick="resetInterests()">관심사 재설정</button>
</div>
<div id="selectedInterests" class="selected-interests">
<!-- 선택된 관심사 버튼이 이곳에 추가됩니다. -->
</div>
<button type="submit" class="submit-btn">회원가입하기</button>
</form>
<footer>
<a href="#">회사소개</a> | <a href="#">이용약관</a> | <a href="#">개인정보처리방침</a>
</footer>
<!-- 관심사 모달 -->
<div id="interestModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeModal()">×</span>
<div class="modal-header">관심사 선택 (최대 8개)</div>
<div class="modal-body">
<div class="interest-list">
<button class="interest-button" data-interest="운동" onclick="toggleInterestSelection(this)">운동</button>
<button class="interest-button" data-interest="음악" onclick="toggleInterestSelection(this)">음악</button>
<button class="interest-button" data-interest="독서" onclick="toggleInterestSelection(this)">독서</button>
<button class="interest-button" data-interest="여행" onclick="toggleInterestSelection(this)">여행</button>
<button class="interest-button" data-interest="영화" onclick="toggleInterestSelection(this)">영화</button>
<button class="interest-button" data-interest="사진" onclick="toggleInterestSelection(this)">사진</button>
<button class="interest-button" data-interest="게임" onclick="toggleInterestSelection(this)">게임</button>
<button class="interest-button" data-interest="등산" onclick="toggleInterestSelection(this)">등산</button>
<button class="interest-button" data-interest="명상" onclick="toggleInterestSelection(this)">명상</button>
<button class="interest-button" data-interest="패션" onclick="toggleInterestSelection(this)">패션</button>
<button class="interest-button" data-interest="캠핑" onclick="toggleInterestSelection(this)">캠핑</button>
<button class="interest-button" data-interest="쇼핑" onclick="toggleInterestSelection(this)">쇼핑</button>
<button class="interest-button" data-interest="디자인" onclick="toggleInterestSelection(this)">디자인</button>
<button class="interest-button" data-interest="DIY" onclick="toggleInterestSelection(this)">DIY</button>
<button class="interest-button" data-interest="과학" onclick="toggleInterestSelection(this)">과학</button>
<button class="interest-button" data-interest="기술" onclick="toggleInterestSelection(this)">기술</button>
<button class="interest-button" data-interest="요가" onclick="toggleInterestSelection(this)">요가</button>
<button class="interest-button" data-interest="춤" onclick="toggleInterestSelection(this)">춤</button>
<button class="interest-button" data-interest="자동차" onclick="toggleInterestSelection(this)">자동차</button>
<button class="interest-button" data-interest="재테크" onclick="toggleInterestSelection(this)">재테크</button>
</div>
<input type="text" id="customInterest" placeholder="직접 입력" class="custom-input" />
<button class="interest-button" onclick="selectCustomInterest()">직접 입력</button>
</div>
<div class="modal-footer">
<button class="modal-save-button" onclick="saveSelectedInterests()">선택 완료</button>
</div>
</div>
</div>
각 폼에서 값을 받을 수 있게 구성을 하였고 관심사는 모달창을 통해서 받을 수 있도록 구성하였습니다.
css파일입니다.
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 0;
}
h1 {
margin-bottom: 100px;
text-align: center;
}
form {
display: flex;
flex-direction: column;
align-items: center;
}
.form-group {
display: flex;
align-items: center;
margin-bottom: 60px;
}
label {
width: 100px;
font-size: 20px;
font-weight: bold;
}
input[type="text"],input[type="email"] ,input[type="date"], input[type="password"] {
padding: 10px;
width: 500px;
border: 1px solid black;
border-radius: 15px;
}
#uid{
width: 355px;
}
input[type="email"]{
width: 355px
}
.gender-buttons, .interest-section {
display: flex;
gap: 20px;
margin: 15px 0;
justify-content: center;
}
.gender-buttons label, .interest-section button {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 45px;
border: 2px solid #000000;
border-radius: 15px;
cursor: pointer;
text-align: center;
font-size: 20px;
background-color: white;
}
.gender-buttons input[type="radio"] {
display: none; /* 라디오 버튼 숨기기 */
}
.gender-buttons input[type="radio"]:checked + label {
background-color: #ff6b6b;
color: white;
border-color: #ff6b6b;
}
.profile-photo-section {
display: flex;
justify-content: center;
gap: 20px; /* 사진 상자 사이 간격 추가 */
flex-wrap: wrap; /* 사진이 많아질 경우 다음 줄로 넘어가게 설정 */
}
.photo-box {
width: 150px; /* 사진 상자 너비 확대 */
height: 200px; /* 사진 상자 높이 확대 */
border: 2px dashed #ccc;
display: flex;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
margin-bottom: 20px; /* 사진 상자 아래 간격 추가 */
}
.photo-box img {
width: 100%;
height: 100%;
object-fit: cover;
}
.add-photo-button {
position: absolute;
bottom: 10px;
right: 10px;
background-color: #ff6b6b;
border: none;
border-radius: 50%;
width: 30px; /* 추가 버튼 크기 확대 */
height: 30px; /* 추가 버튼 크기 확대 */
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 18px;
line-height: 0;
}
.submit-btn {
margin-top: 150px;
padding: 15px 30px;
border: 1px solid black;
background-color: white;
border-radius: 15px;
cursor: pointer;
font-size: 20px;
text-align: center;
width: 300px;
height: auto;
color: black;
}
footer {
margin-top: 80px;
font-size: 12px;
text-align: center;
}
footer a {
text-decoration: none;
color: #333;
margin: 0 10px;
}
.hidden-input {
display: none;
}
/* 선택된 관심사 섹션 스타일 */
.selected-interests {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 20px;
}
.selected-interest-button {
padding: 10px 15px;
background-color: #e0f7fa;
border: 1px solid #00acc1;
border-radius: 5px;
cursor: pointer;
}
.selected-interest-button:hover {
background-color: #b2ebf2;
}
/* 모달 스타일 */
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(5px);
}
.modal-content {
background-color: #fff;
margin: 10% auto;
padding: 20px;
border-radius: 15px;
width: 80%;
max-width: 500px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.modal-header {
font-size: 24px;
font-weight: bold;
margin-bottom: 15px;
text-align: center;
color: #333;
}
.modal-body {
max-height: 300px;
overflow-y: auto;
}
.modal-footer {
text-align: center;
margin-top: 10px;
}
.modal-save-button {
padding: 10px 20px;
background-color: #2aaff8;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.interest-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}
.interest-button {
padding: 15px 30px; /* 버튼 크기를 키움 */
background-color: #f7f7f7;
border: 1px solid #ddd;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 16px; /* 글씨 크기를 키움 */
font-weight: bold;
}
.interest-button.selected {
background-color: #ffffff;
border-color: #2aaff8;
}
.interest-button:hover {
background-color: #e0e0e0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.close {
color: #888;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
/* 입력창 스타일 수정 */
.custom-input {
padding: 5px;
width: 150px; /* 더 작은 크기 */
border: 1px solid #ccc;
border-radius: 10px;
font-size: 12px; /* 더 작은 글씨 */
}
.custom-input:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
}
.check-btn {
margin-left: 10px;
padding: 8px;
background-color: #fbfbfb;
color: #000000;
border: 1px solid black;
border-radius: 15px;
cursor: pointer;
width: 135px;
/* height: 37px; */
}
.check-btn:disabled {
background-color: #ccc;
}
스크립트를 통해서 유효성 검사도 추가를 하였습니다. 유효성검사는 테스트를 용이하게 하기위해 나중에 완성 후 테스트 단계 때 sumit에 연결하도록 하겠습니다.
function validateForm() {
// 아이디: 5자 이상, 20자 이하, 영문자와 숫자만 허용
const uid = document.getElementById('uid').value;
const uidPattern = /^[a-zA-Z0-9]{5,20}$/;
if (!uidPattern.test(uid)) {
alert('아이디는 5~20자의 영문자와 숫자만 허용됩니다.');
return false;
}
// 비밀번호: 8자 이상, 영문자, 숫자, 특수문자 조합
const password = document.getElementById('password').value;
const passwordPattern = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$/;
if (!passwordPattern.test(password)) {
alert('비밀번호는 최소 8자, 영문자, 숫자, 특수문자를 포함해야 합니다.');
return false;
}
// 이메일 포맷 검사
const email = document.getElementById('email').value;
const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailPattern.test(email)) {
alert('올바른 이메일 형식을 입력해주세요.');
return false;
}
// 생년월일 검사
const birthdate = document.getElementById('birthdate').value;
const birthYear = new Date(birthdate).getFullYear();
const currentYear = new Date().getFullYear();
if (currentYear - birthYear < 18) {
alert('만 18세 이상만 가입할 수 있습니다.');
return false;
}
return true;
}
id를 통해 html로 부터 값을 가져오고 패턴매칭과 조건문을 통해 검사 후 조건에 부합하지 않으면 폼이 제출되는 걸 방지한다.
'project > 모임웹프로젝트' 카테고리의 다른 글
| 모임 웹 프로젝트 회원가입 백엔드 구성 (6) (0) | 2024.09.29 |
|---|---|
| 모임 웹 프로젝트 회원가입 코드수정 (5) (0) | 2024.09.29 |
| 모임 웹 프로젝트 프런트 구성2 (4) (1) | 2024.09.28 |
| 모임웹프로젝트 스프링시큐리티 설정 및 회원엔티티 구성 (2) (1) | 2024.09.28 |
| 모임웹프로젝트 기획(1) (2) | 2024.09.28 |