본문 바로가기
R&D

C#, 서버 시간으로 동기화하기 (HTTP 웹사이트 동기화)

by Client. DJ 2022. 8. 13.
반응형

서버시간은 어떻게 가져오는 것일까?

서버 시간을 가져와야하는 이유

대부분의 프로그램(앱)에서는 클라이언트와 서버 간의 통신이 존재합니다. 하지만 클라이언트에서 사용하는 시간이 있고, 서버에서 사용하는 시간이 따로 존재하며 이 둘은 서로 동기화가 필요합니다. 클라이언트에서 단순하게 'DateTime.Now' 사용한다면 이는 기기 시간 변경만으로도 에러 및 프로그램 내부적인 스케줄링에 치명적으로 영향을 줄 수 있습니다.

프로그램 시간 제약 조건

제가 생각하는 제약 조건은 아래와 같습니다.

  1. DateTime.Now와 같은 IDE에서 제공하는 기기 시간을 가져오는 코드 사용은 지양한다. 이는 클라이언트에 영향을 끼칠 수 있습니다.
  2. DateTime.Now 대신 사용할 시간을 따로 관리하는 로직을 구상하여 사용한다. 기기 시간을 조작해도 영향을 끼치지 않는 아키텍쳐가 필요합니다.
  3. 기기 시간은 서버에서 주기적으로 가져와 갱신을 합니다.

적용 방법

  1. 웹 통신의 경우, Headers 중 "Date" 헤더를 가져와서 읽습니다. HTTP 헤더 계층 구조에서 기본적으로 시간을 포함하여 통신을 하기에 기본적으로 송신자의 시간, 즉 서버 시간을 알 수가 있습니다.
  2. 웹 통신의 방식이 아닌 경우, 서버에서 시간 관련된 패킷을 보내어 적용해줍니다. 이 방식은 보통의 방식이 아닌 통신 모듈이 따로 명시되어있는 프로젝트에 한합니다. (TCP 또는 UDP)

스크립트

예시로 적용 방법의 1번 방법으로 웹 통신에서 시간을 가져와 적용하는 방법입니다.

using System;
using System.Net;
using System.Threading.Tasks;

public static class Time
{
    /// <summary>
    /// 클라이언트와 동기화된 시간 차이 (Tick)
    /// </summary>
    private static long diffTick = 0L;

    /// <summary>
    /// 기준 시간
    /// </summary>
    private static DateTime syncTime = DateTime.Now;

    /// <summary>
    /// 현재 시간
    /// </summary>
    public static DateTime NowTime => DateTime.Now.AddTicks(diffTick);

    /// <summary>
    /// UTC 시간 (KST + 9시간)
    /// </summary>
    public static DateTime UtcNowTime => NowTime.AddHours(9f);

    /// <summary>
    /// 동기화 시간 업데이트
    /// </summary>
    /// <param name="dateStr"></param>
    public static void UpdateDateTime(string dateStr)
    {
        if (DateTime.TryParse(dateStr, out DateTime dateTime))
        {
            syncTime = dateTime;
            diffTick = (syncTime - DateTime.Now).Ticks;
        }
    }

    /// <summary>
    /// 서버 동기화
    /// </summary>
    /// <param name="serverURL"></param>
    public static async Task Synchronization(string serverURL)
    {
        WebRequest request = WebRequest.Create(serverURL);
        WebResponse response = await request.GetResponseAsync();

        if (Array.Exists(response.Headers.AllKeys, headerKey => headerKey.Equals("Date")))
            UpdateDateTime(response.Headers["Date"]);
    }
}

예제

private static bool isCheckIn = false;

static void Main(string[] args)
{
    CheckServerTime();

    while (!isCheckIn)
    {
        Thread.Sleep(100);
    }
}

private static async void CheckServerTime()
{
    string url = "https://moondongjun.tistory.com//";

    // 서버와 클라이언트 시간 동기화
    await Time.Synchronization(url);

    // 서버 기준으로 만들어진 시간으로 사용할 수 있다.
    Console.WriteLine($"'{url}'의서버 시간 : {Time.NowTime}");	// DateTime.Now (x)
    
    isCheckIn = true;
}

서버와 클라이언트간 시간을 동기화하여 보다 밀도 높은 정확도로 현재 시간을 사용할 수 있습니다. 예제 코드에는 없지만, Time.UpdateDateTime()를 통하여 시간을 기준 시간을 조정할 수 있습니다. 이 함수를 통하여, 주기적으로 시간을 받아오는 것이 좋겠습니다. PC의 성능은 모두 제각각이기에 주기적으로 동기화를 해주지 않는다면, 결국 서버와 클라이언트 사이에는 시간이 미세하게 점점 더 격차가 생길 수 있습니다.

마무리

개인 프로젝트로 웹 통신을 사용하는 중인데, 기기 시간 조작에 걱정이 되어 알아보던 중, 헤더 계층 구조에 시간이 존재한다는 것을 알고 사용하게 되었습니다. 장기간 켜두는 과정에서 시간 오차가 생기는 이슈가 있었는데, 주기적으로 웹 통신하는 과정 중, 시간을 계속 갱신하여 이상 없이 적용 중입니다.😁

반응형

댓글