실전프로젝트 보일러 플레이트 분석

설탕유령·2022년 6월 27일
0
post-custom-banner

실전 프로젝트를 위해 제공 받은 보일러 플레이트 프로젝트를 분석하고자 한다
https://github.com/rlagudals95/team3_final_project

기술 스택은 다음과 같다

  • Recoil JS
  • Windi CSS
  • Vite
  • axios
  • react-query
  • styled-components
  • eslint
  • prettier
  • typescript

시작은 main.tsx
main에서는 windi css를 import 한다.
index.css는 margin을 0으로 초기화 하고, font-family를 지정한다.
React를 StrictMode(잠재적인 문제를 알아내기 위한 dev 도구)로 설정 후 App을 호출한다

import React from "react";
import ReactDOM from "react-dom/client";
import "virtual:windi-devtools";
import "virtual:windi.css";

import App from "./App";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

main에서 호출되는 App.tsx
전역에서 사용되는 라이브러리들(react-query, reciol 등)의 초기 셋팅을 위해 queryClientProvider, ReciolRoot등을 선언해 감싸준다.

import React, { Suspense } from "react";
import { QueryClient, QueryClientProvider } from "react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { RecoilRoot } from "recoil";

import "./App.css";
import Footer from "./containers/Footer";
import Header from "./containers/Header";
import MainPage from "./pages/MainPage";

// test
const queryClient = new QueryClient();
function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <RecoilRoot>
        <BrowserRouter>
          <Header />
          <div className="App">
            <div className="bg-secondary-1 flex items-center min-h-screen bg-white dark:bg-gray-900">
              <div className="container max-w-screen-lg xl:max-w-screen-xl mx-auto">
                <div className="max-w-md mx-auto my-10 w-full">
                  <Routes>
                    <Route path="/" element={<MainPage />} />
                    <Route path="/myPage" element={<MainPage />} />
                  </Routes>
                </div>
              </div>
            </div>
            <Footer />
          </div>
        </BrowserRouter>
      </RecoilRoot>
    </QueryClientProvider>
  );
}

export default App;

./App.css에는 text-align: center;가 적용되어
<div className="App">가 적용된 모든 하위 요소가 중앙 정렬이 기본이다.
css 부분에 windiCSS가 적용되었고 간단히 설명하자면
flex, items-center는 말 그대로 display 방식이나 정렬 방식을 지정
hin-h-screen은 min-height: 100vh; 최소 높이를 뷰포트 100%로 맞춘다
dark: 표식이 붙은 경우는 다크 모드인 경우에만 적용함을 뜻한다

Container는 화면의 크기에 맞춰서 max-width의 기준을 나눠준다(미디어 쿼리 개념)
xs:, sm:, md:, lg 등은 사이즈를 지정하는 태그로 태그 중간에 들어가는 경우 적용 css의 크기, xs: 등으로 시작하는 경우는 css를 적용할 크기 기준으로 동작한다.
CSS에 대해서는 내용이 많고 이름에서 유추가 가능한 경우가 많이 때문에, 이후부턴 필요한게 아니면 생략한다.

MainPage.txs
기능별로 나뉘어진 components에서 불러오는 형식으로 Card를 가져왔으며,
태그 지정후 넘겨줄 Props 값을 지정함

import React, { Suspense } from "react";

import Card from "../components/Card";

const MainPage = () => {
  return (
    <>
      <Card
        cardTitle="카드 타이틀"
        cardDesc="카드 내용"
        styleCustom={{
          background: "white",
          width: "100%",
          height: "200px",
          border: "1px solid grey",
          padding: "8px",
        }}
      />
    </>
  );
};

export default MainPage;

Card.tsx
코드가 본격적으로 길어지는데, 눈 여겨 봐야 할 형식은
Color 템플릿을 만들어 가져오는 COLOR_V2
StyleCustom이라는 이름으로 인터페이스를 만들고 각 요소 앞에 width?:string; 형태로 ?의 속성을 통해 올 수 있거나 안올수도 있음을 명시

styled.div<any>의 형식으로 스타일이 적용된 객체 명시시, 속성이 부여될 수 있는 요소에는 <any> 태그를 적용

소스코드가 길고 복잡하지만, 재사용을 위한 방식이라고 한다.
어떻게 사용할지 감이 잘 안잡히긴 한다.

import React from "react";
import styled from "styled-components";

import { COLOR_V2 } from "../constants/ColorV2";

export type CardType = "coupon" | "normal";

interface StyleCustom {
  width?: string;
  height?: string;
  margin?: string;
  background?: string;
  color?: string;
  hoverColor?: string;
  activeColor?: string;
  disabledColor?: string;
  padding?: string;
  border?: string;
  fontSize?: string;
  titleColor?: string;
  descColor?: string;
  expirationColor?: string;
}

interface IProps {
  cardType?: CardType;
  cardTitle?: string;
  cardDesc?: string;
  cardExpiration?: string;
  className?: string;
  backgroundColor?: string;
  onClick?: (e: React.MouseEvent) => void;
  disabled?: boolean;
  styleCustom?: StyleCustom;
  cardPromotionValue?: string;
}

function Card(props: IProps) {
  const { onClick, styleCustom, cardTitle, cardDesc, className }: IProps =
    props;

  return (
    <>
      <CardWrapper
        className={className}
        styleCustom={styleCustom}
        onClick={onClick}
        color={COLOR_V2.WHITE1}
        hoverColor={COLOR_V2.PRIMARY5}
        activeColor={COLOR_V2.PRIMARY_ACTIVE}
        disabledColor={COLOR_V2.PRIMARY_DISABLED}
      >
        <CardContentWrapper>
          <CardTitleWrapper styleCustom={styleCustom}>
            {cardTitle}
          </CardTitleWrapper>
          <CardDescWrapper styleCustom={styleCustom}>
            {cardDesc}
          </CardDescWrapper>
        </CardContentWrapper>
      </CardWrapper>
    </>
  );
}

export default Card;

const CardWrapper = styled.div<any>`
  background-color: ${(props) => props.styleCustom?.background ?? ""};
  color: ${(props) => props.styleCustom?.color ?? ""};
  border-radius: 8px;
  border: ${(props) => props.styleCustom?.border ?? ""};
  padding: ${(props) => props.styleCustom?.padding ?? ""};
  font-style: normal;
  font-weight: 600;
  font-size: ${(props) => props.styleCustom?.fontSize ?? "18px"};
  line-height: 22px;
  text-align: left;
  letter-spacing: -0.02em;
  width: ${(props) => props.styleCustom?.width ?? ""};
  height: ${(props) => props.styleCustom?.height ?? ""};
  margin: ${(props) => props.styleCustom?.margin ?? ""};

  @media screen and (max-width: 680px) {
    width: 100%;
  }

  &:hover {
    background-color: ${(props) => props.styleCustom?.hoverColor ?? ""};
  }
  &:active {
    background-color: ${(props) => props.styleCustom?.activeColor ?? ""};
  }
  &:disabled {
    background-color: ${(props) => props.styleCustom?.disabledColor ?? ""};
  }
`;

const CardContentWrapper = styled.div`
  display: flex;
  flex-direction: column;
  height: 100%;
`;

const CardTitleWrapper = styled.div<any>`
  font-size: 18px;
  font-weight: 700;
  line-height: 140%;
  display: flex;
  align-items: center;
  text-align: center;
  letter-spacing: -0.02em;
`;

const CardDescWrapper = styled.div<any>`
  margin-top: 16px;
  font-weight: 400;
  font-size: 12px;
  line-height: 90%;
  display: flex;
  letter-spacing: -0.02em;
`;

예제 소스상 연결은 끝났고, 다음은 프로젝트 내부에 존재하는 다른 재사용 가능한 요소들을 설명하겠다.
helpers라는 폴더에는 다양한 도구들이 존재한다.
animationHelper.ts: reveal라는 클래스명을 가진 요소들에 애니메이션을 적용하는 페이지로 요소의 TOP 위치가 윈도우상 높이에 특정 수치(150)을 뺀 값보다 작아지는 경우, 즉 드래그를 하면서 내리다가 특정 요소가 어느정도 보이면 active라는 클래스 명을 추가하고, 아니면 제거해주는 방식으로 동작 하는 것 같음
formmatHelper.ts: 포멧 지정을 위한 함수로 전화번호, 이메일 기능이 존재
msTimeToDateFormat.tsx: 시간 데이터 형식을 처리하기 위한 요소로 date 타입이나 밀리초 단위의 날짜 데이터를 String으로 구성된 날짜 형식으로 리턴
SameDay.ts: 입력 받은 두 날짜 객체를 비교해 같은 날인지 비교
screenHelper.ts: URL 관련해 지금이 특정 path에 존재하는지, 현재 url을 가져오는 등의 작업을 수행
taskHelper.ts: 특정 Function을 실행 시 시간에 따라 task를 관리하는 기능인듯 함(확실하지가 않음)
TypingHelper.ts: ClassNmae을 지정해 해당 객체의 텍스트를 기준으로 입력 데이터를 제한하는 용도인듯 함(확실하지 않음)
ValidateHelper.ts: 유효성 검증을 위한 것으로 ID, PW, Phone 등의 유효성 검증 기능이 존재

profile
달콤살벌
post-custom-banner

0개의 댓글