bkdragon's log

[gin] Paging Repository 본문

golang

[gin] Paging Repository

bkdragon 2024. 9. 21. 16:42

paging 이 추가된 API를 만들어보려고 한다. gin Clean Architecture 에서 사용하는 기본 CRUD repository 처럼 Paging respository 를 만들고 필요한 도메인에 임베드(상속)로 추가할 수 있게 만들 것이다.

우선 인터페이스를 정의해준다.

type IRepository[M any, ID any] interface {    
    List() ([]*M, error)
    Retrieve(id ID) (*M, error)
    Save(entity *M) (*M, error)
    Update(id ID, updates map[string]interface{}) (*M, error)
    Delete(id ID) error
}

type IPagingRepository[M any, ID any] interface {
    ListWithPaging(page int, page_size int) ([]*M, int64 ,error)
}

위에는 원래 있던 CRUD 용이고 IPagingRepository 인터페이스를 추가했다.

이제 인터페이스를 만족하는 구조체를 만들자.

type PagingDataBase[M any, ID any] struct {
    pagingdb *gorm.DB
}

func (r *PagingDataBase[M, ID]) ListWithPaging(page int, page_size int) ([]*M, int64 ,error) {
    var entities []*M
    var count int64

    err := r.pagingdb.Model(&entities).Count(&count).Error
    if err != nil {
        return nil, 0, err
    }

    offset := (page - 1) * page_size
    err = r.pagingdb.Offset(offset).Limit(page_size).Find(&entities).Error

    if err != nil {
        return nil, 0 ,err
    }

    return entities,count, nil
}

전체 row의 개수는 Count를 통해 찾을 수 있다.

Offset과 Limit를 이용해서 페이징을 구현할 수 있다.Offset은 특정 인덱스 부터 데이터를 조회하고 Limit는 최대 엔티티 수를 의미한다.
Offset이 10 이면 11번째 부터 조회한다. 예를 들어 page가 2고 size가 10이면 Offset이 10이 된다.

이제 이 인터페이스와 구조체를 추가하고 싶은 도메인의 레포지토리에 임베드 하면 된다. (예시는 기존에 작성하던 UserRepository)

type IUserRepository interface {
    IRepository[models.User, int]
    IPagingRepository[models.User, int]
    FindByEmail(email string) (*models.User, error)
}

type UserRepository struct {
    DataBase[models.User, int]
    PagingDataBase[models.User, int]
}

이 코드들을 활용하여 기본 CRUD와 Paging 까지 바로 추가할 수 있게 되었다.

Paging에 특정 조건으로 필터링까지하는 API 가 필요하면 어떻게 하면 될까?
새로운 메서드를 추가하면 된다. (IRepository, IPagingRepository 의 역할은 간단한 부분만 자동화 한다고 생각하면 된다. 복잡한 건 직접 구현!)

FilteredUsersWithPaging 이라는 메서드를 추가하고 구현해준다.

type IUserRepository interface {
    IRepository[models.User, int]
    IPagingRepository[models.User, int]
    FindByEmail(email string) (*models.User, error)
    FilteredUsersWithPaging(name string, page int, pageSize int) ([]*models.User, int64 ,error)
}
func (r *UserRepository) FilteredUsersWithPaging(name string, page int, pageSize int)([]*models.User, int64 ,error) {
    var users []*models.User
    var count int64

    query := r.pagingdb.Model(&users)

    if name != "" {
        query = query.Where("name LIKE ?", "%"+name+"%")
    }

    err := query.Count(&count).Error
    if err != nil {
        return nil, 0, err
    }

    offset := (page - 1) * pageSize
    err = query.Offset(offset).Limit(pageSize).Find(&users).Error

    if err != nil {
        return nil, 0 ,err
    }

    return users,count, nil

}

ListWithPaging 와 거의 비슷하다. Where 절이 추가되었다.

다음엔 정렬을 유연하게 할 수 있는 걸 추가해봐야겠다.

'golang' 카테고리의 다른 글

[gin] Clean Architecture  (1) 2024.09.20
[gorm] 다형성 관계  (0) 2024.09.11
[go] http 통신 과정  (0) 2024.09.10
[gin] JSON  (1) 2024.09.08
[gin] ShouldBindJSON  (0) 2024.09.08