69일차 (2) - Spring, javaScript (인증, 인가 - 중복 검사)

Yohan·2024년 5월 30일
0

코딩기록

목록 보기
106/156
post-custom-banner

중복 검사

  • 중복 검사는 서버(java)에서도 진행해야하고, 클라이언트(js)에서도 진행해야한다.

MemberService

  • 아이디, 이메일은 중복검사 하는 중간 로직을 추가
    // 아이디, 이메일 중복검사
    public boolean checkIdentifier(String type, String keyword) {
        return memberMapper.existsById(type, keyword);
    }

MemberController

  • Controller를 통해 아이디, 이메일 비동기 요청 처리
    // 아이디, 이메일 중복검사 비동기 요청 처리
    @GetMapping("/check")
    @ResponseBody // 비동기이므로
    public ResponseEntity<?> check(String type, String keyword) {
        boolean flag = memberService.checkIdentifier(type, keyword);
        log.debug("{} 중복체크 결과? {}", type, flag);
        return ResponseEntity
                .ok()
                .body(flag);
    }

validation.js (내가 한 것)

아이디, 비밀번호, 이름, 이메일 중복검사 클라이언트에서 진행

// 회원가입 입력 검증 처리


// 회원가입 버튼 활성화 함수
function button() {
  const $successInput = document.querySelectorAll('.success');
  const $button = document.getElementById('signup-btn');
  $button.disabled = [...$successInput].length !== 5;
}

// 계정 중복검사 비동기 요청 보내기
async function fetchDuplicateCheck(idValue) {

  const res = await fetch(`http://localhost:8383/members/check?type=account&keyword=${idValue}`);
  const flag = await res.json();

  idFlag = flag;
  
}

// 계정 입력 검증
const $idInput = document.getElementById('user_id');
let idFlag = false;

$idInput.addEventListener('keyup', async (e) => {

  // 아이디 검사 정규표현식
  const accountPattern = /^[a-zA-Z0-9]{4,14}$/;

  // 입력값 읽어오기
  const idValue = $idInput.value;
  // console.log(idValue);

  // 검증 메시지를 입력할 span
  const $idChk = document.getElementById('idChk');

  if (idValue.trim() === '') {
    $idInput.style.borderColor = 'red';
    $idChk.innerHTML = '<b class="warning">[아이디는 필수값입니다.]</b>';
  } else if (!accountPattern.test(idValue)) {
    $idInput.style.borderColor = 'red';
    $idChk.innerHTML = '<b class="warning">[아이디는 4~14글자 사이 영문,숫자로 입력하세요]</b>';
  } else {

    // 아이디 중복검사
    await fetchDuplicateCheck(idValue);

    if (idFlag) {
      $idInput.style.borderColor = 'red';
      $idChk.innerHTML = '<b class="warning">[아이디가 중복되었습니다. 다른 아이디를 사용하세요!]</b>';
    } else {
      $idInput.style.borderColor = 'skyblue';
      $idChk.innerHTML = '<b class="success">[사용가능한 아이디입니다.]</b>';
      idValid = true;
    }
  }
  button();
});

/////////////////////////////////////////////////////////
// 비밀번호 입력 검증
const $pwInput = document.getElementById('password');

$pwInput.addEventListener('keyup', async (e) => {
  // 패스워드 검사 정규표현식
  const passwordPattern = /([a-zA-Z0-9].*[!,@,#,$,%,^,&,*,?,_,~])|([!,@,#,$,%,^,&,*,?,_,~].*[a-zA-Z0-9])/;

  // 입력값 읽어오기
  const pwValue = $pwInput.value;

  // 검증 메시지를 입력할 span
  const $pwChk = document.getElementById('pwChk');
  // const $pwChk2 = document.getElementById('pwChk2');

  if (pwValue.trim() === '' ) {
    $pwInput.style.borderColor = 'red';
    $pwChk.innerHTML = '<b class="warning">[비밀번호는 필수값입니다.]</b>';
  } else if (!passwordPattern.test(pwValue)) {
    $pwInput.style.borderColor = 'red';
    $pwChk.innerHTML = '<b class="warning">[비밀번호는 8글자 이상, 영문,숫자,특수문자가 포함되게 입력하세요]</b>';
  } else {
    $pwInput.style.borderColor = 'skyblue';
    $pwChk.innerHTML = '<b class="success">[사용가능한 비밀번호입니다.]</b>';
    passwordValid = true;
  }
  button();
})

// 비밀번호확인 입력 검증
const $pwCheckInput = document.getElementById('password_check');

$pwCheckInput.addEventListener('keyup', async (e) => {
  // 패스워드 검사 정규표현식
  const passwordPattern = /([a-zA-Z0-9].*[!,@,#,$,%,^,&,*,?,_,~])|([!,@,#,$,%,^,&,*,?,_,~].*[a-zA-Z0-9])/;

  // 입력값 읽어오기
  const pwValue = $pwInput.value;
  const pwCheckValue = $pwCheckInput.value;

  // 검증 메시지를 입력할 span
  const $pwChk2 = document.getElementById('pwChk2');

  // 비밀번호 확인에서는 비밀번호와 일치하는지 검증
  if (pwValue !== pwCheckValue) {
    $pwInput.style.borderColor = 'red';
    $pwChk2.innerHTML = '<b class="warning">[비밀번호가 일치하지 않습니다.]</b>';
  } else {
    $pwInput.style.borderColor = 'skyblue';
    $pwChk2.innerHTML = '<b class="success">[비밀번호가 일치합니다.]</b>';
    passwordMatchValid = true;
  }
  button();
})


/////////////////////////////////////////////////////////



// 이름 입력 검증
const $nameInput = document.getElementById('user_name');

$nameInput.addEventListener('keyup', async (e) => {
  // 이름 검사 정규표현식\
  const namePattern = /^[가-힣]+$/;

  // 입력값 읽어오기
  const nameValue = $nameInput.value;

  // 검증 메시지를 입력할 span
  const $nameChk = document.getElementById('nameChk');

  if (nameValue.trim() === '' ) {
    $nameInput.style.borderColor = 'red';
    $nameChk.innerHTML = '<b class="warning">[이름은 필수값입니다.]</b>';
  } else if (!namePattern.test(nameValue)) {
    $nameInput.style.borderColor = 'red';
    $nameChk.innerHTML = '<b class="warning">[이름은 한글이여야 합니다.]</b>';
  } else {
    $nameInput.style.borderColor = 'skyblue';
    $nameChk.innerHTML = '<b class="success">[사용가능한 이름입니다.]</b>';
    nameValid = true;
  }
  button();
});

////////////////////////////////////////////////////////

// 이메일 중복검사 비동기 요청 보내기
async function fetchDuplicateCheckEmail(emailValue) {

  const res = await fetch(`http://localhost:8383/members/check?type=email&keyword=${emailValue}`);
  const flag = await res.json();

  emailFlag = flag;

}

// 이메일 입력 검증
const $emailInput = document.getElementById('user_email');
let emailFlag = false;

$emailInput.addEventListener('keyup', async (e) => {

  // 이메일 검사 정규표현식
  const emailPattern = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/;

  // 입력값 읽어오기
  const emailValue = $emailInput.value;

  // 검증 메시지를 입력할 span
  const $emailChk = document.getElementById('emailChk');

  if (emailValue.trim() === '') {
    $emailInput.style.borderColor = 'red';
    $emailChk.innerHTML = '<b class="warning">[이메일은 필수값입니다.]</b>';
  } else if (!emailPattern.test(emailValue)) {
    $emailInput.style.borderColor = 'red';
    $emailChk.innerHTML = '<b class="warning">[이메일은 내용@내용.내용의 형태여야 합니다. ]</b>';
  } else {

    // 이메일 중복검사
    await fetchDuplicateCheckEmail(emailValue);

    if (emailFlag) {
      $emailInput.style.borderColor = 'red';
      $emailChk.innerHTML = '<b class="warning">[이메일이 중복되었습니다. 다른 이메일을 사용하세요!]</b>';
    } else {
      $emailInput.style.borderColor = 'skyblue';
      $emailChk.innerHTML = '<b class="success">[사용가능한 이메일입니다.]</b>';
      emailValid = true;
    }
  }
  button();
});

/////////////////////////////////////////////////////////

선생님 코드

validate.js


// 서버에 중복확인 비동기 요청
export const checkAvailability = async (type, keyword) => {
  const response = await fetch(`http://localhost:8383/members/check?type=${type}&keyword=${keyword}`);
  const flag = await response.json();
  return !flag;
};


// 유효성 검증에 사용될 정규표현식 패턴들 정의

// 아이디 패턴: 영문 대소문자와 숫자, 4~14글자
const accountPattern = /^[a-zA-Z0-9]{4,14}$/;

// 비밀번호 패턴: 반드시 영문, 숫자, 특수문자를 포함하여 8자 이상
const passwordPattern = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*?_~])[A-Za-z\d!@#$%^&*?_~]{8,}$/;

// 이름 패턴: 한글만 허용
const namePattern = /^[가-힣]+$/;

// 이메일 패턴: 기본적인 이메일 형식
const emailPattern = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/;

export const validateInput = {
  // 아이디 유효성 검사 함수
  account: async (value) => {
    // 빈 값 검사
    if (!value.trim()) return { valid: false, message: '아이디는 필수값입니다!' };
    // 정규표현식 검사
    if (!accountPattern.test(value)) return { valid: false, message: '아이디는 4~14글자의 영문,숫자로 입력하세요.' };
    // 빈 값도 아니고 정규표현식도 통과했으면? -> 중복 검사
    const isAvailable = await checkAvailability('account', value);
    // 중복 여부에 따라 결과 반환
    return isAvailable ? { valid: true } : { valid: false, message: '아이디가 중복되었습니다.' };
  },
  // 비밀번호 유효성 검사 함수
  password: (value) => {
    // 빈 값 검사
    if (!value.trim()) return { valid: false, message: '비밀번호는 필수값입니다!' };
    // 정규표현식 검사
    if (!passwordPattern.test(value)) return { valid: false, message: '비밀번호는 영문, 숫자, 특수문자를 포함하여 8자 이상이어야 합니다!' };
    // 유효한 경우
    return { valid: true };
  },
  // 비밀번호 확인 유효성 검사 함수
  passwordCheck: (value, password) => {
    // 빈 값 검사
    if (!value.trim()) return { valid: false, message: '비밀번호 확인란은 필수값입니다!' };
    // 비밀번호 일치 여부 검사
    if (value !== password) return { valid: false, message: '비밀번호가 일치하지 않습니다!' };
    // 유효한 경우
    return { valid: true };
  },
  // 이름 유효성 검사 함수
  name: (value) => {
    // 빈 값 검사
    if (!value.trim()) return { valid: false, message: '이름은 필수정보입니다!' };
    // 정규표현식 검사
    if (!namePattern.test(value)) return { valid: false, message: '이름은 한글로 입력해주세요.' };
    // 유효한 경우
    return { valid: true };
  },
  // 이메일 유효성 검사 함수
  email: async (value) => {
    // 빈 값 검사
    if (!value.trim()) return { valid: false, message: '이메일은 필수값입니다!' };
    // 정규표현식 검사
    if (!emailPattern.test(value)) return { valid: false, message: '이메일 형식을 지켜주세요.' };
    // 중복 검사
    const isAvailable = await checkAvailability('email', value);
    // 중복 여부에 따라 결과 반환
    return isAvailable ? { valid: true } : { valid: false, message: '이메일이 중복되었습니다.' };
  }
};

signUp.js


// 유효성 검증 함수들을 import
import { validateInput } from './validate.js';
import { debounce } from './util.js';

// 폼과 회원가입 버튼 요소를 가져옴
const form = document.getElementById('signUpForm');
const signupButton = document.getElementById('signup-btn');


// 각 필드에 대한 정보 배열 (id, 유효성 검증 함수, 에러 메시지 표시 요소, 초기 유효 상태)
const fields = [
  { id: 'user_id', validator: validateInput.account, errorElement: 'idChk', valid: false },
  { id: 'password', validator: validateInput.password, errorElement: 'pwChk', valid: false },
  { id: 'password_check', validator: (value) => validateInput.passwordCheck(value, document.getElementById('password').value), errorElement: 'pwChk2', valid: false },
  { id: 'user_name', validator: validateInput.name, errorElement: 'nameChk', valid: false },
  { id: 'user_email', validator: validateInput.email, errorElement: 'emailChk', valid: false }
];

// 버튼 상태를 업데이트하는 함수 (모든 값들이 입력되어야 회원가입 버튼 활성화)
const updateButtonState = () => {
  
  // 모든 valid가 true인지 확인
  const isFormValid = fields.every(field => field.valid);

  if (isFormValid) {
    signupButton.disabled = false;
    signupButton.style.backgroundColor = 'orangered'; // 활성화된 버튼 배경색
  } else {
    signupButton.disabled = true;
    signupButton.style.backgroundColor = 'lightgray'; // 비활성화된 버튼 배경색
  }
};


// 각 필드에 대해 입력값 검증 이벤트 리스너를 추가
fields.forEach(field => {
  const $input = document.getElementById(field.id); // 입력 요소 가져오기
  $input.addEventListener('keyup', debounce(async (e) => { // 키보드 입력 시마다 유효성 검증
    const isValid = await field.validator($input.value); // 유효성 검증 함수 호출
    const $errorSpan = document.getElementById(field.errorElement); // 에러 메시지 표시 요소 가져오기
    console.log(isValid);
    if (isValid.valid) { // 유효한 경우
      $input.style.borderColor = 'skyblue'; // 입력 요소의 테두리 색 변경
      $errorSpan.innerHTML = '<b class="success">[사용가능합니다.]</b>'; // 성공 메시지 표시
      field.valid = true; // 필드의 유효 상태를 true로 설정
    } else { // 유효하지 않은 경우
      $input.style.borderColor = 'red'; // 입력 요소의 테두리 색 변경
      $errorSpan.innerHTML = `<b class="warning">[${isValid.message}]</b>`; // 에러 메시지 표시
      field.valid = false; // 필드의 유효 상태를 false로 설정
    }
    if (field.id === 'password' && document.getElementById('password_check').value.trim()) {
      checkPassword([...fields][2]);
    }
    updateButtonState(); // 각 입력 유효성 검사 후 버튼 상태 업데이트
  }, 500)); 
});

// 회원가입 버튼 클릭 이벤트 리스너 추가
signupButton.addEventListener('click', (e) => {
  // 모든 필드가 유효한지 확인
  const isFormValid = fields.every(result => result.valid);

  if (isFormValid) { // 모든 필드가 유효한 경우
    form.submit(); // 폼 제출
  } else { // 유효하지 않은 필드가 있는 경우
    alert('입력란을 다시 확인하세요!'); // 경고 메시지 표시
  }
});

function checkPassword (field){
  const $input = document.getElementById(field.id); // 입력 요소 가져오기
  const $a = document.getElementById('password')
  $a.addEventListener('keyup', debounce(async (e) => { // 키보드 입력 시마다 유효성 검증
      const isValid = await field.validator($input.value); // 유효성 검증 함수 호출
      const $errorSpan = document.getElementById(field.errorElement); // 에러 메시지 표시 요소 가져오기
      console.log(isValid);
      
      if (isValid.valid) { // 유효한 경우
          $input.style.borderColor = 'skyblue'; // 입력 요소의 테두리 색 변경
          $errorSpan.innerHTML = '<b class="success">[사용가능합니다.]</b>'; // 성공 메시지 표시
          field.valid = true; // 필드의 유효 상태를 true로 설정
      } else { // 유효하지 않은 경우
          $input.style.borderColor = 'red'; // 입력 요소의 테두리 색 변경
          $errorSpan.innerHTML = `<b class="warning">[${isValid.message}]</b>`; // 에러 메시지 표시
          field.valid = false; // 필드의 유효 상태를 false로 설정
      }
      updateButtonState(); // 각 입력 유효성 검사 후 버튼 상태 업데이트
  }, 500));
}


// 페이지 로드 시 초기 버튼 상태 업데이트
updateButtonState();

util.js (디바운싱)

// 디바운싱 함수 정의
export function debounce(callback, wait) {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => callback(...args), wait);
  };
}
profile
백엔드 개발자
post-custom-banner

0개의 댓글