상세 컨텐츠

본문 제목

#10. 추상 자료형 : Abstract Data Type(ADT)

프로그래밍 및 언어/자료구조 학습

by 남민우_ 2025. 1. 14. 00:30

본문

추상 자료형

이 추상 자료형(이하 ADT)이란, 말 그대로는 자료형(기능)을 추상적으로 정의한 것으로 '구체적인 기능의 완성과정을 언급하지 않고, 순수하게 기능이 무엇인지 나열한 구조' 를 의미한다.

이러한 ADT를 자료구조에 반영하였다는 것은 보다 더 좋은 코드를 만들어냈다 라는 뜻을 가지고도 있다.

 

먼저 자료형이란? '기능'을 말한다.

키보드의 입력, 핸드폰의 알람 등 여러 기능이 있을텐데, 지갑의 추상 자료형을 예시로 들어보자면

- 카드의 삽입/추출 이 가능하다.

- 현금의 삽입/추출이 가능하다.

라는 하나하나의 기능을 설명할 수 있다.

다만 여기서 카드의 삽입이 어떤 동작으로 이루어지고, 추출이 어떤 동작으로 이루어지는지 그에 대한 진행 과정은 언급하지 않았는데, 이렇게 기능을 나열하되 진행 과정에 대해서는 언급하지 않는 형태를 '추상 자료형' 이라고 한다.

 

그럼 이 기능의 명세를 가리켜 왜 '자료형'이라고 할까?

우리는 보통 자료형의 대표적인 예시 int 를 생각하면 Data를 떠올린다.

int num = 10; 의 코드로 쉽게 예시를 들 수 있는데, 여기서 int 가 자료형, num 이 변수, 10 이 값이고 이 코드 전체는 = 의 대입 연산자를 통해 초기화되는 과정이다.

이 코드가 완성인지에 대해서는 잠시 넘어가자.

 

C언어를 이용해 프로그램을 만들 때 베이스가 되는 순서로는

1. 구조체를 정의하고, 더불어 그 구조체의 함수를 같이 정의한다. => '자료형을 정의한다'

2. 그러고 나서 함수를 정의한다.

의 두 단계가 진행된다.

 

typedef struct _wallet
{
	int coin100Num;
	int bill5000Num;
} Wallet;

데이터를 정의한다는 것은 그와 관련한 연산이 반드시 있기 마련인데, 이 연산을 함수가 담당한다.

int TakeOutMoney(Wallet* pw, int coinNum, int billNum);
void PutMoney(Wallet* pw, int coinNum, int billNum);
...

자료형 정의의 끝은 그 데이터(변수, 구조체)와 그 관련 기능(함수)을 만드는 동작까지이다.

 

정리하자면, 구조체를 만들고 이걸 자료형으로 사용하고자 한다면 반드시 이와 관련된 연산을 정의해야 한다는 것이다.

따라서 구조체를 정의하는 것은 연산을 담당하는 함수를 정의하는 것까지의 동작이라고 할 수 있다.

 

이 구조체의 변수들(wallet 의 경우에는 coin100Num, bill5000Num)을 사용하고자 하는 모든 연산들은 이 함수로 사용하는 것이 원칙이다.

이러한 함수들을 제외하고는 구조체 변수에 직접적으로 접근하는 .(dot) 이나 ->(arrow) 연산을 사용하지 않도록 하는 과정이 필요하다.

자료형의 정의에 있어서 구조체/구조체의 멤버가 중요한 것이 아니라 그를 활용한 연산이 중요한 것이라고 할 수 있다.

 

구조체 wallet 의 추상 자료형 정의 예시

자료형의 정의는 '기능의 명세'라고 언급했었는데, 쉽게 사용 설명서 라고 비유를 들 수 있다.

어떤 기능들이 준비되어 있다~ 라고 기능들을 적어둔 것이 추상 자료형이다.

이렇게 ADT는 어떤 기능들이 있는지 알려주고, 나중에 추가적인 기능이 필요할 경우 그때 다시 함수를 새롭게 선언하거나 수정해도 된다.

이 말은 한번에 완벽한 정의를 하지 않아도 된다는 것이다.

 

이 TakeOutMoney 나 PutMoney 를 사용하는 코드 예시는 다음과 같이 사용할 수 있을 것이다.

int main()
{
	Wallet myWallet;
   	PutMoney(&myWallet, 5, 10);
    	ret = TakeOutMoney(&myWallet, 2, 5);
    	...
}

이 main 코드 예시를 보면 함수를 사용하되, 그 내부 동작이나 구성에 대해서는 알 필요 없이 단순 호출만을 통해 사용하였다.

이는 구조체 Wallet의 정의를 ADT 에 포함시키지 않아도 됨을 나타낸다.

이 말은 특히나 중요한데, ADT가 완벽하게 정의되어 있다면 Wallet 의 내부를 들여다보지 않아도 충분히 사용할 수 있다.

 

예시로 우리가 C++ 언어에서 vector 를 사용하는 것을 생각해보면

#include <vector>

int main()
{
	std::vector<int> testVector = { 1, 2, 3 };
	int vectorLength = testVector.size();	// 3

	return 0;
}

다음과 같이 vector 의 메서드를 자유롭게 사용하면서도 size() 함수의 내부 구조가 어떻게 작성되어 있고, 어떤 로직을 통해 전체 길이를 반환하는 건지 알지 않는다.

 

단순히 STL이라서 그런 것이 아니라 그만큼 vector의 ADT가 완벽하게 정의되어 있다는 것이고, 이 ADT에는 구조체 정의가 포함되어 있지 않음을 알 수 있다.

만약 어떠한 기능을 사용함에 있어 구조체 정의를 포함해야 한다면 그 연산 함수들이 잘못 정의되어 있을 확률이 상당히 높은 만큼, 우리가 알아야 하는 것은 사용하려는 함수가 어떤 값을 필요로 하고 어떤 결과를 도출하는지만 알고 있으면 된다.

마찬가지로 우리가 구조체를 정의하게 될 경우 이러한 방향성을 지양해야 할 것이다.

 

자료구조 학습

정리해서

자료구조의 내부 구현을 모르고도 해당 자료구조의 활용이 가능하도록 ADT를 설계하는 것이 옳다.

main 함수를 먼저 접하게 되면 구현할 자료구조를 구성하는 함수들을 잘 이해할 수 있다.

 

이후에 구조체를 학습하는 과정에 있어서 헤더파일과 그 함수를 활용하는 메인파일이 주어진다면, 기능파일(.C/.Cpp) 는 가급적 내부를 쳐다보지 않고 사용하거나 학습에 있어서 내부를 보기 전에 먼저 직접 정의를 해보는 방법 또한 좋은 학습태도가 될 것으로 보인다.

관련글 더보기