한 DBMS에 종속적이지 않도록 엔티티 속성 개선하기

임현규·2023년 7월 5일
0

Meca project 개발 일지

목록 보기
24/27

기존 문제점

실제 만든 서비스를 이용하려고 하니 도커로 인스턴스 서버에 생성한 DB가 불안했다. 그 이유는 MySQL 이미지를 컨테이너로 띄우면 메모리를 700M 정도 잡아먹는데 여기에 nginx, application server까지 띄우니 1GB 사용을 뛰어 넘는다. 결국 메모리 스왑을 사용해야 하고, 서버가 느려지게 된다. 그 뿐만 아니라, DB의 데이터를 조금 더 안전하게 보관할 방법을 찾아야 하는데 이 때문에 DB를 클라우드 서비스를 활용해서 분리해보기로 했다.

보통 AWS RDS를 많이 사용하는데, 토이 프로젝트를 만드는 입장에서 프리티어가 아니였기 때문에 RDS는 부담스러웠다. 그래서 평생(?) 무료 버전인 Oracle Cloud의 Oracle 19c를 사용하기로 했다.

mysql -> oracle로 바꾸면서 테이블 내 타입이 문법 오류로 생성되지 않는 문제

JPA의 장점은 ORM을 활용해서 특정 DB에 종속되지 않고 유연하게 테이블 구조를 생성할 수 있다. 프로젝트가 MYSQL 8.0 Community -> Oracle 19c 로 변경됨에 따라 application.yml을 거기에 맞게 수정해줘야 하고 driver를 build.gradle에 추가해 주었다. 그래도 엔티티 생성시 오류가 발생했는데 다음과 같은 오류가 발생했다.

이러한 오류는 Oracle DB에서 스키마를 성공적으로 생성하지 못할 때 발생하는 에러이다.

왜 이런 문제가 발생했을까?

기존의 코드는 다음과 같다.

@Entity
@EntityListeners(AuditingEntityListener.class)
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Table(name = "card_history")
@Getter
public class CardHistory {

	@EmbeddedId
	@AttributeOverride(name = "uuid", column = @Column(name = "card_history_id", nullable = false, columnDefinition = "BINARY(16)"))
	private Id cardHistoryId;

	@Embedded
	@AttributeOverride(name = "uuid", column = @Column(name = "solved_user_id", nullable = false, columnDefinition = "BINARY(16)"))
	private Id solvedMemberId;

	@Embedded
	@AttributeOverride(name = "uuid", column = @Column(name = "card_id", nullable = false, columnDefinition = "BINARY(16)"))
	private Id cardId;

	@Embedded
	@AttributeOverride(name = "answer", column = @Column(name = "user_answer", nullable = false, length = 2000))
	private Answer userAnswer;

	@Embedded
	@AttributeOverride(name = "score", column = @Column(name = "score", nullable = false))
	private Score score;

	@Embedded
	private CardSnapShot cardSnapShot;

	private boolean isDeleted;

	@CreatedDate
	private LocalDateTime createdAt;

	public void delete() {
		isDeleted = true;
	}

	public void rollback() {
		isDeleted = false;
	}

}

@AttributeOverride를 활용에서 columnDefinition을 Binary(16)으로 지정하고 있는데 이것이 다른 DBMS에서 문제를 일으킬 가능성이 있다.

해당 Id의 내부는 UUID 타입으로 되어 있는데 UUID는 binary 형태로 변환해서 자동으로 DBMS 타입에 맞게 생성한다. 예를 들어 mysql의 경우에는 binary이고, oracle의 경우 raw로 변환한다. 그러나 BINARY(16)으로 columnDefinition을 이용해 고정했기 때문에 hibernate는 해당 엔티티 타입에 대해서도 지원하지 않는 BINARY(16) 으로 DDL를 생성한다. 이 때문에 오류가 나는 것이다.

문제 해결하기

@AttributeOverride를 다음과 같이 개선해주면 된다.

	@EmbeddedId
	@AttributeOverride(name = "uuid", column = @Column(name = "card_history_id", nullable = false, length = 16))
	private Id cardHistoryId;

length 16을 넣은 이유는 RPAD 문제 때문이다. 길이 16으로만 넣어주면 타입에 맞게 16길에 맞춰서 만들어준다. 모든 ID 타입에 대해서 columnDefinition을 제거해 주었더니 정상적으로 DDL를 생성했다.

columnDefinition 사용하지 말기

인터넷에서 보고 columnDefinition으로 Binary(16)과 같은 방식으로 설정했는데 굳이 그럴 필요가 없었다. 오히려 JPA 엔티티 설계할 때는 columnDefinition을 활용하지 않는 편이 좋다.

DBMS 타입에 종속적으로 설계하지 않으려면 해당 dialect를 상속해서 특정 타입에 대해서 어떻게 처리해줄지 처리하는 것이 더 효과적일 수 있다. dialect별로 어떻게 처리할지 미리 정의해두면 엔티티에 columnDefinition을 둘 필요가 없기 때문에 어떤 DBMS든 유연하게 처리 가능하다.

다행히도 내 프로젝트의 경우에는 ID 외에는 따로 특별하게 처리할 컬럼이 없었지만 미래를 대비해 dialect 상속을 활용해 특정 타입에 대해서 유연하게 처리하는 방법을 공부해봐야겠다.

profile
엘 프사이 콩그루

0개의 댓글