반응형

로버트 C.마틴의 '클린 코드'를 3장까지 읽고나서

이 책은 개발자들의 '필독서'라는 생각을 하게 되었다.

깃허브에서 잘 짜여진 소스코드와 내 소스코드를 비교해보며

어디서 차이가 발생하였는지 고민해본 경험이 있었다.

책을 읽고 답을 찾았다. 핵심은 '클린 코드'였다.

지금까지 공부한 지식을 개인적인 생각으로 녹여보고자 한다.


1. 클린코드란?

"유지보수가 쉬운 코드, 곧 읽기 좋은 코드"

 

 클린 코드란 '가독성이 높으며, 직접적인 코드'이다. 책에선 다방면으로 이야기 하는데 생각을 정리하자면 이렇다.

 

제3자가 코드를 읽거나 수정하기 불편하다면 그건 '클린 코드'가 아니다.

int AB(int a, int b){
    int c;
    c = a + b ;
    return c;
}


제3자가 코드를 읽거나 수정하기 쉬우면
그건 '클린 코드'다.

 

int getSum(int firstArg, int secondArg){
    int sum = firstArg + secondArg;
    return sum;
}

 

보다 싶이 클린 코드는 '길이'와 상관 없다.

제3자는 AB() 함수를 보고 당황을한다. 함수를 따라 들어가 보아야 동작을 이해할 수 있다.

getSum()은 이름만 보아도 해당 함수가 덧셈을 수행한다는 정보를 얻을 수 있다.

바로 이런 점에서 차이가 발생한다.

 

생각보다 클린 코드는 매우 중요하다. 우리는 혼자 일하지 않기 때문이다.

'클린 코드'를 적용하지 않으면 개발 속도는 빠를 수 있으나, 유지보수 시간이 길어진다.

적용하면 개발 속도는 더딜 수 있으나, 추후 90%를 차지할 유지보수 시간이 짧아진다.

어떤 것이 이익일지 우리는 바로 알 수 있다.

 

 


2. 구현 방법

"작은 노력에서 시작된다."

 

 

범위 : 네이밍 지정, 함수 작성

  • 네이밍 - 변수나 함수명엔 의도를 보이자.
    int w; //X
    html(); //X
    
    int weatherToday; //O
    getResponseBody(); //O
     
  • 네이밍 - 길이와 상관없다. 명료하게 쓰자.
    string m; //X
    string i; //X
    getRegistedUser(); //X
    
    string macAddr; //O
    string ipAddr; //O
    getUser(); //O
     
  •  네이밍 - 타입이 이름에 꼭 들어갈 필요 없다.
    int nDate; //X
    string sName; //X
    
    int date; //O
    string name; //O
     
  •  네이밍 - 클래스명은 명사, 메서드명은 동사를 활용하자.
    class User{
    private:
        int age = 0;
    public:
        setAge(int age) //...
    }
     
  • 네이밍 - 헷갈릴만한 단어는 빼고, 같은 단어는 통일해서 쓰자.
    int l = 1; //X => 헷갈리는 변수명은 OUT!
    int O = 0; //X => 헷갈리는 변수명은 OUT!
    int x = 0; //X
    Master.printInfo(...); //X => Info와 Data 혼용
    Slave.printData(...); //X => Info와 Data 혼용
    
    int positionX = 3; //O
    Master.printInfo(...); //O
    Slave.printInfo(...); //O
  • 함수 - 짧아야 한다.
    string getIpAddr(char mac[MAC_LEN]){ //X
        string ip = "";
        ...
        ARP arp = {};
        arp.hardwareType = 0x0000
        ...
        return ip
    }
    
    string getIpAddr(char mac[MAC_LEN]){ //O
        string ip = sendArp(mac);
        return ip;
    }
    ...
  • 함수 - 하는 일은 하나다.
    int isTmpFolder(string path){
    	...
        checkPath(); //O => 함수는 본질의 단 하나 역할만 수행
        setFlag(); //X
        writeFile(); //X
        ...
    }
  • 함수 - 수준을 맞추자. 출력은 출력하는 곳에서..
    class User{
    private:
    	int Age;
    public:
    	int setAge(int age){
    		Age = age;
    		std::cout << Age << std::endl; //X
    	}
    	int getAge(){ //O
    		return Age;
    	}
    }
  • 함수 - 인자는 적을 수록 좋다.
    int sendData(int mac[MAC_LEN], int ip[IP_LEN], char* dev, ...) // X
    int sendData(Packet packet); // O
  • 함수 - 반복하지 마라.
    int setPlace(Place place){ //X
    	if(place.positionX == '1'){
    		place.positionY = '2';
    		place.positionX = '3';
    	}else if(place.positionX == '3'){
    		place.positionY = '2';
    		place.positionX = '3';
    	}
    }
    
    int setPlace(Place place){ //O
    	if(place.positionX == '1' || place.positionX == '3')
    		setPosition(2,3)
    }

 


3. 마치며

"좋은 그림과 나쁜 그림은 누구든 구별할 수 있다.

그러나 직접 그려보지 않으면 좋은 그림을 그리진 못한다."

 

 

 어쩌면 그냥 무시하고 지나갈 수 있는 이론이나, 한번 적용해보면 추후 코드를 다시 볼 때 크게 와닿을 것이다.

본인도 아직 부족해 계속해서 공부해나가고자 한다. 저자는 이론만으론 클린코드를 작성할 수 없다고 말한다.

우리는 계속해서 작성했던 코드를 보완하고, 보완해나가야 한다.

글을 읽는 것에서 끝나지 않고, 본인의 코드를 찾아 변수명을 바꿔보는 작은 행동에서 큰 변화는 시작될 것이라 믿는다.


아직 3장까지 밖에 읽지 못했지만 완독을 하고 다시 한번 정리해볼 계획이다.

+ 피드백 및 저작권에 대한 문의 시 즉시 수정할 예정이다.

반응형
반응형

오늘은 NTFS 파일 시스템에서의 파일복구를 실습해볼것이다.

툴은 NTFS Walker, HxD(+ FTK Imager, 010 Editor)를 이용해 볼 것이다.

 

*본 실습엔 개념을 NTFS에 대한 이해를 전제로 진행한다.

 


0. 파일 생성

실습을 위해 복구할 파일을 만들고자 한다.
D드라이브에 텍스트 파일을 하나 생성해보았다.

 

파일 명 : Forensic_NTFS_Study_Day1.txt

파일 타입 : .txt

크기 : 1,296byte(디스크 할당 크기 4,096byte)

 

*내용은 Non-resident 형태로 만들기 위해 700바이트가 넘도록 내용을 작성하였다.



1. 파일 삭제

우선 실습환경이 SSD 이기때문에 Trim을 비활성화하였다.

CMD 관리자 권한으로 fsutil behavior set DisableDeleteNotify 1 명령어로 비활성화가 가능하다.

 

그리고 파일을 완전히 삭제한다.



2. 분석

이제 분석을 진행 할 시간이다.


우선 HxD(관리자 권한 실행)를 통해 D드라이브를 물리디스크 형식(도구-디스크열기-물리디스크)으로 열어본다.


아래를 보면 VBR이 0x00 00 08 00으로 2048번째 섹터에 위치 해 있는 것을 확인 할 수 있다.

 

 

VBR이다. 해당 영역의 13번째 인덱스의 값은 클러스터 당 섹터의 개수이다. 0x08로 8개인 것을 확인 할 수 있다.

 

 

위 정보를 이용하여 이제 삭제한 파일의 데이터를 찾아가 볼 것이다.



3. 파일 복구

이제 본격적으로 파일을 복구해 볼 것이다.

일단 NTFS Walker를 이용하여 삭제된 파일의 MFT Entry Number를 확인한다.

1209번이 삭제된 파일의 Entry Nubmer이다.

 

한 Entry당 두개의 섹터(본 시스템 기준 512byte*2 = 1024byte 크기)를 가지고 있다.
VBR 영역에서 Start Cluster for $MFT를 통해 해당 섹터를 찾아가서 1209 * 2 섹터만큼 건너 뛰면 되지만,

본 포스팅에서는 편리를 위해 FTK Imager로 $MFT를 덤프하여 010 Editor를 통해 열어 해당 Entry를 찾아가 보았다.

 

 

해당 FileName 영역을 보면 삭제된 파일의 이름이 존재한다. 해당 영역이라는 것은 증빙이 되었다.

 

 

이제 Runlist부분을 찾아가 본다.

 

 

Runlist는 0x31 01 DD 3B 0C 부분이다.

Run Length는 0x01, Run Offset은 0x0C 3B DD이다.

해당 값과 공식을 이용하여 실제 컨텐츠 데이터에 접근해 보겠다.

(사진에 드래그한 앞에 8바이트는 파일의 Initalized Size를 이야기한다. 0x05 10 값으로 1296byte였던 파일이었다는 정보를 알 수 있다.)

 

0C 3B DD * 8(클러스터 당 섹터) + 2048(VBR 시작 섹터) = 6,416,104섹터(10진수/실제 컨텐츠 데이터)

 

HxD에서 해당 섹터로 찾아가본다.

 

 

삭제되었던 텍스트 파일이 존재한다!

해당 주소부터 파일의 크기인 1296byte만큼 복사하여 새로운 파일로 저장한다.

 



3. 파일 복구 성공

다음과 같이 파일이 복구가 된 것을 확인 할 수 있다.

 

 


이제 실습끝났으니 다시 Trim을 활성화하여 빠른 속도로 사용합니다!

반응형

반응형

오늘은 네트워크를 공부해볼 것이다.

유명 오픈소스 IDSSuricata(수리카타)에 대해 공부해보자.


1. Snort? Suricata?

본 주제에 들어가기 전에 Snort부터 이야기를 해보려고 한다.

Snort(스노트)는 sourcefile사에서 만든 오픈소스 IDS(Intrusion Detection System)

실시간 트래픽을 분석하거나 패킷 로깅 용도로 오랫동안 사랑 받아왔다.

또, 그동안의 수많은 개발자들에 의한 rule을 제공받을 수 있고 다양한 OS를 지원한다.

그러나 Snort에도 단점이 존재했는데, 그건 바로 단일-스레드 처리방식이었다.

시간이 지날 수록 처리해야 할 트래픽은 늘어가지만

단일-스레드 방식으로 이 모든 데이터를 처리하기엔 한계가 있었다.

 

그래서 해당 단점을 보완하고자, Suricata가 등장한다.

Suricata(수리카타) 역시 오픈소스 IDS(Intrusion Detection System)이다.

Snort를 발전시킨 만큼, Snort 룰이 호환되어 사용 가능하다.

무엇보다 멀티 코어와 멀티-스레드 처리방식으로 데이터를 처리할 수 있어서

현재도 많은 사랑을 받고있다.

뿐만 아니라 하드웨어(GPU) 가속 역시 지원하기 때문에 큰 기업에서도 많이 사용한다.

 

아래는 Snort와 Suricata에 대한 비교 사진이다.

출저 :  http://wiki.aanval.com/wiki/Snort_vs_Suricata



2. Suricata - 설치

리눅스Suricata를 설치해보자.

 

기존엔 복잡한 방법으로 설치하였으나 최근엔 쉽게 설치가 가능하다.

우선 본인은 기존에 설치되어 있는 OS인 Kali linux 5.3.0버전에서 진행하였다(참고로 Kali에서 실습하는건 추천하지 않는다).

 

1. (sudo)apt-get install suricata 명령어로 설치(본인은 이미 설치가 되어있음)

 

2. suricata 명령어로 실행 확인



3. Suricata - rule 생성

80 포트(HTTP)와 443 포트(HTTPS)를 사용하는 두가지 사이트에 대한 필터링 룰을 생성해보겠다.

1. suri.rules 파일을 생성한다.

2. 해당 파일에 문법에 맞게 rule을 작성한다. > 문법은 https://suricata.readthedocs.io/en/suricata-4.1.4/rules/intro.html 참고

alert tcp any any -> any 80 (msg:"80site.com Access"; content:"GET /"; content:"Host: "; content:"80site.com"; sid:10001; rev:1;)
alert tcp any any -> any 443 (msg:"443site.com Access"; flow:to_server,established; tls_sni; content:"443site.com"; sid:10002; rev:1;)



4. Suricata - 실행

rules 파일까지 작성이 끝났다.

이제 Suricata를 실행시켜보자.

 

1. suricata -s suri.rules -i eth0 명령어로 인터페이스와 파일을 인자로 넣어 실행한다.

2. 해당 사이트에 접속한다(본인은 80포트는 교수님 사이트, 443포트는 얼굴책 사이트를 지정하였다)

 

3. 로그파일을 확인한다. > /var/log/suricata/fast.log에 룰 정책에 의한 접속 로그들이 저장된다.

4. 위와 같이 조건대로 log가 저장된 것을 확인 가능하다.


여기까지 suricata를 실습해보았다.

오픈소스로 사용하기 아까울 정도로 잘 만들어논 프로그램 같다.

Suricata를 올바르게 사용한다면 기업 입장에선 비용을 많이 절감할 수 있어보인다.

 

이후 편에서 문법부터 상세한 설명을 포함하여 포스팅해볼 예정이다.

반응형
반응형

드디어 PE(Portable Executable) 파일 포맷 포스팅을 하고자한다.

내용이 길것으로 예상되나 중요한 파트이므로 천천히 짚고 넘어가자.


1. PE 포맷?

PE 포맷이란 Portable Executable 약자로 윈도우 운영체제에서 사용되는 실행 가능한 파일 형태를 말한다. 

아래서 설명할 PE 파일은 말그대로 PE 포맷 구조를 가지는 파일이다.

 

대표적인 포맷인 만큼 다양한 분석툴에서 PE파일을 분석할 수 있다.

PE Viewer가 대표적이며 본 포스팅에선 개념 이해를 위해 HxD를 이용해 설명하도록 하겠다.

 

 

2. PE 파일 종류

PE 포맷을 가진다고 무조건 실행하는 계열의 확장자만 해당되는 것이 아니다.

다음과 같은 다양한 파일 포맷이 PE 구조를 가진다. 대표적으로 .exe와 .dll이 존재한다.

  • 실행 계열 : EXE, SCR
  • 드라이버 계열 : SYS, VXD
  • 라이브러리 계열 : DLL, OCX, CPL, DRV
  • 오브젝트 파일 계열 : OBJ


3. PE 파일 구조

PE 파일은 다음과 같이 구성되어있다.

  • Header + Section

Header란 실행 파일의 성격과 특징을 나타내며(프로그램 구동 정보 등등),

Section은 구체적이고 세부적인 기능을 나타낸다(코드, 전역변수 등등) .

 

위 구조를 그림으로 좀더 자세히 표현하자면 아래와 같다.

 

각 영역에 대해 살펴보자.

 

 

4. PE 파일 구조 - DOS 영역

MS에서 DOS 파일에 대한 하위 호환성을 고려하여 PE 파일 포맷을 만들었다.

DOS 영역의 두가지 영역을 살펴보자.

 

1) DOS Header

이제부터 몇몇 영역을 아래와 같이 소스코드로 나타낼 건데, 이는 MS에서 제공하는 winnt.h에서 가져온 내용이다.

typedef struct _IMAGE_DOS_HEADER {
    WORD   e_magic;                     // Magic number > DOS Signature : 4D5A("MZ")
    WORD   e_cblp;              
    WORD   e_cp;                     
    WORD   e_crlc;                    
    WORD   e_cparhdr;             
    WORD   e_minalloc;               
    WORD   e_maxalloc;          
    WORD   e_ss;               
    WORD   e_sp;                     
    WORD   e_csum;                   
    WORD   e_ip;                     
    WORD   e_cs;                    
    WORD   e_lfarlc;            
    WORD   e_ovno;                   
    WORD   e_res[4];                
    WORD   e_oemid;                  
    WORD   e_oeminfo;                  
    WORD   e_res2[10];                 
    LONG   e_lfanew;                    // Offset of NT Header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

DOS Header 영역으로 중요한 정보는 두가지다.

첫번째 e_magic 값과 e_lfanew 값이다.

  • e_magic : DOS Signature 값으로 0x4D5A("MZ")라는 값을 가진다.
  • e_lfanew : NT Header의 오프셋 주소를 가진다. DOS Stub 영역을 점프하는 역할을 한다.

2) DOS Stub

에러메세지를 출력하는 부분이다.

가변적인 영역으로 옵션에 의해 존재여부를 결정한다.

없어도 파일은 잘 실행된다.

 

 

5. PE 파일 구조 - NT Header 영역

NT Header 영역은 3가지 영역이 존재한다.

 

1) PE File Signature

NT Header 시작부분에 위치하며 PE File Signature 값으로 0x5045("PE")라는 값을 가진다.

 

2) PE File Header(IMAGE_FILE_HEADER)

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

 이 부분에서 중요한 정보는 Machine, NumberOfSections, SizeOfOptionalHeader, Characteristics로 4가지 이다.

  • Machine : CPU별로 고유한 값을 가진다. 0x014C는 IA-32, 0x0200은 IA-64와 같은 값을 가지고 있다.
  • NumberOfSections : 섹션의 갯수를 나타내며, 1개 이상의 값을 가진다.
    *만약 정의된 섹션수 > 실제 섹션 : error

    *만약 정의된 섹션수 < 실제 섹션 : 정의된 섹션수만큼 인식
  • SizeOfOptionalHeader : 마지막 멤버 Optional Header32 구조체의 크기를 나타낸다.
  • Characteristics : 파일의 속성을 나타낸다. 실행 가능한 파일인지, dll 파일인지와 같은 정보 등을 나타낸다.

3) PE File Optional Header(IMAGE_OPTIONAL_HEADER)

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
 
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16
 
typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;
    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

PE Header 중 가장 크기가 큰 구조체다.

실행에 필수적인 값들이 존재하므로 잘못 값들이 세팅되면 프로그램이 실행되지 않는다.

모두 중요하지만 특히 중요한 정보들에 한하여 설명하겠다.

  • Magic : IMAGE_OPTIONAL_HEADER32이면 10Bh, IMAGE_OPTIONAL_HEADER64면 20Bh
  • AddressOfEntryPoint : EP(Entry Point)의 RVA(Relative Virtual Address)값을 가짐
  • ImageBase : 프로세스 가상 메모리(32bit) : 0 - FFFFFFFFh, 로딩(매핑)된는 시작 주소를 나타냄
  • SectionAlignment, FileAlignment : PE 파일은 섹션으로 나뉨, SectionAlignment는 메모리에서 섹션 최소단위, FileAlignment는 파일에서 섹션의 최소단위 나타냄
  • SizeOfImage : PE 파일 메모리 로딩 시 가상 메모리에서 PE Image가 차지하는 크기
  • SizeOfHeader : PE Header 전체 크기
  • Subsystem : 1이면 Driver 파일(sys, vxd), 2면 GUI 파일, 3이면 CUI 파일
  • NumberOfRvaAndSizes : 마지막 멤머 DataDirectory 배열 크기
  • DataDirectory : IMAGE_DATA_DIRECTORY 구조체의 배열


6. PE 파일 구조 - Section Header 영역

Section Header 영역은 Section에 대한 구성 정보를 담고 있다.

#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;
    DWORD   SizeOfRawData;
    DWORD   PointerToRawData;
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

5가지 정보에 대해 설명하겠다.

  • VirtualSize : 메모리에서 섹션이 차지하는 크기
  • VirtualAddress : 메모리에서 섹션 시작 주소(RVA), SectionAlignment에 맞춰 결정
  • SizeOfRawData : 파일에서 섹션이 차지하는 크기
  • PointerToRawData : 파일에서 섹션 시작 위치, FileAlignment에 맞춰 결정
  • Characteristics : 섹션의 속성

*RVA는 메모리에서 주소를 나타내며, RAW는 파일에서의 주소를 나타낸다.

*RVA를 RAW로 공식을 적용하여 계산할 수 있다.

  • RAW = RVA - VirtualAddress + PointerToRawData
  • 만약 RVA가 101400h, VirtualAddress가 101000h, PointerToRawData가 100000h이면 RAW 값은 100400h

7. PE 파일 구조 - Section 영역

Section 영역은 PE 파일의 실제 데이터를 담고 있다.
대표적인 몇몇 영역을 설명하겠다.

  • .text : 실행코드, 형식 제한 없음, 읽기 권한 지님, 리틀엔디안
  • .data : 초기화된 각종 변수, 형식 제한 없음, 읽기/쓰기 권한 지님
  • .rdata : 읽기 전용
  • .idata : import 섹션으로 DLL/함수 정보
  • .edata : export 섹션으로 - 본인함수 정보
  • .rsrc : 리소스, 비실행, 읽기 권한 지님, 파일 버전이나 아이콘 등 기타 데이터 정보
    ...

이외에도 다양한 영역이 존재한다. 여기까지 이론을 마치고 실습을 해보겠다.



8. notepad.exe 분석

HxD를 통해 위와 같은 구조로 notepad.exe를 분석할 수 있다.

NT Header의 PE File Header 부분까지 살펴보자.

1. DOS Header의 DOS Signature 값인 4D 5A를 확인할 수 있음

2. NT Header Offset을 나타내는 lfanew 값인 F8 00 00 00를 확인하여 F8 주소로 점프

3. 50 45라는 NT Header 시작부분인 PE File Signature를 볼 수 있음

4. NT File Header 부분 Magic값인 64 86를 통해 AMD64 CPU체제 확인 가능

5. 이어서 NumberOfSections이 07 00을 통해 7개의 섹션을 가진 것 확인

6. SizeOfOptionalHeader을 통해 IMAGE_OPTIONAL_HEADER32가 F0의 크기를 가진 것 확인

7. Charateristics값인 22 00을 통해 실행가능파일부터 다양한 정보 확인가능

 

각 헥스값에 대한 의미는 추가적인 검색을 통해 의미를 파악할 수 있으며

위와 같이 분석을 진행해 나가면 된다.

물론 실전에선 PE Viewer와 같은 전용 분석툴을 통해 분석하면

쉽게 구조를 파싱하여 확인 가능하므로 참고하자.

 


 

여기까지 PE 공부를 마친다. 다음시간엔 OLE 구조를 공부해보겠다.

반응형
반응형

데이터베이스를 사용하다보면 여러 테이블이 나눠진 상황에서 2개 이상의 테이블을 조합하여 원하는 결과를 도출해야되는 경우가 있다. 이를 위해서 JOIN을 사용할 수 있는데 MYSQL의 JOIN에 대해 자세히 들여다보자.


JOIN을 실습하기 위해 Mysql과 PhpMyAdmin을 이용해 다음과 같이 테이블을 구성하였다.

 

TABLE A - test_information

문제이름(Mission) 출제자(Maker)
국어문제 김선생
영어문제 이선생
수학문제 조선생
과학문제 구선생
일본어문제 나가사끼선생

TABLE B - test_result

문제이름(Mission) 푼인원수(Solver)
국어문제 30
영어문제 25
수학문제 16
과학문제 10
중국어문제 3

한테이블엔 문제이름과 출제자가 적혀있고, 나머지 한테이블엔 문제이름과 푼 인원수가 적혀있다.

본인은 두 테이블에 들어있는 정보를 이용하여 하나의 출력결과를 만들어 내고 싶다.

이를 위해 JOIN을 하나씩 사용해보자.

 

0. (INNER) JOIN

> 두개 이상의 관련 키가 존재하는 테이블에서 컬럼값을 비교하여 조건에 맞는 행을 검색

> 입력 예제

SELECT test_information.Maker, test_result.Solver : 출제자와 푼 사람수를 출력

FROM test_information : 테이블 A 기준(문제이름, 출제자)

INNER JOIN test_result : 테이블 B로 JOIN(문제이름, 푼 사람수)

ON test_information.Mission = test_result.Mission : 조건은 A의 문제이름과 B의 문제이름이 같은 행만

 

> 결과

 

1. OUTER JOIN

> LEFT 또는 RIGHT, FULL OUTER JOIN이 존재하며, 두 테이블에서 지정된 쪽인 LEFT 또는 RIGHT 방향의 모든 결과를 보여준 후 반대쪽에 대해는 매칭값이 없어도 표시

1-1. LEFT OUTER JOIN

> JOIN문의 LEFT에 있는 테이블 결과를 모두 가져온 후 RIGHT에 있는 테이블 데이터를 매칭하여 매칭되는 데이터가 없을땐 NULL을 표시

> 입력 예제

SELECT test_information.Maker, test_result.Solver : 출제자와 푼 사람수를 출력

FROM test_information : 테이블 A 기준(문제이름, 출제자)

LEFT JOIN test_result : 테이블 B로 LEFT JOIN(문제이름, 푼 사람수)

ON test_information.Mission = test_result.Mission : 조건은 A의 문제이름과 B의 문제이름이 같은 행만

 

> 결과 : A엔 나가사끼선생(일본어문제)이 있지만,  B엔 일본어문제가 존재하지않는다. 그래도 NULL로 넣어 출력한다.

1-2. RIGHT OUTER JOIN

> JOIN문의 RIGHT에 있는 테이블 결과를 모두 가져온 후 LEFT에 있는 테이블 데이터를 매칭하여 매칭되는 데이터가 없을땐 NULL을 표시

> 입력 예제

SELECT test_information.Maker, test_result.Solver : 출제자와 푼 사람수를 출력

FROM test_information : 테이블 A 기준(문제이름, 출제자)

RIGHT JOIN test_result : 테이블 B로 RIGHT JOIN(문제이름, 푼 사람수)

ON test_information.Mission = test_result.Mission : 조건은 A의 문제이름과 B의 문제이름이 같은 행만

 

> 결과 : B엔 3명(중국어문제) 행이 있지만, A엔 중국어문제가 존재하지않는다. 그래도 NULL로 넣어 출력한다.

2. CARTESIAN(CROSS) JOIN

> 연관 테이블(두개 이상)에서 가능한 모든 조합을 찾음

> 입력 예제

SELECT test_information.Maker, test_result.Solver : 출제자와 푼 사람수를 출력

FROM test_information : 테이블 A 기준(문제이름, 출제자)

CROSS JOIN test_result : 테이블 B로 CROSS JOIN(문제이름, 푼 사람수)

 

> 결과 : 출제자와 푼 사람의 수의 모든 조합 경우의 수를 보여준다 행의 갯수는 A x B개이다.

3. SELF JOIN

> 자기 자신과 JOIN

> 입력 예제

SELECT info1.Mission, info2.Maker : 문제이름과 출제자를 출력

FROM test_information info1 : 테이블 A 기준(문제이름, 출제자)

JOIN test_information info2 : 테이블 A로 JOIN(본인에게)

ON info1.Mission = info2.Mission : 문제 이름이 같은경우

 

> 결과 : 당연히 같은 테이블에서 문제이름끼리 비교하면 모두 같으므로, 문제이름과 출제자의 모든 결과를 출력한다.


여기까지 JOIN 공부를 마쳤다. 소스코드 삽입부터 계속해서 포스팅을 수정해나갈 예정이다.

반응형

+ Recent posts