JavaScript String() vs toString(): Next.js에서 안전한 문자열 변환하기

도비·2025년 9월 10일

들어가며

Next.js에서 router.query를 다룰 때 이런 고민 해보신 적 있나요?

const { type } = router.query; // string | string[] | undefined

// 이렇게 써야 할까요?
const typeString = type.toString();

// 아니면 이렇게?
const typeString = String(type);

실무에서 팀원들마다 다른 방식을 사용하는 걸 보면서 "뭐가 더 좋은 방법일까?" 궁금했던 분들을 위해 준비했습니다.

오늘은 JavaScript의 String()toString()의 차이점과 Next.js 환경에서의 안전한 사용법을 알아보겠습니다.

💡 이 글에서 다룰 내용

  • String()toString()의 동작 원리 차이
  • null/undefined 처리에서의 결정적 차이점
  • Next.js router.query에서의 실무 적용법
  • 타입 안전성을 고려한 베스트 프랙티스

🔍 핵심 개념 이해하기

String() 함수의 특징

String()은 JavaScript의 전역 함수로, 어떤 값이든 안전하게 문자열로 변환합니다.

// 모든 값을 안전하게 처리
String(123)       // "123"
String(true)      // "true"
String(null)      // "null"
String(undefined) // "undefined"
String([1,2,3])   // "1,2,3"
String({a: 1})    // "[object Object]"

핵심은 null과 undefined도 에러 없이 처리한다는 점입니다.

toString() 메서드의 특징

toString()은 객체의 프로토타입 메서드로, 각 타입별로 다른 구현을 가집니다.

// 일반적인 사용
(123).toString()    // "123"
true.toString()     // "true"
[1,2,3].toString()  // "1,2,3"

// 하지만 이건 에러!
null.toString()      // TypeError: Cannot read property 'toString' of null
undefined.toString() // TypeError: Cannot read property 'toString' of undefined

⚠️ 주의사항
toString()은 null이나 undefined에서 호출하면 TypeError가 발생합니다!

💻 Next.js에서의 실무 활용

router.query의 타입 특성

Next.js의 router.query는 다음 타입을 가집니다:

query: ParsedUrlQuery // = { [key: string]: string | string[] | undefined }

이때 값이 undefined일 가능성 때문에 안전한 처리가 중요합니다.

위험한 패턴들

import { useRouter } from 'next/router';

const Component = () => {
  const router = useRouter();
  const { productId } = router.query;
  
  // ❌ 위험: productId가 undefined면 에러
  return <h1>{productId.toString()}</h1>;
  
  // ❌ 타입 단언으로 우회하지만 런타임 안전성 없음
  return <h1>{(productId as string).toString()}</h1>;
};

안전한 패턴들

const Component = () => {
  const router = useRouter();
  const { productId } = router.query;
  
  // ✅ String() 사용 - 가장 안전
  const safeProductId = String(productId || '');
  
  // ✅ 옵셔널 체이닝 활용
  const anotherSafe = productId?.toString() || '';
  
  // ✅ 타입 가드 사용
  const getStringValue = (value: string | string[] | undefined): string => {
    if (typeof value === 'string') return value;
    if (Array.isArray(value)) return value[0] || '';
    return '';
  };
  
  return <h1>{safeProductId}</h1>;
};

실무에서 자주 사용하는 유틸 함수

// utils/getSingleQueryParam.ts
export const getSingleQueryParam = (
  param: string | string[] | undefined
): string => {
  if (typeof param === 'string') return param;
  if (Array.isArray(param)) return param[0] || '';
  return '';
};

// 사용 예시
const Component = () => {
  const router = useRouter();
  const productId = getSingleQueryParam(router.query.productId);
  const category = getSingleQueryParam(router.query.category);
  
  return (
    <div>
      <h1>상품: {productId}</h1>
      <p>카테고리: {category}</p>
    </div>
  );
};

🚨 실무에서 마주치는 함정들

1. 서버사이드 렌더링에서의 타이밍 이슈

const Component = () => {
  const router = useRouter();
  
  // ❌ SSR에서 query가 빈 객체일 수 있음
  const productId = String(router.query.productId);
  
  // ✅ router가 준비된 후 처리
  useEffect(() => {
    if (router.isReady) {
      const safeProductId = String(router.query.productId || '');
      // 안전한 처리...
    }
  }, [router.isReady, router.query.productId]);
  
  return <div>...</div>;
};

2. 배열 처리 간과하기

// URL: /products?tags=react&tags=nextjs
const { tags } = router.query; // ["react", "nextjs"]

// ❌ 배열을 고려하지 않은 처리
const tagString = String(tags); // "react,nextjs" (의도한 결과인가?)

// ✅ 배열 처리를 고려한 방법
const getFirstTag = (tags: string | string[] | undefined): string => {
  if (Array.isArray(tags)) return tags[0] || '';
  return String(tags || '');
};

🎯 정리하며

핵심 요약

  • String() 함수: null/undefined 안전, 모든 타입 지원
  • toString() 메서드: null/undefined에서 에러 발생
  • Next.js에서는 String() 사용 권장: router.query의 불확실한 타입 때문

실무 적용 가이드

  • router.query 처리할 때는 String() 함수 우선 사용
  • router.isReady 체크로 SSR 타이밍 이슈 방지
  • 배열 가능성을 고려한 유틸 함수 활용
  • TypeScript와 함께 사용해서 타입 안전성 확보

🛡️ 방어적 프로그래밍의 중요성

// 이런 간단한 차이가
const bad = (value) => value.toString();
const good = (value) => String(value || '');

// 프로덕션에서는 이런 차이를 만듭니다
// bad: 런타임 에러 → 사용자에게 빈 화면
// good: 안전한 처리 → 정상적인 사용자 경험

📚 참고 자료 및 레퍼런스


태그: #JavaScript #NextJS #TypeScript #React #프론트엔드 #실무팁 #타입안전성 #에러핸들링

profile
독립하면 공부할 시간이 많아질 줄 알았는데 집요정도비가 되버린 건에 대하여

0개의 댓글