
선언형이 좋다고? 왜? 그냥 깔끔해서?
개발을 하다보면 많이 듣게 되는 말이 있습니다.
“선언적”
그런데 문득 이런 생각이 듭니다.
정말 선언적인 게 더 좋은 걸까?
그리고, 무엇을 두고 우리는 ‘선언적’이라고 부르는 걸까?
선언형 vs 명령형, 단순한 코드 스타일일까?
많은 사람들이 map, filter 같은 함수형 문법을 사용하면 선언적이라고 말합니다.
혹은 React를 쓰고 있으니 자연스럽게 선언형이라고 느끼기도 하죠.
하지만 ‘선언형’이라는 말의 근거를 물어보면, 대개 설명이 문법 수준에 머뭅니다.
결국 “그냥 깔끔해 보여서요.”라는 답으로 끝나는 경우가 많습니다.
그럼 정말 선언형은 단지 보기 좋은 코드 스타일의 문제일까요?
예: 버튼 클릭 시 상태 업데이트
아래는 동일한 동작을 서로 다른 방식으로 표현한 예시입니다.
명령형
const button = document.querySelector('button')
button?.addEventListener('click', () => {
count += 1
document.querySelector('#counter')!.textContent = String(count)
})
선언형 (React 기준)
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
둘 다 버튼을 누르면 숫자가 올라갑니다. 하지만 코드가 설명하는 ‘관계’는 전혀 다릅니다.
- 명령형은 “이 버튼이 클릭되면, count를 1 증가시키고, DOM을 업데이트하라.”
- 선언형은 “count가 이 값일 때, 이 UI가 그려진다.”
즉, 전자는 시간의 흐름을 다루고, 후자는 상태 간의 관계를 다룹니다. 이 지점에서 두 사고방식의 간극이 드러납니다.
선언형 사고란 무엇인가
처음에는 map()을 쓰는 게 단지 코드가 깔끔해 보여서였을 겁니다. 하지만 규모가 커지면 이건 단순한 문법의 문제가 아닙니다. 프로그램을 바라보는 관점의 문제가 됩니다.
명령형 사고는 이렇게 시작합니다. “이 기능을 어떻게 구현하지?” 반면 선언형 사고는 이렇게 묻습니다. “이 상태에서 무엇이 성립해야 하지?”
둘의 차이는 사소해 보이지만, 코드를 설계하는 철학을 완전히 바꿉니다.
선언형은 ‘UI’에만 국한되지 않는다
많은 사람들이 선언형을 React나 JSX의 특성으로만 이해합니다. 하지만 이 철학은 훨씬 넓은 영역에 걸쳐 있습니다.
상태 관리, API 요청, 라우팅, CSS 작성까지 — 선언형 접근은 “동작의 순서”가 아닌 “관계의 표현”을 중심에 둡니다.
| 영역 | 명령형 접근 | 선언형 접근 |
|---|---|---|
| 상태 업데이트 | store.value = 5 | setState(5) |
| 렌더링 | DOM.innerHTML = ... | return <Component /> |
| 라우팅 | window.location.href = '/page' | <Link to="/page" /> |
| 스타일링 | element.style.color = 'red' | className="text-red-500" |
결국 선언형의 핵심은 “이 시스템이 어떤 관계를 가져야 하는가”에 있습니다. 그 관계가 명확할수록, 복잡성은 제어됩니다.
복잡성을 다루는 방식의 차이
프로젝트가 커질수록 상태와 이벤트의 조합은 폭발적으로 늘어납니다. 명령형 접근에서는 이 모든 변화를 직접 제어해야 합니다. 그런데 선언형에서는 그 관계를 모델로 표현합니다.
// 명령형 - 상태 변화를 직접 제어
if (user.isLoggedIn) {
showWelcomeMessage()
} else {
redirectToLogin()
}
// 선언형 - 상태 관계를 선언
return user.isLoggedIn ? <Welcome /> : <Navigate to="/login" />
선언형 접근은 “언제 실행할까?”가 아니라 “이 상태에서 어떤 결과가 나와야 하는가?”를 기술합니다.
결국 이 방식이 예측 가능성을 만들어냅니다. 동일한 상태에서 동일한 결과가 나온다는 단순한 규칙. 이게 선언형의 가장 큰 힘입니다.
선언형은 절차를 감추는 게 아니다
선언형을 종종 “절차를 감춘 방식”으로 설명하지만, 정확히 말하면 의도를 중심으로 절차를 재구성한 방식입니다.
좋은 선언형 코드는 ‘어떻게’보다 ‘무엇’을 중심으로 기술합니다. 즉, 실행 과정이 아니라 도메인 내에서 성립해야 할 관계를 코드로 표현하는 것이죠.
UI = f(state)
이 짧은 수식이 선언형의 본질을 정확히 보여줍니다. 상태가 같다면 언제나 같은 UI가 만들어진다는 관계. 그 관계를 유지하기 위한 나머지는 시스템이 담당합니다.
선언형이 항상 옳은가?
그렇지는 않습니다. 모든 문제를 선언형으로 풀 수 있는 건 아니니까요.
렌더링 프레임 단위로 제어가 필요한 애니메이션, WebGL처럼 저수준 API를 다루는 경우엔 명령형 접근이 더 직접적이고 효율적입니다.
하지만 대부분의 프론트엔드 애플리케이션이 지향하는 것은 다음과 같습니다.
- 유지보수성
- 협업 용이성
- 확장성
- 테스트 용이성
이런 영역에서는 선언형 접근이 훨씬 현실적이고, 결국 개발자가 복잡도를 감당할 수 있는 여유를 만들어줍니다.
결국, 선언형은 사고의 전환이다
React, Vue, Svelte 같은 프레임워크를 배우는 건 어렵지 않습니다. 하지만 선언형 사고를 익히는 건 그보다 훨씬 느리고, 어렵습니다.
명령형 사고는 "어떻게"를 떠올립니다. 선언형 사고는 "무엇이 되어야 하는가"를 떠올립니다. 이 질문 하나를 바꾸는 데에 몇 년이 걸리기도 합니다.
마무리하며
선언형 프로그래밍은 문법의 문제가 아닙니다. 복잡한 시스템을 사람이 이해할 수 있는 구조로 표현하는 법입니다.
좋은 선언형 코드는 ‘결과’만 남기고, 그 결과에 도달하는 과정을 자연스럽게 숨깁니다.
결국 선언형이란, 코드를 통해 의도를 직접 드러내는 방법입니다. 그리고 그 의도가 명확할수록, 코드의 수명은 길어집니다.
참고 및 추천
만약 이 주제가 흥미로웠다면, Evan Moon 님의 선언적 프로그래밍에 대한 착각과 오해 글을 읽어보시길 권합니다.
이 글의 일부 논의는 위 글을 참고해 재구성했습니다.