고양국제고

GGHS Time Table 5 개발 스토리 #4: 최적화를 위해

카루-R 2022. 2. 23. 10:48
반응형

환영합니다, Rolling Ress의 카루입니다.


사실 지금쯤이라면 GGHS Time Table 5 베타4가 올라갔어야 합니다. 그런데 제가 실수로 개발자용 기능을 모두에게 열어두고 제출한 바람에(...) 급하게 내렸습니다. 소소하게 Bugfix를 하고 있었는데, 기존 채팅 기능에서 문제가 있었던 알고리즘을 같이 손보기로 했습니다.

이게 ReloadChat() 메서드 본문입니다. 보시면 아시겠지만...ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 맞아요. 잘못 본 거 아닙니다. 매 초마다 (chatDelay = 1000) 서버에 있는 모든 내용을 통으로 긁어와서 채팅창에 뿌리는 겁니다. 새로 보낸 메시지가 없더라도. 이게 뭔... 그래요. 저땐 그걸 생각을 못했습니다.

private async Task LoadChatsAsync()
{
    string txt = textBox.PlaceholderText;
    textBox.PlaceholderText = "채팅 불러오는 중...";

    DataSet ds = new();
    object result = new();
    using (SqlConnection sql = new(ConnectionString))
    {
        using SqlCommand cmd = new(@"select max(Time) from chatmsg", sql);
        SqlDataAdapter sda = new("SELECT * FROM chatmsg ORDER BY Time", sql);

        // Start SQL
        await sql.OpenAsync();
        result = await cmd.ExecuteScalarAsync();
        sda.Fill(ds, "chatmsg");
    } // End SQL

    DataTable dt = ds.Tables["chatmsg"];
    StringBuilder sb = new();
    foreach (DataRow row in dt.Rows)
      sb.AppendFormat(chatFormat, DateTime.Parse(row["Time"].ToString()), Convert((byte)row["Sender"]), row["Message"]);
    

    LastSqlString = ((DateTime)result).ToString("yyyy-MM-dd HH:mm:ss.fff");
    viewBox.Text = StringBuffer = sb.ToString();
    ScrollViewBox();
    textBox.PlaceholderText = txt;
}

그래서 제가 생각해낸 방법이, 처음에만 싹 다 긁어오고 그 뒤부터는 새로 갱신된 내용만 가져오자는 거였어요. 말이 쉽지, 구현하기 힘듭니다. 일단, 가장 마지막에 보낸 메시지의 '시간'을 가져옵니다.

 
 

이것때문에 로딩 시간은 약 0.1~0.2초 정도 더 걸리더라고요. (그래서 현재는 약 1.6초 정도) 근데 로딩은 괜찮아요. 어차피 처음 한 번은 참을 수 있잖아요?

private async Task ReloadChatsAsync()
{
    while (true)
    {
        // TODO 정식버전에선 없애기
#if BETA
        try
        {
#endif
            string query1 = $"SELECT COUNT(*) FROM chatmsg WHERE Time > '{LastSqlString}'";
            string query2 = $"SELECT * FROM chatmsg WHERE Time > '{LastSqlString}' ORDER BY Time";

            while (isReloadPaused)
                await Task.Delay(400);

            if (isCancelRequested)
                return;

            SqlConnection sql = new(ConnectionString);
            SqlCommand cmd = new(query1, sql);
            await sql.OpenAsync();

            if (await cmd.ExecuteScalarAsync() is 0)
            {
                sql.Close();
                await Task.Delay(800);
                continue;
            }

            cmd.CommandText = query2;
            StringBuilder sb = new();
            using (SqlDataReader reader = cmd.ExecuteReader())
                while (reader.Read())
                {
                    sb.AppendFormat(chatFormat, reader.GetDateTime(1), Convert(reader.GetByte(0)), reader.GetString(2));
                }
            cmd.CommandText = "select max(Time) from chatmsg";
            object result = await cmd.ExecuteScalarAsync();
            sql.Close();

            LastSqlString = ((DateTime)result).ToString("yyyy-MM-dd HH:mm:ss.fff");
            StringBuffer += sb.ToString();
            viewBox.Text = StringBuffer;
#if BETA
        }
        catch (Exception ex)
        {
            await ShowMessageAsync(ex.ToString(), "오류가 발생했습니다.");
            throw;
        }
#endif
    }
}

그래서 백그라운드 스레드 부분을 새로 만들었습니다. 기존처럼 무식하게 모든 테이블을 읽는 게 아니라, "새로 작성된 메시지가 있을 때에만" 해당 부분을 추가하는 겁니다. SQL단에서 WHERE과 ORDER BY문을 통해 쉽게 제어 가능합니다. 다만 그걸 받아와서 추가하는 건 C# 이 할 일이죠.

그래서 이제 마지막 테스트 겸 Release Build로 지으려고 했더니... 또 'Unable to activate Windows Store app. The activation request failed with error 'The app didn't start'. 오류가 떠버립니다. 이건...마이크로소프트의 버그라 제가 뭐 어쩔 수가 없어요. 이거 왜 안 고쳐주는지. 그냥 어느 순간 됩니다. 무시하고 바로 스토어에 올리려고요.


GGHS 10기, Anonymous 채팅방에서 만나요 ;)

반응형