SignalR 1.x에서 연결 수명 이벤트 이해 및 처리

작성자 : Patrick Fletcher, Tom Dykstra

경고

이 설명서는 최신 버전의 SignalR용이 아닙니다. ASP.NET Core SignalR을 살펴보세요.

이 문서에서는 처리할 수 있는 SignalR 연결, 다시 연결 및 연결 끊기 이벤트, 구성할 수 있는 시간 제한 및 유지 설정에 대한 개요를 제공합니다.

이 문서에서는 SignalR 및 연결 수명 이벤트에 대한 지식이 이미 있다고 가정합니다. SignalR에 대한 소개는 SignalR - 개요 - 시작 참조하세요. 연결 수명 이벤트 목록은 다음 리소스를 참조하세요.

개요

이 자료에는 다음과 같은 섹션이 포함되어 있습니다.

API 참조 topics 대한 링크는 .NET 4.5 버전의 API에 대한 링크입니다. .NET 4를 사용하는 경우 API topics .NET 4 버전을 참조하세요.

연결 수명 용어 및 시나리오

OnReconnected SignalR Hub의 이벤트 처리기는 지정된 클라이언트에 대해 직접 OnConnected 실행할 수 있지만 이후에는 실행할 수 없습니다OnDisconnected. 연결 끊기 없이 다시 연결할 수 있는 이유는 SignalR에서 "connection"이라는 단어가 사용되는 여러 가지 방법이 있기 때문입니다.

SignalR 연결, 전송 연결 및 물리적 연결

이 문서에서는 SignalR 연결, 전송 연결물리적 연결을 구분합니다.

  • SignalR 연결은 SignalR API에서 유지 관리하고 연결 ID로 고유하게 식별되는 클라이언트와 서버 URL 간의 논리적 관계를 나타냅니다. 이 관계에 대한 데이터는 SignalR에서 유지 관리되며 전송 연결을 설정하는 데 사용됩니다. 관계가 종료되고 SignalR이 손실된 전송 연결을 다시 설정하려고 시도하는 동안 클라이언트가 메서드를 호출 Stop 하거나 시간 제한에 도달하면 SignalR이 데이터를 삭제합니다.
  • 전송 연결 은 WebSockets, 서버 전송 이벤트, 영구 프레임 또는 긴 폴링의 네 가지 전송 API 중 하나로 유지 관리되는 클라이언트와 서버 간의 논리적 관계를 나타냅니다. SignalR은 전송 API를 사용하여 전송 연결을 만들고 전송 API는 전송 연결을 만들기 위한 실제 네트워크 연결의 존재 여부에 따라 달라집니다. 전송 연결은 SignalR이 종료되거나 전송 API가 물리적 연결이 끊어진 것을 감지할 때 종료됩니다.
  • 실제 연결 은 실제 네트워크 링크(전선, 무선 신호, 라우터 등)를 나타냅니다. -- 클라이언트 컴퓨터와 서버 컴퓨터 간의 통신을 용이하게 합니다. 전송 연결을 설정하려면 실제 연결이 있어야 하며 SignalR 연결을 설정하려면 전송 연결을 설정해야 합니다. 그러나 물리적 연결을 끊는 것이 이 항목의 뒷부분에서 설명한 대로 전송 연결 또는 SignalR 연결을 즉시 종료하는 것은 아닙니다.

다음 다이어그램에서 SignalR 연결은 Hubs API 및 PersistentConnection API SignalR 계층으로 표시되고, 전송 연결은 전송 계층으로 표시되며, 물리적 연결은 서버와 클라이언트 사이의 줄로 표시됩니다.

SignalR 아키텍처 다이어그램

SignalR 클라이언트에서 메서드를 호출 Start 할 때 서버에 대한 물리적 연결을 설정하기 위해 필요한 모든 정보를 SignalR 클라이언트 코드에 제공합니다. SignalR 클라이언트 코드는 이 정보를 사용하여 HTTP 요청을 수행하고 네 가지 전송 방법 중 하나를 사용하는 물리적 연결을 설정합니다. 전송 연결이 실패하거나 서버가 실패하는 경우 클라이언트에 동일한 SignalR URL에 대한 새 전송 연결을 자동으로 다시 설정하는 데 필요한 정보가 여전히 있기 때문에 SignalR 연결이 즉시 사라지지 않습니다. 이 시나리오에서는 사용자 애플리케이션의 개입이 없으며 SignalR 클라이언트 코드가 새 전송 연결을 설정하는 경우 새 SignalR 연결을 시작하지 않습니다. SignalR 연결의 연속성은 메서드를 호출 Start 할 때 생성되는 연결 ID가 변경되지 않는다는 사실에 반영됩니다.

OnReconnected 허브의 이벤트 처리기는 전송 연결이 손실된 후 자동으로 다시 설정될 때 실행됩니다. OnDisconnected 이벤트 처리기는 SignalR 연결의 끝에서 실행됩니다. SignalR 연결은 다음과 같은 방법으로 종료할 수 있습니다.

  • 클라이언트가 메서드를 Stop 호출하면 중지 메시지가 서버로 전송되고 클라이언트와 서버 모두 SignalR 연결을 즉시 종료합니다.
  • 클라이언트와 서버 간의 연결이 끊긴 후 클라이언트는 다시 연결하려고 시도하고 서버는 클라이언트가 다시 연결될 때까지 기다립니다. 다시 연결 시도가 실패하고 연결 끊기 시간 제한 기간이 종료되면 클라이언트와 서버 모두 SignalR 연결을 종료합니다. 클라이언트는 다시 연결 시도를 중지하고 서버는 SignalR 연결의 해당 표현을 삭제합니다.
  • 클라이언트가 메서드를 호출 Stop 하지 않고 실행을 중지하는 경우 서버는 클라이언트가 다시 연결될 때까지 기다린 다음 연결 끊기 시간 제한 기간 후에 SignalR 연결을 종료합니다.
  • 서버 실행이 중지되면 클라이언트는 다시 연결(전송 연결 다시 만들기)한 다음 연결 끊기 시간 제한 기간 후에 SignalR 연결을 종료합니다.

연결 문제가 없고 사용자 애플리케이션이 메서드를 호출 Stop 하여 SignalR 연결을 종료하면 SignalR 연결과 전송 연결이 거의 동시에 시작되고 종료됩니다. 다음 섹션에서는 다른 시나리오에 대해 자세히 설명합니다.

전송 연결 끊기 시나리오

실제 연결이 느리거나 연결이 중단될 수 있습니다. 중단의 길이와 같은 요인에 따라 전송 연결이 끊어질 수 있습니다. 그런 다음 SignalR은 전송 연결을 다시 설정하려고 시도합니다. 경우에 따라 전송 연결 API는 중단을 감지하고 전송 연결을 삭제하고 SignalR은 연결이 끊어지는 것을 즉시 확인합니다. 다른 시나리오에서는 전송 연결 API 또는 SignalR이 연결이 끊어졌는지 즉시 인식하지 않습니다. 긴 폴링을 제외한 모든 전송의 경우 SignalR 클라이언트는 keepalive라는 함수를 사용하여 전송 API가 검색할 수 없는 연결 손실을 검사. 긴 폴링 연결에 대한 자세한 내용은 이 항목의 뒷 부분에 있는 시간 제한 및 유지 설정을 참조하세요.

연결이 비활성 상태이면 서버는 주기적으로 클라이언트에 유지 패킷을 보냅니다. 이 문서가 작성되는 날짜를 기준으로 기본 빈도는 10초마다입니다. 클라이언트는 이러한 패킷을 수신 대기하여 연결 문제가 있는지 알 수 있습니다. 예상 시 유지 패킷이 수신되지 않는 경우 짧은 시간 후에 클라이언트는 속도 저하 또는 중단과 같은 연결 문제가 있다고 가정합니다. 더 긴 시간 후에도 keepalive가 아직 수신되지 않으면 클라이언트는 연결이 삭제되었다고 가정하고 다시 연결하려고 시도합니다.

다음 다이어그램에서는 전송 API에서 즉시 인식되지 않는 물리적 연결에 문제가 있는 경우 일반적인 시나리오에서 발생하는 클라이언트 및 서버 이벤트를 보여 줍니다. 다이어그램은 다음과 같은 경우에 적용됩니다.

  • 전송은 WebSockets, 영구 프레임 또는 서버에서 보낸 이벤트입니다.
  • 실제 네트워크 연결에는 다양한 중단 기간이 있습니다.
  • 전송 API는 중단을 인식하지 못하므로 SignalR은 유지 기능을 사용하여 검색합니다.

전송 연결 끊김

클라이언트가 다시 연결 모드로 전환되지만 연결 끊기 시간 제한 내에서 전송 연결을 설정할 수 없는 경우 서버는 SignalR 연결을 종료합니다. 이 경우 서버는 허브의 OnDisconnected 메서드를 실행하고 클라이언트가 나중에 연결하도록 관리하는 경우 클라이언트에 보낼 연결 끊김 메시지를 큐에 대기합니다. 그런 다음 클라이언트가 다시 연결하면 disconnect 명령을 수신하고 메서드를 Stop 호출합니다. 이 시나리오 OnReconnected 에서는 클라이언트가 다시 연결할 OnDisconnected 때 가 실행되지 않으며 클라이언트가 를 호출 Stop할 때 실행되지 않습니다. 다음 다이어그램에서는 이 시나리오를 보여 줍니다.

전송 중단 - 서버 시간 제한

클라이언트에서 발생할 수 있는 SignalR 연결 수명 이벤트는 다음과 같습니다.

  • ConnectionSlow 클라이언트 이벤트입니다.

    마지막 메시지 또는 keepalive ping을 받은 이후 유지 시간 제한 기간의 미리 설정된 비율이 경과했을 때 발생합니다. 기본 유지 시간 제한 경고 기간은 유지 시간 제한의 2/3입니다. 유지 시간 제한은 20초이므로 경고는 약 13초에 발생합니다.

    기본적으로 서버는 10초마다 유지 ping을 보내고 클라이언트는 약 2초마다 유지 ping을 확인합니다(유지 시간 제한 값과 유지 시간 제한 경고 값 간의 차이의 3분의 1).

    전송 API가 연결 끊김을 알게 되면 Keepalive Timeout 경고 기간이 지나기 전에 SignalR에 연결 끊김에 대한 알림이 표시될 수 있습니다. 이 경우 ConnectionSlow 이벤트가 발생하지 않고 SignalR이 이벤트로 Reconnecting 직접 이동합니다.

  • Reconnecting 클라이언트 이벤트입니다.

    (a) 전송 API가 연결이 끊어졌거나 (b) 마지막 메시지 또는 keepalive ping이 수신된 이후 유지 시간 제한 기간이 경과했음을 감지할 때 발생합니다. SignalR 클라이언트 코드가 다시 연결하려고 시작합니다. 전송 연결이 끊어지면 애플리케이션이 일부 작업을 수행하도록 하려면 이 이벤트를 처리할 수 있습니다. 기본 유지 시간 제한 기간은 현재 20초입니다.

    SignalR이 다시 연결 모드인 동안 클라이언트 코드가 Hub 메서드를 호출하려고 하면 SignalR이 명령을 보내려고 시도합니다. 대부분의 경우 이러한 시도는 실패하지만 경우에 따라 성공할 수 있습니다. 서버에서 보낸 이벤트, 영구 프레임 및 긴 폴링 전송의 경우 SignalR은 클라이언트가 메시지를 보내는 데 사용하는 통신 채널과 메시지를 받는 데 사용하는 통신 채널 두 개를 사용합니다. 수신에 사용되는 채널은 영구적으로 열려 있는 채널이며 실제 연결이 중단될 때 닫힙니다. 전송에 사용되는 채널은 계속 사용할 수 있으므로 물리적 연결이 복원되면 수신 채널이 다시 설정되기 전에 클라이언트에서 서버로의 메서드 호출이 성공할 수 있습니다. SignalR이 수신에 사용된 채널을 다시 열 때까지 반환 값이 수신되지 않습니다.

  • Reconnected 클라이언트 이벤트입니다.

    전송 연결이 다시 설정될 때 발생합니다. 허브의 OnReconnected 이벤트 처리기가 실행됩니다.

  • Closed client 이벤트(disconnected JavaScript의 이벤트).

    SignalR 클라이언트 코드가 전송 연결을 끊은 후 다시 연결하려고 하는 동안 연결 해제 시간 제한이 만료될 때 발생합니다. 기본 연결 끊기 시간 제한은 30초입니다. (이 이벤트는 메서드가 호출되기 때문에 연결이 Stop 종료될 때도 발생합니다.)

전송 API에서 검색되지 않고 유지 시간 제한 경고 기간보다 오랫동안 서버에서 유지 ping 수신을 지연시키지 않는 전송 연결 중단으로 인해 연결 수명 이벤트가 발생하지 않을 수 있습니다.

일부 네트워크 환경에서는 의도적으로 유휴 연결을 닫고, 유지 패킷의 또 다른 기능은 이러한 네트워크에서 SignalR 연결이 사용 중임을 알려 이를 방지하는 것입니다. 극단적인 경우 유지 ping의 기본 빈도는 닫힌 연결을 방지하기에 충분하지 않을 수 있습니다. 이 경우 더 자주 보내도록 유지 핑을 구성할 수 있습니다. 자세한 내용은 이 항목의 뒷부분에 있는 시간 제한 및 유지 설정을 참조하세요.

참고

중요

여기에 설명된 이벤트 시퀀스는 보장되지 않습니다. SignalR은 이 체계에 따라 예측 가능한 방식으로 연결 수명 이벤트를 발생시키려고 시도하지만 네트워크 이벤트의 다양한 변형과 전송 API와 같은 기본 통신 프레임워크가 이를 처리하는 여러 가지 방법이 있습니다. 예를 들어 클라이언트가 Reconnected 다시 연결할 OnConnected 때 이벤트가 발생하지 않거나 연결 설정 시도가 실패할 때 서버의 처리기가 실행될 수 있습니다. 이 항목에서는 일반적으로 특정 일반적인 상황에서 생성되는 효과에 대해서만 설명합니다.

클라이언트 연결 끊기 시나리오

브라우저 클라이언트에서 SignalR 연결을 유지하는 SignalR 클라이언트 코드는 웹 페이지의 JavaScript 컨텍스트에서 실행됩니다. 따라서 한 페이지에서 다른 페이지로 이동할 때 SignalR 연결이 끝나야 하므로 여러 브라우저 창이나 탭에서 연결하는 경우 여러 연결 ID가 있는 여러 연결이 있습니다. 사용자가 브라우저 창 또는 탭을 닫거나 새 페이지로 이동하거나 페이지를 새로 고치면 SignalR 클라이언트 코드가 해당 브라우저 이벤트를 처리하고 메서드를 호출 Stop 하기 때문에 SignalR 연결이 즉시 종료됩니다. 이러한 시나리오에서 또는 애플리케이션이 메서드를 호출 Stop 할 때 클라이언트 플랫폼에서 이벤트 처리기가 서버에서 즉시 실행되고 클라이언트가 이벤트를 발생합니다Closed(이벤트는 JavaScript로 명명 disconnectedOnDisconnected).

클라이언트 애플리케이션 또는 실행 중인 컴퓨터가 충돌하거나 절전 모드로 가는 경우(예: 사용자가 랩톱을 닫을 때) 서버는 무슨 일이 일어났는지 알 수 없습니다. 서버에서 알 수 있듯이 클라이언트의 손실은 연결 중단으로 인해 발생할 수 있으며 클라이언트가 다시 연결하려고 할 수 있습니다. 따라서 이러한 시나리오에서 서버는 클라이언트에 다시 연결할 수 있는 기회를 제공하기 위해 대기하고 OnDisconnected 연결 끊기 시간 제한 기간이 만료될 때까지(기본적으로 약 30초) 실행되지 않습니다. 다음 다이어그램에서는 이 시나리오를 보여 줍니다.

클라이언트 컴퓨터 오류

서버 연결 끊기 시나리오

서버가 오프라인 상태가 되면 다시 부팅, 실패, 앱 도메인 재활용 등 -- 결과는 연결 끊김과 유사하거나 전송 API 및 SignalR이 서버가 사라졌다는 것을 즉시 알 수 있으며 SignalR은 이벤트를 발생 ConnectionSlow 하지 않고 다시 연결하려고 시작할 수 있습니다. 클라이언트가 다시 연결 모드로 전환되고 서버가 복구 또는 다시 시작되거나 연결 해제 시간 제한 기간이 만료되기 전에 새 서버가 온라인 상태가 되면 클라이언트가 복원된 서버 또는 새 서버에 다시 연결됩니다. 이 경우 클라이언트에서 SignalR 연결이 계속되고 Reconnected 이벤트가 발생합니다. 첫 번째 서버 OnDisconnected 에서는 가 실행되지 않으며 새 서버에서는 이전에 해당 서버 OnReconnected 에서 해당 클라이언트에 대해 실행되지 않았지만 OnConnected 이 실행됩니다. (다시 부팅하거나 앱 도메인을 재활용한 후 클라이언트가 동일한 서버에 다시 연결하는 경우 서버가 다시 시작될 때 이전 연결 작업의 메모리가 없으므로 효과가 동일합니다.) 다음 다이어그램에서는 전송 API가 손실된 연결을 즉시 인식하므로 ConnectionSlow 이벤트가 발생하지 않는다고 가정합니다.

서버 오류 및 다시 연결

연결 끊기 시간 내에 서버를 사용할 수 없게 되면 SignalR 연결이 종료됩니다. 이 시나리오 Closed 에서는 이벤트(disconnected JavaScript 클라이언트의 경우)가 클라이언트에서 발생하지만 OnDisconnected 서버에서 호출되지는 않습니다. 다음 다이어그램에서는 전송 API가 연결이 끊어지는 것을 인식하지 못하므로 SignalR keepalive 기능에 의해 검색되고 ConnectionSlow 이벤트가 발생한다고 가정합니다.

서버 오류 및 시간 제한

시간 제한 및 유지 설정

기본 ConnectionTimeout, DisconnectTimeoutKeepAlive 값은 대부분의 시나리오에 적합하지만 환경에 특별한 요구 사항이 있는 경우 변경할 수 있습니다. 예를 들어 네트워크 환경이 5초 동안 유휴 상태인 연결을 닫는 경우 유지 값을 줄여야 할 수 있습니다.

ConnectionTimeout

이 설정은 전송 연결을 열어 두고 응답을 닫고 새 연결을 열기 전에 대기하는 시간을 나타냅니다. 기본값은 110초입니다.

이 설정은 유지 기능이 비활성화된 경우에만 적용되며, 일반적으로 긴 폴링 전송에만 적용됩니다. 다음 다이어그램에서는 이 설정이 긴 폴링 전송 연결에 미치는 영향을 보여 줍니다.

긴 폴링 전송 연결

DisconnectTimeout

이 설정은 이벤트를 발생 Disconnected 하기 전에 전송 연결이 손실된 후 대기하는 시간을 나타냅니다. 기본값은 30초입니다. 를 설정 DisconnectTimeoutKeepAlive 하면 가 자동으로 값의 1/3으로 DisconnectTimeout 설정됩니다.

KeepAlive

이 설정은 유휴 연결을 통해 유지 패킷을 보내기 전에 대기하는 시간을 나타냅니다. 기본값은 10초입니다. 이 값은 값의 DisconnectTimeout 1/3을 초과하면 안 됩니다.

KeepAlive를 모두 DisconnectTimeout 설정하려면 다음을 DisconnectTimeout설정합니다KeepAlive. 그렇지 않으면 KeepAlive 시간 제한 값의 1/3으로 자동으로 설정 KeepAlive 하면 설정을 덮어씁니다DisconnectTimeout.

유지 기능을 사용하지 않도록 설정하려면 를 null로 설정합니다 KeepAlive . 긴 폴링 전송에 대해 유지 기능이 자동으로 비활성화됩니다.

시간 제한 및 유지 설정을 변경하는 방법

이러한 설정의 기본값을 변경하려면 다음 예제와 Application_Start 같이 Global.asax 파일에서 설정합니다. 샘플 코드에 표시된 값은 기본값과 동일합니다.

protected void Application_Start(object sender, EventArgs e)
{
    // Make long polling connections wait a maximum of 110 seconds for a
    // response. When that time expires, trigger a timeout command and
    // make the client reconnect.
    GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110);
    
    // Wait a maximum of 30 seconds after a transport connection is lost
    // before raising the Disconnected event to terminate the SignalR connection.
    GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);
    
    // For transports other than long polling, send a keepalive packet every
    // 10 seconds. 
    // This value must be no more than 1/3 of the DisconnectTimeout value.
    GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);
}

연결 끊김에 대해 사용자에게 알리는 방법

일부 애플리케이션에서는 연결 문제가 있을 때 사용자에게 메시지를 표시할 수 있습니다. 이 작업을 수행하는 방법과 시기에 대한 몇 가지 옵션이 있습니다. 다음 코드 샘플은 생성된 프록시를 사용하는 JavaScript 클라이언트에 대한 것입니다.

  • connectionSlow 다시 연결 모드로 전환되기 전에 SignalR이 연결 문제를 인식하는 즉시 메시지를 표시하도록 이벤트를 처리합니다.

    $.connection.hub.connectionSlow(function() {
        notifyUserOfConnectionProblem(); // Your function to notify user.
    });
    
  • SignalR이 reconnecting 연결 끊김을 인식하고 다시 연결 모드로 전환될 때 메시지를 표시하는 이벤트를 처리합니다.

    $.connection.hub.reconnecting(function() {
        notifyUserOfTryingToReconnect(); // Your function to notify user.
    });
    
  • disconnected 다시 연결 시도가 시간 초과된 경우 메시지를 표시하는 이벤트를 처리합니다. 이 시나리오에서 서버와의 연결을 다시 설정하는 유일한 방법은 새 연결 ID를 만드는 메서드를 호출 Start 하여 SignalR 연결을 다시 시작하는 것입니다. 다음 코드 샘플에서는 플래그를 사용하여 메서드 호출로 인한 Stop SignalR 연결이 정상적으로 종료된 후가 아니라 다시 연결 시간 제한 후에만 알림을 실행하도록 합니다.

    var tryingToReconnect = false;
    
    $.connection.hub.reconnecting(function() {
        tryingToReconnect = true;
    });
    
    $.connection.hub.reconnected(function() {
        tryingToReconnect = false;
    });
    
    $.connection.hub.disconnected(function() {
        if(tryingToReconnect) {
            notifyUserOfDisconnect(); // Your function to notify user.
        }
    });
    

지속적으로 다시 연결하는 방법

일부 애플리케이션에서는 연결이 끊어지고 다시 연결 시도 시간이 초과된 후 자동으로 연결을 다시 설정할 수 있습니다. 이렇게 하려면 이벤트 처리기(disconnectedJavaScript 클라이언트의 Start 이벤트 처리기)에서 Closed 메서드를 호출할 수 있습니다. 서버 또는 물리적 연결을 사용할 수 없을 때 이 작업을 너무 자주 수행하지 않도록 호출 Start 하기 전에 일정 시간을 기다려야 할 수 있습니다. 다음 코드 샘플은 생성된 프록시를 사용하는 JavaScript 클라이언트용입니다.

$.connection.hub.disconnected(function() {
   setTimeout(function() {
       $.connection.hub.start();
   }, 5000); // Restart connection after 5 seconds.
});

모바일 클라이언트에서 알아야 할 잠재적인 문제는 서버 또는 물리적 연결을 사용할 수 없는 경우 지속적인 다시 연결 시도로 인해 불필요한 배터리 소모가 발생할 수 있다는 것입니다.

서버 코드에서 클라이언트 연결을 끊는 방법

SignalR 버전 1.1.1에는 클라이언트 연결을 끊기 위한 기본 제공 서버 API가 없습니다. 나중에 이 기능을 추가할 계획이 있습니다. 현재 SignalR 릴리스에서 서버에서 클라이언트의 연결을 끊는 가장 간단한 방법은 클라이언트에서 disconnect 메서드를 구현하고 서버에서 해당 메서드를 호출하는 것입니다. 다음 코드 샘플에서는 생성된 프록시를 사용하는 JavaScript 클라이언트에 대한 연결 끊기 방법을 보여줍니다.

var myHubProxy = $.connection.myHub
myHubProxy.client.stopClient = function() {
    $.connection.hub.stop();
};

경고

보안 - 클라이언트 연결을 끊기 위한 이 방법이나 제안된 기본 제공 API는 클라이언트가 다시 연결할 수 있거나 해킹된 코드가 메서드를 제거 stopClient 하거나 수행하는 작업을 변경할 수 있으므로 악성 코드를 실행하는 해킹된 클라이언트의 시나리오를 다루지 않습니다. 상태 저장 DOS(서비스 거부) 보호를 구현하기 위한 적절한 위치는 프레임워크 또는 서버 계층이 아니라 프런트 엔드 인프라에 있습니다.