Kim Seogyu
Backend

Enterprise Go 시리즈 #5: 데이터베이스 연동 패턴

Go에서 트랜잭션 경계를 명확하게 관리하는 패턴을 정리합니다.

Published 2026년 1월 1일2 min read202 words

Enterprise Go 시리즈 #5: 데이터베이스 연동 패턴

문제 정의

Spring의 @Transactional처럼 선언형으로 처리하던 트랜잭션을 Go에서는 명시적으로 다뤄야 합니다. 핵심은 "트랜잭션 경계가 코드에서 보이게" 만드는 것입니다.

권장 패턴: WithTx 래퍼

func (u *TransferUseCase) Transfer(ctx context.Context, from, to string, amount int64) error {
	return dbtx.WithTx(ctx, u.db, func(ctx context.Context) error {
		if err := u.accountRepo.Withdraw(ctx, from, amount); err != nil {
			return err
		}
		if err := u.accountRepo.Deposit(ctx, to, amount); err != nil {
			return err
		}
		return u.logRepo.Save(ctx, from, to, amount)
	})
}

성공 시 커밋, 에러 시 롤백을 한곳에서 보장합니다.

Repository 규칙

Repository는 트랜잭션 생성 책임을 갖지 않습니다. 호출자가 준 ctx에서 트랜잭션 핸들을 얻어 실행합니다.

func (r *Repo) Save(ctx context.Context, m *Model) error {
	db := dbtx.FromContextOrDB(ctx, r.db)
	return db.Create(m).Error
}

풀 설정 기본값(출발점)

  • MaxOpenConns: DB 서버 여유와 동시성에 맞춰 제한
  • MaxIdleConns: MaxOpenConns의 30~50%
  • ConnMaxLifetime: LB/방화벽 timeout보다 짧게

정답값은 없고, 지표 기반으로 맞춰야 합니다.

자주 하는 실수

  • UseCase 밖에서 트랜잭션 경계를 흩뿌림
  • Repository가 내부에서 임의 트랜잭션 시작
  • 긴 트랜잭션에서 외부 API 호출까지 포함

요약

Go DB 연동의 핵심은 ORM 선택이 아니라 트랜잭션 경계 통제입니다. WithTx 패턴으로 경계를 고정하면 롤백 일관성과 테스트 용이성이 함께 올라갑니다.

Share

Related Articles

Comments

이 블로그는 제가 알고 있는 것들을 잊지 않기 위해 기록하는 공간입니다.
직접 작성한 글도 있고, AI의 도움을 받아 정리한 글도 있습니다.
정확하지 않은 내용이 있을 수 있으니 참고용으로 봐주세요.

© 2026 Seogyu Kim