반응형

 

✍️ 본문

👋 이런 코드 본 적 있으세요?

import { type User } from './types';

처음 보면 이런 생각 들죠.

"User는 타입인 거 뻔히 아는데... 굳이 type을 붙여야 하나요?"

저도 처음엔 안 붙여도 되는 줄 알았어요.
그런데 알고 보니, 붙이는 게 더 안전하고 좋은 습관이더라고요.


✅ 일단 User는 타입이에요

예를 들어 이런 타입이 있다고 해볼게요:

// types.ts
export type User = {
  name: string;
  age: number;
};

그럼 우리가 사용할 때는 보통 이렇게 쓰죠.

import { User } from './types';

const me: User = { name: '홍길동', age: 30 };

이거... 아무 문제 없어 보여요. 실제로도 잘 돌아가고요.


❗ 근데 왜 type을 붙일까요?

import { type User } from './types';

이렇게 쓰면 "나는 User를 타입으로만 쓸 거야!" 라고 명확하게 알려주는 거예요.

이게 왜 중요하냐면…


⚠️ 안 붙이면 생길 수 있는 문제

  1. 불필요한 코드가 앱에 들어가요
    → 타입인데도 실제 JS 코드에 포함돼서 번들(최종 파일)이 커져요.
  2. 런타임 에러가 날 수 있어요
    → 어떤 환경(특히 React Native)에서는 User가 실제 JS 값인 줄 알고 실행 중에 에러가 나기도 해요.

✅ 그래서 이렇게 구분하면 좋아요

상황 어떻게 import?

타입만 쓰는 경우 import { type User } from '...'
값(함수, 변수 등)도 쓰는 경우 그냥 import { something } from '...'

💡 결론

  • type을 꼭 붙여야 하는 건 아니에요
  • 하지만 붙이면 더 안전하고, 번들 크기도 줄고, 코드도 명확해져요!
  • 특히 React Native나 Vite 같은 환경에서는 실행 중 오류를 막는 데 도움이 돼요

📌 기억할 것 하나!

"타입만 쓸 거면 type 붙이자!"
그게 깔끔하고, 나중에 에러도 줄여줘요. 😊

반응형
반응형

LeetCode 13번 | 로마 숫자를 정수로 변환하기 (TypeScript 풀이)

이번 글에서는 LeetCode 13번 문제 "Roman to Integer" 를 TypeScript로 해결하는 방법을 다룹니다.
이 문제는 코딩 테스트나 개발자 면접에서 자주 등장하는 문자열 처리 & 수학적 규칙 적용 유형입니다.


🔹 문제 설명 (Roman to Integer)

로마 숫자는 다음과 같은 기호로 표현됩니다.

  • I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000
  • IV = 4, IX = 9, XL = 40, XC = 90, CD = 400, CM = 900 처럼 작은 값이 앞에 오면 감산됩니다.

주어진 로마 숫자 문자열을 정수로 변환하는 함수를 구현해야 합니다.


🔹 풀이 전략

  1. 문자를 숫자로 변환하는 객체 (symbol 맵) 생성
  2. 문자열을 순회하면서 현재 값과 이전 값을 비교
    • 이전 값보다 크다면 감산(-2 * prev) 적용
    • 그렇지 않다면 그대로 더하기
  3. 최적화를 위해 symbol[c] 조회 최소화

🔹 TypeScript 풀이 코드

export function romanToInt(s: string): number {
  const symbol: Record<string, number> = {
    I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000,
  };

  let answer = 0;
  let prev = 0;

  for (const c of s) {
    const value = symbol[c];

    if (prev < value) {
      answer -= prev * 2;
    }
    answer += value;
    prev = value;
  }

  return answer;
}

시간 복잡도: O(n) (문자열 한 번 순회)
공간 복잡도: O(1) (고정된 크기의 symbol 맵 사용)

 


🔹 테스트 케이스

console.log(romanToInt("III")); // 3
console.log(romanToInt("IV")); // 4
console.log(romanToInt("IX")); // 9
console.log(romanToInt("LVIII")); // 58
console.log(romanToInt("MCMXCIV")); // 1994

 


🔹 결론 및 정리

이 문제는 문자열 순회와 조건 비교를 활용한 최적화가 핵심입니다.
처음에는 단순 덧셈을 고려할 수 있지만, 감산 규칙을 적용하기 위해 이전 값과 비교하는 방식이 필요합니다.
코딩 테스트나 면접에서 자주 등장하는 유형이므로, TypeScript로 직접 구현하면서 연습해보는 것이 중요합니다.

📌 이해가 안 되는 부분이나 더 좋은 풀이가 있다면 댓글로 공유해주세요! 😊

 

 

#LeetCode #코딩테스트 #알고리즘 #타입스크립트 #로마숫자 #RomanToInteger #개발자 #프로그래밍 #면접문제

반응형
반응형

LeetCode Merge Sorted Array 문제 풀이: 초기 코드와 공간 복잡도 개선

이번 포스트에서는 LeetCode의 Merge Sorted Array 문제를 해결한 과정을 소개합니다. 처음에는 제가 작성한 초기 코드 방식으로 문제를 해결한 후, 추가 공간 사용 문제(공간 복잡도)를 개선하여 in-place 방식으로 최적화한 과정을 다룹니다.

문제 개요

Merge Sorted Array 문제는 두 개의 정렬된 배열을 하나의 정렬된 배열로 병합하는 문제입니다.

  • 입력:
    • nums1: 크기가 m+n인 배열, 처음 m개의 요소는 유효한 값이며 나머지 n개는 추가 공간입니다.
    • nums2: n개의 요소로 구성된 배열.
  • 목표:
    • nums1nums2의 요소들을 in-place로 병합하여 오름차순으로 정렬된 배열을 만드는 것입니다.

1. 초기 코드 접근 방식

먼저, 제가 작성한 초기 코드는 nums1의 유효한 부분을 새로운 배열(nums3)에 복사한 후, nums3nums2를 비교하며 작은 값부터 nums1에 채워 넣는 방식입니다.

초기 코드 예시

export function merge(
  nums1: number[],
  m: number,
  nums2: number[],
  n: number,
): void {
  const nums3 = nums1.slice(0, m);
  let i2 = 0;
  let i3 = 0;

  for (let index = 0; index < m + n; index++) {
    if (i2 >= n) {
      nums1[index] = nums3[i3++];
      continue;
    }

    if (i3 >= m) {
      nums1[index] = nums2[i2++];
      continue;
    }

    nums1[index] = nums2[i2] < nums3[i3] ? nums2[i2++] : nums3[i3++];
  }
}

이 방법은 시간 복잡도 O(m+n)로 효율적이나, nums1의 첫 m개 요소를 복사하기 위해 O(m)의 추가 공간을 사용합니다.

 

2. 공간 복잡도 개선: in-place 방식

문제의 in-place 조건을 완벽하게 만족하기 위해, 추가 공간 없이 nums1의 뒷부분부터 채워 나가는 방식으로 코드를 개선했습니다.
이 방식은 두 배열의 마지막 인덱스부터 비교하며, 큰 값을 nums1의 끝에 배치하는 방법으로 동작합니다. 이를 통해 공간 복잡도를 O(1)로 줄일 수 있습니다.

개선된 코드 예시

export function merge(
  nums1: number[],
  m: number,
  nums2: number[],
  n: number,
): void {
  let p1 = m - 1;         // nums1의 유효 부분 마지막 인덱스
  let p2 = n - 1;         // nums2의 마지막 인덱스
  let p = m + n - 1;      // nums1의 전체 마지막 인덱스

  while (p2 >= 0) {
    if (p1 >= 0 && nums1[p1] > nums2[p2]) {
      nums1[p] = nums1[p1];
      p1--;
    } else {
      nums1[p] = nums2[p2];
      p2--;
    }
    p--;
  }
}

이 개선된 방식의 주요 장점은 다음과 같습니다:

  • 공간 절약: 추가 배열 없이 in-place로 작업합니다.
  • 효율성: 두 배열의 마지막 요소부터 비교하여 한 번의 순회로 병합할 수 있습니다.

결론

초기 코드에서는 추가 공간을 사용해 문제를 해결했지만, in-place 방식으로 전환함으로써 공간 복잡도 문제를 개선할 수 있었습니다. 이번 문제 풀이를 통해 알고리즘 최적화효율적인 코딩의 중요성을 다시 한번 확인할 수 있었습니다.

이 글이 코딩 테스트 준비와 알고리즘 문제 해결에 도움이 되길 바랍니다!

반응형
반응형

TypeScript를 사용할 때 가장 나쁜 습관 중 하나가 any 타입을 남발하는 것이다.
any를 사용하면 TypeScript의 타입 안정성을 잃게 되고, 예상치 못한 버그가 발생할 가능성이 커진다.
이번 글에서는 any를 대체할 수 있는 세 가지 안전한 타입을 소개한다.


✅ 1. unknown 타입 (안전한 대체 타입)

unknownany와 비슷하지만 타입 검사를 강제하는 장점이 있다.
즉, 값이 어떤 타입인지 확실하지 않을 때 any 대신 unknown을 쓰면 안전성이 올라간다.

typescript
function processData(data: unknown) {
  if (typeof data === "string") {
    console.log(data.toUpperCase()); // ✅ 안전함
  } else {
    console.log("Not a string");
  }
}

💡 any를 사용하면 타입 검사를 우회하지만, unknown을 쓰면 타입을 직접 체크해야 한다.


✅ 2. 제네릭 (Generic) 활용

제네릭을 사용하면 함수나 클래스를 정의할 때 타입을 유연하게 설정할 수 있다.
이 방법은 컴파일 타임에 타입을 결정하므로, any보다 훨씬 안전하다.

typescript
function wrapValue<T>(value: T): T {
  return value;
}

const num = wrapValue<number>(42); // 🔥 TypeScript가 자동으로 타입 체크
const str = wrapValue<string>("hello");

💡 제네릭을 활용하면 타입 안정성을 유지하면서도 유연한 코드를 작성할 수 있다.


✅ 3. 유니온 타입 (Union Type)

여러 개의 타입을 가질 수 있는 경우 any 대신 유니온 타입을 활용하면 타입 안정성을 유지할 수 있다.

typescript
function formatData(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase();
  } else {
    return value.toFixed(2);
  }
}

console.log(formatData("hello")); // "HELLO"
console.log(formatData(3.1415)); // "3.14"

💡 유니온 타입을 사용하면 특정 타입만 허용할 수 있어, any를 남용하지 않아도 된다.


🎯 결론: any 대신 안전한 타입을 쓰자!

Avoid (나쁜 예) Use Instead (좋은 예)
any unknown (타입 검사 필수)
any Generic (<T> 활용)
any Union Type (`

🔥 TypeScript를 제대로 활용하려면 any를 피하고 더 안전한 타입을 사용해야 한다!
🚀 이 글이 도움이 되었다면, 댓글로 의견을 남겨주거나 공유해 주세요! 😊

반응형

+ Recent posts