Skip to content

Conversation

@jaeyoung-kwon
Copy link

안녕하세요 카일!
우테코 7기 FE 재오입니다.

벌써 레벨2의 첫 미션이네요! 이번 미션 리뷰 잘 부탁드립니다! 🙇‍♂️

🎯 미션 소개

이번 미션을 통해 다음과 같은 학습 경험들을 쌓는 것을 목표로 합니다.

  • 재사용 가능한 Input Component를 개발한다.
  • Storybook을 사용하여 컴포넌트의 다양한 상태를 시각적으로 테스트한다.
  • 카드 정보를 효과적으로 렌더링 하기 위한 상태 관리를 경험한다.

🕵️ 셀프 리뷰(Self-Review)

제출 전 체크 리스트

  • 기능 요구 사항을 모두 구현했고, 정상적으로 동작하는지 확인했나요?
  • 기본적인 프로그래밍 요구 사항(코드 컨벤션, 에러 핸들링 등)을 준수했나요?
  • 배포한 데모 페이지에 정상적으로 접근할 수 있나요?

리뷰 요청 & 논의하고 싶은 내용

1) 이번 단계에서 가장 많이 고민했던 문제와 해결 과정에서 배운 점

  1. 상태 관리 관련 질문
  • 이번 미션을 진행하며 상태 관리를 어떤 방식으로 가져갈지에 대해 먼저 고민했습니다.
  • 다른 크루들의 얘기를 들어보니, 상태를 생성하고 내부에 검증 로직을 포함하는 커스텀 훅으로 구성하면 응집도를 높일 수 있다는 의견이 있었습니다.
  • 커스텀 훅을 만들고 사용하는 거보다 필요성을 느낀 다음에 필요하다면 커스텀 훅을 만들자는 얘기가 나와서 커스텀 훅은 만들지 않는 방식으로 진행했습니다.
  • 구현을 마치고 생각해보았을 때, 현재는 복잡한 로직이 존재하지 않아 커스텀 훅을 만들지 않았습니다.
  • 또한, 이번 과제에서는 input 값을 카드 preview에 보여줘야 했기 때문에, 상태를 최상위 컴포넌트에서 관리하고 preview와 inputField 컴포넌트에 각각 inputValue, setInputValue를 넘겨주는 방식이어야 했습니다.
  • 컴포넌트 간 상태 전달이 많아질 것을 고려해 Context API 도입을 고민했지만, 현재 구조에서는 컴포넌트의 depth가 깊지 않아 context는 도입하지 않기로 했습니다.
  • 그래서 최종적으로는 App 컴포넌트에서 상태를 생성하고, InputField에는 setValue를 넘겨 input 값을 검증 및 업데이트하게 하고, Preview에는 값을 넘겨 UI에 표시하는 구조로 구성했습니다.
  • 이러한 상태 관리 구조가 적절했는지, 혹은 더 나은 방식이 있을지 궁금합니다.

2) 이번 리뷰를 통해 논의하고 싶은 부분

  1. Error 상태 관리 관련 질문
  • 에러 상태를 어떻게 관리할지 고민이 많았습니다.
  • 처음에는 input 상태와 비슷하게 isError, errorType을 가진 객체로 관리했지만, 한 Input에 두 개 이상의 에러가 발생할 경우 덮어씌워지는 문제가 있었습니다.
  • 최종적으로는 에러 타입을 배열로 관리하고, 하나 이상의 에러가 있을 경우 첫 번째 메시지를 표시하는 방식으로 변경했습니다.
  • 이러한 방식이 괜찮은 방향인지, 혹시 카일은 복수 에러 상황에서 어떻게 상태를 관리하시는지 궁금합니다.

변경 전

  const [errorTypes, setErrorTypes] = useState<
    Record<CardNumberInputType, ErrorType[]>
  >({
    cardNumberPart1: {isError: false, errorType: null},
    cardNumberPart2: {isError: false, errorType: null},
    cardNumberPart3: {isError: false, errorType: null},
    cardNumberPart4: {isError: false, errorType: null},
  });

변경 후

  const [errorTypes, setErrorTypes] = useState<
    Record<CardNumberInputType, ErrorType[]>
  >({
    cardNumberPart1: [],
    cardNumberPart2: [],
    cardNumberPart3: [],
    cardNumberPart4: [],
  });
  1. Story 관련 질문
  • Storybook은 이번에 처음 사용해보았습니다.
  • Storybook을 UI 테스트 및 문서화의 역할로 이해하고, 상태가 많은 Input, BaseInputField, CardPreview 컴포넌트만 Story로 분리해 작성했습니다.
  • 도메인 로직이 들어간 컴포넌트들은 Story를 만들지 않았는데, 현업에서는 보통 어떤 기준으로 Story를 작성하는지, 제가 생각한 기준이 괜찮았는지 궁금합니다.
  1. 컴포넌트 분리 기준 관련 질문
  • 컴포넌트는 공통 사용 여부, 도메인 기능, 길이 등을 기준으로 분리했습니다.
  • 특히 재사용 가능한 공통 컴포넌트와 도메인 단위의 로직을 나눠보고자 했습니다.
  • 카일이 보시기에 이번 컴포넌트 분리 기준이 적절해보이는지, 혹은 더 개선할 수 있는 기준이 있다면 조언 부탁드립니다.
  1. config 폴더 관련 질문
  • 상수와 타입을 다룰 때, 서로 연관된 경우가 많아 하나의 파일에서 관리하는 방식으로 구성했습니다.
  • 도메인 단위로 분리해서 관리하고 있는데, 이런 기준이 괜찮은지, 그리고 config라는 폴더 네이밍이 적절한지도 궁금합니다.
  • 현업에서는 상수와 타입을 어떤 기준으로 나누고, 폴더를 어떻게 구성하시는지 의견 듣고 싶습니다.

✅ 리뷰어 체크 포인트

1. 컴포넌트 설계

  • 재사용성확장성을 고려해 Input 컴포넌트를 잘 설계했는가?
  • 컴포넌트를 분리할 기준(역할, 책임, 관심사 등)을 고민한 흔적이 보이는가?
  • UI 단위가 지나치게 작거나 크지 않고 적절한 수준으로 구성되어 있는가?

2. 상태 관리

  • 카드 정보 렌더링에 필요한 상태를 React의 상태로 적절하게 관리하고 있는가?

3. Storybook 활용

  • 컴포넌트의 다양한 상태(정상 입력, 오류, 빈값 등)를 확인할 수 있는 스토리가 작성되어 있는가?

4. UI/UX

  • 유효성 검증, 에러 메시지 등에서 사용자 경험을 고려한 처리가 이루어졌는가?

mun-kyeong and others added 30 commits April 15, 2025 14:40
@igy95
Copy link

igy95 commented Apr 19, 2025

UX 피드백

  • visa, master 카드를 가름하는 카드 번호 유효성 기준을 두어 실제 앱과 같은 느낌을 받았습니다 :)
  • 유효기간에서 10 미만의 자리 입력 시 자동으로 0이 채워지는 부분 좋네요 👍
  • 특정 인풋의 값 입력이 완료되었을 때 다음 인풋으로 자동으로 넘어가게 하는 ux도 고려해보면 좋을 것 같네요!

@igy95
Copy link

igy95 commented Apr 19, 2025

상태 관리 관련 질문

전반적으로 custom hook, context api 등 여러 상태 관리 방식을 먼저 고민해보신 뒤, 현재 앱 사이즈에 맞는 결정을 내리신 부분이 합리적으로 느껴집니다.

상태 관리에 대한 방식이 꽤 많지만, 처음부터 이유도 없이 도입을 시도하는 것보다는 가장 기본적인 상태 관리 방식으로 접근하는 것도 좋은 출발인 것 같아요.

이번 미션에서는 컴포넌트의 재사용성에 좀 더 초점을 맞추고 더 복잡해질 다음 미션부터 엄밀한 상태 관리에 대한 고민을 이어가보시죠 !

Error 상태 관리 관련 질문

우선 '하나의 인풋에 대해 여러 에러를 갖고 있어야 하는 이유가 무엇인가?'에 대한 질문이 선행되어야 할 것 같아요. 인풋에 대한 에러는 주로 화면 내에 메시지를 표현하거나, submit 가능 여부를 판가름할 때 사용할 텐데 하나의 인풋에 대한 에러는 가장 우선으로 마주한 에러만 관리하고 있어도 괜찮지 않을까요?

Story 관련 질문

주로 UI가 특정 상황에서 어떤 식으로 보이는지에 대해 초점을 맞춰, 이러한 테스트로 이점을 얻을 수 있는 상황에서 사용하는 것 같습니다. 예를 들어

  1. 디자인 시스템 문서화 - 저희 회사에서는 디자인 시스템을 오픈소스로 관리하여 스토리북으로 문서화를 해두는 데 이런 예시를 들 수 있겠습니다.
  2. 개발 중인 과정을 협업자와 확인하기 위한 용도 - 자주 있지는 않지만, 디자이너와 UI를 같이 확인할 때도 쓰고는 합니다. 앱 내에서 UI를 확인하기 위한 여러 동작을 거치지 않고도 바로 확인이 가능하기 때문이에요

컴포넌트 분리 기준 관련 질문

이 부분은 코드 레벨로 확인해볼게요!

config 폴더 관련 질문

현업 내에서도 데이터를 어떻게 다룰 것인가에 대한 방식이 상이합니다. 그래서 팀 내 컨벤션으로 규정해두는 경우가 많은데, 저 같은 경우 도메인 관련 타입, 상수 또한 모두 비즈니스 로직의 일부로 보기 때문에 '연관된 비즈니스 로직을 가깝게 두어 집중화하는 방식'을 추구해요. 이름은 크게 어색하지는 않지만 특정 domain, model 하위에서 관리되어야 할 것 같은 이름으로 느껴집니다.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normalize.css 를 고려해보셨을까요? reset.css를 사용한 이유가 궁금하네요 ~

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번 미션이 아니라 저번 미션에서 두 가지 사용을 고려해본 적 있습니다.

normalize.css는 'CSS Normalize는 가능한한 브라우저의 내장 스타일을 최대한 건들지 않는 선에서 브라우저 간에 상이한 부분만 스타일을 통일시켜주는 것'이었습니다.

즉, normalize.css 브라우저의 내장 스타일을 초기화해주지 않아서 "저희가 의도한 스타일만"을 적용하기 어려울 것이라고 판단이 되어, reset.css을 사용하게 되었습니다!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호 그렇군요 공감되는 이유입니다. 다만 정말 독창적인 디자인의 앱을 만들지 않는 이상 대부분은 normalize.css를 사용하는 선에서 출발하는 게 초기 스타일 작업량을 줄일 수 있기 때문에 둘 다 써보시고 추후 팀 프로젝트 때 뭘 써야할지 결정의 근거로 삼아보시길 바라요!

@@ -0,0 +1 @@
export type CardType = 'visa' | 'master' | null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null은 타입에 들어가기보다 실제 null이 필요한 상태에 보강해주는 건 어떨까요? 이 자체로 타입에 맞는 의미를 갖긴 어려워 보입니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null을 타입으로 사용하는 것보다, null에 대응되는 타입을 만들어서 의미를 가지게 하는 것이 좋겠다고 이해했는데 맞을까요?
적용하였습니다!

845248c

Copy link

@igy95 igy95 Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 그건 아니었고 유니온에 들어갈 타입은 유효한 값만을 두고 '없는 것'에 대한 처리는 사용처에 위임하는 게 좋겠다는 말이었습니다! null, undefined가 핵심 타입으로 들어가게 되면 사용처에서 불필요한 타입 가드를 추가해야할 수도 있기 때문이에요.

type CardType = 'visa' | 'master'

function getCardTypeOrNull (): CardType | null {
  if (...) {
    return null
  }

    return cardType
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 이해했습니다 ㅎㅎ 2단계에 적용해보겠습니다!

interface InputSectionProps {
title: string;
caption?: string;
children: ReactNode;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PropsWithChildren을 사용해볼 수 있을 것 같네요 !

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 PropsWithChildren에 대해서 처음 알게 되었네요. 감사합니다 🙇‍♂️
해당 내용에 대해서 찾아보았습니다.

type PropsWithChildren<P = unknown> = P & { children?: ReactNode | undefined };

children prop이 옵셔널로 받게 되어 있더라구요. children을 안받을 수도 있다는 의미인데, 제 코드에서 children을 받는 컴포넌트를 봤을 때 children을 옵셔널로 받게 되면 조금 어색해질 것 같다고 판단이 되었습니다.
그래서 StrictPropsWithChildren를 만들어 children을 props를 추가하면서 필수로 받게 하는 것을 만들었습니다!
이렇게 진행해도 괜찮을까요?

f143dbd

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하 그런 의도라면 처음의 방식을 그대로 고수해도 괜찮을 것 같습니다 ! 만일 children을 필수로 받는 컴포넌트가 많아서 이를 확실한 규약으로 삼고 싶다면 수정해주신 방향을 따라가면 될 듯 하네요

] as const;
export type CardNumberInputType = (typeof CARD_NUMBER_INPUT_TYPE)[number];

export const EXPIRATION_DATE_INPUT_TYPE = [
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

유효기간에 대한 키도 month, year로 나눠볼 수 있지 않을까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cardNumber의 input에 대한 키를 cardNumberPart1, cardNumberPart1, ... 로 선언하게 되어, 통일성을 위해 expirationDatePart1, expirationDatePart2로 나누게 되었습니다.

하지만 카일 피드백도 그렇고 마지막에 해당 키 값을 봤을 때, 직관적이지 않다는 느낌을 받았습니다.
통일성보다는 네이밍에 대한 직관성이 더 중요할까요?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

명확성에 대한 좋은 고민이라고 생각합니다. 개인적으로 저는 두 관계는 트레이드 오프가 아닌, 우선순위가 정해져있다고 생각이 들어요.

  1. 그 자체로 명확한 이름을 부여하기
  2. 일관성, 통일성을 지키기

일관성, 통일성으로 얻은 명확성은 이름 하나만으로 판단하기 힘들 때 다른 코드를 같이 보면서 유추하면서 얻는 차선책이기 때문에 우선 개체 하나하나의 이름을 최대한 잘 짓는 것이 더 좋은 방향이라고 봅니다 !

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 두 가지를 같이 가면 가장 좋은 케이스이지만 둘 중에 하나를 택해야 한다면 명확성이라는 의미이군요!
저는 통일성을 최우선으로 보고 있었는데, 카일의 코멘트에 동의하게 되었습니다. 앞으로 카일의 코멘트를 기억하고 적용해보겠습니다!

Copy link

@igy95 igy95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 재오! 이번 미션 잘 부탁드립니다~ 전반적으로 이번 미션은 Input의 재사용성에 초점을 맞추어 코드를 살펴봤는데요, Input과 InputField의 역할을 잘 나눠주신 부분이 인상 깊었습니다. UX 피드백 및 질문에 대한 답변, 세부 코멘트 등을 남겨두었으니 확인 후 재요청 부탁드릴게요 ~!

@jaeyoung-kwon
Copy link
Author

Error 상태 관리 관련 질문
우선 '하나의 인풋에 대해 여러 에러를 갖고 있어야 하는 이유가 무엇인가?'에 대한 질문이 선행되어야 할 것 같아요. 인풋에 대한 에러는 주로 화면 내에 메시지를 표현하거나, submit 가능 여부를 판가름할 때 사용할 텐데 하나의 인풋에 대한 에러는 가장 우선으로 마주한 에러만 관리하고 있어도 괜찮지 않을까요?

현재는 onChange, onBlur에서 하나의 인풋에 대한 에러 처리를 분리해서 처리하기 때문에 에러의 상태(?)가 조금 꼬인 것 같습니다.
카일의 코멘트처럼 validation하는 부분을 통일해서 가장 우선으로 마주한 에러만 관리하는 방식으로 처리해도 좋을 것 같네요. 2단계 진행하면서 에러 처리 방식에 대해서 조금 더 고민해보겠습니다!

config 폴더 관련 질문
현업 내에서도 데이터를 어떻게 다룰 것인가에 대한 방식이 상이합니다. 그래서 팀 내 컨벤션으로 규정해두는 경우가 많은데, 저 같은 경우 도메인 관련 타입, 상수 또한 모두 비즈니스 로직의 일부로 보기 때문에 '연관된 비즈니스 로직을 가깝게 두어 집중화하는 방식'을 추구해요. 이름은 크게 어색하지는 않지만 특정 domain, model 하위에서 관리되어야 할 것 같은 이름으로 느껴집니다.

현재는 코드 규모가 그렇게 크지 않아서 domain 별로 폴더를 분리를 하지 않고 진행했습니다. 하지만 storybook 파일도 그렇고 config 파일도 그렇고 특정 domain 하위에서 관리하면 카일이 언급해주신 '연관된 비즈니스 로직을 가깝게 두어 집중화하는 방식'으로 분리가 되어 유지보수가 좋을 것 같다는 생각이 드네요! 좋은 피드백 감사합니다!

@jaeyoung-kwon
Copy link
Author

안녕하세요 카일!
고민되는 부분에 대해서 답변을 해주셔서 정말 도움이 많이 된 것 같습니다 감사합니다! 😊
코멘트 반영하여 리뷰 재요청 드리겠습니다!

Copy link

@igy95 igy95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 재오 ~ 리뷰 수정사항과 코멘트 확인 완료했습니다. 우선 코드에 대한 본인의 고민과 리뷰를 남겼을 때 그대로 수용하지 않고 좀 더 응용해보려고 하는 모습이 긍정적으로 느껴지네요 👍 CardType에 대해 추가로 남긴 코멘트만 확인 부탁드리고 다음 단계에서 보시죠 !

@igy95 igy95 merged commit e29209d into woowacourse:jaeyoung-kwon Apr 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants