Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2주차 🏠창업Store 기본/심화 과제] #10

Merged
merged 3 commits into from
May 11, 2023
Merged

Conversation

Chanwoo-Jeong
Copy link
Member

@Chanwoo-Jeong Chanwoo-Jeong commented Apr 15, 2023

🌈 구현 결과물

📌https://chanwoo-jeong-io-w1sl.vercel.app/index.html

  • 찬우의 포켓몬 잡화점 100% 즐기기
  1. 전체, 진화1,진화2,진화3 들을 눌러보며 포켓몬의 진화 단계를 확인해보세요!
  2. 사이드바에서 포켓몬 추가를 누르고 포켓몬을 추가해보세요!
    ( 중간에 정보를 빼먹고 등록을 누르셨나요? 걱정하지마세요 유효성체크가 알려드릴겁니다! )
  3. 모든 정보를 입력하고 누르면 메인으로 넘어가며 포켓몬이 등록되어있을겁니다! 이미지까지도요!
    ( 포켓몬 삭제기능이 없어 로컬스트로지에서 직접 지워주세요! )
  4. 포켓몬이 잘 등록되었는지 진화단계를 눌러 확인해보세요!

QA시 오류가 있으면 언제든 문의주세요! 감사합니다!

image

image

✨ 구현 기능 명세

  • 기본 과제
    -‼️‼️‼️ 상품 데이터는 상수파일에 저장해 사용합니다 ‼️‼️‼️✔️

즉, html에 미리 나타나 있는 것이 아닌, js 파일을 실행했을때 데이터를 가져와 보여줍니다.
🔥 Data/cardData.js 에 포켓몬 정보를 담았습니다!

✅ nav

  1. 종류 선택시 태그를 카드섹션 위에 하나씩 부착합니다.✔️
  2. 태그별 상품 리스트 보여줍니다. ✔️
    1. 전체 → 다보여주기✔️
    2. 종류별 → 필터기능✔️
  3. x 클릭시 ✔️
    1. 카드 다시 정렬 ✔️
    2. 종류 선택 해제 ✔️
    3. 태그 삭제✔️

✅ card article

  1. + 아이콘 클릭시 태그 모달이 등장합니다.✔️
  2. 카드 위에 덮어씌워지는 창 → 해시태그 목록 담은 content 보여주기 ✔️
  3. x 버튼을 누르면 모달이 닫힙니다.✔️
  • 심화 과제

✅ 목록

  1. 목록 선택시 리스트로 새 상품 추가가 나타납니다. ✔️
    1. 새 상품 추가 → href =’/addCard’

✅ 새 상품 추가 페이지

  1. label, input 을 연결시켜서 구현합니다.✔️
    1. 상품명 ✔️
    2. 태그 종류 → ,로 구분해서 담기✔️
    3. 이미지 ✔️
      1. 파일선택후 이미지 보여주기 필수
  2. 추가 버튼✔️
    1. 모든 정보가 입력되면 localStorage에 추가후 main페이지로 이동✔️
    2. 메인페이지에서 추가된 상품 조회가능✔️
      → 이미지 미리보기까지만 구현…! 메인페이지의 이미지는 임의!⁄로✔️
      🔥하지만 미리보기 + 메인에도 사진을 띄울 수 있었답니다!🔥

✅ 카드 띄울때 애니메이션

  1. 태그 기능으로 보여지는 상품 카드들이 달라질때 동적인 애니메이션을 가지고 변경됩니다!✔️
    🔥애니메이션은 이제 껌이죠! 하하핫

🌼 PR Point

  1. 카드데이터를 가져와 카드를 만들때 createElement 기능을 쓰기도 하지만 카드의 전체 구성을 createElement 만으로 이용해 만드려면 엄청난 양의 js 코드가 생깁니다. 따라서 저는 template 태그 기능을 이용해보았어요!

이건 html 에 미리 카드 template 을 만들어두고
js로 const cardTemplate = document.querySelector("#temp-card"); 이렇게 가져오면 template 형식이 그대로 가져와 집니다.
그리고 {pokemonName} 의 위치를 html = html.replace("{pokemonName}", card.name); 이렇게 변경하면
createElement와 appendChild 를 수십번 써야할걸 한번도 안쓰고 카드노드를 만들 수 있답니다!
그 node를 appendChild 해주면 끝!!😊

그러나 포켓몬 태그의 경우 포켓몬에 따라 태그의 갯수가 달라 윗처럼 처리하기가 어려울 겁니다.
그래서 태그부분의 경우 createElement 와 appendChild 를 통해 구현했습니다!

**- myStore.html**
<template id="temp-card" type="text/template">
    <article class="card">
        <div class="card-content">
            <header class="pokemonName">
                {pokemonName}
            </header>
            <div class="tag_zone">
                <ul class="tagContainer">
                </ul>
                <button type="button" class="plusTag">+</button>
            </div>
            <div>
                <img src="{img}" alt="{poketmonImg}" />
            </div>
            <div class="JJim">
                <p class="favorite">
                    <input id="{JJimN}" class="heart" type="checkbox">
                    <label for="{JJimN}" class="label"></label>
                </p>
            </div>
        </div>
        <div class="tagModal">
            <div class="tagModal_container">
                <div class="exitIcon">&#10008;</div>
            </div>
        </div>
    </article>
</template>

**-myStore.js**
//필터에 따른 카드 생성 함수
const addCardItem = (filteredCard) => {
  const cardTemplate = document.querySelector("#temp-card");
  const div = document.createElement("div");
  div.innerHTML = cardTemplate.innerHTML;

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

  while (cardSection.firstChild) {
    cardSection.removeChild(cardSection.firstChild);
  }

  filteredCard.map((card, i) => {
    const element = div.cloneNode(true);
    const tagContainer = element.querySelectorAll(".tagContainer")[0];
    const tagModalContainer = element.querySelectorAll(
      ".tagModal_container"
    )[0];

    card.tag.map((tag) => {
      let li = document.createElement("li");
      li.innerHTML = tag;
      li.className = "tag";
      tagContainer.appendChild(li);
    });

    card.tag.map((tag) => {
      let li = document.createElement("li");
      li.innerHTML = tag;
      li.className = "tag";
      tagModalContainer.appendChild(li);
    });

    let html = element.innerHTML;
    html = html.replace("{pokemonName}", card.name);
    html = html.replace("{img}", card.img);
    html = html.replace("{poketmonImg}", `${card.name} 이미지`);
    html = html.replace("{JJimN}", `JJimN${i}`);
    html = html.replace("{JJimN}", `JJimN${i}`);
    element.innerHTML = html;

...(중략)

2. 이미지 연동이 분명 안된다고 했던거같지만 도전해 보았더니 되더라구요! 여러분도 도전해보세요!🔥
3. 포켓몬을 추가할때 early return 을 이용해 유효성 검사를 집어넣어보았답니다!
UX의 아주중요한 요소이기도 하니 해보았어요!

//유효성 체크
  if(!pokemonName) return alert("포켓몬이름을 채워주세요!")
  if(!hashTag) return alert("해시태그를 채워주세요!")
  if(!pokemonPhotoInput.value) return alert("이미지를 추가해주세요!")

🥺 소요 시간, 어려웠던 점

  • 코드리뷰반영, 배포 및 낭비시간 제외하고 정말 설계하고 코드짜는데만 10-12시간 정도 걸렸습니다.
  • 코드를 짤때있어 가독성이 정말 중요한데 아직 많이 부족한것같습니다. js메소드(map , filter 등)도 중첩으로 많이쓰면 좋지 않은데 그래서 최대한 2번 이상 안쓰려고 노력했습니다. 그런데도 다시 읽어봐도 어렵네요.... 숙련도 이슈이지만 지속적으로 보며 리팩토링해서 가독성 높은 코드를 짜는 그날까지 노력해보겠습니다. 코드리뷰때 많이 가르침 부탁드립니다.

Copy link

@lydiacho lydiacho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고많았쉐용 메소드를 진짜 잘쓴다!!!! 나도 많이 공부해야겠얼

Comment on lines 68 to 101
<template id="temp-card" type="text/template">
<article class="card">

<div class="card-content">
<header class="pokemonName">
{pokemonName}
</header>

<div class="tag_zone">
<ul class="tagContainer">

</ul>
<button type="button" class="plusTag">+</button>
</div>


<div>
<img src="{img}" alt="{pokemonImg}" />
</div>

<div class="JJim">
<p class="favorite">
<input id="{JJimN}" class="heart" type="checkbox">
<label for="{JJimN}" class="label"></label>
</p>
</div>
</div>
<div class="tagModal">
<div class="tagModal_container">
<div class="exitIcon">&#10008;</div>
</div>
</div>
</article>
</template>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

template으로 html에서 지정해준거 대박이다 나는 createElement로 정신나가는 코드,,, 열심히 썼는데,,, 너무 깔끔핶!!! 이 방법 배워갑니당

});
};

//필터에 따른 카드 생성 함수

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

함수 기능 주석달아주니까 이해하기 넘 편하군용

Comment on lines 79 to 81
while (cardSection.firstChild) {
cardSection.removeChild(cardSection.firstChild);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

카드섹션을 초기화해주는 부분인가욤?

}

filteredCard.map((card, i) => {
const element = div.cloneNode(true);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cloneNode 메소드 처음봤어..!!! (true)는 children까지 모두 복제, (false)는 해당 노드만 복제해주는..!! 신기하당 혹시 이 코드리뷰를 보는 사람 중 나처럼 cloneNode가 처음이신 분들이 있을까봐 https://developer.mozilla.org/ko/docs/Web/API/Node/cloneNode

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우왕 체고다!!!


// navFilterSection 에 필터종류 노출 및 삭제
const addFilteredSection = (activeNavArray) => {
const tagUl = document.querySelectorAll(".tagUl")[0];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지수언니 코리에도 남겨뒀는데, 혹쉬 querySelectorAll과 getElementsByClassName의 차이를 아시나욤?? 혼용하셨길래! 관심있다면 여기 참고 ! https://sirius7.tistory.com/34

}
element.addEventListener('click', () => setCounter(counter + 1))
setCounter(0)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

세미콜론 안넣어도 오류는 안나겠지만 그래도 넣는걸 권장한대용~~ 참고참고 https://ko.javascript.info/structure

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엥 요건 과제가 아니네

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ그의 세미나 공부... 그의 열정...

JJim: false,
evolution: 3,
}
];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상수데이터들 js 따로 뺀거 넘 좋아요~~

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

진짜! 상수데이터를 완전히 js로 따로 뺀 거 넘 잘했다!!

var key = localStorage.key(i);
var newPokemon = localStorage.getItem(key);
const pokemonObj = JSON.parse(newPokemon);
POKEMON_LIST.push(pokemonObj);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const 배열명은 대문자규칙 안쓰는게 일반적인것 같은데!! 뭐 상관은 없을 것 같기두

const evolution = document.getElementById("pokemon-evolution").value;

//유효성 체크
if(!pokemonName) return alert("포켓몬이름을 채워주세요!")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 궁금증인데!! 보통 if문 실행할 때 return은 안쓰지않나?? return이 왜 필요한거야???

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오홍 나두 궁금하다! alert창이면 return 하지 않아도 될 것 같은데,..!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 if 문의 early return 이라는 문법인데
보통 if else 가 많아지면 가독성이 안좋아지기도 하고,
하나의 조건이 맞지 않아 함수컴포넌트를 즉시 종료시키고 싶을때 사용하는 문법이야

따라서 윗 코드는 포켓몬의 이름을 적지 않았다면 아래 코드를 실행시키지말고 alert 을 띄우면서 즉시 함수를 종료시켜라! 라는 의미인거지! 가독성도 좋아지고 아랫코드들을 실행하지 않아 좋은 문법이라 생각돼서 써봤어!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 12 to 16
activeNav.map((activefilter) => {
const evolutionN = activefilter.charAt(activefilter.length - 1);
return POKEMON_LIST.filter((card) => {
if (card.evolution === Number(evolutionN))
return filterdCard.push(card);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메소드 진짜 잘쓴다...!!!!!!!!!!!!!!!!!!!!!!!!!

Copy link

@seojisoosoo seojisoosoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와아,,, 진짜 감탄뿐!!
전반적으로 깔꼼하고 가독성이 좋았던 것 같아요! 함수를 분리할 수 있는 건 조금 더 분리하면 더 좋을 것 같구, 낯선 메소드들을 많이 발견해서 덕분에 열공했습니다!! 최고에요><

}
element.addEventListener('click', () => setCounter(counter + 1))
setCounter(0)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ그의 세미나 공부... 그의 열정...

Comment on lines 2 to 8
{
name: "이상해씨",
tag: ["진화1", "귀여운", "잎사귀", "식물성"],
img: "https://data1.pokemonkorea.co.kr/newdata/pokedex/mid/000101.png",
JJim: false,
evolution: 1,
},

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

데이터를 넣을 때에는 무결성을 위해서 unique key를 부여해주는 것이 좋아요! id:1 요런식으로 각 데이터별로 절대 겹칠 수 없을 만한 고유 키를 넣어주면 좋을 것 같네용~

JJim: false,
evolution: 3,
}
];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

진짜! 상수데이터를 완전히 js로 따로 뺀 거 넘 잘했다!!

@@ -0,0 +1,55 @@
//포켓몬리스트Data 동기화 (로컬스토리지 반영)
for (var i = 0; i < localStorage.length; i++) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var 보다 let, const를 사용해보는 것은 어떨까요?? 사실 var는 오래된 방식이에요! var는 for의 코드블록을 관통하고, 또 변수의 중복 선언을 허용한다는 이유로 인하여 let과 const가 등장하게 되었답니다. 관련된 모던자바스크립트튜토리얼 내용 공유해용~
var, let, const

그런데 또 다른 모던자바스크립트 튜토리얼 내용을 보면 var로 선언하면 전역 객체가 된다고 하네요! 목적에 맞게 변수 형식을 사용하면 좋을 것 같습니다~

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그리고 사실 자바스크립트 문법에서 for를 사용하는 것보다 forEach, map을 이용하는 것이 훨씬 좋다고 해요! 사실 저두 for가 편해서 for를 쓰긴 했지만, forEach, map을 이용해보면 좋을 것 같네요!!

}

//이미지 미리보기 코드
const pokemonPhotoInput = document.getElementById("pokemon-photo");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹쉬 getElementBy*와 querySelector의 차이점을 알고 계실까용! getElementBy가 돔에 바로 반영되고 속도도 빠르다는 장점이 있고, querySelector는 생산성과 편리성이 뛰어나다는 장점이있어요! 모던 자바스크립트 튜토리얼에 따르면 querySelector를 더 많이들 사용한다고 하더라고요 참고참고~

getElementBy* querySelector


</html>

<template id="temp-card" type="text/template">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

템플릿 있는 거 나도 이번에 첨 알았는데 넘 신기하다!! 덕분에 새로운 방법 알아갑니다~

if (activeNav.indexOf("all") !== -1) {
return POKEMON_LIST;
} else {
activeNav.map((activefilter) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맵도 야무지게 잘 썼다!

Comment on lines 29 to 43
switch (name) {
case "all":
navTagName = "전체";
break;
case "evolution1":
navTagName = "진화1";
break;
case "evolution2":
navTagName = "진화2";
break;
case "evolution3":
navTagName = "진화3";
break;
default:
alert("맞는 nav가 없습니다.");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switch, case까지... 진짜 리스펙합니다

Comment on lines 74 to 75
const div = document.createElement("div");
div.innerHTML = cardTemplate.innerHTML;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요기 변수명 좀 더 의미를 담으면 좋을 것 같아요! div라고 하면 무얼 의미하는지 한 번에 와닿지 않을 수 있으니까용!

}

filteredCard.map((card, i) => {
const element = div.cloneNode(true);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우왕 체고다!!!

Chanwoo-Jeong added a commit that referenced this pull request May 3, 2023
[Edit] : a태그사용시 이동이 안되는 문제로 인해 다시 href처리 도전
@Chanwoo-Jeong Chanwoo-Jeong merged commit 9432d1b into main May 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2주차 🏠창업Store 기본 심화 2주차과제 PR입니다
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants