-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[클린코드 리액트 3기 조용준] 페이먼츠 미션 Step2 (#148)
* chore: chromatic fetch-depth 설정 제거 * refactor: src/shared 경로는 root re-export 개선 * feat: FormatInput, PinInput 컴포넌트 추가 * feat: FormatInput, PinInput stories 추가 * remove: CardInput 관련 컴포넌트 삭제 * feat: xstate를 적용한 Funnel 로직 변경 * refactor: @shared 코드 선언을 절차적으로 order 변경 * refacotr: 카드 추가 확인의 별칭이 빈 입력 값인 경우, 카드 브랜드 이름으로 반영 * refactor: Funnel 폴더 경로 이동 및 goToIndex assign 누락 개선 * refactor: FormatInput, PinInput 컴포넌트 적용, 카드 추가시 등록된 카드 검증, 생성일 추가 * refactor: Card 도메인 코드는 src/card path 변경 및 변수, 함수명을 컨벤션으로 개선 * refactor: 카드완료 페이지의 카드완료시 이미 등록된 카드는 별칭만 변경되도록 개선 * feat: 카드 클릭시 완료페이지 전환 및 카드 삭제 기능 추가 * refactor: 카드 도메인이 아닌 컴포넌트는 shared/components path 변경 및 컴포넌트 별 역할에 충족하는 storybook 테스트 추가 * refacotr: useToggle 관심사 제거 및 isValidateMonthString 네이밍 개선 * refactor: App.js Funnel index를 CardPageIndex를 활용해서 반영 * refactor: AppLayout 네이밍을 AppDisplay로 변경 * refactor: 컴포넌트의 return 상위에는 개행을 추가하도록 컨벤션 적용 * fix: letterSpacingValue의 fontSize unit 검증을 추가 * refactor: 반복되는 타입은 축약해서 활용하도록 개선 * refactor: 카드 추가 페이지의 Input constant 요소 분리 * docs: Step2 요구사항 문서 추가 * refactor: Card stories 타이틀 변경 * refactor: 컨벤션에 따른 함수명 네이밍 개선 * fix: FormatInputTextCounter의 inputRef 초기 변경 감지되지 않는 이슈 개선
- Loading branch information
Showing
118 changed files
with
2,137 additions
and
1,315 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# 🚀 Getting Started | ||
> 복잡한 흐름의 Stepper State Machine 으로 구현합니다. | ||
- [x] 모바일 타겟의 웹 앱을 구현하며 사용하기 편리한 모바일 UI/UX에 대해 고민해봅니다. | ||
- [x] Stepper 기반으로 작성한 애플리케이션을 State Machine XState로 구현합니다. | ||
- Funnel (Funnel 기존 로직을 XState로 변경했어요.) | ||
- [x] 재사용 가능한 Component를 직접 작성하고 사용합니다. | ||
- [x] Controlled & Uncontrolled Components에 입각하여 Form을 핸들링합니다. | ||
|
||
# 📝 Requirements | ||
### 필수 요구사항 | ||
- [x] 원시적인 형태의 Primitive UI 형태의 컴포넌트 작성 | ||
- [x] Storybook 상호 작용 테스트 | ||
- [x] Controlled & Uncontrolled Components를 명확하게 구분하거나 선택하여 구현 | ||
- [x] 설계한 Stepper을 XState Visualizer를 활용하여 구현 | ||
### 카드 추가 확인 | ||
- [x] 이전 폼에서 입력된 카드를 보여준다. | ||
- [x] 카드 별칭을 입력할 수 있다. | ||
- [x] placeholder는 카드 별칭 (선택)이다. | ||
- [x] 빈 입력값인 경우, 카드사 이름이 별칭으로 저장된다. | ||
- [x] 최대 길이는 10자리이다. | ||
- [x] 확인 버튼을 누르면, 카드 목록 페이지로 이동한다. | ||
### 카드 목록 | ||
- [x] 카드 목록을 조회할 수 있다. | ||
- [x] 카드 목록은 최신순(내림차순)으로 정렬된다. | ||
- [x] 목록 최상단에 +을 누르면 카드 추가 페이지로 이동한다. | ||
- `요구사항 변경` 최하단에 카드 추가영역을 고정하고, 카드 리스트는 스크롤되도록 한다. | ||
- [x] 카드를 클릭하면, 카드 별칭 수정(카드 추가 완료 페이지)로 이동한다. | ||
- [x] 카드를 삭제할 수 있다. | ||
|
||
|
||
# 📚 리뷰 개선 사항 | ||
### 코드 구조 및 순서 | ||
- [x] 코드를 절차적으로 순서를 변경했어요. | ||
- 파일 내 먼저 사용되는 함수를 상단으로 이동했어요. | ||
- [x] 관심사의 사용 범위가 다른 파일은 관심사에 맞게 분류한다. | ||
- Card 도메인 코드는 src/card 폴더로 분리했어요. | ||
- [x] shared 폴더는 barrel 적용하여, re-export 한다. | ||
- [x] provider 코드는 폴더를 별도로 분리한다. | ||
|
||
### 컴포넌트 및 UI | ||
- [x] 컴포넌트는 활용 용도에 맞게 Storybook 테스트를 작성한다. | ||
- Flex, Stack, Grid 등 컴포넌트 역할에 충족하도록 작성했어요. | ||
- [x] CardInput 컴포넌트는 도메인을 제거한다. | ||
- Card에 종속되었던 Input 컴포넌트를 개선했어요. | ||
- FormatInput | ||
- PinInput | ||
- [x] AppLayout -> AppDisplay 로 네이밍 변경한다. | ||
- [x] CardDisPlay 컴포넌트의 expiration props는 내부에서 분리한다. | ||
- [x] UI에서 사용하는 props는 행위에 맞게 네이밍한다. | ||
- [x] 컴포넌트의 return 상위에는 개행을 추가한다. | ||
|
||
### 함수 및 변수 네이밍 | ||
- [x] handle, on, submit, change 등의 이벤트 핸들러는 명확한 용도를 가지고 있어야 한다. | ||
- [x] ~is prefix 네이밍은 함수에 대한 Boolean 값을 반환한다. | ||
- [x] 함수는 is prefix, 변수는 ~ed suffix를 사용했어요. | ||
|
||
### 타입 및 유틸리티 | ||
- [x] HTML tag 자체 node 타입의 정의는 제거한다. (button type) | ||
- [x] Pick, Omit 유틸리티 타입은 적합한 상황에 맞게 사용한다. | ||
- [x] 반복되는 타입은 축약해서 활용한다. | grid${'Gap'| 'RowGap' | '...'} | ||
- [x] 형식이 정해져있는 값은 타입을 명확하게 정의한다. CardNumber, ExpirationDate, SecurityCode, Password | ||
|
||
### Hooks | ||
- [x] hooks의 반환 값은 도메인을 포함하지 않고, 최대한 단순하게 유지한다. | ||
- [x] value, selected | ||
- [x] 하나의 hook에 너무 많은 역할을 부여하지 않는다. | ||
|
||
### 기타 | ||
- [x] letterSpacingValue의 fontSize에 대한 수치 변환에 대한 검증을 추가한다. | ||
- [x] 사용하지 않는 불필요한 코드는 제거한다. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,22 @@ | ||
import { AppLayout, CardProvider, Funnel } from '@/components'; | ||
import { CardAddPage, CardCompletePage, CardListPage } from '@/pages'; | ||
import { CardProvider } from 'src/card/providers'; | ||
import { CardAddPage, CardCompletePage, CardListPage, CardPageIndex } from '@/card'; | ||
import { AppDisplay, Funnel } from '@/shared'; | ||
|
||
const App = () => ( | ||
<AppLayout.Root> | ||
<AppDisplay.Root> | ||
<CardProvider> | ||
<Funnel.Root> | ||
<Funnel.Step index={0}> | ||
<Funnel.Step index={CardPageIndex.CardListPage}> | ||
<CardListPage /> | ||
</Funnel.Step> | ||
<Funnel.Step index={1}> | ||
<Funnel.Step index={CardPageIndex.CardAddPage}> | ||
<CardAddPage /> | ||
</Funnel.Step> | ||
<Funnel.Step index={2}> | ||
<Funnel.Step index={CardPageIndex.CardCompletePage}> | ||
<CardCompletePage /> | ||
</Funnel.Step> | ||
</Funnel.Root> | ||
</CardProvider> | ||
</AppLayout.Root> | ||
</AppDisplay.Root> | ||
); | ||
export default App; |
3 changes: 1 addition & 2 deletions
3
...components/CardDisplay/CardAddDisplay.tsx → ...components/CardDisplay/CardAddDisplay.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
14 changes: 7 additions & 7 deletions
14
...omSheet/CardSelectBottomSheet.stories.tsx → ...omSheet/CardSelectBottomSheet.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
src/card/components/CardSelectBottomSheet/CardSelectBottomSheet.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import type { CardBrand } from '@/card'; | ||
import { CARD_BRANDS } from '@/card'; | ||
import { Backdrop, Button, Circle, Grid, Typography, VStack, styleToken } from '@/shared'; | ||
|
||
type CardSelectButtonProps = { | ||
onClick: () => void; | ||
} & CardBrand; | ||
|
||
type CardSelectBottomSheetProps = { | ||
opened: boolean; | ||
onOverlayClick?: () => void; | ||
onCardBrandClick?: (cardBrand: CardBrand) => void; | ||
}; | ||
|
||
const CardSelectButton = ({ color, label, ...props }: CardSelectButtonProps) => ( | ||
<Button variant="ghost" backgroundColor={styleToken.color.white} width="100%" padding="0" {...props}> | ||
<VStack width="100%" justifyContent="center" alignItems="center" spacing="10px"> | ||
<Circle backgroundColor={color} width="36px" height="36px" /> | ||
<Typography variant="caption" color={styleToken.color.black}> | ||
{label} | ||
</Typography> | ||
</VStack> | ||
</Button> | ||
); | ||
|
||
export const CardSelectBottomSheet = ({ opened, onOverlayClick, onCardBrandClick }: CardSelectBottomSheetProps) => | ||
opened ? ( | ||
<Backdrop onClick={onOverlayClick}> | ||
<VStack | ||
position="absolute" | ||
bottom="0" | ||
left="0" | ||
width="100%" | ||
height="230px" | ||
backgroundColor="white" | ||
borderRadius="5px 5px 15px 15px" | ||
> | ||
<Grid | ||
gridTemplateColumns="repeat(4, 1fr)" | ||
alignItems="center" | ||
justifyContent="center" | ||
height="100%" | ||
padding="20px 20px" | ||
> | ||
{CARD_BRANDS.map(({ label, color }) => ( | ||
<CardSelectButton | ||
key={`card-select-${label}`} | ||
color={color} | ||
label={label} | ||
onClick={() => { | ||
onCardBrandClick?.({ label, color }); | ||
}} | ||
/> | ||
))} | ||
</Grid> | ||
</VStack> | ||
</Backdrop> | ||
) : null; | ||
|
||
CardSelectBottomSheet.displayName = 'CardSelectBottomSheet'; |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './CardDisplay'; | ||
export * from './CardSelectBottomSheet'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { CardState } from '@/card'; | ||
import { styleToken } from '@/shared'; | ||
|
||
export enum CardBrandName { | ||
'레냥카드' = '레냥카드', | ||
'블냥카드' = '블냥카드', | ||
'초냥카드' = '초냥카드', | ||
'자냥카드' = '자냥카드', | ||
'로냥카드' = '로냥카드', | ||
'골냥카드' = '골냥카드', | ||
'깜냥카드' = '깜냥카드', | ||
'신냥카드' = '신냥카드', | ||
} | ||
|
||
export const CARD_BRANDS: Pick<CardState, 'label' | 'color'>[] = [ | ||
{ label: CardBrandName.레냥카드, color: styleToken.color.crimson }, | ||
{ label: CardBrandName.블냥카드, color: styleToken.color.azure }, | ||
{ label: CardBrandName.초냥카드, color: styleToken.color.mint }, | ||
{ label: CardBrandName.자냥카드, color: styleToken.color.fuchsia }, | ||
{ label: CardBrandName.로냥카드, color: styleToken.color.rose }, | ||
{ label: CardBrandName.골냥카드, color: styleToken.color.gold }, | ||
{ label: CardBrandName.깜냥카드, color: styleToken.color.black }, | ||
{ label: CardBrandName.신냥카드, color: styleToken.color.teal200 }, | ||
] as const; |
Oops, something went wrong.