[팀 프로젝트-배달어플] 기능 구현
이번 프로젝트에서는 메뉴 기능을 담당하였다.
📋 API 명세
📌 주요 기능
- 메뉴 생성
- 메뉴 수정
- 메뉴 삭제
- 카테고리 순 조회 및 커서 기반 조회
📁 패키지 구조
Rest API 구조에 맞춰 controller,repository,service,dto 로 분리하였고 그안에서 Admin API 와 User API 를 따로 분리 하였다.
✅ DTO
- MenuSaveRequestDto : 저장 요청
- MenuUpdateRequestDto: 수정 요청
🧱 Entity 설계
Menu.java
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false)
private Category category;
@Column(nullable = false)
private String name;
@Column
private String content;
@Column(nullable = false)
private BigDecimal price;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private MenuStatus status;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "storeId")
private Store store;
@Column(columnDefinition = "TINYINT(1)")
private boolean isDeleted = Status.EXIST.getValue();
- Category , MenuStatus : 주기적으로 바뀌는 변수가 아닌 특정한 변수에 의존할 수 있도록 enum 으로 구현
- isDeleted : soft Deleted 를 위한 TINYINT 타입 저장
- Store : 일대다의 관계로 스토어에 많은 메뉴가 존재
✅ 카테고리 순 조회 및 커서 기반 조회
기본적은 CRUD 는 많이 구현해봤기 때문에 이번에 새로 구현해본 기능을 기록해볼려고 한다.
해당가게의 메뉴 목록을 카테고리 별로 나열해야하는데 앱을 보면 오프셋 페이징 보다는 스크롤을 내리며 메뉴를 선택한다.
사용자 UI 의 편리를 위한 방법이라고 생각한다.
그래서 이번에 무한스크롤페이징을 구현해보았다.
참고자료
https://computerreport.tistory.com/125
[Spring]오프셋 페이징보다 커서 페이징을 써야하는 이유
💡 커서 기반 페이징이 가장 효율적인 방법이며, 가능한 항상 사용되어야 한다-페이스북 개발자 이번 글에서는 커서 페이징이 무엇이고, 왜 요즘처럼 실시간 데이터가 중요한 시대에 커서 페이
computerreport.tistory.com
JPQL 을 활용하한 무한스크롤 방식이다.
@Query("""
SELECT m FROM Menu m
WHERE m.store.id = :storeId
AND (:categoryCursor IS NULL
OR m.category > :categoryCursor
OR (m.category = :categoryCursor AND m.id > :lastId)
)
AND m.deletedAt IS NULL
ORDER BY m.category ASC, m.id ASC
""")
- 동작 방식:
- storeId: 조회 대상 가게.
- categoryCursor: 마지막으로 조회한 카테고리.
- lastId: 동일 카테고리 내 마지막 메뉴 ID.
- deletedAt IS NULL: 삭제된 메뉴 제외.
- ORDER BY m.category ASC, m.id ASC: 카테고리 오름차순, 동일 카테고리 내 ID 오름차순.
- Pageable: 페이지 크기 지정.
- 장점:
- 성능: 인덱스를 활용해 대량 데이터 처리 효율적.
- 일관성: 실시간 데이터 변경 시 데이터 누락/중복 없음.
- UX: 무한 스크롤로 사용자 친화적.
Category 별 순서를 명시하기 위해 Entity에 EnumType.ORDINAL 을 추가하여 정렬 순서를 해결 하였다.
이번 프로젝트에서는 5분 기록보드를 적극적으로 활용하면서 회고를 쓰기에도 도움이 됐고 전체적인 프로젝트 흐름을 볼 수 있어서 좋았다.
📎 마무리
이번 프로젝트를 통해 JPQL로 복잡한 쿼리를 작성하며 쿼리 최적화와 데이터 일관성의 가치를 배웠다.하지만 JPQL의 문자열 기반 쿼리는 가독성과 유지보수성 면에서 아쉬움이 남았다.
앞으로의 계획은 QueryDSL을 공부해 더 효율적이고 가독성 높은 쿼리 코드를 작성하는 것이다.
QueryDSL은 타입 안전한 쿼리 작성으로 컴파일 시점에 오류를 잡아주고, 메서드 체이닝으로 직관적인 코드를 만들 수 있어 JPQL의 한계를 보완할 수 있을 거라 생각한다.
또한, 대량 데이터 테스트로 커서 페이징의 성능을 검증하고, @DynamicUpdate로 엔티티 업데이트를 최적화하며, OpenAPI(Swagger)로 API 문서화를 자동화할 계획이다.
다음 프로젝트에서는 QueryDSL을 적극 활용해 깔끔하고 유지보수 가능한 코드를 목표로 하고싶다.