어쩌다보니 C#을 다루는 직장으로 이직하게 되었다. 요즘 대부분의 웹/앱 개발에서는 개발자가 직접 코드 타이핑을 하는 경우는 거의 없지만 아직 게임 개발이나 SCADA 프로그램 개발쪽은 변화 속도가 많이 느린 것 같다.
조만간 과제를 하나 부여받을 예정인데, 그때 AI를 최대한 사용하지 않고 프로그램을 만드는 과제를 받을 예정이다. 따라서, 오랜만에 직접 타이핑하며 코드를 따라가며 학습하는 정통적인 방식으로 돌아와봤다.
또한, C# 뿐만 아니라 개인적으로 다른 언어나 토이 프로젝트로 구현하는 코드에 대해서 가끔씩 이렇게 개발 과정 따라가기 시리즈로 올려볼 예정이다.
1. Visual Studio 설치 및 프로젝트 생성
우선, Visual Studio를 설치한다. C# 언어의 경우 만능 툴인 VS Code와 JetBrains사의 Rider 라는 IDE로 코딩이 가능한데, 앞서 말했듯 이 언어를 다루는 회사 자체가 보수적이고 변화의 둔하다. 그래서 대부분 Visual Studio로 개발하며 Rider의 경우 단순한 프로그램 개발보다는 게임 개발일경우 조금 더 채택되는 분위기다.
IDE 설치 과정은 지금 여기에 올리진 않겠다. 검색하면 바로 알 수 있는 정보기도 하고 아직 이 블로그에 설치 과정에 대한 글이 없다. Visual Studio를 설치하고 실행하면 다음과 같은 순서로 프로젝트를 생성한다.
- 새 프로젝트 만들기(Create a new project) 클릭
- 템플릿 목록에서 Windows Forms App 선택
템플릿을 선택하면 다음 정보들을 설정해주면 된다.
- Project name: MyFileExplorer
- Location: 프로젝트를 저장할 디렉토리 경로
- Framework: 당신이 설치한 Visual Studio에 따라 최신 안정 버전(LTS) 선택 → 참고로, 닷넷 10이 현재까지 가장 최신의 LTS다
프로젝트를 생성하면 다음과 같은 파일들이 생성된 것을 확인할 수 있다.
- Program.cs → 프로그램의 진입점. Main 메서드가 여기에 있다
- Form1.cs → 첫 번째 화면
- Form1.Designer.cs → 화면에 버튼·목록 같은 걸 배치하면 그 배치 코드가 자동으로 이곳에 쌓인다
- Form1.cs [Design] → 디자인된 Form1 화면을 IDE에서 화면으로 볼 수 있다
2. 레이아웃 나누기
위 파일들을 확인했다면 프로젝트가 정상적으로 생성된 상태이다. 이제 파일 탐색기를 만들기 위해 화면의 레이아웃을 나눠보자.
다음 세 가지 컨트롤을 사용할 예정이다.
- SplitContainer: 화면을 두 칸으로 나누고, 가운데 경계선을 마우스로 드래그해서 비율을 조절할 수 있게 해주는 컨테이너
- TreeView: 폴더를 계층적으로(들여쓰기 되면서 펼쳐지는) 보여주는 컨트롤
- ListView: 항목들을 목록으로 보여주는 컨트롤
Form1.cs [Design]을 열어서 도구 상자(Toolbox)를 보자. 만약, 도구 상자가 안 보인다면 상단 메뉴에서 보기(View) > 도구 상자를 클릭하자. 그리고 다음 순서대로 하나씩 컨트롤을 배치한다.
- 먼저 SplitContainer를 창 위로 드래그해서 놓는다. 놓으면 창을 좌우로 꽉 채우면서 가운데 분할선이 생긴다.(만약 위아래로 나뉘어 있으면, 그 컨트롤을 클릭한 뒤 오른쪽 속성(Properties) 창에서 Orientation을 Vertical로 바꾸면 좌우 분할이 된다)
- TreeView를 드래그해서 SplitContainer의 왼쪽 칸 안에 놓는다.
- ListView를 드래그해서 SplitContainer의 오른쪽 칸 안에 놓는다.
TreeView와 ListView까지 배치한 후에 각 컨트롤이 SplitContainer의 내부를 꽉 채우지 않는경우 속성 창에서 Dock 속성을 Fill로 설정하면 컨트롤이 칸에 딱 맞게 늘어난다.
또한, 각 컨트롤의 이름도 미리미리 정리하자. 아마 컨트롤을 배치한 후에 아무런 조치도 취하지 않았다면 treeView1, listView1 이럲게 자동으로 이름이 붙었을 것이다. 코드에서 이 이름으로 컨트롤에 접근하게 되니, 속성 창의 맨 위 (Name) 항목에서 알아보기 쉬운 이름으로 바꿔두면 나중에 편해진다.
각각 treeViewFolders와 listViewFiles로 변경해두자. 여기까지 완료하고 프로그램을 실행(Ctrl + F5)하면 창이 좌우로 나뉘어 있는 화면을 볼 수 있을 것이다.
참고로, 컨트롤을 배치한 지금 Form1.Designer.cs 파일을 보면 우리가 직접 마우스로 클릭해서 배치한 컨트롤에 대해서 코드가 작성되어 있는 것을 확인할 수있다.
3. 왼쪽 TreeView에 실제 드라이브 채우기
레이아웃이 나뉘었으니 이제 실제로 내 PC의 C 드라이브와 D 드라이브를 탐색해서 왼쪽 TreeView에 배치해보자.
이번 step에서는 아래와 같은 클래스들이 도입된다.
- DriveInfo: 내 PC에 붙어있는 드라이브(C, D, USB 등) 정보를 다루는 클래스
- TreeNode: TreeView에 들어가는 "한 줄(항목) 하나"를 나타내는 객체
Form1.cs 파일을 열자. 디자이너 화면에서 F7을 누르거나, 오른쪽 솔루션 탐색기에서 Form1.cs을 우클릭 → 코드 보기(View Code) 하면 열 수 있다. 파일을 열면 이런 코드가 보일 것이다.
public Form1()
{
InitializeComponent();
}
Java를 다루신 분들은 익숙할텐데, Form1() 생성자이다. 이제 코드를 추가해서 다음과 같은 코드로 만들어보자.
public Form1()
{
InitializeComponent();
LoadDrives(); // ← 추가
}
private void LoadDrives()
{
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
TreeNode node = new TreeNode(drive.Name); // 화면에 보일 텍스트: "C:\"
node.Tag = drive.RootDirectory.FullName; // 숨겨둘 실제 경로: "C:\"
treeViewFolders.Nodes.Add(node); // 트리에 이 노드를 추가
}
}
여기까지 완료했다면 다시 프로그램을 실행하여 확인해보자. 아마 왼쪽 TreeView 영역에 당신 PC의 드라이브가 나오는 것을 확인할 수 있다.
4. TreeView에 하위 폴더 채우기
4-1. 드라이브 노드에 가짜 자식 붙이기
이제 C 드라이브를 클릭하면 C 드라이브 안에 하위 폴더들을 볼 수 있도록 만들 예정이다. 여기서 주의할 점이 PC의 폴더는 수십만 개라, 만약 프로그램을 시작하자마자 모든 폴더를 전부 다 읽어서 트리에 넣을 경우 프로그램이 멈춰버린다. 그래서 여기서는 "사용자가 C 드라이브를 클릭하는 순간에 하위 폴더 한 층만" 읽어오는 방식(지연 로딩)을 사용하려 한다.
하지만 이 방식에도 얄궂은 문제가 있다. TreeView는 자식 노드가 하나라도 있어야 펼치기 버튼을 그려준다. 문제는 우리가 지연 로딩 방식을 선택했기 때문에 펼치기 버튼이 안 뜨고, 펼치지 못 하면 지연 로딩을 할 수가 없어진다.
따라서, "가짜 자식(dummy)"를 미리 넣어두는 방식으로 이 문제를 해결하겠다. 아래 코드를 추가해보자.
private void LoadDrives()
{
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
TreeNode node = new TreeNode(drive.Name);
node.Tag = drive.RootDirectory.FullName;
node.Nodes.Add("dummy"); // ← 추가: 펼치기 화살표가 생기도록 가짜 자식
treeViewFolders.Nodes.Add(node);
}
}
노드 하위에 가짜 자식(dummy)를 추가했다. 이 상태로 확인해보면 펼치기 버튼이 생긴 것을 확인할 수 있을 것이다. 물론, 아직은 펼쳐도 하위 폴더가 나오지 않는다. 이제 실제로 펼치기 버튼을 클릭했을 때 하위 폴더를 탐색하여 가져올 수 있도록 설정해보자.
4-2. 이벤트를 트리에 연결하기
다시 디자이너로 돌아가자. 여기서 TreeView쪽에 이벤트를 만들 생각이다.
- 디자이너에서 TreeView를 클릭해 선택
- 오른쪽 속성(Properties) 창 상단의 번개 모양 아이콘(이벤트 탭)을 클릭
- 목록에서 BeforeExpand를 찾아 그 칸을 더블클릭
4-3. BeforeExpand 메서드 로직 작성
이렇게 이벤트를 만들면 Form1.cs 파일에 treeViewFolders_BeforeExpand 라는 메서드가 생성된 것을 확인할 수 있다. 이 메서드에 아래와 같이 로직을 작성해주자.
private void treeViewFolders_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
TreeNode node = e.Node; // 지금 펼치려는 그 노드
// 아직 가짜 자식만 들어있는 상태라면 → 진짜 폴더를 채운다
if (node.Nodes.Count == 1 && node.Nodes[0].Text == "dummy")
{
node.Nodes.Clear(); // 가짜 자식 제거
string path = (string)node.Tag; // 아까 심어둔 실제 경로 꺼내기 (형변환)
try
{
foreach (string dir in Directory.GetDirectories(path))
{
TreeNode child = new TreeNode(Path.GetFileName(dir));
child.Tag = dir;
child.Nodes.Add("dummy"); // 이 폴더도 나중에 펼칠 수 있게 또 가짜 자식
node.Nodes.Add(child);
}
}
catch (UnauthorizedAccessException)
{
// 접근 권한 없는 폴더(시스템 폴더 등)는 그냥 건너뜀
}
}
}
이렇게 BeforeExpand 메서드 로직을 작성하고 테스트해보면 왼쪽 TreeView에서 펼치기 버튼을 통해 하위 폴더를 조회할 수 있음을 확인할 수 있다.
5. 오른쪽 ListView에서 파일 조회하기
이제 이번 목표의 마지막인 왼쪽 TreeView에서 폴더를 클릭하면 오른쪽 ListView에서 폴더에 해당하는 파일을 조회하는 로직을 완성해보자.
5-1. ListView를 표 모양으로 초기화
우리 파일 탐색기를 떠올려보자. 오른쪽 ListView쪽은 파일명, 파일 용량, 수정한 날짜 등등 마치 표 형식처럼 되어있다. 따라서, 다음 코드를 추가하여 ListView를 표 모양으로 만들자.
public Form1()
{
InitializeComponent();
SetupFileList(); // ← 추가
LoadDrives();
}
private void SetupFileList()
{
listViewFiles.View = View.Details; // 표(자세히) 모드로 전환
listViewFiles.Columns.Add("이름", 200); // 1번째 열, 폭 200
listViewFiles.Columns.Add("크기", 100); // 2번째 열
listViewFiles.Columns.Add("수정한 날짜", 150); // 3번째 열
}
5-2. AfterSelect 이벤트 연결
이번에는 AfterSelect라는 이벤트를 사용한다. 왼쪽 TreeView에서 폴더를 선택하면 오른쪽 ListView에 파일이 뜨는 형식이다.
- 디자이너에서 TreeView 클릭
- 속성 창의 번개 아이콘(이벤트 탭)
- AfterSelect 칸을 더블클릭
5-3. AfterSelect 로직 작성하기
앞서 BeforeExpand 이벤트를 설정한 경험처럼 5-2번을 수행하면 Form1.cs 파일에 treeViewFolders_AfterSelect 라는 메서드가 생성된다. 이 메서드의 로직을 아래와 같이 작성해준다.
private void treeViewFolders_AfterSelect(object sender, TreeViewEventArgs e)
{
string path = (string)e.Node.Tag; // 선택된 노드의 실제 경로 꺼내기
listViewFiles.Items.Clear(); // 이전 폴더의 목록 비우기
try
{
foreach (string file in Directory.GetFiles(path))
{
FileInfo info = new FileInfo(file);
ListViewItem item = new ListViewItem(info.Name); // 1열: 이름
item.SubItems.Add(info.Length.ToString()); // 2열: 크기
item.SubItems.Add(info.LastWriteTime.ToString()); // 3열: 수정 날짜
listViewFiles.Items.Add(item);
}
}
catch (UnauthorizedAccessException)
{
// 권한 없는 폴더는 조용히 건너뜀
}
}
이제 마지막 테스트를 해보자. 프로그램이 좌우로 레이아웃이 분할되어 있고, 왼쪽 TreeView 영역에서는 내 PC의 드라이브가 조회되며 드라이브를 펼치면 하위 폴더가 보일 것이다. 각 폴더를 클릭하면 오른쪽 ListView 영역에 그 폴더의 파일들이 표시되는 것을 확인할 수 있다.