본문 바로가기

Project

Project 마무리 - MBTI 설문 사이트 구현

728x90
반응형

1) 마무리

    1-1) 개요

    1-2) 결과물

    1-3) 진행 중 이슈 및 해결

    1-4) 후기

 

 

 

 

 

1) 마무리

1-1) 개요

2022년 11월 28일부터 12월 2일까지 5일간 진행한 MBTI 설문 사이트 구현 프로젝트!

처음에는 클론 코딩을 목적으로 구현해보려 한 프로젝트가 막상 진행하려고 보니 지금까지의

내 자신이 얼마나 HTML, CSS, Javascript에 숙달되었는지 확인하고 싶어졌다.

이에 함께 프로젝트를 진행한 팀원 분과 논의하여 대략적인 디자인과 글자 폰트 크기 등만

원본 사이트를 참고하고 나머지는 우리가 논의하여 구현해나가기로 결정했다.

이렇게 설렘과 긴장을 안고 첫날 To Do List를 작성하는데 처음에는 우리가 작성한 List대로

프로젝트의 모든 상황이 순조롭게 흘러갈 것만 같았다.

하지만 이러한 기대는 불과 하루 만에 산산조각 났고...

우리는 매일 열심히 To DoList 내용을 구성하였음에도 불구하고 구현 과정에서

새로운 이슈가 발생하기도, 또는 처음 구현방식을 시도하는 과정에서

장애물을 만나 해당 구현계획을 수정하기도 하였다.

이처럼 아직은 우당탕탕 개발을 진행하는 초보 개발자지만 이번 5일 간의 예기치 못한 어려움과

이를 극복하기 위해 노력한 나의 경험을 다른 분들께도 소개하고자

이렇게 회고를 시작하게 되었다.

 

 

1-2) 결과물

설문 문항별 선택지 체크 여부 판별 및 미선택 항목으로의 이동 기능, 무한루프 슬라이드/자동 슬라이드 변환, 스크롤 지점에 따른 이미지 애니메이션 등 최대한 설문과 관련된 다양한 기능들을 구현하려 노력했다.

 

 

1-3) 진행 중 이슈 및 해결

(1) 설문 확장성 이슈

처음에는 hard coding으로 각 설문 문항별 선택지 버튼에 대해 일일이 체크 표시를 줌(className 활용)으로써

이를 기반으로 각 선택지 체크에 따른 포인트(비동의 ~ 동의에 이르는 7개의 선택지에 순서대로 1 ~ 7 point 부여)를

총 합산하여 특정 점수급간에 들어가면 해당 MBTI 유형 결과를 반환해주는 식으로 Javascript 코드를 구성하였다.

 

하지만 이렇게 하면 추후 설문 문항을 늘리거나 줄이게 되었을 시 HTML과 더불어 Javascript 역시 귀찮게

매번 계속 바꿔줘야 하는 확장성 문제가 발생한다는 사실을 인지하였다.

 

이에 팀원 분과 함께 Code Refactoring 작업을 진행(for 문, if 문 활용)하여 HTML의 변화에

유동적으로 맞춰 변화할 수 있게 코드를 구성하였다.

예를 들어 아래와 같이 각 설문 문항별 선택지 체크여부를 판별하는 함수를 for 문과 if 문, 그리고 DOM 요소를 적절히 활용하여

직관적으로 만듦과 동시에 확장성을 고려한 반복문 형태로 수정하였다.

refactoring 이전 버전 :

https://codetraveler-hwang.tistory.com/entry/Project-5%EC%9D%BC%EC%B0%A8-MBTI-%EC%84%A4%EB%AC%B8-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B5%AC%ED%98%842

 

refactoring 이후 버전 : 

https://github.com/sangbeomhwang/mbti_renewal

function checkedHandler() {
  answer1.forEach((node1) => {
    if (node1.checked) {
      questions[0].className = "checked";
      arr1.push(node1.value);
      if (arr1.length > 1) {
        arr1.splice(0, 1);
      }
    }
  });

  answer2.forEach((node2) => {
    if (node2.checked) {
      questions[1].className = "checked";
      arr2.push(node2.value);
      if (arr2.length > 1) {
        arr2.splice(0, 1);
      }
    }
  });

  answer3.forEach((node3) => {
    if (node3.checked) {
      questions[2].className = "checked";
      arr3.push(node3.value);
      if (arr3.length > 1) {
        arr3.splice(0, 1);
      }
    }
  });

  answer4.forEach((node4) => {
    if (node4.checked) {
      questions[3].className = "checked";
      arr4.push(node4.value);
      if (arr4.length > 1) {
        arr4.splice(0, 1);
      }
    }
  });

  answer5.forEach((node5) => {
    if (node5.checked) {
      questions[4].className = "checked";
      arr5.push(node5.value);
      if (arr5.length > 1) {
        arr5.splice(0, 1);
      }
    }
  });

  answer6.forEach((node6) => {
    if (node6.checked) {
      questions[5].className = "checked";
      arr6.push(node6.value);
      if (arr6.length > 1) {
        arr6.splice(0, 1);
      }
    }
  });

  answer7.forEach((node7) => {
    if (node7.checked) {
      questions[6].className = "checked";
      arr7.push(node7.value);
      if (arr7.length > 1) {
        arr7.splice(0, 1);
      }
    }
  });

  answer8.forEach((node8) => {
    if (node8.checked) {
      questions[7].className = "checked";
      arr8.push(node8.value);
      if (arr8.length > 1) {
        arr8.splice(0, 1);
      }
    }
  });

  answer9.forEach((node9) => {
    if (node9.checked) {
      questions[8].className = "checked";
      arr9.push(node9.value);
      if (arr9.length > 1) {
        arr9.splice(0, 1);
      }
    }
  });

  answer10.forEach((node10) => {
    if (node10.checked) {
      questions[9].className = "checked";
      arr10.push(node10.value);
      if (arr10.length > 1) {
        arr10.splice(0, 1);
      }
    }
  });

  answer11.forEach((node11) => {
    if (node11.checked) {
      questions[10].className = "checked";
      arr11.push(node11.value);
      if (arr11.length > 1) {
        arr11.splice(0, 1);
      }
    }
  });

  answer12.forEach((node12) => {
    if (node12.checked) {
      questions[11].className = "checked";
      arr12.push(node12.value);
      if (arr12.length > 1) {
        arr12.splice(0, 1);
      }
    }
  });
  }

 

function checkedHandler(e) {
  target = e.target.id;
  index_l = target.indexOf("l");
  index_bar = target.indexOf("-");
  index_survey = target.slice(index_l + 1, index_bar);
  survey1[index_survey - 1] = Number(e.target.value);
  if (questions.length > index_survey) {
    questions[index_survey].className = "checked";
  }
}

 

 

(2) 설문 선택지 버튼 디자인 이슈

각 설문 선택지별 input 버튼(radio: 하나를 클릭하면 기존에 선택했던 버튼이 자동으로 풀리게 설정하기 위함)에 대해

시각적으로 다른 포인트를 주기 위해 CSS 속성(width, height)을 활용해 각 선택지마다 크기를 다르게 설정하였다.

이와 더불어 테두리의 색상 포인트를 주기 위해 label 태그를 활용하여 기존 input 태그는 "display: none;"을 사용하여

가려두고 label 태그를 디자인하여 새로 꾸민 선택지 버튼을 클릭했을 시 input 박스가 체크가 되도록 설정하려 했다.

 

하지만 이 과정에서 label 태그를 활용한 새로운 디자인 선택지 버튼은 구현했지만 클릭 시 가려놓았던 input 박스 자체가

사라져 해당 자리가 비어버림에 따라 자동적으로 선택지 버튼 간의 간격이 좁혀져 전체적으로 위치가 어그러지는 이슈가

발생하였다.

이는 각 설문 문항별 label 태그와 input 태그를 한 줄로 쭈욱 늘어놓았기에 발생한 이슈이기에 넓이조절 등의

방법이 적용되지 않아 우리는 새로운 디자인 방식을 고민해보기로 하였다.

 

우리는 구현하려는 프로젝트 로직 상 핵심이 되는 선택지별 디자인 부분을 꼭 label 태그를 새로 만들어서 구현해야 할까라는

의문에서 시작해 고민을 거쳐 그럼 input 태그 자체에 색상 포인트를 주면 되지 않을까라는 생각에 도달했다.

이에 input 태그의 색상 포인트를 줄 수 있는 속성이 없을까 찾던 중 "accent-color" 속성을 발견하게 되었고,

이를 활용해 아래와 같이 동의, 비동의, 그리고 중간 선택지에 대해 각각 다른 색상을 매겨 선택지별 색상 포인트를 부여하자는

구현 목적을 달성하였다.

#contents > #question > ul > li > #radioSelect > #options > .agree > input {
  accent-color: #33a474;
}

#contents
  > #question
  > ul
  > li
  > #radioSelect
  > #options
  > .high_agree
  > input {
  width: 60px;
  height: 60px;
}

#contents
  > #question
  > ul
  > li
  > #radioSelect
  > #options
  > .half_agree
  > input {
  width: 50px;
  height: 50px;
}

#contents > #question > ul > li > #radioSelect > #options > .low_agree > input {
  width: 40px;
  height: 40px;
}

#contents > #question > ul > li > #radioSelect > #options > .neutral > input {
  accent-color: #9b9faa;
  width: 30px;
  height: 30px;
}

#contents > #question > ul > li > #radioSelect > #options > .disagree > input {
  accent-color: #88619a;
}

#contents
  > #question
  > ul
  > li
  > #radioSelect
  > #options
  > .high_disagree
  > input {
  width: 60px;
  height: 60px;
}

#contents
  > #question
  > ul
  > li
  > #radioSelect
  > #options
  > .half_disagree
  > input {
  width: 50px;
  height: 50px;
}

#contents
  > #question
  > ul
  > li
  > #radioSelect
  > #options
  > .low_disagree
  > input {
  width: 40px;
  height: 40px;
}

 

 

(3) blur 처리로 인한 설문 문항 view 이슈

처음 blur 처리의 경우, CSS 파일 상 설문 문항 영역에 해당하는 부분에 "opacity: 0.5;"를 줘서 비교적

쉽게 구현에 성공하였다.

#contents > #question > #statement > .unchecked {
  opacity: 0.5;
}

 

문제는 그 다음이었는데 위와 같이 설문 문항 전체에 blur 처리를 주는 것은 어렵지 않았지만

선택지가 선택된 설문 문항에 대해 선택적으로 blur 처리가 풀리게 하는 것은 고민이 좀 필요했다.

이에 여러 고민을 거쳐 설문 페이지별 첫 설문 항목에 대해서는 HTML 상 class 속성값을 "checked"로 설정하여

항상 자동적으로 blur 처리가 풀려있게 해놓고, 현재 설문 문항에 대해 선택지를 선택할 시 다음 설문 문항의 class 속성값이

"unchecked"에서 "checked"로 바뀌면서 자동으로 다음에 체크할 설문 문항에 대해 blur 처리가 풀리게 코드를 작성하였다.

function checkedHandler(e) {
  target = e.target.id;
  index_l = target.indexOf("l");
  index_bar = target.indexOf("-");
  index_survey = target.slice(index_l + 1, index_bar);
  survey1[index_survey - 1] = Number(e.target.value);
  if (questions.length > index_survey) {
    questions[index_survey].className = "checked";
  }
}

 

물론 이렇게 해도 여전히 중간에 설문 문항을 건너띄고 다음 설문 문항을 선택할 시 선택지를 선택했음에도 여전히

blur 처리가 적용된다는 이슈가 남아있다.

 

이에 대해서는 추후 좀 더 코드를 보완할 예정이다.

 

 

1-4) 후기

이번 프로젝트를 계기로 내가 HTML, CSS, Javascript에서 어느 부분이

부족한지 그리고 기존에 코드로 직접 구현해보지 않고 머릿속으로만

이러면 되겠지하고 넘겼던 생각들이 실제로는 어떤 이슈를

발생시킬 수 있는지 한 번 더 눈으로 실감하게 되었다.

현재 나는 어떤 기능을 구현함에 있어 해당 기능에 적용될

case를 분류해내는 영역까지는 도달했지만

아직 이를 효과적으로 분류하고 그 중에서 특정 예외처리를

수행하는 것에 있어서는 미숙한 부분이 있는 것으로

판단되었다.

이에 앞으로도 다양한 아이디어를 직접 구현해봄으로써

여러 이슈를 맞닥뜨려 이후 본격적으로 학습을 진행할

Node.js에 있어 꼭 필요한 예외처리 역량을 증진시키기

위해 열심히 노력할 계획이다.

그럼 지금까지 글을 읽어주신 모든 개발자 분들, 혹은 개발에 관심이 있는

학생 분들 모두 파이팅해서 각자 목표로 하는 바를 꼭 이루시길 바래요~