본문 바로가기

IT/SW Engineering

명령형 vs 선언형 프로그래밍

학습 도중 "선언형 프로그래밍은 이해하기 쉬운 코드"를 만들어 준다는 이야기를 많이 들었습니다. 객체 지향 프로그래밍(OOP)은 들어본 적 있는데, 대체 어떤 프로그래밍 기법이기에? 라는 관심이 들어 한번 정리해보고자 합니다.

 


 

명령형 프로그래밍(Imparative Programming)

명령형 프로그래밍이란?

프로그래머가 무엇을(What)할지 보다, "어떻게(How) 할 것인지를 표현"하는 프로그래밍 패러다임입니다.

 

개념만 봐서는 그리 와닿지 않지만, 사실 우리가 지금까지 알던 방식이 이 명령형 프로그래밍과 유사합니다. 소위 절차 지향, 객체 지향 기법이 명령형 프로그래밍 패러다임에 속합니다.

  • 절차 지향(PP) 프로그래밍: 수행해야할 작업을 "순차적"으로 작성하는 프로그래밍 패러다임입니다. (C언어 등)
  • 객체 지향(OOP) 프로그래밍: 데이터와 절차를 하나의 "객체"로 묶어 표현하는 프로그래밍 패러다임입니다. (Java, C++)

 

명령형 프로그래밍의 문제점

숫자 배열을 받아, 각각 원소를 제곱해 반환하는 프로그램을 예시로 들어 보겠습니다.

function squareNumbers(numbers) {
    let squaredNumbers = [];
    for (let i = 0; i < numbers.length; i++) {
        squaredNumbers.push(numbers[i] * numbers[i]);
    }
    return squaredNumbers;
}

 

 위 코드는 "원소를 제곱"하기 위한 방법들을 하나하나 나열하고 있습니다.

  • 각 배열의 원소에 순차적으로 접근하기 위해, 반복문을 사용하고 있습니다.
  • 각 원소를 제곱하기 위해, 배열의 인덱스로 원소에 접근하고 서로 곱해주고 있습니다.
  • 제곱한 결과를 반환하기 위해, squaredNumber 배열을 선언하고 결과를 저장하고 있습니다.

 

우리는 이 코드를 읽을 때 명령들의 의도를 파악하고, 이를 조합해 최종으로 무엇을(What) 하는지 파악해야 합니다. 즉, 핵심 로직을 파악하기 어렵다는 문제가 있습니다.

 


 

선언형 프로그래밍(Declarative Programming)

선언형 프로그래밍이란?

명령형 프로그래밍과 반대로, "무엇을(What) 할 것인지 표현"하는 프로그래밍 패러다임입니다.

 

또, 개념만 봐서는 어떤 내용인지 와닿지 않습니다. 각각 명령형과 선언형 프로그래밍을 자동차 변속기에 빗대 설명해 보겠습니다.

  • 수동변속기: 먼저 클러치 페달을 밟은 다음, 중립(N) 위치의 스틱을 1단으로 옮깁니다. 그 후 악셀 페달을 살짝 밟고, 동시에 클러치 페달을 서서히 떼면 자동차를 운전할 수 있습니다.
  • 자동변속기: P단의 스틱을 D으로 변속하면 자동차를 운전할 수 있습니다.

 

여기서 자동변속기 차량은 "운전(What)"하기 위한 모든 과정을 추상화하고, 이를 "D" 단이라고 표현했습니다. 즉, 자동변속기는 "추상화"를 통해 운전자가 운전에만 집중할 수 있도록 도와줍니다.

 

즉, 선언형 프로그래밍은 목표(WHAT)를 이루기 위한 방법(HOW)을 모두 추상화해, 개발자가 핵심을 파악하기 쉽도록 해주는 프로그래밍 패러다임입니다.

 

코드의 가독성을 높여준다

이제 위 코드를 직접 선언형 프로그래밍 방식으로 바꿔 보겠습니다. 

function squareNumbers(numbers) {
    return numbers.map(number => number * number);
}

 

"배열을 순회하고, 결과를 반환하는 행동"을 map 함수로 추상화하고, 원소를 제곱하는 행동(What)을 전달해 주었습니다. 이제 개발자는는 map을 알기만 하면 단번에 "배열의 원소를 제곱해 반환"하는 핵심 로직을 파악할 수 있습니다.

 

코드의 재사용성과 디버깅에 큰 도움을 준다

선언형 프로그래밍은 가독성 뿐만이 아니라, 코드의 재사용성과 디버깅에도 큰 도움을 줍니다. 에시로, 각 배열의 원소에 2를 곱하고, 반환하는 함수를 추가로 작성해 보겠습니다.

function dobuleNumbers(numbers) {
    return numbers.map(number => number * 2);
}

 

아까 추상화한 map을 다시 활용해 간단하게 함수를 작성할 수 있습니다. 

  • "배열을 순회하고, 결과를 반환하는 행동"은 모두 map을 사용할 수 있으니 "재사용성"이 크게 올라갑니다. 
  • 코드에서 에러를 찾을 때, 부가적인 기능 말고 "핵심 기능"만 찾으면 되니 디버깅이 용이합니다. 

 

오늘은 명령형 프로그래밍과 선언형 프로그래밍의 차이에 대해 알아봤습니다. 선언형 프로그래밍은 "추상화"로 코드의 가독성과 안정성, 재사용성을 높히는데 큰 도움을 줍니다. 다음 포스팅에서는 선언형 프로그래밍 종류인 "함수형 프로그래밍"에 대해 알아보도록 하겠습니다.

'IT > SW Engineering' 카테고리의 다른 글

함수형 프로그래밍이란? (Functional Programing)  (1) 2023.12.31