현재강좌 : UNIX 소켓 응용 프로그래밍 이전 4.5 소켓 함수 처리시간 측정


4.6 네트웍 데이터베이스 액세스 응용

▶ 4.6절에서는 네트웍을 통하여 서버에 있는 데이터베이스(DB: database) 자료를 검색하거나 새로운 자료를 추가하는 프로그램을 작성한다.

▶ 데이터베이스에 자료를 입출력하기 위해서 DBMS(DataBase Management System)와 SQL(Structured Query Language)문을 사용한다.

▶ 본 예제의 서버 프로그램에서는 SQL을 지원하는 c 언어인 ESQL/C을 사용한다.

4.6.1 표준 SQL

▶ SQL은 설치된 DBMS 종류에 무관하게 DB 응용 프로그램을 작성할 수 있도록 하는 명령문의 집합으로 표준 SQL이 제공하는 주요 명령문과 기능을 표 4-2에 정리하였다.

구분

SQL 문

사용법

기 능

자 료

정의문

CREATE DATABASE CREATE DATABASE <DB명> DB를 새로 만든다.
DATABASE DATABASE <DB명> [EXCLUSIVE] 사용할 DB를 선택한다.

[EXCLUSIVE] 옵션은 현재 사용자만 DB에 접근할 수 있도록 한다.

CREATE TABLE CREATE TABLE <테이블명>

(<칼럼명>, <자료형태> )

현행 DB에 새로운 테이블을 생성한다.
DROP TABLE DROP TABLE <테이블명> 현행 DB에서 지정한 테이블을 삭제한다.
DROP DATABASE DROP DATABASE <DB명> 지정한 DATABASE를 삭제한다.

자 료

처리문

DELETE DELETE FROM <테이블명>

WHERE <칼럼명> = <값>

FROM에 지정한 테이블에서 WHERE 조건을 만족하는 행을 삭제한다.
INSERT INSERT INTO <테이블명> ( <칼럼명>) VALUES (<값>) INTO에 지정한 테이블에 새로운 행을 추가한다.
SELECT SELECT <칼럼명>

FROM <테이블명>

WHERE <칼럼명> = <값>

FROM에 지정한 테이블에서 WHERE 조건을 만족하는 행을 선택한다.
UPDATE UPDATE <테이블명>

SET <칼럼명> = <값>

WHERE <칼럼명> = <값>

UPDATE에 지정한 테이블에서 WHERE 조건을 만족하는 행의 내용을 SET 절에서 지정한 것과 같이 변경한다.

표 4-2 주요 표준 SQL 문

▶ 예를들어 새로운 DB를 하나 만들 때에는 CREATE DATA- BASE문을 사용한다.

▶ DB내에 새로운 테이블을 생성할 때에는 CREATE TABLE문을 이용하고 테이블에 어떤 항목을 추가하려면 INSERT문을 사용한다.

▶ 아래는 UNIV(대학)이라는 DB를 새로 만들고 여기에 DEPT(부서)라는 테이블을 만든 후 DEPT의 ID, dept_ID가 01이고 부서명 dept_Name이 '총무과'인 데이터 항목을 추가하는 표준 SQL 문을 나타냈다.

▶ 아래의 테이블 DEPT에서 dept_ID는 두 글자이고, dept_ Name을 20자로 지정하였다.

CREATE DATABASE UNIV

CREATE TABLE DEPT (dept_ID char(2), dept_Name char(20))

INSERT INTO DEPT VALUES ('01', '총무과')

▶ 테이블내의 어떤 데이터 항목 값을 바꿀 때는 UPDATE를 사용하는데 다음은 DEPT 테이블에서 dept_ID가 01인 행의 dept_Name을 '경리과'로 변경하는 예제이다.

UPDATE DEPT

SET dept_Name = '경리과'

WHERE dept_ID = '01'

▶ 테이블에서 어떤 칼럼이 어떤 조건을 만족하는 데이터를 검색하기 위해서는 SELECT를 이용한다.

▶ SELECT 뒤에는 출력할 칼럼의 목록이, FROM 뒤에는 검색할 테이블 명이, 그리고 WHERE 뒤에는 검색 조건을 지정한다.

▶ 예를들어 테이블 DEPT에서 dept_Name 칼럼이 '총무과'인 행을 검색하기 위한 SQL문은 다음과 같다.

SELECT dept_Name, dept_ID

FROM DEPT

WHERE dept_Name = '총무과'

4.6.2 ESQL/C

▶ ESQL/C는 c 언어 프로그램에서 SQL 명령문을 편리하게 사용할 수 있도록 기능을 확장한 언어로 c 언어의 일반적인 기능을 지원하며 SQL문을 사용할 수 있는 함수가 추가되었다(ESQL/C로 작성한 파일은 .ec확장자를 갖는다).

▶ ESQL/C 프로그램 내에서 SQL문은 $로 시작해야 하는데 예를들어 DEPT라는 테이블에 dept_ID 값이 01인 '총무과' 항목을 추가하려면 다음과 같이 하면 된다.

$insert into DEPT values ( '01', '총무과');

 

▶ 또는 $대신 exec sql을 사용할 수도 있다.

exec sql insert into DEPT values ( '01', '총무과');

 

▶ SQL문 내에서 c 언어 프로그램에서 정의한 변수들을 사용할 수 있는데 이러한 c 언어 변수를 호스트 변수라고 한다.

▶ 호스트 변수를 선언할 때에는 변수 타입 앞에 $를 붙여야 하며, SQL문에서 이 변수를 사용할 경우에는 : 를 붙여야 한다.

▶ 예를들어 호스트 변수를 사용하여 DEPT 테이블에 '총무과'를 추가하기 위해서는 다음과 같이 하면 된다.

▶ 아래에서 호스트 변수는 id와 name 두 개이다.

# ID_LEN 2

# NAME_LEN 20

$char id[ID_LEN], name[NAME_LEN];

strcpy(id, "01");

strcpy(name, "총무과");

$insert into DEPT values ( :id, :name ) ;

▶ 표 4-3에 SQL 문에서 사용하는 변수의 종류와 이를 지원하기 위한 ESQL/C의 호스트 변수들을 정리하였다.

SQL 변수

호스트 변수

설 명

CHAR(n),

CHARACTER(n)

char[n+1],

char*

1부터 32,767까지 가변 길이의 문자열을 저장한다. C언어에서 문자열은 널(NULL)로 끝나야 하므로, CHAR(n) 값을 받는 호스트 배열은 (n+1) 크기를 가져야 함
SMALLINT short int -32,767에서 32,767의 범위를 갖는 정수형
INTEGER long int -2,147,483,647에서 2,147,483,647의 범위를 갖는 정수형
SMALLFLOAT REAL float 7자리의 유효 자리수를 갖는 부동 소수점 수
FLOAT,

DOUBLE PRECISION

double 14자리의 유효 자릿수를 갖는 부동 소수점 수
SERIAL long int SERIAL 자료형태는 사용자가 INSERT 문을 실행할 때 ESQL/C가 자동으로 배정하는 순차 정수
DATE long int 1899년 12월 31일 이후의 날짜를 표현하며 4바이트 int에 저장
DECIMAL

DEC

NUMERIC

dec_t,

struct decimal

소수점의 유무에 관계없이 32개의 유효숫자를 갖는 수를 나타냄
MONEY dec_t,

struct decimal

MONEY(m,n)의 형태로 나타내며, 고정된 정밀도 m과 소수자리 수 n을 의미
DATETIME dtime_t,

struct dtime

한 시점을 "날짜 시:분:초"로 나타냄
INTERVAL intrvl_t, struct intrvl 기간을 나타내는 변수

표 4-3 호스트 변수 종류

▶ ESQL/C에서는 처음 SQL문을 실행하기 전에 database 문을 사용하여 앞으로 사용할 데이터베이스를 선언해야 한다.

▶ 프로그램을 종료하기 전에 close database 문으로 선택했던 데이터베이스를 닫아야 한다.

▶ 예를들어 Test_DB라는 DB를 선택하고 닫으려면 다음과 같이 한다.

$database Test_DB;

{ /* Test_DB 사용 */ }

$close database;

▶ 한편 ESQL/C로 작성된 프로그램의 컴파일은 다음과 같이 한다.

esql -o 출력파일명 소스파일명

 

4.6.3 프로그램 개요

▶ 여기서는 네트웍을 통하여 데이터베이스 자료를 검색하거나 자료를 추가하는 서비스를 제공하는 서버 프로그램, db_server.ec와 이를 이용하는 클라이언트 프로그램 db_ client.c의 개요를 설명하겠다.

▶ db_server.ec에서는 사용할 데이터베이스를 선언하고 클라이언트와 접속하기 위한 소켓을 개설한 후 클라이언트에서 요구하는 DB 액세스를 서비스한다.

▶ 본 예제에서는 클라이언트가 요구하는 동작으로 1) 자료추가와 2) 자료검색 두 가지만 구현하였다.

▶ 서버는 두 가지 중 하나의 동작을 실행하고 그 결과를 클라이언트에게 알려준다.

▶ 클라이언트 프로그램 db_client.c에서는 먼저 소켓을 생성하고 서버에 접속한다.

▶ 사용자 화면에 1) 자료추가와 2) 자료검색 메뉴를 보여주고 메뉴선택에 따라 필요한 데이터를 추가로 입력받는다.

▶ 자료 입력이 끝나면 서버로 동작을 요구(request)하고 그 결과(response)를 수신하여 화면에 출력한다.

▶ 아래는 서버 프로그램의 실행 예로 서버가 포트번호로 3000을 사용하는 경우이다.

db_server 3000

▶ 다음은 클라이언트 프로그램(db_client.c)의 실행화면으로 먼저 메뉴 1을 선택하여 부서명(dept_Name)을 추가하는 것을 보이고 있다.

# db_client

>> 자료추가 및 조회 메뉴

1. 자료추가

2. 자료조회

>> 메뉴 선택 : 1

>> 추가할 부서 ID : 01

>> 부서명 : 총무과

▶ 아래는 메뉴 2를 선택하고 부서 ID가 01인 부서명을 조회하는 것을 보이고 있다.

>> 자료추가 조회 메뉴

1. 자료추가

2. 자료조회

>> 메뉴 선택 : 2

>> 조회할 부서 ID : 01

==> 조회 결과 : 총무과

4.6.4 서버 프로그램 주요부분 설명

▶ 먼저 db_server.ec에서 사용할 헤더 파일을 선언해야 한다.

▶ ESQL/C 프로그램에서는 SQL 문이 실행된 뒤 결과 코드가 항상 전역변수 sqlca에 저장되며 이 변수를 사용하기 위하여는 헤더 파일 sqlca.h를 포함해야 한다.

▶ sqlca.h외에 sqlda.h, sqltypes.h 등의 헤더 파일을 포함할 수 있는데 sqlda.h에는 동적으로 정의된 SQL 문을 실행하기 위한 구조체가 정의되어 있다.

▶ sqltypes.h에는 SQL 칼럼의 종류가 정의되어 있다.

▶ 한편 SQL 관련 헤더 파일은 아래와 같이 #include 문이 아닌 $include 문을 사용해야 한다.

$include sqlca.h;

▶클라이언트는 DB 액세스를 할 때마다 '메뉴번호\n부서ID\부서명\n'의 형태로 구성된 메시지를 서버로 보내도록 하였다.

▶메뉴번호는 1이면 자료추가이고 2이면 자료조회를 나타낸다.

▶ 서버는 먼저 클라이언트가 어떤 메뉴를 선택했는지를 알아내야 하는데 이를 위해서 사용자 정의 함수 readline()을 사용한다.

▶ readline()은 소켓에 도착한 데이터를 지정한 크기만큼 읽는 함수인데 예를들어 소켓번호 s를 통해서 입력된 데이터의 앞에서부터 세 바이트(즉, 메뉴번호\n)를 읽어 menu라는 변수에 저장하려면 아래와 같이 한다(메뉴번호는 한 자리 수라고 가정).

▶ 한편 readline()은 user_func.h에 정의되어 있다.

n = readline(s, menu, 3);

▶ 같은 방법으로 dept_ID와 dept_Name을 readline()을 이용하여 읽는다.

▶예를들어 클라이언트가 메뉴 1) 자료추가를 선택한 경우 서버는 아래와 같이 추가할 자료를 읽은 후 insert를 실행한다.

▶ 메뉴 2) 자료조회를 선택한 경우에는 select를 사용하여 DEPT DB에서 dept_ID가 id인 부서를 조회하여 해당 부서명(dept_Name)을 변수 name에 저장한다.

switch((int)menu[0] - '0') {

/*------------------------------------------ 자료 추가 요구 ------------------------------------------*/

case 1:

/* 부서명 읽기 */

n = readline(s, name, NAME_LEN+2);

name[n-1] = '\0';

/* SQL 수행 */

$insert into DEPT values (:id, :name);

/*------------------------------------------- 자료 조회 요구 ----------------------------------------*/

case 2:

/* SQL 수행 */

$select dept_Name into :name from DEPT where dept_ID = :id ;

▶ 메뉴에 따른 처리가 끝났으면 서버는 클라이언트에게 SQL 문의 실행결과를 알려주기 위하여 클라이언트에게 전송할 모든 메시지를 문자 스트링 변수인 msg에 저장하여 이를 클라이언트로 전송한다.

▶ SQL 문 처리중 오류가 발생한 경우는 msg에 오류 메시지를 실어 전송한다.

db_server.ec 프로그램 리스트

4.6.5 클라이언트 프로그램 주요부분 설명

▶ 클라이언트 프로그램 db_client.c에서는 먼저 화면에 메뉴를 보여주고 메뉴를 선택하도록 하는 사용자 정의 함수 printMenu()를 호출한다.

▶ 선택한 메뉴값을 사용자 정의 함수 getMenu() 함수를 통해 입력받는데 그 값이 유효한 경우에는 해당 메뉴 번호를 리턴하고, 유효하지 않은 경우에는 0을 리턴한다.

▶ printMenu()와 getMenu()는 아래와 같다.

▶ 여기서 gets()는 키보드로부터 한 행의 문자열을 입력받는 함수이다.

void printMenu(void) {

printf("\n >> 자료추가 조회 메뉴\n");

printf("\t1. 자료추가\n");

printf("\t2. 자료조회\n");

printf("\n >> 메뉴 선택 : ");

}

int getMenu(void) {

char tmp[2];

gets(tmp);

tmp[1] = '\0';

if (tmp[0] != '1' && tmp[0] != '2' ) return 0;

else return (int)(tmp[0] - '0');

}

▶ 사용자가 선택한 메뉴 종류에 따라 필요한 자료를 추가로 입력받는다.

▶ 1) 자료추가를 선택한 경우에는 추가할 부서의 ID와 부서명을 입력받고 2) 자료조회를 선택한 경우에는 조회할 부서의 ID를 입력받는다.

case 1:

/* 자료추가를 위해 부서ID 이름을 입력받는다 */

printf("\n >> 추가할 부서 ID : ");

gets(dept_ID);

printf("\n >> 부서명 : ");

gets(dept_Name);

case 2:

/* 조회할 부서 ID 입력 */

printf("\n >> 조회할 부서 ID : ");

gets(dept_ID);

▶ 아래는 사용자가 입력한 명령문을 문자열로 만들고 이를 서버로 전송하는 부분이다.

sprintf(msg, "%d\n%s\n%s\n", menu, dept_ID, dept_Name);

msgLen = strlen(msg);

send(s, msg, msgLen, 0) != msgLen);

▶ 아래는 2) 자료조회 메뉴를 선택한 경우에 서버가 보내온 조회결과를 recv()를 이용하여 수신하고 이를 화면에 출력하는 부분이다.

recv(s, msg, MSG_LEN+1, 0);

printf("\t조회 결과 : %s\n\n", msg);

db_client.c 프로그램 리스트


현재강좌 : UNIX 소켓 응용 프로그래밍 이전 4.5 소켓 함수 처리시간 측정