-
Notifications
You must be signed in to change notification settings - Fork 1
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
Conversation
Walkthrough이번 변경 사항은 모바일 사용자를 위한 UI 개선과 조건부 렌더링 로직 도입에 초점을 맞춥니다. Changes
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
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
Assessment against linked issues
Poem
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? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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" -lLength of output: 418
🧹 Nitpick comments (29)
src/components/molecules/InputProfileImage/InputProfileImage.style.ts (1)
40-45
: 모바일 텍스트 스타일 구현에 대한 제안모바일 환경에 맞는 타이포그래피와 터치 인터페이스를 고려한 구현이 잘 되었습니다. 다만 몇 가지 개선사항을 제안드립니다:
- 터치 피드백을 위한 active 상태 스타일 추가
- 접근성을 위한 터치 영역 확보
다음과 같이 수정해보시는 것은 어떨까요?
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
: 비밀번호 확인 검증 로직 검토비밀번호 일치 여부 확인 로직이 명확하게 구현되어 있습니다. 하지만 사용자 경험을 더욱 개선하기 위해 다음 사항을 고려해보시기 바랍니다:
- 실시간 유효성 검사 추가
- 비밀번호 강도 표시기 구현
다음과 같은 개선된 구현을 제안드립니다:
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
: 비밀번호 유효성 검사 로직 검토비밀번호 형식 검증 로직이 잘 구현되어 있습니다. 다만 다음과 같은 개선 사항을 고려해보시기 바랍니다:
- 정규식 패턴을 상수로 분리하여 재사용성 향상
- 각 검증 단계별 구체적인 피드백 제공
다음과 같은 개선된 구현을 제안드립니다:
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
: 반응형 디자인 개선 제안
loginFormStyling
과loginBtnStyling
에서 고정된 픽셀 값을 사용하고 있습니다. 다양한 모바일 화면 크기에 대응하기 위해 상대적인 단위 사용을 고려해보세요.다음과 같이 수정하는 것을 추천드립니다:
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 주석이 누락되어 있습니다. 특히 새로 추가된
isMobile
과success
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
⛔ 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 인터페이스가 적절히 확장되었습니다.모바일 대응을 위한
isMobile
과success
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 3Length 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 구현에 필요한 에셋들이 적절히 추가되었습니다.
export const mobileButtonStyling = css(typo.Mobile.Text.SemiBold_14, { | ||
width: '75px', | ||
height: '32px', | ||
borderRadius: '4px', | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
중복된 스타일 코드를 공통 컴포넌트로 분리하는 것을 추천드립니다.
InputEmail과 동일한 모바일 버튼 스타일링이 중복되어 있습니다. 이를 공통 스타일로 분리하여 재사용성을 높이면 좋을 것 같습니다.
다음과 같이 수정하는 것을 제안드립니다:
- 공통 스타일 파일 생성 (
src/styles/common/buttons.ts
):
export const commonMobileButtonStyle = css(typo.Mobile.Text.SemiBold_14, {
width: '75px',
height: '32px',
borderRadius: '4px',
});
- 각 컴포넌트에서 공통 스타일 사용:
-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;
src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.style.ts
Outdated
Show resolved
Hide resolved
<div css={S.profilePlusWrapper} {...attributes}> | ||
<Plus /> | ||
</div> |
There was a problem hiding this comment.
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
{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> | ||
))} |
There was a problem hiding this comment.
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.
{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> | |
))} |
if ( | ||
status === HTTP_STATUS_CODE.UNAUTHORIZED && | ||
data.message !== ERROR.LOGIN.NOT_MATCH | ||
) { |
There was a problem hiding this comment.
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.
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) | |
) { |
<div css={S.btnContainer}> | ||
<Button active={false} onClick={handleCancel}> | ||
취소 | ||
</Button> | ||
<Button type="submit">회원가입</Button> | ||
</div> |
There was a problem hiding this comment.
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.
<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> |
Text: { | ||
Regular: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '14px', | ||
fontWeight: 400, | ||
lineHeight: '1.6', | ||
letterSpacing: `${16 * -0.05}px`, | ||
}, | ||
}, |
There was a problem hiding this comment.
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.
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`, | |
}, | |
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
모바일 로그인/회원가입 구현하시느라 수고 많으셨어요!!! UI 너무 깔끔하고 이뻐요😍 아주 간단한 커멘트 남겨두었습니닷...!
src/components/molecules/InputProfileImage/InputProfileImage.tsx
Outdated
Show resolved
Hide resolved
isError?: boolean; | ||
} | ||
|
||
const ErrorMessage = ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Input 값에 대한 성공 혹은 실패 메세지 컴포넌트를 ErrorMessage로 정하신 이유가 따로 있으신지 궁금합니다.!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사실 기존에 구현되어있던 컴포넌트를 참고하여 만든 것도 있는데요..!! 아무래도 로직도 웹에서 사용되는 훅을 동일하게 연결해야하다보니 웹 기준의 컴포넌트와 통일시키는 것이 나을 것이라 생각했습니다!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
기존 컴포넌트를 재사용하려고 했던 흔적들이 돋보이네요.. 빠른 구현 고생하셨습니다!!
btn?: ReactElement; | ||
errorMessage?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btn의 경우 축약형을, errorMessage의 경우에는 풀네임을 쓰셨는데, 두개 중에 하나로 통일 되어도 좋아보이네요!
status === HTTP_STATUS_CODE.UNAUTHORIZED && | ||
data.message !== ERROR.LOGIN.NOT_MATCH |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋은 발견이네요!
사실 에러 코드가 다른게 제일 좋은 처리라고 생각되는데, 이 부분은 서버 분들에게 한번 여쭤봐야겠군요!
src/components/mobile/molecules/DefaultProfileModal/DefaultProfileModal.tsx
Outdated
Show resolved
Hide resolved
src/components/mobile/molecules/MobileLoginForm/MobileLoginForm.tsx
Outdated
Show resolved
Hide resolved
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분도 isSuccess로 처리해도 좋아보입니다!
There was a problem hiding this 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()
가value
나pw
가 변경될 때마다 무조건 호출되고 있습니다. 불필요한 검증을 방지하기 위해 조건부 실행을 고려해보세요.useEffect(() => { - handleVerify(); + if (value && pw) { + handleVerify(); + } }, [value, pw]);
39-54
: 코드 재사용성 개선이 필요합니다.
MobileInput
과Input
컴포넌트에 전달되는 공통 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
: 컴포넌트 로직 재사용을 위한 구조 개선이 필요합니다.
InputPw
와InputPwConfirm
컴포넌트 간에 중복되는 로직이 많습니다. 공통 로직을 커스텀 훅으로 분리하는 것을 제안드립니다.다음과 같은 커스텀 훅 생성을 고려해보세요:
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 네이밍에서 일관성이 부족합니다:
isError
와isSuccess
는 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
📒 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
컴포넌트와 동일한 방식으로isMobile
과success
프롭이 추가되어 일관성 있는 구현이 이루어졌습니다.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' 접두사를 사용하는 것이 좋습니다.
success
를isSuccess
로 변경하는 것을 고려해보세요.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
: 스토리 구조가 잘 구성되어 있습니다.다양한 상태와 변형을 보여주는 예제들이 체계적으로 구성되어 있으며, 한글 에러 메시지를 포함한 실제 사용 사례를 잘 보여주고 있습니다.
const handleSocialLogin = (social: string) => { | ||
window.location.href = `${process.env.API_URL}/oauth2/authorization/${social}`; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
소셜 로그인 URL 처리 개선이 필요합니다.
현재 구현은 몇 가지 개선이 필요해 보입니다:
- URL 구성을 상수로 분리하여 관리
- 환경 변수 유효성 검사 추가
- 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.
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); | |
}; |
|
There was a problem hiding this 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
📒 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수정까지 고생 많으셨어요! 최고최고 🏖️
💡 작업 내용
💡 자세한 설명
✅ 모바일 페이지 관련
✅ 컴포넌트 관련
✅ 회원가입 관련
successForm
으로 관리하여 모든 값 검증에 성공했을 시에 회원가입이 진행되도록 되어있습니다. 그런데 한번 검증에 성공한 후, Input 내의 값을 변경하여도 성공 처리가 유지되던 이슈가 있었습니다. (이로 인해 검증한 값과 다른 값이 회원 가입 요청에 전송될 수 있음)✅ interceptor.ts
axiosInstance.response.use
에서는 api 요청의 응답이401
일 때 쿠키의 리프레시 토큰을 이용한 액세스 토큰 재발급 함수를 수행하도록 되어있는데, 로그인 실패 시의 응답도 401이기 때문에 불필요한 토큰 재발급이 실행되는 이슈가 있었습니다. 해당 이슈 해결을 위해 토큰 재발급의 조건문에data.message !== ERROR.LOGIN.NOT_MATCH (로그인 실패 응답이 아닐 때)
를 추가하였습니다.-.Clipchamp.7.mp4
▲ 로그인 모바일 페이지
-.Clipchamp.6.mp4
▲ 회원가입 모바일 페이지
📗 참고 자료 (선택)
📢 리뷰 요구 사항 (선택)
🚩 후속 작업 (선택)
✅ 셀프 체크리스트
closes #291
Summary by CodeRabbit
New Features
ErrorMessage
및Input
컴포넌트 추가로 사용자 경험 향상Style
Bug Fixes