Home 배포하면서 생긴 채팅 관련 오류
Post
Cancel

배포하면서 생긴 채팅 관련 오류

배포하면서 생긴 채팅 관련 오류 모음 집(+SSE)

로컬에서 https를 적용했을 때는 문제가 되지 않았으나 배포하면서 발견한 에러들을 정리해봤다.



1. 공통 JS와 페이지별 기능 JS

채팅 관련 js를 작성한 곳이 chat.js이고

여러 페이지에서 공통적으로 사용한 변수들과 메소드를 모아놓은 곳이 global.js다.


global.js에서 선언한 token, host를 갖고 오지 못해서 chat.js에서 실행 되지 못하고

chat.js에서 nickname을 중복 선언해서 global.js에서 nickname이 중복이라고 에러가 떴다.

이 말은 global.js보다 chat.js가 먼저 실행된다는 얘기였다.


알고보니 HTML 파일에서 스크립트를 작성할 때 순차적으로 실행이 되는데 global.js가 chat.js 보다 아래에 위치해 있었다.

html에 js를 넣을 때도 순서가 중요하다라는걸 느꼈다.


혹시 몰라서 $(document).ready(function(){});를 활용할 수도 있긴 하지만

현재 코드로는 script 실행 순서만 변경해야 동작한다.





2. 채팅 관련 라이브러리 설치

Monolith 구조일 때는 BACK과 FRONT가 함께 BUILD되어서 동작했으나

BACK과 FRONT가 분리되어 서로 독립적으로 BUILD될 때는 필요한 의존성을 script 등을 통해 관리해야한다.

1
2
3
4
5
// SockJS
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.5.2/sockjs.min.js"></script>

// STOMP
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>





3. endpoint/info?t= ~ 404 (Not Found)

WebSocket이 처음 connection 시에 endpoint/info?t= ~ 같은 형태의 URL을 호출해서 연결이 된다.

근데 여기서 404 에러가 났다. → Spring에서 호출 URL을 처리 못한다는 뜻

처음엔 blog에서 수정한 방법을 사용하려고 했으나

cors 오류가 없었고 setAllowedOrigins("*")를 사용하라고 했으나 setAllowedOriginPatterns("*")를 사용하고 있었다.


나는 sockJs 생성 주소를 잘못 입력해서 생긴 오류 였다.

new SockJS('/chat');new SockJS(host + '/chat');

*host = https://www.domain.com



연결 시 info를 호출하는 이유는 어떤 방법으로 연결해야 할 지 정보를 얻기 위함이다.

WebSocket이 모든 브라우저에 적용되지 않고 프록시 서버가 도중에 연결을 끊어버리기도 하기 때문에

이런 단점들을 보완한 WebSocketEmulation (SockJS)를 사용한다.

SockJS는 우선 WebSocket 연결을 시도하고,

실패하였을 경우 Streaming, Long-Polling 등 다른 대체 기술로 연결을 시도한다.


잠깐 다른 주제로 넘어가자면 security에서 setAllowedOrigins("*")setAllowedOriginPatterns("*") 관한 얘기가 나왔다.

이 글은 작성하면서 현재 글과 어울리지 않는다고 판단해서 CORS에서 따로 작성했다.





4. Mixed Content

Mixed Content: The page at https://www.~ was loaded over HTTPS,

but requested an insecure XMLHttpRequest endpoint http://api~.

This request has been blocked; the content must be served over HTTPS.


https 사이트에서 ajax를 사용해서 비동기로 http 사이트에 request를 요청해서 문제가 발생 했다.

암호화된 HTTPS 기반의 사이트에서 암호화되지 않은 HTTP 사이트에 요청을 보내서 Mixed content 에러가 발생한 것이다.


html파일 head에 아래 태그를 추가하면 된다.

1
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

웹페이지의 보안 정책을 설정하는 HTML 메타 태그 중 하나


위 코드가 적용된 페이지에서는 브라우저가 페이지에 있는 모든 HTTP 요청을 HTTPS로 변경하려고 시도하게 된다.

이는 중요한 보안 관행 중 하나로, 특히 웹사이트가 HTTPS를 지원하는 경우에 권장되는 설정이라고 한다.





5. has been blocked by CORS policy

Access to XMLHttpRequest at https://www.domain.com/templates/login.html

(redirected from https://api.domain.com/ws/info?t=)

from origin https://www.domain.com has been blocked by CORS policy:

No ‘Access-Control-Allow-Origin’ header is present on the requested resource.


setAllowedOriginPatterns에 https://api.domain.com을 추가하면 된다.





6. websocket connection failed

image

이 글을 보고 security에

웹소켓 통신에 대한 권한 허용 코드가 없으니 추가를 해야한다는 얘기인 것 같아서

채팅의 endpoint인 ws를 추가했더니 동작했으며 위 2개 오류가 해결되었다. → .antMatchers("/ws/**").permitAll()

image


해당 오류가 뜬 이유는 7번과 동일하다.(JwtAuthenticationFilter로 인해 채팅 불가하므로 접근 허용으로 수정)





7. OAuth2, Form Login

채팅을 test할 때 주로 소셜로그인을 활용해서 했다가 이번에 폼로그인으로 진행하면서 채팅이 되지 않았다.

image


1
2
3
4
5
6
7
8
9
10
11
12
@RequiredArgsConstructor
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtTokenProvider jwtTokenProvider;

    @Override
    public void doFilterInternal(
            HttpServletRequest servletRequest,
            HttpServletResponse servletResponse,
            FilterChain filterChain) throws ServletException, IOException {
    }
}

JwtAuthenticationFilter에서 토큰이 없는 경우나 유효하지 않은 경우에는 인증에 실패한 것으로 처리된다.


1
2
3
4
5
6
7
@Component
@RequiredArgsConstructor
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
    }
}

이 때, CustomAuthenticationEntryPoint 클래스의 commence()가 호출되어 인증 실패를 처리하는데

채팅할 때 ajax로 통신하는 경우 외에는 header에 token을 넣어주지 않아서 발생한 오류 같았다.


그래서 6번에서 해결한 방법과 같이 작성해서 JwtAuthenticationFilter를 실행되지 않게 했다.

WebSocketConfig에서 jwt token 검증을 위해 stompHandler를 Interceptor로 설정했기 때문에 채팅은 따로 검증 체크가 들어간다.





8. EventSource’s response has a MIME type (“text/plain”) that is not “text/event-stream”. Aborting the connection.

SseEmitter 객체를 return해줘야 하는데 해주지 않아서 생긴 오류다.


문제는 2가지가 있었다.

하나는 url을 수정하는 과정에서 back과 front의 url이 서로 달라서 통신하지 못해 생긴 오류이고

또 다른 하나는 7번과 같이 JwtAuthenticationFilter가 실행되면서 인증에 실패해 Controller가 실행되지 못했다.

SSE(Server-Sent Events)에서는 HTTP 요청과는 다르게 헤더에 직접적으로 토큰을 담는 것이 불가능하다.

따라서 .antMatchers(VIEW_LIST).permitAll()에서 VIEW_LIST에 SSE 통신하는 url을 추가해줬다.





REFERENCE

This post is licensed under CC BY 4.0 by the author.