Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 로그인, 회원가입 모바일 페이지 ui 구현 및 api 연결 #292

Merged
merged 22 commits into from
Feb 4, 2025

Conversation

areumH
Copy link
Collaborator

@areumH areumH commented Feb 1, 2025

💡 작업 내용

  • 로그인, 회원가입 모바일 페이지 ui 구현
  • 로그인, 회원가입 모바일 페이지 api 연결
  • 모바일 - Input, DefaultProfileModal, MobileLoginForm 컴포넌트 구현
  • SocialLoginButton 컴포넌트에 사이즈 props 추가 구현
  • Divider 컴포넌트에 색상 props 추가 구현
  • 회원 가입 양식 검증 성공 후 값이 변경되어도 성공 상태로 유지되던 오류 해결

💡 자세한 설명

✅ 모바일 페이지 관련

  • 비교적 구성이 간단한 로그인 페이지는 모바일 상태일 때 MobileLoginForm 컴포넌트가 제시되도록 하여 페이지를 따로 만들지 않았습니다.
  • 반면 회원가입 페이지는 제시되는 기본 프로필 모달 컴포넌트도 다르고, 버튼 스타일도 달라서 모바일 페이지를 따로 만들어 모바일 여부에 따라 제시되도록 구현했습니다.
  • 회원가입 페이지의 경우 모바일 디바이스에 따라 페이지의 세로가 짤릴 수 있다고 생각되어 두 버튼을 화면 밑에 고정시킨 뒤 스크롤이 가능하도록 구현했습니다. 밸런스게임 조회 페이지처럼 버튼 뒤 배경은 흰색 그라데이션 처리하였습니다.

✅ 컴포넌트 관련

  • 최대한 웹에서 사용한 컴포넌트를 재사용하기 위해 SocialLoginButton, Divider, 회원가입에 사용되는 각 Input 컴포넌트는 props를 추가하는 방식으로 구현하였고, 아예 디자인이 다른 Input, DefaultProfileModal, MobileLoginForm 컴포넌트는 mobile 폴더 내에 새로 구현했습니다.

✅ 회원가입 관련

  • 현재 회원 가입 로직은 각 양식을 successForm으로 관리하여 모든 값 검증에 성공했을 시에 회원가입이 진행되도록 되어있습니다. 그런데 한번 검증에 성공한 후, Input 내의 값을 변경하여도 성공 처리가 유지되던 이슈가 있었습니다. (이로 인해 검증한 값과 다른 값이 회원 가입 요청에 전송될 수 있음)
  • 위의 문제 해결을 위해 회원 가입에 사용되는 Input 관련 컴포넌트 및 컴포넌트에 연결된 훅에 값이 변경될 경우 성공 상태가 false로 변경되도록 수정했습니다.
  • 모바일 Input 컴포넌트의 경우, 해당 양식 값 검증 성공 여부를 인자로 받아 밑줄 스타일 색에 반영합니다!

✅ interceptor.ts

  • 현재 interceptor의 axiosInstance.response.use에서는 api 요청의 응답이 401일 때 쿠키의 리프레시 토큰을 이용한 액세스 토큰 재발급 함수를 수행하도록 되어있는데, 로그인 실패 시의 응답도 401이기 때문에 불필요한 토큰 재발급이 실행되는 이슈가 있었습니다. 해당 이슈 해결을 위해 토큰 재발급의 조건문에 data.message !== ERROR.LOGIN.NOT_MATCH (로그인 실패 응답이 아닐 때)를 추가하였습니다.
-.Clipchamp.7.mp4

▲ 로그인 모바일 페이지

-.Clipchamp.6.mp4

▲ 회원가입 모바일 페이지

📗 참고 자료 (선택)

📢 리뷰 요구 사항 (선택)

🚩 후속 작업 (선택)

✅ 셀프 체크리스트

  • PR 제목을 형식에 맞게 작성했나요?
  • 브랜치 전략에 맞는 브랜치에 PR을 올리고 있나요? (master/main이 아닙니다.)
  • 이슈는 close 했나요?
  • Reviewers, Labels, Projects를 등록했나요?
  • 작업 도중 문서 수정이 필요한 경우 잘 수정했나요?
  • 테스트는 잘 통과했나요?
  • 불필요한 코드는 제거했나요?

closes #291

Summary by CodeRabbit

  • New Features

    • 모바일 환경에 최적화된 회원가입 페이지 및 로그인 폼 도입
    • 모바일 전용 입력 필드, 프로필 설정, 기본 프로필 모달, 소셜 로그인 버튼 등의 신규 컴포넌트 추가
    • 모바일 아이콘 및 텍스트 스타일, 조건부 렌더링 기능 확장
    • 새로운 오류 메시지 추가로 로그인 실패 시 사용자 피드백 개선
    • 로그인 페이지에서 모바일 전용 레이아웃 도입
    • 새로운 ErrorMessageInput 컴포넌트 추가로 사용자 경험 향상
  • Style

    • 인풋, 구분선 및 프로필 설정 등 UI 구성요소에 새로운 스타일(그림자, 톤 조절 등) 적용 및 반응형 디자인 강화
    • 로그인 페이지 및 회원가입 페이지에 대한 미디어 쿼리 추가로 반응형 디자인 개선
  • Bug Fixes

    • 로그인 오류 처리 개선 및 업데이트된 오류 메시지 제공

Copy link
Contributor

coderabbitai bot commented Feb 1, 2025

Walkthrough

이번 변경 사항은 모바일 사용자를 위한 UI 개선과 조건부 렌더링 로직 도입에 초점을 맞춥니다.
App.tsx에서는 기기 유형에 따라 기존 회원가입 페이지 대신 새로 추가된 모바일 회원가입 페이지를 렌더링하며, 로그인 페이지 역시 모바일 여부를 판단해 대응합니다.
또한, API 인터셉터의 에러 핸들링 개선, 모바일 전용 아이콘 및 스타일, 그리고 입력 관련 컴포넌트와 훅의 조건부 로직 변경 등이 함께 이루어졌습니다.

Changes

파일/파일 그룹 변경 요약
src/App.tsx
src/pages/LoginPage/LoginPage.tsx
src/pages/mobile/SignUpMobilePage/SignUpMobilePage.tsx
모바일 여부에 따라 회원가입 및 로그인 페이지의 컴포넌트를 조건부로 렌더링하도록 라우팅 로직 추가 및 새로운 모바일 전용 페이지 컴포넌트 구현
src/api/interceptor.ts
src/constants/message.ts
401 에러 처리 조건에 추가 검증(ERROR.LOGIN.NOT_MATCH) 적용, 그리고 새로운 로그인 에러 메시지(이메일 또는 비밀번호가 잘못되었습니다.) 추가
src/assets/index.ts 모바일 전용 아이콘과 중간 크기 로고, 기본 인물 아이콘 등 새로운 SVG 자산 추가
src/components/atoms/Divider/Divider.style.ts
src/components/atoms/Divider/Divider.tsx
tone 속성 추가로 구분선 스타일을 조건부로 변경할 수 있도록 수정
src/components/atoms/ProfileSetting/ProfileSetting.style.ts
src/components/atoms/ProfileSetting/ProfileSetting.tsx
프로필 설정 컴포넌트에서 스타일 개선 및 조건부 렌더링(설정 모드, 기본 이미지) 적용
src/components/atoms/SocialLoginButton/SocialLoginButton.style.ts
src/components/atoms/SocialLoginButton/SocialLoginButton.tsx
size prop 추가 및 모바일 전용 이미지 컴포넌트 선택 로직 도입, 버튼 크기에 따른 스타일 동적 적용
src/components/mobile/atoms/ErrorMessage/* 모바일 환경에 대응하는 새로운 ErrorMessage 컴포넌트 및 스타일 추가
src/components/mobile/atoms/Input/*
src/components/mobile/atoms/ProfileSetting/*
모바일 전용 입력 필드 및 프로필 설정 컴포넌트와 관련 스타일 모듈 추가
src/components/mobile/molecules/DefaultProfileModal/*
src/components/mobile/molecules/MobileLoginForm/*
기본 프로필 모달, 모바일 로그인 폼 컴포넌트 및 관련 스타일/스토리북 구성 파일 추가
src/components/molecules/InputCode/*
src/components/molecules/InputEmail/*
src/components/molecules/InputNickname/*
src/components/molecules/InputProfileImage/*
src/components/molecules/InputPw/*
src/components/molecules/InputPwConfirm/*
src/components/molecules/LoginForm/LoginForm.tsx
각 입력 컴포넌트에 isMobilesuccess와 같은 모바일 대응 prop 추가 및 조건부 모바일 렌더링 로직 적용, 로그인 폼 내부의 텍스트 수정 등
src/hooks/common/inputsUserInfo/useCheckEmail.ts
src/hooks/common/inputsUserInfo/useCheckNickname.ts
src/hooks/common/inputsUserInfo/useCheckPassword*.ts
src/hooks/common/inputsUserInfo/useCheckProfileImage.ts
src/hooks/signup/useSignupForm.ts
이메일, 닉네임, 비밀번호, 프로필 이미지 관련 입력 검증 로직 개선 및 파라미터/인터페이스 정리, 회원가입 훅에 successForm 속성 추가
src/stories/**
src/styles/typo.ts
Storybook 구성 파일 내 새로운 argTypes 및 스토리 추가, 모바일 전용 텍스트 및 타이틀 스타일 추가

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant SignUpMobilePage
    participant SignUpPage

    User->>App: 회원가입 페이지 접근
    App->>App: 기기가 모바일인지 확인 (isMobile 체크)
    alt 모바일 기기인 경우
      App->>SignUpMobilePage: 모바일 회원가입 페이지 렌더링
      SignUpMobilePage->>User: 모바일 UI 제공
    else 데스크탑인 경우
      App->>SignUpPage: 기본 회원가입 페이지 렌더링
      SignUpPage->>User: 데스크탑 UI 제공
    end
Loading
sequenceDiagram
    participant User
    participant LoginPage
    participant useIsMobile
    participant MobileLoginForm
    participant LoginForm

    User->>LoginPage: 로그인 페이지 접근
    LoginPage->>useIsMobile: 모바일 여부 확인
    alt 모바일 기기인 경우
      LoginPage->>MobileLoginForm: 모바일 로그인 폼 렌더링
      MobileLoginForm->>User: 모바일 로그인 인터페이스 제공
    else 데스크탑인 경우
      LoginPage->>LoginForm: 기본 로그인 폼 렌더링
      LoginForm->>User: 데스크탑 로그인 인터페이스 제공
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
모바일 로그인 및 회원가입 UI 구현 (#291)
모바일 로그인 및 회원가입 API 연결 (#291)

Poem

깡총깡총 뛰는 작은 토끼
코드 숲 속에 새싹 피어나네
모바일 세상 UI 반짝이며
API도 춤추듯 연결되네
우리의 코드, 깔끔한 발자국 남기고
토끼 마음으로 축하를 전해요 🐰
코드래빗, 모두 함께 즐기자!


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🔭 Outside diff range comments (1)
src/hooks/common/inputsUserInfo/useCheckProfileImage.ts (1)

인터페이스 이름 변경으로 인한 영향 확인 필요

  • src/components/molecules/InputProfileImage/InputProfileImage.tsx 파일에서 여전히 InputProfileImageProps를 사용하고 있어, 이 부분이 새 이름 ProfileImageProps 변경에 영향을 받습니다.
  • 추가로, src/components/common/Profile/ProfileImage/ProfileImage.style.ts 파일은 올바르게 ProfileImageProps를 import하고 있습니다.

따라서 InputProfileImageProps를 사용하는 컴포넌트를 새 인터페이스 이름으로 업데이트해야 합니다.

🔗 Analysis chain

Line range hint 6-10: 인터페이스 이름 변경에 따른 영향도 확인 필요

InputProfileImageProps에서 ProfileImageProps로의 인터페이스 이름 변경이 다른 컴포넌트에 영향을 미칠 수 있습니다.

다음 스크립트로 영향을 받는 파일들을 확인해보세요:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: 이전 인터페이스명을 사용하는 파일 검색

echo "Searching for files using old interface name..."
rg "InputProfileImageProps" -l

echo "\nSearching for potential imports of the interface..."
rg "import.*ProfileImageProps" -l

Length of output: 418

🧹 Nitpick comments (29)
src/components/molecules/InputProfileImage/InputProfileImage.style.ts (1)

40-45: 모바일 텍스트 스타일 구현에 대한 제안

모바일 환경에 맞는 타이포그래피와 터치 인터페이스를 고려한 구현이 잘 되었습니다. 다만 몇 가지 개선사항을 제안드립니다:

  1. 터치 피드백을 위한 active 상태 스타일 추가
  2. 접근성을 위한 터치 영역 확보

다음과 같이 수정해보시는 것은 어떨까요?

 export const mobileProfileDefaultText = css(typo.Mobile.Text.SemiBold_14, {
   color: color.BK,
   textDecoration: 'underline',
   textUnderlineOffset: '5px',
   cursor: 'pointer',
+  padding: '4px',  // 터치 영역 확보
+  '&:active': {    // 터치 피드백
+    opacity: 0.7,
+  },
 });

Also applies to: 52-55

src/hooks/common/inputsUserInfo/useCheckPasswordCheck.ts (1)

Line range hint 26-33: 비밀번호 확인 검증 로직 검토

비밀번호 일치 여부 확인 로직이 명확하게 구현되어 있습니다. 하지만 사용자 경험을 더욱 개선하기 위해 다음 사항을 고려해보시기 바랍니다:

  1. 실시간 유효성 검사 추가
  2. 비밀번호 강도 표시기 구현

다음과 같은 개선된 구현을 제안드립니다:

const handleVerify = () => {
  if (isEmptyString(value)) {
    return;
  }

  if (!isValidPwChkMatch(value)) {
    setIsError(true);
    setErrorMessage(ERROR.PW.NOT_MATCH);
  } else {
    setIsError(false);
    setErrorMessage(undefined);
    // 추가: 비밀번호 일치 시 성공 피드백
    onSuccess?.();
  }
};
src/hooks/common/inputsUserInfo/useCheckPassword.ts (1)

Line range hint 36-43: 비밀번호 유효성 검사 로직 검토

비밀번호 형식 검증 로직이 잘 구현되어 있습니다. 다만 다음과 같은 개선 사항을 고려해보시기 바랍니다:

  1. 정규식 패턴을 상수로 분리하여 재사용성 향상
  2. 각 검증 단계별 구체적인 피드백 제공

다음과 같은 개선된 구현을 제안드립니다:

const PASSWORD_PATTERNS = {
  ALPHABET: /[a-zA-Z]/,
  NUMBER: /[0-9]/,
  SPECIAL_CHAR: /[`~!@#$%^&*()_+\-=[\]{};':"\\|,.<>?]/,
} as const;

const getDetailedErrorMessage = (pw: string): string | undefined => {
  if (!PASSWORD_PATTERNS.ALPHABET.test(pw)) return ERROR.PW.NEED_ALPHABET;
  if (!PASSWORD_PATTERNS.NUMBER.test(pw)) return ERROR.PW.NEED_NUMBER;
  if (!PASSWORD_PATTERNS.SPECIAL_CHAR.test(pw)) return ERROR.PW.NEED_SPECIAL_CHAR;
  if (pw.length < INPUT_LIMIT.PW_MIN) return ERROR.PW.TOO_SHORT;
  if (pw.length > INPUT_LIMIT.PW_MAX) return ERROR.PW.TOO_LONG;
  return undefined;
};
src/components/atoms/Divider/Divider.style.ts (1)

13-16: 스타일 상수화 제안

스타일 구현이 잘 되었습니다만, 코드의 일관성과 유지보수성을 위해 다음과 같은 개선을 제안드립니다:

  • border 두께(1px)를 상수로 분리하여 관리하면 좋을 것 같습니다.

다음과 같이 개선해보시는 건 어떨까요?

+const BORDER_WIDTH = '1px';
+
 export const getDividerStyling = ({
   orientation,
   length,
   tone,
 }: Required<DividerProps>) => {
   const style = {
     width: css({
       width: `${length}px`,
       borderBottom:
         tone === 'gy'
-          ? `1px solid ${color.GY[2]}`
-          : `1px solid ${color.WT_VIOLET}`,
+          ? `${BORDER_WIDTH} solid ${color.GY[2]}`
+          : `${BORDER_WIDTH} solid ${color.WT_VIOLET}`,
     }),
     height: css({
       height: `${length}px`,
       borderRight:
         tone === 'gy'
-          ? `1px solid ${color.GY[2]}`
-          : `1px solid ${color.WT_VIOLET}`,
+          ? `${BORDER_WIDTH} solid ${color.GY[2]}`
+          : `${BORDER_WIDTH} solid ${color.WT_VIOLET}`,
     }),
   };

Also applies to: 20-23

src/components/mobile/atoms/ErrorMessage/ErrorMessage.tsx (1)

9-17: 성능 최적화를 위한 React.memo 적용을 고려해보세요.

컴포넌트가 잘 구현되어 있지만, 불필요한 리렌더링을 방지하기 위해 React.memo를 적용하는 것이 좋을 것 같습니다.

다음과 같이 수정해보세요:

-const ErrorMessage = ({
+const ErrorMessage = React.memo(({
   isError = false,
   children,
   ...attributes
-}: ErrorMessageProps) => (
+}: ErrorMessageProps) => (
   <p css={getTextStyling(isError)} {...attributes}>
     {children}
   </p>
-);
+));
src/components/molecules/InputEmail/InputEmail.style.ts (1)

22-26: 디자인 토큰 사용을 권장드립니다.

하드코딩된 크기 값들을 디자인 시스템의 토큰으로 관리하면 일관성 있는 UI를 유지하기 더 쉬울 것 같습니다.

다음과 같이 수정하는 것을 고려해보세요:

 export const mobileButtonStyling = css(typo.Mobile.Text.SemiBold_14, {
-  width: '75px',
-  height: '32px',
-  borderRadius: '4px',
+  width: spacing.button.mobile.width,
+  height: spacing.button.mobile.height,
+  borderRadius: radius.small,
 });
src/components/mobile/atoms/Input/Input.tsx (1)

24-32: 성공 상태에 대한 시각적 피드백 개선이 필요합니다.

현재 success 상태는 inputWrapperStyling에만 반영되어 있습니다. 사용자에게 더 명확한 피드백을 제공하기 위해 성공 상태일 때 체크 아이콘이나 다른 시각적 표시를 추가하는 것을 고려해보세요.

src/stories/mobile/molecules/DefaultProfileModal.stories.tsx (1)

21-35: 스토리북 구성에 다양한 상태 추가가 필요합니다.

현재는 기본 상태만 정의되어 있습니다. 다음과 같은 추가 스토리를 고려해보세요:

  • 모달이 닫힌 상태
  • 이미지 선택 시의 상태
  • 에러 상태
src/components/mobile/atoms/ProfileSetting/ProfileSetting.style.ts (2)

17-20: 프로필 이미지 크기를 더 유연하게 관리할 필요가 있습니다.

현재 프로필 이미지의 크기가 고정값으로 설정되어 있습니다. 다양한 화면 크기에 대응하기 위해 상대적인 단위나 theme 시스템을 활용하는 것을 고려해보세요.


22-34: 그림자 값을 상수로 분리하면 좋을 것 같습니다.

여러 곳에서 사용되는 box-shadow 값을 상수로 분리하여 재사용성을 높이고 일관성을 유지하는 것이 좋을 것 같습니다.

+const BOX_SHADOW = '0px 1px 6px rgba(0, 0, 0, 0.1)';
src/components/atoms/SocialLoginButton/SocialLoginButton.style.ts (2)

26-41: 사이즈 타입을 더 엄격하게 정의하면 좋을 것 같습니다.

현재 size 타입이 string으로 처리되고 있습니다. 타입 안정성을 높이기 위해 리터럴 유니온 타입을 사용하는 것을 추천드립니다.

+type ButtonSize = 'medium' | 'small';
-export const getSizeStyling = (size: Required<SocialLoginButtonProps>['size'])
+export const getSizeStyling = (size: ButtonSize)

43-47: 마진 값을 상수로 분리하면 좋을 것 같습니다.

마진 값이 매직 넘버로 하드코딩되어 있습니다. 이러한 값들을 의미 있는 상수로 분리하여 관리하면 유지보수가 더 쉬워질 것 같습니다.

+const MARGIN_TOP = {
+  medium: '90px',
+  small: '62px',
+} as const;
src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.style.ts (1)

5-10: 반응형 디자인 개선 제안

loginFormStylingloginBtnStyling에서 고정된 픽셀 값을 사용하고 있습니다. 다양한 모바일 화면 크기에 대응하기 위해 상대적인 단위 사용을 고려해보세요.

다음과 같이 수정하는 것을 추천드립니다:

export const loginFormStyling = css({
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'center',
-  width: '336px',
+  width: '100%',
+  maxWidth: '336px',
});

export const loginBtnStyling = css(typo.Comment.SemiBold, {
-  width: '335px',
+  width: '100%',
+  maxWidth: '335px',
  height: '52px',
  marginTop: '3px',
});

Also applies to: 28-32

src/components/mobile/atoms/ProfileSetting/ProfileSetting.tsx (2)

26-28: 속성 전파 방식 개선 필요

동일한 속성이 여러 요소에 전파되고 있어 의도치 않은 동작이 발생할 수 있습니다. profilePlusWrapper에 전달되는 속성을 제한하는 것이 좋습니다.

다음과 같이 수정을 제안합니다:

-          <div css={S.profilePlusWrapper} {...attributes}>
+          <div css={S.profilePlusWrapper}>
             <MobilePlus />
           </div>

Also applies to: 39-41


19-24: 접근성 개선 필요

이미지에 대한 대체 텍스트가 너무 일반적입니다. 사용자의 프로필 이미지임을 더 명확하게 설명하는 것이 좋습니다.

-              alt="프로필 이미지"
+              alt="사용자 프로필 이미지"
src/pages/mobile/SignUpMobilePage/SignUpMobilePage.style.ts (1)

41-52: 버튼 컨테이너 위치 처리 개선 필요

절대 위치를 사용한 버튼 컨테이너가 스크롤 시 콘텐츠와 겹칠 수 있습니다. 또한 그라데이션 배경이 iOS에서 성능 저하를 일으킬 수 있습니다.

sticky 포지셔닝을 사용하여 다음과 같이 수정하는 것을 제안합니다:

export const btnContainer = css({
  display: 'flex',
  alignItems: 'center',
  gap: '15px',
  padding: '0 10px',
-  position: 'absolute',
+  position: 'sticky',
  bottom: '0',
-  left: '50%',
-  transform: 'translate(-50%)',
  background:
    'linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0))',
+  width: '100%',
});
src/components/mobile/molecules/DefaultProfileModal/DefaultProfileModal.style.ts (1)

30-31: 이미지 크기의 반응형 처리 필요

고정된 픽셀 단위의 이미지 크기가 다양한 화면 크기에서 일관된 레이아웃을 보장하지 못할 수 있습니다.

상대적인 단위를 사용하여 다음과 같이 수정하는 것을 제안합니다:

export const imageButtonStyling = css({
  all: 'unset',
-  width: '70px',
-  height: '70px',
+  width: 'clamp(60px, 20vw, 70px)',
+  height: 'clamp(60px, 20vw, 70px)',
  borderRadius: '3px',
  position: 'relative',
  overflow: 'hidden',
  cursor: 'pointer',
});
src/stories/mobile/atoms/Input.stories.tsx (1)

48-49: 에러 메시지 상수화 필요

에러 메시지가 하드코딩되어 있어 일관성 유지와 다국어 지원이 어려울 수 있습니다.

상수 파일을 생성하여 다음과 같이 수정하는 것을 제안합니다:

+import { ERROR_MESSAGES } from '@/constants/messages';

-        <Input {...args} isError errorMessage="유효하지 않습니다." />
-        <Input {...args} errorMessage="유효합니다." success />
+        <Input {...args} isError errorMessage={ERROR_MESSAGES.INVALID_INPUT} />
+        <Input {...args} errorMessage={ERROR_MESSAGES.VALID_INPUT} success />
src/hooks/common/inputsUserInfo/useCheckNickname.ts (1)

16-19: 입력값 변경 시 에러 상태 초기화 로직 추가

사용자가 닉네임을 수정할 때마다 에러 상태를 초기화하여 더 나은 사용자 경험을 제공합니다.

다만, 아래와 같이 불필요한 에러 상태 설정을 제거하는 것이 좋을 것 같습니다:

  useEffect(() => {
-   setIsError(true);
    setErrorMessage(undefined);
  }, [value]);

setIsError(true)는 불필요해 보입니다. 사용자가 입력을 수정하는 중에는 에러 상태를 false로 유지하는 것이 더 자연스러울 것 같습니다.

src/hooks/common/inputsUserInfo/useCheckEmail.ts (1)

20-23: 이메일 입력값 변경 시 에러 상태 초기화 로직

닉네임 검증 훅과 동일한 이슈가 있습니다.

아래와 같이 불필요한 에러 상태 설정을 제거하는 것이 좋을 것 같습니다:

  useEffect(() => {
-   setIsError(true);
    setErrorMessage(undefined);
  }, [value]);

사용자가 입력을 수정하는 중에는 에러 상태를 false로 유지하는 것이 더 자연스러울 것 같습니다.

src/components/mobile/molecules/DefaultProfileModal/DefaultProfileModal.tsx (1)

49-50: 옵셔널 체이닝을 사용하여 코드를 개선하세요.

onSelect && onSelect(selectedImg) 구문을 옵셔널 체이닝을 사용하여 더 간결하게 작성할 수 있습니다.

다음과 같이 수정하세요:

-onClick={() => onSelect && onSelect(selectedImg)}
+onClick={() => onSelect?.(selectedImg)}
🧰 Tools
🪛 Biome (1.9.4)

[error] 49-50: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/components/molecules/InputPw/InputPw.tsx (1)

Line range hint 38-77: 중복 코드를 줄이기 위해 컴포넌트를 리팩토링하세요.

모바일과 데스크톱 렌더링 로직에 중복된 props가 많이 있습니다. 공통 props를 추출하여 코드를 더 간결하게 만들 수 있습니다.

다음과 같은 리팩토링을 제안합니다:

const commonProps = {
  id: "password",
  name: "password",
  type: "password",
  placeholder: "비밀번호를 입력해주세요.",
  minLength: INPUT_LIMIT.PW_MIN,
  maxLength: INPUT_LIMIT.PW_MAX,
  isError,
  errorMessage,
  value,
  ref: inputRef,
  onChange,
  onKeyDown: handleVerify,
  onBlur: handleVerify
};

return isMobile ? (
  <MobileInput
    {...commonProps}
    success={success}
  />
) : (
  <div css={S.inputPwContainer}>
    <Label id="password" css={S.labelStyling}>
      비밀번호
    </Label>
    <Input
      {...commonProps}
      size="small"
    />
  </div>
);
src/components/molecules/InputNickname/InputNickname.tsx (2)

13-17: Props 인터페이스에 타입 주석 추가 필요

Props 인터페이스의 각 속성에 대한 JSDoc 주석이 누락되어 있습니다. 특히 새로 추가된 isMobilesuccess props의 용도와 기대값에 대한 설명이 필요합니다.

다음과 같이 주석을 추가하는 것을 제안합니다:

 interface InputNicknameProps {
+  /** 모바일 디바이스 여부 */
   isMobile?: boolean;
+  /** 입력값 */
   value: string;
+  /** 입력값 변경 핸들러 */
   onChange: (e: ChangeEvent<HTMLInputElement>) => void;
+  /** 유효성 검사 성공 시 콜백 */
   onSuccessChange?: (name: string, value: boolean) => void;
+  /** 입력값 유효성 검사 성공 여부 */
   success?: boolean;
 }

36-56: 모바일 입력 컴포넌트의 재사용성 개선 필요

모바일 입력 컴포넌트의 구현이 중복될 수 있는 구조입니다. 공통 props를 추출하여 재사용성을 높이는 것이 좋겠습니다.

다음과 같은 리팩토링을 제안합니다:

+const commonInputProps = {
+  id: "nickname",
+  name: "nickname",
+  placeholder: "닉네임을 입력해주세요.",
+  isError,
+  errorMessage,
+  value,
+  ref: inputRef,
+  onChange,
+};

 return isMobile ? (
   <MobileInput
-    id="nickname"
-    name="nickname"
-    placeholder="닉네임을 입력해주세요."
-    isError={isError}
-    errorMessage={errorMessage}
-    value={value}
-    ref={inputRef}
-    onChange={onChange}
+    {...commonInputProps}
     success={success}
     btn={/*...*/}
   />
 ) : (
   <Input
-    id="nickname"
-    name="nickname"
-    placeholder="닉네임을 입력해주세요."
-    isError={isError}
-    errorMessage={errorMessage}
-    value={value}
-    ref={inputRef}
-    onChange={onChange}
+    {...commonInputProps}
     size="small"
     btn={/*...*/}
   />
 );
src/components/molecules/InputEmail/InputEmail.tsx (1)

40-60: 모바일 버튼 스타일링 로직 개선 필요

모바일 버튼의 스타일링이 인라인으로 적용되어 있어 재사용이 어렵습니다. 또한 활성화 상태 로직이 중복될 수 있습니다.

다음과 같은 리팩토링을 제안합니다:

+const MobileAuthButton = ({ value, onClick }) => (
+  <MobileButton
+    onClick={onClick}
+    css={S.mobileButtonStyling}
+    active={!isEmptyString(value)}
+  >
+    인증
+  </MobileButton>
+);

 return isMobile ? (
   <MobileInput
     {...inputProps}
     success={success}
     btn={
-      <MobileButton
-        onClick={handleSubmit}
-        css={S.mobileButtonStyling}
-        active={!isEmptyString(value)}
-      >
-        인증
-      </MobileButton>
+      <MobileAuthButton value={value} onClick={handleSubmit} />
     }
   />
 ) : (
   // ...
 );
src/api/interceptor.ts (1)

Line range hint 74-94: 토큰 갱신 실패 시 사용자 경험 개선 필요

토큰 갱신 실패 시 단순히 alert를 사용하는 것은 사용자 경험 측면에서 좋지 않습니다.

Toast 메시지나 모달을 사용하여 사용자에게 더 나은 피드백을 제공하는 것을 고려해보세요. 또한 로그아웃 처리를 별도의 유틸리티 함수로 분리하면 좋겠습니다:

+const handleLogout = (message: string) => {
+  delete axiosInstance.defaults.headers.Authorization;
+  store.dispatch(tokenActions.deleteToken());
+  
+  // alert 대신 Toast 사용
+  showToast(message);
+  window.location.href = `/${PATH.LOGIN}`;
+};

 try {
   // ...
 } catch (err) {
-  delete axiosInstance.defaults.headers.Authorization;
-  store.dispatch(tokenActions.deleteToken());
-
-  alert(NOTICE.LOGIN.EXPIRED);
-  window.location.href = `/${PATH.LOGIN}`;
+  handleLogout(NOTICE.LOGIN.EXPIRED);
   return Promise.reject(err);
 }
src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.tsx (1)

71-93: 소셜 로그인 버튼 구현 최적화 제안

반복되는 소셜 로그인 버튼 코드를 map 함수를 사용하여 리팩토링하면 코드를 더 간결하게 만들 수 있습니다.

-      <div css={S.btnWrapperStyling}>
-        <SocialLoginButton
-          variant="kakao"
-          size="small"
-          onClick={() => {
-            handleSocialLogin('kakao');
-          }}
-        />
-        <SocialLoginButton
-          variant="google"
-          size="small"
-          onClick={() => {
-            handleSocialLogin('google');
-          }}
-        />
-        <SocialLoginButton
-          variant="naver"
-          size="small"
-          onClick={() => {
-            handleSocialLogin('naver');
-          }}
-        />
-      </div>
+      <div css={S.btnWrapperStyling}>
+        {['kakao', 'google', 'naver'].map((provider) => (
+          <SocialLoginButton
+            key={provider}
+            variant={provider}
+            size="small"
+            onClick={() => handleSocialLogin(provider)}
+          />
+        ))}
+      </div>
src/components/molecules/InputProfileImage/InputProfileImage.tsx (1)

57-59: 이미지 크기 제한 메시지 개선 제안

사용자 경험 향상을 위해 현재 업로드된 이미지의 크기도 함께 표시하는 것이 좋을 것 같습니다.

-        <span css={S.mobileProfileImageText(isError)}>
-          3MB 이하의 사진만 가능합니다.
-        </span>
+        <span css={S.mobileProfileImageText(isError)}>
+          {isError ? `현재 이미지 크기: ${imageSizeInMB}MB (3MB 이하만 가능)` : '3MB 이하의 사진만 가능합니다.'}
+        </span>
src/pages/mobile/SignUpMobilePage/SignUpMobilePage.tsx (1)

36-83: 스크롤 영역 사용성 개선 제안

긴 폼에서의 사용자 경험 향상을 위해 스크롤 위치 표시기나 진행 상태 표시를 추가하는 것이 좋을 것 같습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a5bdee and eae01cb.

⛔ Files ignored due to path filters (8)
  • src/assets/svg/default-person.svg is excluded by !**/*.svg
  • src/assets/svg/logo-medium.svg is excluded by !**/*.svg
  • src/assets/svg/mobile-default-person.svg is excluded by !**/*.svg
  • src/assets/svg/mobile-google-login.svg is excluded by !**/*.svg
  • src/assets/svg/mobile-kakao-login.svg is excluded by !**/*.svg
  • src/assets/svg/mobile-naver-login.svg is excluded by !**/*.svg
  • src/assets/svg/mobile-plus.svg is excluded by !**/*.svg
  • src/assets/svg/mobile-recent-login.svg is excluded by !**/*.svg
📒 Files selected for processing (47)
  • src/App.tsx (2 hunks)
  • src/api/interceptor.ts (2 hunks)
  • src/assets/index.ts (1 hunks)
  • src/components/atoms/Divider/Divider.style.ts (1 hunks)
  • src/components/atoms/Divider/Divider.tsx (1 hunks)
  • src/components/atoms/ProfileSetting/ProfileSetting.style.ts (1 hunks)
  • src/components/atoms/ProfileSetting/ProfileSetting.tsx (2 hunks)
  • src/components/atoms/SocialLoginButton/SocialLoginButton.style.ts (2 hunks)
  • src/components/atoms/SocialLoginButton/SocialLoginButton.tsx (1 hunks)
  • src/components/mobile/atoms/ErrorMessage/ErrorMessage.style.ts (1 hunks)
  • src/components/mobile/atoms/ErrorMessage/ErrorMessage.tsx (1 hunks)
  • src/components/mobile/atoms/Input/Input.style.ts (1 hunks)
  • src/components/mobile/atoms/Input/Input.tsx (1 hunks)
  • src/components/mobile/atoms/ProfileSetting/ProfileSetting.style.ts (1 hunks)
  • src/components/mobile/atoms/ProfileSetting/ProfileSetting.tsx (1 hunks)
  • src/components/mobile/molecules/DefaultProfileModal/DefaultProfileModal.style.ts (1 hunks)
  • src/components/mobile/molecules/DefaultProfileModal/DefaultProfileModal.tsx (1 hunks)
  • src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.style.ts (1 hunks)
  • src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.tsx (1 hunks)
  • src/components/molecules/InputCode/InputCode.style.ts (2 hunks)
  • src/components/molecules/InputCode/InputCode.tsx (2 hunks)
  • src/components/molecules/InputEmail/InputEmail.style.ts (2 hunks)
  • src/components/molecules/InputEmail/InputEmail.tsx (3 hunks)
  • src/components/molecules/InputNickname/InputNickname.style.ts (2 hunks)
  • src/components/molecules/InputNickname/InputNickname.tsx (2 hunks)
  • src/components/molecules/InputProfileImage/InputProfileImage.style.ts (2 hunks)
  • src/components/molecules/InputProfileImage/InputProfileImage.tsx (2 hunks)
  • src/components/molecules/InputPw/InputPw.tsx (2 hunks)
  • src/components/molecules/InputPwConfirm/InputPwConfirm.tsx (1 hunks)
  • src/components/molecules/LoginForm/LoginForm.tsx (1 hunks)
  • src/constants/message.ts (1 hunks)
  • src/hooks/common/inputsUserInfo/useCheckEmail.ts (2 hunks)
  • src/hooks/common/inputsUserInfo/useCheckNickname.ts (2 hunks)
  • src/hooks/common/inputsUserInfo/useCheckPassword.ts (1 hunks)
  • src/hooks/common/inputsUserInfo/useCheckPasswordCheck.ts (2 hunks)
  • src/hooks/common/inputsUserInfo/useCheckProfileImage.ts (2 hunks)
  • src/hooks/signup/useSignupForm.ts (1 hunks)
  • src/pages/LoginPage/LoginPage.style.ts (2 hunks)
  • src/pages/LoginPage/LoginPage.tsx (3 hunks)
  • src/pages/mobile/SignUpMobilePage/SignUpMobilePage.style.ts (1 hunks)
  • src/pages/mobile/SignUpMobilePage/SignUpMobilePage.tsx (1 hunks)
  • src/stories/atoms/Divider.stories.tsx (3 hunks)
  • src/stories/atoms/SocialLoginButton.stories.tsx (3 hunks)
  • src/stories/mobile/atoms/Input.stories.tsx (1 hunks)
  • src/stories/mobile/molecules/DefaultProfileModal.stories.tsx (1 hunks)
  • src/stories/mobile/molecules/LoginForm.stories.tsx (1 hunks)
  • src/styles/typo.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/components/molecules/LoginForm/LoginForm.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/mobile/molecules/DefaultProfileModal/DefaultProfileModal.tsx

[error] 49-50: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: storybook
🔇 Additional comments (35)
src/components/molecules/InputProfileImage/InputProfileImage.style.ts (1)

11-13: 반응형 디자인 구현이 적절합니다!

모바일 화면에서의 간격 조정이 적절하게 이루어졌습니다. 기존 간격을 절반으로 줄임으로써 모바일에서의 공간 활용도를 높였습니다.

Also applies to: 22-24

src/hooks/common/inputsUserInfo/useCheckPasswordCheck.ts (1)

23-25: 빈 값 처리 로직 개선됨

빈 값에 대한 early return 패턴을 도입하여 불필요한 상태 업데이트를 방지하고 코드 흐름을 개선했습니다. 이는 useCheckPassword 훅의 동작과도 일관성을 유지합니다.

src/hooks/common/inputsUserInfo/useCheckPassword.ts (1)

33-35: 빈 값 처리 로직 개선됨

빈 값에 대한 early return 패턴 도입으로 코드의 가독성과 유지보수성이 향상되었습니다. 이는 useCheckPasswordCheck 훅과 일관된 에러 처리 방식을 제공합니다.

src/components/atoms/Divider/Divider.tsx (1)

6-6: 구현이 깔끔하고 타입 안전성이 잘 유지되었습니다!

tone 프로퍼티의 추가와 기본값 설정이 적절하게 구현되었습니다. 타입스크립트의 장점을 잘 활용하여 타입 안전성을 보장하고 있습니다.

Also applies to: 10-15

src/stories/atoms/Divider.stories.tsx (1)

18-21: 스토리북 구현이 완벽합니다!

새로운 tone 프로퍼티에 대한 스토리북 구현이 매우 체계적으로 되어있습니다:

  • 라디오 컨트롤을 통한 직관적인 옵션 선택
  • 기본 스토리에서의 적절한 기본값 설정
  • All 스토리에서 다양한 사용 사례 제공

사용자가 컴포넌트의 모든 기능을 쉽게 테스트하고 이해할 수 있도록 잘 구성되었습니다.

Also applies to: 36-36, 47-47, 52-52

src/components/mobile/atoms/ErrorMessage/ErrorMessage.style.ts (1)

6-12: 스타일링 구현이 잘 되었습니다!

타이포그래피와 색상 시스템을 일관되게 사용하고 있으며, 에러 상태에 따른 조건부 스타일링이 명확하게 구현되어 있습니다.

src/components/molecules/InputCode/InputCode.style.ts (1)

22-26: 모바일 버튼 스타일링이 적절하게 구현되었습니다!

모바일 환경에 맞는 타이포그래피와 크기가 잘 적용되었습니다. 버튼의 크기와 모서리 둥글기가 모바일 UI/UX 가이드라인에 부합합니다.

src/pages/LoginPage/LoginPage.style.ts (3)

13-16: 로그인 페이지의 모바일 반응형 디자인이 적절합니다!

모바일 화면(430px 이하)에서 패딩과 배경색 변경이 자연스럽게 적용되었습니다.


22-24: 로고 스타일의 모바일 대응이 잘 되었습니다!

모바일 환경에서 로고의 마진 조정이 적절합니다.


34-36: 토스트 모달의 위치가 모바일에 맞게 조정되었습니다!

모바일 화면에서 상단 여백이 적절하게 조정되었습니다.

src/components/atoms/ProfileSetting/ProfileSetting.style.ts (2)

14-14: 프로필 설정 컨테이너의 시각적 개선이 좋습니다!

박스 쉐도우 추가로 시각적 깊이감이 향상되었습니다.


Line range hint 35-39: profilePlusImageWrapper의 크기 제약 제거를 확인해주세요.

width, height, backgroundColor가 제거되었습니다. 이로 인해 레이아웃에 의도하지 않은 영향이 있을 수 있습니다.

src/components/mobile/atoms/Input/Input.style.ts (3)

5-10: 입력 컨테이너의 구조가 잘 설계되었습니다!

컨테이너의 크기와 레이아웃이 모바일 환경에 적합하게 구성되었습니다.


12-22: 입력 필드 래퍼의 상태 처리가 적절합니다!

성공 상태에 따른 테두리 색상 변경이 잘 구현되었습니다. 간격과 크기도 모바일 환경에 맞게 설정되었습니다.


24-39: 입력 필드의 스타일링이 세밀하게 구현되었습니다!

  • 웹킷 자동완성 배경 처리
  • 플레이스홀더 색상 설정
  • 기본 브라우저 스타일 초기화

모든 필요한 스타일링이 적절하게 처리되었습니다.

src/components/mobile/atoms/Input/Input.tsx (1)

6-12: 인터페이스 정의가 잘 되어있습니다!

size 속성을 제외한 input 요소의 모든 속성을 상속받으면서, 필요한 추가 속성들도 잘 정의되어 있습니다.

src/stories/mobile/molecules/DefaultProfileModal.stories.tsx (1)

12-19: 프로필 이미지 목록이 잘 정의되어 있습니다.

기본 프로필 이미지들이 잘 구성되어 있습니다.

src/stories/mobile/molecules/LoginForm.stories.tsx (1)

10-29: 구현이 잘 되었습니다!

스토리북 설정이 적절하게 구현되었으며, 필요한 모든 Provider들이 포함되어 있습니다.

src/stories/atoms/SocialLoginButton.stories.tsx (1)

18-21: 스토리 구조가 잘 구성되었습니다!

사이즈 변형을 보여주는 구조가 명확하고 이해하기 쉽게 구현되었습니다. Medium과 Small 사이즈를 구분하여 보여주는 방식이 효과적입니다.

Also applies to: 38-49

src/pages/mobile/SignUpMobilePage/SignUpMobilePage.style.ts (1)

10-11: 반응형 높이 계산 방식 개선 필요

고정된 높이 계산과 최대 높이 제한이 일부 모바일 기기에서 레이아웃 문제를 일으킬 수 있습니다.

다양한 모바일 기기에서의 호환성을 확인하기 위해 다음 스크립트를 실행해주세요:

src/components/atoms/SocialLoginButton/SocialLoginButton.tsx (4)

3-12: 소셜 로그인 컴포넌트 모바일 대응

모바일 전용 이미지 컴포넌트들이 적절하게 추가되었습니다.


17-19: size prop 타입 정의 적절성

size prop이 적절하게 타입 정의되어 있습니다.


33-41: 조건부 이미지 컴포넌트 선택 로직

size prop에 따른 이미지 컴포넌트 선택 로직이 깔끔하게 구현되어 있습니다.


56-61: 최근 로그인 표시 조건부 렌더링

size prop에 따라 적절한 최근 로그인 컴포넌트를 렌더링하고 있습니다.

src/hooks/signup/useSignupForm.ts (1)

65-65: 폼 성공 상태 노출

successForm을 반환 객체에 추가하여 컴포넌트의 재사용성이 향상되었습니다.

src/pages/LoginPage/LoginPage.tsx (1)

45-59: 모바일 대응이 잘 구현되었습니다!

모바일과 데스크톱 환경에 따른 조건부 렌더링이 깔끔하게 구현되었습니다.

src/components/molecules/InputPwConfirm/InputPwConfirm.tsx (1)

29-31: useEffect 의존성 배열을 검토하세요.

handleVerify가 의존성 배열에 포함되어 있지 않습니다. 이는 잠재적으로 오래된 handleVerify 함수를 사용할 수 있음을 의미합니다.

다음과 같이 수정하는 것을 고려하세요:

  useEffect(() => {
    handleVerify();
-  }, [value, pw]);
+  }, [value, pw, handleVerify]);
src/components/molecules/InputPw/InputPw.tsx (1)

29-30: useEffect 의존성 배열을 검토하세요.

handleVerify가 의존성 배열에 포함되어 있지 않습니다.

다음과 같이 수정하는 것을 고려하세요:

  useEffect(() => {
    handleVerify();
-  }, [value]);
+  }, [value, handleVerify]);
src/components/molecules/InputCode/InputCode.tsx (2)

13-13: Props 인터페이스가 적절히 확장되었습니다.

모바일 대응을 위한 isMobilesuccess props가 잘 정의되어 있으며, 기본값도 적절히 설정되어 있습니다.

Also applies to: 19-19, 23-23, 29-29


43-64: 모바일 입력 폼의 구현이 잘 되어있습니다.

모바일 환경에 맞는 UI 컴포넌트들이 적절히 사용되었으며, 기존 기능들이 모바일 환경에서도 잘 동작하도록 구현되어 있습니다.

src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.tsx (1)

30-32: OAuth 리다이렉션 URL 처리 보안 검토 필요

환경 변수를 통한 API URL 사용은 좋은 방식이나, 보안을 위해 추가적인 검증이 필요할 수 있습니다.

✅ Verification successful

OAuth 리다이렉션 URL 처리 검증 완료

현재 코드에서는 process.env.API_URL 값을 활용하여 OAuth 리다이렉션 URL을 구성하고 있으며, 이 패턴은 webpack 구성 및 API 호출 등 코드 전반에 걸쳐 일관되게 사용되고 있습니다. 환경 변수 설정이 Webpack의 EnvironmentPlugin 등으로 관리되고 있는 점에서, 코드 상으로는 특별한 보안 취약점이 확인되지 않습니다. 다만, 운영 환경에서 API_URL 값이 신뢰할 수 있는 도메인으로만 설정되었는지 추가 확인이 필요합니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# OAuth 리다이렉션 URL 관련 환경 설정 확인
rg -g '*.{ts,js,json}' "API_URL|oauth2|authorization" -A 3

Length of output: 2415

src/components/molecules/InputProfileImage/InputProfileImage.tsx (1)

14-16: 타입 확장이 적절히 구현되었습니다.

ProfileImageProps를 확장하여 isMobile 속성을 추가한 것이 좋은 접근 방식입니다.

src/constants/message.ts (1)

25-25: 로그인 오류 메시지가 적절히 추가되었습니다!

기존 오류 메시지와 일관된 스타일을 유지하면서 사용자에게 명확한 피드백을 제공합니다.

src/App.tsx (1)

71-74: 모바일 기기 대응이 적절히 구현되었습니다!

isMobile 훅을 사용하여 기기 타입에 따라 적절한 회원가입 페이지를 렌더링하는 구현이 잘 되어있습니다.

src/assets/index.ts (1)

135-142: 모바일 UI를 위한 아이콘들이 체계적으로 추가되었습니다!

모바일 소셜 로그인, 프로필 관련 아이콘들이 기존 네이밍 컨벤션을 잘 따르고 있으며, 모바일 UI 구현에 필요한 에셋들이 적절히 추가되었습니다.

Comment on lines +22 to +26
export const mobileButtonStyling = css(typo.Mobile.Text.SemiBold_14, {
width: '75px',
height: '32px',
borderRadius: '4px',
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

중복된 스타일 코드를 공통 컴포넌트로 분리하는 것을 추천드립니다.

InputEmail과 동일한 모바일 버튼 스타일링이 중복되어 있습니다. 이를 공통 스타일로 분리하여 재사용성을 높이면 좋을 것 같습니다.

다음과 같이 수정하는 것을 제안드립니다:

  1. 공통 스타일 파일 생성 (src/styles/common/buttons.ts):
export const commonMobileButtonStyle = css(typo.Mobile.Text.SemiBold_14, {
  width: '75px',
  height: '32px',
  borderRadius: '4px',
});
  1. 각 컴포넌트에서 공통 스타일 사용:
-export const mobileButtonStyling = css(typo.Mobile.Text.SemiBold_14, {
-  width: '75px',
-  height: '32px',
-  borderRadius: '4px',
-});
+import { commonMobileButtonStyle } from '@/styles/common/buttons';
+export const mobileButtonStyling = commonMobileButtonStyle;

Comment on lines +26 to 28
<div css={S.profilePlusWrapper} {...attributes}>
<Plus />
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

속성 전파 방식 개선 필요

attributes를 여러 요소에 전파하는 것은 예기치 않은 동작을 일으킬 수 있습니다. 특히 이벤트 핸들러가 중복 실행될 수 있습니다.

다음과 같이 수정하는 것을 추천드립니다:

-          <div css={S.profilePlusWrapper} {...attributes}>
+          <div css={S.profilePlusWrapper}>
            <Plus />
          </div>

-          <div css={S.profilePlusWrapper} {...attributes}>
+          <div css={S.profilePlusWrapper}>
            <Plus />
          </div>

Also applies to: 39-41

Comment on lines 32 to 44
{imgList.map((src) => (
<button
key={src}
type="button"
onClick={() => handleDefaultImage(src)}
css={[
S.imageButtonStyling,
selectedImg === src && S.selectedImageStyling,
]}
>
<img src={src} alt={src} css={S.profileImage} />
</button>
))}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

이미지 접근성을 개선하세요.

이미지의 alt 텍스트가 src 값을 그대로 사용하고 있어 스크린 리더 사용자에게 의미 있는 정보를 제공하지 않습니다.

다음과 같이 수정하세요:

-<img src={src} alt={src} css={S.profileImage} />
+<img src={src} alt={`프로필 이미지 ${imgList.indexOf(src) + 1}`} css={S.profileImage} />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{imgList.map((src) => (
<button
key={src}
type="button"
onClick={() => handleDefaultImage(src)}
css={[
S.imageButtonStyling,
selectedImg === src && S.selectedImageStyling,
]}
>
<img src={src} alt={src} css={S.profileImage} />
</button>
))}
{imgList.map((src) => (
<button
key={src}
type="button"
onClick={() => handleDefaultImage(src)}
css={[
S.imageButtonStyling,
selectedImg === src && S.selectedImageStyling,
]}
>
<img src={src} alt={`프로필 이미지 ${imgList.indexOf(src) + 1}`} css={S.profileImage} />
</button>
))}

Comment on lines +74 to +77
if (
status === HTTP_STATUS_CODE.UNAUTHORIZED &&
data.message !== ERROR.LOGIN.NOT_MATCH
) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 메시지 상수화 및 타입 안정성 개선 필요

로그인 실패 메시지 체크가 문자열 비교로 되어 있어 오타나 메시지 변경 시 문제가 발생할 수 있습니다.

다음과 같은 개선을 제안합니다:

+type LoginErrorType = typeof ERROR.LOGIN[keyof typeof ERROR.LOGIN];
+
+const isLoginMismatchError = (message?: string): message is LoginErrorType => {
+  return message === ERROR.LOGIN.NOT_MATCH;
+};

 if (
   status === HTTP_STATUS_CODE.UNAUTHORIZED &&
-  data.message !== ERROR.LOGIN.NOT_MATCH
+  !isLoginMismatchError(data.message)
 ) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
status === HTTP_STATUS_CODE.UNAUTHORIZED &&
data.message !== ERROR.LOGIN.NOT_MATCH
) {
type LoginErrorType = typeof ERROR.LOGIN[keyof typeof ERROR.LOGIN];
const isLoginMismatchError = (message?: string): message is LoginErrorType => {
return message === ERROR.LOGIN.NOT_MATCH;
};
if (
status === HTTP_STATUS_CODE.UNAUTHORIZED &&
!isLoginMismatchError(data.message)
) {

Comment on lines +84 to +89
<div css={S.btnContainer}>
<Button active={false} onClick={handleCancel}>
취소
</Button>
<Button type="submit">회원가입</Button>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

하단 버튼 접근성 개선 필요

모바일 환경에서 하단 버튼의 접근성을 높이기 위해 aria-label 속성 추가가 필요합니다.

-        <Button active={false} onClick={handleCancel}>
+        <Button active={false} onClick={handleCancel} aria-label="회원가입 취소">
           취소
         </Button>
-        <Button type="submit">회원가입</Button>
+        <Button type="submit" aria-label="회원가입 완료">회원가입</Button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div css={S.btnContainer}>
<Button active={false} onClick={handleCancel}>
취소
</Button>
<Button type="submit">회원가입</Button>
</div>
<div css={S.btnContainer}>
<Button active={false} onClick={handleCancel} aria-label="회원가입 취소">
취소
</Button>
<Button type="submit" aria-label="회원가입 완료">회원가입</Button>
</div>

Comment on lines +99 to +107
Text: {
Regular: {
fontFamily: 'Pretendard',
fontSize: '14px',
fontWeight: 400,
lineHeight: '1.6',
letterSpacing: `${16 * -0.05}px`,
},
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Text.Regular의 letterSpacing 계산을 수정해주세요.

letterSpacing 계산에 사용된 값이 잘못되었습니다:

-      letterSpacing: `${16 * -0.05}px`,
+      letterSpacing: `${14 * -0.05}px`,

fontSize가 14px인데 letterSpacing 계산에 16을 사용하고 있습니다. 일관성을 위해 실제 fontSize 값을 사용해주세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Text: {
Regular: {
fontFamily: 'Pretendard',
fontSize: '14px',
fontWeight: 400,
lineHeight: '1.6',
letterSpacing: `${16 * -0.05}px`,
},
},
Text: {
Regular: {
fontFamily: 'Pretendard',
fontSize: '14px',
fontWeight: 400,
lineHeight: '1.6',
letterSpacing: `${14 * -0.05}px`,
},
},

Copy link
Collaborator

@alwubin alwubin left a comment

Choose a reason for hiding this comment

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

모바일 로그인/회원가입 구현하시느라 수고 많으셨어요!!! UI 너무 깔끔하고 이뻐요😍 아주 간단한 커멘트 남겨두었습니닷...!

isError?: boolean;
}

const ErrorMessage = ({
Copy link
Collaborator

Choose a reason for hiding this comment

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

Input 값에 대한 성공 혹은 실패 메세지 컴포넌트를 ErrorMessage로 정하신 이유가 따로 있으신지 궁금합니다.!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

사실 기존에 구현되어있던 컴포넌트를 참고하여 만든 것도 있는데요..!! 아무래도 로직도 웹에서 사용되는 훅을 동일하게 연결해야하다보니 웹 기준의 컴포넌트와 통일시키는 것이 나을 것이라 생각했습니다!!

src/hooks/common/inputsUserInfo/useCheckNickname.ts Outdated Show resolved Hide resolved
Copy link
Collaborator

@WonJuneKim WonJuneKim left a comment

Choose a reason for hiding this comment

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

기존 컴포넌트를 재사용하려고 했던 흔적들이 돋보이네요.. 빠른 구현 고생하셨습니다!!

src/components/atoms/Divider/Divider.tsx Outdated Show resolved Hide resolved
Comment on lines +9 to +10
btn?: ReactElement;
errorMessage?: string;
Copy link
Collaborator

Choose a reason for hiding this comment

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

btn의 경우 축약형을, errorMessage의 경우에는 풀네임을 쓰셨는데, 두개 중에 하나로 통일 되어도 좋아보이네요!

src/components/mobile/atoms/Input/Input.tsx Outdated Show resolved Hide resolved
Comment on lines +75 to +76
status === HTTP_STATUS_CODE.UNAUTHORIZED &&
data.message !== ERROR.LOGIN.NOT_MATCH
Copy link
Collaborator

Choose a reason for hiding this comment

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

좋은 발견이네요!
사실 에러 코드가 다른게 제일 좋은 처리라고 생각되는데, 이 부분은 서버 분들에게 한번 여쭤봐야겠군요!

value: Pick<MemberForm, 'email' | 'verificationCode'>;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
onSuccessChange: (name: string, value: boolean) => void;
sendSuccess: boolean;
handleVerifySuccess?: React.Dispatch<React.SetStateAction<boolean>>;
success?: boolean;
Copy link
Collaborator

Choose a reason for hiding this comment

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

이 부분도 isSuccess로 처리해도 좋아보입니다!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (13)
src/components/mobile/molecules/DefaultProfileModal/DefaultProfileModal.tsx (3)

6-11: 인터페이스에 JSDoc 문서를 추가하세요.

각 프로퍼티의 목적과 사용법을 명확하게 설명하는 JSDoc 문서를 추가하면 컴포넌트의 사용성이 향상될 것입니다.

+/**
+ * 기본 프로필 모달 컴포넌트의 프로퍼티
+ */
 export interface DefaultProfileModalProps {
+  /** 모달의 표시 여부 */
   isOpen: boolean;
+  /** 선택 가능한 프로필 이미지 목록 */
   imgList: string[];
+  /** 이미지 선택 시 호출되는 콜백 함수 */
   onSelect?: (selectedImage: string | null) => void;
+  /** 모달 닫기 시 호출되는 콜백 함수 */
   onClose?: () => void;
 }

19-19: 초기 상태값 설정을 개선하세요.

사용자 경험을 향상시키기 위해 imgList의 첫 번째 이미지를 기본값으로 설정하는 것이 좋습니다.

-  const [selectedImg, setSelectedImg] = useState<string | null>(null);
+  const [selectedImg, setSelectedImg] = useState<string | null>(imgList[0] || null);

49-49: 옵셔널 체이닝을 사용하여 코드를 개선하세요.

옵셔널 체이닝을 사용하면 코드가 더 간결해지고 가독성이 향상됩니다.

-          onClick={() => onSelect && onSelect(selectedImg)}
+          onClick={() => onSelect?.(selectedImg)}
🧰 Tools
🪛 Biome (1.9.4)

[error] 49-50: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

src/components/molecules/InputPwConfirm/InputPwConfirm.tsx (3)

29-31: 검증 로직 최적화가 필요합니다.

handleVerify()valuepw가 변경될 때마다 무조건 호출되고 있습니다. 불필요한 검증을 방지하기 위해 조건부 실행을 고려해보세요.

  useEffect(() => {
-   handleVerify();
+   if (value && pw) {
+     handleVerify();
+   }
  }, [value, pw]);

39-54: 코드 재사용성 개선이 필요합니다.

MobileInputInput 컴포넌트에 전달되는 공통 props가 많습니다. 코드 중복을 줄이기 위해 공통 props를 객체로 분리하는 것이 좋겠습니다.

const commonProps = {
  id: "passwordConfirm",
  name: "passwordConfirm",
  type: "password",
  placeholder: "비밀번호를 재입력해주세요.",
  isError,
  errorMessage,
  value,
  ref: inputRef,
  onChange,
  onKeyDown: handleVerify,
  onBlur: handleVerify
};

그리고 다음과 같이 사용:

- <MobileInput
-   id="passwordConfirm"
-   name="passwordConfirm"
-   type="password"
-   placeholder="비밀번호를 재입력해주세요."
-   isError={isError}
-   errorMessage={errorMessage}
-   value={value}
-   ref={inputRef}
-   onChange={onChange}
-   onKeyDown={handleVerify}
-   onBlur={handleVerify}
+   <MobileInput
+     {...commonProps}
    isSuccess={success}
  />

33-37: 에러 처리 로직 개선이 필요합니다.

errorMessage 변경 시에만 성공 상태를 업데이트하고 있는데, 컴포넌트 언마운트 시의 처리가 누락되어 있습니다.

  useEffect(() => {
    if (value && onSuccessChange) {
      onSuccessChange('passwordConfirm', !isError);
    }
+   return () => {
+     if (onSuccessChange) {
+       onSuccessChange('passwordConfirm', false);
+     }
+   };
  }, [errorMessage]);
src/components/molecules/InputPw/InputPw.tsx (2)

28-30: 검증 로직 최적화가 필요합니다.

InputPwConfirm 컴포넌트와 동일한 이슈가 있습니다. 불필요한 검증을 방지하기 위해 조건부 실행을 추가하는 것이 좋겠습니다.

  useEffect(() => {
-   handleVerify();
+   if (value) {
+     handleVerify();
+   }
  }, [value]);

38-55: 컴포넌트 로직 재사용을 위한 구조 개선이 필요합니다.

InputPwInputPwConfirm 컴포넌트 간에 중복되는 로직이 많습니다. 공통 로직을 커스텀 훅으로 분리하는 것을 제안드립니다.

다음과 같은 커스텀 훅 생성을 고려해보세요:

function usePasswordInput({
  value,
  onChange,
  onSuccessChange,
  name,
  validateFn
}: PasswordInputHookProps) {
  const { inputRef, isError, errorMessage, handleVerify } = validateFn(value);

  useEffect(() => {
    if (value) {
      handleVerify();
    }
  }, [value]);

  useEffect(() => {
    if (value && onSuccessChange) {
      onSuccessChange(name, !isError);
    }
    return () => {
      if (onSuccessChange) {
        onSuccessChange(name, false);
      }
    };
  }, [errorMessage]);

  return {
    inputRef,
    isError,
    errorMessage,
    handleVerify
  };
}
src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.tsx (3)

12-16: 인터페이스 문서화 개선이 필요합니다.

각 프로퍼티의 목적과 사용 방법을 명확하게 설명하는 JSDoc 문서를 추가하면 좋을 것 같습니다.

 export interface LoginFormProps {
+  /** 토스트 메시지를 표시하는 함수. 메시지와 콜백을 받을 수 있습니다. */
   showToastModal?: (message: string, callback?: () => void) => void;
+  /** 로그인 상태 정보를 담고 있는 객체 */
   loginState?: State;
+  /** 모달 로그인 성공 시 실행될 콜백 함수 */
   onModalLoginSuccess?: () => void;
 }

70-92: 소셜 로그인 버튼 구현의 중복을 제거해주세요.

반복되는 코드를 맵 함수를 사용하여 더 간결하게 구현할 수 있습니다.

+const SOCIAL_BUTTONS = ['kakao', 'google', 'naver'] as const;
+
 <div css={S.btnWrapperStyling}>
-  <SocialLoginButton
-    variant="kakao"
-    size="small"
-    onClick={() => {
-      handleSocialLogin('kakao');
-    }}
-  />
-  <SocialLoginButton
-    variant="google"
-    size="small"
-    onClick={() => {
-      handleSocialLogin('google');
-    }}
-  />
-  <SocialLoginButton
-    variant="naver"
-    size="small"
-    onClick={() => {
-      handleSocialLogin('naver');
-    }}
-  />
+  {SOCIAL_BUTTONS.map((provider) => (
+    <SocialLoginButton
+      key={provider}
+      variant={provider}
+      size="small"
+      onClick={() => handleSocialLogin(provider)}
+    />
+  ))}
 </div>

18-95: 에러 바운더리 추가를 고려해주세요.

폼 제출이나 소셜 로그인 과정에서 발생할 수 있는 예기치 않은 에러를 우아하게 처리하기 위해 에러 바운더리 컴포넌트로 감싸는 것이 좋을 것 같습니다.

+import { ErrorBoundary } from '@/components/ErrorBoundary';
+
 const MobileLoginForm = ({
   showToastModal,
   loginState,
   onModalLoginSuccess,
 }: LoginFormProps) => {
   // ... existing code ...
   
   return (
+    <ErrorBoundary fallback={<div>로그인 폼을 불러오는데 실패했습니다.</div>}>
       <form onSubmit={handleSubmit} css={S.loginFormStyling}>
         // ... existing code ...
       </form>
+    </ErrorBoundary>
   );
 };
src/components/mobile/atoms/Input/Input.tsx (1)

8-11: Props 네이밍 컨벤션의 일관성이 필요합니다.

Props 네이밍에서 일관성이 부족합니다:

  • isErrorisSuccess는 boolean 타입을 나타내는 'is' 접두사를 사용
  • btn은 축약형을 사용
  • errorMessage는 전체 이름을 사용

네이밍 컨벤션을 통일하는 것이 좋겠습니다.

-  btn?: ReactElement;
+  button?: ReactElement;
src/stories/mobile/atoms/Input.stories.tsx (1)

14-18: 접근성 관련 컨트롤이 추가되면 좋겠습니다.

현재 argTypes에는 기본적인 컨트롤만 정의되어 있습니다. 접근성 향상을 위해 다음과 같은 속성들의 컨트롤도 추가하면 좋겠습니다:

  • aria-label
  • aria-describedby
  • required
  argTypes: {
    isError: { control: { type: 'boolean' } },
    errorMessage: { control: { type: 'text' } },
    isSuccess: { control: { type: 'boolean' } },
+   'aria-label': { control: { type: 'text' } },
+   'aria-describedby': { control: { type: 'text' } },
+   required: { control: { type: 'boolean' } },
  },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1403094 and d3ad331.

📒 Files selected for processing (15)
  • src/components/mobile/atoms/Input/Input.style.ts (1 hunks)
  • src/components/mobile/atoms/Input/Input.tsx (1 hunks)
  • src/components/mobile/molecules/DefaultProfileModal/DefaultProfileModal.tsx (1 hunks)
  • src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.tsx (1 hunks)
  • src/components/molecules/DefaultProfileModal/DefaultProfileModal.tsx (1 hunks)
  • src/components/molecules/InputCode/InputCode.tsx (2 hunks)
  • src/components/molecules/InputEmail/InputEmail.tsx (3 hunks)
  • src/components/molecules/InputNickname/InputNickname.tsx (2 hunks)
  • src/components/molecules/InputProfileImage/InputProfileImage.tsx (3 hunks)
  • src/components/molecules/InputPw/InputPw.tsx (2 hunks)
  • src/components/molecules/InputPwConfirm/InputPwConfirm.tsx (1 hunks)
  • src/components/molecules/LoginForm/LoginForm.tsx (1 hunks)
  • src/constants/message.ts (2 hunks)
  • src/hooks/common/inputsUserInfo/useCheckNickname.ts (2 hunks)
  • src/stories/mobile/atoms/Input.stories.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/components/molecules/DefaultProfileModal/DefaultProfileModal.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/constants/message.ts
  • src/hooks/common/inputsUserInfo/useCheckNickname.ts
  • src/components/molecules/InputProfileImage/InputProfileImage.tsx
  • src/components/molecules/InputNickname/InputNickname.tsx
  • src/components/molecules/InputEmail/InputEmail.tsx
  • src/components/molecules/LoginForm/LoginForm.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/mobile/molecules/DefaultProfileModal/DefaultProfileModal.tsx

[error] 49-50: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (12)
src/components/mobile/molecules/DefaultProfileModal/DefaultProfileModal.tsx (1)

42-42: 이미지 접근성을 개선하세요.

각 프렌즈 캐릭터를 구별할 수 있는 고유한 설명을 alt 텍스트로 제공해야 합니다.

-              <img src={src} alt="프렌즈 이미지" css={S.profileImage} />
+              <img
+                src={src}
+                alt={`PICK-O 프렌즈 캐릭터 ${imgList.indexOf(src) + 1}`}
+                css={S.profileImage}
+              />
src/components/molecules/InputPwConfirm/InputPwConfirm.tsx (1)

9-16: 인터페이스와 프롭 정의가 잘 되어있습니다!

모바일 대응을 위한 isMobile과 상태 표시를 위한 success 프롭이 적절하게 추가되었으며, 기본값도 잘 설정되어 있습니다.

Also applies to: 18-25

src/components/molecules/InputPw/InputPw.tsx (1)

10-16: 컴포넌트 간 일관성이 잘 유지되었습니다!

InputPwConfirm 컴포넌트와 동일한 방식으로 isMobilesuccess 프롭이 추가되어 일관성 있는 구현이 이루어졌습니다.

Also applies to: 18-24

src/components/molecules/InputCode/InputCode.tsx (4)

8-9: 모바일 컴포넌트 분리가 잘 되어있습니다!

모바일 전용 컴포넌트를 별도로 분리하고, 인터페이스에 필요한 props를 추가한 것이 좋습니다.

Also applies to: 13-13, 19-19


19-19: 일관된 네이밍 컨벤션 적용이 필요합니다.

기존 리뷰 코멘트에서 언급된 것처럼 boolean 타입의 상태값은 'is' 접두사를 사용하는 것이 좋습니다. successisSuccess로 변경하는 것을 고려해보세요.

interface InputCodeProps {
-  success?: boolean;
+  isSuccess?: boolean;
}

const InputCode = ({
-  success = false,
+  isSuccess = false,
}: InputCodeProps) => {
  // ...
      disabled={!sendSuccess}
-      isSuccess={success}
+      isSuccess={isSuccess}
  // ...
};

Also applies to: 29-29, 54-54


43-64: 모바일 UI 구현이 깔끔합니다!

모바일 버전에서 MobileInput과 MobileButton을 사용한 구현이 디자인 요구사항에 잘 부합합니다.


37-41: useEffect의 의존성 배열을 검토해주세요.

현재 useEffect는 errorMessage만을 의존성으로 가지고 있지만, value.verificationCode의 변경도 감지해야 할 것 같습니다. 이는 잠재적인 버그를 발생시킬 수 있습니다.

  useEffect(() => {
    if (value.verificationCode) {
      onSuccessChange('verificationCode', !isError);
    }
-  }, [errorMessage]);
+  }, [errorMessage, value.verificationCode, isError, onSuccessChange]);
src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.tsx (1)

36-41: 접근성 속성이 누락되었습니다.

이전 리뷰에서 언급된 접근성 관련 사항을 반영해야 합니다.

 <Input
   name="email"
   value={form.email}
   placeholder="이메일"
   onChange={onChange}
+  aria-label="이메일 입력"
 />
 <Input
   name="password"
   type="password"
   value={form.password}
   placeholder="비밀번호"
   isError={isError}
   errorMessage={errorMessage}
   onChange={onChange}
+  aria-label="비밀번호 입력"
 />

Also applies to: 42-50

src/components/mobile/atoms/Input/Input.style.ts (2)

32-34: 자동 완성 스타일링이 잘 처리되었습니다.

브라우저 호환성을 위한 -webkit-autofill 처리가 적절히 구현되어 있습니다.


8-9: 고정 너비 값 사용에 대한 검토가 필요합니다.

width: '335px'와 같은 고정 너비 값은 다양한 모바일 화면 크기에서 문제를 일으킬 수 있습니다. 반응형 디자인을 위해 상대적인 단위(%, rem) 사용을 고려해보세요.

-  width: '335px',
+  width: '100%',
+  maxWidth: '335px',
src/components/mobile/atoms/Input/Input.tsx (1)

6-12: Props 인터페이스가 잘 정의되어 있습니다.

InputProps 인터페이스가 명확하게 정의되어 있으며, HTML input 요소의 기본 속성을 적절히 확장하고 있습니다.

src/stories/mobile/atoms/Input.stories.tsx (1)

36-53: 스토리 구조가 잘 구성되어 있습니다.

다양한 상태와 변형을 보여주는 예제들이 체계적으로 구성되어 있으며, 한글 에러 메시지를 포함한 실제 사용 사례를 잘 보여주고 있습니다.

Comment on lines +29 to +31
const handleSocialLogin = (social: string) => {
window.location.href = `${process.env.API_URL}/oauth2/authorization/${social}`;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

소셜 로그인 URL 처리 개선이 필요합니다.

현재 구현은 몇 가지 개선이 필요해 보입니다:

  1. URL 구성을 상수로 분리하여 관리
  2. 환경 변수 유효성 검사 추가
  3. URL 인코딩 처리
+const OAUTH_URL = {
+  kakao: `${process.env.API_URL}/oauth2/authorization/kakao`,
+  google: `${process.env.API_URL}/oauth2/authorization/google`,
+  naver: `${process.env.API_URL}/oauth2/authorization/naver`,
+} as const;
+
 const handleSocialLogin = (social: string) => {
-  window.location.href = `${process.env.API_URL}/oauth2/authorization/${social}`;
+  if (!process.env.API_URL) {
+    console.error('API_URL is not defined');
+    return;
+  }
+  const url = OAUTH_URL[social as keyof typeof OAUTH_URL];
+  if (!url) {
+    console.error(`Invalid social login provider: ${social}`);
+    return;
+  }
+  window.location.href = encodeURI(url);
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleSocialLogin = (social: string) => {
window.location.href = `${process.env.API_URL}/oauth2/authorization/${social}`;
};
const OAUTH_URL = {
kakao: `${process.env.API_URL}/oauth2/authorization/kakao`,
google: `${process.env.API_URL}/oauth2/authorization/google`,
naver: `${process.env.API_URL}/oauth2/authorization/naver`,
} as const;
const handleSocialLogin = (social: string) => {
if (!process.env.API_URL) {
console.error('API_URL is not defined');
return;
}
const url = OAUTH_URL[social as keyof typeof OAUTH_URL];
if (!url) {
console.error(`Invalid social login provider: ${social}`);
return;
}
window.location.href = encodeURI(url);
};

Copy link

sonarqubecloud bot commented Feb 3, 2025

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/stories/atoms/Divider.stories.tsx (1)

41-56: All 스토리에서 다양한 구분선 스타일을 잘 보여주고 있습니다.

All 스토리에서 기본 스타일과 violet 톤을 함께 표시하여 사용자가 시각적으로 차이를 비교할 수 있도록 한 것이 좋습니다. 가로와 세로 방향 모두에 대해 두 가지 톤을 보여주어 컴포넌트의 다양한 사용 사례를 잘 설명하고 있습니다.

다만, 스토리북 문서화를 더욱 개선하기 위한 제안사항이 있습니다:

 export const All: Story = {
+  parameters: {
+    docs: {
+      description: {
+        story: '구분선의 방향(가로/세로)과 톤(회색/보라색)에 따른 다양한 스타일을 보여줍니다.',
+      },
+    },
+  },
   render: (args) => (
src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.tsx (1)

12-16: 인터페이스 문서화가 필요합니다.

인터페이스의 목적과 각 프로퍼티의 역할을 명확히 하기 위해 JSDoc 문서화를 추가하는 것이 좋겠습니다.

+/**
+ * 모바일 로그인 폼의 프로퍼티를 정의하는 인터페이스
+ */
 export interface LoginFormProps {
+  /** 토스트 메시지를 표시하는 콜백 함수 */
   showToastModal?: (message: string, callback?: () => void) => void;
+  /** 로그인 상태 */
   loginState?: State;
+  /** 모달 로그인 성공 시 실행되는 콜백 함수 */
   onModalLoginSuccess?: () => void;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d3ad331 and 602a4fc.

📒 Files selected for processing (4)
  • src/components/atoms/Divider/Divider.style.ts (1 hunks)
  • src/components/atoms/Divider/Divider.tsx (1 hunks)
  • src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.tsx (1 hunks)
  • src/stories/atoms/Divider.stories.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/atoms/Divider/Divider.style.ts
  • src/components/atoms/Divider/Divider.tsx
🔇 Additional comments (5)
src/stories/atoms/Divider.stories.tsx (2)

18-21: 새로운 tone 속성이 적절하게 추가되었습니다.

스토리북 설정에 새로운 tone 속성이 잘 정의되어 있습니다. 'gray'와 'violet' 옵션을 통해 구분선의 색상을 명확하게 제어할 수 있습니다.


33-39: 기본 스토리의 tone 설정이 적절합니다.

Default 스토리에서 tone: 'gray'를 기본값으로 설정한 것이 적절합니다. 이는 기존 디자인과의 일관성을 유지하면서도 새로운 속성을 자연스럽게 도입하는 방식입니다.

src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.tsx (3)

18-28: 훅 사용이 잘 구현되어 있습니다!

커스텀 훅을 통한 폼 상태 관리가 깔끔하게 구현되어 있습니다.


29-31: 소셜 로그인 URL 처리 개선이 필요합니다.

이전 리뷰에서 지적된 대로, URL 처리 방식의 개선이 필요합니다.


33-93: 🛠️ Refactor suggestion

접근성 개선이 필요합니다.

폼 요소들에 접근성 속성을 추가하여 스크린 리더 사용자의 경험을 개선해야 합니다:

 <form onSubmit={handleSubmit} css={S.loginFormStyling}>
   <div css={S.loginFormWrapper}>
     <Input
       name="email"
       value={form.email}
       placeholder="이메일"
+      aria-label="이메일 입력"
       onChange={onChange}
     />
     <Input
       name="password"
       type="password"
       value={form.password}
       placeholder="비밀번호"
+      aria-label="비밀번호 입력"
       isError={isError}
       errorMessage={errorMessage}
       onChange={onChange}
     />
     <Button
       type="submit"
       size="large"
       variant="roundPrimary"
+      aria-label="로그인 버튼"
       css={S.loginBtnStyling}
     >
       로그인
     </Button>
   </div>

소셜 로그인 버튼에도 적절한 aria-label을 추가해주세요:

 <SocialLoginButton
   variant="kakao"
   size="small"
+  aria-label="카카오 계정으로 로그인"
   onClick={() => {
     handleSocialLogin('kakao');
   }}
 />

Likely invalid or redundant comment.

Copy link
Collaborator

@WonJuneKim WonJuneKim left a comment

Choose a reason for hiding this comment

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

수정까지 고생 많으셨어요! 최고최고 🏖️

@areumH areumH merged commit 1dd84eb into dev Feb 4, 2025
6 of 7 checks passed
@areumH areumH deleted the feat/291-login-signup branch February 4, 2025 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

로그인, 회원가입 모바일 페이지 ui 구현 및 api 연결
3 participants