데이터 무결성이란 데이터베이스에 들어 있는 데이터의 정확성을 보장하기 위해 데이터의 변경이나 수정시 제한을 두어 안정성을 저해하는 요소를 막아 데이터 상태들을 항상 옳게 유지하는 것을 의미한다.

 

도메인 무결성

컬럼에 대한 무결성을 보장하는 것으로써 해당 컬럼이 널을 허용하거나, 혹은 데이터타입이 적절하거나, 적정한 형식의 데이터가 저장되어있는지를 확인하는것으로써 체크(Check), 디폴트(Default), 룰(Rule) 등의 제약(Constraints)들로 이러한 도메인 무결성을 보장할 수 있다.

 

주민등록번호 컬럼에 틀린 형식의 데이터가 들어가는 경우, '남'|'여'로 입력되는 성별 컬럼에 '남자', 'Male' 등의 데이터가 들어가는 것도 도메인 무결성을 위반하는 것이다.

 

개체 무결성

테이블에 저장되어 있는 컬럼의 값이 동일 개체, 즉 테이블에 저장되어 있는 다른 행의 컬럼값과 비교했을때 중복되지 않아야 하는 경우 이러한 무결성을 지키도록 하는 것이 개체 무결성이다.

사용자 테이블(TB_Member) 과 같이 모든 사용자가 구분된다는 전제 조건이 있는 테이블이라면 동일한 아이디나 주민등록번호가 저장될 경우 개체 무결성을 위한한 것이다.

이러한 개체 무결성을 지원하는 구문은 프라이머리키 제약조건(Primary key constraints)이나 유니크(Unique constraints) 등을 통해 구현할 수 있다.

 

참조 무결성

개체와 개체간에 참조관계가 있을 대 이 두 개체간에 데이터가 일관성을 가질 수 있도록 보증하는 방법을 참조 무결성이라 한다.

일반적으로 외부키(Foreign key)로 제약을 주어 무결성을 보장할 수 있고 트리거를 통해서 데이터의 변경이 있을 때 관련 값들을 재조정할 수 있다.

 

'프로그램 > 데이터베이스' 카테고리의 다른 글

ER 다이어그램 (Entity-Relationship Diagram)  (0) 2017.09.19
[oracle] 토드팁 - sql자동생성  (0) 2016.12.30
DB 기본 용어 (SQL, DDL, DML, DCL, TCL)  (0) 2016.11.28
클라이언트/서버  (0) 2016.11.28
Osi 7 layer  (0) 2016.11.28

Mysql을 처음 설치할 때 database root 계정으로 사용할 password를 설정한다. 하지만 시간이 오래 지나서 그때 설정한 password를 기억할 수 없다면 다음의 방법으로 재설정할 수 있다. (Ubuntu 12.04 기준)

Step 1. 실행중인 mysql service를 중지 시킨다.

Step 2. Password를 검사하지 않도록 mysql 환경설정 파일을 수정한다.
: /etc/mysql/my.conf file에 skip-grant-tables를 추가하면 password를 검사하지 않는다.

Step 3. 새로운 설정 값으로 mysql service를 실행한다.

Step 4. root 계정으로 mysql database를 연다.

Step 5. root password를 재설정한다.

Step 6. my.conf를 복원하고 mysql service를 재실행 시킨다. 

조인(JOIN)은 관계형 데이터베이스(Relational Database, RDB)의 꽃이라고 불립니다. 조인을 명확하게 이해하기 위해서는 관계형 데이터베이스에 대한 이해가 필요합니다. 관계형 데이터베이스에 대한 설명은 이 글의 범위를 벗어나므로, 관계형 데이터베이스에 대한 자세한 내용은 다음 링크를 참조하시기 바랍니다. 참고로 관계형 데이터베이스의 특징을 한 문장으로 요약하면, 정규화(Normalization) 이론을 활용하여 데이터 이상 현상을 제거하고, 데이터 중복을 최소화하여 데이터를 관리하는 것입니다.


조인이 왜 필요할까요?

관계형 데이터베이스의 구조적 특징으로 말미암아 정규화를 수행하면 의미 있는 데이터의 집합으로 테이블이 구성되고, 각 테이블끼리는 관계(Relationship)를 갖게 됩니다. 이와 같은 특징으로 관계형 데이터베이스는 저장 공간의 효율성과 확장성이 향상되게 됩니다. 다른 한편으로는 서로 관계있는 데이터가 여러 테이블로 나뉘어 저장되므로, 각 테이블에 저장된 데이터를 효과적으로 검색하기 위해 조인이 필요합니다. 즉, 조인은 각 테이블 간 의미 있는 데이터(행)를 연결하는 데 활용되는 메커니즘입니다.


예제 데이터

이 글에서 사용하는 예제는 Oracle에서 제공하는 샘플 스키마(EMP, DEPT)를 활용하여 설명하겠습니다. 예제 데이터는 다음 링크에서 확인하실 수 있습니다. 이뿐만 아니라, 각 DBMS별로 조인을 사용하는 문법이 다르므로, ANSI SQL에서 추천하는 문법으로 설명하겠습니다.


내부 조인(INNER JOIN)

내부 조인(INNER JOIN)은 가장 일반적인 조인 형태로 가장 많이 활용하는 조인 기법입니다. 내부 조인은 둘 이상의 테이블에 존재하는 공통 컬럼(속성)의 값이 같은 것을 결과로 추출합니다. 


내부 조인에는 대표적으로 동등 조인(EQUI JOIN), 자연 조인(NATURAL JOIN), 그리고 교차 조인(CROSS JOIN) 등으로 구분할 수 있습니다. 


동등 조인(EQUI JOIN)

동등 조인 혹은 이퀴(Equi) 조인이라고 하며, 조인의 가장 일반적인 활용 형식입니다. 동등 조인은 이름에서 유추할 수 있듯이, 둘 이상의 테이블에 존재하는 공통 컬럼(속성)의 동등 비교만을 사용하며, 부등호 조인은 동등 조인에 포함하지 않습니다.


조인 문법은 명시적 표현법(Explicit Notation)과 묵시적 표현법(Implicit Notation)이 있습니다. 개인적으로는 명시적 표현법을 활용하는 것을 추천합니다만, 현실적으로는 묵시적 표현법이 더 많이 활용되는 것 같습니다. 일반적으로 두 표현법의 성능에는 큰 차이가 없는 것으로 알려져 있습니다.


Explicit Notation
SELECT * FROM emp INNER JOIN dept ON emp.deptno = dept.deptno

Implicit Notation
SELECT * FROM emp, dept WHERE emp.deptno = dept.deptno


동등 조인의 수행 결과


자연 조인(NATURAL JOIN)

자연 조인은 동등 조인과 거의 유사합니다. 단, 조인 대상 테이블의 모든 컬럼을 비교하여, 같은 컬럼명을 가진 컬럼으로 조인을 수행합니다. 이때, 같은 이름을 가진 컬럼은 한 번만 추출합니다. 동등 조인에서 수행했던 결과를 살펴보면 DEPTNO가 두 번 추출된 것을 확인할 수 있지만, 자연 조인의 결과를 살펴보면 DEPTNO를 기준으로 조인을 수행하고, 한 번만 추출된 것을 확인할 수 있습니다.


Explicit Notation
SELECT * FROM emp NATURAL JOIN dept


자연 조인의 수행 결과


교차 조인(CROSS JOIN)

교차 조인은 카티션 프로덕트(Cartesian Product)로 알려져 있으며, 조인에 참여한 테이블들의 모든 데이터가 추출됩니다. 교차 조인이 발생하는 상황은 다음과 같습니다.


  1. JOIN 조건을 잘못 기술했을 때
  2. JOIN 조건을 정의하지 않았을 때
  3. 조인 조건이 조인 조건에 참여하는 테이블의 모든 행이 조인되는 경우


SQL 작성자가 교차 조인을 유도하지 않은 상황에서 교차 조인이 발생하는 경우 큰 문제가 발생합니다. 그러므로 조인을 수행한 후, 반드시 결과를 확인하는 습관을 들여야 합니다. 교차 조인을 예방하기 위한 한 가지 팁은 조인에 참여하는 테이블의 수가 N개라면, 최소한 N-1개의 조인 조건이 질의 안에 포함되어야 합니다.


Explicit Notation
SELECT * FROM emp CROSS JOIN dept
Implicit Notation
SELECT * FROM emp, dept


교차 조인의 수행 결과


셀프 조인(SELF JOIN)

셀프 조인은 자가 조인이라고도 하며, 같은 두 테이블을 활용하여 데이터를 추출하는 조인 기법입니다. 예를 들어, EMP 테이블의 내용을 살펴보면, 각 사원별로 관리자(MGR)가 있습니다. 이때, 사원 정보와 관리자 정보를 함께 보고 싶을 때, 활용하는 조인 기법입니다.

Explicit Notation
SELECT e.empno, e.ename, e.job, e.hiredate, e.sal, m.empno, m.ename, m.job FROM emp E INNER JOIN emp M ON E.mgr = M.empno
Implicit Notation
SELECT e.empno, e.ename, e.job, e.hiredate, e.sal, m.empno, m.ename, m.job FROM emp E, emp M WHERE E.mgr = M.empno


셀프 조인의 수행 결과


외부 조인(OUTER JOIN)

지금까지 살펴본 내부 조인은 조인 대상 테이블에서 공통인 컬럼 값을 기반으로 결과 집합을 생성합니다. 그러나 때때로 조인 대상 테이블에서 특정 테이블의 데이터가 모두 필요한 상황이 있습니다. 이런 요구 사항이 발생했을 때, 외부 조인을 활용하여 효과적으로 결과 집합을 생성할 수 있습니다. 외부 조인은 모두 3가지가 있습니다. 외부 조인의 결과는 생략하겠습니다. 직접 수행하시고, 데이터를 보면서 이해하시는 것을 추천합니다.


외부 조인은 RDBMS 제조사 별로 표기법이 다릅니다. 이 글에서는 ANSI SQL 문법을 기준으로 표기하겠습니다.


왼쪽 외부 조인(LEFT OUTER JOIN)

왼쪽 외부 조인은 우측 테이블(예제: DEPT)에 조인할 컬럼의 값이 없는 경우 사용합니다. 즉, 좌측 테이블의 모든 데이터를 포함하는 결과 집합을 생성합니다. 왼쪽 외부 조인을 그림으로 표현하면 다음과 같습니다.


왼쪽 외부 조인의 예


Left outer join Notation
SELECT * FROM emp LEFT OUTER JOIN dept ON emp.deptno = dept.deptno;


오른쪽 외부 조인(RIGHT OUTER JOIN)

오른쪽 외부 조인은 좌측 테이블(예: EMP)에 조인할 컬럼의 값이 없는 경우 사용합니다. 즉, 우측 테이블의 모든 데이터를 포함하는 결과 집합을 생성합니다. 오른쪽 외부 조인을 그림으로 표현하면 다음과 같습니다.


오른쪽 외부 조인의 예


Right outer join Notation
SELECT * FROM emp RIGHT OUTER JOIN dept ON emp.deptno = dept.deptno;


완전 외부 조인(FULL OUTER JOIN)

완전 외부 조인은은 양쪽 테이블 모두 OUTER JOIN이 필요할 때 사용합니다.


완전 외부 조인의 예


Full outer join Notation
SELECT * FROM emp FULL OUTER JOIN dept ON emp.deptno = dept.deptno;


안티 조인(ANTI JOIN)

안티 조인은 부정형 조인이라고도 하며, 테이블에서 조인의 대상이 되는 테이블과 일치하지 않는 데이터를 추출하는 연산하는 조인 기법입니다.


Full outer join Notation
SELECT e.* FROM emp e WHERE e.empno >= 7500 AND NOT EXISTS (SELECT 'x' FROM dept d WHERE d.deptno = e.deptno AND d.deptno <= 20);


안티 조인의 수행 결과


세미 조인(SEMI JOIN)

세미 조인이라는 이름에서 알 수 있듯이 조인과 유사하게 데이터를 연결하는 조인 기법입니다. 세미 조인은 분산 질의를 효율적으로 수행하기 위해 도입[각주:1]되었습니다. 최근에는 서브 쿼리[각주:2]를 사용할 때, 메인 쿼리와의 연결을 하기 위해 적용하는 유사 조인을 의미하기도 합니다.


Full outer join Notation
SELECT e.* FROM emp e WHERE e.empno >= 7500 AND EXISTS (SELECT /*+ UNNEST NL_SJ */ 'x' FROM dept d WHERE d.deptno = e.deptno AND d.deptno <= 20);

주) 필터 방식으로 처리가 되어 세미 조인으로 유도하기 위해 힌트(UNNEST NL_SJ)를 적용했습니다.


세미 조인의 수행 결과


조인을 사용할 때 주의사항

본 절에서는 조인을 사용할 때, 주의해야 할 사항을 몇 가지 소개하겠습니다.

첫째, SQL 문장의 의미를 제대로 파악해야 합니다. 조금 어려운 내용일지도 모르겠습니다만, SQL을 어떻게 작성하느냐에 따라 성능이 크게 좌우됩니다. 그러므로 어떤 질의를 수행할 것인지를 명확하게 정의한 후, 비효율을 제거하여 최적의 SQL을 작성해야 합니다.

둘째, 조인 조건을 명확하게 제공해야 합니다. 조인 조건을 명확하게 제공하지 않을 경우, 의도치 않게 CROSS JOIN(Cartesian Product)가 수행될 수 있기 때문입니다. 

셋째, 조인 적용 후 테스트를 수행해야 합니다. 적은 수의 테이블을 조인할 때는 큰 문제가 없지만, 여러 개의 테이블을 조인할 때는 예상하지 않은 결과를 얻을 수 있습니다. 이때, 각각의 조인을 따로 테스트하면, 문제가 발생했을 경우 빠르면서도 쉽게 해결할 수 있습니다.


조인을 사용할 때 고려사항

이 단락을 작성하는 것이 옳을지 아닐지에 대해 고민을 많이 했습니다. 자세하게 들어가면, 이 글에서 이야기하고자 하는 내용의 범위를 벗어나므로 간략히 소개하는 것으로 정리를 하겠습니다.

조인은 비교적 많은(?) 자원을 소비하는 연산입니다. 그러므로 아무런 계획과 생각 없이 오로지 결과만 바라보고, 조인을 맺는 질의를 작성하면 DBMS는 고통받고 사용자는 답답한 상황에 놓일 것입니다. 이 단락에서 당부하고 싶은 내용은 크게 두 가지입니다.

첫째, 조인 대상의 양을 최소화하는 것입니다. 다른 표현으로는 조인할 대상의 집합을 최소화하라고 볼 수 있습니다. 즉, 집합을 최소화할 방법이 있으면, 조건을 먼저 적용하여 관계를 맺을 집합을 최소화한 후, 조인을 맺는 것이 효율적이란 이야기입니다.

둘째, 효과적인 인덱스의 활용입니다. 인덱스를 활용하면, 조인 연산의 비용을 극적으로 낮출 수 있습니다.


마치면서

지금까지 조인에 대하여 간략하게 알아봤습니다. 이 글에서 소개하는 내용은 비교적 쉽게 이해할 수 있을 것으로 예상합니다. 그러나 실제 환경에서 조인을 사용하는 것은 어떨까요? 실제 환경에서의 조인은 많은 생각을 하게 만듭니다. 또한, 앞에서 간략히 언급했듯이, 조인을 효과적으로 사용하기 위해 고려해야 할 사항도 많습니다.

조인을 제대로 활용하는 방법은 무엇일까요? 개인적인 생각으로는 하나밖에 없습니다. 많이 경험하고 실패하고 깨우침을 얻는 것입니다.


-출처: http://blog.ngelmaum.org/


Database를 제어하는 명령어 중

확인 : show databases;

생성 : create database kkk;

삭제 : drop database kkk;


*desc  테이블 구조 볼때


1. 사용자 추가


1-1. mysql.user(주의할 점 시스템 반영 -> flush privileges;)

       -> insert into user (host, user, password)

                       values( 'localhost, 'kadmin', password('1111'));

1-2. create user kadmin@localhost identified by '1111';

*사용자 계정 삭제

DROP USER [user명]@[server명];
ex) drop user user1@localhost;


2. 특정 database를 특정 user가 사용할 수 있게 권한 부여


2-1. mysql.db

 -> insert into db values ('localhost','kkk','kadmin','y','y','y','y','y','y','y','y','y','y','y','y','y','y','y','y','y','y','y');

2-2 grant all on kkk.* to kadmin@localhost;



3. 마지막…….한 개인데요. 귀찮은거죠 한 줄로 끝내고 싶습니다.

     grant all on skworld.* to sksk@localhost identified by ‘ssss’;

MySQL에 root로 접속 한 뒤
use mysql;
select host,user from user;
현재 생성된 db의 사용자 계정들을 확인할 수 있다.

사용자 계정 생성
GRANT USAGE ON [database명].[table명] TO [user명]@[server명] IDENTIFIED BY [‘패스워드’];
ex) grant usage on database.* to user1@localhost identified by ‘user1’;

생성된 사용자 계정 권한 설정
GRANT ALL ON [database명].[table명] TO [user명]@[server명];    =>  모든 권한을 준다
GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,ALTER ON [database명].[tabel명] TO [user명]@[server명];   => 특정 권한을 준다
ex) grant all on database.* to user1@localhost;
grant select,insert,update,delete,create,drop.index,alter on database.* to user1@localhost;

REVOKE ALL ON [database명].[table명] FROM [user명]@[server명];    =>  모든 권한을 삭제한다
REVOKE DROP ON [database명].[table명] FROM [user명]@[server명];    => 특정 권한(drop)을 삭제한다
ex) revoke all on database.* from user1@localhost;
revoke drop,index on database.* from user1@localhost;

계정 권한을 새로 로드
FLUSH PRIVILEGES;
flush privileges;

사용자 계정 삭제
DROP USER [user명]@[server명];
ex) drop user user1@localhost;


오늘은 mysql 설치시 사용자를 추가하는 방법에 대해 남겨볼까 한다. 할때마다 잘 기억이 안나서 검색엔진에 항상 의존을 하게 되는데 다음에는 검색엔진에 의존하지 않고 바로 블로그에서 검색해서 보면 좋을 듯 하다. 


사용자를 생성하는 방법은 여러가지가 있는데 아래 방법이 가장 쉽고 심플하다. 다른 방법들은 쿼리문을 직접 던져야 하기 때문에 솔직히 말해서 좀 불편하다. 혹시나 오랜만에 해 보거나 처음해 보는 사람들을 위해서 서버에 mysql을 설치한 후 mysql에 로그인한 후 아래 명령를 실행시켜준다.


-  mysql 서버 로그인하기

 $ mysql -uroot -prootpassword mysql


- 다른 PC에서 mysql 서버로 접속을 하기 위한 사용자 추가

 mysql> create user 'userId'@'%' identified by 'userpassword';


- 위 사용자에게 모든 것을 할 수 있는 권한 주기

mysql> grant all privileges on *.* to 'userid'@'%';


- 위 사용자에게 특정 DB를 관리할수 있는 권한 주기

 mysql> grant all privileges on dbname.* to 'userid'@'%';


- 로컬PC에서 mysql로 접속하기 위한 사용자 추가

 mysql> create user 'userId'@'localhost' identified by 'userpassword';


- 위 사용자에게 모든 것을 할 수 있는 권한 주기

 mysql> grant all privileges on *.* to 'userid'@'localhost';


- 위 사용자에게 특정 DB를 관리할 수 있는 권한 주기

 mysql> grant all privileges on dbname.* to 'userid'@'localhost';



대부분 사용자를 생성하고 권한을 줄때는 특정 DB를 관리하는 계정을 따로 만들기 위해서이다. 그러므로 사용자를 생성하고 권한을 줄때는 특정 DB를 관리할 수 있는 권한만 주면 된다.

C언어의 메모리 구조


프로그램을 실행시키면 운영체제는 우리가 실행시킨 프로그램을 위해 메모리 공간을 할당해준다. 

할당되는 메모리 공간은 크게 스택(Stack), 힙(Heap), 데이터(Data)영역으로 나뉘어진다. 

이러한 메모리 공간이 어떠한 용도로 언제, 어디서 할당되는지 알아보도록 하자.


할당 시기 : 프로그램이 실행될 때마다

할당 장소 : 메인 메모리(RAM)

할당 용도 : 프로그램 실행 시 필요한 메모리 공간(지역변수, 전역변수 선언을 위해) 할당




데이터(Data) 영역


 - 전역 변수와 static 변수가 할당되는 영역

 - 프로그램의 시작과 동시에 할당되고, 프로그램이 종료되어야 메모리에서 소멸됨

 

#include <stdio.h>

int a = 10;	// 데이터 영역에 할당
int b = 20;	// 데이터 영역에 할당

int main() {

	...

	return 0;
}

위와 같은 코드에서 int형 변수 ab는 프로그램 실행시, main 함수가 호출되기 전에 데이터 영역에 할당된다.

그렇기 때문에 프로그램이 종료될 때까지 메모리상에 존재한다.

(전역변수가 프로그램이 종료될 때 까지 존재하는 이유)



스택(Stack) 영역


 - 함수 호출 시 생성되는 지역 변수와 매개 변수가 저장되는 영역

 - 함수 호출이 완료되면 사라짐

 

#include <stdio.h>

void fct1(int);
void fct2(int);

int a = 10;	// 데이터 영역에 할당
int b = 20;	// 데이터 영역에 할당

int main() {

	int i = 100;	// 지역변수 i가 스택 영역에 할당

	fct1(i);
	fct2(i);

	return 0;
}

void fct1(int c) {
	int d = 30;	// 매개변수 c와 지역변수 d가 스택영역에 할당
}

void fct2(int e) {
	int f = 40;	// 매개변수 e와 지역변수 f가 스택영역에 할당
}

main함수와 fct1fct2라는 함수를 추가하였다. 

ab를 데이터 영역에 할당한 뒤에 main함수를 호출하면서 int형 변수 i는 지역변수로서 스택영역에 할당된다.

그 뒤에 fct1()이라는 함수를 호출하면서 fct1함수의 매개변수인 c와 d가 스택영역에 할당된다.

fct1()이라는 함수호출이 끝나면 c와 d는 스택영역에서 삭제되며, 

그 뒤 fct2()라는 함수를 호출하면서 매개변수 e와 지역변수 f가 스택영역에 할당된다.

스택영역은 그 이름그대로 스택의 성질을 띄고있다.


 

힙(Heap) 영역


 - 필요에 의해 동적으로 메모리를 할당 할 때 사용


지금까지 데이터영역과 스택영역을 알아보았는데, 저 두가지 영역만 있으면 코드를 문제없이 짤 수 있을것 처럼 보인다.

그럼 힙영역은 왜 필요한 것일까?


힙 영역은 왜 필요할까?

제일 첫번째 그림을 보면 힙 영역은 프로그래머가 할당한다고 되어있다. 

그럼 언제 할당을 할까? 

배열을 예를들어서 설명을 하겠다.


우리는 배열을 선언할때 상수로 선언을 한다.

int main() {

	// 정상적인 배열선언
	int arr[10];

	// 비 정상적인 배열선언
	int i = 0;
	scanf("%d", &i);
	int arr[i];

	return 0;
}

배열의 길이를 사용자가 입력한 숫자로 잡아주는 것은 비 정상적인 배열선언이다. 왜 비 정상적일까?

메모리 구조에 대해서 잘 파악하고 있다면 당연한 이야기다.


제일 첫번째 그림을 다시보자, 스택 영역에 할당될 메모리의 크기는 컴파일 타임(컴파일 하는 동안)에 결정된다고 되어있다.

정상적인 배열 선언의 경우 arr이라는 배열의 크기가 40바이트 라는것을 알 수 있다.

하지만 비 정상적인 배열선언의 경우 i의 크기가 4바이트 라는 것을 알 수 는 있으나, arr이라는 배열의 크기는 알 수 없다.


그렇다면 다음과 같이 배열을 선언할 때는 문제가 없을까?

int main() {
	
	int i = 10;
	int arr[i];

	return 0;
}

i 라는 변수가 10이기 때문에 arr이라는 배열의 크기가 10이라는 것을 알 수 있지 않을까?

결과는 아니다.


컴파일을 하는 동안 i가 4바이트의 크기라는 것을 알 수는 있으나, 그 값이 10으로 초기화 되었다는 사실은 무시하고 넘어간다. 값이 10으로 초기화 되었다는 사실은 실행되는 동안, 즉 런타임에 결정된다.

그렇기 때문에 컴파일러는 arr의 크기가 40바이트가 된다는 사실을 알 수 없다. 


사용자의 요구에 맞게 메모리를 할당해 주기 위해서는(런타임에 메모리 크기를 결정하고 싶을 때) 메모리 동적 할당을 통해 힙 영역에 메모리를 할당해야 한다.


힙 영역 : 할당해야 할 메모리의 크기를 프로그램이 실행되는 동안 결정해야 하는 경우(런 타임때) 유용하게 사용되는 공간


힙 영역을 사용하기 위해서는 동적할당에 대해서 공부하여야 한다.

switch문이란, 조건문의 일종인데, 여러 개의 if~else 문을 대신하여 간결하게 작성할 때 사용하는 것입니다. if~else 문이 중첩되어 있으면 가독성이 떨어지기 때문에 스위치문이 필요합니다.

그러나 switch문 다음의 괄호()에는 "i <= 0" 이런 식의 판단문이 들어갈 수는 없고, 정수형이나 문자형(char)의 숫자만 들어갈 수 있는 제약이 있습니다. double 등의 실수는 안되고 error C2450: switch expression of type 'double' is illegal 이런 에러가 납니다.

switch는 "함수"가 아니고 "키워드"입니다.

  switch (정수) {
    case 상수 : 실행문; break;
    case 상수 : 실행문; break;
    case 상수 : 실행문; break;
    case 상수 : 실행문; break;

    default : 실행문; break;
  }



스위치문에서 주의해야 할 점은 각 case문 끝에 break; 를 꼭 붙여야 한다는 것입니다. break; 가 없으면, 그 아래쪽의 case문들까지 모두 실행되어 버립니다. break;를 만날 때까지 멈추지 않고 계속 실행됩니다.

의도적으로 break;를 생략한 경우가 아니라, 실수로 누락했을 때는 소스가 폭주하여 위험한 에러가 발생할 수 있습니다. 따라서 우선 무조건 break;를 붙여 놓고 소스의 흐름을 검토하는 것이 안전합니다.

default 라는 것은, 위의 case문들 중에서 어느 것도 해당되지 않을 때 실행할 코드입니다. 필요하지 않다면 default문이 없어도 됩니다.


switch문 사용 방법 예제 소스


소스 파일명: example.cpp
(※ 스크롤 박스 사용법: 박스 안을 마우스로 클릭한 후, 키보드의 좌우 화살표키를 누르면 양옆으로 움직일 수 있습니다. 박스에서 다시 나오려면, 박스 바깥의 아무곳이나 클릭하면 됩니다.)

#include <stdio.h>
#include <conio.h> // getch()
#include <ctype.h> // tolower()


int main(void) {

  int i = 1;

/*
i 의 값이 1일 경우에는 "자장면"이 출력
i 의 값이 2일 경우에는 break가 없기에
군만두"와 "탕수육"이 한꺼번에 같이 출력

i 의 값이 3일 경우에는 "탕수육"이 출력
i 의 값이 4일 경우에는 "짬뽕"이 출력
만약 i 의 값이 그밖의 숫자일 경우에는 "그런 음식은 없습니다."가 출력
*/

  switch (i) {
    case 1 : printf("자장면\n"); break;
    case 2 : printf("군만두\n"); // 아래의 탕수육도 실행됨
    case 3 : printf("탕수육\n"); break;
    case 4 : printf("짬뽕\n"); break;

    default : printf("그런 음식은 없습니다."); break;
  }



  // 또한 아래와 같이, 문자(char)형으로도 판단할 수 있음
  // 다만 double, float 같은 실수형은 불가능

  char ch = (char) getch(); // 키보드에서 문자 1개 입력 받기
  // 글자를 소문자로 변환
  // (대소문자 구분 없이 입력받기 위해)
  ch = (char) tolower(ch);


  switch (ch) {
    case 'a'  : printf("A를 누르셨군요\n");
                break;
    case 'b'  : printf("B를 누르셨군요\n");
                break;
    case 'c'  : printf("C를 누르셨군요\n");
                break;
    case '9'  : printf("9를 누르셨군요\n");
                break;
    case 0x0D : printf("Enter키를 누르셨군요\n");
                break;
    case 0x1B : printf("Esc키를 누르셨군요\n");
                break;

    default   : printf("그밖의 문자...\n");
                break;
  }



  return 0;
}


C언어 포인터 기본에 대해서 배워보자

 

C언어를 처음 접하신 분들은 포인터에 대해서 어려워 하시는 분들이 있는데 어려워 할 필요 없습니다.

 

C언어 포인터 에 대해서 조금만 이해하신다면 예제보고 테스트 소스를 코딩해보고 그러다 보면 언젠가 이게 뭐가 어렵지? 라는 생각을 하게 될 것입니다.

 

포인터에 대해서 조금 더 깊게 파고 들어가면 포인터연산, 함수포인터, 이중포인터 등등 여러가지가 나오는데 이 부분은 일단 포인터에 대해서 공부를 한다음 진도를 나가 봅시다.

 

C언어 를 테스트 할려면 컴파일러가 필요한데 아래 Dev-C++ 무료 컴파일러 입니다. 사용하시는 컴파일러가 있다면 굳이 다운로드 안하셔도 됩니다.

 

C언어 무료 컴파일러인 Dev-c++ 다운로드 방법 입니다.

http://seonissi.tistory.com/admin/entry/post/?id=25

 

 

Dev-c++ 간략한 사용법

http://seonissi.tistory.com/admin/entry/post/?id=26

 

 

C언어 포인터는 단순히 주소라고 생각하면 됩니다.

 

서울시 구로구 구로동 XXXX아파트 101동 1101호

우리가 사는 집엔 모두 주소가 붙듯이 변수를 선언하면 주소가 붙게 됩니다.


 

 

 

 

char 형태의 p라는 변수를 선언 했습니다.

 

 


선언된 변수를 printf문으로 출력해볼 경우 주소가 나타납니다..

 

 

 

 

 

 

 

이렇게 사용하면 변수의 주소를 공유하게 되 포인터로도 변수의 값을 사용 및 수정이 가능합니다. 예를 들어서 우리가 사는 주소에 A라는 사람이 들어가면 그 주소안에는 A라는 사람이 있는거고 B라는 사람이 들어가게 되면 그 주소안에는 B라는 사람이 있는것과 동일한 것입니다..

 

 

변수와 포인터의 주소값을 출력해볼 경우 동일하다라는걸 확인이 가능합니다.

 

 

  

 

포인터 p에 변수를 할당 하지 않을 경우 경우 각각 주소가 다릅니다.

 

위의 결과처럼 주소값이 1만 증가할수 있지만 경우에 따라서 주소값이 다른곳을 가르킬수 있습니다. 포인터가 가르키는 주소값은 메모리 어느 부분을 가르키고 있는데 주소를 할당하지 않은채 사용하다가는 언젠가는 프로

그램이 죽는 현상이 발생하니 주의해야 합니다.

 

 

C언어 포인터 예제 1

 

int main(int argc, char *argv[])

{
    char ch;
    char *p;

 

ch = 'a';
 
printf("ch = %c\n", ch);


    p = &ch;
 
    printf("*p = %c\n", *p);
    printf("ch = %c\n", ch);

}

 

위 예제를 보게 되면 char 형태의 ch를 선언하고 포인터 p를 선언했습니다.

char 형태의 변수값에 'a'라는 값을 넣고 포인터 p에 변수 ch 주소를 할당 했습니다.

그리고 포인터p 가 어떻게 변했느가를 살펴보는 예제 입니다.

 

 

 

 

 결과값을 보면 ch와 포인터p와 값이 동일하다는게 확인이 됩니다.

 

 

 


C언어 포인터 예제 2

int main(int argc, char *argv[])
{
 char ch1;
 char ch2;
 char *p;

 

 ch1 = 'a';
 ch2 = 'b';
 
 printf("ch1 = %c\n", ch1);
 printf("ch2 = %c\n", ch2);
 
 p = &ch1;
 printf("*p1 = %c\n", *p);
 
 p = &ch2;
 printf("*p2 = %c\n", *p);
}

 

 

 

결과값을 보면 할당된 포인터 값에 따라 포인터p의 값이 변환되는 것을 알수 있습니다.

이처럼 포인터는 주소값을 가지고 움직인다는것을 확인했습니다.

 

 

 

C언어 포인터 예제 3

void test1 (char *p)
{
 printf("test1 *p = %c\n", *p);
 *p = '1';
}

 

void test2 (char *p)

{

 printf("test2 *p = %c\n", *p);
 *p = 'a';
}

 

int main(int argc, char *argv[])
{
 char ch1 = 'z';

char *p;

 

 p = &ch1;

 

printf("*p = %c\n", *p);

 test1(p);
 printf("ch1 = %c\n", ch1);

 test2(p);
 printf("ch1 = %c\n", ch1);
}

 

위 예제를 보면 함수에 포인터를 대입해서 함수를 호출 했을때 인자값으로 전송 받았던 포인터 값에 대해 값이 변함이 없는지를 확인할 수 있는 예제 입니다.


거기에 인자값으로 받은 포인터에 다른 값을 대입했을 시 변수로 선언한 ch값이 변화가 있는지 없는지도 확인이 가능합니다.


함수안에서 값을 변경해도 main에서 선언된 ch1이라는 변수에도 영향을 미칩니다.


위의 예제와 같이 함수를 호출할때 함수에 포인터 변수나 주소를 전달하는 것을 call by reference라고 합니다..


ch값을 받고 할당 받은 포인터를 함수로 전달받고 그 값을 printf값으로 출력 하는 예제입니다.

 

 

 


결과값에서 봤듯이 인자값으로 받은 포인터가 ch값과 동일하다는것을 확인할 수 있습니다.


함수안에서 포인터 값을 변경해도 변수로 선언된 ch값 까지 변환되는 부분까지 확인이 가능합니다.


함수의 인자값으로 포인터를 사용해서 그 값을 공유할수 있다는 것입니다.
위와 같이 많이 사용하게 될텐데 포인터를 잘 사용해야 프로그램을 개발할때 모듈화를 하기가 쉽습니다.


이 부분은 더 공부를 해야 될 것입니다.

 


C언어 포인터 예제 4

int main(int argc, char *argv[])

{
 char ch1[10];
 char ch2[20];
 char *p;

 

 ch1[0] = '1';
 ch1[1] = '2';

 ch2[0] = 'a';
 ch2[1] = 'b';
 
 p = ch1;
 printf("ch1 p[0] = %c\n", p[0]);
 printf("ch1 p[1] = %c\n", p[1]);

 

 p = ch2;
 printf("ch2 p[0] = %c"\n, p[0]);
 printf("ch2 p[1] = %c\n", p[1]);
}

 

위 예제는 포인터와 배열에 대한 부분인데 ch1은 10개의 박스를 다 소유하고 있다고 생각하면 됩니다.

 

 

10개의 박스의 안에는 박스 사이즈에 맞게 물건을 넣을수 있는데 사이즈는 "char"형 이라고 보면 되고 물건은 "char"로 선언된 변수라고 보시면 됩니다.

 

포인터로 선언된 p로 ch1을 가르키게 되면 ch1의 데이타를 쓰기 및 읽어올수가 있습니다.


배열을 선언하고 그 배열을 포인터로 할당한 다음 간단하게 포인터로 제어를 하는 식으로 많이 사용합니다.


위의 예제를 보면 포인터 p에 ch1을 할당해서 ch1에 설정된 a, b값을 포인터 p로도 확인을 할수가 있습니다.


다시 포인터 p에 ch2를 할당하면 ch2와 동일하게 사용이 가능합니다.

 

 



결과값을 보시면 ch1과 ch2의 값을 포인터 p로 할당한 다음 printf문으로 출력했을 경우 동일하게 출력 된다는 것을 확인할 수 있습니다.

 

 

C언어 포인터 예제5

 

int main(int argc, char *argv[])

{
 char ch1 = '1';
 char *p1;
 char *p2;

 p1 = &ch1;
 p2 = &ch1;
 
 printf("*p1 = %c\n", *p1);
 printf("*p2 = %c\n", *p2);
 
*p1 = 'a';
 
 printf("*p2 = %c\n", *p2);
}

 

위의 예제를 보면 ch1의 값을 포인터p1과 포인터p2를 동시에 할당을 했습니다. 이럴경우 p1과 p2가 동일하다고 생각하면 되는데 같은 ch1의 변수를 할당받았고 p1을 수정했을 경우 p2의 값도 변한다는 것을 확인할 수 있습니다.


이렇게 포인터를 여러개 선언해서 같이 수정도 가능하다. ch1이 아니라 "p1 = p2"이렇게 선언해도 상관없습니다.
p1도 포인터고 p2도 포인터기 때문에 언제든 주소를 다시 할당할수 있습니다.

 

 

 

 

 

결과값을 보면 p1과 p2는 동일하게 변수 ch1의 값을 동일하게 printf문으로 출력을 하고 있음이 확인 가능합니다.

 

위의 예제처럼 포인터의 경우 다양한 사용이 가능합니다. 구조체를 선언해서 포인터로 할당해서 사용이 가능하고 여러가지 사용이 가능합니다. 위에는 간단하게 C언어 포인터 기본에 대해서만 예제를 설명했습니다.

 

C언어에서 포인터를 사용하지 않아도 프로그램 코딩은 가능하나 포인터를 사용하지 않고 프로그램 코딩을 하는 개발자는 없을 것입니다.

 

실제로 프로그램을 개발하게 되면 이렇게 간단하게 포인터를 사용하지 않고 포인터 연산도 들어가고 포인터 구조체도 쓰고 배열도 들어가고 복잡하게 진행이 됩니다.

 

포인터를 잘못사용할 시 메모리가 뒤죽박죽이 되버릴 수 있습니다. 그리고 포인터를 잘못 정의해도 컴파일러가 체크를 못하는 경우가 종종 발생하기 때문에 개발 완료 후 문제가 발생할 가능성이 있습니다. 그래서 좀더 세세한 코딩이 필요합니다.

도요타 급발진 문제처럼 버그가 발생할 수 있기 때문에 주의해서 개발을 해야 합니다.

MySQL에서 사용자의 로그인 보안 수준을 높이기 위해서,
MySQL 4.0.x 이하 버전과 MySQL 4.1.x 이상 버전의 PASSWORD() 함수의 구현 알고리즘이 달라졌다.
사실 아주 오래전 이야기이지만, 아직도 MySQL 4.0.x를 사용하는 사이트가 많고,
최근 들어서 MySQL 5.1이나 5.5 버전으로 업그레이드를 준비하면서 이런 내용이
업그레이드에 걸림돌이 되는 경우가 많은 것으로 보인다.



MySQL 4.0.x 이하 버전
  - PASSWORD()
  - OLD_PASSWORD() 함수는 없음


  mysql> SELECT PASSWORD('mypass');
  +--------------------+
  | PASSWORD('mypass') |
  +--------------------+
  | 6f8c114b58f2ce9e   |
  +--------------------+


MySQL 4.1.x 이상 버전
  - PASSWORD()         ==> 버전업된 암호화 함수
  - OLD_PASSWORD()     ==> 기존 암호화 (MySQL 4.0.x의 PASSWORD()와 동일)


  mysql>select PASSWORD('mypass');
  +-------------------------------------------+
  | password('mypass')                        |
  +-------------------------------------------+
  | *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
  +-------------------------------------------+
  
  mysql>select OLD_PASSWORD('mypass');
  +------------------------+
  | old_password('mypass') |
  +------------------------+
  | 6f8c114b58f2ce9e       |
  +------------------------+


위 결과를 보면, 새로운 암호화 알고리즘에 의해서 암호화된 내용은 암호 제일 앞에 "*" 마크가 붙게 되므로, 새로운 암호인지 예전 암호인지 쉽게 구분할 수 있다.


그런데, old_passwords=1로 설정하게 되면, MySQL 4.1.x 이상 버전에서도 아래와 같이 MySQL 4.0.x와 같은 결과를 보여주게 된다.
MySQL 4.1.x 이상 버전에서도, 이전 버전과 동일하게 짧은 암호화 문장으로 변환됨
  root@localhost:(none)>select PASSWORD('mypass');
  +--------------------+
  | PASSWORD('mypass') |
  +--------------------+
  | 6f8c114b58f2ce9e   |
  +--------------------+
  
  root@localhost:(none)>select OLD_PASSWORD('mypass');
  +------------------------+
  | OLD_PASSWORD('mypass') |
  +------------------------+
  | 6f8c114b58f2ce9e       |
  +------------------------+


MySQL 4.1.x 이상 버전에서 old_passwords=0 (기본 설정값)인 경우에는, mysql.user 테이블의 비밀번호가 
예전 버전에서 생성된 짧은 비밀번호라면 로그인할 수 없게 된다.


하지만, MySQL 4.1.x 이상 버전에서도 mysql.user 테이블에 사용자의 비밀번호가 예전 버전인 경우 로그인할 수 있도록 
해주기 위해서 old_passwords 라는 옵션을 1로 설정할 수 있도록 해둔 것이다.


근본적인 원인은 MySQL이 업그레이드되면서 기존 비밀번호의 암호화 수준을 보완하여 더 보안 수준을 높이면서 
이런 문제가 야기되었다는 것이며, 가능하다면 업그레이드된 긴 암호를 사용하는 것이 좋아 보인다.


더 중요한 것은
MySQL의 PASSWORD() 함수는 MySQL의 사용자 자체의 계정 및 비밀번호를 관리하기 위한 함수이지
일반 서비스용 계정및 암호를 관리하는 용도로는 적합하지 않다는 것이다.


예를 들어서 MySQL 4.0.x 버전의 회원정보가 예전 방식의 PASSWORD() 함수로 암호화되어서 저장되어져 있다면,
MySQL 4.1.x 이상의 PASSWORD() 함수로는 로그인이 되지 않게 될 것이다. (이 경우에는 OLD_PASSWORD()를 사용해야만 로그인을 할수 있게 된다.)
하지만, 이 경우 예전 버전의 PASSWORD()함수의 암호화 내용을 새로운 버전의 PASSWORD() 함수로 대체할 수 없다.
(한번 암호화된 문장은 다시 풀어낼 수 없는 형태 - 비대칭형 암호화 - 이기 때문)
서비스용 계정이 MySQL의 사용자 계정 암호화 방식에 의존하게 되면 이런 문제가 야기될 수 있으므로 
필요시에는 MD5와 같은 알고리즘을 사용할 것을 추천한다.


또한, 이렇게 두개 버전의 암호화 방식이 존재하는 상태에서
서비스용 MySQL의 버전 업그레이드와 연관되어서 명확히 해결하지 않고 업그레이드를 진행하게 되면,
일부 사용자의 암호는 예전 버전이고, 또 일부 사용자는 새로운 암호화 버전을 사용하도록 되어버리면
걷잡을 수 없는 혼란에 빠져들고, 이를 처리하기 위해서 또 한번 쿼리와 IF ~ ELSE가 필요해질 수도 있다는 것이다.

'프로그램 > MySql' 카테고리의 다른 글

[MySQL]사용자 계정 생성 및 삭제  (0) 2016.11.29
MYSQL 사용자 생성  (0) 2016.11.29
MySQL 계열의 FLUSH PRIVILEGES 명령어  (0) 2016.11.28
MySQL 내장 함수 (MySQL전용 함수)  (0) 2016.11.28
MySQL 기본 명령어 정리  (0) 2016.11.28

+ Recent posts