JavaScript

프론트엔드 프로젝트 캠프 사전교육 1주차에는 자바스크립트 기본 문법을 다시 공부하는 시간을 가졌다. 자바스크립트를 이미 알고 있었지만 다시 공부하면서 새롭게 알게 된 것들이 있었다.

새로 알게 된 것들

1. typeof null의 값은 object이다.

typeof null을 콘솔창에 출력해보면 object가 나온다. 그 이유는 원래 객체 참조가 있어야 하는데 참조가 없음을 나타내는 값으로 만들어졌기 때문이다.

null 외에도 typeof []과 같이 배열의 타입을 출력해봐도 object가 나온다.
그렇다면 배열의 타입은 배열, null의 타입은 null과 같이 정확한 타입을 출력하고 싶으면 어떻게 하면 될까?

Object.prototype.toString.call(null).slice(8, -1); // Null

Object.prototype.toString()메서드를 사용하면 된다. 이 메서드는 객체를 [Object Type]형태의 문자열로 표현하는 기능을 한다.
따라서 위의 코드는 '[Object Null]' 문자열의 8번째 글자부터 끝까지 잘라내는 역할을 한다. 따라서 'Null'이 출력된다.

console.log(Object.prototype.toString.call({})); // [object Object]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(123)); // [object Number]
console.log(Object.prototype.toString.call('Hello')); // [object String]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call(Symbol('sym'))); // [object Symbol]

2. NaN은 숫자 타입이다.

NaN이 Not-A-Number를 의미하는 숫자 타입의 값이다. 숫자 타입이지만 숫자로 나타낼 수 없는 값이기에 NaN이라고 표시한다.

console.log(typeof NaN); // "number"

3. getter / setter 함수

getter 함수와 setter 함수는 객체 속성의 값을 읽거나 설정할 때 사용한다.
getter 함수는 get 키워드를 이용하여 정의할 수 있고 setter 함수는 set 키워드를 이용하여 정의할 수 있다.

const person = {
  firstName: "John",
  lastName: "Doe",
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },
  set fullName(name) {
    [this.firstName, this.lastName] = name.split(" ");
  },
};

console.log(person.fullName); // "John Doe"

person.fullName = "Jane Smith";
console.log(person.firstName); // "Jane"
console.log(person.lastName);  // "Smith"

getter 함수와 setter 함수의 특징은 일반 함수처럼 소괄호를 열고 닫아 호출하는 것이 아니라 프로퍼티에 접근하는 방식과 동일하게 사용할 수 있다.

아토믹 디자인 패턴(Atomic Design Pattern)

아토믹 디자인 패턴은 컴포넌트를 atoms / molecules / organisms / templates or pages로 나누어 설계한다.

  • 원자(atoms)는 가장 작은 구성 요소이다. 더 이상 나눌 수 없는 단위이며, button, input, labe 등이 해당된다.
  • 분자(molecules)는 원자들을 조합하여 만든 작고 독립적인 기능을 가진 구성 요소를 의미한다. input과 button을 조합하여 만든 검색 창 같은 컴포넌트가 분자에 해당한다. 하나 이상의 원자로 이루어져 있으며 특정 기능을 수행한다.
  • 유기체(organisms)는 분자, 원자를 조합하여 더 복잡한 컴포넌트를 만든 것이다. 헤더나 푸터, 카드 컴포넌트 등이 유기체에 해당한다.
  • 템플릿(templates)는 유기체와 컴포넌트를 배치하여 구조적 가이드를 제공한다. 실제 데이터나 스타일을 포함하지는 않는다.
const MainTemplate = ({ header, content, footer }) => (
  <div>
    {header}
    <main>{content}</main>
    {footer}
  </div>
);

페이지(pages)는 템플릿에 실제 데이터를 적용한 것을 의미한다.

const HomePage = () => (
  <MainTemplate
    header={<Header />}
    content={<HomeContent />}
    footer={<Footer />}
  />
);

React: 날씨 앱 API를 이용한 프로젝트


(이미지 출처: 9Diin의 엉망진창 개발시리즈)

만들고자 하는 날씨 앱 프로젝트의 결과물은 위와 같다.

리액트 수업을 진행하기 전, 혼자서 레이아웃 작업을 해보는 과제가 있었다.
내가 제출한 과제의 결과물은 아래 사진과 같다.

혼자서 레이아웃 작업을 하면서, 여러 번의 프로젝트를 제작한 경험이 있지만 여전히 버벅이는 것이 많아 배울 것이 많겠다고 느꼈다. 이 코드에서는 어떤 태그를 써야 하고 어떻게 스타일링을 하는게 맞는지 헷갈리는 일이 많았다.

수업을 들으면서 차근차근 컴포넌트를 구성했는데, 그 과정에서 어떤 흐름으로 컴포넌트를 분리하는 것이 좋을지 다시 한 번 감을 잡게 되었다.

  • 일단 컴포넌트 분리 없이 코드를 작성해본다.
  • HTML 태그로 대략적인 틀을 잡고 레이아웃을 시작한다. 주석을 활용하는 것이 좋았다.
  • 재사용이 가능한 부분을 따로 컴포넌트로 분리한다. Props를 어떻게 넘길지 고민할 수 있는 부분이었다.

그리고 컴포넌트를 분리하고 파일을 관리하는 과정에서 어떤 구조로 파일을 관리할 수 있을지에 대해서도 고민할 수 있었다.
수업을 수강한 후에 배운 내용을 바탕으로 과제 코드를 리팩토링 하는 과제가 주어졌다. 나는 과제를 진행하면서 아토믹 디자인 패턴을 적용해보았다.

  • 특정 기능이 없는 컴포넌트들은 atoms로 구분한다. 온도를 표시하는 영역을 하나의 atoms로 묶어 관리했다. 두 개의 태그로 구성되어 있지만 특정 기능이 없기 때문이다.
  • 여러 개의 atoms로 구성된 컴포넌트는 molecules로 구분한다. 앱 화면 카드 속 카드 헤더나 작은 카드들이 molecules에 해당한다.
  • 여러 개의 atoms, molecules로 구성된 컴포넌트는 organisms로 구분한다. 앱 화면의 큰 카드 하나가 organisms에 해당한다.

컴포넌트 파일들을 직접 atoms, molecules, organisms로 구분하면서 감을 익혀보기도 하고 고민할 수 있는 시간을 가져서 의미있었다.

코드

Jotai: 전역 상태 관리 라이브러리

날씨 앱에서 전역 상태를 관리하는 라이브러리로 Jotai를 사용했다. 나는 Recoil을 사용해본 경험이 있기 때문에 구조가 비슷한 Jotai를 사용하기에는 용이했다.
데이터가 여러 컴포넌트에 걸쳐 전역적으로 사용되기 때문에 전역 상태 관리가 필요하다. 이 앱에서 필요한 전역 상태는 지역에 관한 상태이다. 날씨 검색 지역에 따라 모든 컴포넌트에서 출력되는 결과물이 달라질 것이기 때문이다.

atom

import { atom } from 'jotai';

export const cityNameAtom = atom<string>('seoul');

도시 이름을 atom을 이용하여 전역 상태로 관리할 수 있게 한다. atom은 쉽게 말해 하나의 전역 상태를 만든다고 생각하면 된다.

그리고 이 atom을 사용하기 위해서는 useAtom 훅을 사용하면 된다.

import { useEffect, useState } from 'react';
import { useAtom } from 'jotai';
...
import { cityNameAtom } from '@/store';

function HomePage() {
  ...
  const [cityName] = useAtom(cityNameAtom);

  useEffect(() => {
    fetchApi(cityName, setWeatherData);
    fetchTideApi(cityName, setTideData);
    getOneWeekWeather(cityName, setOneWeekWeatherSummary);
  }, [cityName]);

  return (
    <div className="page bg-gray-200">
      <div className="page__container">
        <Header />
        <div className="w-full flex flex-col items-center justify-start pb-6 px-6 gap-6">
          {/* 상단 3개의 위젯 */}
          <div className="w-full flex items-center gap-6">
            <TodayWidget data={weatherData} />
            <HourlyWidget data={weatherData.forecast.forecastday[0]} />
            <MapWidget />
          </div>
          {/* 하단 2개의 위젯 */}
          <div className="w-full flex items-center gap-6">
            <HighlightsWidget tideData={tideData} currentData={weatherData} />
            <WeeklyWidget data={oneWeekWeatherSummary} />
          </div>
        </div>
      </div>
    </div>
  );
}

export default HomePage;

Recoil도 간편하게 전역 상태 관리를 할 수 있지만 <RecoilRoot>로 컴포넌트를 감싸는 일을 따로 해주어야 한다. Jotai에서는 그런 단계를 하지 않아도 쉽게 전역 상태를 사용할 수 있다.
아직 Jotai Documents를 다 읽어보지 않아서 다 알지는 못하지만 Recoil과 비슷하고 더 간단한 라이브러리로 보였다. 추후에 Jotai에 대해서도 정리하는 시간을 가져보도록 하겠다.


  • FSD 아키텍쳐에 대해서도 배웠지만 개념이 어려워서 따로 공부 후 포스트를 작성하도록 하겠습니다.

본 후기는 [유데미x스나이퍼팩토리] 프론트엔드 프로젝트 캠프 과정(B-log) 리뷰로 작성 되었습니다.

profile
이제는 더 이상 물러날 곳이 없다

0개의 댓글