안녕하세요.

이번엔 AWS의 EC2서비스를 이용해 JIRA서버를 설치해보는 시간을 갖도록 하겠습니다. 여러명이서 협업을 하게될 경우 쓰이면 참 좋은 툴이죠. 팀원들의 작업을 공유할수있고 어느정도 진척 사항을 가지고 있는지 한눈에 쉽게 확인할 수 있습니다. 비슷한게 있다면 트렐로가 있겠네요. 


아틀라시안사에서 제공하는 지라, 트렐로, 컨퓰런스, 소스트리 저는 학생때 다 써봤던 제품들이네요. 지금도 많이 쓰고 있답니다.  그렇담 실제 로 설치에 앞서 각 제품을 다운로드를 먼저 받고 시작하겠습니다.


우선 아틀라시안에서는 30일 트라이얼을 제공해줍니다. 서버형태와, 클라우드형태로 트라이얼을 제공해주는데요. 클라우드형태의 트라이얼을 신청하게되면 아틀라시안에서 제공해주는 도메인에 할당받아 30일동안 다른 서버셋팅없이 간단하게 이용이 가능합니다. 서버형태의 트라이얼은 설치파일을 다운로드받아 window나 linux환경의 서버에서 구동하여 사용하는 형태입니다. 


나는 간단하게 30일 써보고 말꺼야 라고 생각하시는 분들은 클라우드형태의 트라이얼을 이용하시는게 적당하구요. 장기적인 프로젝트에 적용할꺼야(학생이라면 공모전, 졸업작품 등)라고 하시면 AWS를 이용해 서버를 구축해서 사용하시는것도 좋은경험이겠죠?


이번 시간에는 AWS에 Ubuntu 16.04에 jira서버를 설치해보도록 하겠습니다. 


진행에 설치를할때 2가지 유형을 선택할수 있는데요. 트라이얼 / 운영용으로 나뉩니다. 트라이얼은 자체적인 파일 형태의 db로 저장이 되는 반면 운영용은 외부 db를 사용합니다. mysql, maria, 포스테그래 등을 지원합니다. 저는 운영용으로 설치하려기 때문에 mysql이 사전에 설치되어 있어야 합니다.






준비물


1. 아틀라시안 계정 (트라이얼용 라이센스 발급용)


2. ubuntu용 jira 설치파일




진행에 앞서 환경이 미리 셋팅되었다는 가정하에 진행하겠습니다.

1. aws에 ubuntu 인스턴스가 구동중

2. mysql 서버가 구동중

3. ftp를 통해 파일전송이 가능한 환경

4. putty를 통해 터미널 접속이 가능한 환경


위의 4개는 이번 포스팅들에서 다뤘던 내용들이니 참고해주시기 바랍니다.




1. jira 서버 다운로드 받기


https://www.atlassian.com/software/jira/download


- 아틀라시안 계정은 알아서 생성해주세요. 어렵지 않습니다.

- 위의 url에서 Linux 64bit를 클릭후 다운로드 받으세요






스레드란?

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의 코드와 우리가 작성한 코드 모두가 동작한다. 












+ Recent posts

티스토리 툴바