카테고리 보관물: 개발

단기기억 훈련 게임 앱

개요

어느 방송에서 그림을 짧은 시간 보여주고 특정한 사물을 맞추는 게임을 보게 되었습니다. 다른 곳에서도 종종 보았던 것인데 앱으로 만들어 보면 좋을 것 같았습니다. 그래서 단기기억 훈련 게임 앱을 제작하게 되었습니다.

제작

핵심은 몇 가지 사물을 지정된 시간을 보여주고 가려주는 기능이었습니다. 보여주는 사물을 몇 가지로 해야할지 결정은 하지 않고 일단 화면을 구성해서 에뮬레이터와 실제 기기에서 확인해 보았습니다.

단기기억 훈련 게임 앱 처음 화면
단기기억 훈련 게임 앱 처음 화면

이미지 버튼을 나열하는 방식으로 진행하였는데 생각보다 크기가 작아서 더 확대했습니다. 그래서 총 36개의 버튼이 표시되도록 했고 나타나는 사물(아이콘)은 9개로 정했습니다. 그래서 총 4개를 찾으면 게임이 끝나도록 작업했습니다.

실제로 플레이 해 보니 암기할 수 있는 시간이 너무 부족하다는 생각이 들었습니다. 난이도 조절을 암기할 수 있는 시간을 늘이고 줄이는 방식으로 결정했습니다.

아이콘을 모두 찾는데 소요된 시간과 몇번 시도해서 찾았는지 알 수 있도록 했습니다.

모두 찾은 상태

개발하면서 여러번 반복해서 플레이 해 보니 뭔가 훈련이 되는 것 같은 생각이 들었습니다.

부담없이 짧은 시간 게임을 플레이 하면서 기억력 향상에 도움이 되었으면 합니다.

다음 주소에서 확인하실 수 있고 하단 배너 광고가 포함되어 있습니다.

https://play.google.com/store/apps/details?id=jaeyoung.kim.shorttermmemorytraining

OpenXML을 이용한 엑셀파일 읽기 성능 향상

개요

C#을 이용해서 엑셀파일을 읽어들이는 방법이 여러가지가 있습니다. 필자는 주로 OpenXML 을 이용합니다. 파일을 읽어 가공을 하던 중 자료의 양에 비해 속도가 지나치게 떨어지는 현상을 발견했습니다. 대상이 되는 파일이 많아서 아주 많은 시간이 소요될 것으로 예상되었습니다. 아무리 생각해도 정상적인 상황이 아니라는 판단이 들어 더 분석해 보기로 했습니다.

성능 저하 원인

결과부터 이야기하면 텍스트 형태의 셀 자료를 가져오는 부분때문에 발생한 문제였습니다. OpenXML 을 이용해서 엑셀을 읽어오는 글을 보면 셀의 자료를 가져오는 부분이 다음과 같은 형태로 구현되어 있습니다.

string value = cell.CellValue.InnerText;

if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
    return doc.WorkbookPart.SharedStringTablePart.SharedStringTable.ChildElements.GetItem(int.Parse(value)).InnerText;
    //return doc.WorkbookPart.SharedStringTablePart.SharedStringTable.ElementAt(int.Parse(value)).InnerText;
}

속도가 심각하게 저하되는 부분이 5, 6행 입니다. 엑셀을 읽을 때 행 단위로 반복하게 됩니다. 그 안에서 각 컬럼(셀)의 값을 읽어오게 됩니다. 셀이 텍스트 자료인 경우 1행의 결과가 숫자로 나타납니다. 그래서 3행의 조건을 통해 실제 텍스트 값을 가져오게 됩니다.

자료의 개수가 많지 않은 경우는 문제가 되지 않습니다. 그러나 많아지게 되면 심각한 속도저하가 발생합니다.

SharedStringTable

SharedStringTable 은 텍스트 값을 효율적으로 저장하기 위해 만들어진 객체입니다. 엑셀같은 형식의 자료 특성상 같은 텍스트가 반복될 여지가 많기 때문에 중복을 최소화 해서 관리합니다.

어찌보면 당연한 것인데 반복문 안에서 크기가 큰 SharedStringTable 객체를 계속 참조해서 속도 저하가 일어나는 것 입니다.

해결책(Dictionary 이용)

읽어들인 엑셀파일의 SharedStringTable 을 반복문이 시작되기 전 Dictionary 객체에 담아놓고 참조하면 성능이 크게 향상됩니다. 다음의 코드가 설명한 방식으로 구현된 것 입니다.

var dictionary = new Dictionary<int, string>();

var excelFile = @"your_excel_file_path";

using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(excelFile, false))
{
    var sharedStringTablePart = spreadsheetDocument.WorkbookPart.SharedStringTablePart;
    using (OpenXmlReader reader = OpenXmlReader.Create(sharedStringTablePart))
    {
        int i = 0;
        while (reader.Read())
        {
            if (reader.ElementType == typeof(SharedStringItem))
            {
                SharedStringItem ssi = (SharedStringItem)reader.LoadCurrentElement();
                dictionary.Add(i++, ssi.Text != null ? ssi.Text.Text : string.Empty);
            }
        }
    }

    .
    .
    .

    foreach (Row row in rows)
    {
        foreach (Cell cell in row.Descendants<Cell>())
        {
            string value = cell.CellValue.InnerText;

            if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
            {
                var cellValue = dictionary[int.Parse(value)];
                //your code
            }
        }
    }
}

1행에서 SharedStringTable 의 값을 담아둘 Dictionary 변수를 선언합니다. 7~19행 까지 SharedStringTable 객체를 읽어서 변수에 추가합니다. 33행에서 텍스트 값을 SharedStringTable 객체가 아닌 Dictionary 변수에서 참조해서 가져옵니다. 이렇게 하면 무거운 SharedStringTable 객체를 반복문 내에서 참조하지 않아 금방 처리됩니다.

OpenXML 로 엑셀처리 시 성능 문제로 어려움을 겪으신 분들 도움이 되시기를 바랍니다.