1. 데이터베이스와 DBMS
데이터베이스(database)의 사전적 정의는 ‘여러 사람이 공유하여 사용할 목적으로 체계화해 통합, 관리하는 데이터의 집합’이다. 하지만 이 사전적 정의는 다소 모호하게 느껴질 수 있다. 우리가 사용하는 학교 관련 웹사이트를 예로 들면 학생 데이터, 학과 데이터 등이 저장될 수 있을 것이고, 쇼핑몰 관련 웹사이트를 예로 들면 결제 데이터, 품목 데이터 등이 저장될 수 있을 것이다. 이러한 데이터의 집합이 바로 데이터베이스이다. 즉, 데이터베이스란 원하는 기능을 동작하기 위해 마땅히 저장해야 하는 정보의 집합을 말한다.
데이터베이스는 사용자에게 제공되는 프로그램, 특히 웹 서비스에 있어 심장과도 같다. 어떤 데이터가 저장되는지에 따라 웹 서비스의 정체성이 달라질 수 있고, 데이터가 저장되는 방식에 따라 웹 서비스의 성능이 달라질 수 있기 때문이다. 데이터베이스가 이토록 중요하다면 이를 제대로 관리하기 위한 수단이 필요할 것이다. 이 수단이 바로 데이터베이스 관리 시스템(Database Management System, DBMS)이다.
1.1. DBMS의 종류
DBMS는 크게 두 유형으로 구분할 수 있다.
1.
관계형 데이터베이스 관리 시스템(Relational Database Management System)
2.
NoSQL 데이터베이스 관리 시스템(NoSQL Database Management System)
대표적인 관계형 데이터베이스 관리 시스템으로는 MySQL, Oracle, PostrgreSQL, SQLite, MariaDB, Microsoft SQL Server 등이 있으며, NoSQL 데이터베이스 관리 시스템으로는 MongoDB, Redis 등이 있다.
그렇다면 우리가 보다 집중해서 알아 둬야 할 DBMS는 무엇일까? 다음의 DBMS 점유율을 살펴보자. 2024년 약 6만 명의 전문 개발자를 대상으로 진행한 설문조사의 결과이다. 수많은 개발자들이 주로 사용하고 있는 데이터베이스로 RDBMS를 답변한 것을 볼 수 있다. 따라서 우리는 RDBMS를 위주로 학습해보자.
답변 결과를 살펴보면 PostgreSQL의 점유율(51.9%)이 가장 높은 것으로 보이지만, 7위로 꼽힌 MariaDB(17.1%)는 2위인 MySQL(39.4%)의 오픈소스 버전에 해당하므로 점유율이 가장 높은 RDBMS는 사실상 MySQL이다. 따라서 우리는 RDBMS 중 개발자들의 답변 점유율이 높은 MySQL(버전 8.0)을 위주로 학습한다. 따라서 앞으로 데이터베이스 혹은 DBMS라고 언급되는 용어는 기본적으로 RDBMS를 지칭한다는 점을 참고해주길 바란다.
최근에는 RDBMS보다 유연한 형태로 대규모 데이터를 다루기에 용이하고 확장성이 좋은 NoSQL 또한 상승세를 이어가고 있다. MongoDB와 Redis로 대표되는 NoSQL DBMS에 대해서도 추후에 학습해보자.
1.2. 서버로서의 DBMS
DBMS는 여느 응용 프로그램과 다를 바 없는 응용 프로그램이다. 다만, DBMS는 사용자와 직접적으로 상호작용하기보다는 사용자(개발자)가 만든 프로그램과 상호작용하며 실행된다. 따라서 다음과 같은 그림으로 DBMS를 표현할 수 있다.
응용 프로그램들의 입장에서 바라본 DBMS는 마치 서버와 같다. 응용 프로그램이 DBMS를 이용하는 과정은 ‘네트워크’에서 학습한 클라이언트-서버 간의 동작과 유사하다. 주로 쓰리 웨이 핸드셰이크를 통한 TCP 연결을 맺으며, 때로는 데이터베이스에 접속하기 위한 인증이 필요하기도 하다.
네트워크에서 클라이언트가 서버에 요청을 보내듯, DBMS 클라이언트는 DBMS에 쿼리(query)를 보낸다. 이를 위해 DBMS는 데이터베이스를 다루기 위한 언어(데이터베이스 언어)를 제공한다. 사용자/응용 프로그램은 DBMS의 데이터베이스 언어를 통해 데이터베이스를 다룰 수 있게 되는 셈이다. 대표적인 데이터베이스 언어인 SQL(Structured Query Language)은 RDBMS에서 데이터를 조작하고 관리하기 위한 언어로, 이름 그대로 데이터베이스에 질의(Query)하기 위한 구조화된(Structured) 언어(Language)를 말한다.
CREATE TABLE employees {
employee_id INT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
department VARCHAR(50)
};
SQL
복사
‘employees’라는 새로운 테이블을 생성하는 SQL 코드
SELECT first_name, last_name
FROM employees
WHERE department = 'HR';
SQL
복사
‘employees’ 테이블에서 ‘HR’ 부서인 직원들의 이름을 조회하는 SQL 코드
SQL은 크게 네 종류로 분류된다.
1.
데이터 정의를 위한 DDL(Data Definition Language)
2.
데이터 조작을 위한 DML (Data Manipulation Language)
3.
데이터 제어를 위한 DCL (Data Control Language)
4.
트랙잭션 제어를 위한 TCL (Transaction Control Language)
•
DDL
명령 | 설명 |
CREATE | 데이터베이스 혹은 테이블, 뷰, 인덱스 등의 데이터베이스 객체 생성 |
ALTER | 데이터베이스 객체 갱신 |
DROP | 데이터베이스 객체 삭제 |
TRUNCATE | 테이블 구조를 유지한 채 모든 레코드 삭제 |
•
DML
명령 | 설명 |
SELECT | 테이블의 레코드 조회 |
INSERT | 테이블의 레코드 삽입 |
UPDATE | 테이블의 레코드 갱신 |
DELETE | 테이블의 레코드 삭제 |
•
DCL
명령 | 설명 |
COMMIT | 데이터베이스에 작업 반영 |
ROLLBACK | 작업 이전의 상태로 되돌림 |
SAVEPOINT | 롤백의 기준점 설정 |
•
TCL
명령 | 설명 |
GRANT | 사용자에게 권한 부여 |
REVOKE | 사용자로부터 권한 회수 |
2. 파일 대신 데이터베이스를 사용하는 이유
앞서 운영체제에서 우리는 파일 시스템에 대해 배웠다. 운영체제의 파일 시스템을 활용하면 데이터를 파일과 디렉터리의 형태로 관리할 수 있다고 했다. 그렇다면 왜 파일 시스템 대신 데이터베이스를 이용하는 것일까? 가령 학과와 학생에 대한 데이터를 다음과 같은 ‘학과.txt’와 ‘학생.txt’ 파일에 저장해서 관리하면 안될까? 이 질문에 대한 답은 DBMS가 제공하는 주요 기능과 직결된다.
학과 이름=컴퓨터과학, 학과 코드=123, 학생=강민철, 박성훈, ...,
학과 이름=경영학, 학과 코드=234, 학생=김철수, ...,
학과 이름=경제학, 학과 코드=456, 학생=장동호, ...,
SQL
복사
이름=강민철, 나이=27, 사는 곳=서울, 학과=컴퓨터과학, 학번=1234
이름=박성훈 나이=22, 사는 곳=부산, 학과=컴퓨터과학, 학번=2345
이름=김철수, 나이=-1, 사는 곳=대전, 학과=경영학, 학번=3456
이름=장동호, 나이=24, 사는 곳=대구, 학과=경제학, 학번=4567
SQL
복사
데이터를 단순 나열하여 파일에 저장할 경우의 한계점이 이 질문에 대한 답변이 될 것이다. 여러 가지 이유를 찾을 수 있겠지만, 대표적으로 다음 5가지가 있다.
1) 데이터 일관성 및 무결성 제공이 어렵다.
앞서 DBMS는 서버와 같다고 언급했다. 한 웹 서버에 요청을 보내는 클라이언트가 하나만 있는 경우가 거의 없듯, 데이터베이스를 한 명의 사용자 혹은 하나의 프로그램만 이용하는 경우는 거의 없다. 보통 여러 명의 사용자, 프로그램이 동시다발적으로 데이터베이스를 이용한다. 앞서 ‘운영체제’라는 과목에서 우리는 레이스 컨디션이라는 문제를 다뤘었다. 레이스 컨디션이 발생하면 데이터의 일관성이 훼손되기 쉽다. 물론 동기화 도구를 이용하면 되지만, 저장된 파일에 대한 모든 접근에 일일이 동기화 도구를 사용하는 것은 번거로운 일이다. 또 개발자가 파일에 명시된 데이터에 결함이 없음을(무결함을) 일일이 검사하기도 번거롭다. 다시 말해, 데이터의 무결성을 보장하기가 어렵다. 예를 들어, ‘학생.txt’ 파일에 ‘김철수’라는 학생의 나이가 잘못된 값 ‘-1’로 저장되어 있지만, 모든 데이터를 파일을 기반으로 저장할 경우 이러한 잘못된 값을 검출하기가 어렵다.
2) 불필요한 중복 저장이 많아진다.
다량의 데이터 관리에 있어 불필요한 중복 저장은 자칫 스노우볼이 되어 큰 저장 공간 낭비로 이어질 수 있다. 파일로 다량의 데이터를 관리할 경우, 불필요한 중복 저장이 발생하기가 쉽다. 가량 ‘학과.txt’와 ‘학생.txt’파일에서 ‘학과 이름=’과 ‘학과 코드=, 학생=, 이름=, 사는 곳=, 학과=, 학번=’은 모두 굳이 중복하여 저장할 필요가 없는 정보이다. 또 ‘학과.txt’와 ‘학생=’은 ‘학생’txt’의 ‘학생=’ 데이터와 중복된 저장이다.
3) 데이터 변경 시 연관 데이터 변경이 어렵다.
가령 ‘학과.txt’의 ‘학과 이름’이 ‘컴퓨터과학’에서 ‘컴퓨터공학’으로 변경되었다고 가정해보자. 그러면 ‘학생.txt’ 파일에서 학과가 ‘컴퓨터과학’인 모든 학생의 학과 이름을 ‘컴퓨터공학’으로 일일이 변경해야 할 것이다. 또 만약 ‘장동호’라는 학생이 경제학과에서 컴퓨터과학과로 전과한 경우를 가정해 보면 ‘학과.txt’와 ‘학생.txt’를 모두 하나하나 변경해야 한다.
4) 정교한 검색이 어렵다.
파일에서도 데이터 검색은 가능하지만, 많은 경우 파일 내 문자열 검색에 국한되는 경우가 많다. 가령 ‘나이가 25살 이상인 컴퓨터과학과인 서울 거주자’라는 식의 정교한 검색의 경우 파일만으로는 어렵다.
5) 백업 및 복구가 어렵다.
앞서 언급했듯 데이터베이스는 보통 여러 명의 사용자 및 프로그램이 동시다발적으로 이용하기 때문에 많은 데이터베이스에서 백업과 복구 기능을 제공한다. 하지만 단순 파일 입출력에서는 이러한 기능을 애초에 제공하지 않거나 데이터베이스에 비해 부족한 수준으로 지원한다. 이와 같은 이유로 다량의 데이터를 바탕으로 많은 사용자 및 프로그램에 제공해야 하는 경우, 단순 파일 입출력보다 데이터베이스가 효율적일 수 있다.
3. 데이터베이스의 저장 단위와 트랜잭션
데이터베이스 학습을 시작하려면 데이터베이스에 무엇이 어떻게 저장되는지, 데이터베이스의 연산에는 어떤 성질이 있는지 이해해야 한다. 이와 관련해 데이터베이스의 저장 단위인 엔티티와 스키마, 트랜잭션에 대해 알아보자.
3.1. 데이터베이스의 저장 단위
데이터베이스에는 다양한 속성을 가진 독립적 객체들이 저장될 수 있다. 이때 ‘독립적으로 존재할 수 있는 객체’를 엔티티(entity)라고 한다. 다른 말로는, ‘어떠한 특성을 가진 대상’이라면 모두 엔티티라고 할 수 있다. 예를 들어 다음과 같은 그림 속 하나 하나의 대상이 모두 데이터베이스에 저장 가능한 엔티티인 셈이다.
1. 구매자 ID가 dongho18인 남자 ‘장동호’
2. 제품 번호가 123, 제품 이름은 ‘맥북 13인치’
SQL
복사
속성(attribute)은 엔티티의 특성을 의미한다. 위에서 구매자 ID, 구매자의 이름과 성별, 제품 번호와 이름이 바로 각 엔티티의 속성이다.
1. 구매자 ID 속성 = dongho18, 구매자 이름 속성 = '장동호', 구매자 성별 속성 = 남자
2. 제품 번호 속성 = 123, 제품 이름 속성 = '맥북 13인치'
SQL
복사
이때 같은 속성을 공유하는 개별 엔티티는 같은 엔티티 집합에 속한다고 할 수 있다. 예컨대 1은 엔티티 ‘구매자’라는 엔티티 집합으로 표현될 수 있고, 2는 ‘제품’이라는 엔티티 집합으로 표현할 수 있다. 즉, 데이터베이스 내에 엔티티 집합이 정의될 수 있고, 엔티티 집합에 속한 다양한 개별 엔티티들이 저장될 수 있다.
엔티티의 속성이 가질 수 있는 집합은 도메인(domain)이라고 한다. 가령 ‘구매자 성별’ 속성이 가질 수 있는 도메인은 (남자, 여자)이다.
그렇다면 DBMS는 엔티티와 엔티티 집합을 어떻게 각각 표현하고 저장할까? DBMS마다 다양하게 엔티티 집합을 표현할 수 있다. 가령 RDBMS는 엔티티 집합을 테이블(표)의 형태로 표현한다. RDBMS에서 표현되는 이차원 테이블 형태의 엔티티 집합은 릴레이션(relation)이라고도 부른다. 또 NoSQL RDBMS의 일종인 MongoDB에서는 엔티티 집합을 컬렉션(collection)이라는 단위로 표현한다.
엔티티를 데이터베이스에 ‘저장 가능한 대상’이라고 한다면, 레코드는 데이터베이스에 ‘저장된 대상’이라고 할 수 있다. 레코드(record)는 데이터베이스에 ‘저장된 대상’이라고 할 수 있다. 레코드(record)는 우리말로 ‘기록’을 의미한다. 데이터베이스에 ‘기록’된 각각의 엔티티를 레코드라고 부르는 셈이다. 데이터베이스에 저장된 엔티티 속성은 필드(field)라고 한다. 즉, 다양한 속성(attribute)을 지닌 엔티티(entity)들이 다양한 필드(field)를 지닌 레코드(record)로써 데이터베이스에 저장될 수 있다.
RDBMS에서는 개별 레코드를 테이블의 행으로 표현하고, 필드를 테이블의 열로 표현한다. 또한 NoSQL DBMS인 MongoDB에서는 개별 레코드를 JSON 형태의 데이터인 도큐먼트(document)라는 단위로 표현하고, 필드를 Json 키의 형태로 표현한다.
한 필드에 대한 고유 값의 수는 카디날리티(cardinality)라고 부른다. 카디날리티가 낮을수록 중복된 속성이 많음을 시사한다. 필드의 수는 차수(degree)라고 부른다.
3.2. 스키마
RDBMS와 NoSQL을 구분하는 중요한 기준 중 하나로 스키마의 유무를 꼽을 수 있다. 스키마(schema)는 데이터베이스에 저장되는 레코드의 구조와 제약 조건을 정의한 것으로, 레코드가 지켜야 할 틀이자 청사진이라고 할 수 있다.
앞서 RDBMS에서는 레코드를 테이블 내 행으로 저장하고, NoSQL의 일종인 MongoDB에서는 컬렉션 내 도큐먼트로 저장한다고 했다. 따라서 RDBMS에서는 명확한 스키마가 정의되며, 레코드들은 이 스키마로 정해진 테이블의 구조, 필드의 데이터 타입 및 제약 조건을 따라야 한다. 가령 다음과 같은 ‘학생’ 테이블이 있다고 가정해 보자. 이 테이블의 구조와 각 필드의 데이터 타입, 제약 조건 등이 정의된 틀이 바로 스키마이다.
반면, NoSQL에서는 명확한 스키마가 정의되지 않기 때문에 NoSQL 데이터베이스를 스키마-리스(schema-less) 데이터베이스라고도 부른다. 레코드들이 지켜야 할 구조와 제약 조건에 제한이 없어 RDBMS보다 자유로운 형태의 레코드를 저장할 수 있다. 가령 MongoDB에 ‘학생’이라는 컬렉션이 있다고 가정해 보자. 이 컬렉션에는 다음과 같은 2개의 도큐먼트가 저장될 수 있다.
•
첫 번째 도큐먼트
{
"name": "dongho",
"age": 30,
"email": "dongho@naver.com"
}
JSON
복사
•
두 번째 도큐먼트
{
"name": "hodong",
"age": 25,
"email": "hodong@example.com",
"address": {
"street": "123 Main St",
"city": "Somewhere",
"zipcode": "12345"
}
}
SQL
복사
정형화된 데이터만 저장 가능한 RDBMS와는 다르게, 고정된 스키마를 따르지 않아 더욱 유연한 형태로 레코드를 관리할 수 있다.
3.3. 트랜잭션과 ACID
이제까지 데이터베이스가 무엇을, 어떻게 저장하는지 알아봤다면 이번에는 데이터베이스가 데이터를 어떻게 논리적으로 처리하는지에 대해 알아볼 차례이다. 트랜잭션(transaction)은 데이터베이스와의 논리적 상호작용의 단위를 의미한다. 데이터베이스가 처리하는 작업의 단위를 나타내므로 초당 트랜잭션(TPS, Transactions Per Second)이라는 지표로 데이터베이스의 작업 성능을 나타내기도 한다. 유의할 점은 트랜잭션이 하나의 쿼리만 포함하는 것은 아니라는 점이다. 예를 들어 ‘동호’가 ‘유진’에게 5,000원을 이체하는 트랜잭션에서는 다음과 같은 2개 이상의 쿼리가 포함될 수 있다.
•
동호의 계좌 잔액을 5,000원 감소시키기
•
유진이의 계좌 잔액을 5,000원 증가시키기
강조했듯, 데이터베이스는 한 명의 사용자나 하나의 프로그램만 데이터베이스를 사용하는 경우가 거의 없다. 여러 사용자/프로그램이 동시다발적으로 사용하는 것이 일반적이다. 따라서 여러 작업을 내포하는 트랜잭션이 동시다발적으로 실행될 때는 안전한 트랜잭션을 보장하기 위해 지켜야 하는 4가지 성질이 있다. 이를 각 단어의 앞 글자만 따서 ACID라고 한다. 원자성, 일관성, 격리성, 지속성의 줄임말인 ACID는 데이터베이스, 특히 관계형 데이터베이스의 상징과도 같은 단어이다.
1) 원자성(Atomicity)
원자성이란 하나의 트랜잭션 결과가 모두 성공하거나 모두 실패하는 성질의 의미한다. 가령 앞선 예시에서 유진이의 계좌 잔액을 5,000원 증가시키는 작업이 실행되는 도중 DBMS가 다운되었다면 한빛이의 계좌 잔액을 5,000원 증가시키는 작업이 실행되는 도중 DBMS가 다운되었다면 동호의 계좌 잔액을 5,000원 감소시켰던 작업도 취소되어야 한다. 이렇게 트랜잭션이 하나의 단위로 처리되는 것을 원자성이라고 한다.
•
동호의 계좌 잔액을 5,000원 감소시키기
•
유진이의 계좌 잔액을 5,000원 증가시키기
원자성을 상징하는 말로 All or Nothing을 들기도 한다. 말 그대로, 주어진 작업을 모두 성공하거나 모두 실패할 뿐, 일부 성공이나 일부 실패는 허용하지 않는다는 의미이다.
커밋과 롤백
원자성을 트랜잭션이 반드시 커밋되거나 롤백되는 성질이라고도 표현한다. 데이터베이스에서 커밋(commit)이란 트랜잭션을 성공적으로 수행하여 트랜잭션을 종료하는 것을 의미하며, 트랜잭션으로 인한 변경 사항을 확정한다는 의미로도 사용된다. 롤백(rollback)은 이전 트랜잭션을 취소하는 작업을 의미하는데, 데이터베이스를 롤백하면 트랜잭션이 이루어지기 전으로 되돌릴 수 있다.
2) 일관성(Consistency)
트랜잭션 전후로 데이터베이스가 일관된 상태를 유지하는 성질을 일관성이라고 한다. 여기서 ‘일관된 상태’란 데이터베이스가 지켜야 하는 일련의 규칙들을 지키는 상태를 의미한다. 이러한 규칙을 깨뜨리지 않는 데이터를 유지하는 성질이 바로 데이터베이스의 일관성이다.
예를 들어, ‘모든 학생은 학번이 존재해야 한다’라는 학사 시스템의 제약 조건이 있다고 가정해보자. 하지만 입학 예정인 신입생은 아직 정식으로 등록되지 않았기 때문에 학번이 없는 경우가 많다. 이럴 때, 시스템에서는 임시 학번을 부여해서 “모든 학생은 학번을 가진다”는 제약 조건을 지키도록 한다. 이처럼, 상황에 따라 데이터의 형태나 값은 달라질 수 있지만, 데이터베이스는 항상 정해진 규칙을 지키는 방식으로 운영된다. 즉, 규칙을 깨지 않고 다른 방법으로 일관성을 유지하는 것도 데이터베이스의 일관성 개념에 포함된다.
3) 격리성(Isolation)
격리성이란 동시에 수행되는 여러 트랜잭션이 서로 간섭하지 않도록 보장하는 성질을 의미한다. 즉, 레이스 컨디션을 방지하기 위한 성질이다. 한 트랜잭션이 어떤 데이터에 접근하여 조작중일 때는 다른 트랜잭션이 접근할 수 없다. 예를 들어 현재 재고가 하나뿐인 상품을 두 명의 사용자가 동시에 구매하려고 시도하는 상황을 가정해보자. 격리성이 보장되지 않을 경우, 두 트랜잭션이 동시에 재고를 확인하고, 각자 재고가 있다고 판단하여 결제를 시도하게 되므로 결국 최종적으로는 재고 부족 문제가 발생할 수 있다. 하지만 격리성이 보장된다면 동시에 두 트랜잭션이 수행되지 않으므로 이러한 문제를 방지할 수 있다.
4) 지속성(Durability)
지속성이란 트랜잭션이 성공적으로 완료된 후에 그 결과가 영구적으로 반영되는 성질을 의미한다. 즉, 시스템 장애가 발생하더라도 완료된 트랜잭션의 결과는 손실되지 않아야 한다. 예를 들어, 은행에서 특정 계좌에 돈을 입금하는 트랜잭션이 성공적으로 완료되었다면, 그 결과가 디스크에 기록되어 시스템 장애가 발생하더라도 입금 내역이 사라지지 않아야 한다. 오늘날 DBMS에는 대부분 이를 보장하기 위한 회복 메커니즘이 구현되어 있다.