SLiPP.net에 facebook 알림 기능을 구현하려고 이러 저리 삽질하다가 알게된 내용을 공유한다. 어찌 보면 당연한 결과이지만 왜 이 같은 현상이 발생하는지 몰라 해결책을 찾는 개발자에게 도움이 될까하고 공유한다.
처음 구현은 다음과 같다.
먼저 질문에 답변을 다는 경우 답변을 저장한 후 NotificationService.java를 활용해 페이스북에 알림을 하는 기능이다.
@Service("qnaService")
@Transactional
public class QnaService {
public void createAnswer(SocialUser loginUser, Long questionId, Answer answer) {
Question question = questionRepository.findOne(questionId);
answer.writedBy(loginUser);
answer.answerTo(question);
answerRepository.save(answer);
notificationService.notifyToFacebook(loginUser, question);
}
}
@Service
public class NotificationService {
public void notifyToFacebook(SocialUser loginUser, Question question) {
Set<SocialUser> notifierUsers = question.findNotificationUser(loginUser);
if (notifierUsers.isEmpty()) {
return;
}
// 페이스북에 알림을 보낸다.
}
}
최초 구현을 위와 같이 구현했더니 정상적으로 동작했다. 그런데 알림을 보내야 하는 사용자가 많아지면 많아질 수록 답변 글을 쓴 사용자에 대한 응답은 느려진다. 이 같은 단점을 보완하기 위해 알림 기능을 Spring의 Async 기능을 활용해 비동기 처리했다. 비동기 처리는 설정 하나 추가하고 notifyToFacebook에 @Async 어느테이션을 추가하면 쉽게 구현할 수 있었다.
그런데 문제는 다음에 방생했다. 답변 글을 달면 데이터베이스 Transaction을 완료하지 못한다는 에러 메시지가 발생하는 것이다. 원인은 question.findNotificationUser(loginUser); 메소드에 있었다. Async 어노테이션을 사용하는 순간 서로 다른 Thread에서 동작하기 때문에 앞에서 생성한 Transaction이 연장되지 않는다. 즉, Question 객체 내에서 데이터베이스와의 연동 작업을 할 수 없다는 것이다. 그런데 question.findNotificationUser(loginUser); 메소드에서는 데이터베이스와의 연동 작업을 진행하고 있었다. 이 같은 원인을 파악하고 QnaService에서 모든 데이터베이스 작업을 완료한 후 알림을 요청하는 구조로 변경했다. 최종 결과는 다음과 같다.
@Service("qnaService")
@Transactional
public class QnaService {
public void createAnswer(SocialUser loginUser, Long questionId, Answer answer) {
Question question = questionRepository.findOne(questionId);
answer.writedBy(loginUser);
answer.answerTo(question);
answerRepository.save(answer);
notificationService.notifyToFacebook(loginUser, question, question.findNotificationUser(loginUser));
}
}
@Service
public class NotificationService {
@Async
public void notifyToFacebook(SocialUser loginUser, Question question, Set<SocialUser> notifierUsers) {
if (notifierUsers.isEmpty()) {
return;
}
//
}
}
앞으로 @Async를 사용할 경우 무작정 사용할 것이 아니라 각각의 실행이 Thread가 분리된 상태에서 실행되기 때문에 이로 인해 발생할 수 있는 이슈가 있는지 검토한 후에 적용해야겠다. 그리 오랜 시간 삽질을 하지는 않았지만 아무 생각 없이 적용하려하다가 큰 코 다칠 뻔 했다.
0개의 의견 from SLiPP
의견을 남기기 위해서는 SLiPP 계정이 필요합니다.
안심하세요! 회원가입/로그인 후에도 작성하시던 내용은 안전하게 보존됩니다.
SLiPP 계정으로 로그인하세요.
또는, SNS 계정으로 로그인하세요.