🛠️ [03.25] 타임테이블 수학적 그리드 설계 & 설정 UI 컴포넌트화

2026. 4. 1. 17:01·react-native/rutina
반응형

오늘 개발한 '달력/스와이프' 파트를 제외하고, 이전에 뼈대를 잡았던 핵심 로직 3가지(10분 단위 그리드, 실시간 타임라인, 범용 설정 컴포넌트)


1. 수평 구조의 10분 단위 타임테이블 그리드 설계

위아래로 늘어지는 10분 단위 대신, 세로축은 1시간(행), 가로축을 6등분(10분 칸)으로 쪼개서 한눈에 들어오게 설계함. 수학적 퍼센트 수식을 써서 어떤 복잡한 시간대의 이벤트도 완벽히 비율 계산되어 그려지게 만듦.

// 행과 칸 데이터 세팅
const startHour = 0; const endHour = 24; const hourHeight = 60;
const hours = Array.from({ length: endHour - startHour }, (_, i) => startHour + i);
const columns = [0, 1, 2, 3, 4, 5]; // 1시간을 10분단위 6칸으로 쪼갬

// 데이터 렌더링 방식 (특정 시간 Row 기준)
{mockSchedule.map((event) => {
  const eventStart = event.startHour * 60 + event.startMinute;
  const eventEnd = eventStart + event.durationMinutes;

  const hourStart = hour * 60; // 현재 렌더링 중인 행의 시작 시간(분) // 예: 8시면 480
  const hourEnd = hourStart + 60; 

  // 이벤트가 현재 그리는 시간(hour)을 벗어나면 안그림!
  if (eventStart >= hourEnd || eventEnd <= hourStart) return null;

  // 겹치는(보이는) 부분만 퍼센트로 따내기 🌟 (핵심 수학 계산 로직)
  const overlapStart = Math.max(eventStart, hourStart);
  const overlapEnd = Math.min(eventEnd, hourEnd);

  const startOffsetMin = overlapStart - hourStart;
  const durationInHour = overlapEnd - overlapStart;

  // 컨테이너 width 대비 몇 % 위치에 얼마나 길게 그릴지 결정
  const leftPercent = (startOffsetMin / 60) * 100;
  const widthPercent = (durationInHour / 60) * 100;

  return (
    <View
      key={event.id}
      style={[
        styles.eventBlock,
        {
          left: `${leftPercent}%`,   // 정확한 시작점
          width: `${widthPercent}%`, // 정확한 10분단위 길이
          backgroundColor: '#F1F1FB'
        },
      ]}
    >
      <Text>{event.title}</Text>
    </View>
  );
})}

2. 1분 단위로 자동으로 흐르는 '현재 시간 표시선 (Live Time Line)'

타임 테이블을 뷰잉할 때 가장 직관적으로 '내 현재 위치'를 보여주기 위해 빨간 줄을 추가함.
앱이 마운트 될 때 한 번 자동으로 이동시켜 주는 로직(scrollTo)도 필수!

// 1분(60000ms) 단위로 현재 상태 갱신
const [currentTime, setCurrentTime] = useState(new Date());

useEffect(() => {
  // 앱 진입 시 현재 시간대 살짝 위(currentHour - 1)로 자연스럽게 스크롤
  const currentHour = currentTime.getHours();
  const yOffset = Math.max(0, (currentHour - startHour - 1) * hourHeight);
  setTimeout(() => {
    scrollViewRef.current?.scrollTo({ y: yOffset, animated: true });
  }, 100);

  // 시계 타이머 동작
  const interval = setInterval(() => setCurrentTime(new Date()), 60000);
  return () => clearInterval(interval);
}, []);

// 렌더링 로직 안에서 (현재 그리는 행(hour)이 진짜 지금 시간(getHours)일 때만 선을 그림!)
{hour === currentTime.getHours() && (
  <View
    style={[
      styles.currentTimeIndicator,  // 기본 색상 빨강(#FF3B30), absolute로 띄움
      // 현재 분(minute)을 퍼센트로 쪼개서 절댓값에 위치시킴! (예: 30분이면 정중앙)
      { left: `${(currentTime.getMinutes() / 60) * 100}%` },
    ]}
  />
)}

3. 재사용 가능한 iOS 스타일 '설정 화면 (Settings) 뷰 아이템'

설정 화면 UI를 직접 하드코딩하면 코드가 엄청 지저분해짐. 그래서 공통 프로퍼티(텍스트, 토글스위치, 화살표버튼 등)를 다 받아먹을 수 있는 만능 SettingItem 컴포넌트를 만들어버렸음.
나중에 앱 환경설정 탭 늘릴 때 이것만 복붙해서 옵션 넣으면 끝남!

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

// 여러 설정을 조합할 수 있는 통합 인터페이스
interface SettingItemProps {
  icon: string;
  title: string;
  type?: "link" | "switch" | "value";
  value?: boolean | string;
  onValueChange?: (val: boolean) => void;
  onPress?: () => void;
}

// 공용 컴포넌트 선언
function SettingItem({ icon, title, type = "link", value, onValueChange, onPress }: SettingItemProps) {
  return (
    <TouchableOpacity
      style={styles.settingItem}
      onPress={onPress}
      disabled={type === "switch"} // 스위치일 땐 터치 무시하고 스위치 이벤트만 잡음
    >
      <View style={styles.settingItemLeft}>
        <Text style={styles.settingItemIcon}>{icon}</Text>
        <Text style={styles.settingItemTitle}>{title}</Text>
      </View>

      {/* 우측 아이템 조건부 렌더링 🌟 */}
      {type === "switch" && (
        <Switch
          value={value as boolean}
          onValueChange={onValueChange}
          trackColor={{ false: "#E2E5EC", true: "#4A6296" }}
        />
      )}
      {type === "value" && (
        <Text style={styles.settingItemValue}>{value as string}</Text>
      )}
      {type === "link" && (
        <Text style={styles.settingItemChevron}>›</Text>
      )}
    </TouchableOpacity>
  );
}

// 실제 사용 예시 (화면에서 그룹 렌더링 시키기)
<View style={styles.sectionHeader}>
  <Text style={styles.sectionTitle}>일반 설정</Text>
</View>

<View style={styles.cardGroup}>
  <SettingItem
    icon="🔔"
    title="푸시 알림"
    type="switch"
    value={notificationsEnabled}
    onValueChange={setNotificationsEnabled}
  />
  <SettingItem
    icon="🌙"
    title="다크 모드"
    type="switch"
    value={darkModeEnabled}
    onValueChange={setDarkModeEnabled}
  />
</View>

 

TODO

  • 데이터 베이스가 제대로 구성되지 않아서 이후에 맞춰서 일정 시간 데이터에 따라서 구조를 수정할 필요가 있음
  • 시간이 없는 루틴은 따로 구별해서 표현해야함.
  • 기본적인 화면만 구성해서 추가적으로 필요한 기능이 생길 경우 추가해야함
반응형

'react-native > rutina' 카테고리의 다른 글

[React Native/Expo] 소셜 로그인 사용시 expo환경에서의 주의점(Oauth2.0)  (0) 2026.06.26
📅 [04.01] 타임테이블 동적 달력 연동 & 제스처 UI 구현 (메모용)  (0) 2026.04.01
프로젝트 라이브러리 정리(기록용)  (0) 2026.04.01
'react-native/rutina' 카테고리의 다른 글
  • [React Native/Expo] 소셜 로그인 사용시 expo환경에서의 주의점(Oauth2.0)
  • 📅 [04.01] 타임테이블 동적 달력 연동 & 제스처 UI 구현 (메모용)
  • 프로젝트 라이브러리 정리(기록용)
우왕차
우왕차
우왕차 님의 블로그 입니다.
    반응형
  • 우왕차
    MO:DEV
    우왕차
  • 전체
    오늘
    어제
    • 분류 전체보기 (74)
      • Flutter (55)
        • 캠프 (19)
        • 오류 해결 (6)
        • 독서록 앱 (4)
        • 한달 인턴 (3)
        • 성당 앱 (2)
        • Diary 앱 (2)
      • Swift (1)
        • 오류 해결 (0)
        • storyboard (0)
      • 기초 (3)
      • 오류 해결 (0)
      • 정보 (8)
      • react-native (4)
        • rutina (4)
      • AI (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    FCM
    오류
    pub.dev
    문제 해결
    해결완료
    화면전환
    AI
    React-native
    git
    상태관리
    Firebase
    SWIFT
    Flutter
    ios
    gorouter
    setState
    antigravity
    android
    react
    DART
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
우왕차
🛠️ [03.25] 타임테이블 수학적 그리드 설계 & 설정 UI 컴포넌트화
상단으로

티스토리툴바