객체 지향 프로그래밍(OOP) 소개

안내: Think Python 4장How to Think Like a Computer Scientist: Learning with Python 3 내용의 일부를 번역 및 수정하여 활용한 내용입니다.

OOP 정의

객체 지향 프로그래밍(Object-Oriented Programming)을 줄여서 보통 OOP라고 부른다.

OOP는 프로그래밍 기법 중의 하나이며 OOP를 지원하는 언어를 객체 지향 언어라고 부른다. 파이썬을 포함하여 자바, C++, C#, 루비, 자바스크립트 등 많은 컴퓨터 프로그래밍 언어가 객체 지향을 지원한다.

OOP와 대비되는 개념으로 절차 지향 프로그래밍이 주로 언급된다. 절차 지향 프로그래밍은 수행해야 할 일을 순차적으로 처리하는 과정을 묘사하는 것을 가장 중요하게 여기며 프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법이다. C, HTML 등이 대표적인 절차 지향 프로그래밍언어이다.

"해야 할 일을 순차적으로 처리한다"는 표현은 가장 기본적인 프로그래밍 기법이며, 모든 프로그램은 원하는 결과를 얻기 위한 과정을 논리적이며 순차적으로 처리하도록 구현되어야 한다.

OOP 역시 예외가 아니다. 하지만 OOP는 구현해야 할 객체들을 선택하고 객체들 사이의 유기적인 관계를 논리적으로 묘사하는 데에 보다 많은 방점을 둔다. 즉, 프로그램 전체를 하나로 묶어서 구현하는 방식이 아니라 프로그램을 구성하는 객체들을 중심으로 해서 구현해야 할 프로그램을 완성시키는 방식으로 프로그래밍을 진행한다.

OOP와 객체 활용

OOP에 대한 이해는 아래 두 가지 질문과 관련되어 있다.

  1. 객체(object)란 무엇인가?
  2. "객체를 중심으로 프로그래밍한다" 라는 말의 의미는 무엇인가?

위 질문들에 대한 직접적인 설명 대신에 객체가 어떻게 활용되는가를 실전 예제를 통해 소개한다.

주의사항

모든 코드에 그래픽 도구를 지원하는 turtle 모듈을 사용한다. 따라서 그래픽 도구를 지원하지 않는 온라인 서버에서는 제대로 작동하지 않는다. 아래 두 가지 환경 중에 하나를 추천한다.

  1. 온라인
    1. 트링킷 활용 추천 (초보자용)
    2. 레플릿에서 Python (with Turtle)를 선택하여 새로운 repl 생성.
  2. 오프라인 환경: VSCode + Anaconda

예제: 거북이 그래픽

그림그리기와 관련된 도구를 담고 있는 turtle 모듈을 소개한다. turtle 모듈은 파이썬에 기본적으로 포함되어 있다. turtle 모듈에 대한 상세한 설명은 이곳 참조할 것을 추천한다. 여기서는 간단한 예제를 통해 객체(인스턴스)를 클래스의 인스턴스로 구하는 과정을 보여주고자 한다.

먼저 아래 코드를 mypolygon1.py 파일에 저장하고 실행해 보자.


import turtle

wn = turtle.Screen()
bob = turtle.Turtle()

bob.forward(100)
bob.left(90)
bob.forward(100)
bob.left(90)
bob.forward(100)
bob.left(90)
bob.forward(100)
bob.left(90)

wn.mainloop()

그러면 아래 이미지처럼 한 변의 길이가 100인 정사각형을 그리는 화살촉의 움직임을 볼 수 있을 것이다.

주의: 픽셀(pixel, 화소)은 컴퓨터 모니터 등과 같은 디지털 화면를 구성하는 기본 단위이다. 소위 해상도란 바로 픽셀의 개수를 일컫는다. 예를 들어, Full HD(FHD)의 해상도는 1920×1080으로 표시되는데 이는 화면을 가로 1920칸, 세로 1080칸의 격자무늬로 쪼갠 후에 각각의 격자에 하나의 색 정보가 입력되었을 의미한다. 이런 격자 하나하나를 픽셀이라 부른다. 해상도가 높아질 수록 픽셀 수가 많아지며 따라서 보다 선명한 색채구현히 가능해진다.

이제 위 코드를 한 줄씩 살펴보자.

  • turtle 모듈을 불러온다.
    import turtle
    
  • turtle 모듈에 포함된 Screen 함수는 같은 모듈에 선언되어 있는 TurtleScreen 클래스인스턴스(객체)를 하나 생성해서 리턴값으로 내준다.
    wn = turtle.Screen()
    
    TurtleScreen 클래스의 인스턴스를 하나 생성한다는 것은 그림을 그릴 도화지(캔버스)를 한 장 준비한다는 의미이다. 즉, 위 그림에서처럼 정사각형를 그릴 도화지 역할을 수행하는 창을 하나 생성한다.
  • 도화지 속성 변경: 도화지 창에 이름을 지정할 수 있다. 기본값은 Python Turtle Graphics이며 _title 라는 (숨은) 속성에 저장되어 있다.

    주의: 밑줄(언더스코어, underscore)로 시작하는 변수는 숨겨진 변수를 가리킨다. 즉, 사용자가 일부러 선택하여 값을 지정하기 보다는 다른 도구를 활용하라는 의미이다.

    도화지의 이름을 지정하려면 title이라는 함수를 아래와 같이 활용하면 된다. 예를 들어, 도화지의 이름을 'Hello, Bob and Alice!'로 지정하려면 아래와 같이 실행한다.

    wn.title("Hello, Bob and Alice!")
    
  • turtle 모듈에서 정의되어 있는 Turtle 클래스의 인스턴스(객체)를 하나 생성해서 bob 변수에 할당한다.
    bob = turtle.Turtle()
    
    Turtle 클래스의 인스턴스를 하나 생성한다는 것은 그림을 그리는 도구인 펜을 하나 준비한다는 의미이다. 즉, 아래 그림의 화살촉 모양처럼 선을 그리는 펜 역할을 수행하는 도구를 하나 생성한다.

  • 펜을 전진시키면서 선 그리기
    bob.forward(100)
    
    화살촉 모양의 펜은 현재 동쪽을 가리키고 있으며 펜이 현재 가리키는 방향으로 100픽셀만큼 펜을 전진시키면서 선을 긋도록 한다.

  • 왼쪽으로 회전시키기
    bob.left(90)
    
    펜이 가리키는 방향을 왼편, 즉 시계 반대방향으로 90도 회전시킨다. 펜은 가리키는 방향만 바꿀뿐 이동하지는 않는다.

  • 전진과 회전을 3번 더 반복한다.
    bob.forward(100)
      bob.left(90)
      bob.forward(100)
      bob.left(90)
      bob.forward(100)
      bob.left(90)
    
  • 생성된 윈도우 창을 X 버튼이 눌릴 때까지 유지시킨다.
    wn.mainloop()
    
  • 거북이 속성 변경: 그림 그릴 때 사용되는 펜의 색깔인 검은색은 _pencolor 라는 (숨은) 속성에 저장되어 있다. 아래와 같이 확인할 수 있다.

    print(bob._pencolor)
    

    주의: _pencolor 또한 숨겨진 변수이다.

    펜의 색깔을 지정하려면 pencolor 라는 메서드를 아래와 같이 활용하면 된다. 예를 들어, 펜의 색깔을 옅노랑(light yellow)로 지정하려면 아래와 같이 실행한다.

    print(bob.pencolor("lightyellow"))
    

클래스와 인스턴스

아래 내용을 기억하고 있어야 한다.

  • 클래스에는 변수와 함수들이 선언되어 있다.
  • 하나의 클래스에 포함된 변수와 함수들은 특별한 성질의 객체를 묘사하고 다루기 위해 필요한 속성과 도구를 저장한다.

클래스에서 선언된 변수와 함수들은 해당 클래스를 통하여 또는 해당 클래스의 인스턴스를 통하여 호출될 수 있다.

  • 속성(attribute)
    • 클래스에서 선언된 변수
    • 생성되는 객체들이 사용하는 값 또는 특성값 저장
  • 메서드(method)
    • 클래스에서 선언된 함수
    • 속성 정보를 이용하고 다룰 수 있는 도구

특정 클래스의 인스턴스(instance)는 해당 클래스에서 선언된 속성과 기능을 모두 물려받는 대상을 의미한다. 객체(object)는 특정 클래스의 인스턴스를 부르는 보다 일반적인 표현이다.

참고: 파이썬에서 사용되는 모든 값은 특정 클래스의 인스턴스, 즉 객체로 선언된다. 지금까지 사용해왔던 모든 자료형들도 모두 클래스로 정의되어 있다. 심지어는 클래스 조차도 type 이라는 클래스의 객체이다.

앞서 소개한 거북이 그래픽 예제에서 사용된 turtle 모듈과 관련된 프로그래밍 기본요소는 다음과 같다.

  • 모듈: turtle
  • 함수: Screen (대문자로 시작하는 이름에 주의)
  • 클래스: TurtleScreen, Turtle
  • TurtleScreen클래스

    • 인스턴스: wn
    • 속성: _title
    • 메서드: title, mainloop
  • Turtle 클래스

    • 인스턴스: bob
    • 속성: _pencolor
    • 메서드: pencolor, left, forward

Screen 함수의 리턴값

엄밀히 말하면 Screen 함수의 리턴값은 TurtleScreen의 자식 클래스인 _Screen 클래스의 인스턴스이다. 하지만 사용된 밑줄에서 알 수 있듯이 숨겨진 클래스이기 때문에 부모 클래스인 TurtleScreen을 기본적으로 언급한다.

또한 Screen 함수의 리턴값은 _Screen 클래스의 싱글턴 객체(singleton object)이다. 싱글톤 객체라 함은 해당 클래스의 인스턴스가 하나 뿐이라는 말이다. 따라서 Screen 함수를 이용하여 생성할 수 있는 도화지는 단 하나뿐이다.

변수와 메서드 종류

클래스에서 선언된 메서드와 변수를 사용법에 따라 여러 종류로 분류할 수 있다.

  • 변수 종류: 인스턴스 변수, 클래스 변수
  • 메서드 종류: 인스턴스 메서드, 클래스 메서드, 정적 메서드

변수와 메서드의 종류에 대한 보다 상세한 설명은 코드 추상화: 클래스와 객체 1부2부에서 다룬다.

객체 중심 프로그래밍 예제

클래스를 이용하여 생성한 객체들은 기본적으로 상호 독립적으로 활용된다. 하지만 객체들 사이의 상호 교감도 가능하다. 따라서 각각의 객체가 수행하는 기능과 객체들 사이의 교감을 중심으로 해서 프로그램을 작성하는 것이 객체를 중심으로 프로그래밍한다의 기본 의미이다.

여기서는 Turtle 클래스의 인스턴스를 두 개 생성하여, 생성된 두 객체를 상호 독립적으로 활용하는 방법을 설명한다. 객체들 사이의 교감을 구현하는 방식은 나중에 다룰 것이다.

예제 2

아래 코드를 mypolygon2.py 파일에 저장하고 실행하면 두 개의 펜을 이용하여 사각형과 삼각형을 그린 결과를 얻을 것이다. mypolygon.py와는 달리 반복되는 코드를 for 반복문으로 묶었다.

아래 코드에서 Turtle 클래스의 인스턴스가 두 번 생성되었다. 또한 alice 객체에 대해세만 아래 인스턴스 메서드가 추가로 사용되었다.

  • shape: 해당 인스턴스의 펜 모양을 지정된 모양으로 변경시킴
  • color: 해당 인스턴스의 선 색깔을 지정된 색깔로 변경시킴

주의: alice의 속성을 변경한다 하더라도 bob의 속성은 전혀 변하지 않는다. 자세한 설명은 코드 추상화: 클래스와 객체 1부를 참조한다.


import turtle

wn = turtle.Screen()

# bob 생성
bob = turtle.Turtle()

# alice 생성
alice = turtle.Turtle()
alice.shape("turtle")       # 펜 모양을 거북이로 변경
alice.color("red")          # 선 색깔을 빨강으로 변경

alice.penup()               # 펜 들기: 선 그리지 않음
alice.backward(100)         # 뒤로 100픽셀 이동
alice.pendown()             # 펜 내리기: 선 그리기 시작함

# bob 으로 사각형 그리기
for i in range(4):
    bob.forward(100)
    bob.left(90)

# alice로 삼각형 그리기
for i in range(3):
    alice.forward(100)
    alice.right(120)        # 시계방향으로 회전

wn.mainloop()

예제 3

아래 코드를 mypolygon3.py 파일에 저장하고 실행하면 이전 코드와는 달리 배경화면의 색깔과 그림 제목을 다르게 지정한 것을 확인 할 수 있다.

  • bgcolor: Screen 클래스의 인스턴스 메서드. 그림의 배경화면 색 지정
  • title: Screen 클래스의 인스턴스 메서드. 그림의 제목 지정

import turtle

wn = turtle.Screen()
wn.bgcolor("lightyellow")               # 배경화면 색깔 정하기
wn.title("Hello, Bob and Alice!")       # 그래픽 제목 정하기

# bob 생성
bob = turtle.Turtle()

# alice 생성
alice = turtle.Turtle()
alice.shape("turtle")
alice.color("red")

alice.penup()
alice.backward(100)
alice.pendown()

# bob 으로 사각형 그리기
for i in range(4):
    bob.forward(100)
    bob.left(90)

# alice로 삼각형 그리기
for i in range(3):
    alice.forward(100)
    alice.right(120)

wn.mainloop()

turtle 모듈 정보

turtle 모듈에 포함된 클래스와 클래스 메서드와 속성의 활용법은 docs.python.org를 참조한다. 또한 turtle 모듈의 소스코드는 cpython의 turtle.py에서 확인할 수 있다.

Turtle 클래스의 메서드 소개

Turtle 클래스의 메서드 일부를 소개한다. 아래에 언급된 메서드의 기능은 realpython.com에서 확인할 수 있다.

  • goto: 도화지의 중심을 원점으로 하며 2차원 상의 좌표를 지정하여 거북이를 이동시킨다.
    • 예제: bob.goto(100)
  • home: 원점, 즉, 도화지의 중심으로 거북이를 돌려 보낸 후, 오른쪽을 바라보도록 한다.
    • 예제: bob.home()
  • fd, forward: 현재 바라보는 방향으로 전진
  • bd, backward: 현재 바라보는 방향 반대편으로 후진. 바라보는 방향 유지.
  • rt, right: 시계방향으로 90도 회전
  • lt, left: 시계반대방향으로 90도 회전
  • circle: 원 그리기. 인자는 반지름.
    • 예제: bob.circle(100)
  • dot: 원반 그리기. 원 내부는 색으로 채워짐. 원의 지름과 색깔 지정 가능.
    • 예제: bob.circle(20)
    • 예제: bob.circle(20,red)
  • shape: 거북이 모양 설정. 기본은 화살촉 모양.
    • 예제: bob.shape('turtle')
    • 사용 가능 모양
      • 확인법: wn.getshapes()
      • 리턴값: ['arrow', 'square', 'triangle', 'classic', 'turtle', 'circle']
  • shapesize: 거북이 크기 설정
    • 세 개의 인자 사용: 두께(width), 길이(length), 선두께(outline width)
  • pensize: 선 두께 지정
    • 예제: bob.pensize(5)
  • fillcolor: 거북이 내부 색상 지정
    • 예제: bob.fillcolor('red')
  • color: 선 및 내부 색상 지정
    • 예제: bob.color('blue', 'red')
  • begin_fillend_fill: 두 메서드로 감싸인 코드에서 그린 도형 내부를 거북이 색으로 채우기
    bob.begin_fill()
      bob.fd(100)
      bob.lt(120)
      bob.fd(100)
      bob.lt(120)
      bob.fd(100)
      bob.end_fill()
    
  • speed: 펜 속도 조절하기
    • 예제: bob.speed(1)
      가장 빠름 :  0
      빠름     :  10
      보통     :  6
      느림     :  3
      가장 느림 :  1
  • pen: pencolor, fillcolor, pensize, speed 등 펜과 관련된 여러 성질을 동시에 설정 가능.
    • 예제: bob.pen(pencolor="purple", fillcolor="red", pensize="5", speed="8")
  • pd, pendown: 선 그리는 상태로 전환
  • pu, penup: 선 그리지 않는 상태로 전환
  • stamp: 도장 찍기
    • 예제 4 참조
  • down: 거북이가 마지막으로 실행한 명령 취소
  • clear: 거북이가 그린 모든 도형 삭제. 거북이는 마직막 위치에 머무름.
  • reset: 거북이가 그린 모든 도형 삭제 및 거북이 초기화.
  • clone: 거북이 복제. 모든 상태 그대로 복제.
      bobi = bob.clone()
      bob.color("orange")
      bobi.color("magenta")
      bob.circle(100)
      bobi.circle(60)

예제 4

아래 코드를 mypolygon4.py 파일에 저장하고 실행하면 회오리 모양으로 움직이면서 점차 가속도가 붙는 거북이를 확인할 수 있다.

  • penup: Turtle 클래스의 인스턴스 메서드. 그림 그리지 않는 상태로 전환.
  • stamp: Turtle 클래스의 인스턴스 메서드. 도장 찍기.

import turtle
wn = turtle.Screen()
wn.bgcolor("lightyellow")
bob = turtle.Turtle()
bob.shape("turtle")        # 화살촉 대신 거북이 모양 선택
bob.color("blue")

bob.penup()                # 펜 들기 (이동할 때 선을 그리지 않게 됨)
size = 20
for i in range(30):
   bob.stamp()             # 거북이 모양 도장 찍기
   size = size + 3         # 회전을 점차 크게 돌도록 만들기
   bob.forward(size)       # size 크기만큼 전진하기
   bob.right(24)           # 24도 우회전하기

wn.mainloop()

미니 프로젝트 안내

realpython.com에서 소개한 프로젝트이다.

  • 과제명: 거북이 경주
  • 내용:
    • 두 마리의 거북이가 경주하는 게임
    • 동일 선상에 위치한 두 마리의 거북이가 목적지에 먼저 다다르도록 한다.
  • 규칙:
    • 두 명이 번갈아 가며 주사위를 던진다.
    • 주사위 던지기 결과에 맞춰 거북이를 전진시킨다.
    • 목적지에 먼저 도달한 거북이가 승리한다.

작동 방식은 realpython.com: 거북이 경주 그림 참조

게임 구상은 다음과 같다.

  1. 거북이 한 마리가 최종 목적지에 도달할 때 까지 무한 반복
  2. 반복 내용
    • 두 마리중 한 마리가 목적지 도달 여부 판단
    • 아직 아무도 도착하지 않은 경우
      • 주사위 던지기
      • 던져진 주사위 결과만큼 해당 선수의 거북이 전진시키기
  3. 필요한 프로그래밍 기본 요소
    • if 조건문: 경우에 따라 다른 행동 지시
    • while 반복문: 게임 종료 시까지 무한 반복
    • random.choice: 주사위 구현
      • 주사위 결과에 20 정도 곱한 값을 전진 거리로 사용할 것.

파이썬 거북이 게임 예제

인터네서 검색을 통해 Turtle 모듈을 활용하여 만든 컴퓨터 게임을 쉽게 찾아볼 수 있다. 앞서 언급한 거북이 경주와 같이 매우 단순한 게임부터 보다 고급스런 게임까지 Turtle 모듈만으로도 훌륭한 게임을 구현할 수 있다.

참고자료 몇 개를 언급하면 다음과 같다.

연습문제

Turtle 모듈을 활용한다.

  1. 다각형을 그리는 함수를 구현한다.

    1. 아래 기능을 수행하는 함수 square를 구현하기
      • 매개변수 t 사용: 인자는 거북이 객체
      • 실행하면 정사각형 그리기

    2. square 함수에 length 매개변수 추가
      • 정사각형 한 변의 길이가 length의 인자값으로 지정

    3. polygon 함수 구현하기

      • square 함수를 일반화하여 다각형 그리기
      • 셋째 매개변수 n 사용.
      • 실행하면 한 변의 길이가 lengthn각형 그리기
      • 정 n-각형의 외각은 180/n 도 이다.

      <그림 출처: wikipedia.org>

  2. 원과 호를 그리는 함수를 구현하라. 참조: Think Python: 인터페이스 설계

    1. 앞서 정의한 polygon 함수를 이용하여 원을 그리는 함수 circle을 구현하라.
      • 두 개의 매개변수 t와 r을 사용한다.
        • t: 거북이 인자
        • r: 반지름
      • 힌트: 원을 정 n-각형을 이용하여 근사적으로 그릴 수 있다. 단, n이 매우 커야 한다.
        • length * n = 원둘레.

    2. circle 함수를 수정하여 아래 조건을 만족하는 arc 함수를 구현하라.

      • 매개변수 angle을 추가한다.
      • angle은 호(arc)를 그릴 때 사용되는 각도이다.
      • 호는 부채꼴 모양으로 잘린 원의 일부를 가리킨다.
      • 따라서 angle = 360이면 원을 그리게 된다.

      <그림 출처: wikipedia.org)>

  3. 아래 그림의 도형을 그리는 함수들을 구현하라.

    힌트: 모범답안을 담고 있는 파이썬 코드가 여기에 있음. 단, 앞 링크에 있는 코드는 이곳의 모듈을 불러와야 함.

  4. 아래 그림의 도형을 그리는 함수들을 구현하라.

    힌트: 모범답안을 담고 있는 파이썬 코드가 여기에 있음.

  5. 아르키메데스 와선(spiral)을 그리는 함수를 구현하라.

    <그림 출처: wikipedia.org>

    힌트: 모범답안을 담고 있는 파이썬 코드가 여기에 있음.