티스토리 뷰

오늘은 내가 작성했던 파일을 하나씩 뜯어보는 TIL을 작성해 보고자 한다.

당연히 스스로 만들어낸 기능이 아니므로 뜯어서 공부해 보아야 한다!

 

간단하게 기능을 설명해 보자면,

영화 상세페이지마다 리뷰를 작성할 수 있고 리뷰 목록은 클릭된 영화에 해당되는 리뷰 목록만 볼 수 있어야 한다.

리뷰를 작성하는 기능은 modal을 이용했으며, 작성된 리뷰는 localstorage에 저장되도록 했다.

 

HTML과 CSS는 참고할 수 있도록 가장 하단에 작성되어 있으며 

html은 새로 알게된 부분만 작성하고, css는 함수가 필요했던 부분만 각 js파일 설명 시 포함해 작성

 

 

- HTML id 값을 모두 class component로 만들기 -

 

우선 html에서 id 값을 모두 class component로 만들었다.

 

처음에는 id 값을 불러올 때마다 상수로 지정했는데 이름이 겹치니 여간 골치아픈게 아니었다.

그래서 처음 id 값을 불러올때 class component 로 만들자였다.

 

addEventListener 내에서 class component를 인스턴스화 시켰다.

그리고 DOMContentLoaded 이벤트가 발생하면 class component를 활성화 시킬 수 있다!

 

거진 모든 내용이 addEventListener(  DOMContentLoaded  ) 로 진행되기 때문에 다시 한번 간단히 설명하자면 

 

addEventListener

= 특정 이벤트가 발생 했을 때 실행될 함수를 대상 객체에 등록

 

DOMContentLoaded 

= HTML이 완전히 로드되고 파싱되었을때 즉, DOM 구조가 완성되었을 때 발생하는 이벤트이다.

 

DOMContentLoaded 이벤트가 발생 되면 addEventListener 안에 포함된 여러가지 인스턴스된 class component가 실행

 

 

< openModal.js >  리뷰를 작성하기 위한 내용을 담고 있는 모달창 

class OpenButton {
  constructor(modal) {
    this.openButton = document.getElementById("open");
    this.modal = modal;
    this.addEventListeners();
  }

  addEventListeners() {
    this.openButton.addEventListener("click", (event) => {
      this.openModal(event);
    });
  }

  openModal(event) {
    event.preventDefault();
    this.modal.showModal();
  }
}

class Modal {
  constructor() {
    this.modal = document.getElementById("myModal");
  }

  showModal() {
    this.modal.style.display = "flex";
  }
}

document.addEventListener("DOMContentLoaded", function () {
  const modalInstance = new Modal();
  const openButtonInstance = new OpenButton(modalInstance);
});

 

OpenButton class, Modal class 만들기

 

-- class Modal --

class Modal {
  constructor() {
    this.modal = document.getElementById("myModal");
  }

 

먼저 myModal 요소는 모달창 자체를 말한다.

 

상속받은 calss가 없음에도 constructor을 사용한 이유!!

 

인스턴스 변수를 초기화하는 로직을 한곳에 모아서 관리

= showModal에서 modal을 가져가서 사용하는데 만약 constrouctor를 이용해 this.modal 변수를 선언하지 않았다면 

   각각의 매소드에서 this.modal 변수를 계속 선언해서 사용해야 할 것이다.

 

②인스턴스가 생성될 때마다 동일한 초기화(값 재할당)를 해주므로  재사용성이 높다.

= 하지만 나의경우 modal의 값을 초기화(값 재할당)하지 않았음!

 

③ DOM 요소에서 한 번만 찾아서 선언해 놓으면 되기 때문에 필요할때마다 새로 찾지 않아도 된다. 성능 최적화

 

④ modla class안에 매서드들이 this.modal을 사용할 수 있음으로 만약 참조 값을 변경 하고 싶다면 this.modal 만 변경하면 되기 때문에 유지 보수에 좋다.

 

 

 

-- showModal 매서드 --

 showModal() {
    this.modal.style.display = "flex";

 

modal을 화면에 고정으로 나타날 수 있도록 해준다.

모달창은 버튼을 클릭할때만 나올 수 있도록 display 값이 none으로 설정되어 있다.

때문에 showModla이 없으면 0.0001초만에 사라지는 것 같다..

 

 

-- 새로운 인스턴스 생성 --

document.addEventListener("DOMContentLoaded", function () {
  const modalInstance = new Modal();
  const openButtonInstance = new OpenButton(modalInstance);
});

DOMContentLoaded 이벤트 리스너 내부에서 Modalclass를 new Modal로 새로운 인스턴스를 생성하여

OpenButton에 생성자를 전달해 modal에 상속받지 않고 파라미터로 받아서 사용할 수 있도록 구현

 

간단하게 메서드를 사용하기 위해 상속받는 것 대신, 인스턴스를 받아와 사용

➡️재사용성, 모듈성, 각 class의 책임을 명확하게 분리하기에 좋음

 

**의존성 주입

한 객체가 다른 객체의 의존성을 제공하는 형태. 코드의 재사용성을 높이서 용이성 개선

여기서는 OpenButton이 Modal에 의존성을 제공함(나 너 의존중 니꺼 받아다가 호출좀 할께) 

 

extends(상속) 과는 다름!

=Modal을 상속받아 확장하거나 변경이 필요할 때 적합


 

-- class OpenButton --

class OpenButton {
  constructor(modal) {
    this.openButton = document.getElementById("open");
    this.modal = modal;
    this.addEventListeners();
  }

 

open 이라는 요소는 리뷰작성하기라는 버튼의 id 속성 값이다.

 

그리고 변수로는 this.openButton, this.modal, this.addEventListeners 총 3개가 있다.

그리고 new Modal, 즉 새로운 인스턴스를 이용해 파라미터로 Modal을 받아왔다.

 

 

-- addEventListeners --

  addEventListeners() {
    this.openButton.addEventListener("click", (event) => {
      this.openModal(event);
    });
  }

 

변수 this.openButton에  addEventListeners를 사용해 클릭 이벤트가 발생되면 openModal 실행!

 

 

-- openModal --

  openModal(event) {
    event.preventDefault();
    this.modal.showModal();
  }

 

openModal 매서드는 전달된 이벤트 메서드를  event.preventDefault 함수로 이벤트의 기본 행동을 취소해 준다.

만약 없었다면 다음 페이지로 자동적으로 이동되었을 것이지만 preventDefault로 다음페이지로 넘어가지 않고

현재 페이지에 머물 수 있도록 해주는 함수이다.

그리고 modal.showModal, 즉 class Modal의 new Modal인 새로운 인스턴스를 받아와 

showModal을 호출해 페이지를 지속적을 보여줄 수 있는 상태를 만들었다.


 

 

< getStar.js > 

class StarRating {
  constructor() {
    this.stars = document.querySelectorAll(".getstar .star");
    this.starIcons = document.querySelectorAll(".getstar .staricon");
    this.initialize();
  }

  initialize() {
    this.stars.forEach((star, index) => {
      star.addEventListener("change", () => this.updateStars(index));
    });
  }

  updateStars(selectedIndex) {
    this.starIcons.forEach((icon, index) => {
      if (index <= selectedIndex) {
        icon.classList.add("filled");
      } else {
        icon.classList.remove("filled");
      }
    });
  }
}

document.addEventListener("DOMContentLoaded", function () {
  const starRating = new StarRating();
});

 

getStar.js 는 평점을 줄때 사용될 객체이다.

class StarRating {
  constructor() {
    this.stars = document.querySelectorAll(".getstar .star");
    this.starIcons = document.querySelectorAll(".getstar .staricon");
    this.initialize();
  }

 

먼저 getstar 요소는 위에보이는 이미지 전체를 묶고 있는 div가 가지고 있는 class 이름이다.

 

html에서  1개는 각각 <label> 태그 안에 <input>과 <span> 으로 이루어져 있고 전체는 <div>로 묶여 있다.

          <div class="getstar" id="getstar">
            <label class="starlabel">
              <input class="star" id="star2" type="radio" name="star" value="" />
              <span class="staricon"><i class="fa-solid fa-star"></i></span>
            </label>
            <label class="starlabel">
              <input class="star" id="star4" type="radio" name="star" value="" />
              <span class="staricon"><i class="fa-solid fa-star"></i></span>
            </label>
            <label class="starlabel">
              <input class="star" id="star6" type="radio" name="star" value="" />
              <span class="staricon"><i class="fa-solid fa-star"></i></span>
            </label>
            <label class="starlabel">
              <input class="star" id="star8" type="radio" name="star" value="" />
              <span class="staricon"><i class="fa-solid fa-star"></i></span>
            </label>
            <label class="starlabel">
              <input class="star" id="star10" type="radio" name="star" value="" />
              <span class="staricon"><i class="fa-solid fa-star"></i></span>
            </label>
          </div>

 

그리고 

    this.stars = document.querySelectorAll(".getstar .star");
    this.starIcons = document.querySelectorAll(".getstar .staricon");

 document.querySelectorAll 를 이용해 class 이름으로 요소들을 불러왔다.

 

- querySelectorAll  이용해 class를 사용한 이유!

 

① 여러 요소에 동일하게 적용이 되어야 한다.

② <div class="getstar"></div> 로 묶인 star를 전부 사용해야 한다.

= querySelectorAll 를 이용해 class를 불로 오는 경우 자식요소를 선택할 수 있다.\

③ id로 불러오기 위해서는 모두 개별적으로 가져와 개별적으로 동일한 코드를 여러번 작성해야 한다.

 

 

id를 사용할 수 없는 이유!

 

① id의 고유성으로 인해 한개의 요소에만 적용이 된다.

② id를 선택하는 경우 자식요소를 선택할 수 없다 

 

 

   this.initialize();

initialize 매서드 호출 

 

 

 initialize() {
    this.stars.forEach((star, index) => {
      star.addEventListener("change", () => this.updateStars(index));
    });
  }

initialize는 getstar.star에 star과 index 값을 받아 star에  change가 발생 되면 updateStarts(인덱스값) 매서드 호출

 

 

  updateStars(selectedIndex) {
    this.starIcons.forEach((icon, index) => {
      if (index <= selectedIndex) {
        icon.classList.add("filled");
      } else {
        icon.classList.remove("filled");
      }
    });
  }
}

 

 updateStars는

starIcons에 icon, 인덱스 값을 인자로 받아 selectedIndex 와 인덱스의 값이 작거나 같으면 icon을 채워주고

그게 아니라면  비워준다.

즉 선택된 인덱스의 값만큼 별을 채워주고 선택되지 않았다면 별을 채우지 않도록 작성!