Pro*C 강좌.
간식 & 식사 뽑기 만들기 - 2
헤더파일 과 main 함수 작성을 마치고
이제 본격적으로 프로그램에 필요한 각 기능을 수행해줄 함수를 작성하도록 하겠습니다.
( 모든 함수는 main 함수를 작성했던 yc_food.pc 파일에 그대로 작성할거에요 ~ ! )
1. 디비 로그인 함수. ( db_con )
int db_con() { char userid[20]; char passwd[20]; memset(userid, 0x00, sizeof(userid)); memset(passwd, 0x00, sizeof(passwd)); /* 변수를 선언했다면 초기화 해주는건 필수 !!! memset 함수를 이용해 선언한 변수안의 값을 모두 NULL 로 초기화 */ /* 테이블을 만들어둔 계정에 접속 하기위한 아이디 입력 */ printf("DB 접속 아이디: "); scanf("%s", userid); fflush(stdin); /* 패스워드 입력 */ printf("DB 비밀번호: "); scanf("%s", passwd); fflush(stdin); /* SQL 문을 이용해 DB에 접속 시도. */ EXEC SQL CONNECT :userid IDENTIFIED BY :passwd; if(sqlca.sqlcode < 0) /* SQL 문을 수행하고 그 결과값을 담아주는 sqlcode !! 값이 0 이하라면 비정상을 뜻합니다. */ { /* 로그인에 실패 했으므로 결과값을 보여주고 끝!! */ /* sqlcode 는 결과값을 담아주고 아래에 있는 sqlerrmc 는 에러코드 및 에러 원인을 보여줍니다. */ /* 아이디 혹은 패스워드가 옳바르지 않을 경우 - ORA-01017: invalid username/password; logon denied */ printf("%s\n", sqlca.sqlerrm.sqlerrmc); return -1; } /* 정상적으로 로그인 했다면 1 을 돌려줍시다. ( 1이든 0이든 이건 각자의 마음대로..main 에서 어떤 값을 받았을때 종료하게 했느냐에 따라 다르겠죠 ?.. ) */ return 1; }
2. 디비 로그아웃 함수. ( db_discon )
void db_discon() { /* DB에 로그아웃을 하기위한 SQL 문. commit 을 하고 종료 하겠다 라는 뜻 입니다. */ EXEC SQL COMMIT WORK RELEASE; }
3. 무한 루프 함수. ( food_roop )
void food_roop() { int select = 0; int end_flag = 0; while(1) { system("clear"); /* 내가 이용한게 계속해서 한화면에 나오면 지저분하니까 system 함수를 이용해 clear 명령을 내려 화면을 깔끔하게 지우면서 진행해요!! */ if(select == -1) printf("잘못 입력하셨습니다. 다시 입력해 주세요.\n"); /* 이쁜 출력을 위해 추가한 clear 명령이 위쪽에서 수행되다보니 아래쪽에서 경고를 출력할시 보이지 않고 넘어가서 이쪽으로 빼뒀습니다. */ if(end_flag == 1) { printf("프로그램을 종료 합니다.\n"); break; } /* 7번을 누를경우 종료 인데. void 형태의 함수이고 while 안에 switch 를 쓰다보니 break 가 switch 문 만을 빠져나가 종료에 관련된 flag 값을 추가해 while 문을 빠져 나갈수 있도록 추가 했습니다. switch 문 대신 if 문을 이용하면 이럴 필요가 없겠지만 혹시나 추후에 기능이 추가된다면 if 문으로 줄줄이 추가하기엔 뭔가 지저분한듯하고 맘에 들지 않아..switch 고집 !! */ select = select_menu(); /* 메뉴는 자주 출력을 해주기 때문에 따로 함수화 해주고, 거기서 사용자의 입력값도 반환해 줍니다. */ switch(select) /* switch 문 시작. */ { /* 간식 뽑기. */ case 1: get_food(FOOD_SNACK); /* 간식이든 식사이든 동일한 함수로 처리하기 위해 인자값에 food_type 을 둬서 그에 맞는 인자값을 전달 해줍니다. */ break; /* 식사 뽑기. */ case 2: get_food(FOOD_MEAL); /* 위와 동일 */ break; /* 음식 추가. */ case 3: food_insert(); break; /* 음식 수정. */ case 4: food_update(); break; /* 음식 삭제. */ case 5: food_del(); break; /* 음식 출력. */ case 6: food_print(); break; /* 종료. */ case 7: end_flag = 1; /* 여기서 end_flag 값을 변경해주면 위쪽에 if 문에서 걸러내 종료를 할수 있게 해주겠죠 ? */ break; default: select = -1; /* 잘못 입력했다는 경고문 출력을 위쪽에서 하기 위해 select 값에 미리 약속한 -1 값을 채워 줍니다. */ break; } } }
4. 메뉴 출력 및 사용자 입력값 반환 함수. ( select_menu )
int select_menu() { int sel=0; printf("***************************\n"); printf("***** 1. 간식 뽑기 *****\n"); printf("***** 2. 식사 뽑기 *****\n"); printf("***** 3. 음식 추가 *****\n"); printf("***** 4. 음식 수정 *****\n"); printf("***** 5. 음식 삭제 *****\n"); printf("***** 6. 음식 출력 *****\n"); printf("***** 7. 종료 *****\n"); printf("***************************\n"); printf("입력 :"); scanf("%d", &sel); fflush(stdin); return sel; /* 사용자 입력값을 그대로 return */ }
5. 뽑기 함수. ( get_food )
void get_food(char type) { char food_name[20]; char food_phone[20]; char insert_date[20]; char next; int cnt=0, ran=0; /* 음식의 총 갯수와 그 범위 안에서의 무작위 값을 저장하기 위한 변수. */ cnt = get_cnt(type); /* 음식의 총갯수 받아오는 함수 호출 */ ran = get_ran(cnt); /* 총갯수 범위 안에서의 무작위 값을 반환해주는 함수 호출 */ if(ran != 0) /* 데이터가 1개 이상 존재할경우. */ { memset(food_name, 0x00, sizeof(food_name)); memset(food_phone, 0x00, sizeof(food_phone)); memset(insert_date, 0x00, sizeof(insert_date)); /* 역시나 중요한 초기화 진행. */ /* get_food_cur 이라는 cursor (커서) 를 선언하고 해당 커서가 수행할 쿼리문등을 입력해주는 부분 입니다. */ EXEC SQL DECLARE get_food_cur CURSOR FOR select food_name, food_phone, insert_date from (select rownum r, food_name, food_phone, insert_date from select_food where food_type=:type order by food_name) where r = :ran; /* 서브 쿼리를 이용해 type 이 동일한 종류의 데이터를 쭉 읽어온뒤 이름순으로 정렬해두고, 거기서 rownum 이 무작위 값이 저장된 ran 변수의 값과 동일한 데이터를 뽑아냅니다. */ /* 커서 오픈 !! ( 실행의 의미로 위에서 작성해준 쿼리문을 수행하게 됩니다. ) */ EXEC SQL OPEN get_food_cur; /* 커서를 통해 받아온 데이터를 받아오는 부분 입니다. 각 변수 앞에 " : " 가 붙는 이유는 호스트 변수라는 의미를 가집니다. 쿼리문을 작성한 순서대로 food_name, food_phone, insert_date 변수에 값이 저장됩니다. */ EXEC SQL FETCH get_food_cur INTO :food_name, :food_phone, :insert_date; /* 값을 모두 받아온뒤에 커서 닫기. */ EXEC SQL CLOSE get_food_cur; /* 커서를 통해 받아온값 출력 ~ */ printf("\n\n"); printf("############################################\n"); printf(" ***** 뽑은 음식 정보 *****\n"); printf("음식 : [%s]\n", food_name); printf("번호 : [%s]\n", food_phone); printf("입력날자 : [%s]\n", insert_date); printf("렌덤숫자 : [%3d]\n", ran); printf("############################################\n\n"); } else /* 데이터의 갯수가 0개 일때. */ printf("뽑기를 진행할 음식의 데이터가 0개 입니다. 음식을 추가한뒤 뽑기를 해주세요 !!\n"); /* 여기서 빠져나가면 food_roop 함수로 돌아가는데 해당 함수의 시작점에 clear 명령이 있으니 사용자가 자신이 뽑은 정보를 확인하고 다시 진행할수 있게끔 아무키나 입력받는 단계를 추가 해야겠죠? */ printf("계속하시려면 Enter 키를 눌러주세요.\n"); scanf("%c", &next); fflush(stdin); }
6. 음식의 갯수를 반환해줄 함수. ( get_cnt )
int get_cnt(char type) { int cnt = 0; /* 음식 뽑기 함수에서 본것과 똑같습니다. 단지 select 문이 조금 다를 뿐이죠 ~ */ EXEC SQL DECLARE get_cnt_cur CURSOR FOR select count(*) from select_food where food_type = :type; /* 식사 혹은 간식 종류의 데이터가 몇개나 있는지 count 함수를 통해 값을 뽑아냅니다. */ /* 커서를 오픈해 쿼리문을 실행해주고 ~ */ EXEC SQL OPEN get_cnt_cur; /* 수행된 쿼리에 담겨있는 값을 cnt 안에 저장 ~ */ EXEC SQL FETCH get_cnt_cur INTO :cnt; /* 값을 받아냈다면 이제 커서 종료 */ EXEC SQL CLOSE get_cnt_cur; /* 받아온 음식의 종류 갯수 반환 !! */ return cnt; }
7. 램덤 숫자를 반환해주는 함수. ( get_ran )
int get_ran(int cnt) /* cnt 안에는 식사 혹은 간식 종류의 최대 갯수가 들어있겠죠 ? */ { int ran=0; /* 만약 cnt 의 값이 0 이라면 0으로 나눌경우 에러가 발생하니 0을 리턴 해줍시다 !! */ if(cnt == 0) return 0; srand(time(NULL)); /* srand 를 이용해 매번 다양한 무작위 값이 나오게 해줍시다 !! */ ran = (rand() % cnt) + 1; /* 만약 cnt 값이 10 이라면 무작위 수 나누기 10을 한 나머지는 무조건 0 ~ 9 가 나오게 됩니다. 하지만 우리는 1~10이 필요하므로 +1을 해줍시다 !! */ return ran; /* 뽑힌 랜덤숫자 반환 ! */ }
8. 음식 추가 함수. ( food_insert )
void food_insert() { char food_name[20]; char food_phone[20]; char type; char next; memset(food_name, 0x00, sizeof(food_name)); memset(food_phone, 0x00, sizeof(food_phone)); /* 역시나 초기화....아 그냥 전역변수 쓸껄......... */ printf("음식명 입력 : "); scanf("%s", food_name); fflush(stdin); printf("음식 타입 입력 ( 식사 : 1 / 간식 : 2 ) : "); scanf("%c", &type); fflush(stdin); printf("전화번호 입력 : "); scanf("%s", food_phone); fflush(stdin); /* select 는 데이터를 받아와야해서 커서를 이용하지만 insert 는 그런게 없으니 아래처럼 사용 ! */ EXEC SQL INSERT INTO select_food(food_name, food_type, food_phone, insert_date) VALUES (:food_name, :type, :food_phone, sysdate); /* select_food 에 사용자의 입력값 저장 ~ */ /* 위에서 수행한 상태를 저장해라 !! */ EXEC SQL COMMIT WORK; /* 역시나 여기서 빠져나가면 food_roop 에서 clear 을 만나므로 추가에 대한 결과를 충분히 보여주고 사용자가 다음단계로 다시 진행할수 있게 입력값을 받아요~ */ printf("추가 성공!!!\n"); printf("계속하시려면 Enter 키를 눌러주세요.\n"); scanf("%c", &next); fflush(stdin); }
9. 음식 정보 수정 함수. ( food_update )
void food_update() { char update_food[20]; char food_name[20]; char food_type; char food_phone[20]; char next; /* 수정을 위해 수정하고 싶은 음식명을 사용자로부터 입력 받습니다. */ printf("수정할 음식명 입력 : "); scanf("%s", update_food); fflush(stdin); /* 여기부터는 새로 들어갈 데이터 입력 ~ */ printf("음식명: "); scanf("%s", food_name); fflush(stdin); printf("음식 타입: "); scanf("%c", &food_type); fflush(stdin); printf("전화번호: "); scanf("%s", food_phone); fflush(stdin); /* insert 문을 입력했을때와 동일하게 update 문을 추가추가추가 ! */ EXEC SQL update select_food set food_name=:food_name, food_type=:food_type, food_phone=:food_phone where food_name = :update_food; /* 방금 수행한 update 문도 바로 commit 해주세요 ~ ! */ EXEC SQL COMMIT WORK; /* 이제 이거 왜 있는지 그만 말해도 아시죠 ? 하하.. */ printf("수정성공\n"); printf("계속하시려면 Enter 키를 눌러주세요.\n"); scanf("%c", &next); fflush(stdin); }
10. 음식 삭제 함수. ( food_del )
void food_del() { char next; char food_name[20]; memset(food_name, 0x00, sizeof(food_name)); /* 삭제 하고자 하는 음식명을 입력 받고 ~ */ printf("삭제할 음식이름 입력: "); scanf("%s", food_name); fflush(stdin); /* 사용자로부터 입력받은 값과 동일한 데이터를 삭제 해줍시다 ! */ EXEC SQL delete from select_food where food_name = :food_name; /* 삭제 수행수 commit */ EXEC SQL COMMIT WORK; /* 말하지 않아도 알아요 ~ */ printf("삭제 성공!!!\n"); printf("계속하시려면 Enter 키를 눌러주세요.\n"); scanf("%c", &next); fflush(stdin); }
11. 음식 출력 함수. ( food_print )
void food_print() { char food_name[20]; char food_phone[20]; char insert_date[20]; char type; char next; memset(food_name, 0x00, sizeof(food_name)); memset(food_phone, 0x00, sizeof(food_phone)); memset(insert_date, 0x00, sizeof(insert_date)); printf("###################################################\n"); printf("#################### 음식 출력 ####################\n"); printf("###################################################\n"); /* 여기는 조금 주의 깊게 보시는게 좋습니다. 기존의 select 문에선 한개의 데이터만 있었지만 여기선 각 종류에따라 모든 음식을 출력해야해서 데이터를 받는부분이 조금 다릅니다. 여러개의 데이터를 어떻게 처리해야 하는지 참고 해주세요 ! */ EXEC SQL DECLARE get_print_cur CURSOR FOR select food_name, food_phone, insert_date, food_type from select_food order by food_type; /* 커서를 실행 !! */ EXEC SQL OPEN get_print_cur; while(1) /* 여러개의 데이터를 받아야 하므로 일단 무한루프 !! */ { /* 커서를 통해 데이터를 하나씩 받아오기 시작. */ EXEC SQL FETCH get_print_cur INTO :food_name, :food_phone, :insert_date, :type; /* 여기!!!! sqlcode 에 담겨진 결과값이 1403 혹은 100일 경우엔 더이상 데이터가 없다는 뜻입니다. 데이터가 아예 0개 일경우도 여기에 속합니다. */ if(sqlca.sqlcode == 1403 || sqlca.sqlcode == 100) break; printf("음식 : [%s]\n", food_name); printf("번호 : [%s]\n", food_phone); printf("종류 : [%c]\n", type); printf("입력날자 : [%s]\n", insert_date); printf("###################################################\n"); memset(food_name, 0x00, sizeof(food_name)); memset(food_phone, 0x00, sizeof(food_phone)); memset(insert_date, 0x00, sizeof(insert_date)); /* 출력을 하고 다음 데이터를 받아올때 깔끔한 상태로 받기 위해 또다시 초기화 !! */ } /* 사용이 끝난 커서는 종료~ */ EXEC SQL CLOSE get_print_cur; printf("계속하시려면 Enter 키를 눌러주세요.\n"); scanf("%c", &next); fflush(stdin); }
자 각각의 기능을 수행해줄 모든 함수의 작성이 완료 됐습니다.
모든 함수의 소스에 주석으로 설명을 달아놨으니 다른 설명을 생략 하겠습니다.
소스와 설명을 봐도 살짝 이해가 안가거나 할수가 있는 부분은 역시
기존의 C 문법이 아닌 Pro*C 에 들어가는 sql 문의 수행 부분일탠데요.
확실하게 기억해두시면 좋을 부분을 설명 드리겠습니다.
1. 커서.
EXEC SQL DECLARE [커서명] CURSOR FOR
[커서를 통해 실행항 셀렉트 문]
최초 커서를 선언하고 커서를 통해 실행할 셀렉트문을 입력 해주는 것 입니다.
EXEC SQL OPEN [선언한 커서명];
커서를 선언했을때 입력해준 셀렉트문을 실행 하겠다는 뜻 입니다.
EXEC SQL FETCH [선언한 커서명] INTO :[담겨진 데이터를 받을 변수명];
커서를 통해 수행한 셀렉트문의 데이터를 어떤 변수에 담아오겠다고 명시해주는 것입니다.
EXEC SQL CLOSE [선언한 커서명];
사용을 끝낸 커서를 닫아주는 겁니다.
마치 소켓 통신이나 파일 입출력에서 FD 를 할당하고 모두 사용한뒤에 닫아주는것과
동일한 맥락이라고 보시면 됩니다.
2. INSERT, UPDATE, DELETE 등의 사용.
EXEC SQL [UPDATE, DELETE, INSERT 등의 쿼리문 입력];
커서는 오픈을 통해 쿼리를 수행하지만 UPDATE, DELETE, INSERT 와 같은 경우엔 오픈 없이
그대로 바로 수행이 됩니다.
따로 데이터를 돌려받을 필요도 없으므로 간단하게 진행 됩니다.
EXEC SQL COMMIT WORK;
SELECT 문이야 DB 의 데이터의 변함이 없어서 COMMIT 를 해주지 않아도 되지만
UPDATE, DELETE, INSERT 등의 경우엔 데이터가 변하므로 내가 수행한 사항이 있으면
COMMIT 를 해주기 위해 사용합니다.
3. 호스트 벨류. ( :변수명 )
위에서 SELECT 문이나 UPDATE 문 등에서 변수명 앞에 " : " 기호가 붙는데 이게 붙어 있는 변수는
호스트 벨류라는 뜻을 가집니다.
보다 자세한 내용은 나중에 따로 이론편에서 다룰 예정이라 아직 자세한 정보를 갖고 있지 않아 간략하게만
말씀 드리자면, DB 에서 사용하는 컬럼등이 아닌 내가 개발한 프로그램에서 사용하는 변수이다 라는 뜻입니다.
간단하게 예를 들면 만약 저런 기호가 없이
SELECT * FROM TEST_TAB
WHERE TEST_NAME = TEST_NAME;
사용자로부터 입력받은 TEST_NAME 과 동일한 값을 디비에서 찾기위해
이렇게 쿼리를 한다면 Pro*C 에서는 TEST_NAME 을 where 절로 비교할때
당연히 디비안에 있는 TEST_NAME 을 사용하게 됩니다.
내가 원하는건 사용자로부터 입력 받은 TEST_NAME 과 동일한 값을 가지는 데이터를
얻고 싶은데 뒤쪽에 붙은 TEST_NAME 이 호스트 벨류라고 명시해 주지 않았기 때문에
디비에 있는 TEST_NAME 을 사용해서 결과를 돌려주게 됩니다.
그래서 !! 우리는 반드시 내가 비교하고자 하는 값이나 데이터를 받아올 변수 등을 입력할때 !
꼭!!! 이건 호스트 벨류이다 라고 " : " 기호를 붙여 명시해 줘야 합니다 !!
제가 생각하기엔 이렇게 3가지가 가장 중요한 부분이 아닌가 싶어서 좀더 정리를 했는데..
다른분들도 이해가 가실지는 잘 모르겠네요 ㅠㅠ..
나중에 시간을 내서 이론에 대해 좀더 자료를 모아 저도 공부를 한번더 한다음
이론쪽으로 글을 쓰겠습니다.
아무튼 !!
2장은 이걸로 마무리 하겠습니다.
혹시나 이해가 안가는 부분이 있으면 댓글 남겨 주세요 ^^
3장에서는 이제 오늘 완성한 소스를 컴파일해서 간단하게 테스트를 진행하고,
잘 돌아간다면 예외처리 부분을 좀더 추가하는 부분을 쓸게요 !!
수고하셨습니다 !!
'Pro*C > 강좌' 카테고리의 다른 글
[Pro*C 강좌] 간식 & 식사 뽑기 만들기 - 4 (0) | 2014.06.17 |
---|---|
[Pro*C 강좌] 간식 & 식사 뽑기 만들기 - 3 (0) | 2014.06.16 |
[Pro*C 강좌] 간식 & 식사 뽑기 만들기 - 1 (2) | 2014.06.12 |