1. 소개
FileStream이나 다른 스트림 클래스로 파일을 다룰 때, 파일을 열려고 할 때 실제로 무슨 일이 일어나는지 미리 아는 게 진짜 중요해. 파일이 이미 있을 때는 기존 데이터를 잃지 않고 새 데이터를 추가하고 싶을 수도 있고, 파일을 완전히 덮어쓰고 싶을 수도 있지. 반대로, 파일이 아직 없는데 읽으려고 하면, 실수로 빈 파일이 생길 수도 있어서 조심해야 해.
예를 들어, 일기장 같은 메모장 앱을 만든다고 해보자. 기존 파일 끝에 새로운 기록을 계속 추가하고 싶을 거야. 이럴 땐 추가 모드로 파일을 열어야 해. 또 다른 경우, 예를 들어 로그 파일을 앱 시작할 때마다 새로 만들고 싶으면, 기존 데이터를 방해하지 않게 파일을 새로 만들거나 비워야 해. 그냥 파일을 읽기만 하고 싶을 때는, 파일이 없으면 새로 만들지 않고 에러가 나야 해. 안 그러면 원래 없어야 할 빈 파일이 생길 수 있거든.
이런 다양한 상황들 — 추가, 덮어쓰기, 생성 없이 읽기 — 전부 C#에서는 System.IO.FileMode 열거형으로 파일 열기 모드를 지정해서 처리해. 각 모드는 파일을 열거나 만들 때 동작을 제어해서, 파일을 다룰 때 정확하게 원하는 대로 동작하게 해줘.
2. FileMode 열거형 — 손쉽게 이해하기
여기 FileMode 열거형의 주요 값들이 있어:
| 값 | 설명 | 파일이 이미 있을 때 | 파일이 없을 때 |
|---|---|---|---|
|
새 파일을 만들어. 파일이 이미 있으면 예외 발생 | 에러 | OK, 파일 생성 |
|
파일이 없으면 새로 만들고, 있으면 덮어써 | 덮어씀 | OK, 생성 |
|
존재하는 파일을 열어. 파일이 없으면 예외 | OK, 열어줌 | 에러 |
|
있으면 열고, 없으면 새로 만들어 | OK, 열어줌 | OK, 생성 |
|
존재하는 파일을 열고 길이를 0으로 만들어(내용 삭제) | OK, 파일 비움 | 에러 |
|
파일 끝에 추가 쓰기용으로 열고, 없으면 새로 만들어 | OK, 끝에 추가 | OK, 생성 |
일러스트: 동작 시나리오
flowchart TD
Start[시작]
A{파일이 존재해?}
B1[끝에 추가로 열기]
B2[새 파일 만들기]
B3[파일 덮어쓰기]
B4[파일 열기]
B5[에러: 파일 없음]
B6[에러: 파일 이미 있음]
Start --> A
A --Append, OpenOrCreate--> B1
A --Create, Truncate--> B3
A --Open, Truncate--> B4
A --CreateNew--> B6
A --Create, OpenOrCreate, Append--> B2
A --Open, Truncate--> B5
3. 파일 열기 모드별 예제
우리 강의에서 계속 발전시키는 미니 앱으로 직접 해보자. 텍스트 파일에 인사말과 날짜를 쓴다고 해보자. 세 가지 대표적인 시나리오를 볼 거야:
- A. 새 파일 만들기 (CreateNew)
- B. 파일 덮어쓰기 (Create)
- C. 파일 끝에 줄 추가하기 (Append)
새 파일 만들기 (FileMode.CreateNew)
파일이 이미 있으면, 이 모드는 새로 만들 수 없게 해줘: IOException 예외가 발생해. 데이터 손실이나 남의 파일을 실수로 지우면 안 될 때 좋아.
using System;
using System.IO;
string filePath = "greeting.txt";
// 새 파일 만들기 시도 — 이미 있으면 예외!
try
{
var stream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine("안녕! 이건 첫 번째 기록이야.");
writer.WriteLine($"날짜: {DateTime.Now}");
Console.WriteLine("파일이 성공적으로 만들어졌어.");
writer.Close();
}
catch (IOException)
{
Console.WriteLine($"파일 '{filePath}' 이미 있어! 덮어쓰지 않을게.");
}
참고: 이 코드를 두 번 실행하면 — catch가 작동해서 데이터가 지워지지 않아.
파일 덮어쓰기 (FileMode.Create)
이 모드는 전혀 봐주지 않아: 파일이 있으면 완전히 비워버려. 없으면 그냥 새로 만들어. 매번 "새로 시작"할 때 좋아. 예를 들어 리포트 저장할 때.
using System;
using System.IO;
string filePath = "greeting.txt";
// 파일 만들기 또는 완전 덮어쓰기
var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine("안녕! 파일을 완전히 새로 썼어 :)");
writer.WriteLine($"업데이트: {DateTime.Now}");
Console.WriteLine("파일이 만들어지거나 덮어써졌어.");
writer.Close();
이 코드를 여러 번 실행해봐 — 파일에는 항상 마지막에 쓴 줄만 남아. 예전 건 다 사라져.
파일 끝에 추가 쓰기 (FileMode.Append)
로그, 이벤트 기록, 방문 기록 등에 딱 좋은 모드야. 파일이 없으면 새로 만들고, 있으면 끝에 계속 추가해.
using System;
using System.IO;
string filePath = "greeting.txt";
// 파일 끝에 새 줄 추가
var stream = new FileStream(filePath, FileMode.Append, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine($"또 다른 인사! 날짜: {DateTime.Now}");
Console.WriteLine("파일 끝에 새 줄을 추가했어.");
writer.Close();
이 코드를 여러 번 실행해봐. 파일이 점점 커지고, 새 기록이 항상 맨 끝에 붙어.
4. 파일 열기 모드 선택법? 실전 팁
실생활 시나리오:
- 로그 기록: 거의 항상 Append를 써. 로그가 덮어써지면 관리자도 울고, 악당은 좋아하지.
- 리포트 내보내기: 보통 Create를 써서 예전 버전이 폴더를 어지럽히지 않게 해.
- 데이터 손실이 치명적인 내보내기: CreateNew가 좋아. 파일이 있으면 에러, 아무것도 안 건드려.
- 파일 읽기: 생성 모드 필요 없어, FileMode.Open을 써 — 파일이 없으면 문제라는 신호야.
C# 코드 쓸 때는 File 클래스의 static 메서드를 자주 쓰는데, 내부적으로 적절한 모드를 알아서 골라줘. 예를 들어 File.AppendAllText는 항상 Append로, File.WriteAllText는 Create로 동작해.
5. 파일 모드와 작업 시 문제들
약간 까다로운 점들:
FileMode.Create나 FileMode.Truncate로 파일을 열면, 모든 내용이 사라져. 조심해야 해 — 데이터 날려먹은 건 학생만이 아니라 경력 많은 개발자도 당한 적 있어.
FileMode.Open이나 FileMode.Truncate로 없는 파일을 열려고 하면 FileNotFoundException이 나와. File.Exists(path)로 파일이 진짜 있는지 꼭 확인해.
Append 모드는 쓰기만 가능해. 읽기와 쓰기를 둘 다 하고 싶으면 (OpenOrCreate + 적절한 FileAccess)를 써봐.
예제: 읽기와 쓰기
가끔은 읽기도 하고 쓰기도 하고 싶지 (예: 은행 계좌 열어서 입금도 하고 잔고도 확인하고 싶을 때). FileMode.OpenOrCreate를 써:
using System.IO;
string filePath = "balance.txt";
// 읽기/쓰기용으로 파일 열기; 없으면 새로 만들기
var stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
// 여기서 읽기도 쓰기도 가능
6. 유용한 팁
파일 열기 모드
graph TD
A[파일 요청] --> B[모드 선택]
B --> C1[CreateNew] -->|파일 있으면| D1[에러]
C1 -->|파일 없으면| E1[생성]
B --> C2[Create] -->|항상| F1[생성/덮어쓰기]
B --> C3[Open] -->|파일 있으면| G1[열기]
C3 -->|파일 없으면| D1
B --> C4[OpenOrCreate] -->|파일 있으면| G1
C4 -->|파일 없으면| E1
B --> C5[Append] -->|파일 있으면| H1[끝에 추가로 열기]
C5 -->|파일 없으면| E1
B --> C6[Truncate] -->|파일 있으면| I1[비우기]
C6 -->|파일 없으면| D1
파일 열기 모드 요약표
| FileMode | 파일 있음 | 파일 없음 | 데이터 접근 | 기존 내용 삭제? |
|---|---|---|---|---|
|
예외 | 새로 만듦 | 쓰기 | 해당 없음 |
|
덮어씀 | 새로 만듦 | 쓰기 | 예 |
|
열기 | 예외 | 읽기/쓰기 | 아니오 |
|
열기 | 새로 만듦 | 읽기/쓰기 | 아니오 |
|
비움 | 예외 | 쓰기 | 예 |
|
끝에 추가로 열기 | 새로 만듦 | 쓰기만 | 아니오 (추가) |
File 클래스의 고수준 메서드와 비교
- File.WriteAllText(path, text) → 내부적으로 FileMode.Create 사용, 즉 기존 파일이 덮어써짐.
- File.AppendAllText(path, text) → Append 모드 사용, 끝에 추가됨.
- File.ReadAllText(path) → 읽기용으로 파일 열기 (FileMode.Open).
이런 메서드만으로도 충분할 때가 많지만, FileStream과 명시적 모드 지정으로 더 세밀하게 제어할 수 있어.
운영체제별 특징
윈도우에서는 다른 프로세스가 파일을 쓰기용으로 열고 있으면 다시 열 수 없어서 에러가 날 수 있어. 그래서 FileShare 파라미터를 잘 써서 파일 공유 접근을 제어하는 게 중요해 (이건 다음 강의에서 더 자세히 다룰 거야). OS마다 락과 권한 메커니즘이 다를 수 있지만, 파일 열기 모드 개념은 어디서나 똑같아.
GO TO FULL VERSION