오늘 한 일

  • 알고리즘 1문제
  • 팀 프로젝트 회의
  • 테코톡 편집

오늘 jpa 강의를 들으면서 확실하게 알고 있다는 느낌을 받지 못했다. 내일 오전에 영속성 컨텍스트에 대해 포스팅을 목표로 jpa 개념을 정리해보자.

 

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.07.31 일일 회고  (1) 2023.08.01
2023.07.21 일일 회고  (0) 2023.07.21
2023.06.20 일일 회고  (0) 2023.06.21
2023.06.17 일일 회고  (0) 2023.06.18
2023.06.15 일일 회고  (0) 2023.06.16

EC2란?

Amazon Elastic Compute Cloud(Amazon EC2)는 안전하고 크기 조정이 가능한 컴퓨팅 파워를 클라우드에서 제공하는 웹 서비스다.

안정적인 확장 가능한 인프라에 온디맨드로 액세스한다.

온디맨드란 AWS와 같은 외부 서비스 공급자가 데이터를 호스트하는 것을 말한다. 클라우드 공급자가 모든 하드웨어, 소프트웨어 및 기타 지원 인프라를 조달하고 자사의 데이터 센터에 설치 및 관리한다. 비즈니스는 인터넷을 통해 PC, 웹 브라우저 또는 모바일 앱에서 서비스에 액세스하고 계정을 관리한다.

조직 내에서 서버를 물리적으로 관리하는 온프라미스는 서버의 수평적  확장인 scale-out 확장을 하기 위해서는 물리 서버를 추가로 주문하고, 설치하는 과정에서 많은 시간이 들게 된다. 서버를 들여 놓을 공간도 필요하고 직접 관리해주어야 하는 단점도 있다.

 

온디맨드는 서버를 직접 관리하지 않고 네트워크를 통해 관리하기 때문에 쉽고 빠르게 sacle-out 방식의 확장을 하기 매우 쉽다.

 

 

1.  인스턴스 

AWS에 회원가입을 하고, 로그인을 하면 다음과 같은 대시보드를 확인할 수 있다.

가상 머신 시작으로 EC2 인스턴스를 시작한다.

 

2. 인스턴스 시작

인스턴스의 이름을 입력한다.

 

3. OS 및 아키텍처 선택

Ubuntu 22.04 LTS, 64비트 ARM으로 선택했다.

 

4. 인스턴스 유형

Arm 아키텍처는 t4g.small을 프리티어로 제공하고 있으니 이를 선택한다. x86 아키텍처는 무료로 사용 가능한 다른 인스턴스 유형을 제공하고 있으니 프리티어에서 사용가능한 인스턴스 유형을 선택하면 된다.

 

5. 키 페어 생성

적당한 키 페어 이름을 설정하고 키 페어 유형과 파일 형식을 선택한다. 

주의 : 프라이빗 키 파일을 절대로 외부에 유출하는 일이 없도록 하자. 

이 파일을 통해서 인스턴스에 접속할 수 있기 때문에 누군가 접속해서 채굴기로 쓰는 등 악용한다면 수 많은 비용 청구를 겪게 될 것이다.

주변에서도 실수로 이 파일을 유출해서 몇 백만원씩 피해를 봤다는 소문을 듣기도 했다.

6. 네트워크 설정

하나의 인스턴스만 사용할 것이기 때문에 네트워크 설정은 따로 하지 않았다.

추가적인 인스턴스를 할당해서 VPC를 구성한다면 네트워크 설정을 해야 할 것이다.

ssh로 접속해서 사용하고 웹 서버를 배포할 목적이기 때문에 HTTP 트래픽과 HTTPS 트래픽을 허용했다.

인바운드 규칙은 가능하다면 액세스 가능한 IP를 제한하여 사용하는 것이 좋다.

 

7. 인스턴스 시작

선택한 옵션들을 확인하고 인스턴스를 시작한다.

스토리지는 현재 30GiB까지 무료로 제공하고 있어서 30GiB로 설정했다.

인스턴스 시작 버튼을 눌러서 시작한다.

 

8. 확인

EC2 대시보드로 돌아와서 실행 중인 인스턴스를 확인한다.

인스턴스에는 퍼블릭 주소가 있는데, 퍼블릭 주소와 아까 생성했던 pem파일을 통해 ssh 접속을 할 수 있다.

터미널에서 pem 파일이 있는 경로에서 다음 명령어를 실행해 인스턴스에 접근이 가능한지 확인해보자.

ssh -i key_pair.pem ubuntu@xxx.xxx.xxx.xxx

key_pair.pem은 아까 위에서 발급했던 키 페어 파일 이름이고 xxx는 인스턴스의 퍼블릭 주소를 입력하면 된다.

 

참고 문서

- https://www.microsoft.com/ko-kr/microsoft-365/business-insights-ideas/resources/cloud-storage-vs-on-premises-servers

- https://aws.amazon.com/ko/pm/ec2/?trk=4c74fd91-5632-4f18-ac76-a6c66c92e185&sc_channel=ps&ef_id=CjwKCAjw-vmkBhBMEiwAlrMeF4sD-cYm0FfqQgCGMyxrtG121K9moRvGfOKfkvBDxlPJylrD9vAYSxoCcGMQAvD_BwE:G:s&s_kwcid=AL!4422!3!477202774503!p!!g!!ec2!11549843702!111422709806 

 

오라클 공식문서에서는 영속성 컨텍스트에 대해 다음과 같이 정의하고 있다.

A persistence context is a set of entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed.

1. 각각의 영속적인 엔티티 식별자에 대해 유일한 엔티티 인스턴스가 있는 엔티티 인스턴스의 집합이다.
2. 영속성 컨텍스트 내에서, 엔티티 인스턴스들과 그들의 생명주기가 관리된다.

1. 의 내용을 풀어서 말하면

컨텍스트 내에서 엔티티끼리 고유하게 식별하게 하는 id를 가지고, 동일한 id를 가진 엔티티가 존재할 수 없다.

JdbcTemplate을 사용하던 시절로 돌아가 다음 코드를 보자. 

@Test  
void findById() {  
	Member insert = memberDao.insert(new Member("maco", "pass"));  
	  
	Member member1 = memberDao.findById(insert.getId()).get();  
	Member member2 = memberDao.findById(insert.getId()).get();  
	  
	System.out.println(member1);  
	System.out.println(member2);  
	System.out.println(member1 == member2);
}

 

위 코드에서 member1과 member2는 같은 insert.getId()를 이용하여 DB를 조회했기 때문에 같은 데이터를 가진다.

그렇다면 member1과 member2는 같은 객체인가?

출력 결과를 확인해보자.

cart.entity.Member@194c64c9
cart.entity.Member@3a64b5d
false

DB에서 id를 가진 row를 조회하여 같은 데이터를 가지고 있으나 DAO가 객체를 생성할 때 new 오퍼레이션을 사용하여 생성했기 때문에 객체가 동일하지는 않다.

 

1차 캐시

영속성 컨텍스트는 1차 캐시를 제공하여 위 예시에서 설명한 동일성 문제를 해결함과 동시에 성능 최적화를 지원한다.

  1. 같은 객체를 반환하여 동일성 보장하고 식별자 마다 유일한 엔티티를 보장
  2. 캐싱으로 같은 조회라면 쿼리를 실행하지 않아 성능을 최적화한다.

1차 캐시를 이해하기 위해 다음 그림을 보자.

영속성 컨텍스트에서 조회 요청이 들어왔을 때, 1차 캐시에 캐싱된 엔티티가 있다면 DB를 조회하지 않고 바로 캐싱된 엔티티를 반환한다. 

캐시는 데이터베이스 기본키의 값을 키로, 엔티티를 값으로 매핑하여 저장하고 있다. 엔티티의 식별자인 기본 키를 키로 매핑하기 때문에 식별자마다 엔티티의 유일함을 보장한다.

 

엔티티 생명 주기

글의 맨 처음 영속성 컨텍스트의 정의 부분에서 영속성 컨텍스트는 엔티티의 생명 주기를 관리한다고 했다.

엔티티에는 4가지 상태가 존재한다.

  • 비영속 : 영속성 컨텍스트와 관계가 없는 상태
  • 영속 : 영속성 컨텍스트에 저장된 상태
  • 준영속 : 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 삭제 : 삭제된 상태

영속성 컨텍스트는 엔티티 매니저를 통해 이러한 엔티티의 생명 주기를 관리한다.

코드를 보며 이해해보자.

@Transactional
@Rollback(value = false)
@SpringBootTest
class UserTest {

    @PersistenceContext
    private EntityManager entityManager;

    @Test
    void test() {
        User user = new User();    // 비영속
        user.setUsername("maco");    // 비영속
        entityManager.persist(user);    // 영속
    }
}

 

엔티티 매니저를 의존성 주입 받기 위해 @SpringBootTest로 작성했다.

또한, 트랜잭션 내에서 관리해야 하기 때문에 @Transactional을 붙여주고, DB에 데이터가 플러시 되는 것을 직접 확인하기 위해 @Rollback(value = false)로 설정했다.

위 코드에서 엔티티 매니저의 persist를 호출하기 전에 user는 비영속 상태였다가 persist를 호출하면 영속성 컨텍스트에서 관리하기 때문에 영속되었다고 말한다.

위에서 봤던 1차 캐시 그림에서 데이터를 조회하면 영속성 컨텍스트의 1차 캐시 안에 저장되었다.

즉, 조회도 영속성 컨텍스트가 엔티티를 관리하기 때문에 조회를 하면 그 엔티티는 영속 상태라고 한다.

    @Test
    void test() {
        User user = entityManager.find(User.class, 15L);	// 영속
        System.out.println("user = " + user);
    }

 

entityManager.find()를 통해 조회한 user는 영속성 컨텍스트에서 관리하고 있다.

준영속 상태에 대해서도 알아보자.

    @Test
    void test() {
        User user = entityManager.find(User.class, 15L);    // 영속
        entityManager.detach(user);    // 준영속
        System.out.println("user = " + user);
    }

 

준영속 상태는 영속성 컨텍스트에 저장되어 있다가 분리된 상태를 말한다. 위와 같이 직접 엔티티 매니저의 detach()를 호출하거나 close()로 영속성 컨텍스트를 닫거나 clear()로 영속성 컨텍스트를 비우면 관리하던 엔티티가 준영속 상태가 된다.

준영속 상태는 객체의 데이터가 지워지는 것이 아니라 영속성 컨텍스트에서 관리되지 않는 것 뿐이기 때문에 위에서 출력에 사용한 것 처럼 엔티티를 계속 사용할 수 있다.

flush()를 호출하는 것은 DB에 쿼리문만 실행하고
여전히 영속성 컨텍스트에서 엔티티를 관리하고 있기 때문에 엔티티가 준영속 상태가 되지는 않는다.

삭제는 remove를 호출하여 할 수 있으며, 영속성 컨텍스트에서 관리되지 않음과 동시에 DB에 delete쿼리를 실행한다.

    @Test
    void test() {
        User user = entityManager.find(User.class, 15L);
        entityManager.remove(user);
        System.out.println("user = " + user.getUsername());
    }

 

삭제 또한 컨텍스트에서 관리되지 않고 DB에 데이터가 없을 뿐 user 엔티티 자체에는 데이터가 있는 것을 확인할 수 있다.

user = maco

 

정리

영속성 컨텍스트의 정의였던 아래의 내용을 살펴 보았다.

1. 각각의 영속적인 엔티티 식별자에 대해 유일한 엔티티 인스턴스가 있는 엔티티 인스턴스의 집합이다.
2. 영속성 컨텍스트 내에서, 엔티티 인스턴스들과 그들의 생명주기가 관리된다.

1차 캐시를 통해 엔티티의 동일성을 보장하고, 성능을 최적화하였고, 엔티티를 영속시켜 엔티티의 생명 주기를 관리했다.

영속성 컨텍스트가 엔티티를 관리하면 다음과 같은 장점들이 있다.

  • 1차 캐시
  • 동일성 보장
  • 트랜잭션을 지원하는 쓰기 지연
  • 변경 감지
  • 지연 로딩

이번 포스팅에서 1차 캐시와, 동일성 보장에 대해서 다뤘으니 다음 포스팅에서 나머지 특징들에 대해서 알아보자.

 

참고 문서

오늘 한 일

  • query-dsl 강의 듣기
    • 환경 설정
    • 기본 문법
  • 알고리즘 2문제
  • 오브젝트 읽기

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.07.21 일일 회고  (0) 2023.07.21
2023.07.04 일일 회고  (0) 2023.07.05
2023.06.17 일일 회고  (0) 2023.06.18
2023.06.15 일일 회고  (0) 2023.06.16
2023.06.14 일일 회고  (0) 2023.06.15

오늘 할 일

  • jpa 강의 활용 2편 완강
    • API 개발 고급 - 지연 로딩과 조회 성능 최적화
    • API 개발 고급 - 컬렉션 조회 최적화
    • API 개발 고급 - 실무 필수 최적화
  • 알고리즘 1문제
  • 오브젝트

 

CQRS

update와 같은 것은 커맨드기 때문에 쿼리랑 분리한다. 따라서 update가 엔티티를 반환하기 보다 void나 id정도 반환하는 것이 유지보수에 좋음.

 

1:N 에서 고려할 점

db뻥튀기
1:다 관계에서.(컬렉션을 의존할 때)
order가 1개, orderItem이 n개이면 조회의 row는 n개가 된다. order에 대한 데이터는 중복이 됨.
-> jpql에 distinct를 넣어줌
-> 1. 실제 sql에 distinct가 들어가 동작함. -> 이거는 로우의 모든 값이 중복이어야 제거해줌
-> 2. jpql의 distnct는 하나 하나 객체에 담을 때 id가 같은 id면 중복인 애는 버리고 list에 담아서 반환해줌.
단점
- 페이징이 불가능하다.

1:다 인 경우에만 페치 조인을 하면 페이징이 안나간다!!
llimit나 시퀀스 sql문이 안나가고 jpa가 warn을 알려줌.(애플리케이션의 메모리에서 페이징을 처리했다는)

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.07.04 일일 회고  (0) 2023.07.05
2023.06.20 일일 회고  (0) 2023.06.21
2023.06.15 일일 회고  (0) 2023.06.16
2023.06.14 일일 회고  (0) 2023.06.15
2023.06.13 일일 회고  (0) 2023.06.14

오늘 할 일

  • jpa 활용 1 완강
  • 알고리즘 1문제
  • 오브젝트

 

DB 설계 고려 사항

@Transactional  
public Long join(Member member) {  
	validateDuplicateMember(member);  
	memberRepository.save(member);  
	return member.getId();  
}

여러 대의 was가 있고, 동시에 a라는 멤버를 저장한다고 해보자.
그렇다면 둘 다 validate로직을 통과하고 save를 수행할 수 있기 때문에 db에 unique 제약조건을 걸어 주는 것이 좋다.

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.06.20 일일 회고  (0) 2023.06.21
2023.06.17 일일 회고  (0) 2023.06.18
2023.06.14 일일 회고  (0) 2023.06.15
2023.06.13 일일 회고  (0) 2023.06.14
2023.06.12 일일 회고  (0) 2023.06.13

오늘 할 일

  • jpa 기본편 완강
    • 객체지향 쿼리 언어 2 - 중급 문법
  • jpa 활용 1
    • 프로젝트 환경 설정
    • 도메인 분석 설계
  • 알고리즘 1문제
  • 오브젝트

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.06.17 일일 회고  (0) 2023.06.18
2023.06.15 일일 회고  (0) 2023.06.16
2023.06.13 일일 회고  (0) 2023.06.14
2023.06.12 일일 회고  (0) 2023.06.13
[레벨 2] 레벨 인터뷰 회고  (0) 2023.06.09

오늘 할 일

  • jpa 강의 기본편 완강
    • 값 타입
    • 객체지향 쿼리 언어 1 - 기본 문법
    • 객체지향 쿼리 언어 2 - 중급 문법
  • 알고리즘 1문제
  • 테코톡 업로드

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.06.15 일일 회고  (0) 2023.06.16
2023.06.14 일일 회고  (0) 2023.06.15
2023.06.12 일일 회고  (0) 2023.06.13
[레벨 2] 레벨 인터뷰 회고  (0) 2023.06.09
[레벨 2] 레벨 로그  (0) 2023.06.08

+ Recent posts