프로젝트 1주차는 너무 바쁘게.. 아 2주차였던가..?
암튼 취준도 하며 개발까지 하려니 하루가 어떻게 가는지 모르겠다. 분명 이것저것 많이 한 것 같은데 막상 돌아보면 그렇게 많이 한 것까진 아닐 때도 있고..
이번 주는 뭘 하며 바쁘게 지나갔는지 되돌아보자!

(드디어 내 블로그에도 제대로 된 그림이..! 🥹 예쁘게 만들어주신 현수형 감사합니다)
우리 서비스의 유저는, 최대 1개의 종목에 대해 상세정보를 볼 수 있다. 해당 상세정보에는 현재가, 캔들차트, 호가, 체결가 정보가 들어있다.
상세정보들 중, 유저가 실시간으로 받아야 하는 건 뭘까? 바로바로~ 호가와 체결가~!
당장 우리만 해도 실시간 호가, 체결가 정보를 증권사 API로부터 실시간으로 받아오고 있다.
해당 증권사는 WebSocket을 사용하여 실시간 정보를 제공한다.
종목 별로 채널을 운영하며, 종목 코드를 담은 구독 메시지(STOMP는 아니다)를 보내면 서버에서 채널 구독을 시켜주는 형태이고, 하나의 웹소켓 세션 위에서 50개 채널까지 구독이 가능하다.
종목의 구독과 구독해지는 그때그때 유저의 메시지를 통해 이루어진다.
아마 이 때문에 WebSocket이 아닐까 싶다. 양방향으로 메시지를 주고받기 위해서!
하지만 우리의 유저는 1개의 종목만 볼 수 있기 때문에 주식 디테일 페이지에 들어갈 때 구독, 나올 때 구독해지를 하면 된다.
그러면 굳이 양방향이어야 하나?
구현이 간단하다(⭐매우중요)
아무래도 짧은 기간의 프로젝트이다보니, 구현의 용이성을 고려하지 않을 수 없었다.
물론 기구현된 WebSocket의 베이스가 있지만, SSE는 자동 재연결이 되므로 재연결 구현에 대한 부담이 없었다.
오버헤드가 적다(적당히중요)
SSE:
data: {"symbol":"005930","price":75000}
STOMP over WebSocket:
MESSAGE
destination:/topic/stock/005930
message-id:12345
content-type:application/json
{"symbol":"005930","price":75000}SSE를 우선 적용하기로 했다.
우리의 레퍼런스인 토스증권은 WebSocket으로 실시간 이벤트 구독을 구현했지만 SSE로도 충분했을 것 같다고 얘기했기 때문에, 상대적으로 상당히 많이 소규모인 우리 프로젝트도 SSE로 충분할 것 같다는 생각을 하기로 했다.
SSE에서 WebSocket으로 확장이 필요하다고 판단하면, 그때가서 빠르게 마이그레이션 할 수 있기 때문이기도 하고~~
나는 증권사 API 연동 작업을 진행했다.
진짜진짜 별거 아닐거라고 생각했다.
우리 팀은 TDD로 개발한다.
그럼 외부 API를 활용하는 클래스의 테스트는 어떻게 짜야할까?
처음엔 일반적인 컴포넌트 테스트할 때 처럼 실제 응답을 받고, 응답의 형식이나 정상/비정상 여부를 검증하고자 했다.
그러나 그렇게 되면 우리 프로젝트의 테스트가 외부 API에 의존 해버린다!!!
사실 비즈니스 로직 자체가 외부 API에 의존하고 있기 때문에 당연히 테스트 코드도 의존해야 하고, 그래도 상관 없다고 생각했다.
하지만 테스트를 하는 이유가 무엇이냐?
바로바로 우리의 비즈니스 로직이 의도한 대로 잘 작동하는지를 검증하기 위해서이다.
외부 API에 의존한 테스트가 실패하면 실패의 원인이 어디에 있는지 찾아야 하는 것이 큰 문제다.
따라서 외부 API가 잘 작동한다는 것을 가정하고 그 상황에서 우리의 로직이 의도대로 굴러가는지 Mocking하여 판단해야 한다.
@Test
@DisplayName("호가 메시지 처리 시 QuoteEvent가 발행되어야 한다")
void should_publish_quote_event_when_processing_quote_message() throws Exception {
// Given
CustomWebSocketClient client = new CustomWebSocketClient(
dbAuthenticationManager,
eventPublisher
);
String quoteMessage = TEST_DB_QUOTE_DATA;
// When
java.lang.reflect.Method method = CustomWebSocketClient.class
.getDeclaredMethod("processQuoteMessage", String.class);
method.setAccessible(true);
method.invoke(client, quoteMessage);
// Then
verify(eventPublisher, times(1)).publishEvent(any(QuoteEvent.class));
}
내가 구현한 코드에서는 증권사에서 메시지가 오면 TextWebSocketHandler가 (호가 데이터 메시지의 경우) processQuoteMessage()를 호출한다.
processQuoteMessage()는 메시지를 파싱하고, QuoteEvent를 발생시킨다.
테스트 코드에서 볼 수 있듯이, reflection을 사용하여 processQuoteMessage()를 런타임에 직접 호출시켰다(증권사에서 메시지가 온 것 처럼!!).
그리고 eventPublisher를 통해 실제 QuoteEvent가 발생했는지를 검증했다.
직전 프로젝트에서도 외부 API를 사용했었는데(OpenAI API), 그 때는 mocking해서 검증하지는 않았던..(범죄🚨)
Mocking하면 당연히 잘 작동될 수 밖에 없는거 아니야? 라고 생각했지만, 그 당연히 잘 작동되도록 구현을 해가는 것이 중요했던 것이었던 것이었다..!
고민을 너무 오래 했다.
이제는 더이상 고민에 뺏길 시간이 없다.. 이번 주 안에 완성해야하기에...
HELP SOS 해업
찬이 형 말처럼 근육을 찢고 헐크가 될 수 있을까 아니면 그냥 헐큰일났다가 될 것인가..