Skip to content

Conversation

@dh2906
Copy link
Contributor

@dh2906 dh2906 commented Jan 23, 2026

🔍 개요


🚀 주요 변경 내용

  • @Auth 어노테이션을 추가하여 API의 접근 권한을 제한할 수 있습니다.

  • 어드민 기능으로 일정 관리 API를 추가했습니다.

    • 어드민 역할이 있는 유저가 속한 학교를 기준으로 일정을 관리할 수 있습니다. 학교 별 어드민 느낌? 이라고 생각하시면 됩니다.
    • POST /admin/schedules 를 통해 새로운 일정을 추가 합니다.
    • PUT /admin/schedules/batch를 통해 일괄적으로 일정을 추가/수정 합니다.
    • DELETE /admin/schedules/{scheduleId}를 통해 일정을 삭제 합니다.

💬 참고 사항


✅ Checklist (완료 조건)

  • 코드 스타일 가이드 준수
  • 테스트 코드 포함됨
  • Reviewers / Assignees / Labels 지정 완료
  • 보안 및 민감 정보 검증 (API 키, 환경 변수, 개인정보 등)

@dh2906 dh2906 requested a review from Copilot January 23, 2026 08:21
@dh2906 dh2906 self-assigned this Jan 23, 2026
@dh2906 dh2906 added the 기능 새로운 기능을 개발합니다. label Jan 23, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

이 PR은 어드민 사용자가 자신이 속한 대학교의 일정을 관리할 수 있는 API를 추가하고, 역할 기반 접근 제어를 위한 @Auth 어노테이션을 도입합니다.

Changes:

  • @Auth 어노테이션과 AuthorizationInterceptor를 추가하여 역할 기반 접근 제어 기능 구현
  • 어드민 일정 관리 API 추가: 일정 생성, 일괄 생성/수정, 삭제 기능
  • Swagger 설정에 Public API와 Admin API 그룹 분리 추가

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
WebConfig.java AuthorizationInterceptor를 인터셉터 체인에 추가하여 역할 기반 인증 활성화
SwaggerConfig.java Admin API와 Public API를 분리한 Swagger 그룹 추가
ApiResponseCode.java 역할 접근 권한 에러와 일정 미존재 에러 코드 추가
LoginCheckInterceptor.java 인증된 사용자 ID와 공개 엔드포인트 정보를 request attribute로 저장
AuthorizationInterceptor.java @Auth 어노테이션 기반 역할 검증 인터셉터 구현
Auth.java 메서드 레벨 역할 기반 접근 제어를 위한 새 어노테이션
UniversityScheduleRepository.java 대학교와 일정 ID로 일정 조회 기능 추가
ScheduleRepository.java 일정 저장 및 삭제 메서드 추가
UniversitySchedule.java 정적 팩토리 메서드 of() 추가
Schedule.java 일정 생성을 위한 of() 메서드와 수정을 위한 update() 메서드 추가
AdminScheduleService.java 어드민 일정 생성, 일괄 생성/수정, 삭제 비즈니스 로직 구현
AdminScheduleUpsertRequest.java 일괄 일정 생성/수정 요청 DTO
AdminScheduleUpsertItemRequest.java 개별 일정 생성/수정 항목 DTO
AdminScheduleCreateRequest.java 일정 생성 요청 DTO
AdminScheduleController.java 어드민 일정 API 컨트롤러 구현
AdminScheduleApi.java 어드민 일정 API 인터페이스 정의 및 Swagger 문서화

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Comment on lines +85 to +86
universityScheduleRepository.delete(universitySchedule);
scheduleRepository.delete(universitySchedule.getSchedule());
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

일정을 삭제할 때 UniversitySchedule만 삭제하고 Schedule도 함께 삭제하고 있습니다. 그러나 Schedule은 다른 타입(CLUB, COUNCIL, DORM)의 일정과도 연결될 수 있는데, 현재 구조에서는 UniversitySchedule이 삭제되면 Schedule도 무조건 삭제됩니다. 만약 하나의 Schedule이 여러 엔티티(예: ClubSchedule, CouncilSchedule 등)와 연결될 수 있다면, 다른 곳에서 참조하고 있는 Schedule을 삭제하여 데이터 무결성 문제가 발생할 수 있습니다. Schedule과 UniversitySchedule의 관계가 1:1이 확실하다면 문제없지만, 아키텍처를 확인해야 합니다.

Copilot uses AI. Check for mistakes.
Object userId = request.getAttribute(LoginCheckInterceptor.AUTHENTICATED_USER_ID_ATTRIBUTE);

if (!(userId instanceof Integer id)) {
throw CustomException.of(ApiResponseCode.INVALID_SESSION);
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

AuthorizationInterceptor에서 사용자 ID가 Integer가 아닌 경우 INVALID_SESSION 에러를 반환하고 있습니다. 그러나 이 시점에서는 이미 LoginCheckInterceptor를 통과한 상태이므로, userId는 항상 Integer여야 합니다. 만약 userId가 Integer가 아니라면 이는 시스템 내부 오류이므로, INVALID_SESSION보다는 내부 서버 오류를 나타내는 더 적절한 에러 코드를 사용하거나 IllegalStateException을 던지는 것이 더 적절합니다.

Suggested change
throw CustomException.of(ApiResponseCode.INVALID_SESSION);
throw new IllegalStateException("Authenticated user id is not an Integer: " + userId);

Copilot uses AI. Check for mistakes.
Comment on lines 32 to 56
@Auth(roles = {UserRole.ADMIN})
ResponseEntity<Void> createSchedule(
@Valid @RequestBody AdminScheduleCreateRequest request,
@UserId Integer userId
);

@Operation(summary = "일정을 일괄 생성/수정한다.", description = """
scheduleId가 없으면 신규 생성, 있으면 해당 일정 수정입니다.
**scheduleType (일정 구분):**
- `UNIVERSITY`: 대학교 일정
- `CLUB`: 동아리 일정
- `COUNCIL`: 총동아리연합회 일정
- `DORM`: 기숙사 일정
""")
@PutMapping("/batch")
@Auth(roles = {UserRole.ADMIN})
ResponseEntity<Void> upsertSchedules(
@Valid @RequestBody AdminScheduleUpsertRequest request,
@UserId Integer userId
);

@Operation(summary = "일정을 삭제한다.")
@DeleteMapping("/{scheduleId}")
@Auth(roles = {UserRole.ADMIN})
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

모든 메서드에 동일한 @Auth(roles = {UserRole.ADMIN}) 어노테이션이 반복되고 있습니다. DRY(Don't Repeat Yourself) 원칙에 따라 인터페이스 레벨에 @Auth 어노테이션을 한 번만 적용하는 것을 고려해보세요. AuthorizationInterceptor는 이미 클래스 레벨 어노테이션을 지원하도록 구현되어 있습니다(line 61: handlerMethod.getBeanType().getAnnotation(Auth.class)).

Copilot uses AI. Check for mistakes.
@dh2906 dh2906 merged commit b6228a1 into main Jan 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

기능 새로운 기능을 개발합니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants