스프링 16 일차

TaeYoon Kim·2024년 1월 8일
0

SW CAMP

목록 보기
22/30

우리가 서비스를 구현하면서 고려해야하는 것이 무엇이 있을까?

처음에 스타트업을 시작한다고 생각해보자.

처음에서는 프론트엔드 백엔드 DB 하나씩 쓸 것이다.

그 이후 사람들이 몰리면 백엔드와 DB를 늘리게 되는데,

백엔드는 저장하는 데이터가 없으므로 컴퓨터만 늘리면 되는데,

DB는 컴퓨터를 늘릴 때 데이터도 동기화해야하는 문제가 있다.
따라서, 백엔드에 배해 많이 늘릴 수 없다.

벡엔드가 많아지면 DB서버의 부담이 가중되니 DB서버 성능에 관심을 가져야한다.

그러면 개발을 하면서 DB 서버와 관련된 문제들을 살펴보자./

N+1

ORM 기술을 사용하면 항상 N+1 문제가 발생한다.
꼭 나쁜 것만은 아니다.

왜 생기고, 어떻게 해결해야하는가. 해결 방법에 정답이 있는 것이 아니다.
상황마다 적절한 방법들이 있다.

문제
상품 목록을 조회할 때, 상품들을 가져오고, 그와 관계를 맞은 데이터를 가져오기 위해 상품의 갯수(N)만큼 추가로 쿼리문이 실행된다.

왜 JOIN를 쓰지 않았을까?

문제가 생기는 이유.
ORM기술들이 데이터를 가져올 때 관계를 맺은 데이터들이 필요없는 경우가 있기 때문에 데이터가 사용이 될 때 가져오도록 만들었기 때문이다.

ex) 로그인을 하기 위해서 유저의 아이디 패스워드만 불러오면된다.

해결방법

  1. jpql (쉬움. 추가적인 기능이 없음.) 페이징 기능이 좋은 예시
  • JPA Pageable이라는 기능을 활용해서 쉽게 구현하는데 jqpl를 쓰면 직접 추가적인 작업이 필요하다.

사용방법
JpaRepository를 상속받은 인터페이스에서 쿼리문을 자동으로 만드는데,
우리가 직접 만들어 줄 수 있다.

@Query("SELECT u FROM USER u  JOIN u.userImage") 
// 실행할 JQPL쿼리문을 입력하는 어노테이션(SQL문 아님!!)
public List<User> findAll(); //오버라이딩

관계가 맞어져 있는 대부분의 테이블이 문제가 발생하기 때문에 일일이 찾아야한다.

페이징 기능은 어떻게 구현되어있고 어떻

  1. QueryDSL (어려움. 추가적인 기능을 지원함.)

    사용 방법

    1. QueryDSL 라이브러리 다운로드 (apt, jpa 2개)
      (https://mvnrepository.com/artifact/com.querydsl/querydsl-apt/5.0.0)
      (https://mvnrepository.com/artifact/com.querydsl/querydsl-jpa)
    2. 플러그인 추가 (https://github.com/querydsl/apt-maven-plugin)
    3. apt:process 실행
    4. target file 아래에 Qclass들이 생성되었는지 확인
    5. Qclass들이 자바 파일로 인식되도록 설정이 필요
      (인텔리제이 기준)
      Project structure에서 Project Setting - Modules - target아래있는 classes.java를 우 클릭 후 설정 변경.
    1. 새로운 레포지토리 인터페이스 생성
    2. public List findList() 매소드 추가
    3. 인터페이스의 구현체를 만든다.
      QuerydslRepositorySupport 상속받는다.
      생성자에 super(엔티티 클라스.class); 추가
      QClass들 객체를 생성
      쿼리문을 나타내는 매소드를 이용해 쿼리문을 작성한다.
    4. 기존에 쓰던 Repo 인터페이스에 상속시킨다.

    페이지 기능 만들기

  2. public Page findList(Pageable pageable) 매소드 추가

  3. 만든 쿼리 문에 offest(pageable.getOffset) 과 limit(pageable.getPageSize)를 사용한다.

Page는 리스트가 아니기 때문에 처리를 따로 해줘야한다.
products.getContent() 로 리스트 반환 가능.

join 후 에 fetch join를 해줘야한다. 안그러면 N+1문제가 그대로 발생한다.

  1. EntityGraph (잘 안 씀, 수업에서도 안할거다.)
  2. ORM기술 사용 안하기. (빨리 편하게 만들어야한다면 비추천.)

동시성 문제

###쿼리문이 동시에 실행되야하는 것들이 있다.

  1. 상품의 정보를 DB서버에 저장하는 것

  2. S3서버의 상품의 이미지들을 저장하는 것

    1번이 저장됬는데, 2번이 에러가 나면 저장하는 안되는 데이터가 저장된 경우.
    2번을 실행하면서 중간에 에러가 난 경우 모든 사진을 업로드 된 것이 아니다.

    이런 경우를 원자성이 지켜지지 않았다고 표현이 되는데
    해결하기 위해 @Transactional를 매소드 위에 달아두면 된다.

    DB 데이터에 동시에 접근하는 경우

  3. 좋아요 기능을 구현할 때, 동시에 좋아요 요청을 받은 경우 누락되면 안된다.
    이런 경우를 '격리성을 지켜야한다'라고 한다.
    @Transactional과 함께 Lock 설정을 해야한다.

    Lock 설정 방법

  4. repo 인터페이스의 매소드 위에 @Lock를 쓰고
    Lock 의 종류를 정해주면 된다.

    비관적 Lock : 안전하지만 느리다. (SQL 의 For update와 같음.)
    매소드가 실행 중일 때, 다른 스레드들이 읽지도 쓰지도 못한다.

    사용방법 어노테이션 1개
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    public void method(){} // 매소드에 붙이는 어노테이션

    낙관적 Lock : 누락이 생기면 예외를 터트려 버린다.
    자신이 데이터를 사용하기 전 버전을 기록해두고
    사용 후에 버번이 달라져 있으면 예외를 터트린다.
    사용 방법 어노테이션 2개

    @Lock(LockModeType.OPTIMISTIC)
    public void method(){} // 매소드에 붙이는 어노테이션

    @Version
    private Integer asd; //Integer, Date 클라스에만 붙이는 어노테이션

    알아두면 좋은 트랜잭션의 특징 ACID

    • 원자성 (Atomicity) : 트랜잭션 내의 sql문이 모두 실행되거나 모두 실행되지 않아야한다.
    • 일관성 (Consistency) :
    • 고립성 (Isolation) :
    • 영속성 (Durability) :

    알아두면 좋은 동시에 코드를 실행시키는 방법

0개의 댓글