인프런 워밍업 클럽 스터디 1기 FE | 미션1, 미션2 발자국

인프런 워밍업 클럽 스터디 1기 FE | 미션1, 미션2 발자국

image

미션1: 음식 메뉴 앱

간단한 음식 목록을 카테고리 별로 분류하여 보여주는 앱

📇 레포지토리 주소: https://github.com/zldnlto/inflearn-warming-up/tree/main/01_%EC%9D%8C%EC%8B%9D%20%EB%A9%94%EB%89%B4%20%EC%95%B1

  • header, main과 같은 큰 골격을 index.html에 짜 두고 기능이 필요한 요소들은 동적으로 생성하여 DOM에 붙이는 방식으로 구현하였다.

  • 데이터 페칭을 다루는 방식이 미숙하다고 생각하여, 짧은 데이터지만 굳이 data.json 을 생성해서 페칭을 모사해보았다.

     

  • HTML validator 통과, 카드 부분에 <figure>,<figcaption> 사용하는 등 시멘틱한 태그 작성

     


    주요 로직

  • MenuList 라는 객체 배열을 통해 메뉴 버튼을 생성하고 관리한다.

  • init() 함수 는 앱 최초 진입시 실행되며 전체 카드 목록을 불러온다. ('All'이 디폴트)

     

  • renderCard() 함수는 html 요소 템플릿을 찍어내는 함수다. 각각의 Card 컴포넌트를 생성한다.

  • handleFilterBtn() 을 통해 버튼 id와 일치하는 foodData를 필터링한다.


const renderCard = (data) => {
  let newCard = "";
  if (data) {
    data.forEach((v) => {
      const menuItem = `<li class="card">
          <figure class="card-content">
            <img
              src="./assets/${v.image}"
              alt="${v.name}"
              class="card-cover"
            />
            <figcaption class="card-text">
              <div class="card-title">
                <strong>${v.name}</strong>
                <div class="price">${v.price}원</div>
              </div>
              <span class="hr"></span>
              <p class="card-info">
              ${v.description}
              </p>
            </figcaption>
          </figure>
        </li>`;
      newCard += menuItem;
    });
  }
  return newCard;
};

각각의 요소를 따로 생성하기보다는 Card컴포넌트를 생성할 때 <li> 태그 이하의 자식 요소들이 많은 점을 고려하여 innerHTML 로 덩어리 템플릿을 이어붙이고 렌더링 하는 방식으로 구현하였다.

 

아쉬운 점 / 느낀점 :

오랜만에 vanillaJS를 다뤄서 그런지 파일명과 같은 작은 부분들에서 '이게 맞나' 하는 생각의 연속이었다.

프레임워크/라이브러리를 주로 사용했다고 한들 간단한 기능 위주임에도 생각보다 시간이 걸려서 아쉽다. 기능 위주로 구현하면 된다고 하셨는데 이왕이면 하는 마음을 버릴 수가 없어서 디자인도 조금씩 고민하게 된다.
간단한 기능일텐데 코드가 생각보다 길다. 더 효율적으로 짜는 방법이 있을 것 같다.

 


image

미션2: 가위 바위 보 앱

컴퓨터와 가위바위보!

📇 레포지토리 주소: https://github.com/zldnlto/inflearn-warming-up/tree/main/02_%EA%B0%80%EC%9C%84%EB%B0%94%EC%9C%84%EB%B3%B4%20%EC%95%B1

 

미션 프로젝트들을 한 레포지토리에 관리하려 하는데 root 경로에 common 폴더를 만들고 전반적으로 임포트해 사용할 reset.css 파일을 생성하였다.

 

주요 로직

가위바위보 게임의 특성상 -1,0,1 숫자를 이용한 Matrix를 생성하여 승패를 판가름하였다.

const resultMatric = [
  [0, -1, 1], // rock
  [1, 0, -1], // scissors
  [-1, 1, 0], // paper
];

기능 별 함수를 분리시켜서 캡슐화시키는 부분에 신경썼다.

가위,바위,보 버튼을 눌렀을 때 화면 동작이 발생한다. 그런 의미에서 메인이 되는 함수를 뽑자면 각 버튼의 이벤트 함수라고 생각하였다. handleScissorBtn, handleRockbtn, handlePaperbtn 버튼을 보았을 때 어떤 동작이 일어나는지 한 번에 보이도록 하였고 코드는 아래와 같다.

 

const handleScissorBtn = () => {
  USER_PICK = 1;
  COMPUTER_PICK = generateComputerPick();

  calculateScore(USER_PICK, COMPUTER_PICK);
  resultRender(USER_SCORE, COMPUTER_SCORE);

  return USER_PICK;
};
  • generateComputerPick() 0~2 사이의 난수를 리턴한다.

  • calculateScore() 유저의 패와 컴퓨터의 패를 인자로 받아 resultMatric 기반으로 승패를 계산한다.

  • resultRender() 승패 결과를 렌더링한다. 남은 횟수가 0이 되면 결과를 띄운다.

     

     

그 외 코드

  • calculateResult() 게임이 끝난 후 유저가 이겼는지 컴퓨터가 이겼는지 결과만을 판단해 렌더링한다.

아쉬운 부분

const resultRender = (USER_SCORE, COMPUTER_SCORE) => {
  REMAINING_POINT -= 1;
  remainigPoint.innerText = REMAINING_POINT;
  userScore.innerText = USER_SCORE;
  computerScore.innerText = COMPUTER_SCORE;

  if (REMAINING_POINT === 0) {
    // 아쉬운 부분
    resultText.innerText = "";
    remainingBox.style.display = "none";
    rcpBtnBox.style.display = "none";
    selectText.innerText = calculateResult();
    selectText.style.fontSize = "3.5rem";
    restartBtn.style.display = "block";
    return;
  }
};

남은 도전 횟수가 0이 되면 가위바위보 각 버튼과 선택하기 텍스트 등을 화면에서 숨기고 게임 결과와 재도전 버튼을 보여주어야 한다. 이 부분을 각각의 요소들을 일일이 display="none" css 처리를 하는 식으로 동작시키고 있는데, classList.add 메서드를 이용하여 코드를 줄이는 방식으로 리팩터링 할 수 있을 것 같다.

또한 selectText 변수가 선택하기 텍스트를 보여주며 경우에 따라 게임 결과도 표현하고 있는데 변수명이 포괄적이지 못해 아쉽다.

변수를 생성할 때는 영문 기반이라 바위-가위-보 순으로 생각하게 되는데 버튼은 가위 바위 보 순이어서 소소한 통일성 이슈가 있다. 우선 버튼 순서 빼고는 전자로 구현되어 있다.
버튼의 코드 정렬 순서와 가위바위보의 0,1,2 매칭 순서, 그리고 약어로 rsp인데 rcp라 오타나있는 부분 등 통일성 부분에서 개선이 필요하다. (rcpBtnBox)

 

느낀 점

html 태그를 단순히 많이 생성하는 것을 지양하기 위해 body 태그에 id='root' 속성을 부여하고 DOM을 생성하려 했는데 body 안의 <script> 태그 때문에 의도한 대로 작동하지 않았다. 사소한 실수지만 React에서 요소를 렌더링 할 때 <body>를 그대로 쓰지 않고 <div id='root'></div> 를 사용하는 이유가 렌더링 공간을 따로 분리하는 의도였구나 생각한다.

VanillaJs를 하면서 오히려 React의 편리함 측면에서 교훈을 많이 얻게 된다. 특히 DOM을 일일이 변경시키는 작업을 통해 React에서 상태가 변화하면 UI가 자동으로 바뀐다는 게 얼마나 경이롭고 편리한 기능인지 느꼈다.

미션 1과는 달리 이번에는 모든 요소를 js에서 생성해보았는데, html 코드로 골격을 어느정도 짜 놓은 상태에서 로직을 붙이듯 작성하는 것이 html / css / js 가 기능적으로 분리되어 가독성 측면에서 적절하다 생각한다.

 

댓글을 작성해보세요.

채널톡 아이콘