14 minute read

개요

Go의 sort 패키지는 다양한 타입의 슬라이스를 정렬하고 검색하는 기능을 제공합니다.

주요 특징:

  • 기본 타입: Ints, Float64s, Strings 정렬
  • 커스텀 정렬: Interface 구현으로 모든 타입 정렬
  • Slice 정렬: sort.Slice로 간편한 정렬
  • 안정 정렬: Stable sort 지원
  • 검색: 이진 탐색 제공
  • 역순: Reverse로 내림차순 정렬
  • 성능: O(n log n) 복잡도

기본 타입 정렬

1. 정수 정렬

import (
    "fmt"
    "sort"
)

func main() {
    nums := []int{5, 2, 8, 1, 9, 3}
    
    // 오름차순 정렬
    sort.Ints(nums)
    fmt.Println(nums)  // [1 2 3 5 8 9]
    
    // 정렬 확인
    sorted := sort.IntsAreSorted(nums)
    fmt.Println(sorted)  // true
    
    // 내림차순 정렬
    sort.Sort(sort.Reverse(sort.IntSlice(nums)))
    fmt.Println(nums)  // [9 8 5 3 2 1]
}

2. 실수 정렬

func main() {
    nums := []float64{3.14, 1.41, 2.71, 0.58}
    
    // 오름차순
    sort.Float64s(nums)
    fmt.Println(nums)  // [0.58 1.41 2.71 3.14]
    
    // 정렬 확인
    sorted := sort.Float64sAreSorted(nums)
    fmt.Println(sorted)  // true
    
    // 내림차순
    sort.Sort(sort.Reverse(sort.Float64Slice(nums)))
    fmt.Println(nums)  // [3.14 2.71 1.41 0.58]
}

3. 문자열 정렬

func main() {
    words := []string{"banana", "apple", "cherry", "date"}
    
    // 사전순 정렬
    sort.Strings(words)
    fmt.Println(words)  // [apple banana cherry date]
    
    // 정렬 확인
    sorted := sort.StringsAreSorted(words)
    fmt.Println(sorted)  // true
    
    // 역순
    sort.Sort(sort.Reverse(sort.StringSlice(words)))
    fmt.Println(words)  // [date cherry banana apple]
}

4. Search - 이진 탐색

func main() {
    nums := []int{1, 3, 5, 7, 9, 11, 13}
    
    // 값 찾기
    idx := sort.SearchInts(nums, 7)
    fmt.Println(idx)  // 3
    
    // 없는 값 (삽입 위치 반환)
    idx = sort.SearchInts(nums, 6)
    fmt.Println(idx)  // 3 (6이 들어갈 위치)
    
    // 문자열 검색
    words := []string{"apple", "banana", "cherry", "date"}
    idx = sort.SearchStrings(words, "cherry")
    fmt.Println(idx)  // 2
}

Slice 정렬

1. sort.Slice - 간단한 정렬

func main() {
    nums := []int{5, 2, 8, 1, 9}
    
    // 오름차순
    sort.Slice(nums, func(i, j int) bool {
        return nums[i] < nums[j]
    })
    fmt.Println(nums)  // [1 2 5 8 9]
    
    // 내림차순
    sort.Slice(nums, func(i, j int) bool {
        return nums[i] > nums[j]
    })
    fmt.Println(nums)  // [9 8 5 2 1]
}

2. 구조체 정렬

type Person struct {
    Name string
    Age  int
}

func main() {
    people := []Person{
        {"Alice", 30},
        {"Bob", 25},
        {"Charlie", 35},
        {"Dave", 25},
    }
    
    // 나이순 정렬
    sort.Slice(people, func(i, j int) bool {
        return people[i].Age < people[j].Age
    })
    
    fmt.Println(people)
    // [{Bob 25} {Dave 25} {Alice 30} {Charlie 35}]
    
    // 이름순 정렬
    sort.Slice(people, func(i, j int) bool {
        return people[i].Name < people[j].Name
    })
    
    fmt.Println(people)
    // [{Alice 30} {Bob 25} {Charlie 35} {Dave 25}]
}

3. 다중 키 정렬

func main() {
    people := []Person{
        {"Alice", 30},
        {"Bob", 25},
        {"Charlie", 25},
        {"Dave", 30},
    }
    
    // 나이순, 같으면 이름순
    sort.Slice(people, func(i, j int) bool {
        if people[i].Age != people[j].Age {
            return people[i].Age < people[j].Age
        }
        return people[i].Name < people[j].Name
    })
    
    fmt.Println(people)
    // [{Bob 25} {Charlie 25} {Alice 30} {Dave 30}]
}

4. SliceStable - 안정 정렬

type Item struct {
    Value int
    Index int
}

func main() {
    items := []Item{
        {3, 1},
        {1, 2},
        {3, 3},
        {2, 4},
        {1, 5},
    }
    
    // 안정 정렬 (같은 값의 순서 유지)
    sort.SliceStable(items, func(i, j int) bool {
        return items[i].Value < items[j].Value
    })
    
    fmt.Println(items)
    // [{1 2} {1 5} {2 4} {3 1} {3 3}]
    // Value 1인 항목들의 Index 순서가 유지됨 (2, 5)
}

Interface 구현

1. sort.Interface

type Person struct {
    Name string
    Age  int
}

type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }

func main() {
    people := []Person{
        {"Alice", 30},
        {"Bob", 25},
        {"Charlie", 35},
    }
    
    sort.Sort(ByAge(people))
    fmt.Println(people)
    // [{Bob 25} {Alice 30} {Charlie 35}]
}

2. 여러 정렬 기준

type ByName []Person

func (a ByName) Len() int           { return len(a) }
func (a ByName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByName) Less(i, j int) bool { return a[i].Name < a[j].Name }

type ByAgeAndName []Person

func (a ByAgeAndName) Len() int      { return len(a) }
func (a ByAgeAndName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAgeAndName) Less(i, j int) bool {
    if a[i].Age != a[j].Age {
        return a[i].Age < a[j].Age
    }
    return a[i].Name < a[j].Name
}

func main() {
    people := []Person{
        {"Charlie", 25},
        {"Alice", 30},
        {"Bob", 25},
    }
    
    // 이름순
    sort.Sort(ByName(people))
    fmt.Println(people)
    // [{Alice 30} {Bob 25} {Charlie 25}]
    
    // 나이순, 같으면 이름순
    sort.Sort(ByAgeAndName(people))
    fmt.Println(people)
    // [{Bob 25} {Charlie 25} {Alice 30}]
}

3. Reverse

func main() {
    people := []Person{
        {"Alice", 30},
        {"Bob", 25},
        {"Charlie", 35},
    }
    
    // 나이 역순
    sort.Sort(sort.Reverse(ByAge(people)))
    fmt.Println(people)
    // [{Charlie 35} {Alice 30} {Bob 25}]
}

고급 기능

1. IsSorted - 정렬 확인

func main() {
    nums := []int{1, 2, 3, 4, 5}
    
    sorted := sort.SliceIsSorted(nums, func(i, j int) bool {
        return nums[i] < nums[j]
    })
    fmt.Println(sorted)  // true
    
    people := []Person{
        {"Alice", 30},
        {"Bob", 25},
    }
    
    sorted = sort.IsSorted(ByAge(people))
    fmt.Println(sorted)  // false (Bob이 더 작음)
}

2. Search - 일반 검색

func main() {
    nums := []int{1, 3, 5, 7, 9, 11, 13}
    
    // 조건 함수로 검색
    idx := sort.Search(len(nums), func(i int) bool {
        return nums[i] >= 7
    })
    
    if idx < len(nums) && nums[idx] == 7 {
        fmt.Printf("Found at index %d\n", idx)  // Found at index 3
    }
    
    // 첫 번째 짝수 찾기
    idx = sort.Search(len(nums), func(i int) bool {
        return nums[i]%2 == 0
    })
    fmt.Println(idx)  // 7 (없으므로 len 반환)
}

3. 구조체 검색

func main() {
    people := []Person{
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 35},
        {"Dave", 40},
    }
    
    // 나이순 정렬 필요
    sort.Sort(ByAge(people))
    
    // 나이 >= 35인 첫 번째 사람 찾기
    idx := sort.Search(len(people), func(i int) bool {
        return people[i].Age >= 35
    })
    
    if idx < len(people) {
        fmt.Println(people[idx])  // {Charlie 35}
    }
}

실전 예제

1. 점수 순위 매기기

type Student struct {
    Name  string
    Score int
    Rank  int
}

func AssignRanks(students []Student) {
    // 점수 내림차순 정렬
    sort.Slice(students, func(i, j int) bool {
        return students[i].Score > students[j].Score
    })
    
    // 순위 매기기
    for i := range students {
        if i == 0 {
            students[i].Rank = 1
        } else if students[i].Score == students[i-1].Score {
            // 동점자는 같은 순위
            students[i].Rank = students[i-1].Rank
        } else {
            students[i].Rank = i + 1
        }
    }
}

func main() {
    students := []Student{
        {"Alice", 85, 0},
        {"Bob", 92, 0},
        {"Charlie", 85, 0},
        {"Dave", 78, 0},
    }
    
    AssignRanks(students)
    
    for _, s := range students {
        fmt.Printf("%s: %d점 (순위: %d)\n", s.Name, s.Score, s.Rank)
    }
    // Bob: 92점 (순위: 1)
    // Alice: 85점 (순위: 2)
    // Charlie: 85점 (순위: 2)
    // Dave: 78점 (순위: 4)
}

2. 중복 제거 (정렬 후)

func RemoveDuplicates(nums []int) []int {
    if len(nums) == 0 {
        return nums
    }
    
    // 정렬
    sort.Ints(nums)
    
    // 중복 제거
    j := 0
    for i := 1; i < len(nums); i++ {
        if nums[i] != nums[j] {
            j++
            nums[j] = nums[i]
        }
    }
    
    return nums[:j+1]
}

func main() {
    nums := []int{5, 2, 8, 2, 1, 5, 9, 1}
    unique := RemoveDuplicates(nums)
    
    fmt.Println(unique)  // [1 2 5 8 9]
}

3. 병합 정렬된 배열

func MergeSorted(a, b []int) []int {
    result := make([]int, 0, len(a)+len(b))
    i, j := 0, 0
    
    for i < len(a) && j < len(b) {
        if a[i] <= b[j] {
            result = append(result, a[i])
            i++
        } else {
            result = append(result, b[j])
            j++
        }
    }
    
    // 남은 요소 추가
    result = append(result, a[i:]...)
    result = append(result, b[j:]...)
    
    return result
}

func main() {
    a := []int{1, 3, 5, 7}
    b := []int{2, 4, 6, 8, 10}
    
    merged := MergeSorted(a, b)
    fmt.Println(merged)  // [1 2 3 4 5 6 7 8 10]
}

4. K번째로 큰 값 찾기

func FindKthLargest(nums []int, k int) int {
    // 내림차순 정렬
    sort.Slice(nums, func(i, j int) bool {
        return nums[i] > nums[j]
    })
    
    return nums[k-1]
}

func main() {
    nums := []int{3, 2, 1, 5, 6, 4}
    
    fmt.Println(FindKthLargest(nums, 2))  // 5 (두 번째로 큰 값)
    fmt.Println(FindKthLargest(nums, 4))  // 3 (네 번째로 큰 값)
}

5. 구간 병합

type Interval struct {
    Start int
    End   int
}

func MergeIntervals(intervals []Interval) []Interval {
    if len(intervals) == 0 {
        return intervals
    }
    
    // 시작점 기준 정렬
    sort.Slice(intervals, func(i, j int) bool {
        return intervals[i].Start < intervals[j].Start
    })
    
    result := []Interval{intervals[0]}
    
    for i := 1; i < len(intervals); i++ {
        last := &result[len(result)-1]
        current := intervals[i]
        
        if current.Start <= last.End {
            // 겹치는 경우 병합
            if current.End > last.End {
                last.End = current.End
            }
        } else {
            // 안 겹치면 추가
            result = append(result, current)
        }
    }
    
    return result
}

func main() {
    intervals := []Interval{
        {1, 3},
        {2, 6},
        {8, 10},
        {15, 18},
    }
    
    merged := MergeIntervals(intervals)
    fmt.Println(merged)  // [{1 6} {8 10} {15 18}]
}

6. 커스텀 비교자

import "strings"

type File struct {
    Name string
    Size int64
    Time time.Time
}

type FileSorter struct {
    files []File
    by    func(f1, f2 *File) bool
}

func (s FileSorter) Len() int      { return len(s.files) }
func (s FileSorter) Swap(i, j int) { s.files[i], s.files[j] = s.files[j], s.files[i] }
func (s FileSorter) Less(i, j int) bool {
    return s.by(&s.files[i], &s.files[j])
}

func SortFiles(files []File, by func(f1, f2 *File) bool) {
    sort.Sort(FileSorter{files: files, by: by})
}

func main() {
    files := []File{
        {"readme.txt", 1024, time.Now()},
        {"config.json", 512, time.Now().Add(-1 * time.Hour)},
        {"data.csv", 2048, time.Now().Add(-2 * time.Hour)},
    }
    
    // 크기순 정렬
    SortFiles(files, func(f1, f2 *File) bool {
        return f1.Size < f2.Size
    })
    fmt.Println("크기순:")
    for _, f := range files {
        fmt.Printf("  %s (%d bytes)\n", f.Name, f.Size)
    }
    
    // 시간순 정렬
    SortFiles(files, func(f1, f2 *File) bool {
        return f1.Time.Before(f2.Time)
    })
    fmt.Println("\n시간순:")
    for _, f := range files {
        fmt.Printf("  %s (%v)\n", f.Name, f.Time.Format("15:04:05"))
    }
    
    // 이름순 (대소문자 무시)
    SortFiles(files, func(f1, f2 *File) bool {
        return strings.ToLower(f1.Name) < strings.ToLower(f2.Name)
    })
    fmt.Println("\n이름순:")
    for _, f := range files {
        fmt.Printf("  %s\n", f.Name)
    }
}

7. 우선순위 큐

import "container/heap"

type PriorityQueue []int

func (pq PriorityQueue) Len() int           { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool { return pq[i] < pq[j] }
func (pq PriorityQueue) Swap(i, j int)      { pq[i], pq[j] = pq[j], pq[i] }

func (pq *PriorityQueue) Push(x interface{}) {
    *pq = append(*pq, x.(int))
}

func (pq *PriorityQueue) Pop() interface{} {
    old := *pq
    n := len(old)
    x := old[n-1]
    *pq = old[0 : n-1]
    return x
}

func main() {
    pq := &PriorityQueue{5, 3, 7, 1, 9}
    heap.Init(pq)
    
    // 최솟값 추출
    fmt.Println(heap.Pop(pq))  // 1
    
    // 새 값 추가
    heap.Push(pq, 2)
    
    // 정렬된 순서로 출력
    for pq.Len() > 0 {
        fmt.Println(heap.Pop(pq))  // 2, 3, 5, 7, 9
    }
}

8. 정렬 성능 비교

import (
    "math/rand"
    "testing"
    "time"
)

func BenchmarkSort(b *testing.B) {
    sizes := []int{100, 1000, 10000}
    
    for _, size := range sizes {
        // 랜덤 데이터 생성
        data := make([]int, size)
        for i := range data {
            data[i] = rand.Intn(size * 10)
        }
        
        b.Run(fmt.Sprintf("Ints-%d", size), func(b *testing.B) {
            for i := 0; i < b.N; i++ {
                b.StopTimer()
                nums := make([]int, len(data))
                copy(nums, data)
                b.StartTimer()
                
                sort.Ints(nums)
            }
        })
        
        b.Run(fmt.Sprintf("Slice-%d", size), func(b *testing.B) {
            for i := 0; i < b.N; i++ {
                b.StopTimer()
                nums := make([]int, len(data))
                copy(nums, data)
                b.StartTimer()
                
                sort.Slice(nums, func(i, j int) bool {
                    return nums[i] < nums[j]
                })
            }
        })
    }
}

func main() {
    // 벤치마크 실행: go test -bench=.
    fmt.Println("Run: go test -bench=.")
}

일반적인 실수

1. 정렬 후 원본 변경

// ❌ 나쁜 예 (원본이 변경됨)
func main() {
    nums := []int{5, 2, 8, 1}
    
    sort.Ints(nums)
    
    // nums가 변경됨
    fmt.Println(nums)  // [1 2 5 8]
}

// ✅ 좋은 예 (복사 후 정렬)
func main() {
    nums := []int{5, 2, 8, 1}
    
    // 복사
    sorted := make([]int, len(nums))
    copy(sorted, nums)
    sort.Ints(sorted)
    
    fmt.Println(nums)    // [5 2 8 1] (원본 유지)
    fmt.Println(sorted)  // [1 2 5 8]
}

2. 정렬 안 된 데이터 검색

// ❌ 나쁜 예 (정렬 안 된 데이터에 Search)
func main() {
    nums := []int{5, 2, 8, 1, 9}
    
    // 정렬 안 된 상태로 검색
    idx := sort.SearchInts(nums, 5)
    fmt.Println(idx)  // 잘못된 결과
}

// ✅ 좋은 예 (정렬 후 검색)
func main() {
    nums := []int{5, 2, 8, 1, 9}
    
    sort.Ints(nums)
    idx := sort.SearchInts(nums, 5)
    
    if idx < len(nums) && nums[idx] == 5 {
        fmt.Printf("Found at %d\n", idx)
    }
}

3. Less 함수 오류

// ❌ 나쁜 예 (잘못된 비교)
type ByAge []Person

func (a ByAge) Len() int      { return len(a) }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool {
    // 같을 때 true 반환 (잘못됨)
    return a[i].Age <= a[j].Age
}

// ✅ 좋은 예 (올바른 비교)
func (a ByAge) Less(i, j int) bool {
    return a[i].Age < a[j].Age
}

4. Stable vs Unstable 혼동

// ❌ 나쁜 예 (순서 보장 필요한데 일반 정렬)
type Item struct {
    Category string
    Order    int
}

func main() {
    items := []Item{
        {"A", 1},
        {"B", 2},
        {"A", 3},
        {"B", 4},
    }
    
    // 일반 정렬 (Order 순서 보장 안 됨)
    sort.Slice(items, func(i, j int) bool {
        return items[i].Category < items[j].Category
    })
}

// ✅ 좋은 예 (안정 정렬)
func main() {
    items := []Item{
        {"A", 1},
        {"B", 2},
        {"A", 3},
        {"B", 4},
    }
    
    // 안정 정렬 (Order 순서 유지)
    sort.SliceStable(items, func(i, j int) bool {
        return items[i].Category < items[j].Category
    })
    
    fmt.Println(items)
    // [{A 1} {A 3} {B 2} {B 4}]
    // 같은 Category 내에서 Order 순서 유지
}

5. nil 슬라이스 정렬

// ❌ 나쁜 예 (nil 체크 없음)
func SortNums(nums []int) {
    sort.Ints(nums)  // nums가 nil이면 패닉은 안 나지만 의미 없음
}

// ✅ 좋은 예 (nil 체크)
func SortNums(nums []int) {
    if nums == nil || len(nums) == 0 {
        return
    }
    sort.Ints(nums)
}

6. 포인터 슬라이스 정렬

// ❌ 나쁜 예 (포인터 주소로 정렬됨)
func main() {
    nums := []*int{
        ptrInt(5),
        ptrInt(2),
        ptrInt(8),
    }
    
    sort.Slice(nums, func(i, j int) bool {
        return nums[i] < nums[j]  // 포인터 주소 비교!
    })
}

func ptrInt(n int) *int { return &n }

// ✅ 좋은 예 (값으로 비교)
func main() {
    nums := []*int{
        ptrInt(5),
        ptrInt(2),
        ptrInt(8),
    }
    
    sort.Slice(nums, func(i, j int) bool {
        return *nums[i] < *nums[j]  // 값 비교
    })
}

7. 동시성 문제

// ❌ 나쁜 예 (동시 정렬)
func main() {
    nums := []int{5, 2, 8, 1, 9}
    
    go sort.Ints(nums)
    go sort.Ints(nums)  // race condition!
    
    time.Sleep(100 * time.Millisecond)
}

// ✅ 좋은 예 (동기화)
import "sync"

func main() {
    nums := []int{5, 2, 8, 1, 9}
    var mu sync.Mutex
    
    var wg sync.WaitGroup
    wg.Add(1)
    
    go func() {
        defer wg.Done()
        mu.Lock()
        defer mu.Unlock()
        sort.Ints(nums)
    }()
    
    wg.Wait()
}

베스트 프랙티스

1. 적절한 정렬 함수 선택

// ✅ 기본 타입은 전용 함수
func main() {
    nums := []int{5, 2, 8, 1}
    sort.Ints(nums)  // sort.Slice보다 빠름
    
    // 구조체는 sort.Slice
    people := []Person{{"Alice", 30}, {"Bob", 25}}
    sort.Slice(people, func(i, j int) bool {
        return people[i].Age < people[j].Age
    })
}

2. 순서 유지 필요 시 Stable

// ✅ 순서가 중요할 때는 SliceStable
func main() {
    type Task struct {
        Priority int
        ID       int
    }
    
    tasks := []Task{
        {1, 100},
        {2, 200},
        {1, 101},
        {2, 201},
    }
    
    // Priority로 정렬하되 같은 Priority 내에서 ID 순서 유지
    sort.SliceStable(tasks, func(i, j int) bool {
        return tasks[i].Priority < tasks[j].Priority
    })
}

3. 검색 전 정렬 확인

// ✅ 검색 전 정렬 여부 확인
func BinarySearch(nums []int, target int) int {
    if !sort.IntsAreSorted(nums) {
        sort.Ints(nums)
    }
    
    idx := sort.SearchInts(nums, target)
    if idx < len(nums) && nums[idx] == target {
        return idx
    }
    return -1
}

4. 커스텀 타입은 Interface 구현

// ✅ 재사용 가능한 타입 정의
type ByLength []string

func (s ByLength) Len() int           { return len(s) }
func (s ByLength) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s ByLength) Less(i, j int) bool { return len(s[i]) < len(s[j]) }

func main() {
    words := []string{"apple", "pie", "banana"}
    
    sort.Sort(ByLength(words))
    fmt.Println(words)  // [pie apple banana]
}

5. 복잡한 정렬은 함수로 분리

// ✅ 정렬 로직을 함수로 분리
func SortPeople(people []Person, by string) {
    switch by {
    case "age":
        sort.Slice(people, func(i, j int) bool {
            return people[i].Age < people[j].Age
        })
    case "name":
        sort.Slice(people, func(i, j int) bool {
            return people[i].Name < people[j].Name
        })
    case "age_name":
        sort.Slice(people, func(i, j int) bool {
            if people[i].Age != people[j].Age {
                return people[i].Age < people[j].Age
            }
            return people[i].Name < people[j].Name
        })
    }
}

6. 성능 측정

// ✅ 큰 데이터셋은 벤치마크
func BenchmarkSorting(b *testing.B) {
    data := generateData(10000)
    
    b.Run("Ints", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            nums := make([]int, len(data))
            copy(nums, data)
            sort.Ints(nums)
        }
    })
    
    b.Run("Slice", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            nums := make([]int, len(data))
            copy(nums, data)
            sort.Slice(nums, func(i, j int) bool {
                return nums[i] < nums[j]
            })
        }
    })
}

func generateData(n int) []int {
    data := make([]int, n)
    for i := range data {
        data[i] = rand.Intn(n * 10)
    }
    return data
}

7. 테스트 작성

// ✅ 정렬 함수 테스트
func TestSort(t *testing.T) {
    tests := []struct {
        name     string
        input    []int
        expected []int
    }{
        {"empty", []int{}, []int{}},
        {"single", []int{1}, []int{1}},
        {"sorted", []int{1, 2, 3}, []int{1, 2, 3}},
        {"reverse", []int{3, 2, 1}, []int{1, 2, 3}},
        {"duplicates", []int{3, 1, 2, 1}, []int{1, 1, 2, 3}},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            nums := make([]int, len(tt.input))
            copy(nums, tt.input)
            
            sort.Ints(nums)
            
            if !reflect.DeepEqual(nums, tt.expected) {
                t.Errorf("got %v, want %v", nums, tt.expected)
            }
        })
    }
}

8. 문서화

// ✅ 정렬 기준 문서화
// SortByPriority sorts tasks by priority (ascending) and then by
// creation time (descending) for tasks with the same priority.
// This ensures high-priority tasks are processed first, and among
// tasks with the same priority, newer tasks are processed first.
func SortByPriority(tasks []Task) {
    sort.Slice(tasks, func(i, j int) bool {
        if tasks[i].Priority != tasks[j].Priority {
            return tasks[i].Priority < tasks[j].Priority
        }
        return tasks[i].CreatedAt.After(tasks[j].CreatedAt)
    })
}

정리

  • 기본: Ints, Float64s, Strings로 기본 타입 정렬, Search로 이진 탐색
  • Slice: sort.Slice로 간편한 정렬, 구조체와 다중 키 지원
  • Interface: Len, Swap, Less 구현으로 커스텀 정렬
  • 고급: SliceStable (안정 정렬), IsSorted (확인), Reverse (역순)
  • 실전: 순위 매기기, 중복 제거, 병합, K번째 값, 구간 병합, 커스텀 비교자, 우선순위 큐, 성능 비교
  • 실수: 원본 변경, 정렬 없이 검색, Less 오류, Stable 혼동, nil 체크, 포인터 정렬, 동시성
  • 베스트: 적절한 함수 선택, Stable 활용, 정렬 확인, Interface 구현, 함수 분리, 성능 측정, 테스트, 문서화