Skip to content

Commit

Permalink
refactor: 알림 기능 로직 개선 및 알림 테스트 추가 (#497)
Browse files Browse the repository at this point in the history
* test: 테스트용 api 수정 및 추가

* test: @EnableAsync 어노테이션 추가

* test: 알림 이벤트 로그 정보 추가

* test: 미사용 테스트 api 주석 처리

* refactor: Async ThreadExecutor 설정 변경

* refactor: 불필요한 트랜잭션 설정 제거

* refactor: Async ThreadPool 설정 수정

* refactor: Async ThreadPool 설정 30 수정

* refactor: Async ThreadPool 설정 20 수정

* refactor: Async ThreadPool 설정 40 수정

* refactor: Async ThreadPool 설정 20 수정

* refactor: Async ThreadPool 설정 30 수정

* refactor: Async ThreadPool 설정 40 수정

* refactor: Async ThreadPool 램프업 스레드 수 변경

* refactor: Async ThreadPool 스레드 수 변경

* refactor: Async ThreadPool 스레드 수 변경

* refactor: Async ThreadPool 스레드 수 변경

* refactor: Async ThreadPool 스레드 수 변경

* refactor: Async ThreadPool 스레드 수 변경

* refactor: Async ThreadPool 스레드 수 30으로 변경

* refactor: Async ThreadPool 스레드 수 50으로 변경

* refactor: Async ThreadPool 스레드 수 40으로 변경

* refactor: Async ThreadPool 스레드 수 30으로 변경

* refactor: Async ThreadPool 스레드 수 100으로 변경

* refactor: Async ThreadPool 스레드 수 8으로 변경

* refactor: Async 테스트 설정 변경

* refactor: Async 테스트 스레드 풀 설정 15로 변경

* refactor: Async 테스트 스레드 풀 설정 15로 변경

* refactor: Async 테스트 스레드 풀 설정 25로 변경

* refactor: 램프 코드 반복 횟수 변경

* refactor: 알림 허용 시 알림 발송 로직 추가

* refactor: Async thread 30으로 변경

* refactor: Async thread 35으로 변경

* refactor: Async thread 45으로 변경

* refactor: Async thread 50으로 변경

* refactor: Async thread 40으로 변경

* refactor: Async thread 50으로 변경

* test: 알림 구독 시 알림 발송 이벤트 테스트 추가

* test: 알림 전송 메서드 테스트 추가

* refactor: 알림 발송 로직 변경

* test: 알림 이벤트 리스너 테스트 추가

* chore: 테스트 컨트롤러 비활성화

* chore: 비동기 스레드 풀 코어 수 40으로 설정

* chore: 불필요한 주석 제거

* refactor: 사용하지 않는 이벤트 리스너 및 이벤트 객체 삭제
  • Loading branch information
Kim0914 authored Jan 22, 2024
1 parent 052fe2b commit 0f13d5d
Show file tree
Hide file tree
Showing 14 changed files with 270 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.official.pium.admin.service;

import com.official.pium.petPlant.domain.PetPlant;
import com.official.pium.petPlant.event.notification.NotificationEvent;
import com.official.pium.petPlant.repository.PetPlantRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Slf4j
public class TestService {

private final PetPlantRepository petPlantRepository;
private final ApplicationEventPublisher publisher;

// public void sendWaterNotificationTest() {
// List<PetPlant> petPlants = petPlantRepository.findAllByMemberId(7L);
// List<NotificationEvent> events = petPlants.stream()
// .map(plant -> NotificationEvent.builder()
// .title(plant.getNickname())
// .body("(테스트 중) 물을 줄 시간이에요!")
// .deviceToken(plant.getMember().getDeviceToken())
// .build()
// ).toList();
// log.info("동기 알림 테스트 시작. Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
// publisher.publishEvent(NotificationEvents.from(events));
// log.info("동기 알림 테스트 종료. Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
// }

public void sendWaterNotificationAsyncRampTest() {
List<PetPlant> petPlants = petPlantRepository.findAllByMemberId(6L);
// List<NotificationEvent> events = petPlants.stream()
// .map(plant -> NotificationEvent.builder()
// .title(plant.getNickname())
// .body("(테스트 중) 물을 줄 시간이에요!")
// .deviceToken(plant.getMember().getDeviceToken())
// .build()
// ).toList();

for (int i = 0; i < 100; i++) {
PetPlant petPlant = petPlants.get(i);
NotificationEvent event = NotificationEvent.builder()
.title(petPlant.getNickname())
.body("물줘")
.deviceToken(petPlant.getMember().getDeviceToken())
.build();
publisher.publishEvent(event);
}

// log.info("비동기 테스트 램프업 시작");
// for (int i = 0; i < 100; i++) {
// NotificationEvent notificationEvent = events.get(i);
// publisher.publishEvent(notificationEvent);
// }
}

public void sendWaterNotificationAsyncTest() {
List<PetPlant> petPlants = petPlantRepository.findAllByMemberId(6L);
List<NotificationEvent> events = petPlants.stream()
.map(plant -> NotificationEvent.builder()
.title(plant.getNickname())
.body("(테스트 중) 물을 줄 시간이에요!")
.deviceToken(plant.getMember().getDeviceToken())
.build()
).toList();

int i = 1;
log.info("비동기 알림 테스트 시작. Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
for (NotificationEvent event : events) {
log.info(i++ + "번째 알림 이벤트");
publisher.publishEvent(event);
}
log.info("비동기 알림 테스트 종료. Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import com.official.pium.member.domain.Member;
import com.official.pium.member.repository.MemberRepository;
import com.official.pium.notification.application.NotificationService;
import com.official.pium.petPlant.application.ReminderService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;
Expand Down Expand Up @@ -44,7 +43,6 @@ public class AdminPageController {
private final MemberRepository memberRepository;
private final AdminService adminService;
private final NotificationService notificationService;
private final ReminderService reminderService;

@GetMapping("/**")
public String adminPage(@AdminAuth Admin admin, Model model) {
Expand Down Expand Up @@ -164,10 +162,4 @@ public ResponseEntity<Void> logout(HttpServletRequest request) {

return ResponseEntity.ok().build();
}

@GetMapping("/notifications")
public ResponseEntity<String> notificationTest() {
reminderService.sendWaterNotificationTest();
return ResponseEntity.ok("알림 기능 테스트 성공");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.official.pium.admin.ui;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/test")
public class TestController {

// private final TestService testService;

// @GetMapping("/notifications")
// public ResponseEntity<String> notificationTest() {
// testService.sendWaterNotificationTest();
// return ResponseEntity.ok("알림 기능 테스트 성공");
// }

// @GetMapping("/notifications/ramp")
// public ResponseEntity<String> notificationRampTest() {
// testService.sendWaterNotificationAsyncRampTest();
// return ResponseEntity.ok("비동기 알림 기능 테스트 램프업 성공");
// }
//
// @GetMapping("/notifications/async")
// public ResponseEntity<String> notificationAsyncTest() {
// testService.sendWaterNotificationAsyncTest();
// return ResponseEntity.ok("비동기 알림 기능 테스트 성공");
// }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.official.pium.config;

import java.util.concurrent.Executor;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(40);
executor.setThreadNamePrefix("2024-Pium-Thread: ");
executor.initialize();
return executor;
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package com.official.pium.member.application;

import com.official.pium.member.domain.Member;
import com.official.pium.petPlant.domain.PetPlant;
import com.official.pium.history.repository.HistoryRepository;
import com.official.pium.member.application.dto.OAuthProvider;
import com.official.pium.member.domain.Member;
import com.official.pium.member.repository.MemberRepository;
import com.official.pium.petPlant.repository.PetPlantRepository;
import com.official.pium.sessionGroup.repository.SessionGroupRepository;
import com.official.pium.notification.application.dto.NotificationCheckResponse;
import com.official.pium.notification.application.dto.NotificationSubscribeRequest;
import com.official.pium.member.application.dto.OAuthProvider;
import com.official.pium.petPlant.domain.PetPlant;
import com.official.pium.petPlant.event.notification.NotificationEvent;
import com.official.pium.petPlant.repository.PetPlantRepository;
import com.official.pium.sessionGroup.repository.SessionGroupRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -24,6 +26,7 @@ public class MemberService {
private final PetPlantRepository petPlantRepository;
private final SessionGroupRepository sessionGroupRepository;
private final OAuthProvider provider;
private final ApplicationEventPublisher publisher;

@Transactional
public void withdraw(Member member) {
Expand Down Expand Up @@ -51,6 +54,16 @@ public void subscribeNotification(Member member, NotificationSubscribeRequest re
throw new IllegalArgumentException("이미 알림을 구독하고 있습니다.");
}
member.updateDeviceToken(request.getToken());
sendAlarmNotification(member);
}

private void sendAlarmNotification(Member member) {
NotificationEvent event = NotificationEvent.builder()
.deviceToken(member.getDeviceToken())
.title("알림 설정 완료")
.body("리마인더 알림 받기 성공")
.build();
publisher.publishEvent(event);
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.official.pium.member.ui;

import com.official.pium.member.domain.Member;
import com.official.pium.member.application.MemberService;
import com.official.pium.member.domain.Member;
import com.official.pium.notification.application.dto.NotificationCheckResponse;
import com.official.pium.notification.application.dto.NotificationSubscribeRequest;
import jakarta.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -42,8 +42,10 @@ public ResponseEntity<NotificationCheckResponse> checkNotificationStatus(@Auth M
}

@PostMapping("/notification")
public ResponseEntity<Void> subscribeNotification(@Auth Member member,
@RequestBody @Valid NotificationSubscribeRequest request) {
public ResponseEntity<Void> subscribeNotification(
@Auth Member member,
@RequestBody @Valid NotificationSubscribeRequest request
) {
memberService.subscribeNotification(member, request);
return ResponseEntity.ok().build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
package com.official.pium.notification.application;

import com.official.pium.petPlant.event.notification.NotificationEvent;
import com.official.pium.petPlant.event.notification.NotificationEvents;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
@Slf4j
public class NotificationEventListener {

private final NotificationService notificationService;

@EventListener
@Async
public void handleNotificationEvents(NotificationEvents notificationEvents) {
for (NotificationEvent event : notificationEvents.getNotificationEvents()) {
notificationService.sendNotification(event.getDeviceToken(), event.getTitle(), event.getBody());
}
public void handleNotificationEvent(NotificationEvent event) {
log.info("비동기 알림 START, Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
notificationService.sendNotification(event.getDeviceToken(), event.getTitle(), event.getBody());
log.info("비동기 알림 END, Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class NotificationService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@
import com.official.pium.petPlant.domain.PetPlant;
import com.official.pium.petPlant.event.history.HistoryEvent;
import com.official.pium.petPlant.event.notification.NotificationEvent;
import com.official.pium.petPlant.event.notification.NotificationEvents;
import com.official.pium.petPlant.repository.PetPlantRepository;
import java.time.LocalDate;
import java.util.List;
import java.util.NoSuchElementException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
Expand Down Expand Up @@ -81,27 +82,17 @@ public DataResponse<List<ReminderResponse>> readAll(Member member) {
@Scheduled(cron = "0 0 7 * * *")
public void sendWaterNotification() {
List<PetPlant> petPlants = petPlantRepository.findAllByWaterNotification(LocalDate.now());
List<NotificationEvent> events = petPlants.stream()
.map(plant -> NotificationEvent.builder()
.title(plant.getNickname())
.body("물을 줄 시간이에요!")
.deviceToken(plant.getMember().getDeviceToken())
.build()
).toList();

publisher.publishEvent(NotificationEvents.from(events));
log.info("[" + LocalDate.now() + " 물주기] " + "전체 알림: " + petPlants.size() + "개");
petPlants.forEach(this::sendNotification);

}

public void sendWaterNotificationTest() {
List<PetPlant> petPlants = petPlantRepository.findAll();
List<NotificationEvent> events = petPlants.stream()
.map(plant -> NotificationEvent.builder()
.title(plant.getNickname())
.body("(테스트 중) 물을 줄 시간이에요!")
.deviceToken(plant.getMember().getDeviceToken())
.build()
).toList();

publisher.publishEvent(NotificationEvents.from(events));
private void sendNotification(PetPlant petPlant) {
NotificationEvent.builder()
.title(petPlant.getNickname())
.body("물을 줄 시간이에요!")
.deviceToken(petPlant.getMember().getDeviceToken())
.build();
publisher.publishEvent(petPlant);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import com.official.pium.fixture.MemberFixture;
import com.official.pium.fixture.NotificationFixture;
import com.official.pium.notification.application.NotificationService;
import com.official.pium.petPlant.application.ReminderService;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.DisplayNameGeneration;
Expand Down Expand Up @@ -65,9 +64,6 @@ class AdminPageControllerTest extends UITest {
@MockBean
private NotificationService notificationService;

@MockBean
private ReminderService reminderService;

@Nested
class 페이지_정상_호출_ {

Expand Down
Loading

0 comments on commit 0f13d5d

Please sign in to comment.