반응형

🚀 VSCode에서 Vim 사용 시 키 연속 입력이 안 되는 문제 해결 (macOS 기준)

h, j, k, l 키를 누르면서 방향 이동하는 게 Vim의 핵심인데, VSCode에서 Vim 확장 플러그인을 사용할 때 이 키들이 연속으로 입력되지 않는 문제가 발생할 수 있습니다.

이 글에서는 그 원인과 해결 방법을 자세히 정리해봅니다.


🔍 문제 현상

  • h, j, k, l 키를 길게 눌러도 한 글자만 입력됨
  • Vim 모드에서 커서가 연속으로 움직이지 않음
  • 일반적인 키보드 타이핑은 이상 없음

🧠 원인: macOS의 Press and Hold 기능

macOS는 기본적으로 키를 길게 누르면 "특수 문자 선택 팝업"이 뜨도록 설정돼 있습니다. 이 기능이 Vim에서는 방해가 되죠.
VSCode에서도 이 기능이 활성화되어 있다면, 키를 꾹 눌러도 연속 입력이 되지 않습니다.


✅ 해결 방법

1. VSCode에 한해서 Press and Hold 끄기

터미널을 열고 아래 명령어를 입력하세요:

defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false

그리고 VSCode를 완전히 종료한 후 다시 실행합니다. - 창닫기만 할 시 적용 안됨.
이제 h, j, k, l 키를 꾹 눌렀을 때 연속 입력이 정상 동작합니다!


2. 시스템 전체에 적용하고 싶다면 (선택)

다른 앱에서도 연속 입력이 안 되는 경우, 아래 명령어로 전역 설정을 바꿀 수 있어요:

defaults write -g ApplePressAndHoldEnabled -bool false

※ 주의: 이 설정은 시스템 전체에 영향을 미치므로, 일부 앱에서는 부작용이 생길 수도 있습니다.


3. 키 반복 속도 빠르게 조절하기 (추가 팁)

더 쾌적한 Vim 사용을 위해 키보드 입력 속도도 아래처럼 조절해보세요.

  • 시스템 설정 > 키보드에서
    • 키 반복 속도: 빠르게
    • 반복 시작 지연 시간: 짧게

이렇게 하면 j, k로 커서 이동할 때 더 빠르고 자연스럽습니다.


✨ 마무리

VSCode에서 Vim을 제대로 사용하려면 연속 키 입력 설정은 필수입니다.
이 문제는 macOS에서 자주 발생하는 이슈지만, 간단한 설정으로 쉽게 해결할 수 있어요.

혹시 위 방법으로도 해결되지 않거나 QMK 키보드 설정과 관련된 문제가 있다면 댓글로 남겨주세요! 😄

반응형
반응형

앱에서 고유 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가 필요할 때 매우 유용
반응형
반응형

부분합이 특정 값을 넘는 가장 짧은 연속된 부분 배열의 길이를 구하는 문제입니다.
단순히 합만 구하는 것이 아니라, 길이까지 고려해야 하기에 효율적인 알고리즘이 필요합니다.
이 글에서는 Brute-force 접근 → 슬라이딩 윈도우 최적화 과정을 거쳐,
Dart로 완성도 높은 코드를 작성하고 이해하는 과정을 함께합니다.


📘 문제 설명

양의 정수 배열 nums와 목표값 target이 주어질 때,
합이 target 이상이 되는 연속된 부분 배열(subarray) 중에서
가장 짧은 길이를 반환하라.
없으면 0을 반환한다.


❌ Brute-force 접근 (비효율)

int minLength = nums.length + 1;

for (int i = 0; i < nums.length; i++) {
  int sum = 0;
  for (int j = i; j < nums.length; j++) {
    sum += nums[j];
    if (sum >= target) {
      minLength = min(minLength, j - i + 1);
      break;
    }
  }
}
  • 시간복잡도: O(n²) → 큰 입력에서는 시간 초과 가능성 있음
  • 매 i마다 전체 탐색 → 비효율적

✅ 최적화: 슬라이딩 윈도우

슬라이딩 윈도우는 합이 기준 이상일 때 길이를 최소화하는 문제에 딱 적합합니다.


💻 Dart 코드

class Solution {
  int minSubArrayLen(int target, List<int> nums) {
    int n = nums.length;
    int left = 0;
    int sum = 0;
    int minLength = n + 1;

    for (int right = 0; right < n; right++) {
      sum += nums[right];

      while (sum >= target) {
        minLength = minLength < (right - left + 1) ? minLength : (right - left + 1);
        sum -= nums[left];
        left++;
      }
    }

    return minLength == n + 1 ? 0 : minLength;
  }
}

 


🧠 코드 풀이

구간 설명

sum += nums[right] 윈도우 오른쪽 확장하며 합 누적
while (sum >= target) 조건 만족 시 길이 갱신 시도
minLength = ... 현재 길이가 기존 최소보다 짧으면 갱신
sum -= nums[left]; left++ 왼쪽을 줄이며 더 짧은 조합 시도
종료 후 리턴 조건 만족한 적 없으면 0, 있으면 최소 길이

🌊 슬라이딩 윈도우 흐름 예시

입력: target = 7, nums = [2,3,1,2,4,3]

step left right sum 조건 만족 윈도우 길이 minLength
1 0 0 2 -
2 0 1 5 -
3 0 2 6 -
4 0 3 8 4 4
5 1 3 6 - 4
6 1 4 10 4 4 → 3
7 2 4 7 3 3 → 2
8 3 4 4 - 2
9 3 5 7 3 유지
10 4 5 3 - 2

⏱ 시간 & 공간 복잡도

항목 복잡도 설명

시간 O(n) 각 포인터가 최대 한 번씩만 이동
공간 O(1) 추가 공간 없음

✨ 실전 팁 요약

  • “조건 만족하는 최소 길이” 문제는 무조건 슬라이딩 윈도우 먼저 떠올릴 것
  • 오른쪽으로 확장하면서 합을 만들고, 왼쪽을 줄여가며 최소 길이 갱신
  • 투 포인터 + 조건 기반 줄이기 전략은 여러 문제에 응용 가능

LeetCode, Dart 알고리즘, 슬라이딩 윈도우, 투 포인터, 부분합 문제

반응형
반응형

물통에 가장 많은 물을 담는 방법은?
LeetCode의 11번 문제 "Container With Most Water"는 그리디한 사고와 투 포인터 알고리즘을 적절히 활용할 수 있는 대표 문제입니다.

이 글에서는 Dart로 문제를 풀이하며,
왜 투 포인터가 최적인지,
어떻게 중복 계산 없이 최대값을 구하는지
명확히 설명합니다.


📘 문제 요약

  • height[i]는 위치 i에서 세운 수직선의 높이입니다.
  • 두 선을 골라, 그 사이를 x축으로 막아 생기는 컨테이너의 물 저장 용량을 구해야 합니다.
  • 조건: 컨테이너는 기울일 수 없습니다.

🚀 핵심 아이디어: 투 포인터 (Two Pointers)

❌ 브루트 포스 접근 (비효율)

int max = 0;
for (int i = 0; i < height.length; i++) {
  for (int j = i + 1; j < height.length; j++) {
    int area = (j - i) * min(height[i], height[j]);
    max = max > area ? max : area;
  }
}
  • 시간 복잡도: O(n²)
  • → 모든 경우를 비교 = 너무 느림!

✅ 최적화: 투 포인터 전략

import 'dart:math';

class Solution {
  int maxArea(List<int> height) {
    int left = 0, right = height.length - 1, answer = 0;

    while (left < right) {
      int width = right - left;
      int water = width * min(height[left], height[right]);
      answer = max(answer, water);

      if (height[left] < height[right]) {
        left++;  // 더 낮은 쪽을 안쪽으로 이동
      } else {
        right--;
      }
    }

    return answer;
  }
}

💡 왜 투 포인터인가?

  • 물의 양 = 너비 × 높이
  • 너비는 왼쪽/오른쪽 포인터 거리
  • 높이는 둘 중 더 낮은 벽이 기준
  • 더 높은 벽은 바꾸지 않고, 낮은 벽을 바꿔야 더 나은 결과 가능

→ 즉, 낮은 쪽을 안쪽으로 이동시키며 최댓값 갱신


🧪 예제 설명

입력: [1,8,6,2,5,4,8,3,7]
→ 최댓값은 49: height[1]=8, height[8]=7, 거리 7 → 7 × 7 = 49


⏱ 복잡도 분석

구분 복잡도

시간 O(n) — 포인터 한 번씩만 움직임
공간 O(1) — 추가 배열 없이 변수만 사용

✨ 실전 팁

  • 이 문제는 투 포인터 개념을 익히기에 아주 좋아요.
  • 리스트의 양 끝에서 출발하여, 내려오면서 갱신하는 방식은 다양한 문제에 응용 가능
  • 슬라이딩 윈도우 / 그리디 문제에서도 자주 등장하는 패턴입니다.

🏁 마무리

"Container With Most Water"는 단순해보이지만,
투 포인터의 본질적인 사고법을 배울 수 있는 문제입니다.
이 문제 하나만 확실히 이해해도, 이후 LeetCode 문제에서 유사 패턴을 빠르게 인식하게 됩니다.

반응형
반응형

📝 요약 설명 

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 포맷도 원해?

반응형
반응형

LeetCode 167: Two Sum II - Input Array Is Sorted

📌 문제 설명

주어진 정렬된 배열에서 두 숫자의 합주어진 target과 일치하는 인덱스를 반환하는 문제입니다.
배열의 인덱스는 1부터 시작하고, 합이 일치하는 두 숫자의 인덱스를 1-based로 반환해야 합니다.


✅ 풀이 아이디어: Two Pointers 알고리즘

이 문제는 Two Pointers 기법을 사용하여 해결할 수 있습니다.

  1. 배열이 정렬되어 있음을 이용해 left 포인터는 배열의 앞에서 시작하고, right 포인터는 배열의 끝에서 시작합니다.
  2. 두 포인터의 합이 target보다 작으면 left를 증가시키고, 합이 크면 right를 감소시킵니다.
  3. 두 포인터가 가리키는 값이 합이 target과 같다면 그 인덱스를 반환합니다.
  4. 이러한 방식으로 한 번 순회하여 문제를 해결할 수 있습니다. 이때 **시간 복잡도는 O(n)**입니다.

✅ 구현 코드 (TypeScript)

export function twoSum(numbers: number[], target: number): number[] {
  let left = 0;
  let right = numbers.length - 1;

  while (left < right) {
    const sum = numbers[left] + numbers[right];

    if (sum === target) {
      return [left + 1, right + 1]; // 1-based index
    }

    if (sum < target) {
      left++; // 합이 target보다 작으면 left 포인터를 증가
    } else {
      right--; // 합이 target보다 크면 right 포인터를 감소
    }
  }

  return [];
}

✅ 테스트 코드 (Jest)

describe("Medium 167: Two Sum II", () => {
  it("should return [1, 2] for [2, 7, 11, 15] and target 9", () => {
    expect(twoSum([2, 7, 11, 15], 9)).toEqual([1, 2]);
  });

  it("should return [1, 2] for [-1, 0] and target -1", () => {
    expect(twoSum([-1, 0], -1)).toEqual([1, 2]);
  });

  it("should return [1, 3] for [2, 3, 4] and target 6", () => {
    expect(twoSum([2, 3, 4], 6)).toEqual([1, 3]);
  });
});

✅ 시간 복잡도

  • 시간 복잡도: O(n)
    • 배열을 한 번만 순회하므로, 시간 복잡도는 배열의 길이에 비례하여 선형입니다.
  • 공간 복잡도: O(1)
    • 추가적인 공간을 사용하지 않으므로 공간 복잡도는 상수입니다.

✅ 마무리

이 문제를 풀면서 중간 난이도 문제도 이제는 한 번에 풀 수 있게 된 것 같다는 자신감이 생겼습니다.
Two Pointers 알고리즘을 통해 정렬된 배열에서 빠르게 답을 찾는 기법을 잘 활용한 문제였습니다. 이제는 이런 유형의 문제를 더 잘 풀 수 있을 것 같아요.

반응형
반응형

🔍 문제 상황

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가 최상위 엘리먼트에 있는가?" 를 꼭 확인하세요!

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

 

 

반응형
반응형

🔗 문제 링크: https://leetcode.com/problems/is-subsequence/


📌 문제 설명

두 문자열 s와 t가 주어졌을 때, 문자열 s가 t의 **부분 수열(subsequence)**인지 판별하는 문제입니다.

🔹 Subsequence란?

  • 원래 문자열에서 일부 문자를 삭제하여 얻을 수 있는 문자열
  • 문자의 상대적인 순서가 유지되어야 함
  • 연속될 필요는 없음

예시:

  • "abc" → "ahbgdc" 는 부분 수열 ✅
  • "axc" → "ahbgdc" 는 부분 수열 ❌ (x가 순서상 없음)

✅ 해결 아이디어 - Two Pointers

문자열 s의 각 문자가 문자열 t 안에서 순서를 유지한 채 등장하는지만 확인하면 됩니다.

🔹 전략

  • 두 포인터 i, j를 각각 t, s를 탐색하는 인덱스로 사용
  • t[i] === s[j]면 j를 증가시켜 s의 다음 문자로 이동
  • j === s.length에 도달하면 모든 문자가 매칭된 것이므로 true

✅ 구현 코드 (TypeScript)

export function isSubsequence(s: string, t: string): boolean {
  let j = 0;
  for (let i = 0; i < t.length && j < s.length; i++) {
    if (t[i] === s[j]) {
      j++;
    }
  }
  return j === s.length;
}

🧪 테스트 코드 (Jest)

describe("Easy 392: Is Subsequence", () => {
  it("returns true when s is a subsequence of t", () => {
    expect(isSubsequence("abc", "ahbgdc")).toBe(true);
  });

  it("returns false when s is not a subsequence of t", () => {
    expect(isSubsequence("axc", "ahbgdc")).toBe(false);
  });
});

📈 시간 및 공간 복잡도

항목 복잡도

시간 O(N) (t의 길이만큼)
공간 O(1) (추가 메모리 거의 없음)

✅ 마무리 정리

  • 핵심은 s의 각 문자가 t 안에서 순서를 유지하며 존재하는지를 확인하는 것
  • Two Pointers를 통해 한 번의 순회로 간단하게 해결 가능
반응형
반응형

⚙️ 시작: 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 가능은 하지만 비추천

 

반응형

+ Recent posts