

Apache JMeter는 서버가 제공하는 성능 및 부하를 측정할 수 있는 테스트 도구이다. JMeter는 순수 Java 애플리케이션 오픈소스이며 서버나 네트워크 또는 개체에 대해 과부하를 시뮬레이션하여 강도를 테스트 하거나 다양한 부하 유형에서 전체 성능을 분석하는데 사용할 수 있다.
응답 시간
TPS
brew install Jmeter Jmeter 3Basic Graphs 추가 
- ThreadGroup : 몇 개의 쓰레드가 동시에 요청을 보내는지
- Sampler : 어떤 유저가 해야 하는 요청
- LogicController : 테스트 계획의 실행 흐름을 제어
- Listener : 응답을 받았을 때 어떤 동작을 취하는지 (검증, 리포트, 그래프 그리기 등)
- Configuration : Sampler 또는 Listener가 사용할 값 (쿠키, JDBC 커넥션 등)
기본으로 제공하는 그래프 외에 3Basic Graph 를 추가해서 사용 해보려고 한다.




(그래프 추가 플러그인이 설치가 완료된 모습)
100개의 Thread (사용자)가 1초에 30번 (Ramp-up) 100초 동안 (Loop Count) 실행

캐싱 어노테이션을 주석 처리 하여 캐싱 유무 테스트를 진행 했다.

그래프 추가와 댓글 읽기 캐싱(전-후) 추가

JWT 인증 토큰 방식을 사용하고 있는 관계로 헤더에 컨텐츠 타입과 Authorization 액세스 토큰 설정 해주었다.


결과표
1만건의 요청.
전반적으로, 특히 응답 시간 측면에서 상당한 성능 향상이 있었다. 평균 및 일반적인 케이스(중앙값, 90% 라인)에서 약 80% 가까이 응답 속도가 개선되었고, 최소 45%의 개선이 있었습니다. 단순 로컬 환경에서 테스트라 추후 정확한 테스트가 필요해 보인다.
@Query("SELECT COUNT(n) FROM Notify n WHERE n.receiver = :user AND n.isRead = false")
int countUnreadNotifications(User user);

데이터베이스에 두 번 접근. 유저를 찾는 쿼리가 실행되고, 그 다음 해당 유저가 가진 알림 갯수를 COUNT 하는 쿼리가 실행 된다.
@Query("SELECT COUNT(n) FROM Notify n JOIN n.receiver u WHERE u.email = :email AND n.isRead = false")
int countUnreadNotifications(String email);

개선 후 단문의 쿼리만 나가는 모습

전체적인 소폭 개선과 최대 응답시간이 눈에 띄게 줄었다.
Hibernate:
select
u1_0.id,
u1_0.age,
u1_0.deleted_at,
u1_0.email,
u1_0.gender,
u1_0.img,
u1_0.is_deleted,
u1_0.mobile,
u1_0.name,
u1_0.nickname,
u1_0.password,
u1_0.social
from
user u1_0
where
u1_0.email=? // 유저 정보 조회
Hibernate:
select
jcr1_0.user_id,
jcr1_1.id,
jcr1_1.activity_type,
jcr1_1.can_join,
jcr1_1.is_participant,
jcr1_1.participant_count
from
chat_room_participants jcr1_0
join
chat_rooms jcr1_1
on jcr1_1.id=jcr1_0.chat_room_id
where
jcr1_0.user_id=? // 유저가 참여한 채팅방 조회
Hibernate:
select
p1_0.id,
p1_0.activity_type,
p1_0.capacity,
p1_0.chat_room_id,
p1_0.content,
p1_0.del_flag,
p1_0.latitude,
p1_0.local_date,
p1_0.longitude,
p1_0.meeting_time,
p1_0.participate_flag,
p1_0.place_name,
p1_0.road_name,
p1_0.title,
u1_0.id,
u1_0.age,
u1_0.deleted_at,
u1_0.email,
u1_0.gender,
u1_0.img,
u1_0.is_deleted,
u1_0.mobile,
u1_0.name,
u1_0.nickname,
u1_0.password,
u1_0.social,
p1_0.view_count
from
post p1_0
left join
user u1_0
on u1_0.id=p1_0.user_id
where
p1_0.chat_room_id=? // 채팅방과 연관된 게시물 조회
Hibernate:
select
cml1_0.chat_room_id,
cml1_0.id,
cml1_0.chat_message_type,
cml1_0.content,
cml1_0.created_at,
cml1_0.user_id
from
chat_messages cml1_0
where
cml1_0.chat_room_id=? // 특정 채팅방의 연관된 메세지 조회
3N+1 발생 중
@Query("SELECT NEW com.example.simplechatapp.dto.UserChatRoomDTO(" +
"cr.id, " + // chatRoomId
"p.id, " + // postId
"p.title, " + // postTitle
"p.meetingTime, " + // meetingTime
"cr.participantCount, " + // participantCount
"p.capacity, " + // capacity
"MAX(cm.createdAt), " + // lastMessageTime
"SUBSTRING(MAX(CASE WHEN cm.id = (SELECT MAX(cm2.id) FROM ChatMessage cm2 WHERE cm2.chatRoom = cr) THEN cm.content ELSE NULL END), 1, 30), " + // lastMessagePreview
"cr.activityType) " + // activityType
"FROM User u " +
"JOIN u.joinedChatRoom cr " +
"JOIN cr.post p " +
"LEFT JOIN cr.chatMessageList cm " +
"WHERE u.email = :email " +
"GROUP BY cr.id, p.id, p.title, p.meetingTime, cr.participantCount, p.capacity, cr.activityType")
List<UserChatRoomDTO> findUserChatRoomDTOs(@Param("email") String email);
단일문의 쿼리로 원하는 정보를 불러오고 있다.
Hibernate:
select
jcr1_1.id,
p1_0.id,
p1_0.title,
p1_0.meeting_time,
jcr1_1.participant_count,
p1_0.capacity,
max(cml1_0.created_at),
substring(max(case
when cml1_0.id=(select
max(cm1_0.id) from
chat_messages cm1_0
where
cm1_0.chat_room_id=jcr1_1.id)
then cml1_0.content
else null
end), 1, 30),
jcr1_1.activity_type
from
user u1_0
join
chat_room_participants jcr1_0
on u1_0.id=jcr1_0.user_id
join
chat_rooms jcr1_1
on jcr1_1.id=jcr1_0.chat_room_id
join
post p1_0
on jcr1_1.id=p1_0.chat_room_id
left join
chat_messages cml1_0
on jcr1_1.id=cml1_0.chat_room_id