[리팩토링 일기] 이상한 나라의 초콜릿 쇼핑몰 리팩토링 - alert, confirm 커스텀 하기

박소정·2022년 9월 23일
0
post-thumbnail

쇼핑몰 프로젝트를 진행하면서 이틀에 한번씩 현업에서 일하고 계시는 코치님과의 코칭 시간을 가졌었는데, UI 부분에서 알림창을 수정하는 것이 좋겠다고 하셨다. 당시에는 기능 구현에 급급해 UI 부분은 그냥 넘어갔었는데 리팩토링을 하며 알림창 모달을 새로 만들고, 필요한 confirm을 추가하였다.


우선 새로운 alert창을 만들어야하는데 친구랑 했던 vanilla JS 프로젝트duTejs [book-cord]에서 alert를 함수로 만들어 여기저기에 가져다 쓴 경험이 있어 빠르게 만들 수 있을줄 알았다!! 그치만 역시나 예상은 빗나감...

우선 JS파일로 알림창 모달은 만들었는데 왜 가져와지질 않는 걸까....
[book-cord]에서는 따로 JS파일을 만들고 html에 연결하면 바로 불러와졌는데 not defined에러가 떴다.
함수로 export시켜서 import 받아왔더니 api로 백에서 받아오던 것이 동작이 안되었다!!!

문제는 이러했다.
함수를 import 해오는 부분에서 경로 에러가 나는 것 같았는데 경로는 이게 맞단 말이쥐...?

이렇게 경로를 한번만 빠져나가도 alert폴더가 보이는데 ../alert/alert.js로 입력하면 계속 경로 에러가 나 버렸다.
어이없게도 ../../alert/alert.js로 수정하니 제대로 떴는데

이 에러에서 detail 폴더안에 alert가 잡히는게 이상해서 한번 더 나갔더니 제대로 작동했다.... 근데 폴더 트리상으로는 ../alert/alert.js이게 맞는데 말이쥐!!!🤔

참고로 폴더 경로는 이러했다.

같은 views 폴더 안에 product-detail폴더와 alert폴더가 있는데 말이지...

잠시 오류가 난게 아닐까 싶었는데 alert폴터의 css파일 역시 ../../alert/alert.css로 연결해야만 연결이 잡혔다!!
대체 와이..!!

구글링으로는 나와 비슷한 사례를 찾지못했는데 추측하기로는 상품 상세 부분이 id값으로 중첩 라우팅이 되어 있어 발생하는 문제로 보였다. 중첩으로 라우팅되는 다른 부분에서도 같은 에러가 발생하였다.
[viewsRouter.use("/detail/:id", serveStatic("product-detail"));]

라우팅과 파일경로에 대해 찾아서 공부해보고 있긴한데 아직 확실한 정보를 찾지못해 다른 포스팅으로 돌아오겠습니다!!

alert modal은 안의 메세지만 바뀌고 여기저기 경고창에 사용될 수 있도록 하기 위해서

const alertModal = document.querySelector("#alertModal");

export const makingAlertModal = function (message, redirect) {
  const alertContainer = document.createElement("div");
  alertContainer.classList.add("alertModalContainer");

  const alertH2 = document.createElement("h2");
  const alertP = document.createElement("p");
  alertH2.textContent = message;
  alertH2.classList.add("alertH2");
  alertP.textContent = "⚠️";
  alertP.classList.add("alertP");

  const alertButton = document.createElement("button");
  alertButton.textContent = "확인";
  alertButton.classList.add("alertButton");
  alertButton.addEventListener("click", (e) => {
    e.preventDefault();
    alertModal.style.display = "none";
    if (redirect) {
      window.location.href = redirect;
    }
  });

  alertContainer.appendChild(alertP);
  alertContainer.appendChild(alertH2);
  alertContainer.appendChild(alertButton);

  alertModal.appendChild(alertContainer);
};

함수의 파라미터로 메세지를 넣어주면 재사용할 수 있도록 만들어 보았다.


alertModal.addEventListener("click", (e) => {
  e.preventDefault();
  if (e.target == alertModal) {
    alertModal.style.display = "none";
  }
});

이 부분을 추가해 모달영역이 아닌 바깥쪽 영역을 클릭하면 모달이 사라지게 하고,

alertButton.addEventListener("click", (e) => {
    e.preventDefault();
    alertModal.style.display = "none";
    if (redirect) {
      window.location.href = redirect;
    }
  });

alert창 안의 버튼을 클릭했을 때, redirect경로가 있다면 원하는 경로로 redirect 시킬 수 있게 하였다.
(ex, 로그인한 유저만 사용할 수 있는 기능이라면 로그인 페이지로 리다이렉트)

alert 부분은 이 함수를 활용하여 다 수정해 주었고, 다음으로는 confirm을 넣을 차례였다!


쇼핑몰이다 보니 왠지 상품 구매의 이탈을 막을 장치가 있으면 좋을 것 같았고, 장바구니에서 물건을 삭제할 때 정말 삭제할건지 물어보는 장치가 필요해 보였다.

alert 용으로 만들어 둔 함수에 기능을 추가하여 confirm 모달로 할까 싶기도 했는데, 그렇게 되면 이미 만들어둔 alert에도 alert인지 confirm인지를 구분할 수 있는 장치를 다 추가해야하기 때문에 새로 만들기로 결정!

사실 구조는 다 똑같고, 취소, 확인 버튼만 추가되면 된다고 생각했는데 역시나 fail! (그려.. 한번에 성공하면 재미없제...)

처음 생각한 구조는 다음과 같았다.

export const confirmModal = function (message) {
  alertModal.style.display = "block";
  const alertContainer = document.createElement("div");
  alertContainer.classList.add("alertModalContainer");

  const alertH2 = document.createElement("h2");
  const alertP = document.createElement("p");
  alertH2.textContent = message;
  alertH2.classList.add("alertH2");
  alertP.textContent = "⚠️";
  alertP.classList.add("alertP");

  const buttonContainer = document.createElement("div");
  buttonContainer.classList.add("alertButtonContainer");
  const cancelButtonForConfirm = document.createElement("button");
  cancelButtonForConfirm.textContent = "취소";
  cancelButtonForConfirm.classList.add("cancelButtonForConfirm");
  cancelButtonForConfirm.addEventListener("click", (e) => {
    e.preventDefault();
    alertModal.style.display = "none";
    return false;
  });

  const confirmButtonForConfirm = document.createElement("button");
  confirmButtonForConfirm.textContent = "확인";
  confirmButtonForConfirm.classList.add("confirmButtonForConfirm");
  confirmButtonForConfirm.addEventListener("click", (e) => {
    e.preventDefault();
    alertModal.style.display = "none";
    return true;
  });

  buttonContainer.appendChild(cancelButtonForConfirm);
  buttonContainer.appendChild(confirmButtonForConfirm);
  alertContainer.appendChild(alertP);
  alertContainer.appendChild(alertH2);
  alertContainer.appendChild(buttonContainer);

  alertModal.appendChild(alertContainer);
};

앞서 구현했던 alert모달에서 [확인, 취소]버튼을 감싸기 위한 button container를 추가하고 [확인, 취소]버튼에 각각 [true, false]를 return 하여 if문안에서 확인을 누르면 동작이 실행되고, 취소를 누르면 return;이 되게 하려고 하였으나 콘솔에는 undefined가 찍혔다!

왜인지 생각해보니 confirmModal자체에는 return이 없고, confirmModal 안의 clickEvent의 함수 안에 return true/false가 들어있어 클릭 이벤트가 일어나기 전에 if문 안으로 진입하는것 같았다!!!!

해결 방법을 생각해보다가 어차피 [취소]를 클릭하게 되면 confirm 모달이 닫기는 일만 있을 것 같아 [확인]을 클릭했을 경우의 콜백함수만 바꾸어 주면 해결이 될것 같았다.

export const confirmModal = function (message, callback) {
  alertModal.style.display = "block";
  const alertContainer = document.createElement("div");
  alertContainer.classList.add("alertModalContainer");

  const alertH2 = document.createElement("h2");
  const alertP = document.createElement("p");
  alertH2.textContent = message;
  alertH2.classList.add("alertH2");
  alertP.textContent = "⚠️";
  alertP.classList.add("alertP");

  const buttonContainer = document.createElement("div");
  buttonContainer.classList.add("alertButtonContainer");
  const cancelButtonForConfirm = document.createElement("button");
  cancelButtonForConfirm.textContent = "취소";
  cancelButtonForConfirm.classList.add("cancelButtonForConfirm");
  cancelButtonForConfirm.addEventListener("click", (e) => {
    e.preventDefault();
    alertModal.style.display = "none";
  });

  const confirmButtonForConfirm = document.createElement("button");
  confirmButtonForConfirm.textContent = "확인";
  confirmButtonForConfirm.classList.add("confirmButtonForConfirm");
  confirmButtonForConfirm.addEventListener("click", (e) => {
    e.preventDefault();
    alertModal.style.display = "none";
    callback();
  });

  buttonContainer.appendChild(cancelButtonForConfirm);
  buttonContainer.appendChild(confirmButtonForConfirm);
  alertContainer.appendChild(alertP);
  alertContainer.appendChild(alertH2);
  alertContainer.appendChild(buttonContainer);

  alertModal.appendChild(alertContainer);
};


꺄륵 성공~!~!!!

이제 장바구니의 물품 삭제나 게시글, 카테고리・상품의 삭제에 confirm을 붙이며 리팩토링이 끝났다!


자세한 코드가 궁금하다면 깃헙으로 놀러오세요~!

0개의 댓글