국비를 통해 처음 프로그래밍을 배우면서 팀프로젝트 진행하였습니다.
프로젝트의 기능은 잘 구현해서 해커톤 우수상을 받았지만, 코드는 당연히(?) 엉망이였습니다.
그리고 몇개월이 지난 지금... 리펙토링을 해보려고 합니다.
우선 SSE기능에대한 리펙토링을 해보려고 합니다.
사실 SSE는 SSE Emitter에 등록을 하고 삭제한는것을 빼면 전부 같은로직을 반복하는것입니다.
그래서 저는 이 기능을 재사용할 수 있는 코드로 리펙토링 하겠다고 생각하였습니다.
기능 영상

SSE의 기능
SSE를 사용하는 이유는 실시간으로 알림을 보내기 위해서입니다.
그리고 프로젝트에서 SSE는 알림이 발생했을때 (좋아요, 팔로우, 댓글)
사용자에게 알림이 전송되는 기능입니다.
즉, 메인 기능은 1가지입니다.
접근 방식
우선 재사용을 하는 방법은 2가지정도가 생각되었습니다.
Util이라는 폴더를 만들고 공통메서드를 작성해야합니다.
공통 메서드는 SendAlarmTOUser입니다
- Static을 활용해서 공통으로 사용하기
- Bean으로 생성하여 싱글톤으로 메서드 사용하기
저는 구현을 편하게 하기 위해 Static을 사용하여 리펙토링을 해보았습니다.
기존 코드
아래의 코드가 좋아요, 댓글, 팔로우를 할때마다 반복되고 있습니다.
//sse 로직
if (sseEmitters.containsKey(crew.getUser().getUsername())) {
log.info("userName이 Map으로 등록되어있어 알림 sse 작동됩니다.");
log.info("Sse username = {}", crew.getUser().getUsername());
SseEmitter sseEmitter = sseEmitters.get(crew.getUser().getUsername());
try {
sseEmitter.send(SseEmitter.event().name("alarm").data(
user.getNickName() + "님이 \"" + crew.getTitle() + "\" 모임에 좋아요를 눌렀습니다."));
} catch (Exception e) {
sseEmitters.remove(crew.getUser().getUsername());
}
}
- 메서드의 파라미터로 sseEmitters에 알림을 받을 유저가 있는지 확인합니다.
- 만약 유저가 있다면 sseEmitters에 등록되고있기 때문에 알림을 전송합니다.
- sseEmitter를 꺼내 알림받을 유저에게 전송합니다.
이 두가지 로직을 두개의 메서드로 분리하였습니다.
//sse 로직
if (isUserLogin(crew.getUser().getUsername())) {
SendAlarmToUser(user, crew);
}
그리고 Util에서 Static메서드로 구현을 하였습니다.
public class SseUtil {
public static boolean isUserLogin(final String username) {
return sseEmitters.containsKey(username);
}
public static void SendAlarmToUser(final User user, final Crew crew) {
SseEmitter sseEmitter = sseEmitters.get(crew.getUser().getUsername());
try {
sseEmitter.send(SseEmitter.event().name("alarm").data(
user.getNickName() + "님이 \"" + crew.getTitle() + "\" 모임에 좋아요를 눌렀습니다."));
} catch (Exception e) {
sseEmitters.remove(crew.getUser().getUsername());
}
}
}
메소드 오버로딩(overloading)
하지만 각 알림기능마다 세부구현이 조금씩 달랐습니다.
따라서 SendAlarmToUser에 매개변수나 구현이 조금씩변해야 했습니다.
이번에는 메소드 오버로딩을 활용해 모든 기능을 구현해 보았습니다.
public class SseUtil {
public static boolean isUserLogin(final String username) {
return sseEmitters.containsKey(username);
}
public static void SendAlarmToUser(final User user, final Crew crew, final String message) {
SseEmitter sseEmitter = sseEmitters.get(crew.getUser().getUsername());
try {
sseEmitter
.send(SseEmitter.event().name("alarm")
.data(user.getNickName() + "님이 \"" + crew.getTitle() + message));
} catch (Exception e) {
sseEmitters.remove(crew.getUser().getUsername());
}
}
public static void SendAlarmToUser(final User user, final Comment parentComment, final String message) {
SseEmitter sseEmitter = sseEmitters.get(parentComment.getUser().getUsername());
try {
sseEmitter
.send(SseEmitter.event()
.name("alarm")
.data(user.getNickName() + "님이 \"" + parentComment.getComment() + message));
} catch (Exception e) {
sseEmitters.remove(parentComment.getUser().getUsername());
}
}
public static void SendAlarmToUser(final User toUser, final String message) {
SseEmitter sseEmitter = sseEmitters.get(toUser.getUsername());
try {
sseEmitter
.send(SseEmitter.event()
.name("alarm")
.data(message));
} catch (Exception e) {
sseEmitters.remove(toUser.getUsername());
}
}
public static void SendAlarmToUser(final String toUser, final String message) {
SseEmitter sseEmitter = sseEmitters.get(toUser);
try {
sseEmitter
.send(SseEmitter.event().name("alarm")
.data(message));
} catch (Exception e) {
sseEmitters.remove(toUser);
}
}
}
Static vs Bean
Static 메서드는 애플리케이션이 시작될때 메모리에 올라갑니다.
면 Bean은 스프링 Ioc에서 관리합니다.
보안할점
SseSender안에서 Static으로 메서드를 사용하니 각 기능마다 알림전송의 세부구현이 달라 구현이 힘들었습니다.
해결 방안으로 메서드 오버라이딩을 하여 SseSender안에 알림전송기능을 모았습니다.
하지만 알림전송을 재사용하고자 Static 메서드로 만들었는데
메서드 오버라이딩을 많이하여 가독성이 떨어지는 느낌을 받았습니다.
보안방법
앞으로 알림기능이 추가되거나 변경되면 내부구현을 전부 바꿔야합니다.
분명 전에 각기능 Service에서 구현한 알림전송기능보다는 좋지만, 여전히 변화에 유연하지 않다고 느껴졌습니다.
생각해보니 디자인패턴의 전략패턴을 사용하면 될것같습니다.
알림전송을 전략으로 각 기능마다 세부전략을 만들어서 관리하면 변화에 유연하게 대쳐할 수 있고
코드 가독성 또한 좋아질것이라고 판단하였습니다.
다음 리펙토링에는 전략패턴을 사용해서 코드의 유연성과 가독성을 높여보겠습니다.
프로젝트 당시 SSE 선택 및 구현에 대한 글정리
Sse를 활용해 실시간 알림기능 어떤기술을 사용해야할까?
팀프로젝트를 진행하면서 로그인, Oauth, 알림기능을 구현하였습니다.알림기능을 구현하다보니 순간 실시간 알림이 있으면 좋겠는데?라는 생각을 하였고 실시간 알림을 구현하기 위한 방법이 여
velog.io
실시간 알림기능 SSE로 구현하기
앞서 실시간 알림기능을 SSE로 구현하기로 하였습니다.이번글에서는 실시간 알림 기능을 구현해 보겠습니다.서버단의 로직은 생각보다 복잡하여 Front먼저 살펴보겠습니다.클라이언트에서 서버
velog.io
'Project > 팀프로젝트 - 운동메이트' 카테고리의 다른 글
Sse를 활용해 실시간 알림기능 어떤기술을 사용해야할까? (0) | 2023.09.19 |
---|---|
[Refectoring] Sse를 Strategy Pattern -> 컴포넌트(Bean)으로 리펙토링! (0) | 2023.09.19 |
국비를 통해 처음 프로그래밍을 배우면서 팀프로젝트 진행하였습니다.
프로젝트의 기능은 잘 구현해서 해커톤 우수상을 받았지만, 코드는 당연히(?) 엉망이였습니다.
그리고 몇개월이 지난 지금... 리펙토링을 해보려고 합니다.
우선 SSE기능에대한 리펙토링을 해보려고 합니다.
사실 SSE는 SSE Emitter에 등록을 하고 삭제한는것을 빼면 전부 같은로직을 반복하는것입니다.
그래서 저는 이 기능을 재사용할 수 있는 코드로 리펙토링 하겠다고 생각하였습니다.
기능 영상

SSE의 기능
SSE를 사용하는 이유는 실시간으로 알림을 보내기 위해서입니다.
그리고 프로젝트에서 SSE는 알림이 발생했을때 (좋아요, 팔로우, 댓글)
사용자에게 알림이 전송되는 기능입니다.
즉, 메인 기능은 1가지입니다.
접근 방식
우선 재사용을 하는 방법은 2가지정도가 생각되었습니다.
Util이라는 폴더를 만들고 공통메서드를 작성해야합니다.
공통 메서드는 SendAlarmTOUser입니다
- Static을 활용해서 공통으로 사용하기
- Bean으로 생성하여 싱글톤으로 메서드 사용하기
저는 구현을 편하게 하기 위해 Static을 사용하여 리펙토링을 해보았습니다.
기존 코드
아래의 코드가 좋아요, 댓글, 팔로우를 할때마다 반복되고 있습니다.
//sse 로직
if (sseEmitters.containsKey(crew.getUser().getUsername())) {
log.info("userName이 Map으로 등록되어있어 알림 sse 작동됩니다.");
log.info("Sse username = {}", crew.getUser().getUsername());
SseEmitter sseEmitter = sseEmitters.get(crew.getUser().getUsername());
try {
sseEmitter.send(SseEmitter.event().name("alarm").data(
user.getNickName() + "님이 \"" + crew.getTitle() + "\" 모임에 좋아요를 눌렀습니다."));
} catch (Exception e) {
sseEmitters.remove(crew.getUser().getUsername());
}
}
- 메서드의 파라미터로 sseEmitters에 알림을 받을 유저가 있는지 확인합니다.
- 만약 유저가 있다면 sseEmitters에 등록되고있기 때문에 알림을 전송합니다.
- sseEmitter를 꺼내 알림받을 유저에게 전송합니다.
이 두가지 로직을 두개의 메서드로 분리하였습니다.
//sse 로직
if (isUserLogin(crew.getUser().getUsername())) {
SendAlarmToUser(user, crew);
}
그리고 Util에서 Static메서드로 구현을 하였습니다.
public class SseUtil {
public static boolean isUserLogin(final String username) {
return sseEmitters.containsKey(username);
}
public static void SendAlarmToUser(final User user, final Crew crew) {
SseEmitter sseEmitter = sseEmitters.get(crew.getUser().getUsername());
try {
sseEmitter.send(SseEmitter.event().name("alarm").data(
user.getNickName() + "님이 \"" + crew.getTitle() + "\" 모임에 좋아요를 눌렀습니다."));
} catch (Exception e) {
sseEmitters.remove(crew.getUser().getUsername());
}
}
}
메소드 오버로딩(overloading)
하지만 각 알림기능마다 세부구현이 조금씩 달랐습니다.
따라서 SendAlarmToUser에 매개변수나 구현이 조금씩변해야 했습니다.
이번에는 메소드 오버로딩을 활용해 모든 기능을 구현해 보았습니다.
public class SseUtil {
public static boolean isUserLogin(final String username) {
return sseEmitters.containsKey(username);
}
public static void SendAlarmToUser(final User user, final Crew crew, final String message) {
SseEmitter sseEmitter = sseEmitters.get(crew.getUser().getUsername());
try {
sseEmitter
.send(SseEmitter.event().name("alarm")
.data(user.getNickName() + "님이 \"" + crew.getTitle() + message));
} catch (Exception e) {
sseEmitters.remove(crew.getUser().getUsername());
}
}
public static void SendAlarmToUser(final User user, final Comment parentComment, final String message) {
SseEmitter sseEmitter = sseEmitters.get(parentComment.getUser().getUsername());
try {
sseEmitter
.send(SseEmitter.event()
.name("alarm")
.data(user.getNickName() + "님이 \"" + parentComment.getComment() + message));
} catch (Exception e) {
sseEmitters.remove(parentComment.getUser().getUsername());
}
}
public static void SendAlarmToUser(final User toUser, final String message) {
SseEmitter sseEmitter = sseEmitters.get(toUser.getUsername());
try {
sseEmitter
.send(SseEmitter.event()
.name("alarm")
.data(message));
} catch (Exception e) {
sseEmitters.remove(toUser.getUsername());
}
}
public static void SendAlarmToUser(final String toUser, final String message) {
SseEmitter sseEmitter = sseEmitters.get(toUser);
try {
sseEmitter
.send(SseEmitter.event().name("alarm")
.data(message));
} catch (Exception e) {
sseEmitters.remove(toUser);
}
}
}
Static vs Bean
Static 메서드는 애플리케이션이 시작될때 메모리에 올라갑니다.
면 Bean은 스프링 Ioc에서 관리합니다.
보안할점
SseSender안에서 Static으로 메서드를 사용하니 각 기능마다 알림전송의 세부구현이 달라 구현이 힘들었습니다.
해결 방안으로 메서드 오버라이딩을 하여 SseSender안에 알림전송기능을 모았습니다.
하지만 알림전송을 재사용하고자 Static 메서드로 만들었는데
메서드 오버라이딩을 많이하여 가독성이 떨어지는 느낌을 받았습니다.
보안방법
앞으로 알림기능이 추가되거나 변경되면 내부구현을 전부 바꿔야합니다.
분명 전에 각기능 Service에서 구현한 알림전송기능보다는 좋지만, 여전히 변화에 유연하지 않다고 느껴졌습니다.
생각해보니 디자인패턴의 전략패턴을 사용하면 될것같습니다.
알림전송을 전략으로 각 기능마다 세부전략을 만들어서 관리하면 변화에 유연하게 대쳐할 수 있고
코드 가독성 또한 좋아질것이라고 판단하였습니다.
다음 리펙토링에는 전략패턴을 사용해서 코드의 유연성과 가독성을 높여보겠습니다.
프로젝트 당시 SSE 선택 및 구현에 대한 글정리
Sse를 활용해 실시간 알림기능 어떤기술을 사용해야할까?
팀프로젝트를 진행하면서 로그인, Oauth, 알림기능을 구현하였습니다.알림기능을 구현하다보니 순간 실시간 알림이 있으면 좋겠는데?라는 생각을 하였고 실시간 알림을 구현하기 위한 방법이 여
velog.io
실시간 알림기능 SSE로 구현하기
앞서 실시간 알림기능을 SSE로 구현하기로 하였습니다.이번글에서는 실시간 알림 기능을 구현해 보겠습니다.서버단의 로직은 생각보다 복잡하여 Front먼저 살펴보겠습니다.클라이언트에서 서버
velog.io
'Project > 팀프로젝트 - 운동메이트' 카테고리의 다른 글
Sse를 활용해 실시간 알림기능 어떤기술을 사용해야할까? (0) | 2023.09.19 |
---|---|
[Refectoring] Sse를 Strategy Pattern -> 컴포넌트(Bean)으로 리펙토링! (0) | 2023.09.19 |