[코드 리뷰] 3주차 Quiz 구현하기

Soozynn·2021년 8월 8일
0

[프렙] 코드 리뷰

목록 보기
3/6

<피드백>

  • 단어 찾기 기능 ctrl + F => 과제 제출 전 한번 더 확인하기

  • 불필요하게 classList.add만 사용하지 않고, classList.remove도 응용하기

  • 변수명, 함수명 네이밍 유의

  • truthy와 falsy에 정확한 개념 확립하고 조건문 더 간결히 사용하기

  • 불필요한 공백 주의

  • innerHTML, innerText 그리고 textContent의 차이를 알고 그에 맞게 사용하기

  • 변수명 정확한 네이밍 (매개변수로 전달하는 변수 또한)

  • 이벤트 핸들러 함수명 ( handle + DOM element 이름 + 이벤트 명) => handleChoiceClick

  • 올바른 세미콜론의 사용(함수 선언문, if문, 반복문에서 사용x) 과 따옴표 뒤에 공백 추가하기

  • 상수 네이밍 => 가독성 위해, 위치 최상단에 배치하기

  • git 추가를 할 때 commit 은 추가한 내용에 대해 적어 줄 것

  • 동적으로 코드를 짠다는 것은 무슨뜻일까?
    => 자바스크립트 상에서

  • 함수 네이밍 동사 + 명사 좀 잘 지키자.. 뭘 행하는지부터 알려주는 거니까 동사가 앞으로

  • 이벤트를 추가하기 위한 함수handle + 명사 + 동사 로 네이밍



자바스크립트 상에서 css style을 변경할 경우)
  • classList를 통한 스타일 변경이 아닌, 직접적으로 style을 변경할 경우 => 인라인 스타일이 적용됨
    인라인 스타일의 경우 우선 순위가 높아서 스타일 적용에 있어 문제가 발생될 가능성이 있으므로 지양

  • css와 자바스크립트 각자의 역할이 분명하게 존재하기에 서로 섞이지 않게 주의할 것

//직접적인 style 수정은 지양❌
document.querySelector(".quiz").style.display = "block";

// 옳은 예시 ✅
choices.classList.add("hide");

<css 적용 우선순위>

  1. 속성 값 뒤에 !important 를 붙인 속성
  2. HTML에서 style을 직접 지정한 속성
  3. #id 로 지정한 속성
  4. .클래스, :추상클래스 로 지정한 속성
  5. 태그이름 으로 지정한 속성
  6. 상위 객체에 의해 상속된 속성


1) querySelectorAll를 통해 잡은 NodeList 중 일부 요소에만 classList를 적용하는 방법

=> index 접근을 통한 classList를 통해 변경가능

const choices = document.querySelectorAll(".choice"); => 4개의 <li>

// 수정 전 지양하는 방식❌

	if (choicesLength === 2) {
          choices[0].innerHTML = choice[0] ;
          choices[1].innerHTML = choice[1];
          choices[2].style.display = "none";
          choices[3].style.display = "none";
        } else {
          choices[2].style.display = "flex";
          choices[3].style.display = "flex";
          choices[0].innerHTML = choice[0];
          choices[1].innerHTML = choice[1];
          choices[2].innerHTML = choice[2];
          choices[3].innerHTML = choice[3];
        }
        
        
        
        
 //밑은 수정한 코드 ✅
 // index를 이용하여 classList 사용
 
	 if (choicesLength === 2) {
          choices[0].textContent = choice[0] ;
          choices[1].textContent = choice[1];
          choices[2].classList.remove("show");
          choices[3].classList.remove("show");
          choices[2].classList.add("hide");
          choices[3].classList.add("hide");
        } else {
          choices[2].classList.add("flex");
          choices[3].classList.add("flex");
          choices[0].textContent = choice[0];
          choices[1].textContent = choice[1];
          choices[2].textContent = choice[2];
          choices[3].textContent = choice[3];
        }
        
        
        
        
// 위의 코드는 해당 조건이 안될 시에 버그가 생길 수 있으므로
// 밑에 코드처럼 동적으로 코드를 짜서
// hide, show와 같은 class를 부여하지 않고 필요한 DOM 요소를 추가할 수 있어 더 효율적

     function appendQuizChoices() {
      data[dataIndex].choices.forEach(function(Choice) {
        const quizChoiceDiv = document.createElement("div");
        const quizChoicesP = document.createElement("p");
        let pTagText = document.createTextNode(Choice);

        quizChoicesP.appendChild(pTagText);
        quizChoiceDiv.appendChild(quizChoicesP);
        quizChoiceDiv.classList.add("quiz-choices");
        $quizChoices.appendChild(quizChoiceDiv);
      });
    }


2) 변수명

변수명 함수명의 경우 항상 내가 아닌 다른 분들이 코드를 확인했을 때
해당 변수명, 함수명을 통해 어떤 것인지, 어떤 역할을 하는 것인지 (무엇을 하는지) 인지할 수 있는 네이밍이 중요

=> 매개변수로 전달하는 인자 또한 역할에 따라 네이밍

// 의미없는 네이밍 ❌
 showQuiz(data);
 function showQuiz(text) { 코드 내용 }; 
 
 // text 보단 quizList 같은 변수명이 더 적절 ✅
  function showQuiz(quizList) { 코드 내용 };

.


3) 조건문 Truthy / Falsey

// 지양하는 방식 ❌
 if (text[k]["code"] !== null)
 
// 수정한 조건문 ✅
if (text[k]["code"])

=> text[k]["code"] !== null 은 곧, text[k]["code"] 의 값이 Truthy 값일 때를 가리키므로 위와 같이 써주는 것이 더 간결하며 가독성이 좋음

비교연산자를 쓰기 전에 true, false값을 생각해서 조건문을 작성
가독성을 위해 축약해서 작성하는 습관 기르기.

예를 들어,

// 지양하는 코드 ❌
if (person === undefined || person === null)

// 지향하는 코드 ✅
 if (!person) 
  • Falsy 한 값 앞에 느낌표를 붙여주면 true
  • Truthy 한 값 앞에 느낌표를 붙여주면 false

이처럼
true 일 경우) 불필요한 비교연산자 사용없이 true 값만
false일 경우) ! 를 이용하면 조건문을 더 간결하게 표현가능, 상황에 따라 !! 또한 사용가능



4) innerHTML, innerText 그리고 textContent의 차이

예제 1)
<div id='my_div'>
  안녕하세요?     만나서 반가워요.
  <span style='display:none'>숨겨진 텍스트</span>
  
  // 이를 자바스크립트에서 <div id='my_div'> 요소를 잡아 변수로 선언한 뒤
  // 각각 경고창으로 호출 할 경우,
예제 2)
어쩌구.innerHTML  = "<h1>수박</h1>";

  • innerHTML
    Element의 속성으로, 해당 Element의 HTML, XML을 읽어오거나, 설정할 수 있다.

    👉 즉, 작성한 그대로 텍스트를 읽고, 이름 그대로 HTML
    "마크업"이 가능
    But, 마크업이 포함된 콘텐츠를 추가하는 것은 크로스 스크립팅 공격(XSS: Cross-Site Scripting Attacks)에 취약하다

예제 1)
예제 2)

수박


- innerText

Element의 속성으로, 해당 Element 내에서 사용자에게 '보여지는' 텍스트 값을 읽어온다.

👉 연속되는 공백은 하나의 공백으로만 처리하며, html 태그를 읽고 판별할 수 있음
innerText 프로퍼티를 사용하여도 요소의 '텍스트 콘텐츠' 에만 접근할 수 있다.

< 하지만 아래의 이유로 사용하지 않는 것이 좋다.>

  • 비표준이다.

  • CSS에 순종적이다. 예를 들어 CSS에 의해 비표시(visibility: hidden;)로 지정되어 있다면 텍스트가 반환되지 않는다.

  • CSS를 고려해야 하므로 textContent 프로퍼티보다 느리다

    예제 1) <span style='display:none'>숨겨진 텍스트</span> 를 읽고 경고창에 띄우지 않음

    예제 2) 태그와 같이 <h1>수박</h1> 그대로 텍스트에 출력


  • textContent
    Node의 속성으로, innetText와는 달리 해상 노드가 가지고 있는 텍스트 값 을 그대로 읽음

    👉 태그가 어떤 태그이던지간에 상관없이 모든 태그 안 텍스트만 작성한 그대로 다 읽어옴
    성능적인 측면에서 주로 쓰임

    예제 1)

    예제 2)
    innerText와 동일하게 <h1>수박</h1> 그대로 텍스트에 출력

<Node.textContent><HTMLElement.innerText> 차이

  • textContent<script>와 <style> 요소를 포함한 모든 요소의 콘텐츠를 가져오고, 노드의 모든 요소를 반환한다
  • 반면 innerText는 "사람이 읽을 수 있는" 요소만 처리하고 textContent와 달리 스타일링을 고려하며, "숨겨진" 요소의 텍스트는 반환하지 않는다

<Node.textContent><innerHTML>과의 차이

  • Element.innerHTML는 이름 그대로 HTML을 반환한다 간혹 innerHTML을 사용해 요소의 텍스트를 가져오거나 쓰는 경우가 있지만, HTML로 분석할 필요가 없다는 점에서 textContent의 성능이 더 좋다

🤔 innerHTML을 현업에서 사용하는 경우는 있더라도 극히 드뭅니다.
이유는 말씀해주신 것처럼 보안과 성능 이슈가 맞습니다.
제 개인적인 생각을 말씀드리자면 제품이라는 것은 고객에게 다가갈 때
저희가 만든 어플리케이션, 웹뿐만 아니라 거기서 파생되는 고객이 느끼는 감정,
또는 불편 사항을 최소화 시키는 그런 무형적인 모든 것을 포함시키는 것이라고 생각합니다.
innerHTML을 사용하면 저희가 코드를 적을 때 한군데서 모아볼 수 있다는 편리함이 있지만
그 편리함을 위해서 로딩 속도가 느려지며 보안 이슈가 생길 가능성이 생기므로 유저의 UX 측면에서 문제가 생긴다고 생각합니다.

프로그래머는 프로그램이 잘 동작하는 로직을 생각하는 사람이기도 하지만 자신이 만든 제품이 유저에게 다가갔을 때 어떤 식으로 와닿을지 어떤식으로 작동하면 더 편리하게 좋은 UX를 가지며 사용하게될지 고민해야하는 사람이기도하다고 생각합니다.


결론) innerHTML과 insertAdjacentHTML()은 크로스 스크립팅 공격(XSS: Cross-Site Scripting Attacks)에 취약하다. 따라서 untrusted data의 경우, 주의하여야 한다.


👉 텍스트를 추가 또는 변경시에는 textContent, 새로운 요소의 추가 또는 삭제시에는 DOM 조작 방식을 사용하도록 한다.






5) class name 카멜이 아닌 케밥케이스로 작성하기

   <div class="numberOfQuiz">
        <span class="totalNumber"></span>
        <span class="correctNumber"></span>
      </div>
  
  
// 아래는 수정 후 ✅
   <div class="number-quiz">
        <span class="total-number"></span>
        <span class="correct-number"></span>
      </div>



6) css에서 모든 클래스에 각각 .hide / .show 추가하는 것보단 따로 선언하여 중복을 줄일 수 있음

section.quiz.hide {
display: none;
}



//아래와 같이 사용하기 ✅
.hide {
display: none;
}

.show {
display: block;
}



7) 상수 네이밍

// 21은 내가 아닌 다른 분들이 코드를 볼 경우
// 21이라는 숫자가 무슨 기준인지 무엇을 의미하는지를 단번에 알 수 없기에 지양 ❌

 if (text.length === 21) {
          nextButton.classList.remove("show");
          window.setTimeout(restartGame,2000);
        }


// 따로 선언하여 변수명을 통해 그 의미를 보여지는 식으로 코드를 작성하는 것을 지향 ✅
const TOTAL_QUIZ_LENGTH = 21;

 if (text.length === TOTAL_QUIZ_LENGTH) {
          nextButton.classList.remove("show");
          window.setTimeout(restartGame,2000);
        }


classList.remove,

classList를 이용해서 hide와 show 클래스를 만들어 주었을 때 각각 계속 add로 두 가지 다 만들 필요 없이 처음 한가지 것만 만들어 준 상태로 또 추가하지말고 다시 지우는 기능인 remove를 이용하여 굳이 두 번 작성 할 필요 없도록.

출처) MDN

https://hianna.tistory.com/483

0개의 댓글