Skip to content

feat: api-client 공통 패키지 도입 및 admin auth/scores 이관#435

Closed
manNomi wants to merge 2 commits into
mainfrom
feat/admin-api-client-foundation-pr
Closed

feat: api-client 공통 패키지 도입 및 admin auth/scores 이관#435
manNomi wants to merge 2 commits into
mainfrom
feat/admin-api-client-foundation-pr

Conversation

@manNomi

@manNomi manNomi commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • packages/api-client 패키지를 추가하고 runtime(axios/public axios, 토큰 재발급 인터셉터), generated(admin auth/scores), hooks 엔트리 구조를 도입했습니다.
  • 루트 빌드 파이프라인에 codegen:check 태스크를 추가해 Bruno 기반 생성물 드리프트를 CI에서 감지할 수 있도록 했습니다.
  • admin의 lib/api/*types/*@solid-connect/api-client 재사용 구조로 전환해 auth/scores vertical slice를 공통 런타임으로 이관했습니다.

Verification

  • pnpm --filter @solid-connect/api-client run typecheck
  • pnpm --filter @solid-connect/admin exec tsc --noEmit
  • pnpm --filter @solid-connect/admin run build

@vercel

vercel Bot commented Feb 16, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
solid-connect-web-admin Error Error Feb 16, 2026 9:31am
solid-connection-web Error Error Feb 16, 2026 9:31am

@coderabbitai

coderabbitai Bot commented Feb 16, 2026

Copy link
Copy Markdown

Walkthrough

이번 변경은 API 클라이언트 코드 생성 및 관리를 위한 새로운 패키지 구조를 도입하는 작업입니다.

  1. 새로운 패키지 설정packages/api-client 디렉토리를 생성하고, 런타임 구성, 생성된 API 클라이언트, 그리고 훅을 위한 공개 진입점을 정의합니다.

  2. 로컬 구현을 외부 모듈로 마이그레이션 — 기존의 인증 및 점수 API 관련 로직을 @solid-connect/api-client 패키지의 생성된 모듈로부터 재내보내기로 변경합니다.

  3. 런타임 인증 흐름 구현 — 토큰 저장소 어댑터, 토큰 갱신, 그리고 Axios 인터셉터를 통한 자동 토큰 관리 로직을 추가합니다.

  4. 코드 생성 스크립트 추가 — Bruno CLI를 활용해 API 컬렉션과 후킹을 동기화하고, 생성된 코드의 동기화 상태를 검증하는 스크립트를 도입합니다.

  5. 빌드 및 검증 작업 확장 — Turbo 작업 정의에 코드생성 검증 태스크를 추가합니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • wibaek
  • enunsnv
  • khwww
🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (49 files):

⚔️ .github/workflows/ci.yml (content)
⚔️ .github/workflows/release.yml (content)
⚔️ .github/workflows/sync-to-frontend.yml (content)
⚔️ .husky/pre-commit (content)
⚔️ .husky/pre-push (content)
⚔️ apps/admin/package.json (content)
⚔️ apps/admin/src/components/features/scores/GpaScoreTable.tsx (content)
⚔️ apps/admin/src/components/features/scores/LanguageScoreTable.tsx (content)
⚔️ apps/admin/src/components/features/scores/ScoreVerifyButton.tsx (content)
⚔️ apps/admin/src/components/features/scores/StatusBadge.tsx (content)
⚔️ apps/admin/src/components/layout/AdminLayout.tsx (content)
⚔️ apps/admin/src/components/ui/button.tsx (content)
⚔️ apps/admin/src/components/ui/input.tsx (content)
⚔️ apps/admin/src/components/ui/table.tsx (content)
⚔️ apps/admin/src/components/ui/tabs.tsx (content)
⚔️ apps/admin/src/lib/api/auth.ts (content)
⚔️ apps/admin/src/lib/api/client.ts (content)
⚔️ apps/admin/src/lib/api/scores.ts (content)
⚔️ apps/admin/src/routes/scores/index.tsx (content)
⚔️ apps/admin/src/styles.css (content)
⚔️ apps/admin/src/types/auth.ts (content)
⚔️ apps/admin/src/types/scores.ts (content)
⚔️ apps/admin/vite.config.ts (content)
⚔️ apps/web/src/app/(home)/_ui/NewsSection/_hooks/useSectionHadnler.ts (content)
⚔️ apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/_hooks/useSelectedTab.ts (content)
⚔️ apps/web/src/app/mentor/chat/[chatId]/_ui/ChatContent/_ui/ChatMessageBox/index.tsx (content)
⚔️ apps/web/src/app/my/modify/_ui/ModifyContent/_ui/ImageInputFiled/_hooks/useImageInputHandler.ts (content)
⚔️ apps/web/src/app/university/application/ScorePageContent.tsx (content)
⚔️ apps/web/src/app/university/application/ScoreSearchBar.tsx (content)
⚔️ apps/web/src/app/university/score/submit/language-test/LanguageTestSubmitForm.tsx (content)
⚔️ apps/web/src/components/layout/RootModal/index.tsx (content)
⚔️ apps/web/src/components/layout/RootModal/ui/ClientModal/index.tsx (content)
⚔️ apps/web/src/components/mentor/ChannelSelct/_hooks/useSelectHandler.ts (content)
⚔️ apps/web/src/components/mentor/MentorApplyCountContent/index.tsx (content)
⚔️ apps/web/src/components/mentor/MentorExpandChatCard/hooks/useExpandCardClickHandler.ts (content)
⚔️ apps/web/src/components/ui/BottomSheet/hooks/useHandleModal.ts (content)
⚔️ apps/web/src/components/ui/BottomSheet/index.tsx (content)
⚔️ apps/web/src/components/ui/FallbackImage.tsx (content)
⚔️ apps/web/src/components/ui/ProfileWithBadge.tsx (content)
⚔️ apps/web/src/lib/zustand/useAuthStore.ts (content)
⚔️ apps/web/tsconfig.ci.json (content)
⚔️ docs/bruno-typescript-guide.md (content)
⚔️ docs/development-workflow.md (content)
⚔️ docs/skills/univ-extends-bruno-sync-skill.md (content)
⚔️ package.json (content)
⚔️ packages/api-schema/scripts/sync-bruno.mjs (content)
⚔️ pnpm-lock.yaml (content)
⚔️ turbo.json (content)
⚔️ vercel.json (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 핵심 변경사항(api-client 공통 패키지 도입 및 admin auth/scores 이관)을 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명이 주요 변경사항을 정리하고 검증 명령어를 제시했으나, 필수 템플릿 섹션(관련 이슈, 특이사항)이 부분적으로 누락되어 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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/admin-api-client-foundation-pr
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch feat/admin-api-client-foundation-pr
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/api-client/package.json`:
- Line 8: CI is not validating that generated API client files are up-to-date
because the package.json scripts run "sync:bruno" only during build but do not
include "codegen:check" in the CI checks; update the project's npm scripts so
that the CI validation step (the "ci:check" script) depends on or invokes
"codegen:check" (or add a dedicated CI job that runs "codegen:check") to ensure
the generated client from "sync:bruno" is verified during CI; modify the
"ci:check" script to include "codegen:check" (or add a new pipeline stage that
runs "pnpm codegen:check") so any out-of-sync generated files are caught.
🧹 Nitpick comments (6)
apps/admin/package.json (1)

15-15: workspace 의존성 추가가 적절합니다.

workspace:* 프로토콜을 사용한 내부 패키지 참조가 pnpm 모노레포 관례에 부합합니다.

한 가지 참고: apps/admin에서 axios: ^1.6.7을 직접 의존성으로 유지하고 있는데, @solid-connect/api-client도 동일 버전을 선언합니다. 이관이 완료되면 admin 쪽의 직접 axios 의존성이 여전히 필요한지 확인해 보시면 좋겠습니다.

packages/api-client/scripts/sync-bruno.mjs (1)

55-65: run() 함수에서 result.error (spawn 실패) 미처리 가능성.

spawnSync가 명령어 자체를 찾지 못하는 경우(예: pnpm이 PATH에 없을 때), result.statusnull이고 result.error가 설정됩니다. 현재 로직은 status ?? 1로 exit하지만, 에러 원인이 콘솔에 출력되지 않아 디버깅이 어려울 수 있습니다.

🔧 spawn 실패 시 에러 메시지 출력 제안
 function run(command, args, cwd = rootDir) {
   const result = spawnSync(command, args, {
     cwd,
     stdio: "inherit",
     env: process.env,
   });

+  if (result.error) {
+    console.error(`Failed to execute ${command}:`, result.error.message);
+  }
+
   if (result.status !== 0) {
     process.exit(result.status ?? 1);
   }
 }
packages/api-client/src/runtime/axiosInstance.ts (2)

82-116: 동시 401 응답 시 중복 토큰 갱신 가능성이 있습니다.

여러 요청이 동시에 401을 받으면 각각 독립적으로 reissueAccessToken을 호출합니다. 이로 인해 불필요한 갱신 API 호출이 발생하거나, 서버가 refresh token을 rotation 방식으로 관리하는 경우 이전 갱신의 결과가 무효화될 수 있습니다.

일반적인 해결 방법은 진행 중인 갱신 Promise를 공유하여 첫 번째 갱신만 실제 호출하고 나머지는 그 결과를 대기하는 패턴입니다.

♻️ 갱신 Promise 공유 패턴 예시
 let runtimeConfig: RuntimeConfig | null = null;
 let interceptorsBound = false;
+let refreshPromise: Promise<string> | null = null;
+
+const refreshAccessToken = async (refreshToken: string): Promise<string> => {
+  if (!runtimeConfig) throw new Error("Runtime not configured");
+  if (refreshPromise) return refreshPromise;
+  refreshPromise = runtimeConfig.reissueAccessToken(refreshToken).finally(() => {
+    refreshPromise = null;
+  });
+  return refreshPromise;
+};

이후 인터셉터 내 reissueAccessToken(refreshToken) 호출을 refreshAccessToken(refreshToken)으로 교체하면 됩니다.


48-80: 요청 인터셉터에서 토큰이 유효하지 않을 때 요청이 인증 없이 진행됩니다.

Lines 60-61, 68-69에서 refresh 실패 시 토큰을 정리하고 config를 그대로 반환합니다. 이는 인증 없이 요청이 서버로 전달됨을 의미합니다. 응답 인터셉터가 401/403을 잡아서 처리하므로 의도된 설계로 보이지만, 이 흐름을 코드 주석으로 명시해두면 향후 유지보수에 도움이 될 것입니다.

packages/api-client/tsconfig.json (1)

4-5: moduleResolution: "Node"에서 "Bundler"로 전환 검토 — 현재는 일관성 유지 우선

현재 패키지의 exports 필드가 .ts 소스를 직접 가리키고 있으며, Vite를 통해 소비되는 구조로 "Bundler" 전략이 더 적합할 수 있습니다. 다만 다음 점들을 고려하면 현재 설정 유지가 바람직합니다:

  1. 모노레포 일관성 — 모든 패키지(api-schema, bruno-api-typescript 등)가 이미 "Node" 전략을 사용 중입니다.
  2. TypeScript 버전 호환성 — 현재 사용 중인 ^5.3.3 버전은 "Bundler" 지원하므로 향후 전환 가능합니다.
  3. 향후 개선 사항 — 워크스페이스 전체가 moduleResolution 전략을 통일할 때 함께 검토하면 됩니다.
packages/api-client/src/index.ts (1)

1-2: 와일드카드 재수출 시 네임스페이스 충돌 가능성을 확인해 주세요.

현재 소비자들은 모두 서브패스(@solid-connect/api-client/runtime, @solid-connect/api-client/generated/admin)를 통해 import하고 있어 당장은 문제가 없습니다. 하지만 루트 배럴에서 export *로 두 모듈을 합치면, 향후 generated 코드와 runtime에서 동일한 이름의 export가 생길 경우 TypeScript 컴파일 에러가 발생할 수 있습니다.

generated 모듈이 확장되면 충돌 리스크가 높아지므로 참고 부탁드립니다.

"scripts": {
"sync:bruno": "node ./scripts/sync-bruno.mjs",
"sync:bruno:remote": "BRUNO_SOURCE_MODE=remote node ./scripts/sync-bruno.mjs",
"build": "pnpm run sync:bruno",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check turbo.json build and ci:check configuration
echo "=== turbo.json build task ==="
jq '.tasks.build' turbo.json 2>/dev/null || cat turbo.json | grep -A 10 '"build"'

echo ""
echo "=== turbo.json ci:check task ==="
jq '.tasks["ci:check"]' turbo.json 2>/dev/null || cat turbo.json | grep -A 10 '"ci:check"'

echo ""
echo "=== Check sync:bruno task config ==="
jq '.tasks["sync:bruno"]' turbo.json 2>/dev/null || true

echo ""
echo "=== List CI workflow files ==="
fd -e yml -e yaml . .github/workflows 2>/dev/null | head -20

Repository: solid-connection/solid-connect-web

Length of output: 763


🏁 Script executed:

#!/bin/bash
echo "=== CI workflow configuration ==="
cat .github/workflows/ci.yml | head -100

echo ""
echo "=== Search for turbo commands in CI workflow ==="
grep -n "turbo\|pnpm.*build\|pnpm.*check" .github/workflows/ci.yml || echo "No turbo/pnpm commands found in snippet"

Repository: solid-connection/solid-connect-web

Length of output: 2849


🏁 Script executed:

#!/bin/bash
echo "=== Full CI workflow file ==="
wc -l .github/workflows/ci.yml

echo ""
echo "=== Search for codegen references in CI workflow ==="
grep -n "codegen" .github/workflows/ci.yml || echo "No codegen references found"

echo ""
echo "=== api-client package.json exports and build info ==="
cat packages/api-client/package.json | jq '{name, scripts, exports, type, main}'

echo ""
echo "=== Check api-client's actual output structure ==="
ls -la packages/api-client/src/ 2>/dev/null | head -20 || echo "No src directory visible"

Repository: solid-connection/solid-connect-web

Length of output: 1351


codegen:check가 CI 파이프라인에 포함되지 않고 있습니다.

원래 리뷰 의견이 가정했던 상황과 실제가 다릅니다. 현재 설계의 핵심:

  1. 빌드 중 생성 파일 갱신: "build": "pnpm run sync:bruno" 설정은 의도된 것입니다. Admin 앱 빌드 시 turbo의 ^build 의존성으로 인해 api-client가 빌드되며, 항상 최신 API 클라이언트를 생성합니다.

  2. 캐시 비활성화: sync:bruno 태스크에 cache: false가 설정되어 있어서 turbo 캐시를 무시하고 매번 실행됩니다. 이는 빌드 시점에 항상 신선한 생성 파일을 보장하려는 의도입니다.

  3. 누락된 검증: 문제는 codegen:check 스크립트가 존재하지만 CI 파이프라인에 포함되지 않았다는 점입니다. 현재 CI는 ci:check (lint & typecheck만)만 실행하고, 생성된 파일이 소스와 일치하는지 검증하지 않습니다.

권장사항:

  • ci:check 태스크에 codegen:check 의존성을 추가하여 CI에서 생성 파일의 동기화 상태를 검증할 것
  • 또는 별도 CI 단계에서 codegen:check를 실행하여 개발자가 로컬에서 sync:bruno를 실행하지 않은 채 커밋한 경우를 감지할 것
🤖 Prompt for AI Agents
In `@packages/api-client/package.json` at line 8, CI is not validating that
generated API client files are up-to-date because the package.json scripts run
"sync:bruno" only during build but do not include "codegen:check" in the CI
checks; update the project's npm scripts so that the CI validation step (the
"ci:check" script) depends on or invokes "codegen:check" (or add a dedicated CI
job that runs "codegen:check") to ensure the generated client from "sync:bruno"
is verified during CI; modify the "ci:check" script to include "codegen:check"
(or add a new pipeline stage that runs "pnpm codegen:check") so any out-of-sync
generated files are caught.

@manNomi manNomi closed this Feb 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant