스레드란?

OS에서 하나의 프로그램이 실행된다는것은 하나의 프로세스가 생성된다는 의미와 같다. 그렇다면 이 프로세스는 작업 관리자를 통해서 직접 볼수 있는 단위이다. 일반적으로 프로세스는 하나의 실행 파일이 실행될 때 만들어지며, 운영체재 내부적으로는 여러 논리적 코드를 관리하는 단위이기도 하다. 하지만 실행 단위를 논할 때 기본적으로 프로세스지만 더 정확히는 스레드이다.  


프로세스가 하나의 집이라 가정하면 집에 거주하는 사람은 스레드가 된다. 한집에 한명이 살수도 있지만 여러명이 사는 집도 있다. 이렇다면 제한적인 자원을 여러 구성원이 공유해서 사용해야하며 문제가 발생하지 않도록 묵시적인 규칙이 있어서 이에 따라 운영되어야 한다는 것이다. 멀티스레드 프로그래밍에서 이런 이슈처럼 서로 충돌하는일이 없도록 동기화가 중요하다.



작업자 스레드

MFC에서는 스레드를 생성하는 함수는 AfxBeginThread()함수이다. 이 함수를이용하면 특정 전역 함수를 별도의 스레드로 실행할 수 있다. 만일 여러번 같은 방식으로 호출하면 그 수만큼 스레드가 생성된다.



void CWorkerThreadDemoDlg::OnBnClickedButton1()

{

// TODO: Add your control notification handler code here

TCHAR szWinPath[MAX_PATH];

::GetWindowsDirectory(szWinPath, MAX_PATH);

lstrcat(szWinPath, _T("\\notepad.exe."));


SHELLEXECUTEINFO sei;

::ZeroMemory(&sei, sizeof(sei));

sei.cbSize = sizeof(sei);

sei.hwnd = NULL;

sei.lpFile = szWinPath;

sei.nShow = SW_SHOW;

sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;

sei.lpVerb = __TEXT("open");

sei.lpParameters = NULL;


if (::ShellExecuteEx(&sei))

{

::WaitForSingleObject(sei.hProcess, INFINITE);

}

}



위와같이 버튼을 클릭하면 메모장이 오픈되는 소스를 직접 코딩후 실행을 해보면 본 다이얼로그에 응답없음이 뜨는걸 확인할 수 있다. 그러한 이유는 메모장을 실행한 후 (::ShellExecuteEx())한 후 메모장이 종료될떄(::WaitForSingleObject())까지 프로그램이 정지되었기 때문이다. 

프로그램이 정지했다는건 메인메세지 루프가 멈추었음을 의미하고 그렇다면 큐에 쌓이는 메세지를 처리하지 못한다는것이다. 프로그램이 하얗게 변하게 되는데 WM_PAINT 메세지를 처리하지 못해서이기 때문이다. 


::GetWindowsDirectory() = 윈도우가 설치된 폴더의 경로르 알아내는 역할 (C:\Windows)

::GetSystemDirectory() = 윈도우 시스템폴더의 전체 경로릉랄아내는 역할 (C:\Windows\System32)

::ShellExecuteEx()함수는 특정 경로의 파일을 실행하는 함수

::WaitForSingleObject() = 특정 객채의 상태가 설정될 때까지 현재 스레드의 실행을 멈추는 역할을 한다.



이제 위의 소스코드를 스레드 형태로 바꿔보도록 하겠다.



UINT ThreadWitNotepad(LPVOID pParam)

{

// TODO: Add your control notification handler code here

TCHAR szWinPath[MAX_PATH];

::GetWindowsDirectory(szWinPath, MAX_PATH);

lstrcat(szWinPath, _T("\\notepad.exe."));


SHELLEXECUTEINFO sei;

::ZeroMemory(&sei, sizeof(sei));

sei.cbSize = sizeof(sei);

sei.hwnd = NULL;

sei.lpFile = szWinPath;

sei.nShow = SW_SHOW;

sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;

sei.lpVerb = __TEXT("open");

sei.lpParameters = NULL;


if (::ShellExecuteEx(&sei))

{

::WaitForSingleObject(sei.hProcess, INFINITE);

AfxMessageBox(_T("메모장이 종료 되었습니다."));

}


}


스레드에서 수행할 함수를 전역함수로 정의를 하게 되는데 안에 내용은 기존 소스코드와 동일하다. 다만 종료될 경우 메세지박스로 종료되었음을 알리나는 소스코드를 추가한다. 

그런 뒤 버튼이 클릭됬을 경우의 소스코드를 수정해준다.



void CWorkerThreadDemoDlg::OnBnClickedButton1()

{

// TODO: Add your control notification handler code here

//TCHAR szWinPath[MAX_PATH];

//::GetWindowsDirectory(szWinPath, MAX_PATH);

//lstrcat(szWinPath, _T("\\notepad.exe."));


//SHELLEXECUTEINFO sei;

//::ZeroMemory(&sei, sizeof(sei));

//sei.cbSize = sizeof(sei);

//sei.hwnd = NULL;

//sei.lpFile = szWinPath;

//sei.nShow = SW_SHOW;

//sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;

//sei.lpVerb = __TEXT("open");

//sei.lpParameters = NULL;


//if (::ShellExecuteEx(&sei))

//{

// ::WaitForSingleObject(sei.hProcess, INFINITE);

//}

CWinThread* pThread = AfxBeginThread(ThreadWitNotepad, NULL);

if (pThread == NULL)

{

AfxMessageBox(_T("ERROR: FAILED TO BEGIN WORKER-THREAD!"));

}


}




사용자 인터페이스 스레드

일반적인 윈도우 응용 프로그램을 작성할 때 최상위 프레임 윈도우는 보통 CMainFrame클래스 객체이다. 이 개체는 오로지 하나만 존재하는 것이 일반적이고 다른 윈도우아 달라 메인 메시지 루프와 연결되어 있다. 즉 응용 프로그램은 하나는 하나 이생의 스레드를 가지고 있으며, 하나의 최상위 프레임 윈도우는 응용 프로그램 전반에 관여하는 메인 메시지 큐를 갖는다.


다시 말해 최상위 프레임 윈도우 하나에 대해 별도의 스레드와 메시지 큐가 있다고 볼 수 있다. 그러므로 최상위 윈도우 하나를 생성한다는 것은 그에 관련된 별도의 스레드와 메시지 큐를 생성하는 것과 같다. 이들에 대한 코드를 작성하려면 많은 코드가 필요하지만 FMC에서는 AfxBeginThread()함수 하나로 가능하다. 


사용자 인터페이스 스레드를 생성하려면 CWinThread 클래스나 그 파생 클래스를 프로젝트에 등록한 후 InitInstance() 가상 함수를 재정의하여 CWinApp 클래스 객체의 InitInstance()함수와 같거나 유사한 코드를 작성하고, AfxBeginThread() 함수와 RUNTIME_CLASS() 매크로를 이용하여 해당 스레드의 인스턴스를 생성해야 한다.


1. CWinThread를 상속받는 새로운 클래스 CUIThread를 생성한다. 이 객체는 RUNTIME_CLASS() 매크로를 이용하여 생성한다.

2. 메뉴 툴바 리소스에 Create UI Thread 메뉴에 ID_MENU_CREATEUITHREAD로 하고 CMainFrame 클래스에 핸들러 함수를 등록하여 코드를 작성한다. 이때 MainFrame.cpp에 관련 헤더를 인크루드 해야한다.

#include "UIThread.h"


void CMainFrame::OnMenuCreateuithread()

{

AfxBeginThread(RUNTIME_CLASS(CUIThread));

}


3. CUIThread 클래스의 InitInstance() 함수에 다음과 같이 프레임 윈도우를 생성하는 소스를 작성한다.
BOOL CUIThread::InitInstnace()
{
CMainFrame* pFrame = new CMainFrame;
if(!pFrame)
return FALSE;
m_pMainWnd = pFrame;

pFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE NULL, NULL);
pFrame->ShowWindow(SW_SHOW);
pFrmae->UpdateWindow();

return TREU;
}

4. CChildView 클래스의 OnPaint() 함수에 현제 스레드의 id르 ㅊ루력하는 코드를 추가한 후 빌드화여 결과 하긴
void CCHildView::OnPaint() 
{
CPaintDC dc(this);

CString strTmp = _T("");
strTmp.Foramt(_T("Current Thread ID : %D"), ::GetCurrentThreadId());
dc.TextOut(10, 10, strTmp);
}


스레드의 제어
AfxBeginThread()함수를 이용하여 스레드를 새엉하면 CWinThread 클래스 객체가 생성되어 반환된다. 이 객체를 이용하면 해당 스레드를 제어할수 있는데 이리적으로 중지 시키거나 재시작할 수 있다.


1. stdAfx.cpp와 .h을 열어 외부 변수를 선언한다.

CWinThread* g_pUIThread = NULL;

extern CWinThread* g_pUIThread;


2. CMainFrame 클래스의 OnMenuCreatuiThread()함수를 수정하여 스레드의 중복 실행을 막고 처음 생성된 스레드의 주소를 전역 변수에 저장한다.


if(g_pUIThread != NULL)

{

AfxMessageBox(_T("사용자 인터페이스 스레드가 실행중."));

return;

}


g_pUIThread = AfxBeginThread(RUNTIME_CLASS(CUIThread));


3. 사용자인터페이스 스레드가 종료될 때 전역 변수를 NULL로 초기화하도록 CUIThread클래스의 ExitInstance(0함수에 다음 과 같은 코드를 추가합니다.


int CUIThread::ExitInstance()

{

g_pUIThread = NULL;

return CWinThread::ExitInstance();

}


4. 리소스 ui에 Create UI Thread, Resume Thread, Suspencd Thread 메뉴를 생성하고 핸들러 함수를 등록한다.


void CMainFrame::OnMenuResumethread()

{

if(g_pUIThread != NULL)

g_pUIThread->ResumeThread();

}


void CMainFrame::OnMenuSuspendthread()

{

if(g_pUIThread != NULL)

g_pUIThread->SuspendThread();

}

void CMainFrame::OnMenuExitthread()

{

if(g_pUIThread != NULL)

g_pUIThread->PostThreadMessage(WM_QUIT, NULL, NULL);

}


CWinThread 클래스의 SuspendTHread() 메서드는 Sleep() 함숴럼 스레드의 실행을 일시적으로 중지한다. 그러나 중지하는 시간을 따로 명시할수는 없고 ResumeThread()메서드가 호출될 때까지 일시 중지를 상태를 유지한다. 그리고 PostThreadMessage()메서드는 CWnd 클래스의 PostMessage()함수와 기능은 같다. 그러나 메시지 전송 대상이 특정 윈도우가 아니라 특정 스레드의 메시지 큐라는점이 다르다.

어벤져스 : 인피니티 워(2018)



쿠키 있음

(엔딩크레딧이 조금 김)



스포가 도사리는 인터넷을 하루동안 보지않고

개봉 첫날 퇴근하자마자 관람했다.



내가 평소에 즐겨가는 영화관에 전좌석이 꽉차는 광경을 봐버렸다.

우리나라에서 마블영화의 흥행보증은 알고 있었지만 평일 개봉인 첫날에 이렇게 전좌석이 매진되는 풍경은 흔치 않다.




영화를 본 관객들이라면 평소 히어로물 영화에서 마무리되는 엔딩과는 사뭇 다르다.

악당이 승리하는 엔딩은 생소하기 때문이다.



인피니티 워 같은경우에는 다음 시리즈를 위한 포석이라 생각된다.

왜냐면 마블의 돈(히어로)들이 다 죽어버렸기 때문에..

이 돈덩어리들을 마블이 쉽게 놓아버리지 않을꺼란 생각에 다음 편에서 다 살아날꺼라 믿어 의심치 않는다..ㅋㅋㅋ



최근 영화 번역가의 발번역으로 인해 이슈가 되긴 했는데

닥터 스트레인지가 타임 스톤을 순순히 넘겨준 목적이 발번역으로 그 의미가 바뀌어버렸다.


암튼 다음 작품이 기대된다.




ps. 시리즈 초반에는 헐크가 최강 캐릭이였는데 요즘보면 헐크만큼 하는거 없는 히어로가 없는거같다.

 타노스에게 개 뚜까 맞고 변신하길 두려워하는 헐크..

 얘는 어떻게 강화될지 궁금하다.

















'취미 > 영화' 카테고리의 다른 글

어벤져스:인피니티 워 + 쿠키정보  (0) 2018.04.26

MFC란?

- Microsoft사에서 만든 C++기반 윈도우 라이브러리이다.

- Win32 API 는 C기반이지만  MFC Programming은 C++ 기반이다.




왜 MFC를 사용하는가?

- 객체지향 (코드의 재사용)

- 다양한 함수 제공한다.

- MFC Framework에 Win32 API를 섞어 사용 (사실 Win32 API를 클래스화하여 wrapping한 것으로도 봄)




CWnd Class (중요)

- CWnd는 MFC의 부모라 불리는 CObject 클래스와 CCmdTarget 클래스를 상속받은 클래스이다.

- CCmdTarget 클래스는 메세지 맵과 관련된 코드가 구 현된다. ( WinProc()의 switch case문을 대체하고 자 도입된 MFC구조이다.)

- CWnd는 MFC가 제공한 모든 윈도우 클래스의 기본 기능이다.

- Windows 자체를 객채화한 클래스이다보니 CWnd 객체를 선언하여 초기화하고 생성하면 화면에 창이 뜨는거와 같다.




함수 

역할 

Create() / CreateEx() 

 둘 다 윈도우를 생성하는 함수이다. CreateEx()는 추가로 확장 속성을 갖고있다. 

PreCreateWindow() 

윈도우를 생성하기 직전 호출되는 가상 함수로 재정의하여 생성되는 윈도우의 속성을 변경하는 코드를 추가할 수 있다. 

GetStyle() / GetExStyle()

윈도우의 기본 스타일과 확장 스타일을 반환하는 함수이다. 

PreSubclassWindow() 

서브 클래싱 직전에 호출되는 가상 함수이다. 

GetSfeHwnd() 

생성된 윈도우의 핸들을 반환하는 함수이다. 




MFC 마법사로 생성한 프로젝트의 기본 클래스 구조

- CAboutDlg = 제품 정보를 출력하는 대화 상자 클래스이다.

- CMainFrame = 툴바와 상태바를 생성한다.

- C프로젝트App, C프로젝트Doc 클래스는 응용 프로그램 자체와 문서를 객체화한 클래스이다.



예제 어디를 살펴봐도 WInMain(()함수나 그 비슷한 함수를 찾을 수없다. 하지만 프로젝트.cpp 파일을 열면 theApp라는 전역 객체가 선언된것을 확인할 수 있다.



CWinApp class

- CWnd 클래스와 마찬가지로  CCmdTarget 클래스를 상속받다가 CWinThread 클래스를 상속받는다.

- CWinApp 클래스는 응용 프로그램 자체를 의미하는 클래스임이다.



 멤버 변수

의미 

m_hInstance 

현재 응용 프로그램의 인스턴스 핸들이다. WinMain() 함수의 첫번째 파라미터인 hInstance와 같은 것이다. 

m_lpCmdLine

WinMain() 함수의 lpCmdLine 파라미터와 가 ㅌ은 것입니다. 프로그램을 실행하였을 때 명령줄 정보가 들어 있다. 

m_nCmdShow 

WinMain() 함수의 마지막  파라미터인 nCmdShow와 같은것 

m_pAcitveWnd 

응용 프로그램의 최상위 프레임 윈도우에 대한 포인터. SDI 구조에서 이 값은 CMainFrmae 클래스의 객체 포인터이다. 

m_pszAppName 

응용 프로그램의 제목에 해당하는 문자열 포인터이다. 

m_pszExeName 

빌드한 실행 파일에서 활장자(.exe)를 제외한 파일명이다. 


- CWinApp 클래스에는 다양한 메서드가 있는데 이중 InitInstance(), ExitInstance(), Run() 메서드는 반드시 알아야한다.

- CWinApp 클래스의 Run() 메서드가 호출되는 시점 메시지 루프가 시작되는 시점이다. Run() 메서드가 반환하면 응용 프로그램은 종료된다 이건 Win32 API에서 WM_QUIT 메시지가 발생한것과 같다.


- InitInstance() 메서드는 응용 프로그램이 초기화되는 시점에서 호출된다. 그리고 대부분 재정의되어 있으며, 직접 코드 수정이 가능. 프로그램의 시작과 동시에 메모리를 할당한다든지, 시 스템 리소스를 할당받아야 한다면 여기에 넣는게 바람직


- ExitInstance() 메서드는 응용 프로그램을 종료하는 시점에서 호출된다. Run()함수가 반환되는 시점에서 호출이 된다. 메모리나 시스템 리소스 반환하는 코드는 여기게 넣는게 보편적




CFrameWnd class

- SDI 문서/뷰 구조를 갖는 MFC 응용 프로그램을 개발할 대 최상위 프레임 윈도우에 해당하는 CMainFrame이며 이 클래스는 CFrameWnd 클래스를 상속받아 마든다.

- MDI에서는 CMDIFrameWnd 클래스로 대체된다.



- CFrmaeWnd 클래스는 프레임이 있는 윈도우가 가져야 할 기본 기능을 정의한 클래스라 할 수 있다.

- 도구 모음이나, 상태표시줄 같이 특수한 윈도우를 자 식 윈도우로 취할 수 있다.


- OnCreate() 메서드는 WM_CREATE 메시지 핸들러 함수이다. 메인 윈도우가 생성되는 시점에서 호출되는 함수라는 뜻.그리고 그 시점에서 자신의 자식 윈도우인 도구모음, 상태표시줄을 생성한다. 




CDocument class

- MFC는 문서와 관련된 처리를 개발자가 논리적으로 분리할 수 있도록 CDocument 클래스를 제공한다.

- 이클래스는 다양한 가상함수를 제공하여 개발자로 하여금 자료를 처리할 수있도록 도와준다. 예를 들어 사용자가 파일 열기 메뉴를 선택하여 파일을 열면 자동으로 어떤 함수가 호출하도록 구조적으로 지원한다.


- OnNewDocument() 문서가 오픈되었을 경우 실행되는 메서드.

- SetModifyiedFlag() 문서가 변경되었음을 설정/해제하는 역할을 한다.

- 이외에도 OnOpenDocument(), OnCloseDocument(), OnSaveDocument() 등.




메세지 맵

- Win32 API 윈도우 프로시저 함수에서 볼 수있는 switch case를 대체하는 역할이다.

- 특정 메세지가 발생했을때 어떤 함수를 호출해야하는지 명시하는 매크로의 집합체이다.


//CMain Frame

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)


BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)

ON_WM_CREATE()

END_MESSAGE_MAP()


- BEGIN_MESSAGE_MAP() 매크로와 END_MESSAGE_MAP() 매크로를 이용하여 시작과 끝을 명시한다. ON_WM_CREATE() 매크로는 WM_CREATE()메시지 핸들러 함수를 등록할 경우 추가되는 매크로이다. Visual C++을 이용하면 메시지맵을 직접 손대는 경우는 흔하지 않으며 보통은 Wizard를 통해 변경된다. 


- Win32 API를 단순 클래스 포장한것에 지나지 않는다는 평가를 받기도 하지만 Win32 API구조를 훌륭한 방법으로 객체지향적인 구조로 바꿔 주는 것 중 하나가 메시지 맵이라고 평하해도 과언이 아니다.


-결국 특정 메시지가 바 ㄹ생하면 우리가 최종적으로 재정의한 함수를 호출하며, 이 함수에서는 재정의된 상위 계층의 함수를 명ㅈ시적으로 호출함으로써 MFC의 코드와 우리가 작성한 코드 모두가 동작한다. 












안녕하세요. 

이전 파트에서 진행했던 [RDS] AWS의 RDS로 Oracle Database 생성하기에 이어서 접속하는 포스팅을 진행해보겠습니다.



만들어봤으니 접속해보셔야겠죠?



SQLPLUS, TOAD 어떤 툴을 이용하셔도 상관 없습니다만 저는 Orange 7.0 툴을 이용해서 이전 시간에 만든 DB에 접속해보도록 하겠습니다.

계속진행하기 앞서 이번 시간에는 준비물이 필요합니다.






준비물



1. Oracle 11g client (32bit)


2. Orange 7.0 DBA (Unicode Version)


3. 운영중인 Oracle DB







1. Oracle 11g Client 32bit 설치



http://www.oracle.com/technetwork/database/enterprise-edition/downloads/112010-win32soft-098987.html



위의 URL을 통해 11g client를 다운로드 받아주세요.

Oracle 계정이 필요로 합니다. 


Oracle Database 11g Release 2 client <- 이걸로 받으세요!! 

햇갈리셔서 서버를 받으시거나 엉뚱한거 받으시지 않도록 조심하세요.



(1)



(2)


설치시 꼭 관리자로 설치해주세요.

Orange의 모든 기능을 사용할려면 관리자로 설치하셔야하고 tnsname.ora 경로도 자동으로 잡힙니다.


(3) 설치과정


설치경로를 이전에 설정할수있는데 사용자의 환경에 맞게 설정해주세요.



(3) 설치 완료







2. Orange 7.0 DBA Unicode 다운로드 및 설치



http://www.warevalley.com/xml/download/orange_view?num=13&page=1&type=trial




warevalley사에서 제작한 orange tool을 다운로드 받아주세요.

계정이 필요로 합니다. (간단함)


Orange7_ra_dbaU_b14_KR.zip <- 이걸루 받으세요


사용하실려면 트라이얼 라이센스를 발급받으셔야합니다.

설치 후 트라이얼라이센스 발급 받기 하시면 이메일로 활성화 코드가 옵니다.

오렌지 라이센스 매니저를 실행하셔서 활성화 코드를 activate 하시면 28일동안 쓸 수있어요



자 그럼 디비에 접속해보겠습니다.


RDS 디비 인스턴스가 정상적으로 실행중이여야합니다. (ENDPOINT 주소와 계정 ID, PW 정보가 필요합니다)




(1) Orange 최초 실행 화면



- DBMS Logon 창을 닫아주세요.

- tnsname.ora 파일을 수정하겠습니다.




(2) NetworkConfiguration TOOL



- 상단 메뉴에서 Tools->Network Configuration Tool 클릭




(3) tnsname.ora 수정



1. TNS List의 '+' 버튼 클릭

2. 서비스 네임 입력 ( RDS인스턴스의 DB 이름을 입력해주세요 )

3. Modify 버튼클릭




(4) AWS RDS INSTANCE 정보



- RDS 인스턴스에 들어가면 DB 접속 주소 (엔드포이트)와 DB이름 정보가 있습니다.




(5) Address List


- host 주소에 엔드포인트 주소 입력

- 포트나 프로토콜은 건드리지 않습니다.






(6) Address List


- host 주소에 엔드포인트 주소 입력




(7) 성공




(8) DBMS Logon



- ctrl + n 버튼이나 connect 버튼을 클릭해서 DBMS Logon 창 생성

- TNS Name 클릭후 아까 생성한 db접속정보 클릭




(9) DBMS Logon



- ctrl + n 버튼이나 connect 버튼을 클릭해서 DBMS Logon 창 생성

- TNS Name 클릭후 아까 생성한 db접속정보 클릭




(10) 접속 성공



상단에 scott 계정으로 세션이 생성된걸 확인할 수 있습니다.

Alt+F1로 스키마 브라우져를 통해 접속한 db의 스키마를 볼수 있고 spl tool을 사용하여 query작업 가능합니다~


이상으로 aws rds를 이용한 오라클 디비 생성부터 접속까지의 과정을 마무리 하겠습니다.






안녕하세요.

오늘은 AWS의 RDS 서비스를 이용해 Oracle DB를 만들어보려고 합니다.

간단하게 DB의 기능만 필요하신 분들께서는 굳이 EC2로 서버를 생성해서 Oracle을 설치후 방화벽설정까지 해주는 번거로움 없이 RDS기능을 이용하여 간단하고, 쉽고, 빠르게 본인의 DB를 생성하실수 있습니다.



이번 포스팅에서는 AWS 콘솔에서 RDS를 통해 DB 인스턴스를 생성하는 과정까지 다뤄보겠습니다.






1. AWS 콘솔에서 인스턴스 생성하기



https://aws.amazon.com/ko/






AWS에 로그인후 콘솔창 진입까지의 과정은 생략하도록 하겠습니다.


(AWS CONSOLE)



1) 메뉴 우측 상단의 지역을 '서울'로 바꿔주세요.

2) 데이터베이스 항목의 RDS를 클릭하세요.



(RDS DASHBOARD)



3) 좌측 메뉴의 '인스턴스'를 클릭하세요


4) 메인화면의 DB인스턴스 시작 버튼을 클릭하세요



(Step 1. 엔진 선택)


5) 하단의 'RDS 프리 티어에 적용되는 옵션만 사용' 클릭 해서 프리티어에서만 사용가능한 옵션을 확인할 수 있습니다.

(Amazone Aurora 엔진만 비활성화 되네요)


6) Oracle 엔진을 선택하세요


7) Oracle Enterprise Edition을 클릭합니다


8) 다음단계 버튼을 클릭하세요



(Step 2. DB 세부 정보 지정)



9) 사용하실 DB의 버전을  선택하세요. 저는 11g 버전으로 진행하겠습니다.


10) 스크롤을 내려 설정 탭에서 DB 인스턴스 이름을을 지어주세요


11) Oracle db에 접속할 계정 정보를 입력하세요.


12) 다음단계 버튼을 클릭하세요



(Step 3. 고급 설정 구성)



13) 데이터베이스 이름을 지어주세요. 그외 옵션은 건들이지 않겠습니다. 포트번호는 기본으로 두고 진행할게요 

( 포트번호 바꾸고싶으시면 여기서 바꾸시고 가시면 됩니다.)


14) 스크롤을 내려 DB인스턴스 시작 버튼을 눌러주세요





15) 인스턴스 생성이 끝났습니다. 






인스턴스 탭을 보시면 인스턴스가 생성중인걸 확인할 수 있습니다.

몇 분정도 소요되는게 아니라 10~20분 사이 정도 기다리셔야 생성이 완료되는걸 볼 수 있습니다.

인스턴스 상세정보에서 

'엔드포인트' = DB IP

'포트 = DB PORT

DB 이름, 사용자 이름 정보만 기억 해두면 Oracle 접속하는데 문제가 없습니다. 

비밀번호 까먹지 않도록 잘 기억해두시길~








다음 포스팅에서는 Orange tool을 이용해서 생성한 DB에 접속하는 과정을 보여드리겠습니다.



한글 테이블 레코드 분석




한글 문서에 생성되는 표를 바이너리값으로 파싱하는걸 포스팅해보려고한다.
기본적으로 압축이 풀린 Section Stream을 바이너리 뷰어로 읽었다는 가정하에 시작한다.


구분 

2018(예상) 

2017 

2016 

2015 

Orange 7.0 

20 

Orange Ade 

Trusted Orange 

총액 

28 

11 

10 


위와 같은 표가 한글파일로 생성을 했을때 만들어지는 본문의 문단레코드 바이너리는 아래와 같다.






42 00 60 01 문단 헤더

09 00 00 00 00 08 00 00 0E 00 00 00 01 00 00 00 01 00 00 00 00 00 


43 04 20 01 문단의 텍스트

0B 00 20 6C 62 74 00 00 00 00 00 00 00 00 0B 00 0D 00 


44 04 80 00 문단의 글자 모양

00 00 00 00 01 00 00 00 


45 04 40 02 문단의 레이아웃

00 00 00 00 92 3B 00 00 76 1D 00 00 76 1D 00 00 0B 19 00 00 58 02 00 00 00 00 00 00 18 A6 00 00 00 00 06 00 


47 04 C0 02 컨트롤 헤더

20 6C 62 74 11 23 2A 08 00 00 00 00 00 00 00 00 14 A6 00 00 40 1B 00 00 01 00 00 00 1B 01 1B 01 1B 01 1B 01 EB 0D F7 67 00 00 00 00 


4D 08 00 02 표 개체

06 00 00 04 05 00 05 00 00 00 FE 01 FE 01 8D 00 8D 00 05 00 05 00 05 00 05 00 05 00 02 00 00 00 





---------- 여기서부터는 각 셀의 텍스트 정보를 담는 문단 리스트 ( 셀의 갯수만큼 반복됨 ) ---------- 


48 08 E0 02 리스트 헤더 

01 00 00 00 20 00 00 04 00 00 00 00 01 00 01 00 FC 22 00 00 38 07 00 00 FE 01 FE 01 8D 00 8D 00 02 00 FC 22 00 00 00 00 00 00 00 00 00 00 


42 08 60 01 

03 00 00 80 00 00 00 00 0F 00 00 00 01 00 00 00 01 00 00 00 00 00 


43 0C 60 00 

6C AD 84 BD 0D 00 44 0C 80 00 00 00 00 00 01 00 00 00 


45 0C 40 02

00 00 00 00 00 00 00 00 E8 03 00 00 E8 03 00 00 52 03 00 00 58 02 00 00 00 00 00 00 00 1F 00 00 00 00 06 00 


---------------------- 반복 ----------------------






표 개체를 알아보기전에 조금더 위의 바이너리 형태를 구조화 시켜보면

  1. HWPTAG_PARA_HEADER
  2. HWPTAG_PARA_TEXT
  3. HWPTAG_PARA_CHAR_SHAPE
  4. HWPTAG_PARA_LINE_SEG
  5. HWPTAG_CTRL_HEADER
  6. HWPTAG_TABLE
  7. HWPTAG_LIST_HEADER         ( 셀의 수만큼 반복 )
    1. HWPTAG_PARA_HEADER
    2. HWPTAG_PARA_TEXT
    3. HWPTAG_PARA_CHAR_SHAPE
    4. HWPTAG_PARA_LINE_SEG











이번 포스팅에서 알아볼 내용은 표개체 레코드이다.

표개체 레코드는 4D 08 00 02의 바이너리 값을 갖는데

이중 4D는 TAG_ID 값, 02은 LEVEL, 00 02는 해당 레코드의 데이타 사이즈값이다.

이건 눈대중으로 대충 값을 본거이니 제대로 확인을 하고싶다면 bit단위로 끊어서 파싱하길 바란다.(지금은 눈 대중으로 바이트 단위로 파싱)





자료형 

길이 

설명 

 BYTE stream

 N

 개체 공통 속성 

 BYTE stream

 N2 

 표 개체 속성 

 BYTE stream 

 N3 

 셀 리스트 

 셀 Size * 셀 개수

 전체 길이

가변 

N + N2 + N3 

 


위의 표는 표 개체가 문단에 쓰여질때 갖는 형태이다.


N   = HWPTAG_CTRL_HEADER

N2 =  HWPTAG_TABLE

N3 =  HWPTAG_LIST_HEADER


그러면 컨트롤 헤더부터 뜯어보도록 하자.











47 04 C0 02 컨트롤 헤더

20 6C 62 74 11 23 2A 08 00 00 00 00 00 00 00 00 14 A6 00 00 40 1B 00 00 01 00 00 00 1B 01 1B 01 1B 01 1B 01 EB 0D F7 67 00 00 00 00 


HEADER

TAG_ID = 47

LEVEL = 2

SIZE = 2C(44)


DATA

20 6C 62 74

- 컨트롤 ID 'T', 'B', 'L', ' '의 값을 갖고 있다. (4 BYTE)

11 23 2A 08 00 00 00 00 00 00 00 00 14 A6 00 00 40 1B 00 00 01 00 00 00 1B 01 1B 01 1B 01 1B 01 EB 0D F7 67 00 00 00 00 

- 개체 공통 속성 값이다.



한글 문서에 이 컨트롤 헤더의 용도는 Extended type 의 컨트롤 종류를 나타내는 식별기호 32비트 id가 사용된다고 나와있다.

컨트롤 코드가 큰 범주를 나타내는 식별기호라고 한다면 컨트롤  id는 세부 분류를 나타내는 식별 기호인 셈이다.

예를 들어 단 정의 컨트롤 id는 MAKE_4CHID('c', 'o',' l', 'd')와 같은 형식으로 정의된다.


자 이쯤되면 컨트롤 헤더가 어떠한 용도로 사용되는지 이해는 된거 같으니 다음 레코드인 표 개체(HWPTAG_TABLE) 레코드를 분석 해보자.














4D 08 00 02 표 개체

06 00 00 04 05 00 05 00 00 00 FE 01 FE 01 8D 00 8D 00 05 00 05 00 05 00 05 00 05 00 02 00 00 00 


자료형 

길이 

설명 

UINT32

2

속성 

UINT16

2

줄의 갯수 

UINT16

2

칸의 갯수

HWPUINT16

2

셀 스페이싱 

BYTE stream

8

안쪽 여백 정보 

BYTE satream

2*n

Row  

UINT16

2

Border Fill ID 

UINT16

2

Valid Zone Info Size 

BYTE satream

2

영역 속성 

 전체 길이

가변 

22 + (2*row) + (10*zone) 

( 표 개체 속성 )



HEADER

TAG_ID = 4D

LEVEL = 3

SIZE = 20(32)


DATA

06 00 00 04 = 속성 값 (67108870)

05 00  = 줄의 갯수

05 00  = 칸의 갯수

00 00  = 셀스페이싱 0

FE 01  = 안쪽 여백 정보 왼쪽 510

FE 01  = 오른쪽 510

8D 00 = 위쪽 141

8D 00 = 아래쪽 141

05 00 = RowSize

05 00 = RowSize

05 00 = RowSize

05 00 = RowSize

05 00 = RowSize

02 00 = Border Fill (채우기 정보)

00 00 = ZoneInfo


HWPTAG_TABLE 레코드의 정보를 뜯어보면 해당 테이블에 대한 정보가 저장되어있는것을 확인 할 수 있다.

바이너리 값을 보았을때 위의 테이블 정보는 5행 5열의 테이블이며 표의 여백정보를 확인 할 수있다.

그렇다면 셀안에 들어가는 정보는 어디에 저장되는것일까?

위에 표에서도 설명되었지만 다음 레코드에서 셀의 정보가 저장된다.












48 08 E0 02 리스트 헤더 

01 00 00 00 20 00 00 04 00 00 00 00 01 00 01 00 FC 22 00 00 38 07 00 00 FE 01 FE 01 8D 00 8D 00 02 00 FC 22 00 00 00 00 00 00 00 00 00 00 


 자료형

길이 

설명 

BYTE stream 

문단 리스트 헤더 

BYTE stream

26 

셀 속성 

전체 길이

가변 

26+n byte 

( 셀 리스트 )




 자료형

길이 

설명 

 INT16

문단 수 

 UINT32

속성 ( 자세한건 한글 도큐먼트 참조 )

 전체 길이

 

( 문단 리스트 헤더 )

 



자료형 

길이 

설명 

 UINT16

 2

셀 주소 (COL) 

 UINT16

 2

셀 주소 (ROW) 

 UINT16

 2

열의 병합 개수 

 UINT16

 2

행의 병합 개수 

 HWPUINT

 4

셀의 폭 

 HWPUINT

 4

셀의 높이 

 HWPUINT16 array[4]

 2*4

셀4방향 여백 

 UINT16

 2

테두리/배경 아이디 

 전체 길이

26

 

( 셀 속성 )


HEADER

TAG_ID = 48 

LEVEL = 3

SIZE = 40(64)


DATA

01 00 00 00 = 문단 수

20 00 00 04 = 속성

00 00  = 셀 주소 열

00 00  = 셀 주소 행

01 00  = 열의 병합 개수

01 00  = 행의 병합 개수

FC 22 00 00   =  셀의 폭 WIDTH

38 07 00 00   =  셀의 높이 HEIGHT

FE 01  = 왼쪽 마진

FE 01  = 오른쪽 마진 

8D 00  = 위쪽 마진

8D 00  = 아래쪽 마진

02 00  = 채우기 아이디

FC 22  = 필드 이름

00 00 00 00 00 00 00 00 00 00  = 알려지지 않은 바이트 



위와 같이 분석이 되는데 셋팅해야할 값이 너무나 많다.

0행 0열의 셀의 정보를 뜯어본 값이다. 


이후로는 문단의 정보가 들어가게 되는데 해당 셀의 텍스트 문단에 대한 정보이다.


42 08 60 01 

03 00 00 80 00 00 00 00 0F 00 00 00 01 00 00 00 01 00 00 00 00 00 


43 0C 60 00 

6C AD 84 BD 0D 00 44 0C 80 00 00 00 00 00 01 00 00 00 


45 0C 40 02

00 00 00 00 00 00 00 00 E8 03 00 00 E8 03 00 00 52 03 00 00 58 02 00 00 00 00 00 00 00 1F 00 00 00 00 06 00 


위의 레코드들에 대한 설명은 생략하겠다.

이렇게 셀리스트가 다 끝날때까지 반복이 된다.




어도비 프리미어 프로 cc 2018버전을 노트북에 설치했는데

요놈만 유독 동영상 파일을 임포트할때 디스크에서 읽어오지 못한다는 에러는 내뿜었다.


덕분에 2~3번 재설치후 한번의 포맷까지 했지만

증상은 여전했고


한글판이였던 프리미어를 영문으로 바꾼뒤 해당 에러 메세지를 구글링해서

답을 찾아 냈다.





한글판에서는

'디스크에 있는 파일을 열지 못했습니다.' 라는 에러로 출력된다.



구글링 해보니 해당 에러는 윈도우즈 버그인듯 싶다.

마이크로 소프트에서 업데이트 파일을 제공한다.




https://www.microsoft.com/en-us/software-download/mediafeaturepack

( 마이크로 소프트 )


위의 주소에서 본인에게 해당되는 os 버전을 선택후 다운로드하여

업데이트파일을 설치하면된다.


한번의 리부팅이 있은 뒤 프리미어를 다시 키고 파일을 임포트하면 정상적으로 불러오는걸 확인 할 수 있다.




  1. dudans314 2018.04.22 03:27 신고

    저도 2018프리미어에서 mp4를 불려오려는데도 불구하고 파일 가져오기 실패 에러가 떴습니다. 올리신 주소에 가서 파일을 받았는데,
    그런데도 여전히 이 에러가 계속 있습니다.... 어떻게 해야할지 아시나요 ???

    • 루우지 2018.04.22 14:34 신고

      음... 위 사이트에서 os bit에 맞는걸 받아도 안된다고 하시면 저도잘 모르겠네요.. ㅜ ㅜ

섹션 스트림 분석하기 빈문서 분석




섹션 스트림은 바디스토리지 내에 존재하는 스트림이다.

기본적으로 레코드 형식이며, 압축이 되기도 한다.

이번 포스팅에서는 빈 도큐먼트를 파싱하는 과정을 담아보려고 한다.


섹션 스트림을 분석하기 위해서는 한글과 컴퓨터에서 제공하는 도큐먼트를 먼저 정독해야한다.


TagID 

SIZE 

LEVEL 

설명

 HWPTAG_PARA_HEADER

22 

 0 

문단 헤더 

 HWPTAG_PARA_TEXT

가변

 1 

text 정보

 HWPTAG_PARA_CHAR_SHAPE

가변

 1 

글자 모양 

 HWPTAG_PARA_LINE_SEG

가변

1

글자 레이아웃 

 HWPTAG_PARA_RANGE_TAG

가변

1

영역 태그 

 HWPTAG_CTRL_HEADER

 1 

 컨트롤 헤더

 HWPTAG_LIST_HEADER

 2 

 문단 리스트 헤더

 HWPTAG_PAGE_DEF 

40 

 2 

 용지 설정

 HWPTAG_FOOTNOTE_SHAPE

30 

 2 

각주/미주 모양 

 HWPTAG_PAGE_BORDER_FILL 

14 

 2 

쪽 테두리/배경 

 HWPTAG_SHAPE_COMPONENT 

 2 

개체 

 HWPTAG_TABLE 

가변

 2 

 표 개체

 ... (기타 그리기 개채)

가변

 

 

 HWPTAG_CTRL_DATA

가변

3

 

 ... (기타 레코드) 

 

 

 

(필자는 초반에 나오는 이 표를 보지 못해서 각각의 레코드들을 전부 읽어 레벨을 기록했다. -_-;)



위의 표는 Section Stream에서 본문 정보를 담고있는 레코드들을 추려서 표로 정리해본것이다.


포스팅을 하기 앞서 문단이란 무엇인지 알고 가야한다.

한글문서에서의 문단은 '엔터'로 기준을 나눌 수 있다.


첫번째줄 두번째줄이 엔터로 나뉘어서 작성이 되면 2개의 문단으로 인식한다.

한줄에 엔터없이 2번째줄까지 작성되어 내려가게 되면 그건 1개의 문단이다.


섹션의 첫 문단에는 구역 정의 레코드들이 들어가기 때문에 중간 중간의 문단 레코드들보다 필요한 사이즈가 크다. 

첫번째 문단 정보에는 ~ 레코드들이 들어간다.

그다음 문단 부터는 헤더, 텍스트, 글자모양, 글자 위치 정보의 레코드들이 하나의 문단 정보를 이루어 작성된다.



첫번째 문단

 

 두번재 문단

 

 3번째 문단

 

 header + text + shape + line + range_sahpe+ ctrl header + footnote+ border fill + etc

 

 HEADER

 TEXT

 CHAR_SHAPE

 LINE_SEG

 

 HEADER

 TEXT

 CHAR_SHAPE

 LINE_SEG

 

 +

 +

 

 

 

 

 

 

 


위에 표는 스트림에 작성되는 텍스트 헤더들을 표로 정리해본것이다.

첫번재 문단 정보에는 기본적인 텍스트( 테이블도 될수 있음) 정보들이 들어가며 추가적으로 각주/미주 정보라던지, 용지 설정 정보, 각주/미주 모양의 정보와 같은 부수적인 레코드들이 같이 붙는 형태이다.

두번째 문단 부터는 해당 문단에 작성된 텍스트 정보만 작성이 된다. 비교적 첫번째 문단에 비해 담기는 정보가 작다.






이번 포스팅에서는 한글문서가 어떤형식으로 파일구조를 갖고 있고 

어떻게 바이트단위로 박혀있는지 확인 해보는 시간을 갖기 위해 글을 써보았다.



한글 문서는 Compound File (OLE2) 구조를 갖는다.

스토리지와 스트림 형태를 띄고 있는데, 스토리지는 폴더, 스트림은 파일이라고 이해하면 쉽다.

Root 밑에는 여러개의 스토리지와 스트림이 존재한다. 

아래의 표는 hwp 파일이 갖고있는 구조를 표로 표현한것이다.



 

 이름

길이 

레코드 

압축 / 압축화 

 파일 인식 정보

FileHeader (Stream)

고정 

 

 

 문서 정보

DocInfo  (Stream)

고정 

true 

true 

본문 

BodyText (Storage)

- Section0   (Stream)

가변 

true 

true 

문서요약 

\005HwpSummaryInformation  (Stream) 

고정 


 

바이너리 데이타 

BinData  (Storage)

- BinaryData0   (Stream)

- ...   (Stream)

가변 

 

true 

미리보기 텍스트 

PrvText  (Stream)

고정 

 

 

미리보기 이미지 

PrvImage  (Stream)

가변 

 

 

문서 옵션 

DocOptioin  (Storage)

- _LnkDoc  (Stream)

- DrmLicense  (Stream)

- ...

가변 

 

 

스트립트 

Script  (Storage)

- DefaultScript  (Stream)

- JScriptVersion  (Stream)

- ...

가변 

 

 

 XML 템플릿

XML Template (Storage)

- Schema   (Stream)

- Instance  (Stream)

- ...

가변 

 

 

문서 이력 관리 

DocHistroy  (Storage)

- VersionLog0  (Stream)

- VersionLog1  (Stream)

- ...

가변 

true 

 

true 



(ssviewer로 본 한글파일 내부의 구조)

각 스토리지와 스트림의 정보는 한글과 컴퓨터에서 제공하는 문서를 참고하면 이해하기 쉽다.



간단하게 파일 인식 정보를 갖고있는 FileHeaer 스트림을 뜯어보면 아래와 같은 구조를 갖고 있다.




FileHeaer은 256바이트의 고정된 길이를 갖는 스트림이다.

사진에 첨부한 FileHeader 스트림의 Binary값을 확인해보면 

초기 32바이트에는 한글 문서 형식을 알리는 시그니처 문구가 박혀있는것을 확인할 수 있다.


그다음 4바이트 크기로 한글 문서의 버전을 확인 할 수 있다. 

바이너리 뷰어로 해당 스트림을 열어보면 5.0.3.0 버전이 찍힌걸 볼 수있다.


그다음 4바이트는 파일헤더의 속성을 갖는 비트들이다. 0비트부터 11비트는 각각의 문서 속성 정보를 갖고있다.

그 이후 나머지 216바이트는 예약 바이트들로 0의 값으로 초기화되어 있는 형태를 확인할 수 있다.




한글 문서중 제일 파악하기가 쉬운게 파일헤더 스트림이다.

고정 길이를 갖고 있기에 256바이트 중에 

처음 32바이트는 시그니처, 

그다음 4바이트는 파일의 버전, 

그다음 4비트는 속성 정보,

나머지 216바이트는 예약비트로 파싱할때 비교적 수월하다.

암호화도 안되있기 때문에 그냥 가져와 바이트단위로 짜르면 한글문서의 정보를 가져올 수 있다.





다른 스트림도 이러면 좋겠지만 가변적인 길이를 갖는 스트림도 많고 압축/암호화도 걸린게 많기 때문에 

다른 스트림을 파싱하거나 렌더링할때에는 쉽게 되지는 않을꺼같다.




몇몇 스트림들은 레코드형식(헤더와 데이터의 결합)으로 구성되어있기도 하며, 

MSDN의 프로퍼티 셋 형식을 취한 스트림도 있다.




대표적인 예로 \005HwpSummaryInformation 스트림이 MSDN의 프로퍼티셋 형식을 갖고 있다.

Summary Information에 대한 자세한 설명은 MSDN을 참고

The Summary Information Property Set

The DocumentSummaryInformation and UserDefined Property Set

(한글 공식 문서의 설명)


SumamryInformation 은 MSDN의 프로퍼티셋 형식을 따른다.

해당 정보는 아래의 URL에 설명되어있다.


https://msdn.microsoft.com/ko-kr/library/windows/desktop/aa380376%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396


HwpSummaryInformation 정보를 가져오기 위해서는 바이트 단위로 짜르는것 보다는

Apache POI API에서 제공하는 프로퍼티 ID단위로 값을 가져오면 된다.

이 부분은 추 후 포스팅을 통해 값을 가져와보도록 하겠다.









마지막으로 설명하는 내용은 레코드 형식의 데이터다.


(한글의 데이터 레코드 구조)


논리적으로 연관된 데이터들을 헤더 정보와 함께 저장하는 방식을 데이터 레코드라고 한다.

레코드 구조를 가지는 스트림은 연속된 여러 개의 레코드로 구성된다. 데이터 레코드는 헤더와 데이터로 구성되며 각 헤더 정보를 활용하여 전체 논리적 구조를 생성하게 된다.

레코드의 헤더에는 데이터 확장에 대비한 정보를 가지고 있다. 따라서 이후에 ᄒᆞᆫ글의 기능이 확장되어 레코드에 데이터가 추가되는 경우에도 하위 버전의 ᄒᆞᆫ글이 상위 버전의 ᄒᆞᆫ글 문서를 읽을 수 있도록 하위 호환성이 보장된다.



Tag ID : 레코드가 나타내는 데이터의 종류를 나타내는 태그이다. Tag ID에는 10 비트가 사용되므로 0x000 - 0x3FF까지 가능하다.

0x000 - 0x00F = 일반 레코드 태그가 아닌 특별한 용도로 사용한다.

0x010 - 0x1FF = 글에 의해 내부용으로 예약된 영역(HWPTAG_BEGIN = 0x010)

0x200 - 0x3FF = 외부 어플리케이션이 사용할 수 있는 영역


Level : 대부분 하나의 오브젝트는 여러 개의 레코드로 구성되는 것이 일반적이기 때문에 하나의 레코드가 아닌 "논리적으로 연관된 연속된 레코드"라는 개념이 필요하다. 레벨은 이와 같이 연관된 레코드의 논리적인 묶음을 표현하기 위한 정보이다. 스트림을 구성하는 모든 레코드는 계층 구조로 표현할 수 있는데, 레벨은 바로 이 계층 구조에서의 depth를 나타낸다.


Size : 데이터 영역의 길이를 바이트 단위로 나타낸다. 12개의 비트가 모두 1일 때는 데이터 영역의 길이가 4095 바이트 이상인 경우로, 이때는 레코드 헤더에 연이어 길이를 나타내는 DWORD가 추가된다. , 4095 바이트 이상의 데이터일 때 레코드는 다음과 같이 표현된다.

(한글 문서파일형식에서 발췌)



대표적으로 DocInfo스트림과 Body 스토리지 내부의 Section 스트림에서 데이터 레코드형식을 취하고 있다.

TagID의 0x010에 HWP TAG 아이디를 넣음으로써 해당 레코드가 어떤 데이터를 갖고 있는지 구분할 수 있다.


현제 필자는 HwpLib 라이브러리를 통해 한글문서를 파싱, 렌더링 하는 부분을 분석중이다.

바이너리 데이터를 읽고 쓰는것이 익숙하지 않아 하루에도 머리가 여러번 쪼개지는 고통을 받고 있다.ㅋㅋ ㅜㅜ

빨리 이 과제를 마무리 짓고 싶다. ㅜㅜ




















  1. 지나가던 바밤바 2018.03.21 21:26 신고

    분석끝나면 예제샘플 올려주시나여?

회사 수습 연구원으로 진행하게될 과제이다.

Java로 .hwp파일을 파싱 후 렌더링하기까지가 숙제(과제)이다. 


처음에는 low레벨까지 내려가 binary형태에서 부터 파싱을 해야 하나 싶어서 걱정이 많았는데

자비로우신 팀장님께서 결과만 나오면 일단 된다고 하셨기에 갓구글님께 도움을 요청했다.


http://www.hancom.com/etc/hwpDownload.do 

(한글 문서 파일 구조 5.0 도큐먼트)


처음에 70페이지 가까이된 한글 문서 파일 구조를 던져주실때 식겁했는데

다행스럽게도 


https://github.com/neolord0/hwplib

누군가가 만들어주신 라이브러리가 존재했다.


한글과 컴퓨터(한컴)에서 만든 워드프로세서 "한글"의 파일에 대한 라이브러리입니다.

본 라이브러리는 JAVA로 구현되었으며, 한글 파일의 하부 구조인 Microsoft Compound File의 부분은 Apache-POI의 POIFS File System을 사용하여 처리합니다. 본 라이브러리는 한글과컴퓨터의 한글 문서 파일(.hwp) 공개 문서를 참고하여 개발하였습니다. 한컴에서 제공하는 문서(HWP 5.0)는 아래URL에서 받을 수 있습니다.

http://www.hancom.com/etc/hwpDownload.do?gnb0=269&gnb1=271&gnb0=101&gnb1=140



github에 올라온 프로젝트 소개글이다.

처음에 소스 분석을 하겠다고 처음부터 쭉 따라갔는데 왠 아파치 poi 라이브러리가 나오길래 막막했었다.

알고보니 아파치 poi 라이브러니는 마이크로소프트사의 워드 포맷형식을 지원하는 라이브러리였다.


여기서 생긴 의문이 

그렇다면 한글의 .hwp파일은 MicroSoft의 word 형식이랑 같다는것인가?

hwp나 word는 같은 Compound File Binary Format 이였던것이다.

한글은 XML기반으로도 파일이 제공된다고 팀장님이 알려주셨다.

CFBF, OLE라고도 불리는 이 형태는 하단의 블로그에서 자세하게 설명해준다.

http://www.reversenote.info/ole-file-format/



이제 위의 라이브러리를 통해 1차 목표인 한글파일 읽어들여

콘솔창에 띄우는 결과물을 만들어낼 생각이다.

이미 만들어져있기에 거기까진 어렵지 않지만 그냥 가져다가만 쓰면 개인과제에 의미가 없어보여서

최대한 라이브러리를 뜯어보아서 어떤형식으로 .hwp파일을 파싱하는지 공부해보려고 한다.








+ Recent posts

티스토리 툴바