각진 세상에 둥근 춤을 추자
[JSP] 프로젝트 실습 - 게시판 만들기 4 (회원가입 화면 설계) 본문
2022.10.21 - [JSP] - [JSP] 프로젝트 실습 - 게시판 만들기 3 (약관 화면 설계)
앞서 구현한 약관 화면에서 다음 버튼을 클릭하면 회원가입 화면이 나오게 코드를 작성했다.
이번에는 회원가입 화면을 구현해 본다.
1. 회원가입 폼 구현하기
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="./_header.jsp" %>
<section id="user" class="register">
<form action="/Jboard1/user/proc/registerProc.jsp" method="POST">
<table border="0">
<caption>사이트 이용정보 입력</caption>
<tr>
<td>아이디</td>
<td>
<input type="text" name="uid" placeholder="아이디 입력"/>
<button type="button" id="btnIdCheck"><img src="/Jboard1/img/chk_id.gif" alt="중복확인"></button>
<span class="uidResult"></span>
</td>
</tr>
<tr>
<td>비밀번호</td>
<td>
<input type="password" name="pass1" placeholder="비밀번호 입력"/>
</td>
</tr>
<tr>
<td>비밀번호 확인</td>
<td>
<input type="password" name="pass2" placeholder="비밀번호 확인 입력"/>
<span class="passResult"></span>
</td>
</tr>
</table>
<table border="0">
<caption>개인정보 입력</caption>
<tr>
<td>이름</td>
<td>
<input type="text" name="name" placeholder="이름 입력"/>
<span class="nameResult"></span>
</td>
</tr>
<tr>
<td>별명</td>
<td>
<p>공백없이 한글, 영문만 입력가능</p>
<input type="text" name="nick" placeholder="별명 입력"/>
<button type="button" id="btnNickCheck"><img src="/Jboard1/img/chk_id.gif" alt="중복확인"></button>
<span class="nickResult"></span>
</td>
</tr>
<tr>
<td>E-Mail</td>
<td>
<input type="email" name="email" placeholder="이메일 입력"/>
<span class="emailResult"></span>
</td>
</tr>
<tr>
<td>휴대폰</td>
<td>
<input type="text" name="hp" placeholder="- 포함 13자리 입력" minlength="13" maxlength="13" />
<span class="hpResult"></span>
</td>
</tr>
<tr>
<td>주소</td>
<td>
<div>
<input type="text" name="zip" id="zip" placeholder="우편번호" readonly="readonly"/>
<button type="button" onclick="zipcode()" class="btnZip"><img src="/Jboard1/img/chk_post.gif" alt=""></button>
</div>
<div>
<input type="text" name="addr1" id="addr1" placeholder="주소를 검색하세요." readonly/>
</div>
<div>
<input type="text" name="addr2" id="addr2" placeholder="상세주소를 입력하세요."/>
</div>
</td>
</tr>
</table>
<div>
<a href="/Jboard1/user/login.jsp" class="btn btnCancel">취소</a>
<input type="submit" class="btn btnRegister" value="회원가입"/>
</div>
</form>
</section>
<%@ include file="./_footer.jsp" %>
회원가입 폼 css 코드는 다음과 같다.
/* 회원가입 */
*{
margin: 0;
padding: 0;
font: normal 12px '돋움';
border-collapse: collapse;
}
#wrapper{
width: 800px;
height: auto;
margin: 0 auto;
}
header{
width: 100%;
height: 35px;
background-color: #f7f7f7;
border-bottom: 1px solid #ebebeb;
}
header > h3{
float: left;
font-weight: bold;
color: #777;
margin-top: 10px;
margin-left: 10px;
}
#user.register{
width: 600px;
height: auto;
margin: 60px auto;
overflow: hidden;
}
#user.register table{
width: 100%;
border-top: 2px solid #111;
border-spacing: 0;
margin-bottom: 10px;
}
#user.register table input{
width: 162px;
height: 22px;
border: 1px solid #e4eaec;
background: #f7f7f7;
text-indent: 6px;
}
#user.register table caption{
padding: 10px 0;
font-weight: bold;
text-align: left;
}
#user.register table td:nth-child(1){
width: 80px;
background: #f5f8f9;
}
#user.register table div:nth-child(2) > input {
width: 320px;
}
#user.register table div:nth-child(3) > input {
width: 320px;
}
#user.register table td {
padding: 6px 12px;
border: 1px solid #e9e9e9;
}
#user.register table input{
margin: 2px 0;
}
#user.register table button {
border: none;
vertical-align: middle;
margin-left: 3px;
}
#user.register > form >div {
float: right;
margin-top: 10px;
}
footer{
width: 100%;
height: 35px;
background-color: #f7f7f7;
border-top: 1px solid #ebebeb;
}
footer > p {
float: right;
margin-top: 10px;
margin-right: 10px;
color: #777;
}
2. 유효성 검사
폼의 입력란에 올바른 데이터를 입력했는지 검사하는 유효성 검사 코드를 작성한다.
구글에 검색하면 데이터 검증에 사용하는 다양한 정규 표현식을 알 수 있다.
유효성 검사 코드는 register.jsp가 아닌 다른 파일에 별도로 보관한다.
- Jboard1 - src - main - webapp - js 폴더 생성 - validation.js 파일 생성
validation.js 내부에 코드를 작성한다.
- 먼저, 데이터 검증에 사용하는 정규 표현식을 정의한다.
// 데이터 검증에 사용하는 정규표현식
let reUid = /^[a-z]+[a-z0-9]{5,19}$/g;
let rePass = /^(?=.*[a-zA-z])(?=.*[0-9])(?=.*[$`~!@$!%*#^?&\\(\\)\-_=+]).{5,16}$/;
let reName = /^[가-힣]+$/
let reNick = /^[a-zA-Zㄱ-힣][a-zA-Zㄱ-힣]*$/;
let reEmail = /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i;
let reHp = /^01(?:0|1|[6-9])-(?:\d{4})-\d{4}$/;
- 폼에 입력한 값의 검증 결과 상태 변수를 우선 false로 정의한다.
// 폼 데이터 검증 결과 상태변수
let isUidOk = false;
let isPassOk = false;
let isNameOk = false;
let isNickOk = false;
let isEmailOk = false;
let isHpOk = false;
(1) 아이디 검사하기
// 아이디 검사하기
$('input[name=uid]').keydown(function(){
isUidOk = false;
});
$('#btnIdCheck').click(function(){
let uid = $('input[name=uid]').val();
if(isUidOk){
return;
}
if(!uid.match(reUid)){
$('.uidResult').css('color', 'red').text('유효한 아이디가 아닙니다.');
isUidOk = false;
return;
}
let jsonData = {
"uid":uid
};
$('.uidResult').css('color','black').text('...');
setTimeout(function(){
$.ajax({
url: './proc/checkUid.jsp',
method: 'get',
data: jsonData,
dataType: 'json',
success: function(data){
if(data.result == 0){
$('.uidResult').css('color','green').text('사용 가능한 아이디 입니다.');
isUidOk = true;
}else{
$('.uidResult').css('color','red').text('이미 사용 중인 아이디 입니다.');
isUidOk = false;
}
}
});
},500);
});
(2) 비밀번호 검사하기
// 비밀번호 검사하기
$('input[name=pass2]').focusout(function(){
let pass1 = $('input[name=pass1]').val();
let pass2 = $('input[name=pass2]').val();
if(pass2.match(rePass)){
if(pass1 == pass2){
isPassOk = true;
$('.passResult').css('color', 'green').text('사용하실 수 있는 비밀번호 입니다.');
}else{
isPassOk = false;
$('.passResult').css('color', 'red').text('비밀번호가 일치하지 않습니다.');
}
}else{
isPassOk = false;
$('.passResult').css('color', 'red').text('숫자,영문,특수문자 포함 5자리 이상 이어야 합니다.');
}
});
(3) 이름 검사하기
// 이름 검사하기
$('input[name=name]').focusout(function(){
let name = $(this).val();
if(name.match(reName)){
isNameOk = true;
$('.nameResult').text('');
}else{
isNameOk = false;
$('.nameResult').css('color', 'red').text('유효한 이름이 아닙니다.');
}
});
(4) 별명 검사하기
// 별명 검사하기
$('input[name=nick]').keydown(function(){
isNickOk = false;
});
$('#btnNickCheck').click(function(){
let nick = $('input[name=nick]').val();
if(isNickOk){
return;
}
if(!nick.match(reNick)){
$('.nickResult').css('color', 'red').text('유효한 별명이 아닙니다.');
isNickOk = false;
return;
}
let jsonData = {
"nick":nick
};
$('.nickResult').css('color','black').text('...');
setTimeout(function(){
$.ajax({
url: './proc/checkNick.jsp',
method: 'get',
data: jsonData,
dataType: 'json',
success: function(data){
if(data.result == 0){
$('.nickResult').css('color','green').text('사용 가능한 별명 입니다.');
isNickOk = true;
}else{
$('.nickResult').css('color','red').text('이미 사용 중인 별명 입니다.');
isNickOk = false;
}
}
});
},500);
});
(5) 이메일 검사하기
// 이메일 검사하기
$('input[name=email]').focusout(function(){
let email = $(this).val();
if(email.match(reEmail)){
isEmailOk = true;
$('.emailResult').text('');
}else{
isEmailOk = false;
$('.emailResult').css('color', 'red').text('유효하지 않는 이메일 입니다.');
}
});
(6) 휴대폰 번호 검사하기
// 휴대폰 검사하기
$('input[name=hp]').focusout(function(){
let hp = $(this).val();
if(hp.match(reHp)){
isHpOk = true;
$('.hpResult').text('');
}else{
isHpOk = false;
$('.hpResult').css('color', 'red').text('유효하지 않는 휴대폰 입니다.');
}
});
(7) 우편번호 검사하기
우편번호 검사 코드도 register.jsp가 아닌 다른 파일에 별도로 보관한다.
- Jboard1 - src - main - webapp - js 폴더 생성 - zipcode.js 파일 생성
https://postcode.map.daum.net/guide
코드는 카카오에서 제공하는 다음 우편번호 서비스를 이용한다.
오픈 소스를 용도에 맞게 수정한다.
/**
* 다음 주소 API
*/
function zipcode() {
new daum.Postcode({
oncomplete: function(data) {
// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
// 각 주소의 노출 규칙에 따라 주소를 조합한다.
// 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
var addr = ''; // 주소 변수
var extraAddr = ''; // 참고항목 변수
//사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
if (data.userSelectedType === 'R') { // 사용자가 도로명 주소를 선택했을 경우
addr = data.roadAddress;
} else { // 사용자가 지번 주소를 선택했을 경우(J)
addr = data.jibunAddress;
}
// 사용자가 선택한 주소가 도로명 타입일때 참고항목을 조합한다.
if(data.userSelectedType === 'R'){
// 법정동명이 있을 경우 추가한다. (법정리는 제외)
// 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
if(data.bname !== '' && /[동|로|가]$/g.test(data.bname)){
extraAddr += data.bname;
}
// 건물명이 있고, 공동주택일 경우 추가한다.
if(data.buildingName !== '' && data.apartment === 'Y'){
extraAddr += (extraAddr !== '' ? ', ' + data.buildingName : data.buildingName);
}
// 표시할 참고항목이 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
if(extraAddr !== ''){
extraAddr = ' (' + extraAddr + ')';
}
// 조합된 참고항목을 해당 필드에 넣는다.
//document.getElementById("sample6_extraAddress").value = extraAddr;
} else {
//document.getElementById("sample6_extraAddress").value = '';
}
// 우편번호와 주소 정보를 해당 필드에 넣는다.
document.getElementById('zip').value = data.zonecode;
document.getElementById("addr1").value = addr;
// 커서를 상세주소 필드로 이동한다.
document.getElementById("addr2").focus();
}
}).open();
}
(8) 회원가입 파일에 링크 연결하기
앞서 작성했던 유효성 검사 코드 파일(validation.js)과 우편번호 검사 코드 파일(zipcode.js)을 register.jsp에서 연결해 준다.
<script src="/Jboard1/js/validation.js"></script>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script src="/Jboard1/js/zipcode.js"></script>
총 정리
(1) register.jsp
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="./_header.jsp" %>
<script src="/Jboard1/js/validation.js"></script>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script src="/Jboard1/js/zipcode.js"></script>
<section id="user" class="register">
<form action="/Jboard1/user/proc/registerProc.jsp" method="POST">
<table border="0">
<caption>사이트 이용정보 입력</caption>
<tr>
<td>아이디</td>
<td>
<input type="text" name="uid" placeholder="아이디 입력"/>
<button type="button" id="btnIdCheck"><img src="/Jboard1/img/chk_id.gif" alt="중복확인"></button>
<span class="uidResult"></span>
</td>
</tr>
<tr>
<td>비밀번호</td>
<td>
<input type="password" name="pass1" placeholder="비밀번호 입력"/>
</td>
</tr>
<tr>
<td>비밀번호 확인</td>
<td>
<input type="password" name="pass2" placeholder="비밀번호 확인 입력"/>
<span class="passResult"></span>
</td>
</tr>
</table>
<table border="0">
<caption>개인정보 입력</caption>
<tr>
<td>이름</td>
<td>
<input type="text" name="name" placeholder="이름 입력"/>
<span class="nameResult"></span>
</td>
</tr>
<tr>
<td>별명</td>
<td>
<p>공백없이 한글, 영문만 입력가능</p>
<input type="text" name="nick" placeholder="별명 입력"/>
<button type="button" id="btnNickCheck"><img src="/Jboard1/img/chk_id.gif" alt="중복확인"></button>
<span class="nickResult"></span>
</td>
</tr>
<tr>
<td>E-Mail</td>
<td>
<input type="email" name="email" placeholder="이메일 입력"/>
<span class="emailResult"></span>
</td>
</tr>
<tr>
<td>휴대폰</td>
<td>
<input type="text" name="hp" placeholder="- 포함 13자리 입력" minlength="13" maxlength="13" />
<span class="hpResult"></span>
</td>
</tr>
<tr>
<td>주소</td>
<td>
<div>
<input type="text" name="zip" id="zip" placeholder="우편번호" readonly="readonly"/>
<button type="button" onclick="zipcode()" class="btnZip"><img src="/Jboard1/img/chk_post.gif" alt=""></button>
</div>
<div>
<input type="text" name="addr1" id="addr1" placeholder="주소를 검색하세요." readonly/>
</div>
<div>
<input type="text" name="addr2" id="addr2" placeholder="상세주소를 입력하세요."/>
</div>
</td>
</tr>
</table>
<div>
<a href="/Jboard1/user/login.jsp" class="btn btnCancel">취소</a>
<input type="submit" class="btn btnRegister" value="회원가입"/>
</div>
</form>
</section>
<%@ include file="./_footer.jsp" %>
(2) CSS
/* 회원가입 */
*{
margin: 0;
padding: 0;
font: normal 12px '돋움';
border-collapse: collapse;
}
#wrapper{
width: 800px;
height: auto;
margin: 0 auto;
}
header{
width: 100%;
height: 35px;
background-color: #f7f7f7;
border-bottom: 1px solid #ebebeb;
}
header > h3{
float: left;
font-weight: bold;
color: #777;
margin-top: 10px;
margin-left: 10px;
}
#user.register{
width: 600px;
height: auto;
margin: 60px auto;
overflow: hidden;
}
#user.register table{
width: 100%;
border-top: 2px solid #111;
border-spacing: 0;
margin-bottom: 10px;
}
#user.register table input{
width: 162px;
height: 22px;
border: 1px solid #e4eaec;
background: #f7f7f7;
text-indent: 6px;
}
#user.register table caption{
padding: 10px 0;
font-weight: bold;
text-align: left;
}
#user.register table td:nth-child(1){
width: 80px;
background: #f5f8f9;
}
#user.register table div:nth-child(2) > input {
width: 320px;
}
#user.register table div:nth-child(3) > input {
width: 320px;
}
#user.register table td {
padding: 6px 12px;
border: 1px solid #e9e9e9;
}
#user.register table input{
margin: 2px 0;
}
#user.register table button {
border: none;
vertical-align: middle;
margin-left: 3px;
}
#user.register > form >div {
float: right;
margin-top: 10px;
}
footer{
width: 100%;
height: 35px;
background-color: #f7f7f7;
border-top: 1px solid #ebebeb;
}
footer > p {
float: right;
margin-top: 10px;
margin-right: 10px;
color: #777;
}
(3) validation.js (유효성 검사)
/**
* 날짜: 2022/10/21
이름: 이원정
내용: 사용자 회원가입 유효성 검사
*/
// 데이터 검증에 사용하는 정규표현식
let reUid = /^[a-z]+[a-z0-9]{5,19}$/g;
let rePass = /^(?=.*[a-zA-z])(?=.*[0-9])(?=.*[$`~!@$!%*#^?&\\(\\)\-_=+]).{5,16}$/;
let reName = /^[가-힣]+$/
let reNick = /^[a-zA-Zㄱ-힣][a-zA-Zㄱ-힣]*$/;
let reEmail = /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i;
let reHp = /^01(?:0|1|[6-9])-(?:\d{4})-\d{4}$/;
// 폼 데이터 검증 결과 상태변수
let isUidOk = false;
let isPassOk = false;
let isNameOk = false;
let isNickOk = false;
let isEmailOk = false;
let isHpOk = false;
$(function(){
// 아이디 검사하기
$('input[name=uid]').keydown(function(){
isUidOk = false;
});
$('#btnIdCheck').click(function(){
let uid = $('input[name=uid]').val();
if(isUidOk){
return;
}
if(!uid.match(reUid)){
$('.uidResult').css('color', 'red').text('유효한 아이디가 아닙니다.');
isUidOk = false;
return;
}
let jsonData = {
"uid":uid
};
$('.uidResult').css('color','black').text('...');
setTimeout(function(){
$.ajax({
url: './proc/checkUid.jsp',
method: 'get',
data: jsonData,
dataType: 'json',
success: function(data){
if(data.result == 0){
$('.uidResult').css('color','green').text('사용 가능한 아이디 입니다.');
isUidOk = true;
}else{
$('.uidResult').css('color','red').text('이미 사용 중인 아이디 입니다.');
isUidOk = false;
}
}
});
},500);
});
// 비밀번호 검사하기
$('input[name=pass2]').focusout(function(){
let pass1 = $('input[name=pass1]').val();
let pass2 = $('input[name=pass2]').val();
if(pass2.match(rePass)){
if(pass1 == pass2){
isPassOk = true;
$('.passResult').css('color', 'green').text('사용하실 수 있는 비밀번호 입니다.');
}else{
isPassOk = false;
$('.passResult').css('color', 'red').text('비밀번호가 일치하지 않습니다.');
}
}else{
isPassOk = false;
$('.passResult').css('color', 'red').text('숫자,영문,특수문자 포함 5자리 이상 이어야 합니다.');
}
});
// 이름 검사하기
$('input[name=name]').focusout(function(){
let name = $(this).val();
if(name.match(reName)){
isNameOk = true;
$('.nameResult').text('');
}else{
isNameOk = false;
$('.nameResult').css('color', 'red').text('유효한 이름이 아닙니다.');
}
});
// 별명 검사하기
$('input[name=nick]').keydown(function(){
isNickOk = false;
});
$('#btnNickCheck').click(function(){
let nick = $('input[name=nick]').val();
if(isNickOk){
return;
}
if(!nick.match(reNick)){
$('.nickResult').css('color', 'red').text('유효한 별명이 아닙니다.');
isNickOk = false;
return;
}
let jsonData = {
"nick":nick
};
$('.nickResult').css('color','black').text('...');
setTimeout(function(){
$.ajax({
url: './proc/checkNick.jsp',
method: 'get',
data: jsonData,
dataType: 'json',
success: function(data){
if(data.result == 0){
$('.nickResult').css('color','green').text('사용 가능한 별명 입니다.');
isNickOk = true;
}else{
$('.nickResult').css('color','red').text('이미 사용 중인 별명 입니다.');
isNickOk = false;
}
}
});
},500);
});
// 이메일 검사하기
$('input[name=email]').focusout(function(){
let email = $(this).val();
if(email.match(reEmail)){
isEmailOk = true;
$('.emailResult').text('');
}else{
isEmailOk = false;
$('.emailResult').css('color', 'red').text('유효하지 않는 이메일 입니다.');
}
});
// 휴대폰 검사하기
$('input[name=hp]').focusout(function(){
let hp = $(this).val();
if(hp.match(reHp)){
isHpOk = true;
$('.hpResult').text('');
}else{
isHpOk = false;
$('.hpResult').css('color', 'red').text('유효하지 않는 휴대폰 입니다.');
}
});
// 최종 폼 전송할 때
$('.register > form').submit(function(){
// 아이디 검증
if(!isUidOk){
alert('아이디를 확인해 주십시오.');
return false;
}
// 비밀번호 검증
if(!isPassOk){
alert('비밀번호가 유효하지 않습니다.');
return false;
}
// 이름 검증
if(!isNameOk){
alert('이름이 유효하지 않습니다.');
return false;
}
// 별명 검증
if(!isNickOk){
alert('별명이 유효하지 않습니다.');
return false;
}
// 이메일 검증
if(!isEmailOk){
alert('이메일이 유효하지 않습니다.');
return false;
}
// 휴대폰 검증
if(!isHpOk){
alert('휴대폰이 유효하지 않습니다.');
return false;
}
return true;
});
});
(4) 우편코드 검사
/**
* 다음 주소 API
*/
function zipcode() {
new daum.Postcode({
oncomplete: function(data) {
// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
// 각 주소의 노출 규칙에 따라 주소를 조합한다.
// 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
var addr = ''; // 주소 변수
var extraAddr = ''; // 참고항목 변수
//사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
if (data.userSelectedType === 'R') { // 사용자가 도로명 주소를 선택했을 경우
addr = data.roadAddress;
} else { // 사용자가 지번 주소를 선택했을 경우(J)
addr = data.jibunAddress;
}
// 사용자가 선택한 주소가 도로명 타입일때 참고항목을 조합한다.
if(data.userSelectedType === 'R'){
// 법정동명이 있을 경우 추가한다. (법정리는 제외)
// 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
if(data.bname !== '' && /[동|로|가]$/g.test(data.bname)){
extraAddr += data.bname;
}
// 건물명이 있고, 공동주택일 경우 추가한다.
if(data.buildingName !== '' && data.apartment === 'Y'){
extraAddr += (extraAddr !== '' ? ', ' + data.buildingName : data.buildingName);
}
// 표시할 참고항목이 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
if(extraAddr !== ''){
extraAddr = ' (' + extraAddr + ')';
}
// 조합된 참고항목을 해당 필드에 넣는다.
//document.getElementById("sample6_extraAddress").value = extraAddr;
} else {
//document.getElementById("sample6_extraAddress").value = '';
}
// 우편번호와 주소 정보를 해당 필드에 넣는다.
document.getElementById('zip').value = data.zonecode;
document.getElementById("addr1").value = addr;
// 커서를 상세주소 필드로 이동한다.
document.getElementById("addr2").focus();
}
}).open();
}
'JSP' 카테고리의 다른 글
[JSP] JAVA JSON user 관리 프로그램 - 리스트 (0) | 2022.10.23 |
---|---|
[JSP] JAVA JSON (0) | 2022.10.23 |
[JSP] 프로젝트 실습 - 게시판 만들기 3 (약관 화면 설계) (0) | 2022.10.21 |
[JSP] 프로젝트 실습 - 게시판 만들기 2 (로그인 화면 설계) (0) | 2022.10.19 |
[JSP] 프로젝트 실습 - 게시판 만들기 1 ( 유스케이스 작성) (0) | 2022.10.19 |