Skip to content

Conversation

@mark77234
Copy link
Collaborator

작업내용

  • 프로젝트 세팅
  • 달력 페이지
  • 오늘의 운세 페이지
  • 가위질 페이지 넘기기
  • lint 문법 검사 CI 추가

설명

  • 홈 화면을 두 단락으로 나눠서 첫 화면은 달력 페이지 한번 아래로 스크롤하면 오늘의 운세 페이지로 스크롤을 고정시켰습니다
  • 가위 아이콘 드래그로 다음 일력으로 넘어갈 수 있습니다

스크린샷

simulator_screenshot_63AD8BB0-B297-4903-94F4-2CE1674FCE29 simulator_screenshot_F39FC3A9-F0EA-4A04-8640-97F721FD9BD1

@mark77234 mark77234 self-assigned this Dec 17, 2025
Copilot AI review requested due to automatic review settings December 17, 2025 13:07
@mark77234 mark77234 added the refactor 코드 리팩토링 작업 label Dec 17, 2025
@mark77234 mark77234 linked an issue Dec 17, 2025 that may be closed by this pull request
2 tasks
@mark77234 mark77234 requested a review from qlqlrh December 17, 2025 13:14
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements the initial project setup for a daily fortune calendar app ("하루 일력") with an interactive calendar interface, fortune display, and unique paper-tearing navigation using a draggable scissors icon.

Key Changes

  • Refactored from tab-based navigation to a single-screen fortune calendar app with onboarding flow
  • Integrated NativeWind (Tailwind CSS v3) for styling with proper configuration
  • Implemented interactive calendar page with animated scissors-drag navigation and dual-page scroll (calendar + fortune details)

Reviewed changes

Copilot reviewed 19 out of 20 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
frontend/package.json Added AsyncStorage and vector icons dependencies; downgraded Tailwind to v3.4.19 for NativeWind compatibility
frontend/tailwind.config.js Added NativeWind preset for React Native Tailwind support
frontend/metro.config.js Integrated NativeWind with global CSS configuration
frontend/eslint.config.js Added runtime and Node globals configuration for config files
frontend/src/types/fortune.ts Defined TypeScript types for user settings and fortune data structure
frontend/src/services/geminiService.ts Created placeholder service for fetching daily fortune data
frontend/src/constants/config.ts Added safe environment variable access for React Native
frontend/src/components/UserInfoForm.tsx Implemented user information form for initial setup
frontend/src/components/SettingsSheet.tsx Created settings modal for updating user preferences
frontend/src/components/Onboarding.tsx Added onboarding screen with app feature guide
frontend/src/components/CalendarPage.tsx Built main calendar page with animated scissors navigation and fortune display
frontend/src/components/Button.tsx Refactored to use Pressable instead of View for proper touch handling
frontend/app/index.tsx Implemented main app logic with state management and AsyncStorage persistence
frontend/app/_layout.tsx Updated layout to single-screen navigation structure
frontend/.github/workflows/lint-test.yml Added CI workflow for linting on push and pull requests
Files not reviewed (1)
  • frontend/pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (2)

frontend/app/index.tsx:2

  • Unused import Pressable.
import { ActivityIndicator, Alert, Pressable, View } from 'react-native';

frontend/app/index.tsx:13

  • Unused import Feather.
import { Feather } from '@expo/vector-icons';

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +57 to +62
await AsyncStorage.setItem(HAS_ONBOARDED_KEY, 'true');
};

const persistUserSettings = async (settings: UserSettings) => {
setUserSettings(settings);
await AsyncStorage.setItem(USER_SETTINGS_KEY, JSON.stringify(settings));
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error handling for AsyncStorage.setItem is missing. If persisting the onboarding state fails, the UI state and stored state will be out of sync. Consider wrapping the AsyncStorage call in a try-catch block and handling errors appropriately.

Suggested change
await AsyncStorage.setItem(HAS_ONBOARDED_KEY, 'true');
};
const persistUserSettings = async (settings: UserSettings) => {
setUserSettings(settings);
await AsyncStorage.setItem(USER_SETTINGS_KEY, JSON.stringify(settings));
try {
await AsyncStorage.setItem(HAS_ONBOARDED_KEY, 'true');
} catch (error) {
console.warn('Failed to persist onboarding state', error);
Alert.alert(
'저장에 실패했어요',
'온보딩 상태를 저장하지 못했습니다. 앱을 다시 시작하면 온보딩을 다시 진행해야 할 수 있어요.',
);
}
};
const persistUserSettings = async (settings: UserSettings) => {
setUserSettings(settings);
try {
await AsyncStorage.setItem(USER_SETTINGS_KEY, JSON.stringify(settings));
} catch (error) {
console.warn('Failed to persist user settings', error);
Alert.alert(
'설정을 저장하지 못했어요',
'다시 시도해 주세요. 문제가 계속되면 앱을 재시작해 보세요.',
);
}

Copilot uses AI. Check for mistakes.

<View
className="z-10 bg-white px-4"
style={{ paddingTop: insets.top + 8 }} // ✅ 노치 / 다이나믹 아일랜드 대응
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is in Korean and includes emojis. While this may be intentional for a Korean-language project, it's inconsistent with the inline comment style in other parts of the codebase. Consider using a consistent comment style throughout the project.

Suggested change
style={{ paddingTop: insets.top + 8 }} // ✅ 노치 / 다이나믹 아일랜드 대응
style={{ paddingTop: insets.top + 8 }} // Add extra top padding for notch / dynamic island

Copilot uses AI. Check for mistakes.
Comment on lines +81 to +87
<TextInput
value={form.birthdate}
onChangeText={(text) => update({ birthdate: text })}
placeholder="YYYY-MM-DD"
placeholderTextColor="#9ca3af"
className="rounded-xl bg-gray-100 px-3.5 py-3.5 text-base text-gray-900"
/>
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The birthdate input accepts free-form text without validation. Users could enter invalid dates like "2025-13-45" or malformed strings. Consider adding input validation, using a date picker component, or at least validating the format before allowing form submission.

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +61
<Row label="생년월일" divider>
<TextInput
value={form.birthdate}
onChangeText={(text) => update({ birthdate: text })}
placeholder="YYYY-MM-DD"
placeholderTextColor="#d1d5db"
className="min-w-[120] text-right text-[15px] text-gray-900"
/>
</Row>
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The birthdate input in settings accepts free-form text without validation. Users could enter invalid dates. This is the same issue as in UserInfoForm - consider adding input validation or using a date picker component.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
type EnvRecord = Record<string, string | undefined>;

const env: EnvRecord =
typeof process !== 'undefined' && process.env ? (process.env as EnvRecord) : {};
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The environment variable check typeof process !== 'undefined' && process.env may not be sufficient in all React Native environments. Consider using a more robust environment configuration library like react-native-config or expo-constants for better cross-platform support.

Suggested change
type EnvRecord = Record<string, string | undefined>;
const env: EnvRecord =
typeof process !== 'undefined' && process.env ? (process.env as EnvRecord) : {};
import Config from 'react-native-config';
type EnvRecord = Record<string, string | undefined>;
const env: EnvRecord =
(Config as unknown as EnvRecord) ||
(typeof process !== 'undefined' && process.env ? (process.env as EnvRecord) : {});

Copilot uses AI. Check for mistakes.
>
{/* 상단 커스텀 헤더 */}
<View className="flex-row items-center justify-between mb-2">
{/* 왼쪽 더미 공간 (에러 방지) */}
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Korean comment should be in English or use a consistent commenting style across the codebase. The "에러 방지" (error prevention) comment isn't descriptive enough about what error it's preventing.

Suggested change
{/* 왼쪽 더미 공간 (에러 방지) */}
{/* Left spacer to keep header content aligned and prevent layout overlap issues */}

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +100
<TextInput
value={form.notificationTime}
onChangeText={(text) => update({ notificationTime: text })}
placeholder="08:00"
placeholderTextColor="#9ca3af"
className="min-w-[80] text-right text-base text-gray-900"
/>
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notification time input accepts free-form text without validation. Users could enter invalid time formats. Consider adding input validation to ensure the time is in the correct HH:mm format, or use a time picker component.

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +84
<TextInput
value={form.notificationTime}
onChangeText={(text) => update({ notificationTime: text })}
placeholder="08:00"
placeholderTextColor="#d1d5db"
className="min-w-[120] text-right text-[15px] text-gray-900"
/>
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notification time input accepts free-form text without validation. Users could enter invalid time formats. Consider adding input validation to ensure the time is in the correct HH:mm format, or use a time picker component.

Copilot uses AI. Check for mistakes.
cutProgress.setValue(0);
scissorX.setValue(0);
scissorStartX.current = 0;
setTimeout(() => setIsTearing(false), 100);
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using setTimeout without cleanup could lead to memory leaks or state updates on unmounted components. If the component unmounts before the 100ms timeout completes, the setIsTearing(false) call will attempt to update state on an unmounted component. Consider using a ref to track mount status or cleanup the timeout in a useEffect return function.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,151 @@
import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react';
import { ActivityIndicator, Alert, Pressable, View } from 'react-native';
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unused import Pressable should be removed as it's not used anywhere in this file.

Suggested change
import { ActivityIndicator, Alert, Pressable, View } from 'react-native';
import { ActivityIndicator, Alert, View } from 'react-native';

Copilot uses AI. Check for mistakes.
@qlqlrh
Copy link
Collaborator

qlqlrh commented Dec 17, 2025

수고하셨습니다!! 생각한 그대로의 UI예요 최고최고 ~~ 🫵🏻👍🏻 !!
image

@mark77234 mark77234 merged commit 3d297ac into dev Dec 17, 2025
1 check passed
@mark77234 mark77234 deleted the refactor/1 branch December 17, 2025 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor 코드 리팩토링 작업

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor - 프로젝트 구조 세팅

3 participants