Coder Social home page Coder Social logo

chaedie / pre-onboarding-7th-2-1-7 Goto Github PK

View Code? Open in Web Editor NEW

This project forked from wanted-fe-7th-team7/pre-onboarding-7th-2-1-7

0.0 0.0 0.0 4.75 MB

원티드 프론트엔드 프리온보딩 7차 7팀 2-1 과제 레포리토리입니다

Shell 0.71% TypeScript 97.31% HTML 1.98%

pre-onboarding-7th-2-1-7's Introduction

team7-week2-1

원티드 프론트엔드 프리온보딩 7차 7팀 2-1 과제 레포리토리입니다

배포 주소

https://wanted-fe-7th-team7.github.io/pre-onboarding-7th-2-1-7/cars

폴더구조

📦src
 ┣ 📂apis
 ┃ ┗ 📜api.ts
 ┣ 📂components
 ┃ ┣ 📂UIs
 ┃ ┃ ┣ 📜Button.tsx
 ┃ ┃ ┣ 📜Category.tsx
 ┃ ┃ ┣ 📜HeaderBar.tsx
 ┃ ┃ ┣ 📜IconBack.tsx
 ┃ ┃ ┣ 📜Image.tsx
 ┃ ┃ ┣ 📜ListHeader.tsx
 ┃ ┃ ┣ 📜ListItem.tsx
 ┃ ┃ ┣ 📜StyledText.tsx
 ┃ ┃ ┗ 📜Tag.tsx
 ┃ ┣ 📜CarList.tsx
 ┃ ┣ 📜DetailName.tsx
 ┃ ┣ 📜Error.tsx
 ┃ ┣ 📜Loading.tsx
 ┃ ┣ 📜MetaTag.tsx
 ┃ ┗ 📜NoData.tsx
 ┣ 📂hooks
 ┃ ┗ 📜useFetch.ts
 ┣ 📂interfaces
 ┃ ┗ 📜Cars.ts
 ┣ 📂pages
 ┃ ┣ 📜DetailPage.tsx
 ┃ ┗ 📜ListPage.tsx
 ┣ 📂styles
 ┃ ┣ 📜GlobalStyle.tsx
 ┃ ┣ 📜Layout.tsx
 ┃ ┗ 📜theme.ts
 ┣ 📂utils
 ┃ ┣ 📜isNewCarItem.ts
 ┃ ┣ 📜time.ts
 ┃ ┗ 📜variables.ts
 ┣ 📜App.tsx
 ┣ 📜index.tsx
 ┗ 📜react-app-env.d.ts
  1. apis : api 통신 함수 관리
  2. components : 프로젝트에서 사용되는 컴포넌트 관리
    1. UIs: 공통으로 사용되는 UI 컴포넌트 관리
  3. hooks : 공통으로 사용되는 hooks 관리
  4. interfaces : 공통으로 사용되는 interface 관리
  5. pages : 페이지 단위 컴포넌트 폴더
  6. styles: 공통으로 사용되는 스타일 관리
  7. utils : 공통으로 사용되는 기타 함수 관리

API

const http = axios.create({
  baseURL: process.env.REACT_APP_BASE_URL,
});

export const getCars = async (selectedType: string) => {
  const url = types[selectedType];
  const res = await http.get(url);
  return res.data.payload;
};

const types: Types = {
  전체: `/api/cars`,
  대형: `/api/cars?segment=${'E'}`,
  중형: `/api/cars?segment=${'D'}`,
  소형: `/api/cars?segment=${'C'}`,
  전기: `/api/cars?fuelType=${'ev'}`,
};

interface Types {
  [index: string]: string;
}
  • API의 엔드포인트가 1개라서 그에 맞게 깔끔하게 코드를 작성함.
  • 사용되는 types의 경우 Object 형식으로 관리함.
  • types는 키 값을 카테고리의 각 태그 라벨과 일치시켜 별다른 변환 과정없이 불러오도록 작성함.

로딩 / 에러 처리

function useFetch(
  setState: Dispatch<SetStateAction<Cars[]>>,
  selectedType: string
) {
  const [isLoading, setIsLoading] = useState(false);
  const [errors, setErrors] = useState(false);

  useEffect(() => {
    const handleFetch = async () => {
      setIsLoading(true);
      try {
        const data = await getCars(selectedType);
        setState([...data]);
      } catch (error) {
        setErrors(true);
      } finally {
        setIsLoading(false);
      }
    };

    handleFetch();
  }, [setState, selectedType]);

  return [isLoading, errors];
}

export default useFetch;
  • useFetch hook 을 만들어 서버와 통신하는 과정에서 발생하는 로딩과 에러를 관리함.
  • 이렇게 관리된 state는 아래 코드처럼 분기를 나눠 로딩과 에러가 발생했을 때 어떻게 동작하는지 한 눈에 알아볼 수 있도록 작성함.
    • 자세한 내용은 ListPage 참고.

ListPage

useFetch hook 을 이용해 데이터 로딩 상태에 따라 isLoading 을 리턴합니다.

function useFetch(
  setState: Dispatch<SetStateAction<Cars[]>>,
  selectedType: string
) {
  const [isLoading, setIsLoading] = useState(false);
  const [errors, setErrors] = useState(false);

  useEffect(() => {
    const handleFetch = async () => {
      setIsLoading(true);
      try {
        const data = await getCars(selectedType);
        setState([...data]);
      } catch (error) {
        setErrors(true);
      } finally {
        setIsLoading(false);
      }
    };

    handleFetch();
  }, [setState, selectedType]);

  return [isLoading, errors];
}
  • 차량 없을 때 처리
const [isLoading, errors] = useFetch(setCars, selectedType);
const isEmpty = !isLoading && cars.length === 0;
{isEmpty && <NoData />}
  • 차량 불러오는 중 처리
const [isLoading, errors] = useFetch(setCars, selectedType);
{isLoading && <Loading />}
  • 차량 리스트
const [cars, setCars] = useState<Cars[]>([]);
const [isLoading, errors] = useFetch(setCars, selectedType);
const isEmpty = !isLoading && cars.length === 0;
const hasCarList = !isLoading && cars.length > 0;

// ... 생략

{hasCarList && <CarList cars={cars} />}

Header

function HeaderBar({ title }: Props) {
  const navigate = useNavigate();
  return (
    <StyledHeaderBar>
      {title === '차량상세' ? (
        <div className="ICON_back" onClick={() => navigate(-1)}>
          <IconBack />
        </div>
      ) : (
        <div />
      )}
      {title}
      <div />
    </StyledHeaderBar>
  );
}
  • Hearder 컴포넌트로 넘어오는 props를 이용해서 DeatailPage, ListPage인지 확인
  • 삼항연산자를 이용해 간편하게 페이지마다 다른 렌더링할 수 있도록

Category

  • Click 시 selectedType state를 category 이름으로 변경
  • 선택 되었는지 여부를 통해 버튼 Type 지정
<Category selectedType={selectedType} setSelectedType={setSelectedType} />
//...
interface Props {
  selectedType: string;
  setSelectedType: Dispatch<SetStateAction<string>>;
}

function Category({ selectedType, setSelectedType }: Props) {
  return (
    <Wrapper>
      {CATEGORIES.map(category => (
        <Tag
          key={category}
          onClick={() => setSelectedType(category)}
          color={selectedType === category ? 'black' : 'gray'}
          placeholder={category}
        />
      ))}
    </Wrapper>
  );
}

DetailPage

  • useLocation()을 사용하여 원하는 정보를 가져와 화면에 보이도록 하였습니다
function DetailPage() {
  const location = useLocation();
  if (!location.state) {
    return <Error />;
  }

  const { startDate, insurance, additionalProducts, amount } = location.state;
  const { brand, name, segment, fuelType, imageUrl } = location.state.attribute;

  return (
    <Layout>
      <MetaTag car={location.state} />
      <Wrapper>
        <HeaderBar title="차량상세" />
        <Image src={imageUrl} size="DETAIL_PAGE" />
        <DetailName brand={brand} name={name} amount={amount} />

        <ListHeader title="차량 정보" />
        <ListItem name="차종" title={SEGMENT[segment]} />
        <ListItem name="연료" title={FUEL_TYPE[fuelType]} />
        <ListItem name="이용 가능일" title={Time.parseStartDate(startDate)} />
        <ListHeader title="보험" />
        {insurance.map(({ name, description }: Insurance) => (
          <ListItem key={name} name={name} title={description} />
        ))}
        <ListHeader title="추가 상품" />
        {additionalProducts.map(({ name, amount }: AdditionalProducts) => (
          <ListItem
            key={name}
            name={name}
            title={`월 ${amount.toLocaleString()} 원`}
          />
        ))}
      </Wrapper>
    </Layout>
  );
}

Styled-Components

export const theme = {
  black: '#000000',
  gray: '#D9D9D9',
  blue: '#0094FF',
};

export const flex = (
  justifyContent = 'center',
  alignItems = 'center',
  flexDirection = 'row'
) => {
  return `
  display: flex;
  justify-content: ${justifyContent};
  align-items: ${alignItems};
  flex-direction: ${flexDirection};`;
};

export const width = {
  sm: '360px',
  lg: '450px',
};

export const HEADER_HEIGHT = '6rem';
export const CATEGORY_HEIGHT = '4rem';
  • 자주 사용하는 CSS는 미리 함수를 만들어 재사용 가능하도록 작성함.
  • 특정 값을 나타내는 값의 경우 상수로 지정하여 어떤 값인지 명료하게 작성함.
export const StyledTag = styled.button<StyledProps>`
  ${flex()}
  height: 2.7rem;
  padding: 0rem 1.8rem;
  margin: 0 0.4rem;
  flex-shrink: 0;

  ${({ color }) => COLOR_TYPE[color]}

  font-size: 1.4rem;
  appearance: none;
  border: none;
  border-radius: 6.2rem;
  cursor: pointer;
`;

const COLOR_TYPE: ColorType = {
  black: `
    background-color: #000000;
    color: #ffffff;
  `,
  blue: `
    background-color: #0094FF;
    color: #ffffff;
  `,
  gray: `
    background-color: #D9D9D9;
    color: #000000;
  `,
};
  • 분기 처리가 필요한 부분의 경우, 해당 분기를 객체로 만들어 인자 값에 따라 특정 CSS를 반환할 수 있도록 처리.

SEO 처리 (추가 구현 사항)

  • react-helmet-async 라이브러리를 활용
  • MetaTag 컴포넌트를 생성해주어 DetailPage 최상단에 차량 정보를 메타 태그에 할당
  • 브라우져 Element 탭에서 메타 태그 확인 가능
  • 배포, 공유 시 메타 태그 노출 미구현
const MetaTag = ({ car }: Props) => {
  const {
    amount,
    attribute: { brand, name, imageUrl },
  } = car;

  const title = `${brand} - ${name}`;
  const description = '월' + amount.toLocaleString() + '원부터';
  const url = window.location.href;
  const keywords = '알티모빌리티';

  return (
    car && (
      <Helmet>
        <title>{title}</title>

        <meta name="description" content={description} />
        <meta name="keywords" content={keywords} />

        <meta property="og:type" content="website" />
        <meta property="og:title" content={title} />
        <meta property="og:site_name" content={title} />
        <meta property="og:description" content={description} />
        <meta property="og:image" content={imageUrl} />
        <meta property="og:url" content={url} />

        <meta name="twitter:title" content={title} />
        <meta name="twitter:description" content={description} />
        <meta name="twitter:image" content={imageUrl} />

        <link rel="canonical" href={url} />
      </Helmet>
    )
  );
};

export default MetaTag;

Git convention

Tag Name Description
Feat 새로운 기능 추가
Fix 에러 수정
Docs 문서 수정
Style 코드 포맷팅, 세미콜론 누락, 코드 변경이 없는 경우
Refactor 코드 리팩토링
Test 테스트 추가, 테스트 리팩토링
Chore 빌드 업무 수정, 패키지 매니저 수정

pre-onboarding-7th-2-1-7's People

Contributors

chaedie avatar idjaeha avatar so0112 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.