Skip to content

[Fix] 인증 실패 401 통일과 삭제 문서 404 일관성, 업로드 모달 표기 수정 - #237

Merged
Gimini-3 merged 5 commits into
developfrom
fix/232
Aug 18, 2026
Merged

[Fix] 인증 실패 401 통일과 삭제 문서 404 일관성, 업로드 모달 표기 수정#237
Gimini-3 merged 5 commits into
developfrom
fix/232

Conversation

@Gimini-3

Copy link
Copy Markdown
Contributor

📌 배경

문서 API QA(시나리오 3)에서 발견한 응답 일관성 문제 2건과 UI 표기 2건을 함께 정리합니다.

🔧 변경 내용

1. 인증 실패와 권한 부족을 구분한다

SecurityConfigexceptionHandling이 없어 Spring Security 기본 동작이 나갔습니다. 토큰이 없거나 만료·위조된 요청이 본문 없는 403을 받아, Service 계층이 주는 권한 부족 403과 구분되지 않았습니다.

$ curl -i http://localhost:8080/api/documents
HTTP/1.1 403
Content-Length: 0

프론트 lib/api.ts에는 이미 401을 받으면 세션을 정리하는 분기가 있는데 백엔드가 401을 보내지 않아 동작하지 않는 코드였습니다. 이제 만료 토큰 접근 시 재로그인 경로가 실제로 탑니다. 프론트 수정은 필요하지 않습니다.

AccessDeniedHandler도 함께 등록해 경로 단위 거부(/admin/**)가 Service 계층과 같은 ROLE-002 본문을 주도록 맞췄습니다.

2. 삭제된 문서의 새 버전 업로드를 404로 통일한다

soft delete된 문서는 상세·본문·파일·상태 조회와 수정, 중복 삭제가 모두 404 DOCUMENT-001인데 새 버전 업로드만 409 DOCUMENT-VERSION-003이었습니다. validate()에 삭제 검사가 없어 DELETED 상태가 마지막 else로 떨어진 결과입니다.

검사 위치는 소유자 확인 다음으로 잡았습니다. DocumentQueryService.getReadableDocument가 권한 확인 뒤에 삭제를 판별하는 것과 같은 순서이며, 삭제 검사를 앞에 두면 타인의 삭제된 문서 존재 여부가 404와 403 차이로 드러납니다.

3. 업로드 모달 파일 크기 표기

크기를 MB로 고정 계산해 1KB 미만 파일이 0.00MB로 보였습니다. 문서 상세는 구간별 단위로 올바르게 표시하고 있어 같은 파일이 화면마다 다르게 보였습니다. formatBytesDocumentsPage에서 ui.tsx(formatDate 옆)로 옮겨 두 화면이 같은 함수를 쓰게 했습니다.

4. 오타

문서 정보 수정 모달의 설명 라벨 문제 설명문서 설명.

✅ 검증

이 브랜치를 8081에 띄워 develop 기준 8080과 직접 비교했습니다.

미인증 요청:     8080 → 403 (본문 없음)            8081 → 401 {"code":"COMMON-007",...}
삭제 문서 버전:  8080 → 409 DOCUMENT-VERSION-003   8081 → 404 DOCUMENT-001

부가 검증 — 잘못된 토큰 401, USER의 /admin/** 접근이 본문 있는 403 ROLE-002, permitAll 경로(/auth/login, /departments) 200 유지, 정상 인증 요청 200 유지.

브라우저에서 업로드 모달 크기 표기가 728 B / 250.0 KB / 3.0 MB로 구간별 정상 표시되고, 수정 모달 라벨이 문서 설명으로 바뀐 것을 확인했습니다.

기존 테스트 수정에 대해

@WebMvcTest 컨트롤러 테스트 5개 클래스가 토큰 없이 보낸 요청 12건에 403을 기대하고 있었습니다. 고치기 전 동작을 그대로 굳혀둔 단정이라 401로 바꿨습니다. 인증은 됐지만 ADMIN이 아닌 요청 14건은 403 그대로 두었습니다 — 둘을 구분하는 것이 이번 변경의 목적입니다.

@WebMvcTest 슬라이스는 @Component를 스캔하지 않아 두 핸들러를 @Import에 추가했습니다. Mock으로 대체하면 실제 응답을 만들지 않아 상태 코드 단정이 의미를 잃으므로 실제 Bean을 넣었습니다.

테스트 결과

백엔드 975/991 통과. 실패 16건은 전부 통합·동시성 테스트이며 단위·컨트롤러 실패는 0건입니다. 내역은 Redis 미기동 1건, 실행 중인 앱의 Worker가 테스트 Job을 선점하며 생기는 정리 FK 위반 3건·TRUNCATE 데드락 7건, 기타 동시성 5건으로 이 변경과 무관합니다.

프론트 빌드 성공, 17/17 통과, lint에서 이 변경으로 인한 경고 0건입니다.

closes #232

SecurityConfig에 exceptionHandling이 없어 Spring Security 기본 동작이 나갔다.
토큰이 없거나 만료·위조된 요청이 본문 없는 403을 받아서, Service 계층이 주는
권한 부족 403과 구분되지 않았고 다른 오류가 모두 쓰는 ErrorResponse 형식과도
어긋났다.

프론트 lib/api.ts에는 이미 401을 받으면 세션을 정리하는 분기가 있는데 백엔드가
401을 보내지 않아 동작하지 않는 코드였다. 이제 만료 토큰으로 접근하면 재로그인
경로가 실제로 타진다.

AccessDeniedHandler도 함께 등록해 경로 단위 거부(/admin/**)가 Service 계층과
같은 ROLE-002 본문을 주도록 맞췄다. 같은 결함 유형이라 한 커밋에 넣었다.
soft delete된 문서는 상세·본문·파일·상태 조회와 수정, 중복 삭제가 모두
404 DOCUMENT-001을 주는데 새 버전 업로드만 409 DOCUMENT-VERSION-003이었다.
validate()에 삭제 검사가 없어 DELETED 상태가 마지막 else로 떨어진 결과였다.

검사 위치는 소유자 확인 다음으로 잡았다. DocumentQueryService.getReadableDocument가
권한 확인 뒤에 삭제를 판별하는 것과 같은 순서다. 삭제 검사를 앞에 두면 타인의
삭제된 문서 존재 여부가 404와 403 차이로 드러난다.
Security 경계에서 나가는 응답은 Controller를 거치지 않아 GlobalExceptionHandler
테스트가 닿지 않으므로 EntryPoint와 AccessDeniedHandler를 직접 호출해 상태 코드,
오류 코드, 본문 형식과 UTF-8 인코딩을 고정했다.

DocumentVersionUploadService는 단위 테스트가 없어 새로 만들었다. 삭제된 문서가
404를 주는지와 함께, 소유자가 아닌 요청은 삭제 여부보다 먼저 403으로 막혀
문서 존재 여부가 드러나지 않는지도 검증한다.
업로드 모달이 크기를 MB로 고정 계산해 1KB 미만 파일이 0.00MB로 보였다. 문서
상세는 구간별 단위로 올바르게 표시하고 있어 같은 파일이 화면마다 다르게 보였다.
formatBytes를 DocumentsPage에서 ui.tsx로 옮겨(formatDate 옆) 두 화면이 같은
함수를 쓰게 했다.

문서 정보 수정 모달의 설명 라벨이 "문제 설명"으로 되어 있어 "문서 설명"으로
고쳤다.
@WebMvcTest 슬라이스는 @component를 스캔하지 않아 SecurityConfig가 요구하는
EntryPoint와 AccessDeniedHandler를 찾지 못해 Context 로딩이 실패했다. 두 핸들러를
@import에 추가했다. Mock으로 대체하면 실제 응답을 만들지 않아 상태 코드 단정이
의미를 잃으므로 실제 Bean을 넣었다.

토큰 없이 보낸 요청 12건이 403을 기대하고 있었다. 고치기 전 동작을 그대로 굳혀둔
단정이라 401로 바꿨다. 인증은 됐지만 ADMIN이 아닌 요청 14건은 403 그대로 두었다.
둘을 구분하는 것이 이번 변경의 목적이다.
@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Important

Review available on request

  • 🔍 Trigger review

Reviews should be triggered manually for repositories with fewer than 10 stars. Select Trigger review above or comment @coderabbitai review to review the latest changes. For a full review, comment @coderabbitai full review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7dfb6438-d3a6-4717-85d0-38b6adb8b824


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.

@Gimini-3
Gimini-3 merged commit 3e73d90 into develop Aug 18, 2026
1 check passed
@Gimini-3 Gimini-3 self-assigned this Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Fix] 인증 실패 응답 401 통일 및 삭제 문서 404 일관성, 업로드 모달 표기 수정

1 participant