헤헤.. 안녕하세요.. 하하.. 또 리팩토링을 하였습니다
개발자는 오늘본 코드가 내일 마음에 안들어야한다 라는말이 있었는데 그말이 사실이였습니다
왜 저는 리팩토링을 하려고 했었을까요..?
1. 과도한 추상화
제가 제 코드를 못알아 봣습니다 할말이 없었습니다
추상 메서드 패턴을 사용하면서 이쪽 저쪽 숨겨놓고 구현하고
혼자서 보물 찾기 하는거 마냥 메서드 어디있니? 라면서 찾고 다니는 제 자신이 한심해보였습니다
2. 과도한 책임..
각 클라이언트와 인터페이스를 따로 만드는것은 좋았으나 각 거래소마다 메시지 특성을 고려하지않고..
전처리 클래스와 소켓 클래스를 하나로 퉁쳐놓고 모든 거래소의 예외조건을 다 넣을려고 했었었습니다 미치겠습니다 정말
보여드리고싶은데 너무 길어서 올리지도 못하겠습니다
링크 첨부합니다
ExchangePipeline/common/client/market_socket/websocket_interface.py at main · MajorShareholderClub/ExchangePipeline
Contribute to MajorShareholderClub/ExchangePipeline development by creating an account on GitHub.
github.com
3. 난잡한 파일 구성..과 정형화되지 않는 아키텍처 패턴..
네.. 할말이 없습니다 정리한다고 까불었는데.. 못했습니다 ..
이쁘게 readme를 정리하면서 뿌듯했던날이 떠올랐는데 지금 생각하면 이불킥 하다 종아리에 쥐가 날꺼같습니다 이유는..
너무 쌔게차서...
시작해볼까요 ? Let`s get it!
1. 아키텍처 패턴 정리하기..
저는 보물찾기 하듯 추상 메서드를 이리 저리 숨겨놔 또다른 재미를 느낄 수 있게 구현을 찾는 극대노를 느꼈습니다
그러므로 추상메서드를 어느정도 제거하고 EDA를 적용하기로 하였습니다..
1. Event를 정의하고
2. 구독관리와 해제를 managerment를 관리해주는 클래스
3. publish 하는 EventBus 진행하여
publish 해서 발행 하고 -> eventkey를 찾은 후 -> 특정 구독 리스트를 반환 -> 비동기 실행 순으로 하여 순서를 명확하게 보장하였습니다 ..!! 다이어그램으로 따지면 다음과 같은데요..!!
다이어그램 구조 해설하면 다음과 같습니다
- EventTypeRegistry
- IEventTypeRegistry 인터페이스를 구현하며, 문자열 ↔ Enum(EventType)을 변환하고 캐싱하는 역할을 담당합니다.
- get_event_key(event_type) 또는 get_event_type(event_key) 메서드로 서로를 매핑하며, lru_cache를 이용해 성능을 최적화합니다.
- EventSubscriptionManager
- ISubscriptionManager 인터페이스를 구현합니다.
- 내부에 _subscribers(dict 형식)와 asyncio.Lock을 통해 이벤트별로 콜백 리스트를 안전하게 관리하고, subscribe/unsubscribe 메서드로 등록·해제 기능을 제공합니다.
- EventTypeRegistry를 이용해 event_type(Enum 또는 str)을 실제 구독·해제 시에 문자열 키(event_key)로 변환합니다.
- EventBus
- IEventBus 인터페이스를 구현하고, EventSubscriptionManager와 EventTypeRegistry를 내부에서 활용해 이벤트를 발행(publish)하고 구독자에게 전달합니다.
- publish 메서드가 호출되면 _process_event를 통해 이벤트를 비동기(asyncio.gather)로 구독자 전원에게 전파합니다.
- 이벤트 시작·종료 시점도 start와 stop 메서드를 통해 관리합니다.
이와 같은 구조로 EventBus가 전체 이벤트 흐름의 중심이 되고, EventSubscriptionManager가 구독 관리 및 콜백 목록을 책임지며, EventTypeRegistry는 이벤트 타입(문자열 ↔ Enum) 변환 과정을 맡아 세 컴포넌트가 협력하는 형태를 취합니다.
이런식으로 작성하여... 다음과 같은 시퀀스로 동작하여 이벤트 기반으로 실행할 수 있는 아키텍처로 탈바꿈 하였습니다..!!
- Client → EventBus
- 사용자가 event_type, data, metadata를 인자로 하여 publish() 메서드를 호출합니다.
- 이벤트 타입 변환
- EventBus는 EventTypeRegistry의 get_event_key()를 통해 event_type(Enum or str)을 문자열 키로 변환합니다.
- (optional) metadata가 없다면, EventTypeRegistry.get_event_type()를 사용하여 기본 메타데이터(EventMetadata)를 구성합니다.
- 구독자 목록 조회
- 변환된 문자열 키(event_key)를 사용해 EventSubscriptionManager의 get_subscribers()를 호출, 해당 이벤트를 구독 중인 콜백 함수를 모두 가져옵니다.
- 콜백 호출
- 구독자 콜백 함수를 각각 비동기로 실행(asyncio.gather())합니다.
- _safe_callback()에서는 콜백이 Future 또는 Coroutine을 반환할 경우 await를 통해 완료될 때까지 대기합니다. 예외 발생 시 로깅하고 처리합니다.
- 처리 완료
- 모든 콜백 실행이 끝나면 publish() 메서드가 완료되고, 결과가 Client에 반환됩니다.
그러면 저번 프로젝트에서 투박하게 하나하나 클래스 만들어서 그냥 냅다 꽂아버리는 .. 방식이 아닌
구독 관리 형식으로 훨 씬 더 유연한 상태를 유지할 수 있었습니다
이렇게 하면 저는 단순 파라미터와 URL만 추가하면 확장이 쉬워지니 훨씬 더 좋은 선택이었습니다 바뀐 코드는 다음과 같습니다
ExchangePipeline/adapters/base/event/event_process/event_subscription_manager.py at fact/TooMany-Abstract-Removering · MajorSha
Contribute to MajorShareholderClub/ExchangePipeline development by creating an account on GitHub.
github.com
2. 소켓 파라미터 구성하기
저는 처음 소켓 파라미터를 구성할때는 다음과 같은 형태로 구성하였습니다
1. config 에서 yml를 가지고와 거래소를 확인
2. yml 에서 필요한 파라미터를 가지고온 후
3. 팩토리 패턴으로 거래소를 확인 후 마켓 로드 클래스를 따로 만들어서 진행하였습니다
그러면 거래소마다 클래스를 만들어야하고.. 팩토리에 등록을 해야하고.. 여간 유지보수가 굉장히 힘들지 않을까? 라는 생각을 했습니다
전에 있던 코드는 다음과 같습니다
https://github.com/MajorShareholderClub/ExchangePipeline/blob/main/config/yml_param_load.py
ExchangePipeline/config/yml_param_load.py at main · MajorShareholderClub/ExchangePipeline
Contribute to MajorShareholderClub/ExchangePipeline development by creating an account on GitHub.
github.com
저는 생각을 했습니다 그냥 한꺼번에 모아서 파라미터를 미리 만들어서 주자..
너무 귀찮았습니다 yml에 갔다가.. 클래스 등록하고.. 언제 다하고 있을까..?
그래서 저는 빌더 패턴을 사용하였습니다 이렇게 하여..
def create_connection_params(
region: str,
exchange: str,
symbol: str,
timeout: int = 30,
req_type: str = "ticker",
stream_type: str = "socket",
) -> ConnectionParams:
"""ConnectionParams를 생성하는 데 필요한 파라미터를 구성"""
return (
ConnectionParams()
.region(region)
.exchange(exchange)
.request_type(req_type)
.stream_type(stream_type)
.symbol(symbol)
.timeout(timeout)
)
이런식으로 함수를 만들어 거래소가 필요할때 config 함수를 추가를 주면 자연스럽게 적용할 수 있으니 편의성이 더욱 더 좋았습니다!! 옛날 처럼 투박하게 어디 가고 어디어디가고 이럴 필요가 없어진거죠!!
이걸 바탕으로 dictionaryt에 등록하여 paramater와 클래스를 등록할 수 있는 등록함수를 만들어 간편함을 추가하였습니다!!
ExchangePipeline/common/setting/properties.py at fact/TooMany-Abstract-Removering · MajorShareholderClub/ExchangePipeline
Contribute to MajorShareholderClub/ExchangePipeline development by creating an account on GitHub.
github.com
다음편에서는 거래소관리를 어떻게 진행하려고 했는지 포슽잉을 해보겠습니다!
아직은 현재 진행중입니다만 보고싶으면 다음 링크를 타주세요 !! 브런치를 따로 팻습니다! TooMany-Abstract~ 입니다!
https://github.com/MajorShareholderClub/ExchangePipeline/tree/fact/TooMany-Abstract-Removering
GitHub - MajorShareholderClub/ExchangePipeline
Contribute to MajorShareholderClub/ExchangePipeline development by creating an account on GitHub.
github.com
'핀테크프로젝트 > 코인' 카테고리의 다른 글
다시 돌아온 코인 프로젝트 (2) (0) | 2024.11.17 |
---|---|
다시 돌아온 코인 프로젝트 (1) (1) | 2024.11.11 |
다시 돌아온 코인 프로젝트 (0) (1) | 2024.11.09 |
거래소별 API 분석 각 거래소 별 캔들 데이터는 무사한가 (0) | 2024.01.29 |
코인 모듈 그 첫번째 각 거래소 API 정형화 하기 (1) (0) | 2024.01.29 |