반응형
파이썬이라는 언어에 대해서 얘기해 볼까 한다. 이렇게 생소한 언어를 얘기하는 필자를 욕하지 말기 바란다. 이제 새로운 언어에 대한 거부감을 거두어 보자.

파이썬을 소개하고 싶은 이유는 필자가 사용해본 언어중에 가장 간단하고 강력하기 때문이다.

장점을 몇가지 소개하면

  • 문장이 깨끗하다.
  • Hello World를 빠르게 구현해 볼 수 있다. (한줄로 구현가능)
  • OS마다 파이썬 인터프리터가 있어서 어떤 시스템에서도 돌아간다.
  • 아이디어가 떠오르면 바로 구현해 볼 수 있다.
  • Web, GUI, Network, DB거침없다.
  • 간단한 파일작업, 모니터링작업등의 관리자가 사용하는 언어로는 최상이다.
  • C, Java로 만든 파일을 붙여서 실행해 볼 수도 있다.
  • 생산성이 무지 좋다(개발시간이 엄청 빠르다) -> Jolt Awards를 받았다.
  • 1980년대부터 사용되어진 언어로 매우 성숙한 언어이며, 풍부한 라이브러리, 모듈등이 존재한다.

단점을 몇가지 소개하면

  • 기계어 코드를 만들어내는 언어에 비해 느리다.
  • Visual XX 언어들처럼 쉽게 GUI화면을 만들지는 못한다. (현재까지는)
  • 전 세계적으로 파이썬을 사용하는 사람은 C, C++, Java를 사용하는 사람보다는 훨씬 적다.
  • 한국어로 된 Document가 부족하다.

굳이 파이썬일 필요는 없지만, 분명 당신의 무기로 삼을 만한 언어를 찾기 바란다. 그 중에 필자가 추천하는 언어는 파이썬이다.

필자가 파이썬을 활용하는 경우를 예를 들면,

  • 필자의 홈페이지(위키, 블로그, 웹메일)를 파이썬으로 빠르게 만들었다.(1주일정도), 버그가 발견되면 10분내로 해결된다.
  • 테스트 데이터생성 제너레이터 : 테스트 데이터를 만드는 일은 참 귀찮지만 파이썬으로 하면 이것마저도 재미있는 일이 되어 버린다.
  • Spike Solution : 커다란 프로그램을 만들어보기전에 가장 Risk가 큰 핵심 로직만 구현해 보는 것.
  • Quiz : 프로그래밍을로 해결할 수 있는 퀴즈를 파이썬으로 풀어보자.
  • 프로젝트가 끝난후 모니터링 툴이나, 관리자 툴

위 같은 경우는 정말 단순히 활용하는 정도이나 다음과 같이 규모있는 프로젝트에도 적용을 시켜 보았다.

  • LG투자 증권 이미지 챠트 - ChartDirector 라는 파이썬 모듈을 이용해 기존의 사용중이던 C Library를 그대로 활용해 이미지 챠트를 구현

또한 널리 알려진 것들도 파이썬으로 작성되었다.

  • Google의 검색엔진 일부
  • Gmail
  • Nasa의 많은 프로그램들
  • 리눅스의 작지만 유용한 유틸리티
  • Zope, Twisted등의 막강한 Framework

관심이 생겼다면 함께 간단한 프로그램을 하나 만들어 보자. 우리가 파이썬으로 처음 만들어볼 프로그램은 피보나치 수열이다.

다들 잘 알고 있겠지만 피보나치 수열에 대해 간단히 설명해 보면 다음과 같다.

f(n+2) = f(n+1) + f(n) : f(3)은 f(2)+f(1)이 된다. 단(f(1)=1, f(2)=1, n>=1)

피보나치 수열을 나열해 보면
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89,,,

파이썬으로는 다음과 같이 시작해보자. 다음은 파이썬으로 하는 TDD개발의 예이다.

fibo.py

import unittest

if __name__ == "__main__":
    unittest.main()

파이썬에서 유닛테스트를 할 수 있는 기본적인 틀이라고 보면 되겠다.

테스트를 하나 만들어보자.

import unittest

class FiboTest(unittest.TestCase):
    def testFibo(self):
        self.assertEquals(1, fibo(1))

if __name__ == "__main__":
    unittest.main()

위 코드가 생소하다고 어리둥절하지 않기를 바란다. 파이썬 문법은 무시하고 코드가 무엇을 나타내는지 유추해 보자.

FiboTest는 테스트를 수행하는 코드임을 알 수 있다. unittest라는 모듈의 TestCase라는 클래스를 상속받아 assertEquals라는 명령을 수행하는 것이다.

assertEquals는 좌측과 우측의 값이 일치하는지를 조사해서 틀릴경우 에러를 내게끔 되어있는 메써드이다. 무엇을 의미하는 코드인지 이제 짐작이 갈 것이다.

fibo(1)이라는 함수의 결과값이 1이 나온는가? 라고 묻고있고 그렇게 되기를 간절히 기다리고 있지 않은가?

실행해보면 파이썬은 fibo라는 함수가 없다고 에러를 낼 것이다. 그렇다면 파이썬 인터프리터가 원하는데로 fibo함수를 만들어보자. 1을 원하고 있으니 1을 리턴해 주도록 만들어보자.

def fibo(num):
    return 1

위 함수를 테스트안에 만들고 실행해 보면 테스트는 놀랍게도 통과해 버린다. fibo(1)값이 1이 리턴된다고 피보나치 수열이 완성된 것이 아님을 우리 모두는 알고 있다. 자, 이제 우리가 해야 할 일은?

fibo(2)라는 값이 1가 나오는지를 살펴보자.

class FiboTest(unittest.TestCase):
    def testFibo(self):
        self.assertEquals(1, fibo(1))
        self.assertEquals(1, fibo(2))

이 테스트도 역시 통과된다. 놀랍게도 이 멍청한 fibo함수는 테스트를 두개나 통과해 버린다. 이 함수가 멍청함을 다음을 통해서 증명해 보도록 하자.

class FiboTest(unittest.TestCase):
    def testFibo(self):
        self.assertEquals(1, fibo(1))
        self.assertEquals(1, fibo(2))
        self.assertEquals(2, fibo(3))

역시 예상대로 테스트는 실패한다. 파이썬 인터프리터는 친절하게도 다음처럼 실패한 이유를 알려준다.

F
======================================================================
FAIL: testFibo (__main__.FiboTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fibo.py", line 10, in testFibo
    self.assertEquals(2, fibo(3))
AssertionError: 2 != 1

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

2라는 기대값이 나오기를 희망하는데 fibo(3)은 1을 리턴했기 때문에 실패하게 되었음을 확인할 수 있다. 자, 그렇다면 어떻게 이 fibo함수를 조금 더 영리하게 만들 수 있을까?

필자는 다음과 같이 해 보았다.

def fibo(num):
    if num in [1,2]:
        return 1
    else:
        return 2

num이 1이나 2이면 1을 리턴하고 다른경우에는 2를 리턴한다. 바보같다고 놀려도 좋다. 이것이 필자가 프로그램을 만드는 방식이다. 무엇보다도 테스트를 빨리 통과하는것이 최우선 과제이기 때문이다.

테스트는 물론 통과된다. ^^

하지만 위 테스트가 모든것을 통과할리는 없을 것 같다. 아무리봐도...
또한 위 fibo함수를 아무리 들여다 봐도 무엇을 바꾸어야 할지 알수가 없다. (필자는 평범한 보통사람이기 때문이다.) 자, 또 테스트를 해 보도록 하자.

class FiboTest(unittest.TestCase):
    def testFibo(self):
        self.assertEquals(1, fibo(1))
        self.assertEquals(1, fibo(2))
        self.assertEquals(2, fibo(3))
        self.assertEquals(3, fibo(4))

fibo(4)는 fibo(2)+fibo(3)인 1+2인 3이 나와야만 한다는 의도다. 역시 테스트는 실패하고 만다. 너무 실망하지 말자.

위 테스트를 통과하기 위해서 else if문을 하나 더 만들면 될 것이다. (?)

다행히 여러분과 마찬가지로 필자는 멍청이가 아니다....
약간 생각해 볼 시간이 필요하다.

자, 조급한 마음을 잠시 덮고 생각을 해보자. 실패했던 테스트는 주석으로 처리하고 항상 테스트가 통과되도록 우선 만들자.

...

테스트 코드를 다음과 같이 바꾸어 볼 수 있을것 같다.

class FiboTest(unittest.TestCase):
    def testFibo(self):
        self.assertEquals(1, fibo(1))
        self.assertEquals(1, fibo(2))
        self.assertEquals(1+1, fibo(3))
        #self.assertEquals(1+2, fibo(4))

피보나치 수열은 이전 두항의 합을 리턴해야 하기 때문에 위와 같이 테스트를 만들 수 있었다. 조금 더 명확한 테스트 코드가 아닌가?

어 그렇다면, 다음과 같이 바꾸어도 되지 않을까?

class FiboTest(unittest.TestCase):
    def testFibo(self):
        self.assertEquals(1, fibo(1))
        self.assertEquals(1, fibo(2))
        self.assertEquals(fibo(1)+fibo(2), fibo(3))
        #self.assertEquals(1+2, fibo(4))

앗, 테스트가 통과된다.

테스트코드를 위와같이 바꾸었더니 중요한 단서가 발견되었다. 피보나치 수열의 정의에 의해서 fibo(3)은 fibo(1)+fibo(2)이고, fibo(4) = fibo(2)+fibo(3)이라는 것이다.

그렇다면 fibo함수에 위 내용을 적용할 수 있을것만 같다. 3이나 4라는 값은 모두 1,2가 아닌 값이므로 다음과 같이 만들 수 있을 것 같다.

def fibo(num):
    if num in [1,2]:
        return 1
    else:
        return fibo(num-2)+fibo(num-1)

이전에 실패했던 테스트를 다시 해보도록 하자.

class FiboTest(unittest.TestCase):
    def testFibo(self):
        self.assertEquals(1, fibo(1))
        self.assertEquals(1, fibo(2))
        self.assertEquals(fibo(1)+fibo(2), fibo(3))
        self.assertEquals(fibo(2)+fibo(3), fibo(4))

테스트는 모두 통과가 된다. 브라보~
불과 몇번의 테스트를 거치면서 완벽한 피보나치 수열 함수가 생성되는 순간인 것이다.

불안하다면 다음의 테스트를 하나 더 추가해 보도록 하자.

def testFiboNumber(self):
    self.assertEquals(55, fibo(10))

피보나치 수열의 10번째는 55가 되어야만 한다. 테스트는 통과한다. 이정도면 우리가 만든 피보나치 수열이 안전하다는 판단이 든다.

위 처럼 테스트 코드를 만들고 실제코드를 만들어가는 방법을 TDD(Test Driven Development)라고 한다. 이것은 필자가 개발을 하는 방식이기도 하며 앞으로 분명 중요하게 대두될 프로그래밍 개발방법이다.

다음은 우리가 지금껏 만들어온 fibo 프로그램의 전부이다.

import unittest

def fibo(num):
    if num in [1,2]:
        return 1
    else:
        return fibo(num-2)+fibo(num-1)

class FiboTest(unittest.TestCase):
    def testFibo(self):
        self.assertEquals(1, fibo(1))
        self.assertEquals(1, fibo(2))
        self.assertEquals(fibo(1)+fibo(2), fibo(3))
        self.assertEquals(fibo(2)+fibo(3), fibo(4))

    def testFiboNumber(self):
        self.assertEquals(55, fibo(10))

if __name__ == "__main__":
    unittest.main()
반응형
Posted by Real_G