1. 게시물만 조회
EXPLAIN ANALYZE
select
rb1_0.id,
rb1_0.title,
rb1_0.deposit,
rb1_0.monthly_rent,
rb1_0.management_cost,
rb1_0.comeable_date,
rb1_0.hits,
rb1_0.created_at
from roommate_board rb1_0
where rb1_0.is_deleted = false
order by
rb1_0.created_at desc,
rb1_0.id desc
offset 0 rows
fetch first 21 rows only;
2. 방형태, 지역 JOIN
EXPLAIN ANALYZE
select
rb1_0.id,
rb1_0.title,
rb1_0.deposit,
rb1_0.monthly_rent,
rb1_0.management_cost,
rb1_0.comeable_date,
rb1_0.hits,
rt1_0.name,
r1_0.name,
p1_0.name,
p2_0.name,
rb1_0.created_at
from roommate_board rb1_0
join room_type rt1_0
on rt1_0.id = rb1_0.room_type_id
join region r1_0
on r1_0.id = rb1_0.region_id
left join region p1_0
on p1_0.id = r1_0.parents_id
left join region p2_0
on p2_0.id = p1_0.parents_id
where rb1_0.is_deleted = false
order by
rb1_0.created_at desc,
rb1_0.id desc
offset 0 rows
fetch first 21 rows only;
3. 키워드 검색 추가
EXPLAIN ANALYZE
select
rb1_0.id,
rb1_0.title,
rb1_0.deposit,
rb1_0.monthly_rent,
rb1_0.management_cost,
rb1_0.comeable_date,
rb1_0.hits,
rt1_0.name,
r1_0.name,
p1_0.name,
p2_0.name,
rb1_0.created_at
from roommate_board rb1_0
join room_type rt1_0
on rt1_0.id = rb1_0.room_type_id
join region r1_0
on r1_0.id = rb1_0.region_id
left join region p1_0
on p1_0.id = r1_0.parents_id
left join region p2_0
on p2_0.id = p1_0.parents_id
where (
lower(rb1_0.title) like '%부하 테스트 게시글%' escape '!'
or lower(rt1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(r1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p2_0.name) like '%부하 테스트 게시글%' escape '!'
)
and rb1_0.is_deleted = false
order by
rb1_0.created_at desc,
rb1_0.id desc
offset 0 rows
fetch first 21 rows only;
4. member JOIN 추가
EXPLAIN ANALYZE
select
rb1_0.id,
rb1_0.title,
rb1_0.deposit,
rb1_0.monthly_rent,
rb1_0.management_cost,
rb1_0.comeable_date,
rb1_0.hits,
rt1_0.name,
r1_0.name,
p1_0.name,
p2_0.name,
m1_0.id,
rb1_0.created_at
from roommate_board rb1_0
join room_type rt1_0
on rt1_0.id = rb1_0.room_type_id
join region r1_0
on r1_0.id = rb1_0.region_id
left join region p1_0
on p1_0.id = r1_0.parents_id
left join region p2_0
on p2_0.id = p1_0.parents_id
join member m1_0
on m1_0.id = rb1_0.member_id
where (
lower(rb1_0.title) like '%부하 테스트 게시글%' escape '!'
or lower(rt1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(r1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p2_0.name) like '%부하 테스트 게시글%' escape '!'
)
and rb1_0.is_deleted = false
order by
rb1_0.created_at desc,
rb1_0.id desc
offset 0 rows
fetch first 21 rows only;
5. ACTIVE EXISTS 추가
EXPLAIN ANALYZE
select
rb1_0.id,
rb1_0.title,
rb1_0.deposit,
rb1_0.monthly_rent,
rb1_0.management_cost,
rb1_0.comeable_date,
rb1_0.hits,
rt1_0.name,
r1_0.name,
p1_0.name,
p2_0.name,
m1_0.id,
rb1_0.created_at
from roommate_board rb1_0
join room_type rt1_0
on rt1_0.id = rb1_0.room_type_id
join region r1_0
on r1_0.id = rb1_0.region_id
left join region p1_0
on p1_0.id = r1_0.parents_id
left join region p2_0
on p2_0.id = p1_0.parents_id
join member m1_0
on m1_0.id = rb1_0.member_id
where (
lower(rb1_0.title) like '%부하 테스트 게시글%' escape '!'
or lower(rt1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(r1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p2_0.name) like '%부하 테스트 게시글%' escape '!'
)
and rb1_0.is_deleted = false
and exists (
select 1
from state s1_0
where s1_0.member_id = rb1_0.member_id
and s1_0.state = 'ACTIVE'
)
order by
rb1_0.created_at desc,
rb1_0.id desc
offset 0 rows
fetch first 21 rows only;
6. 입주일 조건 추가
EXPLAIN ANALYZE
select
rb1_0.id,
rb1_0.title,
rb1_0.deposit,
rb1_0.monthly_rent,
rb1_0.management_cost,
rb1_0.comeable_date,
rb1_0.hits,
rt1_0.name,
r1_0.name,
p1_0.name,
p2_0.name,
m1_0.id,
rb1_0.created_at
from roommate_board rb1_0
join room_type rt1_0
on rt1_0.id = rb1_0.room_type_id
join region r1_0
on r1_0.id = rb1_0.region_id
left join region p1_0
on p1_0.id = r1_0.parents_id
left join region p2_0
on p2_0.id = p1_0.parents_id
join member m1_0
on m1_0.id = rb1_0.member_id
where (
lower(rb1_0.title) like '%부하 테스트 게시글%' escape '!'
or lower(rt1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(r1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p2_0.name) like '%부하 테스트 게시글%' escape '!'
)
and rb1_0.is_deleted = false
and exists (
select 1
from state s1_0
where s1_0.member_id = rb1_0.member_id
and s1_0.state = 'ACTIVE'
)
and (
rb1_0.comeable_date_negotiable = true
or rb1_0.comeable_date >= '2026-09-30 01:39:04.6046101'
)
order by
rb1_0.created_at desc,
rb1_0.id desc
offset 0 rows
fetch first 21 rows only;
7. 차단 NOT EXISTS 추가
여기서 병목 발생, 평균 5ms → 400ms로 급증했다.
BLOCK 테이블에서 풀스캔을 돌고 있었다.

EXPLAIN ANALYZE
select
rb1_0.id,
rb1_0.title,
rb1_0.deposit,
rb1_0.monthly_rent,
rb1_0.management_cost,
rb1_0.comeable_date,
rb1_0.hits,
rt1_0.name,
r1_0.name,
p1_0.name,
p2_0.name,
m1_0.id,
rb1_0.created_at
from roommate_board rb1_0
join room_type rt1_0
on rt1_0.id = rb1_0.room_type_id
join region r1_0
on r1_0.id = rb1_0.region_id
left join region p1_0
on p1_0.id = r1_0.parents_id
left join region p2_0
on p2_0.id = p1_0.parents_id
join member m1_0
on m1_0.id = rb1_0.member_id
where (
lower(rb1_0.title) like '%부하 테스트 게시글%' escape '!'
or lower(rt1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(r1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p2_0.name) like '%부하 테스트 게시글%' escape '!'
)
and rb1_0.is_deleted = false
and exists (
select 1
from state s1_0
where s1_0.member_id = rb1_0.member_id
and s1_0.state = 'ACTIVE'
)
and (
rb1_0.comeable_date_negotiable = true
or rb1_0.comeable_date >= '2026-09-30 01:39:04.6046101'
)
and not exists (
select 1
from block b1_0
where b1_0.is_deleted = false
and (
(
b1_0.blocker_id = 5
and b1_0.blocked_id = rb1_0.member_id
)
or
(
b1_0.blocker_id = rb1_0.member_id
and b1_0.blocked_id = 5
)
)
)
order by
rb1_0.created_at desc,
rb1_0.id desc
offset 0 rows
fetch first 21 rows only;
8. 최신 basic_information 조회 추가
EXPLAIN ANALYZE
select
rb1_0.id,
rb1_0.title,
rb1_0.deposit,
rb1_0.monthly_rent,
rb1_0.management_cost,
rb1_0.comeable_date,
rb1_0.hits,
rt1_0.name,
r1_0.name,
p1_0.name,
p2_0.name,
m1_0.id,
bi1_0.name,
bi1_0.birth,
bi1_0.gender,
rb1_0.created_at
from roommate_board rb1_0
join room_type rt1_0
on rt1_0.id = rb1_0.room_type_id
join region r1_0
on r1_0.id = rb1_0.region_id
left join region p1_0
on p1_0.id = r1_0.parents_id
left join region p2_0
on p2_0.id = p1_0.parents_id
join member m1_0
on m1_0.id = rb1_0.member_id
left join basic_information bi1_0
on bi1_0.member_id = m1_0.id
and bi1_0.id = (
select max(bi2_0.id)
from basic_information bi2_0
where bi2_0.member_id = m1_0.id
)
where (
lower(rb1_0.title) like '%부하 테스트 게시글%' escape '!'
or lower(rt1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(r1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p2_0.name) like '%부하 테스트 게시글%' escape '!'
)
and rb1_0.is_deleted = false
and exists (
select 1
from state s1_0
where s1_0.member_id = rb1_0.member_id
and s1_0.state = 'ACTIVE'
)
and (
rb1_0.comeable_date_negotiable = true
or rb1_0.comeable_date >= '2026-09-30 01:39:04.6046101'
)
and not exists (
select 1
from block b1_0
where b1_0.is_deleted = false
and (
(
b1_0.blocker_id = 5
and b1_0.blocked_id = rb1_0.member_id
)
or
(
b1_0.blocker_id = rb1_0.member_id
and b1_0.blocked_id = 5
)
)
)
order by
rb1_0.created_at desc,
rb1_0.id desc
offset 0 rows
fetch first 21 rows only;
9. basic_information_file까지 추가
EXPLAIN ANALYZE
select
rb1_0.id,
rb1_0.title,
rb1_0.deposit,
rb1_0.monthly_rent,
rb1_0.management_cost,
rb1_0.comeable_date,
rb1_0.hits,
rt1_0.name,
r1_0.name,
p1_0.name,
p2_0.name,
m1_0.id,
bi1_0.name,
bi1_0.birth,
bi1_0.gender,
rb1_0.created_at
from roommate_board rb1_0
join room_type rt1_0
on rt1_0.id = rb1_0.room_type_id
join region r1_0
on r1_0.id = rb1_0.region_id
left join region p1_0
on p1_0.id = r1_0.parents_id
left join region p2_0
on p2_0.id = p1_0.parents_id
join member m1_0
on m1_0.id = rb1_0.member_id
left join basic_information bi1_0
on bi1_0.member_id = m1_0.id
and bi1_0.id = (
select max(bi2_0.id)
from basic_information bi2_0
where bi2_0.member_id = m1_0.id
)
left join basic_information_file bif1_0
on bif1_0.id = (
select max(bif2_0.id)
from basic_information_file bif2_0
where bif2_0.basic_information_id = bi1_0.id
)
where (
lower(rb1_0.title) like '%부하 테스트 게시글%' escape '!'
or lower(rt1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(r1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p2_0.name) like '%부하 테스트 게시글%' escape '!'
)
and rb1_0.is_deleted = false
and exists (
select 1
from state s1_0
where s1_0.member_id = rb1_0.member_id
and s1_0.state = 'ACTIVE'
)
and (
rb1_0.comeable_date_negotiable = true
or rb1_0.comeable_date >= '2026-09-30 01:39:04.6046101'
)
and not exists (
select 1
from block b1_0
where b1_0.is_deleted = false
and (
(
b1_0.blocker_id = 5
and b1_0.blocked_id = rb1_0.member_id
)
or
(
b1_0.blocker_id = rb1_0.member_id
and b1_0.blocked_id = 5
)
)
)
order by
rb1_0.created_at desc,
rb1_0.id desc
offset 0 rows
fetch first 21 rows only;
최종 SQL 쿼리
46, 52, 54, 40, 40
select
rb1_0.id,
rb1_0.title,
rb1_0.deposit,
rb1_0.monthly_rent,
rb1_0.management_cost,
rb1_0.comeable_date,
rb1_0.hits,
rt1_0.name,
r1_0.name,
p1_0.name,
p2_0.name,
m1_0.id,
bi1_0.name,
f1_0.saved_file_name,
bi1_0.birth,
bi1_0.gender,
rb1_0.created_at
from roommate_board rb1_0
join room_type rt1_0
on rt1_0.id = rb1_0.room_type_id
join region r1_0
on r1_0.id = rb1_0.region_id
left join region p1_0
on p1_0.id = r1_0.parents_id
left join region p2_0
on p2_0.id = p1_0.parents_id
join member m1_0
on m1_0.id = rb1_0.member_id
left join basic_information bi1_0
on bi1_0.member_id = m1_0.id
and bi1_0.id = (
select max(bi2_0.id)
from basic_information bi2_0
where bi2_0.member_id = m1_0.id
)
left join basic_information_file bif1_0
on bif1_0.id = (
select max(bif2_0.id)
from basic_information_file bif2_0
where bif2_0.basic_information_id = bi1_0.id
)
left join file f1_0
on f1_0.id = bif1_0.file_id
and f1_0.is_deleted = false
where (
lower(rb1_0.title) like '%부하 테스트 게시글%' escape '!'
or lower(rt1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(r1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p1_0.name) like '%부하 테스트 게시글%' escape '!'
or lower(p2_0.name) like '%부하 테스트 게시글%' escape '!'
)
and rb1_0.is_deleted = false
and exists (
select 1
from state s1_0
where s1_0.member_id = rb1_0.member_id
and s1_0.state = 'ACTIVE'
)
and (
rb1_0.comeable_date_negotiable = true
or rb1_0.comeable_date >= '2026-09-30 18:51:48.1908491'
)
-- 상대방이 나(5)를 차단했는지
and not exists (
select 1
from block b1_0
where b1_0.is_deleted = false
and b1_0.blocked_id = 5
and b1_0.blocker_id = rb1_0.member_id
)
-- 나(5)가 상대방을 차단했는지
and not exists (
select 1
from block b4_0
where b4_0.is_deleted = false
and b4_0.blocker_id = 5
and b4_0.blocked_id = rb1_0.member_id
)
order by
rb1_0.created_at desc,
rb1_0.id desc
offset 0 rows
fetch first 21 rows only;