1. OS 라이브러리 기초

Golang의 os 패키지는 운영 체제 기능에 대한 플랫폼 독립적인 인터페이스를 제공합니다. 다음으로는 os 패키지를 사용하여 파일 열기, 닫기, 읽기, 쓰기, 파일 속성 획득 및 설정하는 방법에 대해 알아보겠습니다.

1.1 파일 열기와 닫기

Go 언어에서는 os.Open 함수를 사용하여 파일을 열 수 있으며, 이는 *os.File 객체와 오류를 반환합니다. 파일을 열면 읽기, 쓰기 및 기타 작업을 수행할 수 있습니다. 작업을 마치면 file.Close를 호출하여 파일을 닫고 해당 리소스를 해제해야 합니다.

다음은 파일을 열기 위한 예시입니다:

package main

import (
    "fmt"
    "os"
)

func main() {
    // 현재 디렉토리에 있는 test.txt 파일 열기
    file, err := os.Open("test.txt")
    if err != nil {
        // 파일 열기 오류 처리
        fmt.Println("파일 열기 오류:", err)
        return
    }
    // 파일이 최종적으로 닫히도록 defer 문 사용
    defer file.Close()

    // 파일 처리 작업...

    fmt.Println("파일이 성공적으로 열렸습니다")
}

위 코드에서는 defer 문을 사용하여 file.Close가 반드시 실행되도록 보장합니다. 이는 Go 언어에서 리소스 정리를 위한 일반적인 관행입니다.

1.2 파일 읽기 및 쓰기 작업

os.File 타입에는 파일 읽기 및 쓰기 작업에 사용할 수 있는 ReadWrite 메서드가 있습니다. Read 메서드는 파일에서 데이터를 바이트 슬라이스로 읽고, Write 메서드는 바이트 슬라이스에서 파일로 데이터를 쓸 수 있습니다.

다음 예제는 파일에서 읽고 쓰는 방법을 보여줍니다:

package main

import (
    "fmt"
    "os"
)

func main() {
    // 파일 열기
    file, err := os.OpenFile("test.txt", os.O_RDWR, 0644)
    if err != nil {
        fmt.Println("파일 열기 오류:", err)
        return
    }
    defer file.Close()

    // 파일 내용 쓰기
    message := []byte("안녕, 고퍼들!")
    _, writeErr := file.Write(message)
    if writeErr != nil {
        fmt.Println("파일 쓰기 오류:", writeErr)
        return
    }

    // 파일 처음으로 이동하여 읽기
    file.Seek(0, 0)
    buffer := make([]byte, len(message))
    _, readErr := file.Read(buffer)
    if readErr != nil {
        fmt.Println("파일 읽기 오류:", readErr)
        return
    }

    fmt.Println("파일 내용:", string(buffer))
}

이 예제에서는 os.Open 대신 os.OpenFile을 사용합니다. os.OpenFile 함수를 사용하면 파일을 열 때 사용할 모드와 권한을 지정할 수 있습니다. 위 예제에서는 os.O_RDWR 플래그를 사용하여 파일을 읽기-쓰기 모드로 열도록 합니다.

1.3 파일 속성과 권한

os 패키지의 함수를 사용하여 파일 상태 정보에 액세스하고 수정할 수 있습니다. os.Stat 또는 os.Lstat을 사용하여 파일 크기, 권한, 수정 시간 등 파일에 대한 정보를 제공하는 os.FileInfo 인터페이스를 얻을 수 있습니다.

파일 상태를 얻는 방법 예시는 다음과 같습니다:

package main

import (
    "fmt"
    "os"
)

func main() {
    fileInfo, err := os.Stat("test.txt")
    if err != nil {
        fmt.Println("파일 정보 가져오기 오류:", err)
        return
    }

    // 파일 크기 출력
    fmt.Printf("파일 크기: %d 바이트\n", fileInfo.Size())

    // 파일 권한 출력
    fmt.Printf("파일 권한: %s\n", fileInfo.Mode())
}

파일 이름을 변경하거나 파일의 권한을 수정해야 하는 경우, os.Rename을 사용하여 파일의 이름을 변경하거나 os.Chmod를 사용하여 파일 권한을 변경할 수 있습니다.

package main

import (
    "fmt"
    "os"
)

func main() {
    // 파일 권한을 읽기 전용으로 변경
    err := os.Chmod("test.txt", 0444)
    if err != nil {
        fmt.Println("파일 권한 변경 오류:", err)
        return
    }

    // 파일 이름 변경
    renameErr := os.Rename("test.txt", "renamed.txt")
    if renameErr != nil {
        fmt.Println("파일 이름 변경 오류:", renameErr)
        return
    }
    
    fmt.Println("파일 작업 성공적으로 완료")
}

여기서 test.txt 파일의 권한을 읽기 전용으로 변경하고, 그런 다음 파일의 이름을 renamed.txt로 변경합니다. 파일 권한을 수정할 때는 잘못된 권한 설정이 파일에 액세스할 수 없게 만들 수 있으므로 주의해야 합니다.

2. IO 라이브러리의 기본 사용

Go 언어에서 io 라이브러리는 I/O 기본 인터페이스를 제공합니다. io 라이브러리의 설계는 간결함과 일관된 인터페이스의 원칙을 따르며 파일 읽기/쓰기, 네트워크 통신, 데이터 버퍼링 등과 같은 다양한 유형의 I/O 작업에 기본 지원을 제공합니다.

2.2 Reader 및 Writer 인터페이스 사용

io.Readerio.Writer는 객체의 읽기 및 쓰기 작업을 지정하는 두 가지 기본 인터페이스입니다. 파일, 네트워크 연결, 버퍼 등과 같이 다양한 유형으로 구현됩니다.

io.Reader

io.Reader 인터페이스에는 Read 메서드가 있습니다:

Read(p []byte) (n int, err error)

이 방법은 io.Reader에서 p로 최대 len(p) 바이트의 데이터를 읽습니다. 읽은 바이트 수 n (0 <= n <= len(p))와 발생한 오류가 반환됩니다.

샘플 코드:

package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    r := strings.NewReader("안녕, 세상!")

    buf := make([]byte, 4)
    for {
        n, err := r.Read(buf)
        if err == io.EOF {
            break
        }
        fmt.Printf("읽은 바이트: %d, 내용: %s\n", n, buf[:n])
    }
}

이 예에서는 문자열에서 데이터를 읽기 위해 strings.NewReader를 만들고 데이터를 4바이트씩 읽습니다.

io.Writer

io.Writer 인터페이스에는 Write 메서드가 있습니다:

Write(p []byte) (n int, err error)

이 방법은 p에서 데이터를 기본 데이터 스트림으로 쓰고, 쓰인 바이트 수와 발생한 오류가 반환됩니다.

샘플 코드:

package main

import (
    "fmt"
    "os"
)

func main() {
    data := []byte("안녕, 세상!\n")
    n, err := os.Stdout.Write(data)
    if err != nil {
        panic(err)
    }
    fmt.Printf("쓴 바이트: %d\n", n)
}

이 예시에서는 간단한 문자열을 표준 출력인 os.Stdout에 씁니다. 이것은 io.Writer의 구현으로 작동합니다.

2.3 고급 읽기/쓰기 함수

io 패키지는 데이터 복사와 지정된 양의 데이터를 읽는 등 일반적인 작업을 간편하게 처리할 수 있는 몇 가지 고급 기능을 제공합니다.

복사 함수

io.Copy는 중간 버퍼 없이 io.Reader에서 바로 데이터를 io.Writer로 복사하는 편리한 방법입니다.

샘플 코드:

package main

import (
    "io"
    "os"
    "strings"
)

func main() {
    r := strings.NewReader("간단한 복사 작업 예시")
    _, err := io.Copy(os.Stdout, r)
    if err != nil {
        panic(err)
    }
}

이 예시에서는 문자열을 직접 표준 출력으로 복사합니다.

ReadAtLeast 함수

io.ReadAtLeast 함수는 지정된 양의 데이터가 반환되기 전에 최소한 지정된 양의 데이터가 io.Reader에서 읽히도록 하는 데 사용됩니다.

func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)

샘플 코드:

package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    r := strings.NewReader("Go 언어 중국어 웹사이트")
    buf := make([]byte, 14)
    n, err := io.ReadAtLeast(r, buf, 14)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("%s\n", buf[:n])
}

이 예시에서 io.ReadAtLeastbuf로 최소 14바이트의 데이터를 읽으려고 시도합니다.

이러한 고급 읽기/쓰기 함수를 사용하면 I/O 관련 작업을 더 효율적으로 처리할 수 있으며, 더 복잡한 프로그램 로직을 구축하는데 견고한 기반을 제공합니다.