아래는 "스프링 시큐리티" 책의 5장에 포함될 "메서드 수준 보안 설정"과 "@PreAuthorize, @Secured 어노테이션 활용"에 대한 내용입니다. 이 설명은 개념을 명확히 하고 실무에서 사용할 수 있는 예제를 포함해 작성되었습니다.

---

### 5장. 인가와 권한 관리

#### 5.3 메서드 수준 보안 설정

URL 기반 접근 제어는 웹 요청 단위로 보안을 적용하는 데 유용하지만, 더 세밀한 제어가 필요한 경우가 있습니다. 예를 들어, 특정 비즈니스 로직이나 서비스 메서드에 접근을 제한하고 싶을 때 **메서드 수준 보안 설정**을 사용합니다. 스프링 시큐리티는 이를 위해 메서드 호출 시점에서 권한을 검사하는 기능을 제공하며, 주로 어노테이션 기반으로 구현됩니다.

##### 메서드 수준 보안의 필요성
- **세밀한 제어**: URL 패턴만으로는 컨트롤러 내부 메서드의 개별 로직을 구분하기 어렵습니다.
- **비즈니스 로직 보호**: 데이터베이스 작업이나 민감한 연산을 호출하는 메서드를 보호할 수 있습니다.
- **재사용성**: 여러 엔드포인트에서 호출되는 서비스 메서드에 일관된 보안을 적용할 수 있습니다.

스프링 시큐리티는 메서드 수준 보안을 활성화하려면 `@EnableMethodSecurity`를 설정 클래스에 추가해야 합니다. 이후 `@PreAuthorize`, `@Secured` 같은 어노테이션을 사용해 권한을 검사합니다.

##### 기본 설정
```java
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@Configuration
@EnableMethodSecurity
public class SecurityConfig {
    // 다른 설정 (UserDetailsService, SecurityFilterChain 등) 생략
}
```
`@EnableMethodSecurity`는 메서드 보안을 활성화하며, 기본적으로 `@PreAuthorize`, `@PostAuthorize`, `@Secured` 어노테이션을 지원합니다.

#### 5.4 @PreAuthorize와 @Secured 활용

스프링 시큐리티는 메서드 수준 보안을 위해 두 가지 대표적인 어노테이션을 제공합니다: **`@Secured`**와 **`@PreAuthorize`**. 두 어노테이션은 비슷한 목적을 가지지만, 사용법과 유연성에서 차이가 있습니다.

##### @Secured 어노테이션
`@Secured`는 메서드에 접근할 수 있는 역할(Role)을 지정하는 간단한 어노테이션입니다. 주로 역할 기반 접근 제어에 사용되며, 설정이 직관적입니다.

###### 사용 예제
```java
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Service;

@Service
public class AdminService {

    @Secured("ROLE_ADMIN")
    public String manageUsers() {
        return "User management page for admins only";
    }

    @Secured({"ROLE_USER", "ROLE_ADMIN"})
    public String viewProfile() {
        return "Profile visible to users and admins";
    }
}
```
- `@Secured("ROLE_ADMIN")`: `ROLE_ADMIN` 역할이 있는 사용자만 `manageUsers()` 메서드를 호출할 수 있습니다.
- `@Secured({"ROLE_USER", "ROLE_ADMIN"})`: `ROLE_USER` 또는 `ROLE_ADMIN` 중 하나라도 가진 사용자가 `viewProfile()`를 호출할 수 있습니다.

###### 동작 원리
1. 메서드 호출 시 스프링 시큐리티의 AOP(Aspect-Oriented Programming) 프록시가 개입합니다.
2. 현재 인증된 사용자의 `GrantedAuthority` 목록을 확인해 지정된 역할이 있는지 검사합니다.
3. 역할이 없으면 `AccessDeniedException`이 발생하고, 호출이 차단됩니다.

###### 한계
- 역할만 지원하며, 권한(Authority)이나 복잡한 조건은 처리 불가.
- SpEL(Spring Expression Language)을 지원하지 않아 유연성이 제한적.

##### @PreAuthorize 어노테이션
`@PreAuthorize`는 `@Secured`보다 더 강력하고 유연한 어노테이션으로, SpEL을 사용해 복잡한 조건을 정의할 수 있습니다. 역할, 권한, 메서드 매개변수, 인증 객체 등을 기반으로 접근을 제어할 수 있습니다.

###### 사용 예제
```java
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class PostService {

    @PreAuthorize("hasRole('ADMIN')")
    public String deleteAllPosts() {
        return "All posts deleted by admin";
    }

    @PreAuthorize("hasAuthority('WRITE_POST')")
    public String createPost(String content) {
        return "Post created: " + content;
    }

    @PreAuthorize("#username == authentication.name")
    public String viewOwnProfile(String username) {
        return "Profile for " + username;
    }

    @PreAuthorize("hasRole('USER') and #postId > 0")
    public String editPost(int postId, String content) {
        return "Post " + postId + " updated: " + content;
    }
}
```
- **`hasRole('ADMIN')`**: `ROLE_ADMIN` 역할 검사. (`@Secured`와 유사하지만 SpEL 사용 가능)
- **`hasAuthority('WRITE_POST')`**: `WRITE_POST` 권한 검사.
- **`#username == authentication.name`**: 메서드 매개변수(`username`)가 현재 인증된 사용자의 이름과 같은지 확인.
- **`hasRole('USER') and #postId > 0`**: `ROLE_USER` 역할이 있고, `postId`가 양수일 때만 허용.

###### 동작 원리
1. `@PreAuthorize`는 메서드 실행 전에 SpEL 표현식을 평가합니다.
2. 표현식이 `true`면 메서드가 실행되고, `false`면 `AccessDeniedException`이 발생합니다.
3. `authentication` 객체(현재 사용자 정보)와 메서드 매개변수를 활용해 동적 조건을 검사합니다.

###### 장점
- **유연성**: 역할, 권한, 매개변수 기반의 복잡한 로직 가능.
- **조건문**: `and`, `or`, `not` 같은 연산자 사용 가능.
- **커스터마이징**: SpEL을 통해 비즈니스 로직에 맞춘 조건 설정 가능.

##### @Secured vs @PreAuthorize 비교
| 특징              | `@Secured`                | `@PreAuthorize`           |
|-------------------|---------------------------|---------------------------|
| 지원 범위         | 역할(Role)만              | 역할, 권한, SpEL 조건     |
| 유연성            | 낮음                      | 높음                      |
| 사용 난이도       | 간단                      | 약간 복잡                 |
| 예제             | `@Secured("ROLE_ADMIN")`  | `@PreAuthorize("hasRole('ADMIN') and #id > 0")` |

##### 실습 예제
1. 컨트롤러에 `@Secured("ROLE_USER")`를 추가해 특정 엔드포인트 보호.
2. 서비스 메서드에 `@PreAuthorize("#id == authentication.principal.username")`를 적용해 사용자 본인의 데이터만 수정 가능하도록 설정.
3. `ROLE_ADMIN`과 `MANAGE_USERS` 권한을 가진 사용자로 로그인해 두 어노테이션의 동작 확인.

##### 주의사항
- **성능**: 메서드 수준 보안은 AOP 프록시를 통해 동작하므로, 과도한 사용은 성능에 영향을 줄 수 있습니다.
- **예외 처리**: `AccessDeniedException`을 적절히 핸들링해 사용자 친화적인 오류 메시지를 제공하세요.
- **설정 활성화**: `@EnableMethodSecurity`가 없으면 어노테이션이 동작하지 않으니 반드시 추가하세요.

---

위 내용은 메서드 수준 보안의 필요성과 `@Secured`, `@PreAuthorize` 어노테이션의 활용 방법을 체계적으로 설명했습니다. 추가 예제나 특정 상황에 대한 설명이 필요하면 말씀해 주세요!