News Feed

개발자를 위한 데이터베이스 설계 팁

컨텐츠 정보

  • 조회 386

본문

소프트웨어에서 모든 것은 결국 부패한다는 것은 보편적인 진리이다. 상황은 서서히 나빠진다. 처음에는 멋지고 깔끔했던 것이 어느 순간 “대체 무슨 생각으로 만든 거야?”라는 소리가 나오는 지경에 이르게 된다.

하지만 그렇다고 가만히 앉아서 그런 일이 일어나도록 내버려 둘 수는 없다. 고양이가 양말을 신었을 때처럼, 우리는 코드 부패에 맞서 싸워야 한다.

이 중에서도 충분한 주목을 받지 못하는 코드 부패 영역이 바로 데이터베이스 설계다. 데이터베이스를 설계할 때 적용해야 할 크고 중요한 규칙들이 분명히 존재하지만, 여기서 다루려는 내용은 그런 규칙이 아니다. 여기서는 많은 사람이 미처 의식하지 못할 수 있는 데이터베이스 설계 팁에 집중하려 한다. 이들을 작고 중요한 규칙이라고 부르자.

다만 큰 규칙 중 하나에 대해서는 반드시 짚고 넘어가야겠다. 데이터베이스를 설계하면서 데이터베이스 정규화에 대해 모른다면, 지금 당장 멈추고 반드시 먼저 그것부터 학습해야 한다. 데이터베이스 설계를 고려하기에 앞서, 제2정규형과 제3정규형을 반드시 이해하고 있어야 하며, 가능하다면 제3정규형을 충족하는 스키마를 지향해야 한다.

모든 테이블에는 ID 필드를 포함하라

이 부분은 논쟁의 여지가 있을 수 있지만, 필자는 데이터베이스의 모든 테이블이 ID라는 이름의 기본 키를 가져야 한다고 믿는다. CustomerIDOrderID가 아니라, 그냥 ID라고 명명하는 것이다.

이 필드는 자동 증가하는 정수 값이어야 하며, 아주 특별한 경우(예를 들어 분산 시스템과 같은 경우)에만 UUID를 고려해야 한다. 당연히 이 ID 필드에는 인덱스를 설정해야 한다.

다중 필드 키가 필요한 경우는 다대다 관계를 위한 교차 참조 테이블과 같이 극히 드문 예외 상황에 한정되어야 한다.

테이블이나 필드 이름에 공백을 포함하지 마라

테이블이나 필드 이름에 공백을 포함하는 것이 좋은 생각이라고 여긴 사람에게는 영원한 경멸과 수치심을 부여해야 마땅하다. 공백이 있는 이름을 사용하면 항상 따옴표를 사용해야 하며, 따옴표를 잊는 일이 자주 발생하고, 쿼리를 작성할 때마다 “공백이 들어갔던가?” 하고 혼란스러워진다. 이는 단순히 번거롭고, 작업 흐름을 방해한다. 공백을 사용하지 않으면 이러한 문제 자체가 사라진다.

그리고 간곡히 부탁하건대, 밑줄(underscore)도 사용하지 말라. names_like_this 같은 이름을 타이핑하는 것은 정말 고통스럽다. 필자의 새끼손가락은 이 글을 쓰는 것만으로도 산재 보상 청구를 하고 싶어질 정도이다.

테이블 이름은 복수형으로 지정하라

이 또한 오래된 논쟁이지만, 필자는 테이블이 단일 객체가 아닌 다수의 객체를 나타낸다고 확신한다. 따라서 테이블 이름은 항상 복수형으로 지정해야 한다. 예를 들어 Customers가 맞고, Customer는 아니다.

이렇게 하면 Orders 같은 단어를 볼 때 그것이 테이블을 의미한다는 것을 즉시 알아차릴 수 있다. 반면 Order라고 테이블 이름을 지정하면, “지금 테이블을 말하는 건가? 아니면 테이블의 한 행(row)을 말하는 건가?”라는 모호성이 생기게 된다.

이 주제는 오랜 시간 동안 논쟁의 대상이었다. 필자는 복수형 테이블 이름을 강력히 선호한다. 어떤 방식을 택하든, 중요한 것은 일관성을 유지하는 것이다.

외래 키는 명확하게 명명하라

앞서 언급한 ID 필드 규칙은 여기서도 중요한 역할을 한다. 예를 들어 Orders 테이블에 고객을 참조하는 외래 키가 있다면, 그 필드 이름은 CustomerID여야 한다.

ID라는 명명 규칙을 일관되게 적용하면, 해당 필드는 항상 그 엔터티를 참조하는 외래 키라는 것을 누구나 즉시 이해할 수 있다. 이러한 명명 방식을 스키마 전체에 걸쳐 일관되게 적용해야 한다.

쿼리하는 필드에는 반드시 인덱스를 설정하라

WHERE, JOIN, ORDER BY 절에 등장하는 모든 필드에는 무조건 인덱스를 생성하라. 이를 철저히 지키면, 미래에 발생할 수 있는 많은 성능 문제를 피할 수 있다.

물론 예외 상황도 존재할 수 있지만, 인덱스가 과다하다는 점이 확인되어 제거하는 경우가 되어야지, 아예 인덱스를 만들지 않는 방향으로 접근해서는 안 된다. 기본적으로는 “인덱스가 필요하다”는 전제로 시작하고, 쿼리 분석기가 이를 불필요하다고 판단할 경우에만 제거하라.

참조 무결성은 필수

테이블 간 관계가 일관되게 유지되도록 하고, 데이터베이스에 고아 레코드(orphaned record)가 발생하지 않도록 하는 것은 데이터 무결성을 유지하는 데 있어 핵심 요소이다.

모든 현대 관계형 데이터베이스는 참조 무결성 기능을 제공한다. 이 기능을 초기 설계부터 적극적으로 활용하고 강제해야 하며, 코드에서 관계를 수동으로 유지하려 해서는 안 된다. 데이터베이스 자체가 이 기능을 제공하므로, 제대로 활용해야 한다.

SQL을 애플리케이션 코드에 직접 삽입하지 마라

혹시라도 “이번 한 번만”이라는 생각으로 SQL을 코드에 직접 삽입하는 일이 있다면, 반드시 후회하게 될 것이다. 문제는 “이번 한 번만”으로 끝나지 않는다는 것이다.

SQL을 코드에 직접 삽입하면, 코드가 데이터베이스와 강하게 결합되며, 이는 나중에 스파게티 코드로 이어지게 된다. SQL은 데이터베이스가 처리할 일이다. 애플리케이션 코드는 그 로직과 분리되어야 한다.

만약 코드에서 SQL을 사용해야 한다면, SQL을 코드 외부의 파일에 따로 관리하라. 이 파일은 코드에서 임베드하거나 별도로 불러올 수 있으며, 코드 로직을 변경하지 않고도 SQL 로직을 업데이트할 수 있도록 구성해야 한다.

추가적인 조언

일반적인 원칙은 다음과 같다. 데이터베이스가 할 수 있는 일은 데이터베이스에 맡겨라. 데이터베이스는 데이터를 다루는 데 있어 사용자보다 약 453.7배 더 뛰어나다. 그러니 그들의 역할을 가로채려 하지 마라.

필드 이름을 1, 2, 3으로 끝내고 싶은 유혹이 들 경우, 반드시 정규화에 대해 다시 학습하라.

컬럼에는 반드시 적절한 데이터 타입을 지정하라. 불리언 값을 숫자로 저장하거나, 날짜를 문자열로 저장하는 일은 없어야 한다.

모든 테이블에 CreatedAtUpdatedAt 타임스탬프 필드를 추가하는 것을 강력히 고려하라. 이 필드는 나중에 놀랄 만큼 자주 유용하게 쓰인다. 트리거를 통해 자동화하면 이 필드는 더욱 유용하고 부담 없이 관리할 수 있다.

파라미터 기반 저장 프로시저는 최고의 동반자이다. 가능한 자주 활용하라.

쿼리 분석기는 어떤 방식으로 데이터를 쿼리해야 하는지에 대해 사용자보다 수십 배 더 정확한 판단을 내릴 수 있다.

불리언 값은 특히 주의해야 한다. NULL이 존재할 경우, 해당 값은 참도 거짓도 아닌 양자 상태가 된다. 쿼리를 실행할 때까지 해당 필드가 어떤 의미인지 명확하지 않다. 컨텍스트 내에서 NULL의 의미가 명확하지 않다면, 불리언 필드는 사용하지 말아야 한다.

상태 값을 문자열로 저장해서는 안 되며, 열거형 값(enumeration)을 사용해야 한다. 이렇게 하면 잘못된 상태 값이 입력되는 일을 방지할 수 있다. 예를 들어, 누군가 실수로 status = 'bananna'라고 입력했을 때 시스템에 오류가 발생하지 않도록 해야 한다.

결론

여기까지 다양한 지켜야 할 사항(Do)과 지양해야 할 사항(Don’t)을 설명했다. 가장 중요한 핵심은 다음과 같다. 자신만의 규칙 체계를 정하고 이를 엄격하게 적용하라. 그렇게 오늘부터 실행에 옮긴다면, 미래에 그 결정을 진심으로 감사하게 될 것이다.
dl-itworldkorea@foundryco.com

관련자료

댓글 0
등록된 댓글이 없습니다.
Member Rank