티스토리 뷰

1️⃣ 퇴소자 발생

갑작스럽게 퇴소자가 발생했다. 안 그래도 한 분이 적응을 못하시는 것 같아 서로 말은 안했지만, 모든 팀원분들이 도움을 주려는 노력이 계속되었는데... 안타깝게도 결국 퇴소하시고 말았다. 특히 이미 역할과 기획이 마무리된 상태에서의 이탈은 예상치 못한 돌발 상황이라 상당히 당황스러웠다. 하지만 이미 일어난 일을 어쩔 수는 없으니 빨리 계획을 수정하고 다시 작업에 돌입하는 것이 최선이라고 생각한 우리는 이를 빠르게 수용하고 대응하기로 결정했다. 빠른 상의를 거쳐 계획을 수정하고, 작업에 필요한 부분을 조정하여 최대한 효율적으로 진행할 수 있도록 노력했다.

예기치못한 퇴소로 인한 상황 변화에 대응하고, 역할 재조정을 통해 남은 팀원들의 팀워크를 유지하는 것이 또 다른 도전과 경험이었다고 생각된다. 돌이켜보면  팀의 유연성과 소통의 중요성을 향상시켜 더욱 단단해지는 이슈였기를 바라면서...👏

 

 


 

 

2️⃣ 회원가입 구현

폼 제공 및 입력 수집

  • 사용자에게 회원가입을 위한 폼을 제공
  • 필요한 정보: 사용자 아이디, 비밀번호, 이메일, 주소 등
  • 프론트단에서는 사용자로부터 입력받은 정보를 서버로 전송

MemberDTO

package com.dto;

import org.apache.ibatis.type.Alias;

@Alias("MemberDTO")
public class MemberDTO {

	private String userID;
	private String passwd;
	private String name;
	private String email;
	private String post;
	private String addr1;
	private String addr2;
	private String phone;
	
	public MemberDTO() {
		// TODO Auto-generated constructor stub
	}

	public MemberDTO(String userID, String passwd, String name, String email, String post, String addr1, String addr2,
			String phone) {
		this.userID = userID;
		this.passwd = passwd;
		this.name = name;
		this.email = email;
		this.post = post;
		this.addr1 = addr1;
		this.addr2 = addr2;
		this.phone = phone;
	}

	public String getUserID() {
		return userID;
	}

	public void setUserID(String userID) {
		this.userID = userID;
	}

	public String getPasswd() {
		return passwd;
	}

	public void setPasswd(String passwd) {
		this.passwd = passwd;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPost() {
		return post;
	}

	public void setPost(String post) {
		this.post = post;
	}

	public String getAddr1() {
		return addr1;
	}

	public void setAddr1(String addr1) {
		this.addr1 = addr1;
	}

	public String getAddr2() {
		return addr2;
	}

	public void setAddr2(String addr2) {
		this.addr2 = addr2;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	@Override
	public String toString() {
		return "MemberDTO [userID=" + userID + ", passwd=" + passwd + ", name=" + name + ", email=" + email + ", post="
				+ post + ", addr1=" + addr1 + ", addr2=" + addr2 + ", phone=" + phone + "]";
	}
	
}

 

 

MemberMapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="MemberMapper">
 <!-- 회원가입  -->
  <insert id="register" parameterType="MemberDTO">
    insert into member(userID, passwd, name, email, post, addr1, addr2, phone, salt )
    values (#{userID},#{passwd},#{name},#{email},#{post},#{addr1},#{addr2},#{phone},#{salt})
  </insert>
</mapper>

 

 

root-context에 매퍼 추가 잊지말고 해주기! 😂 ( 이전 포스팅에서 실수 경험을 볼 수 있습니다.. )

<!-- SqlSessionFactory 역할의 빈 등록 : SqlSessionFactoryBean -->
	<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 
		<property name="dataSource" ref="basicDataSource"/>
		<property name="mapperLocations"> <!-- Mapper 등록 -->
			<list>
				<value>classpath:com/config/MemberMapper.xml</value>
			</list>
		</property>
       </bean>

 

 

MemberDAO

package com.dao;

import java.util.HashMap;
import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.dto.MemberDTO;

@Repository
public class MemberDAO {
	
	@Autowired // SqlSessionTemplate 주입
	SqlSessionTemplate session;
	
	// 회원가입
	public void register(MemberDTO dto) throws Exception {
		session.insert("MemberMapper.register", dto);
	}
    
}

 

 

registerForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>여담: 회원가입</title>
</head>
<body>
<!-- header -->
<jsp:include page="common/top.jsp" flush="true" />

<!-- content -->
<div id='wrapper'>
  <div>
    <jsp:include page="member/registerForm.jsp" flush="true" />
  </div>
</div>
</body>
</html>

 

 

member/registerForm.jsp

주소찾기는 Daum 우편번호 서비스를 사용했다.

<%@ page contentType="text/html;charset=utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page import="java.net.URLDecoder"%>
 <!-- jquery CDN -->   
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
    <link rel="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css"> <!-- 폰트어썸 코드 -->
    <title>여담: 회원가입</title>
    
    <style >
    	* { box-sizing:border-box; font-family: 'SUIT-Medium'; }

        form {
            width:700px;
            height:850px;
            display : flex;
            flex-direction: column;
            align-items:center;
            position : absolute;
            top:50%;
            left:50%;
            transform: translate(-50%, -50%) ;
            border: 1px solid rgb(89,117,196);
            border-radius: 20px;
            
        }
        
        .input-field {
            width: 300px;
            height: 250px;
            border : 1px solid rgb(89,117,196);
            border-radius:5px;
            padding: 0 6px;
            margin-bottom: 7px;
        }
 		.input-group {
   			display: flex;
    		justify-content: space-between;
    		align-items: center;
    		width: 300px;
    		height: 30px;
		}
		
		#userID{
			width: 185px;
            height: 35px;
            border : 1px solid rgb(89,117,196);
            border-radius:5px;
       
		}
		#sample4_postcode{
			width: 155px;
            height: 35px;
            border : 1px solid rgb(89,117,196);
            border-radius:5px;
       
		}
	
		#idDuplicatedcheck {
    		background-color: #E2E2E2;
    		color: #3A3A3A;
    		width: 80px;
    		height: 35px;
    		font-size: 13px;
    		border: 1px solid rgb(89,117,196);
    		border-radius: 5px;
    		margin-left: 10px;
		}
		#postcodeBtn {
    		background-color: #E2E2E2;
    		color: #3A3A3A;
    		width: 110px;
    		height: 35px;
    		font-size: 13px;
    		border: 1px solid rgb(89,117,196);
    		border-radius: 5px;
    		margin-left: 10px;
		}

        label {
            width:300px;
            height:30px;
            margin-top :4px;
            margin-bottom: 3px;
            font-size: 14px;
            
        }

        #sbtn {
            background-color: rgb(53,99,233);
            color : white;
            width:300px;
            height:290px;
            font-size: 17px;
            border : none;
            border-radius: 5px;
            margin : 20px 0 50px 0;
        }

        .title {
            font-size : 50px;
            font-weight: bold;
            margin: 40px 0 20px 0;
        }

        .msg {
            height: 30px;
            text-align:center;
            font-size:15px;
            color:red;
            margin-bottom: 1px;
        }
        #pwcheck{
        	font-size: 13px;
        	color:red;
        }
        div.pw{
        	position: relative;
        }
        div.pw input{
        	width: 300px;
        	height:40px;
        	text-indent:10px;
        }
       
        div.pw i{
        	position:absolute;
        	left: 88%;
        	top: 10px;
        	color: #3A3A3A;
        }
     
    </style>
</head>
<body>
   <form action="/app/register" method="POST" name="frm" onsubmit="return formCheck(this)"><!-- 이벤트 설정, true면 submit false면 제출 x -->
    <div class="title">회원가입</div>
    <div id="msg" class="msg"></div> 
    
	<span id="result" style="color:#C13D5F; font-weight:bold"></span><br>
    <label for="">아이디</label>
    <div class="input-group" style="margin-bottom: 6px;">
    <div class="container row">
	    <input class="col" type="text" id="userID" name="userID" placeholder="7자 이상의 문자" autofocus>
	    <button class="col" type="button" id="idDuplicatedcheck" style="margin-bottom: 18px;" >중복확인</button>
    </div>
	</div>
    <label for="">비밀번호</label>
    <div class="pw">
    <input class="input-field" type="password" name="passwd" id="passwd" placeholder="7자 이상의 문자">
    <i class="fas fa fa-eye fa-lg"></i>
    </div>
    <label for="">이름</label>
    <input class="input-field" type="text" name="name" placeholder="홍길동">
    <label for="">이메일</label>
    <input class="input-field" type="text" name="email" placeholder="example@naver.com"> 
    <label for="" style="margin-bottom: 2px;">우편번호</label>
    <div class="input-group">
    <div class="container row">
    <input type="text" class="col" name="post" id="sample4_postcode" placeholder="01265">
    <input type="button" class="col" id="postcodeBtn" onclick="sample4_execDaumPostcode()" value="우편번호찾기"><br>
	</div>
	</div>
	<div style="margin-top: 10px;"></div>
	<input class="input-field" type="text" name="addr1" id="sample4_roadAddress" placeholder="도로명주소">
	<input class="input-field" type="text" name="addr2" id="sample4_jibunAddress" placeholder="지번주소">
    <span id="guide" style="color:#999"></span>
    <label for="">전화번호</label>
    <input class="input-field" type="text" name="phone" placeholder="01012341234"> 
    <button type="submit" id="sbtn">회원 가입</button>
   </form> 
  
  <!--주소찾기(Daum 우편번호 서비스)-->
  <script src="http://dmaps.daum.net/map_js_init/postcode.v2.js"></script>
<script>
    function sample4_execDaumPostcode() {
        new daum.Postcode({
            oncomplete: function(data) {
                // 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.

                // 도로명 주소의 노출 규칙에 따라 주소를 조합한다.
                // 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
                var fullRoadAddr = data.roadAddress; // 도로명 주소 변수
                var extraRoadAddr = ''; // 도로명 조합형 주소 변수

                // 법정동명이 있을 경우 추가한다. (법정리는 제외)
                // 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
                if(data.bname !== '' && /[동|로|가]$/g.test(data.bname)){
                    extraRoadAddr += data.bname;
                }
                // 건물명이 있고, 공동주택일 경우 추가한다.
                if(data.buildingName !== '' && data.apartment === 'Y'){
                   extraRoadAddr += (extraRoadAddr !== '' ? ', ' + data.buildingName : data.buildingName);
                }
                // 도로명, 지번 조합형 주소가 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
                if(extraRoadAddr !== ''){
                    extraRoadAddr = ' (' + extraRoadAddr + ')';
                }
                // 도로명, 지번 주소의 유무에 따라 해당 조합형 주소를 추가한다.
                if(fullRoadAddr !== ''){
                    fullRoadAddr += extraRoadAddr;
                }

                // 우편번호와 주소 정보를 해당 필드에 넣는다.
                document.getElementById('sample4_postcode').value = data.zonecode; //5자리 새우편번호 사용
                document.getElementById('sample4_roadAddress').value = fullRoadAddr;
                document.getElementById('sample4_jibunAddress').value = data.jibunAddress;

                // 사용자가 '선택 안함'을 클릭한 경우, 예상 주소라는 표시를 해준다.
                if(data.autoRoadAddress) {
                    //예상되는 도로명 주소에 조합형 주소를 추가한다.
                    var expRoadAddr = data.autoRoadAddress + extraRoadAddr;
                    document.getElementById('guide').innerHTML = '(예상 도로명 주소 : ' + expRoadAddr + ')';

                } else if(data.autoJibunAddress) {
                    var expJibunAddr = data.autoJibunAddress;
                    document.getElementById('guide').innerHTML = '(예상 지번 주소 : ' + expJibunAddr + ')';

                } else {
                    document.getElementById('guide').innerHTML = '';
                }
            }
        }).open();
    }
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
</body>
</html>

 

 

컨트롤러에서 요청 처리

  • Spring MVC 컨트롤러에서 회원가입을 처리하는 메소드를 구현
  • 사용자가 입력한 데이터는 HTTP 요청으로 전달되며, 컨트롤러는 이 요청을 받아 처리

registerController

package com.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.dto.MemberDTO;
import com.service.MemberService;

@Controller
public class registerController {
	
	@Autowired
	MemberService service;
	
	// 신규 회원 입력화면
	@GetMapping("/memberUI")
	public String memberUI() {
		return "registerForm";   //회원가입폼으로
	}
	
	// 신규 회원 등록
	@PostMapping("/register")
	public String register(MemberDTO dto) throws Exception {
		service.register(dto);
		return "main";   //메인으로
	}
    
 }

 

 

 

비즈니스 로직 처리

  • 사용자가 입력한 데이터를 기반으로 회원가입과 관련된 비즈니스 로직을 수행

memberService

package com.service;

import java.util.HashMap;
import java.util.List;

import com.dto.MemberDTO;

public interface MemberService {
	// 회원가입 
	public void register(MemberDTO dto) throws Exception;

}

 

memberServiceImpl

package com.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.dao.MemberDAO;
import com.dto.MemberDTO;

@Service
public class MemberServiceImpl implements MemberService {

	@Autowired
	MemberDAO dao;
    
      // 회원가입
	dao.register(dto);
   
}

 

 

 

회원 정보 저장

  • 사용자 정보가 데이터베이스에 저장됨

 

 


 

 

 

3️⃣ ID 중복체크 (AJAX 연동) 추가

AJAX를 사용하여 비동기적으로 서버에 요청을 보내고, 그에 대한 응답을 받아와 동적으로 화면을 갱신하는 방식을 사용했다.

 

registerForm

• 중복확인 버튼 추가  : <button class="col" type="button" id="idDuplicatedcheck" style="margin-bottom: 18px;">중복확인</button>

<label for="">아이디</label>
<div class="input-group" style="margin-bottom: 6px;">
    <div class="container row">
        <input class="col" type="text" id="userID" name="userID" placeholder="7자 이상의 문자" autofocus>
        <!--ID 중복체크 버튼-->
        <button class="col" type="button" id="idDuplicatedcheck" style="margin-bottom: 18px;">중복확인</button>
    </div>
</div>
<span id="result" style="color:#C13D5F; font-weight:bold"></span><br>

 

• ajax 연동

// id 중복 체크
$("#idDuplicatedcheck").on("click",function(){
    // submit 비활성시키기 (아래 '중복체크' button태그 때문에- button은 기본적으로 submit 된다)
    event.preventDefault();
    // ajax 연동
     $.ajax({
        type:"get",
        url:"memberIdCheck", // get 방식으로 멤버아이디체크서블릿으로 넘어간다는 소리
        data:{
            userID:$("#userID").val()
        },  // 요청코드
        dataType:'text',  // 응답받은 데이터 타입
        success:function(data, status, xhr){ // 그 문자열을 data에 저장
            console.log(data);
            $("#result").text(data);
        },
        error:function(xhr, status, error){
            console.log("error 발생");
        }
     });
});

 

 

MemberMapper

    <!-- id 중복체크 -->
    <select id="idCheck" parameterType="string" resultType="MemberDTO">
    	select *
    	from member
    	where userID = #{userID}
    </select>

 

 

MemberDAO

// id 중복체크
public MemberDTO idCheck(String userID) {
	MemberDTO dto = session.selectOne("MemberMapper.idCheck", userID);
	return dto;
}

 

 

MemberService

public MemberDTO idCheck(String userID);

 

 

MemberServiceImpl

//id 중복 체크
	@Override
	public MemberDTO idCheck(String userID) {
		return dao.idCheck(userID);
	}

 

 

registerController

서버 측 처리 : 클라이언트의 요청에 응답하여 중복 여부를 전달한다.

// 아이디 중복체크 (ajax 연동을 위해 의존성 추가 필요)
@GetMapping(value = "/memberIdCheck", produces = "text/plain;charset=utf-8")
@ResponseBody
public String idCheck(@RequestParam("userID")
			String userID) {
	MemberDTO dto = service.idCheck(userID);
	String msg = "사용 가능한 아이디입니다.";
	if(dto != null) {
		msg = "중복된 아이디입니다.";
	}
	return msg;
}
  • @GetMapping(value = "/memberIdCheck", produces = "text/plain;charset=utf-8")
    "/memberIdCheck" 경로로 GET 요청이 오면 처리하는 메서드
  • @ResponseBody
    메서드가 직접 HTTP 응답의 body 부분에 데이터를 넣어준다. 메서드에서 반환한 데이터가 HTTP 응답의 body 부분으로 전송되어 클라이언트에게 전달됨.
  • public String idCheck(@RequestParam("userID") String userID)
    메서드의 매개변수로 userID를 받아온다. 이는 AJAX 요청에서 전달된 사용자가 입력한 아이디이다.
  • MemberDTO dto = service.idCheck(userID);
    받아온 아이디를 사용하여 서비스 계층에서 중복 체크를 수행. 서비스 계층에서는 해당 아이디를 가진 회원이 있는지 조회하고, 그 결과를 dto에 저장한다.
  • String msg = "사용 가능한 아이디입니다.";
    "사용 가능한 아이디입니다."라는 기본 메시지를 설정.
  • if(dto != null) { msg = "중복된 아이디입니다."; }
    만약 조회된 dtonull이 아니라면, 즉, 중복된 아이디가 존재한다면 메시지를 "중복된 아이디입니다."로 변경
  • return msg;
    최종적으로 설정된 메시지를 반환하게 되고, 이는 AJAX 요청에 응답으로 전달됨.

 

 

응답 처리

function formCheck(frm) {
 if($("#result").text().length == 0) {
            	setMessage('id 중복 여부를 확인해 주세요.', frm.userID);
				return false;
			}
            if($("#result").text() == '중복된 아이디입니다.') {
            	setMessage('중복된 아이디는 사용할 수 없습니다.', frm.userID);
				return false;
			}
 }

 

 


 

 

4️⃣ 유효성 검사

유효성 검사(Validation)란?
사용자로부터 입력받은 데이터가 기대한 형식과 조건을 충족하는지 확인하는 과정이다.

유효성 검사의 목적?
데이터의 일관성 유지
보안 강화
사용자 경험 향상
시스템 안정성 향상


유효성 검사 유형?
데이터 형식 검사

필수 필드 검사
길이 제한 검사
범위 검사
중복 데이터 검사
보안 검사

 

 

registerForm.jsp

<script>
// 아이디값 입력시 result값 초기화
	 $("#userID").on("keydown", function() {
	 $("#result").text("");
 });
       
    // 유효성 검사
    function formCheck(frm) {
    let msg ='';
    
    // 아이디 길이 체크
    if(frm.userID.value.length < 7) {
        setMessage('id의 길이는 7자 이상이어야 합니다.', frm.userID);
        return false;
    }
    
    // 중복 여부 체크
    if($("#result").text().length == 0) {
        setMessage('id 중복 여부를 확인해 주세요.', frm.userID);
        return false;
    }
    
    // 중복된 아이디인 경우
    if($("#result").text() == '중복된 아이디입니다.') {
        setMessage('중복된 아이디는 사용할 수 없습니다.', frm.userID);
        return false;
    }
    
    // 비밀번호 길이 체크
    if(frm.passwd.value.length < 7) {
        setMessage('pwd의 길이는 7자 이상이어야 합니다.', frm.passwd);
        return false;
    }
    
    if($("#pwcheck").text() == '비번 불일치') {
         setMessage('비밀번호가 일치되어야합니다.', frm.passwd);
         return false;
    }
            
    if($("#pwcheck").text().length == 0) {
         setMessage('비밀번호가 일치 여부를 확인해 주세요.', frm.passwd);
          return false;
    }

    // 이름 누락 여부 체크
    if(frm.name.value.length == 0) {
        setMessage('이름이 누락되었습니다.', frm.name);
        return false;
    }

    // 이메일 누락 여부 체크
    if(frm.email.value.length == 0) {
        setMessage('이메일이 누락되었습니다.', frm.email);
        return false;
    }

    // 이메일 형식 체크
    if(frm.email.value.indexOf('@') == -1) {
        setMessage('올바른 이메일 형식이 아닙니다.', frm.passwd);
        return false;
    }

    // 우편번호 누락 여부 체크
    if(frm.post.value.length == 0) {
        setMessage('우편번호가 누락되었습니다.', frm.post);
        return false;
    }

    // 전화번호 누락 여부 체크
    if(frm.phone.value.length == 0) {
        setMessage('전화번호가 누락되었습니다.', frm.phone);
        return false;
    }

    // 전화번호 숫자 체크
    if(isNaN(frm.phone.value)) {
        setMessage('전화번호는 숫자만 입력 가능합니다.', frm.phone);
        return false;
    }
    
    // submit 버튼 한번 클릭하면 비활성화
    document.getElementById("sbtn").disabled = true;

    return true;
}

	function setMessage(msg, element){
         document.getElementById("msg").innerHTML = `<i class="fa fa-exclamation-circle"> ${'${msg}'}</i>`;

         if(element) {
             element.select(); // 양식이 잘못되었을 때 자동 커서 위치
         }
    }

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함