[로봇활용_13주차] C# FileStream 핵심 정리

최윤호·2025년 11월 4일
post-thumbnail

C# 파일 입출력의 핵심

지난번에 데이터가 흐르는 통로, 스트림(Stream)이라는 개념을 알아보았다면
이제 그 통로를 실제 '파일(File)'이라는 목적지에 연결해 볼 시간입니다.
이번 글에서는 C# 파일 입출력의 기본이자 핵심인 FileStream클래스를 다뤄보겠습니다.

1)FileStream이란?

C#에서 FileStream은 파일 시스템 내의 파일과 상호작용을 하기 위한 핵심 클래스입니다.
FileStream은 파일에 직접 접근하여 바이트(byte) 단위의 입출력(I/O) 작업을 수행하며,
다른 파일 관련 클래스들의 기반이 되는 저수준(low-level) 도구입니다.

2)FileStream의 주요 특징

FileStreamSystem.IO.Stream추상 클래스를 상속받으며 다음과 같은 특징을 가집니다.

1. 바이트 기반의 스트림

모든 데이터를 바이트의 연속된 흐름으로 처리합니다.
따라서 텍스트를 다룰 때는 문자를 직접 바이트 배열로 변환하거나,
변환된 바이트 배열을 다시 문자로 해석하는 과정이 필요합니다.

  • 비유: FileStream에게 텍스트, 이미지, 음악 파일은 '바이트'의 나열일 뿐입니다.
    이게 글자인지, 픽셀 정보인지, 소리인지는 전혀 신경 쓰지 않아요.
    이러한 특성 덕분에 모든 종류의 파일을 일관된 방식으로 다룰 수 있습니다.

2. 읽기와 쓰기 지원

FileStream은 파일을 열 때 어떻게 사용할지(FileAccess) 지정하여
읽기 전용, 쓰기 전용, 또는 읽기/쓰기 모두 가능한 상태로 만들 수 있습니다.

  • 비유: 파일을 열 때 FileAccess.Read로 설정하면 '읽기만 가능한 일방통행'이 되고, FileAccess.Write로 하면 '쓰기만 가능한 일방통행',
    FileAccess.ReadWrite로 하면 '읽고 쓰기가 가능한 양방향 도로'가 되는 셈이죠.

3. 순차 접근과 임의 접근 지원

FileStream'순차 접근''임의 접근'을 모두 지원하는 강력한 도구입니다.

  • 비유: 내비게이션에서 Read()Write()를 호출하면
    현재 위치에서 앞으로 쭉 가는 순차 접근(경로 따라가기)을 하고,
    Seek()메소드를 사용하면 원하는 위치로 임의 접근(목적지 검색)도 가능합니다.
    파일의 현재 위치는 Position속성으로 알 수 있습니다.

4. 버퍼링 제어

버퍼링(Buffering)은 데이터의 처리 과정에서 발생하는 속도 차이를 완화하기 위해
'버퍼(Buffer)'라는 공간에 데이터를 일시적으로 저장하는 과정을 의미합니다.
FileStream에서 제공되는 버퍼(Buffer)의 기본 크기는 4096바이트(4KB)입니다.

  • 비유: 마트에서 장을 본다고 상상해 보세요.
    • 버퍼 없음: 물건(데이터) 하나를 집어서 계산대까지 갔다가,
      다시 돌아와서 물건 하나를 집어 계산하는 방식입니다. 비효율적이죠?
    • 버퍼 사용: 쇼핑 카트(버퍼)에 물건을 가득 담은 뒤,
      계산대로 가서 한 번에 처리하는 효율적인 방식입니다.

5. 리소스 관리가 중요함

FileStream은 사용이 끝나면 반드시 파일을 해제해야(Close()) 합니다.
using문을 사용해서 using블록 안에서 FileStream객체를 생성하면
코드 블록이 끝날 때 자동으로 Dispose()메서드가 호출되어 안전하게 정리해 줍니다.

  • 비유: 전화를 하고 나서 끊는 것과 같아요. 만약 파일을 닫지 않으면,
    그 파일이 계속 '사용 중'이라고 인식해서 다른 프로그램이 접근하지 못하거나,
    쓰기 작업 중이던 데이터가 파일에 완전히 저장되지 않는 문제가 발생할 수 있습니다.

3)사용법

FileStream을 사용하기 위해서는 몇 가지 정보가 필요합니다.
"어떤 파일을?", "무슨 목적으로 열 건지?", "읽기용?" "쓰기용?" 같은 것들이죠.

FileStream객체를 생성하는 기본적인 방법은 다음과 같습니다.

using System.IO;

// using 키워드는 스트림 사용이 끝나면 자동으로 닫아주는(Dispose) 역할을 합니다.
// 파일 작업 시에는 반드시 사용해야 하는 필수 구문입니다!
using (FileStream fs = new FileStream("경로", FMode.Args, FAccess.Args, FShare.Args))
{
    // 파일 작업 수행...
}

여기서 FileMode, FileAccess, Fileshare를 하나씩 살펴볼까요?

1. FileMode

파일을 열 때의 시나리오를 정의합니다.

FileMode설명
CreateNew새 파일을 만듭니다. 파일이 이미 있으면 예외 발생!
Create새 파일을 만듭니다. 파일이 이미 있으면 덮어씁니다.
Open기존 파일을 엽니다. 파일이 없으면 예외 발생!
OpenOrCreate기존 파일을 엽니다. 파일이 없으면 새로 만듭니다.
Append기존 파일을 열고, 내용을 맨 끝에 추가합니다.
Truncate기존 파일을 열고, 모든 내용을 지웁니다.

2. FileAccess

파일에 대한 읽기/쓰기 권한을 지정합니다. (생략 시 기본값: ReadWrite 또는 Write)

FileAccess설명
Read읽기 전용
Write쓰기 전용
ReadWrite읽기 및 쓰기

3. FileShare

다른 프로세스에서 파일에 액세스하는 방법을 지정합니다. (생략 시 기본값은 Read)
FileShare에 지정하는 값들은 OR 연산자(|)를 사용하여 조합할 수도 있습니다.
여기서 가장 자주 사용되는 값은 다음과 같습니다.

FileShare설명
NoneFileStream이 열려 있는 동안에는 다른 프로세스가 이 파일에 액세스할 수 없음
Read다른 프로세스가 이 파일을 읽는 것을 허용합니다.
Write다른 프로세스가 이 파일에 쓰는 것을 허용합니다.
ReadWrite다른 프로세스가 이 파일을 읽고 쓰는 것을 모두 허용합니다.
Delete다른 프로세스가 이 파일을 삭제하는 것을 허용합니다.

4)파일에 데이터 쓰기(Write)

FileStream은 데이터를 byte배열, 즉 바이트 덩어리로 다룹니다.
우리가 흔히 쓰는 문자열("Hello World")을 파일에 쓰려면,
먼저 이 문자열을 바이트 배열로 변환하는 과정이 필요합니다.

[코드]

using System;
using System.IO;
using System.Text; // 문자 인코딩을 위해 필요합니다.

string filePath = @"C:\Temp\MyTest.txt";
string content = "Hello, FileStream!";
Directory.CreateDirectory(@"C:\Temp");

try
{
    // 1. 파일을 생성(또는 덮어쓰기)하고 쓰기 권한으로 FileStream 열기
    using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
    {
        // 2. 문자열을 UTF-8 인코딩의 byte 배열로 변환 (UTF-8은 전 세계적인 표준)
        byte[] data = Encoding.UTF8.GetBytes(content);

        // 3. byte 배열을 파일에 쓰기
        fs.Write(data, 0, data.Length);
    }
    Console.WriteLine("파일 쓰기 완료!");
}
catch (Exception ex)
{
    Console.WriteLine($"오류 발생: {ex.Message}");
}
  • Encoding.UTF8.GetBytes(문자열): 문자열을 byte배열로 변환해 줍니다.
  • fs.Write(배열, 시작 위치, 길이): 변환된 byte배열을 파일에 기록합니다.

5)파일에서 데이터 읽기(Read)

파일에서 byte배열을 읽고 우리가 읽을 수 있는 문자열로 변환해 주면 됩니다.

[코드]

// 위에서 만든 파일을 읽어봅시다.
if (File.Exists(filePath))
{
    try
    {
        // 1. 파일을 열고 읽기 권한으로 FileStream 열기
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
        {
            // 2. 파일 크기만큼의 byte 배열(버퍼) 생성
            byte[] buffer = new byte[fs.Length];

            // 3. 파일에서 데이터를 읽어와서 buffer에 채우기
            fs.Read(buffer, 0, buffer.Length);

            // 4. byte 배열을 UTF-8 인코딩으로 다시 문자열로 변환
            string readContent = Encoding.UTF8.GetString(buffer);

            Console.WriteLine("파일에서 읽은 내용:");
            Console.WriteLine(readContent);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"오류 발생: {ex.Message}");
    }
}
  • new byte[fs.Length]: 파일의 전체 크기(Length)만큼 데이터를 담을 버퍼를 준비합니다.
  • fs.Read(버퍼, 시작 위치, 길이): 파일에서 데이터를 읽고 buffer배열을 채웁니다.
  • Encoding.UTF8.GetString(배열): byte배열을 사람이 읽기 좋게 문자열로 변환합니다.

[실행 결과]

파일 쓰기 완료!
파일에서 읽은 내용:
Hello, FileStream!

6)정리

파일 입출력의 기초라고 할 수 있는 FileStream에 대해 알아봤습니다.

구분핵심 개념설명
생성new FileStream(path, mode, access)파일 경로, 모드, 접근 권한을 지정하여 스트림 통로를 엽니다.
필수using 구문FileStream에서 사용이 끝나면 자동으로 리소스를 해제합니다.
쓰기Encoding.GetBytes()fs.Write()문자열을 byte 배열로 변환한 뒤 파일에 씁니다.
읽기fs.Read()Encoding.GetString()파일에서 byte 배열을 읽어온 뒤 문자열로 변환합니다.
profile
🚀 미래의 엔지니어를 꿈꾸는 훈련생의 기록 📝

0개의 댓글