함수명 앞에 _(언더스코어) 를 붙이는 이유는 뭘까?

송연지·2025년 6월 26일
0


프론트엔드 프로젝트를 하다 보면 함수 이름 앞에 _(언더스코어)가 붙은 코드를 종종 보게 됩니다.

예를 들면 _getUser, _registerLeader 같은 함수들이죠.

처음엔 단순한 스타일 차이 정도로 생각하기 쉽지만,

실제로는 이 방식이 실무에서 꽤 널리 쓰이는 네이밍 컨벤션이라는 걸 알게 됩니다.

이번 글에서는

  • 왜 함수 앞에 _를 붙이는지
  • 언제 쓰고 언제 안 쓰는지
  • 어떤 상황에서 유용한지
  • 실제로 제가 작업한 코드에서 어떻게 적용했는지를 기준으로 정리해 보려고 합니다.

_는 "이건 내부 전용입니다"라는 뜻이에요

TypeScript에서는 클래스 내부가 아니라면 private 접근 제한자를 사용할 수 없습니다.

그래서 함수 이름에 _를 붙여 개발자끼리의 약속처럼 "이건 외부에서 직접 호출하지 마세요"라는 의미를 표현합니다.

이걸 문법적으로 막을 수는 없지만, 의도를 명확히 드러내는 방식이죠.

예를 들어 아래처럼 구조를 나눕니다:

// 내부 전용 - 순수 API 호출
const _getUser = createPostApi('/api/getUser');

// 외부에 노출할 래퍼 함수
export const getUser = (params) => {
  console.log('[API 요청]', params);
  return _getUser(params).then(...).catch(...);
};

이렇게 하면 _getUser()는 실제로는 외부에서 직접 쓰지 않고,

getUser()만 import해서 사용하게 됩니다.

이런 식으로 작성하면 코드를 읽는 사람 입장에서도

“아, 이 함수는 외부에서 직접 쓰는 게 아니라 내부에서만 호출해야겠구나” 하고 자연스럽게 받아들이게 됩니다.


✅ 그럼 모든 함수에 _를 붙여야 할까? 그렇진 않습니다.

실제로 제가 작업했던 프로젝트에서도 모든 API 함수에 _를 붙이지는 않았습니다.

예를 들어 leader.ts 모듈에서는 _registerLeader, _requestManager

내부 전용 함수에 _를 붙인 반면,

department.ts 모듈에서는 getDepartments, addDepartment 같은 함수들을 별도 구분 없이 바로 export했습니다.

이 차이는 구조와 의도에 따라 나뉩니다:

구조 형태_ 사용 여부이유
내부 전용 함수 + 래퍼 함수가 분리됨✅ 사용역할이 나뉘어 있고, 직접 호출을 막기 위해
export하는 함수가 바로 사용됨❌ 사용 안 함중간 래핑 없이 단일 책임이므로 구분 필요 없음

✅ 실무에서는 어떤 기준으로 쓰게 될까?

저는 아래 기준 중 하나라도 해당되면 _를 붙여 내부용 함수로 작성하는 편입니다:

  • 해당 함수는 axios 같은 순수 API 호출만 처리하고, 사용자 응답 처리나 로깅은 하지 않을 때
  • 로깅, 에러 메시지, 토스트 처리 등을 담당하는 래퍼 함수가 따로 있을 때
  • 내부적으로만 재사용하고, 외부에는 노출되지 않아야 하는 유틸 로직일 때
  • 테스트나 mocking 대상일 뿐, UI 흐름에는 사용되지 않을 때

반대로 아래 상황에서는 _ 없이 함수명을 바로 export합니다:

  • 컴포넌트나 다른 모듈에서 직접 호출될 목적으로 만들어진 함수
  • 함수 자체가 간단해서 래핑이 필요 없는 경우 (ex. CRUD 단일 처리)
  • 별도 래퍼가 존재하지 않고, 그대로 공식 함수 역할을 할 때

팀 컨벤션으로 정리하면 이런 식입니다

API 네이밍 컨벤션

- `_함수명`: 내부 전용 함수. API 요청만 수행. 예외 처리, 사용자 피드백 없음.
- `함수명`: 외부에서 사용하는 공식 함수. 로깅, 에러 핸들링, 응답 가공 포함.
- `use함수명`: React에서 사용하는 커스텀 훅. get 함수 호출을 감싸서 상태 관리.

실제 디렉터리 구조에 적용해보면

/src
  └── api
        └── user.ts           # _getUser, getUser
  └── hooks
        └── useUser.ts        # useUser
  • api 폴더에는 _ 접두사가 붙은 내부 API 함수와 래퍼 함수가 함께 정의되고,
  • hooks 폴더에는 외부 API를 감싸는 useXXX 형태의 훅이 정의됩니다.

테스트, 유지보수, 협업 모두에 도움이 됩니다

  • _함수는 mocking이나 테스트에서 훨씬 다루기 쉬워요.
  • export된 래퍼 함수는 외부에서는 항상 일정한 방식으로 응답을 처리하게 만들어 줍니다.
  • 또한 역할이 명확하게 나뉘기 때문에, 협업 중에도 함수가 어떤 용도로 쓰이는지 쉽게 파악할 수 있습니다.
//내부 API 함수만 mocking해서 래퍼 함수는 실제 흐름대로 테스트할 수 있습니다.
jest.mock('@/api/user', () => ({
  _getUser: jest.fn().mockResolvedValue(mockData)
}));

📦 래퍼 함수란?

다른 함수(또는 로직)를 감싸서, 추가 동작을 함께 수행하는 함수를 말합니다.

즉, "기존 기능을 그대로 호출하되, 그 앞뒤로 무언가 더 하는 함수"예요.

포장지(wrap)처럼 감싼다고 해서 "래퍼(wrapper)"라는 이름이 붙었어요.

  • 래퍼 함수의 역할 요약
역할설명
로깅 추가요청 전후 로그를 출력
에러 핸들링try/catch 또는 toast 처리
응답 가공res.data.result 등 필요한 데이터만 추출
보안 처리, 권한 검사 등 추가로직필요시 삽입 가능
  • 실무에서 래퍼 함수가 필요한 이유
    • 순수 API 함수는 재사용성이 높고 테스트가 쉬움_함수

    • 래퍼 함수는 사용자 경험, 로깅, 예외처리를 담당 → 실제 호출은 여기서만 하도록 설계

      이렇게 나누면 책임이 분리되고 코드가 더 읽기 쉬워지고,
      실수로 순수 함수만 호출해서 문제가 생기는 걸 막을 수 있습니다.

  • 간단한 정의로 정리

    래퍼 함수는 어떤 함수나 로직을 감싸서, 부가적인 동작을 함께 수행하는 함수입니다.

    API 요청뿐 아니라, 로깅, 오류 처리, 응답 정리 등을 포함할 때 주로 사용됩니다.


_를 안 쓰면 생기는 문제도 있어요

  • 함수의 역할이 모호해져서, 테스트나 구조 분해가 어렵습니다.
  • 실수로 내부 함수 (_getUser)를 외부에서 import해서 써버릴 수도 있어요.
  • 에러 처리나 로깅 없이 API가 호출되면, 사용자 입장에선 아무 반응이 없어서 불편할 수 있어요.

이런 이유로 대부분의 실무 코드에서는 _를 통해 책임을 분리하고,

함수의 성격에 맞게 구분하는 방식을 선택하고 있습니다.


📌 마무리: 단순한 스타일이 아닌 설계의 결과

함수명 앞에 _를 붙이는 건 단지 네이밍 스타일이 아닙니다.

코드의 책임과 사용 범위를 명확히 하기 위한 실용적인 컨벤션입니다.

  • 실수로 잘못된 함수를 import하지 않도록 도와주고
  • 테스트와 디버깅을 쉽게 만들어주며
  • 코드 구조에 일관성을 부여합니다

특히 복잡한 API 처리, 민감한 보안 기능, 사용자 피드백이 필요한 영역에서는

순수 API 호출과 부가 로직을 분리하는 구조가 큰 도움이 됩니다.

이런 구조는 협업에서도 이해하기 쉽고, 유지보수 시에 의도를 파악하기 쉬워서 장기적으로 효율적인 패턴이 됩니다.

profile
프론트엔드 개발쟈!!

0개의 댓글