티스토리 뷰

프로그래머스에서 'roto'님이 진행하는 '[스터디/9기] 프론트엔드 개발을 위한 자바스크립트(feat. VanillaJS)'를 수강하며 배운 내용을 정리한 글입니다.

 

2주차 미션

미션 내용

  • 할 일 추가, 삭제, 완료 기능 추가
  • InputTodo 컴포넌트화
  • TodoCount 컴포넌트 추가
  • Event Delegate
  • 커스텀 이벤트
  • localStorage에 할 일 저장

구현 결과

Mission2 구현결과

 

미션 진행하며 배운 것

  • 할 일 삭제 시 삭제할 할 일이 todoData 중 어떤 것이지 알기 위해 데이터 속성을 이용했다.

    this.createTodoHTMLString = ({ text, isCompleted }, index) => {
      return `
        <li data-id=${index}>
          ...
         <button>Todo 삭제</button>
        </li>
      `
    }
    this.deleteTodoEvent = $target.addEventListener('click', (e) => {
      if (e.target.tagName === 'BUTTON') {
        this.doDeleteTodo(e.target.parentNode.dataset.id)
      }
    })

    data-로 시작하는 형식으로 데이터를 넣으면, data-를 뺀 나머지 이름으로 dataset에서 값을 가져올 수 있다. 이 방법으로 삭제 버튼이 눌린 요소의 인덱스 값을 가져와 해당 데이터를 todoData에서 삭제하였다.

 

코드 리뷰

  • todoData를 조작하는 모든 일(할 일을 추가하거나 삭제하는 등)은 App컴포넌트에서 하며, 하위 컴포넌트에 필요한 일을 하는 함수를 파라미터로 전달해 주어야 한다.
    export default class App {
        constructor({ $app }) {
            this.todoInput = new TodoInput({
                $app: this.$app,
                onAddTodo: this.onAddTodo.bind(this),
            })
        }
    
        this.onAddTodo = (newTodoText) => {
            // 할 일 추가 로직
        }
    }
  • 컴포넌트가 아닌 js파일의 이름은 대문자로 시작하지 않는다.(예. validation.js)
  • 이벤트 핸들러 함수 앞에는 on접두어를 붙인다. 일종의 컨벤션이다.(예. onAddTodo())
  • 하나의 요소만 틀려도 오류를 내는 경우 some을 사용한다. 다만, some은 빈 배열에서 사용 시 false를 반환하므로 원하지 않는다며 따로 처리가 필요하다.

    export const checkTypes = (state, checkCallback) => {
      if (state.length > 0 && !state.some(checkCallback)) {
        throw new Error('올바르지 않은 데이터 형식')
      }
    }

 

세션 내용 정리

변수와 상태를 immutable 하게 관리

새로운 값이 필요하면 원본을 변경하지 않고 새로운 레퍼런스를 만들어 교체한다. 매번 새로운 변수를 만드는 것은 메모리 낭비로 여겨질 수 있으나 요즘은 읽기 쉬운 코드를 작성하고 예측 가능한 동작을 만드는 것을 더 중요하게 생각한다.

// 더이상 참조되지 않으면 브라우저가 적절한 시기에 가비지 컬렉팅
const prevObject = {
    name: 'sunny',
    age: 25
}

// 새로 계산해서 만든 객체로 대체함
const newObject = {
    ...prevObject,
    age: 26
}

특히 React 같은 경우 state를 업데이트할 때 해당 객체의 레퍼런스가 서로 다른지 확인한다. 따라서 상태를 immutable하지 않으면 화면이 렌더링 되지 않을 수 있다.

 

상수

상수가 함수 내에 존재하면 해당 함수가 호출될 때마다 상수를 만든다. 함수의 동작이 종료되면 해당 상수에 대한 메모리도 해제되겠지만 매번 함수가 호출될 때마다 이런 동작을 반복하는 것은 비효율적인 일이다. 따라서 상수는 파일 최상단에 두거나 따로 파일을 만들어 관리해서 메모리에 한 번만 올라가도록 한다.

 

localStorage는 try-catch로 묶기

JSON.parse() 중 오류가 나거나, localStorage.setItem() 실행 시 localStorage에 저장 가능한 용량이 가득 차면 런타임이 중지된다. 따라서 이런 작업을 하는 경우 try-catch를 사용하여 적절하게 오류 처리를 해줘야 한다.

 

render()는 파라미터를 받지 않음

이 스터디에서 진행하는 미션은 render함수가 파라미터를 받지 않고 해당 컴포넌트의 상태 변화에 따라 화면을 갱신하는 것을 규칙으로 한다. render함수가 파라미터를 받는 순간 화면 갱신이 데이터의 변화에 따른 것이라고 보장할 수 없게 되기 때문이다.

 

객체로 파라미터 넘기기

// App.js
export default function App() {
    ...
    this.todoInput = new TodoInput({
        $target: $app, 
        onAddTodo: this.onAddTodo.bind(this)
    })
    ...
    this.onAddTodo = (todoText) => {...}
}
// TodoInput.js
export default function TodoInput({ $target, onAddTodo }) {
    ...
}

위의 예시에서 파라미터를 객체로 넘기지 않으면 파라미터를 정해진 순서대로 집어넣어야 한다. 하나나 두 개의 파라미터를 넘길 때에는 별 문제가 없을 수 있지만 넘기는 개수나 형식이 많아질 수 록 컴포넌트를 만드는 쪽 코드에서 파라미터에 대한 정보를 한눈에 확인하기 어려워진다.

파라미터를 객체로 넘기면 컴포넌트를 만들 때 파라미터의 순서를 기억하지 않아도 된다. 또한 각 인수가 무엇을 뜻하는지 key를 통해 식별되기 때문에 코드의 가독성이 높아진다.

 

Semantic HTML 구조

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <header>
    <nav>
      <ul>
        <li>Home</li>
        <li>Todo</li>
        <li>Post</li>
      </ul>
    </nav>
  </header>

  <main>
    <h1>Todo List</h1>

    <form action="">
      <label for="todo-input">할 일을 입력하세요</label>
      <input id="todo-input" type="text" />

      <fieldset>
        <legend>생년월일</legend>
        <select>
          <option>년</option>
        </select>
        <select>
          <option>월</option>
        </select>
        <select>
          <option>일</option>
        </select>
      </fieldset>

      <button type="submit">제출</button>
    </form>

    <section>
      <h2>Todo List를 보여줄게</h2>
      <ul>
        <li>JS 공부하기</li>
        <li>스터디 미션 하기</li>
      </ul>

      <figure>
        <img src="./img/cat.png" alt="고양이" />
        <figcaption> 이미지에 대한 캡션</figcaption>
      </figure>
    </section>

    <article>
      <h2>기사 제목</h2>
      <section>머리말</section>
      <section>본문</section>
      <section>맺음말</section>
    </article>

    <aside>
      <!-- 사이드바에 주로 사용 -->
    </aside>
  </main>

  <footer></footer>
</body>
</html>
  • <html lang="en"> 스크린리더기가 해당 페이지를 읽을 때 해당 페이지가 lang에 설정된 언어로 구성되어 있다고 생각함
  • <h1> 이 페이지를 표현할 수 있는 타이틀이며 한 페이지에 하나만 사용함
  • id는 중복되면 안 됨

  • <label><input>과 연결하면 <label>을 클릭했을 때 <input>으로 포커스를 줄 수 있음
  • <section>은 비슷한 주제를 가진 내용의 구역을 나눌 때 사용하며 안에 반드시 <h>태그를 포함해야 함
  • <img>태그에는 스크린리더나 크롤러가 해당 이미지가 무엇인지 파악하기 위해 필요하므로 반드시 alt프로퍼티를 추가해야 함. 스크린리더가 <img>태그를 읽을 때 alt내용 + '이미지'로 읽으므로 alt="고양이 이미지" 이런 식으로 작성하지 말 것
  • <button>태그도 스크린리더가 읽을 때 <img>와 같으므로 <button>확인버튼</button> 이런식으로 작성하지 말 것
  • <article>은 기사 내용 등 독립적으로 배포될 수 있는 경우에 사용

 

 


 

참고사이트

 

 

댓글