이번 포스팅은 회사에서의 경험을 토대로... DB를 결정하는 과정에 대해 써보려 한다. 물론 내가 정답은 아니지만, 적어도 이러한 이유로 결정하려 하고, 최소한 이런 부분을 신경써야 함을 적어보려 한다.
발단
회사에서 기존에 있던 서비스의 api 로직을 수정해야 하는 상황이 있었다.
대충 설명하자면 db에서 데이터를 꺼냈을 때 저장되어 있는 값의 수치에 따라 다르게 반환해야 하는 것이였는데, 가져와야 하는 컬럼이 해당 db에는 존재하지 않았다.
예를 들어 해당 api에서 진단 결과에 대해 특정 score가 0~50과 51~100의 반환하는 방법이 다르다고 할 때, 기존 기능을 이해해보니, 회원의 진단을 user_diagnosis_result에 저장하고 있다고 하면 user_diagnosis_result에 id, user_id, result ... 등 여러 컬럼이 있을텐데, score라는 항목이 별도의 컬럼으로 존재하는 것이 아니라 result안에 json을 string타입으로 저장하는데, 여러 프로퍼티를 담고 있었다.
result라는 컬럼 안에 [{score: 100, detail: asdf}] 이런식으로 여러 프로퍼티가 들어있었다.
그래서 result 에서 결과를 가져와서 해당 객체 안에 있는 프로퍼티의 값을 구분하고 있었다.
일단 기존에 왜 그렇게 저장했는지에 대해서 고민해보자.
rdbms는 관계형 데이터베이스 즉, 이름부터 관계 지향적인 데이터베이스고, 해당 값을 지금처럼 가져와서 join하거나 특정 동작을 수행하려면 컬럼으로 구분해서 저장해야 할텐데, 억지로 하나의 값에 여러 뭉치를 넣는다니.
방금 말한 것처럼 여러 동작을 수행하는데도 제약이 생기고, 이건 넣을 때도, 빼낼 때도, 누군가는 stringify, parse하는 과정을 거쳐야 하는 소모값도 생긴다. 결론적으로는 rdbms 본래 장점을 무산시키는 것인데, 왜 그렇게 저장하고 있을까?
내가 짐작하기로는, api가 추가되면서 진단 결과에 대한 데이터가 처음 설계된 결과와 달라졌고, 별도의 컬럼으로 저장하기엔 계속해서 늘어나니까 처음엔 result에 결과값만 저장하다가, 점수를 저장할 일이 생겼고, 그 이외에도 세부적인 내용을 저장해야 하는데 구분은 해야겠고, result를 배열 안에 객체로 해서 여러 값을 저장하고 있지 않았을까 싶다.
예시
당연히 같은 기준대로 저장해야 하는데, 예를 들어서 user_detail로 user의 세부적인 사항을 기록하는데 00대학교 0학년 라고 해보자.
처음에 대학교명과 학년을 저장하다가, 추가적으로 user에 대해 기록해야 하는 것이 생겨서 컬럼을 추가한다고 해보자. 편입여부, 재학 및 졸업여부 등 여러가지가 있겠다. 그런데 이 정보들이 user_detail이 아니라 user라는 항목에 한꺼번에 들어있다고 가정해보자.
만약 대학교의 정보가 중요한 대학의 DB라고 해보자. user 테이블에 유저의 id, 이름, 패스워드, 이메일, 생년월일, 학교 정보가 있다가 누군가는 해외의 대학이라 학기 구분이 다르다든지, 취득한 학점이 일반적인 학점이 아니라 편입을 해서 기준이 다르다든지 하는 일관성이 깨지는 정보가 생기는 것이다. 그래서 머리를 굴려보면 exception이나 detail, etc 등 기타 정보를 입력하게 해볼까? 싶을 수도 있을 것 같다. 그런데 그러한 예외사항이 많아지는데 그것들을 가져올 때 나름대로 데이터가 많을 땐 똑같은 기준을 가진 예외사항이 있을테니 기타 정보라는 컬럼에 여러 기준을 같이 넣어버리고 필요할 때만 가져오려고 머리를 써볼 수는 있을 것 같다.
user의 id: 1 name: 김승환 username: prpn97 password: 12312j3h2k13h1 ...
user_detail: ~~~~
나름대로 그 입장을 이해해보면 이러한 과정을 거쳤지 않았을까? 생각했다.
하지만 대학교를 예시로 든 이유는, 예외사항이 생길 변수가 적다. user_detail이라고 추상적인 테이블이 아니라 정확히 user_univ 혹은 그러한 대학에 대한 정보를 넣겠다고 명시해두면 예외사항이라고 해도 예외사항마다 정말 완전히 새로운 내용이 생기지는 않을 것이다. 그래서 관계형 데이터베이스에 적합하다.
내가 지금 포스팅을 작성하는 계기는, 관계형 데이터베이스에 저장하기 적합하지 않은 데이터를 저장하고 있어서 생기는 문제이기 때문이다. 유저가 건강에 대해 문진표를 작성하고 체크한 항목들을 저장한다고 할 때, 진단마다 문항의 갯수도 다를 것이고 문항 중에는 기타 사항으로 직접 입력해야 하는 값이 생길 수도 있다.
1번 진단은 10문항이 있는데, 9,10이 주관식이고 2번 진단은 12문항이 있는데 1번이 주관식이라면 각각의 컬럼을 넣기가 어려워진다. 실제로 그래서 지금의 데이터는 choose라는 항목으로 입력값이 json으로 한번에 들어가 있는 상태였다.
무엇이 문제가 될까?
만약 기타정보로 하지 않고 그 기준을 세부적으로 다 나눠서 컬럼을 만들게 되면 대부분의 데이터에 해당 컬럼은 null로 들어가고 아주 극히 일부 정보만 값이 들어가게 되고, 또 필요한 데이터마다 더 컬럼을 기약없이 계속 생성한다고 생각하면 끔찍하다. 우리는 null이라고 빈 값이라고 생각할 수는 있지만, 저장공간 입장에서도 언제든 들어갈 수 있도록 그 빈 공간을 자리를 마련해줘야 하는 것이기 때문에 하나의 값이 들어갈 때마다 여유공간을 더 만들어놓고 쓰지 않는 꼴이 되어버린다.
그렇다면, 그렇게 빈 여유공간을 만들어주지 않으면 되니까 기타 사항으로 한꺼번에 넣으면 안되나? 뭐 관계형의 의도는 알겠는데, 한꺼번에 넣으면 안되나?
결론적으로는 굳이 필요하지 않은 작업들이 더 일어나게 된다. 시간복잡도가 길어진다.
일단 지금 내가 겪은 사례를 통해 생각해보자면, 컬럼이 나누어져 있어서 score라는 컬럼이 있다면 score만 확인하면 된다.
result안에 string으로 들어있게 되면 이걸 json으로 변환해서 각 값마다 score라는 프로퍼티를 먼저 찾아야 하고, 그리고 그 중에서 원하는 값을 추려내야 한다.
예를 들면, diagnosis라는 백화점에서 score라는 상점은 3층에 있다고 먼저 데스크에서 확인하면 처음부터 엘리베이터를 타고 3층에 가면 된다. 1,2층은 찾아볼 이유가 없다. 그런데 score라는 상점이 result라는 이름으로 지상 1~4층에 있고, 지하는 아니지만 지상 어딘가에 있다고 하면 전부 찾아봐야 하는 것이다. 심지어 하나의 값만 찾는게 아니라 전부 찾아야 한다면 우연치 않게 바로 찾아지는 것이 아니라.. 그만큼 더 많은 시간이 소요될 것이다.
그렇다고 score가 3층에 있다고 하고 싶어도 score가 늘 있는 상점이 아니라 이벤트성이라 가끔 생기는 상점이라 3층이라고 상시로 안내하긴 힘든 그런 상황이라고 하면, 어쩌면 이해는 된다. 그렇다고 백화점에서 1~4층 안에 있으니 찾아보라고 하지는 않는다. 정확히 몇층 어디에 있다고 짚어줄 것이다.
그렇다면 어떻게 저장하면 좋을까?
B동에는 할인매장만 있는데, 미리 칸을 만들어놓지 않고 할인하는 매장이 언제든지 들어와서 원하는 것을 팔 수 있는 것이다. 그것이 옷이라서 마네킹이 필요할 수도, 식당이라서 그릇이나 식자재가 필요할 수도 있지만 미리 준비하지 않는다. 그렇기 때문에 이 B동은 이름이 파격할인! 뭐든지 할인상점 이라면 원하는 물건을 사러 오는게 아니라 뭐가 할인하는지 살펴보면서 사고 겸사겸사 식사도 하는 그런 매장인 것이다.
적절한 예시가 맞는진 모르겠지만, 이러한 구조는 NoSQL인 것이다. 말그대로 정형화된 구조로 가져올 수가 없다. 마네킹이든 그릇이든 일정해야 어느 자리에 있는 마네킹의 옷을 가져올 수 있는데 구조가 정해져 있지 않기 때문에 SQL로 질의를 할 수가 없다. 그저 내가 원하는 컨셉은 '할인' 이였기 때문에 할인이라는 카테고리로 가져올 수 있게된다. 하지만 그 안의 세부적인 내용을 짚어주기엔 기준이 없는 것이다.
앞선 진단이라는 사례처럼 문항수도 일정하지 않고 문항마다 주관식이 있을 수도, 없을 수도 있고 또 언젠가는 진단에 이미지가 들어갈 수도 있다. 그럴 때 '진단'이라는 카테고리에 입력값을 그대로 넣어주고 필요할 때 해당 일자의 진단을 가져온다고 하면 그대로 값을 가져올 수 있을 것이다. 하지만 그 진단 안에 있는 세부적인 내용만 가져오기는 어려울 것이다.
결론
NoSQL은 결국 비정형 데이터 즉, 정형화되어있지 않은 데이터를 저장하는데 특화된 것이다. 저장방식, 각자의 장단점 등을 이 포스팅에서 설명하기엔 너무 길어지고, 이미 좋은 포스팅이 잘 나와있다. 중요한 것은 초반 DB설계다. 정확한 구분이 지어져 있고, 그 안의 세부적인 내용을 가져오기만 하면 되면 관계형 데이터베이스를 사용하면 되고, 분산된 여러 수정가능성이 높은 상황에서는 문서지향 데이터베이스인 NoSQL을 선택하면 될 것이다.
면접준비하면서 이러한 차이를 달달 외우다가 솔직히 그냥 Mysql 쓰고 잘 정리해서 저장하면 되는거 아닌가 이런식으로 무식하게 생각했는데 직접 마주치니까 확실히 이해가 됐다... 잘 준비해서 다음에는 그래서 이미 엎질러진 물 어떻게 잘 정리해서 비정형적인 데이터들을 mysql에 정리해볼지 포스팅해보려 한다.
'개발 > DB' 카테고리의 다른 글
[prisma] npx prisma db push 업데이트에 실패할 때 (0) | 2024.04.05 |
---|---|
[sequelize | mssql] Error: Please install tedious package manually (0) | 2024.02.22 |
[prisma] Transaction API error (0) | 2023.12.06 |
[prisma] 정의하지 않은 필드에서 생기는 오류.. 가 아니였다 ㅎㅎㅎ (0) | 2023.11.04 |
[sequelize] Cannot delete property 'meta' of [object Array] (0) | 2023.10.29 |