내가 담당한 User 와 Board 의 기본 CRUD 를 구현한 뒤 필수 요구사항 중 하나를 구현해보기 위한 과정을 기록해보려 한다.
요구사항
- 사용자가 비밀번호를 변경할 때 최근 비밀번호 (3개) 와 겹치지 않는 지 확인.
- 비밀번호 변경시 변경한 비밀번호를 저장하고, 오래된 비밀번호는 리스트에서 제거.
제일 먼저 떠오른 방법은 별도의 테이블을 생성하지 않고, 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 를 배우며 엥, 이런 어노테이션도 있어?? 하고 매일 놀라는 중이다. 다양한 어노테이션을 계속 접하는 만큼, 한 번 사용할 때 사용법을 잘 익혀두고 다음에 필요할 때 떠올릴 수 있도록 잘 기억해두어야겠다.
생각보다 구현도 간단했다! 라니 넘 멋져 (❣ㅅ❣)