앞서 정책 API를 호출하여 데이터베이스에 저장하고, 리스트 조회와 상세 조회를 구현했다.
상세 조회 화면에서 찜하기 버튼을 두고, 정책 찜하기 API를 호출할 수 있도록 구현했다.
찜하기 기능을 위해 고려해야할 사항
- 현재 정책 업데이트 방식
- 기존 데이터를 모두 삭제하고 외부 API를 호출해 업데이트된 데이터를 새로 저장.
- 따라서 원본 정책이 사라지면 정책 정보를 불러올 수 없음.
- 찜하기 테이블에 정책 정보 저장의 부담
- 정책 정보를 찜하기 테이블에 저장하기에는 자원이 많이 소모됨.
- 외래키 제약 조건 문제
- 현재 업데이트 방식을 고려하면 찜하기 테이블의 policyId 에 외래키 제약 조건을 설정할 수 없음.
해결 방안
- 정책 찜하기 테이블
- 외래키 제약 조건을 설정하기 않고 정책 찜하기 테이블에 정책 id를 저장.
- isActive 컬럼을 추가. (정책의 활성화 상태를 저장)
- 비활성화 되는 경우를 대비해 정책 찜하기 호출 시 최소한의 정보(정책 제목)를 함께 저장.
- 찜한 정책 조회 로직
- 사용자가 찜한 모든 정책을 조회하고, 각 정책의 policyId를 정책 테이블에서 조회
- 사용자가 찜한 정책이 정책 테이블에 존재하는 경우
- isActive가 false이면 true로 변경 (삭제되었다가 다시 활성화 된 경우)
- 해당하는 정책 정보를 리턴
- 사용자가 찜한 정책이 정책 테이블에 존재하지 않는 경우
- isActive가 true이면 false로 변경
- 최소한의 정보(정책 찜하기 테이블에 저장된) 리턴
위 방법 사용 시
- 사용자는 비활성화된 정책도 확인할 수 있고, 어떤 정책이 비활성화 되었는지 알 수 있음.
- isActive가 false인 정책에 대한 추가적인 안내가 필요. (비활성화 되었다는 메시지 등으로 찜하기 해제 유도?)
1. 프로젝트 구조
src/main/java/com/example/withpeace/
│
├── domain/ # 도메인 모델 (엔티티)
│ ├── YouthPolicy.java # 청년 정책 엔티티
│ └── FavoritePolicy.java # 청년 정책 찜하기 엔티티
│
├── repository/ # 데이터 접근 계층
│ ├── YouthPolicyRepository.java # 청년 정책 레포지토리
│ └── FavoritePolicyRepository.java # 정책 찜하기 레포지토리
│
├── controller/ # 컨트롤러 계층
│ └── PolicyController.java # 정책 관련 API 엔드포인트
│
└── service/ # 비즈니스 로직 계층
└── PolicyService.java # 정책 관련 비즈니스 로직
2. 도메인 엔티티 구현
FavoritePolicy - 정책 찜하기 엔티티 클래스
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@DynamicUpdate
@Table(name = "favorite_policies")
public class FavoritePolicy {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, unique = true)
private Long id;
@Column(name = "policy_id", nullable = false, unique = true)
private String policyId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
private User user;
@Column(name = "title", nullable = false)
private String title;
@Column(name = "is_active", nullable = false)
private boolean isActive;
@Column(name = "create_date", nullable = false)
private LocalDateTime createDate;
@Builder
public FavoritePolicy(String policyId, User user, String title) {
this.policyId = policyId;
this.user = user;
this.title = title;
this.isActive = true;
this.createDate = LocalDateTime.now();
}
public void setIsActive(boolean isActive) { this.isActive = isActive; }
}
`policyId` 에는 정책 ID 값을 저장하되, 외래키 제약 조건을 설정하지 않았다. 정책 리프레시 기능 때문이다. (주기적으로 정책 데이터를 초기화하고 다시 저장함.)
`title` 컬럼을 이용해 정책 제목을 저장한다. 이를 통해 정책이 삭제되더라도 기본적인 정보를 유지할 수 있다.
`isActive` 컬럼을 이용해, 이후 구현할 “내가 찜한 정책 조회” 시 해당 정책을 찾을 수 없는 경우 isActive 값을 false로 설정하여 데이터를 우선 보존하고 해당 정책에 대해 추가적인 처리를 할 수 있도록 했다.
3. 레포지토리 구현
FavoritePolicyRepository - 정책 찜하기 레포지토리
public interface FavoritePolicyRepository extends JpaRepository<FavoritePolicy, Long> {
FavoritePolicy findByUserAndPolicyId(User user, String policyId);
}
`findByUserAndPolicyId` 는 사용자와 정책 ID로 찜한 정책을 찾는다.
4. 컨트롤러 구현
YouthPolicyController
@RestController
@RequiredArgsConstructor
@RequestMapping("api/v1/policies")
@Slf4j
public class YouthPolicyController {
private final YouthPolicyService youthPolicyService;
// ... (기존 코드 생략)
// 정책 찜하기
@PostMapping("/{policyId}/favorites")
public ResponseDto<?> registerFavoritePolicy(@UserId Long userId, @PathVariable String policyId) {
youthPolicyService.favoritePolicy(userId, policyId);
return ResponseDto.ok(true);
}
}
`registerFavoritePolicy`는 사용자 ID와 정책 ID를 파라미터로 받아 정책 찜하기 요청을 처리한다.
5. 서비스 구현
YouthPolicyService
@Service
@RequiredArgsConstructor
@Slf4j
public class YouthPolicyService {
// ... (기존 코드 생략)
@Transactional
public void favoritePolicy(Long userId, String policyId) {
User user = getUserById(userId);
YouthPolicy policy = getPolicyById(policyId);
try{
// 찜하기 되어있는지 확인
FavoritePolicy favoritePolicy = favoritePolicyRepository.findByUserAndPolicyId(user, policyId);
// 찜하기 되어있지 않은 경우 찜하기 처리 수행
if(favoritePolicy == null) {
favoritePolicyRepository.save(FavoritePolicy.builder()
.policyId(policy.getId())
.user(user)
.title(policy.getTitle())
.build());
}
} catch (Exception e) {
throw new CommonException(ErrorCode.YOUTH_POLICY_ERROR);
}
}
}
- 사용자와 정책을 조회한다. (존재 여부 확인)
- `findByUserAndPolicyId`를 통해 사용자가 해당 정책을 찜하기 했는지 확인한다.
- 찜하기 되어있지 않은 경우에만 새로운 FavoritePolicy 엔티티를 생성하고 저장한다.
안드로이드 팀원의 요청에 따라 찜하기 되어있을 경우에도 true를 반환하도록 구현했다. (Optimistic UI 적용해보기 위함이라고 함.)
6. API 응답 예시
요청 URL
[POST] http://cheongha.site/api/v1/policies/R2024080625806/favorites
Response Body
{
"data": true
"error": null
}
'Projects > 청하-청년을 위한 커뮤니티 서비스' 카테고리의 다른 글
[청하] 32. 정책 찜하기 해제 기능 구현 (0) | 2024.10.22 |
---|---|
[청하] 31. 찜한 정책 리스트 조회 기능 구현 (2) | 2024.10.21 |
[청하] 29. GCP 환경 구축 - SSH 키 설정 및 CI/CD 파이프라인 구성 (2) | 2024.10.19 |
[청하] 28. GCP 설정 (feat. GCS 설정 및 스프링부트 연동) (0) | 2024.10.19 |
[청하] 27. 정책 상세 조회 기능 구현 (3) | 2024.10.18 |
댓글