๐Ÿงฑ 45. React Native ๊ณตํ†ต UI ์ปดํฌ๋„ŒํŠธ ์„ค๊ณ„ ์ „๋žต โ€” Button, Input, Text ๋“ฑ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๋Š” ๋ฐฉ๋ฒ•

JM_Devยท2025๋…„ 6์›” 5์ผ
0
post-thumbnail

์•ฑ์„ ๊ฐœ๋ฐœํ•˜๋‹ค ๋ณด๋ฉด ๊ฐ™์€ ๋ฒ„ํŠผ, ๊ฐ™์€ ์Šคํƒ€์ผ์˜ ์ž…๋ ฅ์ฐฝ์„
์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๋ฐ˜๋ณตํ•ด์„œ ๋งŒ๋“ค๊ฒŒ ๋œ๋‹ค.

์ด๋Ÿด ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ ์Šคํƒ€์ผ์„ ๋ณต๋ถ™ํ•˜๊ฑฐ๋‚˜,
๋กœ์ง์„ ๋‹ค์‹œ ๋งŒ๋“œ๋Š” ๊ฑด ์ƒ์‚ฐ์„ฑ ์ €ํ•˜ + ์œ ์ง€๋ณด์ˆ˜ ์ง€์˜ฅ์˜ ์ง€๋ฆ„๊ธธ์ด๋‹ค.

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ๊ณตํ†ต UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ค๊ณ„ํ•ด์„œ
์Šคํƒ€์ผ, ๋™์ž‘, ์žฌ์‚ฌ์šฉ์„ฑ์„ ํ†ต์ผ์‹œํ‚ค๋Š” ์ „๋žต์„ ์จ์•ผ ํ•œ๋‹ค.

์ด๋ฒˆ ๊ธ€์€ React Native์—์„œ ์‹ค์ œ๋กœ ๋งŽ์ด ์“ฐ์ด๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์–ด๋–ป๊ฒŒ ์„ค๊ณ„ํ•˜๋ฉด ์ข‹์€์ง€ ์ •๋ฆฌํ•œ ๊ธ€์ด๋‹ค.


โœ… ์™œ ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?

  • ๋””์ž์ธ ์‹œ์Šคํ…œ์— ๋งž์ถ˜ UI ์ผ๊ด€์„ฑ
  • ๋ณ€๊ฒฝ์ด ์ƒ๊ฒจ๋„ ํ•œ ๊ณณ์—์„œ๋งŒ ์ˆ˜์ • ๊ฐ€๋Šฅ
  • ํƒ€์ž…๊ณผ ์ธํ„ฐํŽ˜์ด์Šค ๋ช…ํ™•ํ™”
  • ํ…Œ๋งˆ(๋‹คํฌ๋ชจ๋“œ ๋“ฑ)์™€๋„ ์œ ์—ฐํ•˜๊ฒŒ ์—ฐ๋™

๐Ÿ’ก Button ์ปดํฌ๋„ŒํŠธ ์˜ˆ์‹œ

interface ButtonProps {
  label: string;
  onPress: () => void;
  variant?: 'primary' | 'outline' | 'ghost';
  disabled?: boolean;
}

export default function Button({ label, onPress, variant = 'primary', disabled = false }: ButtonProps) {
  const backgroundColor = {
    primary: '#007bff',
    outline: 'transparent',
    ghost: 'transparent',
  }[variant];

  const borderColor = variant === 'outline' ? '#007bff' : 'transparent';

  return (
    <TouchableOpacity
      onPress={onPress}
      disabled={disabled}
      style={{
        backgroundColor,
        borderWidth: 1,
        borderColor,
        paddingVertical: 12,
        paddingHorizontal: 24,
        borderRadius: 6,
        opacity: disabled ? 0.5 : 1,
      }}
    >
      <Text style={{ color: variant === 'primary' ? '#fff' : '#007bff' }}>{label}</Text>
    </TouchableOpacity>
  );
}

โœ… variant prop์œผ๋กœ ์Šคํƒ€์ผ ๋ถ„๊ธฐ
โœ… disabled ์ฒ˜๋ฆฌ
โœ… ์žฌ์‚ฌ์šฉ์„ฑ ๋†’์€ ๊ตฌ์กฐ


๐Ÿ“ Text ์ปดํฌ๋„ŒํŠธ ์„ค๊ณ„

interface TextProps {
  children: React.ReactNode;
  size?: 'sm' | 'md' | 'lg';
  weight?: 'regular' | 'bold';
  color?: string;
}

export default function AppText({ children, size = 'md', weight = 'regular', color = '#000' }: TextProps) {
  const fontSize = {
    sm: 14,
    md: 16,
    lg: 20,
  }[size];

  const fontWeight = weight === 'bold' ? '700' : '400';

  return (
    <Text style={{ fontSize, fontWeight, color }}>{children}</Text>
  );
}
  • ํ…์ŠคํŠธ ์Šคํƒ€์ผ์„ ์‹œ์Šคํ…œ์— ๋งž๊ฒŒ ์ •๋ฆฌ
  • ํฌ๊ธฐ, ๊ตต๊ธฐ, ์ƒ‰์ƒ์„ ๊ณตํ†ต ๊ธฐ์ค€์œผ๋กœ ๊ด€๋ฆฌ

๐Ÿ”‘ ์„ค๊ณ„ํ•  ๋•Œ ์‹ ๊ฒฝ ์จ์•ผ ํ•  ํฌ์ธํŠธ

ํ•ญ๋ชฉ์„ค๋ช…
prop ํ™•์žฅ์„ฑvariant, size, ์ƒํƒœ๋ณ„ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ
ํ…Œ๋งˆ ์—ฐ๋™useTheme() or styled-components ์‚ฌ์šฉ
ํ”Œ๋žซํผ ๋Œ€์‘iOS/Android์—์„œ ๋‹ค๋ฅด๊ฒŒ ๋ณด์ด๋Š” ๊ฒฝ์šฐ ๋ถ„๊ธฐ
์ƒํƒœisLoading, disabled, error ๋“ฑ ๋ช…ํ™•ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑtestID ๋ถ€์—ฌ๋กœ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•˜๊ฒŒ ์„ค๊ณ„

โš™๏ธ UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๋ณ‘ํ–‰๋„ ๊ฐ€๋Šฅ

  • react-native-paper, native-base, ui-kitten ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๋”๋ผ๋„
    โ†’ ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ž˜ํ•‘(Wrapping) ํ•ด์„œ ์Šคํƒ€์ผ๊ณผ ์ธํ„ฐํŽ˜์ด์Šค ํ†ต์ผ ๊ฐ€๋Šฅ

๐Ÿง  ์‹ค์ „ ๊ฒฝํ—˜

์ฒ˜์Œ์—” ๊ทธ๋ƒฅ ๋ฒ„ํŠผ๋งˆ๋‹ค ๋”ฐ๋กœ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ,
์•ฑ์ด ์ปค์ง€๋ฉด์„œ ๋””์ž์ธ ํŒ€์—์„œ ์ƒ‰์ƒ, ๋ชจ์„œ๋ฆฌ, ๊ทธ๋ฆผ์ž ์Šคํƒ€์ผ์ด ๋ฐ”๋€Œ๋‹ˆ๊นŒ
๋ชจ๋“  ๋ฒ„ํŠผ์„ ๋‹ค ์ˆ˜์ •ํ•ด์•ผ ํ–ˆ๋‹ค.

๊ทธ๋•Œ๋ถ€ํ„ฐ ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด
๋ฒ„ํŠผ, ํ…์ŠคํŠธ, ์ž…๋ ฅ์ฐฝ, ๋ชจ๋‹ฌ ๋“ฑ ์ฃผ์š” UI๋ฅผ ๋””์ž์ธ ์‹œ์Šคํ…œ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์—ˆ๊ณ 
์ƒ์‚ฐ์„ฑ, ์œ ์ง€๋ณด์ˆ˜, ํŒ€ ํ˜‘์—… ํšจ์œจ๊นŒ์ง€ ํ›จ์”ฌ ์ข‹์•„์กŒ๋‹ค.


๐Ÿงฑ โ€œUI๋Š” ์Œ“๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.โ€


profile
๊ฐœ๋ฐœ์ž๋กœ ์ทจ์—…์„ ์ค€๋น„ ์ค‘ ์ด๋ฉฐ, ์—ด์‹ฌํžˆ ๊ณต๋ถ€ ์ค‘ ์ž…๋‹ˆ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€