본문으로 건너뛰기
Previous
Next
C++ using vs typedef | 타입 별칭 빠른 비교

C++ using vs typedef | 타입 별칭 빠른 비교

C++ using vs typedef | 타입 별칭 빠른 비교

이 글의 핵심

typedef와 using의 차이를 표와 짧은 예제로 빠르게 비교합니다. 함수 포인터·템플릿 별칭·실무 팁은 심화 글 cpp-typedef-using에서 이어집니다.

이 글과 형제 글의 역할

  • 이 글 (cpp-using-typedef): 빠른 비교—표, 짧은 코드, 언제 무엇을 쓰는지 요약.
  • 심화 글 (C++ typedef vs using | 타입 별칭 심화 가이드): 함수·멤버 함수 포인터, 템플릿 별칭, 가독성·실무 패턴을 더 길게 다룹니다. 같은 주제를 두 번 읽는 대신, 필요에 따라 위 둘 중 하나만 골라도 됩니다.

타입 별칭이란?

타입 별칭 (Type Alias)기존 타입에 새 이름을 부여하는 기능입니다. C++03의 typedef와 C++11의 using이 있습니다.

// typedef (C++03)
typedef unsigned long ulong;
typedef std::vector<int> IntVec;
// using (C++11)
using ulong = unsigned long;
using IntVec = std::vector<int>;

왜 필요한가?:

  • 가독성: 복잡한 타입을 간결하게
  • 유지보수: 타입 변경 시 한 곳만 수정
  • 추상화: 구현 세부사항 숨김
  • 이식성: 플랫폼별 타입 통일
// ❌ 복잡한 타입: 읽기 어려움
std::map<std::string, std::vector<std::pair<int, double>>> data;
// ✅ 타입 별칭: 간결
using DataMap = std::map<std::string, std::vector<std::pair<int, double>>>;
DataMap data;

typedef vs using 비교:

특징typedefusing
문법C 스타일현대적
가독성낮음높음
템플릿 별칭❌ 불가✅ 가능
함수 포인터복잡간결
권장❌ 레거시✅ C++11+
// typedef: 복잡
typedef void (*FuncPtr)(int, double);
// using: 간결
using FuncPtr = void(*)(int, double);
// typedef: 템플릿 별칭 불가
template<typename T>
typedef std::vector<T> Vec;  // 에러!
// using: 템플릿 별칭 가능
template<typename T>
using Vec = std::vector<T>;

typedef 기본

C/C++ 예제 코드입니다.

// 기본 타입
typedef int Integer;
typedef double Real;
Integer x = 10;
Real y = 3.14;
// 포인터
typedef int* IntPtr;
IntPtr ptr = &x;
// 배열
typedef int IntArray[10];
IntArray arr = {1, 2, 3};
// 함수 포인터
typedef int (*FuncPtr)(int, int);
FuncPtr func = add;

using 기본

// 기본 타입
using Integer = int;
using Real = double;
// 포인터
using IntPtr = int*;
// 배열
using IntArray = int[10];
// 함수 포인터
using FuncPtr = int(*)(int, int);

using vs typedef

// typedef: 읽기 어려움
typedef void (*FuncPtr)(int, double);
// using: 읽기 쉬움
using FuncPtr = void(*)(int, double);
// typedef: 템플릿 별칭 불가
template<typename T>
typedef std::vector<T> Vec;  // 에러!
// using: 템플릿 별칭 가능
template<typename T>
using Vec = std::vector<T>;

실전 예시

예시 1: 컨테이너 별칭

#include <vector>
#include <map>
#include <string>
// 자주 사용하는 타입
using StringVec = std::vector<std::string>;
using IntMap = std::map<std::string, int>;
using Matrix = std::vector<std::vector<int>>;
int main() {
    StringVec names = {"Alice", "Bob", "Charlie"};
    
    IntMap ages = {
        {"Alice", 30},
        {"Bob", 25}
    };
    
    Matrix grid = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
}

예시 2: 함수 포인터

// typedef 방식
typedef int (*Operation)(int, int);
// using 방식 (더 명확)
using Operation = int(*)(int, int);
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
int calculate(int a, int b, Operation op) {
    return op(a, b);
}
int main() {
    cout << calculate(10, 5, add) << endl;       // 15
    cout << calculate(10, 5, multiply) << endl;  // 50
}

예시 3: 템플릿 별칭

// 스마트 포인터 별칭
template<typename T>
using UniquePtr = std::unique_ptr<T>;
template<typename T>
using SharedPtr = std::shared_ptr<T>;
// 컨테이너 별칭
template<typename T>
using Vec = std::vector<T>;
template<typename K, typename V>
using Map = std::map<K, V>;
int main() {
    UniquePtr<int> ptr = std::make_unique<int>(42);
    Vec<int> numbers = {1, 2, 3, 4, 5};
    Map<std::string, int> ages = {{"Alice", 30}};
}

예시 4: 복잡한 타입 간소화

#include <functional>
#include <vector>
// 복잡한 타입
using Callback = std::function<void(int)>;
using CallbackList = std::vector<Callback>;
class EventManager {
private:
    CallbackList callbacks;
    
public:
    void subscribe(Callback cb) {
        callbacks.push_back(cb);
    }
    
    void notify(int value) {
        for (auto& cb : callbacks) {
            cb(value);
        }
    }
};
int main() {
    EventManager manager;
    
    manager.subscribe([](int x) {
        std::cout << "Callback 1: " << x << '\n';
    });
    
    manager.subscribe([](int x) {
        std::cout << "Callback 2: " << x * 2 << '\n';
    });
    
    manager.notify(10);
}

멤버 타입 별칭

template<typename T>
class Container {
public:
    using value_type = T;
    using reference = T&;
    using const_reference = const T&;
    using pointer = T*;
    using size_type = std::size_t;
    
private:
    std::vector<value_type> data;
    
public:
    void push_back(const_reference value) {
        data.push_back(value);
    }
    
    reference operator[](size_type index) {
        return data[index];
    }
    
    size_type size() const {
        return data.size();
    }
};
int main() {
    Container<int> cont;
    cont.push_back(10);
    
    Container<int>::value_type x = cont[0];
    cout << x << endl;
}

네임스페이스 별칭

doSomething 함수의 구현 예제입니다.

namespace very_long_namespace_name {
    class MyClass {
    public:
        void doSomething() {
            cout << "Doing something" << endl;
        }
    };
}
// 네임스페이스 별칭
namespace vln = very_long_namespace_name;
int main() {
    vln::MyClass obj;
    obj.doSomething();
}

자주 발생하는 문제

문제 1: typedef 순서

// ❌ 읽기 어려움
typedef int* (*FuncPtr)(int);
// ✅ using이 더 명확
using FuncPtr = int*(*)(int);

문제 2: 템플릿 별칭

// ❌ typedef는 템플릿 별칭 불가
template<typename T>
typedef std::vector<T> Vec;  // 에러
// ✅ using 사용
template<typename T>
using Vec = std::vector<T>;
Vec<int> numbers = {1, 2, 3};

문제 3: 타입 은닉

// ❌ 타입이 불명확
using IntVec = std::vector<int>;
IntVec vec;  // 실제 타입이 뭐지?
// ✅ 명확한 경우에만 사용
// 자주 사용하거나 복잡한 타입

사용 시기

// ✅ 사용 권장
// 1. 복잡한 타입
using ComplexType = std::map<std::string, std::vector<int>>;
// 2. 자주 사용하는 타입
using StringVec = std::vector<std::string>;
// 3. 템플릿 별칭
template<typename T>
using Ptr = std::unique_ptr<T>;
// 4. 플랫폼 의존 타입
#ifdef _WIN32
    using FileHandle = HANDLE;
#else
    using FileHandle = int;
#endif
// ❌ 사용 지양
// 1. 간단한 타입
// using Int = int;  // 불필요
// 2. 표준 타입 재정의
// using string = std::string;  // 혼란

typedef vs using 비교

// typedef
typedef std::vector<int> IntVec;
typedef int (*FuncPtr)(int);
// using (권장)
using IntVec = std::vector<int>;
using FuncPtr = int(*)(int);
// 장점:
// - 더 읽기 쉬움
// - 템플릿 별칭 지원
// - 일관된 문법

실무 패턴

패턴 1: 플랫폼 추상화

// platform.h
#ifdef _WIN32
    using FileHandle = HANDLE;
    using SocketHandle = SOCKET;
#elif defined(__linux__)
    using FileHandle = int;
    using SocketHandle = int;
#elif defined(__APPLE__)
    using FileHandle = int;
    using SocketHandle = int;
#endif
// 사용
class File {
    FileHandle handle_;
    
public:
    File(const std::string& path);
    ~File();
};

패턴 2: STL 컨테이너 커스터마이징

#include <vector>
#include <memory>
// 커스텀 allocator
template<typename T>
using PoolVector = std::vector<T, PoolAllocator<T>>;
// 스마트 포인터 컨테이너
template<typename T>
using PtrVector = std::vector<std::unique_ptr<T>>;
// 사용
PoolVector<int> poolVec;
PtrVector<Widget> widgets;
widgets.push_back(std::make_unique<Widget>());

패턴 3: 콜백 타입

#include <functional>
#include <map>
#include <string>
// 콜백 타입 정의
using ErrorCallback = std::function<void(const std::string&)>;
using SuccessCallback = std::function<void(int)>;
using ProgressCallback = std::function<void(double)>;
class AsyncTask {
    ErrorCallback onError_;
    SuccessCallback onSuccess_;
    ProgressCallback onProgress_;
    
public:
    void setErrorCallback(ErrorCallback cb) {
        onError_ = cb;
    }
    
    void setSuccessCallback(SuccessCallback cb) {
        onSuccess_ = cb;
    }
    
    void execute() {
        // 작업 수행
        if (/* 에러 */) {
            if (onError_) onError_("에러 발생");
        } else {
            if (onSuccess_) onSuccess_(42);
        }
    }
};

함수 포인터 별칭 (요약)

복잡한 시그니처를 한 줄 이름으로 묶을 때 using이 읽기 쉽습니다.

typedef void (*OldStyle)(int, double);
using NewStyle = void(*)(int, double);  // “이름 = 타입”

멤버 함수 포인터, noexcept 지정, 다양한 호출 규칙까지 섞이면 typedef만으로는 실수하기 쉽습니다. 상세·패턴심화 가이드의 “함수 포인터 타입 별칭” 절을 참고하세요.

템플릿 별칭 (요약)

템플릿 매개변수를 받는 별칭using만 가능합니다. typedef로는 동일한 표현을 할 수 없습니다.

template<typename T>
using Vec = std::vector<T>;

C++11 이전의 template<typename T> struct Vec { typedef std::vector<T> type; }; 같은 우회는 가능하지만 장황하므로, 모던 코드에서는 using이 표준입니다.

가독성·실무에서의 선택

상황권장
새 C++ 코드using
C 공유 헤더·레거시 규칙typedef 유지 가능
템플릿 별칭using 필수
복잡한 함수 포인터using (의도가 오른쪽 타입에 집중됨)
팀 컨벤션에 “항상 using”이 있으면 그에 따르는 것이 리뷰 비용을 줄입니다.

FAQ

Q1: using vs typedef?

A:

  • using: 권장 (C++11 이상), 가독성 높음
  • typedef: 레거시 코드
// typedef: 복잡
typedef void (*FuncPtr)(int);
// using: 간결
using FuncPtr = void(*)(int);

Q2: 언제 타입 별칭을 사용하나요?

A:

  • 복잡한 타입: 가독성 향상
  • 자주 사용하는 타입: 유지보수 용이
  • 템플릿 별칭: 재사용
// 복잡한 타입
using DataMap = std::map<std::string, std::vector<int>>;
// 자주 사용
using StringVec = std::vector<std::string>;
// 템플릿 별칭
template<typename T>
using Ptr = std::unique_ptr<T>;

Q3: 성능 차이는?

A: 없습니다. 타입 별칭은 컴파일 타임에만 영향을 줍니다.

// 동일한 코드 생성
std::vector<int> v1;
using IntVec = std::vector<int>;
IntVec v2;

Q4: 템플릿 별칭은?

A: using만 가능합니다. typedef는 불가능합니다.

// ❌ typedef: 불가능
template<typename T>
typedef std::vector<T> Vec;
// ✅ using: 가능
template<typename T>
using Vec = std::vector<T>;

Q5: 네임스페이스 별칭은?

A: namespace alias = original; 을 사용합니다.

namespace very_long_name {
    class MyClass {};
}
namespace vln = very_long_name;
vln::MyClass obj;

Q6: 타입 별칭과 새 타입의 차이는?

A:

  • 타입 별칭: 기존 타입의 다른 이름 (동일한 타입)
  • 새 타입: enum class, struct (다른 타입)
// 타입 별칭: 동일한 타입
using Int = int;
Int x = 10;
int y = x;  // OK
// 새 타입: 다른 타입
enum class StrongInt : int {};
StrongInt a = StrongInt{10};
// int b = a;  // 에러: 다른 타입

Q7: 멤버 타입 별칭은?

A: 클래스 내부에서 using 을 사용합니다.

template<typename T>
class Container {
public:
    using value_type = T;
    using reference = T&;
    using const_reference = const T&;
};
Container<int>::value_type x = 10;

Q8: 타입 별칭 학습 리소스는?

A:

  • “Effective Modern C++” by Scott Meyers (Item 9)
  • cppreference.com - Type alias
  • “C++ Primer” by Stanley Lippman 관련 글: typedef, template, auto. 한 줄 요약: 타입 별칭은 기존 타입에 새 이름을 부여하여 가독성과 유지보수성을 높이는 기능입니다.

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

관련 글

심화 부록: 구현·운영 관점

이 부록은 앞선 본문에서 다룬 주제(「C++ using vs typedef | 타입 별칭 빠른 비교」)를 구현·런타임·운영 관점에서 다시 압축합니다. 도메인별 세부 구현은 글마다 다르지만, 입력 검증 → 핵심 연산 → 부작용(I/O·네트워크·동시성) → 관측의 흐름으로 장애를 나누면 원인 추적이 빨라집니다.

내부 동작과 핵심 메커니즘

flowchart TD
  A[입력·요청·이벤트] --> B[파싱·검증·디코딩]
  B --> C[핵심 연산·상태 전이]
  C --> D[부작용: I/O·네트워크·동시성]
  D --> E[결과·관측·저장]
sequenceDiagram
  participant C as 클라이언트/호출자
  participant B as 경계(런타임·게이트웨이·프로세스)
  participant D as 의존성(API·DB·큐·파일)
  C->>B: 요청/이벤트
  B->>D: 조회·쓰기·RPC
  D-->>B: 지연·부분 실패·재시도 가능
  B-->>C: 응답 또는 오류(코드·상관 ID)
  • 불변 조건(Invariant): 버퍼 경계, 프로토콜 상태, 트랜잭션 격리, FD 상한 등 단계별로 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
  • 결정성: 순수 층과 시간·네트워크·스케줄에 의존하는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
  • 경계 비용: 직렬화, 인코딩, syscall 횟수, 락 경합, 할당·GC, 캐시 미스를 의심 목록에 둡니다.
  • 백프레셔: 생산자가 소비자보다 빠를 때 버퍼·큐·스트림에서 속도를 줄이는 신호를 어디에 둘지 정의합니다.

프로덕션 운영 패턴

영역운영 관점 질문
관측성요청 단위 상관 ID, 에러율·지연 p95/p99, 의존성 타임아웃·재시도가 대시보드에 보이는가
안전성입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가
신뢰성재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가
성능캐시·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가
배포롤백 룬북, 카나리/블루그린, 마이그레이션·피처 플래그가 문서화되어 있는가
용량피크 트래픽·디스크·FD·스레드 풀 상한을 주기적으로 검증하는가

스테이징은 데이터 양·네트워크 RTT·동시성을 프로덕션에 가깝게 맞출수록 재현율이 올라갑니다.

확장 예시: 엔드투엔드 미니 시나리오

앞선 본문 주제(「C++ using vs typedef | 타입 별칭 빠른 비교」)를 배포·운영 흐름에 맞춰 옮긴 체크리스트입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.

  1. 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드를 경계에 둔다.
  2. 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 로그·메트릭·트레이스에서 한 흐름으로 본다.
  3. 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
  4. 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지 확인한다.
  5. 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값을 점검한다.
handle(request):
  ctx = newCorrelationId()
  validated = validateSchema(request)
  authorize(validated, ctx)
  result = domainCore(validated)
  persistOrEmit(result, idempotentKey)
  recordMetrics(ctx, latency, outcome)
  return result

문제 해결(Troubleshooting)

증상가능 원인조치
간헐적 실패레이스, 타임아웃, 외부 의존성, DNS최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검
성능 저하N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거
메모리 증가캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납상한·TTL·힙/FD 스냅샷 비교
빌드·배포만 실패환경 변수, 권한, 플랫폼 차이, lockfileCI 로그와 로컬 diff, 런타임·이미지 버전 핀
설정 불일치프로필·시크릿·기본값, 리전스키마 검증된 설정 단일 소스와 배포 매트릭스 표준화
데이터 불일치비멱등 재시도, 부분 쓰기, 캐시 무효화 누락멱등 키·아웃박스·트랜잭션 경계 재검토

권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.

배포 전에는 git addgit commitgit pushnpm run deploy 순서를 권장합니다.


이 글에서 다루는 키워드 (관련 검색어)

C++, using, typedef, alias, 별칭 등으로 검색하시면 이 글이 도움이 됩니다.