지난 1편에서는 코드의 가독성을 높이고 ‘사람이 읽기 쉬운 코드’를 작성하는 방법에 대해 알아보았습니다. 이번 시간에는 프로그래밍 도중 자주 맞닥트리게 되는 어려운 문제들을 해결하는 법에 대해 알아보겠습니다.
프로그래밍은 마치 운동과 같습니다. 비슷한 문제를 반복해서 생각하고 여러 문제에 적용해보면 자연스레 프로그래밍 능력이 스며들어 문제해결 속도가 빨라지고 응용력도 향상되는 것이죠. 학교와 현업에서 제가 마주했던 다양한 사례들을 통해 어려운 문제를 쉽게 해결하는 방법을 알려드리겠습니다.
어려운 문제들은 머리 속에 해결책이 바로 떠오르지 않습니다. 그럴수록 쉽게 생각해야 합니다. 코넬대 응용수학과 교수 스티븐 스트로가츠(Steven Strogatz)의 『X의 즐거움』이라는 책에 나온 문제를 살펴보겠습니다.
아래 그림처럼 온수와 냉수가 나오는 욕조가 있습니다. 냉수만 틀었을 때는 욕조 한 개를 가득 채우는데 60분이 걸리고 온수만 틀었을 때는 30분이 걸립니다. 그렇다면 온수와 냉수를 같이 틀었을 때 욕조 한 개를 가득 채우는데 얼마나 걸릴까요? 잠시 스크롤을 멈추고 풀어보시기 바랍니다.
잘 풀어 보셨나요? 이 문제의 답은 20분입니다. 정확한 답도 물론 중요하겠지만, 풀이 과정도 문제해결에 있어서 매우 중요합니다. 정해진 풀이 과정은 없지만 더 쉬운 해결 방법이 존재하니까요. 두 가지 풀이 과정을 소개해드리겠습니다. 한번 비교해보시죠.
첫 번째 풀이는 분수를 이용하는 것입니다. 욕조의 양을 1이라고 가정해 보겠습니다. 그럼 냉수의 경우는 60분 당 1이라는 양을 채우므로 1분당 1/60이라는 양을 채우게 됩니다. 온수의 경우는 30분당 1이라는 양을 채우므로 1분당 1/30이라는 양을 채우게 됩니다. 결국 냉수와 온수가 1분당 동시에 함께 채우는 양의 합계는 1/60 + 1/30 이죠. 통분을 하면 1/60 + 2/60이 되어 3/60이 됩니다. 3/60을 온수와 냉수가 같이 채우는데 1분이 걸리므로 비례식(1분 : 3/60 = 60/3분 : 1)을 통해 욕조 한 개를 채우는데 20분이 걸린다는 것을 알 수 있죠.
보통 사람들은 위와 같은 첫 번째 방식으로 문제를 풀게 됩니다. 그렇다면 두 번째 풀이도 한번 볼까요?
냉수는 60분에 욕조 한 개를 채웁니다. 온수는 30분에 욕조 한 개를 채우므로 60분 동안 욕조 두 개를 채우죠. 결국 동시에 온수와 냉수를 틀면 60분 동안 총 3개의 욕조를 채우는 셈입니다. 3개를 채우는데 60분이므로 1개를 채우는 데는 60/3, 즉 20분이 걸리게 됩니다.
어떤가요? 두 번째 풀이는 주로 자연수를 이용합니다. 분수를 따로 생각하지 않아도 되므로 충분히 암산이 가능하죠. 이렇듯 문제 해결을 할 때 가장 중요한 것은 생각하기 쉬운 방법으로 해결하는 것입니다. 문제가 풀리지 않고 알고리즘이 너무 복잡해진다 싶을 때는 한 번 더 쉬운 방법이 없는지 고민을 해봐야 합니다. 물론 처음부터 쉬운 방법을 떠올리기 쉽지 않습니다. 운동과 마찬가지로 꾸준한 연습이 필요한 것이죠.
수학자 제임스 탠턴(James Tanton)은 수학에서 가장 중요한 것은 명제에 대한 참/거짓 증명이며, 그 다음에 중요한 것이 패턴을 발견하는 것이라고 하였습니다. 설령 참인지 거짓인지 증명이 안되어도 수가 증가하거나 감소할 때 계속 되는 패턴이 발견된다면 매우 의미가 있는 것이죠.
실제로 제가 2년차 때 겪었던 프로젝트로 패턴 발견하기의 중요성을 얘기할 수 있을 것 같습니다. 그 당시 프로젝트는 데이터의 통계를 내어 여러 분야에 이용하는 것이었습니다. 조회 화면의 구성은 대략 아래 그림1과 같은 식이었습니다.
<그림1>
고객이 원하는 프로그램은 From 또는 To를 선택할 경우, 미리 설정된 조회 기간에 맞춰 다른 항목을 채워주는 것이었습니다. 가령 조회기간이 3개월이라면, From 캘린더에서 2015년 3월을 선택했을 경우 To에는 2015년 5월이 자동으로 선택되는 것이었습니다. 여기서는 조회기간이 1년 이내라는 가정하에 진행하겠습니다.
최초 요구사항은 조회기간이 3개월 이었습니다. 선택한 월에 두 개의 달만 더 조회하면 되므로 선택한 월에 2를 더하였습니다. 하지만 11월 과 12월은 2를 더하면 13과 14가 되므로 이 둘에 대해서만 if문으로 예외 처리를 하였습니다. 하지만 문제는 요구사항이 계속 변경된다는 것이었습니다. 조회 기간이 4개월로, 다시 6개월로 변경되었죠. 그럴 때마다 if문의 조건을 수정했는데, 요구사항에 따라 계속 소스가 바뀌니 좋은 코드라고 할 수 없었습니다.
유지보수가 용이한 코드로 수정하기 위해 고민하던 중, 숫자의 패턴을 파악할 수 있었습니다. From과 To 모두 연속되는 12개의 숫자로 이루어지기 때문에 주기성을 가집니다. %(나머지 연산자)를 사용하여 12로 나눈 나머지 숫자를 구한다면 어떻게 될까요? 요구사항이 수정되어도 별도 예외처리가 필요 없을 것으로 예상됩니다. 실제로 %(나머지 연산자)는 월이나 요일, 날짜 혹은 규칙을 발견하는 문제에 자주 이용됩니다.
<식1>
이제 X % 12라는 식을 세우고 X에 무엇을 넣을지 생각해봐야겠습니다. 일단 무엇이 들어가야 할지 아직 모릅니다. 미리 설정된 조회 기간이 3개월일 경우 선택하는 달을 포함하여 두 개의 달을 더 조회해야 하니 각 월에 2를 더해봅시다. 그럼 3 ~ 14가 되고 이를 <식1>의 X에 넣어봅시다. 방금 말한 과정이 아래 <그림2>에 나와 있습니다.
<그림2>
결과를 보면 10월은 0에 매핑되어 어긋났고, 규칙이 만들어지지 않았습니다. 보다 정확한 패턴을 만들기 위해 조금 변형해보겠습니다. 이해를 돕기 위해 <그림3>으로 먼저 과정을 보시기바랍니다.
<그림3>
12로 나눈 나머지는 0 ~ 11입니다. 우리가 사용하고자 하는 실제 월보다는 1이 적죠. 그럼 여기에 1을 더하면 1~12의 패턴을 구할 수 있습니다. 하지만 1을 더했기 때문에 실제 조회기간 보다는 1이 큽니다. 만약 처음부터 1을 빼고 12로 나눈 나머지에 1을 다시 더한다면 어떻게 될까요? 네! 드디어 우리가 원하던 결과가 나왔습니다.
위 사례를 통해 알 수 있듯이, 패턴을 발견하여 로직에 적용하면 다양한 요구사항에 유연한 프로그램을 만들 수 있습니다. 만약 규칙이 발견되지 않는다면 데이터 변형을 통해 만들 수 있죠. 처음에는 0월이라는 불가능한 데이터가 나왔지만 From 데이터를 수정하여 제대로 된 패턴을 만들 수 있었던 것처럼요. 물론 이런 방법을 한번에 생각해 내기는 쉽지 않습니다. 하지만 비슷한 문제를 여러 번 고민하고 다양한 문제에 적용하여 내공을 쌓는다면 자연스럽게 해결 방법을 떠올릴 수 있습니다.
문제가 너무 많아서 해결하기 어렵다면 더 작은 문제로 쪼개서 해결하는 방법이 있습니다. 당연한 얘기지만 이런 식으로 접근하지 않는 사람들도 많습니다.
가령 프로그래밍 문제로 로또 복권 추첨 시스템 같은 주제가 자주 등장하는데, 막상 아무 것도 없는 상태에서 처음 프로그래밍을 시작하려면 막막합니다. 어떤 기능부터 구현해야 할지도 모르겠고, 제대로 된 구조가 잡히지 않은 채 점점 길어지는 코드에 머리 속도 더 복잡해 집니다. 에러가 날 경우 디버깅하기도 힘듭니다.
이럴 때 처음부터 다시 프로그래밍하기 힘드니 나눠서 생각해야 한다는 것입니다. 로또 복권 추첨 시스템은 구입, 발행, 추첨 등의 큰 기능들이 있습니다. 구입 기능에는 지불, 거스름돈 반환, 번호 생성, 번호 저장… 여러 가지 작은 기능들이 있을 것입니다. 이렇듯 큰 기능에서 작은 기능 들을 뽑아 나가야 합니다. 이 때 중요한 것은 추출한 각 기능들이 서로 독립적이어야 한다는 것입니다. 가령 지불과 거스름돈 반환을 묶어서 돈 관리 기능에서만 끝내버린다면 너무 추상적이고 코딩을 할 때도 추상적인 생각이 반영 될 수 밖에 없습니다. 애매모호한 것이죠. 물론 돈 관리 기능이 끝이 아니라 그 기능에서 또 독립성 있게 추출해내면 됩니다.
소프트웨어 공학을 배우셨다면 모듈의 기능연관성을 나타내는 ‘응집도’와 모듈간 인터페이스 복잡도를 나타내는 ‘결합도’에 대해 들어보셨을 겁니다. 「응집도는 높이고, 결합도는 낮추고!」 프로그래밍 하는 순간마다 이 문장을 기억하시고 적용하시면 품질 좋은 프로그램이 나올 것입니다.
마지막으로 프로그래밍에 자주 인용되는 ‘KISS원칙’으로 글을 마칠까 합니다.
Keep It Simple, Stupid! (바보야, 간단히 설명해!)
LG CNS 페이스북 바로가기 : http://on.fb.me/1L74Jtm
글 ㅣ LG CNS 의료솔루션2팀
<’초보 프로그래머를 위한 코딩 꿀팁’ 연재 현황 및 향후 계획>
● 사람이 읽기 쉬운 코드 : http://blog.lgcns.com/841
● 어려운 코딩, 생각하고 관찰하고 쪼개라! : http://blog.lgcns.com/872
● 인터넷 보다 책! : http://blog.lgcns.com/899
● 안드로이드 제대로 알고 코딩 하기 : http://blog.lgcns.com/931
● 웹 맨땅에 헤딩하지 않기 : http://blog.lgcns.com/959
'CNS Story' 카테고리의 다른 글
LG CNS, 협력회사 ‘기술 지킴이’로 나선다 (0) | 2015.08.22 |
---|---|
LG CNS 대학생 기자단 5기 발대식 현장 속으로 ~ (0) | 2015.08.21 |
LG CNS 대학생 기자단 4기가 알려주는 합격 노하우! (0) | 2015.07.10 |
사람이 읽기 쉬운 코드 (0) | 2015.07.09 |
LG CNS 대학생 기자단 4기의 활동을 파헤쳐보자! (0) | 2015.07.08 |