프로그래밍언어 자바

11.2 DB 핵심 이론

이번 강좌에서는 관계형 데이터베이스의 기본적인 개념과 주요 구성요소 및 용어들을 배운다. 테이블에서 부터 키와 제약조건, 인덱스, 시퀀스 등을 이해하고 SQL을 배우기 전에 꼭 필요한 필수적인 개념과 용어들을 살펴봅니다.

이 강의를 통해 관계형 데이터베이스의 구조와 데이터베이스 관리를 위한 구성요소들에 대해 이해할 수 있게 됩니다.



테이블(Table)

테이블은 관계형 데이터베이스에서 데이터 관리의 기본이 되는 구조 이다. 데이터들이 가지는 공통적인 속성들을 모아 정의한 컬럼(필드)들로 구성되며 컬럼에 저장되는 데이터는 숫자형, 문자형, 날짜형, boolean 등으로 구분되며 파일과 같은 바이너리 데이터를 저장하거나 매우 긴 텍스트를 저장하기 위한 자료형을 가진다.

예를 들어 다음과 같은 데이터가 있다고 가정하자.

김길동, AA대학교, 1999-10-21, kim@aa.com
park@bb.com, 박사랑, BB대학교, 2000-01-21
나최고, 1998-07-11, na@cc.com, CC대학교
김길동, BB대학교, 1999-03-10, kim@bb.com

정리가 되어 있지는 않지만 데이터의 내용을 보면 이름, 대학, 생년월일, 이메일로 공통된 정보를 가지고 있다고 볼 수 있다. 이를 테이블로 표현하면 다음과 같다.

이름 대학 생년월일 이메일
김길동 AA대학교 1999-10-21 kim@aa.com
박사랑 BB대학교 2000-01-21 park@bb.com
나최고 CC대학교 1998-07-11 na@cc.com
김길동 BB대학교 1999-03-10 kim@bb.com

컬럼(Column)

테이블을 구성하는 기본 속성으로 필드, 애트리뷰트 라고도 한다.

앞의 예시에서 이름, 생년월일, 대학, 이메일이 컬럼이 된다. 각각의 컬럼에 저장되는 데이터는 동일한 타입이어야 하며 구체적인 자료형은 데이터베이스 마다 차이가 있다.

로우(row)

컬럼에 해당하는 데이터들을 가지고 있는 하나의 데이터셋을 말하며 레코드 혹은 튜플 이라고도 한다.

앞의 예시에서 한 사람의 데이터 즉 김길동, AA대학교 , 1999-10-21 , kim@aa.com 이 로우가 된다. 예시에서는 총 4개의 로우가 있다고 이야기할 수 있다.

자료형

컬럼에 들어갈 수 있는 데이터의 유형으로 일반적으로 숫자형, 문자형, 날짜형, boolean 등으로 구분

데이터베이스 마다 사용하는 자료형의 이름에 차이가 있다. 예를 들어 숫자의 경우 오라클은 number, MySQL은 int, double 등을 사용한다. 일반적인 자료형 이외에 파일과 같은 바이너리 데이터를 저장하거나 매우 긴 텍스트를 저장하기 위한 자료형등이 별도로 존재하며 이러한 특수 타입들은 데이터베이스 마다 지원하는 범위가 다르다. 또한 최근에는 JSON 데이터의 활용이 늘어남에 따라 JSON 데이터를 저장하기 위한 컬럼 구조 혹은 변환 함수등을 제공하기도 한다.



스키마와 ERD

데이터베이스 스키마는 데이터베이스에서 자료의 구조, 자료의 표현 방법, 자료 간의 관계를 형식 언어로 정의한 구조를 말하며 ERD는 Entity Relationship Diagram 의 약어로 시각적으로 스키마를 설계할때 사용하는 방법중 하나이다.

제대로 스키마를 설계하는것은 전문적인 영역으로 많은 경험과 노하우가 필요한 작업으로 여기서는 데이터베이스를 배우기 위해 꼭 알아야 하는 내용들만 간략히 살펴본다.

스키마(Schema)

스키마는 일반적으로 데이터베이스 구조객체, 속성, 관계, 제약조건등을 포함해 설명한 설계도이며 보통 3계층 구조로 표현 한다.

데이터베이스(DBMS) 종류에 따라 물리적으로 데이터 저장을 위한 테이블을 생성하기 위해서는 먼저 해당 데이터가 관리될 스키마 혹은 데이터베이스를 생성 하거나 지정해야 한다. 데이터베이스에 따라 스키마 혹은 데이터베이스라는 용어가 혼재되어 사용된다.(오라클은 스키마, MySQL, H2 는 데이터베이스)

ERD(Entity Relationship Diagram)

보통 관계형 데이터베이스를 설계할때 많이 사용하는 다이어그램 이다. 시각적인 도형과 선등을 이용해 데이터의 관계를 정의하는 방식으로 전문적인 도구를 사용하거나 단순히 ppt, visio 등의 프로그램을 이용하기도 한다.

[그림: ERD 예시 -> 그림 대체 필요]

ERD는 엔티티, 속성, 관계로 구성된다.

엔티티(Entity)

속성(Attribute)

관계(Relationship)

[그림: ERD 표기법]
대표적인 ERD 표기법은 crow's foot notation(혹은 IE/Information Engineering), Barker 표기법등이 유명하며 ERD를 그리는 도구에 따라 실제 표현되는 모양에는 차이가 있을수 있다.


제약 조건과 키

제약 조건(Constraint)

데이터베이스에서 제약조건이란 컬럼에 부여하는 일종의 속성으로 저장될 데이터에 대한 요구사항이라고 볼 수 있다. 데이터를 저장하는 프로그램측에서 특정 조건을 만족하는 경우에만 저장하도록 로직을 설계할 수는 있지만 로직에 의한 오류도 있을수 있으며 프로그램을 통하지 않고 들어오는 데이터의 경우에는 잘못된 데이터가 저장되는 것을 원천적으로 차단할수는 없다.

제약 조건은 데이터베이스 자체적으로 저장될 데이터에 대한 요구 조건을 설정하는 것으로 제약 조건을 벗어나는 데이터는 원천적으로 저장이 차단된다. 이는 데이터베이스의 무결성(Integrity)을 지키기 위한 방법이 된다.

대표적인 제약 조건은 다음과 같다.

종류 설명 비고
NOT NULL 컬럼에 데이터를 비워둘 수 없음 기본은 NULL 가능
UNIQUE 주키는 아니지만 중복값 허용 안함 기본은 중복 가능
Primary Key 데이터의 유일성을 보장하는 컬럼 NOT NULL + UNIQUE, 테이블에 하나만 설정가능
Foregin Key 두 테이블간의 참조 무결성을 보장 외래키로 지정된 컬럼값은 참조 테이블에 존재하는 값만 사용 가능
CHECK 저장할 값의 범위 등을 지정 숫자범위, 다른 컬럼 값 비교 결과, 특정 문자중 하나 등
IDENTITY 유일한 값을 가지는 id컬럼 PK, Not Null, auto_increment 자동 적용됨
[데이터베이스 제약 조건]

MySQL의 경우 CHECK 제약조건은 8.0.16 이후 버전에서만 지원되니 참고바란다.

키(Key)

관계형 데이터베이스에서 제약 조건중 하나인 키는 데이터의 유일성 및 관계 설정의 목적에 사용된다. 적절한 키의 사용은 데이터베이스 설계에 있어 매우 중요한 요소 이다.

PK(Primary Key)

주 키, 기본 키, 프라이머리 키 라고도 한다. 테이블에 저장된 레코드(로우)를 서로 구분할 수 있도록 특정 컬럼에 설정하는 제약 조건이다. PK가 존재하지 않으면 데이터가 중복되고 특정 데이터를 검색하기 어려워 진다.

예를 들어 앞의 학생정보 테이블 예에서 김길동 학생이 두명이 있기 때문에 이름으로 검색할 경우 두명의 학생이 검색된다. 따라서 이러한 경우 이름만으로는 유일성 확보가 안되기 때문에 생년월일, 학교등 부가적인 컬럼의 정보를 조합해야 하는 어려움이 발생한다.

그러면 이름을 PK로 지정하는 것을 생각해 볼 수 있는데 이름의 경우중복이 가능하기 때문에 별도의 중복되지 않는 값이 필요하다. 쉽게 생각할 수 있는 것은 학번, 주민등록번호등의 고유번호 이다. 그러나 이들 번호는 개인정보에 해당하므로 외부에 노출되지 않는 것이 좋기 때문에 PK로는 부적합 하다.

보통 PK는 시퀀스(Sequence)라고 불리우는 단순 증가 값을 사용한다. 그냥 0부터 시작해서 1씩 증가하는 숫자로 이 숫자의 의미는 단순히 데이터들을 서로 구분하는 용도로만 사용한다고 이해하면 된다.

데이터베이스에 따라서는 auto increment 속성을 통해 자동으로 증가값을 입력하게 설정할수도 있고 별도의 객체를 통해 지정해야 하는 경우도 있어 데이터베이스마다 확인이 필요하다.

FK(Foreign Key)

외래 키는 테이블간의 관계를 설정하기 위해 사용하며 참조 무결성을 제공하기 위한 용도로 사용된다.

앞의 학생정보 테이블에서 학교 컬럼의 자료형은 문자열로 다음의 데이터는 모두 동일 학교를 의미한다.

AA대학교, AA 대학교, 에이에이 대학교, A A 대학교, AA대학 ...

이경우 데이터의 의미는 동일하지만 검색에서 문제가 발생한다. AA대학교 학생들만 검색 한다면 다른 이름들은 같의 의미지만 검색이 안되며 AA대학교 or AA 대학교 or 에이에이 대학교 ... 와 같이 조건 검색을 해야 한다.

이러한 문제를 원천적으로 차단하기 위해서는 등록 가능한 학교테이블을 별도로 두고 이를 참조할 수 있도록 외래키를 등록해 사용해야 한다.

대학정보 테이블은 다음과 같이 만들수 있으며 이때 대학코드를 PK로 설정 한다.

대학코드(PK) 대학명 위치
10011 AA대학교 서울
10012 BB대학교 인천
10013 CC대학교 대전

학생정보 테이블은 다음과 같이 수정되며 대학 컬럼은 FK로 설정 하고 학교이름 대신 대학코드를 등록 한다.

이름 대학(FK) 생년월일 이메일
김길동 10011 1999-10-21 kim@aa.com
박사랑 10012 2000-01-21 park@bb.com
나최고 10013 1998-07-11 na@cc.com
김길동 10012 1999-03-10 kim@bb.com

이때 학생정보 테이블의 대학 컬럼에 대학정보 테이블에 없는 값을 등록하는 경우 에러가 발생하고 데이터가 등록되지 않는다. 또한 대학코드 테이블에서 특정 대학을 삭제하는 경우에도 해당 데이터를 학생정보 테이블에서 참조하고 있기 때문에 단순한 삭제 쿼리로는 데이터가 바로 삭제 되지 않는다.



인덱스와 시퀀스

인덱스(Index)

인덱스는 데이터베이스에서 데이터 검색의 속도 향상을 위해 사용하는데 예를 들어 앞의 학생정보 테이블에 10만건의 학생정보가 들어있고 이중에서 홍길동이라는 이름을 가진 학생들만 찾아야 한다고 가정해 보자. 학생정보는 등록순으로 저장되고 데이터베이스에서 데이터를 저장하는 위치는 순차적이지 않기 때문에 별도의 장치가 되어 있지 않다면 전체 데이터를 하나씩 읽어가면서 이름이 일치하는 로우만 추출해야 한다.

만일 데이터가 이름순으로 정렬되어 있다면 으로 시작되는 위치에서 시작해 데이터를 차례로 찾으면 되기 때문에 10만건 모두를 검색할 필요가 없게된다.

이와같이 인덱스는 하나 혹은 여러 컬럼을 이용해 데이터 검색을 위한 별도 자료구조를 생성하는 것으로 PK로 지정된 컬럼은 자동으로 인덱스가 생성 된다. PK 이외의 컬럼을 이용한 검색이 필요한 경우 해당 컬럼을 이용해 별도의 인덱스 설정이 필요하다.

다음은 인덱스를 생성하는 예 이다.

// 테이블 생성시 추가하는 경우
CREATE TABLE test (
    keyword varchar(20),
    INDEX(keyword(20))
)

// 테이블 생성 이후 추가하는 경우
ALTER TABLE test ADD INDEX(keyword(20));

시퀀스(Sequence)

시퀀스는 PK컬럼을 관리하기 위해 사용하는 데이터베이스 객체로 보통 데이터 추가시 순차적으로 증가하는 값을 자동으로 생성한다.

최근의 데이터베이스들은 auto increment, auto_increment 와 같은 속성을 컬럼에 추가하는 것으로 자동증가 값을 사용할 수 있지만 대표적인 RDBMS인 오라클의 경우 별도의 시퀀스 객체를 사용한다.

H2 의 경우 IDENTITY라고 하는 자료형이 제공되어 auto_increment 를 대체해 사용하는것도 가능하다.

별도의 시퀀스 객체를 사용하는 방식은 다소 불편하고 데이터베이스간 호환성에도 문제를 발생 시킨다. 예를 들어 MySQL 이나 H2 의 경우 PK 에 auto increment 를 지정한 경우 데이터 등록시 PK컬럼을 빼고 나머지 데이터만 등록하면 자동으로 입력되는 방식이라 편리하지만 오라클의 경우 시퀀스 객체를 사용하는 쿼리를 작성해야 하므로 쿼리에 호환이 안되는 문제가 있다.

PK 시퀀스 등록 쿼리 예

// MySQL, H2, 트리거 사용시 Oracle  동일 쿼리 사용가능
insert into student(name, school, email) values('홍길동','AA대학교','test@test.com');

// Oracle
insert into student(id, name, school, email) values(id_seq.nexval, '홍길동','AA대학교','test@test.com');

이러한 문제 해결을 위해서는 오라클에서 시퀀스이외에 트리거라는 것을 만들어 데이터 추가시 자동으로 시퀀스 객체를 참조해 값을 추가하도록 하는 방법이 있다. 이 경우 별도로 트리거를 생성 하는 번거로움은 있지만 다른 데이터베이스와 동일한 쿼리를 사용할 수 있다는 장점이 있다.

오라클에서 트리거를 이용한 auto increment 구현

REATE OR REPLACE TRIGGER s_id 
BEFORE INSERT ON student 
FOR EACH ROW

BEGIN
  SELECT id_seq.NEXTVAL
  INTO   :new.id
  FROM   dual;
END;

트리거는 특정 이벤트 발생시 자동으로 처리하는 간단한 자동처리 작업으로 다음에서 자세히 알아 본다.



트리거, 함수, 트랜잭션

트리거(Trigger)

트리거는 방아쇠라는 뜻이고 총의 방아쇠를 당기면 내부에서 일련의 작업이 이루어지고 총알이 발사되는 원리에 착안해 붙여진 이름이다. 데이터베이스에서는 특정 테이블에 데이터 조작(inser, update, delete 와 같은 DML 문의 실행)시 데이터베이스에서 자동으로 동작하도록 작성된 일종의 프로그램으로 볼 수 있다.

오라클에서 시퀀스를 이용해 자동으로 컬럼값을 증가시키는 트리거 예제와 같이 트리거는 특정 이벤트가 발생했을때 관련된 처리를 진행하는 코드가 추가되는 구조이다.

SQL3 표준에 포함되어 대부분의 상용 DBMS에서 제공되고 있다. 다만 기본 구문은 동일 하지만 처리를 구현하는 코드 영역은 데이터베이스마다 다소 차이가 있으므로 구체적인 사항은 해당 데이터베이스의 매뉴얼을 참고하도록 한다.

함수(Function)

함수는 프로그램안에서 특정 기능을 수행하는 독립 코드를 의미한다. 보통 파라미터(인자)와 리턴값을 통해 다양한 조건에서 동작하도록 설계되어 재사용이 가능한 구조이다.

데이터베이스도 함수를 가지고 있으며 SQL을 통해 데이터를 좀 더 쉽게 다룰 수 있도록 하기 위해 만들어졌다. 이러한 함수는 SQL에 포함된 표준함수도 있지만 데이터베이스마다 지원되는 고유의 함수도 있다.

경우에 따라서는 데이터베이스 함수 사용은 프로그램의 코드를 줄여주기 때문에 당장은 편할수 있으나 이 경우 프로그램이 특정 데이터베이스에 종속되는 결과를 낳을수 있기 때문에 전용 함수 사용에는 신중을 기해야 한다.

대표적인 표준 함수들에 대해서는 뒤에서 배우게될 SQL 에서 살펴보도록 한다.

오라클의 경우 PL/SQL이라는 자체 문법을 통해 왠만한 프로그램 언어수준의 프로그램을 데이터베이스에 내장하는 것이 가능하고 쿼리에서 호출해 사용하는 것도 가능하다. 일부 데이터베이스의 경우에는 자바 클래스를 DB에 넣고 쿼리에서 호출해 사용하는 것도 가능하다.

트랜잭션(Transaction)

프로그램에서 어떤 이벤트가 발생했을때 경우에 따라서는 하나의 테이블에만 데이터를 변경할 수 있지만 많은 경우 여러 테이블의 데이터를 차례로 변경해야 하는 경우가 발생 한다.

예를 들어 은행에서 계좌이체를 한다고 가정해 보자. 동일 은행의 A -> B 계좌로 이체한다고 했을때 계좌는 테이블로 볼 수 있다. 이때 A계좌에서 100만원을 차감한후 B계좌에 100만원을 추가하면 계좌이체가 완료되는 것이다. 이때 A 계좌에서 차감후 B 계좌에 추가하는 과정에서 에러가 발생하면 A 계좌에서 차감했던 금액은 다시 원래대로 되돌려야 한다.

이처럼 트랜잭션은 하나의 논리적 기능을 수행하기 위해 여러 작업을 묶어서 처리하는 것을 의미한다. 이러한 트랜잭션은 프로그램에서 단위 작업마다 성공 여부를 체크해서 진행할 수도 있으나 이 경우 개발자가 로직을 잘못 설계할 가능성도 있기 때문에 데이터베이스 혹은 미들웨어 레벨에서 처리할 수 있어야 한다.

기본적으로 데이터베이스는 다음과 같이 트랜잭션 관리를 위한 명령을 제공

A~C 순으로 진행되는 트랜잭션의 처리 과정을 요약하면 다음과 같다.

작업 A OK
작업 B OK
작업 C OK
commit

작업 A OK
작업 B OK
작업 C Fail
rollback -> A, B 는 모두 처리 이전값으로 복원됨.