Skip to content

[Feature] 카테고리 관리 화면에서, 내가 만든 목표와 그룹에 속한 목표를 분리#91

Merged
dioo1461 merged 6 commits intodevfrom
feature/category-management-separate-scopes
Feb 6, 2026
Merged

[Feature] 카테고리 관리 화면에서, 내가 만든 목표와 그룹에 속한 목표를 분리#91
dioo1461 merged 6 commits intodevfrom
feature/category-management-separate-scopes

Conversation

@dioo1461
Copy link
Collaborator

@dioo1461 dioo1461 commented Feb 6, 2026

  • close #

✨ 구현 기능 명세

  • 내가 만든 목표와 그룹에 속한 목표를 분리했습니다.
  • 그룹에 속한 목표는 해당 화면에서 수정/삭제 불가합니다.
image image

✅ PR Point

😭 어려웠던 점

@dioo1461 dioo1461 requested a review from Copilot February 6, 2026 12:42
@dioo1461 dioo1461 self-assigned this Feb 6, 2026
@dioo1461 dioo1461 added the ✨ feature 새로운 기능 구현 label Feb 6, 2026
@gemini-code-assist
Copy link

Summary of Changes

Hello @dioo1461, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 카테고리 관리 화면의 사용자 경험을 개선하기 위해 목표를 '내가 만든 목표'와 '그룹에 속한 목표'로 시각적으로 분리하는 기능을 구현합니다. 이를 통해 사용자는 자신의 목표와 그룹 목표를 쉽게 구분할 수 있으며, 그룹 목표에 대한 불필요한 수정/삭제 작업을 방지합니다. 또한, 그룹 색상 관리 로직을 최적화하여 애플리케이션 전반의 성능과 일관성을 향상시켰습니다.

Highlights

  • 카테고리 관리 화면 목표 분리: 카테고리 관리 화면에서 사용자가 직접 생성한 목표와 그룹에 속한 목표를 명확하게 분리하여 표시합니다. 그룹 목표는 해당 화면에서 수정하거나 삭제할 수 없습니다.
  • 그룹 색상 키 관리 로직 개선: 개별 그룹의 색상 키를 관리하던 useGroupColorKey 훅을 useGroupColorKeyMap으로 리팩토링하여 모든 그룹의 색상 키를 한 번에 가져와 Map 형태로 관리하도록 변경했습니다. 이는 여러 그룹의 색상 정보를 필요로 하는 곳에서 효율성을 높입니다.
  • UI 컴포넌트 개선 및 신규 추가: 그룹에 속한 목표를 표시하기 위한 GroupCategoryItem 컴포넌트를 새로 추가하고, 기존 CategoryItem 컴포넌트의 스타일을 업데이트했습니다. IconButton 컴포넌트의 style prop을 containerStyle로 변경하여 명확성을 높였습니다.
  • 번역 및 스타일 조정: 카테고리 관리 화면의 텍스트 번역을 업데이트하고, UI 요소 간의 간격 및 버튼 스타일을 조정하여 사용자 경험을 개선했습니다.
Changelog
  • .gitignore
    • 빌드 캐시 파일인 tsconfig.tsbuildinfo.gitignore에 추가하여 버전 관리에서 제외했습니다.
  • src/components/Button/Icon/index.tsx
    • IconButtonProps 인터페이스에서 style prop의 이름을 containerStyle로 변경하고, 컴포넌트 내부에서도 이를 반영하여 스타일 속성의 의미를 명확히 했습니다.
  • src/features/category/hooks/useCategoryColor.ts
    • useGroupColorKey 대신 useGroupColorKeyMap 훅을 사용하여 그룹 색상 키를 가져오도록 변경했습니다.
  • src/features/category/ui/InlineCategoryItem/index.style.ts
    • 컨테이너의 gap 스타일을 theme.spacing[100]에서 theme.spacing[200]으로 조정했습니다.
  • src/features/groups/hooks/useGroupColorKey.ts
    • useGroupColorKey 훅을 useGroupColorKeyMap으로 리팩토링하여 모든 그룹 ID에 대한 색상 키를 useQueries를 통해 일괄적으로 가져오고 Map 형태로 반환하도록 변경했습니다.
    • updateColorKey 함수에 groupId 인자를 추가하여 특정 그룹의 색상 키를 업데이트할 수 있도록 했습니다.
  • src/hooks/useCategoryData.ts
    • 카테고리 목록을 '내가 만든 목표'와 '그룹에 속한 목표'로 분리하는 useSplitCategoriesByGroup 훅을 새로 추가했습니다.
    • useGroupsQueryuseGroupColorKeyMap 훅을 임포트하여 그룹 관련 데이터를 활용할 수 있도록 했습니다.
  • src/locales/ko/manageCategory.json
    • my-goals 번역을 '나의 목표'에서 '내가 만든 목표'로 변경했습니다.
    • group-goals 번역 키를 '그룹에 속한 목표'로 새로 추가했습니다.
  • src/navigation/BottomTabNavigator/BottomNavHeader.tsx
    • Button.Icon 컴포넌트의 style prop을 containerStyle로 변경했습니다.
  • src/screens/bottomTab/GroupsScreen/components/JoinedGroupPanel/index.tsx
    • useGroupColorKey 대신 useGroupColorKeyMap 훅을 사용하여 그룹 색상 키를 가져오도록 변경했습니다.
  • src/screens/category/ManageCategoryScreen/components/CategoryItem/index.style.ts
    • 컨테이너의 content gap 스타일을 theme.spacing[100]에서 theme.spacing[200]으로 조정했습니다.
    • 그룹 색상 표시를 위한 groupColorIndicator와 아이콘 버튼 스타일을 위한 iconContainer를 추가했습니다.
  • src/screens/category/ManageCategoryScreen/components/CategoryItem/index.tsx
    • 카테고리 아이템에 그룹 색상 표시를 위한 groupColorIndicator를 추가했습니다.
    • 수정 및 삭제 아이콘 버튼에 색상 및 테두리 스타일을 적용했습니다.
  • src/screens/category/ManageCategoryScreen/components/GroupCategoryItem/index.style.ts
    • 그룹에 속한 목표 아이템의 스타일을 정의하는 createStyles 함수를 포함한 새 스타일 파일을 추가했습니다.
  • src/screens/category/ManageCategoryScreen/components/GroupCategoryItem/index.tsx
    • 그룹에 속한 목표를 표시하는 GroupCategoryItem 컴포넌트를 새로 추가했습니다. 이 컴포넌트는 수정/삭제 버튼을 포함하지 않습니다.
  • src/screens/category/ManageCategoryScreen/index.style.ts
    • toggleButtonContainerjustifyContent 스타일을 space-between에서 space-around로 변경했습니다.
  • src/screens/category/ManageCategoryScreen/index.tsx
    • useSplitCategoriesByGroup 훅을 사용하여 카테고리를 '내가 만든 목표'와 '그룹에 속한 목표'로 분리했습니다.
    • 분리된 목표 목록을 각각 CategoryItemGroupCategoryItem 컴포넌트를 사용하여 렌더링하도록 UI 로직을 업데이트했습니다.
  • src/screens/group/GroupChatScreen/components/MessageInput/index.tsx
    • Button.Icon 컴포넌트의 style prop을 containerStyle로 변경했습니다.
  • src/screens/group/groupDetail/GroupDetailScreen/index.tsx
    • useGroupColorKey 대신 useGroupColorKeyMap 훅을 사용하여 그룹 색상 키를 가져오도록 변경했습니다.
  • src/screens/group/groupDetail/components/GroupInfoPanel/DashBoardButtonGroup/index.tsx
    • useGroupColorKey 대신 useGroupColorKeyMap 훅을 사용하고, updateColorKey 호출 시 groupId를 인자로 전달하도록 변경했습니다.
  • src/store/studySlice.ts
    • GetGroupsResponse 타입을 임포트했습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

이번 PR은 카테고리 관리 화면에서 사용자가 직접 생성한 목표와 그룹에 속한 목표를 분리하는 기능을 성공적으로 구현했습니다. 특히 useGroupColorKeyuseGroupColorKeyMap으로 리팩토링하여 모든 그룹 색상을 한 번에 가져오도록 한 것은 좋은 성능 개선입니다. 새로운 컴포넌트와 훅 또한 새로운 기능을 지원하기 위해 잘 구조화되었습니다.

다만 몇 가지 개선점을 발견했습니다. useSplitCategoriesByGroup 훅에 그룹 카테고리가 일시적으로 UI에서 사라질 수 있는 중요한 버그가 있습니다. 또한 새로운 GroupCategoryItem 컴포넌트에 불필요한 코드가 남아있고, CategoryItem 컴포넌트에는 잘못된 UI 요소가 포함된 것으로 보입니다. 그 외에도 가독성 향상을 위해 코드를 단순화할 수 있는 몇 군데를 발견했습니다. 자세한 내용은 아래 리뷰 코멘트를 참고해주세요.

Comment on lines 59 to 62
if (belongingGroup) {
const groupColorKey = belongingGroup ? colorKeyMap.get(belongingGroup.groupId) : null;
if (!groupColorKey) return acc;
acc.groupCategories.push({ ...category, groupColorKey: groupColorKey });

Choose a reason for hiding this comment

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

high

if (belongingGroup) 블록 내에 있어 belongingGroup이 항상 참이므로 삼항 연산자는 불필요합니다. 코드를 단순화할 수 있습니다.

그러나 더 중요한 문제가 있습니다. groupColorKey를 찾지 못하면(예: 데이터 로딩 중) 해당 카테고리가 '내 목표'와 '그룹 목표' 목록 양쪽에서 모두 누락되어 UI에서 일시적으로 사라지게 됩니다. 이는 사용자 경험에 좋지 않은 영향을 줍니다. useGroupColorKeyMapisReady 플래그를 사용하여 색상 데이터가 준비되었을 때만 이 로직을 실행하도록 하여 이 문제를 해결하는 것을 고려해 보세요.

        const groupColorKey = colorKeyMap.get(belongingGroup.groupId);
        if (!groupColorKey) return acc;
        acc.groupCategories.push({ ...category, groupColorKey });

return (
<View style={styles.container}>
<View style={styles.content}>
<View style={styles.groupColorIndicator} />

Choose a reason for hiding this comment

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

medium

groupColorIndicator 뷰는 '내가 만든 목표'를 표시하는 CategoryItem에 잘못 추가된 것으로 보입니다. '내가 만든 목표'는 특정 그룹에 속하지 않으므로 그룹 색상 표시가 필요 없습니다. 이 뷰와 관련 스타일(CategoryItem/index.style.tsgroupColorIndicator)을 제거하는 것이 좋겠습니다.

Comment on lines 30 to 58
const navigation = useStackNavigation();
const { mutateAsync: deleteCategoryMutate } = useDeleteCategoryMutation();
const styles = createStyles({ groupColorKey });

const onEditPress = () => {
navigation.navigate('EditCategory', {
// TODO: id가 null일 때 처리 로직 추가
categoryId: id,
temporaryName: null,
});
};

// TODO: 수정, 삭제 연속으로 누르면 둘 다 반응하는 문제 해결하기
const onDeletePress = () => {
dialogService.add({
content: (
<CategoryDeletionAlert
onConfirm={async () => {
// TODO: id가 null일 때 처리 로직 추가
await deleteCategoryMutate(id!);
}}
/>
),
options: {
cancelable: false,
},
});
};

Choose a reason for hiding this comment

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

medium

onEditPress, onDeletePress 함수와 useDeleteCategoryMutation 훅이 정의되어 있지만, 이 컴포넌트에서는 그룹 카테고리를 수정/삭제하는 기능이 없어 실제로는 사용되지 않습니다. 불필요한 코드이므로 제거하여 컴포넌트를 더 명확하게 만드는 것이 좋겠습니다. 관련 import 구문(useStackNavigation, useDeleteCategoryMutation, dialogService, CategoryDeletionAlert)도 함께 정리해주세요.

Comment on lines 21 to 23
const groupColor = colorKeyMap.get(params.groupId)
? GROUP_COLORS[colorKeyMap.get(params.groupId)!]
: null;

Choose a reason for hiding this comment

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

medium

groupColor를 가져오는 로직이 다소 복잡하고, colorKeyMap.get()을 두 번 호출하고 있습니다. 가독성을 높이고 불필요한 중복 조회를 피하기 위해 코드를 단순화하는 것이 좋겠습니다.

  const colorKey = colorKeyMap.get(params.groupId);
  const groupColor = colorKey ? GROUP_COLORS[colorKey] : null;

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

},
containerStyle,
]}
>
<Icon style={iconStyle} {...props} />

P2 Badge Preserve container positioning for legacy Button.Icon style

The new containerStyle prop means any callers still passing style now lose container positioning. For example, PanelHeader supplies BUTTON_ICON_STYLE with style used to absolutely position the button; after this change, that style is forwarded to the SVG via {...props} and no longer affects the touchable container. This causes the header icon to render in the wrong place whenever a caller hasn’t been migrated to containerStyle. Consider mapping legacy style to containerStyle (or updating remaining callers like PanelHeader) to avoid the layout regression.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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 a feature that separates personal goals from group-owned goals in the category management screen. The main changes include refactoring the group color key management system from individual queries to a centralized map-based approach, and creating a distinct UI component for displaying group categories that cannot be edited or deleted.

Changes:

  • Refactored useGroupColorKey to useGroupColorKeyMap for batch loading group colors across all components
  • Added useSplitCategoriesByGroup hook to separate categories into personal and group-owned
  • Created GroupCategoryItem component for read-only display of group goals
  • Renamed Button.Icon prop from style to containerStyle for API clarity
  • Updated Korean localization to distinguish between "내가 만든 목표" (goals I created) and "그룹에 속한 목표" (goals in groups)

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/store/studySlice.ts Added unused import for GetGroupsResponse (should be removed)
src/screens/group/groupDetail/components/GroupInfoPanel/DashBoardButtonGroup/index.tsx Updated to use useGroupColorKeyMap with groupId parameter
src/screens/group/groupDetail/GroupDetailScreen/index.tsx Migrated from useGroupColorKey to useGroupColorKeyMap pattern
src/screens/group/GroupChatScreen/components/MessageInput/index.tsx Updated Button.Icon to use containerStyle prop
src/screens/category/ManageCategoryScreen/index.tsx Added logic to split and display categories by ownership type
src/screens/category/ManageCategoryScreen/index.style.ts Changed toggle button layout from space-between to space-around
src/screens/category/ManageCategoryScreen/components/GroupCategoryItem/index.tsx New component for displaying group categories (contains unused edit/delete code)
src/screens/category/ManageCategoryScreen/components/GroupCategoryItem/index.style.ts Styling for group category items with dynamic group color
src/screens/category/ManageCategoryScreen/components/CategoryItem/index.tsx Added color indicator and updated button styling
src/screens/category/ManageCategoryScreen/components/CategoryItem/index.style.ts Added styles for color indicator and icon containers
src/screens/bottomTab/GroupsScreen/components/JoinedGroupPanel/index.tsx Migrated to useGroupColorKeyMap pattern
src/navigation/BottomTabNavigator/BottomNavHeader.tsx Updated Button.Icon to use containerStyle prop
src/locales/ko/manageCategory.json Added translations for goal type separation
src/hooks/useCategoryData.ts Added useSplitCategoriesByGroup hook with redundant null check
src/features/groups/hooks/useGroupColorKey.ts Refactored from single-group to map-based batch loading
src/features/category/ui/InlineCategoryItem/index.style.ts Updated spacing from 100 to 200
src/features/category/hooks/useCategoryColor.ts Migrated to useGroupColorKeyMap pattern
src/components/Button/Icon/index.tsx Renamed style prop to containerStyle for clarity
.gitignore Added tsconfig.tsbuildinfo to ignored files

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


import type { Category } from '@/features/category/model';
import type { DAY_MASK_BY_UTC_CODE } from '@/features/category/model/types';
import { GetGroupsResponse } from '@/features/groups/model/groups';
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Unused import: GetGroupsResponse is imported but never used in this file. Consider removing the import to keep the code clean.

Suggested change
import { GetGroupsResponse } from '@/features/groups/model/groups';

Copilot uses AI. Check for mistakes.
group.categoryIds.includes(category.id),
);
if (belongingGroup) {
const groupColorKey = belongingGroup ? colorKeyMap.get(belongingGroup.groupId) : null;
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Redundant null check: belongingGroup is already checked on line 59, so the ternary operator belongingGroup ? colorKeyMap.get(belongingGroup.groupId) : null is unnecessary. Since we're inside the if (belongingGroup) block, we can directly use colorKeyMap.get(belongingGroup.groupId) without the additional check.

Suggested change
const groupColorKey = belongingGroup ? colorKeyMap.get(belongingGroup.groupId) : null;
const groupColorKey = colorKeyMap.get(belongingGroup.groupId);

Copilot uses AI. Check for mistakes.
Comment on lines 5 to 57
import { useDeleteCategoryMutation } from '@/features/category/api/mutations';
import type { CategoryTag } from '@/features/category/model';
import { useStackNavigation } from '@/hooks';
import { dialogService } from '@/utils/dialog';
import type { Time } from '@/utils/Time';

import CategoryDeletionAlert from '../CategoryDeletionAlert';
import {
createStyles,
GOAL_NAME_TEXT_STYLE,
GOAL_TIME_TEXT_STYLE,
TAG_CONTAINER_STYLE,
TAG_TEXT_STYLE,
} from './index.style';

interface CategoryItemProps {
id: number | null;
groupColorKey: GroupColorKey;
temporaryName: string | null;
title: string;
goal: Time | null;
tags?: CategoryTag[];
}

const GroupCategoryItem = ({ id, groupColorKey, title, goal, tags = [] }: CategoryItemProps) => {
const navigation = useStackNavigation();
const { mutateAsync: deleteCategoryMutate } = useDeleteCategoryMutation();
const styles = createStyles({ groupColorKey });

const onEditPress = () => {
navigation.navigate('EditCategory', {
// TODO: id가 null일 때 처리 로직 추가
categoryId: id,
temporaryName: null,
});
};

// TODO: 수정, 삭제 연속으로 누르면 둘 다 반응하는 문제 해결하기
const onDeletePress = () => {
dialogService.add({
content: (
<CategoryDeletionAlert
onConfirm={async () => {
// TODO: id가 null일 때 처리 로직 추가
await deleteCategoryMutate(id!);
}}
/>
),
options: {
cancelable: false,
},
});
};
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Dead code: The GroupCategoryItem component defines onEditPress and onDeletePress handlers, and imports useStackNavigation, useDeleteCategoryMutation, dialogService, and CategoryDeletionAlert, but none of these are actually used in the component's render output. According to the PR description, group goals should not be editable/deletable from this screen. These unused imports and handlers should be removed to keep the code clean and avoid confusion.

Copilot uses AI. Check for mistakes.

import type { TextProps } from '@/components/Text';
import type { GroupColorKey } from '@/constants/groupColors';
import { GROUP_COLORS } from '@/constants/groupColors';
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Unused import GROUP_COLORS.

Suggested change
import { GROUP_COLORS } from '@/constants/groupColors';

Copilot uses AI. Check for mistakes.
Comment on lines 34 to 41
const onEditPress = () => {
navigation.navigate('EditCategory', {
// TODO: id가 null일 때 처리 로직 추가
categoryId: id,
temporaryName: null,
});
};

Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Unused variable onEditPress.

Suggested change
const onEditPress = () => {
navigation.navigate('EditCategory', {
// TODO: id가 null일 때 처리 로직 추가
categoryId: id,
temporaryName: null,
});
};

Copilot uses AI. Check for mistakes.
};

// TODO: 수정, 삭제 연속으로 누르면 둘 다 반응하는 문제 해결하기
const onDeletePress = () => {
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Unused variable onDeletePress.

Copilot uses AI. Check for mistakes.
@dioo1461 dioo1461 merged commit d40d0a4 into dev Feb 6, 2026
1 check passed
@dioo1461 dioo1461 deleted the feature/category-management-separate-scopes branch February 6, 2026 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feature 새로운 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants