Skip to content

Commit 8e63b2e

Browse files
authored
feat: 관리자 로그인 및 서포터 토큰 발급 이벤트를 발행하도록 알림 시스템을 이벤트 기반으로 리팩토링 (#405)
1 parent 9a2de7a commit 8e63b2e

15 files changed

Lines changed: 281 additions & 178 deletions

src/main/java/org/ject/support/domain/admin/service/AdminAuthService.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
import org.ject.support.domain.admin.exception.AdminException;
1010
import org.ject.support.domain.member.MemberStatus;
1111
import org.ject.support.domain.member.entity.Member;
12-
import org.ject.support.external.discord.DiscordComponent;
1312
import org.ject.support.external.infrastructure.DiscordRateLimiter;
13+
import org.ject.support.external.notification.event.AdminLoginNotificationEvent;
14+
import org.springframework.context.ApplicationEventPublisher;
1415
import org.springframework.data.redis.core.RedisTemplate;
1516
import org.springframework.security.core.Authentication;
1617
import org.springframework.stereotype.Service;
@@ -24,10 +25,10 @@
2425
@Transactional(readOnly = true)
2526
public class AdminAuthService {
2627

28+
private final ApplicationEventPublisher applicationEventPublisher;
2729
private final RedisTemplate<String, String> redisTemplate;
2830
private final AdminMemberComponent adminMemberComponent;
2931
private final DiscordRateLimiter discordRateLimiter;
30-
private final DiscordComponent discordComponent;
3132
private final JwtTokenProvider jwtTokenProvider;
3233

3334
private static final String ADMIN_LOGIN_AUTH_CODE_KEY_PREFIX = "admin-login:";
@@ -46,9 +47,9 @@ public String sendAdminAuthCode(String email) {
4647

4748
if (discordRateLimiter.tryConsume(1)) {
4849
redisTemplate.opsForValue().set(key, authCode, Duration.ofSeconds(ADMIN_LOGIN_AUTH_CODE_EXPIRATION));
49-
discordComponent.sendAdminLoginMessage(member.getEmail(), authCode)
50-
.doOnError(e -> log.error("Discord 전송 실패: {}", member.getEmail(), e))
51-
.subscribe();
50+
51+
applicationEventPublisher
52+
.publishEvent(new AdminLoginNotificationEvent(email, authCode));
5253
} else {
5354
throw new AdminException(AdminErrorCode.TOO_MANY_REQUESTS);
5455
}

src/main/java/org/ject/support/domain/auth/service/AuthSupporterTokenService.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import org.ject.support.domain.member.Role;
1212
import org.ject.support.domain.member.entity.Member;
1313
import org.ject.support.domain.member.repository.MemberRepository;
14-
import org.ject.support.external.discord.DiscordComponent;
14+
import org.ject.support.external.notification.event.SupporterTokenIssuedEvent;
15+
import org.springframework.context.ApplicationEventPublisher;
1516
import org.springframework.security.crypto.password.PasswordEncoder;
1617
import org.springframework.stereotype.Service;
1718
import org.springframework.transaction.annotation.Transactional;
@@ -28,10 +29,10 @@ public class AuthSupporterTokenService {
2829

2930
private final long TOKEN_EXPIRATION_MILLIS = 24 * 60 * 60 * 1000; // 1일
3031

32+
private final ApplicationEventPublisher applicationEventPublisher;
3133
private final JwtTokenProvider jwtTokenProvider;
3234
private final MemberRepository memberRepository;
3335
private final PasswordEncoder passwordEncoder;
34-
private final DiscordComponent discordComponent;
3536

3637
public void issueReadOnlyToken(String email, String pin) {
3738
Member member = memberRepository.findByEmailAndRole(email, Role.ADMIN)
@@ -50,10 +51,7 @@ public void issueReadOnlyToken(String email, String pin) {
5051

5152
String token = jwtTokenProvider.createToken(claims, TOKEN_EXPIRATION_MILLIS);
5253

53-
discordComponent.sendSupporterTokenIssueMessage(email, token)
54-
.doOnError(e -> {
55-
log.error("지원자 토큰 전송 실패: {}", email, e);
56-
})
57-
.subscribe();
54+
applicationEventPublisher
55+
.publishEvent(new SupporterTokenIssuedEvent(email, token));
5856
}
5957
}

src/main/java/org/ject/support/external/discord/DiscordComponent.java

Lines changed: 0 additions & 104 deletions
This file was deleted.

src/main/java/org/ject/support/external/discord/DiscordWebhookPayload.java

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/main/java/org/ject/support/external/notification/DiscordNotificationMessageFactory.java

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/main/java/org/ject/support/external/notification/NotificationMessageFactory.java

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.ject.support.external.notification.client;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.ject.support.external.notification.payload.DiscordWebhookPayload;
6+
import org.springframework.http.ResponseEntity;
7+
import org.springframework.stereotype.Component;
8+
import org.springframework.util.StringUtils;
9+
import org.springframework.web.reactive.function.client.WebClient;
10+
import reactor.core.publisher.Mono;
11+
12+
@Slf4j
13+
@Component
14+
@RequiredArgsConstructor
15+
public class DiscordClient {
16+
17+
private final WebClient webClient;
18+
19+
public Mono<Void> send(
20+
String webhookUrl,
21+
DiscordWebhookPayload payload
22+
) {
23+
if (!StringUtils.hasText(webhookUrl)) {
24+
log.warn("Discord webhook URL이 비어있어 메시지를 전송하지 않습니다.");
25+
return Mono.empty();
26+
}
27+
28+
return webClient.post()
29+
.uri(webhookUrl)
30+
.bodyValue(payload)
31+
.retrieve()
32+
.toBodilessEntity()
33+
.doOnSuccess(this::logSuccess)
34+
.doOnError(this::logError)
35+
.then();
36+
}
37+
38+
private void logSuccess(ResponseEntity<Void> response) {
39+
log.info("Discord message sent successfully (status={})", response.getStatusCode());
40+
}
41+
42+
private void logError(Throwable e) {
43+
log.error(
44+
"Discord message send failed: {} - {}",
45+
e.getClass().getSimpleName(),
46+
e.getMessage()
47+
);
48+
}
49+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.ject.support.external.notification.event;
2+
3+
public record AdminLoginNotificationEvent(
4+
String email,
5+
String code
6+
) {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.ject.support.external.notification.event;
2+
3+
public record SupporterTokenIssuedEvent(
4+
String email,
5+
String accessToken
6+
) {}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.ject.support.external.notification.executor;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.stereotype.Component;
5+
import reactor.core.publisher.Mono;
6+
7+
import java.time.Duration;
8+
9+
@Component
10+
@Slf4j
11+
public class NotificationExecutorService {
12+
13+
public void execute(Mono<Void> mono) {
14+
mono
15+
.retry(3)
16+
.timeout(Duration.ofSeconds(3))
17+
.doOnError(e -> log.error("Notification failed", e))
18+
.subscribe();
19+
}
20+
}

0 commit comments

Comments
 (0)