
Go 1.18에서 제네릭이 도입되었지만, 메서드에는 타입 파라미터를 선언할 수 없었습니다. 제네릭 타입에 메서드를 정의하는 것은 가능했지만, 메서드가 리시버와 별개로 자기만의 타입 파라미터를 갖는 것은 문법 오류였습니다. Go FAQ에도 "Go에 제네릭 메서드가 추가될 것으로 예상하지 않는다(We do not anticipate that Go will ever add generic methods)"라고 적혀 있었습니다.
그런데 2026년 8월에 출시된 Go 1.27부터 메서드에도 타입 파라미터를 선언할 수 있게 되었습니다. 다만 한 가지 제약이 있습니다. 제네릭 메서드로는 인터페이스를 구현할 수 없습니다.
재미있게도 이 방식은 Go 1.18의 제네릭을 설계할 때 이미 검토했다가 제외했던 방식입니다. 그런데 왜 이제 와서 받아들였을까요?
이번 글에서는 Go가 그동안 제네릭 메서드를 지원하지 않았던 이유를 FAQ 예제로 살펴보고, Go 1.27에서 어떤 방식으로 지원하게 되었는지, 실제로 쓸 때 무엇을 주의해야 하는지 알아보겠습니다. 본문의 코드는 모두 Go 1.27.1에서 직접 실행해 확인했습니다.
목차
- 제네릭 메서드란?
- Go 1.26까지는 어떻게 했을까?
- 왜 그동안 지원하지 않았을까?
- 검토했던 네 가지 해결 방법
- Go 1.27은 이 문제를 어떻게 풀었을까?
- 사용법
- 인터페이스와 함께 쓰면 어떻게 될까?
- 표준 라이브러리에 추가된
Rand.N - 도입 전에 확인할 점
- 언제 제네릭 메서드를 쓰면 좋을까?
- 마치며
- 참고 자료
제네릭 메서드란?
제네릭 메서드(generic method)는 메서드 선언에 타입 파라미터가 붙은 메서드입니다. 리시버 타입과 상관없이, 호출할 때마다 다른 타입을 받을 수 있습니다.
Java에서는 오래전부터 지원하던 기능입니다.
public class Example {
public <T> T nop(T x) {
return x;
}
public static void main(String[] args) {
Example example = new Example();
System.out.println(example.nop("hello")); // hello
System.out.println(example.nop(42)); // 42
}
}
Example 클래스는 제네릭 클래스가 아니지만, nop 메서드는 호출할 때마다 String을 받기도 하고 Integer를 받기도 합니다.
같은 코드를 Go로 작성하면 다음과 같습니다.
type Empty struct{}
func (Empty) Nop[T any](x T) T {
return x
}
메서드 이름 Nop 바로 뒤에 [T any]가 붙어 있습니다. Go 1.26까지는 이 코드를 컴파일할 수 없었습니다.
리시버 타입 파라미터와는 무엇이 다를까?
Go 1.18부터 쓸 수 있었던 것은 리시버 타입 파라미터입니다.
type Box[T any] struct{ v T }
func (b Box[T]) Get() T { return b.v }
여기서 T는 Box[T] 타입을 만들 때 정해집니다. Box[int] 값의 Get은 항상 int를 반환하고, 호출할 때마다 타입이 바뀌지는 않습니다.
제네릭 메서드는 여기에 메서드만의 타입 파라미터를 추가합니다. 하나의 Box[int] 값에서 Convert[string]도, Convert[float64]도 호출할 수 있게 됩니다.
Go 1.26까지는 어떻게 했을까?
메서드에 타입 파라미터가 필요한 대표적인 경우는 값의 타입을 바꾸는 변환 메서드입니다. 슬라이스를 감싼 Stream[T] 타입에 Filter와 Map을 추가해서 체이닝으로 호출하고 싶다고 가정해 보겠습니다.
Filter는 Stream[T]를 받아 Stream[T]를 반환하므로 리시버 타입 파라미터만으로 충분합니다. 하지만 Map은 Stream[T]를 받아 Stream[U]를 반환해야 합니다. U는 Map을 호출하는 시점에 정해지므로 리시버에 둘 수 없습니다.
그래서 Go 1.26까지는 Map을 패키지 함수로 만들어야 했습니다.
type Stream[T any] struct{ items []T }
func Of[T any](xs ...T) Stream[T] { return Stream[T]{xs} }
func (s Stream[T]) Filter(f func(T) bool) Stream[T] {
var out []T
for _, x := range s.items {
if f(x) {
out = append(out, x)
}
}
return Stream[T]{out}
}
func (s Stream[T]) Slice() []T { return s.items }
// 메서드로 만들 수 없어서 패키지 함수로 작성
func Map[T, U any](s Stream[T], f func(T) U) Stream[U] {
out := make([]U, 0, len(s.items))
for _, x := range s.items {
out = append(out, f(x))
}
return Stream[U]{out}
}
호출하는 코드는 이렇게 됩니다.
out := Map(Of(1, 2, 3, 4, 5).Filter(odd), stars).Slice()
// [* *** *****]
동작은 문제없지만 코드를 읽기가 불편합니다. 실제 실행 순서는 Of → Filter → Map → Slice인데, 코드에서는 Map이 가장 앞에 오고 Filter는 그 인자 안에 들어가 있습니다. 변환 단계가 늘어날수록 괄호도 계속 중첩됩니다.
이름 충돌 문제도 있습니다. Map은 Stream 전용 함수인데 패키지 전체에 노출됩니다. 같은 패키지에 Tree[T]용 변환 함수가 필요해지면 MapStream, MapTree처럼 이름에 타입을 붙여서 구분해야 합니다.
이 기능을 요청한 GitHub 이슈 #49085("allow type parameters in methods")는 2021년 10월에 등록되어 900개가 넘는 👍를 받았습니다. 요청 대부분이 바로 이 두 가지, 체이닝과 이름 충돌 문제였습니다.
왜 그동안 지원하지 않았을까?
Go가 그동안 제네릭 메서드를 지원하지 않았던 이유는 구현할 방법이 마땅치 않았기 때문입니다. Go FAQ의 예제를 따라가 보겠습니다.
앞에서 본 Empty 값을 any에 담아서, 이 값에 어떤 메서드가 있는지 모르는 함수에 넘긴다고 해 보겠습니다.
func TryNops(x any) {
if x, ok := x.(interface{ Nop(string) string }); ok {
fmt.Printf("string %s\n", x.Nop("hello"))
}
if x, ok := x.(interface{ Nop(int) int }); ok {
fmt.Printf("int %d\n", x.Nop(42))
}
if x, ok := x.(interface{ Nop(io.Reader) io.Reader }); ok {
data, err := io.ReadAll(x.Nop(strings.NewReader("hello world")))
fmt.Printf("reader %q %v\n", data, err)
}
}
Empty.Nop[T]는 어떤 T든 받을 수 있으니, 논리적으로는 세 타입 단언이 모두 성공해야 합니다. Empty는 Nop(string) string도, Nop(int) int도, Nop(io.Reader) io.Reader도 가지고 있다고 볼 수 있기 때문입니다.
그렇다면 x.Nop(42)를 호출할 때 실행되는 기계어 코드는 어디에 있을까요?
Go는 실행 전에 모든 코드를 미리 컴파일하는 AOT(ahead-of-time) 방식을 사용합니다. 일반 메서드는 컴파일러가 구현 코드를 미리 만들어 바이너리에 넣어 두고, 런타임의 타입 단언은 이미 만들어진 메서드 테이블(itab)을 조회하기만 합니다.
반면 제네릭 메서드는 타입 인자마다 구현이 달라질 수 있어서, 만들 수 있는 구현이 무한히 많습니다. 그리고 컴파일러는 어떤 구현이 필요할지 미리 알 수 없습니다.
TryNops를 컴파일할 때는x에 어떤 타입이 들어올지 모릅니다.Empty를 컴파일할 때는 누군가Nop(io.Reader) io.Reader를 찾을 거라는 사실을 모릅니다.
결국 Nop[io.Reader]가 필요하다는 사실은 프로그램이 실행된 뒤에야 알 수 있습니다.
Go 1.18의 제네릭 설계 문서(Type Parameters Proposal)도 같은 문제를 다룹니다. 제네릭 메서드를 정의하는 패키지, 그 메서드를 요구하는 인터페이스를 정의하는 패키지, 인터페이스로 메서드를 호출하는 패키지, 실제 값을 넘기는 패키지가 모두 다를 수 있습니다. 어느 패키지를 컴파일하든, 그 시점에는 필요한 인스턴스를 알 수 없습니다.
검토했던 네 가지 해결 방법
Go FAQ에는 이 문제를 해결할 수 있는 방법 네 가지와, 각 방법을 채택하지 않은 이유가 정리되어 있습니다.
| # | 방법 | 채택하지 않은 이유 |
|---|---|---|
| 1 | 링크할 때 동적 인터페이스 검사를 모두 모은 뒤, 빠진 메서드 인스턴스를 찾아서 다시 컴파일한다 | 링크 후에 컴파일을 반복해야 해서 빌드가 크게 느려지고, 특히 증분 빌드가 느려진다. 새로 컴파일한 코드에 또 다른 동적 검사가 생기면 이 과정이 끝나지 않을 수도 있다 |
| 2 | JIT를 도입해서 필요한 메서드 코드를 런타임에 컴파일한다 | Go는 AOT 컴파일 덕분에 단순하고 성능을 예측하기 쉽다. 기능 하나 때문에 JIT를 도입하기에는 복잡도가 너무 크다 |
| 3 | 타입 파라미터로 할 수 있는 모든 연산을 함수 테이블로 처리하는 느린 구현을 따로 만들어 두고, 동적 검사에서는 그것을 쓴다 | 컴파일할 때 보지 못한 타입으로 호출하면 훨씬 느려져서 성능을 예측하기 어렵다 |
| 4 | 제네릭 메서드로는 인터페이스를 구현할 수 없도록 한다 | 인터페이스는 Go 프로그래밍의 핵심이므로 설계상 받아들이기 어렵다 |
결국 "어느 방법도 좋지 않아서 아무것도 선택하지 않았다"는 것이 당시의 결론이었습니다. 대신 FAQ에서는 타입 파라미터가 있는 패키지 함수를 쓰거나, 타입 파라미터를 리시버 타입에 추가하라고 안내하고 있습니다.
1.18 설계 문서에는 4번 방법에 대한 설명이 하나 더 있습니다. 제네릭 메서드로 인터페이스를 구현할 수 없다면 "애초에 메서드가 왜 필요한지 훨씬 불분명해진다"는 것입니다. 당시에는 메서드의 가장 중요한 역할을 인터페이스 구현으로 보았습니다.
Go 1.27은 이 문제를 어떻게 풀었을까?
2026년 1월 22일, Go를 공동 설계한 Robert Griesemer가 제안 #77273("spec: generic methods for Go")을 올렸습니다. 이 제안이 승인되어 2026년 8월 19일 출시된 Go 1.27에 포함되었고, 기존 이슈 #49085는 이 제안의 중복으로 닫혔습니다.
Go 1.27이 선택한 것은 FAQ의 4번 방법입니다. 제네릭 메서드로는 인터페이스를 구현할 수 없습니다. 1.18 때 "받아들이기 어렵다"며 제외했던 방법을 그대로 채택한 것입니다.
그렇다면 무엇이 달라졌을까요? 제안서에서는 이렇게 설명합니다.
concrete methods are a language feature that is useful in itself, irrespective of interfaces (구체 타입의 메서드는 인터페이스와 상관없이 그 자체로 유용한 기능이다)
메서드를 쓰는 이유가 인터페이스 구현만은 아니라는 것입니다. 관련된 함수를 타입 아래에 모아 둘 수 있고, x.a().b().c()처럼 실행 순서대로 읽히는 코드를 만들 수 있습니다. 앞에서 본 Stream.Map의 체이닝 문제와 이름 충돌 문제도 인터페이스와는 관련이 없습니다. 인터페이스를 구현하지 못하더라도, 이 두 문제를 해결하는 것만으로 충분히 가치가 있다고 본 것입니다.
구현 문제도 이렇게 풀립니다. 제안서에서는 제네릭 메서드를 리시버가 있는 제네릭 함수로 정의합니다. 제네릭 함수는 호출하는 코드를 컴파일할 때 필요한 인스턴스를 모두 알 수 있습니다. 제네릭 메서드도 인터페이스를 통해 호출될 일이 없으니 마찬가지입니다. 1.18 때 문제가 되었던 "실행 중에 새로운 인스턴스가 필요해지는 상황"이 생기지 않습니다.
제네릭 인터페이스 메서드의 가능성도 남겨 두었습니다. 제안서에는 이번 변경이 "나중에 적절한 구현 방법을 찾는다면 제네릭 인터페이스 메서드를 구현하는 것을 막지 않는다"고 적혀 있습니다.
사용법
선언과 호출
타입 파라미터 목록은 메서드 이름 뒤, 파라미터 목록 앞에 씁니다. 제네릭 함수와 같은 위치입니다.
type Empty struct{}
func (Empty) Nop[T any](x T) T { return x }
func main() {
var e Empty
fmt.Println(e.Nop("a"), e.Nop(1), e.Nop[float64](2))
// a 1 2
}
타입 인자는 제네릭 함수처럼 인자로부터 추론되고, e.Nop[float64](2)처럼 직접 지정할 수도 있습니다. 타입 파라미터는 메서드 이름 다음부터 메서드 본문 끝까지 사용할 수 있습니다.
리시버 타입 파라미터와 함께 쓰기
실무에서 가장 유용한 형태는 리시버와 메서드가 모두 제네릭인 경우입니다. 앞에서 패키지 함수로 만들었던 Map을 이제 메서드로 옮길 수 있습니다.
func (s Stream[T]) Map[U any](f func(T) U) Stream[U] {
out := make([]U, 0, len(s.items))
for _, x := range s.items {
out = append(out, f(x))
}
return Stream[U]{out}
}
out := Of(1, 2, 3, 4, 5).
Filter(func(n int) bool { return n%2 == 1 }).
Map(func(n int) string { return strings.Repeat("*", n) }).
Slice()
// [* *** *****]
이제 코드를 위에서 아래로 읽는 순서와 실행 순서가 같습니다.
리시버의 타입 파라미터 T와 메서드의 타입 파라미터를 함께 쓸 수 있고, 포인터 리시버에도 사용할 수 있습니다.
type List[T any] struct{ xs []T }
func (l List[T]) IndexBy[K cmp.Ordered](key func(T) K) map[K]T {
m := make(map[K]T)
for _, x := range l.xs {
m[key(x)] = x
}
return m
}
type Box[T any] struct{ v T }
func (b *Box[T]) Convert[U any](f func(T) U) *Box[U] {
return &Box[U]{f(b.v)}
}
l := List[user]{[]user{{1, "kim"}, {2, "lee"}}}
fmt.Println(l.IndexBy(func(u user) int { return u.id })[2].name) // lee
b := &Box[int]{21}
fmt.Println(b.Convert(func(n int) string { return fmt.Sprint(n * 2) }).v) // 42
메서드 값과 메서드 표현식
메서드 값(e.Nop)과 메서드 표현식(Empty.Nop)은 제네릭 함수가 됩니다. 그래서 제네릭 함수처럼 타입을 지정하지 않으면 변수에 담을 수 없습니다.
f := e.Nop[int] // 메서드 값: func(int) int
fmt.Println(f(7)) // 7
g := Empty.Nop[string] // 메서드 표현식: func(Empty, string) string
fmt.Println(g(e, "x")) // x
h := e.Nop // 컴파일 오류
// cannot use generic function e.Nop without instantiation
함수 타입이 정해진 파라미터에 넘길 때는 타입 인자를 생략할 수 있습니다. Go 1.27에서 제네릭 함수를 함수 타입 변수에 대입하는 모든 경우로 타입 추론이 확장되었고, 제네릭 메서드 값도 마찬가지입니다.
func apply(f func(int) int, v int) int { return f(v) }
fmt.Println(apply(e.Nop, 3)) // 3 (T = int로 추론)
임베딩
임베딩한 타입의 제네릭 메서드도 일반 메서드처럼 승격(promotion)됩니다.
type Wrapper struct{ Empty }
var w Wrapper
fmt.Println(w.Nop("promoted")) // promoted
인터페이스와 함께 쓰면 어떻게 될까?
Go 1.27 릴리스 노트에는 제약 사항이 이렇게 적혀 있습니다.
methods of interfaces may not declare type parameters nor can interface methods be implemented by generic methods.
인터페이스 메서드에는 타입 파라미터를 선언할 수 없고, 제네릭 메서드로 인터페이스 메서드를 구현할 수도 없다는 내용입니다. 실제로 어떻게 동작하는지 하나씩 확인해 보겠습니다.
인터페이스 메서드에 타입 파라미터 선언하기
type Nopper interface {
Nop[T any](x T) T
}
./main.go:4:5: interface method must have no type parameters
컴파일 오류가 발생합니다.
제네릭 메서드로 인터페이스 구현하기
Empty.Nop[string]은 func(string) string과 시그니처가 같지만, Empty는 Nop(string) string을 요구하는 인터페이스를 구현하지 못합니다.
type StringNopper interface{ Nop(string) string }
var _ StringNopper = Empty{}
./main.go:10:23: cannot use Empty{} (value of struct type Empty) as StringNopper value
in variable declaration: Empty does not implement StringNopper (wrong type for method Nop)
have Nop[T any](T) T
want Nop(string) string
이렇게 컴파일 시점에 확인할 수 있는 경우는 오류가 나기 때문에 바로 알아챌 수 있습니다. 주의할 것은 런타임 타입 단언입니다. 앞에서 본 FAQ의 TryNops를 Go 1.27에서 실행해 보면, 오류 없이 세 분기 모두 실행되지 않습니다.
TryNops(Empty{})
Nop(string) string: no
Nop(int) int: no
Nop(io.Reader) io.Reader: no
(결과를 확인하기 위해 각 if에 else 분기를 추가해서 실행했습니다.)
제네릭 메서드는 동적 검사의 대상이 되지 않기 때문에, 실행 중에 새 인스턴스가 필요해지는 상황도 생기지 않습니다. 1.18 때의 구현 문제가 이렇게 해결됩니다.
reflect에서도 보이지 않는다
같은 이유로 reflect에서도 제네릭 메서드는 보이지 않습니다. 제안서에 따르면, 인스턴스화하지 않은 제네릭 함수를 reflect로 다룰 수 없는 기존 동작에 맞춘 것입니다.
type Empty struct{}
func (Empty) Nop[T any](x T) T { return x }
func (Empty) Hello() string { return "hi" }
t := reflect.TypeOf(Empty{})
fmt.Println("NumMethod:", t.NumMethod())
for i := 0; i < t.NumMethod(); i++ {
fmt.Println(" -", t.Method(i).Name)
}
NumMethod: 1
- Hello
메서드가 두 개인데 Hello 하나만 조회됩니다. 따라서 메서드 이름으로 핸들러를 등록하는 RPC 프레임워크처럼 reflect로 메서드 목록을 순회하는 코드에서는 제네릭 메서드가 없는 것으로 처리됩니다. text/template에서 {{ .Nop }}으로 호출하는 것도 불가능합니다.
표준 라이브러리에 추가된 Rand.N
Go 1.27에서는 표준 라이브러리에도 제네릭 메서드가 하나 추가되었습니다. math/rand/v2의 *Rand 타입에 추가된 N 메서드입니다.
$ go doc math/rand/v2 Rand.N
func (r *Rand) N[Int intType](n Int) Int
N returns a pseudo-random number in the half-open interval [0,n). The type
parameter Int can be any integer type. It panics if n <= 0.
기존에는 패키지 함수 rand.N만 있었습니다. 이 함수는 기본 난수 소스를 사용하기 때문에, 시드를 고정한 *Rand 인스턴스에서는 r.Int64N, r.IntN처럼 타입별 메서드를 골라서 호출해야 했습니다. Go 1.27부터는 인스턴스에서도 타입과 상관없이 N 하나로 호출할 수 있습니다.
r := rand.New(rand.NewPCG(1, 2))
d := r.N(10 * time.Second) // time.Duration을 그대로 전달
표준 라이브러리도 앞에서 본 패키지 함수 방식으로 우회하고 있었는데, 1.27에서 제네릭 메서드로 정리되었습니다.
도입 전에 확인할 점
go.mod의 go 버전
Go 1.27 툴체인을 쓰더라도 go.mod의 go 버전이 1.26 이하라면 제네릭 메서드를 쓸 수 없습니다. 언어 기능은 모듈에 선언된 언어 버전을 기준으로 적용되기 때문입니다.
./main.go:13:18: generic method requires go1.27 or later (-lang was set to go1.26; check go.mod)
라이브러리를 개발하고 있다면 주의해야 합니다. go 버전을 1.27로 올리면 Go 1.26 이하를 사용하는 프로젝트에서는 새 버전을 사용할 수 없습니다. 공개 라이브러리라면 지원할 Go 버전 범위를 먼저 정하고 도입하는 것이 좋습니다.
인터페이스로 추상화해야 하는 곳에는 쓸 수 없다
제네릭 메서드는 인터페이스 메서드가 될 수 없기 때문에 다음과 같은 경우에는 사용할 수 없습니다.
- 저장소 계층을
Repository인터페이스로 정의하고, 그 안에Get[T]메서드를 두려는 경우 - 테스트에서 목(mock)으로 교체해야 하는 타입에 제네릭 메서드를 두는 경우. 인터페이스를 거치지 않으므로 목으로 교체할 수 없습니다
이런 경우에는 기존처럼 타입 파라미터를 리시버 타입에 두는 방식(Repository[T])을 사용해야 합니다.
개발 도구 지원 여부
Griesemer는 이번 변경이 기존 코드와 완전히 호환되지만 "도구들은 따라잡아야 한다"고 언급했습니다. 코드 생성기, 정적 분석기, 린터 중에는 아직 메서드의 타입 파라미터를 처리하지 못하는 것이 있을 수 있습니다. 도입하기 전에 CI에서 사용하는 도구들이 Go 1.27 문법을 지원하는지 확인해 보시길 권장합니다.
언제 제네릭 메서드를 쓰면 좋을까?
제네릭 메서드가 생겼다고 해서 기존 패키지 함수를 모두 메서드로 바꿀 필요는 없습니다. 상황별로 정리하면 다음과 같습니다.
| 상황 | 추천 방식 |
|---|---|
특정 타입의 값을 변환하고, 체이닝으로 이어서 호출한다 (Stream.Map, Box.Convert) |
제네릭 메서드 |
특정 타입에 강하게 묶여 있어서 패키지 함수로 두기 어색하다 (Rand.N) |
제네릭 메서드 |
| 인터페이스로 추상화하거나 목으로 교체해야 한다 | 리시버 타입 파라미터 (Repository[T]) |
여러 타입에서 공통으로 쓰는 알고리즘이다 (slices.Sort, maps.Keys) |
패키지 제네릭 함수 |
reflect나 text/template에서 호출해야 한다 |
일반 메서드 |
표준 라이브러리의 slices, maps 패키지를 참고하면 판단하기 쉽습니다. 여러 컬렉션 타입에서 공통으로 쓰는 연산은 여전히 패키지 함수로 두고, 특정 타입의 상태를 사용하는 연산만 메서드로 두면 됩니다.
마치며
Go 1.18에서는 제네릭 메서드를 인터페이스 구현과 함께 생각했기 때문에, AOT 컴파일 방식으로는 구현할 방법이 없었습니다. 인터페이스를 구현하지 않는 제네릭 메서드라는 선택지도 있었지만, 그러면 메서드로 만들 이유가 없다고 보고 제외했습니다.
Go 1.27은 같은 선택지를 다시 꺼냈습니다. 체이닝과 이름 충돌처럼 인터페이스와 관계없는 문제를 해결하는 것만으로도 충분히 가치가 있다고 본 것입니다.
실무에서 기억해 둘 점을 정리하면 다음과 같습니다.
- 제네릭 메서드로는 인터페이스를 구현할 수 없습니다. 컴파일 시점 대입은 오류가 나고, 런타임 타입 단언은 오류 없이 실패합니다.
reflect와text/template에서는 보이지 않습니다.go.mod의go버전이 1.27 이상이어야 합니다.
이 세 가지만 주의하면, 타입을 변환하는 메서드나 체이닝 API를 만들 때 패키지 함수로 우회하던 코드를 훨씬 깔끔하게 정리할 수 있습니다.
참고 자료
- Go 1.27 Release Notes: Changes to the language
- golang/go#77273: spec: generic methods for Go (Robert Griesemer, 2026-01-22)
- golang/go#49085: proposal: spec: allow type parameters in methods (2021-10-20, #77273의 중복으로 종료)
- Go FAQ: Why does Go not support methods with type parameters?
- Type Parameters Proposal: No parameterized methods
- The Go Programming Language Specification: Method declarations
- math/rand/v2: Rand.N
- The Register, "Generic methods approved for Go, devs miss other features" (2026-03-02)
'Golang' 카테고리의 다른 글
| [Golang] Go에서 SSRF를 막으려면 어디서 검사해야 할까? (0) | 2026.10.11 |
|---|---|
| [Golang] Go의 Goroutine과 Channel: 공유하지 말고 소통하라 (0) | 2025.11.10 |
| [Golang] Go의 defer, panic, recover: 예외 없는 오류 제어 메커니즘 (0) | 2025.11.09 |
| [Golang] Go에 삼항연산자(?:)가 없는 이유: 언어 설계 철학과 가독성 우선주의 (0) | 2025.11.09 |