[우테코 6기 FE] 레벨1- 점심 뭐 먹지? 회고

바다·2024년 3월 20일
0

💫우테코_6기

목록 보기
3/6
post-thumbnail

안녕하세요 우테코 6기 FE 바다입니다!.

타입스크립트, 컴포넌트, cypress를 통한 E2E테스트를 진행한 "점심 뭐먹지?"미션이 끝이 나서 미션을 진행하면서 알게 된 점, 겪은 이슈와 느낀 점들을 이야기 해보려 합니다.

💫미션 소개

🏫 미션의 학습 목표

  • 어플리케이션을 컴포넌트 단위로 모듈화하여 개발
    • UI를 컴포넌트 단위로 생각하고 개발하는 연습
    • 재사용할 수 있는 컴포넌트를 고민해보기
  • TypeScript의 기본 문법을 익히며 필요성을 경험
  • 웹 UI 환경에서의 테스트 기초
    • 사용자 관점에서 중요하다고 생각하는 기능을 스스로 정의하고 E2E 테스트로 검증해보기

💻미션 구현

구현 모습


배포

🖱️ 점심 뭐먹지? 배포 페이지 바로가기
🖱️점심 뭐먹지? 저장소 바로가기

💦 custom element 사용기

custom element 사용 이유

이번 미션을 시작하면서 바닐라js로 컴포넌트를 만들 수 있는 법을 찾았고, custom element의 존재를 알게 되었고 이를 잘 활용하며 재사용성을 챙길 수 있다고 생각해 페어와 상의 후 도입해보기로 했습니다.

custom element의 장단점

미션 구현을 하면서 느낀 custom element는 props와 레이아웃 구조가 간단할 수록 재사용하기 사용하기 좋지만 반대로 props로 넘겨주어햐는 것들이 짧은 string타입이 아니거나(ex:배열, 함수) 레이아웃이 복잡하다면 custom element는 불편한 컴포넌트라는 것입니다.

custom element 관련 이슈

connectedCallback과 addEventListener 오류

상황
select 요소가 들어 있는 dropBox 컴포넌트를 connectedCallback으로 생성해주고 , select에 change 관련 이벤트 리스터를 넣어주려 했습니다.

class AllRestaurantList extends RestaurantListTemplate {
  constructor() {
    super();
  }

  connectedCallback() {
    super.connectedCallback();
    //....

    // filtering, sorting dropbox 추가
    this.#addDropBoxGroupToContainer();
    //dropbox 내 select에 이벤트 추가
    this.#addEventToFilteringAndSorting();

원인
connectedCallback을 통해서 drop box를 음식점 리스트의 형제 요소로 넣어주었지만 아직 drop box가 DOM에 구현된 것이 아니라서 connectedCallback 내에서 실행한 이벤트 리스너는 drop box내의 select 요소를 찾을 수 없었습니다.

해결방법
위의 해결방법으로는 비동기로 이벤트를 주거나, Observer를 사용하는 것이 있습니다. 저는 보다 간편한requestAnimationFrame를 사용해 오류를 해결했습니다.

connectedCallback() {
    // 다음 프레임에서 실행되도록 예약
    requestAnimationFrame(() => {
        const selectElement = this.querySelector('drop-box select');
        console.log(selectElement);
    });
}

🖐️ 잠깐!

  • 해당 컴포넌트는 재사용성을 위해 custom element가 아닌 element를 만드는 클래스로 변경되어 현재는 위의 코드로 진행하고 있지 않습니다.
  • 🤔 requestAnimationFrame ?
    • dom을 화면에 그리기 전에 requestAnimationFrame의 콜백함수를 실행하도록 해주는 기능을 합니다.
  • 🤔 DOM은 constructor와 connectedCallback 중 어디서 구현해야할까?
    • 실행 순서는 constructor와 connetedCallback 순으로 실행되어 constructor에서 DOM을 구현하고 connectedCallback에서 이벤트를 주면 되지 않을까 싶지만, custom element의 초기화 및 라이프 사이클 관리 측면에서 문제를 일으킬 수 있어서 constructor 보다는 connetedCallback에서 DOM을 생성하는 것이 권장됩니다.

shadowRoot와 custom element의 캡슐성💊

이번 미션에서 음식점 입력폼 모달과 음식점 상세정보 모달이 존재해서 모달 컴포넌트가 자식요소를 받아서 사용할 수 있게 하고 싶었습니다. (자식 요소로 다양한 모달 내용을 변경될 수 있도록 함) 그래서 shadow와 slot을 이용했습니다.

shadowRoot를 통해서 상황에 따라 필요한 자식요소를 추가할 수 있지만, 추가 해준 자식요소내의 있는 다른 자식요소에 접근 시 다소 불편한 점들이 있었습니다.

document.querySeletor등의 DOM에 접근하기 위한 Web API 사용 시 shadowRoot를 추가해주어야했고, shadowRoot안에 cutom element가 자식요소로 들어가면 부모 요소에서 자식요소인 custom element의 또 다른 자식 요소에 접근할 수 없는 이슈가 발생했습니다.

예시 설명:

Modal
- shadowRoot
	- RestaurantInfo
    	- input

Modal에서 ResturantInfo의 input에 접근하지 못함

shadowRoot안에 있는 custom element의 자식 요소를 custom element의 외부에서 shadowRoot를 통해서도 접근하지 못하는 이유로 shadowRoot와custom element 모두 캡슐화를 진행되고 두 번의 캡슐화로 인해 접근하지 못하는 것 같다는 생각을 했습니다.

props가 다양하거나, 배열,함수등을 props로 받아야할 때 custom element의 한계

리뷰어에게 음식점 리스트의 재사용성에 대해 더 고민해보라는 코드 리뷰를 받았고 custom element로 이를 해결하려 했지만 다음의 한계점들을 느끼게 되었고 custom element를 활용한 기존의 방식을 버리고 관련 레이아웃,기능을 담은 element를 생성하는 클래스를 만들고 이를 상속해서 음식점 리스트외의 다른 요소나 기능이 필요한 컴포넌트를 생성할 수 있도록 진행했습니다. (ex: 음식점 리스트 + 검색 기능)

음식점 리스트 관련 custom element의 한계점

  • attribute로 리스트 관련 속성들을 불러와서 이용할 수 있지만, 리스트의 대상(모든 음식점,자주 가는 음식점), 변경된 필터링 및 정렬 옵션등 기존의 attribute도 많고 확장성을 생각하면 다루어야할 attribute가 많아진다.

  • 단순히 리스트만 보여주는 요소에서는 필터링,정렬 기능이 필요가 없는데 이것을 만약 List라는 컴포넌트를 만든다면 List컴포넌트의 메서드로 정할 필요는 없고, 해당 기능이 필요한 컴포넌트에서 이를 수행하는 것이 맞다.

  • 보다 많이 재사용할 수 있고 List컴포넌트가 음식점 목록을 보여주는 기능에 집중하게 하려면 음식점 목록의 렌더링,재렌더링이 필요한 곳에서 불러야하는 음식점 목록을 props로 넣어주면 좋은데, 이를 attribute로 하면 태그가 데이터를 가지고 있게 되며 세션 스토리지를 활용해 불러야하는 음식점 목록을 넣어주고 이를 사용하는 방법도 있지만 세션을 관리하는 추가 기능이 필요하다.

변경된 음식점 리스트 관련 모듈 구조와 프로세스

🫠 컴포넌트의 재사용과 오버엔지어링

이번 미션을 수행하는 내내 기능이 한정되고 변화 가능성이 없는 우테코 미션내에서의 컴포넌트의 재사용성 범위에 대해 고민을 했습니다.

범용적으로 사용할 수 있도록 기능이나 속성들을 덜 특정/한정 지을 수록 컴포넌트의 재사용성이 더 좋다고 알고 있었는데, 그러면 관리해야하는 파일이나 폴더가 많아지게 되고 우테코 미션의 사이즈에 비해서 파일이나 폴더의 개수가 커지는 것은 아닐까? 기간 내에 미션 구현이 가능할까? 이게 오버엔지어링이 아닐까?하는 생각이 들었습니다.

이와 관련해 리뷰어에게 질문을 했고, 좋은 답변을 받게 되었어요.

🗨️ 👤 오버엔지니어링은 말 그대로 미래를 대비해서 과하게 공수를 들인다거나 하는 걸 의미하는데, 굳이 확장 가능성을 일부러 닫을 필요는 없다는 얘기에요.

🗨️ 👤재사용성을 챙기는데, 미션 진행에 방해가 될 수 있으니 어느 선까지만 지킨다 라는 생각은 뭔가 지금 방향이 잘못되어있다는 뜻인 것 같습니다.

🗨️ 👤 재사용성 또한 결국에는 구현 및 유지보수의 편리함을 위해 추구하는 개념이니, 구현하다가 재사용의 필요성을 느끼면 그때 해주면 되고 재사용성을 챙기다 보면 오히려 관리해야하는 파일과 폴더의 수가 줄어들고 코드가 명확해지면서 관리하기가 쉬워질겁니다.

재사용성도 결국에는 코드를 쉽게 이해하고 유지 보수하기 쉬운 방향으로 나아가는 하나의 방법이기 때문에 우선 순위는 "누구나 이 코드를 쉽게 이해할 수 있는가, 이를 보다 쉽게 이용할 수 있는가"것을 중점으로 두기로 했습니다.😊
또한 컴포넌트의 재사용성의 범위를 정할때 만약 해당 버튼이 더 늘어난다면? 만약 음식 리스트가 또 필요하다면?이라는 가정을 하면서 구현해보기로 했습니다.

✅ Cypress

이전에 vitest로 E2E테스트를 진행한 적이 있었는데, 그때와 비교해보면 cypress가 더 편리했습니다.

vites보다 관련된 자료들이 많고, 오류가 났을 때 어디서 오류가 났는지 오류 해결을 위한 방법도 cypress내에서 추천해주어서 처음 사용해보지만 나름 편리했습니다.

미션 기능 시, "점심 뭐 먹지?"가 단순히 음식점을 메모하는 기능뿐만아니라 사용자에게 정보 전달의 목적도 있다고 생각해 기본 데이터를 넣어주었는데, 만약 기본 데이터를 넣어주지 않았다면 cypress의 fixture라는 기능을 사용해봐도 좋을 것 같습니다.

E2E 테스트와 단위 테스트의 차이

🐳 도메인 로직에 대한 프로그램 내의 동작에 대한 테스트라면 단위 테스트를, UI와 프로그램 내의 동작에 대한 테스트라면 E2E 테스트를 하자!

단위 테스트는 도메인 로직에 대한 테스트로 주요한 기능을 위주로 그에 대한 테스트 했다면 E2E테스트는 사용자 중심에서 기능이 구현 의도대로 동작하는 지 확인하는 테스트 입니다.
이번에 E2E테스트를 구현하면서 사용자의 UI 조작과 그로 인한 액션도 포함시켜야 하고 다양한 사용자의 사용 패턴에 대해서도 고민해봐야한다는 것을 느꼈습니다.

✨ Typescript

타입 내로잉과 타입 가이드

둘 다 타입을 좁혀가는 방법이지만 적용 방식,사용목적과 키워드에서 차이가 있습니다.

차이점타입 내로잉타입 가이드
적용 방식사용자가 명시할 필요 없이 자동으로 됨사용자가 타입을 명시해주어야 함
사용 목적동적으로 타입이 변하는 상황에서 안전하게 타입을 좁혀나갈때 사용특정 조건 하에 변수의 타입을 명확하게 구분하기 위해 사용
키워드특별한 키워드가 필요하지 않음is키워드를 주로 사용

타입 내로잉 예시 코드

function checkType(x: number | string) {
    if (typeof x === 'string') {
        return x.includes("this is string");
    } else {
        return x.toFixed(7);
    }
}

타입 가이드 예시 코드

const isInputElement = (target: EventTarget | null): target is Element => {
  return target instanceof HTMLInputElement;
};
});

typescript는 컴파일 타임과 런타임 중 어느 시점의 안정성을 보장할 수 있나요?

컴파일 타임과 런타임

컴파일 타임런타임
정의소스 코드가 실행 가능한 코드로 변환되는 시점컴파일된 프로그램이 사용자 혹은 시스템에 의해 실제로 실행되고 있는 시점
특징
  • 소스 코드 검증 및 변환 과정
  • 실행 전 에러 검출
  • 문법적 오류, 타입 체크 등
  • 실행 중인 프로그램의 상태,동적 타입 검증, 메모리 관리
  • 실행 중 예외 처리
에러의 발생 시점,종류소스 코드 컴파일 하는 도중에 발견, 주로 코드의 문법 오류프로그램 실행 도중에 에러 발생, 입력값 오류,메모리 접근등 에러 이유가 다양

typescript를 왜 사용해야할까?

타입스크립트의 주요 장점 중 하나는 컴파일 타임에 코드의 안정성을 확보하는 것으로, 오류를 컴파일 타임에서 발견할 수 있으며 더 빨리는 코드를 짤 때 확인할 수 있다는 장점이 있습니다.

🖐️ 마무리

우선은 웹컴포넌트를 사용해본 좋은 경험이였습니다. 직접 부딪히면서 장단점을 배워보았다는 것에 큰 의의를 두고 있습니다. 😊(다음 미션때는 안 사용할거에요 ㅎㅎㅎㅎ 이번 미션으로 충분해요.ㅎㅎㅎ)

또한 코드를 구현하는 방법,과정과 해당 코드에 대한 리뷰를 받아들이는 자세에 대해 많이 생각하는 미션이였습니다.

리뷰 피드백 후 느낀 점

이번 미션을 진행하고 리뷰를 받으면서 다음의 것들을 느끼고 다짐했습니다.

  • 코드를 짜고 말로 설득하지 말고, 먼저 코드 자체로 설득하자
  • 기능 구현 전,중,후에 내가 짠 코드에 대한 구조,프로세스를 시각화해서 확인해보자
  • 지금의 학습 단계의 학습 목표에 집중하자! (과한 걱정은 하지말기!)
  • 지적 받을 용기!
    • 미션을 진행하고 리뷰를 받으면서 앞으로는 우테코 내의 피드백 뿐만 아니라 팀 프로젝트나 현업의 동료 개발자로 부터 많은 피드백을 받고 그 강도가 더 쎄질것 같다는 생각을 하게 되었습니다. 그렇다면 보다 유연하게 코드에 대한 지적을 받아들이는 것에 대해서 연습을 해봐야겠다는 생각을 했습니다.

끝!!!

이렇게 또 한번의 우테코 미션을 끝냈습니다. 🥳💫
새로운 것을 해보고 고민하고 좌절도 해보며 깊은 생각을 할 수 있었던 시간이였습니다.
다음 미션은 보다 더 잘 구현할 수 있기를 바라며,이번 회고를 여기서 마치겠습니다. 🖐️😊🖐️

➕ 우테코 근황

드디어 우테코 출입증이 나왔어요!!!!

profile
🐣프론트 개발 공부 중 (우테코 6기)

0개의 댓글