Skip to content

Commit b07bc0c

Browse files
committed
feat ( #9 ) : persistence 레이어 추가
1 parent 4188b09 commit b07bc0c

File tree

4 files changed

+163
-0
lines changed

4 files changed

+163
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package hs.kr.entrydsm.user.domain.user.adapter.out.persistence
2+
3+
import hs.kr.entrydsm.user.domain.user.adapter.out.mapper.UserMapper
4+
import hs.kr.entrydsm.user.domain.user.adapter.out.persistence.repository.UserRepository
5+
import hs.kr.entrydsm.user.domain.user.application.port.out.UserPort
6+
import hs.kr.entrydsm.user.domain.user.model.User
7+
import hs.kr.entrydsm.user.global.utils.encryption.EncryptionUtil
8+
import hs.kr.entrydsm.user.global.utils.encryption.HashUtil
9+
import org.springframework.data.repository.findByIdOrNull
10+
import org.springframework.stereotype.Component
11+
import java.time.LocalDateTime
12+
import java.util.UUID
13+
14+
/**
15+
* 사용자 데이터의 영속성 처리를 담당하는 어댑터 클래스입니다.
16+
* 헥사고날 아키텍처의 Driven Adapter 역할을 하며, 도메인 계층의 UserPort를 구현합니다.
17+
*
18+
* @property userRepository JPA 기반 사용자 데이터 저장소
19+
* @property userMapper 도메인 모델과 JPA 엔티티 간 변환 매퍼
20+
* @property encryptionUtil 민감한 데이터 암호화 유틸리티
21+
*/
22+
@Component
23+
class UserPersistenceAdapter(
24+
private val userRepository: UserRepository,
25+
private val userMapper: UserMapper,
26+
private val encryptionUtil: EncryptionUtil,
27+
) : UserPort {
28+
/**
29+
* ID로 사용자를 조회합니다.
30+
*
31+
* @param id 조회할 사용자 ID
32+
* @return 조회된 사용자 도메인 모델 (없으면 null)
33+
*/
34+
override fun findById(id: UUID): User? {
35+
return userRepository.findByIdOrNull(id)
36+
?.let { userMapper.toModel(it) }
37+
}
38+
39+
/**
40+
* 전화번호로 사용자를 조회합니다.
41+
* 전화번호를 암호화하여 데이터베이스에서 조회합니다.
42+
*
43+
* @param phoneNumber 조회할 전화번호
44+
* @return 조회된 사용자 도메인 모델 (없으면 null)
45+
*/
46+
override fun findByPhoneNumber(phoneNumber: String): User? {
47+
val phoneNumberHash = HashUtil.sha256(phoneNumber)
48+
return userRepository.findByPhoneNumberHash(phoneNumberHash)
49+
?.let { userMapper.toModel(it) }
50+
}
51+
52+
/**
53+
* 전화번호로 사용자 존재 여부를 확인합니다.
54+
*
55+
* @param phoneNumber 확인할 전화번호
56+
* @return 사용자 존재 여부
57+
*/
58+
override fun existsByPhoneNumber(phoneNumber: String): Boolean {
59+
val phoneNumberHash = HashUtil.sha256(phoneNumber)
60+
return userRepository.existsByPhoneNumberHash(phoneNumberHash)
61+
}
62+
63+
/**
64+
* 사용자 정보를 저장합니다.
65+
*
66+
* @param user 저장할 사용자 도메인 모델
67+
* @return 저장된 사용자 도메인 모델
68+
*/
69+
override fun save(user: User): User {
70+
val entity = userMapper.toEntity(user)
71+
val savedEntity = userRepository.save(entity)
72+
return userMapper.toModelNotNull(savedEntity)
73+
}
74+
75+
/**
76+
* ID로 사용자를 삭제합니다.
77+
*
78+
* @param userId 삭제할 사용자 ID
79+
*/
80+
override fun deleteById(userId: UUID) {
81+
userRepository.deleteById(userId)
82+
}
83+
84+
/**
85+
* 지정된 일수보다 오래된 탈퇴 사용자를 조회합니다.
86+
*
87+
* @param days 기준 일수
88+
* @return 삭제 대상 사용자 목록
89+
*/
90+
override fun findWithdrawnUsersOlderThan(days: Long): List<User> {
91+
val cutoffDate = LocalDateTime.now().minusDays(days)
92+
return userRepository.findAllByIsActiveFalseAndWithdrawalAtBefore(cutoffDate)
93+
.map { userMapper.toModelNotNull(it) }
94+
}
95+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package hs.kr.entrydsm.user.domain.user.adapter.out.persistence.repository
2+
3+
import hs.kr.entrydsm.user.domain.user.adapter.out.domain.UserCache
4+
import org.springframework.data.repository.CrudRepository
5+
import java.util.UUID
6+
7+
/**
8+
* 사용자 캐시를 위한 Redis 저장소 인터페이스입니다.
9+
* Spring Data Redis를 통해 사용자 캐시 데이터의 관리를 담당합니다.
10+
*/
11+
interface UserCacheRepository : CrudRepository<UserCache, UUID>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package hs.kr.entrydsm.user.domain.user.adapter.out.persistence.repository
2+
3+
import hs.kr.entrydsm.user.domain.user.adapter.out.domain.UserInfo
4+
import org.springframework.data.repository.CrudRepository
5+
6+
/**
7+
* 사용자 인증 정보를 위한 Redis 저장소 인터페이스입니다.
8+
* Spring Data Redis를 통해 사용자 인증 캐시 데이터를 관리합니다.
9+
*/
10+
interface UserInfoRepository : CrudRepository<UserInfo, String>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package hs.kr.entrydsm.user.domain.user.adapter.out.persistence.repository
2+
3+
import hs.kr.entrydsm.user.domain.user.adapter.out.UserJpaEntity
4+
import org.springframework.data.jpa.repository.JpaRepository
5+
import java.time.LocalDateTime
6+
import java.util.UUID
7+
8+
/**
9+
* 사용자 JPA 엔티티를 위한 저장소 인터페이스입니다.
10+
* Spring Data JPA를 통해 기본 CRUD 작업과 커스텀 쿼리를 제공합니다.
11+
*/
12+
interface UserRepository : JpaRepository<UserJpaEntity, UUID> {
13+
14+
15+
16+
17+
/**
18+
* 전화번호로 사용자를 조회합니다.
19+
*
20+
* @param phoneNumberHash 조회할 전화번호
21+
* @return 조회된 사용자 엔티티 (없으면 null)
22+
*/
23+
fun findByPhoneNumberHash(phoneNumberHash: String): UserJpaEntity?
24+
25+
/**
26+
* 전화번호로 사용자 존재 여부를 확인합니다.
27+
*
28+
* @param phoneNumberHash 확인할 전화번호
29+
* @return 사용자 존재 여부
30+
*/
31+
fun existsByPhoneNumberHash(phoneNumberHash: String): Boolean
32+
33+
/**
34+
* ID로 사용자를 삭제합니다.
35+
*
36+
* @param id 삭제할 사용자 ID
37+
*/
38+
fun deleteById(id: UUID?)
39+
40+
/**
41+
* 지정된 일시 이전에 탈퇴한 비활성 사용자들을 조회합니다.
42+
*
43+
* @param cutoffDate 기준 일시
44+
* @return 삭제 대상 사용자 엔티티 목록
45+
*/
46+
fun findAllByIsActiveFalseAndWithdrawalAtBefore(cutoffDate: LocalDateTime): List<UserJpaEntity>
47+
}

0 commit comments

Comments
 (0)