반응형
들어가며
어떤 프로젝트의 백엔드 개발을 하게 되면 CRUD API
는 무조건적으로 개발하게 됩니다. 데이터 생성, 조회, 업데이트, 삭제는 기본 기능입니다.
데이터 조회 API
CRUD 에서 R (Read) 에 해당하는 데이터 조회는 크게 2가지 기능이 있습니다.
1) PK 기반으로 작동하는 데이터 조회
예: userId
100 에 해당하는 데이터 조회
2) 페이징이 필요한 데이터 검색
예: userName
에 "김" 이 포함되어 있고, age
가 20세 이상인 user
모두 검색 (1페이지, 페이지당 10개)
네이밍 컨벤션
위와 같은 데이터 조회 API 를 개발할 때, 메소드의 이름은 주로 어떻게 설정하시나요?
보통 findBy...
, getBy...
, fetchBy...
, searchBy...
등 다양한 이름이 있을 수 있습니다.
정답은 없지만 간단한 rule 을 정해두고 개발을 한다면 헷갈리지 않고 좀 더 정리된 코드를 작성할 수 있을 것입니다.
예시. User 엔티티
(예시를 위해 간단하게 작성합니다.)
@Entity
public class User {
@Id
private final Long userId;
@Column
private final String userName;
@Column
private final String mobile;
@Column
private final String email;
@Column
private final ZonedDateTime lastLogginedAt;
@Column
private final Boolean valid;
}
Rule 1. Find
nullable
한 데이터를 조회할 때 활용합니다.
public interface SearchUserPort {
Optional<User> findByUserId(Long userId);
...
}
Rule 2. Get
not null
데이터를 조회할 때 활용합니다. 반환 데이터가 null
이라면 exception
을 발생시킵니다.
public interface SearchUserPort {
Optional<User> findByUserId(Long userId);
default User getByUserId(Long userId) {
return Optional.ofNullable(userId)
.flatMap(this::findByUserId)
.orElseThrow(() -> new UserNotFoundException(userId));
}
}
public class UserNotFoundException extends RuntimeException {
private final Long userId;
public UserNotFoundException(Long userId) {
super(String.format("사용자 %d 를 찾을 수 없습니다.", userId));
this.userId = userId;
}
}
Rule 3. Search
검색을 하기 위해 활용합니다. 페이지네이션 기능이 함께 구현되어야 합니다.
Page<User> search(UserSearchQuery query, Pageable pageable);
...SearchQuery
검색하고자 하는 조건이 포함되어 있습니다.
@Getter
public class UserSearchQuery {
private final List<Long> userIds;
private final String userName;
private final String mobile;
private final String email;
private final ZonedDateTime lastLogginedAtFrom;
private final ZonedDateTime lastLogginedAtTo;
}
Pageable
pageable
에는 몇 번째 페이지를 조회할 지 (pageNumber), 한 페이지당 조회하고자 하는 아이템은 몇 개 인지 (pageSize) 가 포함되어 있습니다.
검색 코드
public class UserSearchRepository implement SearchUserPort {
...
@Override
Page<User> search(UserSearchQuery query, Pageable pageable) {
BooleanBuilder condition = new BooleanBuilder();
condition.and(user.valid.isTrue);
if (CollectionUtils.isNotEmpty(query.getUserIds())) {
condition.and(user.id.in(query.getUserIds()));
}
if (StringUtils.isNotBlank(query.getUserName())) {
condition.and(user.name.like("%" + query.getUserName() + "%"));
}
var result = from(plan)
.where(condition);
long totalItemCount = result.fetchCount();
var items = result
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
return new PageImpl<>(items, pageable, totalItemCount);
}
}
반응형
'프로그래밍' 카테고리의 다른 글
Interface 를 활용한 Enum 리팩토링 (0) | 2023.06.21 |
---|---|
[번역] 반복적인 DTO-Domain 변환 처리하기 (feat. Kotlin Flow) (0) | 2023.03.31 |
[티스토리] highlight.js 를 활용해서 티스토리 코드블럭 꾸미기 (0) | 2023.02.10 |
[오픈소스] MapStruct 소개와 사용법 (0) | 2023.02.08 |
[디자인패턴] 실무에서 사용해본 전략 패턴 (Strategy Pattern) (0) | 2023.02.02 |
댓글