오늘은 제가 생각하는 데이터베이스 테이블 구성에 관해 알아두면 좋을 것들에 대해 설명해 보겠습니다.
업계 전문가 분들이 보시기에는 많이 부족하겠지만, 시니어의 도움 없이 혼자 데이터베이스 설계를 해야 하는 주니어 입장에서는 도움이 될 수 있을것 같습니다.
id 속성 사용
테이블에는 id라는 이름으로 정수형태의 인공키(surrogate key)를 기본키로 사용하는 것이 좋습니다. 테이블의 속성 중 하나를 골라 기본키로 설정할 수도 있겠지만 인공키를 사용하면
- 모든 테이블이 동일한 형태의 기본키를 사용하므로 가독성이 향상됩니다. (모든 테이블의 기본키 속성 이름은 id라는 것이 보장됨)
- 추후 다른 속성으로 기본키가 변경될 경우, 변경 전 기본키를 참조하던 다른 테이블들의 외래키 설정을 수정할 필요가 없습니다.
- 대부분의 RDBMS에서는 기본키에 자동으로 인덱스 설정이 적용됩니다. 기본키로 데이터 조회 시 인덱스 활용에 가장 효율적인 정수 타입이 사용될 것이 보장됩니다.
uuid 속성 사용
테이블 Join 시에만 활용되는 게 아니라 직접적으로 사용자의 질의의 대상이 되는 테이블이라면 uuid를 갖는 것이 좋습니다. 사용자가 데이터 질의 시 id 값을 알게 되면
- id 값으로 정수를 사용할 경우, 사용자에게 해당 데이터의 순번에 관한 정보를 제공하는 셈이 됩니다
- id 값으로 정수를 사용할 경우, 악의적인 사용자가 여러 리소스를 대상으로 실행하는 공격의 대상이 되기 쉽습니다. (id값을 1씩 늘려가며 다른 데이터들에 접근하여 작업할 수 있으므로 소프트웨어를 통한 공격에 취약)
created_at, updated_at 속성
테이블마다 created_at, updated_at 속성을 두고 데이터의 생성과 업데이트 시 해당 속성들에 현재 시간값을 세팅해주도록 한다면 데이터의 생성과 마지막 수정일시를 추적할 수 있습니다.
Soft Delete 방식의 삭제가 필요한 테이블의 경우 deleted_at 속성을 사용
실수로 삭제한 데이터를 복구할 수 있도록 하거나, 삭제 후 일정기간 데이터를 보관해야 하는 법적 요구사항이 있다면 deleted_at 속성을 사용하여 Soft Delete를 구현하는 것이 좋습니다.
Soft Delete는 테이블에서 데이터를 삭제하지 않고, 삭제된 상태로 표시하여 "삭제했다 치는" 방법입니다.
데이터 생성 시에는 deleted_at 속성에 null을 세팅하고, 삭제 시 현재시간을 세팅하여 삭제 여부와 삭제된 시간을 추적할 수 있습니다. 삭제된 데이터는 해당 테이블 안에서 유니크 키 제약조건에 영향을 미치지 않아야 하므로 (이메일 속성에 유니크 키 설정이 걸려있을 경우 삭제된 행의 이메일 값은 새로 생성될 데이터에 사용될 수 있어야 함) 삭제 시 이메일 값에 "_deleted_202503011400" 같은 Suffix를 붙여 수정하거나, PostgreSQL 같은 경우는 삭제되지 않은 레코드 (deleted_at IS NULL)에만 유니크 제약조건을 적용하는 Partial Index 기능을 사용하기도 합니다.
시간 데이터 저장 시 TIMESTAMP WITH TIMEZONE 타입과 UTC 사용
시간과 관련된 데이터를 저장할 때는 TIMESTAMP 보다 TIMESTAMP WITH TIMEZONE 타입을 사용하여 UTC(Coordinated Universal Time)를 기준으로 저장하는 것이 좋습니다.
UTC(Coordinated Universal Time, 협정 세계시)는 국제적으로 사용되는 표준 시간 기준입니다. 지구는 자전하기 때문에 지역마다 낮과 밤이 다릅니다. 경도 15° 차이마다 약 1시간의 시차가 발생하는데, 국제간 교류가 활발해지면서 모두가 일관되게 공유할 수 있는 표준 시간 체계의 필요성이 생겼고, 그 결과로 UTC가 탄생했습니다. 한국의 표준시인 KST(Korean Standard Time)는 UTC+9입니다. 즉, UTC보다 9시간 빠른 시간대를 사용합니다. UTC 기준 오전 9시는 KST 기준(한국) 오후 6시에 해당합니다.
서버 애플리케이션(예를 들어 Spring Boot)과 DBMS 서버는 일반적으로 OS의 TIMEZONE을 자동으로 따라가기에 시간 저장 시 UTC를 기준으로 저장하지 않으면 다른 TIMEZONE을 가진 OS에 배포됐을 때 시간 값 해석이 달라져 오류가 발생할 수 있기 때문입니다.
Check 제약조건 사용
성별, 국가 등의 속성처럼 사용되는 문자열이 정해져 있는 속성들은 아래와 같이 Check 제약조건을 통해 사용 가능한 문자열들을 지정해 주면 데이터 무결성에 도움이 됩니다. 그리고 이러한 값들에 대문자를 사용하면 가독성에 더욱 좋습니다.
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
gender ENUM('MALE', 'FEMALE', 'OTHER') NOT NULL
);
BOOLEAN 타입 사용
TRUE 혹은 FALSE로 표현 가능한 데이터를 저장할 경우에는 BOOLEAN 타입을 사용하는 것이 좋습니다.
“TRUE” 혹은 “FALSE”를 문자열로 저장할 수도 있지만 BOOLEAN타입을 사용하면 간단하게 무결성을 보장할 수 있습니다. 그리고 BOOLEAN 타입을 사용하는 속성의 이름에는 "is_"라는 Prefix를 붙여주면 가독성에 더욱 좋습니다.