본문 바로가기
C#

"C# 파일 복사 함수에서 불필요한 BinaryReader와 BinaryWriter 객체 사용에 대한 분석"

by 공부봇 2024. 12. 26.
반응형

이 함수 FileCopy는 파일을 복사하는 기능을 수행합니다. 주어진 **원본 파일 (sSrc)**을 읽어서 **목적지 파일 (sDst)**에 씁니다. 이 과정은 버퍼링을 통해 진행되며, FileStream, BinaryReader, BinaryWriter 객체를 사용하여 파일을 순차적으로 읽고 씁니다. 아래에서 각 부분을 상세히 분석하겠습니다.


1. 함수 시그니처 및 파라미터

private bool FileCopy(string sSrc, string sDst)
  • sSrc: 복사할 원본 파일 경로.
  • sDst: 복사할 대상 파일 경로.
  • 반환값: bool (true 또는 false) — 복사가 성공하면 true, 실패하면 false.

2. 버퍼 설정

int nLength = 1024 * 1280;  // 1,310,720 bytes (약 1.3MB)
byte[] btBuffer = new byte[nLength];
  • nLength: 버퍼의 크기(약 1.3MB)로, 한 번에 읽고 쓸 데이터의 크기를 정의합니다.
    이 크기는 파일을 읽고 쓸 때 한 번에 처리할 데이터 양을 결정합니다. 너무 작으면 성능이 떨어지고, 너무 크면 메모리 소비가 커질 수 있습니다.
  • btBuffer: 파일에서 읽은 데이터를 저장할 버퍼 배열입니다.

3. FileStream과 BinaryReader, BinaryWriter를 사용한 파일 읽기/쓰기

using (FileStream fsRead = new FileStream(sSrc, FileMode.Open, FileAccess.Read, FileShare.None, nLength))
{
    using (BinaryReader br = new BinaryReader(fsRead))
    {
        using (FileStream fsWrite = new FileStream(sDst, FileMode.Create, FileAccess.Write, FileShare.None, nLength))
        {
            using (BinaryWriter bw = new BinaryWriter(fsWrite))
            {
  • fsRead: 원본 파일을 읽기 위해 FileStream을 생성합니다.
    • FileMode.Open: 파일을 열고, 파일이 없으면 예외가 발생합니다.
    • FileAccess.Read: 읽기 전용 권한으로 파일을 엽니다.
    • FileShare.None: 다른 프로세스가 파일에 접근할 수 없도록 설정합니다.
    • nLength: 버퍼 크기를 설정합니다.
  • br: BinaryReader는 fsRead에서 데이터를 읽을 때 사용됩니다. BinaryReader는 파일의 내용을 빠르게 바이너리 형식으로 읽는 데 유용합니다. (하지만 이 코드에서는 br 객체는 사용되지 않습니다.)
  • fsWrite: 대상 파일을 쓰기 위해 또 다른 FileStream을 생성합니다.
    • FileMode.Create: 파일이 없으면 새로 만들고, 있으면 덮어씁니다.
    • FileAccess.Write: 쓰기 전용 권한으로 파일을 엽니다.
    • FileShare.None: 다른 프로세스가 파일에 접근할 수 없도록 설정합니다.
  • bw: BinaryWriter는 fsWrite에 데이터를 쓸 때 사용됩니다.

4. 파일 복사 로직

for (;;)
{
    int nRead = fsRead.Read(btBuffer, 0, nLength);
    if (0 == nRead)
        break;

    bw.Write(btBuffer, 0, nRead);
}
  • 무한 루프 (for (;;) 또는 while (true)): 파일을 한 번에 읽어올 수 있는 크기인 nLength만큼 반복해서 읽고 씁니다.
  • fsRead.Read(btBuffer, 0, nLength):
    • btBuffer 배열에 원본 파일에서 nLength만큼 읽어옵니다.
    • 읽은 바이트 수는 nRead에 저장됩니다.
  • if (0 == nRead): 읽을 데이터가 더 이상 없으면(파일 끝에 도달했으면) 반복을 종료합니다.
  • bw.Write(btBuffer, 0, nRead): 읽은 데이터(btBuffer)를 대상 파일에 씁니다. 실제로 읽은 바이트(nRead)만큼 씁니다.

5. 예외 처리

catch
{
    return false;
}
  • 예외가 발생하면 false를 반환합니다.
    예외가 발생하는 경우는 예를 들어, 파일이 없거나, 파일에 접근할 수 없거나, I/O 오류가 발생하는 경우입니다.

6. 복사 성공 시 반환값

return true;
  • 복사가 성공하면 true를 반환합니다.

7. 개선 및 고려사항

성능:

  • 버퍼 크기: 버퍼 크기 nLength는 1.3MB로, 이 크기가 너무 크거나 작으면 성능에 영향을 미칠 수 있습니다. 시스템의 메모리 용량과 파일 크기에 맞게 최적화하는 것이 좋습니다. 예를 들어, 너무 큰 버퍼는 메모리 사용량을 늘리고, 너무 작은 버퍼는 많은 I/O 작업을 발생시켜 성능 저하를 일으킬 수 있습니다.

BinaryReader와 BinaryWriter의 불필요한 사용:

  • BinaryReader와 BinaryWriter는 파일의 바이너리 데이터를 읽고 쓰기 위한 클래스인데, 현재 코드에서는 BinaryReader는 사용되지 않고, BinaryWriter만 사용되고 있습니다.
    따라서, BinaryReader는 불필요한 객체이며, FileStream만으로도 파일을 처리할 수 있습니다.

파일 덮어쓰기 처리:

  • 현재 코드에서는 대상 파일이 이미 존재할 경우 FileMode.Create로 인해 기존 파일이 덮어쓰여지게 됩니다. 덮어쓰기를 원하지 않는 경우 FileMode.CreateNew로 설정하거나, 덮어쓰기를 사용자에게 알리는 로직을 추가하는 것이 좋습니다.

예외 처리 개선:

  • 현재는 모든 예외를 포괄적으로 잡아서 false를 반환하지만, 구체적인 예외 처리가 필요할 수 있습니다. 예를 들어, FileNotFoundException, UnauthorizedAccessException 등을 구분하여 처리하면 문제를 추적하고 디버깅하는 데 도움이 됩니다.

최적화된 코드 (불필요한 객체 제거)

private bool FileCopy(string sSrc, string sDst)
{
    int nLength = 1024 * 1280;  // 1,310,720 bytes (약 1.3MB)
    byte[] btBuffer = new byte[nLength];

    try
    {
        using (FileStream fsRead = new FileStream(sSrc, FileMode.Open, FileAccess.Read, FileShare.None, nLength))
        {
            using (FileStream fsWrite = new FileStream(sDst, FileMode.Create, FileAccess.Write, FileShare.None, nLength))
            {
                int nRead;
                while ((nRead = fsRead.Read(btBuffer, 0, nLength)) > 0)
                {
                    fsWrite.Write(btBuffer, 0, nRead);
                }
            }
        }
    }
    catch
    {
        return false;
    }

    return true;
}
  • BinaryReader와 BinaryWriter를 제거하여, FileStream만 사용해 더 간결하고 효율적인 코드로 개선하였습니다.

결론

  • 이 함수는 파일 복사를 효율적으로 처리할 수 있는 방법을 제공합니다.
  • 하지만 BinaryReader와 BinaryWriter는 필요 없는 객체이므로 제거할 수 있고, 예외 처리나 파일 덮어쓰기 처리에 대해 조금 더 개선할 여지가 있습니다.

 

 

 

BinaryReader와 BinaryWriter 객체가 필요하지 않았을 가능성이 있다는 점에 대해 의심할 수 있는 이유는, 실제로 파일 복사 작업에서 그들의 역할이 불분명하거나 불필요할 수 있기 때문입니다. FileStream 객체만으로도 파일을 읽고 쓰는 데 충분하기 때문에, BinaryReader와 BinaryWriter는 과잉 설계일 수 있습니다. 구체적으로 그 이유를 살펴보겠습니다.


1. FileStream만으로 충분히 파일 읽기와 쓰기 가능

FileStream은 파일을 바이너리 방식으로 읽고 쓸 수 있는 기본적인 클래스입니다. BinaryReader와 BinaryWriter는 파일의 바이너리 데이터를 보다 편리하게 처리하기 위해 추가된 클래스로, FileStream 객체를 통해 데이터를 직접 읽고 쓸 수 있기 때문에 이들 객체가 꼭 필요하지 않습니다.

FileStream 사용 예시 (이미 코드에서 구현된 방식)

using (FileStream fsRead = new FileStream(sSrc, FileMode.Open, FileAccess.Read, FileShare.None, nLength))
{
    using (FileStream fsWrite = new FileStream(sDst, FileMode.Create, FileAccess.Write, FileShare.None, nLength))
    {
        int nRead;
        while ((nRead = fsRead.Read(btBuffer, 0, nLength)) > 0)
        {
            fsWrite.Write(btBuffer, 0, nRead);
        }
    }
}
  • FileStream.Read()와 FileStream.Write()를 통해 파일을 읽고 쓸 수 있기 때문에 BinaryReader와 BinaryWriter 없이도 파일 복사가 가능합니다.

2. BinaryReader와 BinaryWriter의 역할

BinaryReader와 BinaryWriter는 파일을 읽고 쓸 때 데이터 형식에 맞게 변환을 도와주는 도구입니다. 예를 들어, 문자열, 숫자 등 다양한 데이터를 읽을 때 BinaryReader를 사용하면 이를 직관적으로 처리할 수 있습니다. 반대로 BinaryWriter는 파일에 데이터를 쓸 때 숫자나 문자열을 바이너리 형식으로 변환하여 저장합니다.

하지만 단순한 파일 복사의 경우에는 FileStream만으로 충분히 복사가 가능합니다. 복사할 파일을 그대로 읽고 쓸 때, 특별히 형식 변환이 필요하지 않기 때문에 BinaryReader와 BinaryWriter를 사용할 필요가 없습니다.


3. 불필요한 객체 사용의 원인

BinaryReader와 BinaryWriter를 사용하는 이유가 과거에 필요했거나, 추가적인 기능을 염두에 두었기 때문일 수 있습니다. 예를 들어, 파일을 읽을 때 데이터를 형식화하거나 추가적인 처리를 할 필요가 있을 경우, 이러한 클래스를 사용할 수 있습니다.

그러나 단순히 파일을 복사하는 작업에서는 FileStream만으로 충분히 처리할 수 있기 때문에 불필요한 오버헤드를 줄이는 것이 좋습니다.


4. 예시 코드에서의 개선

BinaryReader와 BinaryWriter를 제거한 최적화된 코드는 FileStream만 사용하여 더 간결하고 효율적으로 동작합니다.

private bool FileCopy(string sSrc, string sDst)
{
    int nLength = 1024 * 1280;  // 1,310,720 bytes (약 1.3MB)
    byte[] btBuffer = new byte[nLength];

    try
    {
        using (FileStream fsRead = new FileStream(sSrc, FileMode.Open, FileAccess.Read, FileShare.None, nLength))
        {
            using (FileStream fsWrite = new FileStream(sDst, FileMode.Create, FileAccess.Write, FileShare.None, nLength))
            {
                int nRead;
                while ((nRead = fsRead.Read(btBuffer, 0, nLength)) > 0)
                {
                    fsWrite.Write(btBuffer, 0, nRead);
                }
            }
        }
    }
    catch (Exception ex)
    {
        // 예외 처리
        Console.WriteLine($"오류 발생: {ex.Message}");
        return false;
    }

    return true;
}

이 방식에서는 BinaryReader와 BinaryWriter를 제거하여 불필요한 객체 생성과 관리를 줄였습니다.


결론

  • 불필요한 객체 사용: BinaryReader와 BinaryWriter는 파일을 바이너리 형식으로 읽고 쓰기 위한 도구이지만, 단순한 파일 복사 작업에서는 필요하지 않습니다. FileStream만으로도 충분히 파일 복사 작업을 수행할 수 있습니다.
  • 성능과 가독성 향상: FileStream만을 사용하면 코드가 더 간결하고, 불필요한 오버헤드를 줄여 성능과 가독성을 향상시킬 수 있습니다.

따라서, BinaryReader와 BinaryWriter는 파일 복사 작업에서 불필요한 객체였을 가능성이 큽니다.

 

 

반응형