STUDY/[ JavaScript ]

자바스크립트

Lim임 2025. 11. 3. 03:47

 

 

 

 

 

 

Symbol이 무엇이냐

 

JavaScript의 Symbol은 “유일하고 변경 불가능한” 값인 원시 타입으로, 주로 객체의 충돌 없는 프로퍼티 키로 쓰입니다.

한 줄 요약
Symbol은 서로 절대 겹치지 않는 고유한 키를 만들기 위한 원시 타입이에요.

 

 

= 심볼은 접근할 수가 없어?

 

완전한 비공개는 아니고 보통은 안 보이게 숨겨질 뿐

심볼 키 프로퍼티는
점 표기(obj.foo)나 문자열 키(obj['foo'])로는 못 접근
해당 심볼 값을 갖고 있으면 obj[thatSymbol]로 접근 가능
for...in, Object.keys, JSON.stringify 등에 안 잡힘(숨겨짐)
하지만 Object.getOwnPropertySymbols, Reflect.ownKeys로 “찾아내서” 접근 가능
Symbol.for('key')로 만든 전역 심볼은 누구나 같은 심볼을 얻어 접근할 수 있음.
즉 “비공개처럼 보이지만(private 흉내)” 진짜 비공개는 아님. 진짜 프라이빗은 클래스 #필드 사용 권장.

 

아~그니까 

 

= 아하 그러니까 심볼은 심볼용 도구로만 볼 수 있ㄴ으니까
짜자 ㄴ객체인것ㅇ처럼 보이지만 사실은 심볼이라서 너네마음대로 볼 수 없지롱~ 인거야?

 

심볼은 “객체처럼 보이는 무언가”가 아니라, 원시 타입 값이야. typeof 결과는 'symbol'.
“심볼용 도구로만 볼 수 있다”기보다는, 그 “같은 심볼 값(열쇠)”을 가지고 있어야 해당 프로퍼티에 접근 가능해.
기본 열거(for...in, Object.keys, JSON.stringify)에는 안 보이지만, 반사 API로는 볼 수 있어. 즉 숨김(hiding)이지 보안(secrecy)은 아님.
Object.getOwnPropertySymbols(obj)
Reflect.ownKeys(obj)
Symbol.for('키')로 만든 전역 심볼은 아무 데서나 같은 심볼을 얻을 수 있어서 접근이 더 쉬워짐.
비유

문자열 키 = 이름표. 같은 이름이면 충돌 가능.
심볼 키 = 복제 불가능한 “열쇠”. 열쇠를 가진 사람만 딱 맞춰 열 수 있음. 다만 특수 도구(반사 API)로 “열쇠가 꽂혀 있는지”는 확인 가능.

 

= 딱 하나만 있어서 딱 유일한 키값으로만 접근할 수 있다는 것도 알겠어
그러면 숨김이라는 건 키 값을 알고있는 사람만 볼 수 있어서 숨김이야?

 

짧은 답: “키를 아는 사람만 볼 수 있다”는 절반만 맞습니다. 심볼은 기본적으로 숨겨질 뿐이고, 반사 API로 찾아낼 수 있어요. 즉, 보안용이 아니라 소프트(hiding)한 은닉입니다.

정리

직접 접근: 그 “심볼 값(레퍼런스)”을 가지고 있으면 obj[sym]로 접근 가능.
모를 때도 찾기 가능: 다음 API로 객체의 심볼 키를 꺼낼 수 있어요.
Object.getOwnPropertySymbols(obj)
Reflect.ownKeys(obj) // 문자열 키 + 심볼 키 모두 반환

const secret = Symbol('secret');
const o = { [secret]: 42 };

// 심볼을 알면 바로 접근
o[secret]; // 42

// 심볼을 몰라도 찾아낼 수 있음
const syms = Object.getOwnPropertySymbols(o); // [Symbol(secret)]
o[syms[0]]; // 42

// 기본 열거엔 안 나옴
Object.keys(o);        // []
JSON.stringify(o);     // "{}"
for (const k in o) {}  // 아무 것도 안 돎

 

1. 충돌 없이 내부 메타데이터 붙이기
남이 만든 코드/라이브러리와 같은 객체를 공유할 때, 문자열 키('id', 'type')로는 충돌 위험이 있어요.
심볼 키는 절대 안 겹치니 안전하게 내부값을 숨겨둘 수 있어요.

 

const META = Symbol('meta'); // 설명은 디버깅용
const user = { name: 'Alice' };
user[META] = { loadedAt: Date.now() };

Object.keys(user);           // ["name"]  ← 안 보임
user[META];                  // { loadedAt: ... }  ← 열쇠(심볼)로만 접근

2. 라이브러리/플러그인 간 안전한 확장 포인트
공용 객체(req, ctx, element 등)에 각자 정보를 붙여야 할 때, 키 충돌 없이 확장 가능해요.

// 어떤 미들웨어가 요청객체에 붙이는 내부 컨텍스트
const CONTEXT = Symbol('context');
function attachContext(req) {
  req[CONTEXT] = { userId: 123, roles: ['admin'] };
}

3. 안전한 상수/Enum 비슷하게 쓰기
문자열 상수는 오타/중복으로 섞이기 쉬운데, 심볼은 값 자체가 유일해서 비교가 확실해요.

 

const Status = {
  PENDING:  Symbol('PENDING'),
  DONE:     Symbol('DONE'),
  FAILED:   Symbol('FAILED'),
};

let s = Status.PENDING;
if (s === Status.PENDING) { /* 정확히 이 값일 때만 참 */ }

 

 

사실상 은닉보다는 걍 상수로써 쓰는 일이 많은 것 같다~

 

객체는 메모리 주소값으로 보내진다는 뜻

 

자바스크립트는 정수타입이 없다~ 모든 숫자를 실수로 처리함

 

 

  • UTF-8: 1~4바이트 가변 길이. ASCII는 1바이트, 한글(완성형)은 보통 3바이트, 이모지는 4바이트. 웹/파일/네트워크의 사실상 표준.
  • UTF-16: 2 또는 4바이트 가변 길이(서로게이트). 대부분의 BMP 문자(‘가’, ‘한’ 등 한글 완성형)는 2바이트, 이모지 등 보충평면 문자는 4바이트.

utf8을 쓰면 가변길이라서 

한글은 2바이트 영어는 1바이트

공간을 효율적으로 쓴다~

 

실제로는 “한국어 텍스트니까 UTF-8”이 아니라, “웹/도구/DB/OS 전반의 기본이 UTF-8이기 때문”이에요.
서로 다른 환경 간 호환성과 표준성(특히 웹, API, Git, 데이터베이스의 utf8mb4) 때문에 UTF-8을 권장합니다.
UTF-8에서도 한글 표현은 완벽하며 문제 없습니다. 단지 바이트 수가 한 글자당 보통 3바이트라는 차이만 있어요.

 

= 그러면 string은 왜 utf16코드 단위의 시퀀스로 표현해?

 

결론부터 말하면: 역사적 이유 + 웹 호환성 + 구현 단순성 때문에 JS 문자열은 “UTF‑16 코드 유닛의 시퀀스”로 정의됐습니다.

왜 UTF-16이 되었나? (역사)

1995년 당시: Unicode가 “16비트면 충분(UCS‑2)”하던 시절이었고, 브라우저/OS/언어(특히 Java, Windows, 초기 DOM API)가 16비트 문자 기반이었습니다.
웹 호환성: 초창기 결정이 웹 전반에 퍼졌고, 이후 Unicode가 21비트로 확장되자 JS는 “서로게이트 쌍”을 받아들이며 UCS‑2 → UTF‑16로 자연 전환했습니다. 이미 수많은 코드/사양/브라우저가 16비트 가정을 하고 있어 바꾸기 어려웠습니다.
기술적 이유

단순한 인덱싱: 16비트 “코드 유닛” 단위로 O(1) 인덱싱이 쉬움. UTF‑8처럼 바이트 길이가 가변이면 임의 위치 접근이 비싸집니다.
광범위한 문자 1유닛: BMP(대부분의 한글/한자/알파벳 등)는 1 코드 유닛으로 표현 가능해 다국어 일반 텍스트 처리에 당시 기준으로 실용적이었어요.
플랫폼 연계: OS/폰트/라이브러리(예: Java, ICU, Windows API)와 브리징이 수월했습니다.
왜 지금도 안 바꾸나?

웹 호환성 비용이 너무 큼: String.length, 인덱싱, 정규표현식, DOM/Canvas/Text API 등 수많은 동작이 깨질 수 있음.
사양 안정성: ECMAScript는 “문자열 = 16비트 코드 유닛 시퀀스”라는 계약을 유지합니다. 엔진이 내부적으로 최적화(예: Latin‑1/1바이트 저장)를 하더라도, 겉으로 보이는 의미는 그대로입니다.

 

  • JS 문자열이 UTF‑16인 이유: 초창기 웹/플랫폼의 16비트 문자 전통과 그에 따른 거대한 호환성 이유, 그리고 인덱싱의 단순성.
  • 실무에서는 입출력은 UTF‑8, JS 내부는 UTF‑16이라는 “표현의 차이”를 이해하고, 코드포인트/그래페메 안전 API를 적절히 쓰는 것이 핵심입니다.

 

Reference Type

  1. 쉼표 연산자 (,)
  • 무엇: 여러 표현식을 왼쪽→오른쪽 순서로 평가하고, 마지막 표현식의 값만 반환하는 연산자.
  • 언제 쓰나: 보통 for 루프의 헤더에서 여러 업데이트식을 한 줄에 쓰거나, 한 줄에 부수효과를 순서대로 실행하고 마지막 값만 쓰고 싶을 때.
  • 주의: 연산자 우선순위가 “가장 낮음”. 섞어 쓸 땐 괄호로 의도를 분명히 하세요. 매개변수/배열/객체 리터럴의 쉼표는 “연산자”가 아니라 단순 구분자입니다.
  1. 옵셔널 체이닝 연산자 (?.)
  • 무엇: 체인 중간이 null 또는 undefined(= nullish)이면 평가를 중단하고 undefined를 반환. TypeError를 피하게 해줌.
  • 형태
    • obj?.prop
    • obj?.[expr]
    • func?.(args) // func가 null/undefined면 호출 안 하고 undefined
  • 언제 쓰나: 안전하게 중첩 속성 접근/메서드 호출/인덱싱을 할 때. 실패 시 그냥 undefined로 처리하고 싶을 때.
  • 함께 쓰면 좋은 것: 기본값은 nullish 병합 연산자 ??로 붙이기.
const user = { profile: { address: { city: 'Seoul' } } };

// 깊은 접근(없으면 undefined)
const city = user?.profile?.address?.city; // 'Seoul'

// 안전한 인덱싱
const first = user?.posts?.[0]; // posts가 없으면 undefined

// 선택적 콜(함수일 때만 호출)
const onClick = undefined;
onClick?.('hi'); // 아무 일도 안 일어남 (에러 없음)

// 기본값 결합
const zip = user?.profile?.address?.zip ?? 'unknown';

중요한 차이: ?? vs ||

  • ||는 falsy(0, '', false)도 대체해버림.
  • ??는 null/undefined일 때만 대체.
const n = 0;
n || 10; // 10 (원치 않는 대체)
n ?? 10; // 0  (원래 값 유지)

매개변수 인자값

 

 

async (req,res) 익명함수

 new 머시기() << = 생성자임\