
3편에서 이어진다. 텍스트 추출은 Voice2Text를 사용했으며, 텍스트의 정리는 LLM을 사용했다.
Go 테스트의 거의 모든 것
https://youtu.be/8BDGRsdUtpc?si=W_Bk7VJ46qSk_V9j
발표 개요
이 발표에서는 Go 테스트의 기본부터 고급 기법까지 폭넓게 다루면서, 효과적인 테스트 코드 작성법을 소개함. 단순한 단위 테스트뿐만 아니라 성능 테스트, 퍼즈 테스트, 예제 테스트까지 포함하며, 실무에서 활용할 수 있는 다양한 기법을 공유함.
테스트 개념과 필요성
- 테스트의 목적:
- 코드의 안정성 보장
- 리팩토링 시 예상치 못한 버그 방지
- 협업 시 신뢰성 확보
- Go의 테스트 특징:
- 표준 라이브러리인 testing 패키지를 활용
- 기본적으로 *_test.go 파일을 사용
- go test 명령어로 간편하게 실행 가능
기본적인 단위 테스트
1. Go의 testing 패키지
- 테스트 함수는 func TestXxx(t *testing.T) 형태로 정의
- t.Errorf 또는 t.Fatal 등을 사용해 실패 조건 정의
- go test -v 옵션으로 상세 로그 확인 가능
2. 서브 테스트 (Subtests)
- t.Run을 사용하여 작은 단위로 테스트를 분리 가능
- 반복문을 활용해 여러 입력 값을 테스트할 때 유용
func TestAddition(t *testing.T) {
tests := []struct {
a, b, expected int
}{
{1, 2, 3},
{2, 3, 5},
{10, 5, 15},
}
for _, tc := range tests {
t.Run(fmt.Sprintf("%d+%d", tc.a, tc.b), func(t *testing.T) {
result := Add(tc.a, tc.b)
if result != tc.expected {
t.Errorf("expected %d, got %d", tc.expected, result)
}
})
}
}
3. 테이블 기반 테스트
- 다양한 입력 값과 예상 결과를 한 테이블로 관리하며 테스트
- 서브 테스트와 함께 사용하면 효율적
Mock과 testify 활용
1. testify 라이브러리 소개
- Go에서 테스트를 간편하게 만들 수 있는 라이브러리
- assert와 require를 활용해 코드 가독성을 높임
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSubtraction(t *testing.T) {
assert.Equal(t, 5, Subtract(10, 5))
}
2. Mocking 기법
- testify/mock 패키지를 활용해 의존성을 분리
- 데이터베이스, API 등 실제 환경을 테스트할 수 없는 경우 Mock 객체를 활용
성능 테스트 (Benchmarking)
1. 벤치마크 테스트의 기본
- func BenchmarkXxx(b *testing.B) 형태로 작성
- b.N을 사용해 반복 실행하며 평균 성능 측정
func BenchmarkLoop(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = math.Sqrt(144)
}
}
2. 벤치마크 실행 방법
- go test -bench . 명령어로 실행
퍼즈 테스트 (Fuzzing)
- testing.F를 활용해 예상치 못한 입력값을 자동 생성해 테스트
- 보안 취약점이나 경계값 오류를 찾는 데 유용
func FuzzExample(f *testing.F) {
f.Add("hello")
f.Fuzz(func(t *testing.T, input string) {
_ = process(input)
})
}
- go test -fuzz=FuzzExample 명령어로 실행
예제 기반 테스트 (Example Tests)
- 문서화 + 테스트를 동시에 수행할 수 있는 기능
- ExampleXxx 형태로 함수 작성
func ExampleAdd() {
fmt.Println(Add(2, 3))
// Output: 5
}
- go test 실행 시 자동으로 출력 값과 비교
테스트 커버리지 확인
- go test -cover 옵션으로 커버리지 확인
- go test -coverprofile=coverage.out && go tool cover -html=coverage.out
- HTML로 상세 커버리지 분석 가능
결론 및 정리
✅ Go에서는 표준 testing 패키지를 활용해 간단한 테스트를 작성할 수 있음
✅ testify 같은 라이브러리를 사용하면 테스트 코드 가독성을 높일 수 있음
✅ 벤치마킹과 퍼즈 테스트를 활용해 성능 및 보안 이슈를 사전에 방지 가능
✅ 커버리지 도구를 활용해 테스트 품질을 지속적으로 개선해야 함
버그 없는 프로그램 만들기: 테스팅의 관점으로
https://youtu.be/1GP_M5w7vtU?si=rZ7V5O6OlTTrD8_0
1. 왜 버그 없는 프로그램이 중요한가?
- 블록체인은 사용자의 자산과 직접적으로 연결되어 있음.
- 버그 발생 시 롤백 불가능 → 사용자 자산이 영구적으로 손실될 수 있음.
- 법률적 책임이 클 수 있으며, 해외에서는 개발자가 직접 책임을 지는 사례도 존재.
- 분산된 네트워크 환경 때문에 버그 수정이 더욱 어렵고 복잡함.
- 실제 사례: Osmosis에서 63억 원 상당의 피해 발생 후, 테스팅의 중요성을 더욱 깊이 고민.
2. 블록체인과 테스팅의 차별점
- 블록체인에서는 전통적인 유닛 테스트 개념이 존재하지 않음.
- 모든 테스트는 인테그레이션 테스트처럼 진행됨.
- 특정 함수만 테스트하는 것이 아니라 관련 API, 모듈까지 함께 테스트해야 함.
테스트 기법 소개
1. 유닛 테스팅
- Go의 testing.T를 활용하여 개별 함수 검증.
- 블록체인에서는 유닛 테스트도 인테그레이션 방식으로 진행됨.
- Testify 라이브러리의 Suite 기능 활용:
- 테스트 환경을 종합적으로 셋업할 수 있음.
- SetupSuite()를 이용해 모든 테스트 전에 공통 설정 수행.
2. 뮤테이션 테스팅
- 코드를 자동으로 변형하여 테스트가 이를 감지하는지 확인하는 기법.
- 예시:
- return user.Age >= 18 → return user.Age > 18 등으로 변형.
- 변형 후에도 테스트가 실패하지 않으면, 기존 테스트가 불완전함을 의미.
- 1980년대 논문에서 제안되었으나, 당시 컴퓨터 성능 문제로 활용 어려움.
- 현대에는 go-mutesting 같은 도구를 활용하여 자동화 가능.
3. 엔드 투 엔드 (E2E) 테스팅
- 프로그램 전체 흐름을 시뮬레이션하여 검증하는 방식.
- 블록체인의 특성상, 분산된 여러 개의 노드에서 동작 검증이 필요함.
- 테스트 방법:
- 도커 컨테이너를 활용해 체인 A, B를 띄움.
- 체인 A에서 체인 B로 트랜잭션을 전송하고, 정상 동작 여부 확인.
4. 퍼징(Fuzz) 테스팅
- 랜덤한 입력값을 주입하여 예상치 못한 오류를 찾아내는 기법.
- Go 1.18부터 공식적으로 지원.
- 블록체인에서는 전체 프로그램을 대상으로 퍼징 수행:
- API 요청을 랜덤하게 보내고, 전체적인 상태 변화가 정상적으로 유지되는지 검증.
- 예시: 암호화폐 총 공급량이 변하지 않는지 테스트.
결론: 버그 없는 프로그램을 만들기 위한 노력
- 블록체인은 롤백이 불가능하므로 철저한 테스팅이 필수.
- 전통적인 테스팅 방법뿐만 아니라 뮤테이션, 퍼징, E2E 테스팅까지 적극 활용.
- 선언적인 방식(Declarative Test Generation)으로 공리적 조건을 유지하며 테스트 수행.
- NASA의 사례처럼 완벽한 프로그램을 목표로 지속적인 개선 필요.
"버그 없는 프로그램은 없다"는 말이 있지만, 우리는 완벽을 목표로 해야 한다.
Scenario Tester : 인수 테스트 자동화로 자신감, 생산성 높이기
https://youtu.be/gTw7gTJ4r2Y?si=x4Pim4FzMynN7vI2
1. 배경: AB180과 데이터 파이프라인
- AB180은 마케팅 성과 분석 솔루션인 에어브리지를 운영.
- 데일리 20억 건 이상의 사용자 행동 데이터를 수집하고 분석.
- 핵심 파이프라인 구성:
- WAAS: 사용자 데이터를 수집하는 서버.
- Kafka: 데이터를 저장하고 전달.
- Worker: 데이터를 처리하고 분석.
비용 절감 목표
- 서버 비용 절감이 필요했고, 이를 위해 4가지 액션 아이템을 설정:
- WAAS를 파이썬에서 Go로 마이그레이션.
- Worker를 파이썬에서 Rust로 변경.
- DB 변경.
- 네트워크 비용 최적화.
- 본 발표에서는 Worker의 Rust 마이그레이션 과정과 인수 테스트 자동화에 초점을 맞춤.
2. 마이그레이션 과정에서 테스트 전략
- 기존의 파이썬과 Rust를 병행 운영해야 하는 상황.
- 단위 테스트(Unit Test)와 통합 테스트(Integration Test)는 최대한 재사용.
- 인수 테스트(Acceptance Test)는 완벽하게 옮기고, 추가적인 케이스도 생성.
인수 테스트란?
- 요구사항을 검증하는 테스트.
- 구조:
- Given: 초기 환경 설정 (컨텍스트)
- When: 특정 요청이 들어옴 (입력)
- Then: 예상 결과를 검증 (출력)
3. 기존 인수 테스트의 한계
- 테스트 코드가 많아지면서 유지보수가 어려움.
- 현재 서비스가 어떤 기능을 지원하는지 한눈에 파악하기 어려움.
- 언어를 변경할 경우 기존 테스트를 옮기는 공수가 큼.
4. 해결책: 시나리오 테스트 자동화
- 인수 테스트를 JSON 기반의 "시나리오"로 관리.
- 테스트 자동화를 위해 두 가지 핵심 컴포넌트 추가:
- Scenario Generator
- 실제 서비스와 상호작용하면서 자동으로 테스트 데이터(시나리오)를 생성.
- Scenario Tester
- 저장된 시나리오를 실행해 결과가 일치하는지 검증.
- Scenario Generator
시나리오 구조
{
"context": { "db": "초기 DB 상태", "cache": "초기 캐시 값" },
"input": { "request": "API 요청 데이터" },
"output": { "response": "기대 결과", "db": "최종 DB 상태" }
}
- 컨텍스트(Context): DB, 캐시 등의 초기 상태.
- 입력(Input): 실제 서비스에 전달되는 요청 데이터.
- 출력(Output): 기대하는 응답 및 데이터 변경 사항.
자동화 과정
- Scenario Generator
- 로컬에서 서비스를 실행하고, 입력을 넣어 예상 결과를 저장.
- Scenario Tester
- 저장된 시나리오를 실행하고 실제 서비스의 응답과 비교.
- 차이가 있을 경우 상세 diff 제공.
5. 시나리오 테스트 도입 효과
- 100억 개 이상의 테스트 케이스 생성 및 검증.
- 파이썬과 Rust 코드의 일관된 검증이 가능.
- 언어 변경 시에도 시나리오 파일만 공유하면 검증 가능.
6. 기존 단위 테스트로는 잡지 못했던 문제
- JSON Encoding 이슈
- <, > 문자가 유니코드로 변환되는 문제.
- \u003c, \u003e로 변환되어 클라이언트에서 예상과 다른 응답 발생.
- JSON Unmarshaling 대소문자 문제
- lowerCaseKey vs LowerCaseKey → 예상과 다르게 언마샬링 성공.
- 해결책: 맵(Map) 기반의 수동 언마샬링.
- 타임스탬프 처리 오류
- 13자리 밀리초(millis) vs 10자리 초(seconds).
- 테스트에서는 정상 작동했지만, 실제 배포 후 오류 발생.
- 해시(Hash) 알고리즘 불일치
- MD5 vs SHA-256 → 테스트에서는 통과했지만, 실제 해싱 방식 차이로 문제 발생.
7. 도구 및 팁
- Go JSON Diff (github.com/wI2L/jsondiff)
- JSON diff를 시각적으로 확인 가능.
- GORM After Query Hook
- SQL 쿼리 실행 후 자동으로 결과 저장.
- Makefile 활용
- cat scenario.json | scenario-tester 형태로 CLI 자동화.
8. 단점과 개선점
단점
- 개발 공수가 큼
- 서비스 하나를 개발하는 수준의 비용 발생.
- Rust, Go, Python 각각 Scenario Tester를 만들어야 했음.
- 잘못된 시나리오 생성 시 문제 발생
- 실제 인풋이 바뀌어도 테스트가 실패하지 않는 문제 발생.
- 인풋 데이터 변경 감지 로직 추가로 해결.
개선 목표
- 업스트림/다운스트림 강제화
- A 서비스의 아웃풋을 B 서비스의 인풋으로 활용하도록 테스트 강제.
- 공통 시나리오 테스트 프레임워크 개발
- Python, Go, Rust에서 동일한 시나리오 파일 사용.
- 비기능적 요구사항 검증 추가
- 성능 테스트까지 포함하는 방향으로 확장.
9. 마이그레이션 결과
- 월 1,000만 원 이상의 비용 절감.
- 더 견고한 프로덕트 개발 가능 (강타입 언어 덕분에 숨겨진 버그 발견).
- Rust + Go 도입 후 성능 대폭 향상.
- 코드 리팩토링이 더 쉬워짐 (시나리오 기반 자동 검증 덕분).
10. 결론
✅ 시나리오 테스트 자동화를 통해 안정성과 생산성을 모두 확보
✅ 파이썬 → Rust & Go로의 마이그레이션을 안전하게 수행
✅ 단위 테스트로는 잡을 수 없는 문제까지 포괄적으로 해결
✅ 팀 전체의 코드 품질과 리팩토링 가능성을 크게 향상
"시나리오 테스트 자동화를 통해 자신감을 얻고, 더 빠르게 개발할 수 있다!" 🚀
총정리
GopherCon Korea 2023을 통해 알 수 있는 기술 동향은 다음과 같다.
1. Go가 집중적으로 사용되는 영역
현재 Go는 서버 개발, 클라우드 인프라, 데이터 파이프라인, 모니터링, 테스트 자동화 등 다양한 분야에서 강력한 역할을 수행하고 있습니다.
📌 주요 활용 분야:
✅ 클라우드 인프라 자동화 (Kubernetes + Go)
✅ 고성능 데이터 처리 (Kafka, Rust와 결합한 워커 시스템)
✅ API 서버 및 마이크로서비스 개발
✅ 대규모 서버 모니터링 및 운영 자동화
✅ 테스트 자동화 및 품질 개선 (시나리오 테스트, 버그 탐지)
2. Go를 선택하는 이유
각 기업들이 발표에서 공통적으로 강조한 **"왜 Go인가?"**에 대한 이유는 다음과 같습니다.
🛠 Go를 선택한 이유:
1️⃣ 고성능 & 경량성: Rust보다 쉽고 Python보다 빠름.
2️⃣ Go의 단일 바이너리 배포: 크로스 플랫폼 지원, 서버리스 환경에서 특히 강점.
3️⃣ 쿠버네티스와의 강력한 시너지: K8S 오퍼레이터 패턴을 쉽게 구현 가능.
4️⃣ 자동화 및 병렬 처리에 적합: Goroutine과 채널을 이용한 효율적인 비동기 프로그래밍.
5️⃣ 메모리 관리 최적화: GC(가비지 컬렉션)가 있지만 성능 저하 없이 운영 가능.
6️⃣ 테스트 및 유지보수 용이: 강타입 언어로 코드 안정성이 뛰어나고, 테스트 코드 작성이 쉬움.
💡 Rust와의 비교:
- 성능이 중요한 특정 워커 로직은 Rust,
- 유지보수성이 중요한 비즈니스 로직은 Go를 선택하는 패턴이 많음.
3. Go와 함께하는 핵심 기술 스택
📡 주요 기업들이 Go와 함께 활용하는 기술 스택
분야 | 주요 기술 |
클라우드 인프라 | Kubernetes (K8S), AWS Lambda, Kafka, MSK |
데이터 파이프라인 | Kafka, Redis, InfluxDB (TSDB), PostgreSQL |
모니터링 & DevOps | Prometheus, Grafana, OpenTelemetry |
테스트 & 품질 개선 | Ginkgo, Scenario Tester, Fuzz Testing |
자동화 & 서버리스 | AWS Lambda (Go), Kubernetes Operator |
API & 백엔드 | gRPC, REST API, GraphQL |
Go가 클라우드 환경과 잘 맞는다는 점이 다시 한 번 강조됨.
특히 쿠버네티스 오퍼레이터 패턴과의 결합이 매우 활발하게 이루어지고 있음.
4. 기업들이 Go를 활용하는 방식 & 주요 사례
🔷 현대자동차
- 프라이빗 클라우드 H 클라우드 개발
- K8S 오퍼레이터 패턴을 사용해 데이터센터 자동화
- 자동차 → SDV(Software Defined Vehicle) → 클라우드 연동
🔷 삼성전자
- 대규모 AI 학습 인프라 구축
- 데이터센터 및 클라우드 인프라 자동화
🔷 AB180 (에어브리지)
- 파이썬 → Go & Rust로 마이그레이션
- Scenario Tester 개발로 인수 테스트 자동화
- Kafka & Go 활용한 대규모 데이터 파이프라인 운영
🔷 모니터링 & DevOps
- 서버리스 기반의 경량 모니터링 툴 개발 (Go + InfluxDB)
- Kafka를 활용한 실시간 로깅 시스템 구축
🔷 백엔드 API & 서버 개발
- AWS Lambda + Go로 웹훅 처리 시스템 구축 (Go의 경량성 활용)
- 대규모 API 요청을 처리하는 고성능 REST & gRPC 서버 개발
5. 최신 트렌드 및 향후 전망
🚀 Go를 활용한 최신 트렌드:
✅ 서버리스(Serverless)와 Go
- AWS Lambda + Go로 가벼운 웹훅 처리
- Go의 단일 바이너리 배포가 서버리스 환경에서 강점
✅ Go + Kubernetes (K8S) → 클라우드 인프라 자동화
- Kubernetes 오퍼레이터 패턴으로 데이터센터 자동화 (현대자동차)
- 클라우드 네이티브 개발에 최적화된 언어로 자리 잡음
✅ Rust & Go의 공존
- Rust는 성능 최적화가 필요한 부분에서 선택
- Go는 비즈니스 로직과 유지보수성이 중요한 부분에서 선택
✅ 테스트 자동화와 품질 개선
- Scenario Tester 같은 프레임워크 도입 증가
- 단순 유닛 테스트를 넘어 비기능적 요구사항(성능, 부하 테스트)까지 포함
✅ Go의 테스팅 & QA 혁신
- Fuzz Testing & Property-Based Testing 도입
- 단순한 단위 테스트를 넘어 실제 운영 환경에 가까운 자동화 테스트 증가
✅ Go의 데이터 처리 확장
- Kafka, Redis, TSDB(InfluxDB)와의 조합이 많아짐
- 실시간 데이터 처리 및 대규모 이벤트 스트리밍에서 적극 활용
6. 결론: GopherCon Korea 2023에서 본 Go의 미래
🛠 Go는 이제 단순한 백엔드 개발 언어를 넘어 클라우드 인프라, 서버리스, 데이터 파이프라인, 모니터링, 테스트 자동화 등 다양한 분야에서 핵심 기술로 자리 잡음.
🏆 기업들이 Go를 선택하는 이유는 성능, 배포 편의성, 유지보수성, 자동화 가능성 때문이며, 특히 쿠버네티스와의 강력한 시너지가 Go의 성장을 더욱 가속화하고 있음.
📈 향후 전망
- 서버리스 및 클라우드 인프라 자동화에서 더 강력한 역할 수행
- 테스트 자동화 및 품질 개선에 집중 (Scenario Tester 같은 도구 증가)
- Rust & Go의 조합이 더욱 많아질 것 (각각의 장점을 활용하는 방식)
- Go 기반 데이터 처리 시스템의 증가 (Kafka, TSDB, Redis 활용 확대)
🔍 결론적으로, Go는 클라우드 네이티브 개발과 서버 자동화에서 가장 강력한 언어 중 하나로 자리 잡았으며, 앞으로도 지속적인 성장과 확장이 기대됨! 🚀
'컨퍼런스 정리' 카테고리의 다른 글
GopherCon Korea 2024 Day 1 정리 (0) | 2025.03.22 |
---|---|
GopherCon Korea 2023 정리 3편 (0) | 2025.03.18 |
GopherCon Korea 2023 정리 2편 (0) | 2025.03.18 |
GopherCon Korea 2023 정리 1편 (0) | 2025.03.18 |