반응형

🛠️ Flutter에서 디버그 모드일 때만 기능 실행하기

 

개발 중에만 실행하고 싶은 코드가 있을 때,

릴리즈 빌드에서는 제외되길 원하는 기능이 있을 때,

“디버그 모드에서만 실행되는 코드”**는 정말 유용한 패턴입니다.

 

Flutter는 이런 상황을 위해 간단한 방법을 제공합니다.

바로 kDebugMode를 사용하는 거예요.

 


 

 kDebugMode란?

 

Flutter에서 kDebugMode현재 앱이 디버그 빌드 상태인지 확인하는 전역 상수입니다.

import 'package:flutter/foundation.dart';

if (kDebugMode) {
  print('디버그 모드입니다!');
}

 

  • 디버그 빌드일 때만 true
  • 릴리즈 빌드나 프로파일 모드에서는 false
  • 코드 트리밍으로 인해 릴리즈 빌드에서는 이 조건문 자체가 제거됩니다

 


 

🎯 언제 사용할 수 있을까?

상황예시

개발자 전용 기능 실행 개발 중에만 보이는 버튼/토스트
로그 출력 디버그 중에만 print() 사용
테스트 데이터 주입 가짜 계정으로 자동 로그인
저장소 초기화 SharedPreferences 강제 리셋 등

 

 


 

🧪 실전 예시

 

 

📌 1. 디버그일 때만 실행 횟수 초기화

Future<void> resetLaunchCountIfDebug() async {
  final prefs = await SharedPreferences.getInstance();

  if (kDebugMode) {
    await prefs.setInt('launchCount', 0); // 디버깅 중엔 무조건 0부터 시작
  }

  int count = prefs.getInt('launchCount') ?? 0;
  await prefs.setInt('launchCount', count + 1);

  print('앱 실행 횟수: ${count + 1}');
}

 

 


 

📌 2. 디버그 모드에서만 개발자용 버튼 표시

if (kDebugMode)
  TextButton(
    onPressed: () => print('디버그용 기능 실행'),
    child: Text('개발자 전용 버튼'),
  ),

 

 


 

❗주의할 점

 

  • kDebugModepackage:flutter/foundation.dart에서 제공됩니다.
  • 릴리즈 빌드에서는 조건문 자체가 포함되지 않기 때문에 성능에도 영향 없음
  • kReleaseMode, kProfileMode도 함께 활용 가능해요.

 


 

✨ 마무리

 

Flutter 앱을 개발할 때, 디버깅 전용 기능을 안전하게 분리하고 싶다면

kDebugMode를 적극 활용해보세요!

 

  • 기능 테스트가 깔끔해지고
  • 실수로 릴리즈 빌드에 들어가는 걸 방지할 수 있어요.

 


🧡 이 글이 도움이 됐다면 공감 & 댓글 부탁드립니다 :)

Flutter 실전 팁, 상태관리, 배포 전략 등 계속 업데이트할게요!

반응형
반응형

 

Flutter로 만든 macOS 앱의 기본 아이콘은 Flutter 로고입니다.

이를 커스텀 아이콘으로 바꾸기 위해 가장 확실하게 적용된 방식만 정리합니다.

 

.icns 방식 없이, PNG 파일과 Contents.json 수정만으로 완전히 반영된 방법입니다.

 


 

아이콘 이미지 준비

 

아이콘 이미지는 다음과 같이 정사각형 PNG 파일로 준비합니다.

크기는 Apple에서 요구하는 해상도에 맞춰 리사이징합니다.

파일 이름크기(px)

icon_16x16.png 16×16
icon_16x16@2x.png 32×32
icon_32x32.png 32×32
icon_32x32@2x.png 64×64
icon_128x128.png 128×128
icon_128x128@2x.png 256×256
icon_256x256.png 256×256
icon_256x256@2x.png 512×512
icon_512x512.png 512×512
icon_512x512@2x.png 1024×1024

 

 


 

파일 위치

 

생성한 PNG 파일들을 모두 아래 경로로 복사합니다:

macos/Runner/Assets.xcassets/AppIcon.appiconset/

기존에 있던 Flutter 아이콘 파일들은 덮어쓰거나 삭제해도 됩니다.

 


 

Contents.json 수정

 

같은 폴더에 있는 Contents.json 파일을 아래와 같이 수정합니다:

{
  "images": [
    { "idiom": "mac", "size": "16x16", "scale": "1x", "filename": "icon_16x16.png" },
    { "idiom": "mac", "size": "16x16", "scale": "2x", "filename": "icon_16x16@2x.png" },
    { "idiom": "mac", "size": "32x32", "scale": "1x", "filename": "icon_32x32.png" },
    { "idiom": "mac", "size": "32x32", "scale": "2x", "filename": "icon_32x32@2x.png" },
    { "idiom": "mac", "size": "128x128", "scale": "1x", "filename": "icon_128x128.png" },
    { "idiom": "mac", "size": "128x128", "scale": "2x", "filename": "icon_128x128@2x.png" },
    { "idiom": "mac", "size": "256x256", "scale": "1x", "filename": "icon_256x256.png" },
    { "idiom": "mac", "size": "256x256", "scale": "2x", "filename": "icon_256x256@2x.png" },
    { "idiom": "mac", "size": "512x512", "scale": "1x", "filename": "icon_512x512.png" },
    { "idiom": "mac", "size": "512x512", "scale": "2x", "filename": "icon_512x512@2x.png" }
  ],
  "info": {
    "version": 1,
    "author": "xcode"
  }
}

 

 


 

빌드 및 확인

 

설정이 완료되면 앱을 다시 빌드합니다:

flutter clean
flutter build macos

빌드가 완료된 .app 파일을 실행하거나 Finder, Dock, Spotlight에서 확인했을 때

아이콘이 제대로 보이면 성공입니다.

 


 

마무리

 

Flutter macOS 앱에 아이콘을 적용하는 가장 간단하고 확실한 방법은

.icns 없이 PNG와 Contents.json을 정확히 구성하는 것입니다.

 

이 방식은 Xcode 프로젝트를 수정할 필요도 없고, Flutter 기본 구조 내에서 완전히 동작합니다.

아이콘 적용이 막히셨다면 이 방식으로 시도해보시길 추천드립니다.

반응형
반응형

Flutter로 만든 macOS 앱을 .dmg 형식으로 만들어 사용자가 쉽게 설치할 수 있도록 배포하려면,

create-dmg라는 유틸리티를 사용하면 깔끔한 드래그 앤 드롭 설치 UI를 만들 수 있습니다.

이 글은 그 전체 과정을 정리한 실습 가이드입니다.

 


 

앱 빌드 완료 후 준비

 

먼저 Flutter 앱을 macOS 용으로 빌드합니다:

flutter build macos

빌드가 끝나면 .app 파일이 아래 경로에 생깁니다:

build/macos/Build/Products/Release/YourAppName.app

이제 이 .app.dmg로 포장해 보겠습니다.

 


 

1. 설치에 필요한 폴더 구성

 

.dmg 안에는 앱 외에 /Applications로 연결되는 링크가 필요합니다.

mkdir -p dist
cp -R build/macos/Build/Products/Release/YourAppName.app dist/
ln -s /Applications dist/Applications
dist/ 폴더 안에는 두 개가 있어야 합니다:

 

  • YourAppName.app
  • Applications (심볼릭 링크)

 


 

2. create-dmg 설치 (처음 한 번만)

brew install create-dmg

설치가 완료되면, 어디서든 create-dmg 명령을 사용할 수 있습니다.

 


 

3.  .dmg 파일 생성

create-dmg \
  --volname "YourAppName" \
  --window-size 500 300 \
  --icon-size 100 \
  --icon "YourAppName.app" 125 150 \
  --hide-extension "YourAppName.app" \
  --app-drop-link 375 150 \
  YourAppName.dmg \
  dist/
✅ 가장 중요한 포인트:

 

  • 출력 파일 (YourAppName.dmg) 은 마지막에서 두 번째
  • 소스 폴더 (dist/) 는 가장 마지막 인자

 

예시 결과:

SimpleFitLog.dmg
├── YourAppName.app
├── Applications (→ /Applications)

.dmg를 열면 macOS에서 친숙한 드래그 앤 드롭 설치 UI가 뜹니다.

 


 

4. 자주 발생하는 오류 및 해결

🔸 cd: --: invalid option

 

--output 옵션을 쓰면 안 됩니다.

.dmg 파일 이름은 항상 마지막 두 번째 인자로 넣어야 합니다.

 

잘못된 예:

create-dmg ... --output YourAppName.dmg dist/

올바른 예:

create-dmg ... YourAppName.dmg dist/

 

 


 

5. 추가 팁 (선택 사항)

배경 이미지 넣기

--background "background.png"

 

  • PNG만 지원
  • 이미지 크기는 창 크기보다 크거나 같아야 함

 

아이콘 위치 조정

 

아이콘 좌표는 픽셀 단위로 조정 가능:

--icon "YourAppName.app" 125 150
--app-drop-link 375 150

 

 


 

마무리

 

이제 완성된 .dmg 파일은 다른 사용자에게 배포하거나 웹사이트에 업로드해 사용할 수 있습니다.

앱 실행 시 Gatekeeper 경고가 뜰 경우, 터미널에서 아래 명령어로 실행 제한을 해제할 수 있습니다:

sudo xattr -rd com.apple.quarantine /Applications/YourAppName.app

이 과정을 통해 보다 전문적이고 신뢰감 있는 macOS 앱 배포가 가능해집니다.

 

반응형
반응형

앱에서 고유 ID(Unique ID)를 생성해야 할 때, 가장 널리 쓰이는 방식이 바로 UUID (Universally Unique Identifier)입니다.
Flutter에서는 uuid 패키지를 통해 간편하게 다양한 방식의 UUID를 생성할 수 있습니다.

이 글에서는:

  • uuid 패키지 설치 방법
  • UUID 버전(v1, v4, v5)의 차이점
  • 실전 예제와 사용 팁

까지 하나씩 정리해보겠습니다.


📦 1. uuid 패키지 설치하기

flutter pub add uuid

혹은 pubspec.yaml에 직접 추가:

dependencies:
  uuid: ^4.2.1  # 최신 버전 확인

그리고 import:

import 'package:uuid/uuid.dart';

🧬 2. UUID 버전별 차이점 정리

버전 방식 설명 특징

v1 Time-based 시간 + 기기(MAC 주소) 기반 순차적 생성 가능, MAC 유출 위험
v2 DCE Security v1 + 사용자/그룹 ID 거의 사용되지 않음
v3 Name-based (MD5) 문자열 + 네임스페이스 → MD5 해시 입력이 같으면 UUID도 같음
v4 Random 무작위(random) 값 기반 일반적으로 가장 많이 사용됨
v5 Name-based (SHA-1) v3와 동일하나 SHA-1 해시 사용 더 강력한 보안 해시

⚙️ 3. UUID 생성 예제 (Dart 코드)

final uuid = Uuid();

// ✅ v1 – 시간 + MAC 주소 기반
String idV1 = uuid.v1(); // 예: 'f64c8b26-3e46-11ec-8d3d-0242ac130003'

// ✅ v4 – 랜덤 기반 (가장 많이 사용)
String idV4 = uuid.v4(); // 예: 'c3c3bc94-b070-4787-9f7d-b2592b4d51ef'

// ✅ v5 – 고정된 문자열로 UUID 생성 (SHA-1)
String idV5 = uuid.v5(Uuid.NAMESPACE_URL, 'https://example.com');

📌 v3도 동일 방식으로 가능:

String idV3 = uuid.v3(Uuid.NAMESPACE_URL, 'https://example.com'); // MD5 기반

✅ 실전에서는 언제 어떤 버전?

상황 추천 버전 이유

단순한 고유 ID가 필요할 때 v4 간단하고 충돌 확률 매우 낮음
시간 순서대로 정렬이 필요할 때 v1 생성 시간 정보 포함됨
입력값이 항상 같을 때 동일한 ID가 필요할 때 v5 (또는 v3) 해시 기반 고정 UUID 생성

❗️주의할 점 (v1의 MAC 주소 노출)

  • v1 UUID는 내부적으로 기기의 MAC 주소를 사용합니다.
  • 이 정보는 유출되면 기기 식별 가능성이 있으므로 보안이 중요한 앱에서는 피하는 것이 좋습니다.
  • 대부분의 경우 v4를 사용하는 것이 안전하고 충분합니다.

✨ 실전 사용 예: 모델 ID 생성

class Task {
  final String id;
  final String title;

  Task({required this.title}) : id = const Uuid().v4();
}

→ 매 Task 인스턴스를 생성할 때 자동으로 고유한 UUID를 부여합니다.


🧩 요약

  • uuid 패키지를 설치하면 다양한 방식으로 고유 식별자를 생성할 수 있음
  • 대부분의 경우 v4 (랜덤 기반)가 안정적이고 보편적
  • v5는 동일 입력 → 동일 UUID가 필요할 때 매우 유용
반응형
반응형

📝 요약 설명 

Flutter로 macOS 앱을 개발할 때 기본 창 크기를 설정하고, 앱을 "항상 위에 고정(Always on Top)"으로 띄우는 방법을 소개합니다. 에디터와 앱을 나란히 띄우기 위한 실사용 팁!


🖥️ 문제 상황

Flutter로 macOS 앱을 개발할 때, 앱을 항상 에디터 옆에 띄워놓고 싶었어요.
하지만 앱을 실행하면 매번 이상한 위치에 뜨거나 다른 창 뒤로 가버리는 게 너무 불편했습니다.

그래서 macOS 앱을 항상 위에 고정시키고, 기본 창 크기도 지정하는 방법을 정리해봤습니다.


🔧 해결 방법 요약

  • MainFlutterWindow.swift에서 macOS 앱의 창 속성을 직접 설정할 수 있습니다.
  • 여기서 창 크기, 창 위치, 항상 위에 고정(floating)을 커스터마이징하면 됩니다.

📁 수정할 파일

macos/Runner/MainFlutterWindow.swift


✅ 최종 코드 예시

import Cocoa
import FlutterMacOS

class MainFlutterWindow: NSWindow {
  override func awakeFromNib() {
    let flutterViewController = FlutterViewController()

    // 원하는 창 크기
    let windowWidth: CGFloat = 800
    let windowHeight: CGFloat = 600

    // 화면 중앙에 창 위치 설정
    if let screenFrame = NSScreen.main?.frame {
      let originX = (screenFrame.width - windowWidth) / 2
      let originY = (screenFrame.height - windowHeight) / 2
      let newFrame = NSRect(x: originX, y: originY, width: windowWidth, height: windowHeight)
      self.setFrame(newFrame, display: true)
    }

    // 항상 위에 설정
    self.level = .floating

    self.contentViewController = flutterViewController

    RegisterGeneratedPlugins(registry: flutterViewController)

    super.awakeFromNib()
  }
}

📌 추가 팁

  • self.level = .floating → 이걸 설정하면 앱 창이 항상 다른 창 위에 떠 있어요.
    특히 코드 에디터 + 앱 창 나란히 테스트할 때 엄청 편합니다.
  • self.setFrame(...) → 앱의 기본 위치와 크기를 지정할 수 있습니다.
반응형

✨ 마무리

에디터와 앱을 동시에 띄워놓고 작업할 때, 이 설정 하나로 스트레스가 확 줄었어요.
macOS 앱에서 UI 디버깅 자주 하시는 분들께 꼭 추천드립니다.


필요하면 이미지나 gif 첨부용 예시도 만들어줄 수 있어.
티스토리에 바로 붙여넣을 HTML 포맷도 원해?

반응형
반응형

🔍 문제 상황

React에서 컴포넌트 리스트를 .map()으로 렌더링할 때, 다음과 같은 에러가 자주 발생합니다:

Warning: Each child in a list should have a unique 'key' prop.

그래서 보통은 아래처럼 key를 추가하죠.

items.map((item) => (
  <MyComponent key={item.id} item={item} />
))

하지만! 정상적으로 key를 넣었음에도 불구하고 위와 같은 경고가 계속 발생하는 경우가 있습니다.


❓ 대체 왜?

이 문제의 진짜 원인은… 바로 Fragment (<>...</>) 때문입니다.


💥 문제 코드 예시

items.map((item) => (
  <>
    <MyComponent key={item.id} item={item} />
  </>
))

위 코드는 MyComponent에 key를 넣었지만,
사실상 무시되고 있습니다.

왜냐하면, React는 .map()으로 직접 리턴하는 최상위 노드에 key가 있는지를 보기 때문입니다.

즉, 위의 코드는:

  • Fragment가 최상위인데 key 없음 → ❌
  • 내부 MyComponent에 있는 key → 무시됨 → ❌ 경고 발생

✅ 해결 방법

방법 1: Fragment 없애기 (권장)

items.map((item) => (
  <MyComponent key={item.id} item={item} />
))

방법 2: Fragment에 key 넣기 (가능하지만 비권장)

items.map((item) => (
  <React.Fragment key={item.id}>
    <MyComponent item={item} />
  </React.Fragment>
))

💡 실전 예: React Native ScrollView 안에서 발생한 사례

<ScrollView>
  {items.map((item) => (
    <>
      <TimelineItem key={item.id} item={item} />
    </>
  ))}
</ScrollView>

이 구조에서 Fragment가 key를 무시하면서 경고가 발생했습니다.
→ Fragment를 제거하거나, key를 Fragment로 올리면 해결됩니다!


🧠 정리

구조 key 인식 여부 추천 여부

<Component key={...} /> ✅ 강력 추천
<><Component key={...} /></> ❌ 경고 발생
<Fragment key={...}><Component /></Fragment> ⚠️ 가능하지만 가독성 저하 우려

✅ 마무리

React에서 key 경고가 사라지지 않을 때는
"내가 넣은 key가 최상위 엘리먼트에 있는가?" 를 꼭 확인하세요!

특히, 아무 생각 없이 쓴 <> ... </> 가 문제의 원인일 수 있습니다 😉

 

 

반응형
반응형

⚙️ 시작: uuid 설치 및 사용

React Native 앱에서 일정 추가 시 고유 ID가 필요해서
보통 많이 사용하는 uuid 패키지를 설치하고 다음처럼 사용했습니다:

pnpm add uuid
import { v4 as uuidv4 } from 'uuid';

const id = uuidv4();

그리고 이 값을 일정 객체에 사용했죠:

addSchedule({
  id: uuidv4(), // ❌ 여기서 에러 발생
  title: '주사 맞기',
  ...
});

그런데 앱을 실행하자 다음과 같은 에러가 발생했습니다:

Error: crypto.getRandomValues() not supported.

👀 이 에러는 웹 브라우저에서 제공하는 crypto.getRandomValues()를
React Native에서 사용할 수 없기 때문에 발생한 것입니다.


🔍 원인 요약

  • uuid 패키지는 웹 브라우저 환경을 기준으로 만들어졌고
  • 내부적으로 crypto.getRandomValues()를 사용함
  • 하지만 React Native는 웹이 아니므로 해당 API가 없음

⚠️ 원인: uuid 패키지의 내부 동작

많은 개발자들이 사용하는 uuid 패키지는 브라우저 환경에서 잘 작동합니다.
하지만 이 패키지의 v4 구현은 내부적으로 crypto.getRandomValues()를 사용하기 때문에 React Native에서는 동작하지 않습니다.


💡 해결 방법: React Native에 맞는 UUID 패키지 사용

✅ 방법 1: react-native-uuid 사용 (권장)

  1. 설치
pnpm add react-native-uuid
  1. 사용법
import uuid from 'react-native-uuid';

const id = uuid.v4() as string;

uuid.v4()는 string | number[] 형태를 반환하므로, 타입스크립트에서는 as string으로 명시해주는 것이 좋습니다.


✅ 방법 2: expo-crypto 사용 (Expo 사용자)

  1. 설치
pnpm add expo-crypto
  1. 사용법
import * as Crypto from 'expo-crypto';

const id = await Crypto.getRandomUUID();

이 방식은 async/await이 필요하기 때문에 함수 구조를 비동기적으로 변경해야 합니다.


🧠 정리

패키지 RN 지원 여부 특징

uuid ❌ (웹 전용) crypto.getRandomValues() 사용 → 에러 발생
react-native-uuid 동기 방식, 간단하게 사용 가능
expo-crypto ✅ (Expo 전용) 공식 지원, 비동기 방식 필요

✨ 마무리

React Native에서는 브라우저의 crypto API를 사용할 수 없습니다.
따라서 UUID가 필요할 경우, RN에 맞는 패키지를 선택해서 사용하는 것이 중요합니다.

이번 경험을 통해 웹과 네이티브의 차이를 잘 이해하게 되었고, 앞으로는 환경에 맞는 라이브러리 선택이 얼마나 중요한지 다시 한 번 느꼈습니다. 💪

 

반응형
반응형

 

🔍 에러 상황

React Native 프로젝트에서 아래와 같은 코드를 작성했습니다:

import { colors } from '@repo/constants';

const styles = StyleSheet.create({
  container: {
    backgroundColor: colors.green[900],
  },
});

그런데 앱을 실행했더니 다음과 같은 에러가 발생했습니다:

❌ Cannot read property 'green' of undefined

🤔 왜 이런 에러가 발생할까?

에러의 원인은 default export를 named import로 불러왔기 때문입니다.

📁 colors.ts

const colors = {
  green: {
    900: '#064E3B'
  },
  ...
};

export default colors;

📁 index.ts

export * from './colors';

colors.ts는 default export지만,
index.ts에서는 named export로 넘기지 않았기 때문에
import { colors } from '@repo/constants'는 undefined가 됩니다.


✅ 해결 방법 3가지


✅ 방법 1. default → named export로 바꾸기 (가장 추천)

// colors.ts
export const colors = {
  ...
};
// index.ts
export * from './colors';
// 사용처
import { colors } from '@repo/constants';

✅ 가장 직관적이고 안전한 방식입니다.


✅ 방법 2. default export 유지하면서 export 이름 지정

// colors.ts
const colors = { ... };
export default colors;
// index.ts
export { default as colors } from './colors';
// 사용처
import { colors } from '@repo/constants';

✅ default export를 유지하면서도 named import처럼 쓸 수 있습니다.


⚠️ 방법 3. default import로 직접 경로 import (비추천)

import colors from '@repo/constants/colors';

⚠️ 경로를 직접 노출하게 되어 리팩토링이 어려워질 수 있습니다.


💡 결론

목적 추천 방식

import { colors } 하고 싶다 export const colors = ... 사용
default export 꼭 유지하고 싶다 export { default as colors } 추가
하위 경로로 import 가능은 하지만 비추천

 

반응형
반응형

React Native로 앱을 만들다 보면 버튼이나 카드처럼 누를 수 있는 영역을 만들 때 TouchableOpacity 또는 Pressable 중 어떤 걸 써야 할지 고민될 때가 있다.

두 컴포넌트 모두 유저의 터치 입력을 감지하는 역할을 하지만, 사용 방식과 유연성에서 차이가 있다.
이 글에서는 두 컴포넌트의 차이점과 사용 시점을 정리해 본다.


🔸 TouchableOpacity

TouchableOpacity는 React Native 초창기부터 존재한 컴포넌트로,
사용자가 터치하면 **불투명도(opacity)**가 낮아지는 시각적 피드백을 준다.

import { TouchableOpacity, Text } from 'react-native';

<TouchableOpacity onPress={() => alert('Pressed')}>
  <Text>Click me</Text>
</TouchableOpacity>

✅ 장점

  • 사용법이 매우 간단하다
  • 기본적인 눌림 효과(투명도 변화)가 내장돼 있어 빠르게 UI 구성 가능

❌ 단점

  • 스타일 커스터마이징이 제한적이다
  • 눌림 효과는 opacity 하나뿐

🔸 Pressable

Pressable은 React Native 0.63부터 도입된 최신 터치 컴포넌트로,
눌림 상태에 따라 스타일을 완전히 커스터마이징할 수 있는 것이 가장 큰 특징이다.

import { Pressable, Text } from 'react-native';

<Pressable
  onPress={() => alert('Pressed')}
  style={({ pressed }) => ({
    backgroundColor: pressed ? '#eee' : '#fff',
    padding: 10,
  })}
>
  <Text>Click me</Text>
</Pressable>

✅ 장점

  • pressed 상태를 이용해 배경색, 그림자, 테두리 등 다양한 스타일 변경 가능
  • 눌렀을 때 외에도 onPressIn, onPressOut 등 다양한 콜백 제공

❌ 단점

  • 코드가 상대적으로 길고 복잡할 수 있다
  • 간단한 버튼엔 오히려 과할 수 있음

🆚 한눈에 비교

항목 TouchableOpacity Pressable
도입 시기 오래됨 최신 (RN 0.63+)
눌림 효과 opacity 자동 적용 자유롭게 스타일 커스터마이징
스타일 제어 제한적 pressed로 조건부 스타일링
콜백 이벤트 기본적 onPressIn, onPressOut 등 다양
추천 사용처 간단한 터치 UI 다양한 효과가 필요한 경우

💡 언제 어떤 걸 써야 할까?

  • 단순한 버튼, 리스트 아이템 → TouchableOpacity
  • 눌렀을 때 배경색, 테두리 등 커스텀 UI 효과 → Pressable
  • ✅ 최신 React Native 기준의 코드 스타일을 따르고 싶다면 → Pressable 선호

✍️ 마무리

React Native에서 두 컴포넌트는 결국 터치 이벤트 처리를 위한 도구지만,
개발자의 의도에 따라 적절히 선택하는 것이 중요하다.

  • 빠르게 만들고 싶다면 TouchableOpacity
  • 정교한 UI 피드백이 필요하면 Pressable

 

반응형
반응형

 

🎨 Tailwind 색상 팔레트를 React Native로!

TailwindCSS의 색상 시스템은 직관적인 이름(blue_500, gray_900)과 단계별 명확한 색감으로 많은 개발자에게 사랑받고 있습니다.
React Native에서도 동일한 색상 시스템을 구성하면 일관된 디자인 시스템 구축, 다크모드 대응, alpha 값 처리 등 많은 이점을 누릴 수 있습니다.


🌈 전체 색상표 (100 ~ 900)

아래는 TailwindCSS 기준으로 React Native에서 사용할 수 있도록 구성한 색상 팔레트입니다.

글 하단에 요약된 코드를 넣어 두었습니다.

✅ 각 색상은 colors.색상이름[단계] 형태로 접근합니다.
예: colors.blue[500], colors.red[900]


🔵 blue

단계 HEX 코드

100 #DBEAFE
200 #BFDBFE
300 #93C5FD
400 #60A5FA
500 #3B82F6
600 #2563EB
700 #1D4ED8
800 #1E40AF
900 #1E3A8A

⚫ gray

단계 HEX 코드

100 #F3F4F6
200 #E5E7EB
300 #D1D5DB
400 #9CA3AF
500 #6B7280
600 #4B5563
700 #374151
800 #1F2937
900 #111827

🔴 red

단계 HEX 코드

100 #FEE2E2
200 #FECACA
300 #FCA5A5
400 #F87171
500 #EF4444
600 #DC2626
700 #B91C1C
800 #991B1B
900 #7F1D1D

🟢 green

단계 HEX 코드

100 #D1FAE5
200 #A7F3D0
300 #6EE7B7
400 #34D399
500 #10B981
600 #059669
700 #047857
800 #065F46
900 #064E3B

🟡 yellow

단계 HEX 코드

100 #FEF9C3
200 #FEF08A
300 #FDE047
400 #FACC15
500 #EAB308
600 #CA8A04
700 #A16207
800 #854D0E
900 #713F12

🟣 purple

단계 HEX 코드

100 #F3E8FF
200 #E9D5FF
300 #D8B4FE
400 #C084FC
500 #A855F7
600 #9333EA
700 #7E22CE
800 #6B21A8
900 #581C87

🌸 pink

단계 HEX 코드

100 #FCE7F3
200 #FBCFE8
300 #F9A8D4
400 #F472B6
500 #EC4899
600 #DB2777
700 #BE185D
800 #9D174D
900 #831843

🧊 cyan

단계 HEX 코드

100 #CFFAFE
200 #A5F3FC
300 #67E8F9
400 #22D3EE
500 #06B6D4
600 #0891B2
700 #0E7490
800 #155E75
900 #164E63

 


반응형

 

const colors = {
  gray: {
    100: '#F3F4F6',
    200: '#E5E7EB',
    300: '#D1D5DB',
    400: '#9CA3AF',
    500: '#6B7280',
    600: '#4B5563',
    700: '#374151',
    800: '#1F2937',
    900: '#111827',
  },
  blue: {
    100: '#DBEAFE',
    200: '#BFDBFE',
    300: '#93C5FD',
    400: '#60A5FA',
    500: '#3B82F6',
    600: '#2563EB',
    700: '#1D4ED8',
    800: '#1E40AF',
    900: '#1E3A8A',
  },
  green: {
    100: '#D1FAE5',
    200: '#A7F3D0',
    300: '#6EE7B7',
    400: '#34D399',
    500: '#10B981',
    600: '#059669',
    700: '#047857',
    800: '#065F46',
    900: '#064E3B',
  },
  red: {
    100: '#FEE2E2',
    200: '#FECACA',
    300: '#FCA5A5',
    400: '#F87171',
    500: '#EF4444',
    600: '#DC2626',
    700: '#B91C1C',
    800: '#991B1B',
    900: '#7F1D1D',
  },
  yellow: {
    100: '#FEF9C3',
    200: '#FEF08A',
    300: '#FDE047',
    400: '#FACC15',
    500: '#EAB308',
    600: '#CA8A04',
    700: '#A16207',
    800: '#854D0E',
    900: '#713F12',
  },
  indigo: {
    100: '#E0E7FF',
    200: '#C7D2FE',
    300: '#A5B4FC',
    400: '#818CF8',
    500: '#6366F1',
    600: '#4F46E5',
    700: '#4338CA',
    800: '#3730A3',
    900: '#312E81',
  },
  purple: {
    100: '#F3E8FF',
    200: '#E9D5FF',
    300: '#D8B4FE',
    400: '#C084FC',
    500: '#A855F7',
    600: '#9333EA',
    700: '#7E22CE',
    800: '#6B21A8',
    900: '#581C87',
  },
  pink: {
    100: '#FCE7F3',
    200: '#FBCFE8',
    300: '#F9A8D4',
    400: '#F472B6',
    500: '#EC4899',
    600: '#DB2777',
    700: '#BE185D',
    800: '#9D174D',
    900: '#831843',
  },
  teal: {
    100: '#CCFBF1',
    200: '#99F6E4',
    300: '#5EEAD4',
    400: '#2DD4BF',
    500: '#14B8A6',
    600: '#0D9488',
    700: '#0F766E',
    800: '#115E59',
    900: '#134E4A',
  },
  orange: {
    100: '#FFEDD5',
    200: '#FED7AA',
    300: '#FDBA74',
    400: '#FB923C',
    500: '#F97316',
    600: '#EA580C',
    700: '#C2410C',
    800: '#9A3412',
    900: '#7C2D12',
  },
  lime: {
    100: '#ECFCCB',
    200: '#D9F99D',
    300: '#BEF264',
    400: '#A3E635',
    500: '#84CC16',
    600: '#65A30D',
    700: '#4D7C0F',
    800: '#3F6212',
    900: '#365314',
  },
  cyan: {
    100: '#CFFAFE',
    200: '#A5F3FC',
    300: '#67E8F9',
    400: '#22D3EE',
    500: '#06B6D4',
    600: '#0891B2',
    700: '#0E7490',
    800: '#155E75',
    900: '#164E63',
  },
  zinc: {
    100: '#F4F4F5',
    200: '#E4E4E7',
    300: '#D4D4D8',
    400: '#A1A1AA',
    500: '#71717A',
    600: '#52525B',
    700: '#3F3F46',
    800: '#27272A',
    900: '#18181B',
  },
};

export default colors;

 

🏁 마무리

Tailwind의 색상 시스템은 미적으로 뛰어나고 확장도 쉽습니다.
React Native에서도 이 구조를 도입하면 다크모드 대응은 물론, 협업 시에도 큰 도움이 됩니다.

반응형

+ Recent posts