소개
이전 섹션에서 다음과 같은 코드 줄을 포함하는 기본적인 Go 프로그램을 완성했습니다.
package main
import "fmt"
이 두 줄의 코드를 어떻게 이해해야 할까요? package 및 import 문을 효과적으로 사용하는 방법은 무엇일까요?
이 Lab 에서는 Go 에서 패키지를 생성하고 가져오는 방법을 배우게 됩니다. 이를 통해 코드를 재사용 가능한 모듈로 구성하여 Go 프로젝트의 유지 관리 및 확장성을 향상시킬 수 있습니다.
지식 포인트:
- 패키지의 정의 및 선언
- export 된 (public) 및 unexported 된 (private) 식별자 이해
- 패키지 가져오기의 다양한 형태: 단일, 그룹, dot, alias, 익명 import
패키지 선언 및 정의
Go 에서 **패키지 (package)**는 Python 의 모듈 또는 C 의 라이브러리와 유사합니다. 코드를 구성하고 재사용하기 위해 사용되는 소스 코드 파일의 모음입니다. 모든 Go 파일은 파일 시작 부분에서 패키지를 선언해야 합니다.
참고: Go 프로그램은 실행 진입점 역할을 하는
main이라는 이름의 패키지를 하나만 가져야 합니다. 이것이 없으면 프로그램은 실행 파일을 생성할 수 없습니다.
핵심 포인트:
- Exported (Public) 식별자: 대문자로 시작하는 식별자 (변수, 함수, 타입 등) 는 다른 패키지에서 접근할 수 있습니다. 이것을 패키지의 public 인터페이스라고 생각하십시오.
- Unexported (Private) 식별자: 소문자로 시작하는 식별자는 동일한 패키지 내에서만 접근할 수 있습니다. 이것은 패키지의 내부 구현 세부 사항으로 간주됩니다.
- 패키지 응집성: 동일한 폴더의 모든 파일은 동일한 패키지에 속해야 합니다. 이렇게 하면 관련 코드가 함께 유지됩니다.
- 패키지 명명 규칙: 패키지 이름은 소문자, 짧고 설명적이어야 하며 밑줄 또는 대문자를 사용하지 않아야 합니다.
자신만의 사용자 정의 패키지를 만들어 봅시다:
propagandist라는 폴더와 그 안에propagandist.go파일을 만듭니다:mkdir ~/project/propagandist touch ~/project/propagandist/propagandist.gopropagandist.go에 다음 코드를 작성합니다:package propagandist var Shout = "I Love LabEx" // Public variable var secret = "I love the dress" // Private variable func Hit() string { return "Don't hit me, please!" }Shout는 public입니다. 대문자로 시작하기 때문입니다. 즉,propagandist를 import 하는 다른 패키지에서 접근할 수 있습니다.secret는 private입니다. 소문자로 시작하기 때문입니다.propagandist패키지 내에서만 사용할 수 있습니다.Hit는 다른 패키지에서 접근할 수 있는 public 함수입니다.
패키지에 대한 Go 모듈을 초기화합니다:
cd ~/project/propagandist go mod init propagandist이 명령은
propagandist디렉토리에 새 Go 모듈을 초기화하여 패키지의 종속성을 관리하는 데 도움이 됩니다.
단일 항목 Import
propagandist 패키지를 사용하기 위해 새로운 Go 프로그램을 만들어 봅시다. 이 단계에서는 Go 코드에서 단일 패키지를 import 하고 사용하는 방법을 보여줍니다.
프로젝트 폴더에
pacExercise.go라는 새 Go 파일을 만듭니다:touch ~/project/pacExercise.go프로그램에 대한 Go 모듈을 초기화합니다:
cd ~/project go mod init pacExercisego.mod파일을 업데이트하여 로컬 패키지 종속성을 포함합니다. 터미널에서 다음 명령을 실행합니다:echo "replace propagandist => ./propagandist" >> go.mod중요: 이 명령은
go.mod파일에replace지시문을 추가합니다. 이는 Go 에게propagandist패키지를 원격 저장소에서 다운로드하는 대신 로컬 디렉토리./propagandist에서 가져와야 한다고 알려주기 때문에 중요합니다. 터미널에서 이 명령을 실행해야 하며, 그러면replace propagandist => ./propagandist줄이go.mod파일에 추가됩니다. 이 줄을 파일에 직접 수동으로 작성하지 마십시오.propagandist패키지를 import 하고 사용하기 위해pacExercise.go에 다음 코드를 작성합니다:package main import ( "fmt" "propagandist" ) func main() { fmt.Println(propagandist.Shout) // Access the public variable }- 이 코드는 출력을 위해
fmt패키지와propagandist패키지를 import 합니다. - 그런 다음
propagandist.Shout를 사용하여propagandist패키지에서 public 변수Shout에 접근합니다.
- 이 코드는 출력을 위해
프로그램을 실행합니다:
go mod tidy go run pacExercise.gogo mod tidy명령은go.mod파일이 새로운 종속성으로 업데이트되었는지 확인합니다.go run pacExercise.go명령은 프로그램을 컴파일하고 실행합니다.예상 출력:
I Love LabEx
그룹 임포트
여러 패키지를 import 할 때, 가독성과 구성을 위해 그룹화된 import 를 사용할 수 있습니다. 이는 스타일 선택이며 코드의 기능에는 영향을 미치지 않습니다.
pacExercise.go를 수정하여 그룹화된 import 를 사용합니다:package main import ( "fmt" "propagandist" ) func main() { fmt.Println(propagandist.Shout) }위 코드 조각에서
fmt및propagandist패키지는 괄호로 묶인 단일import블록 내에서 import 됩니다. 이렇게 하면 여러 패키지 import 를 더 쉽게 읽고 관리할 수 있습니다. 이는 이전 예제와 정확히 동일하며 그룹화된 import 구문을 사용하는 방법을 보여줍니다.프로그램을 실행하여 여전히 작동하는지 확인합니다:
go run pacExercise.go프로그램은 오류 없이 실행되어 이전과 동일한 결과를 출력해야 합니다.
Dot Import (점 임포트)
**Dot import (점 import)**를 사용하면 함수나 변수를 호출할 때 패키지 이름 접두사를 생략할 수 있습니다. 이는 네임스페이스 충돌을 일으키고 가독성을 떨어뜨릴 수 있으므로 명시적인 패키지 이름을 사용하는 것이 권장되지 않습니다. 하지만, 이것이 무엇인지 아는 것은 좋습니다.
pacExercise.go를 수정하여fmt에 dot import 를 사용합니다:package main import . "fmt" import "propagandist" func main() { Println(propagandist.Shout) // No `fmt.` prefix needed }- 여기서
import . "fmt"는fmt패키지의 함수와 변수를fmt.접두사 없이 직접 사용할 수 있음을 의미합니다. - 예를 들어,
fmt.Println대신Println을 사용합니다.
- 여기서
프로그램을 실행합니다:
go run pacExercise.go예상 출력:
I Love LabEx
Alias Import (별칭 임포트)
명확성을 위해 또는 두 패키지가 유사한 이름을 가질 때 충돌을 피하기 위해 import 된 패키지에 별칭을 지정할 수 있습니다. 이는 코드를 더 읽기 쉽게 만들고 네임스페이스 충돌을 관리하는 데 유용합니다.
pacExercise.go를 수정하여fmt를io로 별칭 지정합니다:package main import io "fmt" import "propagandist" func main() { io.Println(propagandist.Shout) // Use the alias `io` instead of `fmt` }import io "fmt"는fmt패키지에 대한 별칭io를 생성합니다.- 이제
fmt.Println대신io.Println을 사용합니다.
프로그램을 실행합니다:
go run pacExercise.go
Anonymous Import (익명 임포트)
익명 import 는 내보낸 식별자를 직접 참조할 필요 없이, init() 함수를 실행하는 것과 같은 부작용을 위해 패키지를 import 하는 데 사용됩니다. 이는 드라이버를 등록하거나 기타 초기화 작업을 수행하는 패키지에 유용합니다.
pacExercise.go를 수정하여time에 대한 익명 import 를 포함합니다:package main import ( "fmt" "propagandist" _ "time" // Anonymous import ) func main() { fmt.Println(propagandist.Shout) }import _ "time"은 익명 import 입니다. 밑줄_는 빈 식별자로 사용되어, 컴파일러에게 패키지를 부작용을 위해 import 하고 코드에서 직접적으로 패키지의 어떤 것도 참조하지 않을 것임을 알립니다.- 이 프로그램이 실행될 때
time패키지의init()함수가 실행됩니다.time패키지는 여기에서 특별한 부작용을 가지고 있지 않지만, 많은 패키지가 데이터베이스 드라이버 또는 구성 설정을 등록하기 위해 이것을 사용합니다.
프로그램을 실행합니다:
go run pacExercise.go예상 출력:
I Love LabEx
init() 함수 이해하기
init() 함수는 Go 에서 패키지 초기화에 중요한 역할을 하는 특별한 함수입니다. main 함수의 다른 코드가 실행되기 전에, 패키지가 import 될 때 Go 에 의해 자동으로 실행됩니다. 이 섹션에서는 init() 함수의 세부 사항과 Go 의 초기화 프로세스 내에서 작동 방식을 설명합니다.
init() 함수에 대한 주요 사항:
정의 및 목적:
init()함수는 매개변수와 반환 값이 없습니다:func init() {}- 초기 상태 설정, 드라이버 등록 또는 필수 조건 검증과 같은 패키지 초기화 작업에 사용됩니다.
실행 순서:
- Go 는 패키지가 여러 번 import 되더라도 패키지 초기화가 한 번만 발생하도록 보장합니다.
- 초기화는 잘 정의된 순서를 따릅니다:
- 패키지 수준 변수가 먼저 초기화됩니다.
- 그런 다음
init()함수가 실행됩니다. - 마지막으로
main()함수가 실행됩니다 (main 패키지에서만).
여러
init()함수:- 단일 Go 파일은 여러
init()함수를 포함할 수 있습니다. - 동일한 패키지의 여러 파일은 각각 자체
init()함수를 가질 수 있습니다. - 이 모든
init()함수는 실행되지만, 동일한 패키지 내의 순서는 보장되지 않습니다.
- 단일 Go 파일은 여러
종속성 체인:
- 패키지가 다른 패키지를 import 할 때, Go 는 종속성의
init()함수가 먼저 실행되도록 보장합니다. - 이는 하향식 초기화 흐름을 생성합니다: 가장 깊은 종속성이 먼저 초기화됩니다.
- 패키지가 다른 패키지를 import 할 때, Go 는 종속성의
init() 함수가 어떻게 작동하는지 보여주는 실용적인 예제를 만들어 보겠습니다:
먼저,
propagandist패키지를 수정하여init()함수를 포함해 보겠습니다.propagandist.go를 업데이트합니다:package propagandist import "fmt" var Shout = "I Love LabEx" // Public variable var secret = "I love the dress" // Private variable var initialized bool func init() { fmt.Println("Initializing propagandist package...") initialized = true } func Hit() string { return "Don't hit me, please!" } func IsInitialized() bool { return initialized }이제,
propagandist패키지에 여러init()함수를 보여주기 위해 다른 파일을 만들어 보겠습니다:touch ~/project/propagandist/second.go파일에 다음 내용을 추가합니다:
package propagandist import "fmt" func init() { fmt.Println("Second init function in propagandist package...") }초기화 순서를 보여주기 위해 새로운 helper 패키지를 생성합니다:
mkdir -p ~/project/helper touch ~/project/helper/helper.go파일에 다음 내용을 추가합니다:
package helper import "fmt" var Message = "Helper package is ready" func init() { fmt.Println("Initializing helper package...") } func GetMessage() string { return Message }helper 패키지에 대한 모듈 파일을 추가합니다:
cd ~/project/helper go mod init helperpacExercise.go를 업데이트하여 두 패키지를 모두 사용하고 초기화 순서를 보여줍니다:package main import ( "fmt" "helper" "propagandist" ) func init() { fmt.Println("Initializing main package...") } func main() { fmt.Println("Main function is running") fmt.Println(propagandist.Shout) fmt.Println(helper.Message) fmt.Printf("Propagandist initialized: %v\n", propagandist.IsInitialized()) }main 프로젝트의
go.mod파일을 업데이트하여 로컬 helper 패키지를 포함합니다:cd ~/project echo "replace helper => ./helper" >> go.mod go mod tidy프로그램을 실행하고 초기화 순서를 관찰합니다:
go run pacExercise.go예상 출력 (두 propagandist init 함수의 정확한 순서는 다를 수 있습니다):
Initializing helper package... Initializing propagandist package... Second init function in propagandist package... Initializing main package... Main function is running I Love LabEx Helper package is ready Propagandist initialized: true
이 출력은 Go 의 초기화 프로세스의 주요 측면을 보여줍니다:
- 종속 패키지는 이를 import 하는 패키지보다 먼저 초기화됩니다.
- 단일 패키지 내에서 모든
init()함수가 실행됩니다 (순서는 보장되지 않음). main()함수는 모든 패키지 초기화가 완료된 후에만 실행됩니다.- 패키지 수준 변수는 모든
init()함수가 실행되기 전에 초기화됩니다.
이 초기화 시퀀스는 Go 패키지를 설계할 때, 특히 종속성을 관리하거나 특정 순서로 발생해야 하는 설정 작업을 수행할 때 중요한 고려 사항입니다.
요약
이 랩에서 다음을 배웠습니다:
- 재사용 가능한 코드를 캡슐화하여 Go 에서 사용자 정의 패키지를 생성하고 정의하는 방법.
- 공개 (export) 식별자와 비공개 (unexport) 식별자의 차이점과 접근성에 미치는 영향.
- 각 사용 사례에 맞는 다양한 패키지 import 방법:
- 단일 항목 import: 한 번에 하나의 패키지를 import 합니다.
- 그룹 import: 더 나은 구성을 위해 단일 블록에서 여러 패키지를 import 합니다.
- Dot import: 패키지 이름 접두사 없이 식별자를 직접 사용하는 패키지를 import 합니다. (주의해서 사용)
- Alias import: 가독성을 높이거나 이름 충돌을 피하기 위해 import 된 패키지의 이름을 변경합니다.
- Anonymous import: 초기화와 같은 부작용만을 위해 패키지를 import 합니다.
- 패키지에서
init()함수의 역할과 익명 import 가 실행을 트리거하는 방법. - Go 의 초기화 프로세스에 대한 자세한 작동 방식, 다음을 포함합니다:
init()함수 전에 패키지 수준 변수가 초기화되는 방법- 종속 패키지 간의
init()함수의 보장된 실행 순서 - 패키지 내에서 여러
init()함수가 작동하는 방식 - 종속 패키지에서 main 함수까지의 완전한 초기화 흐름
이 랩을 완료함으로써, 이제 패키지를 효과적으로 사용하여 Go 프로젝트를 구조화하고 관리할 수 있습니다. 재사용 가능한 모듈을 생성하고, 식별자에 대한 접근을 제어하며, 코드를 더 잘 구성하고, 초기화 프로세스를 이해하여 더 유지 관리 가능하고 확장 가능한 Go 애플리케이션을 만들 수 있습니다.



