Skip to content

코딩 컨벤션

Angelo edited this page Aug 23, 2022 · 27 revisions

컴포넌트

컴포넌트 네이밍

  • 컴포넌트 이름, 폴더 이름, 파일의 이름은 모두 동일하게 구성
// 파일 및 폴더 구조
src/components/HomeNavigationBar/HomeGlobalNavigationBar.tsx
src/components/HomeNavigationBar/index.ts
// HomeNavigationBar.tsx
import React from "react";

const HomeGlobalNavigationBar: React.FC = () => {
  return (...);
};

export default HomeGlobalNavigationBar;
// index.ts
export { default as HomeGlobalNavigationBar } from "./HomeGlobalNavigationBar";

컴포넌트 props타입 및 스타일링 코드 위치

  • 컴포넌트 내부에 정의
import React from "react";

...

// interface
interface ILinkButton {
  description: string;
  title: string;
  to: string;
}

// component
const LinkButton: React.FC<ILinkButton> = ({ description, title, to }) => {
  return (...);
};

// styled-components
const LinkButtonWrapper = styled(Link)`
  ...
`;

import 양식

  • import 순서
    • package-things
    • Theme
    • Typography
    • Components
    • Assets (VS Code에서 보여지는 순서)
    • Libs
import React from "react";
import styled from "styled-components";
import Link from "gatsby-link";
// Theme
import theme from "styles/theme";
// Typography
import { XSBody } from "typography/";
// Components
import { SButton } from "components/";
// Assets
import upArrow from "assets/images/icons/arrow-up.svg";
import { BUTTON, LINK, MESSAGE } from "assets/static/phrases";
import { EXTERNAL, INTERNAL } from "assets/static/urls";
// Libs
import { getCurrentPath } from "lib/utils";
...

Typography 관련

  • 자식 요소들이 모두 같은 color나 font-weight를 취할경우 부모 요소에서 스타일링
  • 자식 요소들중 특정 요소만 다른 요소들과 다른 color나 font-weight를 취해야할 경우 인라인으로 스타일링

컴포넌트 (반응형 관련)

반응형 웹 UI 구현을 위해 사용하는 라이브러리: react-responsive

break point에 따라서 렌더링 코드 변경점이 많은경우 (공용 컴포넌트)

<예제 이미지>

  • 데스크탑 & 태블릿: 스크린샷 2022-08-23 오후 6 24 54
  • 모바일: 스크린샷 2022-08-23 오후 6 25 03

<예제 코드>

// 예시 1-1: 데스크탑 UI인지 여부에 따라서 전혀 다른 typography 컴포넌트를 사용하고있음.
const LinkButton: React.FC<ILinkButton> = ({ description, title, to, icon, caption, external }) => {
  const {
    breakPoint: { tablet, desktop },
  } = useTheme();
  const isMobile = useMediaQuery({ query: `(max-width: calc(${tablet} - 1px))` });
  const isDesktop = useMediaQuery({ query: `(min-width: ${desktop}` });

  return (
    <>
      {isDesktop && (
        <LinkButtonWrapper ... >
          <div>
            {description ? (
              <Description>
                <MBold>{description}</MBold>
              </Description>
            ) : null}
            <Title>
              <SHLBold>{title}</SHLBold>
              <img aria-label="arrow-right" src={icons.chevronRight} width="24px" height="24px" />
            </Title>
            {caption ? (
              <Caption>
                <XSBold>{caption}</XSBold>
              </Caption>
            ) : null}
          </div>
          {icon ? <img alt="link-icon" src={icon} width="54px" height="54px" /> : null}
        </LinkButtonWrapper>
      )}
      {isMobile && (
        <LinkButtonWrapper ... >
          <div>
            {description ? (
              <Description>
                <XSBold>{description}</XSBold>
              </Description>
            ) : null}
            <Title>
              <MBold>{title}</MBold>
              <img aria-label="arrow-right" src={icons.chevronRight} width="24px" height="24px" />
            </Title>
            {caption ? (
              <Caption>
                <XSBold>{caption}</XSBold>
              </Caption>
            ) : null}
          </div>
        </LinkButtonWrapper>
      )}
    </>
  );
};
  • 전체를 isDesktop여부에 따라 2개의 분기로 나누지않고 하나의 return에서 각 typography를 isDesktop여부에 따라 조건부 렌더링도 가능.
    • 하지만 위처럼 전체적으로 변경되는경우에는 isDesktop과 같은 조건으로 전체 렌더링을 하는 코드를 작성

페이지 컴포넌트에 반응형 UI 적용

<예제 이미지>

  • 데스크탑 & 태블릿:
스크린샷 2022-08-23 오후 6 35 48
  • 모바일:
스크린샷 2022-08-23 오후 6 37 01

<예제 코드>

const Feature: React.FC = () => {
  const {
    breakPoint: { tablet },
  } = useTheme();
  const isMobile = useMediaQuery({ query: `(max-width: calc(${tablet} - 1px))` });

  const { color } = useTheme();

  const { title, subtitle, description, image }: FeatureType = strainMdxInfo(
    useStaticQuery(FeatureQuery)
  );
  const [detailView, setDetailView] = React.useState(false);

  const splittedDescription = getSplittedPhrase(description);

  return (
    <FeatureWrapper>
      <TitleSet title={TITLE.FEATURE} subtitle={SUBTITLE.FEATURE} bigSubtitle />
      <ContentWrapper>
        <FeatureImg src={features[image]} alt="feature" />
        <Content>
          <div>
            {isMobile && <SHLBold style={{ color: color.greyScale.grey1 }}>{title}</SHLBold>}
            {!isMobile && <HLBold style={{ color: color.greyScale.grey1 }}>{title}</HLBold>}
            <SBold>{subtitle}</SBold>
          </div>
          {splittedDescription.map((descriptionItem: string, index: number) => {
            if (isMobile && !detailView && index > 0) return null;
            return <MBody key={descriptionItem}>{descriptionItem}</MBody>;
          })}
        </Content>
      </ContentWrapper>
      {isMobile && (
        <EButton
          children={TITLE.VIEW_DETAIL}
          disabled={detailView}
          onClick={() => setDetailView(true)}
        />
      )}
    </FeatureWrapper>
  );
};

const ContentWrapper = styled.div`
  display: flex;
  white-space: pre-line;
  & > *:not(:last-child) {
    margin-right: 13.2rem;
  }
  @media (min-width: ${({ theme }) => theme.breakPoint.mobile}) {
    flex-direction: column;
  }
  @media (min-width: ${({ theme }) => theme.breakPoint.desktop}) {
    flex-direction: row;
  }
`;

const Content = styled.div`
  display: flex;
  flex-direction: column;
  & > *:not(:last-child) {
    margin-bottom: 1.6rem;
  }
  @media (min-width: ${({ theme }) => theme.breakPoint.mobile}) {
    margin-top: 3.2rem;
  }
  @media (min-width: ${({ theme }) => theme.breakPoint.desktop}) {
    width: 41.1rem;
  }
`;

const FeatureImg = styled.img`
  @media (min-width: ${({ theme }) => theme.breakPoint.mobile}) {
    width: 31.2rem;
    height: 26.6rem;
  }
  @media (min-width: ${({ theme }) => theme.breakPoint.desktop}) {
    width: 51.9rem;
    height: 44rem;
  }
`;

const FeatureWrapper = styled.div`
  width: 106.2rem;
  padding: 0 18.9rem;
  padding-bottom: 18rem;
  display: flex;
  margin: 0 auto;
  flex-direction: column;
  color: ${({ theme: { color } }) => color.greyScale.grey2};
  & > *:not(:last-child) {
    margin-bottom: 5.6rem;
  }
`;

const ContentWrapper = styled.div`
  display: flex;
  white-space: pre-line;
  & > *:not(:last-child) {
    margin-right: 13.2rem;
  }
`;

const Content = styled.div`
  width: 41.1rem;
  display: flex;
  flex-direction: column;
  & > *:not(:last-child) {
    margin-bottom: 1.6rem;
  }
`;

const FeatureImg = styled.img`
  width: 51.9rem;
  height: 44rem;
`;

컴포넌트 렌더링 코드

  • 렌더링되는 컴포넌트가 많이 바뀌지 않고 특정 break point에 따라 기능이 추가되므로 예시1과는 다르게 return되는 전체 렌더링 코드를 isDesktop으로 분기처리하지 않음.
  • 데스크탑이나 태블릿 버전에는 없는 기능이 생기는 경우가 있는데, 이 경우 컴포넌트를 분리하지 않고 기존 컴포넌트에 기능 추가에 의해 필요한 코드를 작성.

UI 코드

FeatureWrapper

기존 코드

const FeatureWrapper = styled.div`
  width: 106.2rem;
  padding: 0 18.9rem;
  padding-bottom: 18rem;
  display: flex;
  margin: 0 auto;
  flex-direction: column;
  color: ${({ theme: { color } }) => color.greyScale.grey2};
  & > *:not(:last-child) {
    margin-bottom: 5.6rem;
  }
`;

변경된 코드

const FeatureWrapper = styled.div`
  display: flex;
  flex-direction: column;
  color: ${({ theme: { color } }) => color.greyScale.grey2};
  @media (min-width: ${({ theme }) => theme.breakPoint.mobile}) {
    padding: 0 2.4rem;
    padding-bottom: 12rem;
    & > *:not(:last-child) {
      margin-bottom: 2.4rem;
    }
  }
  @media (min-width: ${({ theme }) => theme.breakPoint.desktop}) {
    width: 106.2rem;
    padding: 0 18.9rem;
    padding-bottom: 18rem;
    margin: 0 auto;
    & > *:not(:last-child) {
      margin-bottom: 5.6rem;
    }
  }
`;
  • 컴포넌트 전체를 감싸는 FeatureWrapper에서 display관련 값이나 color는 데스크톱 이외의 UI에서도 변경되지 않으므로 유지
    • TitleSet (제목) 및 ContentWrapper (이미지 / 설명) 을 위에서 아래로 렌더링하는것은 동일하므로 display속성의 값은 flex로 유지됨
TitleSet

break point에 따라서 UI가 변경되는 부분은 TitleSet 내부에서 처리됨으로 이를 사용하는 컴포넌트에서는 특별히 추가할 부분은 없음.

ContentWrapper

기존 코드

const ContentWrapper = styled.div`
  display: flex;
  white-space: pre-line;
  & > *:not(:last-child) {
    margin-right: 13.2rem;
  }
`;

변경된 코드

const ContentWrapper = styled.div`
  display: flex;
  white-space: pre-line;
  & > *:not(:last-child) {
    margin-right: 13.2rem;
  }
  @media (min-width: ${({ theme }) => theme.breakPoint.mobile}) {
    flex-direction: column;
  }
  @media (min-width: ${({ theme }) => theme.breakPoint.desktop}) {
    flex-direction: row;
  }
`;
  • 기존에는 이미지와 설명을 좌/우로 배치하여 flex-direction속성의 값을 기본값인 row로 설정했음.
  • 변경된 UI에서는 모바일 환경의 경우 좌/우 배치를 상/하 배치로 변경하므로, mobile인 경우에는 flex-direction값으로 column을 설정함

테스트 코드

import 양식

  • 컴포넌트 import양식과 동일하게 구성