Repository: mash-up-kr/GSS-Web
Pull request #7: feat: axios api 초기 세팅 
Author: login: jeongwoo903
Labels: name: ✨ Feature
description: 기능 구현
name: 🌐 API
description: API 통신
Created: 2025-05-15T18:22:09Z | Merged: 2025-05-18T15:02:48Z

<!--
# For Maintainers
- 최소한의 설명(팀에 속하지 않은 사람에게 PR을 이해하도록 설명하는 것을 목표로 합니다.)
- 코드 변경사항의 논리를 PR 리뷰어에게 설명하기 위해 필요한 경우 해당 코드 라인에 Review Comment를 추가합니다.
-->

## What?

> 어떤 작업을 했는지 기록합니다.

- axios api 초기 세팅을 진행 했습니다.
- 기본 인스턴스 설정 및 각 인터셉터와 에러 핸들러를 정의했습니다.
- 상황에 따른 에러 핸들링을 해두었습니다.

## Why?

> 왜 이 작업을 했는지 작성합니다.

- 추후 모든 API 호출이 공통된 규칙과 방식으로 처리되도록 기본 세팅을 진행했습니다.
- axios로 작업을 진행한 이유는 이전 기술스택 합의 때, **fetch의 추상화 단계에 대한 리소스를 절약하기 위해** axios 사용하는 것으로 결정했기 때문입니다.

## How?

> 어떤 식으로 작업을 했는지 기록합니다.

### 설명
우선 [해당 블로그](https://velog.io/@bokjunwoo/Axios-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0%EC%97%90%EC%84%9C-%EC%97%90%EB%9F%AC-%ED%95%B8%EB%93%A4%EB%A7%81%ED%95%98%EA%B8%B0)의 구조를 참고하여 공식문서와 여러 자료를 조합해 프로젝트 성격에 맞게 세팅해 보았습니다.

이전 회의에서 `access token`과 `refresh token`을 사용한다곤 했지만, 아직 충분한 논의가 이뤄지지 않았기 때문에 토큰에 대한 부분은 백엔드의 작업 진행도를 보며 추가적으로 고도화 하겠습니다.

이에 따라, 추가 작업이 쉽도록 확장성을 고려해 각 인터셉터와 에러 핸들러를 모듈화해 두었습니다.
> 메세지 상수값 분리, 일급 값 사용, 함수형 프로그래밍 방식을 적용하기 위해 시도를 해보았으나 **가독성 저해, 더 많은 모듈화, 타입 통일에 대한 문제**로 인해 현재 방향으로 구현을 결정하였습니다.

의문이 가는 부분이나 추가적으로 세팅이 필요하다고 느끼시는 부분이 있으면 말씀해주시면 감사하겠습니다! ☺️
> ps. 웹팀 노션에 개념을 가볍게 정리해 봤습니다.([링크](https://www.notion.so/Axios-1f2bee7f59968082a1c4da1c0ab987d6?pvs=4)) 기초적인 내용이지만 정리나 복습하는데 도움이 될 것 같아 문서화 해봤습니다

### 다이어그램
```mermaid
flowchart TD
    subgraph Client
        A[애플리케이션] --> B[API 요청]
    end
    
    subgraph AxiosInstance[Axios 인스턴스]
        B --> C{Request Interceptor}
        C -->|성공| D[onRequest]
        C -->|실패| E[onRequestError]
        
        D --> F[HTTP 요청]
        
        subgraph RequestErrorHandling[onRequestError 에러 처리]
            E --> E1{에러 유형}
            E1 --> E2[요청 실패]
            E1 --> E3[응답 없음]
            E1 --> E4[일반 에러]
            
            E2 & E3 & E4 --> G[에러 반환]
        end
        
        F --> H{Response Interceptor}
        H -->|성공| I[onResponse]
        H -->|실패| J[onResponseError]
        
        J -->|상태별 에러 처리| K[에러 처리]
    end
    
    subgraph ResponseErrorHandling[onResponseError 에러 처리]
        K -->|400| L[잘못된 요청]
        K -->|401| M[인증 실패]
        K -->|403| N[권한 없음]
        K -->|404| O[페이지 없음]
        K -->|500| P[서버 오류]
        K -->|TimeoutError| T[요청 시간 초과]
        K -->|기타| Q[기타 에러]
    end
    
    I --> R[응답 데이터]
    R --> A
    
    G --> A
    L & M & N & O & P & T & Q --> A

    style Client fill:#d4f1f9,stroke:#333,stroke-width:2px
    style AxiosInstance fill:#ffe6cc,stroke:#333,stroke-width:2px
    style RequestErrorHandling fill:#ffcccc,stroke:#333,stroke-width:2px
    style ResponseErrorHandling fill:#ffcccc,stroke:#333,stroke-width:2px
```

## Check List

- [x] Merge 할 브랜치가 올바른가?


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **신규 기능**
  - Axios를 활용한 API 클라이언트가 추가되어 HTTP 요청 기능이 제공됩니다.

- **작업**
  - 외부 라이브러리 'axios'가 새롭게 추가되었습니다.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/package.json b/package.json
index 0b1515a..c9cf12d 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
     ]
   },
   "dependencies": {
+    "axios": "^1.9.0",
     "next": "15.3.1",
     "react": "^19.0.0",
     "react-dom": "^19.0.0"
diff --git a/src/apis/index.ts b/src/apis/index.ts
new file mode 100644
index 0000000..377ede9
--- /dev/null
+++ b/src/apis/index.ts
@@ -0,0 +1,50 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+interface IErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const { API_BASE_URL } = process.env;
+
+const instance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 30000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig) => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse) => {
+  return response;
+};
+
+const onError = (error: AxiosError<IErrorResponse>) => {
+  if (error.response) {
+    return Promise.reject(error);
+  }
+
+  if (error.request) {
+    return Promise.reject(error.request);
+  }
+
+  if (error.message) {
+    return Promise.reject(error.message);
+  }
+
+  return Promise.reject(error);
+};
+
+const setInterceptors = (axiosInstance: AxiosInstance) => {
+  axiosInstance.interceptors.request.use(onRequest, onError);
+  axiosInstance.interceptors.response.use(onResponse, onError);
+
+  return axiosInstance;
+};
+
+export default setInterceptors(instance);
diff --git a/yarn.lock b/yarn.lock
index aee513a..09844b1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1301,6 +1301,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"asynckit@npm:^0.4.0":
+  version: 0.4.0
+  resolution: "asynckit@npm:0.4.0"
+  checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d
+  languageName: node
+  linkType: hard
+
 "at-least-node@npm:^1.0.0":
   version: 1.0.0
   resolution: "at-least-node@npm:1.0.0"
@@ -1324,6 +1331,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"axios@npm:^1.9.0":
+  version: 1.9.0
+  resolution: "axios@npm:1.9.0"
+  dependencies:
+    follow-redirects: "npm:^1.15.6"
+    form-data: "npm:^4.0.0"
+    proxy-from-env: "npm:^1.1.0"
+  checksum: 10c0/9371a56886c2e43e4ff5647b5c2c3c046ed0a3d13482ef1d0135b994a628c41fbad459796f101c655e62f0c161d03883454474d2e435b2e021b1924d9f24994c
+  languageName: node
+  linkType: hard
+
 "axobject-query@npm:^4.1.0":
   version: 4.1.0
   resolution: "axobject-query@npm:4.1.0"
@@ -1629,6 +1647,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"combined-stream@npm:^1.0.8":
+  version: 1.0.8
+  resolution: "combined-stream@npm:1.0.8"
+  dependencies:
+    delayed-stream: "npm:~1.0.0"
+  checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5
+  languageName: node
+  linkType: hard
+
 "commander@npm:^13.1.0":
   version: 13.1.0
   resolution: "commander@npm:13.1.0"
@@ -1872,6 +1899,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"delayed-stream@npm:~1.0.0":
+  version: 1.0.0
+  resolution: "delayed-stream@npm:1.0.0"
+  checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19
+  languageName: node
+  linkType: hard
+
 "detect-file@npm:^1.0.0":
   version: 1.0.0
   resolution: "detect-file@npm:1.0.0"
@@ -2703,6 +2737,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"follow-redirects@npm:^1.15.6":
+  version: 1.15.9
+  resolution: "follow-redirects@npm:1.15.9"
+  peerDependenciesMeta:
+    debug:
+      optional: true
+  checksum: 10c0/5829165bd112c3c0e82be6c15b1a58fa9dcfaede3b3c54697a82fe4a62dd5ae5e8222956b448d2f98e331525f05d00404aba7d696de9e761ef6e42fdc780244f
+  languageName: node
+  linkType: hard
+
 "for-each@npm:^0.3.3, for-each@npm:^0.3.5":
   version: 0.3.5
   resolution: "for-each@npm:0.3.5"
@@ -2712,6 +2756,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"form-data@npm:^4.0.0":
+  version: 4.0.2
+  resolution: "form-data@npm:4.0.2"
+  dependencies:
+    asynckit: "npm:^0.4.0"
+    combined-stream: "npm:^1.0.8"
+    es-set-tostringtag: "npm:^2.1.0"
+    mime-types: "npm:^2.1.12"
+  checksum: 10c0/e534b0cf025c831a0929bf4b9bbe1a9a6b03e273a8161f9947286b9b13bf8fb279c6944aae0070c4c311100c6d6dbb815cd955dc217728caf73fad8dc5b8ee9c
+  languageName: node
+  linkType: hard
+
 "fs-extra@npm:9.1.0":
   version: 9.1.0
   resolution: "fs-extra@npm:9.1.0"
@@ -2936,6 +2992,7 @@ __metadata:
     "@types/react-dom": "npm:^19"
     "@typescript-eslint/eslint-plugin": "npm:^8.31.1"
     "@typescript-eslint/parser": "npm:^8.31.1"
+    axios: "npm:^1.9.0"
     commitizen: "npm:^4.3.1"
     cz-conventional-changelog: "npm:^3.3.0"
     cz-customizable: "npm:^7.4.0"
@@ -3959,6 +4016,22 @@ __metadata:
   languageName: node
   linkType: hard
 
+"mime-db@npm:1.52.0":
+  version: 1.52.0
+  resolution: "mime-db@npm:1.52.0"
+  checksum: 10c0/0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa
+  languageName: node
+  linkType: hard
+
+"mime-types@npm:^2.1.12":
+  version: 2.1.35
+  resolution: "mime-types@npm:2.1.35"
+  dependencies:
+    mime-db: "npm:1.52.0"
+  checksum: 10c0/82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2
+  languageName: node
+  linkType: hard
+
 "mimic-fn@npm:^1.0.0":
   version: 1.2.0
   resolution: "mimic-fn@npm:1.2.0"
@@ -4530,6 +4603,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"proxy-from-env@npm:^1.1.0":
+  version: 1.1.0
+  resolution: "proxy-from-env@npm:1.1.0"
+  checksum: 10c0/fe7dd8b1bdbbbea18d1459107729c3e4a2243ca870d26d34c2c1bcd3e4425b7bcc5112362df2d93cc7fb9746f6142b5e272fd1cc5c86ddf8580175186f6ad42b
+  languageName: node
+  linkType: hard
+
 "punycode@npm:^2.1.0":
   version: 2.3.1
   resolution: "punycode@npm:2.3.1"


Reviews:
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T08:50:33Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T11:47:52Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T11:55:02Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T12:19:04Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (4)</summary><blockquote>

<details>
<summary>src/apis/index.ts (4)</summary><blockquote>

`7-7`: **timeout 값에 대해 백엔드와 논의해 보세요.**

timeout이 10초로 설정되어 있습니다. 이 값은 백엔드 API의 특성과 요구사항에 따라 적절하게 조정되어야 할 수 있습니다.

---

`28-38`: **switch문 대신 if문을 사용해 가독성을 개선하세요.**

switch(true) 패턴은 가독성을 저하시킬 수 있습니다. if-else 구문으로 변경하여 코드의 의도를 더 명확하게 표현할 수 있습니다.


```diff
-  switch (true) {
-    case Boolean(error?.config):
-      onError(status, '요청이 실패했습니다.');
-      break;
-    case Boolean(error?.request):
-      onError(status, '응답이 없습니다.');
-      break;
-    default:
-      onError(status, `에러가 발생했습니다. ${error.message}`);
-      break;
-  }
+  if (error?.config) {
+    onError(status, '요청이 실패했습니다.');
+  } else if (error?.request) {
+    onError(status, '응답이 없습니다.');
+  } else {
+    onError(status, `에러가 발생했습니다. ${error.message}`);
+  }
```

---

`88-88`: **기본 내보내기(default export)를 고려해 보세요.**

현재 코드는 api 상수를 named export로 내보내고 있습니다. 이 파일의 주요 목적이 api 인스턴스를 제공하는 것이라면, default export를 사용하는 것이 더 명확할 수 있습니다.


```diff
-export const api = setInterceptors(instance);
+const api = setupAxiosInterceptors(instance);
+
+export default api;
```

---

`46-70`: _🛠️ Refactor suggestion_

**에러 처리 로직을 개선해 보세요.**

현재 에러 처리 로직은 상태 코드에 따른 고정된 메시지만 제공하고 있습니다. 백엔드가 반환하는 실제 에러 메시지나 코드를 활용하여 더 상세한 정보를 제공하는 것이 디버깅에 도움이 될 수 있습니다.


```diff
  if (axios.isAxiosError(error)) {
+    const errorResponse = error.response?.data;
+    const serverMessage = errorResponse?.message || '';
+    const errorCode = errorResponse?.code || '';
+    
    switch (status) {
      case 400:
-        onError(status, '잘못된 요청입니다.');
+        onError(status, `잘못된 요청입니다. ${serverMessage ? `(${errorCode}: ${serverMessage})` : ''}`);
        break;
      case 401: {
-        onError(status, '인증 실패입니다.');
+        onError(status, `인증 실패입니다. ${serverMessage ? `(${errorCode}: ${serverMessage})` : ''}`);
        break;
      }
      // 다른 case문들도 동일하게 수정...
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>src/apis/index.ts (3)</summary><blockquote>

`3-3`: **환경 변수 유효성 검사 고려해 보세요.**

`process.env.NEXT_PUBLIC_API_BASE_URL`의 값이 존재하는지 확인하는 유효성 검사가 없습니다. 이 값이 없을 경우 기본값을 제공하거나 에러를 발생시키는 방식을 고려해 보세요.

```diff
-const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || '';
+
+if (!API_BASE_URL) {
+  console.warn('API_BASE_URL이 설정되지 않았습니다. 환경 변수를 확인해주세요.');
+}
```

---

`80-86`: **인터셉터 설정 함수명을 명확하게 지정해 주세요.**

`setInterceptors`라는 함수명은 다소 일반적입니다. `setupAxiosInterceptors` 같은 보다 구체적인 이름을 사용하면 함수의 용도를 더 명확하게 할 수 있습니다.

```diff
-/** 인터셉터 설정 */
-const setInterceptors = (axiosInstance: AxiosInstance): AxiosInstance => {
+/** Axios 인스턴스에 인터셉터 설정 */
+const setupAxiosInterceptors = (axiosInstance: AxiosInstance): AxiosInstance => {
  axiosInstance.interceptors.request.use(onRequest, onRequestError);
  axiosInstance.interceptors.response.use(onResponse, onResponseError);

  return axiosInstance;
};
```

---

`1-88`: **토큰 관리 로직을 위한 인터셉터를 준비하세요.**

PR 설명에 따르면 access/refresh 토큰 관리는 논의되었지만 백엔드 진행 상황에 따라 연기되었습니다. 이를 위한 인터셉터 구조를 미리 준비하는 것이 향후 구현을 용이하게 할 수 있습니다.


예를 들어 request 인터셉터에서 access 토큰을 헤더에 추가하고, response 인터셉터에서 401 에러 발생 시 refresh 토큰을 사용해 access 토큰을 갱신하는 로직을 추가할 수 있습니다. 이는 백엔드 API 구현이 완료된 후에 구현할 수 있습니다.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

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


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 8bd6f120c59d105fb3eacbbbddc3c11a8669a6b5 and a0d9fffd7f3e6c19e5064c7716baae194b603332.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `yarn.lock` is excluded by `!**/yarn.lock`, `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `package.json` (1 hunks)
* `src/apis/index.ts` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>package.json (1)</summary>

`18-18`: **Axios 의존성이 잘 추가되었습니다.**

프로젝트에 Axios를 사용하기 위한 의존성이 올바르게 추가되었습니다. 버전은 `^1.9.0`으로 지정되어 있어 호환되는 마이너/패치 업데이트까지 자동으로 적용될 것입니다.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-17T13:42:59Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>src/apis/index.ts (2)</summary><blockquote>

`9-13`: **환경변수 미정의 시 기본값 처리 권장**  
`NEXT_PUBLIC_API_BASE_URL`이 설정되지 않으면 `baseURL`이 `undefined`가 되어 잘못된 요청이 발생할 수 있습니다. 필수 환경변수 검사 또는 기본 URL 제공을 추가해주세요.

---

`23-25`: **응답 인터셉터에서 `data`만 반환 고려**  
현재 전체 `response` 객체를 반환하고 있는데, 대부분 호출부에서는 `response.data`만 사용합니다. 소비 코드를 간소화하고 일관된 반환 형태를 위해 `response.data`를 반환하도록 변경을 추천합니다.  

```diff
-const onResponse = (response: AxiosResponse): AxiosResponse => {
-  return response;
-};
+const onResponse = (response: AxiosResponse): any => {
+  return response.data;
+};
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

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


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 8bd6f120c59d105fb3eacbbbddc3c11a8669a6b5 and 20b8cb473597b01cf12a09b0386b4a9a2289ac09.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `yarn.lock` is excluded by `!**/yarn.lock`, `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `package.json` (1 hunks)
* `src/apis/index.ts` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>package.json (1)</summary>

`18-18`: **의존성 추가 확인: Axios 버전 검증 필요**  
`"axios": "^1.9.0"` 의존성이 추가되었습니다. 해당 버전이 유효하며, 보안 취약점(CVE)이 없는지 확인이 필요합니다.

</details>
<details>
<summary>src/apis/index.ts (1)</summary>

`38-38`: **내보내기(export) 형태 일관성 검토**  
현재 `export const api`로 내보내고 있습니다. 팀 전체가 기본(default) 내보내기 방식을 선호한다면 `export default api;`로 변경하여 일관성을 유지하는 방안을 검토해주세요.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-17T13:51:03Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T13:51:54Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T13:58:20Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T13:59:42Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-17T14:01:16Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T14:08:01Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T14:08:37Z
user: login: Pridesd
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T14:33:16Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T14:39:26Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T15:24:04Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T15:25:46Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T15:25:59Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T15:33:10Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-17T15:33:36Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T15:36:54Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-17T15:36:59Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T15:38:35Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-17T15:39:09Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T15:41:00Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-17T15:42:19Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T15:51:18Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T16:01:17Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T16:41:29Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T16:48:13Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-17T16:48:53Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-18T05:45:18Z
user: login: jeongwoo903
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-18T05:46:27Z
user: login: HaJunRyu
body: 👍 굳~~ 이것저것 반영하느라 고생했으 정우~! 주말 마무리 잘 하고 한 주 또 달려보자고ㅎㅎ
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-18T14:39:15Z
user: login: HaJunRyu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-18T14:40:35Z

Review comments:
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
path: src/apis/index.ts
user: login: HaJunRyu
body: P3

이건 백엔드랑 얘기 나누고 기준을 정해서 수정 한번 하면 좋을거 같네~~
created_at: 2025-05-17T06:20:13Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
path: src/apis/index.ts
user: login: HaJunRyu
body: P3

주석으로 처리하기 보다는 함수가 하는일을 함수명으로 표현해주면 어떨까~?
created_at: 2025-05-17T06:20:36Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+/** 에러 처리 */
+const onError = (status: number, message: string) => {
+  throw { status, message };
+};
+
+const onRequestError = (error: AxiosError) => {
+  const status = error.response?.status || 0;
+
+  switch (true) {
+    case Boolean(error?.config):
+      onError(status, '요청이 실패했습니다.');
+      break;
+    case Boolean(error?.request):
+      onError(status, '응답이 없습니다.');
+      break;
+    default:
+      onError(status, `에러가 발생했습니다. ${error.message}`);
+      break;
+  }
+
+  return Promise.reject(error);
+};
+
+const onResponseError = (error: AxiosError | Error) => {
+  const status = axios.isAxiosError(error) && error.response ? error.response.status : 0;
+
+  if (axios.isAxiosError(error)) {
+    switch (status) {
+      case 400:
+        onError(status, '잘못된 요청입니다.');
+        break;
+      case 401: {
+        onError(status, '인증 실패입니다.');
+        break;
+      }
+      case 403: {
+        onError(status, '권한이 없습니다.');
+        break;
+      }
+      case 404: {
+        onError(status, '찾을 수 없는 페이지입니다.');
+        break;
+      }
+      case 500: {
+        onError(status, '서버 오류입니다.');
+        break;
+      }
+      default: {
+        onError(status, `에러가 발생했습니다. ${error?.message}`);
+      }
+    }
path: src/apis/index.ts
user: login: HaJunRyu
body: P3

[구두 논의 내용]
지금은 statusCode에 따른 특정 status와 메시지만으로 디버깅을 해야하는걸로 보여서 디버깅에 어느정도 불편을 겪을 수 있을거 같다는 생각이 드는거 같음, 차라리 로우 레벨 코드에선 error 자체를 throw하고 좀 더 하이 레벨 코드에서 상황에 맞게 추상화 해주게끔 설계하는건 어떨까? 하는 생각이 들었음

그리고 이렇게 베이스단에서 정의하려면 백엔드와 규칙을 맞추고 진행해야 할 필요가 보임
created_at: 2025-05-17T08:40:32Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+/** 에러 처리 */
+const onError = (status: number, message: string) => {
+  throw { status, message };
+};
+
+const onRequestError = (error: AxiosError) => {
+  const status = error.response?.status || 0;
+
+  switch (true) {
+    case Boolean(error?.config):
+      onError(status, '요청이 실패했습니다.');
+      break;
+    case Boolean(error?.request):
+      onError(status, '응답이 없습니다.');
+      break;
+    default:
+      onError(status, `에러가 발생했습니다. ${error.message}`);
+      break;
+  }
path: src/apis/index.ts
user: login: HaJunRyu
body: P3

여기서는 이렇게 switch를 쓰는게 가독성이 좋지 않은거 같아서 if문으로 표현하면 더 좋을듯!
created_at: 2025-05-17T08:46:52Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+/** 에러 처리 */
+const onError = (status: number, message: string) => {
+  throw { status, message };
+};
+
+const onRequestError = (error: AxiosError) => {
+  const status = error.response?.status || 0;
+
+  switch (true) {
+    case Boolean(error?.config):
+      onError(status, '요청이 실패했습니다.');
+      break;
+    case Boolean(error?.request):
+      onError(status, '응답이 없습니다.');
+      break;
+    default:
+      onError(status, `에러가 발생했습니다. ${error.message}`);
+      break;
+  }
+
+  return Promise.reject(error);
+};
+
+const onResponseError = (error: AxiosError | Error) => {
+  const status = axios.isAxiosError(error) && error.response ? error.response.status : 0;
+
+  if (axios.isAxiosError(error)) {
+    switch (status) {
+      case 400:
+        onError(status, '잘못된 요청입니다.');
+        break;
+      case 401: {
+        onError(status, '인증 실패입니다.');
+        break;
+      }
+      case 403: {
+        onError(status, '권한이 없습니다.');
+        break;
+      }
+      case 404: {
+        onError(status, '찾을 수 없는 페이지입니다.');
+        break;
+      }
+      case 500: {
+        onError(status, '서버 오류입니다.');
+        break;
+      }
+      default: {
+        onError(status, `에러가 발생했습니다. ${error?.message}`);
+      }
+    }
+  } else if (error instanceof Error && error.name === 'TimeoutError') {
+    onError(status, '요청 시간이 초과되었습니다.');
+  } else {
+    onError(status, `에러가 발생했습니다. ${error.toString()}`);
+  }
+
+  return Promise.reject(error);
+};
+
+/** 인터셉터 설정 */
+const setInterceptors = (axiosInstance: AxiosInstance): AxiosInstance => {
+  axiosInstance.interceptors.request.use(onRequest, onRequestError);
+  axiosInstance.interceptors.response.use(onResponse, onResponseError);
+
+  return axiosInstance;
+};
+
+export const api = setInterceptors(instance);
path: src/apis/index.ts
user: login: HaJunRyu
body: P4

이건 api base로직에서 export 할 주요 모듈을 export default로 함수 호출한걸 그대로 내보내주는걸 어떨까?
created_at: 2025-05-17T08:50:21Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
path: src/apis/index.ts
user: login: jeongwoo903
body: 오케이~ 타임아웃 관련해서는 한번 물어보겠습니다! 🫡
created_at: 2025-05-17T11:47:52Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
path: src/apis/index.ts
user: login: jeongwoo903
body: 음 사실 영역을 쉽게 구별해서 보려고 한 거였는데 불필요했으려나?
interceptRequest 같은 네이밍도 생각해봤는데, 전체적으로 봤을때 현재 이름이 의미를 파악하기 어려운 건 아니라는 생각도 드네 🤔

=> 주석은 지우고 네이밍은 그대로 가져가는 방향으로 진행하겠습니다!
created_at: 2025-05-17T11:55:02Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+/** 에러 처리 */
+const onError = (status: number, message: string) => {
+  throw { status, message };
+};
+
+const onRequestError = (error: AxiosError) => {
+  const status = error.response?.status || 0;
+
+  switch (true) {
+    case Boolean(error?.config):
+      onError(status, '요청이 실패했습니다.');
+      break;
+    case Boolean(error?.request):
+      onError(status, '응답이 없습니다.');
+      break;
+    default:
+      onError(status, `에러가 발생했습니다. ${error.message}`);
+      break;
+  }
+
+  return Promise.reject(error);
+};
+
+const onResponseError = (error: AxiosError | Error) => {
+  const status = axios.isAxiosError(error) && error.response ? error.response.status : 0;
+
+  if (axios.isAxiosError(error)) {
+    switch (status) {
+      case 400:
+        onError(status, '잘못된 요청입니다.');
+        break;
+      case 401: {
+        onError(status, '인증 실패입니다.');
+        break;
+      }
+      case 403: {
+        onError(status, '권한이 없습니다.');
+        break;
+      }
+      case 404: {
+        onError(status, '찾을 수 없는 페이지입니다.');
+        break;
+      }
+      case 500: {
+        onError(status, '서버 오류입니다.');
+        break;
+      }
+      default: {
+        onError(status, `에러가 발생했습니다. ${error?.message}`);
+      }
+    }
path: src/apis/index.ts
user: login: jeongwoo903
body: 좋은 의견 감사합니다! 전의 백엔드와 논의 했던 구조를 미처 신경쓰지 못했던 것 같습니다..ㅎㅎ
해당 부분은 이전의 백엔드와 논의 하였던
```
{
  "code": "MEMBER_ID_MISMATCH",
  "status": 400,
  "message": "에러 설명 메시지",
}
```
해당 구조를 조금 더 자유롭게 확인할 수 있는 형태로 수정해 두겠습니다!


created_at: 2025-05-17T12:19:04Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,88 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (status: number, message: string) => {
+  throw { status, message };
+};
path: src/apis/index.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**에러 객체를 더 명확하게 생성하세요.**

현재 코드는 단순 객체를 throw하고 있습니다. 이는 표준 Error 객체가 아니기 때문에 스택 추적이 없고 디버깅이 어려울 수 있습니다. Error 클래스를 확장한 사용자 정의 에러 클래스를 사용하면 더 나은 디버깅 경험을 제공할 수 있습니다.

```diff
-const onError = (status: number, message: string) => {
-  throw { status, message };
-};
+class ApiError extends Error {
+  status: number;
+
+  constructor(status: number, message: string) {
+    super(message);
+    this.name = 'ApiError';
+    this.status = status;
+  }
+}
+
+const onError = (status: number, message: string) => {
+  throw new ApiError(status, message);
+};
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion
class ApiError extends Error {
  status: number;

  constructor(status: number, message: string) {
    super(message);
    this.name = 'ApiError';
    this.status = status;
  }
}

const onError = (status: number, message: string) => {
  throw new ApiError(status, message);
};
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In src/apis/index.ts around lines 21 to 23, the onError function throws a plain
object instead of an Error instance, which lacks stack trace and hinders
debugging. Define a custom error class extending the Error class that includes
status and message properties, then throw an instance of this custom error in
onError to provide clearer error information and maintain stack trace.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-17T13:42:59Z
author_association: NONE
diff_hunk: @@ -0,0 +1,88 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (status: number, message: string) => {
+  throw { status, message };
+};
+
+const onRequestError = (error: AxiosError) => {
+  const status = error.response?.status || 0;
+
+  switch (true) {
+    case Boolean(error?.config):
+      onError(status, '요청이 실패했습니다.');
+      break;
+    case Boolean(error?.request):
+      onError(status, '응답이 없습니다.');
+      break;
+    default:
+      onError(status, `에러가 발생했습니다. ${error.message}`);
+      break;
+  }
+
+  return Promise.reject(error);
+};
+
+const onResponseError = (error: AxiosError | Error) => {
+  const status = axios.isAxiosError(error) && error.response ? error.response.status : 0;
+
+  if (axios.isAxiosError(error)) {
+    switch (status) {
+      case 400:
+        onError(status, '잘못된 요청입니다.');
+        break;
+      case 401: {
+        onError(status, '인증 실패입니다.');
+        break;
+      }
+      case 403: {
+        onError(status, '권한이 없습니다.');
+        break;
+      }
+      case 404: {
+        onError(status, '찾을 수 없는 페이지입니다.');
+        break;
+      }
+      case 500: {
+        onError(status, '서버 오류입니다.');
+        break;
+      }
+      default: {
+        onError(status, `에러가 발생했습니다. ${error?.message}`);
+      }
+    }
+  } else if (error instanceof Error && error.name === 'TimeoutError') {
+    onError(status, '요청 시간이 초과되었습니다.');
+  } else {
+    onError(status, `에러가 발생했습니다. ${error.toString()}`);
+  }
path: src/apis/index.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**TimeoutError 체크 로직을 개선해 주세요.**

현재 코드는 Error 객체의 name 속성이 'TimeoutError'인지 확인하고 있지만, Axios는 명시적으로 이런 이름의 에러를 발생시키지 않습니다. 일반적으로 Axios의 타임아웃은 ECONNABORTED 코드가 있는 AxiosError로 처리됩니다.

```diff
-  } else if (error instanceof Error && error.name === 'TimeoutError') {
+  } else if (axios.isAxiosError(error) && error.code === 'ECONNABORTED') {
    onError(status, '요청 시간이 초과되었습니다.');
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion
  } else if (axios.isAxiosError(error) && error.code === 'ECONNABORTED') {
    onError(status, '요청 시간이 초과되었습니다.');
  } else {
    onError(status, `에러가 발생했습니다. ${error.toString()}`);
  }
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In src/apis/index.ts around lines 71 to 75, the current timeout error check
relies on error.name being 'TimeoutError', which Axios does not use. Instead,
check if the error is an AxiosError and if its code property equals
'ECONNABORTED' to properly detect timeout errors. Update the condition to verify
these Axios-specific properties for accurate timeout error handling.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-17T13:42:59Z
author_association: NONE
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
path: src/apis/index.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**InternalAxiosRequestConfig 대신 AxiosRequestConfig 사용 권장**  
`InternalAxiosRequestConfig`는 내부 구현용 타입으로, 외부 공개 API에는 `AxiosRequestConfig`를 사용하는 것이 안정적입니다.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In src/apis/index.ts at line 1, replace the import of InternalAxiosRequestConfig
with AxiosRequestConfig from 'axios' because InternalAxiosRequestConfig is meant
for internal use and AxiosRequestConfig is the stable public type to use in
external APIs. Update the import statement accordingly to import
AxiosRequestConfig instead of InternalAxiosRequestConfig.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-17T13:51:02Z
author_association: NONE
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
path: src/apis/index.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**요청 인터셉터 타입 정정**  
`onRequest`의 파라미터와 반환 타입을 `AxiosRequestConfig`로 변경하여 public API 명세에 맞게 수정해주세요.  

```diff
-const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
-  return Promise.resolve(config);
-};
+const onRequest = (config: AxiosRequestConfig): Promise<AxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion
const onRequest = (config: AxiosRequestConfig): Promise<AxiosRequestConfig> => {
  return Promise.resolve(config);
};
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In src/apis/index.ts around lines 19 to 21, the onRequest function currently
uses InternalAxiosRequestConfig for its parameter and return type, but it should
use AxiosRequestConfig to align with the public API specification. Change both
the parameter type and the return type of onRequest to AxiosRequestConfig
instead of InternalAxiosRequestConfig.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-17T13:51:03Z
author_association: NONE
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (error: AxiosError<ErrorResponse> | Error) => {
+  return Promise.reject(error);
+};
path: src/apis/index.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**오류 인터셉터에서 에러 변환 로직 강화 필요**  
현재 원본 에러를 그대로 reject하고 있어, 호출부에서 일관된 에러 구조를 기대하기 어렵습니다.  
- `axios.isAxiosError`로 타입 가드를 수행하고, `error.response?.data`를 `ErrorResponse` 형태로 반환  
- 또는 커스텀 `ApiError` 클래스를 정의해 `throw new ApiError(status, message)`  
와 같이 에러 구조를 통일해주세요.  

```diff
-const onError = (error: AxiosError<ErrorResponse> | Error) => {
-  return Promise.reject(error);
-};
+const onError = (error: AxiosError<ErrorResponse>): Promise<never> => {
+  if (axios.isAxiosError(error) && error.response?.data) {
+    return Promise.reject(error.response.data);
+  }
+  return Promise.reject(error);
+};
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion
const onError = (error: AxiosError<ErrorResponse>): Promise<never> => {
  if (axios.isAxiosError(error) && error.response?.data) {
    return Promise.reject(error.response.data);
  }
  return Promise.reject(error);
};
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In src/apis/index.ts around lines 27 to 29, the onError function currently
rejects the original error directly, causing inconsistent error structures for
callers. Modify the function to first check if the error is an Axios error using
axios.isAxiosError; if so, extract error.response?.data as an ErrorResponse and
throw a new custom ApiError instance with the status and message from that data.
Define the ApiError class to encapsulate status and message, ensuring all errors
thrown have a consistent structure.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-17T13:51:03Z
author_association: NONE
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+/** 에러 처리 */
+const onError = (status: number, message: string) => {
+  throw { status, message };
+};
+
+const onRequestError = (error: AxiosError) => {
+  const status = error.response?.status || 0;
+
+  switch (true) {
+    case Boolean(error?.config):
+      onError(status, '요청이 실패했습니다.');
+      break;
+    case Boolean(error?.request):
+      onError(status, '응답이 없습니다.');
+      break;
+    default:
+      onError(status, `에러가 발생했습니다. ${error.message}`);
+      break;
+  }
path: src/apis/index.ts
user: login: jeongwoo903
body: 위 내용을 수정 하면서 일괄 통합했습니다!
created_at: 2025-05-17T13:51:54Z
author_association: MEMBER
diff_hunk: @@ -1,5 +1,11 @@
 import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
 
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
path: src/apis/index.ts
user: login: HaJunRyu
body: P3

해당 interface는 export 처리 해준 이유가 있을까~? 만약 외부에서 참조해서 쓰여야 한다면 interface 선언 위치가 base api를 선언하는곳이 아닌 types라는 레벨이 따로 존재하면 참조 위계를 명확히 정해서 순환 참조를 보다 쉽게 관리할 수 있을거 같은데 :)
created_at: 2025-05-17T13:55:32Z
author_association: MEMBER
diff_hunk: @@ -1,5 +1,11 @@
 import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
 
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
 const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
path: src/apis/index.ts
user: login: HaJunRyu
body: P4

아 근데 이거 생각해보니 브라우저에서 참조되는게 아니라 빌드타임때 참조되는거라 env key값의 prefix로 NEXT_PUBLIC 붙여줄 필요 없지 않나~?
created_at: 2025-05-17T13:57:54Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
path: src/apis/index.ts
user: login: jeongwoo903
body: > Type 'AxiosRequestConfig' is not assignable to type 'InternalAxiosRequestConfig'

interceptors.request.use()는 첫 번째 인자 타입으로 InternalAxiosRequestConfig를 요구하는데?
created_at: 2025-05-17T13:59:42Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
path: src/apis/index.ts
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

@jeongwoo903 죄송합니다, 제 제안이 잘못되었습니다. 귀하의 지적이 정확합니다.

Axios v1.9.0에서 `interceptors.request.use()`는 `InternalAxiosRequestConfig` 타입을 첫 번째 인자로 요구합니다. `AxiosRequestConfig`는 public API에서 사용되는 타입이지만, 인터셉터 내부에서는 `InternalAxiosRequestConfig`를 사용해야 합니다.

기존 코드가 정확히 올바른 타입을 사용하고 있으므로, 변경할 필요가 없습니다. 잘못된 제안을 드려 혼란을 드려 죄송합니다.



^ㅅ^
        /)/)
       ( . .)
       c(")(")

---

🌐 Web query:

```web
Axios v1.9.0 TypeScript types for request interceptors - difference between AxiosRequestConfig and InternalAxiosRequestConfig
```

💡 Result:

In Axios v1.9.0, when working with request interceptors in TypeScript, it's important to understand the distinction between `AxiosRequestConfig` and `InternalAxiosRequestConfig`.

**`AxiosRequestConfig`**:
- This is the public configuration interface used when creating Axios instances or making requests.
- It includes properties like `headers`, `params`, `data`, and others that define the request.

**`InternalAxiosRequestConfig`**:
- This is an internal interface that extends `AxiosRequestConfig` but with a more specific definition for the `headers` property.
- In `InternalAxiosRequestConfig`, the `headers` property is defined as `AxiosRequestHeaders`, which is a more precise type.

**Key Differences**:
- **Headers Property**: The primary difference lies in the `headers` property. In `AxiosRequestConfig`, `headers` can be of type `(RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders;`, whereas in `InternalAxiosRequestConfig`, it's strictly `AxiosRequestHeaders;`. This distinction ensures that the `headers` object in internal configurations adheres to a more specific structure, reducing potential type mismatches.

**Usage in Interceptors**:
- When defining request interceptors, it's recommended to use `InternalAxiosRequestConfig` for the `config` parameter. This is because the interceptor operates on the internal configuration, which has the more specific `headers` type.

**Example**:

```typescript
import axios, { InternalAxiosRequestConfig } from 'axios';

axios.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // Modify the config as needed
    config.headers['Authorization'] = 'Bearer token';
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);
```


By using `InternalAxiosRequestConfig` in interceptors, you align with Axios's internal type definitions, ensuring type safety and consistency in your TypeScript code.

---



</details>

@jeongwoo903 웹 검색 결과가 귀하의 말씀을 정확히 확인해주었습니다.

`InternalAxiosRequestConfig`는 `AxiosRequestConfig`를 확장한 내부 인터페이스로, 특히 `headers` 속성에 대해 더 구체적인 타입 정의(`AxiosRequestHeaders`)를 가집니다. Axios의 요청 인터셉터에서는 `InternalAxiosRequestConfig`를 사용하는 것이 올바른 방법입니다.

웹 검색 결과에서 제공된 예시에서도 인터셉터에서 `InternalAxiosRequestConfig`를 사용하는 것을 권장하고 있으니, 귀하의 원래 구현이 타입 안전성과 일관성을 보장하는 올바른 방법입니다.

잘못된 제안을 드려 죄송합니다.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: jeongwoo903
PR: mash-up-kr/GSS-Web#7
File: src/apis/index.ts:19-21
Timestamp: 2025-05-17T14:01:14.862Z
Learning: In Axios v1.9.0, request interceptors require `InternalAxiosRequestConfig` type, not `AxiosRequestConfig`. The `interceptors.request.use()` method specifically expects handlers that work with `InternalAxiosRequestConfig` which extends `AxiosRequestConfig` with additional internal properties.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer><details>
<summary>🧩 Analysis chain</summary>

@jeongwoo903 죄송합니다, 제 제안이 잘못되었습니다. 귀하의 지적이 정확합니다.

Axios v1.9.0에서 `interceptors.request.use()`는 `InternalAxiosRequestConfig` 타입을 첫 번째 인자로 요구합니다. `AxiosRequestConfig`는 public API에서 사용되는 타입이지만, 인터셉터 내부에서는 `InternalAxiosRequestConfig`를 사용해야 합니다.

기존 코드가 정확히 올바른 타입을 사용하고 있으므로, 변경할 필요가 없습니다. 잘못된 제안을 드려 혼란을 드려 죄송합니다.



^ㅅ^
        /)/)
       ( . .)
       c(")(")

---

🌐 Web query:

```web
Axios v1.9.0 TypeScript types for request interceptors - difference between AxiosRequestConfig and InternalAxiosRequestConfig
```

💡 Result:

In Axios v1.9.0, when working with request interceptors in TypeScript, it's important to understand the distinction between `AxiosRequestConfig` and `InternalAxiosRequestConfig`.

**`AxiosRequestConfig`**:
- This is the public configuration interface used when creating Axios instances or making requests.
- It includes properties like `headers`, `params`, `data`, and others that define the request.

**`InternalAxiosRequestConfig`**:
- This is an internal interface that extends `AxiosRequestConfig` but with a more specific definition for the `headers` property.
- In `InternalAxiosRequestConfig`, the `headers` property is defined as `AxiosRequestHeaders`, which is a more precise type.

**Key Differences**:
- **Headers Property**: The primary difference lies in the `headers` property. In `AxiosRequestConfig`, `headers` can be of type `(RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders;`, whereas in `InternalAxiosRequestConfig`, it's strictly `AxiosRequestHeaders;`. This distinction ensures that the `headers` object in internal configurations adheres to a more specific structure, reducing potential type mismatches.

**Usage in Interceptors**:
- When defining request interceptors, it's recommended to use `InternalAxiosRequestConfig` for the `config` parameter. This is because the interceptor operates on the internal configuration, which has the more specific `headers` type.

**Example**:

```typescript
import axios, { InternalAxiosRequestConfig } from 'axios';

axios.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // Modify the config as needed
    config.headers['Authorization'] = 'Bearer token';
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);
```


By using `InternalAxiosRequestConfig` in interceptors, you align with Axios's internal type definitions, ensuring type safety and consistency in your TypeScript code.

---



</details>

@jeongwoo903 웹 검색 결과가 귀하의 말씀을 정확히 확인해주었습니다.

`InternalAxiosRequestConfig`는 `AxiosRequestConfig`를 확장한 내부 인터페이스로, 특히 `headers` 속성에 대해 더 구체적인 타입 정의(`AxiosRequestHeaders`)를 가집니다. Axios의 요청 인터셉터에서는 `InternalAxiosRequestConfig`를 사용하는 것이 올바른 방법입니다.

웹 검색 결과에서 제공된 예시에서도 인터셉터에서 `InternalAxiosRequestConfig`를 사용하는 것을 권장하고 있으니, 귀하의 원래 구현이 타입 안전성과 일관성을 보장하는 올바른 방법입니다.

잘못된 제안을 드려 죄송합니다.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: jeongwoo903
PR: mash-up-kr/GSS-Web#7
File: src/apis/index.ts:19-21
Timestamp: 2025-05-17T14:01:14.862Z
Learning: In Axios v1.9.0, request interceptors require `InternalAxiosRequestConfig` type, not `AxiosRequestConfig`. The `interceptors.request.use()` method specifically expects handlers that work with `InternalAxiosRequestConfig` which extends `AxiosRequestConfig` with additional internal properties.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-17T14:01:16Z
author_association: NONE
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
path: src/apis/index.ts
user: login: HaJunRyu
body: 예스 백엔드에 물어보고 그걸 그대로 적용 할 필요는 없고 참고 정도로만 생각하면 될거 같긴해, 우리는 결국 얼마나 최대로 request에 대한 response를 기다릴지 정하는거니까 과도하다고 판단되는 시간 이하로 지정하면 될 것 같아

그리고 개인적으로 1000 * 10 이런식으로 쓰는게 좀 더 가독성이 좋은거 같긴합니다ㅎㅎ 그리고 10초는 너무 타이트 한거 같긴해서 임의로 넣어놓는다고 해도 30초 ~ 60초 정도 해놔도 상관은 없을듯~?
created_at: 2025-05-17T14:08:01Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
path: src/apis/index.ts
user: login: HaJunRyu
body: 예스 나도 지금 함수 몸체를 봤을때는 지금 이름으로 해주는게 좋다고 생각되고 이후 함수에 동작을 추가할때 함수 시그니처를 그에 맞춰서 수정해주면 되지 않을까싶네
created_at: 2025-05-17T14:08:37Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+/** 에러 처리 */
+const onError = (status: number, message: string) => {
+  throw { status, message };
+};
+
+const onRequestError = (error: AxiosError) => {
+  const status = error.response?.status || 0;
+
+  switch (true) {
+    case Boolean(error?.config):
+      onError(status, '요청이 실패했습니다.');
+      break;
+    case Boolean(error?.request):
+      onError(status, '응답이 없습니다.');
+      break;
+    default:
+      onError(status, `에러가 발생했습니다. ${error.message}`);
+      break;
+  }
+
+  return Promise.reject(error);
+};
+
+const onResponseError = (error: AxiosError | Error) => {
+  const status = axios.isAxiosError(error) && error.response ? error.response.status : 0;
+
+  if (axios.isAxiosError(error)) {
+    switch (status) {
+      case 400:
+        onError(status, '잘못된 요청입니다.');
+        break;
+      case 401: {
+        onError(status, '인증 실패입니다.');
+        break;
+      }
+      case 403: {
+        onError(status, '권한이 없습니다.');
+        break;
+      }
+      case 404: {
+        onError(status, '찾을 수 없는 페이지입니다.');
+        break;
+      }
+      case 500: {
+        onError(status, '서버 오류입니다.');
+        break;
+      }
+      default: {
+        onError(status, `에러가 발생했습니다. ${error?.message}`);
+      }
+    }
+  } else if (error instanceof Error && error.name === 'TimeoutError') {
+    onError(status, '요청 시간이 초과되었습니다.');
+  } else {
+    onError(status, `에러가 발생했습니다. ${error.toString()}`);
+  }
+
+  return Promise.reject(error);
+};
+
+/** 인터셉터 설정 */
+const setInterceptors = (axiosInstance: AxiosInstance): AxiosInstance => {
+  axiosInstance.interceptors.request.use(onRequest, onRequestError);
+  axiosInstance.interceptors.response.use(onResponse, onResponseError);
+
+  return axiosInstance;
+};
+
+export const api = setInterceptors(instance);
path: src/apis/index.ts
user: login: Pridesd
body: 안녕하세요 지나가는 나그네입니다. 지금 코드에서는 api는 무조건 특정한 인터셉터 핸들러를 적용받는 걸로 보이는데 인터셉터를 적용받는 인스턴스와 안 받는 인스턴스를 분리하는 건 어떠신지요? 
이만 다시 가보겠습니다
created_at: 2025-05-17T14:33:16Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+/** 에러 처리 */
+const onError = (status: number, message: string) => {
+  throw { status, message };
+};
+
+const onRequestError = (error: AxiosError) => {
+  const status = error.response?.status || 0;
+
+  switch (true) {
+    case Boolean(error?.config):
+      onError(status, '요청이 실패했습니다.');
+      break;
+    case Boolean(error?.request):
+      onError(status, '응답이 없습니다.');
+      break;
+    default:
+      onError(status, `에러가 발생했습니다. ${error.message}`);
+      break;
+  }
+
+  return Promise.reject(error);
+};
+
+const onResponseError = (error: AxiosError | Error) => {
+  const status = axios.isAxiosError(error) && error.response ? error.response.status : 0;
+
+  if (axios.isAxiosError(error)) {
+    switch (status) {
+      case 400:
+        onError(status, '잘못된 요청입니다.');
+        break;
+      case 401: {
+        onError(status, '인증 실패입니다.');
+        break;
+      }
+      case 403: {
+        onError(status, '권한이 없습니다.');
+        break;
+      }
+      case 404: {
+        onError(status, '찾을 수 없는 페이지입니다.');
+        break;
+      }
+      case 500: {
+        onError(status, '서버 오류입니다.');
+        break;
+      }
+      default: {
+        onError(status, `에러가 발생했습니다. ${error?.message}`);
+      }
+    }
+  } else if (error instanceof Error && error.name === 'TimeoutError') {
+    onError(status, '요청 시간이 초과되었습니다.');
+  } else {
+    onError(status, `에러가 발생했습니다. ${error.toString()}`);
+  }
+
+  return Promise.reject(error);
+};
+
+/** 인터셉터 설정 */
+const setInterceptors = (axiosInstance: AxiosInstance): AxiosInstance => {
+  axiosInstance.interceptors.request.use(onRequest, onRequestError);
+  axiosInstance.interceptors.response.use(onResponse, onResponseError);
+
+  return axiosInstance;
+};
+
+export const api = setInterceptors(instance);
path: src/apis/index.ts
user: login: HaJunRyu
body: 나그네 ㅎㅇ 이유는 무엇일까요~?
created_at: 2025-05-17T14:39:26Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,90 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+/** 인터셉터 핸들러 */
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+/** 에러 처리 */
+const onError = (status: number, message: string) => {
+  throw { status, message };
+};
+
+const onRequestError = (error: AxiosError) => {
+  const status = error.response?.status || 0;
+
+  switch (true) {
+    case Boolean(error?.config):
+      onError(status, '요청이 실패했습니다.');
+      break;
+    case Boolean(error?.request):
+      onError(status, '응답이 없습니다.');
+      break;
+    default:
+      onError(status, `에러가 발생했습니다. ${error.message}`);
+      break;
+  }
+
+  return Promise.reject(error);
+};
+
+const onResponseError = (error: AxiosError | Error) => {
+  const status = axios.isAxiosError(error) && error.response ? error.response.status : 0;
+
+  if (axios.isAxiosError(error)) {
+    switch (status) {
+      case 400:
+        onError(status, '잘못된 요청입니다.');
+        break;
+      case 401: {
+        onError(status, '인증 실패입니다.');
+        break;
+      }
+      case 403: {
+        onError(status, '권한이 없습니다.');
+        break;
+      }
+      case 404: {
+        onError(status, '찾을 수 없는 페이지입니다.');
+        break;
+      }
+      case 500: {
+        onError(status, '서버 오류입니다.');
+        break;
+      }
+      default: {
+        onError(status, `에러가 발생했습니다. ${error?.message}`);
+      }
+    }
+  } else if (error instanceof Error && error.name === 'TimeoutError') {
+    onError(status, '요청 시간이 초과되었습니다.');
+  } else {
+    onError(status, `에러가 발생했습니다. ${error.toString()}`);
+  }
+
+  return Promise.reject(error);
+};
+
+/** 인터셉터 설정 */
+const setInterceptors = (axiosInstance: AxiosInstance): AxiosInstance => {
+  axiosInstance.interceptors.request.use(onRequest, onRequestError);
+  axiosInstance.interceptors.response.use(onResponse, onResponseError);
+
+  return axiosInstance;
+};
+
+export const api = setInterceptors(instance);
path: src/apis/index.ts
user: login: jeongwoo903
body: > P4
> 
> 이건 api base로직에서 export 할 주요 모듈을 export default로 함수 호출한걸 그대로 내보내주는걸 어떨까?

`export default setInterceptors(instance);` 이렇게 수정해서 `import api from 'src/apis';` 이렇게 쓰도록 해둘께!
created_at: 2025-05-17T15:24:04Z
author_association: MEMBER
diff_hunk: @@ -1,5 +1,11 @@
 import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
 
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
path: src/apis/index.ts
user: login: jeongwoo903
body: [구두 논의 내용]
- ErrorResponse가 차후 다른곳에서 얼마나 참조될지 모르기에 분리 보단 유지. 대신 export는 제거하는 방향으로 수정
- 인터페이스와 타입 네이밍에 대한 컨벤션을 정함. 이에 따라 `IErrorResponse`로 수정
created_at: 2025-05-17T15:25:46Z
author_association: MEMBER
diff_hunk: @@ -1,5 +1,11 @@
 import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
 
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
 const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
path: src/apis/index.ts
user: login: jeongwoo903
body: 빌드타임때만 참조되는거면 필요없다고 하네! `API_BASE_URL`로 수정해두겠습니다~
created_at: 2025-05-17T15:25:59Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (error: AxiosError<ErrorResponse> | Error) => {
+  return Promise.reject(error);
+};
path: src/apis/index.ts
user: login: jeongwoo903
body: 이건 좀 일리있는 말이네. 생각해볼께
created_at: 2025-05-17T15:33:09Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (error: AxiosError<ErrorResponse> | Error) => {
+  return Promise.reject(error);
+};
path: src/apis/index.ts
user: login: coderabbitai[bot]
body: @jeongwoo903 네, 표준화된 에러 처리는 프론트엔드 코드에서 API 에러를 일관되게 처리할 수 있게 해주어 디버깅과 사용자 경험 개선에 도움이 될 것입니다. 천천히 고려해보시고 궁금한 점이 있으시면 언제든지 질문해주세요!


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@jeongwoo903 네, 표준화된 에러 처리는 프론트엔드 코드에서 API 에러를 일관되게 처리할 수 있게 해주어 디버깅과 사용자 경험 개선에 도움이 될 것입니다. 천천히 고려해보시고 궁금한 점이 있으시면 언제든지 질문해주세요!</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-17T15:33:36Z
author_association: NONE
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (error: AxiosError<ErrorResponse> | Error) => {
+  return Promise.reject(error);
+};
path: src/apis/index.ts
user: login: HaJunRyu
body: 음 근데 axios의 error처리를 하는 구문이고 파라미터의 타입을 명시적으로 AxiosError<ErrorResponse>로 정의해줬는데 저렇게 타입가드를 할거면 파라미터에 대한 타입을 제외하고 사용하는게 더 자연스럽지 않을까?
created_at: 2025-05-17T15:36:54Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (error: AxiosError<ErrorResponse> | Error) => {
+  return Promise.reject(error);
+};
path: src/apis/index.ts
user: login: coderabbitai[bot]
body: > Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

<!-- This is an auto-generated mute comment by CodeRabbit -->
created_at: 2025-05-17T15:36:59Z
author_association: NONE
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (error: AxiosError<ErrorResponse> | Error) => {
+  return Promise.reject(error);
+};
path: src/apis/index.ts
user: login: HaJunRyu
body: 아 코드래빗씨 당신한테 물어본거 맞아 @coderabbitai
created_at: 2025-05-17T15:38:35Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (error: AxiosError<ErrorResponse> | Error) => {
+  return Promise.reject(error);
+};
path: src/apis/index.ts
user: login: coderabbitai[bot]
body: @HaJunRyu 맞습니다! 정확한 지적이세요. 저의 제안에 불일치가 있었네요. 파라미터 타입을 `AxiosError<ErrorResponse>`로만 제한했으면서 동시에 `axios.isAxiosError(error)` 타입가드를 사용하는 것은 논리적으로 일관성이 없습니다.

원래 코드의 `error: AxiosError<ErrorResponse> | Error` 타입을 유지하면서 타입가드를 사용하는 것이 더 자연스럽습니다. 수정된 제안은 다음과 같습니다:

```typescript
const onError = (error: AxiosError<ErrorResponse> | Error): Promise<never> => {
  if (axios.isAxiosError(error) && error.response?.data) {
    return Promise.reject(error.response.data);
  }
  return Promise.reject(error);
};
```

이렇게 하면 다양한 에러 타입을 처리하면서도 타입 안전성을 유지할 수 있습니다. 좋은 피드백 감사합니다!

🐰✨


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@HaJunRyu 맞습니다! 정확한 지적이세요. 저의 제안에 불일치가 있었네요. 파라미터 타입을 `AxiosError<ErrorResponse>`로만 제한했으면서 동시에 `axios.isAxiosError(error)` 타입가드를 사용하는 것은 논리적으로 일관성이 없습니다.

원래 코드의 `error: AxiosError<ErrorResponse> | Error` 타입을 유지하면서 타입가드를 사용하는 것이 더 자연스럽습니다. 수정된 제안은 다음과 같습니다:

```typescript
const onError = (error: AxiosError<ErrorResponse> | Error): Promise<never> => {
  if (axios.isAxiosError(error) && error.response?.data) {
    return Promise.reject(error.response.data);
  }
  return Promise.reject(error);
};
```

이렇게 하면 다양한 에러 타입을 처리하면서도 타입 안전성을 유지할 수 있습니다. 좋은 피드백 감사합니다!

🐰✨</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-17T15:39:09Z
author_association: NONE
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (error: AxiosError<ErrorResponse> | Error) => {
+  return Promise.reject(error);
+};
path: src/apis/index.ts
user: login: HaJunRyu
body: 근데 onError 함수는 axios의 error처리를 위한 함수이고 실제로 `axiosInstance.interceptors.response.use(onResponse, onError);` 이런식으로 response에 대한 interceptor로 사용돼, 여기서 굳이 Error타입이 들어올것이라고 가정하고 타입가드를 해줄 필요가 있을까? 정확히는 저 함수의 인자로 AxiosError가 아니라 Error 타입의 인자가 들어올 확률이 존재해? @coderabbitai
created_at: 2025-05-17T15:41:00Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (error: AxiosError<ErrorResponse> | Error) => {
+  return Promise.reject(error);
+};
path: src/apis/index.ts
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

@HaJunRyu 좋은 질문입니다! Axios 인터셉터에서 실제로 `AxiosError`가 아닌 일반 `Error` 객체가 전달될 가능성이 있는지 확인해 봤습니다.

Axios의 타입 정의를 살펴보면, 인터셉터의 에러 핸들러는 사실 `any` 타입을 받도록 설계되어 있습니다. 이는 다음과 같은 경우에 일반 `Error` 타입이 전달될 수 있음을 의미합니다:

1. 네트워크 오류나 타임아웃 중 일부가 `AxiosError` 형식이 아닌 경우
2. 인터셉터 체인 내에서 다른 인터셉터가 던진 커스텀 에러
3. Axios 내부 구현에서의 예외적인 상황

그러나 실무적으로는 대부분의 경우 `AxiosError` 인스턴스만 전달됩니다. 따라서 두 가지 접근 방법이 가능합니다:

1. 방어적 프로그래밍: 현재처럼 `AxiosError<ErrorResponse> | Error` 타입과 타입 가드 사용
2. 간결함 우선: `AxiosError<ErrorResponse>` 타입만 처리 (팀이 이 타입만 발생한다고 확신하는 경우)

팀의 코딩 스타일과 안정성 요구사항에 따라 결정하시면 될 것 같습니다. 엄격한 타입 안전성이 중요하다면 첫 번째 방법을, 코드 간결성이 중요하다면 두 번째 방법을 선택하는 것이 좋겠습니다.



---

🏁 Script executed:
```
#!/bin/bash
# Check Axios TypeScript definitions to verify error handler typing
grep -r "interceptors" node_modules/axios/index.d.ts
```

Length of output: 115

---

@HaJunRyu 앞서 시도한 명령어가 실패했네요. 프로젝트에서 Axios 타입 정의를 찾기 위해 다른 방법을 시도해보겠습니다.

먼저 Axios 타입 정의 파일 위치를 찾고, 인터셉터의 에러 핸들러가 어떻게 타이핑되어 있는지 확인해 보겠습니다.

---

🏁 Script executed:
```
#!/bin/bash
# First, let's try to find where the axios type definitions are located
find node_modules -name "*.d.ts" | grep -i axios

# If we can't find them directly, let's check the package.json of axios
find node_modules/axios -name "package.json" -exec cat {} \;

# Let's also check if we can find the interceptors definition in any typescript file
find node_modules -type f -name "*.d.ts" -exec grep -l "interceptor" {} \; | head -5
```

Length of output: 352

---

@HaJunRyu 안타깝게도 현재 환경에서는 node_modules에 접근할 수 없어 Axios의 타입 정의를 직접 확인하기 어렵네요. 웹 검색을 통해 Axios 인터셉터의 타입 정의를 확인해보겠습니다.

---

🌐 Web query:

```web
axios interceptor error handler typescript definition
```

💡 Result:

In TypeScript, an Axios interceptor error handler can be defined with proper typings to ensure type safety. Here's a basic example of how to define an Axios interceptor error handler with TypeScript:

```typescript
import axios, { AxiosError } from 'axios';

// Error handler for response interceptor
const errorHandler = (error: AxiosError): Promise<never> => {
  // You can handle the error here (e.g., logging, showing notifications)
  console.error('Interceptor caught an error:', error);

  // Optionally, you can customize the error response
  if (error.response) {
    // Server responded with a status code out of 2xx range
    console.error('Response data:', error.response.data);
    console.error('Response status:', error.response.status);
  } else if (error.request) {
    // Request was made but no response received
    console.error('No response received:', error.request);
  } else {
    // Something else happened in setting up the request
    console.error('Error message:', error.message);
  }

  // Reject the promise with the error to propagate it
  return Promise.reject(error);
};

// Usage in Axios interceptor
axios.interceptors.response.use(
  response => response,  // onFulfilled
  errorHandler         // onRejected
);
```

### Explanation:
- The `errorHandler` function takes an `error` parameter typed as `AxiosError`.
- It returns a `Promise<never>` because it always rejects the promise, which is typical behavior in an error handler.
- Inside the function, you can add custom error handling logic, such as logging or displaying error messages.
- This handler can then be passed as the second argument to `axios.interceptors.response.use`.

If you want me to generate a more specific example, such as handling request errors or customizing types, just let me know!

---



</details>

@HaJunRyu 웹 검색 결과를 살펴본 결과, Axios 인터셉터의 에러 핸들러는 일반적으로 다음과 같이 정의됩니다:

```typescript
const errorHandler = (error: AxiosError): Promise<never> => {
  // 에러 처리 로직
  return Promise.reject(error);
};
```

검색 결과에 따르면, 인터셉터의 에러 핸들러는 주로 `AxiosError` 타입만을 처리하도록 타입이 지정되어 있습니다. 실제 Axios 내부 구현에서도 HTTP 오류나 네트워크 오류가 발생하면 `AxiosError` 인스턴스를 생성하여 전달합니다.

즉, 귀하의 지적이 정확합니다! 인터셉터에서 `Error | AxiosError` 유니온 타입을 사용하고 타입 가드로 검사하는 것은 불필요한 과정일 가능성이 높습니다. 대부분의 경우 Axios 인터셉터의 에러 핸들러는 `AxiosError` 타입만 처리하면 충분하며, 이는 일반적인 Axios 사용 패턴과도 일치합니다.

따라서 코드를 다음과 같이 단순화하는 것이 더 자연스럽습니다:

```typescript
const onError = (error: AxiosError<ErrorResponse>): Promise<never> => {
  if (error.response?.data) {
    return Promise.reject(error.response.data);
  }
  return Promise.reject(error);
};
```

이렇게 하면 코드가 더 간결해지고 의도도 명확해집니다.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: HaJunRyu
PR: mash-up-kr/GSS-Web#7
File: src/apis/index.ts:0-0
Timestamp: 2025-05-17T15:42:18.401Z
Learning: Axios 인터셉터의 에러 핸들러는 주로 AxiosError 타입을 처리하지만, 기술적으로는 any 타입을 받을 수 있으므로 방어적 프로그래밍을 위해 Error | AxiosError 타입을 사용하는 것이 안전하다.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer><details>
<summary>🧩 Analysis chain</summary>

@HaJunRyu 좋은 질문입니다! Axios 인터셉터에서 실제로 `AxiosError`가 아닌 일반 `Error` 객체가 전달될 가능성이 있는지 확인해 봤습니다.

Axios의 타입 정의를 살펴보면, 인터셉터의 에러 핸들러는 사실 `any` 타입을 받도록 설계되어 있습니다. 이는 다음과 같은 경우에 일반 `Error` 타입이 전달될 수 있음을 의미합니다:

1. 네트워크 오류나 타임아웃 중 일부가 `AxiosError` 형식이 아닌 경우
2. 인터셉터 체인 내에서 다른 인터셉터가 던진 커스텀 에러
3. Axios 내부 구현에서의 예외적인 상황

그러나 실무적으로는 대부분의 경우 `AxiosError` 인스턴스만 전달됩니다. 따라서 두 가지 접근 방법이 가능합니다:

1. 방어적 프로그래밍: 현재처럼 `AxiosError<ErrorResponse> | Error` 타입과 타입 가드 사용
2. 간결함 우선: `AxiosError<ErrorResponse>` 타입만 처리 (팀이 이 타입만 발생한다고 확신하는 경우)

팀의 코딩 스타일과 안정성 요구사항에 따라 결정하시면 될 것 같습니다. 엄격한 타입 안전성이 중요하다면 첫 번째 방법을, 코드 간결성이 중요하다면 두 번째 방법을 선택하는 것이 좋겠습니다.



---

🏁 Script executed:
```
#!/bin/bash
# Check Axios TypeScript definitions to verify error handler typing
grep -r "interceptors" node_modules/axios/index.d.ts
```

Length of output: 115

---

@HaJunRyu 앞서 시도한 명령어가 실패했네요. 프로젝트에서 Axios 타입 정의를 찾기 위해 다른 방법을 시도해보겠습니다.

먼저 Axios 타입 정의 파일 위치를 찾고, 인터셉터의 에러 핸들러가 어떻게 타이핑되어 있는지 확인해 보겠습니다.

---

🏁 Script executed:
```
#!/bin/bash
# First, let's try to find where the axios type definitions are located
find node_modules -name "*.d.ts" | grep -i axios

# If we can't find them directly, let's check the package.json of axios
find node_modules/axios -name "package.json" -exec cat {} \;

# Let's also check if we can find the interceptors definition in any typescript file
find node_modules -type f -name "*.d.ts" -exec grep -l "interceptor" {} \; | head -5
```

Length of output: 352

---

@HaJunRyu 안타깝게도 현재 환경에서는 node_modules에 접근할 수 없어 Axios의 타입 정의를 직접 확인하기 어렵네요. 웹 검색을 통해 Axios 인터셉터의 타입 정의를 확인해보겠습니다.

---

🌐 Web query:

```web
axios interceptor error handler typescript definition
```

💡 Result:

In TypeScript, an Axios interceptor error handler can be defined with proper typings to ensure type safety. Here's a basic example of how to define an Axios interceptor error handler with TypeScript:

```typescript
import axios, { AxiosError } from 'axios';

// Error handler for response interceptor
const errorHandler = (error: AxiosError): Promise<never> => {
  // You can handle the error here (e.g., logging, showing notifications)
  console.error('Interceptor caught an error:', error);

  // Optionally, you can customize the error response
  if (error.response) {
    // Server responded with a status code out of 2xx range
    console.error('Response data:', error.response.data);
    console.error('Response status:', error.response.status);
  } else if (error.request) {
    // Request was made but no response received
    console.error('No response received:', error.request);
  } else {
    // Something else happened in setting up the request
    console.error('Error message:', error.message);
  }

  // Reject the promise with the error to propagate it
  return Promise.reject(error);
};

// Usage in Axios interceptor
axios.interceptors.response.use(
  response => response,  // onFulfilled
  errorHandler         // onRejected
);
```

### Explanation:
- The `errorHandler` function takes an `error` parameter typed as `AxiosError`.
- It returns a `Promise<never>` because it always rejects the promise, which is typical behavior in an error handler.
- Inside the function, you can add custom error handling logic, such as logging or displaying error messages.
- This handler can then be passed as the second argument to `axios.interceptors.response.use`.

If you want me to generate a more specific example, such as handling request errors or customizing types, just let me know!

---



</details>

@HaJunRyu 웹 검색 결과를 살펴본 결과, Axios 인터셉터의 에러 핸들러는 일반적으로 다음과 같이 정의됩니다:

```typescript
const errorHandler = (error: AxiosError): Promise<never> => {
  // 에러 처리 로직
  return Promise.reject(error);
};
```

검색 결과에 따르면, 인터셉터의 에러 핸들러는 주로 `AxiosError` 타입만을 처리하도록 타입이 지정되어 있습니다. 실제 Axios 내부 구현에서도 HTTP 오류나 네트워크 오류가 발생하면 `AxiosError` 인스턴스를 생성하여 전달합니다.

즉, 귀하의 지적이 정확합니다! 인터셉터에서 `Error | AxiosError` 유니온 타입을 사용하고 타입 가드로 검사하는 것은 불필요한 과정일 가능성이 높습니다. 대부분의 경우 Axios 인터셉터의 에러 핸들러는 `AxiosError` 타입만 처리하면 충분하며, 이는 일반적인 Axios 사용 패턴과도 일치합니다.

따라서 코드를 다음과 같이 단순화하는 것이 더 자연스럽습니다:

```typescript
const onError = (error: AxiosError<ErrorResponse>): Promise<never> => {
  if (error.response?.data) {
    return Promise.reject(error.response.data);
  }
  return Promise.reject(error);
};
```

이렇게 하면 코드가 더 간결해지고 의도도 명확해집니다.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: HaJunRyu
PR: mash-up-kr/GSS-Web#7
File: src/apis/index.ts:0-0
Timestamp: 2025-05-17T15:42:18.401Z
Learning: Axios 인터셉터의 에러 핸들러는 주로 AxiosError 타입을 처리하지만, 기술적으로는 any 타입을 받을 수 있으므로 방어적 프로그래밍을 위해 Error | AxiosError 타입을 사용하는 것이 안전하다.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-17T15:42:19Z
author_association: NONE
diff_hunk: @@ -0,0 +1,38 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+export interface ErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 10000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig> => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse): AxiosResponse => {
+  return response;
+};
+
+const onError = (error: AxiosError<ErrorResponse> | Error) => {
+  return Promise.reject(error);
+};
path: src/apis/index.ts
user: login: jeongwoo903
body: 좋아 잘했어. 반영 👍
created_at: 2025-05-17T15:51:18Z
author_association: MEMBER
diff_hunk: @@ -24,7 +24,10 @@ const onResponse = (response: AxiosResponse): AxiosResponse => {
   return response;
 };
 
-const onError = (error: AxiosError<IErrorResponse> | Error) => {
+const onError = (error: AxiosError<IErrorResponse>): Promise<never> => {
path: src/apis/index.ts
user: login: HaJunRyu
body: P2

정우야 우리 컨벤션에 함수의 return값은 타입을 수기로 정의하는게 아닌 추론에 맡기는걸로 했어서 return type은 빼줘도 될거 같네~~

그리고 내 생각에 interceptor와 별개로 error처리 로직은 [해당 문서](https://axios-http.com/kr/docs/handling_errors)에 나와있는대로 `요청이 전송되었고, 서버는 2xx 외의 상태 코드로 응답받은 케이스`, `요청이 전송되었지만, 응답이 수신되지 않은 케이스`, `앞에 두가지 모두 다 아닌(response와 request가 모두 없는) 케이스`에 대해서 각각 정의를 해줘야 할거 같애

그리고 앞선 리뷰에서 얘기한대로 정상적으로 response가 존재하는 경우에도 error.response.data를 reject하는게 아닌 error 객체 자체를 reject하여 우리가 error 객체를 온전히 활용할 수 있는 형태면 좋을거 같고 만약 response가 없는 케이스에서는 error.request만을, 둘 다 아닌 케이스에서는 단순히 error.message만을, 그리고 앞에 상황들을 코드로 풀어본다면 if, else if, else가 될건데 그냥 이런 분기처리를 위한 블록 바깥에도 단순히 error를 reject하는 구문이 하나 있어야 할거 같다 :) 이유는 함수의 return 타입을 정확히 추론하기 위해서는 분기처리가 없는 구문에서도 return이 동일하게 되어야 하기 때문!
created_at: 2025-05-17T16:01:17Z
author_association: MEMBER
diff_hunk: @@ -24,7 +24,10 @@ const onResponse = (response: AxiosResponse): AxiosResponse => {
   return response;
 };
 
-const onError = (error: AxiosError<IErrorResponse> | Error) => {
+const onError = (error: AxiosError<IErrorResponse>): Promise<never> => {
path: src/apis/index.ts
user: login: jeongwoo903
body: 몇가지 코드 스타일에 관한 부분을 제외하고는 다 반영했어! 👍 (ex. if문만 사용)
사실 처음 구조에서 `return Promise.reject(error);`만 남게된 이유는 조건에 따라 단순화 하니까 구조가 비슷해지고 합쳐버려서 그렇게 되었는데, 리뷰 보니까 통일된 구조를 제공하기 위해선 분리가 또 필요하긴 해 보이겠더라고 🤔 error도 다 같은 에러가 아니니까..

잘 정리해줘서 고마워~ 덕분에 많이 배웠수다..👍 무료 컨설팅 받는거 같네,,ㅋㅋㅋ 짱이야~
created_at: 2025-05-17T16:41:28Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,50 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+interface IErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const { API_BASE_URL } = process.env;
+
+const instance: AxiosInstance = axios.create({
path: src/apis/index.ts
user: login: HaJunRyu
body: P3

마지막으로 이 부분도 변수에 대한 타입을 따로 정의하지 않아도 추론이 될 거 같아서 한번 확인해줄 수 있을까~?
created_at: 2025-05-17T16:44:44Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,50 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+interface IErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const { API_BASE_URL } = process.env;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 30000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig) => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse) => {
+  return response;
+};
+
+const onError = (error: AxiosError<IErrorResponse>) => {
+  if (error.response) {
+    return Promise.reject(error);
+  }
+
+  if (error.request) {
+    return Promise.reject(error.request);
+  }
+
+  if (error.message) {
+    return Promise.reject(error.message);
+  }
path: src/apis/index.ts
user: login: HaJunRyu
body: P5

이건 스타일의 영역인것도 같지만 코드의 의도를 전달하는 관점에서 봤을땐 나는 axios의 공식문서에서 나온 if, else if, else가 좀 더 나은거 같긴한데 `if(error.message)` 구문을 제외하면 동작에는 차이가 없어보여서 이건 단순 첨언 정도로 달아둘게ㅋㅋㅋㅋ
created_at: 2025-05-17T16:47:34Z
author_association: MEMBER
diff_hunk: @@ -24,7 +24,10 @@ const onResponse = (response: AxiosResponse): AxiosResponse => {
   return response;
 };
 
-const onError = (error: AxiosError<IErrorResponse> | Error) => {
+const onError = (error: AxiosError<IErrorResponse>): Promise<never> => {
path: src/apis/index.ts
user: login: HaJunRyu
body: ㅋㅋㅋㅋㅋㅋ굳 하 주말은 이런 여유도 있고 아주 행복하다.. 이번주 평일 아무리 생각해도 너무 바쁘고 정신없었단말이지
created_at: 2025-05-17T16:48:53Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,50 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+interface IErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const { API_BASE_URL } = process.env;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 30000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig) => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse) => {
+  return response;
+};
+
+const onError = (error: AxiosError<IErrorResponse>) => {
+  if (error.response) {
+    return Promise.reject(error);
+  }
+
+  if (error.request) {
+    return Promise.reject(error.request);
+  }
+
+  if (error.message) {
+    return Promise.reject(error.message);
+  }
path: src/apis/index.ts
user: login: jeongwoo903
body: 해당 부분은 우선 처음엔 스타일 적인 부분으로 작성을 했습니다.

한 번 if,else if,else 방식으로 해봤는데 `Unnecessary 'else' after 'return'. ` 이런 린트 에러가 뜨더라구요 🤔
왜냐면 Promise.reject를 return 해주어야 하는데 린트상 안되더라구요.

그럼 어쨋건 if 스타일로 가야 한다고 생각이 드는데, error.message는 항상 존재하니까 마지막 fallback으로 써도 충분하다는 생각이 들었어요.

그럼 정리되는 케이스가 아래 두 케이스라고 생각이 드는데, 혹은 더 나은 방향이 있으시다고 생각되시면 한번 말씀해주시면 많은 도움이 될 것 같슴다!! 🙇🏻‍♂️🙇🏻‍♂️

> A 케이스
```js
const onError = (error: AxiosError<IErrorResponse>) => {
  if (error.response) {
    return Promise.reject(error);
  }

  if (error.request) {
    return Promise.reject(error.request);
  }

  return Promise.reject(error);
};
```
> B 케이스 (return Promise.reject(error);은 같은데 명시적 이유로 살려둘 필요가 있을까 싶기도 해서)
```js
const onError = (error: AxiosError<IErrorResponse>) => {
  if (error.request) {
    return Promise.reject(error.request);
  }

  return Promise.reject(error);
};
```
created_at: 2025-05-18T05:45:18Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,50 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+interface IErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const { API_BASE_URL } = process.env;
+
+const instance: AxiosInstance = axios.create({
path: src/apis/index.ts
user: login: jeongwoo903
body: <img width="366" alt="스크린샷 2025-05-18 오후 2 18 50" src="https://github.com/user-attachments/assets/e5c14778-4e39-4afc-91e4-4a5b45df73b6" />

Oh.. so good.. mistake~ 😉
created_at: 2025-05-18T05:46:27Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,50 @@
+import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
+
+interface IErrorResponse {
+  code: string;
+  status: number;
+  message: string;
+}
+
+const { API_BASE_URL } = process.env;
+
+const instance: AxiosInstance = axios.create({
+  baseURL: API_BASE_URL,
+  timeout: 30000,
+  headers: {
+    'Content-Type': 'application/json',
+  },
+});
+
+const onRequest = (config: InternalAxiosRequestConfig) => {
+  return Promise.resolve(config);
+};
+
+const onResponse = (response: AxiosResponse) => {
+  return response;
+};
+
+const onError = (error: AxiosError<IErrorResponse>) => {
+  if (error.response) {
+    return Promise.reject(error);
+  }
+
+  if (error.request) {
+    return Promise.reject(error.request);
+  }
+
+  if (error.message) {
+    return Promise.reject(error.message);
+  }
path: src/apis/index.ts
user: login: HaJunRyu
body: 이건 그냥 지금처럼 가도 될거 같애ㅎㅎ 결국 A 케이스에서 if, else if  다음에 else 안쓰고 return만 써주는 방식으로 써야 lint에 안걸릴거 같긴한데 막 중요한건 아닌거 같다ㅎㅎ
created_at: 2025-05-18T14:40:35Z
author_association: MEMBER

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-17T13:48:05Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: failure by coderabbit.ai -->

> [!CAUTION]
> ## Review failed
> 
> The pull request is closed.

<!-- end of auto-generated comment: failure by coderabbit.ai -->
<!-- walkthrough_start -->

"""
## Walkthrough

`package.json`에 `axios` 패키지 의존성이 추가되었습니다. `src/apis/index.ts` 파일이 새로 생성되어, 환경 변수 기반의 base URL, 30초 타임아웃, JSON 헤더를 가진 Axios 인스턴스와 인터셉터, 에러 응답 인터페이스, 그리고 설정된 Axios 인스턴스(`api`)의 export가 구현되었습니다.

## Changes

| 파일/경로           | 변경 요약                                                                                   |
|--------------------|---------------------------------------------------------------------------------------------|
| package.json       | `axios`(`^1.9.0`) 의존성 추가                                                              |
| src/apis/index.ts  | Axios 기반 API 클라이언트, 인터셉터, `IErrorResponse` 인터페이스, 30초 타임아웃, `api` 인스턴스 export 추가 |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant api (Axios)
    participant Server

    Client->>api (Axios): HTTP 요청 전송
    api (Axios)->>Server: 요청 전달 (baseURL, timeout, headers 적용)
    Server-->>api (Axios): 응답 반환
    api (Axios)-->>Client: 응답 전달 (인터셉터 처리)
    Note over api (Axios): 에러 발생 시 onError 인터셉터로 처리 후 클라이언트에 전달
```
"""

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKNxU3bABsvkCiQBHbGlcSHFcLzpIACIAMxJqLjQAD3h8ZDRuHUAIWcAGOshADkHAUGXoyDlIbERKSBF8DCIAd3x8AE4ABgBmFAxcCkVsBmkw2BJu9Xg0XyrcbG5IWPw+XBHIAEFU9LWrAElu4dHefBExDRgV5m0sBlhMUmR4DAYvbCUFDFj4ImwKB6J0MrQiHgDDWG3uGEQuEwgwANJAlB8ML8/IFgpD0Bh6P5ENw6lVujQKINuAQKIg4Zh6PBmNxImwesjKH0+DdMZEyWFtF5FlECPD4LF4v4evZBhhnGlEBo3Od8OIJNQ0lgBCQEJjhqgmhQANYoZB8kIqLxIWD/JgQpA0EWU+xQzHOWjwABeUUyhzQ13mi3QPnmeG+o1WO0Yky8yAa6jV+x4fWOmlBksgDUBjFg6TI/CkfHiuE9fP8tAGo2WoxUkKoYiVmcoIzQtApyCUDHgSipWDQMbSS1EsCRDEmto9urQRH8JHpmhlo2ptPH7EVdRRQrIgwyZW5f1x6JtTGYanIyHwguBE18tHwDEc8/Ei4jy34Jb42Pw31XYXw83U0cOcYA5MhyDoKU1i8ZYXyIU0CG1DNWVoY16kgAAKD1VwydV/FibFIPwaCIQASiTFNHUQS9ECqeheBICQ0kqLxZDhL9UARJkogWbN/X8SAyFZQYJwBcj+GVIcyAovpR2kYC4FGJhXlQZh+i8CUXXoPlYg9eBjShGguMBeQSGSK0gTqOE71NKpuGcahpx6ShiVJND6CZb1YPZYDVlwGgaVwfUPxnPopBROsVA09R5BtC4HihB5gs0+Rlj6bAIMgNgyJHaS8Ttby4Q+MlcDAJ5AWQBVnmkCl1VibBHhvcVfEOUc0FYZEGkodKLSUAsyjwCpQOpKy6PsEhQkBFtRkLYsP3NQYKAhdABBfUJ/CCtRYrhfTUMkUZ5MLRSfidBcMDKlTZG4VqgUhFdZGlAxVnhQauVY7kGmuZxQkdEcqGYFAfEqXorP1FZ/CCEJIGNeIGFkJ4SAYx5nkdBDAbRUJItskgSSVVaKGZSAXORNiUSB9E1I0gNyRRHE8Wswk7PRjF6EhahKjAHFRAFYEuMx71DnWhDEJIDQiA0OEABY2jaYW2gARnFjpxaFuEAFZRbhcQ2Hm9nmUQPCrqk6w7EmRBfJhl4hg7eDdT5DsADk5Src9Lz4hwnF25F1kTSbUe8r1uwapcWsePmzmkkYGG1Y10XNHLmH+6hozYChSE5ePBrKKhHlNVAmEx0RJwMfRjHAKARP4WIcAIYgyGUGh6F3CcuF4fhhGzjaZHkGTlFUdQtB0POTCgdx7jQ0vCFIcgqCrhRWHYLgqAaexHAuFwylbxR27UTRtF0MBDHz0wDAskO0o0IQDYwDgDGiC+DAsNZtnL0erLp+fnHkI9U1uaQp1uk7MQukHLT2AABvvbUh9j51AAYRZAsxaAPyXugWgcM/gdnILPYBaUuAAOiCkSU0QIGmUgFmIyVwMpUEipATBAA9CWGgWgaDaLg041sHwjD4Eob+ShHjwCGN6COnxvj7XDC1N+9Q6BXXMJYVYoFK5Kh8tGJsO0BHFy4skXEFBx6c2wAIY0IJ2DjA/rnNYCCohsJEr/TB2D0jRC4NEKhNC6G4L2CWchJif6cOkBAqolZFyvyAUOUBJ8AHSgvtEIwEAwBGEQESAA9JkJAUSHhKGSBobyZ9glXwkbfEelcoiOwXi/Euz0RGICMDdFBWxdhPC4SKLa3hpzgl6P0QY9BKguzBF7SAAAJaA0ArD40RsBbYr0SCIhNlgAB2wACiHMKA2GkLiCEJAIHIwoGpQYt0SI/H3H8Jx5YBgzE4q/JyT45kU3DJGchbcAFwgAfTGYiArm03ISlRAaUIEfBIF4WgkkViu02A8em/s9RvA+F8Dq+COwCEBKMAAqjYAAMvMPon1MBcQwNRPoGA+IKh+EaKG/wOhtCZqIOoKlqQkHmodSAAApAAygAeUtm8K0eVcDHVGLWdq3yxwEhRiSZylJXLoE4jOOk7A6AYLqLMgmuAIHPi8FIf6owEbA14X8Uyastqs37NVAA3OQyVJyFmysGt8GaTjsTzPxICMASA9UALqFM5kxq4yKpjCwJAox8FOIqr6I5KAS7msNfifSlpSZyhYRGfE/gXVJnOYG6VDc4z+pjNIdgcJvROL9c8tKtofgIQFOgBUGlcXZVDMiSFId3zRkWJ8aKvgjmnBuiMLwJ12JVSrDcwa2wbJEg9ose56APIehGIq/Eyy7L9qrU4354IAWDFOAAMTrXRZWKxVUBnoDO7oc7PXnPHX2jkbpjRRFQPpVR48UxOIRGgbwoQz2LFCHjSo1lkpoG1MiLpPS+khClEYcRIFCSKL5Fe0QCjqqHhLvetRUQNFaLZro8Q+ioCrCMW2QkqzRjjMdYsWZ5MjWxvvIcVtiHkAALblwcsvwHk3K0pULgGBHAqgoA8m0ADs2kAo70KjgDIkMBiVkRA8Sf5JO8gAgxKHWy3TUre5R56lEooATO7tO68F7p7ROw93BaRcLbOQ3j/G4kJP0sk+559L65y3jvQu6pX43rLlkseUQa5Tz8GgWeuTn5wLblQDu69u6Wd7hPZg6gAD6LZEAhf8NREgzVaAhfpmoze29AsACY2gCAABwMAEELAA7B0eWLRcsCElgwWIEsUtoDaC0ErHQMsADYctoBaGgFLKWMstYYNVpLBhAv1Ylh0fs8sSD1fq21+WtB6sZZIBN2gAgOhoCqy0AlQt5b1baClxr9Xcv1YJS0HrfXYjy2OwICWbQGAEtyxl8r+X5ZCzm8tiWuXcstFiC0CWaBYgpee2N+W5WDsF0gB0WgKX7sCFoBLSH5W6wdAEPLOstAatXfq+0EbG2Muw9iELIWI2Ox5164DmuoXwuRaolw2LIWi76CAA -->

<!-- internal state end -->
<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

---

> [!NOTE]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

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


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 3d24dbd111f1ad3b5add9b78690e60283bf44e6a and 7fef87bd48e0be9c9e75697bb7e98c451d8488f4.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `src/apis/index.ts` (1 hunks)

</details>

</details>
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=mash-up-kr/GSS-Web&utm_content=7):

- 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](https://www.coderabbit.ai/contact-us/support) 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](https://docs.coderabbit.ai/finishing-touches/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](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: xxj15
created_at: 2025-05-18T15:05:16Z
body: 고생하셨습니다 .. 👏🏻
author_association: CONTRIBUTOR