66import java .util .List ;
77import java .util .UUID ;
88
9- import org .springframework .beans .factory .annotation .Autowired ;
10- import org .springframework .beans .factory .annotation .Value ;
119import org .springframework .core .env .Environment ;
1210import org .springframework .stereotype .Component ;
1311import org .springframework .web .reactive .socket .WebSocketSession ;
1614import com .fasterxml .jackson .core .JsonProcessingException ;
1715import com .fasterxml .jackson .databind .JsonNode ;
1816import com .fasterxml .jackson .databind .ObjectMapper ;
17+ import com .rabbittick .streamer .config .ExchangeProperties ;
1918import com .rabbittick .streamer .converter .ExchangeDataConverter ;
2019import com .rabbittick .streamer .global .dto .MarketDataMessage ;
2120import com .rabbittick .streamer .service .MarketDataService ;
@@ -42,46 +41,8 @@ public class UpbitConnector extends AbstractExchangeConnector {
4241
4342 private final ExchangeDataConverter converter ;
4443 private final ObjectMapper objectMapper ;
45-
46- private static final URI UPBIT_WEBSOCKET_URI = URI .create ("wss://api.upbit.com/websocket/v1" );
47- private static final Duration PING_INTERVAL = Duration .ofSeconds (60 );
48-
49- /** Upbit WebSocket 연결당 최대 구독 가능 마켓코드 수 */
50- private static final int MAX_MARKETS_PER_CONNECTION = 100 ;
51-
52- /** 블로킹 처리 시 동시에 구독할 내부 Mono 개수 상한 (boundedElastic 스레드 풀 사용) */
53- private static final int PROCESSING_CONCURRENCY = 32 ;
54-
55- /** 구독할 마켓 prefix 목록 (KRW, BTC, USDT 등) */
56- @ Value ("${upbit.websocket.market-prefixes:KRW}" )
57- private List <String > marketPrefixes ;
58-
59- /**
60- * 사용할 마켓코드 환경 설정 (development, production, full, fetched)
61- */
62- @ Value ("${upbit.websocket.environment:development}" )
63- private String marketEnvironment ;
64-
65- /**
66- * Ticker 데이터 수집 활성화 여부
67- */
68- @ Value ("${upbit.websocket.data-types.ticker.enabled:true}" )
69- private boolean tickerEnabled ;
70-
71- /**
72- * Trade 데이터 수집 활성화 여부
73- */
74- @ Value ("${upbit.websocket.data-types.trade.enabled:false}" )
75- private boolean tradeEnabled ;
76-
77- /**
78- * OrderBook 데이터 수집 활성화 여부
79- */
80- @ Value ("${upbit.websocket.data-types.orderbook.enabled:false}" )
81- private boolean orderbookEnabled ;
82-
83- @ Autowired
84- private Environment env ;
44+ private final ExchangeProperties exchangeProperties ;
45+ private final Environment env ;
8546
8647 /**
8748 * 생성자 주입.
@@ -92,14 +53,24 @@ public class UpbitConnector extends AbstractExchangeConnector {
9253 * @param marketDataService 시장 데이터 처리 서비스
9354 * @param converter Upbit DTO → 표준 DTO 변환 컨버터
9455 * @param objectMapper JSON 파싱용 ObjectMapper
56+ * @param exchangeProperties 다중 거래소 설정 프로퍼티
57+ * @param env 마켓코드 YAML 로드용 Spring Environment
9558 */
9659 public UpbitConnector (WebSocketClient webSocketClient ,
9760 MarketDataService marketDataService ,
9861 ExchangeDataConverter converter ,
99- ObjectMapper objectMapper ) {
62+ ObjectMapper objectMapper ,
63+ ExchangeProperties exchangeProperties ,
64+ Environment env ) {
10065 super (webSocketClient , marketDataService );
10166 this .converter = converter ;
10267 this .objectMapper = objectMapper ;
68+ this .exchangeProperties = exchangeProperties ;
69+ this .env = env ;
70+ }
71+
72+ private ExchangeProperties .ExchangeConfig config () {
73+ return exchangeProperties .getConfigs ().get ("upbit" );
10374 }
10475
10576 @ Override
@@ -109,27 +80,27 @@ public String getExchangeName() {
10980
11081 @ Override
11182 public URI getWebSocketUri () {
112- return UPBIT_WEBSOCKET_URI ;
83+ return config (). getWebsocketUri () ;
11384 }
11485
11586 @ Override
11687 public Duration getPingInterval () {
117- return PING_INTERVAL ;
88+ return config (). getPingInterval () ;
11889 }
11990
12091 @ Override
12192 public boolean isEnabled () {
122- return true ;
93+ return config (). isEnabled () ;
12394 }
12495
12596 @ Override
12697 protected int getMaxMarketsPerConnection () {
127- return MAX_MARKETS_PER_CONNECTION ;
98+ return config (). getMaxMarketsPerConnection () ;
12899 }
129100
130101 @ Override
131102 protected int getProcessingConcurrency () {
132- return PROCESSING_CONCURRENCY ;
103+ return config (). getProcessingConcurrency () ;
133104 }
134105
135106 /**
@@ -178,10 +149,11 @@ public Mono<Void> handleMessage(String rawJson) {
178149 String messageType = typeNode .asText ();
179150 log .debug ("[UPBIT] 메시지 타입 확인: {}" , messageType );
180151
152+ ExchangeProperties .DataTypesConfig dataTypes = config ().getDataTypes ();
181153 return switch (messageType ) {
182- case "ticker" -> tickerEnabled ? processMessage (converter .convertTicker (rawJson )) : Mono .empty ();
183- case "trade" -> tradeEnabled ? processMessage (converter .convertTrade (rawJson )) : Mono .empty ();
184- case "orderbook" -> orderbookEnabled ? processMessage (converter .convertOrderBook (rawJson )) : Mono .empty ();
154+ case "ticker" -> dataTypes . isTicker () ? processMessage (converter .convertTicker (rawJson )) : Mono .empty ();
155+ case "trade" -> dataTypes . isTrade () ? processMessage (converter .convertTrade (rawJson )) : Mono .empty ();
156+ case "orderbook" -> dataTypes . isOrderbook () ? processMessage (converter .convertOrderBook (rawJson )) : Mono .empty ();
185157 default -> {
186158 log .trace ("[UPBIT] 알 수 없는 메시지 타입 무시: {}" , messageType );
187159 yield Mono .empty ();
@@ -222,8 +194,9 @@ private Mono<Void> processMessage(MarketDataMessage<?> message) {
222194 @ Override
223195 public List <String > loadMarketCodes () {
224196 List <String > marketCodes = new ArrayList <>();
197+ List <String > prefixes = config ().getMarketPrefixes ();
225198
226- for (String prefix : marketPrefixes ) {
199+ for (String prefix : prefixes ) {
227200 List <String > enabledTiers = getEnabledTiers ();
228201 for (String tier : enabledTiers ) {
229202 List <String > tierMarkets = loadTierMarkets (prefix .toLowerCase (), tier );
@@ -239,7 +212,7 @@ public List<String> loadMarketCodes() {
239212 log .warn ("설정에서 마켓코드를 로드할 수 없어 기본값 사용: {}" , marketCodes );
240213 }
241214
242- log .info ("전체 마켓코드 {}개 로드 완료 (prefix: {})" , marketCodes .size (), marketPrefixes );
215+ log .info ("전체 마켓코드 {}개 로드 완료 (prefix: {})" , marketCodes .size (), prefixes );
243216 return marketCodes ;
244217 }
245218
@@ -276,7 +249,7 @@ private List<String> loadTierMarkets(String marketPrefix, String tier) {
276249 * @return 활성화된 티어 목록
277250 */
278251 private List <String > getEnabledTiers () {
279- String environment = marketEnvironment .toLowerCase ();
252+ String environment = config (). getEnvironment () .toLowerCase ();
280253 List <String > tiers = new ArrayList <>();
281254 int index = 0 ;
282255
@@ -317,23 +290,24 @@ private List<String> getEnabledTiers() {
317290 * @throws IllegalStateException 활성화된 데이터 타입이 없는 경우
318291 */
319292 private String createSubscriptionMessage (List <String > marketCodes ) throws JsonProcessingException {
293+ ExchangeProperties .DataTypesConfig dataTypes = config ().getDataTypes ();
320294 List <Object > request = new ArrayList <>();
321295 request .add (new Ticket (UUID .randomUUID ().toString ()));
322296
323- if (tickerEnabled ) {
297+ if (dataTypes . isTicker () ) {
324298 request .add (new Type ("ticker" , marketCodes ));
325299 log .info ("Ticker 구독 활성화: {}개 마켓코드" , marketCodes .size ());
326300 }
327- if (tradeEnabled ) {
301+ if (dataTypes . isTrade () ) {
328302 request .add (new Type ("trade" , marketCodes ));
329303 log .info ("Trade 구독 활성화: {}개 마켓코드" , marketCodes .size ());
330304 }
331- if (orderbookEnabled ) {
305+ if (dataTypes . isOrderbook () ) {
332306 request .add (new Type ("orderbook" , marketCodes ));
333307 log .info ("OrderBook 구독 활성화: {}개 마켓코드" , marketCodes .size ());
334308 }
335309
336- if (!tickerEnabled && !tradeEnabled && !orderbookEnabled ) {
310+ if (!dataTypes . isTicker () && !dataTypes . isTrade () && !dataTypes . isOrderbook () ) {
337311 throw new IllegalStateException ("최소 하나의 데이터 타입은 활성화되어야 합니다" );
338312 }
339313
0 commit comments