Skip to content

Conversation

@areumH
Copy link
Collaborator

@areumH areumH commented Feb 22, 2025

💡 작업 내용

  • 밸런스게임 모바일 페이지 수정, 삭제 api 연결
  • 토스트 모달 모바일 스타일 추가
  • 게임 서브 태그 값을 여러 개로 수정
  • 비회원 여부를 로컬 스토리지 대신 리덕스로 확인하도록 수정

💡 자세한 설명

✅ validateBalanceGameForm

  • 서브 태그는 10글자 이하의 string 값 3개(쉼표로 구분)이기 때문에 이와 관련된 검증 함수를 추가하였습니다.
  • utils 폴더 내에 쉼표를 기준으로 배열을 리턴하는 함수와 배열 내 모든 요소의 길이가 10글자 이하인지 확인하는 함수를 선언해두었습니다!

✅ GameTagModal

  • 서브 태그 값 검증에 성공하지 못한 상태에서 생성 버튼을 눌렀을 때 모달이 사라지던 현상을 개선하기 위해 태그 값 검증을 태그 모달의 handleTagSubmit 함수 내에서 실행하도록 하였습니다.

✅ 밸런스 게임 수정 관련

  • 밸런스 게임 조회 페이지에서 수정 버튼을 클릭했을 때 밸런스 게임 생성 페이지로 이동되면서 state로 밸런스 게임 상세 조회 데이터를 넘겨줍니다. 해당 게임 데이터의 값이 게임 form의 기본 값이 됩니다.
  • 웹에서의 수정 페이지는 모바일 상으론 나타나지 않도록 수정해두었습니다.
  • 게임 생성 페이지에서는 state 값에 게임 데이터가 존재하면 게임 수정을, 존재하지 않으면 게임 생성을 실행하도록 구현했습니다.
  • 게임 이미지 삭제도 위와 같이 게임 데이터가 존재하지 않을 때, 그리고 임시저장한 게임을 불러오지 않았을 때에만 삭제 api가 실행되도록 구현했습니다!
-.Clipchamp.6.mp4

📗 참고 자료 (선택)

📢 리뷰 요구 사항 (선택)

🚩 후속 작업 (선택)

서브 태그 관련으로 게임 태그 모달 ui가 수정되면 이슈 파서 진행하겠습니다!!😃

✅ 셀프 체크리스트

  • PR 제목을 형식에 맞게 작성했나요?
  • 브랜치 전략에 맞는 브랜치에 PR을 올리고 있나요? (master/main이 아닙니다.)
  • 이슈는 close 했나요?
  • Reviewers, Labels, Projects를 등록했나요?
  • 작업 도중 문서 수정이 필요한 경우 잘 수정했나요?
  • 테스트는 잘 통과했나요?
  • 불필요한 코드는 제거했나요?

closes #295

Summary by CodeRabbit

  • 새로운 기능

    • 모바일 기기에서 일부 게임 편집 화면이 자동으로 숨겨져, 보다 최적화된 환경을 제공합니다.
    • 게임 태그 입력에 대한 유효성 검사가 강화되어, 사용자 입력 오류를 사전에 방지합니다.
    • 기존 데이터를 활용한 게임 생성 및 편집 기능이 확장되었으며, 삭제 시 확인 모달이 추가되었습니다.
    • 게임 세트에서 기존 데이터를 활용하여 새로운 게임을 생성하는 기능이 추가되었습니다.
    • 게임 삭제 시 사용자 확인을 위한 모달이 추가되었습니다.
    • 새로운 유틸리티 함수가 추가되어, 문자열을 배열로 변환하는 기능이 제공됩니다.
  • 리팩토링

    • 중앙 집중식 상태 관리로 게스트 사용자 인증 방식이 개선되었습니다.
    • 게임 정보 처리 및 이미지 삭제 로직이 최적화되어 사용자 경험을 높였습니다.
  • 스타일

    • 반응형 디자인 개선으로, 작은 화면에서도 모달과 레이아웃 간격이 최적화되었습니다.

@areumH areumH added ✔︎pull requests pull requests 코드 체크 요청 👩🏻‍💻 frontend 프론트엔드 작업 ✅feature labels Feb 22, 2025
@areumH areumH requested review from WonJuneKim and alwubin February 22, 2025 17:20
@areumH areumH self-assigned this Feb 22, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 22, 2025

Walkthrough

이 풀 리퀘스트는 BalanceGame 관련 컴포넌트와 후크 전반에 걸쳐 여러 기능 개선을 포함합니다. App.tsx에서는 모바일 환경에서 BalanceGameEditPage 렌더링을 제한하며, ToastModal 스타일에 미디어 쿼리를 추가해 반응형 디자인을 지원합니다. 또한, GameTagModal에 태그 검증 로직이 도입되었고, 생성/수정 페이지에서는 기존 게임 데이터를 prop으로 전달합니다. 게스트 판별 로직이 localStorage에서 중앙 상태 관리 방식으로 전환되었으며, 삭제 모달, 유틸리티 함수 추가 등 다양한 기능 및 검증 로직이 강화되었습니다.

Changes

파일(들) 변경 사항 요약
src/App.tsx 모바일(isMobile) 여부에 따라 BalanceGameEditPage 컴포넌트 조건부 렌더링
src/components/atoms/ToastModal/ToastModal.style.ts typo.Main.SemiBold 스타일 적용 방식을 spread로 변경 및 430px 이하 미디어 쿼리 추가
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx handleTagSubmitvalidateGameTag 검증 로직 추가 및 subTagmaxLength 제거
src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx 새로운 BalanceGameCreateSectionProps 인터페이스 추가 및 기존 게임 데이터 전달을 위한 prop 업데이트
src/components/mobile/organisms/BalanceGameEndingSection/BalanceGameEndingSection.tsx,
src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx,
src/components/molecules/BalanceGameEndingBox/BalanceGameEndingBox.tsx
게스트 판별 방식을 localStorage에서 useNewSelector(selectAccessToken) 기반으로 변경; BalanceGameSection에 삭제 모달, 다중 태그 렌더링 기능 추가
src/hooks/api/game/useCreateGameMutation.ts,
src/hooks/game/usePostBalanceGameForm.ts,
src/hooks/game/validateBalanceGameForm.ts
성공 메시지 상수 수정, 게임 생성/수정 및 태그 검증 로직 강화 (기존 및 신규 기능 통합)
src/pages/mobile/BalanceGameCreationMobilePage/BalanceGameCreationMobilePage.tsx,
src/pages/mobile/BalanceGameMobilePage/BalanceGameMobilePage.tsx
React Router의 state를 사용한 기존 게임 데이터 전달 및 멤버 조회 로직 단순화
src/utils/array.ts,
src/utils/balanceGameUtils.ts,
src/utils/validator.ts
콤마 기준 문자열 분리 함수, 게임 데이터 변환 함수, 문자열 길이 검증 함수(isAllLessThan) 등의 유틸리티 함수 추가

Possibly related PRs

Suggested labels

🪄refactor

Suggested reviewers

  • alwubin
  • hsgh085

Poem

깡총깡총 토끼가 노래해요,
코드 숲 속을 뛰놀며 반짝이는 변경 사항들,
모바일과 데스크탑, 각각의 길 따라,
모달과 유틸이 어우러져 춤을 추네요,
개발의 즐거움이 깡충깡충 피어납니다 🥕🐇

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (3)
src/components/molecules/BalanceGameEndingBox/BalanceGameEndingBox.tsx (1)

1-1: 🛠️ Refactor suggestion

console.log 문을 제거해주세요.

프로덕션 코드에서는 디버깅용 console.log 문을 제거하는 것이 좋습니다. eslint-disable 주석도 함께 제거할 수 있습니다.

-/* eslint-disable no-console */
 import React, { useState } from 'react';
 // ...
   .writeText(link)
   .then(() => {
-    console.log('게임 링크 복사 완료!');
   })
   .catch((err) => {
-    console.log(err);
   });

Also applies to: 45-45, 48-48

src/App.tsx (1)

157-159: ⚠️ Potential issue

논리 연산자 사용 오류 수정 필요

널 병합 연산자(??)가 잘못 사용되었습니다. 현재 구현은 isMobile이 null 또는 undefined일 때만 NotificationPage를 렌더링합니다.

다음과 같이 수정을 제안합니다:

-            {isMobile ?? ( // TODO: 404페이지 생성시 조건부로 수정
+            {isMobile && ( // TODO: 404페이지 생성시 조건부로 수정
              <Route path={PATH.NOTIFICATION} element={<NotificationPage />} />
            )}
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (1)

34-45: 🛠️ Refactor suggestion

유효성 검사 실패 시 사용자 피드백이 필요합니다.

유효성 검사가 실패할 경우 사용자에게 어떤 문제가 있는지 알려주는 피드백이 없습니다. 오류 메시지를 표시하여 사용자 경험을 개선하는 것이 좋겠습니다.

 const handleTagSubmit = () => {
   if (currentMainTag) {
     const gameValidation = validateGameTag(form);

     if (!gameValidation.isValid) {
+      // 유효성 검사 실패 메시지를 표시
+      setError(gameValidation.message);
       return;
     }

     submitGame();
     onClose?.();
   }
 };
🧹 Nitpick comments (14)
src/components/molecules/BalanceGameEndingBox/BalanceGameEndingBox.tsx (1)

41-50: 에러 처리를 개선해주세요.

클립보드 API 호출 실패 시 사용자에게 적절한 피드백을 제공하는 것이 좋습니다.

 const copyGameLink = (link: string) => {
   navigator.clipboard
     .writeText(link)
     .then(() => {
-      console.log('게임 링크 복사 완료!');
+      // Success is handled by showToastModal in handleCopyButton
     })
     .catch((err) => {
-      console.log(err);
+      showToastModal('클립보드 복사에 실패했습니다. 다시 시도해주세요.');
     });
 };
src/App.tsx (2)

151-151: 모바일에서 편집 페이지 렌더링 로직 검토

모바일 환경에서 BalanceGameEditPage를 숨기고 BalanceGameCreationPage를 통해 편집 기능을 처리하는 접근 방식이 적용되었습니다. 이는 코드 재사용성 측면에서 긍정적이지만, 몇 가지 고려사항이 있습니다.

다음 사항들을 확인해 주시기 바랍니다:

  1. 모바일 사용자가 직접 URL을 통해 편집 페이지에 접근하려 할 때의 처리
  2. SEO 및 웹 크롤러에 대한 영향

제안사항:

  1. 모바일 사용자를 위한 리다이렉션 로직 추가
  2. 적절한 메타 태그 설정으로 SEO 최적화

163-190: 주석 처리된 코드 정리 필요

주석 처리된 대규모 코드 블록이 파일의 가독성과 유지보수성을 저하시킵니다. 특히 App.tsx와 같은 핵심 라우팅 파일에서는 더욱 중요합니다.

제안사항:

  1. 사용하지 않는 코드는 과감히 삭제
  2. 향후 사용 예정인 코드는 별도 브랜치로 관리
  3. TODO 주석에는 관련 이슈 번호 추가
src/hooks/game/usePostBalanceGameForm.ts (2)

56-71: 수정/생성 로직 분기 검토
existingGame && gameSetId 조건에 따라 수정 혹은 생성 여부를 분기하는 것은 합리적입니다. 다만, editBalanceGamecreateBalanceGame에 대한 에러 상황(네트워크 에러 등) 처리 로직도 추가로 고려해 보시면 완성도가 높아질 것 같습니다.


124-130: 이미지 삭제 분기 로직 주의사항
기존 게임 또는 임시 게임로드 상태(existingBalanceGame 혹은 isTempGameLoaded)일 때에는 deleteFiles를 생략하는 로직이 인상적입니다. 다만, 파일 삭제가 제대로 이뤄지지 않는 경우를 대비한 에러 처리가 필요할 수 있으니 검토해 보세요.

src/utils/array.ts (1)

13-15: 콜론(,) 기준으로 배열을 생성하는 함수 추가
createArrayFromCommaString 함수는 구현 의도가 명확하고 간단하여 활용도가 높습니다. 다만, 공백 처리를 위해 trim() 등을 추가로 고려할 수도 있습니다. 예를 들어 "tag1, tag2 " 형태의 문자열도 있을 수 있으니, 필요하면 공백 제거 로직을 포함하는 것이 좋겠습니다.

src/hooks/game/validateBalanceGameForm.ts (1)

33-38: 서브 태그 유효성 검사 로직을 개선할 수 있습니다.

현재 구현은 기능적으로는 문제가 없으나, 사용자에게 더 명확한 피드백을 제공할 수 있습니다.

다음과 같이 구체적인 오류 메시지를 반환하도록 개선을 제안합니다:

   if (subTagList.length > 3) {
-    return { isValid: false };
+    return { isValid: false, message: '서브 태그는 최대 3개까지만 입력 가능합니다.' };
   }
   if (!isAllLessThan(subTagList, 10)) {
-    return { isValid: false };
+    return { isValid: false, message: '각 서브 태그는 10자 이하여야 합니다.' };
   }
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (1)

76-82: 입력 필드에 시각적 제약이 필요합니다.

maxLength 속성이 제거되어 사용자가 입력 제한을 알 수 없습니다. 입력 길이 제한을 시각적으로 표시하거나 실시간으로 유효성을 검사하는 것이 좋겠습니다.

 <input
   name="subTag"
   css={S.inputStyling}
   placeholder="ex. 너무어려운밸런스게임, 선택장애, 이상형"
   value={form.subTag}
   onChange={setSubTagValue}
+  maxLength={50} // 적절한 최대 길이 설정
 />
src/utils/balanceGameUtils.ts (1)

113-132: 함수 문서화 및 타입 안전성 개선이 필요합니다.

새로 추가된 함수의 문서화와 타입 안전성을 개선하면 코드의 유지보수성이 향상될 것 같습니다.

+/**
+ * GameSet 데이터를 BalanceGame 형식으로 변환하는 함수
+ * @param gameSet - 변환할 GameSet 객체
+ * @returns BalanceGame 형식으로 변환된 객체
+ * @throws {Error} gameSet이 null 또는 undefined인 경우
+ */
 export const transformGameSetToBalanceGameSet = (
   gameSet: GameSet,
 ): BalanceGame => {
+  if (!gameSet) {
+    throw new Error('GameSet must not be null or undefined');
+  }
+
   return {
     title: gameSet.title,
     mainTag: gameSet.mainTag,
     subTag: gameSet.subTag,
     games: gameSet.gameDetailResponses.map((gameDetail) => ({
-      description: gameDetail.description ?? '',
+      description: gameDetail.description || '',
       gameOptions: gameDetail.gameOptions.map((option) => ({
         id: option.id,
         name: option.name,
-        imgUrl: option.imgUrl ?? '',
+        imgUrl: option.imgUrl || '',
         description: option.description,
         optionType: option.optionType,
-        fileId: option.fileId ?? null,
+        fileId: option.fileId || null,
       })),
     })),
   };
 };
src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx (2)

16-19: Props 유효성 검사가 필요합니다.

existingGame이 제공될 때 gameSetId도 필수적으로 제공되어야 하는지 명확하게 하면 좋겠습니다.

 interface BalanceGameCreateSectionProps {
   existingGame?: GameSet;
-  gameSetId?: number;
+  gameSetId: existingGame extends GameSet ? number : undefined;
 }

96-102: 이미지 삭제 핸들러의 타입 안전성 개선이 필요합니다.

fileId가 null이나 undefined일 때의 처리를 더 명확하게 하면 좋겠습니다.

-              handleDeleteImg(
-                form.games[gameStage].gameOptions[selectedOptionId].fileId ??
-                  null,
-                selectedOptionId,
-              );
+              const fileId = form.games[gameStage].gameOptions[selectedOptionId].fileId;
+              handleDeleteImg(fileId || null, selectedOptionId);
src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (3)

102-114: 게임 삭제 중 로딩 상태 처리가 필요합니다.

삭제 기능이 잘 구현되어 있으나, 다음 개선사항을 고려해보세요:

  • 삭제 진행 중 로딩 상태 표시
  • 삭제 중 버튼 비활성화로 중복 클릭 방지
+ const [isDeleting, setIsDeleting] = useState(false);

  const handleGameDeleteButton = () => {
+   setIsDeleting(true);
    deleteBalanceGame(
      { gameSetId },
      {
        onSuccess: () => {
          navigate('/');
        },
        onError: () => {
          showToastModal(ERROR.DELETEGAME.FAIL);
+         setIsDeleting(false);
        },
      },
    );
  };

126-140: 네비게이션 state에 타입 정의가 필요합니다.

navigate 함수에 전달되는 state 객체에 타입을 정의하면 타입 안정성이 향상됩니다.

interface GameNavigationState {
  game: GameSet;
  gameSetId: number;
}

// 사용 예시:
navigate<GameNavigationState>(`/${PATH.CREATE.GAME}`, {
  state: { game, gameSetId }
});

155-160: 삭제 진행 중 모달의 확인 버튼 비활성화가 필요합니다.

TextModal 컴포넌트에 isLoading prop을 추가하여 삭제 진행 중에는 확인 버튼을 비활성화하는 것이 좋습니다.

  <TextModal
    text="해당 게임을 삭제하시겠습니까?"
    isOpen={deleteModalOpen}
+   isLoading={isDeleting}
    onConfirm={handleGameDeleteButton}
    onClose={() => setDeleteModalOpen(false)}
  />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f17e265 and d4ffaf3.

📒 Files selected for processing (16)
  • src/App.tsx (1 hunks)
  • src/components/atoms/ToastModal/ToastModal.style.ts (2 hunks)
  • src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (2 hunks)
  • src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx (4 hunks)
  • src/components/mobile/organisms/BalanceGameEndingSection/BalanceGameEndingSection.tsx (2 hunks)
  • src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts (1 hunks)
  • src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (8 hunks)
  • src/components/molecules/BalanceGameEndingBox/BalanceGameEndingBox.tsx (2 hunks)
  • src/hooks/api/game/useCreateGameMutation.ts (1 hunks)
  • src/hooks/game/usePostBalanceGameForm.ts (6 hunks)
  • src/hooks/game/validateBalanceGameForm.ts (2 hunks)
  • src/pages/mobile/BalanceGameCreationMobilePage/BalanceGameCreationMobilePage.tsx (1 hunks)
  • src/pages/mobile/BalanceGameMobilePage/BalanceGameMobilePage.tsx (1 hunks)
  • src/utils/array.ts (1 hunks)
  • src/utils/balanceGameUtils.ts (1 hunks)
  • src/utils/validator.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: storybook
  • GitHub Check: build
🔇 Additional comments (19)
src/pages/mobile/BalanceGameMobilePage/BalanceGameMobilePage.tsx (1)

10-11: 멤버 데이터 조회 로직이 개선되었습니다!

토큰 파싱 로직을 제거하고 useMemberQuery 훅을 직접 사용하도록 변경하여 코드가 더 간단하고 명확해졌습니다.

src/components/mobile/organisms/BalanceGameEndingSection/BalanceGameEndingSection.tsx (1)

10-11: 게스트 상태 관리가 Redux로 전환되었습니다!

localStorage 대신 Redux를 사용하여 상태 관리를 중앙화했습니다. 이는 애플리케이션의 상태 관리를 더 예측 가능하고 유지보수하기 쉽게 만듭니다.

Also applies to: 34-34

src/components/molecules/BalanceGameEndingBox/BalanceGameEndingBox.tsx (1)

12-13: 게스트 상태 관리가 Redux로 통합되었습니다!

localStorage에서 Redux로의 전환이 일관되게 적용되어 상태 관리가 개선되었습니다.

Also applies to: 30-30

src/hooks/game/usePostBalanceGameForm.ts (6)

1-5: 임포트 구조가 명확하고 사용 의도가 잘 드러납니다.
추가된 임포트들은 모두 필요한 의존성을 알기 쉽게 보여주고 있어 가독성을 높여줍니다.

Also applies to: 9-9


20-21: 추가된 validateBalanceGameFormuseEditGamesMutation 임포트 확인
밸런스 게임 폼 검증 유틸, 게임 수정 API 호출 훅을 명확하게 분리하여 관리 중이니 유지보수성에 도움이 됩니다.


28-28: gameSetId 매개변수 추가
기존 게임 수정 로직에서 식별용 ID를 명확하게 받을 수 있도록 구성한 점이 인상적입니다. 필요한 경우가 아니면 undefined로 처리되는 옵셔널 파라미터로 설계된 점도 좋은 패턴입니다.


30-33: existingBalanceGame를 생성하는 로직 검토
existingGame이 존재할 때만 변환해주는 것으로, null 처리나 변환 실패 시의 에러 처리가 필요한 상황이 있는지 확인해 보세요. 현재 로직으로는 안전해 보이지만, transformGameSetToBalanceGameSet의 예외 케이스가 있을 경우를 대비해 에러 핸들링을 고려해볼 수도 있습니다.


35-35: 기본 폼 상태 설정
existingBalanceGame ?? { ... } 패턴은 재활용 가능성이 높고, 기존 데이터를 우선적으로 사용하는 구조가 직관적입니다. 가독성과 유지보수에 문제 없습니다.


46-46: 게임 수정 훅 초기화
useEditGamesMutation을 통해 수정 API를 명료하게 분리했네요. API 호출 로직을 훅으로 추상화해 재사용성도는 유지하기에 적절해 보입니다.

src/utils/validator.ts (1)

21-25: 문자열 배열 전체 길이 검사 함수 추가
isAllLessThan(arr, num) 함수로 배열 내 모든 문자열 길이가 num 이하인지 단순 명료하게 확인할 수 있습니다. 유효성 검사의 반복 패턴을 효과적으로 추상화했으며, 빈 배열이나 null 처리를 고려해야 할 상황이 있다면 추가 방어 로직을 고려해 볼 수도 있습니다.

src/pages/mobile/BalanceGameCreationMobilePage/BalanceGameCreationMobilePage.tsx (3)

7-10: State 인터페이스가 적절하게 정의되었습니다.

게임 데이터의 타입 안전성을 보장하기 위한 인터페이스가 잘 구현되었습니다.


13-17: 상태 접근 시 타입 안전성과 null 처리가 잘 구현되었습니다.

location.state의 타입 캐스팅과 옵셔널 체이닝을 통한 안전한 상태 접근이 잘 구현되었습니다.


21-24: 게임 수정 기능이 적절하게 구현되었습니다.

기존 게임 데이터와 ID를 props로 전달하여 생성/수정 기능을 통합적으로 처리할 수 있도록 구현되었습니다.

src/components/atoms/ToastModal/ToastModal.style.ts (2)

23-25: 타이포그래피 스프레드 연산자 사용이 적절합니다.

타이포그래피 스타일을 스프레드 연산자로 적용하여 코드의 재사용성과 유지보수성이 향상되었습니다.


34-37: 모바일 반응형 디자인이 적절하게 구현되었습니다.

430px 이하의 화면에서 모바일에 최적화된 타이포그래피와 패딩이 적용되도록 구현되었습니다.

src/hooks/api/game/useCreateGameMutation.ts (1)

24-24: 성공 메시지 상수가 올바르게 업데이트되었습니다.

SUCCESS.CREATEGAME.CREATE에서 SUCCESS.GAME.CREATE로 변경되어 메시지 상수의 일관성이 개선되었습니다.

src/hooks/game/validateBalanceGameForm.ts (1)

28-29: 서브 태그 파싱 로직이 적절하게 구현되었습니다.

createArrayFromCommaString 유틸리티 함수를 사용하여 서브 태그 문자열을 배열로 변환하는 로직이 잘 구현되었습니다.

src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts (1)

83-83: 서브 태그 간격 조정이 적절합니다!

Flex 컨테이너 내의 요소들 간 간격을 8px로 설정한 것이 다른 간격들과 일관성 있게 잘 어울립니다.

src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (1)

4-5: 상태 관리 개선이 잘 이루어졌습니다!

localStorage에서 Redux store로 게스트 상태 체크 로직을 이동한 것이 좋은 개선입니다. 이는 상태 관리를 중앙화하고 타입 안정성을 향상시킵니다.

Also applies to: 60-60

</div>
<div css={S.subTagWrapper}>
{game.subTag && <GameTagChip tag={game.subTag} />}
{game.subTag && subTagList.map((tag) => <GameTagChip tag={tag} />)}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

map 함수 사용 시 key prop이 필요합니다.

React 요소 배열을 렌더링할 때 각 요소에 고유한 key prop을 지정해야 합니다.

- {game.subTag && subTagList.map((tag) => <GameTagChip tag={tag} />)}
+ {game.subTag && subTagList.map((tag, index) => (
+   <GameTagChip key={`${tag}-${index}`} tag={tag} />
+ ))}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{game.subTag && subTagList.map((tag) => <GameTagChip tag={tag} />)}
{game.subTag && subTagList.map((tag, index) => (
<GameTagChip key={`${tag}-${index}`} tag={tag} />
))}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (3)

76-79: 게임 삭제 기능의 사용자 경험 개선이 필요합니다.

삭제 중 로딩 상태 표시와 구체적인 에러 메시지 처리가 필요합니다.

다음과 같은 개선을 제안합니다:

+ const [isDeleting, setIsDeleting] = useState<boolean>(false);

  const handleGameDeleteButton = () => {
+   setIsDeleting(true);
    deleteBalanceGame(
      { gameSetId },
      {
        onSuccess: () => {
+         setIsDeleting(false);
          navigate('/');
        },
        onError: (error) => {
+         setIsDeleting(false);
-         showToastModal(ERROR.DELETEGAME.FAIL);
+         showToastModal(error.message || ERROR.DELETEGAME.FAIL);
        },
      },
    );
  };

Also applies to: 102-114


126-161: 메뉴 아이템과 모달의 일관성 개선이 필요합니다.

삭제 중 메뉴 아이템 비활성화와 모달 네이밍의 일관성이 필요합니다.

다음과 같은 개선을 제안합니다:

  const myGameItem: MenuItem[] = [
    {
      label: '수정',
      onClick: () => {
        navigate(`/${PATH.CREATE.GAME}`, { state: { game, gameSetId } });
      },
+     disabled: isDeleting,
    },
    {
      label: '삭제',
      onClick: () => {
        setDeleteModalOpen(true);
      },
+     disabled: isDeleting,
    },
  ];

그리고 모달 컴포넌트의 네이밍을 일관되게 변경하는 것을 추천드립니다:

  • ShareModal -> TextModal로 통일하거나
  • TextModal -> ConfirmModal로 변경

210-211: 빈 태그 필터링이 필요합니다.

빈 문자열이나 공백만 있는 태그는 렌더링하지 않도록 처리가 필요합니다.

다음과 같이 수정을 제안합니다:

  {game.subTag &&
-   subTagList.map((tag) => <GameTagChip key={tag} tag={tag} />)}
+   subTagList
+     .filter((tag) => tag.trim())
+     .map((tag) => <GameTagChip key={tag} tag={tag.trim()} />)}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4ffaf3 and 34d01af.

📒 Files selected for processing (1)
  • src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: storybook
🔇 Additional comments (1)
src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (1)

4-7: 상태 관리 및 유틸리티 개선이 잘 이루어졌습니다!

Redux를 통한 중앙 상태 관리로의 전환과 타입 안전성이 향상된 유틸리티 함수의 도입이 적절합니다.

Also applies to: 22-24

const gameStages: GameDetail[] =
game?.gameDetailResponses ?? gameDefaultDetail;
const isGuest = !localStorage.getItem('accessToken');
const isGuest = !useNewSelector(selectAccessToken);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

옵셔널 체이닝을 통한 안전한 접근이 필요합니다.

game 객체가 undefined일 때 subTag 접근이 안전하도록 처리가 필요합니다.

다음과 같이 수정을 제안합니다:

- const subTagList = createArrayFromCommaString(game?.subTag ?? '');
+ const subTagList = game ? createArrayFromCommaString(game.subTag ?? '') : [];

Also applies to: 65-65

Copy link
Collaborator

@WonJuneKim WonJuneKim left a comment

Choose a reason for hiding this comment

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

모바일 페이지 관련 코멘트 남겼습니다~ 논의 했던 내용들이 잘 담겨 있네요! 고생하셨어요!

<Route
path={PATH.BALANCEGAME.EDIT()}
element={<BalanceGameEditPage />}
element={!isMobile && <BalanceGameEditPage />}
Copy link
Collaborator

Choose a reason for hiding this comment

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

이런 표현 좋네요~

}: BalanceGameEndingSectionProps) => {
const navigate = useNavigate();
const isGuest = !localStorage.getItem('accessToken');
const isGuest = !useNewSelector(selectAccessToken);
Copy link
Collaborator

Choose a reason for hiding this comment

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

리덕스로 일원화 한 점 아주 좋습니다~

Comment on lines +17 to +23
existingGame?: GameSet;
gameSetId?: number;
}

const BalanceGameCreateSection = ({
existingGame,
gameSetId,
Copy link
Collaborator

Choose a reason for hiding this comment

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

이 organisms의 props는 전부 옵셔널인데, default 설정이 없으면 어떻게 처리되고 있는지 궁금합니다!

Copy link
Collaborator Author

@areumH areumH Feb 24, 2025

Choose a reason for hiding this comment

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

게임이 존재할 때는 게임 수정, 존재하지 않을 때는 게임 생성이 수행됩니다!

};

export { isEmptyString, isLongerThan, isAllTrue, isTimeLimit };
const isAllLessThan = (arr: string[], num: number) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const isAllLessThan = (arr: string[], num: number) => {
const validateElementsLength = (arr: string[], num: number) => {

또는

Suggested change
const isAllLessThan = (arr: string[], num: number) => {
const checkMaxLength = (arr: string[], num: number) => {

요런 네이밍은 어떠신가요??

++) ts doc 로 간단한 사용법(+매개변수에 대한 설명)까지 주어진다면 더 가독성이 좋을 거 같아요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

check나 validate의 이름은 배열의 모든 요소들의 길이가 인자로 받은 수보다 같거나 작은지에 대한 역할을 정확히 보여주지 않다고 생각되어 isAllLessThan을 사용했습니다..!! 해당 파일 내의 함수 이름에 통일성을 주기 위함도 있구요 ㅎㅎ

};

export const createArrayFromCommaString = (str: string): string[] => {
return str.split(',');
Copy link
Collaborator

Choose a reason for hiding this comment

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

이 부분이 서브 태그와 관련이 있군요!


const BalanceGameCreationMobilePage = () => {
const location = useLocation();
const state = location.state as State;
Copy link
Collaborator

Choose a reason for hiding this comment

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

혹시 타입 단언을 사용하게 된 계기가 있을까요?!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

as State 처럼 타입 지정을 해주지 않으면 오류가 나서 해둔건데 질문 주셔서 서치해보니 기본적으로 state는 unknown으로 정의되기 때문에 타입 단언이 필요하다고 하네요!! 🙌

Copy link
Collaborator

@alwubin alwubin left a comment

Choose a reason for hiding this comment

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

늦은 답변 죄송합니다🙇‍♀️ 원준님 리뷰에서 제기되었던 수정 사항들이 잘 반영되었네용ㅎㅎ 전반적으로 코드가 가독성 좋고 유지보수하기 편리하도록 작성된 것 같아요! 고생 많으셨습니다👏

Copy link
Collaborator

@WonJuneKim WonJuneKim left a comment

Choose a reason for hiding this comment

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

수정사항 확인했습니다~ 고생하셨습니다!!

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 5, 2025

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (1)

235-236: 서브태그 렌더링이 개선되었으나 키 처리에 주의가 필요합니다.

서브태그를 배열로 변환하여 렌더링하는 방식은 좋습니다. 현재 키로 태그 값 자체를 사용하고 있는데, 동일한 태그가 있을 경우 중복 키가 발생할 수 있습니다. 태그와 인덱스를 조합하여 키를 생성하는 것이 더 안전합니다.

- {game.subTag && subTagList.map((tag) => <GameTagChip key={tag} tag={tag} />)}
+ {game.subTag && subTagList.map((tag, index) => <GameTagChip key={`${tag}-${index}`} tag={tag} />)}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7c0e2f and cb28cc2.

📒 Files selected for processing (1)
  • src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (10)
src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (10)

4-5: localStorage에서 Redux로의 전환이 잘 이루어졌습니다.

게스트 상태 확인을 위해 localStorage 대신 Redux를 사용하는 방식으로 개선되었습니다. 이는 상태 관리를 중앙화하고 일관성을 유지하는 좋은 접근법입니다.


7-7: 유틸리티 함수 활용으로 코드 재사용성이 향상되었습니다.

쉼표로 구분된 문자열을 배열로 변환하는 유틸리티 함수를 사용함으로써 코드가 더 깔끔해지고 재사용 가능해졌습니다.


61-61: Redux 선택자를 통한 게스트 상태 확인 방식이 개선되었습니다.

localStorage 대신 Redux 상태를 사용하여 게스트 상태를 확인하는 방식으로 변경되었습니다. 이는 상태 관리의 일관성을 높이고 유지보수를 용이하게 합니다.


66-66: subTag 처리 방식 개선이 필요합니다.

game 객체가 undefined일 경우 안전하게 처리할 수 있도록 옵셔널 체이닝을 적용한 것은 좋습니다. 그러나 이전 리뷰 코멘트에서 언급된 것처럼 추가적인 안전장치가 필요합니다.


77-78: 게임 삭제 기능이 적절하게 추가되었습니다.

게임 삭제를 위한 mutation hook이 추가되어 기능이 완성되었습니다.


79-85: 모달 상태 관리가 효율적으로 구현되었습니다.

여러 유형의 모달을 하나의 상태 변수로 관리하는 방식이 깔끔하고 유지보수하기 좋습니다.


109-121: 게임 삭제 핸들러가 적절하게 구현되었습니다.

성공 시 홈으로 리다이렉트하고 실패 시 오류 메시지를 표시하는 방식으로 사용자 경험을 고려한 구현이 잘 되었습니다.


133-146: 내 게임 메뉴 항목이 기능적으로 잘 구현되었습니다.

수정 및 삭제 기능에 대한 메뉴 항목이 적절하게 구현되었으며, 수정 시 필요한 상태 데이터를 함께 전달하는 방식도 좋습니다.


147-154: 다른 사용자의 게임에 대한 메뉴 항목이 적절하게 구현되었습니다.

신고 기능을 위한 메뉴 항목이 잘 구현되었습니다.


169-180: TextModal 컴포넌트 구현이 명확합니다.

게임 삭제와 신고에 대한 확인 모달이 각각의 목적에 맞게 적절하게 구성되었습니다.

Comment on lines +181 to 185
<ReportModal
isOpen={activeModal === 'reportGame'}
onConfirm={() => {}}
onClose={() => setShareModalOpen(false)}
onClose={onCloseModal}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

ReportModal 컴포넌트에 대한 확인 핸들러 구현이 필요합니다.

ReportModal의 onConfirm 핸들러가 빈 함수로 설정되어 있습니다. 실제 신고 기능을 구현하거나 해당 기능이 아직 준비되지 않았다면 TODO 주석을 추가하는 것이 좋습니다.

- onConfirm={() => {}}
+ onConfirm={() => {
+   // TODO: 신고 API 연동 구현 필요
+ }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ReportModal
isOpen={activeModal === 'reportGame'}
onConfirm={() => {}}
onClose={() => setShareModalOpen(false)}
onClose={onCloseModal}
/>
<ReportModal
isOpen={activeModal === 'reportGame'}
onConfirm={() => {
// TODO: 신고 API 연동 구현 필요
}}
onClose={onCloseModal}
/>

@areumH areumH merged commit b2be48d into dev Mar 5, 2025
6 of 7 checks passed
@github-project-automation github-project-automation bot moved this from Todo to Done in PICK-O Mar 5, 2025
@areumH areumH deleted the feat/295-game-edit branch March 5, 2025 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✅feature 👩🏻‍💻 frontend 프론트엔드 작업 ✔︎pull requests pull requests 코드 체크 요청

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

밸런스게임 모바일 페이지 수정, 삭제 기능 연결

4 participants