Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

프론트엔드

Youngjin Park edited this page Aug 26, 2023 · 1 revision

동글 팀의 근거 있는 TypeScript 컨벤션

컨벤션으로 들어가기 전에

  • TypeScript는 우리 팀에 도움이 되고 있나요? 어떤 측면에서 도움이 되고, 혹은 어떤 측면에서는 어려움이나 불편을 겪고 있나요?
    • 미리 선언되어 있는 타입을 통해서 처음 보는 코드를 이해하는 데 도움이 된다.
    • 타입을 통해 런타임 전에 타입 오류를 잡을 수 있다.
  • 우리 팀에서 TypeScript를 사용할 때 중요하게 생각하는 부분은?
    • 타입 네이밍과 올바른 타입 선언(any❌)
    • 동일한 도메인에 대해서 동일한 타입 이용(분산되지 않은 타입 선언)

Component

선언 방식(함수 선언문 vs 표현식)

Component는 화살표 함수를 이용하여 선언한다.

const Component = () => { return ... };

export default Component;
  • 함수 선언 코드 작성의 일관성 유지
  • this바인딩 관련 문제가 발생하지 않음

Props

type vs interface vs inline

type GetWritingResponse = {
  id: number;
  title: string;
  content: string;
};

// styled-components
const LeftSidebarSection = styled.section<{ isLeftSidebarOpen: boolean }>`
	display: ${({ isLeftSidebarOpen }) => !isLeftSidebarOpen && 'none'};
`,
  • Type에 interface 를 사용하지 않고 type 을 사용
    • interface의 선언 병합(declaration merging)을 통해 타입을 보강(augment)하는 기능을 원하지 않았기 때문
  • union type(|)이 필요한 경우 type 을 사용하는데 일관성 유지를 위해 모두 type 사용
  • styled-components의 경우 inline 타입을 사용

Component with Children / Component without Children

VFC, FC, PropsWithChildren

  • React.FC로 children이 있는 props를 사용하려면 React.FC<React.PropsWithChildren<Props>> 이런 식으로 사용해야 한다.
  • 제네릭이 겹쳐서 코드가 복잡해지고, PropsWithChildren을 사용하면 더 간단하게 코드를 작성할 수 있고 직관적이다.
  • children 을 강제해야 하는 경우 PropsWithChildren 을 사용하지 않고 Props 타입에 추가한다
  • Children, render메서드/컴포넌트 반환 타입(JSX.Element vs React.ReactElement vs ReactNode)
    • ReactNode: 다수의 children 허용(elements, strings, numbers, fragments, portals, null 등)

      type ReactNode =
              | ReactElement
              | string
              | number
              | Iterable<ReactNode>
              | ReactPortal
              | boolean
              | null
              | undefined
      type Props = {
        children: ReactNode;
      };
      
      const CustomLayout = ({ children }: Props) => {
        return <>{children}</>;
      };
      
      export default CustomLayout;
      
      • children prop 와 같이 불특정 다수의 Element들을 받아야할 때 ReactNode를 이용한다
    • React.ReactElement: 다수 children 허용 x

      export type Props = {
        ...
        icon?: ReactElement;
      } & ComponentPropsWithRef<'button'>;
      
      // Component
      const Button = (
        {
          ...
          icon,
          ...rest
        }: Props,
        ref: ForwardedRef<HTMLButtonElement>,
      ) => {
        return (
      			...
              {Boolean(icon) && <S.IconWrapper size={size}>{icon}</S.IconWrapper>}
      			...
        );
      };
      

      위와 같이 icon 컴포넌트 하나가 와야하는 경우 ReactElement를 이용한다.

    • JSX.Element: 다수 children 허용 x, props 와 type 의 타입이 any인 ReactElement

Event

Event Handler vs Event

const removeTag: MouseEventHandler<HTMLButtonElement> = (event) => { ... };

이벤트 핸들러 이름은 내부 동작을 잘 나타내도록 짓기로 했기 때문에(ex. removeTag) 이벤트 핸들러임을 명시적으로 나타내주기 위해 Event Handler를 사용한다.

  • import 방식 MouseEventHandler

    이벤트의 import 방식으론 React.[type]를 사용하지 않고, [type]만 명시한다.

Hooks

기본 훅

const [data, setUser] = useState<User | null>(null);
const [writingId, setWritingId] = useState<number | null>(null);
const [nickname, setNickname] = useState<string | null>(null);

string이나 number같이 빈 값을 나타내는 것을 명시적으로 할 때 null이 없으면 애매하다.

Ref

RefObject 의 경우 초기 값을 null로 지정한다.

const inputRef = useRef<HTMLInputElement>(null);

MutableRefObject 의 경우 초기 값을 지정하지 않는다.

const count = useRef<number>();

참고(MutableRefObject, RefObject 타입)

interface MutableRefObject<T> {
	current: T;
}

interface RefObject<T> {
	readonly current: T | null;
}

모듈

type 관리 방식

  • 네이밍 컨벤션

컴포넌트 내의 Props 타입 네이밍은 Props 로 한다.

type Props = {
	...
};
  • Props 타입은 컴포넌트에서 선언되므로 네이밍을 굳이 [Component]Props 로 하지 않아도 의미가 명확하다.

  • 해당 Props 타입이 필요한 곳에서는 as 를 이용해 네이밍을 바꿔서 이용한다.

    • import { Props as WritingViewerProps } from 'components/WritingViewer/WritingViewer';
  • 제네릭을 사용할 때, 명시적인 이름을 사용합니다.

// BAD 👎
const customFetch = <T, U>(url: T, options: U) => { ... }

// GOOD 👍
const customFetch = <URL, OPTIONS>(url: URL, options: OPTIONS) => { ... }
  • 타입 이름에 Type 을 붙이지 않는다.
// BAD 👎
type ApiType = { ... }

// GOOD 👍
type Api = { ... }
  • 타입 이름을 명시적으로 작성하여 이름만 보고도 어떤 타입인지 쉽게 알 수 있다.
  • 디렉터리 구조

image

- `src`폴더 바로 밑에서 성격 별로 정리한다
  • hook 분리
    • 하나의 컴포넌트에서만 사용되는 훅은 해당 컴포넌트 내에 위치한다.
    • 최상단 훅 폴더
      • 라이브러리 성격 (common)
      • 도메인 종속적이지만 재사용 가능한 (폴더 없이)

type import/export

API

Request / Response Type

  • API 호출 로직에서 Request / Response 데이터를 다루는 방식.
  • types/apis 폴더에 Request & Response 타입을 정의한다.
export type GetWritingResponse = {
  id: number;
  title: string;
  content: string;
};

export type GetWritingPropertiesResponse = {
  createdAt: Date;
  isPublished: boolean;
  publishedAt: Date;
  publishedTo: Blog;
};

빌드 설정

loader

  • TS 컴파일을 위해 어떤 loader를 사용하고 있는지와 선택 이유
  • ts-loader
    • 빌드 타임에 tsc를 이용해서 타입 검사를 해주기 위해
    • babel-loaderpreset/typescript 는 별도의 타입 검사를 수행하지 않음

tsconfig

  • 설정 기준과 설정한 항목들에 대한 이해
{
  "compilerOptions": {
    "baseUrl": "./src",                       // 모듈 이름을 해석하기 위한 기본 디렉터리
    "paths": {                                // 절대 경로 설정
      "*": ["*"]
    },
    "target": "ES2021",                       // ES2021 버전에 대한 코드를 생성
    "lib": ["DOM", "DOM.Iterable", "ESNext"], // 컴파일러가 포함해야 하는 라이브러리 목록
    "jsx": "react-jsx",                       // JSX 처리 방식
    "module": "ESNext",                       // 사용할 모듈 시스템
    "moduleResolution": "Node",               // 모듈 해석 방식
    "sourceMap": true,                        // 소스 맵 생성 여부
    "esModuleInterop": true,                  // CommonJS와 ES Modules간의 호환성 설정
    "forceConsistentCasingInFileNames": true, // 파일 이름의 대소문자 일관성을 강제하는 설정
    "strict": true,                           // 엄격한 타입 체크 옵션 설정
    "noImplicitAny": true,                    // 암시적인 'any' 타입에 오류를 발생
    "skipLibCheck": true                      // 타입 체킹 시에 .d.ts 파일을 건너뜀
  },
  "include": ["src", "__tests__"],            // 컴파일할 파일 또는 디렉토리 목록
  "exclude": ["node_modules"]                 // 컴파일에서 제외할 파일 또는 디렉토리 목록
}

서비스 타겟 환경 및 브라우저 지원 범위

동글이 선정한 서비스 타겟 환경과 브라우저 지원 범위를 소개합니다.

서비스 타겟 환경: PC

image

  • 동글의 주요 기능은 블로그에 쓸 글을 **업로드(import)**하고 올라온 글들을 블로그에 포스팅 하는 서비스
  • 모바일 대응도 할 수는 있겠지만 서비스 특성 상 모바일이 핵심 타겟 환경은 아니라고 생각해서 PC 버전을 우선적으로 지원하기로 결정.

브라우저 지원 범위: Chrome, Edge, Whale, Ark

image

  • 크게 Top3인 Chrome, Edge, Whale 을 지원하기로 결정하였습니다.

image

  • WhaleArk 브라우저는 크로미움 기반으로 개발되기 때문에 호환성에 크게 문제가 없을 것이라고 판단하였습니다.
💡 총 **93.69%** 라는 브라우저 지원 수치를 지원하게 되었고 이는 모두 브라우저에 들어가서 제대로 작동이 되는지 확인하였습니다.

Chrome 브라우저

image

Edge 브라우저

image

Whale 브라우저

image

Ark 브라우저

image

Clone this wiki locally