인프런 커뮤니티 질문&답변

개발하는 알파카님의 프로필 이미지
개발하는 알파카

작성한 질문수

[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스

▶ final 과제

이번 final 과제 피드백 부탁드립니다!

해결된 질문

작성

·

124

0

안녕하세요! 강의 잘 듣고 있습니다!

이번 과제 코드 피드백 부탁드립니다!

고맙습니다.

<화면>

<html>

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>SignUp</title>
  <link rel="stylesheet" href="./final.css">
  <script defer src="./final.js"></script>
</head>
<body>
  <div class="wrapper">
    <div class="wrapper__header">
      <span id="header__title">코드캠프 회원가입</span>
    </div>
    <div class="wrapper__body">
      <div class="wrapper__text">
        <input type="text" id="email" placeholder="이메일을 입력해 주세요.">
        <span class="errorMsg email">이메일이 올바르지 않습니다.</span>
        <input type="text" id="name" placeholder="이름을 입력해 주세요.">
        <span class="errorMsg name">이름이 올바르지 않습니다.</span>
        <input type="text" id="pw1" placeholder="비밀번호를 입력해 주세요.">
        <span class="errorMsg pw1">비밀번호를 입력해주세요.</span>
        <input type="text" id="pw2" placeholder="비밀번호를 다시 입력해 주세요.">
        <span class="errorMsg pw2">비밀번호를 입력해주세요.</span>
      </div>
      <div class="wrapper__phone" oninput="phone()">
        <input type="text" id="num1" maxlength="3"> -
        <input type="text" id="num2" maxlength="4"> -
        <input type="text" id="num3" maxlength="4">
      </div>
      <div class="wrapper__certification">
        <div class="cert__number">
          <span id="certNum">000000</span>
          <button class="chkBtn" disabled="true">인증번호 전송</button>
        </div>
        <div class="cert__time">
          <span id="certTimer">3:00</span>
          <button class="chkBtn" disabled="true">인증완료</button>
        </div>
      </div>
      <div class="wrapper__select">
        <div class="select__locale">
          <select id="locale">
            <option selected disabled>지역을 선택하세요</option>
            <option value="서울">서울</option>
            <option value="경기">경기</option>
            <option value="인천">인천</option>
          </select>
          <span class="errorMsg locale">지역을 선택해주세요.</span>
        </div>
        <div class="select__gender">
          <label for="woman">
            <input type="radio" name="gender" id="woman"> 여성
          </label>
          <label for="man">
            <input type="radio" name="gender" id="man"> 남성
          </label>
        </div>
        <span class="errorMsg gender">성별을 선택해주세요.</span>
      </div>
    </div>
    <div class="divideLine"></div>
    <div class="wrapper__check">
      <!-- <button class="submit" disabled="true">가입하기</button> -->
      <button class="submit">가입하기</button>
    </div>
  </div>
</body>
</html>

 

<css>

*{
  box-sizing: border-box;
  margin: 0;
}
html, body{
  width: 540px;
}
.chkBtn{
  width: 120px;
  height: 40px;
  border: 1px solid #D2D2D2;
  border-radius: 7px;
  font-size: 16px;
  font-weight: 400;
  color: #0068FF;
  background-color: #FFF;
  cursor: pointer;
}
.chkBtn.active {
  width: 120px;
  height: 40px;
  border: 1px solid #D2D2D2;
  border-radius: 7px;
  font-size: 16px;
  font-weight: 400;
  background-color: #0068FF;
  color: #FFF;
  cursor: pointer;
}
.errorMsg{
  width: 100%;
  color: red;
  font-size: 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  visibility: hidden;
}
.wrapper{
  width: 100%;
  height: 100%;
  padding: 60px 80px;
  border: 1px solid #AACDFF;
  border-radius: 20px;
  box-shadow: 7px 7px 39px rgba(0, 104, 255, .25);
}

.wrapper__header{
  width: 100%;
  font-size: 32px;
  font-weight: 700;
  color: #0068FF;
  padding-bottom: 60px;
}

.wrapper__body{
  width: 100%;
}
.wrapper__text > input{
  width: 100%;
  height: 60px;
  margin-top: 20px;
  font-size: 16px;
  font-weight: 400;
  border: 1px solid #D2D2D2;
  border-radius: 7px;
  padding: 18px;
}

.wrapper__phone{
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px 0;
}
.wrapper__phone > input{
  width: 100px;
  height: 40px;
  border: 1px solid #D2D2D2;
  border-radius: 7px;
}

.wrapper__certification {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: space-between;
}
#certNum, #certTimer{
  color: #0068FF;
  font-size: 18px;
  padding-right: 20px;
}
.cert__time{
  padding: 20px 0;
}

.wrapper__select{
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.select__locale{
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
#locale{
  width: 100%;
  height: 60px;
  border: 1px solid #D2D2D2;
  border-radius: 7px;
  color: #797979;
  font-size: 16px;
  font-weight: 400;
  padding: 18px;
}
.select__gender{
  width: 140px;
  display: flex;
  justify-content: space-between;
  padding-top: 30px;
}

.divideLine{
  width: 100%;
  border: 1px solid #E6E6E6;
  margin: 20px 0;
}

.wrapper__check{
  width: 100%;
  display: flex;
  justify-content: center;
}
.submit {
  width: 100%;
  height: 60px;
  font-size: 18px;
  font-weight: 400;
  color: #0068FF;
  background-color: #FFF;
  border: 1px solid #0068FF;
  border-radius: 7px;
}

 

<js>

const submit = document.querySelector('.submit'); // 가입하기
const numberChk = document.querySelector('.cert__number .chkBtn'); // 인증번호 전송
const timeChk = document.querySelector('.cert__time .chkBtn'); // 인증완료

let time = 180; // 180초, 인증 시간
let isStarted = false;

// email
const emailChk = () => {
  let email = document.getElementById('email').value;
  if(email.includes('@') === true){
    let isEmail = email.split('@')[1].includes('.');
    if(isEmail === false){
      document.querySelector('.errorMsg.email').style.visibility = 'visible';
      document.querySelector('.errorMsg.email').value = '';
      return false;
    } else {
      document.querySelector('.errorMsg.email').style.visibility = 'hidden';
      return true;
    }
  } else {
    document.querySelector('.errorMsg.email').style.visibility = 'visible';
    document.getElementById('email').value = '';
    return false;
  }
}

// name
const nameChk = () => {
  let name = document.getElementById('name').value;
  if(name.length === 0){
    document.querySelector('.errorMsg.name').style.visibility = 'visible';
    return false;
  } else {
    document.querySelector('.errorMsg.name').style.visibility = 'hidden';
    return true;
  }
}

// pw
const pwChk = () => {
  let pw1 = document.getElementById('pw1').value;
  let pw2 = document.getElementById('pw2').value;
  if(pw1 && pw2){
    if(pw1 === pw2){
      document.querySelector('.errorMsg.pw1').style.visibility = 'hidden';
      document.querySelector('.errorMsg.pw2').style.visibility = 'hidden';
      return true;
    } else {
      document.querySelector('.errorMsg.pw1').style.visibility = 'visible';
      document.querySelector('.errorMsg.pw2').style.visibility = 'visible';
      document.querySelector('.errorMsg.pw1').innerHTML = '비밀번호가 일치하지 않습니다.'
      document.querySelector('.errorMsg.pw2').innerHTML = '비밀번호가 일치하지 않습니다.'
      return false;
    }
  } else {
    document.querySelector('.errorMsg.pw1').style.visibility = 'visible';
    document.querySelector('.errorMsg.pw2').style.visibility = 'visible';
    return false;
  }
}

// phone
const phone = () => {
  let num1 = document.getElementById('num1').value;
  let num2 = document.getElementById('num2').value;
  let num3 = document.getElementById('num3').value;
  if(num1.length === 3) {
    document.getElementById('num2').focus();
    if(num2.length === 4) {
      document.getElementById('num3').focus();
    }
  }
  if(num1.length === 3 && num2.length === 4 && num3.length === 4){
    numberChk.classList.add('active');
    certification();
  }
}

const certification = () => {
  // 인증번호
  numberChk.disabled = false;
  numberChk.addEventListener('click', e => {
    let randomNumber = String(Math.trunc(Math.random() * 1000000)).padStart(6, '0')
    document.getElementById('certNum').innerText = randomNumber;

    // 타이머
    if(isStarted === false){
      isStarted = true;
      timeChk.disabled = false;
      let timer = setInterval(() => {
        if(time >= 0){
          let min = Math.trunc(time / 60);
          let sec = String(time % 60).padStart(2,'0');
          document.getElementById('certTimer').innerText = `${min}:${sec}`;
          time--;
        } else {
          clearTime(timer);
        }
      }, 100)
      timeChk.addEventListener('click', e => {
        if(time >= 0){
          alert('인증이 완료 되었습니다.');
          clearTime(timer);
          submit.disabled = false;
        }
      })
    }
  })
}

const clearTime = (timer) => {
  timeChk.classList.remove('active');
  numberChk.classList.remove('active');
  document.getElementById('certNum').innerText = '000000';
  document.getElementById('certTimer').innerText = '0:00';
  timeChk.disabled = true;
  numberChk.disabled = true;
  isStarted = false;
  clearInterval(timer);
}

const checkValidation = () => {
  emailChk();
  nameChk();
  pwChk();
  if(emailChk() && nameChk() && pwChk()) {
    return true;
  } else {
    return false;
  }
}

// 검증
submit.addEventListener('click', e => {
  checkValidation();
  if(checkValidation()){
    alert('코드캠프 가입을 축하합니다.');
  }
});

답변 1

0

노원두님의 프로필 이미지
노원두
지식공유자

안녕하세요! 지루한 들소님!

전체적으로 코드를 깔끔하게 작성하신 것이 눈에 보입니다!^^
앞으로도 계속 이렇게 만들어 주세요!

실제로 실무에서 여럿이 협업을 하다보면, 코드가 깔끔해야 레고 블록 조립하듯이 붙였다 떼었다 하는 느낌으로 유지보수 하실 수 있어요!

 

코드를 한 곳에 모두 작성하는 것이 아닌, emailChk(), pwChk() 등의 형태로 함수로 분리하여 읽기가 쉬워지는 것 같네요!^^
좋은 부분이라고 생각되며, 이런 코드가 많아서 내가 아닌 다른 사람이 굳이 이해하려고 하지 않고, 눈으로만 보더라도 읽힌다면 그게 바로 좋은 코드라고 할 수 있겠죠?!^^

* 참고) "이건 어려운 작업이라, 아무나 못 하는거야! 나만 이해할 수 있어! 모르면 나한테 물어봐!"
=> 대부분의 이런 경우는 사실 좋은 코드라고 보기는 어려울 것 같네요!^^

 

뒷 수업에서 더 좋은 코드를 만들기 위한 리팩토링(다시 더 좋은 방법으로 만들기) 방법들을 많이 배우니, 앞으로도 이렇게 꾸준히 학습해요!^^

개발하는 알파카님의 프로필 이미지
개발하는 알파카

작성한 질문수

질문하기