Skip to content

Conversation

@i-meant-to-be
Copy link
Contributor

@i-meant-to-be i-meant-to-be commented Jul 15, 2025

🚩 연관 이슈

closed #6

📝 작업 내용

문제 상황

  • 기존 IPC 함수는 단순히 값만 반환하고, 그 과정에서 발생하는 예외는 하나도 처리하지 못함
  • 기존 UI 역시 요청 후 반환까지의 시간(isLoading) 또는 오류(error) 등을 처리하는 로직을 갖고 있지 않았음

개선 상황

요약

  • IPC 함수를 비동기 요청에 더 적합한 형태로 리팩터링
  • 기존 UI가 비동기 요청의 상태에 따라 UI를 조건부 렌더링하도록 개선
  • 모든 테이블 조회 요청이 시간 순으로 정렬되게 개선

비동기 요청에서 사용할 수 있는 새 컴포넌트 추가

비동기 요청 중 오류가 발생했거나, 요청을 보낸 후 응답을 기다리고 있을 때 사용할 수 있는 새로운 컴포넌트 2종을 추가했습니다:

  • src/components/async/ErrorIndicator.tsx
  • src/components/async/LoadingIndicator.tsx

비동기 요청을 처리하는 React 훅 useAsyncRequest 추가

비동기 요청을 처리하는 React 훅 useAsyncRequest을 추가하였습니다. 이 훅의 주된 목적은 async 키워드가 붙은 비동기 함수의 처리를 간편하고 안전하게 할 수 있도록 돕는 것입니다.

이 훅은 기본적으로 TanStack Query의 useQuery 함수와 상당히 유사하며, 차이점은 아래 2가지 정도입니다:

  • 요청 시점을 개발자가 직접 결정할 수 있는 enabled 변수가 없습니다.
  • 요청은 훅의 반환 값 중 하나인 execute를 직접 실행해야만 가능합니다. 즉, 훅 선언 시 자동으로 요청이 나가는 게 아닙니다.

이 훅은 입력으로 다음 값을 받습니다:

  • request | 실행하고자 하는 async 키워드가 붙은 비동기 함수입니다.
  • timeout | 타임아웃 시간입니다. 단위는 밀리초입니다.

이 훅은 반환 값으로 다음 유용한 변수 및 함수를 전달합니다:

  • execute | async 키워드가 붙은 요청을 실행합니다.
  • isLoading | 요청 후 불러오는 중일 경우, true를 반환합니다.
  • data | 요청이 성공적이면, 데이터를 반환합니다.
  • error | 요청에 실패할 경우, 오류를 반환합니다.

이상의 isLoading, error 등을 통해, UI를 요청 진행 상태에 따라 유동적으로 렌더링할 수 있습니다.

또한, 이 훅은 내부적으로 타임아웃 정책을 채택하고 있습니다. 지정된 타임아웃 시간 이상 기다릴 경우, 훅은 요청을 중단하고 자동으로 타임아웃 오류를 던집니다. 이 오류 역시 훅의 반환 값 중 하나인 error로 전달되어, 쉽게 핸들링이 가능합니다.

기존 UI가 비동기 요청의 상태에 따라 UI를 조건부 렌더링하도록 개선

현재 사용하고 있는 4개 페이지가 개선된 IPC 함수를 적극 활용하여, 데이터 패칭 상태에 따라 다른 UI를 표시하게 개선하였습니다. 이 부분은 코드를 직접 보시는 게 빠를 것 같습니다.

export default function TableListPage() {
  const {
    data: getAllTablesData,
    error: getAllTablesError,
    isLoading: getAllTablesLoading,
    execute: getAllTables,
  } = useAsyncRequest(repo.getAllTables);

  // 기타 코드 생략

  useEffect(() => {
    getAllTables();
  }, [getAllTables]);

  return (
    <DefaultLayout>
      <DefaultLayout.ContentContainer>
        { /* 로딩 중일 때, 로딩 인디케이터 출력 */ }
        {getAllTablesLoading && <LoadingIndicator />}

        { /* 오류 발생 시, 오류 인디케이터 출력 */ }
        {!getAllTablesLoading && getAllTablesError && (
          <ErrorIndicator
            onClickRetry={() => getAllTables()}
            message={String(getAllTablesError)}
          />
        )}

        { /* 데이터 패칭 성공 시 */ }
        {!getAllTablesLoading && !getAllTablesError && getAllTablesData && (
          <div className="flex max-w-[1140px] flex-wrap justify-start">
            { /* 필요한 컴포넌트 렌더링 */ }
          </div>
        )}
      </DefaultLayout.ContentContainer>
    </DefaultLayout>
  );
}

모든 테이블 조회 요청이 시간 순으로 정렬되게 개선

DebateTableDatadatetime 필드를 추가하였습니다. 또한, 모든 테이블 조회 요청이 시간 순으로 정렬되도록 개선하였습니다. 이를 위해 luxon 라이브러리를 도입하였음을 알립니다.

비고

글자 수 제한 정책은 이미 FE 단에서 자체적으로 막고 있어, 여기서는 적용하지 않습니다.

🏞️ 스크린샷 (선택)

없음

🗣️ 리뷰 요구사항 (선택)

매번 변경 사항이 많네요... 요구 사항은 아니지만, 항상 많은 코드 같이 꼼꼼하게 봐 주셔서, 두 분께 감사하다는 말씀을 남깁니다.

Summary by CodeRabbit

  • 신규 기능

    • 에러 및 로딩 상태를 보여주는 컴포넌트(Indicator) 추가 및 관련 Storybook 스토리 제공
    • 비동기 요청 처리를 위한 커스텀 훅(useAsyncRequest) 도입
    • UUID 유효성 검사 유틸리티 함수 추가
    • 날짜 및 시간 형식(DATETIME_FORMAT) 상수 도입 및 관련 필드(datetime) 추가
  • 기능 개선

    • 테이블 목록, 상세, 수정, 타이머 등 주요 페이지에 로딩/에러 상태 표시 및 재시도 기능 적용
    • 테이블 데이터의 날짜/시간 필드 추가 및 정렬, 저장 시점 자동 기록
    • 폼 및 버튼 등에서 비동기 처리 중 상태에 따른 UI 제어 강화
  • 버그 수정

    • 테이블 수정 테스트에서 잘못된 URL 파라미터 값을 UUID로 교체
  • 문서 및 테스트

    • 에러/로딩 Indicator 컴포넌트에 대한 Storybook 문서 및 테스트 케이스 추가
  • 기타

    • luxon 라이브러리 및 타입 정의 의존성 추가
    • 내부 타입 및 데이터 구조(datetime 필드 등) 확장

i-meant-to-be and others added 30 commits July 11, 2025 00:12
열린토론대회 예선/본선 및 결선 데이터 2건 추가
- 최초 실행 시 열린토론대회 데이터 2건 추가
- 화면 크기 및 아이콘 설정
- 일부 에셋 및 파일 경로를 빌드 설정에 따라 명확하게 지정
@i-meant-to-be i-meant-to-be requested review from jaeml06 and useon July 15, 2025 17:52
@i-meant-to-be i-meant-to-be self-assigned this Jul 15, 2025
@i-meant-to-be i-meant-to-be added the feat 기능 개발 label Jul 15, 2025
@i-meant-to-be i-meant-to-be added refactor 기능 변경 없이 코드만 바뀌었을 경우 test 테스트 코드 관련 labels Jul 15, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 15, 2025

"""

Walkthrough

이 변경사항은 토론 테이블 데이터 구조에 datetime 필드를 추가하고, 데이터 생성 시 현재 시간을 해당 필드에 저장하며, 전체 목록 조회 시 datetime 기준으로 정렬하는 기능을 도입합니다. 또한, 비동기 요청 처리와 에러/로딩 상태 관리를 위한 커스텀 훅과 UI 컴포넌트가 새로 추가되었습니다.

Changes

파일/경로 요약 변경 요약
electron/api.ts, electron/constants.ts, electron/type.ts, src/type/type.ts, src/constants/sample_table.ts 토론 데이터 info 객체에 datetime 필드 추가, 생성 시 현재 시간 저장, 목록 조회 시 datetime 기준 정렬, 관련 타입/상수/샘플데이터 반영
electron/api.test.ts 테스트 데이터에 datetime 필드 추가
package.json luxon 및 @types/luxon 의존성 추가
src/constants/formats.ts DATETIME_FORMAT 상수 추가
src/components/async/ErrorIndicator.tsx, src/components/async/LoadingIndicator.tsx 에러 및 로딩 인디케이터 컴포넌트 추가
src/components/async/ErrorIndicator.stories.tsx, src/components/async/LoadingIndicator.stories.tsx 위 컴포넌트의 Storybook 스토리 추가
src/page/TableComposition/TableComposition.tsx, src/page/TableListPage/TableListPage.tsx, src/page/TableOverviewPage/TableOverview.tsx, src/page/TimerPage/TimerPage.tsx 데이터 요청/에러/로딩 상태 관리를 useAsyncRequest 훅으로 통합, UI에 인디케이터 적용, UUID 유효성 검사 도입
src/page/TableComposition/hook/useTableFrom.tsx, src/page/TimerPage/hooks/useTimerPageState.ts useAsyncRequest 훅 도입, 비동기 요청 및 로딩/에러 상태 관리, 반환값에 isLoading 등 추가
src/page/TableComposition/components/TimeBoxStep/TimeBoxStep.tsx isLoading prop 추가, 버튼 상태에 반영
src/repositories/useAsyncRequest.ts, src/repositories/Result.ts 비동기 요청/결과 관리용 커스텀 훅 및 Result 타입 추가
src/util/type_guard.ts UUID 유효성 검사 함수 추가
electron/main.ts 상수 import 경로 수정
src/page/TableComposition/TableComposition.test.tsx 테스트에서 tableId를 UUID 형태로 변경

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant useAsyncRequest
    participant Repository
    participant API (electron)
    participant FileSystem

    User->>UI: 페이지 진입/버튼 클릭
    UI->>useAsyncRequest: execute(요청)
    useAsyncRequest->>Repository: API 함수 호출
    Repository->>API (electron): IPC 요청 (예: getAllItems, postItem)
    API (electron)->>FileSystem: 파일 읽기/쓰기
    FileSystem-->>API (electron): 데이터 반환
    API (electron)-->>Repository: 결과 반환 (datetime 포함)
    Repository-->>useAsyncRequest: 데이터/에러 반환
    useAsyncRequest-->>UI: isLoading/data/error 상태 업데이트
    UI-->>User: 로딩/에러/성공 UI 표시
Loading

Assessment against linked issues

Objective Addressed Explanation
getAllItems 요청이 시간을 기준으로 정렬되도록 개선 (#6)
모든 함수에서 비동기 처리가 가능하도록 개선 (#6)
글자 수 제한 정책 적용 (#6) 글자 수 제한 정책 관련 코드가 확인되지 않음.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
ErrorIndicator, LoadingIndicator 컴포넌트 및 Storybook 파일 추가 (src/components/async/*) IPC 함수 고도화 및 datetime 정렬과 직접적인 관련이 없는 UI 컴포넌트 신규 도입임.
useAsyncRequest 커스텀 훅, Result 타입, UUID type guard 추가 (src/repositories/useAsyncRequest.ts, src/repositories/Result.ts, src/util/type_guard.ts) IPC 함수 고도화와 비동기화 목적과 일부 연관 있으나, linked issue에 명시된 범위를 넘어선 공통 인프라/유틸리티 도입임.
TableComposition 등 여러 페이지에서 로딩/에러 처리 UI 통합 (src/page/*) 비동기 처리와 연관 있으나, linked issue의 구체적 요구사항(IPC 함수 고도화)에 포함된다고 보기 어려움.

Possibly related PRs

Suggested reviewers

  • jaeml06
  • useon

Poem

🐇
데이터에 시간의 숨결을 담아
토론의 기록이 정렬되어 간다
에러와 로딩엔 토끼가 등장!
비동기 흐름도 한층 더 단단
새벽의 코드에 박수를 보낸다
"깡총! 오늘도 고도화 완료!"

"""

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-07-16T10_44_20_374Z-debug-0.log


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 849f00f and db80d2b.

📒 Files selected for processing (2)
  • electron/api.ts (3 hunks)
  • src/components/async/ErrorIndicator.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/async/ErrorIndicator.tsx
  • electron/api.ts
✨ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate sequence diagram to generate a sequence diagram of the changes in 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

@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: 6

♻️ Duplicate comments (2)
src/constants/sample_table.ts (2)

84-84: 샘플 데이터의 datetime 필드 값 검토 필요

OPEN_DEBATE_MAIN_ROUND에서도 동일한 빈 문자열 datetime 필드 문제가 있습니다.


153-153: 샘플 데이터의 datetime 필드 값 검토 필요

OPEN_DEBATE_FINAL_ROUND에서도 동일한 빈 문자열 datetime 필드 문제가 있습니다.

🧹 Nitpick comments (9)
src/components/async/ErrorIndicator.tsx (2)

17-24: 접근성 개선 제안

재시도 버튼의 접근성을 향상시키기 위한 개선사항을 제안합니다:

       {onClickRetry && (
         <button
           onClick={() => onClickRetry()}
           className="small-button enabled px-8 py-1"
+          aria-label="데이터 로딩 재시도"
+          type="button"
         >
           다시 시도하기
         </button>
       )}
  • aria-label을 추가하여 스크린 리더 사용자에게 명확한 정보 제공
  • type="button"을 명시하여 의도를 명확히 함

13-15: 아이콘 접근성 개선 제안

에러 아이콘에 접근성 속성을 추가하여 스크린 리더 사용자에게 더 나은 경험을 제공할 수 있습니다:

-      <MdErrorOutline className="size-32 text-red-500" />
+      <MdErrorOutline 
+        className="size-32 text-red-500" 
+        aria-label="오류 아이콘"
+        role="img"
+      />
src/constants/formats.ts (1)

1-1: 날짜 포맷 상수 정의 검토

DATETIME_FORMAT 상수가 적절히 정의되었습니다. luxon 라이브러리와 호환되는 형식입니다.

더 나은 타입 안전성과 문서화를 위해 다음과 같이 개선할 수 있습니다:

-export const DATETIME_FORMAT: string = 'yyyy-MM-dd HH:mm:ss';
+/**
+ * 애플리케이션 전체에서 사용할 날짜-시간 포맷
+ * luxon 라이브러리와 호환되는 형식
+ * @example '2024-01-15 14:30:25'
+ */
+export const DATETIME_FORMAT = 'yyyy-MM-dd HH:mm:ss' as const;
  • JSDoc 주석으로 용도와 예시 제공
  • as const를 사용하여 리터럴 타입으로 추론
src/type/type.ts (1)

44-44: 타입 정의가 올바르게 추가되었습니다.

datetime 필드가 DebateInfo 인터페이스에 적절히 추가되었으며, 다른 파일들과의 타입 일관성을 유지하고 있습니다.

더 나은 개발자 경험을 위해 JSDoc 주석으로 필드의 목적과 형식을 문서화하는 것을 고려해보세요:

export interface DebateInfo {
  id: UUID;
+ /** 토론 생성 날짜 및 시간 (yyyy-MM-dd HH:mm:ss 형식) */
  datetime: string;
  name: string;
  agenda: string;
  prosTeamName: string;
  consTeamName: string;
  warningBell: boolean;
  finishBell: boolean;
}
src/page/TableComposition/TableComposition.test.tsx (1)

93-95: 테스트 데이터가 실제 사용 패턴과 일치하도록 개선되었습니다.

tableId를 UUID 형식으로 변경하여 타입 시스템과 일치시킨 것은 좋은 개선사항입니다.

테스트 가독성을 위해 하드코딩된 UUID를 상수로 정의하는 것을 고려해보세요:

+const TEST_TABLE_ID = '79800bb7-70a1-4564-b790-e2148967af7e';
+
 it('Modification flow test', async () => {
   render(
     <TestWrapper
       initialEntries={[
-        '/composition?mode=edit&tableId=79800bb7-70a1-4564-b790-e2148967af7e&mode=CUSTOMIZE',
+        `/composition?mode=edit&tableId=${TEST_TABLE_ID}&mode=CUSTOMIZE`,
       ]}
     >
       <TableComposition />
     </TestWrapper>,
   );
electron/constants.ts (1)

3-3: 날짜/시간 형식 상수가 적절히 정의되었습니다.

DATETIME_FORMAT 상수를 정의하여 날짜 형식의 일관성을 보장한 것은 좋은 관행입니다.

프론트엔드와 백엔드에 동일한 상수가 중복 정의되는 것을 방지하기 위해 공통 상수 파일로 분리하는 것을 고려해보세요:

// shared/constants.ts (새 파일)
export const DATETIME_FORMAT: string = 'yyyy-MM-dd HH:mm:ss';

// electron/constants.ts
-export const DATETIME_FORMAT: string = 'yyyy-MM-dd HH:mm:ss';
+import { DATETIME_FORMAT } from '../shared/constants';

// src/constants/formats.ts
-export const DATETIME_FORMAT: string = 'yyyy-MM-dd HH:mm:ss';
+import { DATETIME_FORMAT } from '../../shared/constants';
src/page/TableComposition/hook/useTableFrom.tsx (1)

131-133: 로딩 상태 결합을 단순화하세요.

useEffect를 사용한 로딩 상태 결합은 불필요한 리렌더링을 발생시킬 수 있습니다.

다음과 같이 직접 계산하는 방식으로 변경하세요:

-  const [isLoading, setIsLoading] = useState(false);
-
-  useEffect(() => {
-    setIsLoading(postLoading || patchLoading);
-  }, [postLoading, patchLoading]);
+  const isLoading = postLoading || patchLoading;

Also applies to: 141-141

src/repositories/useAsyncRequest.ts (1)

37-38: 불필요한 타입 단언을 제거하세요.

Promise.race의 결과는 이미 타입 T로 추론되므로 타입 단언이 필요하지 않습니다.

-        setData(response as T);
-        return { success: true, data: response as T };
+        setData(response);
+        return { success: true, data: response };
src/page/TableListPage/TableListPage.tsx (1)

33-39: 삭제 실패 시 구체적인 에러 메시지를 표시하세요.

현재는 일반적인 에러 메시지만 표시되고 있습니다. 사용자에게 더 유용한 정보를 제공할 수 있습니다.

     if (!result.success) {
-      alert('테이블을 삭제하지 못했습니다. 다시 시도해주세요.');
+      alert(`테이블을 삭제하지 못했습니다: ${result.error?.message || '알 수 없는 오류가 발생했습니다.'}`);
     } else {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f68b11c and 849f00f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (24)
  • electron/api.test.ts (1 hunks)
  • electron/api.ts (3 hunks)
  • electron/constants.ts (2 hunks)
  • electron/main.ts (1 hunks)
  • electron/type.ts (1 hunks)
  • package.json (2 hunks)
  • src/components/async/ErrorIndicator.stories.tsx (1 hunks)
  • src/components/async/ErrorIndicator.tsx (1 hunks)
  • src/components/async/LoadingIndicator.stories.tsx (1 hunks)
  • src/components/async/LoadingIndicator.tsx (1 hunks)
  • src/constants/formats.ts (1 hunks)
  • src/constants/sample_table.ts (3 hunks)
  • src/page/TableComposition/TableComposition.test.tsx (1 hunks)
  • src/page/TableComposition/TableComposition.tsx (4 hunks)
  • src/page/TableComposition/components/TimeBoxStep/TimeBoxStep.tsx (4 hunks)
  • src/page/TableComposition/hook/useTableFrom.tsx (5 hunks)
  • src/page/TableListPage/TableListPage.tsx (3 hunks)
  • src/page/TableOverviewPage/TableOverview.tsx (3 hunks)
  • src/page/TimerPage/TimerPage.tsx (3 hunks)
  • src/page/TimerPage/hooks/useTimerPageState.ts (4 hunks)
  • src/repositories/Result.ts (1 hunks)
  • src/repositories/useAsyncRequest.ts (1 hunks)
  • src/type/type.ts (1 hunks)
  • src/util/type_guard.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
electron/constants.ts (4)
src/constants/formats.ts (1)
  • DATETIME_FORMAT (1-1)
src/constants/sample_table.ts (1)
  • OPEN_DEBATE_MAIN_ROUND (81-148)
electron/type.ts (1)
  • DebateTableData (34-37)
src/type/type.ts (1)
  • DebateTableData (53-56)
electron/api.ts (1)
electron/constants.ts (1)
  • DATETIME_FORMAT (3-3)
src/components/async/ErrorIndicator.stories.tsx (1)
src/components/async/ErrorIndicator.tsx (1)
  • ErrorIndicator (8-27)
src/components/async/LoadingIndicator.stories.tsx (2)
src/components/async/LoadingIndicator.tsx (1)
  • LoadingIndicator (7-16)
src/components/async/ErrorIndicator.stories.tsx (1)
  • Default (14-18)
src/repositories/useAsyncRequest.ts (1)
src/repositories/Result.ts (1)
  • Result (11-11)
src/page/TableComposition/hook/useTableFrom.tsx (4)
src/repositories/IPCDebateTableRepository.ts (2)
  • postTable (17-19)
  • patchTable (27-29)
src/repositories/useAsyncRequest.ts (1)
  • useAsyncRequest (4-70)
src/hooks/useBrowserStorage.tsx (1)
  • useBrowserStorage (12-62)
src/type/type.ts (1)
  • DebateTableData (53-56)
src/page/TimerPage/TimerPage.tsx (4)
src/util/type_guard.ts (1)
  • isUUID (6-11)
src/page/TimerPage/hooks/useTimerPageState.ts (2)
  • useTimerPageState (30-330)
  • bgColorMap (20-25)
src/components/async/LoadingIndicator.tsx (1)
  • LoadingIndicator (7-16)
src/components/async/ErrorIndicator.tsx (1)
  • ErrorIndicator (8-27)
🔇 Additional comments (30)
package.json (1)

24-24: luxon 의존성 버전 및 보안 검토 결과

  • luxon: 최신 버전(3.7.1)
  • @types/luxon: 최신 버전(3.6.2)
  • 보안 취약점 검사는 package-lock.json(또는 yarn.lock)이 필요해 스크립트 환경에서 실행되지 않았습니다.
    • 로컬에서 잠금 파일을 생성하세요:
    npm i --package-lock-only
    • 이후 아래 명령으로 취약점 여부를 확인하시기 바랍니다:
    npm audit --audit-level=moderate
src/util/type_guard.ts (2)

3-4: UUID 정규표현식 검증 완료

UUID v4 형식의 정규표현식이 정확히 구현되었습니다. 하이픈으로 구분된 8-4-4-4-12 자리 형식과 v4 특성(세 번째 그룹의 첫 번째 문자가 4, 네 번째 그룹의 첫 번째 문자가 8, 9, a, b, A, B 중 하나)을 올바르게 검증합니다.


6-11: 타입 가드 함수 구현 우수

TypeScript의 타입 가드 패턴을 적절히 활용하여 런타임 타입 검사와 컴파일 타임 타입 추론을 모두 지원하는 함수가 잘 구현되었습니다.

src/components/async/ErrorIndicator.tsx (1)

3-6: 인터페이스 정의 우수

ErrorIndicator 컴포넌트의 props 인터페이스가 명확하게 정의되었습니다. 선택적 props 사용으로 유연성을 제공합니다.

electron/main.ts (1)

7-7: import 경로 리팩토링 승인

상수들을 별도의 constants 파일로 분리하는 것은 코드 구조를 개선하는 좋은 리팩토링입니다.

electron/type.ts (1)

19-19: 백엔드-프론트엔드 타입 일관성이 잘 유지되고 있습니다.

datetime 필드가 src/type/type.ts와 동일하게 추가되어 타입 안전성을 보장하고 있습니다.

electron/api.test.ts (1)

15-15: 테스트 데이터가 새로운 인터페이스 구조와 일치하도록 적절히 업데이트되었습니다.

datetime 필드를 빈 문자열로 초기화하여 테스트 데이터 구조를 유지하고 있습니다.

electron/constants.ts (2)

9-9: 상수 데이터가 새로운 인터페이스 구조와 일치하도록 업데이트되었습니다.

OPEN_DEBATE_MAIN_ROUND 상수에 datetime 필드를 추가하여 타입 일관성을 유지하고 있습니다.


78-78: 상수 데이터가 새로운 인터페이스 구조와 일치하도록 업데이트되었습니다.

OPEN_DEBATE_FINAL_ROUND 상수에 datetime 필드를 추가하여 타입 일관성을 유지하고 있습니다.

src/components/async/ErrorIndicator.stories.tsx (1)

1-26: Storybook 설정이 올바르게 구성되었습니다.

ErrorIndicator 컴포넌트의 스토리북 스토리가 적절하게 구성되어 있고, 기본 상태와 재시도 버튼이 활성화된 상태 두 가지 케이스를 잘 보여주고 있습니다.

src/components/async/LoadingIndicator.tsx (1)

1-17: 깔끔하고 재사용 가능한 LoadingIndicator 컴포넌트입니다.

컴포넌트 구조가 간단하고 명확하며, 적절한 기본 메시지와 함께 일관된 스타일링이 적용되어 있습니다.

electron/api.ts (2)

5-6: DateTime 처리를 위한 적절한 라이브러리 import

luxon 라이브러리와 DATETIME_FORMAT 상수를 올바르게 import하여 일관된 날짜 처리가 가능합니다.


56-58: 새 아이템에 대한 적절한 datetime 할당

DateTime.local().toFormat()를 사용하여 현재 시간을 일관된 형식으로 할당하는 로직이 올바르게 구현되었습니다.

src/page/TableComposition/components/TimeBoxStep/TimeBoxStep.tsx (3)

18-18: 로딩 상태 처리를 위한 props 추가

isLoading prop이 적절히 추가되어 비동기 요청 처리 중 UI 상태를 관리할 수 있습니다.


54-54: 제출 버튼 활성화 로직 개선

isLoading 상태를 포함하여 제출 버튼 활성화 조건을 적절히 업데이트했습니다. 로딩 중에 중복 제출을 방지할 수 있습니다.


114-117: 버튼 비활성화 로직 적절히 구현

CSS 클래스와 disabled 속성 모두에 isSubmitButtonEnabled 조건을 적용하여 일관된 UI 상태를 유지합니다.

src/components/async/LoadingIndicator.stories.tsx (1)

14-18: 스토리북 설정이 올바르게 구성되어 있습니다.

LoadingIndicator 컴포넌트의 스토리북 설정이 표준 패턴을 따르고 있으며, 타입 정의와 메타데이터가 적절합니다.

src/page/TableOverviewPage/TableOverview.tsx (2)

51-55: 조건부 렌더링 로직이 올바르게 구현되어 있습니다.

로딩, 에러, 데이터 상태에 따른 조건부 렌더링이 적절하게 구현되어 있고, 재시도 기능도 포함되어 있습니다.


33-35: useEffect의 의존성 배열이 올바르게 설정되어 있습니다.

getTable과 id를 의존성 배열에 포함시켜 적절한 리렌더링을 보장하고 있습니다.

src/repositories/Result.ts (1)

1-11: Result 타입 정의가 잘 구성되어 있습니다.

discriminated union을 사용한 Result 타입 정의가 적절하며, 성공/실패 상태를 타입 안전하게 처리할 수 있도록 설계되었습니다. 이는 비동기 작업 결과를 처리하는 일반적이고 효과적인 패턴입니다.

src/page/TimerPage/hooks/useTimerPageState.ts (3)

33-38: useAsyncRequest 훅 통합이 올바르게 구현되어 있습니다.

새로운 비동기 요청 훅을 적절히 사용하여 로딩, 에러, 데이터 상태를 관리하고 있습니다. 구조 분해 할당을 통해 필요한 상태들을 명확하게 추출하고 있습니다.


139-150: 비동기 데이터 처리 로직이 안전하게 구현되어 있습니다.

응답 성공 여부를 확인하고 데이터가 존재할 때만 상태를 업데이트하는 방식이 적절합니다. 에러 처리도 useAsyncRequest 훅에서 자동으로 관리되므로 안전합니다.


326-329: 새로운 상태 값들이 적절히 반환되고 있습니다.

error, isLoading, patchedData 상태들이 훅의 반환 객체에 추가되어 컴포넌트에서 비동기 상태를 관리할 수 있도록 구성되어 있습니다.

src/page/TimerPage/TimerPage.tsx (2)

79-96: 조건부 렌더링과 상태 관리가 올바르게 구현되어 있습니다.

로딩, 에러, 데이터 상태에 따른 조건부 렌더링이 적절하게 구현되어 있고, patchedData를 사용하여 안전하게 데이터 존재 여부를 확인하고 있습니다.


54-57: 안전한 데이터 접근을 위한 옵셔널 체이닝이 적절히 사용되었습니다.

data?.info.name과 같은 옵셔널 체이닝을 통해 안전하게 데이터에 접근하고 있으며, 기본값 처리도 적절합니다.

src/page/TableComposition/TableComposition.tsx (2)

31-36: 비동기 요청 처리가 잘 구현되었습니다!

useAsyncRequest 훅을 활용한 데이터 페칭 구현이 깔끔하고, 로딩 상태의 이름을 getLoading으로 구분한 것도 좋은 접근입니다.


89-123: 조건부 렌더링이 체계적으로 잘 구성되었습니다!

로딩, 에러, 성공 상태에 따른 UI 렌더링이 명확하게 분리되어 있고, 에러 발생 시 재시도 기능도 제공하여 사용자 경험이 향상되었습니다.

src/page/TableComposition/hook/useTableFrom.tsx (1)

96-103: Result 타입 처리가 적절하게 구현되었습니다!

성공/실패 케이스를 명확하게 구분하여 처리하고, 성공 시 저장된 폼 데이터를 정리하는 로직이 잘 구성되어 있습니다.

Also applies to: 107-114

src/repositories/useAsyncRequest.ts (1)

4-70: 비동기 요청 훅이 견고하게 구현되었습니다!

타임아웃 처리, 에러 핸들링, 상태 관리가 모두 적절하게 구현되어 있습니다. 특히 AbortController를 활용한 타임아웃 구현이 깔끔합니다.

src/page/TableListPage/TableListPage.tsx (1)

15-21: 비동기 상태 관리 리팩토링이 훌륭합니다!

useAsyncRequest를 활용한 데이터 페칭과 삭제 로직이 깔끔하게 구현되었고, 로딩/에러/성공 상태에 따른 조건부 렌더링이 사용자 경험을 크게 향상시킵니다.

Also applies to: 57-88

Copy link

@useon useon left a comment

Choose a reason for hiding this comment

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

정말 많은 작업을 하셨네요 숀 .. 이걸 어떻게 다 하신 것인지 .. 이해하는데도 쉽지 않았는데 숀이 친절하게 PR에 작성해 주셔서 도움이 많이 되었습니다!!!! 질문 위주의 코멘트를 달았으니 편하게 확인해 주셔요!! 수고 많으셨어요 ✨✨👍

<TestWrapper
initialEntries={['/composition?mode=edit&tableId=1&mode=CUSTOMIZE']}
initialEntries={[
'/composition?mode=edit&tableId=79800bb7-70a1-4564-b790-e2148967af7e&mode=CUSTOMIZE',
Copy link

Choose a reason for hiding this comment

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

해당 tableId 값은 어디에서 온 건가요?? 임의로 설정해 준 건가용?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이 UUID는 src\setup.ts 파일에서 모방 데이터로 오는 2개 샘플 중 1번 샘플의 UUID입니다.

}}
/>
{getLoading && <LoadingIndicator />}
{!getLoading && error && (
Copy link

Choose a reason for hiding this comment

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

실제로 위의 useEffect에서 throw로 error 던지면 여기서 걸려서 UI가 나오나요 ???

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아뇨. throw로 던져지는 모든 오류는 자동적으로 src\components\ErrorBoundary\ErrorBoundary.tsx로 넘어갑니다. 여기서 잡히는 error는 정확히는 useAsyncRequest의 처리 과정 중 던져진 오류가 바로 던져지지 않고 error로 넘어온 거예요.

navigate(`/overview/customize/${response.data.info.id}`);
} else {
throw new Error('테이블 추가에 실패했어요.');
}
Copy link

Choose a reason for hiding this comment

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

네트워크 요청 에러나 그런 것들을 잡을 필요가 없으니까 굳이 try-catch를 사용하지 않은 걸까요 ?!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아뇨. 이미 useAsyncRequest에서 예외를 잘 처리하고 있기 때문에 여기서 try-catch-finally를 2중으로 사용할 필요가 없다고 봤습니다.

const onAddTable = async (item: DebateTableData) => {
  const response = await postTable(item);

  if (response.success) {
    await removeValue();
    navigate(`/overview/customize/${response.data.info.id}`);
  } else {
    throw new Error('테이블 추가에 실패했어요.');
  }
};

내용을 보시면, 2번째 줄에서 response를 패칭하는 과정에서 오류가 잡힐 경우, response.success === false 그리고 response.error === 무언가로 넘어오게 됩니다. 그래서 이후 추가적인 예외 핸들링은 별도로 필요 없을 거라고 생각했어요.

const result = await deleteTable(tableId);

if (!result.success) {
alert('테이블을 삭제하지 못했습니다. 다시 시도해주세요.');
Copy link

Choose a reason for hiding this comment

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

어떤 것은 실패했을 때 throw를 던지고 어떤 것은 alert를 던지는데 어떤 기준으로 다르게 작성하셨는지 궁금해요!!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

오류를 던진 케이스는 페이지가 테이블 데이터가 없으면 아예 동작하지 못하는 등 시도가 실패했을 때 문제가 매우 큰 경우입니다. 그러나 이 경우는 기본적으로 모든 테이블 목록이 패칭된 상태에서 삭제 동작을 시도하는 것이고, 다시 말해 삭제 동작을 사용자가 실행할 수 있다는 건 전체 테이블 목록이 (일단) 전부 패칭되었음을 의미합니다. 문제가 비교적 사소하다는 말이죠. 따라서 throw를 통해 ErrorBoundary로 넘어가는 게 아니라, 경고 정도로 정리할 수 있게 낮은 수위로 구현했습니다.

import LoadingIndicator from '../../components/async/LoadingIndicator';
import ErrorIndicator from '../../components/async/ErrorIndicator';

export default function TableOverview() {
Copy link

Choose a reason for hiding this comment

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

다른 페이지는 TableListPage처럼 Page로 끝나는 이름을 지었는데 이 친구는 폴더는 TableOverviewPage로 되어 있는데 컴포넌트 이름은 TableOverView로 되어 있네요 !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아 이건 오랜 전통(?)입니다. 그래도 얘기 나온 김에 정리해둘게요.


export default function useAsyncRequest<T, Args extends unknown[]>(
request: (...args: Args) => Promise<T>,
timeout: number = 5000,
Copy link

Choose a reason for hiding this comment

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

궁금한 점이 exe 기반의 형태에서도 요청에 오래 걸리는 이유는 무엇일까요 ????

Copy link
Contributor Author

Choose a reason for hiding this comment

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

요청이 오래 걸리진 않습니다. JSON 파일 읽는 거니까 금방 돼요. 근데 개발 중 테스트를 하면서 일부 throw에 잡히지 않는 오류가 무한히 기다리게 만드는 문제가 있더라구요. 그래서 혹시 제가 챙기지 못했을지도 모르는, 식별 불가능한 오류가 발생했을 경우에 대한 최후의 대비책 정도로 생각해주시면 될 것 같습니다.

Copy link

@jaeml06 jaeml06 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

Choose a reason for hiding this comment

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

tanstack query에서 제공하는 훅의 경우, api이외에 실제 비동기 처리 작업을 진행할 때에도 사용가능한데 새롭게 훅을 만들자고 선택한 이유가 있을까요?
있다면
| 또한, 이 훅은 내부적으로 타임아웃 정책을 채택하고 있습니다.
이러한 이유 때문이었는지 궁금합니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TanStack Query 사용해도 되는 거 알고 있습니다만, 다음 이유들로 인해 직접 간단하게 개발하게 되었습니다:

  • 말씀하신 대로 타임아웃 정책 적용하려고 한 부분도 있음
  • 라이브러리 하나 빼서 용량 줄이기
  • 어차피 debate-timer-fe 쪽에서도 비동기 처리를 구현해야 하는데, 그것에 대한 연습 겸 이해 증진을 위해 일부러 직접 개발

@i-meant-to-be i-meant-to-be merged commit eb676e3 into develop Jul 16, 2025
2 checks passed
@i-meant-to-be i-meant-to-be deleted the refactor/#6 branch July 16, 2025 10:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 개발 refactor 기능 변경 없이 코드만 바뀌었을 경우 test 테스트 코드 관련

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[REFACTOR] IPC 함수 고도화

4 participants