Skip to content

[FEAT] Vite 프록시 도입을 통한 개발 환경 개선 및 API 호출 리팩터링 (#154)#155

Merged
kanghaeun merged 5 commits intodevelopfrom
feat/dev-cors-proxy#154
Oct 12, 2025
Merged

[FEAT] Vite 프록시 도입을 통한 개발 환경 개선 및 API 호출 리팩터링 (#154)#155
kanghaeun merged 5 commits intodevelopfrom
feat/dev-cors-proxy#154

Conversation

@kanghaeun
Copy link
Contributor

@kanghaeun kanghaeun commented Oct 11, 2025

#️⃣연관된 이슈

close #154

📝작업 내용

로컬 개발 환경에서 백엔드 API 호출 시 발생하는 CORS 오류 해결

1. Vite 프록시 설정 추가 (vite.config.ts)
server.proxy 옵션을 사용하여 /api 경로로 들어오는 모든 요청을 백엔드 서버로 전달

  server: {
      proxy: {
        '/api': {
          target: env.VITE_API_BASE_URL,
          changeOrigin: true,
          secure: false,
        },
      },
    },

2. API 호출 방식 전체 리팩터링
기존에 import.meta.env.VITE_API_BASE_URL을 직접 사용하던 모든 fetch 함수를 프록시를 통하는 상대 경로(/api/...)를 사용하도록 수정

변경 전

const response = await fetch(

import.meta.env.VITE_API_BASE_URL + `/clubs/${clubId}/applicants/${applicantId}/application`,
);

변경 후

  const url = `/api/clubs/${clubId}`;
  const res = await fetch(url);

💬리뷰 요구사항(선택)

로컬 환경에서는 정상적으로 동작하지만 배포 환경에서도 동일하게 적용되는지는 확인이 필요합니다. AWS 관련 설정이 추가로 필요할 수 있을 것 같은데 검토 한번 부탁드려요!

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability of admin and user API calls with consistent response checks and clearer (some Korean) error messages.
    • Aligned update operations to use partial updates to avoid unintended overwrites.
  • Refactor

    • Switched endpoints to relative API paths and standardized network requests and error handling.
    • Dashboard: applicant fetching no longer applies status filtering via API, which may change visible results due to cache key usage.
  • Chores

    • Updated dev server proxy configuration for API routing during local development.

@kanghaeun kanghaeun self-assigned this Oct 11, 2025
@kanghaeun kanghaeun added the ✨ Feature 기능 구현 이슈 label Oct 11, 2025
aaaaaattt
aaaaaattt previously approved these changes Oct 12, 2025
Copy link
Collaborator

@aaaaaattt aaaaaattt left a comment

Choose a reason for hiding this comment

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

merge 이후 우선 바로 dev 브랜치 기준으로 수동으로 배포해보겠습니다

body: JSON.stringify({ content, rating }),
const url = `/api/applications/${applicationId}/comments/${commentId}`;
const response = await fetch(url, {
method: 'PUT',
Copy link
Contributor

Choose a reason for hiding this comment

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

스웨거/명세 기준으로 지원서 댓글 수정은 PUT이 아니라 PATCH 인 것 같습니다

Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분 확인해주시면 바로 승인할게욥

@coderabbitai
Copy link

coderabbitai bot commented Oct 12, 2025

Walkthrough

Refactors API calls to use relative paths and Vite dev-server proxying; adds explicit response.ok checks and throws on errors across endpoints; changes comment update from PUT to PATCH; removes status parameter from applicants fetch while leaving it in one hook's queryKey; converts vite.config.ts to an env-aware function that sets server.proxy.

Changes

Cohort / File(s) Summary
Vite dev proxy setup
vite.config.ts
Converted to a config function using loadEnv and ConfigEnv; returns original config and adds server.proxy using env.VITE_API_BASE_URL; preserves plugins.
Admin: Application comments API
src/pages/admin/ApplicationDetail/api/comments.ts
Switched to relative endpoints, introduced local url variables, added response.ok checks and error throws for fetch/create/delete/update, changed update from PUT to PATCH; public signatures unchanged.
Admin: Application detail API
src/pages/admin/ApplicationDetail/api/detailApplication.ts
Uses relative endpoints; added response.ok validation with Korean error messages; updateApplicationStatus now sends PATCH, checks response, and returns response.json() — signature changed to Promise<unknown>.
Admin: Club detail edit API
src/pages/admin/ClubDetailEdit/api/clubDetailEdit.ts
Removed BASE_URL env usage, switched to relative /api/... endpoints, standardized response variable, throws on non-ok responses and returns response.json().
Admin: Dashboard applicants API + hook
src/pages/admin/Dashboard/api/applicant.ts, src/pages/admin/Dashboard/hooks/useApplicants.ts
fetchApplicants now GETs /api/clubs/{clubId}/dashboard/applicants, removed status param and related import; added detailed error throws. Hook still includes status in queryKey but calls fetchApplicants(clubId) (cache-key/data mismatch). Public signature: fetchApplicants(clubId): Promise<ApplicantData[]>.
User: Apply APIs
src/pages/user/Apply/api/apply.ts
Replaced env-based base URL with relative /api/clubs/{id}/apply and /apply-submit; added response.ok checks and specific error messages for fetch/submit; payloads/headers unchanged.
User: Club detail API
src/pages/user/ClubDetail/api/clubDetail.ts
Switched to relative endpoint, renamed resresponse, preserves error throwing and returns parsed JSON as ClubDetail.
User: Main clubs list API
src/pages/user/Main/api/club.ts
Replaced base URL usage with a local url variable pointing to relative endpoint; response handling unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Browser (Dev)
  participant Vite as Vite Dev Server (Proxy)
  participant API as Backend

  Dev->>Vite: Request /api/...
  Note over Vite: Proxy rule matches /api → forward to env.VITE_API_BASE_URL
  Vite->>API: Forward /api/...
  API-->>Vite: Response
  Vite-->>Dev: Proxied response (CORS-free)
Loading
sequenceDiagram
  autonumber
  participant UI as Client UI
  participant ClientAPI as client API module
  participant Server as Backend

  UI->>ClientAPI: updateComment(id, payload)
  ClientAPI->>Server: PATCH /api/.../comments/{id} (JSON)
  alt response.ok
    Server-->>ClientAPI: 2xx + body
    ClientAPI-->>UI: parsed JSON
  else not ok
    Server-->>ClientAPI: Error status + body
    ClientAPI-->>UI: throw Error(status, message)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

I hop through routes both near and far,
Proxy winds guiding each API star.
PATCH for fixes, while checks catch slips,
I nibble errors, tighten the scripts.
Carrots for tests and cozy dev nights. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request introduces additional modifications that fall outside the scope of the linked issue, such as enhanced error handling with explicit response checks and thrown error messages, altered function signatures (e.g., updateApplicationStatus return type and fetchApplicants parameter removal), and potential mismatches in query caching logic. These changes are not part of the proxy setup or basic URL refactoring objectives defined in issue #154. Including these extra adjustments could risk unintended side effects and makes the review and testing process more complex. Consider isolating these enhancements into separate pull requests by first merging only the proxy configuration and URL refactor changes defined in issue #154, then addressing error handling improvements and signature updates in follow-up PRs. This separation will ensure focused reviews and reduce unexpected integration issues while maintaining clarity on the objectives each change set addresses.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title accurately captures the main goal of the pull request by referencing the setup of a Vite proxy and the subsequent API call refactoring. It concisely reflects the core changes without unnecessary details or file lists. The inclusion of the issue number is a common convention and does not detract from the clarity.
Linked Issues Check ✅ Passed The pull request fulfills the primary requirements of linked issue #154 by adding the Vite proxy configuration in vite.config.ts and updating all fetch calls to use the relative /api path. Both specified tasks are implemented and verified in the code changes. No outstanding tasks from the linked issue remain unaddressed.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/dev-cors-proxy#154

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

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca3edac and aac2954.

📒 Files selected for processing (9)
  • src/pages/admin/ApplicationDetail/api/comments.ts (3 hunks)
  • src/pages/admin/ApplicationDetail/api/detailApplication.ts (1 hunks)
  • src/pages/admin/ClubDetailEdit/api/clubDetailEdit.ts (1 hunks)
  • src/pages/admin/Dashboard/api/applicant.ts (1 hunks)
  • src/pages/admin/Dashboard/hooks/useApplicants.ts (1 hunks)
  • src/pages/user/Apply/api/apply.ts (2 hunks)
  • src/pages/user/ClubDetail/api/clubDetail.ts (1 hunks)
  • src/pages/user/Main/api/club.ts (1 hunks)
  • vite.config.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
src/pages/admin/ApplicationDetail/api/detailApplication.ts (1)
src/pages/admin/ApplicationDetail/types/detailApplication.ts (1)
  • DetailApplication (3-19)
src/pages/user/ClubDetail/api/clubDetail.ts (1)
src/pages/user/ClubDetail/types/clubDetail.ts (1)
  • ClubDetail (3-20)
src/pages/admin/Dashboard/api/applicant.ts (1)
src/pages/admin/Dashboard/types/dashboard.ts (1)
  • ApplicantData (14-22)
src/pages/admin/Dashboard/hooks/useApplicants.ts (1)
src/pages/admin/Dashboard/api/applicant.ts (1)
  • fetchApplicants (3-13)
src/pages/admin/ClubDetailEdit/api/clubDetailEdit.ts (1)
src/pages/admin/ClubDetailEdit/types/clubDetailEdit.ts (1)
  • ClubDetailEdit (3-3)
src/pages/admin/ApplicationDetail/api/comments.ts (1)
src/mocks/repositories/applicant.ts (1)
  • deleteComment (131-138)
🔇 Additional comments (4)
src/pages/user/Apply/api/apply.ts (2)

8-14: LGTM! Clean refactor to relative endpoint with proper error handling.

The fetch call now uses the proxy-relative path and includes appropriate response validation. The error message is clear and user-facing.


16-34: LGTM! Consistent pattern applied to POST request.

The changes follow the same pattern as fetchApplicationForm, using relative paths and proper error handling.

src/pages/admin/ApplicationDetail/api/detailApplication.ts (1)

3-12: LGTM! Proper refactor with error handling.

The relative endpoint and response validation are correctly implemented.

src/pages/admin/Dashboard/api/applicant.ts (1)

3-13: LGTM! Simplified API with enhanced error handling.

The removal of the status parameter and the shift to a relative endpoint are correct. The enhanced error handling that includes both HTTP status and response text is an improvement.

Comment on lines 13 to 17
const { data, isLoading, error } = useQuery({
queryKey: ['applicants', clubId, status],
queryFn: () => fetchApplicants(clubId, status),
queryFn: () => fetchApplicants(clubId),
staleTime: 1000 * 60 * 2,
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix cache key to match API call signature.

The queryKey at Line 14 includes status, but the API call at Line 15 no longer passes status to fetchApplicants. This creates a cache key mismatch:

  • Different status values create separate cache entries
  • But all entries fetch the same unfiltered data
  • This wastes memory and creates confusing cache behavior

Apply this diff to align the cache key with the API call:

   const { data, isLoading, error } = useQuery({
-    queryKey: ['applicants', clubId, status],
+    queryKey: ['applicants', clubId],
     queryFn: () => fetchApplicants(clubId),
     staleTime: 1000 * 60 * 2,
   });

If client-side filtering by status is still needed, implement it in the hook's return value instead:

return {
  data: status && status !== '전체' 
    ? (data || []).filter(applicant => applicant.status === status)
    : (data || []),
  isLoading,
  error,
};
🤖 Prompt for AI Agents
In src/pages/admin/Dashboard/hooks/useApplicants.ts around lines 13 to 17, the
queryKey currently includes `status` while the queryFn calls
`fetchApplicants(clubId)` without `status`, causing cache key mismatch; remove
`status` from the `queryKey` so it matches the actual API call, and if you still
need client-side filtering, apply the provided filtering logic in the hook's
return value to filter `data` by `status` (treating '전체' as no filter) while
returning `isLoading` and `error` unchanged.

Comment on lines +9 to +10
const url = `/api/clubs?category=${filter}`;
const response = await fetch(url);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Relative /api requests will 404 in production

By dropping import.meta.env.VITE_API_BASE_URL and hardcoding /api, every fetch now assumes a same-origin reverse proxy. In production builds there is no Vite dev proxy, so deployments that serve the SPA from S3/CloudFront (or any origin different from the backend) will start issuing /api/... requests to the static host and immediately fail with 404/CORS. We just lost the ability to point the frontend at the real backend domain.

Please keep using the environment-provided base URL (and only fall back to /api when it is intentionally empty) and apply the same fix to every API module touched in this PR. Example patch:

-  const url = `/api/clubs?category=${filter}`;
-  const response = await fetch(url);
+  const endpoint = `/clubs?category=${filter}`;
+  const baseUrl = import.meta.env.VITE_API_BASE_URL ?? '';
+  const response = await fetch(`${baseUrl}${endpoint}`);

This preserves production correctness while still letting you set VITE_API_BASE_URL="" for dev to leverage the proxy. Please reflect the same pattern across the other updated API files.

📝 Committable suggestion

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

Suggested change
const url = `/api/clubs?category=${filter}`;
const response = await fetch(url);
const endpoint = `/clubs?category=${filter}`;
const baseUrl = import.meta.env.VITE_API_BASE_URL ?? '';
const response = await fetch(`${baseUrl}${endpoint}`);
🤖 Prompt for AI Agents
In src/pages/user/Main/api/club.ts around lines 9-10, the code hardcodes a
relative "/api" base which will 404 in production when the frontend is served
from a different origin; restore use of import.meta.env.VITE_API_BASE_URL with a
fallback to "/api" (e.g. const base = import.meta.env.VITE_API_BASE_URL ||
'/api') and build the fetch URL by joining base and the path safely (avoid
duplicate slashes), then replace the hardcoded const url assignment with this
composed URL; apply the same pattern to every other API module changed in this
PR.

@aaaaaattt aaaaaattt self-requested a review October 12, 2025 10:02
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/pages/admin/ApplicationDetail/api/detailApplication.ts (1)

17-28: Past review addressed; consider more specific return typing.

Good work addressing the previous review comment—the return type is now Promise<unknown> and matches the returned response.json(). The authentication concern from the previous comment applies here as well.

As a minor refinement, consider typing the return value more specifically if the response shape is known. For example:

+type UpdateStatusResponse = { message: string; updatedAt: string };
+
 export const updateApplicationStatus = async (
   applicationId: number,
   status: DetailApplication['status'],
-): Promise<unknown> => {
+): Promise<UpdateStatusResponse> => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aac2954 and 9cd631a.

📒 Files selected for processing (1)
  • src/pages/admin/ApplicationDetail/api/detailApplication.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/admin/ApplicationDetail/api/detailApplication.ts (1)
src/pages/admin/ApplicationDetail/types/detailApplication.ts (1)
  • DetailApplication (3-19)
🔇 Additional comments (1)
src/pages/admin/ApplicationDetail/api/detailApplication.ts (1)

7-11: Confirm authentication for this fetch call
No existing fetch invocations include credentials or an Authorization header. Verify whether this endpoint requires auth; if so, update the request to include credentials: 'include' or the appropriate Authorization header.

@ganimjeong ganimjeong self-requested a review October 12, 2025 10:25
@kanghaeun kanghaeun merged commit f4b851a into develop Oct 12, 2025
11 checks passed
@kanghaeun kanghaeun deleted the feat/dev-cors-proxy#154 branch October 12, 2025 10:52
ganimjeong pushed a commit that referenced this pull request Oct 27, 2025
…s-proxy#154

[FEAT] Vite 프록시 도입을 통한 개발 환경 개선 및 API 호출 리팩터링 (#154)
ganimjeong pushed a commit that referenced this pull request Oct 27, 2025
…s-proxy#154

[FEAT] Vite 프록시 도입을 통한 개발 환경 개선 및 API 호출 리팩터링 (#154)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 기능 구현 이슈

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 개발 환경 CORS 해결을 위한 Proxy 적용

3 participants