Post

[Golang] Cache 1편 - Cache interface 구현

캐시란 무엇인지 알아보고 interface 를 구현하는 방법을 알아보겠습니다.

캐시란?

  • 자주 사용되는 데이터를 메모리와 같은 빠른 저장소에 임시로 저장하여, 필요할 때 더 빠르게 접근할 수 있도록 하는 기술

작동 원리

  • 데이터 요청 : 클라이언트가 데이터를 요청할 때, 먼저 캐시를 확인
  • 캐시 적중 (Cache Hit) : 요청한 데이터가 캐시에 존재하면, 이를 바로 반환
  • 캐시 미스 (Cache Miss) : 요청한 데이터가 캐시에 없으면, 원본 데이터 소스(ex> 데이터베이스)에서 데이터를 가져와 캐시에 저장한 후 반환

주요 목적

  • 성능 향상: 데이터 접근 시간을 단축시켜 애플리케이션의 응답 속도를 개선
  • 부하 분산: 원본 데이터 소스에 대한 요청 수를 줄여 서버의 부하를 감소

캐시의 종류

  • 메모리 캐시: RAM과 같은 빠른 저장소에 데이터를 저장 (예: Redis, Memcached)
  • 디스크 캐시: 하드 드라이브나 SSD와 같은 느린 저장소에 저장하지만, 일반 데이터베이스보다 빠르게 접근할 수 있음

캐시 전략

  • TTL (Time to Live): 캐시에 저장된 데이터의 유효 기간을 설정하여 일정 시간 후 자동으로 제거되도록 함
  • LRU (Least Recently Used): 가장 오래 사용되지 않은 데이터를 삭제하여 새로운 데이터를 저장할 공간을 확보

인터페이스 설계의 이점

인터페이스는 Go에서 중요한 설계 원칙 중 하나로, 다음과 같은 이점을 가짐

  • 유연성
    • 다양한 구현체를 동일한 방식으로 사용할 수 있음
  • 테스트 용이성
    • 테스트할 때 Mock 객체를 쉽게 만들 수 있어, 실제 캐시 구현체를 사용하지 않고도 테스트할 수 있음
  • 캡슐화 및 의존성 주입
    • 구체적인 구현에 대한 의존성을 줄이고, 코드의 캡슐화를 향상시킬 수 있음
  • 다형성
    • 다른 구현체가 동일한 메서드를 제공함으로써, 코드에서 다형성을 활용할 수 있음 (하나의 인터페이스로 여러 타입의 객체를 처리)
  • 코드 일관성 및 유지보수
    • 각 구현체는 특정 조건을 준수해야 하며, 이를 통해 코드의 일관성을 유지할 수 있음 ( 새로운 캐시 구현체를 추가하더라도 기존 코드를 변경할 필요가 줄어듦)
  • 기능 확장 및 변경 용이성
    • 기존 캐시 구현체의 기능을 확장하거나 변경할 때, 인터페이스를 통해 새로운 기능을 추가해도 기존 코드에 영향을 주지 않음

[Golang] Cache Interface 설계

  • Cache 인터페이스는 다양한 캐시 구현체(In-Memory Cache, Redis Cache 등)를 동일한 방식으로 사용할 수 있도록 설계

설명

1
2
3
4
5
6
7
8
9
package cache

import "errors"

var (
	ErrKeyNotFound = errors.New("key not found")
	ErrKeyExpired  = errors.New("key expired")
	ErrCacheClosed = errors.New("cache closed")
)
  • 인터페이스의 구현체에서 사용 할 에러 정의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package cache

import (
	"context"
	"time"
)

// 세 가지 메모리 타입을 정의
const (
	INMEMORYCACHE = "memory"
	REDISCACHE    = "redis"
	DUMMYCACHE    = "dummy"
)

type Cache interface {
	Get(context.Context, string) (any, error)
	Set(context.Context, string, any, time.Duration) error
	Delete(context.Context, string) error
	SetTTL(context.Context, string, time.Duration) error
	GetTTL(context.Context, string) (time.Duration, error)
	Exists(context.Context, string) (bool, error)
	Clear(context.Context) error
	Close() error
	Description() string
}

// cacheType 에 따라, 다른 구현체의 인스턴스를 생성하여
// Cache interface 로 리턴함
func NewCache(cacheType string, addr, password string, db int) (Cache, error) {
	switch cacheType {
	case INMEMORYCACHE:
		return NewInMemoryCache(), nil
	case REDISCACHE:
		return NewRedisCache(addr, password, db)
	default:
		return NewDummyCache(), nil
	}
}

  • Get(context.Context, string) (any, error)
    • 역할: 캐시에서 데이터를 읽어옵니다.
    • 설명: 키를 통해 캐시에서 데이터를 검색합니다.
  • Set(context.Context, string, any, time.Duration) error
    • 역할: 캐시에 데이터를 저장합니다.
    • 설명: 주어진 키와 값을 캐시에 저장하며, TTL(Time to Live)을 설정하여 데이터의 유효 기간을 관리합니다.
  • Delete(context.Context, string) error
    • 역할: 캐시에서 데이터를 삭제합니다.
    • 설명: 주어진 키에 해당하는 데이터를 캐시에서 삭제합니다.
  • SetTTL(context.Context, string, time.Duration) error
    • 역할: 이미 존재하는 캐시 항목의 TTL을 설정합니다.
    • 설명: 특정 키에 대해 TTL을 설정하여 데이터의 유효 기간을 갱신합니다.
  • GetTTL(context.Context, string) (time.Duration, error)
    • 역할: 특정 키에 대한 TTL을 조회합니다.
    • 설명: 주어진 키에 대한 TTL을 반환하여 데이터의 남은 유효 기간을 확인합니다.
  • Exists(context.Context, string) (bool, error)
    • 역할: 특정 키가 캐시에 존재하는지 확인합니다.
    • 설명: 주어진 키가 캐시에 존재하는지 여부를 확인합니다.
  • Clear(context.Context) error
    • 역할: 캐시를 비웁니다.
    • 설명: 캐시의 모든 데이터를 삭제합니다. 이 메서드는 캐시를 완전히 비울 때 사용됩니다.
  • Close() error
    • 역할: 캐시 리소스를 정리합니다.
    • 설명: 캐시가 사용 중인 리소스를 정리하고, 필요한 경우 연결을 종료합니다.
  • Description() string
    • 역할: 캐시 구현체에 대한 설명을 제공합니다.
    • 설명: 캐시 구현체에 대한 간단한 설명을 반환하여, 어떤 캐시가 사용되고 있는지 알 수 있게 합니다.
This post is licensed under CC BY 4.0 by the author.