서비스 개발의 데이터베이스 모델링 관점

박상원·2023년 2월 14일
0

데이터베이스

목록 보기
1/1

데이터베이스, 서버 개발자라면 데이터베이스는 뗄레야 뗄 수 없는 관계다. 마치 서버의 입장에서 보면 누구나 가지고 있는 집의 서랍장같은 존재다. 데이터베이스는 데이터 저장의 목적, 스위치의 목적, 기록의 목적 등 다양한 용도로 사용 될 수 있다.

서버 개발을 6년간 하면서 어떤 사고방식의 변화가 있었을까?

최근에는 신사업을 전담하면서 데이터베이스 설계부터 전반적인 확장까지 고려하게 됐다. 데이터베이스의 스키마와 테이블을 분리하고, 어떤 데이터를 어떻게 담을지, 나중에 쿼리를 실행할때, JOIN을 하는게 좋은지, row를 가져와서 정립하는게 좋을지, plain text column 을 가지는 게 좋을지, 브릿지방식의 테이블 구조가 좋을지... 너무 고려할 건 많지만 하다보면 익숙해지고 그 상황에 맞게 사용하곤 한다.

모델링이란 위 과정을 통합하는 모든 과정을 말한다. 컴퓨터공학, 코딩을 배웠다면 데이터베이스라는 학문을 적어도 한 번은 배웠을 것이다. 필수전공이기도하고, 없으면 안되는 존재다. 가장 많이 해보는 도서관 대여 데이터베이스로 시작해보겠다.

우리는 데이터베이스를 설계할 때, 무엇을 먼저 생각해야할까? 도서관 도서대여 시스템을 만든다고 가정해보면 크게 세가지로 분리할 수 있다. 열람객, 도서, 대출이력 로 정할 수 있고, 익숙하게 User, Book, Loan 로 바꿔보자.

플로우

설계를 하기전에 무턱대고 진행하는건 되돌아가는 범위만 넓힐 뿐이다. 우리는 먼저 실제 생활에서 어떤식으로 대출이 이루어지는지 상세하게 생각해볼 필요가 있다. 여기서는 플로우라고 말했지만 통용되는 단어는 아니다. 나의 경우 실제 비즈니스를 이해하는 과정을 항상 거친다. 어떤방식으로 어플리케이션의 사용자가 데이터를 볼지, 사용자는 실제 유저가 아닌 관리자도 사용자라고 할 수 있으니 대여 관리하는 직원이 보는 관점도 생각해야한다. 그 사이 대여시스템을 구축하면서 발생할 수 있는 옵션은 무엇일까?

내가 대여를 하고자하는 방문자라고 생각해보자. 대여자는 책을 찾아야한다. 집에서 찾을 수 있고, 도서관에 와서 책을 찾을 수 있다. 이 과정을 검색 이라고 해본다. 검색후에는 집에서 찾았을 경우 예약을 할 수 있다. 이 과정을 예약 이라고 한다. 예약을 했거나, 도서관에서 원하는 책을 찾았다. 그다음엔 대여를 진행할 수 있다. 대여를 하는 과정에서 사용자의 정보를 입력받게 되고, 입력받은 값은 대여 기록에 쌓일 것이다. 이 과정을 대여 라고 한다. 대여를 한 후에는 반납을 하거나 연체가 될 수 있다. 이 과정을 반납연체 라고 한다. 그런 뒤 책은 다시 대여중인 상태에서 대여가능으로 변경될 것이다.

이렇게 간단한 비지니스 플로우를 고려해본다면 시작은 훌륭하다. 물론 빠진 것이 있을 수 있다. 사용자 등록과정과 탈퇴, 대여책의 손상으로 인한 구매, 교체 등 여러 케이스가 발생할 수 있다. 이런 케이스를 초기에 모두 담을 수 있을지도 모르지만, 실제 비지니스에서 이런 케이스를 단번에 챙기기란 오히려 느린 개발로인해 오픈조차 못하는 상황에 부딪힐 것이다.

이 과정을 이렇게 풀기 위해서는, 비지니스를 확실하게 이해해야하고, 다양한 팀원들과 토론이 필요할 수 있다.

생각의 깊이가 얕으면 데이터베이스는 간단하게 나올 수 밖에 없다. 물론 사용하는데 있어 테이블이 한두개라면 개발하기에는 편할지몰라도 사업을 확장하는데 있어서 무조건 어려움에 부딪힐 것이다. 초기 설계가 복잡해봤자이기 때문에 더 고민하고, 최적의 개발량을 설정하여 데이터베이스의 가설계를 하는게 중요하다.

모델링

이제 모델링의 중요함이 나타난다. 얼마나 테이블을 생성해야할까.

위에서 크게 나눈 세가지는 기본일 것이다.

User

id - bigint not null pk
phone - text not null 
name - text not null
age - text null
address - text null
created_at - datetime not null
updated_at - datetime not null

사용자는 간단하게 기본 정보만 구현했다. 이름, 폰은 개인정보로 암호화가 필요할 수 있고, 나이는 성인 도서의 대출을 방지하기 위해 넣을 수 있다. 주소는 동네 도서관의 경우, 지역주민을 타겟으로하여 대출이 가능한지 특정 조건이 될 수 있다. 이정도만 구현해보자

Book

id - bigint not null pk
name - text not null
author - text not null
publisher - text not null
genre - text null
status - enum('booking', 'borrow', 'lost') not null
created_at - datetime not null

책은 책의 기본정보만 가질 것이다. 이 내부에 책과 관련없는 예약일, 반납일, 누가 대여했는지 등 데이터를 담게 되면 Book 이라는 테이블은 혼잡해질 것이다. 우리는 여기서 단순하고 명확한 데이터베이스 설계가 중요하다. 데이터베이스가 복잡해지는 순간 데이터의 퀄리티와 유지보수성이 낮아진다.

이름을 Book 이라고 하면 정말 해당 테이블의 순수 기능만 포함한다. 여기서 status라는 컬럼은 고려해볼만 하다. 해당책의 상태값을 넣음으로서 Book의 상태값을 알 수 있어 데이터베이스 관리하는데 편리함이 있을 수 있다. 또한 없는 경우도 생각해 볼 수 있는데, 이는 Loan 이라는 테이블에서 관리하는 방법을 찾을 수 있다. 전자의 경우 Book 상태를 확실하게 체크할 수 있는 방법을 고려해야한다.

Loan

id - bigint not null pk
user_id - bigint not null
book_id - bigint not null
type - enum('booking', 'borrow', 'return') not null
created_at - datetime not null
expired_at - datetime

이는 대여상태를 관리한다. 대여가 진행중인지, 대여가 끝났는지, 예약된 상황인지 알 수 있다. 위에서 말했던 status는 이것으로도 충분히 커버할 수 있다. status를 찾을 때, status 없이 Loan 테이블의 book_id 를 통하여 where 절 조건을 넣어주고, created_at과 expried_at, 그리고 type 으로 대여상태를 체크할 수 있다.

이 세가지 테이블 만으로도 도서관 시스템은 만들어졌다.

이제 작업하면서 생각난 부분을 분리 할 계획이다.

publisher

id - bigint not null pk
publisher_code - text not null
name - text not null
created_at - datetime not null

이는 출판사 테이블이다. 출판사의 이름이 book에 있었는데, 그렇게 둬도 상관은 없지만 책 등록시 출판사를 관리하면 추후 검색과정에서 특정 출판사만 검색하는것의 관리가 쉬워진다. 예를 들어 민음사라는 출판사를 등록할때, 믿음사라고 오타가 난다면 출판사 도서중 몇개는 누락 되는 케이스가 있기 때문에 출판사를 별도로 관리해주면서 출판사 코드를 발급해준다던지 데이터관리할 수 있는 관리자 기능이 필요하다. 이렇게 되면 위 Book 테이블은 컬럼 하나가 변경된다.

Book
id - bigint not null pk
publisher_id - biging not null
name - text not null
author - text not null
genre - text null
status - enum('booking', 'borrow', 'lost') not null
created_at - datetime not null

publisher_id 가 변경됐다. 우리는 FK를 사용할 수 있고 사용하지 않을 수 있다. 두가지 관점이 있을텐데, 첫째 외래키를 이용한 JOIN의 활용. 두번째 외래키없이 각각의 테이블을 SELECT하여 코드레벨의 컨트롤이 있다. 어떤걸 사용하든 문제는 없지만 나의 경우 두번째 방법을 주로 사용한다. JOIN 의 경우 테이블의 사이즈가 커질 수록 데이터 처리도 문제고, JOIN이 한두개 늘어나면 시스템에 문제가 많을 수 있다. 하지만 아에 사용하지 않는것은 아니다. 데이터양이 무수히 많이지지않을 것 같다는 시스템, 비지니스 관점에서 그렇다면 JOIN을 하여 사용하는게 좋다. 굳이 외래키의 필요성을 주지 않는 것도 데이터베이스의 각각의 독립성을 지켜주면서 각 Index를 사용하여 튜닝이 가능하다. 외래키의 유무는 그 도메인 성격에 따라 달라질 수 있다는 의미다.

이런식으로 흐름을 통해 데이터베이스 모델링을 진행한다. 모델링의 기초는 테이블의 성격을 분리해야하며, 관리 체계를 기본적으로 잡아가야한다. 어느 스키마가 어울릴지, 테이블에 있으면 관리하면서 혼란이 있을 내용인지 판단해야한다.

관리

모델링에서 신경쓰는 부분 중 하나는 created_at라는 컬럼으로, 즉 시간 값이다. 로우의 생성시간을 의미하기도하며, 데이터의 관리 시점을 예측할 수 있기 때문에 모든 테이블 내 필수적으로 default expression 으로 CURRENT_TIMESTAMP 생성을 해둔다. 물론 데이터 관점에서 불필요한 데이터일 수 있지만 서비스가 운영되면서 수많은 테이블이 생성 될 수 있고, 그 데이터의 시점을 테이블의 시간에 의해 유추할 수 있는 장점이 있다. 늘 필수적으로, id or no, created_at 은 필수 컬럼으로 지정한다.

데이터의 관리는 늘 이루어져야한다. 설계 당시의 구조가 완벽한 것이 아니기 때문에, 추가 개발을 하면서도 꾸준하게 바뀌게 된다. 의도하지 않았던 컬럼과 그렇지 못한 의미로 사용되는 type 컬럼 등 문제가 서비스를 운영하면서도 발생하기 때문에, 가능하면 서비스 초기에 많은 고민을 하면서 테이블을 발전시켜나가야한다. 데이터가 많아지고 마이그레이션을 하거나, 분리하게 되면 오히려 유지보수 측면에서 어려움이 발생할 수 있다고 생각한다. 결론은 늘 꾸준히 개발된 제품을 분석하고 고민하면서 퀄리티 높은 데이터베이스를 구축해야한다고 본다.

여기까지 작성된 주제는 학원, 학교에서 배우던 방식의 모델링과는 비슷하면서도 다를 것이다. 주로 비지니스의 초점이 맞추어져 모델링이 진행되며, 서비스를 운영하면서 발생할 수 있는 특이 케이스들도 있다. 대부분의 공부를 통해 얻는 과정에서 모델링은 정형화 되어있을 것이고, 왜 그렇게 만드는지 고민을 하지 못할 수 있다. 실습을 통해 경험할 수 있지만, 약간의 비지니스 고민을 더하게 되면 더 좋은 퀄리티의 모델링이 나오지 않을까 생각해본다.

profile
BE Dev

0개의 댓글