Skip to content

Conversation

@jinwon1234
Copy link
Collaborator

@jinwon1234 jinwon1234 commented Jan 6, 2026

변경점 👍

close: #32

gemini api를 통한 게시글 신고 처리 로직을 완성했습니다.
(gemini를 사용한 이유는.. 공짜이기 때문입니다.)

  • 로직 흐름
  1. 유저는 게시글을 신고합니다. (이미 신고한 게시글은 중복 신고 불가능)
  2. 매 시간마다 스케줄링을 돌려서 gemini api에게 부적절한 표현을 사용한 diary를 감지하도록합니다.
  3. 부적절한 표현을 사용한 diary는 삭제합니다.

Spring AIChatClient 기능과 OpenAi와의 호완성을 위해서 일반적인 gemini api 설정 방법과 다른 방법으로 설정했습니다.

spring.ai.openai.api-key=tempkey
spring.ai.gemini.api-key=${AI_KEY}
spring.ai.gemini.base-url=https://generativelanguage.googleapis.com/v1beta/openai/
spring.ai.gemini.completions-path=chat/completions
spring.ai.gemini.model=gemini-2.5-flash

.properties의 tempkey는 의존성 주입을 위한 부분이기 때문에 무시하셔도됩니다.

비고 ✏

public void deleteDiaryId(Long userId, Long diaryId) {
   diaryOrderRepository.findByUserId(userId)
           .ifPresent(order -> order.removeDiaryId(diaryId));
   diaryOrderRepository.flush();
}

public void deleteByDiaryIds(Set<Long> userIds, List<Long> diaryIds) {
   diaryOrderRepository.findByUserIds(userIds)
           .forEach(order -> order.removeDiaryIds(diaryIds));
   diaryOrderRepository.flush();
}

diaryOrder 삭제의 경우 영속성 컨텍스트를 기반으로한 dirty checking이라는 것을 아실겁니다.
저희는 안정적인 삭제를 위해 @Modifying(clearAutomatically = true)를 기반으로한 벌크쿼리를 사용하고 있기 때문에, dirty checking이 적용되기 전에 다른 쿼리로 인해 영속성 컨텍스트가 지워질 수 있습니다.
이런 현상을 방지하기 위해서 diaryOrder 삭제의 경우 .flush() 를 넣어줬습니다.

addDiaryId의 경우에는 벌크 쿼리와 같이 사용되는 일은 없어서 일단 그대로 뒀습니다.

@jinwon1234 jinwon1234 self-assigned this Jan 6, 2026
@jinwon1234 jinwon1234 added the enhancement New feature or request label Jan 6, 2026
Copy link
Collaborator

@sinsehwan sinsehwan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

프롬프트까지 다 구성해주셨네요! 고생하셨습니다 다만 몇 가지 사항에 대해 의견 남겨주시면 감사하겠습니다

Comment on lines 24 to 28
@Modifying(clearAutomatically = true)
@Query("delete from DiaryReportEntity dr")
void deleteAllWithBulk();

@Modifying(clearAutomatically = true)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Modifying 시 flushAutomatically = true 옵션 추가해서 flush하도록 하는 방법은 어떤가요 지금 상황에서는 @Modifying이 있는지 개발자가 직접 체크해야 할 것 같아서 나중에 놓치는 부분이 생길 수 있을 것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 좋습니다! 제가 깜빡하고 있었네요! 일괄적으로 flushAutomatically = true 부여하도록 하겠습니다!

Comment on lines +45 to +47
public boolean existsByUserId(UserEntity user, DiaryEntity diary) {
return diaryReportRepository.existsByUserAndDiary(user, diary);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기에도 readOnly 붙여도 될 것 같습니다!

Comment on lines 70 to 75
Set<Long> response = chatClient.prompt(prompt)
.call().entity(AiDiaryReportResponseDto.class).diaryIds();

ArrayList<Long> invalidDiaryIds = new ArrayList<>(response);

diaryReportLowService.deleteAllWithBulk();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI 요청에서 삭제까지 한 트랜잭션에 엮여 있고 해당 부분이 동기로 구성되어 있네요! 매 시간 정각마다 다른 요청 처리가 미뤄질 것 같아서 걱정이 조금 됩니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그래서 1분이상 응답이 안오면 연결을 종료하는 것으로 AiConfig에 설정을 해뒀습니다..! 아무래도 하나의 비즈니스 로직이라고 생각해서 트랜잭션으로 묶어뒀는데 생각하시는 다른 방법이 있을까요??

Copy link
Collaborator Author

@jinwon1234 jinwon1234 Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좀 생각해보니.. 트랜잭션 분리를 해서 DB 커넥션을 덜 잡아 놓는게 좋은 거 같아서 해당 방향으로 리팩토링했습니다!

Comment on lines +65 to +71
public void removeDiaryIds(List<Long> diaryIds) {
if (this.orderList == null) {
return;
}
this.orderList = new ArrayList<>(this.orderList);
diaryIds.forEach(diaryId -> this.orderList.remove(diaryId));
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스케쥴링 시 해당 함수 호출해서 사용하는군요! 확인했습니다.

Comment on lines 64 to 68
SystemMessage systemMessage = new SystemMessage(reportPrompt);

UserMessage userMessage = new UserMessage(reportRequest);

Prompt prompt = new Prompt(systemMessage, userMessage);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분 별도 함수로 구성하는 것도 좋아보입니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 별도 함수로 구성해보도록 하겠습니다!

Copy link
Collaborator

@sinsehwan sinsehwan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

피드백 사항들 잘 반영해주셨네요! 나중에 ai 분석을 비동기로 간다면 @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) 이거 도입해보는 거 고려해봐도 좋을 것 같아요 결과 왔을 때 삭제 처리하는 것만 따로 구성해도 될 것 같아요

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

게시글 신고

3 participants