본문으로 바로가기
제품데스크톱 도구TASTECAD도면 확인TASTEFILES파일 관리TASTEFTP파일 전송TASTESSH원격 작업공간TASTEZIP압축과 해제개발 도구TASTECODERust 통합 개발 환경TASTESTUDIO통합 개발·테스트 환경TASTEETL데이터 통합(ETL)서버TASTEREMOTE원격 데스크톱 서버TASTEGEO공간정보 서버TASTEMAIL메일 작업공간시작하기다운로드문서구매모든 제품
마켓플레이스
소식
개발소식오늘의 개발 소식개발 문서프로그래밍 언어RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron알고리즘알고리즘 기초알고리즘 핵심알고리즘 심화실무 알고리즘
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스커뮤니티 안내
협업기술 전수홈페이지 구축데스크톱 앱 구축릴리스·결제 파이프라인판매 파트너문의
서비스원격 데스크톱설치 패키지 커스텀
TASTEDEV
제품

데스크톱 도구

TASTECAD복잡한 도면을 가볍게 열다TASTEFILES필요한 파일만, 가장 빠르게TASTEFTP파일이 오가는 가장 단정한 길TASTESSH서버와 나 사이, 더 단단한 연결TASTEZIP더 빠르게 묶고, 더 안전하게 풀다

개발 도구

TASTECODERust 앱을 편집 · 빌드 · 디버깅 · 테스트까지 한 창에서TASTESTUDIO편집부터 원격 테스트와 승인 기반 AI 수정까지TASTEETL값을 보며 데이터를 옮깁니다

서버

TASTEREMOTE우리 회사 서버로 운영하는 원격 데스크톱TASTEGEOGeoServer 호환 공간정보 서버를 Rust로 다시 세웁니다TASTEMAIL메일의 주도권을 다시 내 손에

시작하기

다운로드모든 제품 최신 판문서제품 사용법구매구독 · 영구 라이선스모든 제품모든 제품 한눈에
모든 제품
마켓플레이스소식
개발

소식

오늘의 개발 소식언어 · 프레임워크 · GitHub · AI 공식 발표개발 문서언어 · 프레임워크 · 알고리즘 안내서

프로그래밍 언어

RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart

프레임워크

프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular
서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express
앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron

알고리즘

알고리즘 기초복잡도 분석 · 정렬 · 탐색 · 기본 자료구조 · 트리와 힙알고리즘 핵심그래프 탐색 · 최단 경로와 최소 신장 트리 · 동적 계획법 · 탐욕 알고리즘 · 분할 정복 · 백트래킹알고리즘 심화문자열 알고리즘 · 수학과 정수론 · 비트 연산 · 구간 트리 · 유니온 파인드실무 알고리즘코딩 면접 풀이 전략 · LRU 캐시 · 레이트 리미터 · 일관된 해시
개발 문서 전체 보기
커뮤니티
프로젝트빌더와 작업 발견개발 로그결정과 시행착오프롬프트재사용 가능한 지시AI 도구 이슈Claude · Codex · Gemini 문제와 우회법쇼케이스완성된 결과커뮤니티 안내공개하고 함께 만드는 방식
협업
기술 전수Rust·egui 네이티브 앱 만들기홈페이지 구축10개 언어, 계정, 문서·다운로드데스크톱 앱 구축Rust·egui, WebView 없이릴리스·결제 파이프라인서명 · MSI·deb·rpm·AppImage · 해시판매 파트너소개하고 결제액의 30% 받기문의구축 · 제휴 · 투자 문의
서비스
원격 데스크톱브라우저로 내 컴퓨터에 접속설치 패키지 커스텀필요한 기능만, 설정은 미리
구매
KO
한국어EnglishDeutschEspañolFrançaisItalianoPortuguês (Brasil)日本語简体中文繁體中文
로그인
로그인
TASTEDEV

작은 도구부터, 더 좋은 개발까지.

support@tastedev.netTASTEDEV 후원하기
제품제품군구매문서다운로드마켓플레이스고객 지원
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스
정책개인정보처리방침이용약관구독 약관환불 정책커뮤니티 운영 정책
계정로그인가입하기
방문자
오늘
012345678901234567890123456789012345678901234567890123456789
어제
012345678901234567890123456789012345678901234567890123456789
누적 조회
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
상호
지엑스소프트
대표자
최영구
사업자등록번호
853-44-00368
주소
인천광역시 미추홀구 경인로 351 9층 V24
전화
+82-10-4404-1523
이메일
whitecalvin@tastedev.net
개인정보 보호책임자
최영구

© 2026 TASTEDEV제품 정보는 현재 검증된 개발 소스를 기준으로 합니다.출시·고도화 중

Go 문서
Go
Go 시작하기기본 문법타입과 데이터모듈과 패키지오류 처리와 테스트핵심 개념: 동시성
토론

Go 안내서 · 5/6

오류 처리와 테스트

Go에는 예외(exception)가 없습니다. 오류는 평범한 값으로 돌려주고, 호출한 쪽이 그 자리에서 확인합니다. 테스트 역시 별도 프레임워크 없이 표준 testing 패키지와 go test 명령으로 충분합니다. 이 장에서는 오류 값, 오류 감싸기, panic과 recover, 그리고 테스트와 벤치마크 작성법을 다룹니다.

오류는 값이다

error는 Error() string 메서드 하나만 가진 내장 인터페이스입니다. 실패할 수 있는 함수는 마지막 반환값으로 error를 돌려주고, 성공하면 nil을 돌려줍니다. 호출한 쪽은 바로 if err != nil로 확인합니다.

import (
	"errors"
	"fmt"
	"strconv"
)

var ErrEmpty = errors.New("빈 입력") // 센티널 오류: 비교 대상으로 공개한다

func parseAge(s string) (int, error) {
	if s == "" {
		return 0, ErrEmpty
	}
	n, err := strconv.Atoi(s)
	if err != nil {
		return 0, fmt.Errorf("나이 %q 해석 실패: %w", s, err)
	}
	if n < 0 {
		return 0, fmt.Errorf("나이는 음수일 수 없습니다: %d", n)
	}
	return n, nil
}

오류를 무시하지 않고 하나하나 처리하는 것이 Go의 기본 태도입니다. 오류 메시지는 소문자로 시작하고 마침표를 붙이지 않는 것이 관례인데, 다른 메시지 안에 이어 붙여지기 때문입니다.

오류 감싸기와 errors.Is / errors.As

fmt.Errorf에서 %w 동사를 쓰면 원래 오류를 감싼(wrap) 새 오류가 만들어집니다. 감싼 오류에는 맥락이 더해지면서도 원래 오류를 다시 찾을 수 있습니다. 비교에는 == 대신 errors.Is를, 특정 타입의 오류를 꺼낼 때는 errors.As를 씁니다. 두 함수 모두 감싼 사슬을 따라가며 찾습니다.

type NotFoundError struct {
	ID string
}

func (e *NotFoundError) Error() string { return "찾을 수 없음: " + e.ID }

func load(id string) error {
	return fmt.Errorf("load: %w", &NotFoundError{ID: id})
}

func main() {
	_, err := parseAge("")
	if errors.Is(err, ErrEmpty) {
		fmt.Println("입력이 비어 있습니다")
	}

	err = load("42")
	var nf *NotFoundError
	if errors.As(err, &nf) {
		fmt.Println("없는 ID:", nf.ID)
	}
	fmt.Println(err) // load: 찾을 수 없음: 42
}

%v로 오류를 넣으면 메시지만 이어 붙고 원래 오류와의 연결은 끊깁니다. 호출자가 원래 오류를 확인해야 한다면 %w를, 내부 구현을 감추고 싶다면 %v를 고릅니다. 여러 오류를 하나로 합칠 때는 errors.Join을 쓸 수 있습니다.

panic 과 recover

panic은 정상적인 오류 처리로 복구할 수 없는 상황, 즉 프로그래머의 실수나 불가능해야 할 상태를 알릴 때 씁니다. 배열 범위를 벗어난 접근이나 nil 맵 쓰기도 런타임 패닉을 일으킵니다. 패닉이 나면 함수가 차례로 풀리면서 defer가 실행되고, 아무도 잡지 않으면 프로그램이 종료됩니다.

recover는 defer된 함수 안에서만 효과가 있으며, 진행 중인 패닉을 멈추고 그 값을 돌려줍니다. 서버가 요청 하나의 패닉 때문에 전체가 죽지 않도록 경계에서 잡는 용도로 주로 씁니다.

func safeDivide(a, b int) (result int, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("복구된 패닉: %v", r)
		}
	}()
	return a / b, nil // b 가 0 이면 런타임 패닉
}

func main() {
	_, err := safeDivide(1, 0)
	fmt.Println(err) // 복구된 패닉: runtime error: integer divide by zero
}

라이브러리는 패닉을 호출자에게 넘기지 말고 error를 돌려주는 것이 원칙입니다. 이름 붙은 반환값 err를 defer 안에서 바꾸면 복구 결과를 호출자에게 전달할 수 있습니다.

testing 패키지

테스트 파일은 이름이 _test.go로 끝나고, 테스트 함수는 Test로 시작하며 *testing.T를 받습니다. go test가 이 함수들을 찾아 실행합니다. t.Errorf는 실패를 기록하고 계속 진행하며, t.Fatalf는 그 테스트를 즉시 멈춥니다.

// 파일: age_test.go
package main

import (
	"errors"
	"testing"
)

func TestParseAgeEmpty(t *testing.T) {
	_, err := parseAge("")
	if !errors.Is(err, ErrEmpty) {
		t.Fatalf("ErrEmpty 를 기대했지만 %v", err)
	}
}

표 기반 테스트

Go에서 가장 흔한 테스트 형태는 입력과 기대값을 표(슬라이스)로 늘어놓고 반복하는 표 기반 테스트입니다. t.Run으로 각 경우를 하위 테스트로 실행하면 실패한 경우의 이름이 보고되고, 특정 경우만 골라 실행할 수도 있습니다.

func TestParseAge(t *testing.T) {
	tests := []struct {
		name    string
		in      string
		want    int
		wantErr bool
	}{
		{"정상", "30", 30, false},
		{"숫자 아님", "abc", 0, true},
		{"음수", "-1", 0, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := parseAge(tt.in)
			if (err != nil) != tt.wantErr {
				t.Fatalf("err = %v, wantErr %v", err, tt.wantErr)
			}
			if got != tt.want {
				t.Errorf("parseAge(%q) = %d, want %d", tt.in, got, tt.want)
			}
		})
	}
}

벤치마크와 실행 명령

벤치마크 함수는 Benchmark로 시작하고 *testing.B를 받습니다. 측정할 코드를 b.Loop()가 참인 동안 반복하면, 테스트 도구가 반복 횟수를 알아서 정합니다. 오래된 코드에서는 for i := 0; i < b.N; i++ 형태도 볼 수 있습니다.

func BenchmarkParseAge(b *testing.B) {
	for b.Loop() {
		parseAge("42")
	}
}
go test ./...                 # 모든 패키지 테스트
go test -v -run 'TestParseAge/음수' .  # 특정 하위 테스트만
go test -race ./...           # 데이터 경쟁 검사와 함께
go test -cover ./...          # 코드 커버리지
go test -bench . -benchmem    # 벤치마크와 메모리 할당 측정

정리

  • 오류는 마지막 반환값 error로 돌려주고 if err != nil로 바로 확인합니다.
  • fmt.Errorf("...: %w", err)로 맥락을 더해 감싸고, errors.Is/errors.As로 확인합니다.
  • panic은 복구 불가능한 상황에만 쓰고, recover는 defer 안에서 경계에서만 씁니다.
  • 테스트는 _test.go 파일의 TestXxx(t *testing.T)이며, 표 기반 테스트와 t.Run이 관례입니다.
  • BenchmarkXxx(b *testing.B)와 go test -bench로 성능을 측정합니다.
이 장에 대해 질문이 있나요?
이전 장모듈과 패키지다음 장핵심 개념: 동시성

댓글 0개

인기 댓글순최신순

로그인 · 로그인하면 댓글을 남길 수 있습니다.