TIL 최종프로젝트(18) 배포전 수정2

2023. 7. 4. 00:48TIL/2023.6월

2023.07.03 <- 06.30 금요일에 작성했어야 할 TIL(깃허브야 고마워! 너 덕분에 기억이 난다)

 

[수정사항]

1.좋아요, 북마크 기능 사용 시 비로그인 일 경우 로그인 페이지로 이동

2.행사 기간 안내 스티커 범위 수정

3.댓글 없을 시 안내문구 출력

4.리뷰 최대 글자 수 제한

5.행사 기간 안내 스티커에 행사종료 항목 추가

6.들어오는 이미지의 크기를 판별하여, 일정 크기 이상 시 alert를 통해 안내

 

1.좋아요, 북마크 기능 사용 시 비로그인 일 경우 로그인 페이지로 이동

    bookmarkIcon.addEventListener('click', async () => {
      const event_id = parseInt(element.id, 10);
      const token = localStorage.getItem("access");
      if (payload) {
        try {
          const bookmarkResponse = await fetch(`${backend_base_url}/events/${event_id}/bookmark/`, {
            method: 'POST',
            headers: {
              "Authorization": `Bearer ${token}`
            }
          });

          const bookmarkData = await bookmarkResponse.json();
          alert(bookmarkData.message);
        } catch (error) {
          console.error('Error bookmarking event:', error);

        }
      } else {
        alert("로그인이 필요합니다")
      }

북마크나 좋아요의 경우 한 번 클릭하여 해당 회원의 id가 있을 경우 해제, 없을 경우 추가 하는 방식을 사용합니다.

 

기존의 코드에서는 백엔드에서 json 형태로 만들어진

{"message":"like했습니다"} {"message":"unlike했습니다"} 메시지를 출력시켜 주었습니다.

 

이 경우 db에는 저장되지만, 프론트에서 페이지가 리로드 되기 전까지, 이것이 반영되었다는 것을 사용자는 alert창을 통해서만 알 수 있었습니다.

 

사용자가 즉각적으로 이를 알 수 있게 코드를 추가하여, 아이콘을 눌렀을 경우, alert창과 함께 페이지를 리로드 되게 변경하였습니다.

bookmarkResponse.json();
          alert(bookmarkData.message);
          window.location.reload()

추가로 사용자가 로그인이 필요하다는 메시지를 통해 로그인을 했을 경우에만 북마크, 좋아요 기능을 사용할 수 있다는 것을 인지한 후, 로그인을 어디서 하는지 알고싶을 것이라 생각하여, 마찬가지로 비로그인 시 북마크, 좋아요 아이콘을 클릭하였고, 로그인이 필요합니다 메시지를 alert창을 통해 확인 후 로그인 페이지로 이동되게 코드를 수정하였습니다.

else {
        alert("로그인이 필요합니다")
        location.replace(`${frontend_base_url}/login.html`)

      }

[수정된 코드]

    bookmarkIcon.addEventListener('click', async () => {
      const event_id = parseInt(element.id, 10);
      const token = localStorage.getItem("access");
      if (payload) {
        try {
          const bookmarkResponse = await fetch(`${backend_base_url}/events/${event_id}/bookmark/`, {
            method: 'POST',
            headers: {
              "Authorization": `Bearer ${token}`
            }
          });

          const bookmarkData = await bookmarkResponse.json();
          alert(bookmarkData.message);
          window.location.reload()
        } catch (error) {
          console.error('Error bookmarking event:', error);

        }
      } else {
        alert("로그인이 필요합니다")
        location.replace(`${frontend_base_url}/login.html`)

      }

    });

 

2.행사 기간 안내 스티커 범위 수정

      const get_event_start_date = events.event_start_date
      const get_event_end_date = events.event_end_date
      const currentDate = new Date();
      currentDate.setHours(0, 0, 0, 0);
      const eventStart = new Date(get_event_start_date);
      const eventEnd = new Date(get_event_end_date);
      eventStart.setHours(0, 0, 0, 0);
      eventEnd.setHours(0, 0, 0, 0);
      const oneDay = 24 * 60 * 60 * 1000;
      const diffDaysStart = Math.round(Math.abs((currentDate - eventStart) / oneDay));
      const diffDaysEnd = Math.round(Math.abs((currentDate - eventEnd) / oneDay));

      const season = document.createElement('p')
      season.setAttribute('class', 'reservation')
      if (currentDate >= eventStart && currentDate <= (eventEnd - 7 * oneDay)) {
        season.innerText = '행사중';
      } else if (diffDaysStart > 0) {
        season.innerText = '행사예정';
      } else if (diffDaysEnd <= 7 && diffDaysEnd > 0) {
        season.innerText = '마감임박';
      } else {
        season.innerText = '삑';

event. 으로 시작하는 것은 fetch를 사용하여 GET 메소드로 백엔드에서 받아온 데이터 입니다.

currentDate는 오늘의 날짜를 의미합니다.

setHours를 한 이유는 2023-05-05 (시간) 여기서 시간 부분을 제거하기 위해서 사용합니다. 

시간이 있을 경우 계산이 꼬여 예상한 결과물이 출력되지 않기 때문에 사용했습니다.

eventStart, eventEnd는 행사의 시작과 종료 날짜를 담으며, 마찬가지로 시간부분은 제거해주었습니다.

 

oneDay는 하루를 나타내기 위한 식입니다, 시간, 분, 초, 밀리초를 나타냅니다.

diffDaysStart와 diffDaysEnd는 각각 오늘로 부터 공연까지의 날짜를 하루 단위로 나타낸 것입니다.

2023-07-03이 오늘이고 2023-07-05가 eventStart라면,  diffDaysStart의 값은 2가 됩니다.

 

조건문은 보통 7일 전후로 스티커를 다르게 붙여주고자 했습니다.

그런데 실제 공연이나 행사를 보면, 3일간 진행되거나, 5일간 진행되는 등 7일을 기준으로 잡았을 시, 해당 조건문을 만족시키지 못해 삑 에러가 출력되었습니다.

 

그렇기에 행사 기간 범위를 조금 더 타이트하게 잡아 7일->2일로 변경하여, 최대한 조건에 맞는 스티커를 붙일 수 있게 코드를 수정하였습니다.

      const season = document.createElement('p')
      season.setAttribute('class', 'reservation')
      if (currentDate >= eventStart && currentDate <= (eventEnd - 2 * oneDay)) {
        season.innerText = '행사중';
      } else if (diffDaysStart > 0) {
        season.innerText = '행사예정';
      } else if (diffDaysEnd <= 2 && diffDaysEnd > 0) {
        season.innerText = '마감임박';
      } else {
        season.innerText = '삑';

아직도 이 조건문에는 문제가 있습니다.

개막식이나 특정 날짜의 특별 공연같은 경우 하루만 진행되는 공연/ 행사가 있습니다. 이 부분을 잡기 위해 조건을 추가해주어야 합니다(나중에)

 

3.댓글 없을 시 안내문구 출력

이미지로 바로 이해

07-03 수정사항이 적용되어 조금 다르긴 합니다

댓글이 없을 경우 작성칸 아래에 아무런 안내가 없습니다.

  if (review_response_json.length === 0) {
    const noReviewContainElement = document.createElement('div');
    noReviewContainElement.className = 'contant-page';

    const noReviewTextElement = document.createElement('p');
    noReviewTextElement.className = 'NoneText';
    noReviewTextElement.textContent = "리뷰가 없습니다.";

    noReviewContainElement.appendChild(noReviewTextElement);
    review_list.appendChild(noReviewContainElement);
    return;
  }

forEach를 돌기 전 fetch를 통해 GET 받아온 review_response_json의 길이가 0일 때 즉, 아무런 데이터도 없을 때 

리뷰가 없습니다 라는 메시지를 아래에 출력합니다.

noReviewContainElement 라는 이름의 div태그를 만듭니다.

contant-page는 css에 만들어진 스타일을 사용하기 위해 className을 사용하여 지정해 준 것입니다.

noReviewTextElement라는 이름의 p 태그를 만들어 줍니다.

마찬가지로 css에 만들어진 스타일을 사용하기 위해 className을 정해주고, textContent 속성을 사용하여 안에 들어갈 내용을 적어줍니다.

 

이런형태로 들어가게 하기위해 appendChild를 사용합니다.

js못했는데 이걸 이해하고 나니 js를 다룰 수 있게 되었습니다.

 

리뷰가 없습니다 가 출력됩니다.

 

4.리뷰 최대 글자 수 제한

이건 원래 백엔드에서 모델을 만들 때, 필드에 넣어주어야 했습니다.

배포 후 최대한 백엔드를 수정하지 않는 방향으로 프로젝트가 진행되었기에, 모델에서 필드를 수정하는 방법을 사용할 수 없게 되었습니다.

최대한 프론트에서 이 기능을 만들기 위해 고민하여 생각해낸 것이 일정 글자 수가 초과되면, 이후 텍스트를 ...으로 출력되게 하는 방법을 사용했습니다.

document.createElement('p');
    reviewContentElement.id = 'content';
    reviewContentElement.className = 'content';
    reviewContentElement.textContent = get_content;

이전에는 간단하게 fetch를 통해 받아온 내용을 변수에 넣어 출력시켜주는 형태였지만, 제한 없이 값을 넣을 경우 글이 영역을 벗어나거나, 비정상적으로 출력되는 문제가 있었습니다.

 

    const reviewContentElement = document.createElement('p');
    reviewContentElement.id = 'content';
    reviewContentElement.className = 'content';
    
    const max_lenght = 20;
    if (get_content.length > max_lenght) {
      reviewContentElement.textContent = get_content.substr(0, max_lenght) + '...';
    } else {
      reviewContentElement.textContent = get_content;
    }

max_length라는 변수에 20이라는 값을 넣어주고 받아오는 content의 길이가 20보다 클 경우 받아오는 댓글 내용의 0~20자리 까지 출력하고 이후는 ...을 붙여 출력하게 구현했습니다.

 

단, 이 방법을 사용할 수 없는 이유는 현재 만든 페이지에 댓글 상세보기 기능이 없어 ...으로 변한 댓글 내용을 볼 수 있는 방법이 없다는 것입니다. 

그렇기에 다른 방법을 고안하여야 합니다.

 

5.행사 기간 안내 스티커에 행사종료 항목 추가

행사 기간이 끝난 행사를 어떻게 처리할지 의논하던 중 나온 방법입니다.

 

빠르게 피드백을 받아 리펙토링 시간을 벌어야 했던 저희 팀은 일단 문제 없는 배포를 방향으로 잡고 최대한 기능 추가를 미뤘습니다.

행사 기간이 끝났을 경우 그 행사를 어떻게 관리할지 의논하였고,

-관리자가 admin 페이지에서 행사를 삭제하자!

-백엔드를 수정하여 status를 False로 바꾸고 예약기능을 막고, 종료된 행사만을 관리하는 페이지를 만들어 관리하자!

등의 의견이 나왔지만, 삭제의 경우 사용자가 작성한 댓글이 날아간다는 점에서 기각, status를 통한 관리는 백엔드, 프론트 두 곳 모두에서 많은 작업이 필요하기에 기각되었습니다.

 

그렇기에 기존의 행사 기간 안내 스티커에 행사 종료 조건을 추가하여 행사가 종료되었다는 것을 사용자가 알 수 있게 하자는 쪽으로 결정되었습니다.

    if (currentDate >= eventStart && currentDate <= (eventEnd - 2 * oneDay)) {
      reservationTag.innerText = '행사중';
    } else if(eventEnd < currentDate) {
      reservationTag.innerText = '행사종료';     
    } else if (diffDaysStart > 0) {
      reservationTag.innerText = '행사예정';
    } else if (diffDaysEnd <= 2 && diffDaysEnd > 0) {
      reservationTag.innerText = '마감임박';
    }  else {
      reservationTag.innerText = '삑';
    }

행사 종료날짜가 오늘의 날짜보다 작다면 행사종료를 출력하게 만들었습니다.

 

6.들어오는 이미지의 크기를 판별하여, 일정 크기 이상 시 alert를 통해 안내

배포 후 댓글을 작성할 때, 등록하려고 하는 이미지가 너무 큰 경우 console에서 502에러를 출력했습니다.

구입한 서버의 크기가 작기 때문에 발생하는 문제라고 들었습니다.

 

이를 502에러를 방지하기 위해 애초에 이미지를 등록할 때, 크기를 제한하여 받고자 기능을 구현했습니다.

 

async function HandleComment() {
  const select_grade = document.getElementById('grade').value;
  const in_img = document.getElementById('in_img').files[0];
  const com_txt = document.getElementById('com_txt').value;
  const grade = select_grade.split('')[0]
  const maxSixe = 2 * 1024 * 1024

  if (in_img.size >= maxSixe){
    alert("이미지가 너무 큽니다.")
    window.location.reload()
  }else{

  const formdata = new FormData();
  formdata.append("grade", grade)
  formdata.append("review_image", in_img)
  formdata.append("content", com_txt)
  const response = await fetch(`${backend_base_url}/events/${event_id}/review/`, {
    headers: {
      "Authorization": `Bearer ${token}`,
    },
    method: 'POST',
    body: formdata
  })
  if (response.status == 201) {
    alert("작성되었습니다.")
    window.location.reload()
  } else if (response.status == 400) { 
    alert("내용이 필요합니다.")
    (response.status)
    
  }
  else{
    alert("로그인이 필요합니다.")
  }
}}

maxsize를 정해주고 sixe는 오타입니다. 사용자가 등록한 이미지의 size를 측정한 후 비교하여, 해당 이미지를 받을지 말지를 결정합니다.

 

별 문제 없을 경우 else로 넘어가 값을 formdata에 넣어 post 메소드를 사용하여 데이터를 전송해주고 댓글을 생성합니다.

 

느낀점

백엔드를 수정하지 못하는 상황에서, 최대한 js를 통해 처리하려고 하니 프론트 실력이 일취월장 중입니다. 

배포 전 백엔드 수정을 최소화하여 기능을 구현하고 수정하려고 하니 TIL도 프론트엔드 내용 뿐이네요

이전에도 말했지만, 백엔드와 프론트의 연계, 계획과 클라이언트의 요구에 맞게 개발하는 것이 얼마나 중요한 것인지 알게 되었습니다.

내가 만든 백엔드 기능을 가지고 내가 프론트에 연결을하니 어휴 이걸 빼먹었네 이러고 넘어가지만, 솔직히 협업 과정에서 이런 계획과 다른 구현, 누락된 구현이 발생한다면, 많이 화가 날 거 같습니다.