[SpringBoot] AuditorAware 기능을 통해 현재 작업자 기록하기 (@CreatedBy, @LastModifiedBy)

JUNHYUK CHANG·2024년 2월 5일
1

TIL

목록 보기
16/33

이전 포스트에서 Auditing 기능을 활용하여 생성일자와 수정일자를 자동으로 지정하는 BaseEntity 를 생성하고 이를 다른 Entity 에서 상속받아 사용할 수 있도록 하였다. 여기에 추가로 @CreatedBy 와 @LastModifiedBy 를 통해 생성한 사람과 수정한 사람의 id 를 기록해보기로 하자.

BaseEntity 는 다음과 같이 수정하였다.

지난 포스트와 달라진점
1. 컬럼명을 직접 지정해주지 않음.
-> 변수명의 카멜케이스를 따라 자동으로 생성해주기 때문!
-> 단, yml에 ddl-auto : update 로 지정되어 있는지 확인 필수!
2. 최초 생성시 수정일자와 생성일자를 LocalTime.now() 로 초기화 함.

생성일자/수정일자는 단순히 이벤트 발생 시의 시각을 기록해주면 되기 때문에 별 다른 처리가 필요 없지만, 생성자/수정자를 어떻게 기록할 것인지에 대한 정의와 방법이 필요하기 때문에 별도의 작업이 필요하다.

바로 AuditorAware 를 사용하는 방법이다.

AuditorAware 는 SpringBoot에서 제공하는 인터페이스로, AuditorAware의 구현체를 작성하여 @Component로 등록하면 JPA는 이를 통해 Entity가 생성되거나 수정될 때 getCurrentAuditor() 메서드를 호출하여 사용자 정보를 가져와서 @CreatedBy, @LastModifiedBy 에 값을 자동으로 채워주게 된다. ( 별도의 호출은 필요 없다 ! )

우리는 CustomAuditorAware 라는 클래스를 생성하여 AuditorAware 를 상속받고 getCurrentAuditor() 메서드를 오버라이드하면 된다.

우리는 사용자의 ID(Long) 를 반환할 것이기 때문에 반환형도 Long 으로 지정해 주었다. 여기서 Optional 이란 값이 있을 수도 / 없을 수도 있는 상황을 명시적으로 나타낸 것으로, null 값을 다룰 때 발생할 수 있는 문제를 방지해주는 역할을 해준다.

  1. SecurityContextHolder 에서 getContext() 로 내용을 가져온다.
    (이때, 값은 null 일 수도 있음)
  2. 그 내용 중 authentication(인증정보) 를 가져오고
  3. principal 을 우리가 사용중인 UserPrincipal 형으로 변환한 뒤
  4. id 값만 return 하도록 한다.
    (map{} 또한 Optional 클래스의 메서드 중 하나로, 값이 존재할 때만 작업을 수행하도록 한다.)

그리하면 현재 인증된 유저의 id 를 CreatedBy 와 LastModifiedBy 에 저장하게 되는 것!

만약 인증되어있지 않은 상태라면 null 값을 저장하게 된다. 정책적으로 이를 허용하지 않으려면 throw 로 예외처리를 해주면 된다.


하지만! 여기엔 논리적 문제점이 하나 있다.

바로 User Entity 를 생성하는 signUp 메서드 이다.

상식적으로 생각해볼 때 User 는 기본적으로 Authorize 하기 전에 생성하게 되므로 항상 null 일 수 밖에 없게된다. 그렇기 때문에 당연히 SecurityContextHolder 의 내용도 텅텅 비어있게 되고, 에러가 발생하게 되는 것.

여기선 두 가지 해결방법을 생각해 보았다.

  1. User는 다른 Entity 와 별개로 취급하여 BaseEntity 를 상속받지 않고, CreatedAt, ModifiedAt 만 기록하도록 하는 것.
  2. signUp 의 경우를 고려하여 SecurityContextHolder 의 내용이 null 일 경우 별도의 처리를 해주는 것.

단순하게 생각하면 1번으로 진행해도 괜찮을 것 같았지만 혹시 관리자가 테스트용 유저나 특별한 계정을 생성하고, 관리기록을 남겨야하는 필요성이 있는 상황을 가정하면 2번 문제를 해결해야 할 것 같았다.

이를 반영한 코드는 다음과 같다.

authentication 을 가져와서 it.Principal 과 UserPrincipal 가 서로 호환 되는지 (is) 확인하여 true 라면 id 값을 가져오고, 아니면 Empty() 값(=null) 로 반환하도록 하였다.

이렇게 처리하면 일반 유저가 signUp 으로 User 를 생성할 땐 CreatedBy 가 null 로 생성되고, 관리자가 계정을 생성할 땐 해당 관리자의 id 값이 저장되도록 한다.


BaseEntity 를 분리하여 공통되는 파라미터를 쉽게 처리할 수 있다는 점은 매우 편리하고 좋았지만, 상황에 따라 다른 처리를 해주어야 하는 경우도 꼭 체크해보아야 한다는 점을 다시 한번 느끼게 되었다.

0개의 댓글