Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#106 [RESTRUCTURING] : 이벤트를 활용한 fcm push 알림 구현 #122

Merged
merged 8 commits into from
Apr 22, 2024

Conversation

hye-on
Copy link
Contributor

@hye-on hye-on commented Apr 18, 2024

☀️ 작업 사항

  • fcm push알림을 이벤트를 활용해서 구현하였습니다.

☀️ 관련 이슈

resolved : #106

☀️ 참고 사항

fcm 전송실패에는 아래와 같은 이유들이 있습니다. (코드 참고)
UNREGISTERED 한 토큰만 삭제를 하게 구현하였습니다.
나머지 토큰들은 잘못된 토큰으로 인해 발생하는 것이 아닐 수도 있기 때문에 삭제 전에 조사 및 수정 시도가 필요하다고 생각하여 로그로 남기고 삭제하지 않았습니다.

package com.google.firebase.messaging;

/**
 * 클라우드 메시징 API에서 발생할 수 있는 오류 코드들입니다.
 */
public enum MessagingErrorCode {

  /**
   * APNs 인증서 또는 웹 푸시 인증 키가 잘못되었거나 누락되었습니다.
   */
  THIRD_PARTY_AUTH_ERROR,

  /**
   * 요청에 지정된 하나 이상의 인수가 잘못되었습니다.
   */
  INVALID_ARGUMENT,

  /**
   * 내부 서버 오류입니다.
   */
  INTERNAL,

  /**
   * 메시지 대상에 대한 발송 제한을 초과했습니다.
   */
  QUOTA_EXCEEDED,

  /**
   * 인증된 발신자 ID가 등록 토큰의 발신자 ID와 다릅니다.
   */
  SENDER_ID_MISMATCH,

  /**
   * 클라우드 메시징 서비스가 일시적으로 사용 불가능합니다.
   */
  UNAVAILABLE,

  /**
   * 앱 인스턴스가 FCM에서 등록 해제되었습니다. 이는 사용된 토큰이 더 이상 유효하지 않고 새로운 토큰을 사용해야 함을 의미합니다.
   */
  UNREGISTERED,
}

@hye-on hye-on requested a review from 05AM April 18, 2024 12:47
@hye-on hye-on added D-2 2일 전 까지 리뷰해주세요 RESTRUCTURING 코드베이스를 정리하거나 구조를 바꾸는 모든 작업 labels Apr 18, 2024
@hye-on hye-on self-assigned this Apr 18, 2024
Copy link
Member

@05AM 05AM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구현하신 방식에 대해 몇가지 궁금한 점이 있습니다.
주로 저희가 같이 스터디한 책 DDD start의 이벤트 관련 예제 코드를 변형하여 반영하신 이유가 궁금합니다.

  1. 이벤트의 부모 클래스인 Event를 만들지 않은 이유
  2. EventPublisher를 선언하고 관련 메서드를 정의하는 Events 클래스를 만들지 않고 이벤트가 일어나는 서비스 클래스들의 인스턴스로 선언한 이유

public class NotificationEventPublisher {
private final ApplicationEventPublisher eventPublisher;

public void publishNotificationSendEvent(NotificationSendVo notificationSendVo) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P5: 해당 메서드의 인수에서 ~event 클래스를 받지 않는데 특별히 이렇게 구현하신 이유가 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤 것이 궁금하신가요??
NotificationSendVo의 이름을 NotificationSendEvent로 하지 않은 이유가 궁금하신지
아니면 NotificationEventPublisher의 구현이 궁금하신지 알려주시면 좋을 것 같습니다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eventPublisher는 event를 인수로 받는다고 알고 있는데 따로 event를 만들지 않고 Vo를 사용하신 이유 (재사용인지는 모르겠습니다)가 궁금합니다.

Copy link
Contributor Author

@hye-on hye-on Apr 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내부 구현에 들어가면 오브젝트를 인자로 받을 수 있습니다! 원래 이름이 NotificationSendVo이였는데 변경할 생각을 못했습니다. NotificationSendEvent으로 변경해도 좋을 것 같네요!

	void publishEvent(Object event);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 내부에서는 Object를 받지만 해당 클래스를 vo로 사용할 것인지 event로 사용할것인지 명확하게 하기 위해 클래스 이름을 변경하는 것이 좋아보입니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 수정하겠습니다 :)

@hye-on
Copy link
Contributor Author

hye-on commented Apr 21, 2024

  1. 이벤트의 부모 클래스인 Event를 만들지 않은 이유

책에서 구현된 것처럼 (createdAt을 공통으로 가짐) 부모클래스를 만들 이유가 없다고 생각했습니다.
현재 push빼고 이벤트를 도입하자고 한 부분이 없는데 상속을 사용해서 미리 공통으로 가질 필드를 정하면
자식클래스가 부모 클래스에 강하게 결합되기 때문에 설계가 유연하지 못하다고 생각합니다.

만약에 한 도메인에서 여러 이벤트가 생기는데 공통되는 필드가 필요하다면 그 도메인을 위한 부모클래스를 만들어서 사용하면 좋을 것 같습니다.


  1. EventPublisher를 선언하고 관련 메서드를 정의하는 Events 클래스를 만들지 않고 이벤트가 일어나는 서비스 클래스들의 인스턴스로 선언한 이유

Events의 역할을 NotificationEventPublisher가 하고 있습니다!

image

package com.wakeUpTogetUp.togetUp.api.notification;

import com.wakeUpTogetUp.togetUp.api.notification.vo.NotificationSendVo;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class NotificationEventPublisher {
    private final ApplicationEventPublisher eventPublisher;

    public void publishNotificationSendEvent(NotificationSendVo notificationSendVo) {
        eventPublisher.publishEvent(notificationSendVo);
    }
}
public class Events {

  private static ApplicationEventPublisher publisher;

  static void setPublisher(ApplicationEventPublisher publisher) {
    Events.publisher = publisher;
  }

  public static void raise(Object event) {
    if (publisher != null) {
      publisher.publishEvent(event);//이벤트 발생
    }
  }

}

fcmToken에 대해서는 FcmTokenEventPublisher 를 따로 만들이유가 없다고 생각하여 fcmService에 바로
private static ApplicationEventPublisher publisher 을 선언했습니다.
추가로 유저에 대한 이벤트가 생긴다면 UserEventPublisher를 만들어 사용해도 좋을 것 같습니다.

@05AM
Copy link
Member

05AM commented Apr 21, 2024

책에서 구현된 것처럼 (createdAt을 공통으로 가짐) 부모클래스를 만들 이유가 없다고 생각했습니다. 현재 push빼고 이벤트를 도입하자고 한 부분이 없는데 상속을 사용해서 미리 공통으로 가질 필드를 정하면 자식클래스가 부모 클래스에 강하게 결합되기 때문에 설계가 유연하지 못하다고 생각합니다.

만약에 한 도메인에서 여러 이벤트가 생기는데 공통되는 필드가 필요하다면 그 도메인을 위한 부모클래스를 만들어서 사용하면 좋을 것 같습니다.

제 개인적인 생각으로는 이벤트에 공통 필드로 현재 시간을 저장하는 것이 좋을 것이라고 생각이 드는데요, 그 이유는 이벤트는 비동기로 처리되기 때문에 성공하지 못해도 클라이언트에는 성공했다는 응답이 갈텐데,
이후에 어떤 것이 실패했는지, 언제 실패했는지 알기 위해서는 이벤트가 발생한 시간을 정확히 아는 것이 중요할 것 같습니다.

그래서 이벤트가 실패할 경우에 발생시킬 custom exception을 만들고, 해당 exception에 맞는 조치를 취할 수 있도록 처리하는 것도 좋을 것 같습니다.

Events의 역할을 NotificationEventPublisher가 하고 있습니다!

image

책에서는 ApplicationEventPublisher 를 인스턴스 변수로 가진 하나의 Events 클래스를 정의하고 따로 다른 publisher를 정의하고 있지 않은데요, 사실 Events 내부의 필드와 메서드들은 이벤트를 출판하는 공통의 코드고, 인수로 들어오는 event만 달라지기 때문에 (그래서 Object로 선언)

각 서비스마다 publisher와 handler를 따로 선언하는 것이 아니라, 책에서처럼 공통으로 처리하는 ApplicationEventPublisher를 주입받는 Events 클래스를 두고, handler만 각 상황에 맞게 정의하는 것이 좋아 보이는데 어떻게 생각하시나요?

@hye-on
Copy link
Contributor Author

hye-on commented Apr 21, 2024

제 개인적인 생각으로는 이벤트에 공통 필드로 현재 시간을 저장하는 것이 좋을 것이라고 생각이 드는데요, 그 이유는 이벤트는 비동기로 처리되기 때문에 성공하지 못해도 클라이언트에는 성공했다는 응답이 갈텐데,
이후에 어떤 것이 실패했는지, 언제 실패했는지 알기 위해서는 이벤트가 발생한 시간을 정확히 아는 것이 중요할 것 같습니다.

그래서 이벤트가 실패할 경우에 발생시킬 custom exception을 만들고, 해당 exception에 맞는 조치를 취할 수 있도록 처리하는 것도 좋을 것 같습니다.

이벤트가 발생한 시간을 정확히 알아야한다는 것에 동의합니다.
이벤트의 성격에 따라 또 도메인의 특성에 따라 실패 시 처리가 달라질 수 있을 것 같습니다.
(손실, 재시도, 저장소 유무 등등)
그래서 push의 경우 실패시 처리를, pr에 적은 것처럼 UNREGISTERED 한 토큰을 삭제하는 이벤트를 구현하고 다른 에러는 로그를 찍었습니다..

그리고 custom exception을 만들어서 어떤 exception에 대응하면 좋을까요?? 어떤 조치를 취할 수 있는지 궁금합니다!
(개인적으로 UNREGISTERED의 토큰을 삭제하고 다른 에러는 로그를 찍는 것으로 충분하다고 생각했습니다.)

2024-04-21 20:38:25.889  WARN 148580 --- [nio-9010-exec-1] c.w.togetUp.infra.fcm.FcmService         : [PushException][sdfafda]INVALID_ARGUMENT The registration token is not a valid FCM registration token
log.warn("[PushException][{}]{} {}",
token,
errorCode,
errorMessage);

책에서는 ApplicationEventPublisher 를 인스턴스 변수로 가진 하나의 Events 클래스를 정의하고 따로 다른 publisher를 정의하고 있지 않은데요, 사실 Events 내부의 필드와 메서드들은 이벤트를 출판하는 공통의 코드고, 인수로 들어오는 event만 달라지기 때문에 (그래서 Object로 선언)
각 서비스마다 publisher와 handler를 따로 선언하는 것이 아니라, 책에서처럼 공통으로 처리하는 ApplicationEventPublisher를 주입받는 Events 클래스를 두고, handler만 각 상황에 맞게 정의하는 것이 좋아 보이는데 어떻게 생각하시나요?

책에 있는 코드를 바로 가져오기 보다는 실제로 어떻게 적용되고 있나 다른 레포를 참고했습니다.
도메인 별로 응집도를 높이고 유연하다고 생각하여 참고했습니다.
개인적으로 어떤 이벤트들을 어떻게 처리할지 논의하지 않은 상태에서 상속을 이용하는 것은 오히려 유연하지 않고 변경이 어려워질 수 있다고 생각합니다.

아니면 각 서비스마다 각 서비스마다 publiser를 두지 않고 이벤트를 발행하는 서비스에 ApplicationEventPublisher를 선언하는 것은 어떨까요??(Event패키지를 따로 만들지 않고 각 도메인에 핸들러를 두면 좋을 것 같습니다. )

@05AM
Copy link
Member

05AM commented Apr 22, 2024

이벤트가 발생한 시간을 정확히 알아야한다는 것에 동의합니다. 이벤트의 성격에 따라 또 도메인의 특성에 따라 실패 시 처리가 달라질 수 있을 것 같습니다. (손실, 재시도, 저장소 유무 등등) 그래서 push의 경우 실패시 처리를, pr에 적은 것처럼 UNREGISTERED 한 토큰을 삭제하는 이벤트를 구현하고 다른 에러는 로그를 찍었습니다..

Event 부모 클래스 구현에 대한 것은 그 필요성이 크지 않다고 생각하지만, 아래 메서드 구현 시 Object 대신에 부모 클래스를 인수로 넣어주도록 하여 event만 들어갈 수 있게 강제할 수 있을 것 같습니다.

  public static void raise(Object event) {
    if (publisher != null) {
      publisher.publishEvent(event);//이벤트 발생
    }
  }

살펴보니 현재 로그가 발생 시간까지 찍히도록 세팅되어 있어 현재로써 생각하기에는 이벤트의 부모 클래스는 필수적이진 않은 것 같습니다!

그리고 custom exception을 만들어서 어떤 exception에 대응하면 좋을까요?? 어떤 조치를 취할 수 있는지 궁금합니다! (개인적으로 UNREGISTERED의 토큰을 삭제하고 다른 에러는 로그를 찍는 것으로 충분하다고 생각했습니다.)

현재는 이벤트에서 에러가 발생했을 때 위에서 코드 예제를 가져와주신 것처럼 log.warn으로 찍고 있는데, 클린 코드에서 예외 처리와 로직을 분리하라고 한 것처럼 두 코드를 분리할 필요성이 있어보입니다.
저희 프로젝트에서 exception을 handling하고 있는 클래스는 global exception 핸들러로, 특정 예외 발생시 저희가 custom하여 정의한 예외를 정의하여 원하는 형식과 데이터를 가지고 로그를 남기는 것이 가능할 것 같습니다.

책에 있는 코드를 바로 가져오기 보다는 실제로 어떻게 적용되고 있나 다른 레포를 참고했습니다. 도메인 별로 응집도를 높이고 유연하다고 생각하여 참고했습니다. 개인적으로 어떤 이벤트들을 어떻게 처리할지 논의하지 않은 상태에서 상속을 이용하는 것은 오히려 유연하지 않고 변경이 어려워질 수 있다고 생각합니다.

아니면 각 서비스마다 각 서비스마다 publiser를 두지 않고 이벤트를 발행하는 서비스에 ApplicationEventPublisher를 선언하는 것은 어떨까요??(Event패키지를 따로 만들지 않고 각 도메인에 핸들러를 두면 좋을 것 같습니다. )

책의 코드를 바로 가져오지 않고 참고하신 것은 좋은 습관이라고 생각합니다. 그렇지만 책에서 구현한 방법도 그 나름의 이유도 있다고 생각하는데,

여기서 ApplicationEventPublisher를 이벤트를 사용하는 서비스마다 매번 선언하는 것은 코드 중복을 늘린다고 생각합니다. 공통의 클래스에 한번에 정의하고 static 메서드를 구현하여 이벤트 발생할 필요가 있는 곳마다 사용한다면 편리할 것 같습니다.

또한 개인적으로 책의 Events라는 클래스명에 공감이 잘 안가는데, EventPublisher 등의 이름으로 선언하면 좋을 것 같은데 이에 대해 어떻게 생각하시나요?

@hye-on
Copy link
Contributor Author

hye-on commented Apr 22, 2024

Event 부모 클래스 구현에 대한 것은 그 필요성이 크지 않다고 생각하지만, 아래 메서드 구현 시 Object 대신에 부모 클래스를 인수로 넣어주도록 하여 event만 들어갈 수 있게 강제할 수 있을 것 같습니다.

인수 통일 만을 위해 안티패턴인 상속을 사용하는 것은 장점보다 단점이 많을 것 같습니다. 위에 리뷰로 남겨주신 Object로 반영하겠습니다!

현재는 이벤트에서 에러가 발생했을 때 위에서 코드 예제를 가져와주신 것처럼 log.warn으로 찍고 있는데, 클린 코드에서 예외 처리와 로직을 분리하라고 한 것처럼 두 코드를 분리할 필요성이 있어보입니다. 저희 프로젝트에서 exception을 handling하고 있는 클래스는 global exception 핸들러로, 특정 예외 발생시 저희가 custom하여 정의한 예외를 정의하여 원하는 형식과 데이터를 가지고 로그를 남기는 것이 가능할 것 같습니다.

클린코드는 소프트웨어 전반적인 이야기를 다루기 때문에 전체적인 상황을 고려하여 종합적으로 설계하면 좋을 것 같습니다.

MessagingErrorCode errorCode = responses.get(idx).getException().getMessagingErrorCode();
ntStream.range(0, responses.size())
                        .filter(idx -> !responses.get(idx).isSuccessful())
                        .forEach(idx -> {
//로직 생략
//각각의 에러코드 로그 남기기 로직 등

});

토큰 배열을 각각 조회해서 에러코드를 가져옵니다. 따라서 토큰마다 에러 이유가 다를 수 있습니다.
그리고 api로 전체 유저에게 노티를 (공지) 보내는 기능도 있지만 미션을 성공했을 때 방의 유저들에게 노티를 보내는 기능처럼 서버에서 일어나는 이벤트 즉 도메인의 행위로 인해 push를 보내는 일도 있습니다.
아바타가 해금되거나 미션이 성공했을 때 외부 오류(firebase)로 인해 클라이언트에게 push에러를 보내주는 것은 좋지 않다고 생각합니다.
다음 에러 코드를 보면 클라이언트에서 대응해야 하는 코드는 없다고 생각합니다.
(물론 서버에서 대응할 수 있는 코드도 많지 않은 것 같습니다. 그렇기에 서버에서 대응하지 못한 에러코드를 클라이언트에 보내주는 것은 지양하면 좋을 것 같습니다.)

public enum MessagingErrorCode {

  /**
   * APNs 인증서 또는 웹 푸시 인증 키가 잘못되었거나 누락되었습니다.
   */
  THIRD_PARTY_AUTH_ERROR,

  /**
   * 요청에 지정된 하나 이상의 인수가 잘못되었습니다.
   */
  INVALID_ARGUMENT,

  /**
   * 내부 서버 오류입니다.
   */
  INTERNAL,

  /**
   * 메시지 대상에 대한 발송 제한을 초과했습니다.
   */
  QUOTA_EXCEEDED,

  /**
   * 인증된 발신자 ID가 등록 토큰의 발신자 ID와 다릅니다.
   */
  SENDER_ID_MISMATCH,

  /**
   * 클라우드 메시징 서비스가 일시적으로 사용 불가능합니다.
   */
  UNAVAILABLE,

  /**
   * 앱 인스턴스가 FCM에서 등록 해제되었습니다. 이는 사용된 토큰이 더 이상 유효하지 않고 새로운 토큰을 사용해야 함을 의미합니다.
   */
  UNREGISTERED,
}

그리고 현재 로직을 보면 유효하지 않은 토큰을 모아서 삭제하는 이벤트를 발행하고 있습니다.
에러를 던지게 되면 제어흐름이 달라져 이 로직에 대한 처리를 하기 어려울 것 같습니다.
(혹시 에러를 보내고 나서 다시 에러가 발생한 지점으로 돌아와서 코드를 실행시킬 수 있는 방법이 있나요??
제가 알아보기에 어려웠는데 혹시 아신다면 알려주시면 감사하겠습니다!
(각 토큰마다 에러코드가 다르기 때문에 하나 에러 리스폰스가 왔다고 에러를 던질 수 없을 것 같습니다.))

push말고 에러처리를 해야하는 다른 중요한 이벤트가 있다면 찬미님이 말씀하신 방법을 이용하면 좋을 것 같습니다!

여기서 ApplicationEventPublisher를 이벤트를 사용하는 서비스마다 매번 선언하는 것은 코드 중복을 늘린다고 생각합니다. 공통의 클래스에 한번에 정의하고 static 메서드를 구현하여 이벤트 발생할 필요가 있는 곳마다 사용한다면 편리할 것 같습니다.
또한 개인적으로 책의 Events라는 클래스명에 공감이 잘 안가는데, EventPublisher 등의 이름으로 선언하면 좋을 것 같은데 이에 대해 어떻게 생각하시나요?

좋습니다! EventPublisher위치를 어디에 두면 좋을지 의견 부탁드립니다. 저는 common, event패키지가 생각나네요!
Events패키지를 따로 만들기에는 EventPublisher만 들어갈 것 같아서 고민이네요. 만약 Events패키지를 만든다면 어떤 것들이 추가로 들어가면 좋을까요??
(각각의 이벤트 핸들러는 각 도메인에 위치하면 좋을 것 같습니다.)

@hye-on
Copy link
Contributor Author

hye-on commented Apr 22, 2024

리뷰 반영했습니다.
EventPublisher위치에 대한 의견 남겨주시면 확인하겠습니다.
머지하겠습니다 :)

@hye-on hye-on merged commit 7fcf388 into main Apr 22, 2024
1 check passed
@05AM
Copy link
Member

05AM commented Apr 22, 2024

그리고 현재 로직을 보면 유효하지 않은 토큰을 모아서 삭제하는 이벤트를 발행하고 있습니다. 에러를 던지게 되면 제어흐름이 달라져 이 로직에 대한 처리를 하기 어려울 것 같습니다. (혹시 에러를 보내고 나서 다시 에러가 발생한 지점으로 돌아와서 코드를 실행시킬 수 있는 방법이 있나요?? 제가 알아보기에 어려웠는데 혹시 아신다면 알려주시면 감사하겠습니다! (각 토큰마다 에러코드가 다르기 때문에 하나 에러 리스폰스가 왔다고 에러를 던질 수 없을 것 같습니다.))

push말고 에러처리를 해야하는 다른 중요한 이벤트가 있다면 찬미님이 말씀하신 방법을 이용하면 좋을 것 같습니다!

아하 제가 로직 측면에서 이해도가 낮았던 것 같습니다. 사실 custom exception을 발생시켜 global exception handler에 에러 토큰 리스트와 함께 보내고 로그로 찍은 다음 invalid token을 삭제하려고 했는데 너무 비즈니스 로직이 섞이는 것 같아서 단념했습니다.

좋습니다! EventPublisher위치를 어디에 두면 좋을지 의견 부탁드립니다. 저는 common, event패키지가 생각나네요! Events패키지를 따로 만들기에는 EventPublisher만 들어갈 것 같아서 고민이네요. 만약 Events패키지를 만든다면 어떤 것들이 추가로 들어가면 좋을까요?? (각각의 이벤트 핸들러는 각 도메인에 위치하면 좋을 것 같습니다.)

공통으로 사용하는 코드이기도 해서 common/event가 위치가 좋아보이는 것 같습니다!
handler 클래스는 참고 레포에서는 infra 계층에 있는 것 같습니다. 도메인이라는게 domain, application, infa 중에 domain이 아니라 해당 member면 member 패키지 내부에 있는 것이 좋다고 말씀하신건가요?

이것저것 찾아본 결과 domain 수정 등의 로직과 관련있다면 domain, 응용 계층의 로직을 사용한다면 application, 인프라 기반의 외부 서비스 통신 등을 사용한다면 infra에 위치 시키는 등 유연한 대처가 가능할 것 같습니다.

추가) 아니면 common/event/handler 에 위치시키는 것도 괜찮을 것 같습니다!

@hye-on
Copy link
Contributor Author

hye-on commented Apr 22, 2024

공통으로 사용하는 코드이기도 해서 common/event가 위치가 좋아보이는 것 같습니다!

좋아요 event패키지에 넣었는데 common으로 옮기겠습니다!

handler 클래스는 참고 레포에서는 infra 계층에 있는 것 같습니다. 도메인이라는게 domain, application, infa 중에 domain이 아니>라 해당 member면 member 패키지 내부에 있는 것이 좋다고 말씀하신건가요?

후자입니다!

이것저것 찾아본 결과 domain 수정 등의 로직과 관련있다면 domain, 응용 계층의 로직을 사용한다면 application, 인프라 기반의 외부 서비스 통신 등을 사용한다면 infra에 위치 시키는 등 유연한 대처가 가능할 것 같습니다.

저도 이 배치가 좋은 것 같아요. domain의 행위로 일어나면 domain 안에 그 나머지는 infra에 위치하게 하는 것은 어떨까요?

@hye-on hye-on deleted the feature/push branch September 4, 2024 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
D-2 2일 전 까지 리뷰해주세요 RESTRUCTURING 코드베이스를 정리하거나 구조를 바꾸는 모든 작업
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[RESTRUCTURING] 리스트럭쳐링할 목록 - 혜온
2 participants