ํ๋กํ ํ์ธ ํ๋ก์ ํธ์ ๋ฆฌํฉํ ๋ง ์์ ์์ ์๋ฆผ ๊ธฐ๋ฅ์ ๊ตฌ์กฐํ&์ค๊ณํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.
์ฐ๋ฆฌ ํ๋กํ ํ์ธ์ ์๋ฆผ์ ๋ค์๊ณผ ๊ฐ์ 2๊ฐ์ง ๋ฐฉ๋ฒ์ผ ๋ ํธ๋ฆฌ๊ฑฐ๋๋ค:
- ๋ณด์ ํฐ์ผ ๊ฐ์์ ์ ๋์ ์ธ ๋ณํ๊ฐ ์๊ธด๋ค.
- ๊ธฐ์ ์๊ฒ ํจ๋ํฐ๋ฅผ ๋ฐ์ ์์, ํด๋น ์ฌ์ค์ ๊ณ ์งํด์ค๋ค.
(๋ด์ฉ: ํจ๋ํฐ๋ฅผ ๋ถ์ฌํ ๊ธฐ์ , ๊ฐ๋จํ ์ฌ์ )
ํธ์ ์๋์ ํต์ฌ์ ์ฌ์ฉ์๊ฐ ์ฐ๋ฆฌ ํ๋ซํผ์ ์ฌ์ฉํ๊ณ ์์ง ์์๋, ์ฌ์ฉ์์๊ฒ ์๋ฆผ์ ๋ณด๋ด์ผ ํ๋ค๋ ๊ฒ์ด๋ค.
๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ์ ๋ฉ์ธ ์ด์๋ HTTP๋ connectionlessํ๋ค๋ ๊ฑฐ๋ค. (์ฐ๊ฒฐํ ์ ์ด ์์ด๋ ๋์ด๋ฒ๋ฆผ)
๊ธฐ์กด์ ์๋ฒ-ํด๋ผ์ด์ธํธ๋ ์๋ฐฉํฅ ํต์ ์ ์ฑํํ๋ฏ๋ก ์ด๋ฐ ๋ฌธ์ ๊ฐ ์๊ธฐ๋ ๊ฒ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ํธ์ ์๋์ ์ด๋ป๊ฒ ๊ตฌํํ๋ ๊ฒ์ด ์ข์๊น?
๋์ด๋ธํ๊ฒ๋ ์ผ๋ฐฉ์ ์ธ ๋จ๋ฐฉํฅ ํต์ ์ ์ฑํํ๋ฉด ๋ ๊ฒ๋ง ๊ฐ๋ค.
ํธ์ ์๋์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์๋ Server-Sent-Event(SSE)
๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์๊ณผ, Firebase Cloud Messaging(FCM)
์ ์ฌ์ฉํ๋ ๋ฐฉ์์ด ์๋ค.
ํ์ฌ ์ฐ๋ฆฌ ์๊ตฌ์ฌํญ์ ๋ ์ ํฉํ ๊ตฌํ ๋ฐฉ๋ฒ์ ๋ ์ค ๋ฌด์์ผ์ง ๊ณ ๋ฏผํ๋ค.
๋จผ์ Server-Sent-Event(SSE)
์ ๋ํด ์์๋ณด์.
Server
โ FCM
โ Client
1. ํด๋ผ์ด์ธํธ๊ฐ FCM ์๋ฒ์์ ์ ์ ๋ณ๋ก ๊ณ ์ ํ FCM ํ ํฐ์ ๋ฐ๊ธ๋ฐ๋๋ค.
2. ์๋ฒ๋ ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ํด๋น ํ ํฐ์ ์ ๋ฌ๋ฐ๊ณ ์ ์ฅํ๋ค.
3. ํน์ ์ํฉ์ ๋ฉ์์ง์ ํ ํฐ์ ๋ด์์ FCM ์๋ฒ์ ์ ์ก ์, FCM์ด ํด๋น ํ ํฐ์ ์ ์ ์๊ฒ ๋ฉ์์ง๋ฅผ ์ ์กํ๋ค.
โ ๏ธ FCM ์๋ฒ์ ์์กดํ๋ค๋ฉด, FCM ์๋ฒ๊ฐ ๋ค์ด๋์์ ๋ ์ฐ๋ฆฌ ํ๋ซํผ ์ ์ฒด๊ฐ ๋ค์ด๋ ์ํ์ด ์์๋ค.
๐ ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด์ ์๋ฆผ ์ ์ก ๊ตฌํ ์์, ๋๋ SSE๋ฅผ ์ฌ์ฉํ๊ฒ ๋ค. ํธ๋์ญ์ ๋จ์๋ฅผ ๋ถ๋ฆฌํ ํ์, ์๋ฆผ ์ ์ก ๋ด์ญ์ SQS Queue์ ๋ฃ๊ณ ์คํธ๋ฆผ ์ฒ๋ฆฌ๋ฅผ ํ๋ ค๊ณ ํ๋ค.
package com.prototyne;
import com.prototyne.Users.service.AlarmService.AlarmService;
import com.prototyne.Users.web.controller.AlarmController;
import com.prototyne.config.JwtManager;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
public class AlarmControllerTest {
@InjectMocks
private AlarmController alarmController;
@Mock
private AlarmService alarmService;
@Mock
private JwtManager jwtManager;
@Test
void subscribe_shouldReturnSseEmitter() {
String accessToken = "mockAccessToekn";
SseEmitter mockEmitter = new SseEmitter();
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
given(jwtManager.getToken(any(HttpServletRequest.class))).willReturn(accessToken);
given(alarmService.subscribe(accessToken)).willReturn(mockEmitter);
SseEmitter res= alarmController.subscribe(mockRequest);
assertNotNull(res);
verify(jwtManager).getToken(any(HttpServletRequest.class));
verify(alarmService).subscribe(accessToken);
}
}
๋ ์์ธํ ํ
์คํธ ์คํ๊ฒฐ๊ณผ๋ฅผ ํธ๋ํนํ๊ธฐ ์ํด์,
ํ
์คํธ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๋ ๋ช
๋ น์ด๋ฅผ ํฐ๋ฏธ๋์ ์
๋ ฅํ๋ค.
gradlew test --info
ํ
์คํธ ๊ฒฐ๊ณผ๊ฐ ๋์จ๋ค.
build/reports/tests/test/index.html
๊ฒฝ๋ก์ ์๋ htmlํ์ผ์ ๋ธ๋ผ์ฐ์ ๋ก ์ด์ด๋ณธ๋ค.
์ด๋ ๊ฒ ํ
์คํธ ๊ฒฐ๊ณผ๊ฐ ๋์จ๋ค.
ํ
์คํธ ์ปจํ
์คํธ๋ 2๊ฐ(์๋ฆผ subscribe api & ์ ์ฒด @SpringBootTest
) ์, ์๋ฒฝํ๊ฒ ์ฑ๊ณตํ๋ค๊ณ ๋์จ๋ค.
๊ฐ ์ปจํ
์คํธ ๋ณ ์คํ์๊ฐ์ ํ์ธํ๊ธฐ ์ํด, ํด๋์ค ๋ณ ๋ฉํธ๋ฆญ์ ํด๋ฆญํ๋ค.