구조설계서 작성 후기 - 구조 검토가 우선

공상프로젝트/이기적인 총무 2018.04.06 21:56 Posted by 아는 개발자 아는 개발자

총 네번의 포스트로 작성한 구조 설계서의 원본 파일을 드.디.어 깃허브에 업로드했다 -> 링크. 이 포스트를 몇 분이나 읽고 계실지는 모르겠는데 관심 있으신 분들은 얼마든지 환영이다(욕설만 하지 않는다면 태클도 받는다) 아직 채워야할 내용도 있을 뿐만 아니라 문장이 깔끔하지 않고 오타도 있는 부실한 문서지만 본래 소프트웨어 개발자는 선 릴리즈 후수정이 원칙이고(응?) 난 지금 마무리 포스트를 쓰고 싶은 관계로 일단은 올렸다. 한 달 후의 내가 꼭 수정 하기를 바란다. 혹시나 독자가 생긴다면 더 일찍 하게 될 지도 모르겠다.


처음 이기적인 총무의 구조설계서를 작성하기로 할 때는 열정이 가득차 있었던 것 같다. 한달 간의 회사 교육 프로그램을 통해 구조설계의 중요성을 뼈저리게 배웠고 심화해서 공부해보고자 교육을 마칠즈음 만들어본 소프트웨어의 구조 설계 문서를 작성 하기로 마음 먹었었다. 그때도 이미 설계서 만드는 일이 쉽지 않다는 건 알고 있었지만 그래도 제로베이스가 아니라 이미 구현되어 있는 것을 문서로 옮기는 것이기 때문에 힘들지언정 지겨움은 느끼지 않을 것 같았다.


초반에 품은 마음 가짐 덕분인지 기능적/비기능적 요구사항 작성까지는 괜찮았다. 그런데 시스템구조를 작성하는 일부터는 문서 작성이 너무 힘들었다. 이미 구조를 다 짠 상태로 그것을 문서로 옮기기만 하면 되는 일인데도 이때부터는 노트북을 켜는 것 자체가 힘들었다. 내가 게을러지기도 했고 읽을 사람도 없는 무의미한 문서를 만드는 일에 싫증이 나기도 했지만 무엇보다 잡일이 많다는 점이 컸다. 구조설계 작업에선 요구사항을 토대로 다양한 후보구조를 도출하고 꼼꼼히 검토해 최종 구조를 선정하는 것이 주요한 일인데 예상외로 이 구조들을 일일이 다이어그램으로 옮기고 문서에 설명하는 일에 손이 많이 갔다. 총 시간만 본다면 구조를 검토하는 시간보다 문서 자체를 작성하는데 시간을 더 오래 소요 했으니 정작 중요한 작업은 뒷전으로 물러난 셈이다. 시작 하기 전에 나름 간소화 한다고 UML, Class 다이어그램 문법은 무시하고 그린건데 만약 꼼꼼히 챙겨서 그렸다면 더 오랜 시간이 소요 됐을 것이다. 만약 그랬다면 난 아마 중간에 포기했을지도 모르겠다.


모두다 잘하면 좋겠지만 시간이 촉박하다면, 구조 설계서 작성은 구조 검토와 문서 작업 사이에서 적정선을 찾는 것이 중요한 것 같다. 주된 작업은 요구사항 분석을 통한 후보구조 도출이지만 다른 사람이 읽는 산출물을 만든다는 점에서 문서 작업 또한 가볍게 여길 순 없다. 그러나 가독성을 지나치게 중시해 다이어그램의 문법을 꼼꼼히 체크하고 문서의 퀄리티를 높이는데 치중한다면 정작 중요한 구조 검토는 제대로 하지 못하는 주객 전도의 상황이 발생하게 된다. 양쪽에서 얼만큼 비중을 두느냐에 따라 훌륭한 설계이지만 사람들이 읽을 수 없는 문서가 될 수도 있고 문서 퀄리티는 훌륭하나 정작 소프트웨어 결과물은 엉망이 될 수도 있다.


다시 작성 한다면 문서 작업 자체는 좀 느슨하게 하게 하고 구조 검토에 중점을 두게 될 것 같다. 문서 작업 중에서도 꽤 오랜 시간을 잡아먹는 일은 다이어그램을 그리는 일을 많이 줄이게 될 것 같다. 클래스, 시퀀스 다이어그램은 현 시스템에 대해 모르는 이해 관계자들에게 쉽게 설명하기 위한 용도로 사용되는데, 소프트웨어에 문외한 이해 관계자는(주로 고객에 해당한다) 다이어그램이 있는 시스템구조까지 읽는 경우가 흔치 않고 이 챕터를 주로 읽는 개발자들은 다이어그램에서 클래스와 컴포턴트의 이름만 추출하고 구체적인 기능은 직접 코드로 보는 것을 선호한다. 문법대로 클래스 다이어그램에 속성과 함수명까지 빼곡히 입력하고 시퀀스 다이어그램에 루프까지 넣어가며 입력하지만 실제로 참고하는 경우는 미미한 것 같다. 각 클래스간의 매핑 관계와 역할만 둬도 충분 할 것 같다. 중요한 것은 훌륭한 구조를 만드는 것이지 이쁜 문서를 만드는 것이 아니다.

컴포넌트 사양 - 구조설계서 작성(4/4)

공상프로젝트/이기적인 총무 2018.03.24 12:33 Posted by 아는 개발자 아는 개발자

시스템구조에서 소프트웨어의 전반 구조를 추상적으로 정리 했다면 컴포넌트 사양에서는 전반 구조를 구체적으로 채우는 작업을 하게 된다. 앞선 작업에서는 클래스의 이름과 역할을 간단히 소개하고 속성(Attribute)와 동작(Operation) 칸은 비워 두었다면 지금부터는 직접 코드 상에서 구현하게 될 클래스의 함수의 이름과 속성을 명시하고 각각의 사용 목적, 기능을 설명해야 한다. 함수 내부 코드만 없을 뿐 헤더 파일에 전역 변수 명과 함수를 선언하는 것과 동일 하다. 클래스 내부 속성을 구체적으로 작성하고 난 후에는 각 클래스들의 시스템 동작하는 형태 결정한다. 여기서 말하는 '동작 형태'란 프로세스, 쓰레드 또는 핸드러와 같이 시스템 상에서 특정 작업을 수행하는 주체의 종류를 말한다. 다소 전문적인 용어 일 수 있지만 개발자들이라면 충분히 이해 할 수 있는 선에서 작성하면 무난하다.


그림 1. 각 클래스에 들어갈 속성과 함수를 명시하는 작업이라고 보면 된다.


시스템 구조를 작성 할 때 꼼꼼히 검토해서 작성 했다면 여기서 할 일은 속성과 동작 칸이 비어있는 클래스에 기능적 요구사항에 있는 기능들을 채워넣는 작업만 하면 될 것이다. 그런데 꼼꼼히 작성하지 않았다면 중간에 막히는 경우가 종종 생긴다. 예를 들어 간단한 기능 A 기능을 구현하려고 클래스들에 함수를 채워 넣었는데 현재 구조 상으로는 구현이 불가능 하거나 또는 하나의 클래스 내에 넣기에 객체지향에 어긋나는 경우가 있다. 이럴 때는 시스템 구조로 돌아가 동작 할 수 있도록 구조를 변경하고 난 후 다시 돌아와서 작성해야 한다. 컴포넌트 사양 작성이 까다로운 이유중 하나다. 이기적인 총무처럼 소규모 소프트웨어에서는 거의 발생하지 않는데 OS나 서버처럼 대규모 소프트웨어에서는 자주 발생하니, 다들 알만한 소프트웨어 회사에서는 최대한 삽질을 방지하기 위해 시스템 구조 단계에서 심사숙고하게 검토를 한다고 한다. 잘못하면 몇백 명이 삽질을 하게 될 수도 있으니까. 그런데 대충하고 몇백 명이 삽질하게 두는 곳도 있다.


이기적인 총무는 앞서 시스템 구조에서 크게 MVC 구조 및 이를 적용하는 방법과 시스템에서 필요한 Database 관리 기법에 대해서 소개 했었다. 컴포넌트 사양에서도 읽는 독자들이 예측하기 쉽게 앞선 작업의 연장선으로 MVC와 Database 관리 부분으로 나누어 작성했다.


1. MVC 구조 구체화


클래스 다이어그램을 명시화하는 일이라고 바로 그림 1에 있는 클래스들에 함수와 속성값을 대입해버리면 독자들이 이해하기 어렵다. 기승전결을 따르는 소설처럼 천천히 밑밥을 던지면서 전체 결과를 볼 수 있도록 전개해야 하는데 바로 결과물부터 던져주면 처음부터 직접 따라가야해 읽기가 쉽지 않다.


독자들이 시스템 구조를 읽고 가장 궁금해 했을 요소부터 시작하는 것이 좋다. 시스템 구조는 추상적으로 작성하기 때문에 개발자는 읽는 도중에 의문점이 많이 생기기 마련이다. 이 부분에 대한 궁금증을 먼저 채우면서 채우는 도중에 생길 수 있는 의문점으로 다시 채우는 방식으로 전개하는 것이 좋다.


그림 2. Controller 기본 구조


시스템 구조에서 MVC 구조를 설명하면서 Controller에 대해서 주구장창 설명을 했었는데 이들이 기본 골격에 대해서는 설명하지 않았다. 아마 독자들이 읽으면서 '도대체 이 시스템의 Controller는 어떤걸 말하는 것일까?'라고 생각 할 것 같아 먼저 Controller의 기본 구조에 대해서 설명했다. onCreate 함수를 통해 생성시 수행하는 작업을 하고 SetView 함수를 통해 화면을 출력한다. 그리고 createController를 함수를 통해 다른 Controller를 생성 할 수 있다. 아마 이렇게만 읽으면 생성 작업에서 어떤 일이 발생하는지 의문점이 생길 것이다. 이를 위해 시퀀스 다이어그램을 준비했다.


그림 3. Controller 생성 작업 시퀀스 다이어그램


그림 3은 Controller가 생성 될 때 일어나는 작업을 설명한 것이다. Parent Controller는 create Controller 함수를 부르면서 set Argument For Created Controller 함수를 통해 생성할 Controller에게 전달할 데이터 정보를 세팅한다. 그러면 생성된 Controller는 이걸 받고 메시지를 보내고 on Create 함수를 실행하고 화면을 띄우고 블라블라~ 사실 그림만 봐도 대강 예측은 가능하다. 읽는 독자도 그림보고 대강 이해가 됐으면 슬쩍 넘어가는 것이 좋다. 텍스트까지 읽으려면 읽어야할 글자 수가 너무 많다.


시퀀스 다이어그램 후에는 각각의 Controller들을 구현 할 떄 고려할 사항들을 정리했다. 이것들을 여기에 모두 쓰는건 무리고 나중에 마무리 포스트에서 깃허브에 정리해 올리도록 하겠다.


2. 데이터베이스 관리 


그림 4. 테이블 속성 명시


시스템 구조에서는 어떤 테이블이 있는지와 각각의 역할 및 매핑 관계에 대해서 간략하게 소개했다면 여기서는 구체적으로 어떤 속성을 자기조 있으며 각각의 속성이 어떤 형태인지 그리고 어떤 기능을 가지고 있는지를 명시해야 한다. 이미 코드상에 있는 create Table 쿼리문을 가져와서 각각에 대해서 썼다. 구현 할 때는 분명히 각각의 의미를 생각하고 만든것 같았는데 문서로 옮기다보니 어떤 목적으로 썼는기 기억이 잘 나지 않았다. 미리미리 주석으로 정리했으니 금방 글을 썼지 그렇지 않았다면 코드 뒤져보느라 시간이 오래 걸릴 뻔 했다. 포기 했을 지도 모르고..


그림 5. 테이블 관리 Controller


앞서 설명한 테이블을 관리하는 주체다. . 이미 구현한 코드상에는 있었는데 시스템 구조에는 빠져있어서 쓸지 말지 고민하다가.. 귀차니즘을 극복하고 시스템 구조에 필요성을 명시하고 다시 썼다.. 시스템 구조를 꼼꼼히 해야하는 이유다.

시스템 구조 - 구조설계서 작성(3/4)

공상프로젝트/이기적인 총무 2018.03.12 23:16 Posted by 아는 개발자 아는 개발자

소프트웨어 구조설계서 작성 과정은 그림을 그리는 것과 비슷하다. 문서의 앞부분인 기능적/비기능적 요구사항을 도출하고 사용자 화면을 구상하는 작업은 어떤 그림을 그릴지 고민하는 작업으로 볼 수 있고 시스템 구조와 컴포넌트 사양을 작성하는 일은 앞에서 구상한 그림을 직접 도화지에 표현하는 작업으로 볼 수 있다. 표현하는 작업은 스케치 작업과 스케치 한 바탕에 색을 칠하는 작업으로 나눌 수 있는데 이중 시스템 구조를 작성하는 일은 스케치 하는 작업에 해당한다.  전체 그림의 윤곽을 그리는 스케치 작업처럼 시스템 구조를 작성하는 일 또한 거시적인 시각에서 동작하는 소프트웨어의 전반적인 틀을 짜는 작업이다.


스케치는 한 번 마무리 되면 다시 되돌리기가 어렵다. 색을 입히는 도중 그림 상에서 배치가 마음에 안들면 도화지를 찢고 다시 그려야 한다. 편리한 소프트웨어를 이용해 그리더라도 이미 색을 입힌 다른 영역을 지워야 하는 수고를 감수해야 한다. 그렇기 때문에 색을 입히기 전까지 신중히 고민하게 되고 지우는 작업을 반복하기도 한다.  시스템 구조를 작성하는 일도 동일하다. 전체 구조를 바꾸는 일은 쉽지 않기 때문에 처음에 구조를 짤 때 다양한 요구사항을 고려해서 만들어야 한다. 구현 단계에 진입 한 후에는 사소한 요구사항을 수정하기 위해 전체를 바꿀 수 없기 때문에 충분한 시간을 들여 난감할 수 있는 상황을 미리 대처 하는 것이 중요하다. 개인적으로 나는 설계 문서 작성 작업 중에서 가장 힘든 작업이라고 생각한다.


그래도 이기적인 총무처럼 네이티브 애플리케이션인 경우에는 안드로이드에서 기본적으로 제공하는 구조를 따라가면서 설계해가면 된다. 그런데 OS나 서버처럼 대규모의 시스템인 경우엔 스케줄러처럼 커널 내부의 요구사항과 그래픽 라이브러리처럼 미들웨어단이 모두 엮여 있는 경우가 있어 쉽지가 않다. 다양한 요구사항을 훌륭하게 반영한 시스템 구조는 최적화가 잘 돼 편리하게 사용할 수 있을 것이고 그렇지 못한 소프트웨어는 기능은 모두 동작 할지 모르나 업데이트 하기도 어렵고 좀 시간이 지나면 버벅 거리게 될 수도 있다. 소프트웨어의 품질은 시스템 구조를 작성하는 데서 판가름 난다고 봐도 무방하다.


그런데 나처럼 혼자서 미리 만들어 본 사람들은 다른 종류의 어려움을 겪는다.. 바로 매우매우 귀찮다는것. 사실 처음부터 하는 것도 아니고 이미 만들어 놓은 코드를 보고 밑그림 하는 일인데도 마우스가 생각보다 움직이질 않았다. 공부도 열심히 해보겠다고 기계식 키보드도 샀는데 이때 만큼은 치기가 싫어질 정도니. 기능적/비기능적 요구사항을 작성 할 때는 의지에 가득 차 있었는데 UI 문서를 만들 때부터 조금씩 싫증나기 시작하더니 클래스 다이어그램까지 그려야 하는 시스템 구조를 작성 할때는 과거의 나를 책망하게 되기도 했다. 내가 왜 이런 일을 한다고 설쳤을까. 교육 받은지 꽤 오랜 시간이 흘러서 약발이 떨어진 것 같기도 하고. 첫 문장을 쓰기 전까지는 '이거 굳이 꼭 해야할까?'라는 생각 뿐이었다.


그래도 한번 하기로 마음먹은 일인데 끝장은 봐야하지 않겠나. 문서 만들고 나면 뿌듯한 기분이 들 것 같기도 하고 교육 받았던 내용을 복습하는 효과가 있을 것 같기도 하고. 정 안되면 두툼한 포트폴리오라도 만들자는 생각으로 월요일 밤마다 꾸역꾸역 작성해갔다. 훗날 이직할 곳에서 과연 높게 봐줄지 모르겠다. 뭐 적어도 없는 것 보다는 낫겠지. 혼잣말이 길었다.


시스템 구조는 큰 그림을 설명하는 작업이기 때문에 추상적으로 설명하게 된다. 특히 첫 장부터 문서별로 Distributed니 Layered Architecture니 하며 그림만 딱 보여주는 경우가 많다. 그림만 보고선 구현의 방향만 짐작하게 될 뿐 구체적인 구현 방법은 감이 오지 않는데 이는 당연한 일이다. 그림을 그릴 때도 스케치로는 대충 어떤 그림이 나오겠거니 예상은 할 수 있지만 무슨 색으로 그려질지, 어떤 물감을 사용할 지는 알 수 없다.. 설계 문서 또한 세부 적인 요소들을 거시적인 관점에서 요약해야 하기 때문에 각 요소들을 꼼꼼하게 작성하는 일은 한계가 있다. 시스템의 윤곽을 소개하는 것이 이 문서의 목적이다.


단, 세부적으로 표현하진 못할지라도 독자에게 뒤에 컴포넌트 사양에서 어떻게 다뤄질 지 예상할 수 있도록 작성해야 한다. 그림 그리는 일과는 다르게 구조설계서를 작성하는 일은 다른 개발자에게 설명하는 것이 목적이다. 스케치는 작가 혼자서 이해하고 색칠할 수 있으면 그만이지만 시스템 구조는 작성자 뿐만 아니라 같이 개발할 동료 또한 시스템에 대해서 이해 해야 한다. 위 목적에 충분히 부합해서 작성할 수 있다면 잘 쓴 시스템 구조라 볼 수 있다. 이렇게 글을 쓰고 나니 검토해야 할 것 같은 의무감이 솟는다...


그림 1. MVC 패턴 개념도


구현한 사항을 반영해 작성하다 보니 이기적인 총무는 여러 개의 화면을 가지고 있고 각 화면에 표시할 데이터와 이 둘을 통제하는 컨트롤러가 존재하는 기본적으로 MVC 구조를 따르는 시스템이었다. 기본적으로 문서 작성은 모든 시스템 환경을 포괄 할 수 있도록 썼고 세부 단락에서 안드로이드에 해당하는 경우 구현 방법에 대해서 작성 했다.


그림 2. Android향 MVC


Controller에서는 Android App의 생명 주기의 요소인 Activity, Fragment 요소를 넣었다. View는 기본적으로 사용하는 XML 파일과 추가로 필요한 기능인 Additional Feature 내용을 넣었다. Model에서는 기본적인 데이터베이스 요소화 Activity에서 제공하는 모델 요소를 교집합으로 표현했다. 그림을 보고난 후에 글을 읽으면 이해하기 쉬울 것 같다는 느낌이 든다.


그림 3. 클래스 다이어그램


시스템에 존재하는 MVC를 클래스로 보고 각 클래스들의 요소와 각각의 관계를 표현한다. 그림을 보면 클래스들이 매우 많고 각각의 관계도 복잡해보이는데 실제로 시스템 상에서 존재하는 요소들이다. 이렇게 까지 표현했다면 꽤 꼼꼼히 스케치를 그렸다고 본다. 색 칠할 때는 각 클래스들이 갖고 있는 변수와 함수를 표현하기만 하면 된다.


그림 4. 테이블 관계도


클래스 뿐만 아니라 모델에 해당하는 데이터베이스에 대해서도 설명해야한다. 여기선 시스템 상에 존재하는 테이블의 종류와 각각의 역할 그리고 이들 간의 매핑 관계를 설명 했다.

UI 문서 - 구조설계서 작성(2/4)

공상프로젝트/이기적인 총무 2018.02.08 10:58 Posted by 아는 개발자 아는 개발자

앞 포스트에선 이기적인 총무 소프트웨어를 만들게된 계기(시스템 개요)와 시스템이 제공하는 기능 및 품질(요구사항)에 대해서 소개했다면 지금부터는 앞 문서에서 설명한 소프트웨어가 사용자에게 화면으로 보여지는 형태를 정리한 문서, 즉 User Interface 가이드 문서(이하 UI 문서)를 만들어야 한다.


개발자가 아닌 일반인들도 들어보진 못했어도 한번 쯤은 봤을 것이다. 주로 전자제품을 산 후 사용설명서에서 접해보게 된다. 겨울철이니 가습기를 예로 들어 보자. 가습기 사용 설명서에는 어떤 단추를 눌러야 가습기를 실행 할 수 있으며 분무량을 조절하는 버튼은 어디 있는지 그리고 세척 할때는 어떤 부품들을 분해해서 세척할 수 있는지 설명되어 있다. 아무런 안내가 없으면 사용자가 직관적으로 판단해야하는데 전자제품을 아주 잘 다루는 고객을 제외하면 대부분 사용에 어려움을 겪게 된다. 소프트웨어도 똑같다. 이기적인 총무를 사용하는 사용자가 어떤 버튼을 눌러야 '결제 내역 추가'기능과 '정산하기'기능을 이용 할 수 있는지 소개하는 짤막한 화면이 필요하다. 애플리케이션을 처음 다운로드 받으면 반투명한 소개페이지가 나오는데 그것이 UI 문서로 봐도 무방하다.


이렇게만 생각하면 UI 문서는 오로지 사용자를 위한 문서인것 같으나 꼭 그렇지만은 않다. 개발자는 UI 문서를 통해 요구사항 분석에 있는 기능들이 화면에 어떻게 적용되는지 확인 할 수 있고 이를 통해 시스템의 전반적인 플로우를 파악 하게된다. 여기서 파악한 시스템 플로우는 화면을 관리하는 객체(Android의 경우는 Activity로 보면 된다)를 선정하고 각각의 기능을 명시할 때 중요한 요소가 된다. 또한 문서에 명시된 화면을 구현 할 때 특수한 기능이 필요한 경우가 종종 있는데 이런 요구사항들은 기능적 요구사항에는 명시되어 있지 않는 것이라 개발의 뒷발목을 잡는 경우가 많다. 이런 경우를 방지하기 위해선 개발 기한을 산정할 때 위 문서를 꼼꼼히 읽어보고 화면을 구현할 때 어떤 어려움이 있을지 미리미리 확인하는 것 절차가 필요하다. 이 작업을 통해 화면 기능 요구사항이 추가로 만들어진다. 이 작업이 완료되고 나면 개발자는 도출한 객체와 기존에 있던 기능적 요구사항 및 추가적으로 도출된 화면 요구사항을 꼼꼼히 분석하고 각각의 요소들을 어디에 배치 할 지 시스템 세부 설계 할 수 있게 된다. 


사실 UI문서는 커널이나 미들웨어처럼 콘솔화면 외엔 화면에 보여질게 없는 소프트웨어에선 필요 없기도 하고 Web처럼 html에서 View가 막강한 권한이 있는 경우 크게 중요하진 않기도 하다. 그런데 Android처럼 MVC(Model View Controller)패턴을 이용해서 구현해야 하는 경우 Model과 Controller 의 권한을 부여할 때 중요한 요소가 되고 View에서 할 수 있는 기능이 제한되어 있는 경우(xml로는 여러개 탭도 못만든다)는 매번 직접 기능을 구현해야해 예상치못한 기술적 난제가 되기도 한다. 이런 경우엔 UI 문서를 통해서 미리미리 어려움을 예측하는 것이 중요하다. 실제로 이기적인 총무 앱을 만들 때도 기능적 요구사항을 구현하는 것은 그다지 어렵지 않았으나 미리 계획한 UI문서를 만들때는 많은 어려움을 겪었다.


문서내용은 심플하다. 사용자에게 보여줄 화면들을 쭈욱 나열하고 요구사항에서 명시된 기능을 사용 할 수 있는 인터페이스를 설명하면 된다. 개발자가 이해할 수 있는 정도로만 쓰면 된다. Word 형식 보다는 ppt로 여러 개의 슬라이드로 설명하는 것이 훨씬 간편하고 이해하기 쉽다.



문서를 통해 문서는 총 3개의 탭으로 이뤄져 있으며 각각의 탭은 버튼을 클릭하는 것 뿐만 아니라 손가락 슬라이딩으로 좌우로 이동할 수 있어야 하는 것을 확인했다. 기능적 요구사항에는 없지만 실제로 구현해보려고 하면 생각보다 꽤 오래 걸린다. 위 화면을 구현하려고 꽤 오랜 시간을 구글링 하는데 썼던 것 같다.

위 화면을 통해 기능적 요구사항에 명시한 기능 '결제내역 추가/수정/삭제'이 사용자 화면에 어떻게 적용되는지 확인할 수 있었다.

소프트웨어 설계 문서는 프로그래밍 언어의 문법처럼 반드시 따라야하는 규칙이 있는것은 아니다. 설계 문서의 주 목적은 소프트웨어 시스템을 모르는 사람에게 설명하는 것이므로 어떤 양식을 사용하든 위 목적에 충분히 부합한다면 잘 작성한 설계 문서라고 할 수 있다. 그러나 '충분히 설명할 수 있는 문서'라는 기준은 애매모호하다. 문서를 작성한 사람이나 소프트웨어를 개발한 사람은 전문적인 용어에 익숙하고 객체들 사이의 관계를 명확히 이해하고 있기 때문에 시스템을 추상화한 다이어그램을 이해하는것이 어렵지 않겠지만 시스템을 경험하지 못한 사람에겐 전문적인 용어는 외계어일 것이고 다이어그램은 초현실주의 작품으로 보이게 된다.


설계자와 독자의 간극을 해소하고자 오래 전부터 학계에서는 '어떤 문서가 바람직한 소프트웨어 문서인지'를 놓고 연구를 했었다. 학자들마다 뚜렷한 소신을 가진 탓에 바람직한 소프트웨어 문서의 절대적인 기준(사람의 의견은 매우 주관적이니까)은 내놓지는 못했으나 설계 문서라면 반드시 다뤄야 하는 내용과 이것을 표현하는 방법에 대해선 어느정도 공감대를 이뤘다. 예를 들어 설계문서라면 소프트웨어가 가진 기능을 설명하는 것이 필요하다고 생각해 기능적 요구사항이란 항목을 작성하게 됐고 위 항목은 UML(Unified Modeling Language)을 이용해 작성하는 것이 좋다고 여기게 됐다. 학계에선 기능적 요구사항을 도출하는 방법을 가지고 '내가 옳니 네가 틀리니' 하며 논쟁하고 있지만 산업계에선 어느정도 공감대가 형성된 요소들을 토대로 설계문서를 작성하고 있다.


이기적인 총무는 소프트웨어 설계 문서라면 반드시 포함되어야할 내용을 충실하게 작성하는 것을 목표로 뒀다. 비주얼 스튜디오나 크롬처럼 대형 소프트웨어도 아니기 때문에 특수한 내용이 추가될 일이 없기도 하다. 예전에 과제로 쓰던 설계 문서의 템플릿에서 반드시 필요하다고 생각하는 것들만 더 추려서 작성했다. 크게 네개 항목으로 나뉜다.


1. 시스템 개요


시스템을 처음 접한 사람에게 소프트웨어에 대해서 대략적으로 설명하는 항목이다. 소프트웨어를 만든 목적, 시스템이 동작하는 환경과 주요한 기능에 대해서 다룬다.



이 항목에서는 소프트웨어가 어떤 수익을 줄 수 있는지, 사업적인 측면을 중점으로 서술하기도 하는데 이기적인 총무는 돈을 벌려고 만든 시스템은 아니기 때문에 시스템이 갖고 있는 기능을 충실하게 작성하는 것에 초점을 뒀다.


아래의 그림은 시스템이 동작하는 환경(System Boundary)과 소프트웨어를 개발하는 범위를 다룬 그림이다. 시스템과 상호 통신하는 외부의 주체를 설명하고 이들 각각이 어떤 역할을 하는지 설명한다. 위 그림에서는 Actor1(졸라맨처럼 생긴)로 표현된 모바일 사용자가 이기적인 총무를 사용하는 것을 표현했다. 사용자를 제외하면 시스템이 별도로 외부와 통신하는 일이 없기 때문에 간단하게 표현 할 수 있었다. 만약 정부가 관리하는 서버와 통신한다면 또다른 Actor를 추가해야 할 것이다.


만약 이기적인 총무에서 외부 서버를 만든다면 이것은 Actor로 표현해야할까? 이때 기준은 설계 문서에서 서버에 대해서 다루는지로 판단한다. 설계 문서가 서버에 대해서 요구사항과 개발에 필요한 구조를 도출한다면 이것은 개발해야할 범위중 하나이므로 Actor로 표현해선 안된다. 그렇지 않고 이미 완성된 서버를 사용하는 거라면 Actor로 표현해 밖으로 빼낸다. 네모칸 안은 우리가 개발 할 영역이고 밖에 Actor는 시스템과 통신하는 주체로 파악하면 쉽다.


Use Case는 시스템에서 제공하는 기능을 말한다. 예를들어 Virtual Box라면 'Host PC에서 다른 OS를 Virtual Machine으로 실행 할 수 있다'가 Use Case가 된다. Use Case Diagram에서는 시스템 경계의 네모상자 안에 개발해야할 주요 내용들을 채워 넣는다. 설계자는 시스템에서 필요한 Use Case를 도출하고 네모안에 타원으로 기록한다.


그림에서 보면 여러 개의 Use Case가 <<include>>로 연결된 것을 볼 수 있는데 이는 각각의 Use Case들 끼리 포함되는 관계인 것을 표현한 것이다.


2. 요구사항(Requirement)


요구사항(Requirement)이란 말 그대로 시스템이 갖춰야 할 요건들을 말한다. 정산하기나 모임 관리처럼 시스템이 갖고 있는 기능은 기능적 요구사항이라하고 정산하는 속도, 시스템의 메모리 사용량처럼 기능은 아니나 측정해서 제한을 두고 시스템이 만족하도록 해야 하는 것은 비기능적 요구사항이라 한다.


2.1 기능적 요구사항


Use Case Diagram에서 타원형으로 기록한 Use Case에 대해 세부적으로 작성한다. 각 Use Case 별로 시스템이 어떤 동작을 하게 되는지를 단계별로 설명하고 각 단계별로 발생 할 수 있는 예외 케이스를 정리한다.



원래는 UML로 표현하는데 소규모 소프트웨어는 표로 표현해도 이해하는데 어려움이 없을 것 같아서 위 그림처럼 표현했다. 위 그림은 지출내역을 생성하는 Use Case에 대해서 다룬 기능적 요구사항이다. 기본 동작은 총 9개의 단계로 나뉘며 체크로 표시한 부분은 동작 수행시 발생 가능한 예외 케이스에 대해서 처리 방법을 설명한 것이다. 예외 케이스를 꼼꼼하게 작성 할 수록 개발할 때 편리해진다.


2.2 비기능적 요구사항


비기능적 요구사항은 성능과 가용성처럼 기능적 요구사항에서 다루지 못한 품질적인 요소를 다룬다. 예를 들면 '페이스북 좋아요는 0.5초 이내에 업데이트 돼야 하고 시스템은 24시간 사용이 가능해야 한다'같은 것이다. 반드시 만족해야하는 요구사항이기 때문에 항목들은 객관적으로 측정 할 수 있어야 하고 제약사항은 현실적으로 구현 가능한 수준이어야 한다. 측정 방식이 객관적이지 못하면 SE 부서와 개발자마다 측정 방식이 달라 릴리즈 단계에서 애를 먹을 수 있고 제약 사항이 비현실적이면 애초 구현 불가능한 것을 삽질한 셈이 된다. 



이기적인 총무는 주요 기능이 회계이므로 시스템에서 정산한 금액이 정확해야 한다. 측정하는 방법은 시스템에서 계산하는 금액이 실제로 회계에서 정산한 금액과 동일한지로 판단했고 제약사항은 이 둘이 반드시 동일하도록 했다. 동일하지 않다면 회계 기능은 신뢰할 수 없는것이 되므로 비기능적 요구사항을 충족하지 못한것이 된다. 기능적 요구사항을 개발하면서도 비기능적 요구사항을 생각하면서 개발해야 한다.

이기적인 총무 리팩토링 - Database 관리 부분

공상프로젝트/이기적인 총무 2018.01.21 15:50 Posted by 아는 개발자 아는 개발자

구조설계서에 시스템에서 사용할 클래스 다이어그램을 그리다 보니 작성한 내용 그대로 실제로 시스템이 적용 할 수 있을지 의문이었다. 문서의 내용은 그럴듯 한데 여기에 안드로이드 시스템 특유의 feature들을 넣다보면 중간에 엉키게 되는 부분이 상당수 있을 것 같은 느낌이 드었다. 나중에 반복해서 고치는것 보단 직접 먼저 코드를 수정한 후 현실 가능성을 검증하고 옮기는 것이 나을것 같아 예전부터 가장 먼저 손대기로 했었던 DatabaseHelper 클래스를 리팩토링 했다.


수정전 DatabaseHelper.java 클래스는 이랬다.


그림 1. 초기 DatabaseHelper 클래스 다이어그램


DatabaseHelper 클래스는 이름 그래도 이기적인 총무의 데이터베이스를 관리할 수 있는 기능을 제공하는 클래스다. 그런데 여기에 Party, Pay, Person, Pay-Participation 테이블에 대해서 각각 CRUD 명령을 모두 넣다보니 위 클래스가 갖는 함수가 너무 많다. 클래스는 하나의 Responsibility만 가져야 하는데 위 경우에는 4개의 Responsibility를 가지게 됐으며 이런 경우 한 영역의 수정이 다른 영역의 수정에 영향을 미치게 된다. 객체지향적 관점에서 부적합한 설계 방법론이다. 위 클래스가 갖고 있는 Responsibility를 최소화하기 위해 총 4개의 subclass로 분리했다.


1. 역할에 맞춰서 클래스를 분리한다


그림 2. 1차 수정 버전


DatabaseHelper 클래스를 abstract 클래스로 두고 Table 별로 서브클래스를 두었다. 이렇게 하니 각각이 기능성으로 분류돼 각 클래스 별로 하나의 기능만 존재하게 됐다. 그런데 위 디자인을 적용해서 설치해보니 실행중 에러가 발생한다. 테이블이 생성되지 않았다는 에러다.


테이블을 생성하는 작업은 onCreate() 함수 안에서 담당한다. 그림 1에선 onCreate 함수 내에 모든 테이블을 생성하도록 했고, 그림 2에서는 각 클래스내에 존재하는 onCreate 함수에 테이블을 생성하도록 했다. 그런데 이렇게하니 문제가 발생했다. onCreate 함수는 애플리케이션내에서 데이터 베이스 함수를 열 때(getWritableDatabase 함수를 실행할 때) 딱 한번만 실행 된다. 각각이 현재 분리돼 있지만 시스템 내에서 실행되는 경우는 딱 한번 뿐이다. 그래서 PartyDatabseHelper에서 먼저 onCreate 함수를 실행 했다면 나머지 클래스에선 모두 생략된다.


위 문제는 시스템을 시작할 때 테이블을 생성하는 작업을 실행하도록 해 간단히 해결 할 수 있다. 그런데 테이블을 업그레이드 해야하는 경우는 문제가 생긴다. 업그레이드 명령인 경우 현재 데이터베이스 버전 번호와 최신 데이터베이스 버전 정보가 필요한데 이런 경우에는 기존 onUpgrade 루틴을 벗어나서 버전 정보를 읽어오는 것이 불가능하며 설사 가능하더라도 기존에 있는 기능을 중복해서 만드는것도 바람직한 코딩이 아니다.


마침 다른 블로그에 여러개의 테이블을 관리하는 코드가 있어 그 코드를 참조해서 리팩토링 했다.


2. 기존 create/upgrade 루틴과 호환되도록 한다.


그림 3. 2차 수정 버전


Party 데이터 베이스 관리 클래스가 DatabaseHelper 클래스로의 서브 클래스였는데 이제는 모두 별도의 클래스가 됐고 onCreate/onUpgrade 함수 대신 createTable(), upgradeTable()이 생겼다. 이 함수들은 static으로 선언해 외부에서도 불러줄 수 있도록 했다. DatabaseHelper에서는 기존대로 onCreate 루틴을 타는데 이때 각 서브클래스에서 선언된 createTable를 불러주도록 했다. onUpgrade 일때는 각 서브클래스에서 선언된 upgradeTable을 부른다. 


    @Override
    public void onCreate(SQLiteDatabase db) {
        PartyDatabaseManager.createTable(db);
        PayDatabaseManager.createTable(db);
        PersonDatabaseManager.createTable(db);
        PayParticipateDatabaseManager.createTable(db);

        Log.d(LOGTAG, "Create table is called");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        PartyDatabaseManager.upgradeTable(db, oldVersion, newVersion);
        PayDatabaseManager.upgradeTable(db, oldVersion, newVersion);
        PersonDatabaseManager.upgradeTable(db, oldVersion, newVersion);
        PayParticipateDatabaseManager.upgradeTable(db, oldVersion,newVersion);

        onCreate(db);
    }


기존 생성/업데이트 루틴을 타게 됐으니 앞서 말한 문제점이 깔끔하게 해결 된 것 같지만 이 코드도 문제점이 없는건 아니다. 만약 시스템에서 관리하는 테이블이 추가된다면 DatabaseHelper 클래스에 함수의 테이블 생성 작업과 업그레이드 작업을 추가해야 한다. 그런데 DatabaseHelper가 시스템 테이블의 구체적인 정보까지 알고 있는 것은 바람직하지 않다. DatabaseHelper는 각 테이블에게 추가 또는 업데이트를 명령할 뿐이지 구체적으로 어떤 테이블이 있는지는 알 필요가 없다.


차라리 외부에서 DatabaseHelper가 관리 해야하는 테이블을 등록하고 생성/업데이트 할 때 등록된 테이블만 관리하는 방법이 깔끔한 것 같다. 그래서 다음과 같이 변경했다.


3. DatabaseHelper에는 세부적인 내용을 넣지 않는다.


그림 4. 3차 수정 버전


 
// startActivity.java 내에 관리할 테이블 정보를 등록하는 코드.
        DatabaseHelper databaseHelper = new DatabaseHelper(this);

        databaseHelper.registerTableManager(new PartyTableManager());
        databaseHelper.registerTableManager(new PayJoinTableManager());
        databaseHelper.registerTableManager(new PayTableManager());
        databaseHelper.registerTableManager(new PersonTableManager());
 
// DatabaseHelper 클래스는 등록된 테이블 별로 create/update 함수를 부른다
    @Override
    public void onCreate(SQLiteDatabase db) {
        for (TableManager tableManager : tableManagerList)
            tableManager.createTable(db);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        for (TableManager tableManager : tableManagerList)
            tableManager.upgradeTable(db, oldVersion, newVersion);

        onCreate(db);
    }


TableManager라는 인터페이스를 둬 DatabaseHelper가 각 클래스의 세부 내용을 모르더라도 함수를 호출 할 수 있도록 했고 각각의 Table을 관리하는 클래스는 TableManager 인터페이스를 구현하도록 했다. DatabaeHelper 생성시 관리할 테이블을 등록해 안에 세부적으로 구현하지 않고 생성/업그레이드 작업을 수행할 수 있도록 했다. 관리할 테이블이 추가되면 startActivity에 테이블을 추가로 등록하면 되고 테이블을 삭제할 경우에는 등록하는 부분의 테이블을 지우면 된다. 이전보다 훨씬 쉽게 변경이 가능한 코드가 됐다.

이기적인 총무 - 리팩토링 계획

공상프로젝트/이기적인 총무 2018.01.07 23:25 Posted by 아는 개발자 아는 개발자

단순히 기능적 요구사항만 만족하는 소프트웨어가 아니라 변경 용이성, 성능, 사용 편의성과 같은 품질 요소와 객체 지향적인 관점을 고려해서 구현하는 것이 구조적으로 튼튼한 소프트웨어를 만드는 일이다. 이기적인 총무를 처음 만들때는 빠르게 출시 해보는것이 목적이어서 기능 구현과 UX 디자인 이외의 것들을 생각하지 못했다. 그러다보니 몇몇 기기에서는 정상적으로 작동하지 않고 있고 수정 하려고 해도 어디서 부터 손대야 할지 엄두가 나지 않는다. 이미 출시 된지 꽤 많은 시간이 흘러 조금 늦은 감이 있지만 앞으로 두달 간은 그동안 놓쳐 왔던 것들을 계획을 세워서 보완해 볼 생각이다.


1. 구조설계서 작성


가장 중요한 일임에도 불구하고 빠르게 개발하는데 급급해 문서 남기는 일을 생략했다. 그 결과 마지막으로 업데이트한지 4달이 된 지금, 블로그를 제외하곤 아무런 문서가 없으니 처음부터 끝까지 개발한 나도 어떤 기능적 요구사항이 있었는지 그리고 이건 왜 이렇게 만들었는지 헷갈릴 지경이다. 적어도 나를 위해서도 문서를 남겨 뒀어야 했었는데.


그리고 문서를 만드는 과정이 없어 그런지 품질 요구사항에 대해서 진지하게 고려해보지 못했다. 이기적인 총무에 새로운 기능을 제안해주시는 분들이 있는데 개발 할 때 확장성과 변경 용이성을 고려하지 않아서 어떻게 수정해야 할 지 엄두가 안난다. 클래스 다이어그램이라도 그려 뒀으면 구조 파악이 쉬웠을텐데 말이다.


하지만 문서를 이쁘게 만드는데 비중을 두진 않을 것이다. 구조설계서의 주요 독자는 나와 혹시 모를 이기적인 총무앱의 동업자이며 소프트웨어를 잘 만들기 위해 작성하는 문서지 다른 사람들을 설득하기 위해 작성하는 문서가 아니다. 그리고 다른 개발자가 오독하지 않는 선에서 다이어그램의 문법을 적당히 지키려고 한다. 띄어쓰기를 지키지 않아도 무슨 말을 하는지 이해할 수 있는 것처럼 다이어그램도 개발자가 읽고 이해 할 수 있는 수준이기만 하면 된다.


2. 객체 지향 프로그래밍


나름 객체 지향의 관점으로 구현했는데 SE 수업을 듣고나니 이렇게 많은 기법이 있을 줄이야. 왜 90년대 이후로 객체지향/객체지향/객체지향 하는지 이제 알 것 같았다. 학자들이 연구한 내용을 보니 용어도 다 기억하기 어려울 정도로 수많은 원칙/프로세스가 있었다. 물론 다들 비스무리한 것들을 말하는 것 같지만, 이런 비스무리한 것들을 구현 할 때는 고려하지 못했다.


모든 원칙을 적용하기는 어려울 것 같아 핵심적인 것들만 간추려서 적용해보려고 한다. 현재 개발된 코드에 GRASP와 SOLID 원칙을 적용해 객체를 다시 명세한 후 Design Pattern이 적용될 부분을 찾아 개발에 옮길 생각이다. 이것만으로도 충분 한 것 같다.


이것들에 대해서 짤막하게 소개하자면...


GRASP - 시스템에서의 클래스를 나누는 기법을 말한다. 즉 시스템 내에서 새로운 객체를 만들고 역할을 부여할 때 판단의 기준이 된다


SOLID - 객체 지향 프로그래밍의 가장 기본적인 5가지 원칙.

  • 클래스는 하나의 책임(Responsibility)만 가지고 있어야 하고(SRP)
  • 확장에는 열려 있어야 하고 수정에는 닫혀 있어야 하며(OCP)
  • 객체는 시스템의 정확도를 깨뜨리지 않으면서 상속 할 수 있어야 하고(LSP)
  • 여러개의 범용 인터페이스보다는 특정화된 인터페이스가 나으며(ISP)
  • 객체는 인터페이스에 의존해야지 구체화된 것에 의존하면 안된다(DIP) 가 있다.

Design Pattern - 객체 지향 기법을 이용해서 설계시 자주 발생하는 문제들의 해결 방법. 23가지가 존재한다.


3. Metric 측정


CCM, LCOM처럼 객체지향 프로그래밍에 다양한 지표가 있는 줄 몰랐다. 아마 이런 걸 알았더라면 막 짜지는 않았을 것 같은데 말이다. 그리고 내가 무의식적으로 얼마나 이런 지표를 잘 지키면서 구현했는지 궁금하기도 하다. 


2번 과정을 하기 전에 먼저 지표를 측정해보고 적용 후의 지표를 측정해볼 생각이다. 리팩토링 결과 코드의 품질을 수치적으로 비교할 수 있는 기록이 될 것 같다.

TAG 총무앱
  1. juju 2018.01.22 15:03  댓글주소  수정/삭제  댓글쓰기

    "혹시 모를 이기적인 총무앱의 동업자"를 위한 문서 ㅎㅎ 좋네요 ㅋㅋㅋ 심지어 Metric이라니! 세상에...Fully 이해 가능한 글을 여기서 보다니....!! ㅋㅋㅋㅋ 화이팅입니다.!

폴리페이스 - 데이터 학습시키기

공상프로젝트/폴리페이스 2017.10.12 21:21 Posted by 아는 개발자 아는 개발자

데이터 구하고 가공하기에서 한 작업은 정치인들의 사진을 인터넷에서 크롤링 한 후 정확도 향상을 위해 사진에서 얼굴 부분만 추려낸 후 이 것을 Tensorflow서 사용 할 수 있는 파일 형식인 tfrecord로 변환하는 작업이었다. 이제는 변환된 데이터를 이용해서 학습을 시키는 일만 남았다. 이번 포스팅에서는 데이터를 학습시키는 과정들을 쭉 살펴보려고 한다.


1. Tensorflow 설치하기 (feat, 그래픽 카드 없이는 못쓴다)


Tensorflow는 내 데스크탑에 그래픽 카드가 있느냐 아니면 없느냐에 따라서 설치 명령어가 달라진다.


(tensorflow)$ pip install --upgrade tensorflow      # for Python 2.7
(tensorflow)$ pip3 install --upgrade tensorflow     # for Python 3.n
(tensorflow)$ pip install --upgrade tensorflow-gpu  # for Python 2.7 and GPU
(tensorflow)$ pip3 install --upgrade tensorflow-gpu # for Python 3.n and GPU


이 프로젝트를 처음 시작 했을 때 내 리눅스 PC는 그래픽 카드가 없었다. 애초에 이 PC는 오직 교육용으로만 사용하기 위해 게임을 할 수 있는 환경 자체를 차단 하려고 했었다. 그래서 맨 처음에 tensorflow를 설치 했을 때는 가장 위에 있는 CPU만 사용 하는 tensorflow 버전으로 설치했었다. CPU만 사용하니 한 번 학습 시키는데(뉴럴 네트워크에서 1회 학습을 하는 경우) 10초 정도 소요됐다. 10000회 정도 학습 시키려면 컴퓨터를 켜 두고 출근해야 하기도 했다.


그런데 회사 선배가 자기가 쓰던 그래픽 카드를 싸게 판다는 말을 듣고 컴퓨터에 설치해서 학습 시켜보니 퍼포먼스가 무려 20배 이상 차이가 났다. CPU만 사용했을 때 10초 정도 소요되던 것이 그래픽 카드를 사용하니까 0.5초면 가능했다. 10000회 학습 시키려고 컴퓨터를 켜두고 출근을 했었는데 이제는 1-2시간 정도면 끝났다. 그동안 내가 얼마나 많은 시간을 학습 과정에서 소비하고 있었던 것일까. 시간이 넉넉한 사람이면 모르겠지만 그래픽 카드의 유무에 따라서 작업의 퍼포먼스 차이가 크다. 초기부터 그래픽 카드를 사용했다면 불필요하게 시간을 허비하지 않았을텐데.


그래픽 카드를 사용했을 때 학습 속도, 1회 학습하는데 0.5초가 소요되고 있다

2. 학습 모델 선택하기


Tensorflow github에선 이미지를 분류 할 수 있는 다양한 학습 모델을 파이썬 코드로 짜뒀다. 개발자는 학습시키는 명령어에 사용할 학습 모델을 옵션으로 추가하면 간단히 학습 시킬 수 있다. 현재 tensorflow에서 만들어둔 학습 모델로는 이런 것들이 있다.


위 사진에 있는 학습 모델들은 이미 논문에 등재된 뉴럴 네트워크를 구글이 tensorflow에서 사용 할 수 있는 형태로 변환 한 것이다. 학습 모델의 이름을 인터넷에 검색해보면 논문 자료를 다운 받을 수 있으니 관심이 있으면 읽어보는 것도 좋다. 물론 내용이 결코 쉽진 않다. 나는 간단히만 읽어봤다.


이미지 인식 모델 중 가장 유명한 모델은 inception 시리즈이다. 이 모델은 현재 네번째 버전까지(inception_v4) 나왔고 imagenet 컨테스트에서 가장 우수한 학습 알고리즘으로 선정됐다. 하지만 서비스를 하려면 정확도 뿐만 아니라 학습하는데 소요되는 시간 및 유지 관리의 측면도 중요할 것 같아서(당시엔 그래픽 카드가 없었기 때문이기도 하다) 이 모든 요소를 절충 할 수 있는 학습 모델을 찾기로 했다. 이때 검증용으로 사용한 데이터는 tensorflow에서 제공하는 flower 데이터를 사용했다. inception_v1을 사용 했을 때는 학습 속도 자체는 나쁘지 않았으나 퍼포먼스가 좋지 않았고 inception_v3, inception_v4는 학습 속도가 너무 느렸다. 결과적으로 inception_v2가 학습시간 대비 가장 훌륭한 퍼포먼스를 보였다. 이미지 학습 모델은 inception_v2로 선정했다.


3. 학습 결과


정치인들 한 명당 대략 100장 정도 사진이 있었고 총 정치인의 수는 99명 이었다. 이 데이터로 inception_v2 모델로 10000회 정도 학습을 시켰는데 정확도가 70%정도 나왔다. 이 정도면 데모용으로 사용하기엔 적합한 수치였다. 기존 논문 대비 적은 양의 데이터임에도 불구하고 퍼포먼스가 좋은 이유는 아마 크롤링한 데이터에서 따로 얼굴 부분만 추려내고 동명 이인인 데이터를 필터링 하면서 순도 높은 데이터를 골라 냈기 때문인 것 같다. 많은 양의 데이터도 중요하지만 순도 높은 데이터도 중요하다는 사실을 확인해 볼 수 있었던 것 같다.

폴리페이스 - 데이터 구하고 가공하기

공상프로젝트/폴리페이스 2017.09.12 22:41 Posted by 아는 개발자 아는 개발자

인공지능 개발시 훌륭한 학습 알고리즘도 중요하지만 양질의 데이터를 구하는 작업 또한 중요하다. 데이터는 되도록 많을 수록 그리고 정확할 수록 학습에 도움이 된다. 이번 포스팅에서는 폴리페이스에서 어떤 방식으로 데이터를 구했고 가공했는지를 정리해보려 한다.


1. 학습에 사용할 데이터 모으기


학습에 사용할 전혀 손때 묻지 않는 RAW 데이터를 구하는 것이 첫번째 과정이다. 폴리페이스의 서비스는 정치인 얼굴 닮은 정도를 측정하는 것이므로 이때 필요한 RAW 데이터는 인터넷 상에 떠도는 정치인의 얼굴이 담긴 사진이다. 가장 간단한 방법은 정치인의 이름으로 구글 이미지 검색을 해서 나오는 이미지들을 다운 받는 것이다. 그런데 한 명당 몇장이나 필요한가가 의문이었다. 딥마인드는 알파고를 학습시키려고 10만기보를 준비했다는데 사진은 10 ~ 20장으로는 택도 없을 것 같다.


구하기에 앞서 한 명당 얼마나 사진이 필요한지를 논문으로 확인해봤는데 논문에선 한명당 최소 500장은 사용하고 있었다고 한다. 인물 한명당 500장의 사진을 다운 받는것 자체가 매우 힘든 일인데 게다가 우리는 100명의 정치인 얼굴을 학습시키는 것을 목표로 했으니 총 합하면 50,000장을 다운 받아야 했다. 직접 검색하며 다운받기엔 너무 방대한 양이다. 개발을 시작하기도 전에 사진 데이터를 모으다가 시간이 다 갈것이다.


몇차례 우여곡절 끝에 친구가 이미지 다운 전용 크롤러를 만들어냈다. 처음에는 bing에서만 사용 할 수 있다느니 몇장 이상은 다운로드가 되지 않는다며 제약사항을 걸었는데 다행이 icrawler라는 패키지에서 여러 검색 포탈을 대상으로 이미지 다운을 할 수 있게 구현해뒀다. 우리는 구글 이미지 검색으로 설정해뒀고 좀더 확장해 파이썬에서 정치인의 명단이 담긴 엑셀파일을 읽고 자동으로 정치인의 사진을 다운 받을 수 있도록 구현했다.


로그메시지가 나오면서 자동으로 다운로드를 시작한다. 받는 양과 속도에 따라서 시간이 오래 걸린다


500장 넘게 받으려고 했는데 대부분의 정치인들의 사진이 100~150장 정도에서 끝났다. 구글에서 500장 이상을 결과로 내놓지 않아서 그런것 같다. 일단 한명당 100장 또는 그 이상을 기준으로 두고 데이터를 구했다.


2. 데이터 가공하기


앞서 말했듯디 크롤러로 받은 데이터는 구글 이미지 검색에서 정치인 이름을 검색했을 때 나온 사진 리스트이다. 문재인이나 안철수처럼 대중성 있는 정치인들은 정확도가 높아 100% 순도 높은 사진들만 나오는데, 연관 인물이 있거나 또는 동명이인으로 연예인이 있는 정치인들은 순도 높은 사진이 나오지 않았다. 예로 유승민을 크롤링한 결과 유승민의 자녀분인 유담씨의 사진이 적지 않게 나왔고 국민의당 김경진의원의 사진 데이터에는 개그맨 김경진의 사진이 상당수 나왔다(김경진 의원님은 좀더 노력하시길) 이 사진들을 모두 학습에 사용하면 정확도가 떨어지기에 따로 분류를 해야 한다. 이건 모두 수작업으로 했다. 100명이나 되다보니 생각보다 시간이 오래 걸렸다. 데이터를 구하면서 가장 힘들었던 일이다.


사람의 얼굴을 학습하는 작업인만큼 사진에서 불필요한 배경 없이 사람의 얼굴만 보여 진다면 학습 오차를 줄일 수 있을 것 같았다. 파이썬 패키지로 된 opencv2를 이용하면 쉽게 사진에서 사람의 얼굴을 추출 할 수 있었고 뿐만 아니라 여러 사람이 있는 사진의 경우에는 얼굴을 여러장 추출 할 수 있었다. 이 기능을 이용해서 정치인 얼굴만 뽑아내는 작업 뿐만 아니라 여러사람이 찍힌 사진인 raw 데이터를 분류 해낼 수 있었다. 여러 사람이 찍힌 raw 데이터는 불순한 데이터로 두고 학습에 사용하지 않았다. 


문재인 대통령 사진에서 얼굴만 추출해낸 작업


이 작업이 전체 학습에 얼마나 도움이 됐는지 수치상으로 확인 할 수는 없다. 하지만 한 명당 500장을 사용한 논문과 학습 결과가 별반 차이가 없는 것을 보면 얼굴 부분을 오려내는 작업이 적지 않은 도움이 됐던 것 같다. 비록 적지만 순도 높은 데이터를 이용해서 학습효과를 극대화 시킨 작업 이었던 것 같다.


3. Tensorflow용 데이터로 전환


얼굴만 추출한 순도높은 데이터를 Tensorflow 학습에 사용할 수 있는 학습데이터(TF Record)로 전환하는 과정이다. 물론 사진 이미지 파일을 그대로 학습에 사용 할 수 있는데 그대로 사용할 시 "터진다"는 로그가 많았고 어차피 Tensorflow에서 제공하는 이미지 학습 모델이 대부분 TF record 형식을 input으로 사용해서 우리도 변환해서 사용하기로 했다.


변환하는 파일은 tensorflow flower 예제 코드를 이용해서 쉽게 만들 수 있었다. 이 코드는 참 좋은것이 학습(training)에 사용할 데이터와 평가(validation)에 사용할 데이터를 자동으로 분리해서 만들어준다. 이렇게 만들어두면 학습할때도 사용 할 수 있고 평가 할 때도 사용 할 수 있다. 정말 편리한 툴인것 같다


train이라고 된 파일은 학습용, validation으로 된 파일은 평가용이다.


10,000장 정도 되는 데이터를 크게 학습과 평가용 데이터로 분류하고 여기서도 데이터를 크게 5개로 분류한다. 이건 어떤 원리인지는 잘 모르겠다. 코드상으로 한번 찾아봐야 할 것 같다.


labels.txt는 학습데이터로 전환된 정치인들의 목록이다. 데이터를 변환 할 때 사진 데이터가 모인 장소를 argument로 전달하는데 사진 데이터들은 모두 특정 폴더 안에 존재하고 이 폴더의 이름이 사진 데이터의 label이 된다. 사소한것 같은데 정말 편리하게 만들어뒀다.

폴리페이스 -기획

공상프로젝트/폴리페이스 2017.09.04 21:13 Posted by 아는 개발자 아는 개발자

회사안에서 계속 시대에 뒤쳐진 기술을 부여잡고 살다간 밥벌이를 잃게 될 것 같아서 마음이 맞는 친구와 공부를 해볼겸 새로운 프로젝트를 시작했다. 이름은 폴리페이스(Poliface)다. 


폴레페이스 이름은 정치인(Poliface)와 얼굴(Face)를 합친 말이다. 이름에서 대강 짐작할 수 있듯이 이 프로젝트는 '정치인 닮은 꼴'을 찾아주는 서비스다. 웹크롤러를 이용해 인터넷 상에 있는 정치인들의 얼굴을 모으고 tensorflow에 있는 이미지 학습 기능을 응용해 학습시킨 후 파이썬을 사용하는 서버 프레임워크를 입혀 사용자들이 웹 또는 모바일에서 자신과 닮은 정치인을 찾아 볼 수 있게 하는 것이 이번 프로젝트의 목표다.


(폴리페이스 구상도)


이것으로 특별히 수익을 낼 생각은 없다. 이번 프로젝트는 순수히 배우는 것이 목적이다. 무언가를 공부 할 때는 배운것을 직접 응용하는 것이 가장 학습 효과가 좋다. 이 프로젝트는 '어디가서 인공지능을 해봤다'고 말할 수 있을 정도의 결과물을 내는 것 일차적인 목표이며 세부 목표는 기본적인 인공지능 지식을 습득하고 최신 인공지능 API인 tensorflow를 사용해보며 회사에서는 사용하지 않는 웹 기술(크롤러, 서버 프레임워크)를 적극적으로 사용하는 것이다.


계획은 나름 거창하게 세웠는데 결과가 어떨지는 모르겠다. 대중적으로 사용되진 않더라도 포트폴리오로 내놓아도 손색이 없을 정도만 되었으면 좋겠다.


굳이 닮은 꼴 대상을 정치인으로 고른 이유는 신선함 때문이다. 이미 연예인 닮은꼴 프로그램은 충분히 많다. 우리까지 나서서 연예인 닮은 꼴을 만든다면 아무도 재미삼아라도 사용하지 않을 것 같다. 


어쩌면 정치인 닮은 꼴이 연예인 닮은 꼴 보다 훨씬 만족스러운 결과가 나올 것 같기도 하다. 연예인들은 일반인들과 생김새 차이가 많아(매우 잘생기고/매우 이쁘고/매우 개성이 있고) 나온 예측 결과가 일반인 얼굴과 괴리가 발생할 여지가 많은데 정치인들의 얼굴은 일반인들과 생김새가 비슷해서(다들 평범한 수준) 상대적으로 설득력 있는 결과가 나올 것 같은 예감이다. 물론 내가 싫어하는 정치인이 나와 닮았다면 거부감이 들겠지만.


추가로 정치인 기본 정보까지 볼 수 있는 기능을 넣으려고 한다. 기본적인 정보만 넣어놓고 세부 정보는 위키/나무위키 링크를 넣어서 연동할 계획이다. 정치에 관심이 없는 사람들이라도 자신과 닮은 정치인 만큼은 호기심이 생길 것 같다. 정치인이 어떤 이력을 지닌 인물인지, 무슨 의정활동을 했는지 알게 된다면 정치에 조금은 관심을 가지게 되지 않을까? 특별히 이러려고 만든 것은 아니지만 긍정적인 효과가 있을 것 같기도 하다. 물론 성공적으로 완성을 하는것이 먼저겠지만!

예상보다 힘들었던 디자인 작업도 끝나고 이제 드디어 런칭까지 했다! 하아 그런데 생각보다 런칭이 이렇게 오래 걸릴 줄이야... 


https://play.google.com/store/apps/details?id=com.cholab.kwony.jochongmu



애플리케이션을 출시하는 일은 가게 차리하는 것과 비슷했다. 개발하는 일 뿐만 아니라 가게를 꽃단장 하는 것처럼 사용자들이 스토어에 애플리케이션을 다운받으러 들어 올 때 홍보할 그래픽 이미지 및 문구도 필요했고 음식점이 식약청에 검사 받는 것처럼 내 애플리케이션도 유해한 요소가 있는지 없는지 간단한 설문 조사를 통해 검사받았다(3세 이상 이용 가능한 애플리케이션이 됐다)


막상 출시하고 친구들에게 공유를 했는데... 아 생각보다 허접한 느낌이다. 마치 많이 공부 한 것 같은데 시험장에 들어오면 머리가 빈 것 같은 느낌이랄까. 남들이 과연 이걸 쓰고 편안 할 지 모르겠고... 최대한 심플하게 만들겠다고 했는데 반드시 필요한 기능까지 뺀 것은 아닌가 생각했다.


성실하게 피드백 해준 친구 한명 덕에 고칠 부분들을 많이 찾아냈다. 모임을 관리하는 사람의 입장에선 참여한 멤버를 관리 할 수 있는 기능이 반드시 필요하다고 했다. 쉽게 모임 멤버수만 갖고 계산하면 참여한 멤버수를 기억하기가 쉽지 않다고 한다. 다른 애플리케이션도 결제 목록별로 참여자 정보를 모두 관리하고 있었다. 맞는 말인 것 같아서 바로 수용했다. 대리결제자 탭을 참여 멤버 탭으로 변경하고 각 결제 내역 별로 참여한 멤버들을 관리 하도록 했다. 물론 친구의 표현처럼 건물을 3센치 옮기는 일이긴 하지만. 건물이 더 커지기 전에 빨리 옮겨야겠다.


그리고 반올림 기능보단 올림 기능이 좋다고 했다. 반올림하면 손해보는 경우가 생기니 염치 없으려면 제대로 하는게 좋다고(ㅋㅋ) 적극 수용했다.


결제 내역별 참여자 정보는 Relation table을 만들어서 결제내역별로 참여자 정보를 관리 할 수 있도록 했다. 테이블 구성은 아래와 같다.


먼저 party ID로 한 번 거르고 그 다음에는 결제 내역 X 참여자 정보로 관리하는 방식이다. DB숙제에서도 안 썼던 Relation Table을 여기서 쓸 줄이야. Relation Table은 하나의 객체가 다른 여러 객체와 연결이 필요한 경우 데이터 수를 최소화 하면서 관리 할 수 있는 방법이다. 매번 table을 찾아다녀야 하는 cost가 있지만 그래도 공간을 많이 차지하지 않고 관리가 용이하다는 점에서 좋은 방법이다.

총무앱 - 디자인

공상프로젝트/이기적인 총무 2017.07.02 16:43 Posted by 아는 개발자 아는 개발자

솔직히 말해 대학생 시절에는 디자인을 무시했었다. 그때의 난 디자이너들이 공학자들이 힘들게 만들어 둔 기술위에 숟가락을 올리는 일을 한다고 생각했다. 핸드폰이나 티비의 디자인 정도는 나같은 일반인들도 손쉽게 할 수 있을 것 같은데 디자이너들은 쓸데없이 굴리는 영어 발음과 패션 스타일로 폼 잡는다고 생각했다. 공대생들에게 가야할 노고가 디자이너들에게 뺏긴 것 같아 못마땅한 기분도 들었다.


하지만 이 편견은 회사에 들어와서 일차적으로 깨졌다. 내가 만든 피피티는 디자이너들의 손을 하루만 거치면 놀랍게 변해 있었다. 내가 대강 만들어둔 그림을 아름답게 바꿀 뿐만 아니라 발표에서 내가 강조하려고 했던 단어와 문장들을 정확히 캐치해 보는 가장 강렬하게 인상을 줄 수 있는 곳에 배치해뒀다. 피피티뿐만 아니라 제품을 보는 관점도 달랐다. 내가 만든 기능들을 디자이너들은 사용자가 어려움을 겪지 않고 사용 할 수 있도록 변형해주었다. 이 과정은 전혀 숟가락을 올리는 일이 아니었다. 오히려 디자이너가 없었으면 공들여 만든 기술이 모두 허공이 될 판이었다.


그리고 이 앱을 직접 만들면서 이제 뼈저리게 알게됐다. 아마 디자인 하는 작업이 개발하는 작업보다 1.5배는 더 오래 소요됐을것이다. 사용자가 편하게 앱을 사용 할 수 있도록 디자인 하는 것은 생각보다 매우매우 어려운 일이었다. 개발은 궁금한 점을 검색해보면 모두 나오는데 디자인은 인터넷에 '어떻게 편리하게 사용 할 수 있을까'라고 물어볼 수도 없고 아무런 기초 지식이 없는 상태라 그런지 참 답답했다. 거의 모든 순간들이 고민의 연속이었다. 어떤 아이콘을 사용해야 할지 그리 어떻게 배치해야할지, 어떤 텍스트가 손쉽게 사용자가 이해 할 수 있을지 그리고 어떤 레이아웃이 안정감이 있을지를 놓고 한참을 고민했다. 아마 회사에서 짧은 짬이 날때는 모두 볼펜으로 낙서해가며 작업을 했던 것 같다. 뿐만아니라 다른 애플리케이션은 어떻게 디자인 되어 있는지 참고도 해보고 주변 사람들에게 피드백도 받았다. 만족스럽지는 않지만 그래도 봐줄 만한 정도는 된 것 같다.


전체적인 디자인 플로우는 이렇다.


1. 앱 실행시 짧게 보여주는 스크린. 카카오톡이나 네이버 실행할 때 애플리케이션 마크가 나오는걸 생각하면 된다. 있으면 좀더 fancy해보이지 않을까 해서 만들어 넣어봤다. 가운데 있는 아이콘은(조커 얼굴에 계산기랑 돈다발이 있는) iconflows라는 에디터를 이용해서 만든거다. 시중에 무료로 배포된 아이콘을 사용해서 만들려고 했는데 아이콘에 윤기가 없는 느낌이라 직접 5000원 결제하고 에디터를 이용해서 만들었다. 나름 귀여운 아이콘인것 같다 ㅎㅎ



2. 모임 목록 부분. 총무인 사람이 자신이 관리하고 있는 모임들의 목록을 관리하는 인터페이스다. 오른쪽 아래 버튼을 이용해 간단히 추가 할 수 있다. 만들고 나니 카카오톡 친구 목록 인터페이스랑 비슷하다. 사용자들에게 가장 친숙한 인터페이스인것 같다.


      



3. 파티별 결제내역/대리결제자/정산하기 디자인. 이부분은 이 애플리케이션의 가장 핵심인 부분이라 사용자가 별다른 어려움을 겪지 않고 사용할 수 있도록 많은 고심을 들였던 부분이다. 특히 여기서 정산하기 탭을 만들 때 어려움이 많았다. 하나의 화면에 결제 내역 반올림 하는 인터페이스와 정산 기능 그리고 은행 계좌를 설정하는 것을 모두 표현해야 했는데, 사용자에게 정보를 많이 주려고하니 텍스트가 많아져 애플리케이션이 아마추어 같아졌고 또 정보를 전혀 안주자니 사용하기 불편해지는 것 같았다. 아래 그림은 가장 초기버전이다.



크게 결제내역 조정 레이아웃, 정산내역 레이아웃, 입금계좌 레이아웃 세개로 나눠져 있는데 경계선이 없으니 안정감이 없었고 텍스트가 지나치게 많아서 지저분한 느낌이었다. 그리고 위의 화살표는 반올림 조정 버튼이었는데 따로 설명이 없다면 전혀 이미지가 반올림을 표현하지 못하고 있었다. 일단 각 레리아웃별로 경계선을 넣었다. 텍스트를 최대한 없애기위해 정산해보기와 계좌의 텍스트는 없애버리고 Dialog로 보여주는 것으로 바꿨다. 그리고 반올림 버튼은 0의자리, 1의자리, 10의자리별 이미지를 다르게해서 각자리별 반올림을 표현했다. 이것도 만족스럽진 않지만 그래도 이전보다는 직관적인 느낌이다.


    


정산 결과와 입금계좌 설정하는 다이얼로그. 텍스트를 다이얼로그로 빼버렸다. 다이얼로그까지 이쁘게 만들고 싶은데 이건 어떻게 해야할지 감이 안온다. 일단 먼저 앱스토어에 출시하고 업데이트를 해볼 생각이다.

'공상프로젝트 > 이기적인 총무' 카테고리의 다른 글

이기적인 총무 - 리팩토링 계획  (1) 2018.01.07
총무앱 - 이기적인 총무 런칭 그리고 업데이트  (0) 2017.07.15
총무앱 - 디자인  (0) 2017.07.02
총무앱 - 개발+a  (0) 2017.06.06
총무앱 - 개발  (0) 2017.05.14
총무앱 - 기획  (0) 2017.05.05

총무앱 - 개발+a

공상프로젝트/이기적인 총무 2017.06.06 17:47 Posted by 아는 개발자 아는 개발자

사실 애초에 개발은 지난번 포스트에 올린 요구사항들만 만드는 것으로 끝내려고 했는데 막상 만들고 나니 그냥 일반 계산기와 다를바 없는 것 같았다. 어디가서 내 이름 걸고 만들었다고 하기 창피할 것 같아 몇가지 기능을 더 넣었다. 총무로 살아온 기간이 길어서 그런지 있으면 좋을 법한 기능들은 금방 떠올랐고 현재는 모두 구현해둔 상태다


1. 대리결제 기능


모든 계산을 총무가 처리하지 않고 다른 사람이 결제하는 경우도 있다. 예를들어 여행중에 총무가 아닌 다른 사람이 대신 여행 물건을 사러 다녀오는 경우나 총무 카드가 한도초과돼서 다른 사람이 대신 결제하는 경우들이 그렇다. 대리 결제가 한두건이면 별 문제가 되지 않는데 결제 수가 많거나 여러 사람이 대리 결제를 한 경우에는 총무 입장에선 난감하다. 대리 결제한 당사자들은 나중에 정산 할 때 자신이 낸 결제내역을 참고해서 정산 해달라고 하는데 이 사람들에게 얼마를 덜 받아야하는지 그리고 혹시 내가 지금 손해보고 있는 것은 아닌지 머리가 아프다. 


이렇게 골치아픈 경우들을 애플리케이션 자체에서 모두 처리 할 수 있도록 만들었다. 결제 내역을 추가 할 때 대리 결제자를 등록 할 수 있게 하고 각 대리 결제자별로 결제 총액을 덜 내도 되는 금액으로 공유 메시지에 넣었다. 총무 입장에선 결제 내역을 추가할 때 대리 결제자 이름만 잘 등록하면 된다. 


(대리 결제자 목록이 따로 존재한다. 오른쪽 아래 버튼으로 대리 결제자를 추가 할 수 있다)



2. 똑똑한 정산 기능


총 결제 금액을 참석자 수로 나누면 0원으로 정확히 떨어지는 경우가 많이 없다. 결제 건수가 많아지면 더 그렇다. 이런 경우 내림을 하거나 반올림을 하게 되는데 비록 적은 금액이지만 총무 입장에선 손해가 되는 경우도 있고 이득을 볼 수 있는 경우도 있다. 소소한 금액이 이득으로 돌아오면 소위 개이득이 되지만 손해로 오면 번거로운 직책을 맡고 있는데 손해까지 보는 경우가 되어 기분이 좋지 않다.


그래서 각각의 금액으로 정산 할 경우 얼마를 손해볼 지 애플리케이션에서 모두 계산해주도록 했다. 각 결제 내역별로 특정 자리에서 반올림 할 수 있도록 만들고 조정된 금액으로 정산 할 경우 얼마나 이득/손해를 볼지 자동으로 계산해줄 수 있도록 만들었다. 


(각 결제 내역 별로 반올림 조정을 할 수 있다. 왼쪽 하단의 이미지를 누르면 조정된 내역에 대한 정산 값이 나온다)


이 기능을 구현하면서 이기적인 총무라는 애플리케이션 이름도 생각해냈다. 앱스토어에 등록된 다른 총무앱들에 비해서 컨셉이 독특하고 사람들이 한 번쯤 관심을 가질만한 이름인 것 같다. 

'공상프로젝트 > 이기적인 총무' 카테고리의 다른 글

이기적인 총무 - 리팩토링 계획  (1) 2018.01.07
총무앱 - 이기적인 총무 런칭 그리고 업데이트  (0) 2017.07.15
총무앱 - 디자인  (0) 2017.07.02
총무앱 - 개발+a  (0) 2017.06.06
총무앱 - 개발  (0) 2017.05.14
총무앱 - 기획  (0) 2017.05.05

총무앱 - 개발

공상프로젝트/이기적인 총무 2017.05.14 18:22 Posted by 아는 개발자 아는 개발자

구현할 기능 자체가 단순하고 잉여력 측정기 개발 할 때 다양한 기능들을 경험해봐서 그런지 개발을 빠르게 진행 할 수 있었다. 현재 총무앱에 필요한 기본 기능들은 모두 구현이 완료됐다. 각 기능별로 어떻게 구현했는지 간단히 정리하려고 한다.


- 모임/결제 데이터베이스 관리


잉여력측정기에서 애플리케이션 데이터베이스를 만드는 것을 한 번 경험해봐서 데이터베이스 기본 동작을 쉽게 구현 할 수 있었다. 테이블은 크게 모임 테이블과 결제 내역 테이블 두개를 생성해서 관리한다.


모임테이블은 Party Table, 결제내역 테이블은 Pay Table로 관리한다. 굵은 글씨로 표시한 것은 각 테이블의 primary key이다. 붉은색으로 표시한 pay_party_id는 외래키인데, 결제 내역은 항상 특정 모임에 포함되는 관계이기 때문에 선언해뒀다. 모임을 선택하면 결제 내역은 모임 테이블의 party id와 동일한 값들만 읽어오게 되고 모임이 삭제되면 외래키 관계에 있는 값들만 자동으로 삭제할 수 있다.


DB시간에 배운 외래키를 적용하고 자동으로 삭제되는 기능을 구현하기까지 에러가 많이 발생했었다. 나는 분명히 제대로 SQL 문을 작성하고 적용한 것 같은데 이상한데서 문제들이 툭 튀어나왔다. 지금은 아래와 같이 해결했다



먼저 테이블 생성 할때 특정 attribute가 외래키임을 선언하는 구문(Foreign key 로 시작하는...)은 테이블 생성 맨 아래에 와야 했다. 중간에 끼어 넣으니까 테이블 생성 할 때 자꾸 죽었다. 이유는 모르겠다.. 애초에 문법상의 문제가 있었던 것인지.. 그런데 이렇게만 두면 생성할때는 문제가 없으나 외래키가  참조하는 튜플(party)을 삭제할 때 또 죽었다. 알고보니 바로 삭제하는(on cascade) 옵션이 적용되려면 아래처럼 DB에 따로 또 옵션을 줘야했다.



이상한데서 시간을 말짱 보냈다 ㅡㅡ


- 결제내역 공유하기 기능


카카오톡 공유기능처럼 깔끔하게 만들려면 무지 시간이 걸릴 수 있는데 나는 애초에 모든 애플리케이션과 공유하는 걸 목표로 했었다. 다행히 안드로이드에서 쉽게 공유 할 수 있는 기능을 만들어놔서 그저 가져다 사용하면 됐다. 정산 내역을 텍스트로만 보내서 깔끔하지 않은 것 같긴 하다. 하지만 뭐 이정도면 감지덕지지. 디자인 할 때 앱 홍보 링크도 넣고 하면 좀더 깔끔하게 보내 질 것 같다.


(결제 내역이 카카오톡에 공유된 모습)


나머지 기능들은 디자인과 관련된 기능이므로 디자인때 총 정리해야겠다.

'공상프로젝트 > 이기적인 총무' 카테고리의 다른 글

이기적인 총무 - 리팩토링 계획  (1) 2018.01.07
총무앱 - 이기적인 총무 런칭 그리고 업데이트  (0) 2017.07.15
총무앱 - 디자인  (0) 2017.07.02
총무앱 - 개발+a  (0) 2017.06.06
총무앱 - 개발  (0) 2017.05.14
총무앱 - 기획  (0) 2017.05.05

총무앱 - 기획

공상프로젝트/이기적인 총무 2017.05.05 16:59 Posted by 아는 개발자 아는 개발자

필자는 모임에서 주로 총무를 도맡아(떠밀려서) 하는 편인데 그때마다 거래내역 영수증 챙기고 구글 Keep에 거래내역과 금액 써둬가며 관리를 한다. 하지만 이렇게 관리를 하면 나중에 결산 할 때 불편한 점이 영수증은 지갑에서 구겨져 형체를 알아 볼 수 없게 되어 있고, 구글 Keep에 써둔 결제 내역을 계산기 애플리케이션으로 옮기자니 애플리케이션을 계속 여러번 왔다갔다 해야하는 불편함이 있다. 게다가 옮기는 도중에 거래 금액을 한 번 잘못 쓰거나 계산기 글자수 초과가 나버리면 처음부터 다시 입력해야하는 삽질도 있으니 은근히 시간도 많이 잡아먹고 짜증나는 작업이다.


그래서 날 위해(혹여나 나처럼 돈계산으로 스트레스받는 사람들을 위해) 모임 돈계산을 도와주는 총무앱을 만들어 보기로 했다. 이미 시중에 총무앱이 몇 개 있긴 한데 대부분 계모임 돈관리를 목적으로 만들어져 모임 구성원 관리처럼 불필요한 기능들도 많고 UI 디자인은 촌스럽고 사용하기 어렵게 되어있다. 그래서 난 최대한 심플하고 쉽게 사용할 수 있는 더치페이 전용 총무앱을 만들어보려고 한다.


현재 구상은 대략 이렇다



다른 앱들이 모임 참가자랑 중간에 빠진 사람들을 관리를 앱을 통해서 할 수 있도록 많이 골몰한것 같은데 이렇게 할 경우 앱에다가 구성원 목록을 일일히 저장하고 각 모임별 구성원들도 만들어야해서 아마 사용중에 지쳐버릴 것이다. 나는 간단히 구성원 추가 이런거 없고 각 결제 내역별 참가자 수와 인당 청구 금액 그리고 모든 모임에 참여한 경우 청구 금액을 쭉 나열해 SNS에 공유 할 수 있도록 만들 생각이다. 참가자는 자신이 결제에 참여하지 않은 금액을 제외하고 입금하면 된다. 총무는 입력 할 내용이 적어서 심플하고, 참가자들은 자기 결제 내역을 꼼꼼히 검토해볼 수 있어서 서로 좋지 않을까 생각한다.

'공상프로젝트 > 이기적인 총무' 카테고리의 다른 글

이기적인 총무 - 리팩토링 계획  (1) 2018.01.07
총무앱 - 이기적인 총무 런칭 그리고 업데이트  (0) 2017.07.15
총무앱 - 디자인  (0) 2017.07.02
총무앱 - 개발+a  (0) 2017.06.06
총무앱 - 개발  (0) 2017.05.14
총무앱 - 기획  (0) 2017.05.05

잉여력측정기 -개발 중단

공상프로젝트 2017.04.24 22:03 Posted by 아는 개발자 아는 개발자

이 프로젝트를 처음 시작 할 때 구글이 보안상의 문제로 기존에 있던 프로세스 관련 함수들을 모조리 없애서 핵심 기능을 구현 할 때 힘들었었다. 다행히 여러 오픈소스 프로젝트에서 이런 구글의 보안 정책들을 피해 프로세스 정보들을 추출 할 수 있게 해주어서 애플리케이션의 실행 유무를 확인 할 수 있도록 만들어 왔는데, 최신 안드로이드 OS 누가(Nougat) 에서는 아예 (/proc) 디렉토리의 권한을 바꿔버려 일반 프로세스가 접근하는 것조차 막아버렸다. 이젠 시스템 애플리케이션이 아니면 프로세스 정보 읽을 때 자신의 프로세스 정보 밖에 볼 수가 없다. 다른 애플리케이션의 실행 유무도 알 수 없고 커널에 백그라운드로 돌아가는 앱도 알 수 없다. 이렇게 되면 이 프로젝트의 가장 중요한 기능인 다른 애플리케이션의 실행 유무를 확인 할 수가 없다ㅠㅠ


누가 업데이트에 대해서도 빈틈을 찾아낸 오픈소스가 있는지 확인해보았으나 별 다른 방법이 없었다. 내가 사용한 오픈소스 프로젝트의 오너는 이슈페이지를 만들고 다른 개발자들과 방안을 모색 하기도 했는데 이번에는 안드로이드 정책에 다들 항복한 것 같다. 몇몇은 안드로이드 업데이트를 욕하기도 하지만 보안상 어쩔 수 없는 결정이라고 다들 수긍하는 것 같았다. 결국 이 프로젝트를 중단하겠다는 코멘트를 남기고 이슈가 종료됐다.


나름 열정적으로 추진했던 프로젝트여서 아쉬움이 남는다. 비록 다른 사람들한테 홍보 할 때 반응들이 시큰둥 했지만 내 이름으로 앱마켓에 하나라도 올려보고자 시작한 프로젝트라 남들이 뭐라 하든 개의치 않았다. 예전에도 이런 프로젝트를 다른 사람들이랑 같이 해봤는데 그때마다 개발 도중에 무슨 일이 생겨서 중단돼 끝을 맺지 못했었다. 그래서 이번 만큼은 죽이 되든 밥이 되든 완성해서 런칭을 해보려고 했는데 이번에는 플랫폼이 문제라니. 그것도 개발 다하고 디자인 입히는 과정에서 이러니 매우 허탈한 심정일 따름이다. 


아직 까지는 보안 정책을 피할 수 있는 방법이 보이지 않는다. 이번에는 정말 꼼꼼히 막아 놨나보다. 왜 하필 내가 야심차게 앱을 만들 때 이런 건지 모르겠지만 그래도 일단 기다려보려고 한다. 혹시나 시간이 지나면 보안 정책이 바뀌지 않을까 아니면 나랑 같은 고민을 하고 있는 벤치마크 개발자가 또 뚫어내는 방법을 찾아 놨을지도. 그때를 위해 프로젝트는 계속 갖고 있어야겠다.

'공상프로젝트' 카테고리의 다른 글

잉여력측정기 -개발 중단  (0) 2017.04.24
잉여력 측정기 -개발(2/2)  (0) 2017.03.30
잉여력 측정기 -개발(1/2)  (0) 2017.03.26
잉여력 측정기 -기획  (0) 2017.03.26

잉여력 측정기 -개발(2/2)

공상프로젝트 2017.03.30 22:31 Posted by 아는 개발자 아는 개발자

잉여력 측정기 개발 두 번째 이야기다. 핵심적인 기능들은 첫번째 포스트에서 만들어 뒀고 이번 포스트에서 설명할 내용들은 필요한 기능들이나 많이 어렵지는 않았던 내용들이다. 현재 구현한 사항들은 안드로이드 버전이 올라가면서 없어지던 기능이 없었다(아주 당연한 일이지만). 스택 오버플로우와 옛날 학교에서 프로젝트 했던 경험을 살려서 빠르게 구현 할 수 있었다.


1. App 실행 횟수 저장할 데이터베이스 구축


데이터베이스를 구축 해보는 것이 너무 오랜만이라 스키마의 기본적인 개념도 까먹었다(튜플이랑 key가 왜이렇게 생소하게 느껴지는 건지) 사실 여기서 구현할 데이터베이스 형태는 정말 단순한 것인데도 허둥 지둥 했다. 기억력 복원 작업을 한 두 시간정도 하고 나서 스키마를 짜고 Android SQLite Tutorial을 따라가면서 DB를 만들었다. 다행히 한방에 깔끔하게 구현 된 것 같다. 만든 DB 테이블은 이렇다.



ID값은 primary key이다. Auto Increment로 생성되도록 했다. 애플리케이션들마다 'Package Name'이란게 존재하는데 프로세스들은 이 이름으로 안드로이드내에서 실행되고있다. 애플리케이션 이름은 한글 일수도 있는데 반해 Package Name은 영어로 통일되어 있고 혹시나 이름이 동일한 애플리케이션이 있더라도 Package이름으로 구분 할 수 있을 것 같아서 DB에 넣기로 했다. Count값은 실행 횟수고 Date는 Count 하는 날짜다. Reason Code는 이 앱이 카운팅 된 이유를 기록한 값이다.


아직 구현은 안됐는데 일별 총 카운팅 횟수를 기록할 테이블도 만들 생각이다. 만든다면 아마 테이블은 요렇게 간단히 만들 수 있을 것이다.



별일 아닌데 막상 만들려고 하니 보람을 내일로 미루고 싶은 욕구가 강렬해졌다.


2. 달력으로 앱 실행 횟수 가져오기


날짜 별로 나의 앱 실행 횟수 기록을 카운팅 할 수 있어야하는 요구 사항이 있었다. 다행히 요건 안드로이드에서 DatePickerDialog라는 것을 이용해 쉽게 날짜를 선택 할 수 있게 해줬고(UI도 이쁘게!) 내가 할 일은 캘린더에서 리턴한 날짜 기록에 맞춰서 DB에서 값들을 읽어오는 일 뿐이었다. 날짜에 맞춰서 읽어오는 일은 기존에 있던 SQL 명령어를 조금 수정해서 구현 할 수 있었다.


         


(캘린더 토글로 3/27 기록에 맞춰서 기록을 가져온 결과)


3. 사용량 그래프 그리기


애플리케이션마다 날짜별 사용량을 그래프로 보여 줄 수 있어야 했는데 안드로이드 API 형태로 제공되는 그래프 UI는 없었다. 다행히 오픈 소스에서 여러가지 프로젝트가 있었고 구글 검색 결과 제일 많이 사용하는 프로젝트를 골라서 구현했다. 요즘 깃허브는 그냥 소스만 제공하는게 아니라 가이드도 아주 아주 상세한 것 같다. 다른 오픈소스 프로젝트 가이드 뺨치는 수준. 대충 훑어 보긴 했지만 그래프 축 최대/최소값 조절 뿐만 아니라 스타일링까지 함수 하나로 해결 할 수 있었고 원한다면 추가로 기능을 구현해 애니메이션을 넣을 수 있는 효과도 있었다. 이건 디자인 스펙이 결정되면 코드레벨까지 내려가서 세세히 볼 계획이다.


(카카오톡 사용량을 그래프로 찍어본 모습. 정말 데모 수준의 UI이고 손 볼 곳이 많지만 시작이 반이니까!)


이 정도면 대강 구현은 완료된 것 같다. 이제는 앱을 이쁘게 꾸미는 작업을 해야겠다.

'공상프로젝트' 카테고리의 다른 글

잉여력측정기 -개발 중단  (0) 2017.04.24
잉여력 측정기 -개발(2/2)  (0) 2017.03.30
잉여력 측정기 -개발(1/2)  (0) 2017.03.26
잉여력 측정기 -기획  (0) 2017.03.26

잉여력 측정기 -개발(1/2)

공상프로젝트 2017.03.26 16:29 Posted by 아는 개발자 아는 개발자

옛날 안드로이드 애플리케이션 개발 경험 상으로 요구 사항들을 구현하는 것이 별거 아닐 거라고 생각했는데... 왜 안드로이드는 시간이 흐르면서 계속 지원하지 않는 기능들이 많아지는 건지...! 스택 오버플로우에 있는 가이드 내용을 따라 가려고 하니 되는 것이 하나도 없고, 구현에 가장 핵심적인 API함수가 갑자기 depracated로 떠서 어떻게 구현해야 할 지 난감했었다. 다행히 깃허브에서 구해낸 소스와 여기저기 뒤져가면서 코드를 구현해 기획단계에서 만들어둔 요구사항들은 일단 구현을 해냈다. 이번 포스트에서는 개발과정에서 어떤 어려움이 있었고 어떻게 해결했는지를 정리해보려고 한다.


1. 실행되는 애플리케이션 목록 가져오기.


잉여력 측정기 애플리케이션 내에서 실행되고 있는 애플리케이션을 알아 낼 수 있어야 한다. 이 프로젝트를 시작하기 전에 현재 실행하고 있는 애플리케이션의 목록을 쭉 보여 줄 수 있는 안드로이드 API(ActivityManager 클래스에서 getRunningAppProcess()라는 함수를 사용하면 된다)가 있는 것을 알았었다. 이 함수를 이용해 쉽게 Application과 Service를 구별 할 수 있어서 별 다른 어려움 없이 구현 할 수 있을 줄 알았다.


https://developer.android.com/reference/android/app/ActivityManager.html 참조.


하지만 막상 위 함수를 사용하니 리턴된 애플리케이션 목록에 현재 설치한 애플리케이션 말고는 아무것도 보이지 않았다. 어떻게든 실행되고 있는 애플리케이션 목록을 보려고 다음도 실행하고 인스타그램도 실행 해봤는데 여전히 결과는 똑같았다... 알고 보니 시스템 애플리케이션이 아니면 함수를 호출한 애플리케이션 밖에 안보여준다고 한다... 보안상의 이유로 바꿨다는데 핵심적인 기능을 사용 할 수 없으니 대략 난감했다.


혹시나 애플리케이션 안에서 리눅스 'ps' 명령어를 사용 할 수 있을지 찾아봤다. 이걸로 실행되는 애플리케이션 목록을 얻어와서 여기서 애플리케이션의 실행 유무를 파악할 생각이었다. 다행이 깃허브 오픈소스중에서 "ps"와 동일한 역할 을 할 수 있도록 만들어뒀다.


(https://github.com/jaredrummler/AndroidProcesses)


프로세스 목록만 가져오는게 아니라 package 이름, pid, foreground 유무 등등을 가져와서 프로세스의 종류를 파악하는데 많은 도움이 됐다. 아마 이 프로젝트가 없었다면 나머지기능들을 만드는데 무지 애를 먹었을 것이다.


2. 애플리케이션 실행 횟수 카운팅하기.


애초의 내 계획은 스레드 하나를 돌리고 여기서 실행되는 프로세스를 주기적으로 관찰해 새로 생성된 프로세스를 보고 카운팅 하려고 했다. 하지만 이렇게 간단한 방식을 도입했더니 백그라운드 애플리케이션이 무지 많이 카운팅됐다(난 한번도 SSF Shop 애플리케이션을 실행 한 적이 없었는데 내 의지와 무관하게 가장 많이 사용한 애플리케이션이 됐다). 애플리케이션들마다 개발 할 때 서비스를 구현한 방식이 모두 달라서 실행하지 않으면 돌아가지 않는 앱이 있는 반면, 스마트폰 부팅과 동시에 실행되는 애플리케이션이 있었다. 이런 경우 '잉여력측정기'에서는 애플리케이션이 여러 번 실행 된 것으로 착각 할 수 있다.


또한 프로세스 생성 유무로만 판단 하다 보니 재실행 하는 경우(실행 된 앱 목록에서 눌러서 다시 실행시키는 경우)를 측정 할 수가 없었다. 사용자들이 애플리케이션을 종료하지 않고 다시 사용하는 경우도 있기 때문에 프로세스가 새로 생성되지 않아도 실행 횟수를 판단 할 수 있어야 한다.


예외 경우의 수들을 처리 하기 위해 나는 실행 횟수가 증가하는 경우를 크게 세가지로 분류 했다.

      • 새로 생성된 프로세스: 스레드에서 관리하고있는 프로세스 목록 중에 존재하지 않는 프로세스가 생겨난 경우이다. 하지만 백그라운드 프로세스도 여러 번 반복해서 생겨날 수 있기 때문에 생겨난 프로세스가 Foreground인지 확인 한 후에 맞다면 실행 횟수를 증가 시키도록 한다.

      • pid가 변화된 프로세스: 스레드가 관리하고 있는 프로세스 목록에는 있으나 pid가 변화된 경우이다. 이 경우는 사용자가 한 번 애플리케이션을 종료하고 난 후 다시 실행 한 경우이다. 새로 생성된 프로세스와 동일한 경우이며 동일하게 프로세스가 foreground에 있는지 유무로 실행 횟수를 증가시킨다.
      • Background -> Foregrond 변화: 실행되고 있는 프로세스의 상태가 Background에서 Foreground로 변화한 상태이다. 이 경우는 애플리케이션을 종료하지 않고 다시 재실행한 경우를 처리한다.

세가지 경우에 대해서 만들어두니 백그라운드 애플리케이션이 카운팅되는 횟수가 눈에 띠게 줄었다. SSF Shop은 아예 없어졌고 기껏해야 KT 고객센터 정도... 이건 추가적으로 처리해야겠다.



(애플리케이션 카운팅 횟수 목록. 많이 실행된 순서로 정렬되어있다. 노래를 틀어놓고 개발해서 그런지 지니앱 실행 횟수가 가장 많다)


지금까지 나온 결과는 만족스럽다. 다른 기기에서도 동일하게 실행 된다면 현재의 알고리즘을 그대로 유지 할 수 있을 것 같다. 한 가지 걱정되는 점은 프로세스 목록을 보는 주기이다. 주기가 짧을 수록 카운팅 횟수의 정확도는 높아지고 주기가 길어질 수록 정확도는 낮아진다. 현재는 주기를 3초로 해두었는데 스마트매니저에서 배터리를 잡아먹는 애플리케이션이라고 뜬다.. 배터리 소모까지 생각하면 추가적으로 어떻게 구현해야 할 지 고민해봐야 겠다.

'공상프로젝트' 카테고리의 다른 글

잉여력측정기 -개발 중단  (0) 2017.04.24
잉여력 측정기 -개발(2/2)  (0) 2017.03.30
잉여력 측정기 -개발(1/2)  (0) 2017.03.26
잉여력 측정기 -기획  (0) 2017.03.26

잉여력 측정기 -기획

공상프로젝트 2017.03.26 15:06 Posted by 아는 개발자 아는 개발자

여느때처럼 회사 화장실 좌변식 칸에 들어가 생리 현상 해결 겸 머리를 식히기 위해 다음 앱을 실행하다가 문득 이런 생각이 들었다. '나는 하루에 다음 앱을 몇 번이나 켰을까?' 거의 매번 화장실 갈 때마다 실행 했을 것이고, 밥 먹으러 가는 도중이나 친구를 기다리는 와중에도 뉴스를 확인 하러 들어갔었다. 회의가 너무 길어져 따분해질 때나 코딩이 너무 힘들 때도 눌러 봤고 아침에 일어 나자마자 세상에 별 일이 없는지 확인 할 때도 눌러 봤으니 이 모든 걸 다 합하면 대략 하루에 20-30회는 넘게 실행했지 않았을까 하는 생각이 들었다.


그러면 난 스마트폰을 하루에 총 몇 번이나 실행 했을까? 다음 앱 실행횟수만 봐도 20-30회는 넘을텐데 그렇다면 자는 시간을 포함해서 거의 한 시간에 한 번 꼴로 나는 스마트폰을 실행하고 있는 것이다. 맨날 스마트폰을 멀리해야 한다고 말만 했지 실제로 스마트폰을 사용하는 횟수에 대해선 깊이 생각해보지 않았다. 그리고 다른 애플리케이션들은 얼마나 실행하고 있는걸까? 내 스마트폰에 설치된 애플리케이션만 40-50개는 될 텐데 이중에서 내가 자주 사용하는 애플리케이션은 손에 꼽을 것이다.


그래서 "애플리케이션 실행 횟수를 측정해주는" 애플리케이션을 만들기로 했다. 임시로 이름은 "잉여력측정기"로 정했다. 애플리케이션 유형을 따지자면 벤치마크류로 분류 할 수 있는데 난 '자아 성찰 프로그램'으로 분류하고 싶다. 개발에 들어가기 앞서 '잉여력측정기'의 요구사항을 정리해봤다.


  1. 애플리케이션 실행 횟수 기록: 사용자가 사용하는 애플리케이션 모든 실행 횟수를 기록한다. 여기서 실행 횟수를 판단하는 기준은 단순히 애플리케이션 아이콘을 눌러서 실행되는 것 뿐만 아니라 카톡으로 받은 메시지를 볼 때 실행 되는 것과 백그라운드에 있는 애플리케이션 목록(앱리스트를 누를 때 나오는 앱 목록)을 다시 눌러서 실행하는 횟수를 포함하는 것이다. 또한 카운팅 되는 애플리케이션은 유저와 인터랙션(Interaction)이 있었던 애플리케이션이어야 한다. 이는 곧 백그라운드에서 돌아가는 애플리케이션은 카운팅 횟수에서 빠지게 된다. 정확한 실행 횟수를 파악하려면 엄격하게 카운팅 케이스를 분류 할 필요가 있다.

  2. 날짜별로 기록하기: 애플리케이션 실행 횟수를 날짜 별로 기록하고 또 날짜 별로 각 애플리케이션의 실행 횟수를 볼 수 있다. 평일에 비해 주말에는 애플리케이션을 얼마나 더 자주 사용 할 수 있는지 등등을 파악하려고 만든 요구사항이다. 그리고 한 애플리케이션에 대해 날짜별로 사용량이 어떻게 달라지는지 알 수 있도록 한다.

추가적으로 아이디어가 더 떠오를 수 있지만 일단은 두 가지를 중점적으로 보고 구현하기로 했다.


요구사항들을 모두 충족하고 디자인까지 깔끔하게 만들어서 플레이스토어에 런칭해보는 것이 목표다. 다운로드 횟수는 목표로 두지 않기로 했다.. 어차피 한 번 경험해보는게 목표니까.. ㅎㅎ

'공상프로젝트' 카테고리의 다른 글

잉여력측정기 -개발 중단  (0) 2017.04.24
잉여력 측정기 -개발(2/2)  (0) 2017.03.30
잉여력 측정기 -개발(1/2)  (0) 2017.03.26
잉여력 측정기 -기획  (0) 2017.03.26