폴더 구조 설계 시 헬퍼와 유틸을 구분하는 방법

장운서·2025년 12월 4일

이론

목록 보기
6/6
post-thumbnail

안녕하세요, 서운입니다.
이번 글에서는 관심사 분리(Separation of Concerns)라는 큰 원칙 안에서, 실무에서 자주 헷갈리는 헬퍼 함수와 유틸 함수의 차이를 정리해보려고 합니다.

빠르게 MVP를 만들어야 하는 상황에서는 함수 책임을 세세하게 나누기 어렵고, 그 결과 폴더 구조나 모듈 구조가 중장기적으로 유지보수가 어려워지곤 합니다.
저 역시 프로젝트를 진행하면서 헬퍼와 유틸의 경계를 명확히 못 잡아 후반에 구조를 다시 손봐야 했던 경험들이 많습니다.

그래서 이번 글에서는 헬퍼와 유틸을 어떤 기준으로 구분해야 하는지, 폴더 구조를 어떤 관점으로 설계하면 좋은지, 실무에선 어떻게 적용해야하는지를 차분하게 정의해보겠습니다.


1) 헬퍼와 유틸의 정의

헬퍼(Helper)

  • 특정 기능이나 도메인의 로직을 보조하는 함수
  • 특정 페이지·상황·모듈과의 의존성이 있음
    예시:
// 예약 폼에서만 사용하는 날짜 포맷
export function formatReservationDate(date: Date) {
  return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
}

// 장바구니 페이지에서 필요한 총 가격 계산(쿠폰 로직 포함)
export function calculateCartTotal(cartItems, coupon) {
  const sum = cartItems.reduce((acc, item) => acc + item.price * item.qty, 0);
  return coupon ? sum - coupon.discount : sum;
}

유틸(Utility)

  • 어디서든 재사용 가능한 범용 함수
  • 도메인 의존성이 없음
  • 순수 함수(Pure Function)일 가능성이 높음
    예시:
// 숫자를 금액 포맷으로 변환
export function formatCurrency(value: number) {
  return value.toLocaleString();
}

// 딜레이 제어
export function debounce(fn: Function, delay: number) {
  let timer: any;
  return (...args: any[]) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

➡ 성격이 다른 두 함수를 각각 다른 페이지에서 쓰려고 가져가면 대부분 맞지 않습니다.


어떻게 구분해야 하는가?(체크 리스트 4가지)

아래 기준이면 대부분 정확히 구분이 된다고 생각합니다.

1) 도메인(업무 로직)에 의존하는가?

  • 의존하면 헬퍼
  • 의존하지 않으면 유틸

예:
formatReservationDate → 예약 도메인 의존 → 헬퍼
formatCurrency → 비즈니스 관계 없음 → 유틸

2) 재사용 범위

  • 한 페이지/도메인에서만 쓰임 → 헬퍼
  • 전역 공통 → 유틸

3) 부작용(Side-effect)

  • API 호출, UI 상태 변경, 로컬 스토리지 접근 → 헬퍼
  • 순수 계산만 한다 → 유틸

4) 이름에서 도메인이 느껴지는가?

  • calculateCartTotal, validateReservationTime → 헬퍼
  • debounce, deepClone → 유틸

3. 폴더 구조에 반영하는 방법

아래 구조는 React·Vue·Next 모두 적용 가능하며, 제가 항해 부트캠프를 하고 나서 생각해본 도메인 위주의 폴더 구조 아키텍쳐 입니다.

src/
  utils/
    format/
      formatCurrency.ts
    math/
      clamp.ts
    string/
      toKebabCase.ts

  helpers/
    cart/
      calculateCartTotal.ts
      buildCartPayload.ts
    reservation/
      formatReservationDate.ts
      isAvailableTime.ts

핵심 규칙:

  • 유틸은 완전한 범용성
  • 헬퍼는 도메인별로 폴더 구분

특히 헬퍼를 도메인 단위로 나누면
“여기는 카드 결제 로직”, “여기는 예약 로직”
처럼 기능 영역이 명확해져 가독성이 크게 올라갑니다.


4. 실무에서 자주 발생하는 문제와 해결 패턴

4챕터에서는 보통 실무에서 일어나는 유틸과 헬퍼 함수를 제대로 구분하지 않아 발생하는 문제의 패턴들을 정리해보도록 하겠습니다.

문제 1) utils/index.ts 안에 모든 공용 함수가 다 들어감
원인: 헬퍼와 유틸 구분 기준이 없음**

해결:
먼저 “도메인 의존 여부”로 나누고
헬퍼는 도메인 폴더로 이동
유틸은 카테고리별로 정리(숫자/문자열/날짜)

문제 2) 유틸처럼 보이지만 실제론 도메인 로직이 섞임

예:

export function formatDateForUI() {
  return dayjs().format("YYYY-MM-DD (예약)");
}

여기 (예약)이 들어가 있으므로 → 헬퍼입니다.

문제 3) 같은 함수지만 폴더가 헷갈림

예: 날짜 변환 로직
formatDate(date) → 유틸
formatReservationDate(date) → 헬퍼
이 기준을 유지하면 중복 또는 이동 혼란이 줄어듭니다.


마무리

헬퍼와 유틸을 구분하는 핵심은 도메인 의존성과 재사용 범위입니다.
이 기준만 잡히면 폴더 구조가 자연스럽게 정리되고, 유지보수가 훨씬 수월해집니다.

  • 유틸 = 어디서나 동작하는 순수 기능
  • 헬퍼 = 특정 기능을 보조하는 도메인 로직

프로젝트가 커질수록 이 기준은 더욱 중요해집니다.
폴더 구조가 흔들리지 않는 개발 환경을 만들고 싶다면,
헬퍼와 유틸의 역할을 명확히 구분하는 것부터 시작해도 충분히 효과가 있습니다.

profile
성공을 위해선 과정만 있을 뿐이다

0개의 댓글