파일 다루기

파일 다루기

Reflection
Published

May 17, 2025

Abstract

dynamic은 컴파일 타임 형식 검사를 생략하고 런타임에 형식 및 멤버 접근의 유효성을 확인하는 특별한 형식입니다. 이를 통해 정적 형식 언어인 C#에서도 동적 형식 지정의 유연성을 활용할 수 있으며, 특히 COM 상호 운용성이나 동적 언어와의 연동 시 유용합니다.

파일 정보와 디렉터리 정보 다루기

C#에서는 System.IO 네임스페이스를 통해 파일과 디렉터리를 관리할 수 있습니다. 주요 클래스로는 File, Directory, FileInfo, DirectoryInfo가 있습니다.

  • File/Directory: 정적 메서드를 제공하는 클래스
  • FileInfo/DirectoryInfo: 인스턴스 메서드를 제공하는 클래스

예제 프로그램: 디렉터리/파일 정보 조회하기

using System;
using System.IO;

class FileInfoExample
{
    static void Main()
    {        
        string dirPath = @"C:\Example";
        if (Directory.Exists(dirPath))
        {
            Console.WriteLine("디렉터리 존재함: " + dirPath);
            
            DirectoryInfo di = new DirectoryInfo(dirPath);
            
            Console.WriteLine("이름: " + di.Name);
            Console.WriteLine("생성 시간: " + di.CreationTime);
            Console.WriteLine("마지막 접근 시간: " + di.LastAccessTime);
            
            FileInfo[] files = di.GetFiles();
            Console.WriteLine("\n파일 목록:");
            foreach (FileInfo file in files)
            {
                Console.WriteLine($"파일명: {file.Name}, 크기: {file.Length}바이트");
            }
            
            DirectoryInfo[] dirs = di.GetDirectories();
            Console.WriteLine("\n하위 디렉터리 목록:");
            foreach (DirectoryInfo dir in dirs)
            {
                Console.WriteLine($"디렉터리명: {dir.Name}");
            }
        }
        else
        {
            Console.WriteLine("디렉터리가 존재하지 않습니다. " + dirPath);
        }
        
        string filePath = @"C:\Example\sample.txt";
        if (File.Exists(filePath))
        {
            Console.WriteLine("\n파일 존재함: " + filePath);
            
            FileInfo fi = new FileInfo(filePath);
            
            Console.WriteLine("파일명: " + fi.Name);
            Console.WriteLine("확장자: " + fi.Extension);
            Console.WriteLine("크기: " + fi.Length + "바이트");
            Console.WriteLine("생성 시간: " + fi.CreationTime);
            Console.WriteLine("마지막 수정 시간: " + fi.LastWriteTime);
            Console.WriteLine("읽기 전용: " + fi.IsReadOnly);
        }
        else
        {
            Console.WriteLine("\n파일이 존재하지 않습니다. " + filePath);
        }
    }
}

예제 프로그램: 디렉터리/파일 생성하기

using System;
using System.IO;

class CreateFileDirectoryExample
{
    static void Main()
    {
        string dirPath = @"C:\Example\NewFolder";
        try
        {
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
                Console.WriteLine("디렉터리 생성됨: " + dirPath);
            }
            else
            {
                Console.WriteLine("이미 디렉터리가 존재합니다. " + dirPath);
            }
            
            string subDirPath = Path.Combine(dirPath, "SubFolder");
            DirectoryInfo di = new DirectoryInfo(subDirPath);
            if (!di.Exists)
            {
                di.Create();
                Console.WriteLine("하위 디렉터리 생성됨: " + subDirPath);
            }
            
            string filePath = Path.Combine(dirPath, "newFile.txt");
            
            if (!File.Exists(filePath))
            {
                File.Create(filePath).Close();
                Console.WriteLine("빈 파일 생성됨: " + filePath);
                
                string textFilePath = Path.Combine(dirPath, "textFile.txt");
                File.WriteAllText(textFilePath, "안녕하세요. 이것은 텍스트 파일입니다.");
                Console.WriteLine("텍스트 파일 생성됨: " + textFilePath);
                
                string binaryFilePath = Path.Combine(dirPath, "binaryFile.dat");
                byte[] bytes = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F }; // "Hello"의 ASCII 코드
                File.WriteAllBytes(binaryFilePath, bytes);
                Console.WriteLine("바이너리 파일 생성됨: " + binaryFilePath);
            }
            
            string anotherFilePath = Path.Combine(dirPath, "anotherFile.txt");
            FileInfo fi = new FileInfo(anotherFilePath);
            if (!fi.Exists)
            {
                using (StreamWriter sw = fi.CreateText())
                {
                    sw.WriteLine("FileInfo 클래스를 사용한 파일 생성");
                }
                Console.WriteLine("FileInfo로 파일 생성됨: " + anotherFilePath);
            }
            
            string copyFilePath = Path.Combine(dirPath, "copyFile.txt");
            fi.CopyTo(copyFilePath, true); // true: 기존 파일 덮어쓰기
            Console.WriteLine("파일 복사됨: " + copyFilePath);
            
            string moveFilePath = Path.Combine(subDirPath, "movedFile.txt");
            fi.MoveTo(moveFilePath);
            Console.WriteLine("파일 이동됨: " + moveFilePath);
        }
        catch (Exception ex)
        {
            Console.WriteLine("오류 발생: " + ex.Message);
        }
    }
}

System.IO.Stream 클래스

Stream은 모든 입출력 작업의 기본이 되는 추상 클래스입니다. 대표적인 파생 클래스는 다음과 같습니다.

  • FileStream: 파일에서 데이터를 읽고 쓰는 스트림
  • MemoryStream: 메모리에서 데이터를 읽고 쓰는 스트림
  • NetworkStream: 네트워크 소켓에서 데이터를 읽고 쓰는 스트림
  • BufferedStream: 버퍼를 사용하여 읽기/쓰기 성능을 향상시키는 스트림
using System;
using System.IO;

class StreamExample
{
    static void Main()
    {
        string filePath = @"C:\Example\stream.dat";
        
        using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
        {
            byte[] data = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F }; // "Hello"
            fs.Write(data, 0, data.Length);
            Console.WriteLine($"{data.Length}바이트를 파일에 작성했습니다.");
        }
        
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
        {
            byte[] buffer = new byte[fs.Length];
            int bytesRead = fs.Read(buffer, 0, buffer.Length);
            Console.WriteLine($"{bytesRead}바이트를 파일에서 읽었습니다.");
            
            Console.Write("데이터: ");
            foreach (byte b in buffer)
            {
                Console.Write($"{b:X2} ");
            }
            Console.WriteLine();
            
            string text = System.Text.Encoding.ASCII.GetString(buffer);
            Console.WriteLine("텍스트: " + text);
        }
        
        using (MemoryStream ms = new MemoryStream())
        {
            byte[] data = new byte[] { 0x57, 0x6F, 0x72, 0x6C, 0x64 }; // "World"
            ms.Write(data, 0, data.Length);
            
            ms.Position = 0;
            
            byte[] buffer = new byte[ms.Length];
            ms.Read(buffer, 0, buffer.Length);
            
            string text = System.Text.Encoding.ASCII.GetString(buffer);
            Console.WriteLine("MemoryStream 데이터: " + text);
        }
    }
}

실수를 줄여주는 using 선언

using 문은 IDisposable 인터페이스를 구현하는 객체의 Dispose 메서드가 자동으로 호출되도록 보장합니다. 이는 파일 핸들과 같은 자원을 안전하게 해제하는 데 매우 중요합니다.

using System;
using System.IO;

class UsingExample
{
    static void Main()
    {
        string filePath = @"C:\Example\using_example.txt";
        
        using (StreamWriter writer = new StreamWriter(filePath))
        {
            writer.WriteLine("이것은 전통적인 using 문입니다.");
        } 
        
        void UsingDeclaration()
        {
            using StreamReader reader = new StreamReader(filePath);
            string line = reader.ReadLine();
            Console.WriteLine("읽은 내용: " + line);
        }
        
        UsingDeclaration();
        
        void TryFinallyExample()
        {
            StreamWriter writer = null;
            try
            {
                writer = new StreamWriter(filePath, true);
                writer.WriteLine("이것은 try-finally 예제입니다.");
            }
            finally
            {
                if (writer != null)
                {
                    writer.Dispose();
                }
            }
        }
        
        TryFinallyExample();
        
        using (StreamWriter writer = new StreamWriter(filePath, true))
        using (StreamReader reader = new StreamReader(@"C:\Example\input.txt"))
        {
            string line = reader.ReadLine();
            if (line != null)
            {
                writer.WriteLine("복사된 내용: " + line);
            }
        }
    }
}

이진 데이터 처리를 위한 BinaryWriter/BinaryReader

BinaryWriterBinaryReader는 기본 데이터 타입을 이진 형식으로 쓰고 읽을 수 있게 해주는 클래스입니다.

using System;
using System.IO;

class BinaryIOExample
{
    static void Main()
    {
        string filePath = @"C:\Example\binary_data.bin";
        
        using (FileStream fs = new FileStream(filePath, FileMode.Create))
        using (BinaryWriter writer = new BinaryWriter(fs))
        {
            writer.Write(42);         // int
            writer.Write(3.14159);    // double
            writer.Write(true);       // bool
            writer.Write("안녕하세요"); // string
            writer.Write(new byte[] { 0x01, 0x02, 0x03 }); // byte[]
            
            Console.WriteLine("이진 데이터 쓰기 완료.");
        }
        
        using (FileStream fs = new FileStream(filePath, FileMode.Open))
        using (BinaryReader reader = new BinaryReader(fs))
        {
            int intValue = reader.ReadInt32();
            double doubleValue = reader.ReadDouble();
            bool boolValue = reader.ReadBoolean();
            string stringValue = reader.ReadString();
            byte[] byteArray = reader.ReadBytes(3);
            
            Console.WriteLine($"읽은 정수: {intValue}");
            Console.WriteLine($"읽은 실수: {doubleValue}");
            Console.WriteLine($"읽은 불리언: {boolValue}");
            Console.WriteLine($"읽은 문자열: {stringValue}");
            Console.Write("읽은 바이트 배열: ");
            foreach (byte b in byteArray)
            {
                Console.Write($"{b:X2} ");
            }
            Console.WriteLine();
        }
        
        using (FileStream fs = new FileStream(filePath, FileMode.Create))
        using (BinaryWriter writer = new BinaryWriter(fs))
        {
            Person person = new Person
            {
                Id = 1001,
                Name = "홍길동",
                Age = 30,
                Salary = 5000000.50
            };
            
            writer.Write(person.Id);
            writer.Write(person.Name);
            writer.Write(person.Age);
            writer.Write(person.Salary);
            
            Console.WriteLine("구조체 데이터 쓰기 완료.");
        }
        
        using (FileStream fs = new FileStream(filePath, FileMode.Open))
        using (BinaryReader reader = new BinaryReader(fs))
        {
            Person person = new Person
            {
                Id = reader.ReadInt32(),
                Name = reader.ReadString(),
                Age = reader.ReadInt32(),
                Salary = reader.ReadDouble()
            };
            
            Console.WriteLine("\n구조체 데이터 읽기 결과:");
            Console.WriteLine($"ID: {person.Id}");
            Console.WriteLine($"이름: {person.Name}");
            Console.WriteLine($"나이: {person.Age}");
            Console.WriteLine($"급여: {person.Salary:C}");
        }
    }
    
    struct Person
    {
        public int Id;
        public string Name;
        public int Age;
        public double Salary;
    }
}

텍스트 파일 처리를 위한 StreamWriter/StreamReader

StreamWriterStreamReader는 텍스트 파일을 다루기 위한 고수준 클래스입니다.

using System;
using System.IO;
using System.Text;

class TextFileExample
{
    static void Main()
    {
        string filePath = @"C:\Example\text_file.txt";
        
        using (StreamWriter writer = new StreamWriter(filePath, false, Encoding.UTF8))
        {
            writer.WriteLine("안녕하세요! StreamWriter 예제입니다.");
            writer.WriteLine("두 번째 줄입니다.");
            writer.WriteLine("숫자: {0}, 실수: {1:F2}", 42, 3.14159);
            
            for (int i = 1; i <= 5; i++)
            {
                writer.Write(i + " ");
            }
            writer.WriteLine();
            
            writer.WriteLine("파일의 마지막 줄입니다.");
            
            Console.WriteLine("텍스트 파일 쓰기 완료.");
        }
        
        using (StreamReader reader = new StreamReader(filePath, Encoding.UTF8))
        {
            Console.WriteLine("\n파일 내용 (한 줄씩 읽기):");
            int lineNumber = 1;
            
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                Console.WriteLine($"{lineNumber++}: {line}");
            }
        }
        
        string allText = File.ReadAllText(filePath, Encoding.UTF8);
        Console.WriteLine("\n파일 전체 내용:");
        Console.WriteLine(allText);
        
        string[] allLines = File.ReadAllLines(filePath, Encoding.UTF8);
        Console.WriteLine("\n파일 줄 배열 (인덱스 접근):");
        for (int i = 0; i < allLines.Length; i++)
        {
            Console.WriteLine($"줄 {i}: {allLines[i]}");
        }
        
        using (StreamWriter writer = new StreamWriter(filePath, true, Encoding.UTF8))
        {
            writer.WriteLine("\n이 텍스트는 기존 파일에 추가됩니다.");
            Console.WriteLine("파일에 텍스트 추가 완료.");
        }
        
        using (StreamReader reader = new StreamReader(filePath, Encoding.UTF8))
        {
            Console.WriteLine("\n파일 내용 (문자 단위 읽기, 처음 20자):");
            for (int i = 0; i < 20; i++)
            {
                int charCode = reader.Read();
                if (charCode == -1) break; // 파일의 끝
                Console.Write((char)charCode);
            }
            Console.WriteLine("...");
        }
    }
}

객체 직렬화하기

객체 직렬화는 객체의 상태를 바이트 스트림으로 변환하여 파일, 메모리, 네트워크 등에 저장하거나 전송할 수 있게 해주는 기술입니다.

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.Text.Json;

[Serializable]
class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    
    [NonSerialized]
    private string password;
    
    public Person() { }
    
    public Person(int id, string name, int age)
    {
        Id = id;
        Name = name;
        Age = age;
    }
    
    public override string ToString()
    {
        return $"Id: {Id}, 이름: {Name}, 나이: {Age}";
    }
}

[Serializable]
public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    
    [XmlIgnore]
    public string SecretCode { get; set; }
    
    public Product() { }
    
    public Product(int id, string name, decimal price)
    {
        ProductId = id;
        Name = name;
        Price = price;
    }
    
    public override string ToString()
    {
        return $"제품 ID: {ProductId}, 이름: {Name}, 가격: {Price:C}";
    }
}

class SerializationExample
{
    static void Main()
    {
        string binaryFilePath = @"C:\Example\person.bin";
        string xmlFilePath = @"C:\Example\product.xml";
        string jsonFilePath = @"C:\Example\product.json";
        
        Person person = new Person(1, "홍길동", 30);
        
        Console.WriteLine("원본 객체: " + person);
        
        BinarySerialize(person, binaryFilePath);
        
        Person deserializedPerson = BinaryDeserialize(binaryFilePath);
        Console.WriteLine("역직렬화된 객체: " + deserializedPerson);
        
        Product product = new Product(101, "노트북", 1500000);
        product.SecretCode = "SECRET123"; // 직렬화에서 제외됨
        
        Console.WriteLine("\n원본 제품: " + product);
        
        XmlSerialize(product, xmlFilePath);
        
        Product deserializedProduct = XmlDeserialize<Product>(xmlFilePath);
        Console.WriteLine("역직렬화된 제품: " + deserializedProduct);
        Console.WriteLine("SecretCode (XML 무시됨): " + deserializedProduct.SecretCode);
        
        JsonSerialize(product, jsonFilePath);
        
        Product deserializedJsonProduct = JsonDeserialize<Product>(jsonFilePath);
        Console.WriteLine("\nJSON 역직렬화된 제품: " + deserializedJsonProduct);
    }
    
    static void BinarySerialize(object obj, string filePath)
    {
        using (FileStream fs = new FileStream(filePath, FileMode.Create))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(fs, obj);
            Console.WriteLine($"객체를 이진 형식으로 직렬화 완료: {filePath}");
        }
    }
    
    static T BinaryDeserialize<T>(string filePath)
    {
        using (FileStream fs = new FileStream(filePath, FileMode.Open))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            return (T)formatter.Deserialize(fs);
        }
    }
    
    static void XmlSerialize<T>(T obj, string filePath)
    {
        using (FileStream fs = new FileStream(filePath, FileMode.Create))
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            serializer.Serialize(fs, obj);
            Console.WriteLine($"객체를 XML 형식으로 직렬화 완료: {filePath}");
        }
    }
    
    static T XmlDeserialize<T>(string filePath)
    {
        using (FileStream fs = new FileStream(filePath, FileMode.Open))
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            return (T)serializer.Deserialize(fs);
        }
    }
    
    static void JsonSerialize<T>(T obj, string filePath)
    {
        string jsonString = JsonSerializer.Serialize(obj, new JsonSerializerOptions
        {
            WriteIndented = true // 들여쓰기 적용
        });
        File.WriteAllText(filePath, jsonString);
        Console.WriteLine($"객체를 JSON 형식으로 직렬화 완료: {filePath}");
    }
    
    static T JsonDeserialize<T>(string filePath)
    {
        string jsonString = File.ReadAllText(filePath);
        return JsonSerializer.Deserialize<T>(jsonString);
    }
}

비동기 파일 입출력

현대 애플리케이션에서는 UI 응답성을 유지하고 자원을 효율적으로 사용하기 위해 비동기 파일 입출력이 중요합니다. C#에서는 asyncawait 키워드를 통해 비동기 프로그래밍을 쉽게 구현할 수 있습니다.

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;

class AsyncFileIOExample
{
    static async Task Main()
    {
        string filePath = @"C:\Example\async_file.txt";
        
        await WriteFileAsync(filePath);
        
        string content = await ReadFileAsync(filePath);
        Console.WriteLine("파일 내용:");
        Console.WriteLine(content);
        
        await ProcessMultipleFilesAsync();
    }
    
    static async Task WriteFileAsync(string filePath)
    {
        Console.WriteLine("파일 쓰기 시작...");
        
        string content = "이것은 비동기로 작성된 파일입니다.\n";
        for (int i = 1; i <= 10; i++)
        {
            content += $"비동기 작업 라인 {i}\n";
        }
        
        using (StreamWriter writer = new StreamWriter(filePath, false, Encoding.UTF8))
        {
            await writer.WriteAsync(content);
        }
        
        Console.WriteLine("파일 쓰기 완료.");
    }
    
    static async Task<string> ReadFileAsync(string filePath)
    {
        Console.WriteLine("파일 읽기 시작...");
        
        string content;
        using (StreamReader reader = new StreamReader(filePath, Encoding.UTF8))
        {
            content = await reader.ReadToEndAsync();
        }
        
        Console.WriteLine("파일 읽기 완료.");
        return content;
    }
    
    static async Task ProcessMultipleFilesAsync()
    {
        string[] files = new string[]
        {
            @"C:\Example\async_file1.txt",
            @"C:\Example\async_file2.txt",
            @"C:\Example\async_file3.txt"
        };
        
        var writeTasks = new Task[files.Length];
        for (int i = 0; i < files.Length; i++)
        {
            string content = $"이것은 파일 {i+1}의 내용입니다.\n";
            writeTasks[i] = File.WriteAllTextAsync(files[i], content);
        }
        
        Console.WriteLine("여러 파일 동시에 쓰기 시작...");
        await Task.WhenAll(writeTasks);
        Console.WriteLine("모든 파일 쓰기 완료.");
        
        var readTasks = new Task<string>[files.Length];
        for (int i = 0; i < files.Length; i++)
        {
            readTasks[i] = File.ReadAllTextAsync(files[i]);
        }
        
        Console.WriteLine("여러 파일 동시에 읽기 시작...");
        string[] contents = await Task.WhenAll(readTasks);
        Console.WriteLine("모든 파일 읽기 완료.");
        
        for (int i = 0; i < contents.Length; i++)
        {
            Console.WriteLine($"파일 {i+1} 내용: {contents[i]}");
        }
    }
}

파일 감시하기 (FileSystemWatcher)

FileSystemWatcher 클래스를 사용하면 특정 디렉터리나 파일의 변경사항을 감지하고 대응할 수 있습니다.

using System;
using System.IO;

class FileWatcherExample
{
    static void Main()
    {
        string directoryToWatch = @"C:\Example";
        
        using (FileSystemWatcher watcher = new FileSystemWatcher())
        {
            watcher.Path = directoryToWatch;
            
            watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | 
                                  NotifyFilters.LastWrite | NotifyFilters.Size;
            
            watcher.Filter = "*.*";
            
            watcher.IncludeSubdirectories = true;
            
            watcher.Created += OnChanged;
            watcher.Deleted += OnChanged;
            watcher.Changed += OnChanged;
            watcher.Renamed += OnRenamed;
            
            watcher.EnableRaisingEvents = true;
            
            Console.WriteLine($"'{directoryToWatch}' 디렉터리 감시 중...");
            Console.WriteLine("종료하려면 아무 키나 누르세요.");
            Console.ReadKey();
        }
    }
    
    private static void OnChanged(object source, FileSystemEventArgs e)
    {
        Console.WriteLine($"[{DateTime.Now}] {e.ChangeType}: {e.FullPath}");
        
        if (e.ChangeType == WatcherChangeTypes.Changed && File.Exists(e.FullPath))
        {
            try
            {
                FileInfo fileInfo = new FileInfo(e.FullPath);
                Console.WriteLine($"  파일 크기: {fileInfo.Length} 바이트");
                Console.WriteLine($"  마지막 수정 시간: {fileInfo.LastWriteTime}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"  파일 정보 읽기 실패: {ex.Message}");
            }
        }
    }
    
    private static void OnRenamed(object source, RenamedEventArgs e)
    {
        Console.WriteLine($"[{DateTime.Now}] {e.ChangeType}: {e.OldFullPath} -> {e.FullPath}");
    }
}

메모리 매핑 파일 (Memory-Mapped Files)

메모리 매핑 파일은 대용량 파일을 효율적으로 다루거나 프로세스 간 통신을 위해 사용할 수 있습니다.

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Text;

class MemoryMappedFileExample
{
    static void Main()
    {
        string filePath = @"C:\Example\mapped_file.dat";
        long fileSize = 10 * 1024; // 10KB
        
        Console.WriteLine("메모리 매핑 파일 예제 시작");
        
        CreateFileBasedMappedFile(filePath, fileSize);
        
        ReadMemoryMappedFile(filePath);
        
        UseMemoryBasedMappedFile();
        
        Console.WriteLine("\n프로세스 간 통신 예제:");
        Console.WriteLine("이 예제를 실행하려면 별도의 프로세스가 필요합니다.");
        Console.WriteLine("실제 코드에서는 두 개의 별도 프로그램으로 분리하여 사용하세요.");
    }
    
    static void CreateFileBasedMappedFile(string filePath, long fileSize)
    {
        Console.WriteLine($"\n1. 파일 기반 메모리 매핑 파일 생성: {filePath} ({fileSize} 바이트)");
        
        try
        {
            using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
            {
                fs.SetLength(fileSize); // 파일 크기 설정
            }
            
            using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open))
            {
                using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
                {
                    string message = "안녕하세요! 이것은 메모리 매핑 파일 예제입니다.";
                    byte[] messageBytes = Encoding.UTF8.GetBytes(message);
                    
                    accessor.Write(0, messageBytes.Length);
                    
                    accessor.WriteArray(4, messageBytes, 0, messageBytes.Length);
                    
                    accessor.Write(1024, 12345); // int
                    accessor.Write(1028, 3.14159); // double
                    accessor.Write(1036, true); // bool
                    
                    Console.WriteLine("데이터 쓰기 완료.");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"오류 발생: {ex.Message}");
        }
    }
    
    static void ReadMemoryMappedFile(string filePath)
    {
        Console.WriteLine($"\n2. 메모리 매핑 파일 읽기: {filePath}");
        
        try
        {
            using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open))
            {
                using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
                {
                    int stringLength = accessor.ReadInt32(0);
                    
                    byte[] buffer = new byte[stringLength];
                    accessor.ReadArray(4, buffer, 0, stringLength);
                    string message = Encoding.UTF8.GetString(buffer);
                    
                    int intValue = accessor.ReadInt32(1024);
                    double doubleValue = accessor.ReadDouble(1028);
                    bool boolValue = accessor.ReadBoolean(1036);
                    
                    Console.WriteLine($"읽은 문자열: {message}");
                    Console.WriteLine($"읽은 정수: {intValue}");
                    Console.WriteLine($"읽은 실수: {doubleValue}");
                    Console.WriteLine($"읽은 불리언: {boolValue}");
                }
                
                using (MemoryMappedViewStream stream = mmf.CreateViewStream(0, 0))
                {
                    byte[] lengthBuffer = new byte[4];
                    stream.Read(lengthBuffer, 0, 4);
                    int stringLength = BitConverter.ToInt32(lengthBuffer, 0);
                    
                    byte[] stringBuffer = new byte[stringLength];
                    stream.Read(stringBuffer, 0, stringLength);
                    string message = Encoding.UTF8.GetString(stringBuffer);
                    
                    Console.WriteLine($"스트림을 통해 읽은 문자열: {message}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"오류 발생: {ex.Message}");
        }
    }
    
    static void UseMemoryBasedMappedFile()
    {
        Console.WriteLine("\n3. 메모리 스트림 기반 메모리 매핑 파일");
        
        try
        {
            using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("SharedMemory", 1024 * 1024))
            {
                using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
                {
                    accessor.Write(0, 42);
                    accessor.Write(4, 3.14159);
                    accessor.Write(12, (long)9876543210);
                    
                    Employee emp = new Employee
                    {
                        Id = 1001,
                        Salary = 5000000.50,
                        IsActive = true
                    };
                    
                    accessor.Write(100, emp.Id);
                    accessor.Write(104, emp.Salary);
                    accessor.Write(112, emp.IsActive);
                    
                    Console.WriteLine("메모리 매핑 파일에 데이터 쓰기 완료.");
                }
                
                using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
                {
                    int intValue = accessor.ReadInt32(0);
                    double doubleValue = accessor.ReadDouble(4);
                    long longValue = accessor.ReadInt64(12);
                    
                    Employee emp = new Employee
                    {
                        Id = accessor.ReadInt32(100),
                        Salary = accessor.ReadDouble(104),
                        IsActive = accessor.ReadBoolean(112)
                    };
                    
                    Console.WriteLine($"읽은 값: {intValue}, {doubleValue}, {longValue}");
                    Console.WriteLine($"읽은 구조체: Id={emp.Id}, Salary={emp.Salary}, IsActive={emp.IsActive}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"오류 발생: {ex.Message}");
        }
    }
    
    static void IpcServerExample()
    {
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("SharedMemoryIPC", 4096))
        {
            using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
            {
                string message = "서버에서 보낸 메시지";
                byte[] messageBytes = Encoding.UTF8.GetBytes(message);
                
                accessor.Write(0, messageBytes.Length);
                accessor.WriteArray(4, messageBytes, 0, messageBytes.Length);
                
                Console.WriteLine("서버: 데이터 기록 완료. 클라이언트 대기 중...");
                Console.ReadKey();
            }
        }
    }
    
    static void IpcClientExample()
    {
        using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("SharedMemoryIPC"))
        {
            using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
            {
                int messageLength = accessor.ReadInt32(0);
                byte[] buffer = new byte[messageLength];
                accessor.ReadArray(4, buffer, 0, messageLength);
                
                string message = Encoding.UTF8.GetString(buffer);
                Console.WriteLine($"클라이언트: 서버에서 받은 메시지: {message}");
            }
        }
    }
    
    struct Employee
    {
        public int Id;
        public double Salary;
        public bool IsActive;
    }
}