엘로퀀트 서브 쿼리 free

2019-09-05

엘로퀀트 ORM을 쓰면 정말 너무너무 편합니다.

예~전에 너무 감동한 나머지 이런 뻘글도 블로그에 올렸었네요. ㅎㅎ


하지만 엘로퀀트 ORM은 간단한 CRUD에나 쓰는거고 결국 복잡한 쿼리는 날코딩으로 해결해야 한다는 인식이 강합니다. 왜냐하면 엘로퀀트 ORM으로 복잡한 데이터를 처리하려면 성능 이슈가 발생하기 때문입니다.


하지만 이번 라라벨 6.0에 추가된 엘로퀀트 서브쿼리 기능을 잘 활용하면 성능을 희생하지 않으면서도 엘로퀀트 ORM의 편리함을 누릴 수 있을 것 같아요.


엘로퀀트 서브쿼리는 라라콘 US 2019에 연사로 참여했던 조나단 리인잉크(Jonathan Reinink)가 개인적으로 만들어쓰던 매크로가 라라벨 정식 기능으로 추가된 것입니다. 라라콘 US 2019 발표 영상을 보시면 엘로퀀트 서브쿼리를 쓰면 어떤게 가능하고, 어떤식으로 퍼포먼스가 올라가는지 보실 수 있을 겁니다. 제가 영어를 잘 못해서 좀 쫄았는데 자막(도 영어지만..) 틀어놓고 보니까 조금 더 잘 들리더군요. (뭐 코드만 봐도 절반은 먹고 들어가는거 아니겠습니까? 설명은 거들뿐 ㅎㅎ) 엘로퀀트 ORM 을 더 잘쓰고 싶은 분들은 꼭 한 번 보세요. 저는 살짝 감동먹었습니다.


라라벨 6.0에 정식 기능으로 추가되면서 개발자가 라라벨 뉴스에 라라벨 6.0의 엘로퀀트 서브쿼리 강화(Eloquent Subquery Enhancements in Laravel 6.0)라는 제목의 글도 기고했습니다. 이 글에 있는 예제를 함께 보시죠.


“Select” 서브쿼리


목적지 테이블(destinations)과 항공편 테이블(flights)가 있고 항공편 테이블은 도착 시간(arrived_at) 컬럼이 있다고 예를 들었습니다.


목적지를 보여주되 목적지별로 가장 최근에 도착한 항공편의 이름을 보여주고자 한다면 라라벨 6.0 부터는 다음과 같이 할 수 있습니다.


`return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
])->get();
`

위의 예제는 서브 쿼리 조회시 엘로퀀트 모델을 이용한 것인데, 쿼리 빌더를 사용할 수도 있습니다.


`return Destination::addSelect(['last_flight' => function ($query) {
$query->select('name')
->from('flights')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1);
}])->get();
`

엘로퀀트 모델을 쓰는 편이 더 간결해보이네요.


“Order by” 서브쿼리


목적지를 보여주되 가장 최근에 도착한 항공편이 순으로 보여주고자 한다면 다음과 같이 할 수 있습니다.


`return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
)->get();
`

Flight에서 정렬한 결과로 Destination을 정렬하는 걸 볼 수 있습니다. 정말 편해보입니다.


“From” 서브쿼리


이번 업데이트로 Derived 테이블도 쉽게 활용할 수 있게 되었습니다. 예를들어 사용자당 후원금 평균을 구하고 싶다고 해봅시다. SQL은 집계 함수가 중첩되지 않습니다.


`AVG(SUM(amount)) // 요렇게는 안되는거죠
`

그래서 이럴 때는 사용자별 후원금액 합계를 Derived 테이블로 만들어서 평균을 구합니다. 라라벨 6.0에서는 다음과 같이 할 수 있습니다.


`return DB::table(function ($query) {
$query->selectRaw('sum(amount) as total')
->from('donations')
->groupBy('user_id');
}, 'donations')->avg('total');
`

마치며


저는 앞서 언급한 동영상을 보고나서 ‘어차피 복잡한 쿼리는 날쿼리로’라는 편견이 깨지는 계기가 됐습니다. 조나단 리이잉크의 동영상 강의(엘로퀀트 퍼포먼스 패턴)도 사서 봐야겠어요.


1일 1식 라라벨 48호

2019년 9월 5일


이현석

바쁜 팀장님 대신 알려주는 신입 PHP 개발자 안내서를 쓰고, 클린 아키텍처 인 PHP를 번역했습니다. 2020년에 출간될 Laravel Up & Running 2nd Edition을 번역하고 있습니다.