Skip to content

Comments

[API] AI 독서감상문 생성 및 AI 이용횟수 조회 API 연동#288

Merged
ljh130334 merged 5 commits intodevelopfrom
feat/api-ai
Oct 30, 2025
Merged

[API] AI 독서감상문 생성 및 AI 이용횟수 조회 API 연동#288
ljh130334 merged 5 commits intodevelopfrom
feat/api-ai

Conversation

@ljh130334
Copy link
Member

@ljh130334 ljh130334 commented Oct 29, 2025

#️⃣ 연관된 이슈

#287

📝 작업 내용

AI 독서감상문 API 연동 완료

이번 PR에서는 AI 독서감상문 생성 기능과 관련된 2개의 API를 연동하였습니다.

1. API 연동

  • AI 독서감상문 생성 API (POST /rooms/{roomId}/record/ai-review)
    • 컴포넌트 마운트 시 자동으로 API 호출
    • 생성 중 로딩 스피너 표시
    • 성공 시 생성된 독서감상문 표시 및 클립보드 복사 기능
    • 실패 시 서버 에러 메시지를 스낵바로 표시하고 기록장 페이지로 이동
  • AI 이용 횟수 조회 API (GET /rooms/{roomId}/users/ai-usage)
    • AI 작성 버튼 클릭 시 호출하여 사전 검증
    • recordCount: 사용자가 작성한 기록 개수
    • recordReviewCount: AI 독서감상문 사용 횟수 (0~5)

2. 사용자 검증 로직 구현

  • AI 작성 버튼 클릭 시 3가지 케이스 처리:
    • Case 1: 기록 개수 부족 (recordCount < 2)

      • 스낵바로 에러 메시지 표시
      • 모달 표시하지 않고 즉시 종료
      • 메시지: "독후감 생성을 위해서는 최소 2개의 기록이 필요합니다. 현재 기록 개수: {recordCount}"
    • Case 2: 이용 횟수 초과 (recordReviewCount >= 5)

      • 스낵바로 에러 메시지 표시
      • 모달 표시하지 않고 즉시 종료
      • 메시지: "사용자의 독후감 작성 수가 5회를 초과했습니다."
    • Case 3: 정상 (recordCount >= 2 && recordReviewCount < 5)

      • 확인 모달 표시
      • 실시간 잔여 이용 횟수 표시: (서비스 내 잔여 이용횟수 : {recordReviewCount}/5)
      • 확인 버튼 클릭 시 AI 독서감상문 생성 페이지로 이동

3. 버그 수정

  • 무한 루프 수정: AIWrite 컴포넌트의 useEffect dependency array에서 navigate, openSnackbar 제거

4. 파일 변경 사항

  • 신규 파일:
    • src/api/record/createAiReview.ts - AI 독서감상문 생성 API
    • src/api/record/getAiUsage.ts - AI 이용 횟수 조회 API
  • 수정 파일:
    • src/types/record.ts - CreateAiReviewData, AiUsageData 타입 추가
    • src/api/record/index.ts - 신규 API export 추가
    • src/pages/aiwrite/AIWrite.tsx - API 연동 및 로딩/에러 처리
    • src/components/memory/MemoryAddButton/MemoryAddButton.tsx - 검증 로직 구현

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 방 기반 AI 리뷰 생성 및 내용 표시 기능 추가
    • 방별 AI 사용량 조회 기능 추가(남은 횟수 확인)
  • UI/동작 개선

    • AI 작성 흐름을 API 기반 비동기로 전환하여 로딩과 복사 대상이 실제 응답에 따라 갱신
    • 확인 모달에 남은 사용 횟수 표시 후 이동
  • 버그 수정 / 오류 처리

    • API 오류 시 스낵바 안내 및 이전 화면 복귀 처리 강화

@ljh130334 ljh130334 requested review from heeeeyong and ho0010 October 29, 2025 13:56
@ljh130334 ljh130334 self-assigned this Oct 29, 2025
@ljh130334 ljh130334 added the 📬 API 서버 API 통신 label Oct 29, 2025
@vercel
Copy link

vercel bot commented Oct 29, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
thip Ready Ready Preview Comment Oct 29, 2025 2:16pm

@coderabbitai
Copy link

coderabbitai bot commented Oct 29, 2025

Walkthrough

AI 리뷰 생성 및 사용량 조회 API 클라이언트와 타입을 추가하고, MemoryAddButton과 AIWrite 컴포넌트의 AI 쓰기 흐름을 정적에서 API 기반 동적 흐름으로 전환합니다.

Changes

Cohort / File(s) 변경 요약
API 클라이언트 함수 & 익스포트
src/api/record/createAiReview.ts, src/api/record/getAiUsage.ts, src/api/record/index.ts
createAiReview() (POST /rooms/{roomId}/record/ai-review) 및 getAiUsage() (GET /rooms/{roomId}/users/ai-usage) 추가, 응답 타입 CreateAiReviewResponse, GetAiUsageResponse 정의, index.ts에 재익스포트 추가.
타입 정의
src/types/record.ts
CreateAiReviewData (content: string, count: number) 및 AiUsageData (recordReviewCount: number, recordCount: number) 인터페이스 추가.
UI 컴포넌트 변경
src/components/memory/MemoryAddButton/MemoryAddButton.tsx, src/pages/aiwrite/AIWrite.tsx
MemoryAddButton: getAiUsage() 호출로 최소 기록(2) 및 사용 한도(5) 검사, 에러 스낵바/확인 모달/네비게이션 처리로 비동기 흐름 변경. AIWrite: 마운트 시 createAiReview() 호출로 aiContent 로드, 로딩·에러 처리 및 UI/복사 버튼 레이아웃 조정.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MemoryAddButton
    participant API as API Client
    participant Server
    participant AIWrite

    User->>MemoryAddButton: AI 쓰기 버튼 클릭
    MemoryAddButton->>API: getAiUsage(roomId) [GET /rooms/{roomId}/users/ai-usage]
    API->>Server: 요청
    Server-->>API: AiUsageData
    API-->>MemoryAddButton: 사용량 응답

    alt recordCount < 2
        MemoryAddButton->>User: 에러 스낵바 (최소 기록 필요)
    else recordReviewCount >= 5
        MemoryAddButton->>User: 에러 스낵바 (사용량 초과)
    else 사용 가능
        MemoryAddButton->>User: 확인 모달 (남은 사용 횟수 표시)
        User->>MemoryAddButton: 확인
        MemoryAddButton->>AIWrite: 네비게이션 (/aiwrite/{roomId})
        AIWrite->>API: createAiReview(roomId) [POST /rooms/{roomId}/record/ai-review]
        API->>Server: 요청
        Server-->>API: CreateAiReviewData
        API-->>AIWrite: aiContent
        AIWrite->>User: AI 생성 콘텐츠 표시
    end

    alt API 오류
        MemoryAddButton->>User: 에러 스낵바 (메시지)
        AIWrite->>User: 에러 스낵바 및 리다이렉트
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 주의 영역:
    • MemoryAddButton.tsx: 비동기 흐름, 예외 처리, 팝업/모달 상태 검증
    • AIWrite.tsx: 마운트 시 API 호출·로딩/에러 분기, 네비게이션 타이밍
    • src/types/record.ts: 서버 응답 스키마와 타입 일치 여부 확인

Possibly related issues

Possibly related PRs

Suggested labels

✨ Feature

Suggested reviewers

  • ho0010
  • heeeeyong

Poem

🐰 새벽 코드 밭에 버튼을 눌러,
작은 요청이 숲을 지나와,
한 줄의 텍스트가 꽃으로 피어나네,
토끼는 깡총깡총 기뻐하며 말하네,
"동적 AI로 이야기가 살아났어요!"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 제목 "[API] AI 독서감상문 생성 및 AI 이용횟수 조회 API 연동"은 changeset의 주요 변경사항을 정확하게 반영하고 있습니다. PR의 핵심 목표인 두 개의 새로운 API(createAiReview와 getAiUsage)의 추가 및 애플리케이션으로의 통합을 명확하게 설명하고 있으며, 관련 컴포넌트와 타입 파일의 수정도 포함된 전체 변경사항의 맥락을 올바르게 전달합니다. 제목은 구체적이고 간결하며, 개발자가 커밋 히스토리를 스캔할 때 이 PR의 목적을 명확히 이해할 수 있는 충분한 정보를 제공합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/api-ai

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f82faca and d24985d.

📒 Files selected for processing (1)
  • src/components/memory/MemoryAddButton/MemoryAddButton.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/memory/MemoryAddButton/MemoryAddButton.tsx

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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: 1

🧹 Nitpick comments (1)
src/pages/aiwrite/AIWrite.tsx (1)

41-52: 에러 처리 타입 개선 제안

현재 수동 타입 가드를 사용한 에러 처리가 작동하지만, axiosAxiosError 타입을 사용하면 더 간결하고 타입 안전하게 작성할 수 있습니다.

다음과 같이 리팩토링할 수 있습니다:

+import { AxiosError } from 'axios';
       } catch (error) {
         console.error('AI 독서감상문 생성 실패:', error);
         let errorMessage = 'AI 독서감상문 생성에 실패했습니다';
 
-        if (error && typeof error === 'object' && 'response' in error) {
-          const axiosError = error as {
-            response?: {
-              data?: {
-                message?: string;
-              };
-            };
-          };
-          if (axiosError.response?.data?.message) {
-            errorMessage = axiosError.response.data.message;
-          }
+        if (error instanceof AxiosError && error.response?.data?.message) {
+          errorMessage = error.response.data.message;
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 390eac5 and 9558164.

📒 Files selected for processing (6)
  • src/api/record/createAiReview.ts (1 hunks)
  • src/api/record/getAiUsage.ts (1 hunks)
  • src/api/record/index.ts (1 hunks)
  • src/components/memory/MemoryAddButton/MemoryAddButton.tsx (3 hunks)
  • src/pages/aiwrite/AIWrite.tsx (3 hunks)
  • src/types/record.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/api/record/createAiReview.ts (2)
src/types/record.ts (2)
  • ApiResponse (88-93)
  • CreateAiReviewData (76-79)
src/api/index.ts (1)
  • apiClient (7-13)
src/api/record/getAiUsage.ts (2)
src/types/record.ts (2)
  • ApiResponse (88-93)
  • AiUsageData (82-85)
src/api/index.ts (1)
  • apiClient (7-13)
src/pages/aiwrite/AIWrite.tsx (2)
src/hooks/usePopupActions.ts (1)
  • usePopupActions (9-35)
src/api/record/createAiReview.ts (1)
  • createAiReview (8-13)
src/components/memory/MemoryAddButton/MemoryAddButton.tsx (2)
src/hooks/usePopupActions.ts (1)
  • usePopupActions (9-35)
src/api/record/getAiUsage.ts (1)
  • getAiUsage (8-13)
🔇 Additional comments (6)
src/types/record.ts (1)

75-85: 타입 정의가 명확합니다.

AI 독서감상문 및 이용 횟수 조회를 위한 타입이 잘 정의되었습니다. 주석도 명확하여 이해하기 쉽습니다.

src/components/memory/MemoryAddButton/MemoryAddButton.tsx (1)

59-89: 검증 로직이 올바르게 구현되었습니다.

API 호출 및 에러 처리, 사용자 검증 로직이 적절하게 구현되어 있습니다.

src/api/record/createAiReview.ts (1)

1-31: API 함수가 잘 구현되었습니다.

표준 API 클라이언트 패턴을 따르고 있으며, 사용 예시 주석이 포함되어 있어 다른 개발자가 이해하기 쉽습니다.

src/api/record/getAiUsage.ts (1)

1-31: API 함수가 잘 구현되었습니다.

표준 API 클라이언트 패턴을 따르고 있으며, 타입 안정성이 확보되어 있습니다.

src/pages/aiwrite/AIWrite.tsx (2)

19-68: useEffect 구현이 적절합니다.

API 호출 및 에러 처리가 잘 구현되어 있습니다. eslint-disable 주석은 PR 목표에 명시된 무한 루프 버그 수정을 위해 의도적으로 추가된 것으로 보입니다. navigateopenSnackbar는 훅에서 제공되는 안정적인 함수이므로 의존성 배열에서 제외해도 문제없습니다.


17-17: 동적 콘텐츠 로딩이 잘 구현되었습니다.

API로부터 받은 AI 생성 콘텐츠를 상태로 관리하고 UI에 표시하는 로직이 적절하게 구현되어 있습니다.

Also applies to: 27-27, 85-85, 124-124

Comment on lines 91 to 102
// 모달 표시 (잔여 횟수 = 5 - 사용한 횟수)
openConfirm({
title: 'AI 독서감상문 생성 (Beta)',
disc: `기록장에서 작성한 기록을 기반으로<br/>독서감상문을 생성하시겠어요?<br/>(서비스 내 잔여 이용횟수 : ${recordReviewCount}/5)`,
confirmText: '확인',
cancelText: '취소',
onConfirm: () => {
closePopup();
navigate(`/aiwrite/${currentRoomId}`);
console.log('AI 독서 감상문 생성 시작 - roomId:', currentRoomId);
},
});
Copy link

@coderabbitai coderabbitai bot Oct 29, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

잔여 이용횟수 표시 로직 오류

PR 목표와 검증 로직(라인 81)을 보면 recordReviewCount는 "사용 횟수"(0~5)를 의미합니다. 그러나 라인 94에서 이 값을 "잔여 이용횟수"로 직접 표시하고 있어 잘못된 정보가 사용자에게 표시됩니다.

예를 들어, 사용자가 AI 독서감상문을 3번 사용했다면 recordReviewCount는 3이고, 잔여 횟수는 2회여야 하지만, 현재 코드는 "3/5"로 표시됩니다.

다음과 같이 수정하세요:

         // 모달 표시 (잔여 횟수 = 5 - 사용한 횟수)
         openConfirm({
           title: 'AI 독서감상문 생성 (Beta)',
-          disc: `기록장에서 작성한 기록을 기반으로<br/>독서감상문을 생성하시겠어요?<br/>(서비스 내 잔여 이용횟수 : ${recordReviewCount}/5)`,
+          disc: `기록장에서 작성한 기록을 기반으로<br/>독서감상문을 생성하시겠어요?<br/>(서비스 내 잔여 이용횟수 : ${5 - recordReviewCount}/5)`,
           confirmText: '확인',
           cancelText: '취소',
           onConfirm: () => {

추가로, src/types/record.ts의 라인 83 주석도 명확성을 위해 수정하는 것을 권장합니다:

-  recordReviewCount: number; // AI 독서감상문 작성 가능 횟수
+  recordReviewCount: number; // AI 독서감상문 사용 횟수 (0~5)
🤖 Prompt for AI Agents
In src/components/memory/MemoryAddButton/MemoryAddButton.tsx around lines 91 to
102, the UI displays recordReviewCount as the "remaining uses" but
recordReviewCount is actually the number of times used (0–5); change the
displayed value to remaining = Math.max(0, 5 - recordReviewCount) and inject
that remaining value into the disc string instead of recordReviewCount; ensure
the calculation caps at 0 so negative remaining never shows. Also update the
comment in src/types/record.ts at line 83 to clearly state that
recordReviewCount represents the number of times used, not remaining uses.

Copy link
Member Author

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.

@ljh130334 수정 확인했습니다! 사용 횟수로 통일하신 것이 명확하고 일관성 있는 접근이네요. 감사합니다! 👍


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

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: 0

🧹 Nitpick comments (2)
src/pages/aiwrite/AIWrite.tsx (2)

67-68: 의존성 배열 누락에 대한 더 안전한 대안을 고려하세요.

무한 루프를 수정하기 위해 navigateopenSnackbar를 의존성에서 제거한 것은 이해하지만, eslint-disable을 사용하는 것은 잠재적 위험을 내포합니다. 이들 함수가 안정적인 참조라고 가정하고 있지만, 향후 변경 시 버그가 발생할 수 있습니다.

더 안전한 패턴으로 리팩토링을 고려하세요:

  useEffect(() => {
    const fetchAiReview = async () => {
      if (!roomId) return;

      try {
        const result = await createAiReview(Number(roomId));

        if (result.isSuccess) {
          setAiContent(result.data.content);
        } else {
          openSnackbar({
            message: result.message,
            variant: 'top',
            isError: true,
-           onClose: () => {},
          });
          navigate(`/rooms/${roomId}/memory`);
        }
      } catch (error) {
        console.error('AI 독서감상문 생성 실패:', error);
        let errorMessage = 'AI 독서감상문 생성에 실패했습니다';

        if (error && typeof error === 'object' && 'response' in error) {
          const axiosError = error as {
            response?: {
              data?: {
                message?: string;
              };
            };
          };
          if (axiosError.response?.data?.message) {
            errorMessage = axiosError.response.data.message;
          }
        }

        openSnackbar({
          message: errorMessage,
          variant: 'top',
          isError: true,
-         onClose: () => {},
        });
        navigate(`/rooms/${roomId}/memory`);
      } finally {
        setIsLoading(false);
      }
    };

    fetchAiReview();
-   // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [roomId]);
+ }, [roomId, navigate, openSnackbar]);

navigateopenSnackbar는 실제로 안정적인 참조이므로 의존성에 포함해도 무한 루프가 발생하지 않아야 합니다. 만약 여전히 문제가 발생한다면, usePopupStore의 구현을 확인하여 메모이제이션이 적절히 되어 있는지 검토해야 합니다.


41-52: 에러 메시지 추출 로직을 유틸리티 함수로 추출하는 것을 권장합니다.

수동으로 axios 에러를 파싱하는 현재 코드는 장황하고 타입 안정성이 낮습니다. 재사용 가능한 유틸리티 함수로 추출하면 코드베이스 전반에서 일관된 에러 처리가 가능합니다.

유틸리티 함수를 생성하여 사용하세요 (예: src/utils/errorHandler.ts):

// src/utils/errorHandler.ts
import { AxiosError } from 'axios';

export const extractErrorMessage = (error: unknown, fallback: string): string => {
  if (error instanceof AxiosError) {
    return error.response?.data?.message ?? fallback;
  }
  return fallback;
};

그런 다음 이 함수를 사용하도록 리팩토링:

      } catch (error) {
        console.error('AI 독서감상문 생성 실패:', error);
-       let errorMessage = 'AI 독서감상문 생성에 실패했습니다';
-
-       if (error && typeof error === 'object' && 'response' in error) {
-         const axiosError = error as {
-           response?: {
-             data?: {
-               message?: string;
-             };
-           };
-         };
-         if (axiosError.response?.data?.message) {
-           errorMessage = axiosError.response.data.message;
-         }
-       }
+       const errorMessage = extractErrorMessage(
+         error,
+         'AI 독서감상문 생성에 실패했습니다'
+       );

        openSnackbar({
          message: errorMessage,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9558164 and f82faca.

📒 Files selected for processing (1)
  • src/pages/aiwrite/AIWrite.tsx (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/aiwrite/AIWrite.tsx (2)
src/hooks/usePopupActions.ts (1)
  • usePopupActions (9-35)
src/api/record/createAiReview.ts (1)
  • createAiReview (8-13)
🔇 Additional comments (6)
src/pages/aiwrite/AIWrite.tsx (6)

10-10: API 연동 및 상태 관리 초기화가 적절합니다.

새로운 API 함수를 임포트하고 동적 콘텐츠를 위한 상태를 추가한 것이 올바릅니다.

Also applies to: 17-17


28-36: navigate 호출 후 잠재적인 상태 업데이트 경고를 확인하세요.

에러 처리와 실패 케이스에서 navigate()를 호출하지만, 컴포넌트가 언마운트되기 전에 다른 비동기 작업이 여전히 진행 중일 수 있습니다. 현재 코드는 finally 블록에서 setIsLoading(false)를 호출하는데, 이는 navigate 후에도 실행됩니다.

개발 환경에서 다음을 확인해 주세요:

  1. API 에러 시나리오를 테스트하여 콘솔에 "Can't perform a React state update on an unmounted component" 경고가 나타나는지 확인
  2. 경고가 발생한다면, cleanup 로직이나 isMounted 플래그 추가를 고려

예시 개선안:

useEffect(() => {
  let isMounted = true;
  
  const fetchAiReview = async () => {
    if (!roomId) return;

    try {
      const result = await createAiReview(Number(roomId));

      if (!isMounted) return;

      if (result.isSuccess) {
        setAiContent(result.data.content);
      } else {
        openSnackbar({
          message: result.message,
          variant: 'top',
          isError: true,
        });
        navigate(`/rooms/${roomId}/memory`);
      }
    } catch (error) {
      if (!isMounted) return;
      // ... error handling
    } finally {
      if (isMounted) {
        setIsLoading(false);
      }
    }
  };

  fetchAiReview();
  
  return () => {
    isMounted = false;
  };
}, [roomId]);

Also applies to: 54-60


83-100: 클립보드 복사 로직이 올바르게 업데이트되었습니다.

동적 AI 콘텐츠를 복사하도록 변경된 것이 적절하며, 에러 처리도 잘 구현되어 있습니다.


124-124: 콘텐츠 렌더링이 올바르게 구현되었습니다.

동적으로 로드된 AI 콘텐츠를 표시하도록 정확하게 변경되었습니다.


135-138: 반응형 레이아웃 개선이 적절합니다.

컨테이너에 최소/최대 너비 제약과 중앙 정렬을 추가하여 다양한 화면 크기에서 더 나은 사용자 경험을 제공합니다.


215-218: 복사 버튼 중앙 정렬이 올바르게 구현되었습니다.

고정 위치 버튼을 화면 중앙에 배치하고 컨테이너의 최대 너비와 일치시켜 일관된 레이아웃을 유지합니다.

@ljh130334 ljh130334 merged commit 63c2bc6 into develop Oct 30, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📬 API 서버 API 통신

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant