Experience 2023. 10. 14.

부스트캠프 8기 멤버십 6주차 후기

키워드

this, 파이프 함수, debugger & 개발자 도구,

함수형 프로그래밍, 애니메이션, Vite, Typescript, 타입 추론, 컴포넌트 분리, 이벤트 위임

새로 학습한 내용

함수형 프로그래밍과 상태 관리

함수형 프로그래밍의 목적을 바르게 이해하여 상태 관리 방법에 대한 계획을 세울 수 있었다.

함수형 프로그래밍에서 상태라는 개념은 없다. 함수형 프로그래밍에서 상황에 따라 값이 변경되는 상태라는 것은 없으며, 모든 값은 인자를 통해 함수에 전달되어 immutable 하게 사용되고, 새로운 값의 재료가 되며, 사라진다.

하지만 프론트엔드 컴포넌트를 구현하기 위해서는 반드시 상태가 필요한 순간이 온다. 유저가 현재 선택한 탭, 페이지 등 유저와의 상호작용을 통해 컴포넌트 랜더링을 변경하기 위해서는 하나의 상태를 지정하여, 이 값을 계속해서 관찰하고, 변경해줘야 할 필요가 있다.

그렇다면 함수형 프로그래밍에는 없는 개념인 상태를 관리하기 위해선 어떻게 해야 할까? 해답은 의외로 다른 방면에 있었다. 나는 함수형 프로그래밍을 하면서 상태를 포함한 모든 코드를 완벽한 함수형으로 구현해야만 한다는 생각이 있었다. 하지만 마스터님의 말씀을 통해 내 생각이 잘못됐음을 알 수 있었다.

함수형 프로그래밍이 목적이 아닌, 함수형 프로그래밍을 통해 좋은 모듈을 만드는 것을 목적으로 하여라.

위 말씀을 통해 좋은 모듈을 만들기 위해서 함수형 프로그래밍을 사용하고 있었다는 것을 다시금 깨달을 수 있었다. 함수형 프로그래밍에만 매몰될 필요가 없었던 것이다.

이러한 깨달음을 얻은 후에는 자연스럽게 상태관리도 어떻게 해야 할지 감을 잡을 수 있었다. 상태를 담는 객체를 만들고, 해당 객체의 getter와 setter를 구현하여 store에서 자유롭게 상태를 가져오고, 변경할 수 있는 방식으로 상태관리를 구현하고자 계획하였다.

Vite

Vite의 강력한 컴파일 기능에 대해 학습하고,
이를 바탕으로 기존의 불필요한 script 코드를 제거하여 리소스 낭비를 줄일 수 있었다.

Vite는 저번주에 새로 도입한 번들러이다. 도입할 때 Vite와 더불어 Sass, Typescript를 빌드 설정 해주었었는데, 당시에는 Vite도 Webpack처럼 당연히 Sass —watch 및 컴파일 설정을 따로 해주어야 한다고 생각을 하여 다음과 같이 script 코드를 작성하였다.

"scripts": {
  "dev": "vite & npx sass --watch ./styles:./styles",
  "build": "tsc && vite build",
  "preview": "vite preview"
},

그러나 스크럼 때, 한 캠퍼분이 번들러는 기본적으로 번들링하는 과정에서 해당 언어를 컴파일해주지 않냐고 의문을 제기해 주셨다. 나는 질문을 듣고 바로 대답할 수 없었다. 즉 Vite가 어디까지 지원해 주는지에 대한 공부도 하지 않고 그저 쉽다는 이유 하나만으로 Vite를 쓰고 있었음을 일할 수 있었다.

문제를 인지한 후 바로 Vite의 공식 문서, 특히 컴파일 부분을 자세히 읽어 보았고, 이를 통해 Vite가 기본적으로 정말 다양한 언어들에 대한 컴파일러를 내장하고 있음을 배울 수 있었다. Sass, typescript는 물론 ES6+ 문법, JSON, VUE, JSX 등 기본 지원하는 언어들에 대한 더 자세한 소개는 이 공식사이트에서 볼 수 있다.

이 때 학습한 내용이 너무너무…!! 도움이 되어서 캠퍼분들에게도 공유하는 글도 작성했다.

 

이후 공식 문서를 통한 학습을 바탕으로 다음과 같이 script 코드를 수정하였다. 이를 통해 불필요한 프로그램 실행을 줄여 리로스 낭비를 줄일 수 있었다.

"scripts": {
    "dev": "vite", // npx sass --watch ./styles:./styles 제거
    "build": "vite build",
    "preview": "vite preview"
  },

타입스크립트의 타입 추론 (feat. object)

타입스크립트의 타입 추론 원칙의 중요성을 배웠다.

타입스크립트는 객체 내부에서 선언된 문자열 리터럴의 타입은 해당 리터럴 그대로 타입 추론하지 않고, 그보다 더 넓은 범위인 ‘string’으로 추론하는 원칙을 발견했다. (이 원칙으로 인해 따로 타입을 만들어 선언해 주는 등.. 불필요한 작업을 많이 하게 되었다.)

이러한 타입 추론은 자바스크립트 객체가 변경될 수 있는 값이기 때문에 발생한 추론이었다. 컴파일러 입장에서는 해당 객체에서 선언된 문자열 리터럴이 언제 또 다른 문자열로 변경될지 알 수 없기 때문에, 변경 가능한 범위인 string으로 추론을 하는 것이다.

해당 문제를 해결하는 방법으로는 다음이 있다.

  1. as const를 이용하여 해당 객체가 변경되지 않을 것이라고 컴파일러에게 가르쳐주기
  2. const person = { name : "owall", // as const를 통해 owall이라는 문자열이 변경되지 않음을 알려줄 수 있다. } as const;
  3. 타입 가드 유틸을 만들어 컴파일러에게 해당 타입에 대해 알려주기
  4. 이 방식에 대해서는 추가적인 학습이 필요할 것 같다. 다만 다른 캠퍼분의 조언에 따르면 as를 이용하기 보다 타입 가드를 통해 해결하는 것이 더 현명할 것 같다는 생각이다.

사실 이 문제는 근본적으로 내가 타입스크립트의 타입 추론 원리에 대해서 온전히 학습하고 있지 않아서 생긴 문제였다. 따라서 공식 문서를 통해 타입 추론 원리에 대한 자세한 학습이 필요할 것 같다.

해당 문제가 금요일날 발생되어서 미처 이번주에 학습까지 할 순 없었다. 학습은 다음 주로…😂

교체되는 컴포넌트에 대한 이벤트 관리 (feat. 이벤트 위임)

이벤트 위임을 통해 반복되는 이벤트 생성 로직을 줄여 메모리 리소스를 아낄 수 있었다.

지금 내 프로젝트는 화면의 컴포넌트를 교체할 때, 해당 컴포넌트의 HTML을 템플릿 리터럴로 작성하여 innerHTML로 삽입하는 방식을 사용한다.

이러한 방식은 한가지 단점이 존재했는데, 새로운 컴포넌트를 생성할 때마다 해당 컴포넌트에서 사용하는 이벤트들을 일일이 붙여줘야 했다는 것이다. 예시로 페이지를 넘기는 버튼이 새로 생성될 때마다, 해당 버튼에서 사용하는 클릭 이벤트도 함께 붙여줘야 했다. 이처럼 이벤트를 매번 붙여줘야 하다 보니 컴포넌트가 생성될 때마다 이벤트를 붙이는 함수도 불필요할 정도로 반복적으로 실행되었으며, 이는 꽤나 많은 메모리를 잡아먹었다. (유지 보수도 귀찮았다.)

이벤트 할당을 어떻게 하면 효율적으로 할 수 있을지 고민하던 중, 마스터님을 통해 힌트를 얻을 수 있었다. 바로 이벤트 위임 방식을 이용하는 것이다. 컴포넌트를 생성할 때 해당 컴포넌트의 이벤트를 최상의 컴포넌트에 할당함으로써 이벤트는 컴포넌트가 삭제되었다 재생성되어도 그대로 유지될 수 있었다. 해당 원리에 대해 더 자세히 알고 싶다면 이벤트 버블링에 대해 알아보면 좋다.

사실 아직은 아이디어만 내놓고 코드로 반영하지는 못했다. 아마 다음주 리팩토링 시간에 적용할 듯싶다.

그 외 간단한 것들

마스터, 멘토님의 조언들

변수명은 해당 코드의 역할을 명확히 알 수 있도록 작성하여라.

cf. 변수명의 길이보다는 변수가 충분한 정보를 잘 담고 있느냐가 중요하다.

showPress(); // X
displayPress(); // O

참고 사이트

이름이 긴 메서드가 반복적으로 사용이 되면 유틸로 따로 빼서 사용해도 좋다.

같은 의미일 때, 긴 이름보다는 짧은 이름이 더 빠르게 작성할 수 있으며 가독성이 좋다.

/** 변경 전 */
document.getElementById("root"); // 한번 메서드를 사용할 때 마다 너무 많은 코드를 쳐야 함.

/** 변경 후 */
const getDOMById = (id, target = document) => {
  return target.getElementById(id);
};
getDOMById("root"); // 더 짧고, 가독성이 좋아졌다.

매직 넘버를 사용하지 말고, 상수 값으로 선언해서 사용하라.

매직 넘버란 숫자의 의미가 보는 즉시 명확하지 않고 왜 이 값이 들어갔는지 알기 힘든 수치값들을 의미한다.
매직 넘버를 상수 값으로 선언해 사용함으로써 코드 가독성과 유지보수성을 높일 수 있다.

/** 변경 전 */
if(currentPageNum === 24) {...} // 왜 24가 조건의 기준이 되는지 즉시 알기 힘들다.

/** 변경 후 */
const LAST_PAGE_NUM = 24;
if(currentPageNum === LAST_PAGE_NUM) {...} // 변수명을 통해 코드의 의미를 바로 알 수 있다.

과도한 애니메이션은 좋지 않다.

과도한 애니메이션을 화면 렌더링을 느리게 만들며, 이는 유저가 화면이 버벅거린다고 느끼게 된다.
명심할 것. 유저는 버벅거리는 화면을 매우, 매우 좋아하지 않는다.

애니메이션은 담백하고 필요한 것들로만 구성하는 것이 좋다.

CSS로 구현할 수 있는 애니메이션은 가급적 CSS로 구현하자.

CSS와 Javascript 모두 애니메이션을 구현할 수 있다.
하지만 성능에 있어서 CSS가 Javascript보다 훨씬 빠르다.

함수형 프로그래밍이 목적이 아닌, 함수형 프로그래밍을 통해 좋은 모듈을 만드는 것을 목적으로 하여라.

함수형 프로그래밍을 사용해도 상태는 store라는 형태로 있을 수 있다.
이 store를 mutable 하게 할 것인지, immutable 하게 할 것인지 고민해 보는 것도 좋다.
가능하다면 immutable하게 구현하여 함수형 프로그래밍의 장점을 느껴 보아도 좋다!

모듈을 나누는 데 있어 중요한 것은 모듈의 크기이다.

객체지향인지, 함수형인지에 따라 다르지만 보통은 프로젝트의 규모, 프로젝트의 생명주기를 고려하여
모듈 분리 및 의존성을 결정한다.

짧은 시간 생성하여 빠르게 사라질 프로젝트는 모듈을 크게, 의존성 있게 만들어도 괜찮지만,
거대하고 오래가는 프로젝트에서는 코드가 복잡해질 것을 미연에 방지하기 위해
각 모듈을 작게, 의존성이 적게 만드는 것이 좋다. (이 글은 내 생각)

파일의 가장 하단에는 개행을 추가해 주어라.

참고 사이트

interface 네이밍을 할 때는 맨 앞을 대문자로 해 주는 것이 Typescript 관례이다.

이를 통해 일반 변수와 interface를 혼동하지 않고 사용할 수 있다.

/** 개선 전 */
interface state {...}
const state : state = {...} // 일반 변수명과 interface 변수명이 동일하여 코드의 의문성 증가

/** 개선 후 */
interface State {...}
const state : State = {...} // 대문자를 통해 일반 변수명과 interface 변수명을 구분할 수 있다.

네이밍을 할 때 카멜 케이스와 스네이크 케이스를 혼용하지 말아라.

/** 개선 전 */
.rollingBar-news {...}

/** 개선 후 */
.rolling-bar-news {...}

그 외

  • util 간의 의존성이 없게 하여라.
  • css는 가급적 classList를 통해 핸들링하는 것이 좋다.
  • 적절한 주석은 좋다.

이번 주에 있었던 이벤트들

쉬는 날에도 팀구성으로 바빴던 캠퍼들

이번주 월요일은 한글날이었기에 코어 타임이 존재하지 않았다. 이날 모각코가 열려 참가했었는데, 나를 포함한 대부분의 캠퍼들이 정작 모각코보다는 팀원을 모으는 것이 더 중요한 듯했다 😂

쉬는 날인데도 쉴 새 없이 DM이 오가고, 팀을 모으는 구인글이 올라와 하루종일 슬랙이 시끌시끌했다. 나도 팀을 구하기 위해 진땀 뺐던 기억이…😇

조금은 활성화된 슬랙

멤버십이 시작되고 나서 전반적으로 슬랙이 많이 조용해진 것이 아쉬웠다. 하지만 내가 올렸던 vite 글을 시점으로 마스터님의 적극적인(?) 홍보 하에 기술 채널이 꽤 활성화 된 것 같아 기쁘다! 다들 자신이 학습한 내용을 아낌없이 공유해 주어 더 효율적으로 많은 지식을 학습할 수 있게 되었다.

새로운 방식의 피어 세션

이번주는 구현한 코드가 아닌, 주제 중심으로 피어 세션을 진행해 보자는 의견이 나왔다! 그 동의 피어 세션들은 구현한 코드를 위주로 이야기가 쭉 진행되었는데, 가끔의 예외적인 코드를 제외하면 대부분의 피어분들의 발표 내용이 비슷하여 전체적으로 지루해진다는 아쉬움이 있었다.

그래서 이번주는 색다르게 주제 중심으로 피어 세션을 해보자!라는 의견이 나왔고, 모두 동의하여 해당 방식으로 피어 세션을 진행하였다. 결과는 정말 대만족이었다. 애니메이션, 타입스크립트 타입, 모듈과 컴포넌트, 이벤트 할당 등 다양한 주제에 대해 더 다양한 캠퍼들의 의견들이 나왔고, 서로의 의견을 주고받는 과정에서 더욱 깊은 학습을 할 수 있었다. 무엇보다, 끝까지 지루하지 않았다!! 다음 주에도 이 방식으로 진행하자고 캠퍼분들께 제안할 것 같다.

이번주 회고 및 다음주 다짐

이번주부터 학습을 중요시하며 다시 학습 정리를 작성하기 시작했는데, 아주 만족스럽다. 구현에만 급급해하지 않고 내가 학습해야 하는 것에 깊게 파고들며 집중하니 남는 것이 있어 뿌듯하다.
다음 주에서 이번주처럼 나의 학습 속도를 유지하는 것이 목표이다. 그리고 가능하다면 정리하여 기술 글 작성해서 공유하기..!