데이터베이스에 null값을 활용해도 될까?

Alex·2025년 2월 11일
0

Plaything

목록 보기
103/118

신고하기 기능을 만들어야 하는데

신고 타입을 'A', 'B', 'C'로 선택하거나 'ETC'를 선택해서 이유를 적게끔 해달라는
요구사항을 받았다.

이렇게 되면
ETC에만 내용을 따로 저장해야 하는 상황이 생긴다.

처음 생각했던 구조는
모든 신고마다 reason이라는 칼럼을 두고서, ETC가 아니면 null로 저장하는 구조를 생각했다.

하지만, DB에서 null은 '값이 없음', '아직 값이 정해지지 않음'이라는 뜻이 있다는 내용을 예전에 본 적이 있다.

그래서, 이걸 어떻게 해야할지 조사를 했다.

NULL사용을 왜 조심해야 하는데?

이 경우와 딱 맞는다고 하긴 어렵지만 사칙연산을 생각해보자.

3+A -> 이 값이 3NULL이 되버리는 괴상한 상황이 발생할 수도 있다.
요지는 개발자가 예측할 수 없는 상황이 생겨버릴 수 있다는 것이다.

숫자와 null간에 비교도 계산이 정확하게 되지 않는다고 한다. 집계 함수도 마찬가지다.

DB를 조회하는 과정에서 '컨트롤할 수 없는 이상한 케이스'들이 생기는 경우를 고려하면
NULL 사용을 조심해야 함은 분명해보인다.

애플리케이션에서 처리해야할 복잡성이 늘어나기도 한다. 만약에 null 처리를 계속 해줘야 하기 때문이다.

DB 설계 관점에서는 어떤데?

Nulls are negatively viewed from the perspective of database normalization. The idea being that if a value can be nothing, then you really should split that out into another sparse table such that you don't require rows for items which have no value.

애초에 null값들을 갖지 않게끔 테이블을 분리해야 한다는 의견이 있다. 물론, 성능상의 목적으로 join을 피하기 위해서 null을 허용하는 게 useful한 상황도 있다.

null 데이터를 보는 사람 입장에서는 그 데이터의 의미를 두고 혼란스러울 수도 있다.

null은 아래처럼 해석될 수 있기 때문이다.

  • 값이 없다.
  • 아직 값이 정해지지 않았다.
  • 아직 값이 입력되지 않았다.
  • Empty string이다.(""과 null을 구분하지 못하는 db의 경우)
  • null과 관련된 비즈니스적인 의미
  • 에러가 발생해서 null값이 있으면 안되는데, null값이 들어가버림(not null이 아닌 경우일 것이다)

이런 생각도 해볼 수 있다. 어떤 숫자를 넣어야 할지 모르는 경우라면 무슨 수를 디폴트 값으로 넣어야 할까? 0? 하지만, 0 자체가 의미있는 비즈니스 로직이 있을 수 있다. String 타입이면? null을 써야 할까 empty string을 써야할까? 이런 경우에 활용할 수 있는 게 null이다.

다만, 아래와 같은 의견도 있다.

In many cases where an attribute is unknown, it makes little sense to join to another table for each and every column which could allow NULLs in a simpler design. The overhead of joins, the space requirements for theprimary keys make little sense in the real world.

NULL값을 피하려고 계속 정규화를 하면 불필요한 Join이 늘어나 성능적으로 이슈가 생길 수 있다.

중요한 건 컨벤션이다

For instance, a column like NUM_CHILDREN - what do you do if you don't know the answer - it should be NULL. In my mind, there is no other best option for this column's design (even if you have a flag to determine whether the NUM_CHILDREN column is valid, you still have to have a value in this column).

On the other hand, if you don't allow NULLs and have special reserved values for certain cases (instead of flags), like -1 for number of children when it is really unknown, you have to address these in a similar way, in terms of conventions, documentation, etc.

So, ultimately, the issues have to be addressed with conventions, documentation and consistency.

결국, 일관성과 컨벤션을 지키는 게 더 중요하다는 입장도 있었다.

이번에 nullable에 대해서 새롭게 생각해본 점은
누구나 값을 가지는 게 아니라면 nullable을 해서는 안된다는 점이다.

가령, 요즘은 대부분의 사람들이 신용카드를 갖고 있지만, 그렇지 않은 사람도 있다. 이런 경우라면 신용카드 정보를 nullable로 해야할 수도 있는 것이다.

그럼 어떻게 적용해야할까?

우선, 비즈니스 로직을 좀더 살펴보자.
ETC를 제외한 3개의 유형은 신고의 구체적인 details를 저장하지 않는다.

그렇다는 건, 70%이상의 데이터가 details(혹은 descrption)을 null로 갖는다는 말이다.

null 데이터는 용량을 차지할까?

While a NULL itself does not require any storage space, NDB reserves 4 bytes per row if the table definition contains any columns allowing NULL, up to 32 NULL columns. (If an NDB Cluster table is defined with more than 32 NULL columns up to 64 NULL columns, then 8 bytes per row are reserved.)

MySQL 공식문서에 따르면 4바이트의 공간을 예약해두긴 한다.

물론, DB 설계를 용량 관점에서만 하는 건 좋지 않다고 한다.

The basic rule is, design your schema based on the properties of the data, not on the storage impact. Fix things only if they turn out to be a problem.(StackOverflow 댓글중)

우선, 조회 패턴을 고려해보자.
사용자별로 모든 신고내역이 조회돼야 한다는 요구사항이 있다.

이런 경우라면 굳이 join을 할 필요 없이
단일 테이블로 가져가면서, 나머지 값들은 null로 저장해도 될듯하다. (어차피 클라이언트로 응답을 null로 보내야하기도 한다)

DB설계 관점에서는 그렇게 좋은 건 아닐 수도 있지만
StackOverflow를 보니 그렇다고 무조건 정규화를 하진 않는 것 같다. trade-off를 고려해서 설계해야하는 것 같다.

profile
답을 찾기 위해서 노력하는 사람

0개의 댓글