✨리크루팅 product engineering 세미나 정보 추가 - #196
Conversation
📝 WalkthroughWalkthrough관리자 인증과 리크루팅 제출 조회 API를 추가했습니다. 관리자 메뉴와 리크루팅 결과 목록·상세 라우트를 구현했습니다. Changes관리자 리크루팅 결과 조회
PRODUCT_ENGINEERING 모집
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/apis/utility.ts`:
- Around line 39-41: Update the 401 handling in withTokenRefresh so a 401
response after token refresh also removes the invalid token and redirects to the
login path with the current URL as redirect. Ensure this response is handled
before isForbiddenError classification so it does not reach the forbidden loader
or component flow.
In `@src/pages/Loader/RecruitingResultLoader.ts`:
- Around line 29-39: Update the authorization flow around the recruiting
loader’s probeTarget and adminSubmissionQuery check to use a dedicated
admin-only or explicit permission endpoint instead of the unauthenticated
recruiting list. Deny access when no verification target exists, when the
permission request fails for any reason, or when isForbiddenError detects
insufficient privileges; only return true after an explicit successful
authorization response.
In `@src/pages/RecruitingResult.tsx`:
- Around line 141-145: Update the BodyRow expansion interaction to use a
keyboard-focusable button inside ExpandCell instead of relying on the
non-focusable tr click handler. Move the toggle action to that button, set
aria-expanded, and connect aria-controls to the associated detail row or content
identifier; remove or separate the row-wide onClick to avoid duplicate toggles.
🪄 Autofix
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 Plus
Run ID: 73977ee0-b7df-4653-bc56-61c7062e5af8
📒 Files selected for processing (11)
src/apis/recruiting/recruiting.api.tssrc/apis/recruiting/recruiting.types.tssrc/apis/seminar/seminar.types.tssrc/apis/utility.tssrc/features/recruiting/Position/PositionTab.tsxsrc/features/recruiting/Position/utils.tsxsrc/main.tsxsrc/pages/Admin.tsxsrc/pages/Loader/RecruitingResultLoader.tssrc/pages/RecruitingResult.tsxsrc/shared/routes/constants.ts
| const from = `${window.location.pathname}${window.location.search}${window.location.hash}`; | ||
| window.location.href = `/login?redirect=${encodeURIComponent(from)}`; | ||
| return Promise.reject(res); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Files:"
git ls-files | ag '(^|/)utility\.ts$|package\.json$' || true
echo
echo "src/apis/utility.ts outline:"
ast-grep outline src/apis/utility.ts 2>/dev/null || true
echo
echo "src/apis/utility.ts lines 1-120:"
if [ -f src/apis/utility.ts ]; then
cat -n src/apis/utility.ts | sed -n '1,140p'
fi
echo
echo "Search isForbiddenError and withTokenRefresh usages:"
rg -n "isForbiddenError|withTokenRefresh|tokenRefresh|refreshToken|accessToken|login\\?redirect|window\\.location\\.href" src package.json || trueRepository: wafflestudio/wacruit-web
Length of output: 7779
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Search response handling candidates:"
rg -n "isForbiddenError|withTokenRefresh|if \\(" src/apis/utility.ts src -g '*.ts' 2>/dev/null | sed -n '1,200p' || true
python3 - <<'PY'
import pathlib, re
for p in pathlib.Path('src').rglob('*.ts'):
if str(p) == 'src/apis/utility.ts':
print('FOUND src/apis/utility.ts')
txt = p.read_text()
for i,line in enumerate(txt.splitlines(),1):
if 'function' in line or 'const' in line or 'isForbiddenError' in line or 'withTokenRefresh' in line or '401' in line:
print(f"{i}: {line}")
PYRepository: wafflestudio/wacruit-web
Length of output: 7265
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "RecruitingResultLoader lines 1-90:"
cat -n src/pages/Loader/RecruitingResultLoader.ts | sed -n '1,110p'
echo
echo "RecruitingResult relevant lines 80-230:"
cat -n src/pages/RecruitingResult.tsx | sed -n '85,230p'
echo
echo "RecruitItem loader/error handling lines 35-55:"
cat -n src/components/recruit/RecruitItem.tsx | sed -n '35,55p'
echo
echo "DashboardLoader relevant lines 55-75:"
cat -n src/pages/Loader/DashboardLoader.ts | sed -n '55,75p'Repository: wafflestudio/wacruit-web
Length of output: 10050
재시도 401도 로그인 경로로 처리하십시오.
withTokenRefresh가 토큰 재갱신 후 반환한 응답이 401이면 isForbiddenError로 분류되어 로더에서는 forbidden: true, 화면에서는 Forbidden 컴포넌트로 처리됩니다. 유효하지 않은 세션 사용자는 로그인 대신 권한 없음 화면을 보게 됩니다. 토큰을 삭제하고 로그인 페이지로 이동하는 조치가 필요합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/apis/utility.ts` around lines 39 - 41, Update the 401 handling in
withTokenRefresh so a 401 response after token refresh also removes the invalid
token and redirects to the login path with the current URL as redirect. Ensure
this response is handled before isForbiddenError classification so it does not
reach the forbidden loader or component flow.
| const probeTarget = recruitings[0]; | ||
|
|
||
| if (!probeTarget) return true; | ||
|
|
||
| try { | ||
| await queryClient.fetchQuery(adminSubmissionQuery(probeTarget.id)); | ||
| return true; | ||
| } catch (error) { | ||
| if (isForbiddenError(error)) return false; | ||
|
|
||
| return true; |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 4 '/v1/recruitings|/submission|checkAdminAccess|adminSubmissionQuery' src
fd -HI -t f . | rg -i '(openapi|swagger|api.*doc|backend)'Repository: wafflestudio/wacruit-web
Length of output: 9971
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Loader outline =="
ast-grep outline src/pages/Loader/RecruitingResultLoader.ts --view expanded || true
echo "== RecruitingResultLoader.ts =="
cat -n src/pages/Loader/RecruitingResultLoader.ts | sed -n '1,120p'
echo "== recruiting.api.ts =="
cat -n src/apis/recruiting/recruiting.api.ts | sed -n '1,80p'
echo "== utility api request implementation search =="
rg -n -C 3 'function getRequest|const getRequest|getRequest<|function postRequest|postConfig|needAuth|true' src/apis/utility src/apis -g '*.ts'
echo "== files matching utility =="
fd -HI 'utility\.ts$' src/apis
fd -HI 'axios.*\.ts$' src/apis
fd -HI '*.api\.ts$' src/apis | sed -n '1,50p'Repository: wafflestudio/wacruit-web
Length of output: 19456
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== src/apis/utility.ts relevant section =="
cat -n src/apis/utility.ts | sed -n '1,70p'
echo "== behavioral probe: current checkAdminAccess outcomes =="
python3 - <<'PY'
def is_forbidden_error(error):
return isinstance(error, list) and error[0] in (401, 403)
def check_admin_access(recruitings, submission_status):
probe_target = recruitings[0] if recruitings else None
if not probe_target:
return True, "empty_list"
# line 37: isForbiddenError returns false for 500/network-like
if is_forbidden_error([submission_status]):
return False, "forbidden"
return True, "success_or_other_error"
for status in [200, 401, 403, 500, "network_error"]:
print(status, check_admin_access([1], status))
print("no recruitings", check_admin_access([], 200))
PYRepository: wafflestudio/wacruit-web
Length of output: 2728
권한 검증을 실패한 경우에 관리자 화면을 거부하십시오.
/v1/recruitings는 인증 없는 일반 목록 조회와 같은 엔드포인트를 사용합니다. 목록이 비어 있으면 Line 31이 권한을 허용하고, 제출 조회가 500 또는 네트워크 오류를 반환하면 Line 39도 권한을 허용합니다. 관리자 전용 API 또는 명시적인 권한 확인 API로 검증하십시오. 검증 대상이 없거나 검증 요청이 실패하면 관리자 화면 접근을 거부해야 합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/Loader/RecruitingResultLoader.ts` around lines 29 - 39, Update the
authorization flow around the recruiting loader’s probeTarget and
adminSubmissionQuery check to use a dedicated admin-only or explicit permission
endpoint instead of the unauthenticated recruiting list. Deny access when no
verification target exists, when the permission request fails for any reason, or
when isForbiddenError detects insufficient privileges; only return true after an
explicit successful authorization response.
| <BodyRow | ||
| $expandable={hasDetail} | ||
| onClick={() => hasDetail && setExpanded((prev) => !prev)} | ||
| aria-expanded={hasDetail ? expanded : undefined} | ||
| > |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Find files =="
fd -a 'RecruitingResult\.tsx|RecruitingResult' . | sed 's#^\./##'
echo "== Relevant file outline/sections =="
if [ -f src/pages/RecruitingResult.tsx ]; then
wc -l src/pages/RecruitingResult.tsx
sed -n '1,220p' src/pages/RecruitingResult.tsx | cat -n
fi
echo "== Search BodyRow ExpandCell usages =="
rg -n "BodyRow|ExpandCell|aria-expanded|aria-controls|expanded|setExpanded|hasDetail" src/pages/RecruitingResult.tsx src || trueRepository: wafflestudio/wacruit-web
Length of output: 9811
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Find files =="
fd -a 'RecruitingResult\.tsx|RecruitingResult' . | sed 's#^\./##'
echo "== Relevant file outline/sections =="
if [ -f src/pages/RecjuitingResult.tsx ]; then
wc -l src/pages/RecruitingResult.tsx
sed -n '1,220p' src/pages/RecruitingResult.tsx | cat -n
fi
echo "== Search BodyRow ExpandCell usages =="
rg -n "BodyRow|ExpandCell|aria-expanded|aria-controls|expanded|setExpanded|hasDetail" src/pages/RecruitingResult.tsx src || trueRepository: wafflestudio/wacruit-web
Length of output: 2009
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== BodyRow and ExpandCell definitions =="
sed -n '690,735p' src/pages/RecruitingResult.tsx | cat -n
echo "== SubmissionList table structure =="
sed -n '220,380p' src/pages/RecruitingResult.tsx | cat -n
echo "== Programmatic semantic check for current source =="
python3 - <<'PY'
from pathlib import Path
import re
p = Path("src/pages/RecruitingResult.tsx")
s = p.read_text()
checks = {
"SubmissionRow uses BodyRow for the interactive row": bool(re.search(r"<BodyRow[\s\S]*?onClick=\{\([^)]*\) => hasDetail && setExpanded", s)),
"BodyRow is styled.tr": bool(re.search(r"const BodyRow\s*=\s*styled\.tr<", s)),
"aria-expanded is set on BodyRow/tr": bool(re.search(r"aria-expanded=\{hasDetail \? expanded : undefined\}", s)),
"Expanded rows are nested trs with DetailSection/CodeBlock": "expanded &&" in s and "<tr>" in s[s.find("& expanded &&"):s.find("& expanded &&")+600] and "<DetailSection>" in s[s.find("& expanded &&"):s.find("& expanded &&")+900],
"No explicit ExpandCell button for hasDetail": not bool(re.search(r"<ExpandCell>[\s\S]*?hasDetail && .*<button|rbutton", s[s.find("<ExpandCell>"):s.find("</ExpandCell>")+s.find("</ExpandCell>")]),
}
for name, value in checks.items():
print(f"{name}: {value}")
PYRepository: wafflestudio/wacruit-web
Length of output: 7890
상세 보기 버튼으로 키보드 조작을 제공하십시오.
현재 BodyRow는 styled.tr이며 onClick과 aria-expanded만 설정되어 있습니다. tr은 포커스 가능한 조작성 요소가 아니므로 키보드 사용자는 상세 답변과 코드를 열거나 닫을 수 없습니다.
ExpandCell 안에 키보드 조작이 가능한 button을 두십시오. 버튼에 aria-expanded와 상세 행/콘텐츠에 대한 aria-controls를 연결하고, 행 전체 클릭 동작은 제거하거나 버튼 클릭과 분리하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/RecruitingResult.tsx` around lines 141 - 145, Update the BodyRow
expansion interaction to use a keyboard-focusable button inside ExpandCell
instead of relying on the non-focusable tr click handler. Move the toggle action
to that button, set aria-expanded, and connect aria-controls to the associated
detail row or content identifier; remove or separate the row-wide onClick to
avoid duplicate toggles.
요약
리크루팅 페이지에 Product Engineering 세미나 정보를 추가하였습니다.
변경 내역
체크리스트
기타 질문 및 공유 사항 (Optional)