현재 테스트 코드를 작성할 때 생성하는 데이터는 AuditingEntityListener로 처리된 BaseTimeEntity
를 상속받아서 공통된 createdAt
, updatedAt
을 상속받고 있다.
@MappedSuperclass
@EntityListeners(AuditingEntityListener::class)
abstract class BaseTimeEntity {
@CreatedDate
@Column(updatable = false)
var createdAt: LocalDateTime = LocalDateTime.now()
@LastModifiedDate
var updatedAt: LocalDateTime = LocalDateTime.now()
}
별 생각 없었는데 발생한 이슈는 updatable = false
인 상황에서 새로 생성하는 Entity의 createdAt을 어떻게 조정할 것인가? 였다. 생성할 때 createdAt을 여러모로 조정해봤지만 DB에 들어갈 땐 Auditing으로 인해 LocalDateTime.now()
로 설정이 됐다.
원래는 간단히 생각해서 수정하면 되는거 아닌가 싶었는데 여러가지 고려할 부분이 있었다.
updatable = false
때문에 createdAt에 대한 UPDATE Query는 JPA 제약으로 무시된다.처음엔 1차 캐시에 있는 Entity에 대한 정보만 수정하게 해서 어차피 Test 데이터를 생성하고 시나리오에서 조회할 때는 1차 캐시의 Entity를 가져오고 있었기에 UPDATE 쿼리는 발생하지 않지만 1차 캐시는 변경됐으니 변경했다 치고 테스트를 하는 건 어떤가 생각이 들었다.
그런데 createdAt이 언제나 독립적일 수도 없고 어디선가는 추가로 쓰게되거나 DB와 1차 캐시의 불일치등의 이슈는 계속 끌어안아야 한다는 생각에 접었고 결국 본 코드 수정 없이 DB를 업데이트하는 방법으로 nativeQuery
를 선택했다.
// Repository Test
@PersistenceContext
private lateinit var entityManager: EntityManager
// 기본 데이터 생성 (defaultPostList)
entityManager.clear()
defaultPostList.forEach {
val randomDate = fixtureMonkey.giveMeOne(LocalDateTime::class.java)
entityManager.createNativeQuery("UPDATE post SET created_at = :randomDate, updated_at = :randomDate WHERE id = :id")
.setParameter("randomDate", randomDate)
.setParameter("id", it.id)
.executeUpdate()
}
이후 해당 Entity를 즉시 사용하거나 할 땐 .refresh
등을 처리해주면 된다.
Hibernate: UPDATE post SET created_at = ?, updated_at = ? WHERE id = ?
Hibernate: select p1_0.id,c1_0.id,c1_0.name,p1_0.content,p1_0.created_at,p1_0.status,p1_0.title,p1_0.updated_at,u1_0.id,u1_0.created_at,u1_0.password,u1_0.updated_at,u1_0.username from post p1_0 left join category c1_0 on c1_0.id=p1_0.category_id left join app_user u1_0 on u1_0.id=p1_0.user_id where p1_0.id=?
updatable = false
를 무시하고 무사히 UPDATE 쿼리가 발생하는 것을 확인할 수 있었다.
단점이라면 쿼리가 너무 많이 발생한다는 것 같다. 따지고 보면 GIVEN의 설정 자체가 잘못되었다는 뜻이 아닐까 생각이 들지만 현재 본 코드를 건드리지 않고 구성하기엔 나쁘지 않은 접근이라 생각이 든다.
이렇게 뭔가 고민거리는 많이 적지만 여전히 테스트 시나리오의 완성도나 구현, 데이터의 범위등 많은게 부족하다.
테스트 코드를 작성하는게 어지간한 과제 한개 끝낼만큼 시간을 투자했는데도 시행착오가 많고 Mocking등에 대한 처리도 노하우가 부족해서 제대로 처리가 안되는 상황이 많아 계속 경험을 쌓아올려야 할 것 같다.
AWS의 전반적인 강의고 구체적인 내용이 많지는 않았다. 가입부터 시작해서 소개등이 있는데 AWS의 사용 기업으로 내가 옛날에 일하던 기업 이름이 나오니까 조금 신기했다.
서울에도 1개의 AWS Region이 있고 4개의 AZ가 존재한다고 한다. AZ는 데이터 센터를 의미한다.
2016년 당시에 일할때 AWS 서울 리전이 나온다고 살짝 핫이슈였는데 실제로 어떻게 쓰고 있었는지는 기억이 안난다.
AZ의 용도는 여러 AZ에 어플리케이션을 배포해서 하나의 AZ에 문제가 생겨도 서비스의 이용을 보장해주는 방식이다.
기본적으로 사용자와 가까울 수록 빠르지만 모든 Region이 AWS의 모든 서비스를 제공하는건 아니라서 특정 AWS 서비스를 사용해야 한다면 다른 Region을 써야할 수도 있다고 한다.
AWS의 Access 관련 인증/인가 서비스로 User, Group을 구분해서 정책, 권한을 설정한다.
최초 회원가입시 만든 Root account는 사용과 공유되지 않아야 한다고 한다. 권한 때문에 사용도 추천하지 않는건 처음 알았다. 현재 계정의 주인인 나 자신도 User를 별도로 설정하는 작업을 진행했다.
권한을 조정할 때는 무조건 권한을 최소한으로 설정하는 최소한의 권한 원칙(least privilege principle) 을 따른다고 한다.
가장 중요한 예산 설정이 있는데 Free tier로 생성했지만 진짜 무료로 쓸 수는 없고 사용량에 따라 언제든 과금될 수 있기 때문에
Billing에서 Simple로 설정해Zero-Spend Budget
을 생성해 $1를 예산으로 두고 $0.01을 초과하면 바로 메일 알림이 날라오게 설정했다.
Elastic Search에서만 듣던 Elastic이 EC2에도 쓰인줄은 몰랐다. 대충 해석하면 탄력적인 클라우드 컴퓨팅이라는 뜻으로 유동적으로 사용량에 따라 조절이 가능한 Cloud computer인 느낌이다.
EC2는 IaaS 방식으로 서비스를 제공해주고 가상 서버 인스턴스를 AZ의 하드웨어에 생성해서 제공받을 수 있다. 가상 서버기 때문에 서버의 스케일 업 또는 다운이 쉽고 인스턴스의 추가 제거도 쉽다.
하드웨어 인프라를 인터넷을 통해 제공하는 서비스를 의미한다.
IaaS는 가상화된 하드웨어, 스토리지, 네트워크, 운영체제 등을 제공하며 이를 이용해 사용자가 업로드한 애플리케이션을 실행할 수 있다.
예) AWS EC2, Microsoft Azure, Google Compute Engine
애플리케이션을 개발, 실행, 관리하기 위한 플랫폼을 인터넷을 통해 제공하는 서비스를 의미한다.
PaaS는 IaaS에서 제공하는 하드웨어 인프라와 미들웨어를 이용하여 개발, 배포, 운영 등에 필요한 환경을 제공한다.
예) AWS Elastic Beanstalk, Heroku, Google App Engine
클라우드 제공업체 서버에서 실행되는 완전한 애플리케이션을 제공해주는 서비스를 의미한다.
사용자는 애플리케이션에 대한 제한된 제어권을 가지며 클라우드 제공업체가 모든 인프라, 운영 체제, 백엔드 애플리케이션 및 데이터 관리를 담당한다.
예) 구글 드라이브, 마이크로소프트 오피스 365, Salesforce
인스턴스 유형: CPU, 메모리, 스토리지, 네트워크 리소스를 결정하며, 특정 작업에 적합한 유형을 선택할 수 있다.
EC2의 인스턴스는 다양한 형태로 특정 작업에 최적화된 인스턴스 형태도 있고 범용의 형태도 있어서 필요한 만큼 잘 선택해서 생성하면 된다.
나는 범용중에서 Free tier에서 주로 쓰는 t2.micro를 쓰게된다.
운영 체제: Amazon Linux, Ubuntu, Windows 등 다양한 운영 체제를 지원한다.
스토리지 옵션: 인스턴스 스토어, Amazon EBS, Amazon S3 등 다양한 스토리지를 선택할 수 있다.
보안 그룹: 인바운드 및 아웃바운드 트래픽을 제어하는 방화벽 규칙을 설정한다.
키 페어: SSH를 통한 EC2 인스턴스 액세스를 위해 키 페어를 사용한다.
탄력적 IP 주소: 고정 IP 주소를 할당하여 EC2 인스턴스 재시작 시에도 IP를 유지한다.
사용 가능한 영역: 가용 영역 내에서 인스턴스를 실행하여 장애 대응 및 고가용성을 보장한다.
잠깐 강의를 따라하면서 t2.micro instance를 실행하고 nginx 실행해서 접근 가능한것까지 테스트를 해봤는데 Inbound, Outbound에 대한 설정을 중간에 진행해야 했다. 그래서 강의에서 추가로 정리했다.