Skip to content

#71 feat: 과제 제출 API 연동#73

Merged
suminb99 merged 89 commits intodevelopfrom
feat/71-assignment-submit-api
Mar 23, 2026
Merged

#71 feat: 과제 제출 API 연동#73
suminb99 merged 89 commits intodevelopfrom
feat/71-assignment-submit-api

Conversation

@suminb99
Copy link
Copy Markdown
Contributor

@suminb99 suminb99 commented Mar 18, 2026

⚙️ Related ISSUE Number

Related #71



📄 Work Description

  • 과제 상세, 코드 조회/제출, 사이드바 강의 API 연동
  • WebSocket 기반 코드 실행 훅 및 UI 연동
  • 과제 제출 결과 모달 연동 (추후 업데이트 필요)
  • 과제 제출 라우트에 courseId 추가 및 권한 분리
  • 하드코딩된 경로를 ROUTES 상수로 교체 및 API 엔드포인트 상수화
  • 코드 조회 API 연동
  • 코드 제출 API 및 관련 스키마, useAssignmentSubmission 훅 업데이트
  • etc



📷 Screenshot



💬 To Reviewers

TODO:

  • 코드 실행 기능 업데이트



🔗 Reference

suminb99 added 30 commits March 8, 2026 21:37
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/entities/assignment/api/assignmentQueries.ts (1)

31-36: ⚠️ Potential issue | 🟠 Major

getAssignmentsByCourse 호출 시 불필요한 API 요청이 발생합니다.

useAssignmentList 훅에서 selectedCourseId ?? 0으로 호출하면, selectedCourseIdnull일 때도 courseId=0으로 쿼리가 실행됩니다. 다만 조건 처리(20번 줄: selectedCourseId ? ... : ...)로 인해 결과는 사용되지 않아 불필요한 API 호출이 발생합니다.

수정 제안:

useAssignmentList 훅에서 enabled 옵션을 추가하여 selectedCourseId가 없을 때 쿼리 실행을 방지하세요:

const {data: assignments} = useQuery({
  ...assignmentQueries.getAssignmentsByCourse(selectedCourseId ?? 0),
  enabled: selectedCourseId !== null
});

이렇게 하면 선택된 강의가 없을 때 불필요한 요청을 차단할 수 있습니다. React Query 문서 - enabled 옵션을 참고하세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/entities/assignment/api/assignmentQueries.ts` around lines 31 - 36, The
current getAssignmentsByCourse query is being called with courseId=0 when
selectedCourseId is null, causing unnecessary API calls; in useAssignmentList
(where you call assignmentQueries.getAssignmentsByCourse(selectedCourseId ?? 0))
add the React Query enabled option so the query only runs when a course is
selected: call
useQuery({...assignmentQueries.getAssignmentsByCourse(selectedCourseId ?? 0),
enabled: selectedCourseId !== null}) to prevent the queryFn from executing when
selectedCourseId is null; keep the existing queryOptions/getAssignmentsByCourse
implementation unchanged.
♻️ Duplicate comments (3)
src/pages/submit-assignment/AssignmentSubmitPage.tsx (1)

9-9: ⚠️ Potential issue | 🔴 Critical

Mock 결과가 하드코딩되어 실제 API 응답이 사용되지 않습니다.

tcPassResponse를 직접 사용하고 있어서 실제 제출 결과가 UI에 반영되지 않습니다. useAssignmentSubmission 훅에서 반환하는 result를 사용해야 합니다.

또한 tcFailResponse는 import만 되고 사용되지 않아 ESLint 오류가 발생합니다.

🔧 수정 제안
-import {tcFailResponse, tcPassResponse} from './mock';
+// mock import 제거 또는 개발 플래그로 분기

-  const result = tcPassResponse.response;

-  const {onSubmit, isSubmitPending, isModalOpen, closeModal} =
-    useAssignmentSubmission(courseDetails, Number(assignmentId)); // result 임시 제거
+  const {onSubmit, isSubmitPending, isModalOpen, closeModal, result} =
+    useAssignmentSubmission(courseDetails, Number(assignmentId));

   // ...
   {isModalOpen && result && (
     <SubmissionResultModal result={result} closeModal={closeModal} />
   )}

참고: TanStack Query - Mutation 응답 처리

,

Also applies to: 19-19, 33-34, 56-57

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/submit-assignment/AssignmentSubmitPage.tsx` at line 9, 현재
AssignmentSubmitPage가 하드코딩된 tcPassResponse를 사용해 실제 API 결과를 무시하고 있으며
tcFailResponse는 불사용으로 ESLint 오류를 유발합니다; 수정하려면 AssignmentSubmitPage에서
useAssignmentSubmission 훅이 반환하는 result(예: const { mutate, result } =
useAssignmentSubmission(...))를 화면에 반영하도록 모든 UI 참조(성공/실패 메시지, 상태 표시 등)를
tcPassResponse 대신 result로 바꾸고, 더 이상 필요없는 tcFailResponse import는 제거해 주세요; 또한 제출
흐름에서 mutate 호출 후에 result가 업데이트되는지 확인하고(또는 onSuccess/onError 콜백을 사용) UI 상태가 그 값을
기반으로 렌더링되도록 조정하세요.
src/pages/submit-assignment/ui/AssignmentProblem.tsx (1)

23-27: ⚠️ Potential issue | 🟠 Major

뒤로가기 경로가 항상 관리자 경로로 고정되어 있습니다.

학생 사용자가 이 페이지에서 뒤로가기를 클릭하면 관리자 페이지로 이동하게 됩니다. 현재 pathname을 기준으로 admin/student를 분기하고, courseId가 없을 때의 fallback 처리도 필요합니다.

🔧 수정 제안
-import {useNavigate, useParams} from 'react-router-dom';
+import {useLocation, useNavigate, useParams} from 'react-router-dom';

 const AssignmentProblem = ({...}: AssignmentProblemProps) => {
   const navigate = useNavigate();
+  const {pathname} = useLocation();
   const {courseId} = useParams();
+  
+  const backPath = courseId
+    ? pathname.startsWith('/admin')
+      ? ROUTES.ADMIN.COURSES.DETAIL(courseId)
+      : ROUTES.STUDENT.COURSES.DETAIL(courseId)
+    : '/';

   // ...
-          onClick={() => navigate(ROUTES.ADMIN.COURSES.DETAIL(courseId!))}>
+          onClick={() => navigate(backPath)}>

참고: React Router useLocation

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/submit-assignment/ui/AssignmentProblem.tsx` around lines 23 - 27,
The back button always navigates to the admin course detail; change it to
inspect the current pathname (useLocation) and branch between
ROUTES.ADMIN.COURSES.DETAIL(courseId) and
ROUTES.STUDENT.COURSES.DETAIL(courseId) (or the equivalent student route) so
student users are routed correctly, and add a safe fallback when courseId is
undefined (e.g., navigate to ROUTES.ADMIN.COURSES.LIST or
ROUTES.STUDENT.COURSES.LIST based on the same pathname check); update the
onClick handler that calls navigate(...) and use the location-based branch logic
around courseId to avoid routing to admin for students.
src/entities/assignment/model/schemas.ts (1)

52-55: ⚠️ Potential issue | 🔴 Critical

condeId 오타 수정 필요 - codeId로 변경해주세요.

condeId는 명백한 오타입니다. 백엔드 API가 codeId를 반환하면 Zod 스키마 검증이 실패하거나 해당 필드가 undefined로 파싱됩니다.

관련 파일에서도 동일하게 수정이 필요합니다:

  • src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts (Line 31)
🔧 수정 제안
 export const assignmentSubmissionResultSchema = z.object({
-  condeId: z.number(),
+  codeId: z.number(),
   score: z.number(),
 });

참고: Zod 공식 문서 - Object Schemas

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/entities/assignment/model/schemas.ts` around lines 52 - 55, The Zod
schema field name is misspelled: update assignmentSubmissionResultSchema to use
codeId instead of condeId (change the key in the z.object from condeId to codeId
and keep type z.number()), and make the same rename where the parsed property is
used in the submission hook (update references in useAssignmentSubmission —
e.g., any variable/property accesses named condeId should be renamed to codeId)
so schema validation and subsequent code use the correct API field.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pages/submit-assignment/AssignmentSubmitPage.tsx`:
- Around line 28-31: The expression Number(codeId) ?? 0 doesn't fall back for
NaN; change the conversion so the fallback applies before or via a falsy-check.
Replace the argument to assignmentQueries.getAssignmentCode in the useQuery call
to compute a safe numeric id (e.g., use Number(codeId ?? 0) or compute const
safeCodeId = codeId ? Number(codeId) : 0 and pass safeCodeId) so
getAssignmentCode(Number(...)) receives 0 when codeId is missing or invalid;
update the useQuery call that references assignmentQueries.getAssignmentCode and
codeId accordingly.

---

Outside diff comments:
In `@src/entities/assignment/api/assignmentQueries.ts`:
- Around line 31-36: The current getAssignmentsByCourse query is being called
with courseId=0 when selectedCourseId is null, causing unnecessary API calls; in
useAssignmentList (where you call
assignmentQueries.getAssignmentsByCourse(selectedCourseId ?? 0)) add the React
Query enabled option so the query only runs when a course is selected: call
useQuery({...assignmentQueries.getAssignmentsByCourse(selectedCourseId ?? 0),
enabled: selectedCourseId !== null}) to prevent the queryFn from executing when
selectedCourseId is null; keep the existing queryOptions/getAssignmentsByCourse
implementation unchanged.

---

Duplicate comments:
In `@src/entities/assignment/model/schemas.ts`:
- Around line 52-55: The Zod schema field name is misspelled: update
assignmentSubmissionResultSchema to use codeId instead of condeId (change the
key in the z.object from condeId to codeId and keep type z.number()), and make
the same rename where the parsed property is used in the submission hook (update
references in useAssignmentSubmission — e.g., any variable/property accesses
named condeId should be renamed to codeId) so schema validation and subsequent
code use the correct API field.

In `@src/pages/submit-assignment/AssignmentSubmitPage.tsx`:
- Line 9: 현재 AssignmentSubmitPage가 하드코딩된 tcPassResponse를 사용해 실제 API 결과를 무시하고 있으며
tcFailResponse는 불사용으로 ESLint 오류를 유발합니다; 수정하려면 AssignmentSubmitPage에서
useAssignmentSubmission 훅이 반환하는 result(예: const { mutate, result } =
useAssignmentSubmission(...))를 화면에 반영하도록 모든 UI 참조(성공/실패 메시지, 상태 표시 등)를
tcPassResponse 대신 result로 바꾸고, 더 이상 필요없는 tcFailResponse import는 제거해 주세요; 또한 제출
흐름에서 mutate 호출 후에 result가 업데이트되는지 확인하고(또는 onSuccess/onError 콜백을 사용) UI 상태가 그 값을
기반으로 렌더링되도록 조정하세요.

In `@src/pages/submit-assignment/ui/AssignmentProblem.tsx`:
- Around line 23-27: The back button always navigates to the admin course
detail; change it to inspect the current pathname (useLocation) and branch
between ROUTES.ADMIN.COURSES.DETAIL(courseId) and
ROUTES.STUDENT.COURSES.DETAIL(courseId) (or the equivalent student route) so
student users are routed correctly, and add a safe fallback when courseId is
undefined (e.g., navigate to ROUTES.ADMIN.COURSES.LIST or
ROUTES.STUDENT.COURSES.LIST based on the same pathname check); update the
onClick handler that calls navigate(...) and use the location-based branch logic
around courseId to avoid routing to admin for students.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5ad5ce8a-bfe9-41eb-abd5-f15387ea4d02

📥 Commits

Reviewing files that changed from the base of the PR and between 02c68f2 and a2b75c9.

📒 Files selected for processing (10)
  • src/entities/assignment/api/assignmentApi.ts
  • src/entities/assignment/api/assignmentQueries.ts
  • src/entities/assignment/model/schemas.ts
  • src/entities/course/model/schemas.ts
  • src/pages/course-overview/ui/AssignmentList.tsx
  • src/pages/dashboard/Dashboard.tsx
  • src/pages/dashboard/ui/CourseCard.tsx
  • src/pages/submit-assignment/AssignmentSubmitPage.tsx
  • src/pages/submit-assignment/ui/AssignmentProblem.tsx
  • src/pages/submit-assignment/ui/CodeEditor.tsx
✅ Files skipped from review due to trivial changes (3)
  • src/pages/dashboard/ui/CourseCard.tsx
  • src/pages/dashboard/Dashboard.tsx
  • src/pages/submit-assignment/ui/CodeEditor.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/pages/course-overview/ui/AssignmentList.tsx
  • src/entities/assignment/api/assignmentApi.ts

Comment thread src/pages/submit-assignment/AssignmentSubmitPage.tsx
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts (1)

40-45: ⚠️ Potential issue | 🟠 Major

unitId 누락을 0으로 대체하지 말고 제출을 중단해 주세요.

Line 42는 매핑 실패를 숨겨 잘못된 API 경로로 요청을 보낼 수 있습니다. unitId가 없으면 submit 호출 전에 조기 반환 처리해 주세요.
참고: https://tanstack.com/query/latest/docs/framework/react/guides/mutations

수정 예시
   const onSubmit = (code: string) => {
+    if (!unitId) {
+      console.error('unitId를 찾을 수 없어 제출을 중단합니다.');
+      return;
+    }
     submit({
-      unitId: unitId ?? 0,
-      assignmentId: Number(assignmentId),
+      unitId,
+      assignmentId,
       code,
     });
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts`
around lines 40 - 45, The onSubmit in useAssignmentSubmission currently masks a
missing unitId by substituting 0 and proceeding to call submit; change it to
validate unitId before calling submit (e.g., if unitId is
null/undefined/invalid, early return or throw) so you don't send requests to an
incorrect API path—update the onSubmit handler to perform this guard before
invoking submit({ unitId, assignmentId: Number(assignmentId), code }) and keep
the rest of the submit payload unchanged; reference onSubmit and the submit
mutation in useAssignmentSubmission when making the change.
src/features/assignment/submit-assignment/ui/SubmissionResultModal.tsx (1)

38-40: ⚠️ Potential issue | 🟠 Major

아이콘 닫기 버튼에 접근 가능한 이름을 추가해 주세요.

Line 38 버튼은 아이콘-only라 보조기기에서 목적이 전달되지 않습니다. aria-label을 넣고 아이콘은 장식용으로 숨겨 주세요.
참고: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label

수정 예시
-            <button className='cursor-pointer' onClick={closeModal}>
-              <DeleteIcon className='w-4 h-4' />
+            <button
+              className='cursor-pointer'
+              onClick={closeModal}
+              aria-label='모달 닫기'>
+              <DeleteIcon className='w-4 h-4' aria-hidden='true' />
             </button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/assignment/submit-assignment/ui/SubmissionResultModal.tsx`
around lines 38 - 40, The close icon button in SubmissionResultModal is
icon-only and lacks an accessible name; update the button that calls closeModal
to include an explicit aria-label (e.g., aria-label="Close") and mark the
decorative icon (DeleteIcon) as hidden from assistive tech (e.g.,
aria-hidden="true" or equivalent) so screen readers announce the button purpose
while the icon is ignored; locate this change near the button element that wraps
DeleteIcon in the SubmissionResultModal component and ensure onClick still
invokes closeModal.
src/pages/submit-assignment/AssignmentSubmitPage.tsx (1)

31-31: ⚠️ Potential issue | 🔴 Critical

Number(codeId) ?? 0는 fallback이 동작하지 않아 CI를 깨뜨립니다.

Line 31은 Number() 결과가 null/undefined가 아니므로 ?? 0가 무의미합니다. 현재 ESLint/CI 실패 원인이라 바로 수정이 필요합니다.

수정 예시
-  const {data: assignmentCode} = useQuery({
-    ...assignmentQueries.getAssignmentCode(Number(codeId) ?? 0),
+  const safeCodeId = codeId ? Number(codeId) : 0;
+  const {data: assignmentCode} = useQuery({
+    ...assignmentQueries.getAssignmentCode(safeCodeId),
     enabled: !!codeId,
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/submit-assignment/AssignmentSubmitPage.tsx` at line 31, The
expression Number(codeId) ?? 0 doesn't provide a fallback because Number(...)
never returns null/undefined (it yields NaN on invalid input), so change the
argument passed to assignmentQueries.getAssignmentCode to use a falsy-check
fallback like Number(codeId) || 0 (or codeId ? Number(codeId) : 0) so
NaN/undefined/empty values fall back to 0; update the call where
assignmentQueries.getAssignmentCode(Number(codeId) ?? 0) appears to
assignmentQueries.getAssignmentCode(Number(codeId) || 0).
🧹 Nitpick comments (3)
src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts (1)

34-36: 실패 시 사용자 피드백 경로를 하나 추가해 주세요.

현재는 콘솔 로그만 남아 사용자 입장에서 실패 원인을 알기 어렵습니다. 토스트/에러 배너 등 최소 1개 사용자 피드백 경로를 권장합니다.
참고: https://tanstack.com/query/latest/docs/framework/react/guides/mutations#mutation-side-effects

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts`
around lines 34 - 36, The onError handler in useAssignmentSubmission currently
only logs to console; update the onError: (error) => { ... } block in
useAssignmentSubmission to also surface a user-facing feedback (e.g., call your
toast/error-banner API or set a submission error state) so the user sees a clear
message like "과제 제출에 실패했습니다" with optional error detail; ensure you reference
the same onError callback inside useAssignmentSubmission and include the error
message or a user-friendly mapping when invoking the toast/badge.
src/pages/submit-assignment/AssignmentSubmitPage.tsx (1)

70-72: 조건부 렌더링의 false 분기는 null로 단순화해도 됩니다.

빈 Fragment(<></>) 대신 null을 반환하면 의도가 더 명확해집니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/submit-assignment/AssignmentSubmitPage.tsx` around lines 70 - 72,
In AssignmentSubmitPage replace the conditional false branch that currently
returns an empty fragment (<> </>) with null to simplify and clarify intent;
locate the JSX conditional in the AssignmentSubmitPage component (the ternary
rendering block around lines shown) and change the false/else expression to
return null instead of an empty fragment.
src/features/assignment/submit-assignment/ui/SubmissionResultModal.tsx (1)

1-1: 주석 처리된 import는 제거해도 됩니다.

Line 1은 이미 Line 5에서 동일 타입을 정상 import하고 있어 유지 이점이 없습니다. 작은 정리지만 파일 가독성이 좋아집니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/assignment/submit-assignment/ui/SubmissionResultModal.tsx` at
line 1, Remove the redundant commented import of the type
TAssignmentSubmissionResult at the top of SubmissionResultModal.tsx; locate the
commented line "// import type {TAssignmentSubmissionResult} ..." and delete it
since TAssignmentSubmissionResult is already imported later (the existing import
on the file that provides the same type can be retained).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/features/assignment/submit-assignment/ui/SubmissionResultModal.tsx`:
- Around line 31-41: The modal container currently lacks ARIA dialog semantics;
update the root div rendered by SubmissionResultModal (the element using
styles.base()) to include role="dialog" and aria-modal="true" so screen readers
recognize it as a modal dialog; ensure any existing close handler (closeModal)
and the heading (RESULT_MESSAGE[status]) remain focusable/identifiable and, if
not already present, ensure the title element (h3) can be referenced (e.g.,
aria-labelledby) from the dialog container for proper labeling.

---

Duplicate comments:
In `@src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts`:
- Around line 40-45: The onSubmit in useAssignmentSubmission currently masks a
missing unitId by substituting 0 and proceeding to call submit; change it to
validate unitId before calling submit (e.g., if unitId is
null/undefined/invalid, early return or throw) so you don't send requests to an
incorrect API path—update the onSubmit handler to perform this guard before
invoking submit({ unitId, assignmentId: Number(assignmentId), code }) and keep
the rest of the submit payload unchanged; reference onSubmit and the submit
mutation in useAssignmentSubmission when making the change.

In `@src/features/assignment/submit-assignment/ui/SubmissionResultModal.tsx`:
- Around line 38-40: The close icon button in SubmissionResultModal is icon-only
and lacks an accessible name; update the button that calls closeModal to include
an explicit aria-label (e.g., aria-label="Close") and mark the decorative icon
(DeleteIcon) as hidden from assistive tech (e.g., aria-hidden="true" or
equivalent) so screen readers announce the button purpose while the icon is
ignored; locate this change near the button element that wraps DeleteIcon in the
SubmissionResultModal component and ensure onClick still invokes closeModal.

In `@src/pages/submit-assignment/AssignmentSubmitPage.tsx`:
- Line 31: The expression Number(codeId) ?? 0 doesn't provide a fallback because
Number(...) never returns null/undefined (it yields NaN on invalid input), so
change the argument passed to assignmentQueries.getAssignmentCode to use a
falsy-check fallback like Number(codeId) || 0 (or codeId ? Number(codeId) : 0)
so NaN/undefined/empty values fall back to 0; update the call where
assignmentQueries.getAssignmentCode(Number(codeId) ?? 0) appears to
assignmentQueries.getAssignmentCode(Number(codeId) || 0).

---

Nitpick comments:
In `@src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts`:
- Around line 34-36: The onError handler in useAssignmentSubmission currently
only logs to console; update the onError: (error) => { ... } block in
useAssignmentSubmission to also surface a user-facing feedback (e.g., call your
toast/error-banner API or set a submission error state) so the user sees a clear
message like "과제 제출에 실패했습니다" with optional error detail; ensure you reference
the same onError callback inside useAssignmentSubmission and include the error
message or a user-friendly mapping when invoking the toast/badge.

In `@src/features/assignment/submit-assignment/ui/SubmissionResultModal.tsx`:
- Line 1: Remove the redundant commented import of the type
TAssignmentSubmissionResult at the top of SubmissionResultModal.tsx; locate the
commented line "// import type {TAssignmentSubmissionResult} ..." and delete it
since TAssignmentSubmissionResult is already imported later (the existing import
on the file that provides the same type can be retained).

In `@src/pages/submit-assignment/AssignmentSubmitPage.tsx`:
- Around line 70-72: In AssignmentSubmitPage replace the conditional false
branch that currently returns an empty fragment (<> </>) with null to simplify
and clarify intent; locate the JSX conditional in the AssignmentSubmitPage
component (the ternary rendering block around lines shown) and change the
false/else expression to return null instead of an empty fragment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c32b42e5-c232-4875-95f5-984f0a658311

📥 Commits

Reviewing files that changed from the base of the PR and between a2b75c9 and 51504c2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • src/entities/assignment/model/schemas.ts
  • src/entities/course/model/schemas.ts
  • src/entities/course/model/types.ts
  • src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts
  • src/features/assignment/submit-assignment/ui/SubmissionResultModal.tsx
  • src/pages/submit-assignment/AssignmentSubmitPage.tsx
  • src/pages/submit-assignment/ui/AssignmentProblem.tsx
  • src/pages/submit-assignment/ui/AssignmentSideBarItem.tsx
  • src/pages/submit-assignment/ui/CodeEditor.tsx
  • src/pages/submit-assignment/ui/Terminal.tsx
  • src/shared/ui/button/button-styles.ts
  • src/themes/onedarkpro.json
💤 Files with no reviewable changes (1)
  • src/entities/course/model/types.ts
✅ Files skipped from review due to trivial changes (1)
  • src/themes/onedarkpro.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/shared/ui/button/button-styles.ts
  • src/pages/submit-assignment/ui/AssignmentProblem.tsx
  • src/pages/submit-assignment/ui/CodeEditor.tsx
  • src/pages/submit-assignment/ui/AssignmentSideBarItem.tsx
  • src/entities/assignment/model/schemas.ts

Copy link
Copy Markdown

@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

♻️ Duplicate comments (3)
src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts (1)

40-45: ⚠️ Potential issue | 🟠 Major

unitId! 단언 대신 제출 전 검증이 필요합니다.

Line 42의 unitId!는 매핑 실패를 숨깁니다. unitId가 없으면 제출을 중단하고 에러 처리로 분기해야 잘못된 API 호출을 막을 수 있습니다.
공식 문서: https://tanstack.com/query/latest/docs/framework/react/guides/mutations

🔧 제안 패치
 const onSubmit = (code: string) => {
+  if (unitId == null) {
+    console.error('unitId를 찾을 수 없어 제출을 중단합니다.');
+    return;
+  }
   submit({
-    unitId: unitId!,
-    assignmentId: Number(assignmentId),
+    unitId,
+    assignmentId,
     code,
   });
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts`
around lines 40 - 45, The onSubmit handler uses a non-null assertion on unitId
(unitId!) which hides missing mapping; before calling submit (the mutation
triggered in onSubmit), validate that unitId is present and early-return or call
the mutation's onError/consumer error path when it's missing (e.g., check if
unitId is undefined/null, log or surface an error and do not call submit).
Update the onSubmit function to perform this guard using the unitId variable and
keep the submit invocation only when unitId is valid (use Number(assignmentId)
as before).
src/entities/assignment/model/schemas.ts (1)

29-33: ⚠️ Potential issue | 🟠 Major

isPublic 전환 시 isHidden 소비 코드도 함께 마이그레이션이 필요합니다.

Line 32, Line 47에서 isPublic으로 통일한 변경은 좋지만, 현재 AssignmentFormPage.tsxisHidden을 구조분해하고 있어 데이터 바인딩이 깨질 수 있습니다. 스키마 변경과 소비 코드 변경을 한 PR 범위에서 같이 맞춰 주세요.
공식 문서: https://zod.dev/?id=objects

🔧 제안 패치 (소비 코드 동기화 예시)
- assignmentData.testcases.map(({testcase, answer, isHidden}) => ({
+ assignmentData.testcases.map(({testcase, answer, isPublic}) => ({
    testcase,
    answer,
-   isHidden,
+   isHidden: !isPublic,
  }))

Also applies to: 47-47

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/entities/assignment/model/schemas.ts` around lines 29 - 33, The schema
field was renamed from isHidden to isPublic (seen in id, testcase, answer,
isPublic), but the consumer AssignmentFormPage.tsx still destructures and binds
isHidden, breaking data flow; update the consumer to read and write isPublic
instead of isHidden (or add a compatibility mapping when reading form values)
and ensure any default values, prop typings, and submit payloads in
AssignmentFormPage.tsx are updated to use isPublic so the Zod schema and usage
stay in sync.
src/index.css (1)

101-103: ⚠️ Potential issue | 🟠 Major

custom-scrollbar hover 중첩 선택자가 Stylelint 에러를 계속 유발합니다.

Line 101은 nesting-selector-no-missing-scoping-root에 걸립니다. 스코핑 루트를 명시해 lint blocker를 제거해 주세요.
공식 문서: https://stylelint.io/user-guide/rules/nesting-selector-no-missing-scoping-root

🔧 제안 패치
-  &:hover ::-webkit-scrollbar-thumb {
+  &.custom-scrollbar:hover ::-webkit-scrollbar-thumb {
     background-color: `#555267`;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/index.css` around lines 101 - 103, The nested selector "&:hover
::-webkit-scrollbar-thumb" is missing a scoping root and triggers Stylelint's
nesting-selector-no-missing-scoping-root; fix it by changing the selector to
target the element's pseudo-element directly (remove the space) as
"&:hover::-webkit-scrollbar-thumb" so the scoping root is explicit and the lint
error is resolved.
🧹 Nitpick comments (1)
src/pages/course-overview/ui/AssignmentList.tsx (1)

49-51: 불필요한 non-null assertion 제거

courseIdAssignmentItemProps에서 number 타입으로 필수 prop으로 정의되어 있습니다. 따라서 courseId!의 non-null assertion(!)은 불필요합니다.

♻️ 개선안
  const assignmentPath = pathname.startsWith('/admin')
-    ? `${ROUTES.ADMIN.ASSIGNMENTS.SUBMIT(courseId!, id)}`
-    : `${ROUTES.STUDENT.ASSIGNMENTS.SUBMIT(courseId!, id)}`;
+    ? ROUTES.ADMIN.ASSIGNMENTS.SUBMIT(courseId, id)
+    : ROUTES.STUDENT.ASSIGNMENTS.SUBMIT(courseId, id);

추가로 템플릿 리터럴 래핑도 불필요합니다. ROUTES.*.SUBMIT()이 이미 문자열을 반환하므로 직접 사용 가능합니다.

참고: TypeScript Non-null Assertion Operator

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/course-overview/ui/AssignmentList.tsx` around lines 49 - 51, Remove
the unnecessary non-null assertion and extra template literal wrapping when
computing assignmentPath: AssignmentItemProps declares courseId as number so
drop the postfix "!" and call ROUTES.ADMIN.ASSIGNMENTS.SUBMIT(courseId, id) or
ROUTES.STUDENT.ASSIGNMENTS.SUBMIT(courseId, id) directly instead of wrapping the
result in a template string; update the assignmentPath expression that currently
references assignmentPath, ROUTES.ADMIN.ASSIGNMENTS.SUBMIT, and
ROUTES.STUDENT.ASSIGNMENTS.SUBMIT accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/entities/assignment/model/schemas.ts`:
- Around line 37-50: The assignmentDetailSchema is missing the score field
consumed by AssignmentFormPage (assignmentData.score), causing
parsing/consumption mismatch; update assignmentDetailSchema to include a score
property that matches the type expected by the consumer (e.g., add score:
z.number() or score: z.number().nullable() if it can be null) so parsed
assignmentData aligns with AssignmentFormPage's usage.

In `@src/index.css`:
- Line 111: The class token w-73.75 used in the `@apply` on the rule containing
"bg-white w-73.75 px-[14.6px] py-[10.5px] ..." is undefined so Tailwind won't
generate CSS; fix by either replacing w-73.75 with an explicit computed value
like w-[295px] in that `@apply`, or add a corresponding custom width token to your
Tailwind theme (e.g., define a spacing/width key for 73.75) and then use that
theme token name instead; update the rule to use the chosen approach so the
width is valid and compiled.

In `@src/pages/submit-assignment/ui/CodeEditor.tsx`:
- Around line 112-117: The editor uses defaultValue which only applies on
initial mount so updates to assignmentCode from async API won't show; update the
component to capture the Monaco editor instance in handleEditorDidMount (or a
ref created there) and when assignmentCode changes call
editor.setValue(assignmentCode) (e.g., via a useEffect that checks the stored
editor ref), leaving handleEditorWillMount unchanged—ensure you reference
Editor, defaultValue, assignmentCode, handleEditorDidMount, and
editor.setValue() when making the change.

---

Duplicate comments:
In `@src/entities/assignment/model/schemas.ts`:
- Around line 29-33: The schema field was renamed from isHidden to isPublic
(seen in id, testcase, answer, isPublic), but the consumer
AssignmentFormPage.tsx still destructures and binds isHidden, breaking data
flow; update the consumer to read and write isPublic instead of isHidden (or add
a compatibility mapping when reading form values) and ensure any default values,
prop typings, and submit payloads in AssignmentFormPage.tsx are updated to use
isPublic so the Zod schema and usage stay in sync.

In `@src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts`:
- Around line 40-45: The onSubmit handler uses a non-null assertion on unitId
(unitId!) which hides missing mapping; before calling submit (the mutation
triggered in onSubmit), validate that unitId is present and early-return or call
the mutation's onError/consumer error path when it's missing (e.g., check if
unitId is undefined/null, log or surface an error and do not call submit).
Update the onSubmit function to perform this guard using the unitId variable and
keep the submit invocation only when unitId is valid (use Number(assignmentId)
as before).

In `@src/index.css`:
- Around line 101-103: The nested selector "&:hover ::-webkit-scrollbar-thumb"
is missing a scoping root and triggers Stylelint's
nesting-selector-no-missing-scoping-root; fix it by changing the selector to
target the element's pseudo-element directly (remove the space) as
"&:hover::-webkit-scrollbar-thumb" so the scoping root is explicit and the lint
error is resolved.

---

Nitpick comments:
In `@src/pages/course-overview/ui/AssignmentList.tsx`:
- Around line 49-51: Remove the unnecessary non-null assertion and extra
template literal wrapping when computing assignmentPath: AssignmentItemProps
declares courseId as number so drop the postfix "!" and call
ROUTES.ADMIN.ASSIGNMENTS.SUBMIT(courseId, id) or
ROUTES.STUDENT.ASSIGNMENTS.SUBMIT(courseId, id) directly instead of wrapping the
result in a template string; update the assignmentPath expression that currently
references assignmentPath, ROUTES.ADMIN.ASSIGNMENTS.SUBMIT, and
ROUTES.STUDENT.ASSIGNMENTS.SUBMIT accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 07f49ef5-ff9e-4174-a482-6ddf057b974f

📥 Commits

Reviewing files that changed from the base of the PR and between 51504c2 and 6fcb258.

⛔ Files ignored due to path filters (1)
  • public/fonts/JetBrainsMono-Regular.ttf is excluded by !**/*.ttf
📒 Files selected for processing (8)
  • src/entities/assignment/model/schemas.ts
  • src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts
  • src/index.css
  • src/pages/course-overview/ui/AssignmentList.tsx
  • src/pages/submit-assignment/AssignmentSubmitPage.tsx
  • src/pages/submit-assignment/ui/AssignmentSideBarItem.tsx
  • src/pages/submit-assignment/ui/CodeEditor.tsx
  • src/pages/submit-assignment/ui/Terminal.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/pages/submit-assignment/ui/Terminal.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/pages/submit-assignment/AssignmentSubmitPage.tsx
  • src/pages/submit-assignment/ui/AssignmentSideBarItem.tsx

Comment thread src/entities/assignment/model/schemas.ts
Comment thread src/index.css

.input-field {
@apply bg-white w-[295px] px-[14.6px] py-[10.5px] rounded-[9px] border-[0.9px] border-purple-stroke focus:outline-1 focus:outline-primary;
@apply bg-white w-73.75 px-[14.6px] py-[10.5px] rounded-[9px] border-[0.9px] border-purple-stroke focus:outline-1 focus:outline-primary;
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 목적: w-73.75 사용과 spacing 토큰 정의 여부 확인 (read-only)
# 기대 결과:
# 1) --spacing-73.75(또는 프로젝트 컨벤션에 맞는 동등 토큰) 정의가 있거나
# 2) 없다면 w-73.75는 무효 가능성이 높음

rg -n --type=css --type=ts --type=tsx "w-73\\.75|--spacing-73\\.75|--spacing-73_75|--spacing-295"

Repository: 2025-snowCode/snowCode-Client

Length of output: 100


🏁 Script executed:

# 1. Tailwind 설정 파일 찾기
find . -maxdepth 3 -type f \( -name "tailwind.config.*" -o -name "tailwind.config.*" \) | head -20

Repository: 2025-snowCode/snowCode-Client

Length of output: 55


🏁 Script executed:

# 2. w-73.75와 spacing-73.75 토큰 검색 (올바른 ripgrep 문법)
rg "w-73\.75|spacing.*73\.75|spacing.*295" --type css --type js --type ts -n

Repository: 2025-snowCode/snowCode-Client

Length of output: 229


🏁 Script executed:

# 3. src/index.css 파일의 실제 내용 확인
head -120 src/index.css | tail -30

Repository: 2025-snowCode/snowCode-Client

Length of output: 626


🏁 Script executed:

# 4. 프로젝트 전체에서 w-[ 형식의 임의 너비값 사용 패턴 확인
rg "w-\[.*px\]" --type css -n | head -10

Repository: 2025-snowCode/snowCode-Client

Length of output: 55


🏁 Script executed:

# Tailwind 설정 파일을 전체 저장소에서 검색
find . -type f \( -name "tailwind.config.*" -o -name "tailwind.*" \) ! -path "*/node_modules/*"

Repository: 2025-snowCode/snowCode-Client

Length of output: 55


🏁 Script executed:

# 프로젝트 루트 파일들 확인
ls -la | grep -E "(tailwind|theme|config)" || echo "No matching files in root"

Repository: 2025-snowCode/snowCode-Client

Length of output: 483


🏁 Script executed:

# tsconfig나 package.json에서 Tailwind 설정 위치 확인
find . -maxdepth 2 -type f -name "tsconfig.json" -o -name "package.json" ! -path "*/node_modules/*" | head -5

Repository: 2025-snowCode/snowCode-Client

Length of output: 103


🏁 Script executed:

# CSS 변수나 spacing 토큰 정의 검색
rg "@layer|@theme|--spacing|spacing:" --type css -n | head -20

Repository: 2025-snowCode/snowCode-Client

Length of output: 130


🏁 Script executed:

# 73.75라는 값이 다른 곳에서도 사용되는지 확인
rg "73\.75|295" --type css -n

Repository: 2025-snowCode/snowCode-Client

Length of output: 229


🏁 Script executed:

# src/index.css의 `@theme` 섹션 확인 (line 47 근처)
sed -n '40,115p' src/index.css

Repository: 2025-snowCode/snowCode-Client

Length of output: 1896


🏁 Script executed:

# 전체 `@theme` 섹션의 범위와 내용 확인
rg "@theme" -A 50 --type css -n

Repository: 2025-snowCode/snowCode-Client

Length of output: 2280


🏁 Script executed:

# w-73.75가 다른 곳에서 정의되었는지 마지막 확인
rg "spacing|width" --type css -n src/index.css

Repository: 2025-snowCode/snowCode-Client

Length of output: 91


🏁 Script executed:

# 73.75 * 4 = 295 계산 확인
python3 -c "print('73.75 * 4 =', 73.75 * 4)"

Repository: 2025-snowCode/snowCode-Client

Length of output: 90


Line 111의 w-73.75는 정의되지 않은 토큰입니다.

@theme 섹션에 이 토큰이 없으면 CSS가 생성되지 않습니다. 같은 줄의 px-[14.6px], py-[10.5px] 같이 명시적 픽셀값을 사용하는 것이 일관성 있습니다. w-[295px]로 변경하거나(73.75 × 4 = 295), @theme--spacing-73.75: 73.75rem 같은 토큰을 추가해 주세요.

Tailwind CSS 너비 문서

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/index.css` at line 111, The class token w-73.75 used in the `@apply` on the
rule containing "bg-white w-73.75 px-[14.6px] py-[10.5px] ..." is undefined so
Tailwind won't generate CSS; fix by either replacing w-73.75 with an explicit
computed value like w-[295px] in that `@apply`, or add a corresponding custom
width token to your Tailwind theme (e.g., define a spacing/width key for 73.75)
and then use that theme token name instead; update the rule to use the chosen
approach so the width is valid and compiled.

Comment on lines +112 to +117
<Editor
theme='OneDarkPro'
defaultLanguage='python'
defaultValue={assignmentCode}
beforeMount={handleEditorWillMount}
onMount={handleEditorDidMount}
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

🌐 Web query:

In @monaco-editor/react, if defaultValueprop changes after mount, does the editor content update automatically, or shouldvalue/editor.setValue() be used?

💡 Result:

In @monaco-editor/react, changing the defaultValue prop after the editor mounts does not automatically update the editor content. defaultValue sets the initial value only (uncontrolled behavior). To update content after mount, use the value prop for controlled mode or call editor.setValue via the onMount callback.

Citations:


비동기 로드 후 에디터 콘텐츠가 반영되지 않는 문제를 해결하세요.

defaultValue는 초기 마운트 시점의 값만 설정하므로, API 응답으로 assignmentCode가 업데이트되어도 에디터에 반영되지 않습니다. onMount 이후 editor.setValue()를 호출하도록 수정이 필요합니다.

제안 수정사항
-import {useRef} from 'react';
+import {useEffect, useRef} from 'react';
...
   const handleEditorDidMount: OnMount = (editor) => {
     editorRef.current = editor;
   };
+
+  useEffect(() => {
+    if (assignmentCode != null && editorRef.current) {
+      editorRef.current.setValue(assignmentCode);
+    }
+  }, [assignmentCode]);
...
       <Editor
         theme='OneDarkPro'
         defaultLanguage='python'
-        defaultValue={assignmentCode}
+        defaultValue=''
         beforeMount={handleEditorWillMount}
         onMount={handleEditorDidMount}

참고: @monaco-editor/react 공식 문서

📝 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
<Editor
theme='OneDarkPro'
defaultLanguage='python'
defaultValue={assignmentCode}
beforeMount={handleEditorWillMount}
onMount={handleEditorDidMount}
<Editor
theme='OneDarkPro'
defaultLanguage='python'
defaultValue=''
beforeMount={handleEditorWillMount}
onMount={handleEditorDidMount}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/submit-assignment/ui/CodeEditor.tsx` around lines 112 - 117, The
editor uses defaultValue which only applies on initial mount so updates to
assignmentCode from async API won't show; update the component to capture the
Monaco editor instance in handleEditorDidMount (or a ref created there) and when
assignmentCode changes call editor.setValue(assignmentCode) (e.g., via a
useEffect that checks the stored editor ref), leaving handleEditorWillMount
unchanged—ensure you reference Editor, defaultValue, assignmentCode,
handleEditorDidMount, and editor.setValue() when making the change.

Copy link
Copy Markdown
Contributor

@JiiminHa JiiminHa left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!

@suminb99 suminb99 merged commit 5f20f9a into develop Mar 23, 2026
5 checks passed
@suminb99 suminb99 added the 🧩 feature 기능 개발 label Mar 23, 2026
@JiiminHa JiiminHa mentioned this pull request Mar 24, 2026
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: 과제 제출 페이지 api 및 웹소켓 연결

2 participants