Skip to content

트러블 슈팅

Taehee Kim edited this page Jul 30, 2022 · 14 revisions

민영

지훈

상품 이미지 POST요청

이미지 POST요청시 이미지를 불러오지 못하는 문제


기존코드

// 이미지 업로드 시 미리보기
const imgPreView = async (event) => {
  const reader = new FileReader();

  reader.onload = (event) => {
    $productImg.setAttribute('src', event.target.result);
    $productImg.style.display = 'block';
  };

  reader.readAsDataURL(event.target.files[0]);
};
// 이미지 POST요청
const storeImage = async (event) => {
  const formData = new FormData();
  formData.append('image', event.target.files[0]);
  try {
    const res = await fetch(url + '/image/uploadfile', {
      method: 'POST',
      body: formData,
    });
    const resJson = await res.json();
    console.log(resJson);
  } catch (err) {
    console.error(err);
  }
};

해결방법

  • 서버로부터 받은 응답 중 숫자로 이루어진 filename을 사용
  • 그 이름을 서버 주소에 붙여서 상품명, 가격 등의 다른 상품 정보와 함께 서버로 전송
  • 전역에 응답받은 파일명을 저장할 변수를 선언 let filename = '';
  • 리턴 받은 파일명을 filename에 저장
  filename = await storeImage(event.target); 
  • 서버주소와 파일명 붙여서 서버로 전송
itemImage: `${url}/${filename}`,

해결 코드

// 이미지 업로드 시 미리보기
const imgPreView = async (event) => {
  const reader = new FileReader();

  reader.onload = (event) => {
    $productImg.setAttribute('src', event.target.result);
    $productImg.style.display = 'block';
  };

  reader.readAsDataURL(event.target.files[0]);
  filename = await storeImage(event.target); // 리턴 받은 파일명을 filename에 저장
};

// 이미지 POST요청
const storeImage = async (target) => {
  const formData = new FormData();
  formData.append('image', target.files[0]);
  try {
    const res = await fetch(url + '/image/uploadfile', {
      method: 'POST',
      body: formData,
    });
    const resJson = await res.json(); // 서버로부터 받은 응답
    return resJson.filename; // 응답 중 filename 리턴
  } catch (err) {
    console.error(err);
  }
};
async function productData() {
  try {
    const res = await fetch(url + '/product', {
      method: 'POST',
      body: JSON.stringify({
        product: {
          itemName: $inputProductTitle.value,
          price: parseInt($inputProductPrice.value.replaceAll(',', '')),
          link: $inputProductLink.value,
          itemImage: `${url}/${filename}`, 
        },
      }),
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-type': 'application/json',
      },
    });
    const resJson = await res.json();
    console.log(resJson);
    // alert('상품이 정상적으로 등록되었습니다');
    // isProductTrue();
  } catch (err) {
    console.error(err);
  }
}

상품 수정시 상품의 상세페이지 보여주기

myProfile.html에서 판매중인 상품을 클릭하고 [수정] 버튼 클릭시, 상품의 상세페이지로 이동하면서 상품을 수정할 수 있는 기능을 구현해야한다.


해결방법

  • 상품 수정 버튼과 연결된 url에 상품 id를 파리미터로 붙이기
  • 상품 수정 페이지에서 상품 상세 요청(GET)과 상품 수정 요청(PUT)을 수행해야 하는데 이때 상품 아이디가 필요하기 때문에 id를 사용할 수 있도록 파라미터에 붙여준다.
  • productList.js에서 [수정] 링크의 href를 다음과 같이 수정한다.
a.href = `../pages/productModification.html?id=${상품 id}`

(productList.js)

const btnModifyProduct = document.querySelector('.btnModifyProduct');
btnModifyProduct.addEventListener('click', (event) => {
  location.href =
    '../pages/productModification.html?id=' +
    event.target.getAttribute('dataId');
});
  • location.search로 개별 상품 id를 추출하여 prodId에 할당한다.

(productModification.js)

const prodId = location.search.split('id=')[1];
  • 상품 데이터 GET요청으로 불러온다.

(productModification.js)

// 상품 데이터 GET요청으로 가져오기
(async function getProductData() {
  try {
    const res = await fetch(url + `/product/detail/${prodId}`, {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-type': 'application/json',
      },
    });
    const resJson = await res.json();
    setProductData(resJson.product);
  } catch (err) {
    console.error(err);
  }
})();

태희

1. Location Property

  • 문제 상황

    • 하나의 html파일과 API로 yourProfile, myProfile를 동시에 구현해야 했음
    • following/follower 리스트에서 프로필로 넘어갈 때, 해당 유저에 맞는 yourProfile을 보여줘야 함
    • 토큰에서는 상대방의 accountname을 가져올 수 없다는 것과 클릭 했을 때 해당 유저의 accountname만을 정확하게 집어내지 못하고 있었음
  • 원인 추론

    • GET 요청 url뒤에 accountname을 넣어줘야 하는데, 나의 프로필은 로컬스토리지에서 getItem으로 사용하면 되지만 상대방 프로필 accountname을 가져오지 못했음
  • 해결 방법

    • Location (현재 URL) Property 중에서 query string이라고 불리는 search string을 사용하여 해결
    • search string은 url에서 ? 를 포함한 뒷부분을 string 형식으로 반환해주는 역할

    실제 적용 코드)

    followingList.html에서 유저를 선택할 때 넘어가는 a태그의 href 주소를 ?accountname=유저의 accountname 붙여서 보냄

    link.setAttribute(
          'href',
          '../pages/myProfile.html?accountname=' + resJson[i].accountname
        );

    프로필 html로 넘어와서 상대방의 accountname을 location.search로 변수에 담아 사용

    내 계정일 경우에는 로컬스토리지에서, 상대방 프로필이면 url을 query string값이 accountname에 들어가게 처리

    1. location.search⇒ ?accountname=유저아이디

    2. location.search.replace(’?’,’’) ⇒ accountname=유저아이디

    3. location.search.replace(’?’,’’).split(’=’) ⇒ ['accountname', '유저아이디']

    📌 userInfo.js (프로필 상단, 유저 정보 API GET 요청 코드)

    const url = 'https://mandarin.api.weniv.co.kr';
    const myAccountname = `${window.localStorage.getItem('accountname')}`;
    const yourAccountname = location.search.replace('?', '').split('=')[1];
    const accountname = yourAccountname ? yourAccountname : myAccountname;
    const userSettings = document.querySelector('.userSettings');
    //프로필 정보 보여주기
    async function infoUser() {
      try {
        const res = await fetch(url + '/profile/' + accountname, {
          method: 'GET',
      
          ..생략..}
    }

2. 요소 생성(createElement vs innerHTML)

  • 문제 상황

    • JavaScript에서 요소를 생성할 때(특히 반복되는 리스트들), map과 innerHTML을 사용함
    • innerHTML은 간혹 인식되 않아 null 값을 반환하는 경우가 생겨 Error가 발생
    • 사용은 편리하지만 성능이 가장 나쁨
    • 보안 상의 문제도 존재(스크립트 해킹을 당할 수 있음)
  • 해결 방법

    • 코드가 길어지더라도 안전하고 성능이 좋은 createElement로 모든 요소를 생성하고 반복되는 요소를 생성할 때는 for문을 사용
    • 팀원 모두 createElement를 공통적으로 사용

기존 코드)

function createProductList() {
  productList.innerHTML = prodcutListDummy
    .map(
      (element) =>
        `
  <li>
    <button type="button" class="btnProductItem" id="btnProductItem">
      <img src=${element.itemImage} alt="상품1" />
      <span class="prodcutTitle">${element.itemName}</span>
      <strong class="prodcutPrice">${element.price
        .toString()
        .replace(/\B(?=(\d{3})+(?!\d))/g, ',')}</strong>
    </button>
  </li>
  `
    )
    .join('');
}

해결 코드)

function createProductList() {
  for (let i = 0; i < prodcutListDummy.length; i++) {
    const li = document.createElement('li');
    const button = document.createElement('button');
    const img = document.createElement('img');
    const span = document.createElement('span');
    const strong = document.createElement('strong');
		
	**생략 ...**
}

3. click 이벤트와 querySelectorAll

  • 문제 상황

    • 마이프로필의 경우, 상품리스트나 게시물 리스트 등 반복되어 같은 class나 id를 갖고 있는 요소들을 선택해야 하는 경우가 많았음
    • 그래서 querySelectorAll으로 요소를 선택하고 click evnent를 줘야 했는데, 선택된 요소가 null 값으로 반환되어 error
  • 원인 추론

    • 노드리스트 배열를 반환하는 querySelectorAll이기 때문에 반복문을 사용하지 않음
    • 함수 내 지역변수로 선택 요소를 선언하지 않고, 함수 밖의 전역변수로 설정하여 for 구문이 요소를 null 값을 반환

문제의 코드

const btnProductItem = document.qeurySelectorAll('.btnProductItem')
function onProductSettingModal() {
btnProductItem.addEventListenr('click', () => {
	...**생략...**
	})
}
  • 해결
    • 반복문 사용
    • 선택할 요소를 함수 내부로 옮겨 지역변수로 바꿈
function onProductSettingModal() {
  let btnProductItem = document.querySelectorAll('.btnProductItem');
  for (let i = 0; i < btnProductItem.length; i++) {
    btnProductItem[i].addEventListener('click', (event) => {
     
		...**생략...**
  }

지수

1. 토큰 유효성 검사

로그인 성공 시 토큰을 로컬 스토리지에 담았습니다. 배포 페이지에 재 진입 시 서버에 토큰 유무를 파악하여 페이지를 달리하는 로직을 구현하였습니다. 즉시 실행 함수를 사용하여 비동기 통신으로 구현하였습니다.

//토큰 유효성 검사
(async function tokenValid() {
  try {
    const res = await fetch(url + '/user/checktoken', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${localToken}`,
        'Content-Type': 'application/json',
      },
    });
    const resJson = await res.json();
    console.log(resJson);
    if (resJson.isValid === true) {
      changePageeHome();
    } else {
      splash();
    }
  } catch (err) {
    changePageTo404();
  }
})();

2. 스플레시 효과

소셜 로그인 페이지 진입 시 서서히 등장 하는 애니메이션 구현하였습니다. CSS에 keyframes를 활용하여 구현 하였습니다. 성능 향상을 위해 window.requestAnimationFrame() API를 활용하여 리팩토링할 예정입니다.

기존코드

@keyframes fadein {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@-moz-keyframes fadein {
  /* Firefox */
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@-webkit-keyframes fadein {
  /* Safari and Chrome */
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@-o-keyframes fadein {
  /* Opera */
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
Clone this wiki locally