58. 스트림(Stream)과 파일(File) 입출력 맛보기

  • 90 minutes to read

프로그램 코드에서 작성된 데이터는 목적에 따라서 영구적으로 보관해야할 경우가 있습니다. 이러한 경우에는 파일 또는 데이터베이스에 저장할 수 있습니다. 이번 강의는 파일에 데이터를 저장하고 이를 사용하는 방법에 대해 알아봅니다.

> // System.IO: 파일에 데이터를 저장하고 읽어오는 등의 과정을 처리하는 명령어들의 집합체

58.1. System.IO 네임스페이스

C#을 사용하여 파일에 대한 입력과 출력을 담당하는 네임스페이스는 System.IO입니다. 이 네임스페이스에는 많은 수의 파일과 디렉터리 관련 클래스를 제공합니다. 이러한 클래스들을 사용하면 파일 생성 및 삭제, 텍스트 파일 읽고 쓰기, 바이너리 파일 읽고 쓰기, CSV 파일 읽고 쓰기 등의 파일 처리 관련된 기능을 수행할 수 있습니다.

자주 사용되는 파일 처리 관련 API는 다음과 같습니다. 너무 많고 사용하지 않을 수도 있기에 한번 정도 읽어보고 넘어가면 됩니다.

  • File.Exists(): 지정된 경로에 파일이 있으면 True 반환하고 파일이 없으면 False 반환
  • new DirectoryInfo().Parent.FullName: 지정된 경로의 부모 폴더 값 반환
  • Path.GetDirectoryName(): 지정한 경로의 디렉터리 값 반환
  • Path.Combile(): 2개의 경로를 합치거나 경로와 파일명을 묶어서 반환
  • Directory.Exists(): 지정된 디렉터리가 있는지 확인
  • Directory.CreateDirectory(): 디렉터리 생성
  • Directory.Delete(): 디렉터리 삭제
  • Path.GetFileName(): 파일명 추출
  • Path.GetExtension(): 파일 확장자 추출
  • File.Copy(): 파일 복사
  • File.Move(): 파일 이동
  • Directory.GetFiles(): 지정된 경로의 파일명 리스트를 문자열 배열로 반환
  • File.ReadAllText(): 텍스트 파일 읽기
  • File.WriteAllText(): 텍스트 파일 쓰기
  • File.ReadAllLines(): 텍스트 파일의 모든 줄 읽어서 문자열 배열로 반환
  • File.WriteAllLines(): 문자열 배열을 줄 단위로 텍스트 파일에 저장
  • File.AppendAllText(): 텍스트 파일에 내용 추가
  • File.AppendAllLines(): 컬렉션 형태의 문자열 추가
  • File.ReadAllBytes(): 바이너리 파일 읽기
  • File.WriteAllBytes(): 바이너리 파일 쓰기

58.1.1. 파일(File) 클래스

File 클래스는 사용하기 편합니다. 다만, 모든 파일의 내용을 다 읽어서 처리하기에 대용량 파일 처리에는 메모리 관리에 단점을 가집니다. File 클래스는 텍스트 파일 및 간단한 바이너리 파일을 다룰 때 권장됩니다.

58.1.2. 스트림(Stream) 클래스

File 클래스와 달리 Stream 클래스는 모든 데이터를 메모리에 올려두지 않고 사용하지 않기에 대용량 파일 처리에 효과적입니다.

58.2. 문자열에서 파일명 추출

이번에는 문자열에서 파일명만을 추출하는 프로그램을 만들어보겠습니다.

코드: GetFileNameDemo.cs

using System;

class GetFileNameDemo
{
    static void Main()
    {
        //[Q] 아래 전체 경로가 입력되었을 때 파일명과 확장자 추출      
        //[1] Input
        string dir = "C:\\Website\\RedPlus\\images\\test.gif";
        string fullName = String.Empty;
        string name = "";
        string ext = name;

        //[2] Process
        // 전체파일명: 마지막 \\ 위치값 + 1한 위치부터 마지막까지 검색
        fullName = dir.Substring(dir.LastIndexOf('\\') + 1);
        name = fullName.Substring(0, fullName.LastIndexOf('.'));
        ext = fullName.Substring(fullName.LastIndexOf('.') + 1);

        //[3] Output
        Console.WriteLine($"전체 파일명: {fullName}");
        Console.WriteLine($"순수 파일명: {name}");
        Console.WriteLine($"확장자: {ext}");
    }
}
전체 파일명: test.gif
순수 파일명: test
확장자: gif

문자열에서 파일명과 파일 확장자를 구하는 방법은 많은 방법이 있습니다. 하지만, 매번 이러한 방법을 사용해야 할까요? 그렇지 않습니다. System.IO 네임스페이스에 있는 Path 클래스의 주요 메서드를 사용하면 편리하게 이러한 기능을 구현할 수 있습니다.

58.3. Path 클래스로 파일명 및 확장자 그리고 폴더 정보 얻기

C# Interactive를 열고 다음 코드를 작성해 보세요. Path 클래스의 GetFileName과 같은 정적 메서드를 사용하면 파일명 및 확장자 등을 손쉽게 구할 수 있습니다.

코드: PathClassDemo.cs

> var dir = "C:\\Website\\RedPlus\\images\\test.gif";
> Path.GetFileName(dir)
"test.gif"
> Path.GetFileNameWithoutExtension(dir)
"test"
> Path.GetExtension(dir)
".gif"
> Path.GetDirectoryName(dir)
"C:\\Website\\RedPlus\\images"
> Path.GetFullPath(dir)
"C:\\Website\\RedPlus\\images\\test.gif"

58.4. 파일과 디렉터리 관련 클래스

58.4.1. 파일 생성 및 삭제

파일을 생성하고 삭제하는 일반적인 절차를 알아보겠습니다.

  • StreamWriter 클래스를 사용하여 텍스트 파일 쓰기
  • StreamReader 클래스를 사용하여 텍스트 파일 읽기
  • File 클래스와 FileInfo 클래스로 파일의 정보 얻기
  • Directory 클래스와 DirectoryInfo 클래스로 폴더 정보 얻기

58.4.2. 텍스트 파일 만들고 문자열 저장하기

이번에는 텍스트 파일을 저장하는 방법을 알아보겠습니다. 윈도 운영 체제의 C 드라이브에 Temp 폴더가 있다고 가정하고 텍스트 파일을 쓰는데에는 StreamWriter 클래스가 사용됩니다.

코드: StreamWriterWriteLineDemo.cs

using System;
using System.IO;

class StreamWriterWriteLineDemo
{
    static void Main()
    {
        string data =
            "안녕하세요.\r\n반가습니다." + Environment.NewLine + "또 만나요.";

        //[1] StreamWriter 클래스를 사용하여 파일 생성
        // C 드라이브에 Temp 폴더 미리 생성해야 함
        StreamWriter sw = new StreamWriter("C:\\Temp\\Test.txt");
        
        //[2] Write() 메서드 : 저장
        sw.WriteLine(data);

        //[3] StreamWrite 개체를 생성했으면 반드시 닫기
        sw.Close();
        
        //[4] 메모리 해제 
        sw.Dispose();
    }
}

위 코드를 실행하면 C 드라이브의 Temp 폴더에 Test.txt 파일이 만들어지고 data 변수에 지정된 문자열의 값이 저장됩니다. 문자열의 \r\n 또는 Environmemt.NewLine 값은 텍스트 파일에서 줄바꿈을 나타냅니다.

[1]번 코드와 같이 StreamWrite와 같은 파일 처리 관련 클래스의 인스턴스를 생성하고 사용했다면, 반드시 [3]번과 [4]번 코드처럼 닫기와 해제의 작업을 해주는게 좋습니다.

그림: 저장된 텍스트 파일

저장된 텍스트 파일

58.4.3. 텍스트 파일의 내용 읽어 출력하기

이번에는 텍스트 파일의 내용을 읽는 방법을 알아보겠습니다. 텍스트 파일의 내용을 읽을 때에는 StreamReader 클래스가 사용됩니다.

코드: StreamReaderReadToEndDemo.cs

using System;
using System.IO;

class StreamReaderReadToEndDemo
{
    static void Main()
    {
        //[1] StreamReader 클래스로 텍스트 파일 읽기
        StreamReader sr = new StreamReader(@"C:\Temp\Test.txt");

        //[2] ReadToEnd() 메서드로 텍스트 파일의 내용 읽어 콘솔에 출력하기 
        Console.WriteLine("{0}", sr.ReadToEnd()); // 전체 읽어오기

        //[3] 사용 후 파일 닫고 메모리 정리하기 
        sr.Close(); 
        sr.Dispose();
    }
}
안녕하세요.
반가습니다.
또 만나요.

StreamReader 클래스는 텍스트 파일과 같은 파일의 내용을 읽을 때 사용합니다. ReadToEnd() 메서드 등을 사용하여 텍스트 파일의 내용을 읽어올 수 있습니다. 이 예제 역시 [3]번 코드와 같이 Close() 메서드와 Dispose() 메서드를 호출하여 메모리를 정리하는 작업을 습관적으로 해주는게 좋습니다. 그렇지 않으면 파일이 사용중이라는 예기치 않는 오류를 만날 수 있습니다.

58.4.4. 텍스트 파일의 정보 얻기

이번에는 텍스트 파일의 정보를 얻는 방법을 알아보겠습니다.

코드: FileAndFileInfo.cs

using System;
using System.IO;

class FileAndFileInfo
{
    static void Main()
    {
        string file = "C:\\Temp\\Test.txt";

        //[1] File 클래스: 정적 멤버 제공
        if (File.Exists(file)) // 파일이 있다면.
        {
            Console.WriteLine("{0}", File.GetCreationTime(file));
            File.Copy(file, "C:\\Temp\\Test2.txt", true); // 파일 복사 테스트
        }

        //[2] FileInfo 클래스: 인스턴스 멤버 제공
        FileInfo fi = new FileInfo(file);
        if (fi.Exists) // 파일이 존재한다면,
        {
            Console.WriteLine($"{fi.FullName}"); // 파일명 출력
        }
    }
}
2023-01-26 오후 8:40:35
C:\Temp\Test.txt

파일을 다룰 때에는 이번 예제에서처럼 정적 클래스인 File 클래스 또는 인스턴스 클래스인 FileInfo 클래스를 사용해도 동일한 결과를 얻을 수 있습니다.

58.4.5. 폴더 정보 얻기

이번에는 폴더의 정보를 얻는 방법을 알아보겠습니다.

폴더 정보를 얻을 때에는 Directory 클래스 또는 DirectoryInfo 클래스가 사용됩니다.

코드: DirectoryAndDirectoryInfo.cs

using System;
using System.IO;

class DirectoryAndDirectoryInfo
{
    static void Main()
    {
        string dir = "C:\\";

        //[1] Directory 클래스
        if (Directory.Exists(dir))
        {
            Console.WriteLine("[1] C 드라이브의 모든 폴더 목록을 출력");
            foreach (string folder in Directory.GetDirectories(dir))
            {
                Console.WriteLine($"{folder}");
            }
        }

        //[2] DirectoryInfo 클래스
        DirectoryInfo di = new DirectoryInfo(dir + "Temp\\");
        if (di.Exists)
        {
            Console.WriteLine("[2] C 드라이브의 Temp 폴더의 모든 파일 목록 출력");
            foreach (var file in di.GetFiles())
            {
                Console.WriteLine($"{file}");
            }
        }
    }
}
[1] C 드라이브의 모든 폴더 목록을 출력
C:\DotNet
C:\Program Files
C:\Program Files (x86)
C:\ProgramData
C:\Temp
C:\Users
C:\Windows
C:\책
[2] C 드라이브의 Temp 폴더의 모든 파일 목록 출력
Test.txt
Test2.txt

위 실행 결과는 박용준 강사의 컴퓨터의 환경 중 일부를 표시했습니다. Directory 또는 DirectoryInfo 클래스의 GetDirectories() 메서드는 전체 디렉터리 리스트를 보여주고 GetFiles() 메서드는 전체 파일 리스트를 보여줍니다.

58.5. 텍스트 데이터를 컬렉션 데이터로 가져오기

이번에는 텍스트 파일 데이터를 읽어 컬렉션 형태의 데이터로 읽어오는 방법을 알아보겠습니다.

우선 C 드라이브의 Temp 폴더에 Src.txt 파일에 다음과 같은 데이터가 있다고 가정하겠습니다. 만약 이 예제를 실행하려면 Src.txt 파일을 만들고 다음 데이터를 입력한 후 실행하세요.

코드: C:\Temp\Src.txt

홍길동,112,2019-09-01,1234
백두산,114,2020-10-02,2345
한라산,119,2021-11-03,3456

코드: TextToCollection.cs

using System;
using System.Collections.Generic;

namespace DotNet
{
    public class Record
    {
        public string Name { get; set; }
        public string PhoneNumber { get; set; }
        public DateTime BirthDate { get; set; }
        public string AuthCode { get; set; }
    }

    class TextToCollection
    {
        static void Main()
        {
            //[1] 텍스트 파일 읽기
            string[] lines =
                System.IO.File.ReadAllLines(
                    @"C:\Temp\src.txt", System.Text.Encoding.Default);
            foreach (var line in lines)
            {
                Console.WriteLine(line);
            }

            //[2] 문자열 배열의 정보를 컬렉션 형태의 개체에 담기
            List<Record> records = new List<Record>();
            foreach (var line in lines)
            {
                string[] splitData = line.Split(',');
                records.Add(
                    new Record
                    {
                        Name = splitData[0],
                        PhoneNumber = splitData[1],
                        BirthDate = Convert.ToDateTime(splitData[2]),
                        AuthCode = splitData[3]
                    });
            }

            //[3] 한개 데이터만 출력해보기 
            Console.WriteLine(records[0]?.Name ?? "데이터가 없습니다.");
        }
    }
}
홍길동,112,2019-09-01,1234
백두산,114,2020-10-02,2345
한라산,119,2021-11-03,3456
홍길동

이번 예제에서는 구분자로 콤마를 사용하는 텍스트 파일의 데이터를 읽어 List<T> 형태의 C# 컬렉션으로 변환하는 내용입니다. [1]번 코드 영역에서처럼 파일의 내용을 File.ReadAllLines() 메서드를 사용하여 읽은 데이터를 [2]번 코드에서 List<Record> 형식의 리스트에 담는 작업을 보여줍니다.

지금은 간단한 텍스트 파일에서 컬렉션으로 데이터를 옮겨보았는데요. 추후 실제 현업에서는 텍스트 파일, XML 파일, JSON 파일, CSV 파일, 엑셀 파일 등 수 많은 데이터 형식들을 사용할 수도 있습니다. 물론, 그때마다 서로 다른 API를 사용해야 합니다.

58.6. 마무리

C#의 API를 사용하면 텍스트 파일 이외에 이진 파일에 대한 입출력 기능 및 윈도 운영 체제의 레지스트리에 대한 입출력 기능도 구현이 가능합니다. 다만, 이러한 부분에 대해서는 Microsoft Learn 사이트에 대한 검색을 권장하고, 이 강의에서는 다루지 않겠습니다.

58.7. 장 요약

변수, 배열, 클래스, 컬렉션이 아닌 영구적으로 저장할 수 있는 파일에 데이터를 저장하고 읽어오는 간단한 파일처리를 학습하였습니다. 일단, 이 정도로 파일처리를 다루어보고 다음 장에 이어서 텍스트 파일이 아닌 XML과 JSON 파일을 다루는 부분으로 우리의 능력을 확장해 나가겠습니다.

More...

좀 더 많은 예제는 다음 내용들을 참고하세요.

파일 크기 구하기

파일 크기는 디스크 공간이나 전송 시간을 측정하는 데 중요한 역할을 합니다. 여러 프로그래밍 언어를 사용하여 특정 파일의 크기를 확인하는 방법이 존재합니다. 이 글에서는 다양한 프로그래밍 언어로 작성된 예제 코드를 통해 파일 크기를 확인하는 방법을 소개합니다.

파일 크기 확인하는 C# 프로그램 예제

이번 아티클에서는 C# 언어를 사용하여 특정 파일의 크기를 확인하는 방법에 대해 설명합니다. 아래 예제 코드를 통해 파일 크기를 확인하는 방법을 배울 수 있습니다.

예제: 파일 크기 확인

코드: GetFileSize.cs

using System;
using System.IO;

class Program
{
    static void Main()
    {
        try
        {
            string filePath = "example.txt";
            FileInfo file = new FileInfo(filePath);
            long size = file.Length;

            Console.WriteLine($"파일 크기: {size} 바이트");
        }
        catch (Exception e)
        {
            Console.WriteLine($"파일 열기 실패: {e.Message}");
        }
    }
}
실행 결과
파일 크기: 11 바이트

위 예제 코드는 example.txt라는 텍스트 파일의 크기를 확인하는 프로그램입니다. 프로그램은 FileInfo 클래스를 사용하여 파일의 정보를 얻어오고, Length 속성을 통해 파일 크기를 계산합니다. 마지막으로 파일 크기를 출력합니다.

파일 크기 확인을 위한 이 프로그램은 다양한 환경에서 사용할 수 있으며, 소스 코드를 약간 수정하여 다른 파일 형식이나 용도에도 사용할 수 있습니다. 이 예제를 통해 파일 크기를 얻는 방법을 익혀 보세요.

이러한 예제들을 통해 각 프로그래밍 언어로 파일 크기를 확인하는 방법을 배웠습니다. 이를 기반으로 다양한 환경에서 사용할 수 있으며, 소스 코드를 약간 수정하여 다른 파일 형식이나 용도에도 사용할 수 있습니다. 파일 크기 확인을 위한 이 프로그램들을 참고하여 원하는 언어로 구현해 보세요.

파일에서 문자열 읽기

파일에서 문자열 읽기 예제는 다양한 프로그래밍 언어를 사용하여 텍스트 파일의 내용을 읽어 화면에 출력하는 간단한 프로그램입니다. 이 예제는 각 언어의 파일 입출력 기능을 이해하고 사용하는 방법을 소개합니다. 파일을 여는 데 실패하면 에러 메시지를 출력하고 프로그램을 종료합니다. 그렇지 않으면 파일의 모든 줄을 읽어 화면에 출력한 후 파일을 닫습니다.

이 코드를 사용하여 파일에서 문자열을 읽을 수 있습니다. 파일에 저장된 다양한 정보를 처리하려면 이 예제를 확장하고 수정해야 합니다. 이 도입 문구는 각 언어별 예제에 공통적으로 적용되며, 해당 언어의 구체적인 코드와 출력 예시는 각 언어별로 제공됩니다.

파일에서 문자열 읽기 예제 (C#)

파일에서 문자열을 읽어 출력하는 간단한 C# 프로그램 예제입니다. 이 예제에서는 StreamReader를 사용하여 텍스트 파일을 읽어와서 화면에 출력합니다.

코드: FileReadString.cs

using System;
using System.IO;

class FileReadString
{
    static void Main()
    {
        try
        {
            using (StreamReader sr = new StreamReader("example.txt"))
            {
                string line;
                while ((line = sr.ReadLine()) != null)
                {
                    Console.WriteLine(line);
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("파일을 열 수 없습니다.");
            Console.WriteLine(e.Message);
        }
    }
}
파일을 열 수 없습니다.

예상 출력 결과

파일 "example.txt"의 내용이 다음과 같다고 가정하면 출력 결과는 다음과 같습니다.

example.txt:

안녕하세요.

output:

안녕하세요.

이 예제에서는 "example.txt"라는 파일을 읽기 모드로 열고, 해당 파일의 내용을 문자열로 읽어 화면에 출력합니다. 만약 파일을 여는 데 실패하면 에러 메시지를 출력하고 프로그램을 종료합니다. 그렇지 않으면 파일의 모든 줄을 읽어 화면에 출력한 후 파일을 닫습니다.

이 코드를 사용하여 파일에서 문자열을 읽을 수 있습니다. 파일에 저장된 다양한 정보를 처리하려면 이 예제를 확장하고 수정해야 합니다.

파일에 문자열 쓰기

파일에 문자열 쓰기는 프로그래밍에서 자주 사용되는 기능 중 하나입니다. 이 기능은 사용자가 입력한 문자열을 파일에 저장하여 데이터를 유지할 수 있게 해줍니다. 다양한 프로그래밍 언어에서 제공하는 파일 I/O 라이브러리와 함수를 활용하여 파일에 문자열을 쓰고 저장하는 방법을 살펴보겠습니다.

파일에 문자열 쓰기

이 예제에서는 C# 언어를 사용하여 파일에 문자열을 쓰는 방법을 설명합니다. 파일에 문자열을 쓸 때는 StreamWriter 클래스와 using 구문을 사용합니다.

예제 코드: FileWriteString.cs

using System;
using System.IO;

class FileWriteString
{
    static void Main()
    {
        Console.Write("저장할 문자열을 입력하세요: ");
        string str = Console.ReadLine();

        try
        {
            using (StreamWriter file = new StreamWriter("example.txt"))
            {
                file.Write(str);
            }
            Console.WriteLine("파일에 저장되었습니다.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("파일을 열 수 없습니다. 에러: " + ex.Message);
        }
    }
}

실행 예시

저장할 문자열을 입력하세요: 반갑습니다.
파일에 저장되었습니다.

파일에 문자열 쓰기

파일에 문자열 쓰기는 프로그래밍에서 자주 사용되는 기능 중 하나입니다. 이 기능은 사용자가 입력한 문자열을 파일에 저장하여 데이터를 유지할 수 있게 해줍니다. 다양한 프로그래밍 언어에서 제공하는 파일 I/O 라이브러리와 함수를 활용하여 파일에 문자열을 쓰고 저장하는 방법을 살펴보겠습니다.

파일에 문자열 쓰기

이 예제에서는 C# 언어를 사용하여 파일에 문자열을 쓰는 방법을 설명합니다. 파일에 문자열을 쓸 때는 StreamWriter 클래스와 using 구문을 사용합니다.

예제 코드: FileWriteString.cs

using System;
using System.IO;

class FileWriteString
{
    static void Main()
    {
        Console.Write("저장할 문자열을 입력하세요: ");
        string str = Console.ReadLine();

        try
        {
            using (StreamWriter file = new StreamWriter("example.txt"))
            {
                file.Write(str);
            }
            Console.WriteLine("파일에 저장되었습니다.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("파일을 열 수 없습니다. 에러: " + ex.Message);
        }
    }
}

실행 예시

저장할 문자열을 입력하세요: 반갑습니다.
파일에 저장되었습니다.

텍스트 파일의 줄 수 세기

텍스트 파일의 줄 수 세기는 프로그래머들에게 자주 사용되는 기본적인 기능입니다. 이 예제에서는 다양한 프로그래밍 언어로 작성된 코드를 통해 텍스트 파일을 읽어들이고, 그 파일의 줄 수를 계산하고 출력하는 방법을 살펴보겠습니다. 이를 통해 각 언어의 파일 처리와 문자열 조작 기능에 대한 이해를 높일 수 있습니다.

텍스트 파일의 줄 수 세기 (C#)

이 프로그램은 C# 언어로 작성되었으며, 주어진 텍스트 파일에서 줄 수를 세어 출력합니다.

예제 코드: CountLinesInTextFile.cs

using System;
using System.IO;

class CountLinesInTextFile
{
    static void Main()
    {
        try
        {
            // 파일을 읽기 모드로 열기
            using (StreamReader file = new StreamReader("example.txt"))
            {
                int lineCount = 0; // 줄 수를 저장할 변수
                string line; // 파일에서 읽은 문자열을 저장할 변수
                // 파일 끝(EOF)에 도달할 때까지 문자열을 하나씩 읽기
                while ((line = file.ReadLine()) != null)
                {
                    lineCount++; // 줄 수 증가
                }

                // 파일에 있는 줄 수를 출력
                Console.WriteLine("파일에 있는 줄 수: {0}", lineCount);
            }
        }
        catch (Exception e)
        {
            // 파일 열기 실패 시 오류 메시지 출력 후 종료
            Console.WriteLine("파일 열기 실패: {0}", e.Message);
        }
    }
}

예제 출력:

파일에 있는 줄 수: 1

이 프로그램은 StreamReader 클래스를 사용하여 텍스트 파일을 읽기 모드로 열고, ReadLine 메소드를 이용하여 파일의 끝(EOF)에 도달할 때까지 문자열을 하나씩 읽습니다. 줄 수를 카운트한 후, 최종적으로 줄 수를 출력합니다.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com