nodejs

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

어떻게(WHAT)가 아니라, 무엇(HOW)을 할지에 대해 고민하는 것이 선언형 프로그래밍의 시작이라고 할 수 있습니다. 개발을 하다 보면 map, filter, SQL, React, Terraform 같은 도구들을 선언형이라고 부르는 말을 자주 듣습니다. 하지만 막상 "선언형 프로그래밍이 뭐야?"라는 질문에 정확하게 설명하기는 어렵습니다. 이번 글에서는 선언형 프로그래밍의 개념, 예시, 그리고 왜 지금의 소프트웨어 개발에서 중요해지고 있는지를 차근히 정리해보겠습니다.


선언형 프로그래밍이란?

선언형 프로그래밍(Declarative Programming)은 "어떻게(How)"가 아니라 "무엇(What)"을 기술하는 방식입니다. 즉, 프로그램이 수행해야 할 목적과 결과를 중심으로 코드를 작성하고, 그 목적을 달성하기 위한 내부 실행 로직은 추상화하거나 위임합니다.

예를 들어, "배열에서 짝수만 골라내라"는 작업이 있을 때, 선언형 프로그래밍은 그 작업을 직접 수행하는 루프를 짜는 것이 아니라, 어떤 조건을 만족하는지를 명시합니다.


선언형 프로그래밍 예시

declarative programming
// 명령형
const numbers = [1, 2, 3, 4, 5, 6]
const evens = []
for (let i = 0; i < numbers.length; i++) {
  if (numbers[i] % 2 === 0) {
    evens.push(numbers[i])
  }
}
 
// 선언형
const numbers = [1, 2, 3, 4, 5, 6]
const evens = numbers.filter((n) => n % 2 === 0)

두 코드 모두 동일한 결과를 내지만, 선언형 스타일은 무엇을 원하는지를 간결하게 표현하고, **어떻게 구현되는지는 내부 함수(filter)**가 책임집니다.


프론트엔드에서 자주 사용하는 React와 Zustand 상태 관리를 예로 들어보겠습니다. 예전에는 사용자가 로그인했는지에 따라 UI를 바꾸기 위해, DOM을 직접 조작하거나 document.getElementById로 요소를 제어하는 명령형 방식이 흔했습니다. 하지만 선언형 방식에서는 다음처럼 간결하고 직관적으로 구현할 수 있습니다.

declarative programming
import { useStore } from './store'
function Header() {
  const isLoggedIn = useStore((state) => state.isLoggedIn)
 
  return (
    <header>
      <button>{isLoggedIn ? '로그아웃' : '로그인'}</button>
    </header>
  )
}

여기서 useStore는 내부 상태를 구독하고, isLoggedIn의 상태에 따라 버튼이 자동으로 바뀝니다. 개발자는 상태가 변경될 때마다 DOM을 직접 조작하거나 변경에 대한 고민 없이, 단지 상태가 이러할 때 어떤 UI를 보여줘야 하는지만 선언하면 됩니다. 이처럼 선언형 스타일은 UI 로직을 명확하게 분리하고, 상태 중심의 사고 방식을 가능하게 하여 컴포넌트 단위 테스트, 유지보수, 리팩터링에 큰 장점을 제공합니다. 특히 팀 개발에서는 협업 시 코드의 의도 파악이 쉬워져, 커뮤니케이션 비용도 줄어드는 효과가 있습니다.


선언형 프로그래밍의 특징

선언형 프로그래밍은 세 가지 핵심 특징으로 요약할 수 있습니다. 첫째, 결과 중심의 표현 방식입니다. 복잡한 절차나 흐름을 직접 기술하기보다는, 최종적으로 원하는 결과를 간결하게 표현함으로써 코드의 길이를 줄이고 의도를 명확하게 전달할 수 있습니다. 둘째, 높은 수준의 추상화를 제공합니다. 반복, 조건문, 상태 변경과 같은 저수준 제어는 내부 로직에 숨겨져 있어, 개발자는 구현 방식보다는 목적에 집중할 수 있습니다. 이는 유지보수와 리팩토링에 특히 유리한 구조를 만듭니다. 셋째, 테스트와 병렬 처리에 유리한 구조입니다. 불변성과 순수 함수 기반의 설계 덕분에 사이드 이펙트가 줄어들고, 동일한 입력에 대해 예측 가능한 출력을 보장할 수 있어 단위 테스트가 용이하며, 멀티스레드 환경에서도 안정성이 뛰어납니다.


선언형과 명령형, 언제 어떤 걸 써야 할까?

선언형이 항상 더 좋은 건 아닙니다. 내부 로직을 완전히 제어하거나, 반복적이고 상태 기반의 세밀한 흐름 제어가 필요한 알고리즘, 성능 병목 지점에서는 명령형이 더 적합할 수 있습니다. 반면, 구조가 반복되고 상태가 안정적이며, 복잡한 로직을 감추고 단순한 선언만으로 결과를 표현할 수 있는 부분은 선언형으로 추상화하는 것이 훨씬 효과적입니다. 결국 중요한 건 스타일이 아니라 문제에 맞는 도구와 접근 방식을 고르는 판단력입니다.


정리

선언형 프로그래밍은 무엇을 할지 명확하게 표현하고, 구현 방식은 추상화하는 방식으로, 가독성과 유지보수에 강점을 가집니다. 복잡한 상태 제어를 단순화하여 현대 개발 환경에 잘 맞는 패러다임으로 평가받습니다. 단, 실무에서는 명령형과 선언형을 적절히 조합하는 유연한 활용이 핵심입니다.


참고