[BackOffice Project] 최근 변경한 비밀번호 저장하기 - SQL에 List 타입 저장

JUNHYUK CHANG·2024년 1월 23일
1

TIL

목록 보기
9/33

내가 담당한 User 와 Board 의 기본 CRUD 를 구현한 뒤 필수 요구사항 중 하나를 구현해보기 위한 과정을 기록해보려 한다.

요구사항

  1. 사용자가 비밀번호를 변경할 때 최근 비밀번호 (3개) 와 겹치지 않는 지 확인.
  2. 비밀번호 변경시 변경한 비밀번호를 저장하고, 오래된 비밀번호는 리스트에서 제거.

제일 먼저 떠오른 방법은 별도의 테이블을 생성하지 않고, User Entity에 passwordList를 만들어 비밀번호가 생성될 때 마다 저장하고 불러오는 것이었다.

하지만 그러면 SQL에 배열 형태의 값을 저장해야하는데.. 여기서부터 고민 스타트.


Kotlin 에서 사용하던 것 처럼 listOf() 형태로 저장해서 쉽게 [0][1] 하면서 쇽쇽 불러오면 너무 편하겠지만, ARRAY 방식은 데이터베이스마다 정책이 다르다고 한다. 현재 사용중인 postgres에선 지원중인 방식이긴 했지만 정석적인 방법이 아닌 것 같아 다른 방법을 찾기로 했다.
(그리고 Mapper? 라는 복잡한 구조로 너무 크게 확장되는 것 같아서..)

차선책으로 떠올린 방법은 passwordList 를 문자열로 변경하여 저장하는 방법이었다.
"password1/password2/password3" 같은 방식으로.

생각보다 구현도 간단했다.

@Entity
@Table(name = "users")
class User(
    
    ...

    @Column(name = "password_list")
    private var passwordListAsString: String? = null,

	...
    
) {
    
    ...
    
    @Transient
    var passwordList: List<String>?
        get() = passwordListAsString?.split(",")
        set(value) {
            passwordListAsString = if (passwordListAsString.isNullOrEmpty()) {
            value?.joinToString(",")
        } else {
            "${passwordListAsString},${value?.joinToString(",")}"
        }
        
   ...
}

@Transient 어노테이션은 User 엔티티에 속하긴 하지만, 데이터베이스에는 저장되지 않도록 한다. 비밀번호 업데이트 시에만 값을 불러와 변환하고 확인하면 되므로 사용하기 좋은 방법인 것 같다. 하지만 자주 불러와야 하거나 값이 너무 커질 경우 작업단계가 번거로워지기 때문에 상황에 따라 사용해야 할 것 같다.

사용법도 간단하다. user.passwordList.get(0) 혹은 user.passwordList.set("password") 라고만 적으면 끝!

세 번째 방법은 @ElementCollection 어노테이션을 사용하는 것이다. JPA 에서 컬렉션(리스트, 세트) 등을 매핑할 때 사용되는 어노테이션으로, @JsonInclude(JsonInclude.Include.NON_NULL) 과 함께 사용하여 해당 필드가 null일 경우 JSON 에서 해당 필드를 제외시키도록 하는 방법이 있다.

이 방법을 사용하면 @ElementCollection 을 통해 별도의 테이블에 매핑되지만 사용할 때는 user.passwordList 로 불러올 수 있다.

@ElementCollection 어노테이션은 아직 한 번도 사용해보지 않은 어노테이션이기 때문에 이번 기회를 통해 사용해보기로 했고, 덕분에 password 확인 로직은 간편하게 구현할 수 있었다.

user.passwordList = when {
    user.passwordList == null -> listOf(request.newPassword)
    request.newPassword in user.passwordList!! -> throw IllegalArgumentException("Password already in use")
    user.passwordList!!.size < 3 -> listOf(request.newPassword) + user.passwordList!!
    else -> user.passwordList!!.drop(1) + request.newPassword
}

SpringBoot 를 배우며 엥, 이런 어노테이션도 있어?? 하고 매일 놀라는 중이다. 다양한 어노테이션을 계속 접하는 만큼, 한 번 사용할 때 사용법을 잘 익혀두고 다음에 필요할 때 떠올릴 수 있도록 잘 기억해두어야겠다.

1개의 댓글

comment-user-thumbnail
2024년 1월 24일

생각보다 구현도 간단했다! 라니 넘 멋져 (❣ㅅ❣)

답글 달기