← 블로그 · · ConvertCaseHQ 팀 · 6분 분량

JavaScript 변수 이름 짓기: camelCase 중심의 개발자 가이드

JavaScript에서는 변수, 함수, 매개변수, 객체 속성의 이름을 camelCase(cartTotal, fetchOrders)로, 클래스와 React 컴포넌트의 이름을 PascalCase(ShoppingCart)로 짓고, CONSTANT_CASE는 프로그램이 실행되는 내내 고정된 값(MAX_RETRIES)에만 쓰세요. Google과 Airbnb의 JavaScript 스타일 가이드가 이 정도는 의견이 같고, JavaScript 자체의 내장 객체도 같은 패턴을 따릅니다.

이 가이드의 나머지는 엔진이 실제로 강제하는 것, 그 위에 얹힌 관례, 그리고 팀마다 의견이 갈리는 예외 상황을 다룹니다. 어떤 const 값에 대문자를 쓸지, private 필드, 약어, API에서 오는 snake_case 데이터가 그것입니다.

JavaScript 자체가 허용하는 것

언어의 규칙은 짧습니다. 식별자는 글자, 밑줄, 달러 기호로 시작하고, 그 뒤에는 숫자도 올 수 있습니다. 글자는 ASCII에 한정되지 않으며, MDN의 예시는 독일어 단어 Früh입니다. 이름은 대소문자를 구분하므로 total과 Total은 서로 다른 변수이고, new, class, delete 같은 예약어는 변수 이름으로 쓸 수 없습니다.

let total = 1;
let Total = 2;      // a different variable
const café = "ok";  // Unicode letters are allowed
const $price = 9.99;
const _unused = null;

// const 2fast = true;  // SyntaxError: can't start with a digit
// const new = 1;       // SyntaxError: reserved word

속성 이름은 훨씬 느슨합니다. 따옴표로 묶으면 어떤 문자열이든 쓸 수 있고, headers["Content-Type"]이 가능한 이유가 이것입니다. 이 규칙 너머의 모든 것은 관례이며, 그래서 미리 합의해 둘 가치가 있습니다.

변수, 함수, 속성은 camelCase

camelCase는 JavaScript에서 이름을 붙이는 거의 모든 것의 기본값입니다. 값에는 명사로, 함수에는 동사로 이름을 지으면 코드 한 줄이 문장처럼 읽힙니다.

const orderCount = orders.length;
const isLoggedIn = Boolean(session);
const user = { firstName: "Ada", lastLogin: null };

function calculateShipping(order) {
  // ...
}

불리언에는 is, has, can 접두사(isLoggedIn, hasAccess)를 붙이면 아무도 묻기 전에 “무엇이 참이거나 거짓인가?”라는 물음에 답이 됩니다. Google의 가이드도 불리언 getter에 같은 접두사(isFoo, hasFoo)를 씁니다.

Google의 가이드에는 길이에 관한 좋은 규칙도 있습니다. 무리 없는 범위에서 최대한 서술적으로 짓고, 글자를 빼서 줄이지 말라는 것입니다. errorCount와 customerId는 통과하고, nErr와 cstmrId는 통과하지 못합니다. 이 가이드의 예외는 유효 범위가 10줄 이하인 변수로, 짧은 반복문의 i나 points.map((x) => x * 2)의 x가 그런 예입니다.

컬렉션, 개수, 이벤트 핸들러

몇 가지 패턴만 따르면 이름을 예측할 수 있게 됩니다. 배열에는 복수형(users)을, 항목 하나에는 단수형을 써서 for (const user of users)가 자연스럽게 읽히게 하세요. 조회용 객체는 키 이름을 따서(usersById), 숫자는 세는 대상을 따서(userCount) 이름을 짓습니다. React 문서는 이벤트에 대한 흔한 관례를 설명합니다. 핸들러는 handle에 이벤트를 붙여(handleClick) 이름을 짓고, 핸들러를 받는 props는 on과 대문자로 시작합니다(onClick, onSave).

클래스, 생성자, 컴포넌트는 PascalCase

new로 생성하는 것은 무엇이든, 그리고 React가 컴포넌트로 렌더링하는 것은 무엇이든 PascalCase를 씁니다.

class ShoppingCart {
  addItem(item) {
    // ...
  }
}
const cart = new ShoppingCart();

function OrderSummary({ order }) {
  return <section>{order.id}</section>;
}

클래스에서는 관례이지만 React에서는 필수입니다. React 문서에 따르면 컴포넌트 이름은 대문자로 시작해야 하며, 그러지 않으면 동작하지 않습니다. JSX가 <section> 같은 소문자 태그를 평범한 HTML 요소로 취급하기 때문입니다. TypeScript에서는 인터페이스, 타입 별칭, 열거형에도 같은 PascalCase를 씁니다.

내장 객체가 이 모든 것의 본보기입니다. 생성자는 PascalCase(Map, Promise, Date), 메서드는 camelCase(Object.fromEntries, Array.prototype.flatMap), 고정된 값은 대문자(Math.PI, Number.MAX_SAFE_INTEGER)입니다.

CONSTANT_CASE를 쓸 때와 쓰지 않을 때

상수가 가장 큰 혼란의 원인입니다. const가 ‘상수’를 뜻하지 않기 때문입니다. 이 키워드는 변수에 다시 값을 대입하는 것을 막을 뿐, 그 변수가 가리키는 배열이나 객체는 여전히 바뀔 수 있습니다. 두 대형 스타일 가이드는 대문자를 그보다 엄격한 경우에 한정합니다.

  • Google: CONSTANT_CASE는 깊은 수준까지 불변인 모듈 수준의 값(과 정적 속성)에 씁니다. 객체를 바꾸지 않겠다는 의도만으로는 충분하지 않으며, 함수 안에서 선언한 상수는 lowerCamelCase를 유지합니다.
  • Airbnb: 대문자는 선택 사항이며, export되고 const로 선언되었으며 (중첩된 속성까지 포함해) 절대 바뀌지 않는다고 믿을 수 있는 값에만 씁니다. 파일 안의 모든 const를 대문자로 쓸 필요는 없습니다.
// Module level, never changes: capitals
export const MAX_UPLOAD_BYTES = 5 * 1024 * 1024;
export const ORDER_STATUS = Object.freeze({ PENDING: "pending", SHIPPED: "shipped" });

function visibleNames(items) {
  const visible = items.filter((item) => !item.hidden); // const, but not a constant
  const names = [];                                     // const binding, mutable array
  for (const item of visible) names.push(item.name);
  return names;
}

두 가이드는 열거형처럼 쓰는 객체의 키에서 의견이 갈립니다. Google은 위처럼 열거형 멤버를 CONSTANT_CASE로 쓰고, Airbnb는 대문자로 export한 객체 안의 키를 소문자로 쓰는 쪽을 선호합니다. 코드베이스 전체가 같은 방식을 쓴다면 어느 쪽이든 괜찮습니다. 한 가지 더 알아 둘 점이 있습니다. Object.freeze는 얕게 동결하므로, 동결된 객체도 안에 중첩된 객체를 가지고 있으면 그 객체는 바뀔 수 있습니다.

private 필드: 밑줄 말고 #을 쓰세요

맨 앞 밑줄(this._count)로 ‘private’ 멤버를 표시하던 오랜 습관은 힌트일 뿐이며, 누구든 그 속성을 여전히 읽고 쓸 수 있습니다. Airbnb의 가이드는 바로 그 이유로 앞이나 뒤에 밑줄을 쓰지 말라고 하고, Google의 TypeScript 가이드도 접두사와 접미사로서의 밑줄을 금지합니다. 최신 JavaScript에는 진짜 private 필드가 있습니다.

class Counter {
  #count = 0;
  increment() {
    return ++this.#count;
  }
}

const counter = new Counter();
counter.increment();
counter.count;   // undefined
// counter.#count  // SyntaxError outside the class

약어: userId일까, userID일까?

Google의 규칙은 약어를 일반 단어처럼 다루는 것입니다. customerId, xmlHttpRequest, loadHttpUrl처럼요. Airbnb의 규칙은 반대입니다. SMSContainer나 httpRequests처럼 약어를 전부 대문자로 쓰거나 전부 소문자로 씁니다. 브라우저 자체도 결정하지 못해서, XMLHttpRequest, innerHTML, getElementById, encodeURIComponent가 한 플랫폼에 다 들어 있습니다.

저희라면 Google의 규칙을 고르겠습니다. 예측할 수 있고, 단어 경계가 눈에 보이며, 다른 대소문자 스타일로 깔끔하게 변환되기 때문입니다. 하지만 어느 쪽을 고르느냐보다 일관성이 더 중요합니다. 주요 가이드마다 약어 규칙이 어떻게 다른지는 camelCase와 PascalCase 차이에서 비교했습니다.

API에서 오는 snake_case 데이터 처리

Python, Ruby, SQL 데이터베이스를 기반으로 한 API는 종종 first_name 같은 키를 내려 줍니다. 이런 키가 코드 곳곳에 퍼지게 두지 마세요. 데이터가 들어오는 지점에서 이름을 바꾸세요. 구조 분해(destructuring)를 쓰면 한 줄로 끝납니다.

const response = await fetch("/api/users/42");
const { first_name: firstName, last_login: lastLogin } = await response.json();

페이로드 전체라면 방향마다 키를 재귀적으로 변환하는 작은 함수를 하나씩 만들고 API 클라이언트에서 호출하세요. 그런 함수 하나를 camelCase JSON 키를 snake_case로 변환하는 법에서 단계별로 만들어 봅니다. 한 번만 바꾸면 되는 경우에는 저희 대소문자 변환기가 last_login을 클릭 한 번으로 lastLogin으로 바꿔 줍니다.

ESLint로 강제하기

ESLint의 핵심 규칙 두 가지가 이 대부분을 다룹니다. camelcase는 이름 중간의 밑줄을 지적하면서 전부 대문자인 상수와 앞뒤 밑줄은 무시합니다. new-cap은 소문자 이름에 new를 쓴 경우와, 대문자로 시작하는 함수를 new 없이 호출한 경우를 지적합니다.

// eslint.config.js
export default [
  {
    rules: {
      camelcase: ["error", { properties: "never" }],
      "new-cap": "error",
    },
  },
];

properties: "never" 옵션은 camelcase가 data.first_name 같은 API 필드에는 불평하지 않게 하면서, 직접 선언한 변수는 계속 검사하게 합니다. TypeScript 프로젝트에서는 @typescript-eslint/naming-convention이 한 걸음 더 나아가, 변수, 함수, 클래스, 타입 매개변수 등 식별자 종류마다 허용하는 형식을 정할 수 있게 해 줍니다. Google의 전체 규칙은 Google JavaScript Style Guide에 있습니다.

JavaScript 변수 이름 짓기 체크리스트

  • 변수, 함수, 매개변수, 메서드, 속성은 camelCase로.
  • 클래스, 생성자, React 컴포넌트, TypeScript의 타입, 인터페이스, 열거형은 PascalCase로.
  • CONSTANT_CASE는 절대 바뀌지 않는 모듈 수준의 값에만 쓰고, 모든 const에 쓰지는 않기.
  • 밑줄 접두사 대신 #private 필드 쓰기.
  • 약어 규칙은 하나만 정해서 기여 가이드(contributing guide)에 적어 두기.
  • snake_case API 키는 경계에서 한 번만 변환하기.

© 2026 ConvertCaseHQ. All rights reserved.