Skip to content

[Feat/#189] 빌릴게 근무 시간표 관리자 API 추가 - #193

Open
xeoxxn wants to merge 7 commits into
mainfrom
feat/#189-billilge-work-schedule-admin-api
Open

xeoxxn wants to merge 7 commits into
mainfrom
feat/#189-billilge-work-schedule-admin-api

Conversation

@xeoxxn

@xeoxxn xeoxxn commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

#️⃣연관된 이슈

🎯 해결하려는 문제가 무엇인가요?

관리자 기타 관리 → 근무 시간표 관리 화면이 쓸 API가 없었습니다. 화면은 월금, 1017시 그리드에 근무자를 1시간 단위로 배치하고 주 단위로 넘겨 보는데, 지금 work_schedules는 요일(day_of_week)만 저장해서 주마다 다른 시간표를 담을 수 없고, 이 테이블에 쓰는 코드도 없었습니다.

명세의 5개 엔드포인트를 만들었습니다.

엔드포인트 화면
GET /v1/admin/billilge/work-schedules?date= 주 단위 시간표 그리드 (date 생략 시 이번 주)
GET /v1/admin/billilge/work-schedules/workers 등록·수정 모달의 근무자 드롭다운
POST /v1/admin/billilge/work-schedules + 근무 시간 등록 — 근무자 1명 + 시간대 여러 개
PUT /v1/admin/billilge/work-schedules/{scheduleId} 근무 시간 수정 모달 — 근무자·근무일·시간 전체 교체
DELETE /v1/admin/billilge/work-schedules/{scheduleId} 근무 시간 삭제

❓ 왜 해결해야 하나요?

빌릴게 근무자 배치를 관리자 화면에서 할 수 있어야 하는데, 화면은 만들어져 있어도 붙일 API가 없어 mock 데이터로만 돌고 있습니다. 요일만 저장하는 지금 구조로는 "다음 주 월요일만 바꾸기"도, "지난 주 기록은 그대로 두기"도 할 수 없습니다.

⭐ 어떻게 해결했나요?

1. 요일 대신 근무일을 저장합니다 (V28·V29)

schedule_id | member_id | work_date  | start_hour | end_hour
1           | 7         | 2026-10-26 | 10         | 11        ← 이번 주 월
2           | 7         | 2026-11-02 | 10         | 11        ← 다음 주 월 (별개 행)

요일은 저장하지 않고 응답을 만들 때 workDate.getDayOfWeek()로 계산합니다. 기존 행은 어느 주의 근무인지 알 수 없어 날짜로 옮길 수 없고, 이 테이블에 쓰는 API도 없었기 때문에 비우고(V28) 컬럼을 바꿨습니다(V29). 데이터 삭제와 스키마 변경은 flyway-migration.md 3-5절대로 파일을 나눴습니다.

2. 근무 시간 규칙은 도메인이, 오늘·다른 근무 시간이 필요한 규칙은 서비스가 검증합니다

규칙 위치 에러
월~금만 WorkSchedule.create/change 400 INVALID_WORK_DATE
1017시, 시작 < 종료, 점심시간(1213시)에 걸치지 않음 WorkSchedule.create/change 400 INVALID_WORK_HOURS
지난 주가 아님 (수정은 옮기기 전·후 둘 다) AdminWorkScheduleServiceImpl.validateNotPastWeek 400 PAST_WEEK_NOT_EDITABLE
한 시간 칸에 한 명 (근무자가 달라도, 요청 안끼리도) AdminWorkScheduleServiceImpl.validateNotConflicted 409 WORK_SCHEDULE_CONFLICT

FeedbackRound.create의 기간 검증처럼 값 하나로 판단할 수 있는 규칙은 생성할 때 막고, 지난 주·겹침처럼 오늘 날짜나 다른 행이 필요한 규칙은 서비스의 validate~에 뒀습니다. 지난 주는 날짜가 아니라 주(월요일) 단위로 판단해서 이번 주의 지난 요일은 고칠 수 있습니다.

3. "근무자"는 근무 시간표 쪽에서 조립합니다

근무자는 학생회 부서가 있는 회원입니다(운영진 권한 role은 보지 않음). 이 판단을 member에 두면 member가 빌릴게 근무라는 개념에 묶이므로, member에는 사실만 범용으로 두고 해석은 AdminWorkScheduleUseCase가 합니다.

  • member: Member.hasCouncilDepartment(), MemberService.findAllCouncilMembers() 추가 (기존 메서드는 그대로)
  • AdminWorkScheduleUseCase: 근무자 목록 = 학생회 부서가 있는 회원, 배정할 회원 확인 = getById(없거나 탈퇴면 MEMBER_NOT_FOUND) 후 부서가 없으면 WORKER_NOT_FOUND

근무자 규칙이 바뀌면(예: 회장단 제외) 이 UseCase만 고치면 됩니다. 근무 시간(internal)에 이름·학번(member)을 붙이는 조합도 여기서 합니다(AdminFeedbackSearchUseCase와 같은 방식).

🧩 이 PR의 한계 & 트레이드오프

  • 동시 등록 구멍: 겹침은 "같은 값"이 아니라 "구간이 맞물림"이라 유니크 제약으로 막을 수 없어서, 저장 전에 조회해서 확인합니다. 두 관리자가 같은 칸을 거의 동시에 저장하면 둘 다 통과할 수 있습니다. 운영진 몇 명이 쓰는 화면이라 드물고, 생겨도 시간표에 보여서 하나를 지우면 되므로 감수했습니다(피드백 회차 기간 겹침과 같은 방식, 주석으로 남김).
  • 기존 work_schedules 행 삭제: 요일만 있는 데이터는 날짜로 옮길 방법이 없습니다.
  • date 형식 오류 메시지: ?date=abc는 400 INVALID_INPUT으로 처리되지만, 메시지에 스프링 변환 오류 원문(타입 이름)이 그대로 나갑니다. 공통 GlobalExceptionHandler가 FieldError 기본 메시지를 쓰기 때문이고 기존 ArchiveListParams.year도 같아서, 이 PR에서는 건드리지 않고 후속으로 남깁니다.

⛓️ 기존 기능에 미치는 영향

  • work_schedules 스키마가 바뀝니다. 이 테이블을 쓰던 코드는 없었습니다.
  • member 도메인은 메서드 추가만 있고 기존 동작은 바뀌지 않습니다.
  • Flyway V28·V29: main은 V19까지이고, 진행 중인 브랜치가 V20~V27을 쓰고 있습니다(V27은 아카이빙 목록·상세 조회 관리자 API 추가 #185). 머지 순서에 따라 번호를 다시 맞춰야 할 수 있습니다.

🔀 Edge Case & 실패 시나리오

상황 처리
date가 토·일 그 앞 월요일의 주를 돌려준다
date 형식 오류 (abc, 2026-13-01) 400 INVALID_INPUT
탈퇴한 회원의 근무 시간 조회에 그대로 나오고 name·studentId는 null
학생회 부서가 빠진 회원의 근무 시간 조회에 그대로 나온다. 수정할 때 근무자를 그대로 두면 WORKER_NOT_FOUND라 다른 근무자로 바꿔야 한다
맞닿은 시간대 (1011시, 1112시) 겹침이 아니다. 합치지 않고 보낸 그대로 저장한다
수정 대상이 자기 원래 칸과 겹침 같은 id는 겹침 검사에서 뺀다
지난 주 근무를 이번 주로 / 이번 주 근무를 지난 주로 옮김 둘 다 400 PAST_WEEK_NOT_EDITABLE
등록 요청 중 하나만 규칙 위반 아무것도 저장하지 않는다 (한 트랜잭션)

📋 검토한 대안과 선택 이유

  • 1시간 칸마다 한 행 + (work_date, hour) 유니크: 동시 등록까지 DB가 막아 주지만, 화면이 구간 단위로 등록·수정·삭제하고 명세도 구간(startHour~endHour)이라 행 쪼개기·합치기 비용이 큽니다. 구간 저장 + 조회 후 확인을 택했습니다.
  • 저장 전 잠금(SELECT ... FOR UPDATE, named lock): 위 동시성 구멍을 막을 수 있지만 사용 빈도에 비해 코드가 늘어 보류했습니다.
  • 근무자 판정을 member에 (isWorker(), AdminMemberService): 처음엔 그렇게 만들었다가, member가 근무 개념에 묶이는 문제로 위 3번 구조로 바꿨습니다.
  • @RequestParam LocalDate date: GlobalExceptionHandler에 MethodArgumentTypeMismatchException 처리가 없어 형식 오류가 500이 됩니다. @Valid @ModelAttribute Params record로 받아 400으로 가는 것을 확인했습니다.

💬 리뷰 포인트

  • AdminWorkScheduleServiceImpl.validateNotConflicted — 수정 대상 제외, 요청 안 겹침 판정이 맞는지
  • AdminWorkScheduleUseCase — 근무자 판정(학생회 부서)을 UseCase에 둔 구조
  • V28·V29 마이그레이션과 번호

명세(노션 「API 명세서」 빌릴게 근무 시간표 5건)는 관리자 화면과 대조해 근무자 기준·에러 코드를 고쳐 두었습니다(🔁 표시). ./gradlew check(ModularityTests·DomainImplAccessTests 포함) 통과했습니다.

@xeoxxn xeoxxn self-assigned this Oct 29, 2026
@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

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.

빌릴게 근무 시간표 관리자 API 추가 (주 단위 조회·등록·수정·삭제·근무자 목록)

1 participant