블로그 이미지
자유로운설탕

calendar

1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

Notice

2017. 7. 2. 19:54 프로그래밍

  이번 시간에는 파이썬 웹 프레임워크인 Flask, Django 를 살펴보기 전에 웹을 구성하는 기초적인 부분들에 대해서 살펴보려고 한다. HTML, CSS, Javascript, Web Server(IIS), Web Language(ASP), Ajax 에 대해 개념을 간단히 설명하고, 간단한 예제를 만들어 시연해 보면서, 일반적인 웹 환경이 어떻게 구성되어 있는지를 살펴보며, 다음 시간에 얘기할 비교적 최신 개념인 MVC(model view controller)나 Url Rewriting(Routing) 설명을 위한 사전 지식을 쌓아 놓으려 한다. 다만 위 하나하나의 분야는 이렇게 블로그 한 챕터에 담기에는 각각 수 권의 책으로 따로 분리해야 할 만큼 넓은 분야라서, 가볍지만 필요한 개념은 이해할 수 있을 정도로 설명을 진행해 보려고 한다. 만약 기존에 ASP, PHP, JSP 등으로 웹 페이지를 만들어 봤던 분들은 이번 장은 대충 넘겨보거나, 생략하여도 될듯 하다.

 

 

 

[목차]

0. 왜 파이썬 공부에 구글을 이용하는게 좋은가?

1. 언어를 바라보는 방법. 파이썬을 어떻게 바라봐야 할까?

2. 파이썬 설치와 환경, 버전 선택 하기의 이유.

3. 만들고자 하는 기능을 모르는 조각으로 나눠 조사해 보기

4. 데이터 베이스에서 내용 가져와 출력하기

5. 암호화 모듈을 이용해 암복호화 해보기

6. 퍼즐 조각들을 합쳐보기

7. 엑셀 파일 사용해 보기 -> 부록 : fuction 을 이용해서, 코드 정리해 보기

8. 정규표현식을 왜 사용해야 할까? 언어속의 미니 언어 정규표현식 살펴보기

9. 입력과 결과를 GUI 화면과 연결해 보기

10. Whois API 이용해 보기

11. 웹페이지 호출해 내용 파싱 하기(BeautifulSoup 그리고 한계)

12. 자동화 - 웹 자동화(with Selenium)

13. 자동화 - 윈도우즈 GUI 자동화(with pywinauto)

14. 자동화 - 작업 자동화

15. 수학 라이브러리 살펴보기

16. 그래픽 라이브러리 살펴보기

17. 머신러닝에서의 파이썬의 역활

18. 웹 프로그래밍 - Legacy Web

19. 웹 프로그래밍 - Flask 살펴보기(feat. d3.js)

20. 웹 프로그래밍 - Django 살펴보기

21. 정리 - 이런저런 이야기

 

 

 

 

[들어가면서]

  왜 파이썬 공부 관련 글에서 하나의 챕터를 따로 빼내 HTML, Javascript, CSS 등의 다른 웹 언어와 지금은 유행이지난 스크립트형 웹 프로그래밍 언어인 ASP 를 언급하려 하냐면, 다음 시간을 위해 Flask 와 Django 를 살펴보다보니, 처음 웹을 접하는 사람들이 접근하기는 꽤 어려운 구조라는 생각이 들었기 때문이다. 아마 Django 같은 프레임 워크를 이해하기 위해서는 기존 Legacy Web 에 대한 지식은 기본으로 갖춘 상태에서, Url Rewriting, MVC 같은 비교적 최신의 개념들에 대한 이해가 추가적으로 선행되야 할 듯 싶다. 물론 파이썬도 어느정도 익숙해 졌다고 가정하고 말이다.

 

  다른 웹 프로그래밍 언어 등에서 기존 웹을 어느정도 경험해 본 사람들은 해당 쪽 방식과 접근 방식이 다른 부분 위주로 비교해 가면서 적응하면 되겠지만, 웹 프로그래밍 세상에 처음으로 들어온 사람들은 해당 프레임워크를 배울때 웹의 일반적인 지식들이 한꺼번에 같이 쏟아져 들어오기 때문에, 아마도 뭐가 프레임위크에 대한 얘긴지, 뭐가 일반적인 웹 기술에 대한 얘기인지 혼란에 빠질 듯 싶다. 또 해당 방식이 기존 방식에 대해 어떤 장단점을 가지고 있는 건지를 알지 못하고 맹목적으로 받아들일 수도 있다. 앞의 시간하고 비교하면 웹 자동화 프레임워크인 selenium 을 사용하고 싶은데 웹 동작을 담당하는 HTML 이나 자바스크립트를 이해 못한 상태에서 막연히 배우는 것보다도 더 힘들지 않을까 싶다. 

 

  또한 구조적인 프레임워크는 분명히 여러 장점을 가지고 있는건 맞지만, 초보자의 입장에서 봤을때는 ASP 와 같은 Legacy Web Language은 아무래도 URL 이 바로 웹 페이지 파일 자체와 일치되는 1:1 관계의 직관성을 제공하여 접근하기가 좀더 쉽지 않을까 싶다. 또한 ASP, PHP 같은 스크립트 언어들은 컴파일 과정이 없이 바로 결과를 볼수 있어서 .NET 이나 JAVA 같은 컴파일 형 언어보다는 에러를 쉽게 만나고 수정해 볼수 있다(개인적으로는 수많은 에러를 만나고 해결하는 과정이 프로그램을 배우는데는 아주 중요한 경험이라고 생각한다). 그래서 flask 나 Django 에 대한 컨셉 설명을 용이하게 해보기 위해 앞서 머신러닝 챕터 진행 전에 수학, 그래픽 라이브러리를 소개하여 분리할 수 있는 개념을 떼어낸 것과 비슷한 일을 시도하려고 한다.

 

 

[IIS, ASP 에 대해서]

  참고로 여기서 잠시 다루는 웹 스크립트 언어인 ASP 는 현재 글을 진행하는 환경인 윈도우즈10 홈 버전이면 프로그램 추가/제거를 이용해서, IIS(Internet Information Server-아파치 같은 윈도우즈쪽 MS 웹 서버임)를 설치해서 비교적 간단히 사용이 가능하다. ASP 는 PHP, JSP 와 거의 기능적으로 비슷하다고 봐도 될듯 하다(뭐 두 언어는 계속 발전해 와서 .NET 으로 전략적으로 마이그레이션 하면서 버려진 ASP 와는 갭이 크다고 말하시는 분들도 있겠지만, 개인적으로 생각하기에는, 서로들 좋아 보이는 점들을 한참 차용했기 때문에 일반적인 기능 범위는 비슷하고 문법 측면만 틀리다고 생각한다. 물론 어떻게든 비슷하게 구현을 할수 있다는 얘기지, 더 이상 새로운 문법 구조나 라이브러리가 지원 되지 않기 때문에 난이도가 같다는 것은 아니다).

 

  지금 생각하면 좀 낯설지만 예전엔 위의 3개 언어가 웹프로그래밍 언어의 패권을 다투기 위해 경쟁하던 시대도 있었었다. 지금은 뭐 더 많은 웹프로그래밍 언어들이 경쟁하는 춘추전국시대에 있는듯 하며, 사실 고수준 언어가 많은 부분을 모듈화 해서 관리해 주지만, 어떤 언어를 쓰냐보다는 어떻게 설계하여 쓰느냐가 더 중요한듯도 싶긴한다.

 

  윈도우즈 7의 경우는 그때의 MS 라이센스 정책의 방향 땜에 홈 버전에서는 IIS 설치가 안되고, 프로페셔널 버전에서만 지원되니, 혹 윈도우즈 7 홈 환경으로 강좌를 따라오고 있는 분이라면, 눈으로 코드 흐름만 살펴 보셔야 할듯 싶다. 개념을 설명하기 위해 관련 코드를 만든것이기 때문에 그러셔도 무방하다(의사코드 대신 ASP 를 사용했다고 봐도 좋을듯 싶다)

 

 

 

 

[웹은 어떻게 동작하는가?]

  웹 브라우징은 기본적으로 아래 그림과 같이 브라우저와 웹 서버가 중심이 되어 일어나는 행위이다. 웹서버는 우리가 많이 아는 IIS(ASP, .NET), 아파치(PHP), 톰캣(JSP) 부터 node.js(Javascript), 파이썬 자체 웹서버 등 다양하다. 브라우저 주소창에 웹페이지 주소를 입력하거나, 또는 결제창에서 결제 버튼을 누르거나, 특정 페이지에서 다음 버튼을 누르거나 할때, 브라우저가 웹 서버에 명시적으로 요청을 보낸다. 해당 요청은 패킷이라는 조그만 신호 단위에 담겨서, 네트워크 카드를 통해서, 인터넷 세상으로 나가게 된다. 

 

  인터넷 세상에서는 라우터와 스위치라는 장치를 통해서 해당 되는 주소(정확하게는 DNS 서버를 통해 얻어온 IP)가 가리키는 사이트로 이동되게 된다. 그럼 해당 서버는 그 요청을 받아서, 포트에 대기(listen)하고 있는 있는 웹 서버(예를 들면 아파치) 프로그램에게 전달하게 된다. 해당 웹 서버 프로그램은 해당 요청의 form 요소등에 대해서 프로그래밍 로직을 적용하여 DB의 내용을 조회하거나, 저장하거나 한후 최종 처리 결과를 HTML 형식으로 꾸며 사용자 브라우저에게 다시 보내준다. 사용자 브라우저는 해당 정보를 구조<tag>에 맞게 적절히 해석하여 사용자에게 그래픽 적인 웹 페이지 화면으로 보여준다. 브라우저에서 특정한 옵션을 설정하는 경우 브라우저와 네트워크 카드 사이에서 웹 프록시 형태의 프로그램이 패킷을 중계하는 일도 있는데, 그게 우리가 사용해본 fiddler 같은 HTTP 패킷을 보는 툴의 동작 원리이다.

 

 

 

 

[웹을 구성하는 언어들]

  웹에서 사용되는 언어들은 보통 어느 측면에서 사용되느냐에 따라 클라이언트 언어(브라우저)와 서버(웹 서버 프로그램) 언어로 나눠볼 수 있다. 클라이언트 언어는 HTML, CSS, Javascript, AJAX 같은 언어로 이루어져 있고, 서버 언어는 JAVA, .Net 같이 컴파일이 되어 동작하는 언어와 ASP, PHP, JSP, Python 같은 스크립트 형태(사실 이것도 실시간 컴파일이라고 봐야된다. 그리고 PHP 도 컴파일 해서 사용이 되는 것 같기도 하고, JSP 도 뒷단은 자바 class 파일을 호출하는 경우도 많은 듯 하니 사실은 구분이 조금 묘하긴 하다)로 이루어진 언어로 이루어져 있다(요즘은 Javascript로 동작하는 서버 환경인 Node.js 가 나오는 등 서버와 클라이언트 언어라는 절대적인 구분이 점점 모호해 져가는 듯은 하다. 파이썬도 웹과 시스템 양쪽에서 쓸수 있듯이 말이다). 또 DB쪽 언어인 SQL(Structed Query Lanauage) 언어도 있다. 브라우저는 클라이언트 언어들을 이용해 사용자의 액션에 반응하거나, 그래픽적인 화면 UI을 보여주고, 사용자의 입력들을 받아 form 이나 json 등에 담아서 서버 쪽으로 전달한다. 서버 언어는 전달된 클라이언트의 데이터들을 미리 작성된 프로그램 로직에 맞춰 처리하여, DB에 저장하거나 하며, 이후 클라이언트 언어 형태로 브라우저에게 적절히 응답을 주게된다.

 

 

  HTML(Hyper Text Markup Language) 은 우리가 매일 클릭하는 링크(hyperlink)와, 페이지 구조를 담고 있는 태그(markup)로 이루어진 언어이다. CSS(Cascade Sytle Sheet)는 초기 HTML 로부터 디자인 속성들을 따로 분리해낸 언어라고 볼 수 있다. Javascript는 초창기의 정적인 HTML위에 event 속성과의 협업을 통한 사용자와의 상호작용으로 생명을 불어 넣어주었다고 할수 있으며, HTML을 개념적으로 구조화한 DOM(Document Object Model) 객체를 이용하여 HTML 요소들을 조작한다. 우리가 웹에서 보는 모든 동적인 동작들이 Javascript 의 출현 덕분에 일어난다고 보면 되며, 파이썬과 비슷할 정도로 다재다능하고 복잡한 언어이며, Node.js 의 출현 덕분에 서버 쪽 언어로도 사용되게 됬다. Ajax(Asynchronous JavaScript and XML)는 자바스크립트로 만들어진, 멈춰진 HTML 페이지 뒤에서 리퀘스트를 날릴 수 있는 라이브러리 묶음이라고 생각하면 될것 같고, HTML 의 <form> 을 이용하지 않고도 json, xml, text 등의 데이터 형태를 이용하여 브라우저 뒤에서 비동기적으로 통신하는 것을 지원한다.

 

  서버쪽의 JAVA나 .NET 등의 컴파일 언어의 경우 사용전 빌드 과정이 꼭 필요하고, 초기 공통 바이너리 로딩 등에 부하가 걸린다고 하지만, 일반적으로 초기 로드 동작이 끝나면 스크립트 형식의 언어보다는, 메모리를 이용해 좀 더 자원을 효율적으로 공유한다고 한다. SQL(Structed Query Language) 은 MSSQL, Oracle, MySQL 등에 쿼리를 날리는 공통 표준으로 실제로는 SQL서버 종류별로 문법이 조금씩 차이는 있으며, 사용자의 요청에 따라 서버 쪽 프로그램에서 데이터를 조회하거나 저장하는 데 사용한다.

 

 

 

 

[HTML 살펴보기]

  HTML 은 아래의 그림 처럼, 하이퍼링크와 태그로 이루어진 언어이다. Markup 은 문서의 활자나 구조를 잡아주는 것을 얘기하는데, HTML 에서도 비슷하게 구조를 잡아주는 요소의 의미를 가지게 된다. HTML 은 밑의 로봇 그림처럼 헤더(header)와 바디(body)라는 것을 가지게 되는데(HTML5 에는 푸터-footer도 있긴 하던데, 어찌 봄 전체 구분 구조자체가 많이 바뀌었으니 여기서는 무시하자), 헤더에는 문서에 대한 여러가지 배경정보(제목, 작성자 등)들이 들어가고, 바디안에 우리가 실제 브라우저에서 보는 화면들이 들어간다고 보면 된다.

 

  그림을 보면 <html> 태그안에 <head> 와 <body> 태그 쌍이 있고, 제목(<title>)을 좀 큰 글자(<h1>)로 보여주고, 한칸을 띈후(<p>), '본문' 이라고 적힌 HTML 문서가 브라우저에서 열리면 해당 정의된 대로 화면에 표시되는 것을 볼 수 있다.

 

 

  브라우저가 HTML 을 해석하는 것은 사실 이미지뷰어 프로그램이나 메모장이 하는 일과 비슷하다. 이미지 뷰어 프로그램이 읽은 이미지에 대해 이미지 종류, 압축방식(jpeg, png 등), 좌표와 색정보에 따라서 화면에 뿌려주거나, 메모장이 텍스트 파일내에 있는 문자, 줄바꿈 기호, 탭(우리 눈에는 글자들이 탭으로 구분된 것으로 보이지만, 실제의 텍스트 파일 내부에는 아스키 코드 09 같은 특별한 기호로 사실 정의되어 있다)을 해석해 우리에게 보여주듯, 브라우저가 HTML 형태의 정보를 받으면 헤더, 바디에 있는 여러 태그 정보들을 분석해서, 화면에 우리가 볼 수 있도록 표시해 주는 것이다(이를 HTML 랜더링 이라고 말한다) 

 

 

 

 

  그럼 HTML의 모든 태그를 다 볼순 없으니 대표적인 몇개만 살펴보자

 

[1- TABLE 태그]

  11교시에서 잠시 다루었지만, 테이블은 아래와 같은 기본 구조를 가진다. 가장 바깥은 <table> 태그로 쌓여 있고, <th> 는 맨위에 있는 제목 필드라고 보면 되고(테이블에서는 옵션 태그라 없어도 무방), <tr> 은 엑셀의 row 같이 테이블의 한 행을 나타내고, <td> 는 하나의 입력 칸인 셀(cell)을 얘기한다. 그래서 테이블의 구조는 <table> 태그 안에 줄을 나타내는 <tr> 태그들이 쭉 있고, 각 <tr> 태그 안에 칸을 나타내는 <td> 태그들이 들어가 있는 단순한 구조이다. 근데 이 단순한 구조로 이것저것 다양한 형태의 테이블을 만들거나 페이지의 구조를 잡다보니 은근 분석하기 어려울 정도로 복잡해 질 때도 많다.

 

 

 

  가장 간단한 구조의 테이블 예제는 아래와 같다. <table> 태그가 맨 밖에 있고, 선(border) 굵기가 1 사이즈를 가진다. <th> 태그안에 제목인 '과자'와 '초콜릿'이  들어있고, 줄을 나타내는<tr> 태그가 두개 있는데, 하나에는 '파이, 카카오45%' 가, 나머지 하나에는 '머랭, 카카오100%'  가 각각 <td> 태그안에 나눠 담겨 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<table border=1>
  <tr> 
     <th>과자</th>    
     <th>초콜릿</th>  
  </tr>  
  <tr>    
     <td>파이</td>
     <td>카카오45%</td>
  </tr>  
  <tr>   
     <td>머랭</td>
     <td>카카오100%</td>  
  </tr>
</table>
cs

 

  c:\python\code 폴더에, 파일형식을 '모든 파일'로 선택하고 table.html (또는 table.htm) 이라고 저장한다. 이후 탐색기에서 해당 파일을 더블클릭해 실행하면 브라우저가 뜨면서 아래와 같이 지정한 테이블이 표시된다.

 

 

  테이블에는 아래와 같은 스타일을 나타내는 속성들이 있다(속성 중 주요한 일부만 표시함). HTML을 복잡하게 생각하지 말고, 우리가 많이 쓰는 워드나, 한글 등에서 작성하는 문서를 브라우저가 이해할 수 있게 태그로 표현한다고 생각하면 된다. 글자를 크게하거나, 오른쪽 정렬을 하거나, 표의 색을 정하거나, 셀의 여백 값을 정하거나 하는 부분들을 아래와 같은 태그 내 속성에 넣어 넣어서 해결한다고 생각해 보면, 속성이 이렇게 많은 이유를 이해할 수 있을 것이다.

 

  그럼 일부 속성을 사용해 보자. 속성 이름들은 <table> 같은 하나의 태그에서만 독점해 쓰이는게 아니고, 비슷하게 속성을 정의할 다른 태그들이 있다면 동일한 이름으로 사용된다. 아래에서는 <th> 태그 내에 배경색(bgcolor : 색은 'yellow' 와 같은 예약된 영어이름이나, '# + 16진수 숫자' 을 이용해 R, G, B 로 표현 가능하다)을 입히고, '파이'와 '머랭'가 들어간 셀의 사이즈를 200 pixel로 늘이고, 특히 '파이'가 들어간 셀은 가운데 정렬을 한다.  

1
2
3
4
5
6
7
8
9
10
11
12
13
<table border=1>
  <tr>
     <th bgcolor=#FF22CC>과자</th>    
     <th bgcolor=yellow>초콜릿</th>
  <tr>    
     <td width=200px align = center>파이</td>
     <td>카카오45%</td>
  </tr>  
  <tr>   
     <td width=200px>머랭</td>
     <td>카카오100%</td>  
  </tr>
</table>
cs

 

  위와 마찬가지로 c:\python\code 폴더에 table2.html 에 저장하여, 브라우저로 실행하면 아래와 같다. 

 

 

  이번엔 맨 마지막에 <tr> 행을 하나 추가하며, 내부의 두개의 셀을 합쳐보자. 해당 역활을 해주는 속성이 'colspan'(column span:컬럼 폭)이다. 이것을 2라고 해주면 위의 테이블을 기반으로 해서 2개의 셀을 세어서, 밑에 하나로 합쳐 표시해 준다(처음 테이블을 만들어 이것저것 해보면 colspan, rowspan{위아래합치기} 개념이 복잡한 테이블에서는 조금 헷깔리긴 했었다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<table border=1>  
  <tr>
     <th>과자</th>    
     <th>초콜릿</th>  
  </tr>  
  <tr>    
     <td>파이</td>
     <td>카카오45%</td>
  </tr>  
  <tr>   
     <td>머랭</td>
     <td>카카오100%</td>  
  </tr>
  <tr>   
     <td colspan=2>비고:살찌는거조심</td>
  </tr>
</table>
cs

 

   c:\python\code 폴더에 table3.html 에 저장하여, 브라우저로 실행하면 아래와 같다. 

 

 

 

[2- FONT 태그]

  다음으로 <font> 태그는 아래와 같다. 해당 태그 사이에 들어가 있는 문장의 색, 크기, 폰트 등을 정의 한다(역시 워드의 글자 스타일을 생각해보면 된다).

1
2
3
4
5
6
<html>
  <body>
    <font size="5" color="blue">첫번째 폰트</font>
    <font face="궁서체" color="green">두번째 폰트</font> 
  </body>
</html>
cs

 

  c:\python\code 폴더에 font.html 에 저장하여, 브라우저로 실행하면 아래와 같다. 

 

 

 

[3- Form 태그]

  세 번째 태그인 폼(<form>) 은 사용자가 입력한 데이터를 서버로 전송하기 위한 요소이다. 우리가 검색 페이지에서 검색어를 넣고 '검색하기 버튼'을 누르거나, 여러 결제 옵션을 선택하고 '결제하기 버튼'을 눌렀을때, 우리가 입력하거나 라디오버튼 등으로 선택한 값들을 서버 쪽으로 묶어 전송하는 역활을 하는 태그가 <form> 이다. 서버 쪽으로 데이터를 날릴때는 HTML 전체 데이터가 아니라 이 <form> 안에 담긴 데이터만 날아간다(물론 이 설명 부분은 요즈음에 와서는 json 이나 xml 을 데이터 형식으로 주로 쓰는 AJAX 와 같은 비동기 방식이나, .net의 viewState 같은 새로운 전송 역활을 하는 형식들이 생겨서 예전같이 절대적이진 않는듯 하다).

 

 

  밑의 그림에 나타난것 처럼 폼은 <form> 이라는 태그로 감싸져 있고, 그 안에 여러가지 사용자의 입력을 받는 태그들이 들어가게 된다(이 부분도 윈도우 GUI 화면 요소들을 떠올리면 쉽게 이해갈 것이다. <form>은 윈도우즈의 다이얼로그 박스와 비견 될듯하다). 폼안에 들어가는 태그는 txt 형태의 필드, 입력값을 와일드 카드로 가려주는 password 형태의 필드, 라디오 버튼, 체크 박스, 셀렉트 박스 등의 다양한 요소 들이 있다. 사용자가 type="submit" 으로 속성이 지정된 버튼을 누르게 되면, 폼의 action 속성에 지정된 URL이 호출되며 폼내 정보들이 전달된다. 

 

  아래의 소스는 위의 그림의 소스를 옮겨놓은 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<form name=“basic" action="http://127.0.0.1/demo_form_action.asp" method="get">
이름: <input type="text" name="myname">
<br><br>
아이디: <input type="text" name="id">
<br>
패스워드: <input type="password" name="pwd">
<br><br>
<input type="radio" name="address" value="seoul">서울
<br>
<input type="radio" name="address" value="busan">부산
<br><br>
<input type="checkbox" name="hobby" value="gym">취미는 헬스
<br>
<input type="checkbox" name="hobby" value="book">취미는 독서
<br><br>
한달 용돈 : 
<select name="pocketmoney">
   <option value="100000">10만원</option>
   <option value="200000">20만원</option>
 </select>
<br><br>
<input type="submit" value="전송"> 
 
</form> 
cs

 

  c:\python\code 폴더에 form.html 로 저장하여 브라우저로 띄워보자. '전송'버튼을 누르면 지금은 없는 페이지를 호출 하기 때문에, 에러가 나긴 할 것이다.

 

  그럼 피들러를 이용해 샘플 페이지에서 전송 버튼을 눌렀을때 폼이 어떻게 날아가는지 실제로 봐보도록 해보자(피들러는 10교시 때 설치 및 기초 사용법을 설명했다). 밑의 피들러 그림을 보면 왼쪽 url 항목에 우리가 지정했던 demo_form_action.asp 파일이 있고(상대 경로로 지정했기 때문에 앞의 도메인과 폴더 부분은 127.0.0.1의 루트 폴더 그대로 이다), 파일이름 뒤에 물음표와 함께 form 안에 담겨있는 태그 요소들이 name 속성을 기준으로 'myname=Hello', 'id=freesugar' 식으로 값이 어사인되어 전송되는 것이 보인다(예전에 웹페이지 파싱 시간에 잠시 얘기했지만 폼 및 폼 내부의 태그들은 name 속성을 기준으로 구별된다).

 

 

  조금 더 보충해서 설명하면, 우리가 주소창에 주소를 입력하여 구글 웹사이트에서 특정 웹 폴더내에 있는, 특정 파일(test.html)을 요청하여 가져오는 것처럼, 페이지내에서 submit 버튼을 눌렀을 때는 폼 태그 내의 action 에 정의되어 있는 URL 경로를 호출하면서 form 안에 지정된 값을 모두 모아서 전송을 한다(해당 부분은 브라우저가 알아서 해준다)

 

 

 

[4-EVENT]

  event 속성의 설명은 여기서 진행하진 않고 Javascript 와 뗄수 없는 관계니 뒤쪽 Javascript 섹션에서 설명 하려고 한다.

 

 

 

[HTML 마무리]

  그럼 이런 다양한 HTML 태그들과 속성들은 어떻게 접근해야 될까? 추천 하는 방법은 '헤드 퍼스트 html' 같은 가벼운 책을 한권 읽어보거나(개인적으로 헤드퍼스트 시리즈가 있으면 워밍업 용으로 먼저 본다. 대신 안의 낱말 맞추기나 퀴즈는 시간도 걸리고 쪽지시험 같아서 잘 안 푸는 편이다. 다만 저자가 다 다르기 때문에 시리즈 별로 품질이 차이가 좀 있다). HTML 책에 돈을 들이기 아까운 분은, 아래의 w3school 사이트의 샘플을 보거나, 구글 검색을 통해 필요한 태그를 조금씩 봐도 된다. 어차피 웹 프로그래밍 공부를 하다보면 태그는 계속 찾아 볼수 밖에 없다.

   https://www.w3schools.com/html/default.asp

 

  하지만 아마도 초보자 분은 w3school 나 구글에서 뭘 봐야할지를 모를 것 같기 때문에, 책이나 웹상의 HTML 관련 블로그나 관련 무료 강의를 보기를 추천한다(대신 이 방식은 a b c 로 진행되는 경우가 많아서 따라가다가 지칠 수도 있다). 그리고 무엇보다 중요한 것은 어느정도 알 것 같은 느낌이 들면, 직접 원하는 UI의 웹 페이지를 만들어 보면서 벽에 부딫치고, 해결해 보는 것이 좋다.

 

  HTML 은 브라우저로 모든 소스를 볼수 있기 때문에, 디자인이 좋은 페이지의 궁금한 요소들을 뜯어 보는 것도 좋다(11교시에서 설명한 브라우저 개발자 도구의 '요소보기'는 웹의 보고싶은 부분을 뜯어보는데 아주 편리한 도구이다). 어느 정도 공부를 해서 잘 안다고 생각해도 막상 웹페이지를 만들어 보거나, 현실 웹의 소스를 보기 힘들도록 꼬아놓은 자바스크립트, CSS, HTML 을 보게되면 한숨이 나올때가 있을 것이다. 쉽게 안되는게 아쉽지만 이런 공부는 'no pain, no gain' 이기 때문에 어쩔수 없다.

 

  HTML 은 4.01 표준과 5.0 이 있는데, 5.0 은 정적인 4.01 환경에서 좀더 동적인 웹을 위한 확장 킷이라고 봐도 될것 같다(개인적으로 게임의 확장팩 같다고 생각한다). 그래서 일단 4.01 위주로 공부한 후 5.0 내용을 보는 것이 좀 더 효율적일 것 하다(초보자분이 HTML 공부한다고 HTML5 책을 덜컥 사버리면 아마 맨붕이 올지도 모른다). HTML 태그가 워낙 잡다한게 많기 때문에(MS워드의 잘 안쓰는 잡다한 기능들과 같다고 보면 된다) 먼저 공부를 추천하는 기초 html 요소들을 아래에 정리해 놨으니 참고 하시길 바란다. 개인적으로 아래 정도만 알고 조금 헤메보면  beautifulsoup 같은 웹 라이브러리를 사용해 일반적인 웹페이지의 HTML을 파싱할 정도는 될 거라고 생각한다 . 물론 다음에 언급할 자바스크립트와 CSS는 HTML과 실과 바늘의 관계라고 볼수 있어 크롤링 등을 위해 페이지에 대한 분석을 잘 하고 싶다면 세 가지 언어를 비슷한 레벨로 수준을 맞춰 놓는게 좋다(어찌봄 원래 하나일 걸 3개로 나눴다고 봐도 된다). 거기다 웹프로그래밍 언어까지 얹어 배우게 되면(아마 자연스럽게 DB도 배우게 될테고) 웹 기술에 대한 전체적인 기초 그림이 완성이 된다고 생각한다.

 

[HTML 추천 태그 및 개념]

  • 기본구조용: <head>, <body>, <br>, <table>, <font>, <a>, <b>, <h1>~<h6>, <hr>, <i>, <p>, <title>, <meta>, <ol>, <ul>, <li>,
  • 프레임 태그: <frame>, <frameset>, <iframe>
  • 이미지 관련: <image>, <map>, <area>
  • 미묘한 구조의 확장: <div>, <span>
  • 외부와의 연결: <object>, 폼: <form>, <input>, <textarea>, <select>, <option>
  • 주석: <!-- -->
  • URL, 절대경로, 상대경로
  • (form 에 관련된) get, post 인자 개념
  • (자바스크립트를 배우는 초입인) event 속성

 

 

 

 

[CSS]

  CSS 는 문법으로 세세히 들어가면 무척 복잡해지는거 같긴 하지만, 간단하게 컨셉만 얘기하면, HTML 에서 각 태그의 디자인 속성들을 독립시켜 읽고 쓰거나, 관리하기 편하게 만든 것이라고 생각한다. 이렇게 무언가를 분리시켜 관리가 편하게 만드는 것은 다음에 나올 MVC 나 객체지향 프로그램, 함수 같은 요소의 공통점인것 같다.  아무래도 복잡히 꼬인 실타래 코드 보다는, 정리되고 분리되어 명확한 코드가 파악도 잘 되고 유지보수도 쉬울 테니까 말이다. 

 

 

  예를 들어 아래와 같은 HTML 코드가 있다면, 단순한 기본 모양의 테이블 이겠지만,

1
2
3
4
5
6
7
8
9
10
<table> 
  <tr>     
     <th>과자</th>    
     <th>초콜릿</th>  
  </tr>  
  <tr>    
     <td>파이</td>
     <td rowspan=2>카카오45%</td>
  </tr>  
</table>
cs

 

  아래와 같은 <style> 태그 안에 담긴 CSS 형식으로 <table>, <td>, <th> 의 디자인 속성을 정의한 파일이 있다면,

1
2
3
4
5
6
7
8
9
10
11
<style>
table, td, th
 {
 border:1px solid green;
 }
 th
 {
 background-color:green;
 color:white;
}
</style>
cs

 

  위의 두 개의 서로 다른 코드를(HTML+CSS) 합쳐서, 아래와 같이 하나의 html 파일로 만들면 서로 독립된 HTML 과 CSS 가 같이 연합해 동작하게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<style>
table, td, th
 {
 border:1px solid green;
 }
 th
 {
 background-color:green;
 color:white;
}
</style>
 
<table> 
  <tr>     
     <th>과자</th>    
     <th>초콜릿</th>  
  </tr>  
  <tr>    
     <td>파이</td>
     <td rowspan=2>카카오45%</td>
  </tr>  
</table>
cs

 

  c:\python\code 폴더에 css.html 로 저장하여 브라우저로 열어보자. <style> 태그안에 CSS 형식으로 정의한 스타일들이, 공간적으로는 분리되어 있는 HTML 코드에 적용되어 아래와 같이 꾸며진다(물론 이 부분은 브라우저가 소스를 파싱하여 적용해주는 것이다).

 

 

  위와 비슷한 표현을 하는 HTML 코드 형식으로만 이루어진 아래의 코드와 비교해 보면, 디자인 속성의 분리라는 점이 얼마나 코드를 깔끔하게 정리해 주는지 볼수 있다. 만약 복잡한 HTML 페이지의 디자인을 수정 시 아래처럼 각각 태그마다 디자인이 정의된 코드를 수정하는 것보다는, 위의 CSS 스타일로 분리된 코드를 수정하는 편이 좀 더 쉽고, 편리할 것 같지 않은가 싶다. CSS 는 HTML 의 디자인 작업을 개념적으로 분리시키고, 중복 코드를 제거해 쉽고 명확하게 만들어 준 측면이 있는 것 같다(HTML5 에는 아래 대부분의 디자인 속성을 안 쓰고 CSS 스타일을 사용하게 하는듯 하다).

1
2
3
4
5
6
7
8
9
10
<table border="1"> 
  <tr>     
     <th border="1" bgcolor="green"><font color=white>과자</font></th>    
     <th border="1" bgcolor="green"><font color=white>초콜릿</font></th> 
  </tr>  
  <tr>    
     <td border="1">파이</td>
     <td rowspan=2 border="1">카카오45%</td>
  </tr>  
</table>
cs

 

 

[CSS 마무리]

  CSS 에는 많은 디자인 요소들이 있고, class 를 지정하여 특정한 디자인을 선택해 적용하거나, css selector 같은 주제도 있다.  자세한 부분은 관련 블로그나 책을 한권 훝어 보는 걸 권장한다.

 

 

 

 

 

[Javascript]

  자바스크립트를 설명하려면, HTML 파트에서 설명을 뒤로 미뤘던, HTML 과 자바스크립트를 연결 해주는 요소인 '이벤트(event)' 에 대해 설명해야 한다. 이벤트는 아래 그림과 같이 윈도우즈 프로그램을 움직이게 하는 이벤트 개념이, 브라우저 내의 DOM 객체를 대상으로 구현된 것으로 봐도 될듯 한다. 윈도우즈 운영체제에서 사용자의 키보드, 마우스의 움직임이 어떤 프로그램 창의 어떤 사용자 컨트롤에서 발생했는 지에 따라 이벤트를 발생시켜 처리를 한다면, 브라우저 내에서도 사용자들의 여러 키보드, 마우스 액션이 HTML 페이지내 DOM 의 어떤 요소에서 일어났는지에 따라서, 해당되는 이벤트를 일으켜 자바스크립트를 이용해 처리하게 만드는 구조이다. 아래에 종종 볼수 있는 HTML 이벤트들을 정리해봤다.

 

 

  해당 이벤트가 동적인 웹을 구성하는데 어떤 역활을 하는지 처음 보는 분들은 감이 안 잡힐 듯도 싶어서, 대표적인 적용 예들을 밑에 표시했다(구글이 onchange 인지, onkeyup 일지는 잘 모르겠다^^). 밑의 예에서 유추해 보면 웹에서 UI가 사용자 동작에 따라서 반응하는 부분은 대부분 이런 이벤트+자바스크립트의 도움으로 이루어진다고 보면 될 것이다.

 

 

[자바스크립트 예제 1]

  그럼 간단한 자바스크립트 예제를 2개만 보자. 아래의 코드를 간단히 설명하면 하단에 input box가 두개 있고, 박스 내를 클릭하면 'onfocus' 이벤트가 발생 하며, 위쪽 input box 의 이벤트는 배경을 노란색으로 바꾸어주는 setSytle1 자바스크립트 함수에, 아래쪽 input box 는 파란색으로 바꿔주는 setStyle2 함수에 연결되어 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html>
 
<head>
  <script>
     function setStyle1(x)
     {
       document.getElementById(x).style.background="yellow";
     }
 
     function setStyle2(x)
     {
       document.getElementById(x).style.background="blue";
     }
  </script>
</head>
 
<body>
 
<p>Color Change</p>
First name: <input type="text" id="fname" onfocus="setStyle1(this.id)"><br>
Last name: <input type="text" id="lname" onfocus="setStyle2(this.id)">
 
</body>
</html>
cs

 

  위의 파일을 c:\python\code 폴더에, colorchange.html 이라고 저장하고, 더블 클릭해 브라우저로 열어본다. 로컬 파일에서 자바스크립트가 돌아가려 하기 때문에, '차단된 콘텐츠 허용' 경고가 브라우저 하단에 뜰텐데, 이 경우는 딱히 위험한건 아니니 클릭해서 허용을 해줘야 자바스크립트 코드가 동작한다.

 

  처음엔 둘다 하얀 입력 박스인데, 각각 클릭하여 포커스를 주고 나면 아래와 같이 색이 바뀌게 된다.

 

 

 

[자바스크립트 예제 2]

  2번째는 조금 더 복잡한 예제를 해보자. 쇼핑몰 들에서 종종 볼수 있는 메뉴에 마우스를 오버하면 해당 하위 메뉴가 뜨는 예제이다. 이번엔 과정을 보여주기 위해 처음부터 전체 코드를 제시 하지 않고 HTML(원래는 css 와 구분되는게 더 낫겠지만), Javascript, event 각각의 코드를 소개하고 이후 합쳐서 동작을 보려고 한다.

 

  먼저 디자인을 나타내는 HTML 코드이다. 애니매이션이 1초에 수십장의 그림을 사람에게 연속으로 보여줘서 실제 움직이는 것처럼 속이는 것처럼, 자바스크립트도 비슷하게 여러 트릭을 통해 사람을 눈을 피해 표현하는 경우들이 많다(개인적으로 별로 우아하게 느껴지는 코드는 아니다). 아래를 보면 맨 위의 id 가 'mainCate' 인 <td> 태그가 상위 메뉴인 '과자' 를 보여주는 셀이고, 그 안을 보면 id 가 'subCateMenu' 인 '파이와 머랭' 정보를 담고 있는 <div> 태그가 하나 들어가 있는데, 속성들을 잘보면 뒤 쪽에 숨김 속성(display:none;)이 있다. 그래서 첨에는 <div> 태그 안에 있는 '파이와 머랭' 은 안보이고, 보는 사람의 눈에는 상위 메뉴인 '과자'만 보이게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<table width="100" border="0" cellpadding="0" cellspacing="0">   
    <tr><td align="left" id="mainCate">
       <div id="subCateMenu" style="width: 260px; position: absolute; margin-left: 120px; border: 
3px solid rgb(100, 200, 100); padding: 10px; z-index: 10000; display: none; background: rgb(255, 255, 255);">
          <table width="100%" border="0" cellspacing="0" cellpadding="0">
              <tr>
                 <td>                          
                       <div style="width:115px; border-bottom:1px solid;"><a href=“test1">파이</a></div>
                       <div style="width:115px; border-bottom:1px solid;"><a href=“test2">머랭</a></div>     
                </td>
              </tr>
          </table>
         </div>
        과자
        </td>
    </tr>
</table>
cs

 

 

  다음으로 동적인 움직임을 구현해 주는 자바스크립트 코드를 보자. showMenu 와 showSubCateMenu가 함수가 있는데, showMenu 가 하위메뉴가 나타날때, 상위메뉴인 '과자'가 들어있는 셀의 색을 바꿔주는 역활을 하고(backgroundColor), showSubCateMenu 가 숨겨놓은

'파이와 머랭'이 들은 <div> 태그를 보여준다(disaplay=""). 나머지 2개의 hide 계열 함수들 view의 반대의 역활을 해서 원래 상태로 돌려주는 역활을 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script language="javascript">
<!--
  function showMenu(td){
    td.style.backgroundColor = "#444444";
    td.style.color="#ffffff";
  }
 
  function hideMenu(td){
    td.style.backgroundColor = "#ffffff";
    td.style.color="#555555";
  }
 
  function showSubCateMenu(i) {
    document.getElementById("subCateMenu").style.zIndex = 10000;
    document.getElementById("subCateMenu").style.display = "";
  }
 
  function hideSubCateMenu(i) {
    document.getElementById("subCateMenu").style.display = "none";
  }
 
//-->
</script>
cs

 

 

  여기까지 오더라도, 마지막에 빠진 고리가 있다. 지금으로서는 HTML 하고 자바스크립트가 서로의 존재를 모른다는 것이다. 이것은 앞에서 얘기했던 'event' 요소가 연결해준다. '과자'가 들어있는 <td> 태그 안에 넣을 이벤트들은 아래와 같다. 이렇게 되면 마우스를 '과자' 셀위에 올리면(onmouseover) show 계열 함수들을 실행해 메뉴를 보여주고 색을 바꾸며, '과자' 셀을 벗어나면(onmouseout) hide 메뉴를 사용해서 원복한다.

1
2
onmouseover="javascript:showSubCateMenu(); showMenu(this);" 
onmouseout="javascript:hideSubCateMenu(); hideMenu(this);"
cs

 

 

  그럼 위의 HTML, Javascript, event 세 가지 코드를 결합한 최종 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<script language="javascript">
<!--
  function showMenu(td){
    td.style.backgroundColor = "#444444";
    td.style.color="#ffffff";
  }
 
  function hideMenu(td){
    td.style.backgroundColor = "#ffffff";
    td.style.color="#555555";
  }
 
  function showSubCateMenu(i) {
    document.getElementById("subCateMenu").style.zIndex = 10000;
    document.getElementById("subCateMenu").style.display = "";
  }
 
  function hideSubCateMenu(i) {
    document.getElementById("subCateMenu").style.display = "none";
  }
 
//-->
</script>
 
 
<table width="100" border="0" cellpadding="0" cellspacing="0">   
    <tr><td align="left" id="mainCate" onmouseover="javascript:showSubCateMenu(); showMenu(this);" 
onmouseout="javascript:hideSubCateMenu(); hideMenu(this);">
       <div id="subCateMenu" style="width: 260px; position: absolute; margin-left: 120px; border: 
3px solid rgb(100, 200, 100); padding: 10px; z-index: 10000; display: none; background: rgb(255, 255, 255);">
          <table width="100%" border="0" cellspacing="0" cellpadding="0">
              <tr>
                 <td>                          
                       <div style="width:115px; border-bottom:1px solid;"><a href=“test1">파이</a></div>
                       <div style="width:115px; border-bottom:1px solid;"><a href=“test2">머랭</a></div>     
                </td>
              </tr>
          </table>
         </div>
        과자
        </td>
    </tr>
</table>
 
cs

 

  c:\python\code 에 menu.html 로 저장한 후, 브라우저로 열어본다. 아래와 같이 '과자' 가 들어간 셀 부분에 마우스를 올리거나 내렸을때 서브 메뉴인 '파이와 머랭'이 모양이 나왔다 사라지는 메뉴 동작을 볼수 있을 것이다.

 

  그리고 위에서 자바스크립트 코드에 나온 document.getElementById("subCateMenu") 같은 부분들은 자바스크립트가 HTML 코드에 접근할때 쓰는 DOM(Document Object Model)의 개념이 들어가 있다. 우리가 앞에서 beautifulsoup, 이나 selenium 을 사용할때 의식은 안 했지만 자연스럽게 해당 개념을 이용했다고 볼수 있다.

 

 

 

[Javascript 마무리]

  그럼 마지막으로 자바스크립트를 공부하려면 어떻게 할까? 개인적으로 자바스크립트는 깊이 들어가게 되면 파이썬하고 비슷한 깊이의 복잡도를 가진다고 본다(서버 쪽으로 눈을 돌리면 node.js 같은 서버 언어로도 사용되고 있고 말이다). 그래서 되도록 처음엔 너무 깊은 쪽으로는 가지 않도록, 쉬운 책이나 블로그를 보면서 개념을 잡은 후, 이후 웹 프로그래밍 공부를 하면서 궁금한 부분을 만났을때마다 구글 등을 찾아서 개념을 이해하는 것을 추천한다. 자바스크립트 라이브러인 jQuery 를 많이 사용하긴 하나, 실제 웹페이지들은 jQuery 와 일반 자바스크립트 두 가지 코드가 공존하고 있기 때문에 jQuery 는 응용편이라고 생각하고 접근하는 게 나을듯 싶다. 

 

  일단 기초가 잡히면 무엇을 모르는지와 공부해야할 방향을 스스로 알수 있게 되고, 집밥 백선생에서 요리하기 전에 재료를 섞음 어떤 맛이 될지 상상해 보라는 말 같이, 무언가를 덥석 구체적으로 습득하는 것도 좋지만, 그 전에 이것을 습득하게 되면 어떻게 될까를 잠시 생각해 보는 것도 나쁘진 않은듯 하다. 공부할수 있는 시간은 한정되어 있기 때문에, 가야될 방향을 정확하게 잡는것도 중요하다. 사실 이 강의의 의미도 구체적인 구현 지식의 전달 보다는 각 주제에 대한 접근방식과 개념을 전달하는 것이라고 생각하고 있다. 

 

 

 

 

 

[Web Server]

  이제 슬슬 후반부로 들어간다. 웹 프로그래밍을 공부하려면, 기본적으로 웹 서버의 존재를 이해해야 한다. 웹서버는 단순하게 얘기하면 특정 포트로 요청이 오기를 기다리고 있는 서비스 프로그램이다. 사용자(또는 다른 프로그램일수도 있고)로부터 요청이 들어오면, 적당한 프로그램적 처리를 한 후, 다시 요청한 쪽에 HTTP 형태로 결과를 돌려준다. 웹 서버는 사실 2개의 모듈로 나뉘어져 있다고 생각하는 게 좋다. HTTP 통신을 받거나 응답해 주는 순수 웹 모듈과, 받은 데이터를 특정한 언어에 기반해서 처리해 주는 프로그래밍 모듈로 구성되어 있다고 볼수 있다.

 

  예를 들어 순수 아파치 웹서버는 PHP 와 HTML 을 처리할 수 있는 반면, 아파치-톰캣은 아파치에 톰캣 프로그래밍 모듈이 얹어져 있어 JSP 파일의 처리가 가능하다(심지어 성능이 얼마나 나올지는 모르겠지만, IIS 에도 PHP 모듈을 설치해 PHP 웹서버로 사용할 수도 있다). 파이썬도 flask 같은 샘플을 보면 샘플 프로그램을 띄울때 웹서버가 같이 실행되어 프레임워크 뒤로 숨겨져 있어서 잘 안보이이긴 하지만, 웹서버 모듈이 있어야지만 사용자의 요청에 응답할 수 있다. flask 나 Django 도 아파치와 연동해서, 클라이언들과 주고받는 처리는 검증된 아파치 서버가 해주고, flask 나 django 는 뒤에서 웹어플리케이션 모듈로서만 동작하게도 할수 있는거 같다(이렇게 보면 모든 웹서버의 꿈이 어떤 언어라도 연결해 중계해 주고 싶은것인가 싶기도 하다).  

 

 

 

[IIS 설치]

  그럼 다음 섹션에서 ASP 를 돌려보기 위해서 윈도우 10에서 기본으로 지원하는 IIS 를 설치해서 샘플페이지를 하나 호출해 보자.

 

  먼저 IIS를 설치해 보자. '윈도우키+x' 를 누른후 옆 쪽에 나타나는 메뉴에서, '프로그램 및 기능' 메뉴를 선택한다(설치 부분을 설명하기 위해 잠시 전 uninstall 을 했지만, 이전 시간에서 소개한 예전 윈도우 스타일로 시작메뉴를 보여주는 'classic shell' 을 깔았을 경우는 '제어판>프로그램>프로그램및 기능' 으로 가면 된다).

 

  왼쪽에서 '윈도우즈 기능 켜기/끄기' 를 선택한다.  

 

  이후 인터넷 정보 서비스에서 1) 'World Wide Web 서비스' 를 체크하고, 2) 하위 메뉴에서 '응용 프로그램 개발기능>ASP' 를 선택한다. 3) 그리고 웹 관리 도구' 도 체크한다(나머진 그냥 디폴트로 두면 된다). 체크가 다 되었음 확인 버튼을 누른다. 잠시 기다림 IIS 가 설치가 된다.

 

 

 

['Hello ASP' 샘플 페이지 실행]

  이제 메모장으로 샘플 ASP 파일을 하나 만들건데, 기본 사용자 권한으로는 IIS 의 웹루트 폴더인 c:\inetpub\wwwroot\ 폴더에 파일을 쓰지 못하게 되어 있기 때문에, 메모장을 관리자 권한으로 실행해야 된다. '윈도우+x' 키를 눌러서, 왼쪽 메뉴에서 '검색'을 선택한다. 검색 창이 나오면 '메모장' 이라고 찾는다. '메모장' 아이콘이 나오면 마우스 오른쪽 버튼을 눌러서 컨텍스트 메뉴를 띄워 '관리자 권한으로 실행' 을 선택한다. (매번 이게 귀찮다면 wwwroot 폴더 '속성'의 '보안탭'에서 현재 로그인한 사용자에게 '모든 권한'을 주면 파일 저장이 가능해진다) 

 

  이후 메모장에 아래와 같이 입력한다.

1
2
3
<%
   Response.Write "Hello ASP"
%>
cs

 

  해당 내용을 c:\inetpub\wwwroot 폴더에 파일형식을 '모든 파일'로 하여 'test.asp' 로 저장한다. 이후 브라우저를 띄워 주소창에 http://localhost/test.asp 를 입력한다(localhost 나 127.0.0.1 은 현재 컴퓨터의 주소를 나타낸다). 그럼 아래와 같이 ASP 로 만든 간단한 문서가 IIS의 ASP 모듈에게 해석되어 브라우저에게 전달되어 화면에 보이게 된다.

 

 

 

 

[ASP 로 DB 조회하여 HTML 테이블로 출력하는 샘플 만들기]

  그러면 ASP 웹페이지를 하나 만들어 보자. 데이터베이스의 테이블을 조회 해서 결과를 화면에 HTML 테이블 형태로 뿌려주는 페이지를 만드려고 한다. 여러모로 시간을 아끼기 위해서^^; 4교시때 설치 및 세팅해 놓았던 SQL Server 와 그때 만들어 놓은 supermarket 테이블을 그대로 이용한다(4교시를 안보신 분들은 4교시에 소개된 MSSQL Server 설치 및 테이블 생성을 하시던지, 귀찮으시면 의사코드라 생각하시고 눈으로만 보고 동작을 이해하셔도 된다).

 

  4교시에 만든 테이블은 아래와 같은 테이블 이였다. 4 or 7교시 때 해당 테이블의 내용을 가져오는 파이썬 코드를 만들어 봤었는데, 그때 로직을 기억을 더듬어 보면 1) 먼저 DB 정보를 입력해 연결하고, 2) 각 행을 루프를 돌며 하나씩 읽어오면서, 3) 화면에 프린트를 하거나 엑셀로 저장했다. ASP 코드도 사실 이와 엄청 비슷한 흐름을 가진다(어차피 비슷한 스크립트 언어이기도 하고, 언어들은 서로 영향을 받아 유사한 경향이 있기 때문이다. 코드를 비교해 보시면 파이썬 코드쪽이 좀더 현대적이여서 흐름이 세련된 느낌을 받을 것이다). 작성할 ASP 코드에서는 1) DB 정보를 입력해 연결하고 2) 각 행을 하나씩 읽어 오면서, 3) HTML 테이블을 출력하면서 <td> 태그안에 해당되는 컬럼을 하나씩 넣어준다. 

 

 

 

[ASP 샘플 코드 만들어 보기]

  그럼 ASP 를 공부하는 시간은 아니기 때문에 만들어진 예제를 바로 보자. 원래는 웹프로그래밍 예제라고 하면, 사용자가 입력한 내용을 폼으로 전송해, 해당 값을 서버에서 받아 SQL 조건에 조합해 넣어 특정 itemno 의 상품을 조회해 오거나 하는 등의 예제가 좀더 현실적이겠지만, 단순함과, 이전에 만든 파이썬 코드와 비교할 수 있도록 하기 위해 전달 받는 인자가 없이 테이블의 모든 정보를 조회해 오는 방식의 예제를 만들었다.

 

  ASP, PHP, JSP 같은 스크립트 코드를 처음 보시면 낯설수도 있겠지만, ASP 에서는 <% %> 안에 든 내용이 VBscript 문법의 순수 ASP 코드이고, 코드를 보면 거의 파이썬으로 구현했던 예제와 비슷하게 진행이 된다. ASP 를 배울 것은 아니니 코드의 상세 문법을 보지 말고 주석 위주로 흐름만 보길 바란다(참고로 ASP 는 대소문자를 안가린다).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<%@ Language=VBScript %>
 
<%
   ' 연결 문자열 정의
   strMyTest = "Provider=SQLOLEDB; Data Source=localhost; Initial Catalog=mytest; User Id=pyuser; Password=test1234"
   Set objConn = Server.CreateObject("ADODB.Connection")
   objConn.Open strMyTest 
 
   ' 실행할 SQL 정의
   strSQL = "select itemno, category, foodname, company, price from supermarket s(nolock)"
 
    ' 쿼리 실행 하여 결과 얻어옴
   Set rtnRow = objConn.Execute(strSQL)
%>
 
 
<html>
   <head>
      <title>supermarket</title>
   </head>
 
   <body>       
      <p>supermarket 상품</p>
      <table border=1>
         <tr>
            <td>번호</td>
            <td>카테고리</td>
            <td>종류</td>
            <td>상품이름</td>
            <td>가격</td>       
         </tr>    
 
<%
   'DB 에서 조회한 행이 끝이 아니라면 루프를 돌리면서 각 컬럼을 <td> 태그안에 끼워 넣는다.
   Do while Not rtnRow.EOF 
%>
 
         <tr>
            <td><%=rtnRow("itemno")%></td>
            <td><%=rtnRow("category")%></td>
            <td><%=rtnRow("foodname")%></td>
            <td><%=rtnRow("company")%></td>
            <td><%=rtnRow("price")%></td>
         </tr>
                        
<%
   'rsList의 내용을 다음 결과 행으로 이동하며 Do 문을 반복한다.
   rtnRow.MoveNext
   Loop
%>
 
      </table>
</body>          
 
<%
   '열었던 연결 닫기
   objConn.Close
   Set objConn=Nothing
%>
cs

 

  그럼 위의 내용을 샘플파일과 비슷하게 관리자 계정으로 실행한 메모장에 붙여넣기 하여 c:\inetpub\wwwroot 폴더에 'supermarket.asp' 이름으로 저장을 하자. 이후 브라우저 주소창에서 http://localhost/supermaket.asp 을 호출한다. 그럼 아래와 같이 DB에서 supermarket 데이터를 가져와서 HTML 테이블로 정리하여 보여주는 페이지가 나오게 된다. ASP 가 처음이신 분은 처음 파이썬으로 DB 를 조회했을 때처럼 조금은 신기한 느낌을 받지 않을까 싶다.

 

 

  참고 1:  여기서 잠시 위의 코드의 구조를 보면, 우리가 배운 HTML 과 ASP 의 프로그램 코드가 하나의 파일에 섞여 있어서, 보기 조금 힘든 것 같지 않는가 싶다(뭐 익숙해짐 편한면도 있긴하다). 나중에 Django, JAVA 나 .NET 같은 언어에서 사용하는 MVC 개념을 통해, 마치 CSS를 통해 디자인 코드를 분리해 낸 것처럼, 이런 레거시 웹 프로그램에 섞여있는 프로그램 코드와 HTML 코드를 개별 요소로 분리해 다루려는 시도를 하게 된다.

 

  참고 2: 현재 기본 설정으로는 에러가 발생시 어떤 에러가 났는지 상세하게 보여주지 않는다. SQL 에러같은 상세한 에러를 보기를 원하는 경우는 아래의 웹사이트를 참고하여 세팅한다.

http://ooz.co.kr/172

 

  참고 3: 그리고 하나 더 노파심에 얘기할 것은, 위의 예제 코드는 소스를 간단히 만들기 위해서, SQL Injection(DB에 공격자가 임의의 쿼리를 삽입할 수 있는 웹취약점) 에 노출되어 있다. 저 방식으로 페이지를 만들어서는 안된다^^; 궁금하신 분은 구글에 'sql injection defense asp' 라고 찾아보심 된다. 요점은 stored procedure 같이 preparedstatement 타입으로 쿼리를 호출하고, 입력 데이터들을 validation 하고, 어플리케이션 DB계정의 권한을 최소화 하고 등등 이다. 현대 웹이 좋은 이유 중 하나는 이러한 패턴을 프레임워크 자체에서 막아주는 경우가 많다.

 

 

 

 

[Ajax]

  그럼 오늘의 마지막 주제인 Ajax에 대해 얘기해 보자. 개인적으로 모든 현대의 세련되고 편리한 웹페이지들은 이 Ajax 의 활약으로 이루어진게 아닌가 싶다. 구글의 검색어 추천이나, 여러 사이트 들의 사용자의 액션에 반응하는 부드러운 UI 의 이면에는, 웹 브라우저의 뒤에서 열심히 데이터를 요청해 나르고 있는 Ajax 가 존재한다. Ajax(Asynchronous JavaScript and XML) 의 비동기적이라는 의미는, 아마도 기존 웹 페이지 간의 명시적인 호출을 동기적이라고 가정했을때, 페이지가 정지해 있는 상태에서 뒷단에서 Ajax 라이브러리를 이용해 데이터를 교환하는 행위를 비동기적이라고 바라보는 관점인 것 같다. Ajax 는 자바스크립로 구현된 라이브러리로 단순하게 봐도 괜찮을 듯 싶다.

 

 

  Ajax 의 간단한 예는 아래 그림의 구글 검색어 추천 기능이다. 사용자가 'python' 이라고 입력하는 동안 계속 추천하는 검색어를 바꿔가면서 보여준다.

 

  해당 추천 검색어가 나오는 과정을 fiddler 로 살펴 보게되면, 아래와 같이 www.google.co.kr 도메인에서 /complete/search? 페이지를 호출하는 6개의 요청('p' 'y' 't' 'h' 'o' 'n' 각각 입력에 따라 총 6개의 요청이 날아간다)을 볼수 있다. 그 중 맨 마지막 요청 항목을 클릭해 보면, 폼안의 'q' 인자 안에 우리가 입력했던 'python' 이라는 글자가 넘어가고, 밑 쪽 보면 결과값으로 'python' 에 해당하는 추천 검색어들이 'json 데이터 형식'으로 담겨 응답으로 오게 된다. 브라우저는 해당 json 내의 값을 적당히 풀러서, HTML 페이지의 DOM 개체에 넣어서, 위의 그림과 같은 추천 검색어 들을 보여주게 된다.

 

 

 

[Ajax 샘플 코드 만들어 보기]

  그럼 실제로 간단한 Ajax 샘플을 하나 만들어보자. 우선 전송되는 데이터 타입은 json 이 아닌 평문으로 간단하게 구현 하려고 한다(ASP 가 좀 구식언어이고, .NET 이 나오면서 업데이트가 없이 버려져서, json 같은 최신 데이터 구조를 파싱하기가 까다로운 점도 이유이다). 아래 코드에 최대한 주석을 달아 놓았다(// 는 자바스크립트 주석이고, <!-- --> 는 HTML 주석이다).

 

  위쪽의 자바스크립트에는 Ajax 리퀘스트가 정의되어 있다(처음 보는 분들은 조금 사용하는 구조가 낯설어 보일테지만, 처음 Ajax 를 만든 사람이 저렇게 사용하라고 정해 놓은거라서, 파이썬에서 SQL 연결 할때 사용하는 모듈의 문법대로 구현 해야하는 것과 같다고 보면 된다. jQuery 등의 라이브러리를 사용하면 또 그 쪽에서 시키는 스타일대로 하면 된다). 하단 HTML 에 정의된 사용자가 입력한 <input> 값을 읽어와서, ajax_sub.asp 페이지를 호출(폼의 action과 비슷하다고 보면 된다)하면서 'no' 인자에 사용자 입력값을 넣어서 보내고, 결과가 반환되어 오면 그 값을 아래 HTML 에 정의된 <span> 태그에 innerHTML 속성을 이용해서 넣는다

 

  하단 HTML 에는 버튼 속성을 가진 <input> 태그가 있는데, 자바스크립트의 getMenu 함수가 onclick 이벤트를 통해 연결되어 있다. 사용자가 번호를 입력하고 버튼을 클릭하면, getMenu 함수가 실행되면서 Ajax 요청을 실행하는 도미노 형태의 구조이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<%@ Language=VBScript %>
 
<script>
function getMenu() {
   var xhttp;
   // 사용자가 입력한 값을 id 를 통해 가져온다.
   var menuNo = document.getElementById("menuNo").value;
   
   // 새로운 ajax 요청을 만든다
   xhttp = new XMLHttpRequest();
   // 요청에 대해 응답이 정상으로 올때까지 기다려서 
   xhttp.onreadystatechange = function() {
      if (xhttp.readyState == 4 && xhttp.status == 200) {
         // span 태그내에 응답으로 온 텍스트 값을 살짝 끼워 넣는다.
         document.getElementById("menuName").innerHTML = xhttp.responseText;
      }
   }
   // 실제 요청하는 페이지는 ajax_sub.asp 페이지 이고, get 인자로 no 에 사용자가 입력한 값을 넣는다.
   xhttp.open("GET""ajax_sub.asp?no="+menuNotrue);
   xhttp.send();
}
</script>
 
<html>
   <head>
      <title>ajax 샘플</title>
   </head>
<body>
   <table>
      <tr>    
         <td> 메뉴 번호: </td>
         <!-- 사용자가 입력하는 값 -->
         <td width=120> <INPUT id="menuNo" size="10" type="text" value=""> </td>    
           <td width=200> 
               <!-- 버튼을 누르면 getMenu 함수를 실행 한다 -->
               <input type="button" value="해당되는 메뉴 찾기" onclick="getMenu()">
               <!-- 나중에 응답 값을 끼워 넣을 span 태그. 첨에는 아무 내용도 없다 -->
               : <span id="menuName"></span>
           </td>
         </tr>
   </table>         
</body>
</html>
cs

 

  앞의 ASP 예제들과 마찬가지로 해당 코드를 메모장에 붙여놓고, c:\inetpub\wwwroot에 ajax_main.asp 라고 일단저장한다.

 

 

  그 다음은 위의 페이지에서 호출하는 대상인 ajax_sub.asp 페이지를 만들어야 한다. 보통 이 호출 당하는 쪽은 데이터만 받고 보내면 되기 때문에 API 같은 형태로 많이 구현 되어있다. 구현한 코드는 아주 간단하다. 넘어온 no 값을 받아서(사용자가 입력한 값이다), 1 이면 'pizza', 2 이면 'pasta', 그 이외의 숫자면 'drink' 를 반환(response) 해준다(참고로 값이 없을 때의 에러처리는 안되서, 값을 안 넣음 아무 결과도 넘어오지 않는다).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ Language=VBScript %>
 
<%
    ' request 값 받기
   menuNo = request("no")
    
   ' 넘어온 메뉴 번호에 해당하는 메뉴이름을 반환해 준다..
   Select Case menuNo
      Case 1
         Response.Write("pizza")
      Case 2
         Response.Write("pasta")
      Case Else
         Response.Write("drink")
   End Select
%>
cs

 

  해당 코드도 메모장에 붙여놓고, c:\inetpub\wwwroot에 ajax_sub.asp 라고 저장한다. 

 

 

  이후 브라우저를 열어서 http://localhost/ajax_main.asp 라고 주소창에 입력한다. 아래의 화면이 나오면 메뉴번호에 1을 넣고, '해당되는 메뉴찾기' 버튼을 클릭하면, 페이지 뒤에서 Ajax 기능을 이용해 ajax_sub.asp 에 인자를 넘겨 데이터를 조회해서, 'pizza' 를 받아 옆에 표시하게 된다.

 

 

  Ajax 호출하는 과정을 피들러로 살펴보게 되면 앞에 봤던 구글 검색어 추천과 비슷하게, 숨겨진 호출(request)이 보인다. 이제 Ajax 를 통한 비동기적인 호출이 어떤 원리로 일어나고, 어떻게 보이는 건지 대충 감을 잡으시리라 생각한다.

 

 

 

 

[마무리 하면서]

  그럼 이렇게 되어 가벼운 깊이지만 꽤 길게 진행됬던 Legacy Web 편을 마치려 한다(개인적으로 소주제별 균형을 맞추기 가장 힘들었던 시간 중 하나일듯 싶다). 뒤돌아 보면 자잘하게 많았던 HTML(form, event), CSS, Javascript, Web Server, Web Language, Ajax 같은 기초 요소들이 우리가 보고 있는 웹을 지탱하고 있다는걸 볼수 있었다. 이런 'Legacy Web' 에 대한 기초가 잘 잡혀 있다면 파이썬이든, 다른 언어로든 웹프로그래밍을 공부할때 배워야 할 주제들이 한결 가벼워지게 된다고 생각한다. 앞에서 얘기했던 프로그래밍을 배울 때의 외적요소들을 미리 알고 있을 때처럼 말이다.

 

  혹시 위의 주제들을 잘 모르는 상태에서 파이썬으로 만드는 웹을 공부하고 싶다면, flask 나 Django 프레임워크를 공부하는 중간 중간에 꼭 각각의 주제에 대한 쉬운 책 한권 정도는 보기를 추천한다. 개인적으로 프레임워크는 웹의 구성요소들을 잘 배치할 수 있도록 돕는 껍데기 역활에 불과하지 않는가도 싶다. 다음 시간에는 이런 Legacy Web의 요소들이 파이썬의 Flask 라는 프레임워크에 어떻게 녹여져 소개되고 있는지, 가벼운 맘으로 체크해 보도록 해보자.

 

 

 

 

2017.7.15 by 자유로운설탕
cs

 

 

posted by 자유로운설탕
2017. 6. 18. 15:49 프로그래밍

  이번 시간에는 머신러닝에 대한 이런저런 생각들을 얘기하고, 이전 시간에 배웠던 numpy, scipy, 그리고 요즘 주목 받고 있는 텐서플로우(tensorflow) 라이브러리를 이용해서, 머신러닝 계의 구구단 이라고 할수 있는 최소제곱법(Least Square Fit)으로 데이터에 맞는 직선을 추정하는 샘플 3가지를 실행해 보려고 한다.

 

 시작하기전에 먼저 양해를 얻고 싶은 점은 스스로 생각했을 때 머신러닝에 대해서는 아주 조금 밖에는 알고 있지 못하다는 점이다. 비유하자면 수영을 해서 강을 건너야 하는 상황인데, 아직 강가에서 손가락만 살짝 담구고 '물이 얼마나 찬가' 하고 체크하는 레벨이라고 볼수 있다. 그래서 여기서 얘기한 얘기들이 틀릴 수도 있기 때문에, 내용을 최대한 비판적으로 받아 들이라고 권고를 드린다.

 

 

[목차]

0. 왜 파이썬 공부에 구글을 이용하는게 좋은가?

1. 언어를 바라보는 방법. 파이썬을 어떻게 바라봐야 할까?

2. 파이썬 설치와 환경, 버전 선택 하기의 이유.

3. 만들고자 하는 기능을 모르는 조각으로 나눠 조사해 보기

4. 데이터 베이스에서 내용 가져와 출력하기

5. 암호화 모듈을 이용해 암복호화 해보기

6. 퍼즐 조각들을 합쳐보기

7. 엑셀 파일 사용해 보기 -> 부록 : fuction 을 이용해서, 코드 정리해 보기

8. 정규표현식을 왜 사용해야 할까? 언어속의 미니 언어 정규표현식 살펴보기

9. 입력과 결과를 GUI 화면과 연결해 보기

10. Whois API 이용해 보기

11. 웹페이지 호출해 내용 파싱 하기(BeautifulSoup 그리고 한계)

12. 자동화 - 웹 자동화(with Selenium)

13. 자동화 - 윈도우즈 GUI 자동화(with pywinauto)

14. 자동화 - 작업 자동화

15. 수학 라이브러리 살펴보기

16. 그래픽 라이브러리 살펴보기

17. 머신러닝에서의 파이썬의 역활

18. 웹 프로그래밍 - Legacy Web

19. 웹 프로그래밍 - Flask 살펴보기(feat. d3.js)

20. 웹 프로그래밍 - Django 살펴보기

21. 정리 - 이런저런 이야기

 

 

 

 

[들어가면서]

  여러 웹사이트와 블로그들을 돌아다녀본 결과, 보통 두 가지 입장이 눈에 많이 뜨이는 것 같다.  한 측면은 수학이나 통계학에 대한 경험을 보유하고 있는 상태에서, R이나 매트랩 등의 연구 목적의 툴을 쓰다가, 좀더 일반적인 머신러닝 프레임윅에 관심을 가져 길을 가다보니, 파이썬 같은 범용 프로그래밍 언어를 해야될 필요성을 느끼게 되어 본격적으로 프로그래밍의 세계에 발을 들여 놓게 되는 경우 이다.

 

  다른 한 측면은 프로그래밍, DB, 보안, 시스템 등의 기술적인 업무 등을 하는 입장에서 머신러닝 책이나 강의를 찾아 보다보니, 원리를 설명하는데 사용하는 선형대수, 통계 그리고 RNN 같은 낯선 이론들을 만나게 되고, 책이나 강의를 이것저것 봐도 머신러닝 라이브러리를 작동 시키는 부분은 대충 따라 할 수 있을 것 같은데, 해당 도메인에 대한 부족한 이해 때문에 전체적인 그림이 그려지지 않고, 어떤 원리로 그런 일들이 가능한지, 어떤 데이터를 가져다 어떻게 가공해야 하는지 대해서는 여전히 모호한 상태임을 느끼게 되어, 수학이나 통계 분야에 대한 공부를 시작하는 경우일 것이다(저같은 경우는 후자 그룹에 속하는 경우인거 같다).

 

  그런데 사실 그렇게 가지지 못한 지식에 대한 대한 니즈나, 동경에서 공부를 시작한다고 해도, 만족할 만한 끝은 좀처럼 잘 보이지 않게 된다. 왜냐하면 그 동안에 관심이 없었던 상대의 영역의 본질에 접근하기는 쉽지 않기 때문이다. 다른 분야의 예를 들자면 캐릭터를 잘 만들거나 그림을 잘 그리기 위해서 3D 모델링 프로그램, 일러스트레이터, 페인터 등의 그래픽 관련 프로그램을 마스터 하려는 것과 비슷한 접근이라고 느껴진다. 아무리 해당 툴의 메뉴얼을 달달 외고, 기능을 잘 이해해서 쓴다고 해도, 그러한 행위의 본질이라고 할수 있는 미학적, 공간적인 감각을 기르는 경험들이 부재한다면, 그러한 노력이 올바른 결실을 맺긴 힘들다고 본다.

  

  마찬가지로 현재 올렸던 글들을 계속 읽어오신 분들은 공감하셨겠지만, 프로그래밍 이란 것은 단지 가시적으로 보이는 코드란 측면에 국한된 지식은 아니다. 외국어를 배울때 그 나라의 문화를 같이 이해해야 자연스럽고 맥락에 맞는 표현을 할수 있게 되는 것처럼, 머신러닝을 받치고 있는 프로그래밍 지식은 파이썬이란 언어 자체에서 출발하여 해당 언어가 동작하는 OS, 네트워크 등의 여러 주변환경과, 데이터를 담는 빅데이터 시스템 등 기술 생태계 전체가 포괄적으로 엮여 있다고 생각한다. 그래서 스펙트럼이 넓을 뿐만 아니라, 인기가 좋아 급속히 성장하며 확장되는 기술 영역들이 의례 그렀듯이 자료들이 초보자들에게 그다지 친절하게 정리되어 있진 않고 계속 변하기 때문에 접근 난이도가 높은 편인 것 같다. 물론 서로 다른 문화가 그렇듯이, 서로 다른 분야 사이에도 자신의 분야의 경험을 기반으로 유추할 수 있는 비슷한 공통 개념들도 많긴 하지만 말이다.

 

  잘은 모르지만 반대로 프로그래밍 영역과 비슷하게 짐작해 보면, 수학이나 물리, 통계 등의 분야도 단순히 보이는 이론과 수학적 지식들이 전부는 아닐 것 같다. 그 학문을 오래 접했던 사람들만이 가질 수 있는 특유의 사고 방식과 문제에 대한 접근법, 데이터와 숫자를 보는 시야나 감을 얻는다는 것은 단순히 통계나 선형대수 책을 공부하는 것과는 좀 많이 다른 일이 아닌가 하는 생각이 든다. 

 

  그래서 어떤 측면에서는 한 사람이 모든 부분을 다 잘 알순 없기 때문에, 효율성을 위해서는 데이터를 다루는 직군, 모델과 알고리즘을 다루는 직군, 프로그램을 관리하는 직군을 따로 나누어 조직해야 한다고 하는 주장도 있지만, 뭐 그런 경우라도 서로서로 상대방이 하는 일을 이해하면서 일을 하면 효율적이기도 하고, 이해 타산을 버리고 순수하게 공부하는 현 시점에서는 무시하기로 하자. 이 시간엔 조금이라도 머신러닝이라는 분야가 정밀한 숫자, 연관된 수학, 복잡한 통계이론을 차지하고도 합리적인 활동으로 보일 수 있는지 생각해보자.

 

 

 

 

[머신러닝이 하는 일에 대해 상상해보기]

  우선 어떻게 기계가 학습을 할수 있다는 것을 직관적으로 이해할 수 있을까? 기계가 학습을 한다는 것은 기계가 사람처럼 알고리즘을 만드는 작업, 즉 프로그래밍을 할수 있다는 얘기다. 예전부터 자동으로 프로그래밍을 짜는 일은 인간의 창조적인 능력의 한 부분이라고 믿어왔고, 여러가지 쉽고 자동화된 프로그래밍 툴을 만들려는 시도가 많이 실패 해 온것으로 알고 있는데, 모두 인간의 자만이였을뿐일까? 개인적인 생각으로는 기계가 학습하여 프로그래밍을 한다고 하는 부분은 사람이 프로그래밍을 하는 부분과는 다르면서도 동일한 모순적인 측면이 있다고 본다.

 

 

  예를 들어 하나의 프로그래밍 로직에 대해 생각해 보기 위해, '1교시 언어를 바라보는 방법'에서 보았던 아래 그림을 다시 한번 보자.

 

  당연한 얘기 같지만 모든 목적을 가진 프로그램은 입력과, 출력을 가지고 움직인다(실행 시점에 인자가 전달되지 않는 프로그램도 실행 시 시스템과 입력과 출력을 하고 있다고 볼수 있을 것이다). 그럼 프로그래머가 작성한 프로그램이 내부적으로 입력에 항상 3을 곱한 후 1을 더 해서 결과를 보여주는 프로그램이라고 해보자. 초등학교 수학식으로 나타내면 'y=3x+1' 일 것이다. 파이썬 식으로 나타내면 아래와 같다.

1
2
3
4
.... 입력으로 x 가 들어옴.
 
= 3x + 1
print (y)
cs

 

  근데 우리가 해당 로직을 이해할 수 없는 상태에서(곱하기가 엄청 고차원 적인 수학이라고 가정해 보자), 사람들이 해당 프로그램을 사용해서 쌓인 충분히 많은 입력-출력 쌍 데이터가 있다고 해보자. 예를 들면 (1, 4), (4, 13), ... (10000, 30001) 같은 형태일 것이다. 해당 데이터를 관찰해 보면 결과 데이터의 경우는 입력 데이터에 사람이 만든 어떤 프로그램 로직(여기서는 x*3 +1 )이 들어가 만들어지는 것이라고 볼수 있을 것이다.

 

  그럼 반대로 만약 어떤 임의의 시스템이 x 입력을 받아 y를 출력을 나타내는 것을, 위의 수집한 데이터에 대해서 최대한 높은 '옳음'으로 처리할 수 있다면, 해당 시스템의 속이 어떻게 생겼는지 상관없이, 해당 시스템은 사람이 작성한 위의 프로그램과 근사적으로(어쩌면 극한적으로) 동일한 자동화 프로그램이라고 할 수 있을 것이다(모든 사칙연산 데이터를 그대로 흉내내는 머신러닝 모델을 상상해 보자. 사람이 만든 계산기와 비슷하다 볼수 있지 않겠는가?).  

 

  즉 데이터 측면만을 보면 충분한 규모의 객관적으로 수집된 입력과 출력 데이터가 있고, 그 안에 현상을 왜곡하는 가짜 데이터가 무시할 만큼만 존재하고(또는 모델내에 그러한 데이터를 걸러내 무시하는 안전장치가 있어도 되고), 그 데이터들이 뭔가 사람이나 현상의 의미있는 활동을 나타낼 가능성이 있다면, 해당 데이터에는 사람이 논리적으로 파악하긴 힘들 수도 있지만, 그 데이터 쌍들을 만들어낸 로직을 포함하고 있다고 볼수 있지 않을까 싶다. 

 

 

  충분한 규모의 객관적인 입력과 출력 데이터 쌍은 비즈니스, 데이터 분석과, 빅데이터의 도움으로 이루어 진다고 보면 될것 같고, 가짜 데이터의 제거 부분은 비즈니스, 데이터 분석, 노이즈에 강한 모델 등을 포함해 모든 동원할수 있는 방법이 다 포함 될수 있을 것 같고, 입출력 데이터들 자체에 숨어있는 로직이나 패턴을 적절한 필터를 써서 참기름 짜듯 뽑아내는 것이 여러 머신러닝 알고리즘과 모델의 앙상블이라고 생각해보면 어떨까 싶다.

 

  그래서 사람이 가진 논리적 기술로 데이터를 가공하지 않고, 머신러닝 알고리즘(물론 이것도 넓은 범위에선 논리인것 같다)이 데이터 안의 로직이나 패턴을 추출해 내는 과정을 '기계학습'이라고 명명 지은게 아닌가 싶다. 로직을 짜내는 과정에서, 해당 머신러닝 필터는 데이터에 커스터 마이즈된 특정한 모양(모델이 데이터에 커스터마이즈 된 부분을 얘기하는지, 데이터에 독립된 필터를 얘기하는지는 좀 아리송 하긴 하긴 하지만. 현재로서는 왠지 모델 자체에 데이터의 숨은 로직의 특성도 포함되는거 같다)을 가진다. 이후 이 특정한 모양으로 커스터 마이즈된 필터를 향후 들어오게 되는 같은 타입의 새 데이터에 적용하게 되면, 사람이 프로그래밍 하지 않아도 기존 결과 데이터와 비슷한 로직의 영향을 받은, 결과 데이터를 만들어 내게 되지 않을까 싶다.

 

  그 후 일반적으로 만들어진 모델을 검증하기 위해서, 보통 전체 데이터셋을 트레이닝셋과 검증셋으로 적당히 나누어 트레이닝셋으로 훈련을 시킨 후에 검증셋으로 실제 경기를 진행 하게 한다. 트레이닝 셋은 일종의 원어민 회화 이고, 검증셋은 실제 다른 외국인에게 배운걸 시도 하는거라고 생각해봄 이상할까--; 

 

 

 

 

[몇가지 문제 들]

  머신러닝이 입력과 결과 데이터 사이에 숨어있는 로직을 뽑아내는 작업이라는 것이 어느정도 맞다는 가정에서, 몇가지 머리가 아픈 모호한 문제들이 발생할 수 있다.

 

 

  1번째 문제는 'garbage in garbage out' 이다. 어떤 우수한 머신러닝 모델이라도 좋은 데이터를 공급해 주어야 좋은 알고리즘을 추출해 준다는 것이다. 정신건강의학과 의사가 상담을 하는데, 환자가 거짓말만 늘어 놓는다면 어떻게 될까? 아무리 실력있는 의사라도 환자의 마음의 병의 원인에 대해서 잘못된 판단을 하게 될것 이다(물론 정말 뛰어나다면 거짓말을 한다는 것 자체를 눈치채고 진실을 얘기하도록 유도할 수도 있겠지만^^). 그럼 이러한 가짜 데이터들은 왜 들어 가게 될까?

 

  우선 충분히 신뢰할 만큼 데이터 양이 많지 않거나 특정 군으로 편향될 수도 있다. 우리가 매번 선거 시기에 듣는 여론 조사용 표본 데이터의 중요성이다. 게임센터를 들어가는 학생 10명의 의견을 듣고 우리나라 전체 학생이 어떤 생각을 가지고 있다라고 말하는 것은 의미 없을 것이다. 근데 사실 현실적으로 충분히 좋을 정도라는 표현은 참 추정하기 힘든 일인거 같다. 충분히 좋을 정도의 개발, 충분히 좋을 정도의 테스트 같이, 잘은 모르지만 통계적으로 의미 있는 충분한 데이터 라는 것도 약간은 바라보는 관점에 따라 애매한 영역이지 않을까 생각해 본다.

 

  다른 원인은 노이즈이다. 만약 누군가 사실을 들키는 것이 창피해서 속마음과는 다른 가짜 선택을 했다면? 데이터를 취합하는 과정에서 잘못된 정보들이 우연히 섞여졌다면? 쇼핑몰에서 연령대별 분석을 하는데 회원들의 아이디들을 가족이 모두 공유해서 사용하고 있다면?(뭐 예를 들어서 회원제인 코스트코 같은데는 지인이 부탁한 물건을 사다주는 사람도 많을거 같다) 해커가 들어와 정보를 본인에게 유리하게 몰래 변경했다면? 해당 데이터의 형태가 애초부터 랜덤적인 선택 요소가 표함되어 있다면? 등등.. 다양한 일들이 생길 수도 있다. 또는 아래의 알렉사 기사처럼 TV 에서 나온 음성 같은 외부 테이터를 잘못 받아들여 엉뚱한 판단을 하게 될지도 모른다.(뭐 홍채나 얼굴 인식이 사진으로 된다든지 하는 것도 비슷한 경우일 것이다) 

http://thegear.co.kr/13718

 

   

  2번째 문제는 데이터 무더기 에서 학습을 위해 제공하기 위해 골라낸 요소들이, 정말 해당 현상을 제대로 설명하는 인자인가 하는 문제이다. 어떤 인자는 혼동만 주는 필요 없는 인자일 수도 있고, 어떤 중요한 인자는 빠져있을 지도 모른다. 최악의 경우는 현재 수집을 안하고 있거나, 현실적으로 수집을 못하는 요소일 지도 모른다. 데이터를 제대로 이해 못하고 머신러닝 모델에게 전달한다는 것은, 사람이 비즈니즈 룰도 이해를 못하고 프로그래밍을 하는 것과 그닥 차이가 없을 것이다. 로직은 돌아 갈테지만 아마도 아무 의미도 없거나 재앙일 것이다.

 

  또는 데이터의 범위를 잘못 잡은 학습을 할수도 있다. 예를 들의 앞의 예가 아래와 같은 로직으로 동작하는데, 현실로 수집가능한 데이터가 10000 까지 였다면, 해당 데이터로 학습된 모델은 10000 을 넘어가는 데이터를 만날때 커다란 재앙을 안겨줄 지도 모른다(버퍼 오버플로우 처럼 말이다).

1
2
3
4
if input <= 10000:
    우리가 수집한 데이터(0~10000)가 적용된 로직
else:
    새로운 타입의 데이터가 생성됨
cs

 

  비슷하게 여론 조사 결과 조작이나, 통계의 여러 부작용 처럼, 데이터의 어떤 집합, 성질을 선택 하느냐에 따라서 의도된 답을 선택하거나 실제와는 다른 결과를 보여줄 수도 있을 듯 싶다. 

 

 

 3번째 문제는 학습된 모델을 실제 현실에 적용할수 있느냐 하는 문제이다. 자율자동차와 같이 운전자의 보호냐 보행자의 보호냐를 선택하는 문제일 수도 있고, 99.9999% 의 정확도라도 false-positive(안전하다고 판단했지만 실제로는 위험함) 가능성이 존재하는 한 실수에 대한 윤리적 문제가 일어날 수 있다. 예로서 머신러닝 기반의 의료검사를 신뢰 하다가 드문 케이스 때문에 암을 놓쳤다고 해보자. 시스템의 정확도가 엄청 높고, 해당 경우의 데이터가 트레이닝 데이터에 포함되지 못한 정말 운이 없는 경우기 때문에 어쩔수 없다라고 할수 있겠는가? 이런 분야라면 의사가 먼저 체크 후 괜찮다고 판단한 환자를 머신러닝으로 2차 체크 하여, 의사의 실수를 줄이는 방식으로 이용하는 등의 사람들이 납득할 수 있는 합리적인 기술 적용 프로세스가 필요하다. 그래서 어떤 사람들은 머신러닝을 기존 기술의 대체제가 아닌 보완제의 관점에서 접근하기도 한다.

 

 

  마지막 문제는 아마도 모델의 오차와 별개로 우리가 가지고 있는 데이터 자체가 제공된 시점부터 이미 실제 진실과는 조금은 차이가 날 수 있을지 모른다는 것이다. 사람들의 해당 데이터를 만들게된 모든 이유를 다 이해한다면 모르지만, 그런 전지전능함을 가지지 못한 우리는 데이터에 담긴 fact 만을 믿어, 중요한 사실을 놓칠지도 모른다. 마치 우리가 매일 만나는 사람이 겉모습과는 다른 속마음을 가질 수 있는 것과 비슷하게 말이다. 또한 해당 진실은 해당 시간대에서만 유효했을 수도 있다. 시간이 흐름에 따라 다른 영향을 받을 수도, 또한 아예 해당 데이터는 시간에 따라 변화를 하는 데이터일 수도 있으니 말이다.

 

 

  그래서 알파고가 바둑이나 스타크래프트 같은 분야를 선택한건 아주 영리한 선택이라고 생각한다. 현실의 데이터가 왜곡되듯이 바둑기사가 불리하다고 바둑알을 속이거나 바둑판을 엎는 일 따위는 없을 테니까 말이다. 또한 바둑과 같은 테이블 게임은 경우의 수는 무한대일지 모르겠지만, 바둑 룰과 선의 공간, 그리고 승리라는 목적으로 닫힌 세상이라는 부분은 그 무한대 성을 많이 제한해 주며, 데이터의 무결성을 보장하는데 오픈된 분야보다 많이 유리하다고 생각한다(게임이란건 사람의 이해 가능성을 전제로 성립될 수 있기 때문에 무한에 가깝다곤 하지만 어째든 제한은 있다고 본다. 다만 해당 부분은 효율적인 승리의 관점에서 제한된다는 거지, 바둑의 철학적 관점에 대한 제한을 얘기하는 것은 아니다). 그것은 다음에 도전할 지도 모른다는 스타크래프트 같이 현재 머신러닝이 강세를 보이고 있는 게임 분야도 비슷할 듯 싶다. 아무리 자유도가 높더라도 게임 분야는 사람이 흥미를 가지도록 방향이 제한되고, 추출해야될 로직들이 머물고 있는 데이터들에게 비교적 안전한 닫힌 세상이라고 생각한다(뭐 그렇다고 쉽다는 얘기가 아니라 real world 에 비해서 상대적으로 그렇다는 의미다. 그리고 부루마블 같은 주사위와 황금열쇠라는 랜덤요소를 가지는 게임 같은건 좀 다른 문제일 것이다). 어떤 기사의 마지막에서 살짝 언급했듯이 알파고의 승리의 숨은 주역은 뒤에 적절한 장르와 데이터를 선택하고, 적절한 알고리즘으로 학습시킨 과학자 들이라는 말이, 물위를 헤엄치는 오리의 바쁜 발을 떠올리게 된다.

 

 

 

 

[기술적인 미래]

  이왕 이것저것 얘기를 한 김에, 앞으로의 머신러닝 기술이 어떻게 될까도 추측해 보자. 빅데이터 분야의 진행을 기반으로 추측해보면, 처음에 나온 하둡이 hive 나 pig 같은 좀더 쉬운 작업을 가능케 하는 프레임윅들로 감싸지듯이, 현재 같이 하나하나 이해하며 초기 값 및 모델, 제어 요소들을 조정해, 좀더 빠르고 높은 정확도의 결과를 얻으려 하는 부분은 점차 통합되고 자동화된 로직으로 대체될 가능성이 높을 듯 하다. 군웅할거중인 머신러닝 프레임윅들도 한두개의 강력한 범용 프레임 윅과, 몇개의 특수 프레임윅들로 정리되지 않을까 생각한다(안 쓰는 애들은 버려지니...). 여튼 머신러닝 기법을 사용하는 방법은 점점 쉬워지며 블랙박스로 감싸질 것 같다. 이런 부분이 많은 강의 하는 분들이, 관련 수학을 깊게 몰라도 머신러닝을 배우는덴 무리가 없다고 강조하는 측면인 듯 싶다. 우리가 기계공학을 몰라도 자동차를 운전할수 있듯이 말이다.

 

  물론 머신러닝 분야의 알고리즘 개발에 발을 담그고 있는 분들이나, 좀더 디테일하며 컨트롤을 원하는 분들은 껍데기 안쪽을 다루기 위해서 좀 더 깊게 이해하려 하는게 맞을 수도 있지만, 어느 정도 깊이로 이해하려 하는지는 좀 생각해 볼 문제 같다. 깊이 문제도 수학식이나 통계 공식의 논리적 정확성의 증명에 너무 집착하다보면 원래 그 식이 얘기하려고 했던 이미지나, 본질을 놓칠수도 있으니까 말이다(공부하기 싫어서 하는 얘긴 아니다^^). 수학은 연산보다는 그 연산이 무엇을 의미하는지를 이해하는게 더 중요한 듯도 싶다. 그리고 아마도 커다란 메이저 회사들이 필요한 프로그래밍 기술들을 머신러닝 프레임윅 안 쪽에 쉬운 인터페이스로 구현해 놓음으로서 현재의 프로그래밍 성격의 장벽은 점점 낮게 될 것 같다. 물론 어려운건 마찬가지 겠지만 현재보다 상대적으로 쉬울수 있다는 얘기긴 하다. 또한 개인이나 자그마한 기업들은 유용한 데이터 자체를 수집하기 힘들수도 있기 때문에 일반적인 분야에 대해서는 1+1 상품처럼 데이터와 학습된 머신러닝을 같이 묶어서 제공하는 부분들도 늘어날 것 같다. 지금 한참 관심을 받고 있는 음성이나 이미지 인식 분야 같은 인류의 공공재 분야에서 말이다.

 

 

  그리고 반대로 생각해 보면 미래에도 개선되기 힘들 것 같은 부분들이 있다. 우리가 비즈니스를 이해하고, 적정한 테이터를 디자인해 수집하고, 학습에 필수적인 값들을 뽑아내고, 머신러닝이 추출해낸 반투명한(뭐 RNN 같은 신경망 이론인 경우는 가중치 들이 왜 그런지 사람은 이해하기 힘들 듯 하다) 로직들이 맞는지를 이해하고, 오차의 본질을 이해해 모델을 조정하는 능력이다(원석을 감정하는 능력이라고 할까..). 또 문제를 파악하고 적절한 기법(알고리즘과 모델)을 선택하는 것도 오랜기간은 수학적, 통계적 기법을 잘 아는 사람들의 손을 대부분 필요로 하지 않을까 싶다. 물론 프레임 윅들이 여러가지 시각화 툴을 통해, 해당 작업을 많이 도와줄 것은 같지만 말이다. 또 아마 지금의 장미빛 전망의 끝이 지나면, 자동화된 프로그램이 못하는 분야에 대해 다른 해결을 원하는 새로운 장르가 생겨날 것도 같다(어차피 현재의 로직으로도 해결 못하는 문제는 많으니까. 왠지 머신러닝은 로직을 벗어난 로직이라 그러한 부분을 일부 해결해 줄것은 같다). 

 

  현재 트레닝셋과 검증 셋으로 나누어, 리그레이션 테스트와 비슷하게 신뢰도를 확인 하는 방법은, 뭔가 약간 너무 낙관주의적인면(출처가 같은 트레이닝과 검증 데이터가 무조건 옳다고 가정하는 측면에서)도 있다고 느껴지기 때문에, 수집을 포함한 전체적인 데이터 케어 관점에서 추출된 로직에 대해 테스트 및 검증 하거나, 구성된 시스템의 여러 부분과 보안적인 측면을 검토하는 검증 활동 영역도 활성화 되지 않을까 싶다. 다만 머신러닝으로 추출된 로직은 프로그래머가 만든 로직과 다르게 명확히 알수 없으므로(그래서 앞에서 반투명이라고 표현했다), 모순적으로 알고리즘과 모델을 이해해야 최소한의 검증이 가능하므로 기존에 일반적인 테스팅이나 보안 활동을 하던 사람들이 접근하기는 장벽이 높은 분야 같기도 하다.


  결과적으로 머신러닝은 데이터를 기반으로 현대의 많은 과학 분야가 그렇듯이 완전한 해에 대한 실용적인 근사로 나아가는 로직이다(퍼펙트할수 있는 예외는 일부 게임과 같이 닫힌 세상의 데이터 일 것이다). 프로그래머들이 프로그래밍 언어를 통해 기호로 분리해 놓은 논리 기호들을 녹여 다시 새로운 기호를 만드는 느낌이라고 할까? 그래서 머신러닝을 바라볼때는 수학이나 통계학을 기호의 학문이 아닌 알고리즘을 구성할 레고의 블록(블랙박스적 도구)으로 바라보는 접근법도 어떨까 싶다. 실제로 우리가 파이썬을 할때도 모든 세세한 라이브러리의 내부 로직까지 모르면서 사용하는 것과 비슷하게 말이다.

 

  왠지 헤메고 있는 사람을 더 복잡하게 만드는 어설픈 얘기들을 늘어놨는지도 모르겠지만^^;, 이런저런 블로그를 돌아다니다가 알게되어 읽어봤던 몇 가지 괜찮았던 머신러닝의 '주변영역'을 건드리는 책들을 소개하면서 생각의 나래를 마무리 하려 한다. 혹시 위의 생각에 조금은 일리가 있다고 생각하시는 분들은, 아래의 책들과 함께, 아래 링크의 유투브의 유명한 '모두를 위한 딥러닝 강좌' 를 보시고, 이후 스스로 자기가 갈 공부 방향을 선택하시면 될것 같다.

https://www.youtube.com/playlist?list=PLlMkM4tgfjnLSOjrEJN31gZATbcj_MpUm

 

  • 틀리지 않는 법 : 수학적 사고의 힘 (현실을 심리학, 경제학으로 해석하는 대신 수학으로 해석해 보며 수학적 접근법의 한계도 얘기 한다. 은근 그 과정에서 머신러닝의 한계 또한 생각해 보게 되는 듯 하다)
  • 화이트헤드의 수학이란 무엇인가(앞의 수학 라이브러리의 복소수 파트에 대해 아이디어를 얻은 책이다. 왜 이런 수학 장르가 생겨나게 됬을까를 찬찬히 생각하게 하는 책 같다. 수학과 별로 안 친한 저와 비슷한 레벨이면 이해가 안가서 살짝 넘겨야 되는 수학적 개념들이 좀 있다)
  • 마스터 알고리즘 (머신러닝에 대한 얘기다. 해당 분야를 오래 경험해온 전문가가 세상에는 5종류의 머신러닝에 대한 접근방식들이 있고, 그들을 조화시킬 궁극의 알고리즘이 있을수 있다는 얘기를 한다. 이 책을 읽게 되면 머신러닝 책들이 왜 그렇게 여러 주제의 잡학 사전처럼 구성되어들 있는지 이성적이면서 감성적으로 수긍이 되게된다)
  • 헤드 퍼스트 통계학 (통계를 직관적으로 설명하려는 시도를 한다. 뒤로 갈수록 직관성이 사라지고 수학만 남는 아쉬움이 있다--;)
  • How to solve it (어떻게 문제를 풀어나가느냐에 대한 책이다. 문제 풀이에 대한 접근 방법을 얘기하는 책이고, 학생들에게 어떻게 수학을 가르치느냐에 대해 얘기하는 책이지만, 어려워서 이해가 안가는 문제들이 종종 있음에도 읽을 가치가 있다. 이 수업의 방향이 이 책 영향을 좀 받은듯 하다^^;) 
  • CODE 코드 : 하드웨어와 소프트웨어에 숨어 있는 언어(이건 프로그래밍 공부가 의미 없게 느껴지는 분들을 위해서 덤으로~ 불빛 신호에서 컴퓨터가 만들어지기 까지의 과정을 설명한다. 중간에서 길을 잃어 필름이 끊기더라도 읽어볼 가치가 있다고 본다. 비 전공자한테는 생각보다 어려울지도 모르지만, 해당 책의 저자가 비전공자도 읽을 수 있도록 노력해 쓴 책이고 파이썬 공부를 하고자 하는 의지 정도만 있음 가능할 듯 싶다.)

 

[몸풀기 - 공학 라이브러리의 푸리에 변환]

   머신러닝을 하다 갑자기 이름도 낯선 푸리에 변환이란 것이 나오게되서 이상하게 생각할지 모르겠지만, 이 라이브러리를 소개하는 이유는, 머신러닝 라이브러리가 특별한 형태의 라이브러리라는 생각을 버리게 하고 싶어서 이다. 그리고 지금 소개하는 라이브러리를 잘 보면 왠지 머신러닝 라이브러리랑 닮아있다는 느낌도 있어서 이다. 그래도 맘에 안든다면 opencv 라는 이미지 라이브러리(정확하게 얘기함 컴퓨터 비전 쪽 라이브러리)를 살펴보는 부록 챕터라고 생각해도 좋다.

 

  푸리에 변환은 보통 전자기파 쪽에서 많이 쓰는 이론으로, 저도 수학적으론 잘 모르지만, 모든 주기를 가지는 신호는 사인과 코사인의 합으로 분리해 나타낼 수 있다는 이론이다(우리가 쓰는 스마트폰의 통신들도 이런 이론들 땜에 가능하게 됬다) 그러다보니 분리된 사인, 코사인 함수들의 주기를 보면, 해당 신호가 어떤 주파수(1초에 몇번 지그재그로 움직이나. 2.6G 와이파이의 경우 1초에 2억 6천번 파형이 움직인다)들을 포함하고 있는지 알게된다. 뭐 주기가 없는 신호도 무한대의 주기를 가졌다고 가정해서 변환하는 신묘함도 있다.

 

  근데 묘하게 이걸 그림에 적용하면, 그림에 분포되 있는 명암 요소에 따라서 주파수 영역으로 변환할 수 있다(사실 정확히 설명하기 위해 이리저리 사이트 들을 찾아봤는데, 그림의 평균적인 명암값 으로부터의 차이를 가지고 판단하는지, 경계 값과의 변화정도 인지 기준을 잘 모르겠다. 아시는 분은 댓글에 좀 --;). 여튼 그렇게 그림에 푸리에 변환을 적용해 정리하면, 그림과 동일한 2차원 영역의 주파수 그림이 나오는데, 중심 부분에 가까울 수록 그림에서 주로 면이라고 얘기하는 색이 비슷한 공간속의 점들의 정보를 가지고 있고, 중심에서 멀어질 수록 그림의 외곽선 이라고 부르는 경계를 구분해 주는 점들의 정보를 가지고 있다. 

 

  그래서 그림을 읽어와 푸리에 변환을 해서 나온 네모난 주파수 평면에서, 가운데 원 영역의 값을 0 값으로 덮어 지워버리면(보통 마스킹 한다고 한다. 밑의 그림 처럼 까맣게 정보를 없앴다), 그림의 외곽선 정보만이 남는 효과를 가져오게 된다(약하게 삭제하면 sharpening effect 가 되는듯 하다). 그것은 뭐 포토샵 같은 전문 프로그램에선 더 정밀한 로직을 쓰겠지만, 이 로직을 적용하면 어떤 그림을 적용하든 동일한 윤곽선 추출 효과를 가져온다(마치 학습된 머신러닝 로직이 데이터에 동일한 효과를 가져오는 것처럼 말이다)

 

 

 

  그럼 예제를 보자(요건 머신러닝 예제가 아니라 구글을 헤맨 과정은 생략한다^^). 일단 컴퓨터에 있는 사진 등을 복사해도 좋고 적당한 이미지가 없다면 무료 사진 사이트인 https://pixabay.com/ 로 가서 이미지를 하나 다운 받아서, c:\python\code 폴더에 mypic.jpg 라고 저장한다.

 

  그리고 opencv 를 설치해야 되는데 불행하게도 python 3.5 버전은 pip 인스톨이 안되는 듯하다. https://www.solarianprogrammer.com/2016/09/17/install-opencv-3-with-python-3-on-windows/ 사이트에서 안내하는 데로, http://www.lfd.uci.edu/~gohlke/pythonlibs/ 사이트로 가서, opencv_python-3.2.0-cp35-cp35m-win_amd64.whl 를 다운받아(가끔 파일이 이름이 바뀌어서 그런데 cp35 가 파이썬 3.5란 의미이고, amd64가 64비트 윈도우이다.) c:\python\code 에 넣고, 아래와 같은 명령어로 whl 파일을 설치했다. whl 파일이 무언가는 예전 시간에 설명했다~

(참고로 어떤 블로그 보다보니 python 3.6 버전에서는 pip 로 그냥 설치가 되는걸 보긴 했다)

 

c:\Python\code>pip install opencv_python-3.2.0-cp35-cp35m-win_amd64.whl
Processing c:\python\code\opencv_python-3.2.0-cp35-cp35m-win_amd64.whl
Installing collected packages: opencv-python
Successfully installed opencv-python-3.2.0

 

 

 그럼 예제를 위해 만든 소스를 구글에서 'python opencv fft low pass filter image' 라고 찾아서, 돌려보다 보니 python 2를 기준으로 만든거라서 나누기 코드 부분에서 문제가 생겨서(나누기 후 결과를 integer 로 취급할거냐 float 로 취급할거냐가 버전이 올라가면서 달라졌다), 아래의 사이트를 찾아, 코드를 조금 수정했다.

[소스 사이트]

http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_transforms/py_fourier_transform/py_fourier_transform.html

 

[에러 메시지와 해결 사이트]

TypeError: slice indices must be integers or None or have an __index__ method 에러

https://stackoverflow.com/questions/28272322/typeerror-slice-indices-must-be-integers-or-none-or-have-an-index-method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import cv2
import numpy as np
from matplotlib import pyplot as plt
 
# 이미지를 읽어옴
img = cv2.imread('mypic.jpg',0)
 
# 푸리에 변환 하고 이런저런 맞춤
= np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20*np.log(np.abs(fshift))
 
# 작은 마스크를 만들어서 푸리에 변환한 영역의 가운데를 지워 버림(하이패스 필터)
rows, cols = img.shape
crow,ccol = rows//2 , cols//2
fshift[crow-30:crow+30, ccol-30:ccol+30= 0
f_ishift = np.fft.ifftshift(fshift)
 
# 다시 이미지로 역변환
img_back = np.fft.ifft2(f_ishift)
img_back = np.abs(img_back)
 
# 그림으로 보여줌.
plt.subplot(131),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow(img_back, cmap = 'gray')
plt.title('Image after HPF'), plt.xticks([]), plt.yticks([])
 
plt.show()
 
print ("ok")
 
cs

 

 

  언제나 처럼 c:\python\code 폴더에 'python opencv_fft_sample.py' 란 이름으로, 파일형식은 '모든 파일'을 선택하고,  인코딩은 'utf-8' 로 저장하고 실행한다.

c:\Python\code>python opencv_fft_sample.py
ok

 


  위와 같이 그림이 세개 나오는데 왼쪽이 원본 이미지, 가운데의 빛이 가운데 모인듯한 이미지가 푸리에 변환을 통해 주파수 영역으로 변환된 이미지, 오른쪽이 주파수 영역의 가운데 데이터를 삭제하여(코드안에 로직이 있다). 다시 이미지로 복구하였을때, 주로 경계선 요소들만 남기고 평면 요소들이 사라진 경우이다.

 

  보시면 푸리에 변환이 왠지 머신러닝과 비슷한 느낌이 들지 않는가?(머신러닝은 사실 데이터를 적절히 해석해 내부 특징을 얻을 수 있는 모든 알고리즘 들을 가져다 쓴다고도 할수 있다) 차이라면 데이터에서 로직을 추출하진 않는다. 하지만 데이터를 넣으면(디지털 이미지 또한 좌표 기반의 데이터 이다) 임의의 처리를 해서 데이터에 특정한 특징을 노출해준다는 부분에서 왠지 비슷하다는 느낌이 들어 굳이 이 시점에 소개했다. 그럼 실제 머신러닝 로직으로 가보자.

 

 

 

 

[최소제곱법 이란]

  많은 머신러닝 및 데이터 분석 책에서, 제일 처음에 나오는게 회귀분석이고(과거의 데이터를 기반으로 미래의 데이터를 예측함), 그 중 가장 먼저 나오는게 최소 제곱법 같다(이후는 최우추정법, 베이즈 같은 낯선 이론들이 나온다). 디테일한 의미를 다 알진 못하지만 개념적으로만 근사한다면, 해당 컨셉은 제공되는 데이터들로부터 가장 가까운 어떤 선(직선 or 곡선 : 사실 직선은 곡선의 한 형태 일 것이지만)을 찾기위해서, 선과 데이터 들의 수직 거리를 제곱한 값들의 합이 제일 작아지는 선을 나타내는 방정식의 상수들을 찾는 것이다. 즉 직선이라고 가정한다면 y = ax + b 의 a, b 값을 계속 특정 방향으로 조금씩 변화해 가면서, 각 점과의 거리를 제곱한 합이 제일 작아지는 지점을 찾는다(엑셀의 추세선이라고 봐도 될것 같다). 뭐 자세한 내용은 관련 책이나 블로그 등에서 보시라고 하고 살짝 넘어간다 --; 

    

 

 


[numpy 에서의 최소제곱법 구현]

  원래 numpy 는 계획에 없었는데, 찾다보니 있어서 다른 라이브러리와 비교를 위해서 넣게 되었다.구글에서 'linear least square fit python' 라고 찾아 아래의 페이지를 찾는다.

https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.lstsq.html

 

  이후 적당히 편집하여, y = 3x + 1 의 공식에 맞도록 x, y 값을 바꿔 넣었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np
import matplotlib.pyplot as plt
 
# x, y 좌표 지정(y = 3x + 1), 방정식 모델 지정
= np.array([1257])
= np.array([471622])
= np.vstack([x, np.ones(len(x))]).T
 
# 선형대수 라이브러리의 least squre 호출
slope, intercept = np.linalg.lstsq(A, y)[0]
print("기울기:", slope, ", 절편:", intercept)
 
# 기존 값을 점으로, 찾은 기울기를 선으로 그린다.
plt.plot(x, y, 'o', label='Original data', markersize=10)
plt.plot(x, slope*+ intercept, 'r', label='Fitted line')
plt.legend()
plt.show()
cs

 

  앞 시간의 예제들을 시연해 봤다면 이미 라이브러리들을 설치되어 있을테니(아니라면 설치 방법은 15, 16교시를 참고한다), c:\python\code 폴더에 'numpy_least_sample.py' 이름으로, 파일형식은 '모든 파일'을 선택하고,  인코딩은 'utf-8' 로 저장하고 실행한다(지금보니 utf-8 로 저장하면 한글 주석이 달리는 경우에도 상단에  '#-*- coding: utf-8 -*-' 를 안 넣어도괜찮다). 정상적으로 해를 찾아 기울기 3, 절편 1 을 찾게 된다.

 

c:\Python\code>python numpy_least_sample.py
기울기: 3.0 , 절편: 1.0

 

 

 

 

[scipy 에서의 최소제곱법 구현]

  이번에는 과학 라이브러리인 scipy 를 이용해 보자. 구글에 'linear least square fit python scipy' 라고 찾아 아래의 2개 페이지를 찾아 적당히 믹스 했다. 전체적인 흐름은 numpy 와 거의 같다고 보면 될 듯 하다.

http://www2.warwick.ac.uk/fac/sci/moac/people/students/peter_cock/python/lin_reg/
https://docs.scipy.org/doc/scipy-0.19.0/reference/generated/scipy.stats.linregress.html

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
 
# 데이터 지정
= np.array([1257])
= np.array([471622])
 
# 데이터에 맞는 값 찾기, Slope: 기울기, Intercept: 절편
slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)
print ("Slope and intercept", slope, intercept)
print ("R-squared", r_value**2)
 
# 데이터를 점으로, 찾은 선과 같이 화면에 표시
plt.plot(x, y, 'o', label='original data')
plt.plot(x, intercept + slope*x, 'r', label='fitted line')
plt.legend()
plt.show()
cs

 

  c:\python\code 폴더 'scipy_least_sample.py' 이름으로, 파일형식은 '모든 파일'을 선택하고,  인코딩은 'utf-8' 로 저장하고 실행한다.

c:\Python\code>python scipy_least_sample.py
Slope and intercept 3.0 1.0
R-squared 1.0

 

  뭐 결과가 같으니 이미지도 동일하다.

 

 

 

 

[tensorflow 에서의 최소제곱법 구현]

  마지막으로 tensorflow 인데, 운이 좋게도 금년에 텐서플로우가 64비트 윈도우 버전의 python 3.5 까지 지원하게 되서 시연이 가능하게 됬다. 근데 뭐 아직 윈도우즈는 텐서플로우에게 관심의 바깥 같은 느낌이 좀 들어서, 텐서플로우를 사용하는 사람들은 보통 리눅스나 맥환경에서 많이 쓰는 것은 같다. 

 

  먼저 pip 를 이용해서 텐서플로우를 설치한다(꼭 윈도우 64비트에, 파이썬 3.5 대 버전이여야 가능하다!. 구글을 찾다 보니 새로 빌드를 해서 32비트에 설치해 쓰는 사람들도 있다고는 한다)

c:\Python\code>pip install tensorflow
Collecting tensorflow
  Downloading tensorflow-1.2.0-cp35-cp35m-win_amd64.whl (21.2MB)
...
Successfully installed backports.weakref-1.0rc1 bleach-1.5.0 html5lib-0.9999999 markdown-2.2.0 protobuf-3.3.0 tensorflow-1.2.0 werkzeug-0.12.2 wheel-0.29.0

 

 

  이후 구글에 'tensorflow least squares matplotlib' 라고 검색하여 아래의 두 페이지를 찾았다.

https://gist.github.com/tomonari-masada/ed2fbc94a9f6252036eea507b7119045
https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/2_BasicModels/linear_regression.py

 

  위의 페이지의 예제가 좀더 간단하게 보이지만 텐서플로우에서 생성된 변수(x, y_)를 matplotlib 의 넘겼을때 plot 에서 타입 에러가 난다. numpy 리스트 형태를 넘겨야 괜찮다. 그리고 두번째 예제가 좀 모범답안 같이 전체적인 사용 플로우를 잘 보여주는거 같아서 두번째 예제에서 훈련 후 검증 테스트를 하는 코드 부분만 제거하고, 적당히 편집하면 아래와 같다. (상세 로직은 어차피 텐서플로우에서 하라는데로 하는거니, 전체적인 코드의 흐름을 보자 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import tensorflow as tf
import numpy
import matplotlib.pyplot as plt
rng = numpy.random
 
# 파라매터들 변수 조정 수치, 전체 실행 수, 몇 번마다 화면에 로그를 보여주는지
learning_rate = 0.01
training_epochs = 1000
display_step = 50
 
# 훈련용 데이터 지정
train_X = numpy.asarray([1257])
train_Y = numpy.asarray([471622])
n_samples = train_X.shape[0]
 
# 텐서플로우 변수들 만들기
= tf.placeholder("float")
= tf.placeholder("float")
 
= tf.Variable(rng.randn(), name="weight")
= tf.Variable(rng.randn(), name="bias")
 
# 모델 만들기 'y = Wx + b' 를 정의한다.
pred = tf.add(tf.multiply(X, W), b)
 
# least square 공식을 이용하여 최소값을 만들 요소를 지정한다. 
cost = tf.reduce_sum(tf.pow(pred-Y, 2))/(2*n_samples)
 
# 기울기를 보정하는 경사하강법이란것을 사용하고, 비용을 최소화 하는 방향으로 학습 한다.
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
 
# 초기화?
init = tf.global_variables_initializer()
 
# 텐서플로우 기동
with tf.Session() as sess:
    sess.run(init)
 
    # 데이터 넣기
    for epoch in range(training_epochs):
        for (x, y) in zip(train_X, train_Y):
            sess.run(optimizer, feed_dict={X: x, Y: y})
 
        # 50번 마다 로그 뿌려서 찾는 값 변화를 보여 줌.
        if (epoch+1) % display_step == 0:
            c = sess.run(cost, feed_dict={X: train_X, Y:train_Y})
            print("Epoch:"'%04d' % (epoch+1), "cost=""{:.9f}".format(c), \
                "W=", sess.run(W), "b=", sess.run(b))
 
    # 완료 되면 결과 출력
    print("Optimization Finished!")
    training_cost = sess.run(cost, feed_dict={X: train_X, Y: train_Y})
    print("Training cost=", training_cost, "W=", sess.run(W), "b=", sess.run(b), '\n')
 
    # 찾은 결과 그래프로 보여주기
    plt.plot(train_X, train_Y, 'ro', label='Original data')
    plt.plot(train_X, sess.run(W) * train_X + sess.run(b), label='Fitted line')
    plt.legend()
    plt.show()
cs

 

    c:\python\code 폴더에 'tensorflow_least_sample.py' 이름으로, 파일형식은 '모든 파일'을 선택하고,  인코딩은 'utf-8' 로 저장하고 실행한다.

 

c:\Python\code>python tensorflow_least_sample.py
Epoch: 0050 cost= 0.031498514 W= 3.08722 b= 0.53246
Epoch: 0100 cost= 0.023787469 W= 3.07587 b= 0.593684
Epoch: 0150 cost= 0.017966598 W= 3.06593 b= 0.64688
Epoch: 0200 cost= 0.013570149 W= 3.0573 b= 0.693111
Epoch: 0250 cost= 0.010249456 W= 3.0498 b= 0.73329
Epoch: 0300 cost= 0.007741358 W= 3.04328 b= 0.768208
Epoch: 0350 cost= 0.005847037 W= 3.03761 b= 0.798554
Epoch: 0400 cost= 0.004416286 W= 3.03269 b= 0.824928
Epoch: 0450 cost= 0.003335610 W= 3.02841 b= 0.847849
Epoch: 0500 cost= 0.002519361 W= 3.02469 b= 0.867768
Epoch: 0550 cost= 0.001902884 W= 3.02146 b= 0.88508
Epoch: 0600 cost= 0.001437244 W= 3.01865 b= 0.900125
Epoch: 0650 cost= 0.001085538 W= 3.01621 b= 0.913202
Epoch: 0700 cost= 0.000819894 W= 3.01409 b= 0.924565
Epoch: 0750 cost= 0.000619259 W= 3.01224 b= 0.934442
Epoch: 0800 cost= 0.000467722 W= 3.01064 b= 0.943025
Epoch: 0850 cost= 0.000353270 W= 3.00925 b= 0.950485
Epoch: 0900 cost= 0.000266819 W= 3.00804 b= 0.956967
Epoch: 0950 cost= 0.000201530 W= 3.00698 b= 0.962601
Epoch: 1000 cost= 0.000152215 W= 3.00607 b= 0.967497
Optimization Finished!
Training cost= 0.000152215 W= 3.00607 b= 0.967497

 

  데이터를 보면 1000번을 찾으면서 50번마다 화면에 현재 상태를 출력하라고 했으니 (1000/50) 번 해서 20개의 로그가 화면에 표시된 것을 볼수 있다. Epoch(찾기 반복 횟수), cost 및 optimizer(해 찾기의 목표가 되는 기준 값과, 기준 값을 어떻게 사용할 것인지. 여기서는 최소제곱값 함수가 가장 작아지는 값을 기울기 하강법으로 찾는다). W(기울기) b(절편) 값의 변화를 보면 점점 횟수가 늘수록 기울기와 절편 값들이 의도했던 3과 1로 수렴해 가는 것을 볼 수 있다.

 

  이렇게 비교해 보면 뭔가 전문적인 머신러닝 라이브러리들은 위의 numpy, scipy 같은 일반 선형대수 라이브러리 처럼 결과만 그냥 보여주기 보다는, 모델을 지정하고(pred = tf.add(tf.multiply(X, W), b)), 잘된 학습의 판단기준도 정하고(cost = tf.reduce_sum(tf.pow(pred-Y, 2))/(2*n_samples)), 학습 과정을 살펴보며(로그) 조정하는 듯 체계적인 접근 방식을 유도하고 있다. 뭐 제가 모르는 다른 기능 들도 많겠지만, 꼭 개발쪽의 유닛테스트 프레임윅이 하는 역활과 비슷한 느낌이 든다(저런 프레임윅들은 관련 기능과 함께 조금 논란의 여지는 있지만 'best practice' 를 동시에 제시한다)

 

  matplotlib 으로 그린 그래프 화면은 원래 정답(3, 1)과 비교함 소수점 약간 차이라서, 앞의 2개 그래프와 구분이 안되서 생략했다. 설명의 일관성을 위해 정답을 제한해 놓은게 맘에 안드신다면, 아래와 같이 소스에서 train_X, train_Y 의 배열 값들을 임의의 값으로 바꾸거나 배열 데이터 갯수를 늘여보면 좀더 점들의 중심에 위치하고자 하는 선의 모양을 볼 수 있을 듯 하다. 머신러닝 및 텐서플로우에 이론과 기능에 대한 더 깊은 내용들은 관련 커뮤니티의 강좌나 책을 참고하시면 될듯 하다.

1
2
train_X = numpy.asarray([135912])
train_Y = numpy.asarray([45101813])
cs

 

 

 

[마치면서]

  자 그럼 마무리를 위해 먼 길을 돌아 처음 목적으로 돌아갈 시간이 됬다. 머신러닝에서의 파이썬의 역활은 무엇일까? 개인적인 생각으로는 numpy, panda, matplotlib 과 같이 입출력 데이터를 선, 후처리 할 수 있는 기능을 머신러닝 라이브러리들에게 무료로 제공해 주며, 더 나아가서 세계 공용어인 영어의 역활과 비슷하게 머신러닝과 관련된 여러 관련 라이브러리들이 서로 대화와 협력을 나눌 수 있도록 중재하는 역활을 한다고 하고 싶다. 아마 다른 언어에 의해서 뒤집어 지긴 힘들듯한 이런 중요성 때문에 머신러닝을 배우려는 분들은 꼭 파이썬을 조금씩, 하지만 꾸준히 관심을 가지는게 맞을 듯 싶다.

 

  그리고 다시 한번 얘기하지만 머신러닝 라이브러리는 최근 화려한 스포트라이트와 지원을 받고 있어 그렇지, 어쩌면 원래는 일반 공학 라이브러리와 같은 평범한 라이브러리 출신 일지도 모른다. 그래서 아마도 현재 유행되는 머신러닝 장르 이외에도 데이터의 특별한 측면을 노출해주는 기존의 평범한 알고리즘들이 재조명되어 새로운 머신러닝의 장르를 열수도 있을 것이다. 

 

  또한 앞의 푸리에 변환 라이브러리의 사용과 같이, 사용자가 가진 라이브러리의 동작 원리와 적용 대상에 대한 이해의 깊이에 따라서 그 유용성이 달라지게 된다. 그러므로 머신러닝 프레임윅들의 사용법이나 트레이닝, 오버피팅, 모델, 학습율, 초기값, 오차 등의 방법론들에 집중하는 것도 좋지만, 그것은 어쩌면 그림자만을 쫓는 행위일수도 있으므로, 파이썬을 공부할 때와 마찬가지로 그 기술의 배경이 되는 여러가지의 주변 요소들에 대해서도 교양이라고 생각하고 꾸준히 관심을 가지도록 해보자. 모든 진실은 데이터 안에 숨어있다는 것을 잊지말고...(데이터도 가끔 거짓말을 하긴 하지만 말이다.)

 

 

 

[보충]

1) 이런저런 블로그를 보다보니 푸리에 변환도 컴퓨터 비전 측면에서 머신러닝 쪽에 포함된다고 하는 얘기가 있다. 그럼 예제가 엉뚱한건 아니게 되서 더 다행이다~

 

2) 한 분이 numpy 예제를 보고 "파이썬에선 함수가 2개의 리턴이 가능해요?" 하고 물어보셨다. 그러려니 하고 별 생각 없이 넘어갔던 저였지만--;, 궁금해서 함 찾아봤다. google 에서 'python function multiple return' 으로 찾으면 여러 스타일이 나오는데, 가장 간단한 샘플을 보인다.

https://stackoverflow.com/questions/354883/how-do-you-return-multiple-values-in-python

1
2
3
4
def f():
    return 12
x, y = f()
print ("x = ",x, ",y = ", y)
cs

 

c:\Python\code>python return_test.py
x =  1 ,y =  2

 

3) 아래 글도 괜찮은 듯 해서 참고 자료로 링크를 건다.

머신러닝속 수학(번역)

https://mingrammer.com/translation-the-mathematics-of-machine-learning

 

 

2017.6.25 by 자유로운설탕
cs

 

  

 

 

 

 

 

 

posted by 자유로운설탕
2017. 5. 21. 15:38 프로그래밍

  이번 시간은 지난 시간에 이어 수학 라이브러리의 일환인 그래픽(정확하게는 plotting 이겠지만..) 라이브러리를 살펴보려고 한다. 지난 시간에 언급되었던, matplotlib 과 Plotly 로 예제를 구현해 보면서, 어떻게 사용에 접근하면 좋은지에 대해 얘기해 보려고 한다.

 

 

[목차]

0. 왜 파이썬 공부에 구글을 이용하는게 좋은가?

1. 언어를 바라보는 방법. 파이썬을 어떻게 바라봐야 할까?

2. 파이썬 설치와 환경, 버전 선택 하기의 이유.

3. 만들고자 하는 기능을 모르는 조각으로 나눠 조사해 보기

4. 데이터 베이스에서 내용 가져와 출력하기

5. 암호화 모듈을 이용해 암복호화 해보기

6. 퍼즐 조각들을 합쳐보기

7. 엑셀 파일 사용해 보기 -> 부록 : fuction 을 이용해서, 코드 정리해 보기

8. 정규표현식을 왜 사용해야 할까? 언어속의 미니 언어 정규표현식 살펴보기

9. 입력과 결과를 GUI 화면과 연결해 보기

10. Whois API 이용해 보기

11. 웹페이지 호출해 내용 파싱 하기(BeautifulSoup 그리고 한계)

12. 자동화 - 웹 자동화(with Selenium)

13. 자동화 - 윈도우즈 GUI 자동화(with pywinauto)

14. 자동화 - 작업 자동화

15. 수학 라이브러리 살펴보기

16. 그래픽 라이브러리 살펴보기

17. 머신러닝에서의 파이썬의 역활

18. 웹 프로그래밍 - Legacy Web

19. 웹 프로그래밍 - Flask 살펴보기(feat. d3.js)

20. 웹 프로그래밍 - Django 살펴보기

21. 정리 - 이런저런 이야기

 

 

 

[들어가면서]

  우선 예제를 구현해 보기전에 플로팅(plotting) 이라는게 뭔지 잠깐 생각해 보자. plot 이란 특정한 데이터 셋을 공간상에 표시해 주는 것을 이야기 한다. 굳이 2D 평면이 아닌 공간이라고 얘기해도 되는건, 미래에 홀로그램 형태의 3차원 형태의 디스플레이 같은 것이 일반화 된다면 아마 공간 상에 뿌려서 살펴 보는 것도 가능할 것 같아서이다(3D 프린터 같이 말이다). 뭐 여튼 현재 라이브러리는 2차원 화면상에 3차원 좌표까지 표시가 가능하긴 하지만, 아래와 같이 하나의 차원을 색으로 치환해, 평면상에 4차원의 데이터를 표시하는 것도 가능하다(뭐 공간적인 4차원이 아니라고 가짜라고 볼 수도 있겠지만, 어차피 plot 의 목적은 공간에 요소를 단순히 나타낸다기 보다는 각 구성 요소들의 독립, 복합적 관계를 파악하기 위한 목적이 크기 때문에(그 날의 매출 현황이라든지, 이상 이벤트 발생이라든지), 그래프 상에 사람이 차이를 식별할 수 있는 색이나, 점의 모양, 기울기 등도 하나의 인식가능한 차원의 그래프 요소라고 할 수 있을 것 같다.

http://engi-agora.tistory.com/37

 

  우리는 이렇게 좌표 형태로 그려진 그래프를 봄으로써 데이터의 추이, 이상현상, 군집, 분포, 차이 등의 특성에 대해 일련의 데이터 숫자 값들을 직접 보는 것보다 시각적으로 쉽게 파악할 수 있게 된다(물론 의미 있는 데이터를 잘 선택해 적절한 기법으로 뿌려줬을 때의 얘기긴 하지만 말이다). 어느정도 플로팅은 통계나 추이와 같은 감각들을 인간에게 시각적으로 전달해 주는 수학적 도구라고 볼 수 있을 것 같다.

 

 

  개인적으로 이 글을 쓰기 위해서 이런 저런 책과 웹페이지들을 참고해 보면서, 인상 깊었던 2개의 그래프는, 복소수의 곱을 좌표상에 표시해 준 것과, 산포도 이다. 첫번째 복소수의 곱을 좌표상에 표시한 부분은 아래의 페이지에 나온 것 같이 두 복소수를 곱했을 때의 의미가 공간 적으로는 아래와 같이 두 개의 좌표상의 각을 더한 회전으로 나타내게 된다는 것을 이야기 한다.  

[출처 : https://www2.clarku.edu/~djoyce/complex/mult.html ]

 

  두 번째 산포도는 특정한 두 요소가 서로 얼마나 상관이 있느냐 하는 것을 보여 주는 것이다. 예를 들어(통계는 잘 몰라서 예가 적절한진 모르겠지만 --;) 치킨을 100만큼 좋아하는 사람이 맥주를 얼마나 좋아하고, 치킨을 10만큼 좋아하는 사람이 맥주를 얼마나 좋아하는지를 각각 좌표안에 찍는다면, 아래의 그래프와 같이, 오른쪽으로 상승하는 좁은 타원 형태의 그래프 분포를 보인다면, 두 요소는 상관 관계를 가지고 있을 가능성이 존재한다고 합리적 의심을 가져볼 수 있다.

 

  그러면 한번 앞에 언급했던 2개의 많이 쓰이는 그래픽 라이브러리(matplotlib, plotly)를 이용해서, 복소수의 곱과, 산포도를 그려보는 예제를 만들어 보도록 해보자. 앞 시간에 살펴봤던 numpy 를 이용해 데이터를 생성하거나 담는 그릇으로 사용할 것이고, 해당 그릇을 그래프를 그리는 라이브러리에 전달하려고 한다.

 

 

 

 

 

[Matplotlib 을 이용한 복소수의 곱 과정을 그래프로 표시하기]

   뭐 저도 수학은 정말 잘 못해서, 복소수에 대해서 잘 모르기에, 이 예제를 만들기 위해서 책도 좀 읽고, 이것저것 급조를 좀 해봤다. 지나가는 수학과 분이 계시면 그냥 살포시 지나가시기를...

 

 

  위의 방정식의 해를 풀기 위해서는 x의 제곱이 -1 이 되어야 한다. 즉 아래와 같이 x가 루트 -1 이 되어야 하는데, 이 실수 관점에서 보면 혈통을 인정하기 힘든 가상의 수(루트 -1)를 i (복소수)라고 그런다. 그래서 i 의 제곱은 -1 이 된다.

 

그럼 그래프로 그려볼 간단한 곱을 하나 해보자. A 를 0.5+1i, B 를 1+0.5i 라고 하면,

(0.5+1i)*(1+0.5i) = 0.5*1 + 0.5*0.5i + 1i*1 + 1i*0.5i = 0.5 + 0.25i + 1i + 0.5i^2

= 0.5 + 0.25i + 1i + 0.5*(-1) = 1.25i 

 

  음 수식 상으로는 실수 부가 사라지고, 허수부만 '1.25i' 로 남게 된다.(고백하자면 일부러 각도가 합해지는게 잘 인지되는 그림을 그리기 위해서 복소수를 정할때, B는 x축 기준으로 30도, A는 60도 가 되게 만들어서, 두 개를 곱하면 직각인 90도가 되게 만들었다).

 

 

 

  이제 파이썬으로 그래프를 그려 보려 한다. 일단 첫번째로 막히는게, 파이썬에선 복소수를 어떻게 다루지? 하는 부분이다. 어떤 라이브러리를 써야 되는지도 모르겠고, 어떤 문법을 가졌는지도 모르겠다. 구글에다 우선 'python complex number' 라고 검색해 보자.

https://stackoverflow.com/questions/8370637/complex-numbers-usage-in-python

 

  위의 페이지를 보니, 생각보다 휠씬 간단해 보인다. 복소수를 쓰기 위해 complex 란 함수를 이용해도 되지만, 그냥 1 + 2j (파이썬은 i가 아니라 j임, 위키를 보면 전류를 나타내주는 기호 i와 구분하기 위해서 공학 쪽에서 j라고 쓴다고 하는 설이...) 라고 써주면 파이썬이 알아서 인식해 주나보다. 그럼 샘플을 참고하여 해당 방식으로 두 개의 복소수를 곱해서 결과를 화면에 나타내는 코드를 만들어 보자.

1
2
3
4
5
6
#-*- coding: utf-8 -*-
 
= 0.5+1j
= 1+0.5j
= a*b
print(c)
cs

 

  위의 파일을 c:\python\code 폴더에 utf-8 인코딩으로, complextnum.py 라고 저장한다(역시 이 부분을 잘 모르겠으면 2교시에서 복습을!). 이후 아래와 같이 실행해 본다.

c:\Python\code>python complextnum.py
1.25j

 

 

  아까 손으로 열심히 계산한, '1.25j' 결과가 나온다. 그럼 이제 그래프를 그려보기 위해서 일단 matplotlib 을 설치한다.

c:\Python\code>pip install matplotlib
Collecting matplotlib
  Downloading matplotlib-2.0.2-cp35-cp35m-win_amd64.whl (8.9MB)
..
Successfully installed cycler-0.10.0 matplotlib-2.0.2 pyparsing-2.2.0

 

 

  이후 구글에서 'complex number python plot' 라고 검색해서 아래의 스택오버플로우 페이지를 찾았다.

https://stackoverflow.com/questions/17445720/how-to-plot-complex-numbers-argand-diagram-using-matplotlib

 

  제일 밑에 보면, 복소수를 좀 더 잘 나타내는거 같은 극좌표 형식의 그래프도 있는 것 같지만, 좀 더 상황을 간단히 하기위해서(현재 그래프를 그려보는 시간이지, 복소수를 공부하는 시간은 아니므로...), 일반 평면 좌표계를 사용하려 한다.

 

  개인적으로 그리기 전에 원하던 그래프의 모양은 아래와 같았다.

  • 화면에 점만 찍는게 아니라 원점으로 부터 점까지 선이 연결되길 바랬다(그게 좀더 점들의 각도를 잘 보여줄 것 같아서)
  • 그래프의 비율이 실제 좌표와 동일하게 1:1 이길 바랬다. 비율이 다르다면 각도가 왜곡 되어 보일 수 있기 때문에 말이다
  • 각 선은 서로 다른 색으로 표시됬음 했고, 가로-세로 축 선을 표시하고, 그리드도 표시되었음 했다.

 

   여기 까지 계속 읽어 오신 분들은 이제 저랑 함께 초보 수준은 살짝 벗어났다고 생각해서, 읽는 지루함을 없애기 위해서 예전 시간들 처럼 모든 시행 착오를 다 보여주진 않고, 각각의 요소를 해결한 방법들을 간단히 명시후에, 최종 코드를 제시해 보이려고 한다^^

 

 

  먼저 위의 스택 오버플로우 코드를 참고해서 그래프를 그려 보니, 가로축과 세로축의 비율이 일정치 않아 각도가 정확하게 보이지 않았다. 그래서 구글에 '
matplotlib aspect ratio'  로 검색해 아래의 페이지의 코드를 찾았다.

https://matplotlib.org/examples/pylab_examples/equal_aspect_ratio.html

1
plt.axes().set_aspect('equal''datalim')
cs

 

 

  가로, 세로 축을 보이게 하는 코드는 'matplotlib show x y axis' 로 찾아 아래와 같다.

1
2
ax.axhline(y=0, color='k')
ax.axvline(x=0, color='k')
cs

 

 

  그리고 각각의 선의 색을 다르게 표시하기 위해 구글에 'matplotlib plot change color' 라고 검색해서 아래의 페이지에서 관련 코드를 찾았다. 여기서는 그래프에 화살표와 함께 주석을 다는 코드도 있어서 해당 코드를 이용해서 해당 되는 복소수의 선들에 이름표를 달기로 했다.

https://matplotlib.org/users/pyplot_tutorial.html

1
2
3
4
5
6
7
8
# c 가 색을 나타냄
Here are the available Line2D properties.
color or c : any matplotlib color 
 
# 그래프 안에 주석 
plt.annotate('local max', xy=(21), xytext=(31.5),
            arrowprops=dict(facecolor='black', shrink=0.05),
            )
cs

 

 

  참고로 그래프 내의 title 등에 한글 표시를 원하는 분들은 아래의 블로그 같은데에 해결 방법이 있지만('matplotlib 한글 깨짐' 같은 걸로 찾아서), 적용 해보니까 폰트에 따라서 숫자의 '-' 표시가 안 나온다든지 하는 여러가지 사소하게 귀찮은 일들이 생겨서 글의 논점을 흐릴 듯 해서 그냥 전 영문으로 진행했다.

http://pinkwink.kr/956

 

 

  그래서 우여곡절 끝에 완성된 최종 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#-*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
 
# 수식 정의
= 0.5+1j
= 1+0.5j
= a*b
 
# numpy 에 복소수들 담기
arr = np.array([a, b, c])
 
# 여기서 i 는 색 루프를 돌리기 위해서..
= 0
for x in arr:
   # 칼라 지정용 배열
   arrColor = np.array(['yellow','brown','magenta'])
   # 지정된 색으로 해당 복소수의 실수부(real), 허수부(iamg)를 그림.
   plt.plot([0,x.real],[0,x.imag],'ro-',c=arrColor[i])
   i = i + 1
 
# 그래프 제목
plt.title('Complex Number Multiplication')
# 그리드 보이기
plt.grid(True)
# 가로세로 1:1 비율 만들기
plt.axes().set_aspect('equal''datalim')
# x,y 축 이름 짓기
plt.ylabel('Imaginary')
plt.xlabel('Real')
# 그래프가 보이는 최대 최소 범위 지정
plt.xlim((-2,2))
plt.ylim((-2,2))
# x, y 축 보이기
plt.axhline(y=0, color='k')
plt.axvline(x=0, color='k')
 
# 주석 달기       
plt.annotate('0.5+1i (a)', xy=(0.51), xytext=(0.70.8),
            arrowprops=dict(facecolor='black', shrink=0.01),
            )
 
plt.annotate('1+0.5i (b)', xy=(10.5), xytext=(1.20.3),
            arrowprops=dict(facecolor='black', shrink=0.01),
            )
            
plt.annotate('(1+0.5i)*(0.5+1i) (c=a*b)', xy=(01.25), xytext=(0.21.1),
            arrowprops=dict(facecolor='black', shrink=0.01),
            )           
 
# 그린 그래프 보여주기
plt.show()
cs

 

 

  저장 후 실행하면 아래와 같이 그래프가 보이게 된다. 두 개의 복소수가 곱해져서, 길이는 1.25 가 되고(1.25i), 각도는 x축 기준으로 30도(b), 60도(a)가 합해져 90도(c)가 된 모습을 기하학적으로 확인 할수 있다(개인적으로는 이렇게 대수학이 기하학으로 표시되어 직관적으로 이해되는 부분이 재밌는거 같다~).

c:\Python\code>python complextnum.py

 

 

 

 

 

 

[Plotly 을 이용해 산포도 그리기]

   먼저 진행 하기 앞서서 결론적인 하나의 부분만 얘기하면, plotly 는 matplotlib 처럼 완전 무료는 아니고(커뮤니티 라이센스는 있어 그래프 종류에 따라 무료로 하루에 50~250개의 그래프는 그려볼 수 있다-> 보충 부분에 링크를 걸어놓았지만, Offline 모드로 실행을 할 수 있다. 일단 온라인 모드에만 해당하는 이야기로 정정한다.), 파이썬에서 해당 사이트로 데이터를 보내서, 사이트 내의 계정에 데이터와 그래프를 저장하는 방식같다. 클라우드 그래프 라이브러리라고 보면 될듯 하다. 다만 그래프는 상용이라 그런지 조금 더 기본 디자인이 깔끔해 보이고 이런 저런 export 등 도 가능해 보인다(예제에는 ipython notebook 에 삽입 가능하다고 되어 있는데, 명령어로 실행하는 입장이라서 잘 모르겠다..). 가격별 차이의 상세한 부분은 아래 링크를 참조한다.

https://plot.ly/products/cloud/ 

 

  먼저 plotly 라이브러리를 설치한다.

c:\Python\code>pip install plotly
Collecting plotly
....
Successfully installed decorator-4.0.11 ipython-genutils-0.2.0 jsonschema-2.6.0 jupyter-core-4.3.0 nbformat-4.3.0 plotly-2.0.8 traitlets-4.3.2

 

 

  산포도를 그리는 방법을 찾기 위해 구글에서 'scattergram plotly' 라고 검색하여 아래의 샘플 페이지를 찾는다.

https://plot.ly/python/line-and-scatter/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#-*- coding: utf-8 -*-
import plotly 
import plotly.plotly as py
import plotly.graph_objs as go
import numpy as np
 
# 랜덤 값 만들기
= 1000
random_x = np.random.randn(N)
random_y = np.random.randn(N)
 
# 트레이스 만들기
trace = go.Scatter(
    x = random_x,
    y = random_y,
    mode = 'markers'
)
 
data = [trace]
 
# 그리기
py.iplot(data, filename='basic-scatter')
cs

 

 위의 파일을 c:\python\code 폴더에 utf-8 인코딩으로, plotlytest.py 라고 저장한다. 이후 아래와 같이 실행해 본다(...)

c:\Python\code>python plotlytest.py

KeyError: 'plotly_domain'

 

  실행하면 위와 같은 에러가 발생한다. 구글에 'KeyError: 'plotly_domain''로 찾아서 확인해 보면 iplot 대신 plot 을 사용하라고 한다. 아래 해당 부분 코드만 수정해 다시 돌려본다. (편집 환경으로 ipython 을 사용하시는 분들은 에러가 안 날수도 있을 것 같다)

https://stackoverflow.com/questions/34929778/keyerror-plotly-domain-when-using-plotly-to-do-scatter-plot-in-python

1
py.plot(data, filename='basic-scatter')
cs

 

c:\Python\code>python plotlytest.py

Aw, snap! We don't have an account for ''. Want to try again? You can authenticate with your email address or username. Sign in is not case sensitive.
Don't have an account? plot.ly
Questions?
support@plot.ly

 

그런데 이번엔 계정이 없다고 얘기한다(첨에 나온 라이센스 관련 페이지가 이걸 만나서 찾아보다가 가게된 것이다. 그래서 사실 완전 무료가 아닌 이 라이브러리를 소개해야 되나마나 조금 고민하긴 했다). 다시 구글에 'Don't have an account? plot.ly' 라고 검색하여 아래의 안내 페이지를 찾는다.

https://plot.ly/python/getting-started/

 

 

결국 plotly 라이브러리를 사용하기 위해서는 아래 네 가지를 해야한다.

  • 회원 가입
  • API 키 발급(kisa api 를 사용할 때와 비슷하다)
  • 이메일 인증(API키만 발급 하고 해보니, 안되서 이메일 인증도 했다)
  • python 코드안에 계정 정보 넣기. (어떤 원리로 캐싱이 되는진 모르지만 한번 넣고 실행 후엔 해당 계정 정보는 코드에서 빼도 해당 계정으로 실행이 되긴 한다)

 

  그럼 회원 가입 페이지로 가서 회원 가입을 한다. (이메일, 계정 이름, 패스워드)

https://plot.ly/accounts/login/?next=%2Fsettings%2Fapi

 

 

  이후 로그인 한 상태에서 아래 페이지로 가서 API 키를 '재생성' 시킨다. 이후 해당 키를 적당히 메모장 등에 저장해 둔다.

https://plot.ly/settings/api

 

 

  자신의 메일로 가면 plotly 에서 보낸 인증 메일이 와 있을 것이다. 인증 링크를 클릭한다.

 

 

  이후 코드를 최종 수정하여, 아래와 같이 API 키 정보를 집어 넣는다. (whois API 때와 마찬가지로 코드내의 '본인계정', '본인API키' 항목 부분은 각자 발급한 키로 수정해 주어야 동작한다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#-*- coding: utf-8 -*-
import plotly 
import plotly.plotly as py
import plotly.graph_objs as go
import numpy as np
 
# API 키 정보 넣기
plotly.tools.set_credentials_file(username='본인계정', api_key='본인API키')
 
# 랜덤 값 만들기
= 1000
random_x = np.random.randn(N)
random_y = np.random.randn(N)
 
# 트레이스 만들기
trace = go.Scatter(
    x = random_x,
    y = random_y,
    mode = 'markers'
)
 
data = [trace]
 
# 그리기
py.plot(data, filename='basic-scatter')
cs

 

  다시 저장해 실행해 본다.

c:\Python\code>python plotlytest.py

High five! You successfuly sent some data to your account on plotly. View your plot in your browser at https://plot.ly/~계정이름/0 or inside your plot.ly account where it is named 'basic-scatter'

 

 

  실행이 잘 됬다고 나오면서, 브라우저 창에 https://plot.ly/~본인아이디/0/ url이 뜨면서, 로그인 창이 나온다. 로그인 탭을 눌러 아까 발급 받은 계정으로 로그인 한다.

 

 

  로그인이 되면 아래와 같이 그리려던 그래프가 웹 화면에 보이게 된다. 상단의 'export' 메뉴를 클릭하면 그래프를 이미지 파일로 저장하거나, 데이터를 문서로 만들거나, 다른 언어로(매트랩, R 등) 코드를 내보낼 수도 있다.

 

 

 

 

[마무리 하면서]

  설명은 엄청 장황했지만, 결론은 엄청 간단하다. 그래픽 라이브러리를 쓰는 방법은 아주 단순하다. 데이터들을 잘 모아서 numpy 배열 같은데에 담는다. 이후 plot 해 주는 라이브러리에 해당 배열을 전달해 주면 된다. 그리고 적당히 원하는데로 좌표 공간을 꾸미면 된다. 하지만 이 간단함에 숨어있는 어려움이 올바른 데이터를 수집하고, 그 수집한 데이터의 품질이 충분히 현실과 견주어 의미가 있어야 하고, 해당 특징을 잘 해석해 나타내 줄 적절한 그래프 함수를 선택하는 눈을 키우는 부분이다. 이것은 아마 다음 시간에 다룰 머신러닝에서의 파이썬 측면에서도 비슷한 문제가 될 것 같다.

 

 

 

[보충]

  검색을 하다보니 plotly 를 offline 모드로 하는 예제를 보았다. 이렇게 하면 위와 같은 인증 절차도 필요 없어 지는듯 싶다. (어쩐지 온라인 모드만 되는 툴이 저렇게 인기가 많나 했다;)

http://hamait.tistory.com/800

 

  plot 공식 사이트 쪽 설명은 아래에, 특별한 기능차이가 없다면 오프라인을 쓰는게 더 편할 듯 싶다. 온라인과 오프라인이 차이가 있는지는 명확하게 설명된 내용은 없는것 같다.

https://plot.ly/python/getting-started/

https://plot.ly/python/offline/

 

 

 

 

2017.5.28 by 자유로운설탕
cs

 

 

posted by 자유로운설탕
2017. 5. 6. 23:38 프로그래밍

  이번 시간의 주제는 향후 17교시에 '머신러닝에서의 파이썬의 역활'에 대한 얘기를 풀어보기 위한 사전 작업(밑밥...)이라고 볼수 있다. 파이썬에서 쓰는 여러가지 수학 라이브러리들을 어떤 관점에서 바라보는 것이 좋을지에 대해 설명 페이지와 간략한 샘플들을 통해 살펴보려고 한다.

 

 

[목차]

0. 왜 파이썬 공부에 구글을 이용하는게 좋은가?

1. 언어를 바라보는 방법. 파이썬을 어떻게 바라봐야 할까?

2. 파이썬 설치와 환경, 버전 선택 하기의 이유.

3. 만들고자 하는 기능을 모르는 조각으로 나눠 조사해 보기

4. 데이터 베이스에서 내용 가져와 출력하기

5. 암호화 모듈을 이용해 암복호화 해보기

6. 퍼즐 조각들을 합쳐보기

7. 엑셀 파일 사용해 보기 -> 부록 : fuction 을 이용해서, 코드 정리해 보기

8. 정규표현식을 왜 사용해야 할까? 언어속의 미니 언어 정규표현식 살펴보기

9. 입력과 결과를 GUI 화면과 연결해 보기

10. Whois API 이용해 보기

11. 웹페이지 호출해 내용 파싱 하기(BeautifulSoup 그리고 한계)

12. 자동화 - 웹 자동화(with Selenium)

13. 자동화 - 윈도우즈 GUI 자동화(with pywinauto)

14. 자동화 - 작업 자동화

15. 수학 라이브러리 살펴보기

16. 그래픽 라이브러리 살펴보기

17. 머신러닝에서의 파이썬의 역활

18. 웹 프로그래밍 - Legacy Web

19. 웹 프로그래밍 - Flask 살펴보기(feat. d3.js)

20. 웹 프로그래밍 - Django 살펴보기

21. 정리 - 이런저런 이야기

 

 

 

[들어가면서]

  머신러닝 관련 코드들을 보다 보면 numpy 나 pandas 같은 라이브러리(모듈)들이 단골로 등장 하게 된다. 대충 알기에는 해당 모듈들이 데이터들이 담긴 array 등을 처리해 주는 것으로 알고 있지만, 해당 라이브러리들이 도데체 어떤 기능들을 가지고 있길래 그렇게 자주 애용하게 되는걸까? 해당 코드들을 보면서 자주 쓰이는 기능들을 중심으로 이해하는 방법도 있겠지만, 실제 해당 라이브리러가 어떤 목적으로 만들어 졌고, 어떤 범위를 가지고 쓰이는지 전체적으로 살펴보는 것도, 그다진 나쁘진 않은 접근 방법이라고 생각하며 이야기를 시작한다.

 

  우선 파이썬에서 자주 쓰이는 수학 라이브러리들을 리스트업 하기 위해 구글에서 'python math libraries' 라고 검색한다. 아래의 페이지를 보면 2015년 8월 기준으로 파이썬 공식페이지에서 다운로드 수 기준으로 수학 라이브러리들의 순위를 매겼다.

http://www.palrad.com/top-python-math-statistics-libraries-w-12007/ 

 

  위로부터 7개 정도만 쭉 나열해 보면 NumPy, Pandas, Scipy, matplotlib, Patsy, Sympy, Plotly 이다. 이 중 matplotlib, Plotly 는 다음 시간에 살펴볼 그래픽 라이브러리라고 볼수 있고, Patsy 는 통계모델에 대한 라이브러리라는거 같아서 잘 모르니 패스하고, NumPy, Pandas, Scipy, Sympy 네 개의 라이브러리에 대해서 간단한 예제와 함께 살펴보려고 한다.

 

  실제로 'machine learning example' 또는 'tensorflow example' 등으로 구글에서 찾아서 아래의 이런 저런 페이지를 찾아 본 결과 그 이외의 특별한 라이브러리의 사용 예는 보이지 않았다.

http://machinelearningmastery.com/machine-learning-in-python-step-by-step/
https://www.toptal.com/machine-learning/machine-learning-theory-an-introductory-primer

https://github.com/aymericdamien/TensorFlow-Examples

 

 

 

 

[Numpy]

  어떻게 보면 파이썬을 이용하면서 가장 많이 보게 되는 수학 라이브러리 이다. 블로그의 예제들을 찾아보면서 쓰임을 알아 보는 것도 좋지만, 이렇게 유명한 라이브러리들은 설명 문서가 잘되어 있기 때문에, 해당 페이지를 전체적으로 훝어 보려고 한다. 그럼 구글에서 'numpy documentation' 라고 검색하면, 아래의 공식 문서 페이지가 나온다.

https://docs.scipy.org/doc/

 

  분위기를 보면 Numpy 와 Scipy 는 같은 목적에서 시작된 프로젝트 인거 같다. 'Numpy reference' 링크를 클릭한다.

https://docs.scipy.org/doc/numpy/reference/

 

  첫번째 'array objects' 설명을 보면(https://docs.scipy.org/doc/numpy/reference/arrays.html), 여러 차원의 array(수학적 의미로는 '행렬'이 적절할 듯 싶다)를 만들고, 임의의 부분만 짜르고, 특정 요소를 지정하고, 루프를 돌리고, array 차원을 재 배열 하고, 정렬하고 하는 등에 대한 설명들이 있다.

 

  두번째 'Universal functions' 설명을 보면(https://docs.scipy.org/doc/numpy/reference/ufuncs.html), 해당 array 를 수학적 연산에 의해 서로 사칙연산을 한다든지, 각각의 요소에 대해 로그 연산을 한다든지, 서로 비트 연산을 한다든지, 두개의 array 에서 최대 값들만을 뽑아 낸다든지 하는 여러가지 array 에 대한 연산들을 설명한다.

 

  세번째 'Routines' 에서는(https://docs.scipy.org/doc/numpy/reference/routines.html) 이제 상세한 부분으로 들어가 array 의 다양한 생성 방법이나, 합치거나, 자르는 등의 조작, 텍스트 및 스트링 등으로 읽거나 쓰기, 수학이나, 기본 통계함수 적용 등 상세한 사용시 필요한 세부 기능에 대해서 설명하는 듯 하다.

 

  그 이외는 다른 여러가지 기타 설명인걸 보니 결국 numpy 의 범위는 다양한 차원의 array 를 만들고, 해당 array 끼리 연산하거나, 각각의 array 요소들에 수학적 연산을 일괄 적용하거나, array 를 여러 다양한 차원의 형태로 자유롭게 변환하게 만들어 주는 라이브러리라고 봐도 무방할 듯 싶다. 그럼 간단한 샘플을 하나 만들어 보려고 한다.

 

 

  아래의 명령어로 numpy 를 설치 한다.

c:\Python\code>pip install numpy
Collecting numpy
  Downloading numpy-1.12.1-cp35-none-win_amd64.whl (7.7MB)
    100% |################################| 7.7MB 92kB/s
Installing collected packages: numpy
Successfully installed numpy-1.12.1

 

  해당 설명 페이지를 기반으로 샘플 기능을 만들어 보면 아래와 같다. 1) array 를 만들고, 2) 각 원소에 값을 더하거나, 3) array 를 재조정 하고 4) log 함수를 적용하는 등의 일을 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#-*- coding: utf-8 -*-
import numpy as np
 
# 2*2 array 를 만든다.
= np.array([[12], [34]])
 
# array 원소들에 모두 1을 더해 b array 를 만든다.
= a + 1
print (b)
 
# b array 를 재 정렬해 1*4 로 c array 로 변환한다.
= np.reshape(b, (1,4))
print (c)
 
# c array 원소들에 log 함수를 적용해 d array 를 만든다. 
= np.log(c)
print (d)
cs

 

  언제나처럼 c:\python\code 폴더에 numpytest.py 라고 저장하고, 아래와 같이 실행을 한다. (잘 모르시는 분은 2교시를 참조한다)

c:\Python\code>python numpytest.py
[[2 3]
 [4 5]]  -> 원래의 array 에 1을 더한 결과
[[2 3 4 5]]  -> 1*4 array 로 재 조정함.

[[ 0.69314718  1.09861229  1.38629436  1.60943791]]  -> 각 요소에 log 함수를 적용함.

 

 

 

 

[Scipy]

  아까 처음의 numpy documentation 페이지에서, 'scipy reference' 링크를 클릭한다.

https://docs.scipy.org/doc/scipy/reference/

 

  목차를 살펴보면 해당 라이브러리는 numpy 구조를 기반으로, 전문적인 과학분야의 수학연산들을 도와주는 라이브러리들이다. 적분(Intergration)이나, 영상이나 신호처리 영역에서 자주 쓰이는 푸리에 변환(fourier Transforms), 선형 대수(Linear Algebra), 통계학(Statistics) 등에서 필요한 수학적 연산들을 모아두었다고 보면 될거 같다. 해당 부분의 특징은 '구슬이 서말이라도 꿰어야 보배'라는 속담처럼 해당 분야에 대한 이해를 바탕으로 특정 연산을 수행하고 싶을 때야 의미가 있을 것 같다. 지금으로선 적분 관련된 예제 하나만 살짝 보고 넘어가려고 한다.

 

 

  우선 scipy 를 설치하는데 오랜만에 에러가 발생했다.

c:\Python\code>pip install scipy
Collecting scipy

...

numpy.distutils.system_info.NotFoundError: no lapack/blas resources found

 

  구글에서 'numpy.distutils.system_info.NotFoundError: no lapack/blas resources found' 라고 찾아서 스택오버플로우 페이지로 간다.

http://stackoverflow.com/questions/28190534/windows-scipy-install-no-lapack-blas-resources-found

 

  빌드 툴을 다운받거나 통합 패키지인 아나콘다를 다운 받으라는 등 여러가지 가이드가 있지만, 그 중에 wheel 파일이 있다고 그걸 가져다 설치해 보라는 내용이 눈에 띈다(예전 시간에 설명했지만, wheel 파일은 빌드된 바이너리가 zip 으로 압축된 형태의 파일이다.) 

 

  명시된 uci 대학교 홈페이지로 가서(http://www.lfd.uci.edu/~gohlke/pythonlibs/#scipy), 64비트이고, python 3.5 를 위해 빌드된 wheel 파일(scipy‑0.19.0‑cp35‑cp35m‑win_amd64.whl)을 다운받으려 하다보니, 아래와 같은 문구가 눈에 띈다. (사실은 해당 문구를 못보고 scipy 설치만 먼저하고 샘플 코드를 실행하니 에러가 나서 구글을 찾아서 다시 돌아 왔다--;). 기존 numpy 만을 설치하면 동작이 안되고 mkl 이라는 패키지와 통합된 numpy 를 설치해야 하나 보다

Install numpy+mkl before installing scipy.

 

  해당 링크로 가면 python 3.5 버전의 64비트 wheel 파일(numpy‑1.12.1+mkl‑cp35‑cp35m‑win_amd64.whl)이 보인다. 위의 scipy wheel 파일과 함께 두개의 파일을 다운하여 c:\python\code 폴더에 저장한다. 그리고 아래와 같이 명령어로 두개를 순서대로 설치한다. 

 

c:\Python\code>pip install numpy-1.12.1+mkl-cp35-cp35m-win_amd64.whl
Processing c:\python\code\numpy-1.12.1+mkl-cp35-cp35m-win_amd64.whl
Installing collected packages: numpy
Successfully installed numpy-1.12.1+mkl

 

c:\Python\code>pip install scipy-0.19.0-cp35-cp35m-win_amd64.whl
Processing c:\python\code\scipy-0.19.0-cp35-cp35m-win_amd64.whl
Successfully installed scipy-0.19.0

 

 

  위와 같은 y = x^2 인 함수에 대해서 0~1 구간을 적분하는 소스는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
#-*- coding: utf-8 -*-
from scipy.integrate import quad
 
# y=x^2 함수를 정의
def myfn(x):
    return x**2
 
# 0~1 사이에 해당 y 함수를 적분 한다.
ans, err = quad(myfn, 01)
print (ans)
cs

 

c:\python\code 폴더에 scipytest.py 라고 저장하고, 아래와 같이 실행을 하면 적분 결과를 볼수 있다.

c:\Python\code>python scipytest.py
0.5

 

 

 

 

[Sympy]

구글에서 'sympy documentation' 이라고 검색한다.

http://docs.sympy.org/latest/index.html

 

  해당 메뉴얼을 보면 보통 대수학이나 기하학에 대한 기능들이 많은데, symbolic(기호) 연산이 가능하다는 특징이 있다고 한다. 이것은 혹시 'mathematica' 라는 툴을 써본 경험이 있는 분은 알 것 같은데(전 개인적으로 좋아하는 툴이다), 마치 수학 공식을 사람이 푸는 것처럼 기호 형태로서 풀이하는 것을 얘기한다. 온라인에서 경험해 보자면, https://www.wolframalpha.com/ 페이지로 가서, 검색 란에 'y=x^2-x-6' 를 넣고 검색을 하면, 해당 함수의 그래프나, 근, 인수분해 결과 및 과정 등을 보여준다.

 

  해당 부분은 수식 계산에도 쓸수 있지만, 수학 공부 할때 병행해 이리저리 사용해도, 뭐 나쁘진 않아 보인다. 먼저 아래와 같이 설치해 본다.

c:\Python\code>pip install sympy
Collecting sympy
....
Successfully installed mpmath-0.19 sympy-1.0

 

 

  위의 wolframalpha 페이지에 넣었던 'y=x^2-x-6' 를 인수분해 하는 코드는 아래와 같다. n승 기호가 '^' 가 아닌 '**' 로 좀 다르다(이런 부분은 만드는 사람 맘대로니...)

1
2
3
4
5
6
7
8
9
#-*- coding: utf-8 -*-
from sympy import *
 
# x 를 심볼로 지정
= symbols('x')
 
# x^2-x-6 의 인수분해를 한다.
ans = factor(x**2 - x - 6)
print(ans)
cs

 

c:\python\code 폴더에 sympytest.py 라고 저장하고, 아래와 같이 실행을 하면 인수분해한 결과가 표시 된다.

c:\Python\code>python sympytest.py
(x - 3)*(x + 2)

 

 

 

 

[pandas]

마지막으로 팬더스 차례다. 마찬가지로 구글에 'pandas documentation' 라고 검색한다.

http://pandas.pydata.org/pandas-docs/stable/

 

  뭐 여기서도 여러가지 설명들이 있지만, 기본적인 기능을 보면 csv, json 등의 데이터를 url 등으로 부터 가져와서 dataframe 이라는 numpy 의 arrary 와 비슷한 저장 공간에 넣거나, 데이터의 기준이 되는 시간 데이터 등을 자동으로 생성해 주거나, 마치 DB 안의 데이터와 같이 join 이나, 정렬, 그룹핑 등이 가능하다. 실제 panda는 numpy 를 베이스로 만들어 졌다고 한다.

http://stackoverflow.com/questions/11077023/what-are-the-differences-between-pandas-and-numpyscipy-in-python 

 

  근데 SQL 문법을 아는 분은 마지막에 있는 'Comparison with SQL' 항목을 살펴보게 되면 pandas 가 무슨일을 할수 있는지를 대략적이겠지만 좀 더 쉽게 이해할 수 있게 되는 것 같다. (R을 아는 분은 R 하고의 비교 링크도 있다) 결국 개인적인 생각으로는 pandas 는 데이터 분석 작업을 위해 메모리상에 구현되 있는 엑셀과 비슷하다고 개념을 잡으면 어떨까 싶다. 엑셀의 여러 내장 함수를 조합해서 다중 배열이나, SQL 의 여러 기능들과 비슷한 작업을 할 수 있듯이, pandas 도 메모리 상에 데이터들을 정렬해 뿌려 놓고 엑셀을 조작하듯이, 해당 데이터 개개에 여러가지 numpy 와 같은 방식으로 연산을 가하거나, DB 에 저장한 데이터 같이 여러가지 조건에 의해서 분류하거나, 조합하거나, 필터링 하는것을 쉽게 해주는 라이브러리라고 생각하면 될듯 싶다.

 

 

  그럼 샘플 실행을 위해 pandas를 설치한다.

c:\Python\code>pip install pandas
Collecting pandas
...
Successfully installed pandas-0.20.1 python-dateutil-2.6.0 pytz-2017.2

 

  pandas 용 dataframe 을 하나 만들고, 그 중에서 SQL 의 where 조건을 적용한 것과 비슷하게 'A' 열이 'fruit' 인 데이터만 추출하는 예제의 코드는 아래와 같다.  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#-*- coding: utf-8 -*-
import pandas as pd
import numpy as np
 
# pandas 용 dataframe 을 만든다(메모리 버전 엑셀이라고 생각하자)
df = pd.DataFrame({'A''fruit drink cookie fruit'.split(),
                   'B''orange Coke chocopie mango'.split(),
                   'C': np.arange(4)})
 
# 만든 df 를 출력
print(df)
print('------------------------------')
 
# 뿌리면 아래와 같이 생김.
#      A         B      C
# 0  fruit    orange    0
# 1  drink    juice     1
# 2  cookie   chocopie  2
# 3  fruit     mango    3
 
# df 중에 A 열이 fruit 인 데이터만 추출
print(df.loc[df['A'== 'fruit'])
cs

 

c:\python\code 폴더에 pandatest.py 라고 저장하고, 아래와 같이 실행을 한다.

c:\Python\code>python pandatest.py
        A         B        C
0   fruit      orange    0
1   drink      Coke     1
2  cookie  chocopie   2
3   fruit     mango     3
------------------------------
       A       B      C
fruit  orange   0
fruit   mango  3

 

 

 

 

[마무리 하면서]

  위의 4개의 수학 모듈들을 살펴 보면서(사실 pandas 는 수학 모듈이라기 보단 범용 데이터 처리를 위한 공통 프레임 이라고 보는게 맞을 것 같지만...) 머신러닝이나 기타 분야에서 왜 numpy 나 pandas 같은 모듈들을 애용하는지에 대해서 공감을 가지게 됬음 하는 바램을 가진다. 참 이렇게 보면 파이썬은 여러 모듈들이 엃히고 설키면서 서로를 보조하며 시너지를 내어 더 인기가 많은 듯도 싶다. 이 시간을 진행함으로서 나중에 머신러닝에 대해서 얘기할때는 해당 코드에서 보이는 numpy 나 pandas 코드가 밥위의 콩처럼 분리되어 보여, 머신러닝 모듈 자체에만 집중 하게 되어, 좀 더 설명이 간략하게 될 거라고 기대를 하고 있다. 다음 시간에는 비슷한 목적으로 가공된 데이터를 좌표상에 표시해 주는 그래픽 라이브러리에 대해서 살펴보려고 한다.

 

 

 

 

2017.5.7 by 자유로운설탕
cs
posted by 자유로운설탕
2017. 4. 19. 20:34 프로그래밍

  이번 시간에는 작업 자동화라는 주제를 진행해 보려고 한다. 뭐 그다지 거창한건 아니라는걸 먼저 밝힌다. '작업' 이라는 것은 이전 시간에 얘기했던 웹이나, GUI 등 다른 자동화의 측면도 포함되는 주제지만, 좀더 단순하게 축소해서 윈도우 상에서 작업 하는 여러 자잘한 일들을 파이썬을 이용해서 쉽게 동작되게 만드는 과정이라고 정의해 보자.

 

  예제로는 하위 폴더를 가진 특정 폴더에서 특정한 확장자 들을 가진 파일 들만 zip 으로 압축하여, ftp 에 업로드 한후, 로컬에서 기간이 오래된 zip 파일을 삭제하는 작업을 순차적으로 진행하는 파이썬 프로그램을 만들려고 한다. 그리고 마지막으로 해당 작업을 주기적으로 실행 할때 사용할 수 있는 방법에 대해 살펴 보려 한다. 해당 기능을 구현할때 오직 파이썬 모듈들만을 이용해서 구현이 가능 할 수도 있겠지만, 여기서는 윈도우에서 지원하는 몇 가지 명령어와 무료 압축 프로그램인 7-zip 등을 이용해, 파이썬 코드에서 호출을 통한 구성을 해보려고 한다.

 

 

[목차]

0. 왜 파이썬 공부에 구글을 이용하는게 좋은가?

1. 언어를 바라보는 방법. 파이썬을 어떻게 바라봐야 할까?

2. 파이썬 설치와 환경, 버전 선택 하기의 이유.

3. 만들고자 하는 기능을 모르는 조각으로 나눠 조사해 보기

4. 데이터 베이스에서 내용 가져와 출력하기

5. 암호화 모듈을 이용해 암복호화 해보기

6. 퍼즐 조각들을 합쳐보기

7. 엑셀 파일 사용해 보기 -> 부록 : fuction 을 이용해서, 코드 정리해 보기

8. 정규표현식을 왜 사용해야 할까? 언어속의 미니 언어 정규표현식 살펴보기

9. 입력과 결과를 GUI 화면과 연결해 보기

10. Whois API 이용해 보기

11. 웹페이지 호출해 내용 파싱 하기(BeautifulSoup 그리고 한계)

12. 자동화 - 웹 자동화(with Selenium)

13. 자동화 - 윈도우즈 GUI 자동화(with pywinauto)

14. 자동화 - 작업 자동화

15. 수학 라이브러리 살펴보기

16. 그래픽 라이브러리 살펴보기

17. 머신러닝에서의 파이썬의 역활

18. 웹 프로그래밍 - Legacy Web

19. 웹 프로그래밍 - Flask 살펴보기(feat. d3.js)

20. 웹 프로그래밍 - Django 살펴보기

21. 정리 - 이런저런 이야기

 

 

 

[들어가면서]

  작업 자동화에 대해 어떻게 얘기를 풀어볼까 하다가 옛날 얘기로 시작해 보려 한다. 예전에 윈도우가 나오기 전에 도스란 cmd 타입의 OS 만 있을때의 컴퓨터는 지금 x-windows 를 사용안하는 리눅스 서버와 비슷한 모드로, 모든 작업이 까만 cmd 화면에서의 명령어 중심으로 이루어져 있었다(물론 그 안에서도 여러가지 트릭을 사용해서 그래픽으로 표시는 했었다). 도스 기반으로 컴퓨터를 처음 접했던 사람들은 아직도 윈도우, 맥 화면의 PC 나, 스마트 폰의 화려한 화면을 볼때도 왠지 뒷면에 놓여있는 cmd 창의 존재를 쉽게 떨쳐버리진 못할 것 이다.

 

  윈도우즈 또한 많은 부분이 GUI 로 감싸 이루어져 있어, 거의 cmd 창을 사용 안하더라도 가능한 부분들도 많지만, GUI 화면으로는 복잡하게 해야되는 일들을 좀더 간단하게 만들어주는 여러 유용한 명령어라든지, 서버 관리자용 확장쉘인 powershell 이라든지 하는 컴퓨터 관리에 도움을 주는 텍스트 기반의 툴들이 많이 내장되어 있다. 리눅스 또한 여러 쉘이나 유틸 기반의 유용한 툴들이 많으며, 어찌보면 GUI 로 구현된 화면들은 그러한 명령어 기능 중에 사람들이 자주 쓸 것 같은 기능들을 편하게 사용하도록 구현된거라고 봐도 무방할 듯 하다. 그래서 윈도우즈 10같이 점점 사용자들이 잘 안쓰는 디테일한 기능들을 디폴트로 숨기는 최신 OS 의 경우 이것저것 시스템을 건드리는 작업이 필요한 사람들 한테는 어떻게 보면 불편한 느낌까지도 주게되는 것 같다.

 

  뭐 여튼 그래서, 파일 관련 여러 작업들(복사, 압축, 백업..) 등의 기본적인 작업 들에 대해서는 대부분의 언어에서 지원하는 라이브러리보다 시스템에서 기본, 확장 지원 하는 기능들을 호출하여 사용하는 것이 좀더 효율 적인 경우도 있게 된다. 적당히 해당 명령어를 실행하고, 결과가 완료 되기만 기다릴 수 있다면, 어떤 언어를 사용하든 비슷한 효과를 가진 프로그램을 쉽게 만들 수 있게 될 것이다.

 

 

 

[무료 ftp 설치하기]

  그럼 실습을 위해서, 무료 ftp 를 하나 설치해서 운영해 보려 한다. ftp 는 'file transfer protocol' 의 약자로, 서버와 클라이언트 사이에서 서로 파일을 교환하기 위한 오래된 방식이다. 해당 방식을 이용해서, 클라이언트의 파일을 원격지에 있는 서버의 특정 폴더로 이동 등의 작업이 가능하다. 뭐 요즘으로 따지면 구글 드라이브 등의 클라우드, 웹 드라이브와 기능이  비슷하다고 봐도 무방할 듯 하다. 비슷한 용도로 쓰이는 개념으로는 공유 폴더 같은게 있다. 개인적으로 사용하고 있는 ftp 가 있다면 소스의 ftp 코드의 연결 부분만 바꾸어서 그 쪽에서 실습해도 된다.

 

 

[ftp 서버 설치]

  구글에서 'free ftp' 라고 검색하여, 파일질라를 사용하기 위해 https://filezilla-project.org/ 페이지로 이동한다. 'Download FileZiller Server' 버튼을 클릭하고, 다음 페이지에서 소스포지에서 'Download Now' 버튼을 클릭 한다.

 

  다운로드한 파일을 실행을 하여 디폴트로 설치를 진행 하다가, 테스트로 설치한 ftp 가 계속 실행 되고 있는게 좋을 건 없으니 시작 유형만 'Start Manually' 로 바꿔준다.

 

 

  설치 완료 후 '모든 프로그램' > 'FileZiller Server' > 'FileZiller Server Interface' 를 실행 한다. 아래의 다이얼로그가 나오면 'Connect' 버튼을 누른다. 그럼 서버가 실행 되게 된다.

 

  그럼 익명 사용자로 사용해도 되긴 하지만, 아무나 자기 서버에 들어와 사용하는건 그러므로, 사용자를 등록해 세팅해 보자. 그전에 우리가 매 시간 코드를 실습했던 c:\python\code 폴더에 아래와 같이 실습에 사용할 2개의 폴더를 만들어 보자

c:\python\code\ftproot

c:\python\code\ftproot\mybackup

 

  그 후 'Edit' > 'Users' 메뉴를 선택해 보자. 

 

  'Users' 창이 나오면 왼쪽에서 'General' 항목을 선택하고(기본이긴 하다), 오른쪽 'Users' 섹션에서 'Add' 버튼을 클릭한다. 이후 'Add user account' 다이얼로그가 뜨면, 'pyftpuser' 라고 사용자 이름을 넣고, 'OK' 버튼을 누른다.

 

   그리고 패스워드를 'test1234' 라고 적어 준다. 그럼 사용자의 id/pass 가 정해졌다.

 

  사용자를 생성하였으니 그 담에는 사용자에게 사용 가능한 특정 폴더를 할당해 주어야 한다. 사용자가 로그인 하였을때 해당 폴더가 디폴트로 보이며, 해당 폴더 안에서만 이런저런 작업이 가능하다(리눅스의 유저 기본 폴더와 같은 개념이다). 왼쪽에서 'Shared folders' 항목를 선택하고, 아래쪽 'add' 버튼을 눌러서 아까 만든 'c:\python\code\ftproot' 폴더를 선택한다. 이후 오른쪽의 체크 박스를 다 체크해서 해당 폴더에 대해서 풀 권한을 준다. 그리고 맨 하단의 'OK' 버튼을 눌러서, 'Users' 다이얼로그 창을 닫고 메인창으로 온다. 그럼 ftp 운영을 위한 설정이 모두 완료되게 된다.

 

 

[ftp 서버 동작 확인]

  그럼 파이썬 코드를 작성하기 전에, 세팅한 ftp 의 정상 동작을 확인해 보도록 하자(반드시 외부에서 쓰이는 프로그램들은 파이썬 코드를 만들기 전에 다른 외부 수단으로 기본 동작을 확인해 보는게 좋다. 그래야 파이썬 코드에서 연결 에러가 발생 시, 서버 세팅 문제가 아니라 코딩이나 모듈 잘못이라고 체크 하기가 쉬워 덜 헤메게 된다). 아까 파일질라 홈페이지에 있던 클라이언트를 설치해 확인해도 되지만, 그럼 또 클라이언트 사용법도 익혀야 되니, 간단히 하기 위해 브라우저를 하나 띄운 후 주소창에 ftp://localhost 라고 입력한다. 그럼 id/password 를 입력하라는 창이 뜨게 된다(시스템 환경에 따라 좀 느릴 수도 있다). 아까 설정한 pyftpuser/test1234 를 넣는다.

 

  그럼 ftp 에 연결되면서 아까 우리가 c:\python\code\ftproot 안에 만들어 놓은 'mybackup' 폴더가 보이게 된다. (/ 로 표시되는 사용자의 현재 폴더가 ftproot 폴더이다)

 

 

 

 

[7-zip 설치하기]

  그럼 마찬가지로 실습을 하기 필요한 7-zip 이라는 무료 zip 프로그램을 설치해 보자. 구글에서 '7zip' 이라고 검색하여, http://www.7-zip.org/ 로 이동한다.

 

  아래의 다운로드 링크에서 '64비트' 다운로드 링크(제 컴퓨터가 windows 10 home 64bit 라서 그렇다)를 클릭하여 설치한다. 설치 할때는 특별히 신경 쓰지 말고 디폴트 옵션으로 설치하면 된다.

 

 

 

[테스트 파일 준비하기]

  우리가 매일 실습하던 c:\python\code 폴더에 아래와 같이 실습에 사용할 4개의 폴더를 만들어 보자.

c:\python\code\source  (원본 폴더)

c:\python\code\source\subfolder   (원본폴더 서브 폴더)

c:\python\code\target   (복사할 폴더)

c:\python\code\zipfile    (압축 파일 떨굴 폴더)

 

 

  c:\python\code\source 폴더에 png 파일 한개와, txt 파일 한개, ini 파일 한개, exe 파일 한개를 넣어보자. 저 같은 경우 아래 같이 넣었다.

mypic.png
win.ini
winhlp32.exe
사고싶은 책.txt

 

  c:\python\code\source\subfolder 에는 txt 파일 한개를 넣어보자(원래 샘플 파일을 첨부 할까 하다가 왠지 사람들이 다운받기는 찜찜해 할 듯 싶어서 직접 만드는 것으로 했다). 아래와 같이 넣었다.

     사고싶은 책 2.txt

 

  png 파일은 캡쳐 도구 등으로 만들면 되고, txt 파일은 메모장 등으로 적당히 만들면 되고, ini, exe 파일은 c:\windows 폴더에서 복사(시스템 파일이니 옮기면 안된다 복사!!)해서 장만했다. 이렇게 여러 개의 확장자를 가진 파일을 만드는 이유는, 이 중 2개 정도의 확장자를 가진 파일만 선택적으로 압축하는 예제를 보여주고 싶어서이다. 실제 소스등을 백업하려다 보면 용량만 차지하고 백업은 필요없는 파일도 있기 때문에 제외하기 위해서 이다. 뭐 대충 아래와 같은 모양이다.

 

 

 

 

[파이썬 코드 만들기]

  이제 모든 밑작업이 완료 되었으니, 코드 작성을 시작해 보자. 우리가 만드려는 기능을 위해 현재 필요한 사항들을 아래와 같다.

  1) 특정 폴더에서 특정 확장자들의 파일만 서브 디렉토리 까지 포함해서 zip 으로 압축 하기

  2) 압축이 끝날때까지 기다리기

  3) 압축 된 내용을 ftp 에 올리기

  4) 7일 지난 오래된 zip 파일은 하드 용량을 줄이기 위해 삭제 하기

 

  모든 내용이 다 앞의 수업에서는 다루진 않았던 거니 하나하나 살펴보도록 하자.

 

 

[zip 만들기와 기다리기 구현]

  우선 zip 을 만드는데, 2가지 제약 사항이 있다. 첫째는 서브 폴더까지 포함되서 압축이 되야하며, 둘째는 특정 확장자 파일만을 선택 적으로 압축해야 한다. 구글에서 'python zip only extension' 라고 찾으면 아래의 python 에서 사용하는 zip 모듈이 나온다.

https://docs.python.org/2/library/zipfile.html 

 

  해당 모듈을 사용해서 특정 확장자를 포함하려 하면, 아래와 같은 스택 오버플로우에 나오는 방식처럼 특정 확장자를 하나 하나 이름에서 체크하면서, 압축 하거나 풀어야 할것 같다.

http://stackoverflow.com/questions/41965026/extracting-all-the-files-of-a-selected-extension-from-a-zipped-file

 

 

  좀더 간단하게 명령어 하나로만 완료해 보려고 7-zip을 이용하기 위해서, 다시 구글에서,  '7zip include extensions' 이라고 찾았다.

https://superuser.com/questions/409456/how-do-i-use-7-zip-to-add-a-folder-to-an-archive-including-only-files-with-cert

 

  그런데 위에 나타난 방법은 7-zip 명령어 하나만 사용해 구현할 수 있지만, 우리가 원하던 바와는 반대로 특정 확장자들을 포함 하는게 아니라, 특정 확장자들만 제외하는 것이다. 빼야하는 확장자를 잘 알고 압축해야 할 확장자를 잘 모를 경우엔 유용하지만, 반대로 압축해야 될 확장자를 잘알고, 빼야하는 확장자가 많거나 늘어날 수 있다면 조금은 귀찮은 일이다.(뭐 빼는 것도 나쁜건 아닌거 같지만 이렇게 가정해 보자^^) 

 

 

 

  일단은 원하는 확장자만를 압축하는 방식을 찾기 위해서 조금 더 검색을 해본다. 구글에서 '7zip only some extensions' 라고 검색해서 아래와 같이 2개의 페이지를 보다보면. cmd 에서 이용할 수 있는 for 명령어를 사용해서 특정 확장자를 가진 각 파일들을 하나씩 선택해서, zip 으로 만드는 예제가 있다. 그래서 왠지 구현이 잘 될거 같아서, 아래와 같이 만들어 실행해 보았다(몇 가지 테스트를 해보니 아래 페이지들에서 제시된 명령어 그대로 실행 하려면 bat 파일 안에서만 수행이 되며, cmd 창에서 바로 호출해 쓰려면 인자의 %% 를 %로 바꾸어야 한다)

https://superuser.com/questions/923775/7zip-batch-compression-for-a-specific-file-extension-in-different-folders

http://stackoverflow.com/questions/19848192/a-batch-script-to-zip-only-certain-types-of-files 

1
for /R C:/python/code/source %x in (*.txt *.png) do ("c:\Program Files\7-Zip\7z" a "myzip.zip" "%x" )
cs

 

  cmd 창에서 아래 명령을 입력한다.

c:\Python\code>for /R C:/python/code/source %x in (*.txt *.png) do ("c:\Program Files\7-Zip\7z" a "myzip.zip" "%x" )

c:\Python\code>("c:\Program Files\7-Zip\7z" a "myzip.zip" "C:\python\code\source\사고싶은 책.txt"  )

7-Zip [64] 16.04 : Copyright (c) 1999-2016 Igor Pavlov : 2016-10-04

Open archive: myzip.zip
......
Files read from disk: 1
Archive size: 9223 bytes (10 KiB)
Everything is Ok

 

  압축된 zip 파일을 보면 zip 명령어는 넘어온 파일들의 상대 경로를 따지지 않아서, 모든 파일이 하나의 폴더에 들어가 있다. 아래의 그림을 보면 alzip 으로 열었을때, 서브폴더의 파일도 정상적으로 가져오긴 하지만 원래는 source 폴더 안의 subfolder 에 들어가 있어야 할 '사고싶은 책 2.txt' 가 루트에 '사고싶은 책.txt'와 같이 저장되 있는게 보일 것이다. 그럼 해당 방식은 디렉토리 구조를 유지 못하고, 만약 이름이 같은 파일이 있을 경우 충돌이 날테므로 사용할 수 없다.

 

 

 

  그럼 명령어 하나로는 조금 힘들겠다고 생각하고, 2개로 나누어 실행하는 방식으로 다시 전략을 짜보자. 첫번째는 아까 만들어 놓은 target 폴더로 특정 확장자들을 가진 파일들만을 폴더 구조를 유지하면서 복사 후, 7-zip 을 이용하여 원하는 파일만 들어있는 target 폴더를 압축하면 된다. target 폴더의 파일과 디렉토리 들은 압축 완료 후 삭제하여 처음 상태로 리셋하면 된다. 만약 백업 대상 파일 용량이 크거나 숫자가 많아 복사가 부담스러운 경우엔 맨 처음에 찾아봤던 특정 확장자 들을 제외하는 방식으로 구현해도 좋겠지만, 그 경우는 zip 방식이 아닌 폴더 자체를 원격지 폴더와 싱크 시키는 다른 프로그램 등을 이용하는게 더 날지도 모르겠다.

 

  그럼 일단 특정 확장자들만 복사하는 명령어를 찾기위해 구글에서 이것저것 헤메다가 'xcopy copy only certain file extensions' 로 검색해, 앞에 보았던 for 명령을 이용해 파일을 xcopy 명령어로 넘겨서 복사하는 코드를 찾았다.

http://stackoverflow.com/questions/15753420/how-to-copy-specific-file-types-from-one-folder-into-another-folder 

1
for %x in (png txt) do xcopy "c:\python\code\source\*.%x" "c:\python\code\target\" /S /Y
cs

  위의 xcopy 옵션은 아래와 같다. 옵션이 궁금하면 cmd 창에서 'xcopy /?' 를 입력해 본다. 

 /S           비어 있지 않은 디렉터리와 하위 디렉터리를 복사합니다.

 /Y           기존 대상 파일을 덮어쓸지 여부를 묻지 않습니다.

 

 

  해당 명령어를 cmd 창에 입력하면 앞의 zip 과는 달리 다행히 정상적으로 서브 폴더까지 유지되면서 특정 확장자만 복사가 된다.

c:\Python\code>for %x in (png txt) do xcopy "c:\python\code\source\*.%x" "c:\python\code\target\" /S /Y

c:\Python\code>xcopy "c:\python\code\source\*.png" "c:\python\code\target\" /S /Y
C:\python\code\source\mypic.PNG
1개 파일이 복사되었습니다.

c:\Python\code>xcopy "c:\python\code\source\*.txt" "c:\python\code\target\" /S /Y
C:\python\code\source\사고싶은 책.txt
C:\python\code\source\subfolder\사고싶은 책 2.txt
2개 파일이 복사되었습니다.

 

 

  그럼 이제 target 폴더만 이제 전체 압축하면 된다. 7-zip 명령어를 사용하는데 이왕이면 'backup_현재날짜.zip' 으로 날짜를 포함하게 압축파일 이름을 지정하고 싶어서 구글에서 'cmd file name date' 로 검색해 아래 페이지와 참조해서 명령어를 만들어 낸다.

http://stackoverflow.com/questions/4984391/cmd-line-rename-file-with-date-and-time

1
"c:\Program Files\7-Zip\7z" a c:\python\code\zipfile\backup_"%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%".zip c:\python\code\target
cs

 

  위의 명령어를 cmd 창에 입력하면, 우리가 원하는 데로 정상적으로 압축되는 것을 볼 수 있다. 

c:\Python\code>"c:\Program Files\7-Zip\7z" a c:\python\code\zipfile\backup_"%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%".zip c:\python\code\target

7-Zip [64] 16.04 : Copyright (c) 1999-2016 Igor Pavlov : 2016-10-04

Open archive: c:\python\code\zipfile\backup_20170423.zip
...
Files read from disk: 3
Archive size: 9557 bytes (10 KiB)
Everything is Ok

 

 

  그럼 cmd 상으로 위의 명령어를 순차적으로 실행 시키면, 특정 확장자를 가진 파일들이 target 파일로 복사 된후, 'backup_현재날짜.zip' 로 압축되는 작업이 일어난다. 그럼 이젠 이용할 외부 로직은 다 해결 났으니, python 에서 해당 두 명령어를 순차적으로 실행해 주면서, 각각의 명령어가 끝날때까지 다음 코드의 실행을 안하고 기다리게 해야 한다. 구글에서 'python run windows cmd' 라고 검색하면 subprocess 모듈에 있는 'check_output' 명령어를 사용해 보라고 권한다. 근데 문제는 해당 subprocess 모듈로 cmd 명령을 실행 했을때, 실행한 명령이 끝날때까지 파이썬이 다음 코드를 실행 안하고 기다려 줄까 하는 부분이다.

http://stackoverflow.com/questions/14894993/running-windows-shell-commands-with-python

 

  해당 문제를 확인 하기 위해 'python subprocess check_output' 로 검색해 아래 파이썬 라이브러리 메뉴얼을 보면, 다행이 실행한 서브 프로세스가 끝날 때까지 기다린다고 한다(하나의 프로그램이 생성되면 윈도우 내부에 프로세스가 생성되고, 해당 프로세스가 다른 프로세스를 실행 했을때 해당 프로세스를 subprocess 또는 자식 프로세스라고 그런다). call 명령어는 성공 실패 코드만 반환해 가져오는 듯 하고, check_output 은 화면 출력등의 메시지도 받아 올수 있나 보다.

https://docs.python.org/2/library/subprocess.html

 

 

  그럼 첫번째 복사하는 cmd 명령어를 python 의 코드를 이용해서 호출해 동작하는지 검증해 보자.

1
2
3
4
import subprocess 
from subprocess import check_output
 
check_output('for %x in (png txt) do xcopy "c:\python\code\source\*.%x" "c:\python\code\target\" /S /Y', shell=True) 
cs

 

  위의 코드를 복사하여 'c:\python\code' 폴더에 'copytest.py' 라는 이름으로 'uft-8' 포맷으로 저장한다. 실행을 하면 계속 무한 루프가 나면서 이상하게 종료가 안된다. 'ctrl+z' 로 종료를 해본다.

c:\Python\code>python copytest.py

 

  흠 cmd 창으로 그대로 잘 실행됬던 명령어를 그대로 파이썬에서 실행했는데 에러가 나는 듯 하다. 왜 그런가 하고 상상의 나래를 펼치다가 보니 뭔가 하나 걸리는 게 있긴 하다. escape 문자 이다. escape 문자는 보통 어떤 언어에서든 문법적 요소로 쓰는 문자를 사용자의 문자열 안에 넣고 싶을때 해당 문자 앞에 적어주는 회피 문자 이다. 그 회피 문자를 보고 해당 언어는 이 문자가 문법 문자가 아닌 일반 표시 문자라는 것을 인식한다. 구글에서 'python string Double Quotation' 로 검색하면, 아래의 페이지에 escape 문자를 쓰는 방법이 나온다. 예를 들어 " 문자는 \" 로 넣어주어야 하는거다. (한국어 키보드 \(금액 표시) 문자가 밑의 예제 코드에 나오는 역 슬레쉬 이다). escape 문자는 '\n' 같이 키보드로 표시 못하는 기호를 표시할때도 쓴다.

https://docs.python.org/2.0/ref/strings.html

 

  이것 저것 조정해 보다보니 아래 코드로 정리됬다.

1
2
3
4
import subprocess 
from subprocess import check_output
 
check_output('for %x in (png txt) do xcopy \"c:\\python\\code\\source\\*.%x\" \"c:\\python\\code\\target\\\" /S /Y', shell=True) 
cs

 

  다시 실행해 보니 정상적으로 복사가 된다. 그럼 압축 하는 코드 까지 같이 적용해 만들어 보면 아래의 코드가 된다.

1
2
3
4
5
6
import subprocess 
from subprocess import check_output
 
check_output('for %x in (png txt) do xcopy \"c:\\python\\code\\source\\*.%x\" \"c:\\python\\code\\target\\\" /S /Y', shell=True) 
 
check_output('\"c:\\Program Files\\7-Zip\\7z\" a c:\\python\\code\\zipfile\\backup_\"%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%\".zip c:\\python\\code\\target', shell=True)
cs

 

  아래와 같이 실행 하면, cc:\Python\code\zipfile\ 폴더안에 backup_20170423.zip 파일이 생성되게 된다(날짜는 실행한 날짜별로 달라진다).

c:\Python\code>python copytest.py

 

 

 

[ftp 업로드 코드 만들기]

  그럼 만들어진 파일을 ftp 로 업로드하는 코드를 만들어 보려 한다. 구글에서 'python ftp upload' 로 검색을 하면, 아래의 스택오버플로우 페이지에서 바이너리와, 텍스트 형태의 파일을 각각 저장하는 ftp 샘플이 보인다.

http://stackoverflow.com/questions/17438096/ftp-upload-files-python

 

  추가로 만들어진 zip 파일 이름이 'backup_yyyymm' 형식이기 때문에 구글에서 'python filename date' 라고 검색하여 아래 페이지에서 datetime 모듈을 통해 날짜로 이름을 만드는 코드를 얻을 수 있다.

http://stackoverflow.com/questions/10607688/how-to-create-a-file-name-with-the-current-date-time-in-python

 

  두 개의 코드를 합쳐서 아까의 만들어진 zip 파일이 올라가는 코드를 만들어 보면 아래와 같다. 이미 zip 파일은 만들어져 있을테니 위의 압축하는 코드는 일단 제외하고 ftp 코드 부분만 만들어 검증해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#-*- coding: utf-8 -*-
import ftplib
import os
from datetime import datetime
 
# datatime 모듈을 사용하여 오늘의 압축 파일 이름을 생성
filename = "backup_" + datetime.now().strftime("%Y%m%d"+ ".zip"
 
# ftp에 연결
ftp = ftplib.FTP("127.0.0.1")
ftp.login("pyftpuser""test1234")
 
# ftp 에서 myback 폴더로 이동함
ftp.cwd("/mybackup")
 
# zip 파일이 있는 폴더로 이동
os.chdir(r"c:\python\code\zipfile")
 
# 바이너리 형태로 파일을 업로드 함
ftp.storbinary("STOR " + filename, open(filename, 'rb'))
 
print ('upload completed')
cs

 

  위의 코드를 'c:\python\code' 폴더에 'utf-8' 인코딩으로 'ftpuplodtest.py' 라고 저장 후 실행해 본다.

c:\Python\code>python ftpuplodtest.py
upload completed

 

  위와 같이 업로드가 완료되었다고 출력되며, ftp 로 로그인 시 사용자에게 할당된 폴더인, 'c:\Python\code\ftproot\mybackup' 폴더로 가보면 backup_20170423.zip 파일이 업로드되어 있다(하나의 컴퓨터에서 ftp 서버를 같이 실행해서, 로컬 경로에서 확인을 하긴 했지만, 실제 프로그램 입장에서는 원격에 있는 ftp 폴더에 업로드 된 것이다)

 

 

[임시파일 삭제와 오래된 zip 파일 삭제]

  target 폴더안에 복사된 폴더와 파일들이 압축 후에는 필요 없기 때문에, 모두 삭제 하기 위해서 구글에서 'delete file and directory in directory' 를 찾았는데, powershell 명령어 이외에는 파일만 지우거나, 폴더 자체를 삭제 하는거나 둘 중에 하나만 가능한 것 같다. 밑에 제시된 바와같이 target 폴더를 지우고 같은 이름의 빈 폴더를 다시 생성하는 방법으로 구현하려 한다(파일 삭제를 테스트 할때는 조심히 테스트 폴더를 만들어 그 안에서만 명령어를 사용하는게 좋다. 저도 code 폴더에서 이것저것 시도해 보다가, 만들어 놨던 샘플 파일들을 싹 지워 버려 블로그에서 주섬주섬 복원해야 했다 --;). 리눅스 rm 명령 처럼 사용법이 모호한 상태에서 잘못 날리게 되면 시스템 파일과 같은 중요한 파일들을 다 지워 버릴 수도 있다.)

1
2
3
4
5
6
7
8
9
#-*- coding: utf-8 -*-
import subprocess 
from subprocess import check_output
 
# target 폴더를 통채로 삭제
check_output('rd /s /q c:\\python\\code\\target', shell=True) 
 
# target 폴더 다시 생성
check_output('md c:\\python\\code\\target', shell=True)
cs

 

  위와 비슷하게 c:\python\code 폴더에 utf-8 인코딩으로 deltest.py 라고 저장해 실행하면 target 폴더 전체를 삭제 된 후, 같은 이름의 빈 폴더를 생성한 것을 볼수 있다.

c:\Python\code>python deltest.py

 

 

  마지막으로 오래된 zip 파일들을 삭제하는 코드이다. 여기서는 7일이 지난 파일을 삭제한다고 해보자. 구글에서 'cmd delete file older than' 이라고 찾으면 아래 스택오버플로우 페이지가 나온다. forfiles 라는 명령어를 통해 7일 이내의 파일을 찾아 각각의 파일에 대해서 del 로 삭제 하는 코드는 아래와 같다. (비슷하게 escape 처리를 했는데, " 문자는 ' 안에 들어 있을때는 굳이 escape 처리를 안해도 되는듯 해서 가독성을 위해 제외했다).

http://stackoverflow.com/questions/51054/batch-file-to-delete-files-older-than-n-days

1
subprocess.call('forfiles /p "c:\\python\\code\\zip " /s /m backup*.zip /d -7 /c "cmd /c del @path"', shell=True)
cs

 

  'check_output' 대신 'call' 을 사용한 이유는 1주일 이상된 파일이 없어 forfiles 결과가 없을 경우엔 에러가 나버리는데, 해당 에러를 출력하는 부분이 파이썬 코드에 영향을 주어서 아래와 같은 오류가 나서, 이것저것 고민하다가 call 은 에러가 나도 실행에 관계가 없어서 대체 했다. 
오류: 검색 조건에 해당되는 파일을 찾을 수 없습니다.
Traceback (most recent call last):
  File "deltest.py", line 7, in <module>

 

 

[최종 코드]

그럼 긴 헤멤을 거쳐서 만들어진 최종 코드는 아래와 같다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#-*- coding: utf-8 -*-
import ftplib
import os
from datetime import datetime
import subprocess 
from subprocess import check_output
 
# 압축 코드
# png, txt 파일만 source 폴더에서 target 폴더로 복사한다. 
check_output('for %x in (png txt) do xcopy \"c:\\python\\code\\source\\*.%x\" \"c:\\python\\code\\target\\\" /S /Y', shell=True) 
 
# 7-zip 을 이용해 backcup_yyyymmdd 형식으로 압축을 한다.
check_output('\"c:\\Program Files\\7-Zip\\7z\" a c:\\python\\code\\zipfile\\backup_\"%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%\".zip c:\\python\\code\\target', shell=True)
 
print ('zip completed')
 
 
# ftp 코드 
# datatime 모듈을 사용하여 오늘의 압축 파일 이름을 생성
filename = "backup_" + datetime.now().strftime("%Y%m%d"+ ".zip"
 
# ftp에 연결
ftp = ftplib.FTP("127.0.0.1")
ftp.login("pyftpuser""test1234")
 
# ftp 에서 myback 폴더로 이동함
ftp.cwd("/mybackup")
 
# zip 파일이 있는 폴더로 이동
os.chdir(r"c:\python\code\zipfile")
 
# 바이너리 형태로 파일을 업로드 함
ftp.storbinary("STOR " + filename, open(filename, 'rb'))
 
print ('ftp upload completed')
 
 
# 삭제 코드
# target 폴더를 지움
check_output('rd /s /q c:\\python\\code\\target', shell=True) 
 
# taget 폴더를 재생성
check_output('md c:\\python\\code\\target', shell=True)
 
# 7일 이상 된 로컬의 backup 일을 삭제 함
subprocess.call('forfiles /p "c:\\python\\code\\zipfile " /s /m backup*.zip /d -7 /c "cmd /c del @path"', shell=True)
 
print ('del completed')
 
cs

 

  해당 내용을 'c:\python\code' 폴더에 'utf-8' 인코딩으로 'fileftpbackup.py' 파일로 저장 후 아래와 같이 실행해 본다.

c:\Python\code>python fileftpbackup.py
zip completed
ftp upload completed
오류: 검색 조건에 해당되는 파일을 찾을 수 없습니다.
del completed

 

 

[해당 기능을 주기적으로 돌리기]

  해당 기능을 주기적으로 돌리는 부분도 반복 주기가 짧다면 파이썬의 timer 모듈 등을 사용하여 구현할 수도 있겠지만, 하루에 한 번이라든지 몇 시간에 한번씩 돌릴 예정이라면, 파이썬을 계속 실행시켜 놓는 것보단, 윈도우즈 경우 작업 스케줄러에 걸어놓음 컴퓨터가 재시작 해도, 알아서 지정된 시간이나, 간격으로 돌아가기 때문에 딱히 신경 안 쓰고 반복적으로 실행할 수 있다. 내용이 넘 길어 졌으니 해당 내용은 구글에서 '윈도우즈 10 작업 스케줄러' 로 찾아서 아래와 같은 블로그를 보면 된다. 실행할 프로그램은 'python c:\python\code\fileftpbackup.py' 식으로 등록하면 될듯 하다.

http://blog.naver.com/PostView.nhn?blogId=celine2011&logNo=220673965249&beginTime=0&jumpingVid=&from=search&redirect=Log&widgetTypeCall=true

 

  참고로 만약 실행시 콘솔이 뜨는게 싫다면 아래 안내된 바와 같이,

소스 확장자를 pyw 라고 바꾸어 실행만 하면, pythonw.exe 가 실행이 되면서 콘솔 창이 없는 사일런스 모드로 실행 되는 듯하다.

http://stackoverflow.com/questions/764631/how-to-hide-console-window-in-python

 

 

 

[마무리 하면서]

  어찌 보면 그다지 복잡하지 않은 기능을 꼬아서 복잡하게 설명한 것 같기도 하다 --; 요지는 작업 자동화를 할때 꼭 해당 언어의 스콥 안에서 작업할 필요는 없다는 것이다. 그러다 보면 오히려 외부 cmd 방식의 명령어를 이용하면 좀더 쉽게 구현할 수 있는 코드를 복잡하게 구현하게 되면서 시간이 낭비될 수도 있다. 특히 파일이나 디렉토리 등을 기반으로 하는 작업들은 for 나 forfiles 같이 일괄로 작업하는 명령어 들이나 powershell 기반의 명령어들이 있으니, 가능한 필요한 기능을 구글에 검색해서 가져다 쓰는 것도 좋은 것 같다. 특별한 사정이 없는 이상 내부 모듈이 있다고 꼭 그 모듈을 쓸 필요는 없다고 생각한다. 여튼 이렇게 해서 자동화 시리즈를 마무리 하고, 다음 시간에는 파이썬으로 접근해 보는 머신러닝에 발을 살짝 담궈보기 위한 사전 작업으로 파이썬의 몇몇 수학 라이브러리들이 하는 일들을 살펴보려고 한다.

 

 

[보충]

  최근에 내용을 다시 리뷰하다가, 7zip 으로 여러확장자의 파일이 디렉토리 구조까지 유지하면서 압축 가능하다는 부분을 알게 되었습니다(어쩐지 그런 명령어가 없는게 이상하긴 했습니다--;)

 

 왜 글 쓰는 시점에서 놓쳤는지 모르겠지만, 아래 링크를 보시면 있습니다.

https://stackoverflow.com/questions/28636349/7zip-cli-whitelist-files-to-add-by-extension

 

이 글의 샘플을 예로들면 아래와 같습니다. 삽질을 했네요;

c:\"Program Files"\7-Zip\7z a -r -tzip test.zip c:\python\code\source\*.txt c:\python\code\source\*.jpg

 

 

2017.4.23 by 자유로운설탕
cs
posted by 자유로운설탕
2017. 4. 7. 23:30 프로그래밍

  이번 시간은 Windows GUI 자동화를 살펴보는 시간이다. 만들어 보려는 프로그램은 1) 메모장을 열어서, 2) 작은 python 소스를 입력 후, 3) 콤보 박스에서 인코딩과, 파일 형식을 선택하고 특정 폴더에 저장을 하는 프로그램이다. 이후 이전 시간에 잠시 언급했던 상용 GUI 자동화 툴인 unified functional testing 으로 구현된 코드와 비교해 보며, 오픈소스 모듈과 상용 모듈의 차이점에 대해서 얘기하려 한다.

 

 

[목차]

0. 왜 파이썬 공부에 구글을 이용하는게 좋은가?

1. 언어를 바라보는 방법. 파이썬을 어떻게 바라봐야 할까?

2. 파이썬 설치와 환경, 버전 선택 하기의 이유.

3. 만들고자 하는 기능을 모르는 조각으로 나눠 조사해 보기

4. 데이터 베이스에서 내용 가져와 출력하기

5. 암호화 모듈을 이용해 암복호화 해보기

6. 퍼즐 조각들을 합쳐보기

7. 엑셀 파일 사용해 보기 -> 부록 : fuction 을 이용해서, 코드 정리해 보기

8. 정규표현식을 왜 사용해야 할까? 언어속의 미니 언어 정규표현식 살펴보기

9. 입력과 결과를 GUI 화면과 연결해 보기

10. Whois API 이용해 보기

11. 웹페이지 호출해 내용 파싱 하기(BeautifulSoup 그리고 한계)

12. 자동화 - 웹 자동화(with Selenium)

13. 자동화 - 윈도우즈 GUI 자동화(with pywinauto)

14. 자동화 - 작업 자동화

15. 수학 라이브러리 살펴보기

16. 그래픽 라이브러리 살펴보기

17. 머신러닝에서의 파이썬의 역활

18. 웹 프로그래밍 - Legacy Web

19. 웹 프로그래밍 - Flask 살펴보기(feat. d3.js)

20. 웹 프로그래밍 - Django 살펴보기

21. 정리 - 이런저런 이야기

 

 

 

[들어가면서]

  굳이 파이썬 자동화 공부를 하면서 상용 자동화 툴을 언급하는 이유는, 첫째는 파이썬 언어 또는 연관된 selenium,  beaulifulsoup 같은 모듈들을 공부하는 이유가 효율적인 프로그래밍적 사고 및 구현 방식을 찾기 위한 것이라고 생각하기 때문이다. 그런 측면에서 향후 더 효율적인 언어나 개선된 모듈들이 나왔을 때, 현재 언어의 선입견에 갇혀서 고정된 관점에서 새로운 것들을 바라보는 것보다, 현재 사용하고 있는 언어의 장단점에 대해 객관적인 생각을 가지고 있는게 맞을 것 같기 때문이다. 둘째로는 뒤에서 보면 알겠지만, 상용툴은 사람들이 제품을 구입 하도록 어필할 수 있는 부분들이 있어야 하기 때문에, 오픈소스의 경우 수동으로 구축을 해야되는 설계, 구조적 측면 이나, 초창기 버전 들에서 흔히 간과되는 사용성 및 유지보수에 관련된 기능들이 제품 안에 기본으로 포함되어 있는 경우가 많다. 하지만 역으로 그러한 사용자를 돕는 기능들이 툴을 사용하는 프로세스를 고정시켜 버려서, 사용 범위의 제한을 가져오는 독이 될 수도 있다.

 

 

  앞의 selenuim 이나 beautifulsoup 을 보면 해당 모듈이 웹 페이지를 인식 할때, 태그(element)나 속성(attribute), css selector, xpath(이전 시간의 예제에는 사용을 안했지만, selenium은 xpath 를 지원한다. beautifulsoup 은 지원 안 하는 거 같지만, 최근 크롤링에서 많이 사용이 된다고 하는 scrapy 란 모듈도 xpath 를 지원한다). xpath 에 대해선 잘은 모르지만, xml 도큐먼트의 요소들을 정의할 때 사용된다고 하며, 왠지 정규 표현식 같은 스타일이여서, 문법에 익숙해지면 꽤 효율적일 것 같다. xpath 에 대한 상세 내용은 아래 링크를 참고한다.

  https://www.w3schools.com/xml/xpath_syntax.asp

 

 

  비슷한 맥락에서 gui 자동화 모듈은 원하는 개체(윈도우, 메뉴, 버튼, 리스트 박스 등)를 선택하고 조작하기 위해서, 위의 web 자동화의 element, attiribute 와 비슷한 기준 요소가 필요하게 되며, 그런 부분이 class 라든지 text 라든지, 좌표(position) 라든지 하는 속성들이다. 밑의 visual studio 로 gui 프로그램을 작성하는 화면을 보면, 폼 안에 위치한 버튼을 선택 했을때, 오른쪽 properies 창에서 해당되는 버튼 내의 text 등 버튼을 정의하는 많은 속성(property)들을 볼 수 있다. 모든 윈도우즈 gui 프로그램은 이 속성들을 기준으로 개체를 식별하고 메시지들을 교환한다. 이 부분이 바로 오늘 진행하는 내용의 핵심이다.

 

  그럼 앞에서 web 자동화를 구현하는데는 관련된 웹 기술들을 잘 알아야 유리하다고 얘기했듯이, gui 자동화의 구현에는 windows(또는 x-windows 든지 osx 든지) 및 그 환경에서 돌아가는 gui 프로그램 들의 구조에 대해서 잘 아는 것이 유리하다(하지만 잘 알게 되는게 쉬운일은 아니다 --; 이 부분은 저도 초보이다). 

 

 

 

 

 

[GUI 코드 구현]

  그럼 본격적으로 코드 구현으로 들어가 원하는 기능을 만들기 위해 필요한 부분을 생각해 보자.

1) 어떤 자동화 모듈을 사용해야 되는지 결정해야 한다.

2) 메모장을 띄울 수 있어야 한다.

3) 메뉴장의 메뉴를 선택하거나, 글을 입력할 수 있어야 한다.

4) 저장 다이얼 로그에서, 경로를 지정하고, 인코딩 콤보 박스와, 확장자 콤보 박스에서 원하는 항목을 선택하고, 파일 이름을 넣은 후, 저장 버튼을 누르는 작업을 할 수 있어야 한다.

 

 

 

[메모장 실행과 메뉴 선택]

  우선 메모장을 조작할 수 있는 적절한 모듈을 찾기 위해서 구글에 'windows gui automation python notepad' 라고 검색한다.

  https://pywinauto.github.io/

 

  위의 'pywinauto' 라는 모듈의 홈페이지를 보면 원하는 코드가 다 있는 건 아니지만, 기본적으로 메모장을 실행하고, '도움말 > 메모장 정보' 메뉴를 선택해서 창을 띄운 후 닫고, 키를 입력하는 코드가 들어있다. 근데 소스 내용을 보니 영문 윈도우 기준 코드인거 같아서, 한글 윈도우에선 잘 돌아가려는지 확신이 안 든다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
from pywinauto.application import Application
 
# Run a target application
app = Application().start("notepad.exe")
 
# Select a menu item
app.UntitledNotepad.menu_select("Help->About Notepad")
 
# Click on a button
app.AboutNotepad.OK.click()
 
# Type a text string
app.UntitledNotepad.Edit.type_keys("pywinauto Works!", with_spaces = True)
cs

 

  뭐 일단 좀더 정보를 수집하기 위해서 홈페이지 오른쪽에 있는 'Documentation' 링크를 눌러 보자.

  https://pywinauto.readthedocs.io/en/latest/index.html

 

  비교적 도움말이 잘되어 있다, 천천히 살펴보고 싶을 경우는, 맨 위의 'What is pywinauto' 부터 'Methods available to each different control type' 까지의 5개 정도의 설명들을 살펴 보면 될것 같다. 마지막 'Methods available to each different control type' 부분은 menu 나 textbox 등의 GUI 컨트롤 들을 어떻게 다룰수 있는지 설명하는 전반적인 레퍼런스 이다.

 

 

  도움말 첫 페이지의 내용을 보다보면 눈에 띄는 내용이 하나 있는데, 여러가지 오픈소스와 무료 및 상용 자동화 툴을 소개한 리스트가 있다. 몇 가지를 살펴 봤는데, 먼저 python 에서 쓸수 있는 winguiauto,  pyautogui 라는 툴은 메뉴얼이 상세하지 않은거 같아서 제외했고, 파이썬 모듈은 아니지만 독립적으로 돌아가는 무료 어플리케이션인 Autoit 은 윈도우즈 gui 자동화에서 유명하지만, 2015년 9월이후 더 이상 업데이트가 안되고 있어, win 7과 win 10의 지원이 명시되어 있지 않다(혹시 돌리면 돌아가는 지는 잘 모르겠지만 말이다...). 또 활발하게 버전업 되고 있는 다른 무료툴인 autohotkey 는 gui 컨트롤의 자동화 보다는 키보드와 마우스 동작 중심의 macro 프로그램(상용 프로그램으로 따지면 macro express 정도의 포지션)에 좀 더 가까운것 같다. 또 상용 gui 자동화 툴인 Winrunner 는 나중에 언급할 unified functional testing 의 과거 이름이다. silktest 는 예전엔 무척 독특하게 좋다고 생각 했었지만, 그 독특함 때문에 범용적인 winrunner 에게 시장을 많이 뺐긴 후, 오픈소스 와의 통합으로 방향을 틀었었는데 지금은 어찌 되고 있는지 잘 모르겠다. 웹이나 GUI 자동화 자체에 관심있는 분들은 해당 링크의 툴들을 찬찬히 살펴보면 괜찮을 듯 싶다.

 

 

  그럼 우선 도움말에 명시된 pip 명령어를 이용해 pywinauto 모듈을 설치해보자. 아래와 같이 명령어를 입력하면 잘 설치가 된다.

c:\Python\code>pip install pywinauto
Collecting pywinauto
  Running setup.py install for pywinauto ... done
Successfully installed pywinauto-0.6.2

 

 

  그럼 위의 샘플 소스 내용(좀 많이 위가 됬다)을 긁어서, c:\python\code 폴더에 utf-8 인코딩으로 notepad1.py 라고 저장 후, 실행해 보자(실행 부분을 잘 모르겠으면 2교시에서 복습을...)

c:\Python\code>python notepad1.py
Traceback (most recent call last):
  File "notepad1.py", line 7, in <module>
    app.UntitledNotepad.menu_select("Help->About Notepad")

.......
pywinauto.findbestmatch.MatchError: Could not find 'Help' in 'dict_keys(['서식(&O)', '보기(&V)', '도움말(&H)', '편집(&E)', '파일(&F)'])'

 

  그런데 메모장이 실행되긴 하지만, 위의 에러가 난다. 에러가 난 부분을 살펴보면 'Help->About Notepad' 메뉴를 선택하면서 에러가 난 것 이다. 위에서 보면 'menu_select' 를 실행하다 에러가 났고, 메뉴를 찾으려 하는데 'Help' 란 메뉴는 없고 자신이 알고 있는 상단 메뉴들은 (['서식(&O)', '보기(&V)', '도움말(&H)', '편집(&E)', '파일(&F)'] 밖에 없다고 한다. 대충 유추해 보면 'Help' 라는 부분을 pywinauto 가 인지하고 있는 '도움말(&H)' 로 바꿈 될거 같다. 그럼 소스를 수정해 보자(장황하겠지만 우선은 step by step 으로 진행한다--;)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#-*- coding: utf-8 -*-
from pywinauto.application import Application
 
# 메모장를 띄운다.
app = Application().start("notepad.exe")
 
# '도움말' > '메모장 정보' 메뉴를 선택한다.
app.UntitledNotepad.menu_select("도움말(&H)->About Notepad")
 
# '확인' 버튼을 눌러서 다이얼로그를 닫는다.
app.AboutNotepad.OK.click()
 
# 메모장에 내용을 적는다.
app.UntitledNotepad.Edit.type_keys("pywinauto Works!", with_spaces = True)
cs

 

  저장 후 다시 실행해 본다.

c:\Python\code>python notepad1.py
Traceback (most recent call last):
  File "notepad1.py", line 8, in <module>
    app.UntitledNotepad.menu_select("도움말(&H)->About Notepad")
...

pywinauto.findbestmatch.MatchError: Could not find 'About Notepad' in 'dict_keys(['', '도움말 보기(&H)', '메모장 정보(&A)'])'

 

 

  위를 보면 앞에 난 에러와 비슷하게 'About Notepad' 부분에서 에러가 발생했다. 그럼 한번 해본거니 비슷하게 대응되는 한글 부분으로 수정한다. 이후 저장 후 다시 실행 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#-*- coding: utf-8 -*-
from pywinauto.application import Application
 
# 메모장를 띄운다.
app = Application().start("notepad.exe")
 
# '도움말' > '메모장 정보' 메뉴를 선택한다.
app.UntitledNotepad.menu_select("도움말(&H)->메모장 정보(&A)")
 
# '확인' 버튼을 눌러서 다이얼로그를 닫는다.
app.AboutNotepad.OK.click()
 
# 메모장에 내용을 적는다.
app.UntitledNotepad.Edit.type_keys("pywinauto Works!", with_spaces = True)
cs

c:\Python\code>python notepad1.py
Traceback (most recent call last):
...
pywinauto.findbestmatch.MatchError: Could not find 'OK' in 'dict_keys(['', 'Edit'])'

 

  그런데 또 에러가 난다. 이번엔 버튼을 클릭하는 부분이다(근데 슬슬 익숙해 지지 않는가?). 이번엔 힌트도 잘 표시되지 않지만, 이 경우는 실제 실행된 메모장 화면을 참고하면 된다.

 

  위의 화면을 보면 위쪽 타이틀엔 '메모장 정보' 가 아래쪽 버튼엔 '확인' 이라는 텍스트 들어가 있다. 여기 까지 오게 되면 메뉴얼을 찬찬하게 보진 않았지만, 조금은 pywinauto 가 gui 개체를 인식하는 방식을 알수 있을 것도 같다. 메뉴는 메뉴의 이름으로(단축키 기호 포함) 접근하고, 창은 타이틀로, 버튼은 버튼에 쓰여 있는 텍스트로 접근하나 보다. 최종으로 샘플 파일을 수정해 본다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#-*- coding: utf-8 -*-
from pywinauto.application import Application
 
# 메모장를 띄운다.
app = Application().start("notepad.exe")
 
# '도움말' > '메모장 정보' 메뉴를 선택한다.
app.UntitledNotepad.menu_select("도움말(&H)->메모장 정보(&A)")
 
# '확인' 버튼을 눌러서 다이얼로그를 닫는다.
app.메모장_정보.확인.click()
 
# 메모장에 내용을 적는다.
app.UntitledNotepad.Edit.type_keys("pywinauto Works!", with_spaces = True)
cs

c:\Python\code>python notepad1.py

 

  실행하면 정상적으로 메모장이 실행되서, '메모장 정보' 창이 열렸다가 닫히고, 메모장에는 pywinauto Work! 라는 글자가 입력이 된다.

 

 

 

[파일 저장하기]

  그럼 이제 utf8 방식으로 인코딩을 선택해, c:\python\code 폴더에 samplecode.py 란 이름으로  저장하는 코드를 구현해 보자. 먼저 메모장에서 저장 메뉴를 선택했을 때 뜨는 '다른 이름으로 저장' 다이얼로그를 한번 살펴 보면서 고민을 해보자.

 

  위의 그림 상에서 원하는 대로 파일을 저장하려면 4가지 부분을 구현해야 하는데, 1) c:\python\code 로 폴더를 선택해야 하며, 2) 파일이름 텍스트 박스에 'samplecode.py' 라고 텍스트를 입력해야 하며, 3) 파일형식 콤보박스에서 '모든 파일' 로 선택해 바꿔 주어야 하고, 4) 인코딩을 ANSI 에서 'UTF-8' 로 변경해 준 후 저장 버튼을 클릭하면 된다. 일단 이렇게 되면 애매한 부분이 각각의 개체들을 어떤 '특성' 으로 접근해야 하는지 알수가 없다. 버튼처럼 타이틀이 있는 것도 아니고, 가리키는 텍스트가 있는 것도 아니다(텍스트 박스 옆에 있는 '파일이름(N):' 이라는 문장은 사실 텍스트 박스와 직접 관계는 없는 독립된 개체이다).

 

 

  일단 아직까진 힌트가 별로 없으니 구글에서 'pywinauto save as dialog' 라고 찾아보자. 그런데 파일을 save 하는 코드는 잘 보이진 않고, 아래의 2가지 스택오버플로우 글이 눈에 띈다. 

 

  http://stackoverflow.com/questions/9482019/how-do-i-select-a-folder-in-the-saveas-dialog-using-pywinauto 

  첫번째 글은 save 하며 폴더를 지정하고 싶은데, 어떻게 지정이 가능하냐는 문의다. 근데 답변 글은 대안을 제시하며, 파일 이름 텍스트 박스에 c:\python\code\samplecode.py 라고 풀 경로로 적으라는 것이다. 폴더를 선택 할수 있는 기능을 실제로 구현할수 있을지도 모르지만(이건 전적으로 pywinauto 를 만든 사람이 해당 컨트롤을 다룰 수 있게 기능을 구현해 넣었냐에 달려있다), 동일한 결과를 가지므로 폴더 선택 코드 부분은 이런 식으로 해결하자. 

 

 

  http://stackoverflow.com/questions/37027644/open-file-from-windows-file-dialog-with-python-automatically/37214623

  2번째 글은 파일을 save 하는 예제는 아니고 open 을 하는 코드이다. 근데 어차피 save 나 open 창이 비슷하기도 하고, 창을 열어 값을 입력하고, (콤보 박스 값을 선택하는 코드)만 넣음 비슷할 듯 하니 이 코드를 참고하자.

1
2
3
4
5
6
7
8
9
from pywinauto import application
 
app = application.Application().start_('notepad.exe')
 
app.Notepad.MenuSelect('File->Open')
 
# app.[window title].[control name]...
app.Open.Edit.SetText('filename.txt')
app.Open.Open.Click()
cs

 

 

  그럼 그 다음은 콤보 박스를 어떻게 선택할 것이냐는 문제가 된다. 구글에서 다시 'pywinauto combobox select' 라고 검색하여 아래의 글을 참고한다.

  https://pywinauto.github.io/docs/code/pywinauto.controls.win32_controls.html

 

  해당 내용들을 pywinauto 에서 다룰수 있는 모든 컨트롤 들을 설명한 페이지인데, 중간 쯤에 보면 콤보 박스 관련 설명이 있다.

class pywinauto.controls.win32_controls.ComboBoxWrapper(hwnd)

   Bases: pywinauto.controls.HwndWrapper.HwndWrapper

....

Select(item)
Select the ComboBox item

item can be either a 0 based index of the item to select or it can be the string that you want to select

 

  콤보박스 컨트롤 뒤에 .select 로 호출해 숫자(순서)나, 이름을 넣음 된단다.

 

 

  근데 여기까지 오니 마지막 문제에 다다르게 된다. 위의 '다른 이름으로 저장' 다이얼로그 화면을 보면, 콤보 박스가 여러 개 있다. 각각의 콤보 박스의 이름을 어떻게 알아 낼 수 있을까? 상용 자동화 툴같이 레코딩 기능(레코딩 버튼을 누르고 사용자가 원하는 행동을 하면 해당 동작을 (완벽하진 않지만) 자동화 코드로 만들어 주는 기능)이라도 지원해 주면, 결과로 저장되는 코드에서 인식되는 개체 이름들을 파악하면 되지만, pywinauto 는 그러한 레코딩 기능도 없는것 같다.

 

  다시 pywinauto 메뉴얼 페이지를 하나씩 훝어보고, 구글을 검색하고 하다가 아래 페이지 들을 찾게 되었다.  
  https://pywinauto.readthedocs.io/en/latest/getting_started.html#attribute-resolution-magic

  http://stackoverflow.com/questions/5039642/how-to-access-the-control-identifiers-in-pywinauto

 

  해결책은 print_control_identifiers() 함수를 사용하게 되면, 각 창 안에 있는 모든 컨트롤 들의 속성들이 쭈루룩 나타난다는 거다. 그걸 보고 원하는 개체를 찾아서 이용해 코딩하면 된다고 한다(이건 윈도우 개발툴인 spy++의 텍스트 버전같다. --; spy++는 아래 글을 참고로...).

  http://happyguy81.tistory.com/51

 

  왠지 좀 노가다 일 것 같은 같은 불길한 예감이 들긴 했지만 뭐 시키는 데로 했다. --;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#-*- coding: utf-8 -*-
from pywinauto.application import Application
 
# 메모장를 띄운다.
app = Application().start("notepad.exe")
 
# 메모장에 code를 적는다.
app.UntitledNotepad.Edit.type_keys("print ('test')", with_spaces = True)
 
# '파일' > '저장' 메뉴 실행
app.UntitledNotepad.menu_select("파일(&F)->저장(&S)")
 
# '다른 이름으로 저장' 창의 속성을 리스트업 한다.
app.다른_이름으로_저장.print_control_identifiers()
cs

 

  위의 내용을 c:\python\code 폴더에 utf-8 인코딩으로 notepad2.py 라고 저장해서 실행해 보자. 헐 그런데 위의 마지막 코드에 의해 '다른 이름으로 저장' 창에 있는 컨트롤 특성들이 쭈르륵 나오는데 거의 100페이지쯤은 된다.(정말 저 함수를 무슨 생각으로 저렇게 나이브하게 만들었는지 싶다. 찾는 사람 입장도 생각해야지...)

 

c:\Python\code>python notepad2.py
Control Identifiers:

Dialog - '다른 이름으로 저장'    (L770, T191, R1709, B800)
['다른 이름으로 저장', 'Dialog', '다른 이름으로 저장Dialog']
child_window(title="다른 이름으로 저장", class_name="#32770")
   |
   | DUIViewWndClassName - ''    (L781, T287, R1698, B697)
   | ['DUIViewWndClassName', '다른 이름으로 저장DUIViewWndClassName']
   | child_window(class_name="DUIViewWndClassName")
   |    |
   |    | DirectUIHWND - ''    (L781, T287, R1698, B697)
   |    | ['DirectUIHWND1', '다른 이름으로 저장DirectUIHWND1', 'DirectUIHWND0', '다른 이름으로 저장DirectUIHWND0', '다른 이름으로 저장DirectUIHWND', 'DirectUIHWND']
   |    | child_window(class_name="DirectUIHWND")

.... 이런 식으로 100페이지쯤 됨

 

 

  내용을 위의 다이얼로그 화면과 비교해 몇번의 시행 착오를 거쳐서, 아래의 항목들을 찾아내서 한땀한땀(정말로 이런 기분으로) 코드를 작성했다.

 

   |    |    |    | Edit - '*.txt'    (L972, T562, R1661, B589)
   |    |    |    | ['다른 이름으로 저장Edit1', 'Edit', '다른 이름으로 저장Edit', 'Edit1', 'Edit0', '다른 이름으로 저장Edit0']

 

ComboBox - '텍스트 문서(*.txt)'    (L969, T595, R1690, B628)
   |    |    |    | ['ComboBox2', '다른 이름으로 저장ComboBox2']
   |    |    |    | child_window(title="텍스트 문서(*.txt)", class_name="ComboBox")

 

   | ComboBox - 'ANSI'    (L1147, T738, R1372, B771)
   | ['ComboBox3', '인코딩(&E):ComboBox']

 

   |    |    |    | Button - ''    (L781, T287, R781, B287)
   |    |    |    | ['Button0', 'Button', '다른 이름으로 저장Button', 'Button1']
   |    |    |    | child_window(class_name="Button")

 

 

-> 불편한 걸 직접 느껴봤음 이제부턴 맨 밑의 '보충 내용'에 있는 AutoHotKey 의 spy++를 써서 이름을 알아내보자!

 

 

[최종 코드]

  결과적으로 아래와 같이 최종 코드를 만들어 내게 됬다.(하지만 솔직히 이런 방식으로 개발하게 되면, 메모장 말고 다른 프로그램을 대상으로 하게 되면 잘 될련지 자신은 없다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#-*- coding: utf-8 -*-
from pywinauto.application import Application
 
# 메모장를 띄운다.
app = Application().start("notepad.exe")
 
# 메모장에 code를 적는다.
app.UntitledNotepad.Edit.type_keys("print ('test')", with_spaces = True)
 
# '파일' > '저장' 메뉴 실행
app.UntitledNotepad.menu_select("파일(&F)->저장(&S)")
 
# '다른 이름으로 저장' 창의 속성을 리스트업 한다.
# app.다른_이름으로_저장.print_control_identifiers()
 
# 파일 full 경로 입력
app.다른_이름으로_저장.Edit1.SetEditText("c:\python\code\samplecode.py")
 
# '파일이름' 콤보박스에서 파일 종류 선택
app.다른_이름으로_저장.ComboBox2.Select("모든 파일")
 
# '파일형식' 콤보박스에서 인코딩 선택
app.다른_이름으로_저장.ComboBox3.Select("UTF-8")
 
# 바로 저장 버튼을 누르면 미처 콤보 박스가 안 바뀌어 에러가 나서 1초 시간 줌
import time
time.sleep(1.0)
 
# 저장 버튼 누름
app.다른_이름으로_저장.Button1.click()
 
 
cs

 

  최종 완성된 소스를 저장하고, 실행해 보자. 아래와 같이 해당 폴더에 정상적으로 문서가 저장이 된다.

c:\Python\code>python notepad2.py

 

 

 

 

[최종 코드의 유지보수 문제점]

  위의 코드의 유지보수 문제를 하나 생각해 보자(약간 이건 테스팅 관점이다). 저 코드는 메모장 프로그램이 대상이라 사실 변경될 일이 없지만, 만약 메모장이 아니라 자주 변경되는 프로그램에 대한 코드라 가정하면 '다른 이름으로 저장' 창의 타이틀이 바뀌면 어떻게 될까? 저 코드 내의 모든 '다른_이름으로_저장' 문자열을 모두 바뀐 이름으로 치환해 주어야 한다. 그런데 만약 해당 다이얼로그를 언급하는 파이썬 파일이 수십, 수백개라면 바꾸는 작업은 카오스가 될 것이다(파일이 많아지다보면, 이름들이 일부 겹치기도 하기(저장, 다른이름으로저장, 저장하기 등등) 때문에 일괄로 바꾸게 되면 경험상 분명 예상치 못한 오류가 날수 있다). 그래서 원래 저런 변화될수 있는 값들은 따로 빼놓아 관리하면 좋은데(마치 프로그램에서 하드코딩 하지 않고, 상수 등으로 빼는 것과 비슷하다고 보면 될 듯 하다), pywinauto 의 경우 아쉽게도 아래와 같이 유지보수에 조금이라도 도움을 주는 코딩 방식를 지원하지 않아 에러가 난다.

1
2
3
4
5
6
# 관리를 위해 변수로 뺌
saveAs = '다른_이름으로_저장'
 
# 파일 full 경로 입력
#app.다른_이름으로_저장.Edit1.SetEditText("c:\python\code\samplecode.py")
app.saveAs.Edit1.SetEditText("c:\python\code\samplecode.py")
cs

 

pywinauto.findbestmatch.MatchError: Could not find 'saveAS' in 'dict_keys(['Dialog', 'Notepad', '제목 없음 - 메모장', ' 다른 이름으로 저장', '제목 없음 - 메모장Notepad', '다른 이름으로 저장Dialog'])' <- saveAS 에 대응되는 컨트롤을 못 찾아 이런 에러가 난다.

 

  ※ 아마 내부적으로 이미 '다른_이름으로_저장' 이 컨트롤 이름으로 강제로 취급되어 관리되는 듯 하다(뭐 이 부분은 제가 pywinauto 를 제대로 이해 못해 그럴 수도 있으니 방법이 있을 지도 모른다고 꼬리를 남기는게 날 것 같다).

 

 

 

 

[한 걸음 더 - 상용툴(unified function testing)과의 비교]

  처음에 얘기했듯이, 위의 pywinauto 의 부족한 부분들을 이해하고, 혹시나 더 좋은 모듈이 나온다면 선택 할 수 있게 하기 위해서, 상용툴의 기능과 함 비교해 보겠다. 다만 현재 글의 목적에 맞게 테스팅 관점은 배제하고, 유지보수와 코드 구현의 관점에서만 살펴보려 한다.

 

  해당툴은 회사가 2번인가 바뀌면서 이름까지 바뀌었지만 지금도 왠지 자리를 못잡은 느낌도 나긴 한다(왜냐하면 현재 최신 버전이 윈도우7 밖에 지원 안한다). 그래도 예전의 무거운 느낌의 사용감은 요즘의 SSD 의 힘 때문인지 부드럽게 동작한다. 

 

  지금은 UI 자동화 업무는 명시적으로 안하기 때문에 해당 툴이 없으므로(예전 기준으로 몇 백만원 이상 하기 때문에 이런저런 잡무에 쓰면 좋겠다고 생각은 하지만...) trial 버전을 찾아봤다. 다행히 다운로드 방식의 트라이얼을 제공하고, 트라이얼 기간도 60일로 늘어난듯 해서, 옛 향수를 떠올리며 다운하여 설치를 했다. 뭐 파이썬 강좌이기 때문에 구현 과정을 생략하고 결과만 얘기한다.

 

 

  해당 툴의 레코딩 기능을 이용해서 pywinauto 코드를 비슷하게 구현한 코드가 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 윈도우 시작메뉴에서 notepad 실행함
Window("시작 메뉴").WinObject("항목 보기").WinList("항목 보기").Select "notepad"
 
# 메모장에 test 라고 입력 함
Window("메모장").WinEditor("Edit").Type "print ('test')"
 
# 메뉴에서 파일 > 저장 선택
Window("메모장").WinMenu("Menu").Select "파일(F);저장(S)    Ctrl+S"
 
# 다이얼 로그의 디렉토리 창에서 c:\python\code 로 이동 
Window("메모장").Dialog("다른 이름으로 저장").WinObject("항목 보기").WinList("항목 보기").Activate "python"
Window("메모장").Dialog("다른 이름으로 저장").WinObject("항목 보기").WinList("항목 보기").Activate "code"
 
# 파일 형식과 인코딩 선택
Window("메모장").Dialog("다른 이름으로 저장").WinComboBox("파일 형식:").Select "모든 파일"
Window("메모장").Dialog("다른 이름으로 저장").WinComboBox("인코딩(E):").Select "UTF-8"
 
# 이름 저장
Window("메모장").Dialog("다른 이름으로 저장").WinEdit("파일 이름:").Set "testscript.py"
 
# 저장 버튼 
Window("메모장").Dialog("다른 이름으로 저장").WinButton("저장(S)").Click
cs

 

  위의 코드를 찬찬히 보게되면, notepad 실행도 실제 시작메뉴의 메뉴 리스트를 '선택해' 실행하고, 디렉토리 선택도 실제 디렉토리 리스트를 '선택'해서 이동 했다. 나머지 부분은 거의 비슷해 보인다. 하지만 'object repository' 라는 인식된 컨트롤을 관리해 주는 메뉴로 가면 하나의 중요한 차이가 있다. 아래의 그림을 보면 코드에서 쓰는 이름과 속성들이 트리 형태로 구조적으로 정의되어 있다. 그럼 1) 코드상에 쓰이는 이름을 수정할 수도 있고(아마 관련된 코드에 자동 반영되는 걸로 안다). 2), 4)  속성이 여러개 조합될 수 있고, 추가 삭제도 할수 있다. 그리고 전체적인 윈도우와 그 안의 컨트롤 들에 대한 트리구조를 왼편에 보여준다. 아래 기능은 pywinauto의 print_control_identifiers() 함수의 결과를 시각화해서 관리하는 모드라고 볼 수있다. 이 기능은 실제 써보면 꽤 직관적이고, 메이저 GUI 상용툴은 거의 이런 식으로 컨트롤들의 실제 속성들을 개념적으로 코드와 분리해 관리한다.

 

 

  또 하나는 print_control_identifiers() 의 GUI 버전으로 아까 소개한 spy++ 와 비슷한 object spy 라는 기능이다. print_control_identifiers() 를 매번 코드에 넣어 디버깅 하듯 특성을 확인하는 것보다 스파이형태로 직접 윈도우 창을 클릭해 확인하는게 확실히 효율적이 아닐까 싶다.

 

 

  마지막으로 특정 이미지 영역을 커스텀 컨트롤로 지정해서 인식가능한 기능이 최근 추가됬다고 한다(이 기능은 정말 있었음 하던 기능이다). 이 기능으로 인해서 비표준적인 컨트롤 영역에 대한 인식을 좌표 방식과 이미지 방식 사이에서 취사선택 할 수 있는 선택점이 생긴 것 같다.

https://www.itcentralstation.com/product_reviews/hpe-uft-qtp-review-33718-by-don-ingerson

 

  뭐 업체에서 광고하는 여러가지 다른 요소들도 있겠지만, 제가 느끼기엔 위의 3가지 정도가 현재의 오픈소스 gui 자동화 툴과 상용툴의 가장 큰 차이인 것 같다. 반대로 다양한 언어를 지원하는 것은 보통 상용툴은 한가지 언어(이 툴의 경우 vbscript)만 주로 지원하고, 보통 자바나 .net 정도만 플러그인 같은 형식으로 추가적으로 지원 하기 때문에, 오픈소스 쪽이 휠씬 유리한 것 같다. 혹시 트라이얼 버전을 사용해보고 싶으면 아래의 링크에서 'free trial' 버튼을 클릭해 정보를 넣고, 메일 인증을 받은 후, 로그인해 다운 받으면 된다.

https://saas.hpe.com/en-us/software/uft 

 

 

 

 

[마치면서]

  개인적으로 pywinauto 같은 오픈 소스 gui 자동화 툴은 selenium 의 완성도에 비해서는 아직은 부족한 듯한 느낌이 있다. 하지만 위의 한계들을 인지하고, 구글에 있는 여러 레퍼런스 글들을 참조하여, 다른 좋은 파이썬 모듈들과 결합하여 사용한다면 해당 장점이 단점을 상쇄 할듯 싶다. 개인적으로 상용툴 만큼의 편리성을 지니도록 발전되었음 하는 바램을 가지면서, 다음 시간에는 자동화의 마지막 시간으로 작업 자동화 부분을 진행하려고 한다.

 

 

 

 

[보충내용]

<지나가는나그네님 문의 답변>

1) 브라우저에서 뜬 업로드 팝업을 어떻게 다루느냐에 대해서 autoit, pywinauto, win32 함수를 이용해서 3가지 해결책을 보여줌

https://sqa.stackexchange.com/questions/12851/how-can-i-work-with-file-uploads-during-a-webdriver-test

 

 

2) 위의 글에 있는 pywinauto 예제가 connect 를 안해 에러나서 찾음

https://github.com/pywinauto/SWAPY/issues/45

 

 

3) visual studio 설치시 제공하는 spy++ 를 이용해서 pywinauto 코드를 만드는 설명 - 이 글은 pywinauto 의 아쉬운 부분을 spy++로 보강할 수 있는 꼭 한번 읽어볼만한 글 인거 같음. 요즘은 visual studio 도 개인 개발자에겐 무료인듯 하니 설치해 같이 사용해 보시길... 설치시 옵션에서 공통 툴을 선택해야 깔리다고 함) --> 그리고 그것보다 본문에서 잠시 언급했던 autohotkey 설치할때 같이 깔리는 Active Window Info 툴이 제일 좋다고 쓰여 있다. (밑에 '다른 이름으로 저장' 창에 대한 정보를 보여주는 해당 툴의 그림을 추가했다-> print_control_identifiers 쓰지 말고, autohotkey 에서 깔리는 이 프로그램을 쓰자!!

http://stackoverflow.com/questions/42213490/pywinauto-how-to-select-this-dialog-which-spying-tool-to-use-what-information

 

 

  위의 세 개를 조합하여 만든 코드. 네이버 메일에서 팝업 창이 띄워져 있다는 가정하에서, 아래 코드를 실행 시키면 팝업 창의 파일 이름에 test.txt 라고 쓰여지고 열기 버튼 클릭.(물론 해당 폴더에 test.txt 파일이 없다고 에러 메시지는 날거임. IE 에서 확인)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#-*- coding: utf-8 -*-
from pywinauto.application import Application
import pywinauto
 
# 열려진 다이얼로그 창에 컨넥트 함
app = pywinauto.application.Application()
app.Connect(title="업로드할 파일 선택"
 
# 다이얼로그 창 정의
mainWindow = app['업로드할 파일 선택'# main windows' title
 
# 파일 이름 입력하는 창에가서 'test.txt' 라고 입력
ctrl=mainWindow['Edit'
mainWindow.SetFocus()
ctrl.ClickInput()
ctrl.TypeKeys("test.txt")
 
# 열기 버튼 클릭
mainWindow.Button1.click()
cs

 

 

<비주얼스튜디오_자동화님 문의 답변>

4) 일단 메뉴 아이콘을 인식하여 클릭하는 방법을 찾긴 어려울거 같고, 비주얼 스튜디오 프로그램 특성상 화면의 변경이 없이 매번 일정할 것이기 때문에, 화면 좌표를 이용한 매크로 방식으로 접근하기로 결정.

 

  a) 전체 윈도우 창을 기준으로 클릭 하긴 뭔가 예상못한 일이 생길 수도 있을 듯 해서, 일단 비주얼 스튜디오 윈도우를 인지하고, 그 창 내부의 상대 좌표에서 클릭하기 위해서 'pywinauto click within window' 로 찾아서 아래 페이지를 찾음.

https://stackoverflow.com/questions/28665941/python-click-by-coordinate-inside-a-window

 

  b) Open 아이콘을 클릭하기 위해서, 위에 언급했던, Active Window Info 로 아이콘 위치를 파악.

 

  c)  위의 코드를 적당히 맞춰 변경해서, Open 다이얼로그가 뜨는 것까지 확인 했음. 창이 넘 작으면 위의 메뉴가 겹쳐져서 좌표가 틀려질 수 있으니 주의 할것. 이번에 알았는데 타이틀로 창 이름 지정할시, '공백'이나 '-' 등은 생략해도 인지한다('Start Page - Microsoft Visual Studio' ->StartPageMicrosoftVisualStudio,  '-' 같은 경우는 생략을 안하면 에러가 난다. 아마 공백이나 특수문자는 모두 빼버려도 무방한듯 싶다). --;

1
2
3
4
5
6
7
8
9
10
from pywinauto.application import Application
import pywinauto
 
# 창 타이틀로 프로그램을 연결
app = pywinauto.Application().connect(title='Start Page - Microsoft Visual Studio')
# 메인 윈도우 창을 찾고, (200, 100) 상대 좌표를 클릭함
app.StartPageMicrosoftVisualStudio.ClickInput(coords=(200100))
 
# 최대 창으로 하고 그냥 윈도우 좌표로 클릭하고 싶을 경우는 아래의 코드로
# pywinauto.mouse.click(button='left', coords=(200, 100))
cs

 

 

 

[추가]

파이썬 3.7에서는 pywinauto 설치시 에러가 날텐데, 링크의 설명대로 wheels 를 업데이트 해주면 됩니다^^

https://stackoverflow.com/questions/14296531/what-does-error-option-single-version-externally-managed-not-recognized-ind

 

 

2017.4.8 by 자유로운설탕
cs

 

posted by 자유로운설탕
prev 1 2 3 4 5 6 7 8 9 next