-
Notifications
You must be signed in to change notification settings - Fork 0
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
[YS-243] 홈화면 & 로그인 & 회원가입 스타일링 vanilla-extract로 마이그레이션 #44
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Walkthrough이번 변경사항은 Emotion 기반 CSS-in-JS 스타일을 vanilla-extract 기반의 CSS 모듈로 전환하는 작업입니다. 홈화면, 로그인, 회원가입, 검색 및 기타 관련 컴포넌트에서 기존의 Changes
Sequence Diagram(s)sequenceDiagram
participant JoinPage
participant ResearcherForm
participant ParticipantForm
JoinPage->>JoinPage: 현재 step 체크 (예: step === success)
alt step이 success인 경우
JoinPage->>ResearcherForm: 연구자용 폼 렌더링 (role이 Researcher인 경우)
JoinPage->>ParticipantForm: 참여자용 폼 렌더링 (role이 Participant인 경우)
else
JoinPage->>JoinPage: 기본 회원가입 폼 렌더링
end
Assessment against linked issues
Possibly related PRs
Suggested labels
Tip 🌐 Web search-backed reviews and chat
✨ Finishing Touches
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: 9
🔭 Outside diff range comments (5)
src/app/join/components/JoinInput/JoinInput.tsx (1)
87-127
: 접근성 개선이 필요합니다.입력 필드의 접근성을 높이기 위해 다음과 같은 개선사항을 제안드립니다:
<div className={inputWrapper}> {type === 'input' ? ( <input {...field} ref={inputRef} placeholder={placeholder} disabled={disabled} maxLength={maxLength} aria-invalid={fieldState.invalid ? true : false} + aria-describedby={`${name}-error ${name}-tip`} style={{ width: '100%' }} className={joinInput} onFocus={() => setIsFocused(true)} onBlur={(e) => handleBlur(e, field.onBlur)} /> ) : ( <textarea {...field} ref={textareaRef} placeholder={placeholder} disabled={disabled} aria-invalid={fieldState.invalid ? true : false} + aria-describedby={`${name}-error ${name}-tip`} rows={3} maxLength={maxLength ?? 0} style={{ width: '100%' }} className={joinInput} onFocus={() => setIsFocused(true)} onBlur={(e) => handleBlur(e, field.onBlur)} /> )} - {isFocused && field.value && !disabled && ( + {isFocused && field.value && !disabled && ( <button className={inputResetButton} ref={resetButtonRef} + aria-label="입력 내용 지우기" + type="button" >🧰 Tools
🪛 Biome (1.9.4)
[error] 95-95: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with(lint/complexity/noUselessTernary)
[error] 107-107: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with(lint/complexity/noUselessTernary)
src/app/login/components/LoginCard/LoginCard.tsx (1)
35-43
: 보안 및 유지보수성 개선이 필요합니다.OAuth URL 생성 로직에 대한 몇 가지 개선사항을 제안드립니다:
- OAuth URL 생성 로직을 별도의 유틸리티 함수로 분리
- 환경 변수 존재 여부 검증 추가
다음과 같이 개선해보세요:
+const validateEnvVariables = () => { + const required = [ + 'NEXT_PUBLIC_GOOGLE_CLIENT_ID', + 'NEXT_PUBLIC_GOOGLE_REDIRECT_URI', + 'NEXT_PUBLIC_NAVER_CLIENT_ID', + 'NEXT_PUBLIC_NAVER_REDIRECT_URI', + 'NEXT_PUBLIC_NAVER_STATE' + ]; + + const missing = required.filter(key => !process.env[key]); + if (missing.length > 0) { + throw new Error(`Missing required environment variables: ${missing.join(', ')}`); + } +}; +const createGoogleOAuthURL = (role: Role) => { + validateEnvVariables(); + return `https://accounts.google.com/o/oauth2/v2/auth?client_id=${ + process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID + }&redirect_uri=${ + process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI + }&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email&state=${ + roleMapper[role] + }`; +}; +const createNaverOAuthURL = (role: Role) => { + validateEnvVariables(); + return `https://nid.naver.com/oauth2.0/authorize?client_id=${ + process.env.NEXT_PUBLIC_NAVER_CLIENT_ID + }&response_type=code&redirect_uri=${ + process.env.NEXT_PUBLIC_NAVER_REDIRECT_URI + }&state=${ + process.env.NEXT_PUBLIC_NAVER_STATE + }|${ + roleMapper[role] + }`; +}; const goToLoginGoogle = () => { - const googleOauthURL = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID}&redirect_uri=${process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI}&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email&state=${roleMapper[role]}`; + const googleOauthURL = createGoogleOAuthURL(role); router.push(googleOauthURL); }; const goToLoginNaver = () => { - const naverOauthURL = `https://nid.naver.com/oauth2.0/authorize?client_id=${process.env.NEXT_PUBLIC_NAVER_CLIENT_ID}&response_type=code&redirect_uri=${process.env.NEXT_PUBLIC_NAVER_REDIRECT_URI}&state=${process.env.NEXT_PUBLIC_NAVER_STATE}|${roleMapper[role]}`; + const naverOauthURL = createNaverOAuthURL(role); router.push(naverOauthURL); };src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.tsx (1)
18-19
: any 타입 사용을 피하고 명시적인 이벤트 타입을 사용해주세요.eslint-disable 주석과 함께 any 타입을 사용하고 있습니다.
다음과 같이 명시적인 이벤트 타입을 사용해주세요:
interface JoinCheckboxProps { label: string; isChecked: boolean; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onChange: (e: any) => void; + onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; isRequired?: boolean; isAllCheck?: boolean; isArrow?: boolean; isAlert?: boolean; }src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx (1)
18-20
: 대면/비대면 값이 잘못 매핑되어 있습니다.현재 구현에서 대면은 "ONLINE"으로, 비대면은 "OFFLINE"으로 설정되어 있어 직관적이지 않습니다.
다음과 같이 수정해주세요:
{ label: '전체', value: 'ALL' }, - { label: '대면', value: 'ONLINE' }, - { label: '비대면', value: 'OFFLINE' }, + { label: '대면', value: 'OFFLINE' }, + { label: '비대면', value: 'ONLINE' },src/app/home/components/PostContainer/AreaFilter/AreaFilter.tsx (1)
58-64
: TODO 주석 관련 개선이 필요합니다.여러 개의 하위 지역 선택 기능이 아직 구현되지 않았습니다. 현재는 첫 번째 선택된 항목만 처리되고 있습니다.
다음과 같이 수정을 제안합니다:
- onChange('areas', selectedSubArea[0]); + onChange('areas', selectedSubArea.join(','));
🧹 Nitpick comments (60)
src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.tsx (1)
14-18
: className prop으로의 전환이 잘 이루어졌습니다.css prop에서 className prop으로의 전환이 올바르게 이루어졌으며, 스타일 적용 방식이 vanilla-extract의 권장 사항을 잘 따르고 있습니다.
다만, 접근성 향상을 위해 다음과 같은 개선을 제안드립니다:
- 검색 결과가 없다는 것을 스크린 리더에게 더 명확하게 전달하기 위해 aria-label 추가
- <div className={emptyPostCardLayout}> + <div className={emptyPostCardLayout} aria-label="검색 결과 없음">src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.tsx (1)
24-24
: className 문자열 포맷팅 개선이 필요합니다.현재 템플릿 리터럴 내부에 불필요한 공백과 줄바꿈이 있어 가독성이 떨어집니다. 조건부 클래스네임을 더 간결하게 작성할 수 있습니다.
다음과 같이 개선해보세요:
- className={` - ${customRadioButton} - ${selectedValue === option.value ? activeRadioButton : ''} - ${isError ? errorRadioButton : ''} - `} + className={`${customRadioButton} ${selectedValue === option.value ? activeRadioButton : ''} ${isError ? errorRadioButton : ''}`}또는
clsx
라이브러리를 사용하여 더 깔끔하게 작성할 수 있습니다:import clsx from 'clsx'; // ... className={clsx( customRadioButton, selectedValue === option.value && activeRadioButton, isError && errorRadioButton )}Also applies to: 29-33
src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.css.ts (1)
6-16
: 스타일 구현이 체계적입니다.중앙 집중식 테마 시스템을 활용하고 일관된 단위를 사용한 점이 좋습니다.
스타일의 목적과 사용법을 설명하는 간단한 JSDoc 주석을 추가하면 좋을 것 같습니다:
+/** + * 약관 동의 체크박스들을 포함하는 컨테이너의 스타일입니다. + * 체크박스들을 세로로 배치하고 일관된 간격을 유지합니다. + */ export const termContainer = style({src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.tsx (1)
5-7
: 스타일 관련 import 구문의 구조화가 필요합니다.스타일 관련 import 구문들이 서로 다른 경로에서 가져오고 있습니다. 코드의 일관성과 유지보수성을 위해 스타일 import를 그룹화하는 것이 좋습니다.
다음과 같이 스타일 관련 import를 그룹화하는 것을 제안합니다:
import { useFormContext, useWatch } from 'react-hook-form'; import JoinInput from '@/app/join/components/JoinInput/JoinInput'; + import { joinButton } from '@/app/join/components/Participant/JoinInfoStep/JoinInfoStep.css'; import { joinContentContainer, joinForm } from '@/app/join/JoinPage.css';
src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.tsx (1)
60-85
: 인라인 스타일을 CSS 모듈로 이동하는 것이 좋습니다.인라인 스타일로 적용된 width 속성을 CSS 모듈로 이동하면 스타일 관리가 더 용이해질 것 같습니다.
다음과 같이 수정하는 것을 제안드립니다:
JoinInput.css.ts
에 width 스타일 추가:export const joinInput = style({ + width: '100%', // ... 기존 스타일 });
- 컴포넌트에서 인라인 스타일 제거:
<input - style={{ width: '100%' }} className={joinInput} // ... 나머지 props />
src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx (1)
41-42
: 스타일링 적용 방식이 성공적으로 변경되었습니다.css prop에서 className prop으로의 전환이 올바르게 이루어졌습니다. 다만, 재사용 가능한 스타일의 경우 별도의 상수로 분리하는 것을 고려해보시기 바랍니다.
자주 사용되는 스타일의 경우 다음과 같이 분리하는 것을 추천드립니다:
// constants/styles.css.ts export const commonButtonStyle = style({ // ... 공통 버튼 스타일 });Also applies to: 65-65
src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.tsx (1)
24-24
: 스타일 적용 방식에 대한 제안현재 구현은 잘 되어 있으나, 오픈/에러 상태에 따른 스타일 변화를 더 효과적으로 관리할 수 있습니다.
다음과 같이 vanilla-extract의
recipe
를 활용하여 상태별 스타일을 관리하는 것을 고려해보세요:// JoinSelect.css.ts import { recipe } from '@vanilla-extract/recipes'; export const triggerWrapper = recipe({ base: { /* 기본 스타일 */ }, variants: { isOpen: { true: { /* 오픈 상태 스타일 */ }, }, isError: { true: { /* 에러 상태 스타일 */ }, } } });그리고 컴포넌트에서는 다음과 같이 사용할 수 있습니다:
<Select.Trigger className={triggerWrapper({ isOpen, isError })} aria-invalid={isError} >Also applies to: 31-32, 34-34
src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.tsx (1)
38-45
: 유효성 검사 로직이 개선되었습니다.불필요한
Boolean()
래퍼를 제거하여 코드가 더 간결해졌습니다. 추가적인 개선을 위해 조건들을 상수로 분리하는 것을 고려해보세요.다음과 같이 리팩토링하면 가독성이 더욱 향상될 수 있습니다:
+ const isEmailsValid = !errors.contactEmail && !errors.univEmail; + const isAgreementsValid = serviceAgreeCheck.isTermOfService && serviceAgreeCheck.isPrivacy; const allValid = oauthEmail && univEmail && - !errors.contactEmail && - !errors.univEmail && + isEmailsValid && isEmailVerified && - serviceAgreeCheck.isTermOfService && - serviceAgreeCheck.isPrivacy; + isAgreementsValid;src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.tsx (1)
13-22
: control prop의 타입 안전성을 개선할 수 있습니다.현재
control
prop이any
타입으로 정의되어 있는데, react-hook-form의 구체적인 타입을 사용하면 더 안전한 타입 체크가 가능합니다.다음과 같이 개선해 보세요:
- // eslint-disable-next-line @typescript-eslint/no-explicit-any - control: any; + import { Control } from 'react-hook-form'; + control: Control<any>;src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.css.ts (2)
6-13
: 매직 넘버를 상수로 분리하는 것이 좋을 것 같습니다.
gap: '1.6rem'
와height: '30.8rem'
와 같은 매직 넘버는 의미있는 상수로 분리하면 유지보수가 더 용이할 것 같습니다.다음과 같이 상수를 분리하는 것을 제안드립니다:
+const LAYOUT_CONSTANTS = { + GAP: '1.6rem', + HEIGHT: '30.8rem', +} as const; export const emptyPostCardLayout = style({ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', - gap: '1.6rem', - height: '30.8rem', + gap: LAYOUT_CONSTANTS.GAP, + height: LAYOUT_CONSTANTS.HEIGHT, });
22-30
: 텍스트 스타일을 더 효율적으로 구성할 수 있습니다.
emptyListTitle
와emptyListContent
가 비슷한 구조를 가지고 있어 베이스 스타일을 공유할 수 있을 것 같습니다.다음과 같이 리팩토링하는 것을 제안드립니다:
+const baseTextStyle = style({ + color: colors.text04, +}); export const emptyListTitle = style([ + baseTextStyle, { ...fonts.body.normal.M16, - color: colors.text04, }, ]); export const emptyListContent = style([ + baseTextStyle, { ...fonts.label.small.M12, - color: colors.text04, }, ]);src/app/home/components/PostCardList/PostCardList.css.ts (1)
1-7
: 구현이 깔끔하고 명확합니다!grid 레이아웃을 사용하여 카드 리스트를 구현한 방식이 적절합니다.
반응형 디자인 고려 제안
모바일 환경에서의 사용성을 위해 미디어 쿼리를 추가하는 것을 고려해보세요.
다음과 같이 반응형 그리드를 구현할 수 있습니다:
export const postCardListLayout = style({ display: 'grid', - gridTemplateColumns: '1fr 1fr 1fr', + gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '1.2rem', });이렇게 하면 화면 크기에 따라 자동으로 그리드 열 수가 조정됩니다.
src/app/home/components/PostContainer/AreaFilter/AreaFilter.css.ts (3)
8-22
: CSS 변수를 활용한 동적 스타일링이 잘 구현되어 있습니다.트리거 래퍼의 스타일링에서 색상을 CSS 변수로 처리한 것이 좋은 접근 방식입니다. 다만, CSS 변수의 기본값 설정을 고려해보시면 좋을 것 같습니다.
다음과 같이 기본값을 추가하는 것을 고려해보세요:
export const triggerWrapper = style({ ...fonts.label.large.SB14, - color: 'var(--trigger-color)', - backgroundColor: 'var(--trigger-bg)', + color: 'var(--trigger-color, ${colors.text06})', + backgroundColor: 'var(--trigger-bg, ${colors.field02})', display: 'flex', alignItems: 'center',
56-57
: 스크롤 관련 스타일 속성을 개선할 수 있습니다.
overflow: 'scroll'
은 항상 스크롤바를 표시합니다. 필요할 때만 스크롤바가 나타나도록overflow: 'auto'
를 사용하는 것이 더 나은 사용자 경험을 제공할 수 있습니다.다음과 같이 수정하는 것을 추천드립니다:
- overflow: 'scroll', + overflow: 'auto',Also applies to: 133-134
191-212
: 버튼 레시피의 재사용성이 좋습니다.공통 스타일을 base로 정의하고 variant를 통해 타입별 스타일을 구분한 것이 좋은 패턴입니다. 다만, 버튼의 hover 상태에 대한 스타일이 누락되어 있습니다.
다음과 같이 hover 상태를 추가하는 것을 고려해보세요:
variants: { type: { reset: { backgroundColor: colors.field03, color: colors.text06, + selectors: { + '&:hover': { + backgroundColor: colors.field04, + }, + }, }, save: { backgroundColor: colors.primaryMint, color: colors.text01, + selectors: { + '&:hover': { + backgroundColor: colors.primaryMintHover, + }, + }, }, }, },src/app/join/page.tsx (2)
30-38
: 성공 단계에 대한 조건부 렌더링이 추가되었습니다.성공 단계일 때의 레이아웃이 명확하게 분리되어 있습니다. 하지만 이 부분의 중복 코드를 줄일 수 있을 것 같습니다.
다음과 같이 공통 레이아웃을 추출하는 것을 고려해보세요:
+ const renderContent = () => ( + role === ROLE.researcher ? <ResearcherForm /> : <ParticipantForm /> + ); if (step === STEP.success) { return ( <section className={joinLayout}> <div className={contentContainer}> - {role === ROLE.researcher ? <ResearcherForm /> : <ParticipantForm />} + {renderContent()} </div> </section> ); }
51-53
: 진행 상태 표시줄의 동적 스타일링이 개선되었습니다.
assignInlineVars
를 사용한 진행 상태 표시가 깔끔하게 구현되었습니다. 하지만 매직 넘버('50%', '100%')를 상수로 분리하는 것이 좋을 것 같습니다.다음과 같이 상수를 분리하는 것을 추천드립니다:
+ const PROGRESS_WIDTH = { + [STEP.email]: '50%', + [STEP.info]: '100%', + [STEP.success]: '100%', + } as const; style={assignInlineVars({ - '--progress-width': step === STEP.email ? '50%' : '100%', + '--progress-width': PROGRESS_WIDTH[step], })}src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.css.ts (2)
6-10
: border-radius 값에 디자인 토큰 사용을 고려해보세요.하드코딩된 '1rem' 대신 디자인 시스템의 토큰을 사용하면 일관성 있는 스타일링이 가능합니다.
16-32
: 버튼 스타일의 개선 가능한 부분이 있습니다.
하드코딩된 값들을 디자인 토큰으로 대체하는 것이 좋습니다:
- padding: '0.7rem 1.6rem'
- borderRadius: '1rem'
- right: '1.2rem'
- top: '1rem'
절대 위치(absolute positioning)는 반응형 레이아웃에서 문제를 일으킬 수 있습니다. 더 유연한 레이아웃 방식을 고려해보세요.
src/app/join/components/JoinSuccessStep/JoinSuccessStep.css.ts (3)
6-13
: 간격 값을 변수로 분리하는 것이 좋을 것 같습니다.큰 간격 값(9rem)을 재사용 가능한 변수로 분리하면 일관성 있는 스페이싱 시스템을 구축하는 데 도움이 될 것 같습니다.
+const LAYOUT_GAP = '9rem'; export const joinSuccessLayout = style({ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', - gap: '9rem', + gap: LAYOUT_GAP, paddingTop: '3.6rem', });
28-30
: 스타일 속성명을 더 명확하게 작성하면 좋을 것 같습니다.수직 마진만 사용하는 경우
marginBlock
을 사용하면 의도가 더 명확해질 것 같습니다.export const image = style({ - margin: '3.2rem 0', + marginBlock: '3.2rem', });
32-39
: 홈 링크에 호버 상태를 추가하면 좋을 것 같습니다.사용자 경험 향상을 위해 호버 시 시각적 피드백을 제공하는 것이 좋습니다.
export const homeLink = style({ ...fonts.body.normal.SB16, backgroundColor: colors.primaryMint, color: colors.text01, borderRadius: '1.2rem', padding: '1.2rem 3.35rem', marginTop: '2.6rem', + transition: 'background-color 0.2s ease', + ':hover': { + backgroundColor: colors.primaryMintHover, + }, });src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.css.ts (2)
6-10
: 간격 값을 변수로 분리하는 것을 고려해보세요.현재 구현은 잘 되어있지만, 간격 값('0.8rem')을 스타일 변수로 분리하면 재사용성과 일관성을 더욱 향상시킬 수 있습니다.
+import { spacing } from '@/styles/spacing'; export const customRadioGroup = style({ display: 'flex', flexFlow: 'row nowrap', - gap: '0.8rem', + gap: spacing.sm, });
12-30
: 매직 넘버를 상수로 분리하면 좋겠습니다.현재 구현은 잘 되어 있으며, 특히 접근성과 사용자 경험을 고려한 부분이 좋습니다. 다만 다음과 같은 개선사항을 제안드립니다:
- 높이('4.8rem'), 패딩('1rem 2rem'), 테두리 반경('1.2rem') 등의 값을 상수로 분리
- transition 속성에 대한 구체적인 속성 명시
+const RADIO_BUTTON = { + height: '4.8rem', + padding: '1rem 2rem', + borderRadius: '1.2rem', +} as const; export const customRadioButton = style({ ...fonts.label.large.M14, flexGrow: 1, - height: '4.8rem', - padding: '1rem 2rem', + height: RADIO_BUTTON.height, + padding: RADIO_BUTTON.padding, border: `0.1rem solid ${colors.line01}`, - borderRadius: '1.2rem', + borderRadius: RADIO_BUTTON.borderRadius, backgroundColor: colors.field01, cursor: 'pointer', - transition: 'all 0.2s ease', + transition: 'background-color 0.2s ease, border-color 0.2s ease', // ... rest of the codesrc/app/join/components/JoinInput/JoinInput.css.ts (1)
19-40
: 입력 필드 스타일링에 대한 개선 제안현재 구현은 잘 되어 있으나, 다음과 같은 개선사항을 고려해보세요:
- borderRadius 값을 디자인 토큰으로 분리하면 일관성 유지에 도움이 될 것 같습니다.
- outline 스타일을 재사용 가능한 변수로 분리하는 것을 고려해보세요.
+import { radii } from '@/styles/tokens'; + +const inputOutline = { + default: `0.1rem solid ${colors.line01}`, + focus: `0.1rem solid ${colors.lineTinted}`, + error: `0.1rem solid ${colors.textAlert}`, +}; + export const joinInput = style({ ...fonts.body.normal.M16, color: colors.text06, - border: `0.1rem solid ${colors.line01}`, + border: inputOutline.default, padding: '1.6rem', - borderRadius: '1.2rem', + borderRadius: radii.medium, selectors: { '&:focus': { - outline: `0.1rem solid ${colors.lineTinted}`, + outline: inputOutline.focus, }, "&[aria-invalid='true']": { - outline: `0.1rem solid ${colors.textAlert}`, + outline: inputOutline.error, }, }, });src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.tsx (1)
14-16
: 접근성 개선을 위한 aria-label 추가 필요툴팁 트리거 버튼에 스크린 리더 사용자를 위한 aria-label이 누락되어 있습니다.
- <button> + <button aria-label="지역 선택에 대한 추가 정보"> <Icon icon="Information" width={18} height={18} /> </button>src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.css.ts (1)
6-9
: 애니메이션 성능 최적화 제안transform 속성에 GPU 가속을 활용하여 애니메이션 성능을 개선할 수 있습니다.
export const slideDownAndFade = keyframes({ - from: { opacity: 0, transform: 'translateY(-2px)' }, - to: { opacity: 1, transform: 'translateY(0)' }, + from: { opacity: 0, transform: 'translate3d(0, -2px, 0)' }, + to: { opacity: 1, transform: 'translate3d(0, 0, 0)' }, });src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.css.ts (1)
6-21
: 버튼 스타일의 재사용성 개선 제안버튼 스타일이 여러 곳에서 재사용될 수 있으므로, 공통 스타일을 분리하여 재사용 가능한 형태로 만드는 것이 좋습니다.
+ // common/styles/buttons.css.ts + export const baseButton = style({ + borderRadius: '1.2rem', + padding: '1.2rem 0', + alignItems: 'center', + }); + + // JoinInfoStep.css.ts export const joinButton = style([ + baseButton, { ...fonts.body.normal.SB16, backgroundColor: colors.primaryMint, color: colors.text01, - borderRadius: '1.2rem', - padding: '1.2rem 0', width: '20rem', - alignItems: 'center', marginBottom: '5.6rem', selectors: { '&:disabled': { color: colors.text02, backgroundColor: colors.field04, }, }, } ]);src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.css.ts (1)
46-65
: 선택 항목 스타일의 유지보수성 개선 제안상태별 스타일을 별도의 변수로 분리하면 유지보수가 더 용이해질 것 같습니다.
+ const itemStateStyles = { + highlighted: { + backgroundColor: colors.field02, + outline: 'none', + }, + checked: { + backgroundColor: colors.primaryTinted, + color: colors.textPrimary, + border: `0.1rem solid ${colors.textPrimary}`, + }, + }; + export const selectItem = style({ ...fonts.body.normal.M16, color: colors.text06, display: 'flex', padding: '0.6rem 1.2rem', alignItems: 'center', borderRadius: '1.2rem', cursor: 'pointer', selectors: { - '&[data-highlighted]': { - backgroundColor: colors.field02, - outline: 'none', - }, - "&[data-state='checked']": { - backgroundColor: colors.primaryTinted, - color: colors.textPrimary, - border: `0.1rem solid ${colors.textPrimary}`, - }, + '&[data-highlighted]': itemStateStyles.highlighted, + "&[data-state='checked']": itemStateStyles.checked, }, });src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.tsx (3)
19-19
: 불필요한 주석 제거 필요코드의 일관성을 위해 한글 주석을 제거하거나, 전체 코드베이스에서 주석 스타일을 통일하는 것이 좋습니다.
-import { joinContentContainer, joinForm } from '@/app/join/JoinPage.css'; // 이미 vanilla‐extract로 변환된 JoinPage 스타일 파일 +import { joinContentContainer, joinForm } from '@/app/join/JoinPage.css';
37-42
: 폼 유효성 검사 로직 개선 제안현재 구현된
isAllFilled
로직은 다음과 같은 개선이 가능합니다:
- 명시적인 타입 체크
- null 체크 로직 단순화
- 재사용 가능한 유틸리티 함수로 분리
- const isAllFilled = values.every((value) => (value ?? '').trim() !== '' && value !== undefined); + const isValueFilled = (value: unknown): boolean => { + if (value === null || value === undefined) return false; + if (typeof value === 'string') return value.trim() !== ''; + return true; + }; + const isAllFilled = values.every(isValueFilled);
171-177
: 접근성 개선 필요제출 버튼의 접근성을 개선하기 위해 다음 사항들을 추가하는 것이 좋습니다:
- aria-label 속성
- 비활성화 상태일 때의 이유를 설명하는 aria-describedby
<button className={joinButton} onClick={handleSubmit} disabled={!(isAllFilled && Object.keys(errors).length === 0)} + aria-label="회원가입 완료" + aria-describedby={!(isAllFilled && Object.keys(errors).length === 0) ? "submit-error" : undefined} > 회원가입 </button> + {!(isAllFilled && Object.keys(errors).length === 0) && ( + <span id="submit-error" className="sr-only"> + 필수 항목을 모두 입력해주세요 + </span> + )}src/app/join/JoinPage.css.ts (3)
6-13
: 패딩값을 디자인 토큰으로 관리하는 것을 고려해보세요.레이아웃 스타일이 잘 구성되어 있습니다. 하지만
paddingTop: '8.4rem'
과 같은 하드코딩된 값은 디자인 시스템의 일관성을 위해 spacing 토큰으로 관리하는 것이 좋습니다.
28-36
: 간격 단위의 일관성을 유지하면 좋겠습니다.스타일이 전반적으로 잘 구성되어 있으나, gap은 '2.8rem'을 사용하고 padding은 '3.2rem 4rem'을 사용하는 등 간격 단위가 일관적이지 않습니다. 디자인 시스템의 spacing 토큰을 활용하여 일관성을 유지하는 것이 좋겠습니다.
67-82
: 버튼 스타일에 디자인 토큰 활용을 확대하면 좋겠습니다.버튼 스타일이 전반적으로 잘 구성되어 있으나, 다음 값들을 디자인 토큰으로 관리하면 좋겠습니다:
padding: '1.2rem 0'
width: '20rem'
marginBottom: '5.6rem'
이렇게 하면 디자인 시스템의 일관성을 더욱 높일 수 있습니다.
src/app/join/components/JoinInput/JoinInput.tsx (2)
21-34
: Props 유효성 검사 개선을 제안드립니다.
maxLength
prop이 textarea에서 필수적으로 사용되지만, 현재는 선택적 prop으로 정의되어 있습니다. 타입 안정성을 높이기 위해 다음과 같은 개선을 제안드립니다:-interface JoinInputProps<T extends FieldValues> { +type BaseJoinInputProps<T extends FieldValues> = { type?: 'input' | 'textarea'; name: Path<T>; control: Control<T>; rules?: object; placeholder?: string; label?: string; required?: boolean; disabled?: boolean; tip?: string; value?: PathValue<T, Path<T>>; isTip?: boolean; - maxLength?: number; +} + +type JoinInputProps<T extends FieldValues> = + | (BaseJoinInputProps<T> & { type?: 'input', maxLength?: number }) + | (BaseJoinInputProps<T> & { type: 'textarea', maxLength: number });
128-139
: UI/UX 개선을 제안드립니다.에러 메시지와 팁 메시지의 가시성을 높이기 위해 다음과 같은 개선사항을 제안드립니다:
- 에러 메시지와 팁 메시지에 ID를 추가하여 aria-describedby와 연결:
-{fieldState.error && <span className={errorMessage}>{fieldState.error.message}</span>} +{fieldState.error && ( + <span className={errorMessage} id={`${name}-error`}> + {fieldState.error.message} + </span> +)} -{tip && !fieldState.error && ( - <div className={tipWrapper}> +{tip && !fieldState.error && ( + <div className={tipWrapper} id={`${name}-tip`}>
- vanilla-extract에서 애니메이션 추가:
// JoinInput.css.ts import { keyframes, style } from '@vanilla-extract/css'; const fadeIn = keyframes({ from: { opacity: 0, transform: 'translateY(-5px)' }, to: { opacity: 1, transform: 'translateY(0)' } }); export const errorMessage = style({ // ... 기존 스타일 animation: `${fadeIn} 0.2s ease-in-out` });src/app/login/components/LoginCard/LoginCard.tsx (1)
50-55
: 스타일 로직 개선을 제안드립니다.역할에 따른 색상 매핑을 상수로 분리하면 코드의 가독성과 유지보수성이 향상될 것 같습니다.
다음과 같이 개선해보세요:
+const ROLE_STYLES = { + 연구자: { + color: theme.colors.secondaryPink, + background: theme.colors.secondaryTinted, + }, + 참여자: { + color: theme.colors.primaryMint, + background: theme.colors.primaryTinted, + }, +} as const; style={assignInlineVars({ - '--badge-color': - role === '연구자' ? theme.colors.secondaryPink : theme.colors.primaryMint, - '--badge-bg': - role === '연구자' ? theme.colors.secondaryTinted : theme.colors.primaryTinted, + '--badge-color': ROLE_STYLES[role].color, + '--badge-bg': ROLE_STYLES[role].background, })}src/app/login/LoginPage.css.ts (2)
9-9
: 뷰포트 높이 계산 방식 개선이 필요합니다.
calc(100vh - 12.2rem)
는 모바일 브라우저의 동적 툴바나 키보드가 표시될 때 문제가 될 수 있습니다.다음과 같이
dvh
단위 사용을 고려해보세요:- height: 'calc(100vh - 12.2rem)', + minHeight: 'calc(100dvh - 12.2rem)',
20-23
: 스타일 재사용성 향상이 필요합니다.
descriptionWrapper
와sloganWrapper
가 동일한 폰트 스타일과 색상을 사용하고 있습니다. 공통 스타일을 추출하여 재사용하면 유지보수가 더 쉬워질 것 같습니다.다음과 같이 공통 스타일을 분리하는 것을 제안합니다:
const commonTextStyle = style({ ...fonts.title.medium.SB20, color: colors.text06, }); export const descriptionWrapper = style([ commonTextStyle, ]); export const sloganWrapper = style([ commonTextStyle, { textAlign: 'center', } ]);src/app/login/components/LoginCard/LoginCard.css.ts (1)
37-47
: 로그인 버튼 스타일의 재사용성 개선이 필요합니다.현재
loginButton
스타일이 너무 구체적으로 정의되어 있어 재사용이 어려울 수 있습니다. 기본 버튼 스타일과 변형을 분리하면 더 유연하게 사용할 수 있을 것 같습니다.다음과 같이 스타일을 분리하는 것을 제안합니다:
export const baseButton = style({ ...fonts.label.large.R14, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.4rem', padding: '0.8rem', borderRadius: '0.8rem', }); export const loginButton = style([ baseButton, { backgroundColor: colors.field01, color: colors.text06, } ]);src/app/join/components/EmailToast/EmailToast.css.ts (2)
7-19
: z-index 관리 방식 개선 제안
theme.zIndex
를 사용하여 z-index를 관리하는 것은 좋은 접근이지만, toast의 위치 지정에 있어 더 견고한 방식을 제안드립니다.export const toastLayout = style({ color: colors.text06, height: '5.2rem', backgroundColor: colors.fieldToast, boxShadow: '0px 4px 16px rgba(53, 59, 61, 0.2)', borderRadius: '8rem', padding: '1.4rem 2.4rem', position: 'fixed', - top: 0, + top: '2rem', left: '50%', transform: 'translateX(-50%)', zIndex: theme.zIndex.toastContent, });
21-29
: width 속성 최적화 제안
width: 'max-content'
대신 더 유연한 방식을 고려해보세요.export const toastTitle = style({ ...fonts.body.normal.SB16, color: colors.text01, display: 'flex', flexFlow: 'row nowrap', alignItems: 'center', gap: '1rem', - width: 'max-content', + minWidth: 'max-content', + maxWidth: '80vw', });src/app/home/components/Banner/Banner.css.ts (3)
10-15
: 반응형 디자인 개선 제안고정된 높이값(
15vh
)은 다양한 화면 크기에서 문제가 될 수 있습니다.export const bannerWrapper = style({ position: 'relative', - height: '15vh', + minHeight: '15vh', + maxHeight: '20rem', display: 'flex', alignItems: 'center', });
27-30
: 트랜지션 속성 구체화 제안
all
트랜지션은 성능에 영향을 줄 수 있습니다. 특정 속성만 트랜지션하는 것이 좋습니다.export const carouselContainer = style({ display: 'flex', - transition: 'all 1s', + transition: 'transform 1s ease-in-out', });
37-40
: 이미지 최적화 제안이미지 렌더링 최적화를 위한 추가 속성을 제안드립니다.
export const bannerImage = style({ width: '100%', height: 'auto', + objectFit: 'cover', + aspectRatio: '16 / 9', });src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.css.ts (1)
6-24
: 동적 스타일링을 위한 CSS 변수 사용을 고려해보세요.현재 스타일이 정적으로 정의되어 있어 상태에 따른 스타일 변경이 어려울 수 있습니다.
체크박스의 상태(checked, disabled 등)에 따른 스타일링을 위해 CSS 변수 사용을 고려해보세요:
export const checkboxLayout = style({ display: 'flex', justifyContent: 'space-between', alignItems: 'center', + backgroundColor: 'var(--checkbox-bg, transparent)', + opacity: 'var(--checkbox-opacity, 1)', });src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.css.ts (2)
8-10
: 주석 처리된 코드는 제거해주세요.불필요한 주석 처리된 코드가 남아있습니다. 버전 관리 시스템을 사용하고 있으므로 이전 코드는 히스토리에서 확인할 수 있습니다.
다음과 같이 주석을 제거해주세요:
export const triggerWrapper = style({ ...fonts.label.large.SB14, - // color: colors.text06, color: 'var(--trigger-color)', backgroundColor: 'var(--trigger-bg)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '0.4rem', borderRadius: '1.2rem', padding: '0.5rem 1.4rem', - // backgroundColor: colors.field01, cursor: 'pointer', ... });Also applies to: 17-18
21-22
: 박스 쉐도우 값을 테마 상수로 분리하는 것이 좋습니다.여러 곳에서 동일한 박스 쉐도우 값이 하드코딩되어 있습니다.
styles/theme.ts
에 쉐도우 값을 상수로 정의하고 재사용하는 것을 추천드립니다:// styles/theme.ts export const shadows = { dropdown: '0px 4px 16px rgba(53, 59, 61, 0.2)', // ...other shadow values };Also applies to: 33-34
src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.tsx (1)
36-36
: 조건부 클래스네임 로직을 clsx나 classnames 라이브러리를 사용하여 개선할 수 있습니다.템플릿 리터럴을 사용한 조건부 클래스네임 로직이 복잡해질 수 있습니다.
clsx나 classnames 라이브러리를 사용하여 더 깔끔하게 작성할 수 있습니다:
-<div className={`${checkboxLayout} ${isAllCheck ? allCheckWrapper : ''}`}> +<div className={clsx(checkboxLayout, isAllCheck && allCheckWrapper)}>src/app/home/components/Banner/Banner.tsx (1)
45-47
: 이미지 최적화 개선이 필요합니다.동일한 이미지를 여러 번 렌더링하는 것은 성능에 영향을 줄 수 있으며, 모든 이미지에
priority
속성을 사용하는 것은 최적화에 도움이 되지 않을 수 있습니다.다음과 같이 개선해보세요:
- <Image src={BannerImage} alt="배너1" className={bannerImage} priority /> - <Image src={BannerImage} alt="배너2" className={bannerImage} priority /> - <Image src={BannerImage} alt="배너3" className={bannerImage} priority /> + <Image src={BannerImage} alt="배너1" className={bannerImage} priority /> + <Image src={BannerImage} alt="배너2" className={bannerImage} loading="lazy" /> + <Image src={BannerImage} alt="배너3" className={bannerImage} loading="lazy" />src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.tsx (1)
47-48
: TODO 주석에 대한 후속 조치가 필요합니다.사용자 정보에서 gender와 age를 가져오는 기능이 아직 구현되지 않았습니다. 이 기능을 구현하여 사용자 경험을 개선할 수 있습니다.
이 기능을 구현하기 위한 코드를 생성해드릴까요?
src/app/home/components/PostContainer/AreaFilter/AreaFilter.tsx (1)
98-100
: 성능 최적화가 필요합니다.매 렌더링마다
reduce
연산이 실행되고 있습니다. 이 값을 메모이제이션하면 성능을 개선할 수 있습니다.다음과 같이 수정을 제안합니다:
+ const totalCount = useMemo(() => + postArea?.reduce((acc, cur) => acc + cur.count, 0) ?? 0, + [postArea] + ); // ... - {postArea?.reduce((acc, cur) => acc + cur.count, 0)} + {totalCount}src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.css.ts (1)
6-21
: 스타일 개선 제안버튼 스타일링에 대해 다음과 같은 개선사항을 제안드립니다:
- 반응형 디자인을 위해 고정 너비 대신 상대적 단위 사용을 고려해보세요.
- 재사용성을 위해 마진을 props로 받아 유연하게 조절할 수 있도록 하는 것이 좋습니다.
export const nextButton = style({ ...fonts.body.normal.SB16, backgroundColor: colors.primaryMint, color: colors.text01, borderRadius: '1.2rem', padding: '1.2rem 0', - width: '20rem', + width: 'min(20rem, 100%)', - marginBottom: '5.6rem', alignItems: 'center', selectors: { '&:disabled': { color: colors.text02, backgroundColor: colors.field04, }, }, });src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.css.ts (1)
25-37
: 디자인 토큰 활용 제안버튼 스타일의 매직 넘버들을 디자인 토큰으로 분리하면 일관성 있는 디자인 시스템 구축에 도움이 될 것 같습니다.
+ // tokens.css.ts에 추가 + export const spacing = { + sm: '0.7rem', + md: '1rem', + lg: '1.6rem', + }; + export const borderRadius = { + md: '1rem', + }; // AuthCodeInput.css.ts export const authCodeButton = style({ ...fonts.label.large.SB14, - padding: '0.7rem 1.6rem', - borderRadius: '1rem', + padding: `${spacing.sm} ${spacing.lg}`, + borderRadius: borderRadius.md, backgroundColor: colors.primaryMint, color: colors.text01, selectors: { '&:disabled': { backgroundColor: colors.field04, color: colors.text02, }, }, });src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.tsx (3)
50-50
: TODO 주석 처리 필요이미 인증된 유저 처리에 대한 TODO 주석이 있습니다. 이 부분을 구현하는데 도움이 필요하신가요?
79-80
: 불필요한 불리언 표현식 단순화
aria-invalid
속성의 불리언 표현식을 단순화할 수 있습니다.- aria-invalid={fieldState.invalid ? true : false} + aria-invalid={fieldState.invalid}🧰 Tools
🪛 Biome (1.9.4)
[error] 80-80: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with(lint/complexity/noUselessTernary)
91-92
: 에러 처리 개선 제안필드 에러와 API 에러를 구분하여 표시하는 것이 좋습니다. 사용자가 어떤 종류의 에러가 발생했는지 명확하게 알 수 있도록 스타일을 다르게 적용하는 것을 추천드립니다.
// UnivAuthInput.css.ts에 추가 + export const fieldError = style({ + color: colors.error, + fontSize: '1.2rem' + }); + + export const apiError = style([ + fieldError, + { + fontWeight: 'bold' + } + ]); // UnivAuthInput.tsx - {fieldState.error && <span className={errorMessage}>{fieldState.error.message}</span>} - {sendError && <span className={errorMessage}>{sendError.message}</span>} + {fieldState.error && <span className={fieldError}>{fieldState.error.message}</span>} + {sendError && <span className={apiError}>{sendError.message}</span>}src/app/home/components/PostContainer/PostContainer.css.ts (2)
18-23
: 반응형 레이아웃 고려가 필요합니다.고정된 최소 높이(40rem)는 다양한 화면 크기에서 문제를 일으킬 수 있습니다. 다음과 같은 개선을 고려해보세요:
export const postCardContainer = style({ display: 'flex', flexDirection: 'column', gap: '0.6rem', - minHeight: '40rem', + minHeight: 'min(40rem, 90vh)', });
30-34
: 모바일 대응을 고려해보세요.현재 필터 래퍼의 레이아웃이 모바일에서는 부적절할 수 있습니다. 화면 크기가 작을 때는 세로 배치로 전환하는 것을 고려해보세요.
export const filterWrapper = style({ display: 'flex', alignItems: 'center', justifyContent: 'space-between', + '@media': { + 'screen and (max-width: 768px)': { + flexDirection: 'column', + alignItems: 'flex-start', + gap: '1rem', + }, + }, });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (82)
src/app/home/components/Banner/Banner.css.ts
(1 hunks)src/app/home/components/Banner/Banner.styles.ts
(0 hunks)src/app/home/components/Banner/Banner.tsx
(2 hunks)src/app/home/components/PostCard/PostCard.css.ts
(1 hunks)src/app/home/components/PostCard/PostCard.styles.ts
(0 hunks)src/app/home/components/PostCard/PostCard.tsx
(3 hunks)src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.css.ts
(1 hunks)src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.styles.ts
(0 hunks)src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.tsx
(1 hunks)src/app/home/components/PostCardList/PostCardList.css.ts
(1 hunks)src/app/home/components/PostCardList/PostCardList.styles.ts
(0 hunks)src/app/home/components/PostCardList/PostCardList.tsx
(2 hunks)src/app/home/components/PostContainer/AreaFilter/AreaFilter.css.ts
(1 hunks)src/app/home/components/PostContainer/AreaFilter/AreaFilter.styles.ts
(0 hunks)src/app/home/components/PostContainer/AreaFilter/AreaFilter.tsx
(4 hunks)src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.css.ts
(1 hunks)src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.styles.ts
(0 hunks)src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.tsx
(2 hunks)src/app/home/components/PostContainer/FilterContainer/FilterContainer.css.ts
(1 hunks)src/app/home/components/PostContainer/FilterContainer/FilterContainer.styles.ts
(0 hunks)src/app/home/components/PostContainer/FilterContainer/FilterContainer.tsx
(2 hunks)src/app/home/components/PostContainer/PostContainer.css.ts
(1 hunks)src/app/home/components/PostContainer/PostContainer.styles.ts
(0 hunks)src/app/home/components/PostContainer/PostContainer.tsx
(3 hunks)src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.css.ts
(1 hunks)src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.styles.ts
(0 hunks)src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx
(2 hunks)src/app/join/JoinPage.css.ts
(1 hunks)src/app/join/JoinPage.styles.ts
(0 hunks)src/app/join/components/EmailToast/EmailToast.css.ts
(1 hunks)src/app/join/components/EmailToast/EmailToast.styles.ts
(0 hunks)src/app/join/components/EmailToast/EmailToast.tsx
(2 hunks)src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.css.ts
(1 hunks)src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.styles.ts
(0 hunks)src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.tsx
(2 hunks)src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.css.ts
(1 hunks)src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.styles.ts
(0 hunks)src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.tsx
(2 hunks)src/app/join/components/JoinInput/JoinInput.css.ts
(1 hunks)src/app/join/components/JoinInput/JoinInput.styles.ts
(0 hunks)src/app/join/components/JoinInput/JoinInput.tsx
(6 hunks)src/app/join/components/JoinSuccessStep/JoinSuccessStep.css.ts
(1 hunks)src/app/join/components/JoinSuccessStep/JoinSuccessStep.styles.ts
(0 hunks)src/app/join/components/JoinSuccessStep/JoinSuccessStep.tsx
(2 hunks)src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.styles.ts
(0 hunks)src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx
(3 hunks)src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.css.ts
(1 hunks)src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.styles.ts
(0 hunks)src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.tsx
(2 hunks)src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.css.ts
(1 hunks)src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.styles.ts
(0 hunks)src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.tsx
(6 hunks)src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.css.ts
(1 hunks)src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.styles.ts
(0 hunks)src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.tsx
(2 hunks)src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.css.ts
(1 hunks)src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.styles.ts
(0 hunks)src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.tsx
(2 hunks)src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.css.ts
(1 hunks)src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.styles.ts
(0 hunks)src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.tsx
(4 hunks)src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.css.ts
(1 hunks)src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.styles.ts
(0 hunks)src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.tsx
(4 hunks)src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.css.ts
(1 hunks)src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.tsx
(3 hunks)src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.css.ts
(1 hunks)src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.styles.ts
(0 hunks)src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.tsx
(3 hunks)src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.styles.ts
(0 hunks)src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.tsx
(3 hunks)src/app/join/page.tsx
(3 hunks)src/app/login/LoginPage.css.ts
(1 hunks)src/app/login/LoginPage.styles.ts
(0 hunks)src/app/login/components/LoginCard.styles.ts
(0 hunks)src/app/login/components/LoginCard/LoginCard.css.ts
(1 hunks)src/app/login/components/LoginCard/LoginCard.tsx
(4 hunks)src/app/login/google/GoogleLoginPage.css.ts
(1 hunks)src/app/login/google/GoogleLoginPage.styles.ts
(0 hunks)src/app/login/google/page.tsx
(2 hunks)src/app/login/layout.tsx
(1 hunks)src/app/login/naver/NaverLoginPage.css.ts
(1 hunks)
⛔ Files not processed due to max files limit (5)
- src/app/login/naver/NaverLoginPage.styles.ts
- src/app/login/naver/page.tsx
- src/app/login/page.tsx
- src/app/page.tsx
- src/components/Icon/icons/AllEmpty.tsx
💤 Files with no reviewable changes (27)
- src/app/login/google/GoogleLoginPage.styles.ts
- src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.styles.ts
- src/app/home/components/PostContainer/FilterContainer/FilterContainer.styles.ts
- src/app/home/components/PostCardList/PostCardList.styles.ts
- src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.styles.ts
- src/app/join/components/EmailToast/EmailToast.styles.ts
- src/app/home/components/PostContainer/PostContainer.styles.ts
- src/app/join/JoinPage.styles.ts
- src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.styles.ts
- src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.styles.ts
- src/app/login/LoginPage.styles.ts
- src/app/login/components/LoginCard.styles.ts
- src/app/join/components/JoinSuccessStep/JoinSuccessStep.styles.ts
- src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.styles.ts
- src/app/home/components/PostCard/PostCard.styles.ts
- src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.styles.ts
- src/app/join/components/JoinInput/JoinInput.styles.ts
- src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.styles.ts
- src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.styles.ts
- src/app/home/components/Banner/Banner.styles.ts
- src/app/home/components/PostContainer/AreaFilter/AreaFilter.styles.ts
- src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.styles.ts
- src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.styles.ts
- src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.styles.ts
- src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.styles.ts
- src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.styles.ts
- src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.styles.ts
✅ Files skipped from review due to trivial changes (4)
- src/app/home/components/PostContainer/PostContainer.tsx
- src/app/home/components/PostCard/PostCard.tsx
- src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.css.ts
- src/app/home/components/PostContainer/FilterContainer/FilterContainer.css.ts
🧰 Additional context used
🪛 Biome (1.9.4)
src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.tsx
[error] 80-80: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with
(lint/complexity/noUselessTernary)
🔇 Additional comments (60)
src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.tsx (1)
3-8
: 스타일 임포트 경로가 올바르게 변경되었습니다.Emotion에서 vanilla-extract로의 마이그레이션이 정상적으로 이루어졌습니다.
src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.tsx (1)
8-8
: 스타일 임포트 마이그레이션이 올바르게 수행되었습니다.Emotion에서 vanilla-extract로의 마이그레이션이 성공적으로 이루어졌습니다.
src/app/home/components/PostContainer/FilterContainer/FilterContainer.tsx (2)
1-1
: 스타일 마이그레이션이 올바르게 수행되었습니다.Emotion에서 vanilla-extract로의 마이그레이션이 성공적으로 이루어졌습니다:
.styles.ts
에서.css.ts
로의 import 변경css
prop에서className
prop으로의 전환컴포넌트의 기능적 로직은 그대로 유지되어 있습니다.
Also applies to: 12-12
6-8
: 컴포넌트 구조가 잘 설계되어 있습니다.
- TypeScript 인터페이스를 통한 명확한 props 타입 정의
- 자식 컴포넌트들로의 일관된 props 전달
- 필터 변경 핸들링이 적절하게 구현되어 있습니다
Also applies to: 10-18
src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.css.ts (1)
1-5
: 임포트 구성이 깔끔합니다!외부 라이브러리와 내부 모듈을 구분하여 임포트한 것이 좋습니다.
src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.tsx (2)
20-21
: 폼 유효성 검사 로직이 잘 구현되었습니다.
useWatch
를 사용하여 여러 필드의 값을 동시에 모니터링하고,every
메서드로 모든 필드가 채워졌는지 확인하는 방식이 효율적입니다.
24-25
: Emotion에서 vanilla-extract로의 마이그레이션이 깔끔하게 이루어졌습니다.
css
prop에서className
으로의 전환이 일관되게 적용되었으며, 기존 스타일링의 기능을 그대로 유지하고 있습니다.Also applies to: 57-57
src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.tsx (1)
6-16
: 스타일 임포트가 올바르게 마이그레이션되었습니다.Emotion에서 vanilla-extract로의 마이그레이션이 정상적으로 이루어졌습니다.
src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx (3)
1-1
: 'use client' 지시문이 적절히 추가되었습니다.클라이언트 사이드 렌더링이 필요한 컴포넌트에 'use client' 지시문이 올바르게 추가되었습니다.
9-9
: 스타일 임포트가 vanilla-extract로 성공적으로 마이그레이션되었습니다.Emotion에서 vanilla-extract로의 스타일 마이그레이션이 올바르게 이루어졌습니다. 스타일 파일의 경로와 임포트 구문이 적절히 수정되었습니다.
27-31
: allValid 로직이 개선되었습니다.Boolean 래퍼를 제거하여 조건문이 더 간결해졌습니다. 하지만 이 로직에 대한 단위 테스트 추가를 고려해보시기 바랍니다.
유효성 검사 로직이 예상대로 동작하는지 확인하기 위한 테스트 케이스 작성을 추천드립니다. 테스트 코드 작성을 도와드릴까요?
src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.tsx (1)
6-6
: 스타일 임포트가 잘 구성되어 있습니다!스타일 변수명이 명확하고 계층 구조가 잘 표현되어 있습니다.
src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.tsx (2)
6-6
: 스타일 임포트가 올바르게 마이그레이션되었습니다!Emotion에서 vanilla-extract로의 스타일 임포트 전환이 깔끔하게 이루어졌습니다.
Also applies to: 12-12
52-53
: 스타일 적용 방식이 성공적으로 마이그레이션되었습니다!Emotion의
css
prop에서 vanilla-extract의className
으로의 전환이 일관성 있게 이루어졌습니다.Also applies to: 77-77
src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.tsx (2)
1-11
: 스타일 마이그레이션 및 클라이언트 컴포넌트 설정이 올바르게 되었습니다!클라이언트 사이드 렌더링을 위한 'use client' 지시문 추가와 vanilla-extract로의 스타일 마이그레이션이 잘 이루어졌습니다.
24-62
: 컴포넌트 구현이 잘 되어있습니다!
- vanilla-extract로의 스타일 마이그레이션이 일관성 있게 잘 적용되었습니다.
- react-hook-form과의 통합이 잘 유지되었습니다.
- 필수 필드 표시와 에러 상태 처리가 적절합니다.
src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.css.ts (2)
1-5
: 스타일 임포트가 잘 구성되어 있습니다!전역 스타일 토큰(colors, fonts)을 활용하여 일관된 디자인 시스템을 유지하고 있습니다.
15-20
: 컨텐츠 스타일링이 깔끔하게 되어있습니다!flex 레이아웃을 활용하여 컨텐츠를 효과적으로 배치하고 있습니다.
src/app/home/components/PostContainer/AreaFilter/AreaFilter.css.ts (3)
1-6
: 라이브러리 임포트와 기본 설정이 잘 구성되어 있습니다.필요한 vanilla-extract 라이브러리와 공통 스타일이 적절하게 임포트되어 있습니다.
77-104
: 버튼 레시피가 잘 구현되어 있습니다.선택 상태에 따른 스타일 변화가 명확하게 정의되어 있으며, 기본값 설정도 적절합니다.
156-160
: 접근성 관련 스타일이 잘 구현되어 있습니다.체크박스를 시각적으로 숨기면서도 스크린 리더 접근성을 유지한 방식이 좋습니다.
src/app/join/components/JoinSuccessStep/JoinSuccessStep.tsx (3)
5-11
: 스타일 임포트 경로가 올바르게 변경되었습니다.Emotion에서 vanilla-extract로의 마이그레이션이 잘 이루어졌습니다.
21-28
: className 속성으로의 전환이 잘 이루어졌습니다.기존 Emotion의 css prop에서 vanilla-extract의 className으로 전환이 깔끔하게 이루어졌습니다.
29-31
: Link 컴포넌트의 스타일링이 적절하게 적용되었습니다.Next.js의 Link 컴포넌트에 className을 사용한 스타일 적용이 올바르게 이루어졌습니다.
src/app/join/page.tsx (1)
3-3
: 동적 CSS 변수 지원을 위한 적절한 도구가 추가되었습니다.
@vanilla-extract/dynamic
의assignInlineVars
를 사용하여 동적 스타일링을 구현한 것이 좋습니다.src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.css.ts (4)
1-5
: 스타일 관련 import 문이 잘 구성되어 있습니다!필요한 스타일 유틸리티와 프로젝트의 디자인 토큰을 적절히 가져오고 있습니다.
12-14
: 필수 입력 필드 스타일이 잘 정의되어 있습니다!디자인 시스템의 색상 토큰을 적절히 활용하고 있습니다.
34-38
: 수정 버튼 스타일이 잘 정의되어 있습니다!색상 토큰을 일관성 있게 사용하고 있습니다.
40-43
: 에러 메시지 스타일이 적절히 정의되어 있습니다!폰트와 색상 토큰을 올바르게 활용하고 있습니다.
src/app/join/components/JoinSuccessStep/JoinSuccessStep.css.ts (3)
1-4
: 모듈 가져오기가 잘 구성되어 있습니다!공유 스타일 모듈을 활용하여 일관된 디자인 시스템을 유지하고 있습니다.
15-21
: 타이틀 컨테이너 스타일이 잘 구현되어 있습니다!플렉스박스를 활용한 중앙 정렬과 일관된 간격 사용이 돋보입니다.
23-26
: 타이포그래피와 색상 토큰 사용이 훌륭합니다!공유 폰트 스타일과 시맨틱 컬러 토큰을 활용하여 디자인 시스템을 잘 따르고 있습니다.
src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.css.ts (3)
1-5
: 스타일 임포트가 잘 구성되어 있습니다!공유 스타일 토큰(colors, fonts)을 활용하여 일관된 디자인 시스템을 유지하고 있습니다.
32-41
: 활성 상태 스타일링이 잘 구현되어 있습니다!호버 상태에서도 일관된 스타일을 유지하여 사용자 경험이 자연스럽습니다.
43-45
: 에러 상태 스타일링이 명확하게 구현되어 있습니다!시맨틱 컬러 토큰을 사용하여 에러 상태를 시각적으로 잘 표현하고 있습니다.
src/app/join/components/JoinInput/JoinInput.css.ts (4)
1-5
: 스타일 설정이 잘 구성되어 있습니다!공유 스타일 토큰(colors, fonts)을 활용하여 일관된 디자인 시스템을 구현한 점이 좋습니다.
6-18
: 컨테이너와 라벨 스타일링이 적절합니다!Flexbox를 활용한 레이아웃과 일관된 간격 설정이 잘 되어있습니다.
42-56
: 부가 UI 요소들의 스타일링이 적절합니다!필수 입력 표시와 리셋 버튼의 위치 지정이 잘 구현되어 있습니다.
57-79
: 피드백 텍스트 스타일링이 잘 구현되어 있습니다!에러 메시지, 도움말, 글자 수 표시 등의 스타일링이 디자인 시스템을 잘 따르고 있습니다.
src/app/join/JoinPage.css.ts (6)
1-5
: LGTM! 의존성 가져오기가 잘 구성되어 있습니다.필요한 스타일 유틸리티와 디자인 토큰을 올바르게 가져오고 있습니다.
15-20
: 컨테이너 스타일링이 깔끔하게 되어 있습니다.flex 레이아웃을 사용한 컨테이너 구성이 적절합니다.
22-26
: 제목 컨테이너의 레이아웃이 잘 구성되어 있습니다.space-between과 center 정렬을 적절히 활용하고 있습니다.
38-41
: 타이포그래피 스타일이 디자인 시스템을 잘 따르고 있습니다.폰트와 색상에 디자인 토큰을 적절히 활용하고 있습니다.
43-56
: 프로그레스 바 구현이 효율적으로 되어 있습니다.CSS 변수를 활용한 동적 너비 조절과 부드러운 전환 효과가 잘 구현되어 있습니다.
58-65
: 폼 레이아웃이 깔끔하게 구성되어 있습니다.flex 레이아웃을 활용한 폼 요소 배치가 적절합니다.
src/app/join/components/JoinInput/JoinInput.tsx (1)
6-17
: 스타일 임포트가 잘 구성되어 있습니다!vanilla-extract로의 마이그레이션이 깔끔하게 이루어졌으며, 스타일 네이밍이 명확하고 일관성 있게 구성되어 있습니다.
src/app/login/components/LoginCard/LoginCard.tsx (1)
3-3
: 스타일 정의 파일 검증이 필요합니다.스타일 변수들이 올바르게 정의되어 있는지 확인해주세요.
다음 스크립트를 실행하여 스타일 정의를 확인할 수 있습니다:
Also applies to: 13-14
✅ Verification successful
스타일 정의 및 파일 검증 완료
LoginCard.css.ts
및LoginPage.css.ts
파일에서 필요한 모든 스타일 변수들이 올바르게 정의되어 있는 것으로 확인되었습니다.- 해당 스타일 변수들이 vanilla-extract 마이그레이션 과정에 문제없이 반영되고 있으므로, dynamic styling 및 관련 import 구문(
assignInlineVars
) 사용에 이상이 없습니다.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # LoginCard.css.ts와 LoginPage.css.ts 파일에서 스타일 변수 정의 확인 fd -e css.ts -x cat {} \; | grep -E "badge|buttonContainer|cardTitleContainer|loginButton|loginCardLayout|descriptionWrapper"Length of output: 659
src/app/login/google/GoogleLoginPage.css.ts (1)
3-5
: 이전 파일과 동일한 이슈가 있습니다.NaverLoginPage.css.ts 파일에서 언급한 것과 같이, 매직 넘버에 대한 설명이 필요하며 중복된 스타일을 공통 모듈로 분리해야 합니다.
src/app/login/layout.tsx (1)
1-1
: 구현이 깔끔하고 vanilla-extract 마이그레이션이 잘 되었습니다.Emotion의 css prop에서 vanilla-extract의 className으로의 전환이 올바르게 이루어졌습니다.
Also applies to: 4-4
src/app/login/google/page.tsx (1)
9-27
: 구글 로그인 구현이 안전하고 효율적으로 되어 있습니다.다음과 같은 좋은 구현 사례들이 있습니다:
useRef
를 사용하여 중복 제출 방지- URL 파라미터의 안전한 처리
- vanilla-extract로의 스타일 마이그레이션이 깔끔하게 완료
src/app/home/components/PostCardList/PostCardList.tsx (1)
3-3
: 스타일 마이그레이션이 올바르게 수행되었습니다!Emotion에서 vanilla-extract로의 마이그레이션이 정확하게 이루어졌습니다:
.styles
→.css
임포트 변경css
prop →className
prop 변경Also applies to: 21-21
src/app/join/components/EmailToast/EmailToast.tsx (1)
5-5
: 스타일 마이그레이션이 올바르게 수행되었습니다!Emotion에서 vanilla-extract로의 마이그레이션이 정확하게 이루어졌습니다:
.styles
→.css
임포트 변경css
prop →className
prop 변경Also applies to: 20-20, 25-25, 30-30
src/app/home/components/PostCard/PostCard.css.ts (1)
1-89
: 구현이 잘 되었습니다!vanilla-extract로의 마이그레이션이 잘 이루어졌으며, 테마 변수와 폰트 스타일을 적절히 활용하고 있습니다.
src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.css.ts (1)
1-126
: 접근성과 사용성을 고려한 훌륭한 구현입니다!CSS 변수를 활용한 동적 스타일링, 트랜지션 효과, 그리고 포커스 상태 처리가 잘 구현되어 있습니다.
src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.tsx (1)
88-91
: 동적 스타일링 구현이 잘 되었습니다.
assignInlineVars
를 사용한 동적 스타일링 구현이 깔끔하게 되었습니다. 선택 상태에 따른 색상 변경이 명확하게 처리되었습니다.src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.css.ts (1)
11-18
: 타이머 위치 지정 방식 개선 필요절대 위치(absolute positioning) 사용은 다른 컨텍스트에서 문제를 일으킬 수 있습니다. 상대적인 위치 지정을 고려해보세요.
export const authTimerWrapper = style({ - position: 'absolute', - right: '1.2rem', - top: '1rem', + position: 'relative', display: 'flex', alignItems: 'center', gap: '0.8rem', + marginLeft: 'auto', });✅ Verification successful
타이머 스타일 개선 검증 완료
현재
src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.css.ts
파일의authTimerWrapper
에서는 절대 위치(absolute positioning
)가 사용되고 있습니다. 이는 부모 컨테이너가 상대 위치(relative
)를 가지고 있지 않은 경우, 다양한 레이아웃 이슈를 발생시킬 수 있습니다. 리뷰어가 제시한 diff처럼, 절대 위치 대신 상대 위치를 사용하고 오른쪽 정렬을 위해marginLeft: 'auto'
를 적용하는 방식이 더 유연한 레이아웃 구성을 제공할 수 있습니다.
- 파일:
src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.css.ts
- 이슈:
authTimerWrapper
에position: 'absolute'
가 사용됨으로써, 다른 컨텍스트에서 레이아웃 충돌 가능성이 존재합니다.- 개선 제안: 제시된 diff와 같이
position: 'relative'
로 변경하고marginLeft: 'auto'
를 추가하여 위치 지정 방식을 개선할 수 있습니다.🏁 Scripts executed
The following scripts were executed for the analysis:
Error: No shell scripts or web queries found
src/app/home/components/PostContainer/PostContainer.css.ts (4)
1-5
: 스타일 설정이 잘 구성되어 있습니다!공유 스타일 토큰(colors, fonts)을 활용하여 일관된 디자인 시스템을 구현한 점이 좋습니다.
13-16
: 타이틀 스타일링이 잘 구현되어 있습니다!공유 폰트 스타일과 색상 토큰을 적절히 활용하였습니다.
25-28
: 게시물 수 스타일링이 잘 구현되어 있습니다!레이블 폰트와 텍스트 색상이 적절히 적용되었습니다.
6-11
: rem 단위 사용이 디자인 시스템과 일치하는지 확인해주세요.현재 margin과 gap에 사용된 rem 값들(2rem, 1.6rem)이 디자인 시스템의 spacing 토큰과 일치하는지 확인이 필요합니다.
@@ -95,7 +96,7 @@ const JoinInfoStep = ({ handleSubmit }: JoinInfoStepProps) => { | |||
onChange={(value) => setValue('basicAddressInfo.region', value)} | |||
placeholder="시·도" | |||
options={JOIN_REGION} | |||
isError={Boolean(fieldState.error) && Boolean(!field.value)} | |||
isError={Boolean(fieldState.error) && !field.value} |
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
에러 상태 체크 로직 통일 필요
Select 컴포넌트들의 에러 상태 체크 로직이 일관되지 않습니다:
- 기본 지역:
Boolean(fieldState.error) && !field.value
- 추가 지역:
Boolean(fieldState.error)
에러 처리 로직을 통일하여 일관성을 유지하는 것이 좋습니다.
- isError={Boolean(fieldState.error) && !field.value}
+ isError={Boolean(fieldState.error)}
Also applies to: 113-113, 136-136
export const emptyLayout = style({ | ||
height: 'calc(100vh - 17.8rem)', | ||
}); |
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
매직 넘버에 대한 설명이 필요하며, 중복된 스타일을 공통 모듈로 분리해야 합니다.
17.8rem
이라는 매직 넘버의 의미와 계산 방식에 대한 주석이 필요합니다. 또한 이 스타일이 GoogleLoginPage.css.ts
에도 동일하게 존재하므로, 공통 모듈로 분리하는 것이 좋습니다.
다음과 같이 공통 모듈로 분리하는 것을 제안합니다:
// src/app/login/styles/common.css.ts
+import { style } from '@vanilla-extract/css';
+
+/**
+ * 전체 높이에서 헤더 높이(17.8rem)를 뺀 레이아웃
+ */
+export const emptyLayout = style({
+ height: 'calc(100vh - 17.8rem)',
+});
그리고 각 페이지에서 임포트하여 사용:
-import { style } from '@vanilla-extract/css';
+import { emptyLayout } from '../styles/common.css';
-
-export const emptyLayout = style({
- height: 'calc(100vh - 17.8rem)',
-});
+export { emptyLayout };
📝 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.
export const emptyLayout = style({ | |
height: 'calc(100vh - 17.8rem)', | |
}); | |
import { style } from '@vanilla-extract/css'; | |
/** | |
* 전체 높이에서 헤더 높이(17.8rem)를 뺀 레이아웃 | |
*/ | |
export const emptyLayout = style({ | |
height: 'calc(100vh - 17.8rem)', | |
}); |
export const emptyLayout = style({ | |
height: 'calc(100vh - 17.8rem)', | |
}); | |
import { emptyLayout } from '../styles/common.css'; | |
export { emptyLayout }; |
minWidth: '100rem', | ||
height: 'calc(100vh - 12.2rem)', |
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
반응형 디자인 개선이 필요합니다.
최소 너비를 100rem
으로 고정하면 작은 화면에서 가로 스크롤이 발생할 수 있습니다. 모바일 환경을 고려하여 반응형으로 구현하는 것이 좋습니다.
다음과 같이 수정하는 것을 제안합니다:
- minWidth: '100rem',
+ width: '100%',
+ maxWidth: '100rem',
📝 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.
minWidth: '100rem', | |
height: 'calc(100vh - 12.2rem)', | |
width: '100%', | |
maxWidth: '100rem', | |
height: 'calc(100vh - 12.2rem)', |
color: 'var(--badge-color)', | ||
backgroundColor: 'var(--badge-bg)', |
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
CSS 변수에 대한 타입 안전성이 필요합니다.
--badge-color
와 --badge-bg
CSS 변수가 타입 체크 없이 사용되고 있습니다. vanilla-extract의 createVar()
를 사용하면 타입 안전성을 보장할 수 있습니다.
다음과 같이 수정하는 것을 제안합니다:
import { createVar, style } from '@vanilla-extract/css';
const badgeColor = createVar();
const badgeBg = createVar();
export const badge = style({
...fonts.label.large.SB14,
width: 'fit-content',
padding: '0.4rem 0.8rem',
borderRadius: '1.2rem',
color: badgeColor,
backgroundColor: badgeBg,
});
// 사용 예시:
// assignInlineVars({
// [badgeColor]: colors.text01,
// [badgeBg]: colors.field03,
// })
export const checkbox = style({ | ||
position: 'absolute', | ||
opacity: 0, | ||
pointerEvents: 'none', | ||
}); |
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
접근성 개선이 필요한 hidden checkbox 구현입니다.
opacity: 0
과 pointerEvents: 'none'
만으로는 스크린 리더 사용자를 위한 충분한 접근성을 제공하지 않을 수 있습니다.
다음과 같이 수정하는 것을 추천드립니다:
export const checkbox = style({
position: 'absolute',
- opacity: 0,
- pointerEvents: 'none',
+ clip: 'rect(0 0 0 0)',
+ clipPath: 'inset(50%)',
+ height: '1px',
+ overflow: 'hidden',
+ position: 'absolute',
+ whiteSpace: 'nowrap',
+ width: '1px'
});
📝 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.
export const checkbox = style({ | |
position: 'absolute', | |
opacity: 0, | |
pointerEvents: 'none', | |
}); | |
export const checkbox = style({ | |
position: 'absolute', | |
clip: 'rect(0 0 0 0)', | |
clipPath: 'inset(50%)', | |
height: '1px', | |
overflow: 'hidden', | |
position: 'absolute', | |
whiteSpace: 'nowrap', | |
width: '1px' | |
}); |
@@ -18,7 +18,7 @@ const JoinCheckboxContainer = ({ | |||
const isAllCheck = isTermOfService && isPrivacy && isAdvertise && (isRecommend ?? true); | |||
|
|||
return ( | |||
<div css={termContainer}> | |||
<div className={termContainer}> |
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
체크박스 그룹에 대한 시맨틱 마크업이 필요합니다.
현재 일반 div로 감싸져 있는 체크박스 그룹을 시맨틱하게 개선할 수 있습니다.
다음과 같이 fieldset과 legend를 사용하여 체크박스 그룹을 시맨틱하게 마크업해주세요:
-<div className={termContainer}>
+<fieldset className={termContainer}>
+ <legend className="sr-only">서비스 이용약관 동의</legend>
{/* checkbox components */}
-</div>
+</fieldset>
추가로 screen reader only 클래스도 정의해주세요:
// styles/utils.css.ts
export const srOnly = style({
position: 'absolute',
width: '1px',
height: '1px',
padding: '0',
margin: '-1px',
overflow: 'hidden',
clip: 'rect(0,0,0,0)',
border: '0'
});
<input | ||
className={ageInput} | ||
onChange={handleChangeFilteredAge} | ||
placeholder="만 나이 입력" | ||
/> |
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
입력값 유효성 검사가 필요합니다.
나이 입력 필드에 대한 유효성 검사가 누락되었습니다. 음수나 문자 입력을 방지하고 적절한 나이 범위를 설정하는 것이 좋습니다.
다음과 같이 수정을 제안합니다:
<input
className={ageInput}
onChange={handleChangeFilteredAge}
placeholder="만 나이 입력"
+ type="number"
+ min="1"
+ max="120"
+ value={filteredAge}
/>
📝 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.
<input | |
className={ageInput} | |
onChange={handleChangeFilteredAge} | |
placeholder="만 나이 입력" | |
/> | |
<input | |
className={ageInput} | |
onChange={handleChangeFilteredAge} | |
placeholder="만 나이 입력" | |
type="number" | |
min="1" | |
max="120" | |
value={filteredAge} | |
/> |
<input | ||
type="checkbox" | ||
css={checkbox} | ||
className={checkbox} | ||
checked={!!checkedSubAreas[subArea.name]} | ||
onChange={() => handleSubAreaCheck(subArea.name)} | ||
/> |
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
접근성 개선이 필요합니다.
체크박스 입력에 대한 접근성 레이블이 누락되었습니다.
다음과 같이 수정을 제안합니다:
<input
type="checkbox"
className={checkbox}
checked={!!checkedSubAreas[subArea.name]}
onChange={() => handleSubAreaCheck(subArea.name)}
+ aria-label={`${subAreaMapper[subArea.name]} 선택`}
/>
📝 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.
<input | |
type="checkbox" | |
css={checkbox} | |
className={checkbox} | |
checked={!!checkedSubAreas[subArea.name]} | |
onChange={() => handleSubAreaCheck(subArea.name)} | |
/> | |
<input | |
type="checkbox" | |
className={checkbox} | |
checked={!!checkedSubAreas[subArea.name]} | |
onChange={() => handleSubAreaCheck(subArea.name)} | |
aria-label={`${subAreaMapper[subArea.name]} 선택`} | |
/> |
<div className={univInputWrapper}> | ||
<input | ||
{...field} | ||
style={{ width: '100%' }} |
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
인라인 스타일 제거 필요
인라인 스타일을 CSS 모듈로 이동하여 스타일 일관성을 유지하는 것이 좋습니다.
// UnivAuthInput.css.ts에 추가
+ export const fullWidthInput = style({
+ width: '100%'
+ });
// UnivAuthInput.tsx
- <input style={{ width: '100%' }} />
+ <input className={fullWidthInput} />
📝 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.
style={{ width: '100%' }} | |
import { style } from '@vanilla-extract/css'; | |
export const fullWidthInput = style({ | |
width: '100%', | |
}); |
style={{ width: '100%' }} | |
// ... other imports | |
import { fullWidthInput } from './UnivAuthInput.css'; | |
// ... inside the component's render method | |
<input className={fullWidthInput} /> |
|
||
// 체크박스 숨김 처리 | ||
export const checkbox = style({ | ||
position: 'absolute', |
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.
마이그레이션 작업 고생 많으셨습니다! 🤝
일정이 바쁜 와중 시간을 많이 쓰셨다니..ㅠㅠ 다시 한번 고생하셨어요!
해당 pr 머지 후 따로 이슈를 생성해서 emotion 관련 설정 및 파일들은 모두 제거하겠습니다!
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: 3
🧹 Nitpick comments (3)
src/app/join/JoinPage.css.ts (2)
6-12
: 반응형 디자인 고려 필요
minHeight
계산에서 고정된 픽셀값을 사용하고 있습니다. 다양한 화면 크기에 대응하기 위해 반응형 단위 사용을 고려해보세요.- width: '56rem', + width: 'min(56rem, 100%)', - minHeight: 'calc(100vh - 12.2rem)', + minHeight: 'calc(100vh - var(--header-height, 12.2rem))',
58-64
: 프로그레스 바 애니메이션 최적화현재 transition 속성이 width에만 적용되어 있습니다. 성능 최적화를 위해 transform을 사용하는 것이 좋습니다.
export const progressBarFill = style({ - width: 'var(--progress-width)', + transform: 'scaleX(var(--progress-scale, 0))', + transformOrigin: 'left', height: '100%', backgroundColor: colors.primaryMint, borderRadius: '0.6rem', - transition: 'width 1s', + transition: 'transform 1s ease-out', });src/types/post.ts (1)
10-10
: 타입 변경 사유에 대한 문서화 제안
univName
이 null을 허용하게 된 배경과 사유를 코드 주석으로 문서화하면 좋을 것 같습니다.다음과 같이 주석을 추가하는 것을 제안드립니다:
+ /** 대학교 이름이 없는 경우를 처리하기 위해 null 허용 */ univName: string | null;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/app/home/components/PostCard/PostCard.tsx
(3 hunks)src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx
(3 hunks)src/app/join/JoinPage.css.ts
(1 hunks)src/app/join/layout.tsx
(1 hunks)src/app/layout.tsx
(2 hunks)src/types/post.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/app/home/components/PostCard/PostCard.tsx
- src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
src/app/layout.tsx
[error] 32-32: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🔇 Additional comments (4)
src/app/layout.tsx (1)
2-2
: 스타일 마이그레이션과 무관한 변경사항입니다.이 PR의 주요 목적은 Emotion에서 vanilla-extract로의 스타일 마이그레이션입니다. Beusable RUM 클라이언트 추가는 별도의 PR로 분리하는 것이 좋을 것 같습니다.
src/app/join/layout.tsx (1)
3-8
: 스타일 마이그레이션이 잘 수행되었습니다!Emotion에서 vanilla-extract로의 마이그레이션이 깔끔하게 이루어졌으며, 컴포넌트의 구조가 잘 유지되었습니다.
src/app/join/JoinPage.css.ts (1)
1-5
: 디자인 토큰 활용이 잘 되었습니다!colors와 fonts 토큰을 일관성 있게 사용하여 디자인 시스템을 잘 반영하였습니다.
src/types/post.ts (1)
13-14
: 타입 사용의 일관성이 잘 유지되었습니다
durationInfo
내의startDate
와endDate
처럼 다른 nullable 필드들과 동일한 패턴으로 타입이 정의되어 있어 일관성이 잘 유지되었습니다.
<Script | ||
id="beusable-script" | ||
dangerouslySetInnerHTML={{ | ||
__html: ` | ||
(function(w, d, a){ | ||
w.__beusablerumclient__ = { | ||
load: function(src){ | ||
var b = d.createElement("script"); | ||
b.src = src; | ||
b.async = true; | ||
b.type = "text/javascript"; | ||
d.getElementsByTagName("head")[0].appendChild(b); | ||
} | ||
}; | ||
w.__beusablerumclient__.load(a + "?url=" + encodeURIComponent(d.URL)); | ||
})(window, document, "//rum.beusable.net/load/b250203e183750u380"); | ||
`, | ||
}} | ||
/> |
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.
보안 위험: dangerouslySetInnerHTML 사용을 재고해 주세요.
dangerouslySetInnerHTML
을 사용하면 XSS(Cross-Site Scripting) 공격에 취약할 수 있습니다. Beusable RUM 클라이언트 스크립트를 더 안전한 방식으로 구현하는 것이 좋습니다.
다음과 같이 수정하는 것을 제안드립니다:
- <Script
- id="beusable-script"
- dangerouslySetInnerHTML={{
- __html: `
- (function(w, d, a){
- w.__beusablerumclient__ = {
- load: function(src){
- var b = d.createElement("script");
- b.src = src;
- b.async = true;
- b.type = "text/javascript";
- d.getElementsByTagName("head")[0].appendChild(b);
- }
- };
- w.__beusablerumclient__.load(a + "?url=" + encodeURIComponent(d.URL));
- })(window, document, "//rum.beusable.net/load/b250203e183750u380");
- `,
- }}
- />
+ <Script
+ id="beusable-script"
+ src="//rum.beusable.net/load/b250203e183750u380"
+ strategy="afterInteractive"
+ />
📝 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.
<Script | |
id="beusable-script" | |
dangerouslySetInnerHTML={{ | |
__html: ` | |
(function(w, d, a){ | |
w.__beusablerumclient__ = { | |
load: function(src){ | |
var b = d.createElement("script"); | |
b.src = src; | |
b.async = true; | |
b.type = "text/javascript"; | |
d.getElementsByTagName("head")[0].appendChild(b); | |
} | |
}; | |
w.__beusablerumclient__.load(a + "?url=" + encodeURIComponent(d.URL)); | |
})(window, document, "//rum.beusable.net/load/b250203e183750u380"); | |
`, | |
}} | |
/> | |
<Script | |
id="beusable-script" | |
src="//rum.beusable.net/load/b250203e183750u380" | |
strategy="afterInteractive" | |
/> |
🧰 Tools
🪛 Biome (1.9.4)
[error] 32-32: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
export const nextButton = style({ | ||
...fonts.body.normal.SB16, | ||
backgroundColor: colors.primaryMint, | ||
color: colors.text01, | ||
borderRadius: '1.2rem', | ||
padding: '1.2rem 0', | ||
width: '20rem', | ||
alignItems: 'center', | ||
marginBottom: '5.6rem', | ||
selectors: { | ||
'&:disabled': { | ||
color: colors.text02, | ||
backgroundColor: colors.field04, | ||
}, | ||
}, | ||
}); |
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
접근성 개선 필요
버튼 스타일에 focus와 hover 상태에 대한 스타일이 누락되어 있습니다. 접근성 향상을 위해 이러한 상태들도 고려해야 합니다.
export const nextButton = style({
...fonts.body.normal.SB16,
backgroundColor: colors.primaryMint,
color: colors.text01,
borderRadius: '1.2rem',
padding: '1.2rem 0',
width: '20rem',
alignItems: 'center',
marginBottom: '5.6rem',
selectors: {
'&:disabled': {
color: colors.text02,
backgroundColor: colors.field04,
},
+ '&:hover:not(:disabled)': {
+ backgroundColor: colors.primaryMintHover,
+ },
+ '&:focus-visible': {
+ outline: `2px solid ${colors.primaryMint}`,
+ outlineOffset: '2px',
+ },
},
});
📝 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.
export const nextButton = style({ | |
...fonts.body.normal.SB16, | |
backgroundColor: colors.primaryMint, | |
color: colors.text01, | |
borderRadius: '1.2rem', | |
padding: '1.2rem 0', | |
width: '20rem', | |
alignItems: 'center', | |
marginBottom: '5.6rem', | |
selectors: { | |
'&:disabled': { | |
color: colors.text02, | |
backgroundColor: colors.field04, | |
}, | |
}, | |
}); | |
export const nextButton = style({ | |
...fonts.body.normal.SB16, | |
backgroundColor: colors.primaryMint, | |
color: colors.text01, | |
borderRadius: '1.2rem', | |
padding: '1.2rem 0', | |
width: '20rem', | |
alignItems: 'center', | |
marginBottom: '5.6rem', | |
selectors: { | |
'&:disabled': { | |
color: colors.text02, | |
backgroundColor: colors.field04, | |
}, | |
'&:hover:not(:disabled)': { | |
backgroundColor: colors.primaryMintHover, | |
}, | |
'&:focus-visible': { | |
outline: `2px solid ${colors.primaryMint}`, | |
outlineOffset: '2px', | |
}, | |
}, | |
}); |
@@ -7,7 +7,7 @@ export interface PostInfo { | |||
experimentPostId: number; | |||
title: string; | |||
views: number; | |||
univName: string; | |||
univName: string | null; |
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.
💡 Verification agent
🧩 Analysis chain
univName
타입 변경에 따른 영향도 확인 필요
univName
필드의 타입이 string
에서 string | null
로 변경되었습니다. 이는 breaking change이므로 다음 사항들을 확인해주세요:
- 이 인터페이스를 사용하는 모든 컴포넌트에서 null 처리가 적절히 되어있는지 확인
- 특히 PostCard와 PostCardList 컴포넌트에서 univName이 null일 때의 UI 처리 방식 검토
다음 스크립트를 실행하여 PostInfo
인터페이스를 사용하는 모든 위치를 확인해주세요:
🏁 Script executed:
#!/bin/bash
# Description: Find all usages of PostInfo interface and univName property
echo "Finding PostInfo interface usages:"
rg "PostInfo" -t typescript
echo -e "\nFinding direct univName property access:"
rg "univName" -t typescript
Length of output: 301
🏁 Script executed:
#!/bin/bash
# Updated: Find all usages of PostInfo interface and univName property by searching TypeScript files without the -t flag
echo "Finding PostInfo interface usages:"
rg "PostInfo" --glob "*.{ts,tsx}" || echo "No results for PostInfo"
echo -e "\nFinding direct univName property access:"
rg "univName" --glob "*.{ts,tsx}" || echo "No results for univName"
Length of output: 3148
univName 타입 변경 관련 추가 점검 필요
-
PostInfo 인터페이스 변경:
src/types/post.ts
에서 univName 필드를string
에서string | null
로 변경한 점은 null 값에 대한 처리를 명시적으로 하려는 의도로 보입니다. -
확인된 컴포넌트:
PostCard 등 일부 컴포넌트에서는 univName 사용 시 null인 경우 기본값("비대면"
) 처리를 하고 있어 적절한 처리가 이루어지고 있습니다. -
이슈 발견:
- src/apis/login.ts: univName이 여전히
string
으로 선언되어 있어, PostInfo와 일관되지 않은 타입 정의가 확인되었습니다. - src/upload/hooks/useUploadExperimentPostMutation.ts: univName의 타입이 파일 내에서
univName?: string | null;
와univName: string;
로 상이하게 선언되어 있어 타입 불일치로 인한 문제 발생 가능성이 있습니다.
- src/apis/login.ts: univName이 여전히
전체 코드베이스에서 univName의 타입 적용이 일관적이고, null 값에 따른 예외 처리가 누락되지 않도록 위 파일들을 중심으로 추가 점검 후 수정이 필요합니다.
Issue Number
close #37
As-Is
To-Be
Check List
Test Screenshot
(Optional) Additional Description
마이그레이션이 생각보다 오래걸렸네요... 어려운 스타일링 코드가 있진 않았는데 이 작업으로 인해 다른 게 좀 많이 딜레이 됐네요 ㅠ 빠르게 작업해보겠습니다!
Summary by CodeRabbit
스타일 개선
리팩토링