본문 바로가기
Architecture

is_left vs left_at vs left_status

by 향로 (기억보단 기록을) 2021. 6. 3.
반응형

트위터를 보다가 재미난 포스팅을 하나 발견했다.

전체적인 글의 주장은 다음과 같다.

  • 소프트웨어 개발에서는 상황에 의존하는게 좋은 설계이지, 어떤 상황에도 좋은 기준이라는건 있을 수 없다고 생각한다.
    • 다만, boolean (is_deleted) 대신 timestamp (deleted_at) 를 저장하는 것은 상황에 관계 없이 확실하게 좋은 설계라고 생각한다.
  • 일반적으로 어떤 flag 값이 필요하다고 하면 그 flag가 언제 변경되었냐도 거의 필수적으로 필요하게 된다.
    • 이미 boolean으로 결정해서 사용할 경우 이 시간을 찾아 내기 위한 마이그레이션 비용이 생각보다 많이 필요하고, 그때 가서 변경하기엔 많이 늦었다.
  • 만약 timestamp (deleted_at)가 필요하지 않은 경우에 boolean (is_deleted) 대신에 timestamp (deleted_at)를 사용했다고 해도 문제 될 것이 없다.
    • is null, is not nulltrue, false와 동일하게 판단이 가능하기 때문이다.

이 글을 작성하신 분은 이미 업계에서 15년정도 일하신 분이시기 때문에 그 경험에 기반해서 소개하는 내용이라 신빙성이 충분하다고 생각한다.

다만, 내 생각은 이와는 조금 다른데, 이 부분 역시 상황에 따라 다를 수 있다고 보기 때문이다.

나 같은 경우 이럴때 다음을 고민한다.

(1) 애플리케이션 (Java, TS 등)의 Enum과 RDBMS의 Varchar를 사용해서 Flag를 꼭 2개로 제한하지 않도록 고민한다.

  • RDBMS의 Enum은 절대 사용하면 안된다.

(2) 설계상 시간이 필요하다면 내역 테이블 (audit)을 별도로 관리해서 특정 요청시에 내역 테이블을 별도 조회해서 보여주는 형태를 가져가도록 한다.

(3) 본인인증 / 성인인증 같이 단발성 상태의 경우에는 booleantimestamp기능에 따라 고민한다.

  • 거의 한번 정도의 상태만 변경 가능한 기능임에도 booleantimestamp를 고민하는 이유는 성능 때문이다.
  • 이는 아래에서 좀 더 이야기한다.

1. Enum을 최우선으로 고민

(1)의 경우는 실제로 발생한 사례이기도 한데, 예를 들어 계정 탈퇴 기능을 구현한다고 보자.
사용자 테이블에 탈퇴 상태를 넣는다고 하면 위에서 언급한대로 true/false 혹은 null/2021-05-03 11:00:00.9999 와 같이 (탈퇴 yes / no) 상태값을 관리하면 어떻게 될까?

  • 회사에서는 즉시 탈퇴 기능에 추가로 탈퇴를 별도의 처리 기간을 두어 사용자가 탈퇴 번복을 가능하게 해달라는 변경 요청을 준다.
  • 이럴 경우 탈퇴 대기 라는 상태가 필요하게 된다.
    • boolean이나 timestamp의 경우에는 탈퇴요청상태 혹은 탈퇴요청시간 으로 추가 컬럼이 필요하게 된다.

이렇게 Yes / No 외에 다른 상태가 빈번하게 일어나는 경우에 boolean , timestamp가 모두 마이그레이션이 되어야하거나 추가 컬럼이 필요하게 된다.

2. timestamp보다는 Enum을 선호하는 이유

내가 timestmap 보다는 Enum 을 선호하는 이유는 is not null 때문이다.
RDBMS의 버전과 종류에 따라 다르지만, is not null인덱스를 타지 못할때가 많다.

무언가를 부정하는 모든 조건은 기본적으로 데이터베이스가 최적화하기가 쉽지 않다.
여기서의 부정 조건이란, IS NOT NULL 또는 col1! = 'something' 등이다.

그 이유는 일치하지 않는 모든 row를 찾도록 데이터베이스에 지시하면, DBMS가 이를 수행 할 수 있는 유일한 방법은 모든 row를 검토하고 일치하지 않는 것들은 버리는 것뿐이다.

만약 아직 탈퇴하지 않는 계정을 조회하는 기능을 구현한다고 하면, 이 기능에서 timestamp만 유일하게 인덱스를 못탈 수도 있는 쿼리가 발생한다.

where is_left = false;  
where left_at is not null; // 인덱스를 사용이 안될 수 있음
where left_status = 'ACTIVE';
  • boolean, varchar (애플리케이션에서는 Enum) 는 모두 인덱스를 탄다.

그래서 우리쪽 API / Admin / Batch에서 구현해야할 기능 / 앞으로 추가될것 같은 기능들을 고려해서 인덱스가 필요할것 같다고 생각되면 timestamp 만으로 모든 상태를 관리하는 것은 꺼리게 된다.

3. timestamp를 고민할 때

반대로 booleanEnum 보다 timestamp가 성능이나 코드 구현에서 훨씬 더 뛰어날때도 있다.

이를테면, 매번 사용자에게 가장 최근 성인 인증한 시간을 보여줘야한다고 가정해보자.

이럴 경우엔 사용자 테이블에서 성인인증시간 컬럼을 가지고 있느냐, 인증 내역 테이블에서 관리하느냐에 따라 성능 차이가 발생한다.
후자의 경우에는 매번 사용자 테이블과 인증내역 테이블을 2번씩 조회하고 최신순 정렬을 해야하기 때문이다.

  • 물론 요청 파라미터에 사용자 ID가 있고, 인증 내역 테이블에서는 커버링 인덱스가 구현되어 있다면 큰 성능 차이가 발생하진 않는다.
  • 다만, 매번 모든 테이블에서 이렇게 할 것이냐의 효율성을 고민해볼 필요가 있다.

이럴 경우엔 당연하게도 booleanEnum 보다 timestamp이 훨씬 효율적이다.

마무리

무조건 A가 더 좋다라는 건 있을 수 없다.
위에서 언급한 상황에 따라 현 데이터베이스의 종류와 버전/성능/효율/확장가능성등을 모두 고려해서 데이터 타입을 결정해야만 한다.

반응형