포스트

[OOP] 캡슐화 (Encapsulation)

캡슐화(Encapsulation)는 객체지향 프로그래밍(OOP)의 중요한 개념 중 하나로, 데이터와 그 데이터를 처리하는 메서드하나의 단위로 묶고, 실제 구현 내용 일부를 내부에 감추어 은닉합니다.

사실 이게 핵심이자 전부입니다.

“이건 나도 알고 있던건데? 이게 객체지향만의 중요한 특징이라고?” 혹은 “이건 당연한거 아니야?” 라고 생각할 수도 있습니다.

하지만, 이런 개념이 생긴 이유와 그로 인해 얻을 수 있는 이점에 대해서는 좀 더 생각해봐야 합니다.

객체 지향의 개념 혹은 특징에 해당하는 것은 이 객체지향의 등장 시점이나 객체지향 이전의 프로그래밍과 관련해 생각하면 좀 더 쉽게 이해할 수 있습니다.

절차적 프로그래밍 (Procedural)의 문제

절차적 프로그래밍(Procedural Programming)은 프로그램을 프로시저함수로 데이터를 처리하는 프로그래밍 패러다임입니다.

만약, 여러분이 C언어를 처음 배울 때가 있었다면, 그때 했던 프로그래밍 방식을 생각해보면 쉽습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 전역 변수
std::string name;
int age;

// 이름을 설정하는 함수
void setName(const std::string& newName)
{
    name = newName;
}
// 나이를 설정하는 함수
void setAge(int newAge)
{
    age = newAge;
}
// 정보를 출력하는 함수
void displayInfo()
{
    std::cout << "Name: " << name << ", Age: " << age << std::endl;
}

int main()
{
    setName("Alice");
    setAge(30);
    displayInfo();

    setName("Bob");
    setAge(25);
    displayInfo();
}

이런 방식의 프로그래밍에는 여러 문제가 있었습니다.

1. 데이터 보호의 부재

절차적 프로그래밍에서는 데이터에 직접적으로 접근할 수 있어 여러 함수에서 공통으로 사용되는 경우가 많습니다.

이로 인해 의도치 않은 데이터 변경이 발생하게 되거나, 요구사항 변경이나 추가로 인해 데이터를 직접 사용하던 코드들을 일일이 찾아 수정해 줘야 하는 문제가 있습니다.

2. 높은 결합도

절차적 프로그래밍에서는 함수의 접근 또한 쉬워 함수 간의 의존성이 높아질 수 있습니다.

이로 인해 한 함수의 변경이 다른 함수의 동작에 영향을 줄 수 있게 되고, 이는 유지보수를 어렵게 만듭니다.

3. 재사용성 부족

절차적 프로그래밍에서는 함수와 데이터가 분리되어 있어, 특정 기능을 다른 프로젝트에서 재사용하기 어렵습니다.

캡슐화의 등장

캡슐화는 위와 같은 절차적 프로그래밍의 문제점을 해결하기 위해 도입된 개념입니다.

캡슐화는 데이터와 그 데이터를 처리하는 메서드를 하나의 단위로 묶어, 데이터의 보호모듈화, 재사용성을 높입니다.

이는 객체지향 프로그래밍의 핵심 개념으로, 복잡한 소프트웨어를 보다 효율적으로 설계하고 관리할 수 있게 했습니다.

이제, 위키백과의 내용을 보더라도 바로 이해할 수 있을 것입니다.

캡슐화(encapsulation)는 객체 지향 프로그래밍에서 다음 2가지 측면이 있다.

  • 객체의 속성(data fields)과 행위(메서드, methods)를 하나로 묶고,
  • 실제 구현 내용 일부를 내부에 감추어 은닉한다.

출처: 캡슐화 - 위키백과, 우리 모두의 백과사전 (wikipedia.org)

정보 은닉

위키에서 말하는 두 번째 측면인, “은닉”에 대해서도 쉽게 이해할 수 있을 것입니다.

정보 은닉(Information Hiding) 이란, 결국 데이터에 쉽게 접근해서 발생한 데이터 보호 문제나 높은 결합도 문제를 해결하기 위한 방법입니다.

즉, 객체의 내부 상태를 외부에서 직접 접근할 수 없도록 함으로써, 객체의 내부 구현을 숨기고, 외부에서는 객체가 제공하는 기능을 통해서만 상호작용하게 하여 데이터의 무결성을 유지하게 합니다.

데이터의 무결성이 유지된다는 말은 외부에서 영향을 받거나 주지 않고 내구 구현을 변경할 수 있다는 말이 되고, 따라서 외부 코드에 영향을 최소화하여 유지보수성이 높아지게 됩니다.

캡슐화 원칙

캡슐화와 관련하여 소프트웨어 설계에서 언급되는 몇 가지 원칙들이 있습니다.

Tell, Don’t Ask

객체의 상태를 요청(Ask)하지 말고, 필요한 작업을 지시(Tell)하도록 설계하라는 원칙입니다.

객체의 내부 상태에 대한 직접적인 접근을 피하고, 객체가 스스로 자신의 상태를 관리하고 작업을 수행하게끔 설계해야 한다는 말입니다.

1
2
3
4
5
6
// 나쁜 예: 객체의 상태를 요청한 후 객체의 상태를 변경하고 있음 (Ask)
if (person.getAge() > 18) {
    person.setAdult(true);
}
// 좋은 예: 객체에 필요한 작업을 지시 (Tell)
person.updateAdultStatus();

Law of Demeter (디미터 법칙)

객체가 자신이 직접적으로 가지고 있는 구성 요소협력하는 객체의 메서드만 호출해야 한다는 원칙입니다.

이를 통해 객체 간의 결합도를 낮추고, 변경에 유연하게 대응할 수 있습니다.

1
2
3
4
// 나쁜 예: Person에서 시작해서 다른 객체를 가져와 결국 City의 이름을 묻고 있다.
person.getAddress().getCity().getCityName();
// 좋은 예: 객체 간의 결합도를 낮추고, 각 객체가 자신의 데이터와 동작을 관리하게 합니다.
person.printCityName();

목적

캡슐화는 단순히 데이터와 메서드를 묶는 것 이상의 가치를 제공합니다.

이는 소프트웨어의 설계를 개선하고, 데이터의 무결성을 보호하며, 유지보수를 용이하게 만들고, 재사용성을 증가시키는 중요한 기법입니다.

  • 정보 은닉(Information Hiding): 객체의 내부 상태를 외부에서 직접 접근할 수 없도록 함으로써, 객체의 내부 구현을 숨기고, 외부에서는 객체가 제공하는 인터페이스를 통해서만 상호작용하게 합니다. 이를 통해 객체의 무결성을 유지할 수 있습니다.
  • 코드의 재사용성 증가: 캡슐화된 객체는 독립적으로 동작하기 때문에, 다른 프로젝트나 코드베이스에서도 재사용하기 쉽습니다.
  • 유지보수성 향상: 객체의 내부 구현이 변경되더라도, 외부 인터페이스는 그대로 유지될 수 있으므로, 코드 변경 시 다른 부분에 영향을 최소화할 수 있습니다.
  • 응집도(Cohesion) 증가: 관련된 데이터와 메서드를 하나의 객체로 묶음으로써, 코드의 응집도가 높아지고, 코드가 더 이해하기 쉽고 유지보수하기 쉬워집니다.

참고

[OOP] 객체지향 프로그래밍(Object-Oriented Programming, OOP) 정리

[OOP] 상속성(Inheritance)

[OOP] 추상화(Abstraction)

[OOP] 다형성(Polymorphism)

이 기사는 저작권자의 CC BY-NC-ND 4.0 라이센스를 따릅니다.