728x90
반응형
🔹 1️⃣ 쿼리 최적화: JOIN을 활용한 단일 쿼리 조회
✔ 문제점
기존에는 여러 개의 테이블을 개별적으로 조회한 후, reportId를 기준으로 매핑하는 방식이었다.
이 방식은 쿼리 실행 횟수가 많아지고, 추가적인 연산이 필요하여 성능 저하를 유발할 수 있었다.
✔ 해결 방법
- 여러 개의 개별 쿼리를 실행하는 대신, JOIN을 활용하여 한 번의 쿼리로 데이터를 조회하도록 변경했다.
- Report 엔티티와 관련된 데이터를 LEFT JOIN을 통해 조회하고, 최신 데이터(createdAt 기준)를 가져오는 서브쿼리를 적용했다.
- 불필요한 데이터 연산을 줄이고, 쿼리 실행 시점에서 필요한 데이터를 가공하여 반환하는 방식으로 개선했다.
✔ 개선된 방식
val latestHistorySubquery = select(
max(path(History::createdAt))
).from(
entity(History::class)
).where(
path(History::reportId).eq(path(Report::tsid))
).asSubquery()
val responses = repository.findPage(pageable) {
selectNew<ResponseDTO>(
path(Report::tsid),
path(Report::name),
path(History::fileId),
path(History::createdAt)
).from(
entity(Report::class),
leftJoin(History::class).on(
path(History::reportId).eq(path(Report::tsid))
.and(path(History::createdAt).eq(latestHistorySubquery)) // 🔥 최신 데이터만 선택
)
).whereAnd(
path(Report::projectId).eq(projectId)
).orderBy(
path(Report::createdAt).desc()
)
}
✔ 결과
- ✅ 여러 개의 쿼리 실행 → 단일 쿼리로 변환 (JOIN)
- ✅ 불필요한 groupBy() & associateBy() 제거 → 쿼리 실행 시점에서 데이터 정리
- ✅ 최신 데이터 조회 시 서브쿼리 활용하여 max(createdAt) 필터링
- ✅ 쿼리 실행 속도 및 전체 성능 최적화
🔹 2️⃣ 서브쿼리 분리로 가독성 향상
쿼리에서 서브쿼리를 직접 leftJoin 내부에 작성하면 가독성이 떨어지고, 재사용이 어렵다.
따라서 서브쿼리를 별도로 변수로 분리하여 가독성과 유지보수성을 높이는 방식을 적용했다.
val latestHistorySubquery = select(
max(path(History::createdAt))
).from(entity(History::class))
.where(path(History::reportId).eq(path(Report::tsid)))
.asSubquery()
val responses = repository.findPage(pageable) {
selectNew<ResponseDTO>(
path(Report::tsid),
path(Report::name),
path(History::fileId),
path(History::createdAt)
).from(
entity(Report::class),
leftJoin(History::class).on(
path(History::reportId).eq(path(Report::tsid))
.and(path(History::createdAt).eq(latestHistorySubquery)) // 🔥 서브쿼리 적용
)
)
}
✔ 결과
- ✅ 쿼리 가독성 향상 (서브쿼리를 별도로 정의)
- ✅ 서브쿼리 재사용 가능 (필요 시 다른 로직에서도 활용 가능)
🔹 3️⃣ stringLiteral() 활용법
✔ 문제점
쿼리 내에서 문자열 값을 직접 비교해야 하는 경우, eq() 등을 사용하면 런타임 오류가 발생할 가능성이 있었다.
Kotlin JDSL에서는 stringLiteral()을 사용하면 JPQL에서 안전하게 문자열 값을 다룰 수 있다.
✔ 해결 방법
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions.stringLiteral
val responses = repository.find {
selectFrom(entity(Report::class))
.where(path(Report::name).eq(stringLiteral("Sample Report"))) // 🔥 문자열 값을 안전하게 비교
}
✔ stringLiteral()의 장점 ✅ JPQL에서 안전하게 문자열 값을 표현 가능 ✅ eq(), like(), in() 등의 조건에서 문자열을 안전하게 비교 ✅ 직접 문자열을 사용했을 때 발생할 수 있는 런타임 오류 방지
🔹 4️⃣ 예외 처리 개선
✔ 문제점
- 특정 조건(예: WEEKLY에서 요일이 여러 개 입력되는 경우)에 대해 예외 처리가 부족했다.
- 잘못된 값이 들어왔을 때 명확한 오류 메시지를 제공할 필요가 있었다.
✔ 해결 방법
when (repeatCycle) {
WEEKLY -> {
if (repeatDays.size > 1) {
throw ServiceException(ErrorCode.INVALID_REPEAT_DAYS) // ✅ 요일이 여러 개면 예외 발생
}
if (repeatDays.any { it !in validWeekDays }) {
throw ServiceException(ErrorCode.INVALID_REPEAT_DAYS) // ✅ 유효하지 않은 요일이면 예외 발생
}
}
MONTHLY -> {
if (repeatDays.any { it.toIntOrNull() !in 1..31 }) {
throw ServiceException(ErrorCode.INVALID_REPEAT_DAYS) // ✅ 1~31 범위 밖이면 예외 발생
}
}
}
✔ 결과
- ✅ 불필요한 데이터 입력 방지
- ✅ 잘못된 입력 시 명확한 오류 메시지 제공
- ✅ 비즈니스 로직 안정성 향상
728x90
반응형
'Blog > TIL' 카테고리의 다른 글
2025-03-20 (목) (0) | 2025.03.20 |
---|---|
2025-03-18 (화) (2) | 2025.03.18 |
2025-03-13 (목) (0) | 2025.03.13 |
2025-03-11 (화) (1) | 2025.03.11 |
2025-03-10 (월) (1) | 2025.03.10 |
댓글