선언적인 개발에 대해

Hunjin·2025년 12월 19일
post-thumbnail

선언형 프로그래밍이란?

선언의 사전적 의미는 “널리 펴서 말함, 또는 그런 내용”입니다. 그러면 선언형 프로그래밍은 원하는 결과를 묘사하는 방식으로 코드를 작성하는 프로그래밍입니다.

즉, 선언적인 개발은 “WHAT”이라고 할 수 있습니다.

그러면 선언적인 개발을 하기 위해서 “추상화”라는 단어를 뗄 수 없습니다.

추상화란?

필요 없는 디테일을 숨기고, 필요한 것만 드러내는 것
Toss에서는 이렇게 말합니다.

“토스에서는 선언적인 코드가 항상 좋은 것이 아니라, 앞으로의 제품이 어떻게 변화할지, 비즈니스 요구사항이 어떻게 되는지에 따라 달라질 수 있다고 생각합니다. 앞으로의 코드의 어떤 부분이 수정될지 예측하고, 이에 따라 적절한 선언 레벨을 따르는 코드를 작성할 필요가 있습니다.”

그럼 추상화 레벨이 뭘까?

한 문장으로 말하면 “얼마나 WHAT에 가깝고, 얼마나 HOW에 가깝냐”의 정도입니다.

  • 추상화 레벨이 높다 → 의도, 목적, 역할
  • 추상화 레벨이 낮다 → 구현, 절차, 수단

동일한 기능에서 다른 추상화 레벨에 대한 예시입니다.

  1. 추상화 레벨 낮음
if(user){
    setIsLoggedIn(true);
} else {
    setIsLoggedIn(false);
}
  • 상태를 하나 바꾸는데 구현 디테일이 매몰된다.
  1. 추상화 레벨 높음
const authStatus = getAuthStatus(user);
  • 내부 구현 몰라도 됨
  • “인증 상태를 가져올게~” 라는 의도를 파악할 수 있음

즉 선언적 개발은 추상화 레벨을 위로 끌어올리는 행위입니다.


아하! 그럼 선언적인 개발이 추상화 레벨을 높여 함수로 감싸서 내부 로직이 보이지 않게 하는 행위를 말하는구나!! 라고 생각하였습니다.

과연 이게 선언적인 개발일까요?

정답은 반은 맞고 반은 틀렸다 입니다.

단순히 함수를 감싸는 행위 자체가 “선언적”인것은 아니지만, 함수를 통해 “어떻게(HOW)를 숨시고” 무엇을(WHAT)만 남겼다면 그것은 선언적인 코드로 나아가는 방법이 맞습니다.

하지만 많은 분들이 착각하는 부분이 있습니다.

함수는 ‘포장지’이지 ‘마법’이 아니다

많은 개발자들이 function 키워드나 화살표 함수로 코드를 감싸기만 하면 추상화가 일어났다고 생각합니다. 하지만 함수 내부가 “A를 먼저 하고, 그 다음 B를 해”라는 단계별 지시 사항으로 되어 있다면 그것은 추상화가 아닌 ‘이름표가 붙은 명령형 코드’일 뿐입니다.

  • 명령형 함수 : 이제부터 내가 시키는 대로 순서대로 움직여
  • 선언형 함수 : 나는 이런 입력을 주면 저런 출력이 나오는 관계야

배열 메소드라는 도구의 오용

  • 가짜 선언형 : map 안에서 let을 선언하고 조건문을 덕지덕지 붙임 → 도구만 선언형일 뿐, 사고방식은 여전히 HOW
  • 진짜 선언형 : map(applyTax) 처럼 비자니스 로직의 이름을 전달함

많은 개발자가 놓치는 부분인데 items.map(item → {… 복잡한 로직…})은 사실 for 루프를 map이라는 이름으로 바꾼 것에 불과할 떄가 많습니다.

즉 선언형은 문법이 아니라 “로직의 책임 위치”을 옮기는 작업이라 할 수 있습니다.

JSX는 왜 선언적일까?

JSX의 선언적 특성은 어떻게(How)가 아닌 무엇(What)을 표현하는 데 있습니다.

1. 시간적 순서가 아닌 구조적 관계 표현

명령형 방식

function createUserProfile(user) {
  const container = document.createElement('div');
  container.className = 'user-profile';
  
  const nameElement = document.createElement('h2');
  nameElement.textContent = user.name;
  container.appendChild(nameElement);  // 순서가 중요!
  
  const emailElement = document.createElement('p');
  emailElement.textContent = user.email;
  container.appendChild(emailElement);  // 순서가 중요!
  
  return container;
}

이 코드는

  1. 컨테이너를 만듦
  2. 이름 요소 만듦
  3. 이름 요소 추가
  4. 이메일 요소 만듦
  5. 이메일 요소 추가

라는 절차적 단계에 집중하고 있습니다.

선언적 방식

function UserProfile({ user }) {
  return (
    <div className="user-profile">
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      {user.avatar && <img src={user.avatar} alt={`${user.name}의 아바타`} />}
    </div>
  );
}

이 코드는 “UserProfile은 이름, 이메일, 아바타로 구성된다”는 최종 구조만 선언합니다.

DOM 조작 순서는 리액트가 알아서 처리합니다.

2. 데이터 구조와 UI 구조의 직관적 대응

JSX의 진짜 장점은 데이터의 모양이 곧 UI가 된다는 점입니다.

const TodoList = ({ todos }) => (
  <ul>
    {todos.map(todo => (
      <TodoItem 
        key={todo.id} 
        text={todo.text} 
        completed={todo.completed} 
      />
    ))}
  </ul>
);

이 코드를 보면

  • todos 배열 → <ul> 리스트
  • todo 객체 → 각 <TodoItem> 컴포넌트

라는 1:1 대응 관계가 한눈에 들어옵니다.

이 코드를 명령형으로 작성하면

function createTodoList(todos) {
  const ul = document.createElement('ul');
  
  for (let i = 0; i < todos.length; i++) {
    const li = document.createElement('li');
    li.textContent = todos[i].text;
    if (todos[i].completed) {
      li.classList.add('completed');
    }
    ul.appendChild(li);
  }
  
  return ul;
}

데이터 순회, 요소 생성, 조건 처리, DOM 추가 등이 섞여있어 전체 로직을 읽고 실행해봐야 최종 구조를 파악할 수 있습니다.

선언형의 본질

JSX가 선언적인 이유는

  • 복잡한 DOM 조작을 추상화
  • 데이터와 UI 사이의 관계를 직접적으로 표현할 수 있음

느낀점


이번 아티클 작성을 하면서 선언적인 개발 사고가 앞으로 AI 시대에 중요한 개념이라는 생각이 들었습니다.

지난 수십 년간 개발의 정석은 문법을 외우고 알고리즘을 짜는 것이었습니다.
하지만 이제 최고의 프로그래밍 언어는 영어입니다.

개발자는 코드를 치는 사람이 아니라 스펙을 정의하는 사람, 즉 프롬프트가 코드가 되고 문서가 소프트웨어가 되는 세상입니다.

개발자의 정의가 바뀌었습니다.

HOW에 집착하는 기술에서 WHAT을 만들지 결정하는 설계자

그래서 저는 선언적인 사고 방식이 더욱 중요하며 앞으로 개발자로서 뒤쳐지지 않고 성장하는데
필수적인 개념이라고 생각하였습니다.

profile
프론트 개발을 해보아요👨🏻‍💻

0개의 댓글