4 분 소요

1. Golang의 특징

  • Golang은 구글이 개발한 정적 타입, 컴파일 언어로, 간결한 문법과 빠른 컴파일 속도, 내장된 동시성 지원이 특징인 언어다.
  • 특히 서버 사이드, 분산 시스템, CLI 도구 개발에서 많이 쓰이며, 하나의 바이너리로 배포가 가능해 운영 측면에서도 장점이 크다.
  • 클래스가 없다. 구조체와 포인터를 활용해 객체지향을 구현할 수 있다.

2. Golang을 이용하게 된 이유

  • 직무 상, 가장 많이 만지는 언어는 역시 “Python”이다. 하지만 “Python”은 GIL(Global Interpreter Lock)이라는 제약이 존재한다. 즉 CPU-bound 작업에서 멀티 코어(1코어 이상)을 활용하기 어렵다.(stack overflow)
  • 이를 해결하고자 일반적으로 multiprocessing을 이용하지만 이는 메모리 사용량이 배로 높아질 가능성이 크다. 멀티 코어 기반 고성능 컴파일 언어를 찾던 와중, Golang을 알게 되었다.
  • 처음엔 C++을 공부 하려 다가, 고성능+단순한 문법에 빠져 실제 회사에서도 이용해보기도 하였다. 또한 실제 개발진들이 C++을 욕(?)하며 해당 언어를 개발하였다는 점도 흥미롭게 다가왔다.

3. 기본 문법

1) 기본 데이터 타입 종류

카테고리 타입 크기 Zero Value 비고
불리언 bool 1바이트 false
정수 (부호) int8 1바이트 0 -128 ~ 127
int16 2바이트 0
int32 (rune) 4바이트 0
int64 8바이트 0
int 4/8바이트 0 플랫폼 의존 (추천)
정수 (부호없음) uint8 (byte) 1바이트 0
uint16 2바이트 0
uint32 4바이트 0
uint64 8바이트 0
uint 4/8바이트 0
실수 float32 4바이트 0.0
float64 8바이트 0.0 실무 표준
복소수 complex64 8바이트 0+0i
complex128 16바이트 0+0i
문자열 string 가변 "" 불변 UTF-8
참조 타입 map[K]V - nil make() 필수
[]T (slice) - nil make() 필수
*T (포인터) - nil new() 또는 &

2) 변수 선언 방법

  • var 선언 (타입 명시)
var name string        // 변수 선언 + Zero Value 할당
name = "neo"           // 값 재할당

var age int = 30       // 변수 선언 + 값 할당
  • 단축 선언 (타입 추론)
name := "neo"           // string 자동 추론
age := 30               // int 자동 추론

3) 함수 정의 방법

  • 미반환 함수
func log(msg string) {
	log.Print("[log] " + msg)
}

// 사용 예시
log("Hello World")  // "[log] Hello World"
  • 반환 함수
func add(a int, b int) int {
	var c int
	c = a + b
	return c
}

// 사용 예시
c := add(1, 2) // c는 3
  • 포인터 활용 함수
func add(c *int, a int, b int) {
	*c = a + b	
}

// 사용 예시
var c int
add(&c, 1, 2)  // c는 3

4. 기본 사용 예시

1) 예시 파일 구조

  • 📁 프로젝트 구조 예시 : 하나의 모듈은 하나 이상의 패키지로 구성됨
myapp/
├── go.mod             # 모듈 정의
├── main.go            # package main
└── utils/
    ├── helper.go      # package utils
    └── calc.go        # package utils (같은 패키지)

2) go.mod

  • 📁 go.mod 파일 역할 : 모듈 식별자, 의존성 관리 파일(python 기준 : myapp.toml + requirements.txt)
module myapp                        // 프로젝트 전체 이름
// module github.com/neatynut/myapp // ✅ GitHub에 올릴 때(공유 시)

go 1.22                             // 최소 Go 버전 요구사항

require (                            // 직접 사용하는 외부 패키지들
    github.com/gin-gonic/gin v1.9.1  // 웹 프레임워크
    github.com/sirupsen/logrus v1.9.3 // 로깅
)

require github.com/spf13/viper v1.18.2 // indirect (간접 의존성, 자동 추가)

3) main.go

  • 📁 main.go 파일 역할 : 프로그램 진입 점(main() 실행)
package main

import (  
"fmt"         // 기본 go 패키지
)

func main() {  
fmt.Println("Hello, neo")  
}
  • package : python에서 *.py 또는 class단위로 import 가능하다. 이와 비슷한 단위로 이해하였다.
  • import : 외부에서 package를 이용할 때 사용(python의 import와 동일)

4) 모듈 구성 예시

  • 주의 사항
    • 같은 패키지는 같은 폴더에 위치해야 함
    • 패키지 간 순환 import(import cycle) 불가

  • 📁 utils/helper.go 예시
package utils

import "fmt"

// 공개 함수: 문자열 인사말 생성
func Greet(name string) string {
    return "Hello, " + name + "!"
}

// 내부 전용 함수 (같은 패키지만 사용)
func log(msg string) {
    fmt.Println("[LOG]", msg)
}
  • exported vs unexported : 대/소문자 시작 여부
    • Greet 함수는 package main/utils 에서 사용 가능하나 log 함수는 package utils 내부에서만 사용 가능

  • 📁 utils/calc.go 예시
package utils

// 공개 구조체 + 메서드
type Calculator struct {
    Name string
}

// 값 리시버: 읽기 전용
func (c Calculator) Add(a, b int) int {
    return a + b
}

// 포인터 리시버: 상태 변경
func (c *Calculator) Multiply(result *int, a, b int) {
    *result = a * b
    log("Multiplied: " + c.Name)  // 같은 패키지 내부 함수 호출
}
  • 값 리시버 vs 포인트 리시버
항목 값 리시버(u User) 포인터 리시버(u *User)
전달 방식 복사본 전달 주소 전달(8바이트)
원본 변경 ❌ 불가능 ✅ 가능
메모리 복사 구조체 전체 복사 주소만 복사
용도 읽기전용(getter) 쓰기/변경(setter)
  • 값 리시버, 포인터 리시버가 상황에 따라 잘 사용해야 하는 이유
    • 성능 최적화 : 구조체의 용량이 클때, 메모리 주소 만을 넘기는 포인터 리시버가 유리할 수 있음.
    • 안정성 : 원본 변경에 제약을 둬야 할때, 값 리시버가 유리.

  • 📁 main.go의 utils 사용 예시
package main

import (
    "fmt"
    "myapp/utils"    // 내부 패키지 포함
)

func main() {
    // helper.go 함수 사용
    fmt.Println(utils.Greet("neo"))  // Hello, neo!

    // calc.go 구조체 + 메서드 사용
    calc := utils.Calculator{Name: "basic"}
    sum := calc.Add(2, 3)
    fmt.Println("2+3 =", sum)  // 2+3 = 5

    var product int
    calc.Multiply(&product, 4, 5)
    fmt.Println("4*5 =", product)  // 4*5 = 20
}
  • 이때 main 패키지에서 utils 패키지를 import 했기에, utils 패키지가 main 패키지를 import 하여 사용하는 것이 불가능

태그: ,

카테고리:

업데이트:

댓글남기기