OOP (Object-Oriented Programming)

mingu Lee·2025년 11월 25일

CS

목록 보기
5/21

OOP (Object-Oriented Programming)객체 지향 프로그래밍의 약자로 프로그램을 객체의 모음으로 구성하는 프로그래밍 방법론이다.

1. OOP의 핵심 개념


1-1. 객체(Object)와 클래스(Class)


  • 객체(Obejct): 프로그램을 구성하는 기본 단위로 아래 정보를 함께 가지고 있음
    • 필드/속성(field, attribute): 객체의 상태를 나타내는 데이터
    • 메서드(method): 그 데이터를 읽거나 변경하는 함수
  • 클래스(Class): 객체를 만들기 위한 설계도또는 에 비유할 수 있으며, 객체가 가져야 할 공통적인 속성(데이터)과 행동(메소드)을 정의
  • 인스턴스(Instance): 클래스라는 설계도를 바탕으로 실제로 메모리 상에 만들어진 실체

1-2. 4가지 핵심 원칙


캡슐화


  • 의미: 데이터와 그 데이터를 처리하는 함수를 하나로 묶고, 외부에서 데이터를 직접 건드리지 못하게 보호하는 것
  • 목적: 객체의 내부 상태가 외부의 예상치 못한 접근이나 변경으로 인해 훼손되는 것을 방지하고, 내부 구현이 어떻게 되든 외부 인터페이스만 일관되면 되므로 유지보수가 용이
  • 예시:
class BankAccount
{
private:
    int balance;              // 외부에서 직접 접근 불가

public:
    BankAccount(int initial) : balance(initial) {}

    void Deposit(int amount)  // 공개 메서드
    {
        if (amount > 0)
            balance += amount;
    }

    bool Withdraw(int amount) // 공개 메서드
    {
        if (amount > 0 && balance >= amount)
        {
            balance -= amount;
            return true;
        }
        return false;
    }

    int GetBalance() const    // 읽기 전용 인터페이스
    {
        return balance;
    }
};

balanceprivate이라 외부 코드에서 account.balance = -100 이런 식으로 막 건드릴 수 없다.

또한, 입출금 로직은 내부에서 숨겨져 있고, 외부에는 메서드라는 인터페이스만 노출된다.

2. 추상화 (Abstraction)


  • 의미: 복잡한 현실 세계의 문제를 프로그램으로 표현할 때, 불필요한 부분은 생략하고 핵심적인 특징이나 기능에 집중하는 과정
  • 목적: 복잡한 구현 세부사항을 숨기고, 중요한 공통 특성/동작만을 노출하는 것
  • 예시:
class Shape   // 추상적인 개념
{
public:
    virtual void Draw() = 0;   // 순수 가상 함수 → 인터페이스만 정의
    virtual double GetArea() const = 0;
    virtual ~Shape() = default;
};

class Circle : public Shape   // 구체적인 원
{
public:
    Circle(double r) : radius(r) {}

    void Draw() override
    {
        // 원을 그리는 실제 코드
    }

    double GetArea() const override
    {
        return 3.14159 * radius * radius;
    }

private:
    double radius;
};

class Rectangle : public Shape   // 구체적인 사각형
{
public:
    Rectangle(double w, double h) : width(w), height(h) {}

    void Draw() override
    {
        // 사각형을 그리는 실제 코드
    }

    double GetArea() const override
    {
        return width * height;
    }

private:
    double width;
    double height;
};

Shape는 도형이라는 추상 개념만 정의하고 Circle, Rectangle에서 구체적인 구현을 한다.

3. 상속 (Inheritance)


  • 의미: 부모 클래스의 특성을 자식 클래스가 물려받아, 마치 자신의 것인 듯 사용하는 것
  • 목적: 코드의 중복을 줄이고 재사용성을 극대화
  • 예시:
class Animal
{
public:
    void Eat()
    {
        // 공통 먹기 로직
    }

    virtual void MakeSound()
    {
        // 기본 소리 (필요하면 비워 둘 수도 있음)
    }
};

class Dog : public Animal
{
public:
    void MakeSound() override
    {
        std::cout << "Woof!" << std::endl;
    }
};

class Cat : public Animal
{
public:
    void MakeSound() override
    {
        std::cout << "Meow!" << std::endl;
    }
};

위 예시에서 Dog, CatAnimal의 자식 클래스이다.

Eat() 같은 공통 기능은 부모 클래스인 Animal에 두고, 각 동물마다 다른 MakeSound()만 Override해서 구현하고 있다.

4. 다형성 (Polymorphism)


  • 의미: 형태가 다양하다는 뜻으로, 동일한 이름의 메서드가 객체의 종류에 따라 다르게 동작하는 것을 말함
  • 목적: 코드를 유연하게 확장 가능하게 만들고, 같은 명령이라도 객체에 따라 다르게 반응하므로 코드의 중복을 줄이고 깔끔하게 관리할 수 있음
  • 예시:
void MakeAnimalSound(Animal* animal)
{
    animal->MakeSound(); // 어떤 소리가 날지는 실제 객체 타입에 따라 달라짐
}

int main()
{
    Animal* a1 = new Dog();
    Animal* a2 = new Cat();

    MakeAnimalSound(a1);  // Dog::MakeSound() -> "Woof!"
    MakeAnimalSound(a2);  // Cat::MakeSound() -> "Meow!"

    delete a1;
    delete a2;
}

MakeAnimalSound 함수는 인자로 Animal*만 알고 있지만, 실제로 들어오는 객체가 Dog인지 Cat인지에 따라 런타임에 다른 함수가 호출된다.

2. OOP의 장단점


2-1. 장점


모듈성/유지보수성 향상

  • OOP는 데이터를 캡슐화하고 클래스로 나누어 구조화하기 때문에, 기능 단위(클래스/객체)로 코드를 관리하기 쉬움
  • 한 클래스 내부 구현을 바꿔도, 외부에는 공개 메서드만 유지하면 되므로 변경 영향 범위를 줄일 수 있음

재사용성/확장성

  • 상속, 다형성, 구성(composition) 등을 활용하면 공통 기능은 상위/기본 클래스에 두고, 차이점만 하위 클래스에서 구현할 수 있음
  • 이런 구조 덕분에 클래스를 재사용하거나, 새로운 기능을 추가할 때 기존 코드를 덜 건드리면서 확장성(extensibility) 있게 설계 가능

현실 세계 모델링 용이

  • 현실 세계의 사물과 개념을 프로그램 설계에 직접적으로 반영할 수 있어, 문제 해결 과정이 직관적이고 이해하기 쉬움

테스트·협업에 유리한 구조

  • 잘 쪼개진 클래스/객체는 단위 테스트(unit test) 를 작성하기 쉬움
  • 모듈 경계가 명확하면, 팀 단위로 이 모듈을 나눠 개발하기 좋고 인터페이스만 합의하면 병렬로 작업하기가 수월

2-2. 단점


구조/설계 복잡도 증가

  • 프로젝트 초기에 클래스 간의 관계를 정의하고 설계하는 데 많은 시간과 노력이 필요
  • 커다란 OOP 시스템은 클래스 수가 기하급수적으로 늘고, 클래스 간 관계가 복잡해져서
    전체 구조를 이해하기 어려워짐

과도한 추상화의 위험

  • 지나치게 많은 클래스와 상속 관계를 만들면 오히려 코드의 가독성이 떨어지고, 복잡성이 증가하여 유지보수가 어려워짐

성능 오버헤드

  • 동적 디스패치같은 메커니즘은 절차적/함수형 스타일에 비해 호출 오버헤드가 생기고, 객체 그래프 관리로 인한 메모리 사용 증가
profile
Github: https://github.com/dlalsrn

0개의 댓글