INI 파일을 활용한 간단한 GUI 데이터베이스 시스템 구현

소규모 프로젝트에서는 SQLite와 같은 전용 데이터베이스를 구축하지 않고도 INI 설정 파일을 사용하여 기본적인 데이터베이스 기능을 구현할 수 있습니다. 이 문서에서는 Windows Forms를 이용해 INI 파일 기반의 최소한의 CRUD(생성, 조회, 수정, 삭제) 기능을 갖춘 간단한 GUI 데이터베이스 시스템을 설계합니다.

INI 파일 구조

INI 파일은 다음과 같은 형식으로 구성됩니다:

[섹션1]
키1=값1
키2=값2

[섹션2]
키3=값3
키4=값4
...

GUI 구성 요소

데이터베이스를 시각화하기 위해 ComboBox 컨트롤을 사용합니다. 주요 인터페이스 요소는 다음과 같습니다:

  • cmb_region: 섹션 목록 표시
  • cmb_item: 선택된 섹션의 키 목록 표시
  • tbx_data: 값 입력/수정 필드

핵심 기능 구현

1. 모든 섹션 목록 가져오기

public void ExtractAllSections(string fileName, out List<string> sectionCollection)
{
    sectionCollection = new List<string>();
    string content = "";

    try
    {
        using (StreamReader reader = new StreamReader(fileName, Encoding.Default))
        {
            content = reader.ReadToEnd();
        }
    }
    catch (Exception)
    {
        MessageBox.Show($"파일 {fileName}을(를) 읽을 수 없습니다.");
        return;
    }

    try
    {
        string[] segments = content.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries);
        
        foreach (string segment in segments)
        {
            string[] parts = segment.Split(new char[] { ']' }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length > 0 && !string.IsNullOrWhiteSpace(parts[0]))
            {
                sectionCollection.Add(parts[0].Trim());
            }
        }
    }
    catch (Exception)
    {
        // 예외 처리
    }
}

2. 특정 섹션의 모든 키 추출

public void ExtractKeysInSection(string fileName, out List<string> keyCollection, int sectionIndex)
{
    keyCollection = new List<string>();

    try
    {
        string content;
        using (StreamReader reader = new StreamReader(fileName, Encoding.Default))
        {
            content = reader.ReadToEnd();
        }

        string[] sections = content.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries);
        if (sectionIndex + 1 >= sections.Length) return;

        string[] sectionParts = sections[sectionIndex + 1].Split(new char[] { ']' }, StringSplitOptions.RemoveEmptyEntries);
        if (sectionParts.Length < 2) return;

        string keyValueContent = sectionParts[1];
        keyValueContent = keyValueContent.Replace("\r\n", "|");
        string[] entries = keyValueContent.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);

        foreach (string entry in entries)
        {
            if (!string.IsNullOrWhiteSpace(entry))
            {
                string[] keyValue = entry.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                if (keyValue.Length > 0 && !string.IsNullOrWhiteSpace(keyValue[0]))
                {
                    keyCollection.Add(keyValue[0].Trim());
                }
            }
        }
    }
    catch (Exception)
    {
        MessageBox.Show($"파일 {fileName} 처리 중 오류가 발생했습니다.");
    }
}

3. 리스트 항목 ComboBox에 추가

public void PopulateComboBox(List<string> dataSource, ComboBox targetCombo)
{
    targetCombo.Items.Clear();
    foreach (string item in dataSource)
    {
        targetCombo.Items.Add(item);
    }
}

전체 애플리케이션 코드

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace SimpleIniDatabase
{
    public partial class DatabaseForm : Form
    {
        private const string DATA_FILE = "database.ini";

        public DatabaseForm()
        {
            InitializeComponent();
            this.StartPosition = FormStartPosition.CenterScreen;
        }

        private void DatabaseForm_Load(object sender, EventArgs e)
        {
            RefreshSections();
        }

        private void RefreshSections()
        {
            List<string> sections = new List<string>();
            List<string> firstSectionKeys = new List<string>();

            ExtractAllSections(DATA_FILE, out sections);
            PopulateComboBox(sections, cmb_region);

            if (sections.Count > 0)
            {
                ExtractKeysInSection(DATA_FILE, out firstSectionKeys, 0);
                PopulateComboBox(firstSectionKeys, cmb_item);
            }
        }

        private void cmb_region_SelectedIndexChanged(object sender, EventArgs e)
        {
            int selectedIndex = cmb_region.SelectedIndex;
            cmb_item.Items.Clear();
            cmb_item.Text = "";

            List<string> keys = new List<string>();
            ExtractKeysInSection(DATA_FILE, out keys, selectedIndex);
            PopulateComboBox(keys, cmb_item);
        }

        private void btn_delete_Click(object sender, EventArgs e)
        {
            try
            {
                string currentSection = cmb_region.Text;
                string currentKey = cmb_item.Text;
                string currentValue = ReadValue(DATA_FILE, currentSection, currentKey);
                
                string entryToRemove = $"{currentKey}={currentValue}";
                
                string fileContent;
                using (StreamReader reader = new StreamReader(DATA_FILE, Encoding.Default))
                {
                    fileContent = reader.ReadToEnd();
                }

                fileContent = fileContent.Replace(entryToRemove, "");

                using (StreamWriter writer = new StreamWriter(DATA_FILE, false, Encoding.Default))
                {
                    writer.Write(fileContent);
                }

                MessageBox.Show("항목이 성공적으로 삭제되었습니다.");
                RefreshSections();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"삭제 작업 중 오류 발생: {ex.Message}");
            }
        }

        private void btn_update_Click(object sender, EventArgs e)
        {
            string section = cmb_region.Text.Trim();
            string key = cmb_item.Text.Trim();
            string value = tbx_data.Text.Trim();

            WriteValue(DATA_FILE, section, key, value);
            MessageBox.Show("데이터가 성공적으로 업데이트되었습니다.");
        }

        // 섹션 추출 메서드
        public void ExtractAllSections(string fileName, out List<string> sectionCollection)
        {
            sectionCollection = new List<string>();
            string content = "";

            try
            {
                using (StreamReader reader = new StreamReader(fileName, Encoding.Default))
                {
                    content = reader.ReadToEnd();
                }
            }
            catch (Exception)
            {
                MessageBox.Show($"파일 {fileName}을(를) 읽을 수 없습니다.");
                return;
            }

            try
            {
                string[] segments = content.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries);
                
                foreach (string segment in segments)
                {
                    string[] parts = segment.Split(new char[] { ']' }, StringSplitOptions.RemoveEmptyEntries);
                    if (parts.Length > 0 && !string.IsNullOrWhiteSpace(parts[0]))
                    {
                        sectionCollection.Add(parts[0].Trim());
                    }
                }
            }
            catch (Exception)
            {
                // 예외 처리
            }
        }

        // 키 추출 메서드
        public void ExtractKeysInSection(string fileName, out List<string> keyCollection, int sectionIndex)
        {
            keyCollection = new List<string>();

            try
            {
                string content;
                using (StreamReader reader = new StreamReader(fileName, Encoding.Default))
                {
                    content = reader.ReadToEnd();
                }

                string[] sections = content.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries);
                if (sectionIndex + 1 >= sections.Length) return;

                string[] sectionParts = sections[sectionIndex + 1].Split(new char[] { ']' }, StringSplitOptions.RemoveEmptyEntries);
                if (sectionParts.Length < 2) return;

                string keyValueContent = sectionParts[1];
                keyValueContent = keyValueContent.Replace("\r\n", "|");
                string[] entries = keyValueContent.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);

                foreach (string entry in entries)
                {
                    if (!string.IsNullOrWhiteSpace(entry))
                    {
                        string[] keyValue = entry.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                        if (keyValue.Length > 0 && !string.IsNullOrWhiteSpace(keyValue[0]))
                        {
                            keyCollection.Add(keyValue[0].Trim());
                        }
                    }
                }
            }
            catch (Exception)
            {
                MessageBox.Show($"파일 {fileName} 처리 중 오류가 발생했습니다.");
            }
        }

        // ComboBox 채우기 메서드
        public void PopulateComboBox(List<string> dataSource, ComboBox targetCombo)
        {
            targetCombo.Items.Clear();
            foreach (string item in dataSource)
            {
                targetCombo.Items.Add(item);
            }
        }

        #region INI 파일 읽기/쓰기 API

        [DllImport("kernel32")]
        private static extern int GetPrivateProfileString(string section, string key, string defaultValue,
            StringBuilder result, int size, string filePath);

        [DllImport("kernel32")]
        private static extern long WritePrivateProfileString(string section, string key, string value, string filePath);

        public string ReadValue(string fileName, string section, string key)
        {
            var buffer = new StringBuilder(255);
            string fullPath = Path.Combine(Environment.CurrentDirectory, fileName);
            GetPrivateProfileString(section, key, "", buffer, 255, fullPath);
            return buffer.ToString();
        }

        public void WriteValue(string fileName, string section, string key, string value)
        {
            string fullPath = Path.Combine(Environment.CurrentDirectory, fileName);
            WritePrivateProfileString(section, key, value, fullPath);
        }

        #endregion
    }
}

참고: 위 코드는 학습 목적의 최소 구현이며, 실제 프로덕션 환경에서는 오류 처리 및 성능 최적화가 필요합니다.

태그: C# Windows Forms INI 파일 데이터베이스 GUI

7월 4일 02:08에 게시됨