API 성능 최적화하기

JaeGu Jeong·2024년 2월 23일
0

개발 도구들

목록 보기
10/12

GET 대신 HEAD

헤더는 동일하면서 페이로드만 제외되는 특징을 활용합니다.
주로 자원 유무 확인과 캐시 유효성 확인에 사용하면 Response비용을 아낄 수 있습니다.
또한 페이로드가 없기 때문에 보안 실수도 줄일 수 있습니다.

데이터 압축 전송

다음은 응답을 압축하는 스프링부트 프로퍼티 파일 설정입니다.

브라우저가 gzip으로 받을 수 있다고 요청 헤더로 알려주면 서버에서 gzip으로 인코딩해서 응답합니다.

Header 설정으로 부하 방지

server.tomcat.keep-alive-timeout=0

keep-alive를 사용하지 않습니다.
서버와 클라이언트 모두 지원하고 직접 연결되었다면 핸드쉐이크를 줄이는 좋은 기능입니다.
하지만 블라인드 프록시같은 중개 서버가 keep-alive를 지원하지 않으면서 그대로 헤더를 양쪽에 전달하면 서버와 클라이언트 모두 소켓을 열고 대기하다 닫고를 반복하면서 부하가 발생합니다.
중개 서버가 keep-alive를 지원하지 않더라도 양쪽에 close로 전달하면 문제 없지만 그렇지 않은 경우를 대비해 "keep-alive-timeout=0"으로 리스크를 관리해야합니다.

비동기 활용

Log4j2나 response에 당장 필요없는 동작은 비동기로 처리합니다.

DB 최적화

https://velog.io/@jay13jeong/JPA를-DTO로-반환받아야하는이유

1. Query문 전체 조회 지양

필터링을 위한 서비스 로직이 늘어날 수 있지만 전체 조회를 지양합니다.
Tuple을 처리할 때 쿼리에 필요한 데이터만 정의 합니다.
List단위로 가져올 때는 Range 설정을 지향합니다.
DB의 View기능 사용도 고려합니다.
핵심 목표는 I/O 최소화 입니다.

2. INDEX 사용

조회가 주 목적인 테이블이면 INDEX 기능을 고려합니다.
테이블 간에 WHERE문과 JOIN이 사용되는 경우 해당 컬럼에 INDEX를 고려합니다.
중요한 점은 일반 테이블에 비해 추가 저장공간을 요구하므로 시스템 자원을 생각해서 적용해야 합니다.

3. KeySet 사용

인덱스를 사용하더라도 페이징할 때 offset방식만을 사용하면 튜플이 백만개 이상일 때 큰 지연 발생합니다.
마지막으로 읽은 데이터를 기준으로 순서를 +1 해서 다음 리스트를 페이징합니다.

4. QUERYDSL 최적화

  1. exists을 사용금지합니다.
    QUERYDSL의 exists는 "SQL exists"가 아닌 "count > 0"를 사용하기에 시간복잡도가 O(n)이 걸립니다.
    JPQL을 "from"없이는 쿼리를 생성 불가입니다.
    ".fetchFirst()"를 사용해서 null을 검사하는 방식을 사용하여 "SQL exists"와 비슷한 성능을 유지할 수 있습니다.

  2. Cross Join 지양합니다. 일부 DB는 최적화를 지원하지만 그렇지 않은경우의 DB도 있습니다. Cross를 의도한 것이 아니더라도 묵시적 Join을 사용하면 Cross Join이 발생할 수 있습니다. "InnerJoin"을 사용하여 명시하면 성능 저하를 막을 수 있습니다.

엔드포인트 세분화

캐싱 전략을 세우기 용이합니다. 업데이트 빈도는 낮지만 조회가 많은 API를 구분해서
필요한 캐싱 전략을 적용하기 좋은 구조가 됩니다.
또한 명확성이 높아지므로 버전 관리와 같은 유지보수에 유리합니다.

캐싱

필요에 따라 상태코드 304를 이용합니다.
304를 이용해서 서버의 자원 업데이트 시간을 비교해서 클라이언트 기존 자원을 재사용합니다.
사이즈가 작고 업데이트가 적으며 자주 읽는 데이터는 Redis같은 도구로 메모리 저장소를 사용합니다.
업데이트가 잦은 데이터는 캐싱하는데 비용이 많이 발생해서 비효율적입니다.
사이즈가 크면 싱글스레드 Redis같은 도구는 block 될 수 있습니다.

profile
BackEnd Developer

0개의 댓글