-
Notifications
You must be signed in to change notification settings - Fork 8
코딩 컨벤션
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";
- 컴포넌트 내부에 정의
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 순서
- 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";
...
- 자식 요소들이 모두 같은 color나 font-weight를 취할경우 부모 요소에서 스타일링
- 자식 요소들중 특정 요소만 다른 요소들과 다른 color나 font-weight를 취해야할 경우 인라인으로 스타일링
반응형 웹 UI 구현을 위해 사용하는 라이브러리: react-responsive
<예제 이미지>
- 데스크탑 & 태블릿:
- 모바일:
<예제 코드>
// 예시 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과 같은 조건으로 전체 렌더링을 하는 코드를 작성
<예제 이미지>
- 데스크탑 & 태블릿:

- 모바일:

<예제 코드>
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으로 분기처리하지 않음.
- 데스크탑이나 태블릿 버전에는 없는 기능이 생기는 경우가 있는데, 이 경우 컴포넌트를 분리하지 않고 기존 컴포넌트에 기능 추가에 의해 필요한 코드를 작성.
기존 코드
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로 유지됨
break point에 따라서 UI가 변경되는 부분은 TitleSet 내부에서 처리됨으로 이를 사용하는 컴포넌트에서는 특별히 추가할 부분은 없음.
기존 코드
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양식과 동일하게 구성