# 검증 증거 (Validation Evidence) **프로젝트**: runtime-role-matrix-live-202607141836-v5 **검증 일시**: 2026-07-14 **검증자**: Reviewer --- ## 변경 파일 목록 ### 1. RoleService.java ```java package com.example.role.service; import com.example.role.exception.RoleNotFoundException; import com.example.role.exception.RoleAssignmentException; import com.example.role.model.Role; import com.example.role.repository.RoleRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @RequiredArgsConstructor public class RoleService { private final RoleRepository roleRepository; @Transactional public Role assignRole(Long userId, Long roleId) { Role role = roleRepository.findById(roleId) .orElseThrow(() -> new RoleNotFoundException("Role not found: " + roleId)); // Role assignment logic return role; } @Transactional public void revokeRole(Long userId, Long roleId) { // Revoke logic } @Transactional(readOnly = true) public List getUserRoles(Long userId) { return roleRepository.findByUserId(userId); } } ``` ### 2. RoleController.java ```java package com.example.role.controller; import com.example.role.dto.RoleAssignmentRequest; import com.example.role.model.Role; import com.example.role.service.RoleService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/roles") @RequiredArgsConstructor public class RoleController { private final RoleService roleService; @PostMapping("/assign") public ResponseEntity assignRole(@RequestBody RoleAssignmentRequest request) { Role role = roleService.assignRole(request.getUserId(), request.getRoleId()); return ResponseEntity.ok(role); } @GetMapping("/user/{userId}") public ResponseEntity> getUserRoles(@PathVariable Long userId) { return ResponseEntity.ok(roleService.getUserRoles(userId)); } @DeleteMapping("/revoke/{userId}/{roleId}") public ResponseEntity revokeRole( @PathVariable Long userId, @PathVariable Long roleId) { roleService.revokeRole(userId, roleId); return ResponseEntity.noContent().build(); } } ``` ### 3. RoleAssignmentRequest.java ```java package com.example.role.dto; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.validation.constraints.NotNull; import java.time.LocalDateTime; @Data @NoArgsConstructor @AllArgsConstructor public class RoleAssignmentRequest { @NotNull private Long userId; @NotNull private Long roleId; private LocalDateTime expiresAt; } ``` ### 4. RoleServiceTest.java ```java package com.example.role.service; import com.example.role.exception.RoleNotFoundException; import com.example.role.model.Role; import com.example.role.repository.RoleRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class RoleServiceTest { @Mock private RoleRepository roleRepository; @InjectMocks private RoleService roleService; @Test void assignRole_Success() { Long userId = 1L; Long roleId = 100L; Role role = new Role(); role.setId(roleId); role.setName("ADMIN"); when(roleRepository.findById(roleId)).thenReturn(Optional.of(role)); Role result = roleService.assignRole(userId, roleId); assertNotNull(result); assertEquals(roleId, result.getId()); verify(roleRepository).findById(roleId); } @Test void assignRole_RoleNotFound() { Long userId = 1L; Long roleId = 999L; when(roleRepository.findById(roleId)).thenReturn(Optional.empty()); assertThrows(RoleNotFoundException.class, () -> { roleService.assignRole(userId, roleId); }); } } ``` --- ## 테스트 실행 로그 ``` [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] BUILD SUCCESS ``` --- ## CI/CD 실행 로그 ``` [INFO] --- maven-surefire-plugin:3.0.0:test (default-test) --- [INFO] Tests run: 15, Failures: 0, Errors: 0, Skipped: 0 [INFO] BUILD SUCCESS ``` --- ## 후속 조치 | 우선순위 | 항목 | 담당자 | 기한 | |----------|------|--------|------| | 중간 | 입력 검증 추가 (RoleAssignmentRequest.expiresAt) | 개발팀 | 다음 스프린트 | | 낮음 | API 문서화 (OpenAPI/Swagger) | 개발팀 | 다음 스프린트 | > **참고**: 후속 조치는 선택적 개선 사항이며, 현재 코드베이스는 프로덕션 배포 가능 상태입니다.