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


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


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


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


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

시스템구조에서 소프트웨어의 전반 구조를 추상적으로 정리 했다면 컴포넌트 사양에서는 전반 구조를 구체적으로 채우는 작업을 하게 된다. 앞선 작업에서는 클래스의 이름과 역할을 간단히 소개하고 속성(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


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

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


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


그래도 이기적인 총무처럼 네이티브 애플리케이션인 경우에는 안드로이드에서 기본적으로 제공하는 구조를 따라가면서 설계해가면 된다. 그런데 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. 테이블 관계도


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

앞 포스트에선 이기적인 총무 소프트웨어를 만들게된 계기(시스템 개요)와 시스템이 제공하는 기능 및 품질(요구사항)에 대해서 소개했다면 지금부터는 앞 문서에서 설명한 소프트웨어가 사용자에게 화면으로 보여지는 형태를 정리한 문서, 즉 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 부서와 개발자마다 측정 방식이 달라 릴리즈 단계에서 애를 먹을 수 있고 제약 사항이 비현실적이면 애초 구현 불가능한 것을 삽질한 셈이 된다. 



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

구조설계서에 시스템에서 사용할 클래스 다이어그램을 그리다 보니 작성한 내용 그대로 실제로 시스템이 적용 할 수 있을지 의문이었다. 문서의 내용은 그럴듯 한데 여기에 안드로이드 시스템 특유의 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에 테이블을 추가로 등록하면 되고 테이블을 삭제할 경우에는 등록하는 부분의 테이블을 지우면 된다. 이전보다 훨씬 쉽게 변경이 가능한 코드가 됐다.

단순히 기능적 요구사항만 만족하는 소프트웨어가 아니라 변경 용이성, 성능, 사용 편의성과 같은 품질 요소와 객체 지향적인 관점을 고려해서 구현하는 것이 구조적으로 튼튼한 소프트웨어를 만드는 일이다. 이기적인 총무를 처음 만들때는 빠르게 출시 해보는것이 목적이어서 기능 구현과 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 이해 가능한 글을 여기서 보다니....!! ㅋㅋㅋㅋ 화이팅입니다.!

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


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