[데브코스] Spring Boot 인증·인가(Auth) (27강) - 글 수정에 인증(Authorization) 적용 + 작성자만 수정 가능하도록 인가(권한체크) 추가하기

zuno·2026년 1월 13일

이번 강의에서는 글 수정 API에서도 인증 정보를 전달하고,
단순히 “로그인했는지”를 넘어서 해당 글을 수정할 권한이 있는지(인가) 까지 검증하는 로직을 구현했다.


🔐 인증(Authentication) vs 인가(Authorization)

인증(Authentication)

  • 사용자가 누구인지 확인하는 과정
  • 로그인, API Key 검증 등이 여기에 해당
  • 예시
    • 아이디 / 비밀번호 확인
    • Authorization 헤더의 Bearer 토큰 검증

인가(Authorization)

  • 인증된 사용자
    특정 리소스에 접근할 권한이 있는지 확인하는 과정
  • 흔히 말하는 권한 체크
  • 예시
    • 본인만 글 수정 가능
    • 관리자만 삭제 가능

👉 이번 강의의 핵심은
“글 수정은 로그인만 했다고 되는 게 아니라, 작성자 본인인지 확인해야 한다”는 점이다.


✏️ 글 수정 API 핵심 코드

@PutMapping("/{id}")
@Transactional
@Operation(summary = "수정")
public RsData<Void> modify(
        @PathVariable int id,
        @Valid @RequestBody PostModifyReqBody reqBody,
        @NotBlank
        @Size(min = 30, max = 50)
        @RequestHeader("Authorization") String authorization
) {

    String apiKey = authorization.replace("Bearer ", "");
    Member actor = memberService.findByApiKey(apiKey)
            .orElseThrow(() -> new ServiceException("401-1", "존재하지 않는 apiKey 입니다."));

    Post post = postService.findById(id).get();

    if (!actor.equals(post.getAuthor()))
        throw new ServiceException("403-1", "글 수정 권한이 없습니다.");

    postService.modify(post, reqBody.title, reqBody.content);

    return new RsData<>(
            "200-1",
            "%d번 글이 수정되었습니다.".formatted(post.getId())
    );
}

🔍 인증 흐름 정리

  1. 클라이언트는 Authorization: Bearer {apiKey} 헤더를 전달
  2. 서버는 헤더에서 apiKey 추출
  3. apiKey로 Member 조회
  4. apiKey가 유효하지 않으면 401 에러
  5. 이후 글 수정 로직 진행

👉 여기까지는 인증(Authentication) 단계


🚨 인가(권한 체크) 로직의 핵심

if (!actor.equals(post.getAuthor()))
    throw new ServiceException("403-1", "글 수정 권한이 없습니다.");

이 부분이 인가(Authorization) 다.

의미

  • 현재 요청을 보낸 사용자(actor)가
  • 이 글의 작성자(author)와 같은 사람인지 확인
  • 다르면 403 Forbidden

🤔 “DB에서 뽑아온 객체인데, 어떻게 equals가 true가 될까?”

이 질문이 자연스럽게 생길 수 있다.

“apiKey로 조회한 Member랑
post.getAuthor()로 가져온 Member는
서로 다른 객체 아닌가?”

정답은 BaseEntity의 equals 구현 때문이다.


🧩 BaseEntity의 equals / hashCode 구현

@MappedSuperclass
@Getter
public abstract class BaseEntity {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private int id;

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BaseEntity that = (BaseEntity) o;
        return id == that.id;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(id);
    }
}

이 equals의 기준은?

  • 객체 주소 ❌
  • DB의 PK(id) 기준 ⭕

즉,

  • 서로 다른 시점에
  • 서로 다른 방식으로 조회된 엔티티라도
  • 같은 테이블 + 같은 id면 같은 엔티티로 판단

✅ 그래서 이 비교가 안전한 이유

actor.equals(post.getAuthor())
  • actor → apiKey로 조회한 Member
  • post.getAuthor() → Post에 연관된 Member
  • 둘 다 Member 엔티티
  • 둘 다 id가 동일

👉 BaseEntity의 equals 구현 덕분에 true


📌 왜 이런 설계가 필요한가?

  • JPA 엔티티는 프록시, 영속성 컨텍스트 등으로 인해
    객체 인스턴스 비교(==) 가 위험함
  • 그래서 실무에서도
    • equals / hashCode를
    • PK 기준으로 통일하는 경우가 많음

이 덕분에

  • 작성자 본인 여부
  • 소유권 체크
  • 권한 검증 로직을
    명확하고 안전하게 작성할 수 있다.

✨ 정리

  • 글 수정 시에도 인증 정보(apiKey)는 반드시 필요
  • 인증 후에는 작성자 본인인지 인가(권한 체크) 가 추가로 필요
  • actor.equals(post.getAuthor()) 비교는
    • BaseEntity에서 id 기준 equals를 구현했기 때문에 정상 동작
  • 이는 JPA 환경에서 권한 체크를 위한 대표적인 패턴

0개의 댓글