[UI/#8] 공통 컴포넌트 1차 제작 및 기타 설정#11
Conversation
WalkthroughSVG 임포트, Tailwind 텍스트 유틸리티, 앱 시작 폰트 로딩을 위한 설정이 추가되었고, 공통 버튼·토글·추가 버튼 컴포넌트와 cn 유틸이 새로 구현되었다. ChangesSVG 및 스타일링 인프라 구성
공통 UI 컴포넌트
Estimated code review effort: 3 (Moderate) | ~25 minutes Related issues: Suggested labels: enhancement, ui, dependencies Suggested reviewers: 프론트엔드 리뷰어 Poem SVG가 길을 찾고 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
tailwind.config.js (1)
63-115: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSUIT 폰트 패밀리 문자열 중복 정의
theme.extend.fontFamily와 플러그인 내부FONT_FAMILY_BY_WEIGHT가 동일한 폰트 문자열("SUIT-Regular","SUIT-Medium"등)을 각각 하드코딩하고 있습니다. 추후 폰트 파일명이 바뀌거나 체급이 추가될 때 한쪽만 갱신되면.text-{name}유틸리티와font-suit-*클래스 간 불일치가 발생할 수 있습니다.♻️ 제안: 단일 소스로 통합
+const FONT_FAMILY = { + regular: ["SUIT-Regular"], + medium: ["SUIT-Medium"], + semibold: ["SUIT-SemiBold"], + bold: ["SUIT-Bold"], +}; + module.exports = { theme: { extend: { fontFamily: { - suit: ["SUIT-Regular"], - "suit-medium": ["SUIT-Medium"], - "suit-semibold": ["SUIT-SemiBold"], - "suit-bold": ["SUIT-Bold"], + suit: FONT_FAMILY.regular, + "suit-medium": FONT_FAMILY.medium, + "suit-semibold": FONT_FAMILY.semibold, + "suit-bold": FONT_FAMILY.bold, }, }, }, plugins: [ require("tailwindcss/plugin")(function ({ addUtilities }) { const FONT_FAMILY_BY_WEIGHT = { - 400: "SUIT-Regular", - 500: "SUIT-Medium", - 600: "SUIT-SemiBold", - 700: "SUIT-Bold", + 400: FONT_FAMILY.regular[0], + 500: FONT_FAMILY.medium[0], + 600: FONT_FAMILY.semibold[0], + 700: FONT_FAMILY.bold[0], };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tailwind.config.js` around lines 63 - 115, The SUIT font family values are duplicated between theme.extend.fontFamily and the plugin’s FONT_FAMILY_BY_WEIGHT mapping, so keep them in a single source of truth. Update the Tailwind config so the .text-{name} utilities in the plugin reuse the same font family definitions as the font-suit-* classes, using the existing fontFamily keys or a shared constant referenced by both the theme and the plugin.svg-env.d.ts (1)
1-6: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win공식 템플릿과 다르게
Reactimport 누락
react-native-svg-transformer공식 README 예시는 다음과 같이React를 명시적으로 import합니다: "declare module "*.svg" { import React from "react"; import { SvgProps } from "react-native-svg"; const content: React.FC; export default content; }"현재 파일은 최상위에 import/export가 없어 global script로 처리되어
@types/react의 UMD globalReact를 통해 대부분 정상 동작할 가능성이 높지만, tsconfig의isolatedModules/모듈 처리 방식에 따라 타입 해석이 달라질 수 있습니다. 공식 예시와 동일하게 명시적으로 import하는 것이 안전합니다.🔧 제안
declare module "*.svg" { + import type React from "react"; import type { SvgProps } from "react-native-svg"; const content: React.FC<SvgProps>; export default content; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@svg-env.d.ts` around lines 1 - 6, The SVG module declaration is missing the explicit React import used by the official react-native-svg-transformer template, which can make type resolution depend on ambient UMD globals. Update the `declare module "*.svg"` block to match the official pattern by importing `React` alongside `SvgProps`, so the `content: React.FC<SvgProps>` declaration is treated as a proper module and works consistently with `isolatedModules` and different tsconfig module settings.src/components/common/Toggle.tsx (1)
36-44: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winswitch 시맨틱 접근성 속성 추가 권장.
Pressable에accessibilityRole="switch"와accessibilityState={{ checked: value }}를 추가하면 스크린리더 사용자가 on/off 상태를 인지할 수 있습니다.♻️ 제안 diff
<Pressable className={cn( "w-12 flex-row items-center justify-start rounded-[100px] p-1", value ? "bg-green-100" : "bg-gray-200", className )} onPress={() => onValueChange?.(!value)} + accessibilityRole="switch" + accessibilityState={{ checked: value }} {...rest} >🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/common/Toggle.tsx` around lines 36 - 44, The Toggle component’s Pressable is missing switch accessibility semantics, so screen readers can’t reliably announce its on/off state. Update the Pressable in Toggle.tsx to expose switch behavior by adding accessibilityRole="switch" and accessibilityState with checked bound to value, while keeping the existing onPress/onValueChange wiring and other props intact.src/components/common/Button.tsx (1)
41-56: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win접근성 속성 보완 권장.
Pressable에accessibilityRole="button"과accessibilityLabel={label}(또는accessibilityState={{ disabled }})을 추가하면 스크린리더 사용자에게 버튼임을 명확히 전달할 수 있습니다.♻️ 제안 diff
<Pressable className={cn( "h-12 w-full items-center justify-center rounded-lg px-2.5", disabled ? "bg-gray-200" : container, className )} disabled={disabled} + accessibilityRole="button" + accessibilityLabel={label} + accessibilityState={{ disabled }} {...rest} >🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/common/Button.tsx` around lines 41 - 56, The Button component’s Pressable is missing accessibility metadata, so update the Pressable in Button to explicitly declare its role and state. Add accessibilityRole="button" and an appropriate accessibilityLabel derived from label, and include accessibilityState with disabled status so screen readers can identify the control correctly. Keep the change localized to the Button component’s Pressable wrapper and preserve the existing disabled behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@package.json`:
- Around line 43-46: The package dependency for tailwind-merge is on an
incompatible major version for the current Tailwind setup; update the
tailwind-merge entry in package.json from v3.6.0 to ^2.6.0 so cn() class merging
stays aligned with tailwindcss v3.4.19.
In `@src/components/common/Toggle.tsx`:
- Around line 17-33: The Toggle knob travel distance is too short for the
current sizing, so update the KNOB_TRANSLATE_X value in Toggle so the on-state
knob reaches the far right edge with no leftover gap. Also add default
accessibility props on the Pressable used by Toggle, specifically
accessibilityRole set to switch and accessibilityState reflecting the current
value via checked, so the component is properly announced as a switch.
---
Nitpick comments:
In `@src/components/common/Button.tsx`:
- Around line 41-56: The Button component’s Pressable is missing accessibility
metadata, so update the Pressable in Button to explicitly declare its role and
state. Add accessibilityRole="button" and an appropriate accessibilityLabel
derived from label, and include accessibilityState with disabled status so
screen readers can identify the control correctly. Keep the change localized to
the Button component’s Pressable wrapper and preserve the existing disabled
behavior.
In `@src/components/common/Toggle.tsx`:
- Around line 36-44: The Toggle component’s Pressable is missing switch
accessibility semantics, so screen readers can’t reliably announce its on/off
state. Update the Pressable in Toggle.tsx to expose switch behavior by adding
accessibilityRole="switch" and accessibilityState with checked bound to value,
while keeping the existing onPress/onValueChange wiring and other props intact.
In `@svg-env.d.ts`:
- Around line 1-6: The SVG module declaration is missing the explicit React
import used by the official react-native-svg-transformer template, which can
make type resolution depend on ambient UMD globals. Update the `declare module
"*.svg"` block to match the official pattern by importing `React` alongside
`SvgProps`, so the `content: React.FC<SvgProps>` declaration is treated as a
proper module and works consistently with `isolatedModules` and different
tsconfig module settings.
In `@tailwind.config.js`:
- Around line 63-115: The SUIT font family values are duplicated between
theme.extend.fontFamily and the plugin’s FONT_FAMILY_BY_WEIGHT mapping, so keep
them in a single source of truth. Update the Tailwind config so the .text-{name}
utilities in the plugin reuse the same font family definitions as the
font-suit-* classes, using the existing fontFamily keys or a shared constant
referenced by both the theme and the plugin.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 0dc5ea8c-6715-47f4-b87b-2cffe55c1272
⛔ Files ignored due to path filters (2)
assets/icons/plus.svgis excluded by!**/*.svg,!**/assets/**package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (10)
metro.config.jspackage.jsonsrc/components/common/AddButton.tsxsrc/components/common/AddScheduleButton.tsxsrc/components/common/Button.tsxsrc/components/common/Toggle.tsxsrc/lib/cn.tssvg-env.d.tstailwind.config.jstsconfig.json
| const KNOB_TRANSLATE_X = 20; | ||
|
|
||
| export function Toggle({ | ||
| value, | ||
| onValueChange, | ||
| className, | ||
| ...rest | ||
| }: ToggleProps) { | ||
| const progress = useSharedValue(value ? 1 : 0); | ||
|
|
||
| useEffect(() => { | ||
| progress.value = withTiming(value ? 1 : 0, { duration: 150 }); | ||
| }, [progress, value]); | ||
|
|
||
| const knobTranslateStyle = useAnimatedStyle(() => ({ | ||
| transform: [{ translateX: progress.value * KNOB_TRANSLATE_X }], | ||
| })); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== src/components/common/Toggle.tsx =="
nl -ba src/components/common/Toggle.tsx | sed -n '1,220p'
echo
echo "== Search for Toggle usage and related accessibility props =="
rg -n "accessibilityRole|accessibilityState|role=|ToggleProps|KNOB_TRANSLATE_X|translateX|size-4|w-12|p-1" src -g '!**/node_modules/**'
echo
echo "== Tailwind/theme config or spacing scale references =="
fd -a 'tailwind.config.*|tailwind.*.config.*|theme.*|tokens.*|spacing.*' . 2>/dev/null | head -n 50Repository: TEAM-DAILOG/FE
Length of output: 231
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== src/components/common/Toggle.tsx =="
sed -n '1,220p' src/components/common/Toggle.tsx | cat -n
echo
echo "== Related references in src =="
rg -n "KNOB_TRANSLATE_X|accessibilityRole|accessibilityState|size-4|w-12|p-1|translateX|ToggleProps" src -g '!**/node_modules/**'
echo
echo "== Tailwind config / design tokens files =="
fd -a 'tailwind.config.*' .
fd -a 'tailwind.*.config.*' .
fd -a '*token*' src .Repository: TEAM-DAILOG/FE
Length of output: 2949
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== tailwind.config.js =="
sed -n '1,260p' tailwind.config.js | cat -n
echo
echo "== Search for custom spacing / size utilities / NativeWind config =="
rg -n "spacing|fontSize|size-4|w-12|p-1|nativewind|tailwind" . -g '!**/node_modules/**' -g '!**/.git/**'Repository: TEAM-DAILOG/FE
Length of output: 8615
노브 이동 거리 수정 필요
w-12 / p-1 / size-4 조합이면 최종 이동값은 24px입니다. 지금 20px이면 on 상태에서 노브가 오른쪽 끝까지 가지 않아 4px 여백이 남습니다.
Pressable에 accessibilityRole="switch"와 accessibilityState={{ checked: value }}도 기본으로 넣어주세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/common/Toggle.tsx` around lines 17 - 33, The Toggle knob
travel distance is too short for the current sizing, so update the
KNOB_TRANSLATE_X value in Toggle so the on-state knob reaches the far right edge
with no leftover gap. Also add default accessibility props on the Pressable used
by Toggle, specifically accessibilityRole set to switch and accessibilityState
reflecting the current value via checked, so the component is properly announced
as a switch.
There was a problem hiding this comment.
이동하는 값이니까 여백이 남아야 해서 20px로 한거다.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/_layout.tsx`:
- Around line 22-39: `RootLayout`의 `useFonts` 처리에서 로딩 실패 시 스플래시가 영구 대기할 수 있습니다.
`useFonts`가 반환하는 두 번째 값인 `error`를 함께 받아서, `fontsLoaded || error`일 때
`SplashScreen.hideAsync()`가 실행되도록 `useEffect`와 초기 반환 분기를 조정하세요. `RootLayout`,
`useFonts`, `SplashScreen.hideAsync`를 기준으로 로직을 수정하면 됩니다.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: ab14c4b0-54d6-44e7-b2e3-492487a30ba9
📒 Files selected for processing (2)
app.jsonapp/_layout.tsx
💤 Files with no reviewable changes (1)
- app.json
| export default function RootLayout() { | ||
| const colorScheme = useColorScheme(); | ||
| const [fontsLoaded] = useFonts({ | ||
| "SUIT-Regular": require("@/assets/fonts/SUIT-Regular.otf"), | ||
| "SUIT-Medium": require("@/assets/fonts/SUIT-Medium.otf"), | ||
| "SUIT-SemiBold": require("@/assets/fonts/SUIT-SemiBold.otf"), | ||
| "SUIT-Bold": require("@/assets/fonts/SUIT-Bold.otf"), | ||
| }); | ||
|
|
||
| useEffect(() => { | ||
| if (fontsLoaded) { | ||
| SplashScreen.hideAsync(); | ||
| } | ||
| }, [fontsLoaded]); | ||
|
|
||
| if (!fontsLoaded) { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
expo-font useFonts hook return value error handling latest documentation
💡 Result:
The useFonts hook from expo-font is designed to load a map of fonts at runtime [1][2]. It returns an array containing two values: [loaded, error] [1][2]. Return Value Breakdown: 1. loaded (boolean): Indicates whether the provided font map has finished loading [2]. 2. error (Error | null): Contains an error object if the font loading process failed, or null if the operation was successful [2]. Error Handling and Best Practices: According to the official Expo documentation, the error return value is primarily intended for use during development to help identify issues with font loading [1][3]. To implement robust error handling and prevent common issues (such as the app hanging on a splash screen), developers should monitor both loaded and error states [4]. A standard pattern is to use the useEffect hook to hide the splash screen once the loading process has either finished successfully or encountered an error [1][2]. Example Implementation: import { useFonts } from 'expo-font'; import * as SplashScreen from 'expo-splash-screen'; import { useEffect } from 'react'; // Keep the splash screen visible while fonts are loading SplashScreen.preventAutoHideAsync; export default function App { const [loaded, error] = useFonts({ 'My-Custom-Font': require('./assets/fonts/my-font.ttf'), }); useEffect( => { // Hide the splash screen once loading is complete or an error occurs if (loaded || error) { SplashScreen.hideAsync; } }, [loaded, error]); // Optionally render null or a fallback while loading if (!loaded &&!error) { return null; } // Render your app content return ; } Important Considerations: - Behavior Inconsistency: Some community reports indicate potential inconsistencies in how the error state behaves on different native platforms [5]. Always ensure you are testing font loading behavior across all target environments. - Configuration Plugins: If possible, use the Expo config plugin approach to include fonts, as this is preferred over runtime loading via useFonts [1]. Runtime loading should generally be reserved for scenarios where configuration plugins cannot be used [1]. - Type Safety: Recent updates to the expo-font package have improved type safety for the useFonts hook, turning some former runtime errors into build-time type errors when incorrect arguments are passed [6]. - Font Names: On Android, ensure you are referencing the fontFamily name as defined in the key of the object passed to useFonts, rather than relying solely on the filename, to avoid display issues [7].
Citations:
- 1: https://docs.expo.dev/versions/latest/sdk/font
- 2: https://docs.expo.dev/versions/v54.0.0/sdk/font
- 3: https://docs.expo.dev/versions/latest/sdk/font/
- 4: useFonts hook randomly failing to load fonts expo/expo#21885
- 5: [expo-font] [native] Lost error handling, race condition of fonts loading expo/expo#43717
- 6: [expo-font] Fix bug in
useFontshook and turn runtime errors into type errors expo/expo#30434 - 7: expo-font does not display custom fonts on Android expo/expo#33673
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate and inspect the layout file with line numbers
if [ -f app/_layout.tsx ]; then
sed -n '1,120p' app/_layout.tsx | cat -n
else
echo "app/_layout.tsx not found"
fi
# Gather package metadata relevant to expo-font if available
for f in package.json app/package.json; do
if [ -f "$f" ]; then
echo "===== $f ====="
sed -n '1,220p' "$f"
fi
doneRepository: TEAM-DAILOG/FE
Length of output: 3806
useFonts의 error도 함께 처리하세요. expo-font의 useFonts는 [loaded, error]를 반환하므로, 로딩 실패 시에도 SplashScreen.hideAsync()가 실행되도록 loaded || error 기준으로 분기해야 합니다. 지금처럼 fontsLoaded만 보면 실패 상황에서 스플래시 화면이 계속 남을 수 있습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/_layout.tsx` around lines 22 - 39, `RootLayout`의 `useFonts` 처리에서 로딩 실패 시
스플래시가 영구 대기할 수 있습니다. `useFonts`가 반환하는 두 번째 값인 `error`를 함께 받아서, `fontsLoaded ||
error`일 때 `SplashScreen.hideAsync()`가 실행되도록 `useEffect`와 초기 반환 분기를 조정하세요.
`RootLayout`, `useFonts`, `SplashScreen.hideAsync`를 기준으로 로직을 수정하면 됩니다.
📌 관련 이슈번호
📌 PR 유형
어떤 변경 사항이 있나요?
📌 PR 요약
src/하위 파일도 스캔하도록 content 경로 추가📌 작업 세부 내용
1.
cn유틸 함수 추가 (clsx + tailwind-merge)2. SUIT 폰트 굵기별 family 매핑 수정
font-weight:600에 대해semiBold매핑이 동작하지 않음text-b-02-sb이런식으로 작성하면 바로 적용됨3. 피그마에서 추출한 아이콘
.svg를 컴포넌트처럼 import해서 쓰도록 metro/타입 설정 추가react-native-svg,react-native-svg-transformer추가stroke="currentColor"로 처리해 사용처에서 색상 지정 가능Button컴포넌트 추가 (fill-green / fill-gray / stroke-green variant, disabled 상태 지원)AddButton,AddScheduleButton컴포넌트 추가Toggle컴포넌트 추가📸 스크린샷 (선택)
2026-07-05.10.35.22.mov
🔗 기타 공지