이벤트 기반 아키텍처를 적용해서 느슨한 결합(Coupling)을 만들어 보자
📖 서론
1. 결합도가 높은 login() 메서드 작성하기
결합도가 높은 login() 메서드 코드 분석하기
하단의 login() 메서드에서 처리하는 주관심사와 외부 관심사를 분리시켜 보자. 여기서 주관심사는 사용자의 로그인 과정이며, 외부 관심사(비관심사)는 로그인 후의 추가 작업들(예: 알림 보내기, 타 기기에서의 로그아웃 처리 등)다.
이것들은 모두 login() 메서드 내부에 존재하며 하나의 트랜잭션으로 묶여있는 상황이다.
login() 메서드는 주관심사와 외부 관심사가 혼재되어 있어, 한 부분의 변경이 전체 메서드에 영향을 미칠 수 있다. 예를 들어, 로그인 알림을 보내는 로직을 수정하려면, 사용자 인증 로직이 포함된 login() 메서드 전체를 이해하고 사이드 이팩트를 고려하며 수정을 진행해야 한다. 이는 코드의 유지보수를 복잡하게 만들고, 시스템의 확장성을 제한할 수 있다.
이는 결합도가 높은 설계의 전형적인 문제점으로, 각 기능이나 로직이 서로 밀접하게 연결되어 있어서 한 부분의 수정이 다른 부분에 예상치 못한 영향을 미치는 경우가 발생할 수 있다. 이를 해결하기 위해선 주관심사와 외부 관심사를 분리하여, 각각을 독립적으로 관리할 수 있는 설계 방식을 적용시켜줘야 한다.
@Transactional
@Service
public class UserService {
public void login(String username, String password) {
// 사용자 인증 로직
authenticateUser(username, password);
// 외부 관심사
sendLoginNotification(username); // 로그인 알림 보내기
logoutOtherDevices(username); // 다른 기기에서 로그아웃 처리
}
private void authenticateUser(String username, String password) {
// 실제 인증 로직
}
private void sendLoginNotification(String username) {
// 로그인 알림 로직
}
private void logoutOtherDevices(String username) {
// 다른 기기에서의 로그아웃 로직
}
}
이 메서드는 여러 책임을 가지고 있으며 이러한 설계에서는 다음과 같은 문제가 발생할 수 있다.
1. 변경에 대한 영향
메서드 내의 한 부분을 변경하려 할 때, 다른 부분에도 영향을 미칠 가능성이 높다. 예를 들어, 알림 로직을 수정하려면 사용자 인증 로직도 이해해야 하고, 이 과정에서 실수로 인증 로직에 부정적인 영향을 미칠 수 있다.
2. 유지보수의 어려움
모든 로직이 하나의 메서드에 포함되어 있기 때문에, 이 메서드는 점점 더 복잡해지고 이해하기 어려워진다. 이는 유지보수를 어렵게 만들며, 새로운 기능을 추가하거나 기존 기능을 수정하는 데 더 많은 시간과 노력이 필요하게 된다.
3. 재사용성의 부족
메서드가 여러 책임을 가지고 있기 때문에, 그 중 일부 기능만을 다른 곳에서 재사용하기 어려워진다. 예를 들어, 로그인 알림 기능만을 다른 컨텍스트에서 재사용하고 싶어도, 전체 login() 메서드의 로직과 결합되어 있기 때문에 분리하기가 어렵다.
2. 이벤트 기반 아키텍처를 적용하여 느슨한 결합 만들기
이벤트 기반 아키텍처 적용하기
이벤트 기반 아키텍처를 사용하면, 로그인 후 처리되는 외부 관심사들을 이벤트로 분리할 수 있다. 이를 통해 login() 메서드는 주관심사인 사용자 인증에만 집중할 수 있으며, 나머지 외부 관심사는 별도의 이벤트 리스너에서 처리된다.
이벤트 기반 아키텍처의 핵심은 컴포넌트 간의 의사소통을 이벤트 기반으로 처리하는 것이다. 컴포넌트들은 필요한 이벤트를 발행하거나, 관련된 이벤트를 구독함으로써 서로 정보를 교환한다. 이 과정에서 각 컴포넌트는 다른 컴포넌트의 구체적인 구현 세부사항을 몰라도 되므로, 시스템의 결합도가 현저하게 낮아진다. 이로 인해, 시스템의 한 부분을 변경하더라도 다른 부분에 미치는 영향이 최소화되며, 이는 시스템을 더욱 유연하고 확장 가능하게 만든다.
login 메서드를 수정하여 느슨한 결합 만들기
수정된 메서드는 주관심사인 사용자 인증 로직(login)을 호출한 후에 스프링 이벤트를 발행하고 메서드를 종료한다. 그럼 이벤트 리스너가 동작하여 비관심사 로직들을 처리하게 된다.
@Transactional
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void login(String username, String password) {
// 사용자 인증 로직
authenticateUser(username, password);
// 이벤트 발행
eventPublisher.publishEvent(new UserLoginEvent(username));
}
}
이벤트 클래스 작성하기
public class UserLoginEvent {
private String username;
}
이벤트 리스너 구현하기
아래와 같이 이벤트 리스너를 구현하면 UserLoginEvent가 발생할 때마다 로그인 알림과 타 기기 로그아웃 로직을 독립적으로 처리한다. 이는 UserService에 존재했던 로직(비관심사)들을 제거함으로써, UserService가 사용자 인증(주관심사) 로직에만 집중할 수 있게 해 준다.
@Component
public class LoginEventListener {
// 로그인 알림을 처리하는 이벤트 리스너
@EventListener
public void sendLoginNotification(UserLoginEvent event) {
sendLoginNotification(event.getUsername());
}
// 타 기기에서 로그아웃시키는 이벤트 리스너
@EventListener
public void logoutOtherDevice(UserLoginEvent event) {
logoutOtherDevices(event.getUsername());
}
}
이 코드의 흐름은 다음과 같다.
1. 메서드 내 주관심사 처리
login() 메서드는 사용자 인증과 같은 주관심사인 핵심 로직을 처리한다. 이는 로그인 과정의 가장 중요한 부분이며, 메서드의 주된 목적에 해당한다.
2. 스프링 이벤트 발행
주관심사를 처리한 후, 메서드는 스프링 이벤트를 발행한다. 이 이벤트는 로그인 과정이 성공적으로 완료되었음을 시스템의 다른 부분에 알리는 신호 역할을 한다.
3. 이벤트 리스너 활성화
이벤트가 발행되면, 이에 반응하여 이벤트 리스너가 활성화된다. 이 리스너는 로그인 후 처리해야 하는 비관심사 로직들, 예를 들어 로그인 알림 전송이나 다른 기기에서의 로그아웃 처리 등을 수행한다.
4. 독립적인 로직 처리
이벤트 리스너에서 처리되는 로직들은 login() 메서드의 로직과 독립적이다. 따라서, 이러한 비관심사 로직들을 수정하거나 업데이트할 때 login() 메서드에 영향을 미치지 않으며, 이는 결합도를 낮추는 데 기여한다.
이벤트 기반 아키텍처를 적용하여, UserService의 기능을 재구성했다. 이제 이 클래스는 로그인 프로세스의 핵심 로직에만 집중하고, 로그인 이후의 추가 작업들은 LoginEventListener를 통해서 독립적으로 처리하게 변경되었다.
이러한 변화는 각 클래스의 역할을 명확히 하여, 결합도를 효과적으로 낮추고, 시스템의 유연성을 향상시킨다. 결과적으로, 각 컴포넌트는 더 높은 재사용성과 유지보수성을 가지게 되었다.
아래의 포스트를 읽고 결합도에 대해 이해를 하고 지금 이 글을 다시 읽는다면 더 깊은 이해가 될 것이다.👇🏻👇🏻
'Spring 기초 > Spring 기초 지식' 카테고리의 다른 글
[Spring] @ModelAttribute 바인딩 실패와 해결 (33) | 2024.02.01 |
---|---|
[Spring] 톰캣과 스프링: 웹 요청의 라이프사이클 이해하기 (27) | 2023.12.31 |
주니어 개발자의 결합도(Coupling) 이해하기: 스프링에서 결합도 관리하기 (2) | 2023.12.25 |
[Spring] RuntimeException의 메시지 (0) | 2023.12.21 |
가볍게 알아보는 디자인 패턴 - 퍼사드 패턴(Facade Pattern) (3) | 2023.12.11 |