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 비교:
| 특징 | typedef | using |
|---|---|---|
| 문법 | 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++ typedef vs using | 타입 별칭 심화 가이드
- C++ namespace | “이름 충돌 방지” 완벽 가이드
- C++ 템플릿 템플릿 인자 | template template parameter 가이드
관련 글
- C++ typedef vs using | 타입 별칭 심화 가이드
- C++ 이름 은닉 |
- C++ namespace |
- C++ 클래스 템플릿 | 제네릭 컨테이너와 부분 특수화
- 배열과 리스트 | 코딩 테스트 필수 자료구조 완벽 정리
심화 부록: 구현·운영 관점
이 부록은 앞선 본문에서 다룬 주제(「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 | 타입 별칭 빠른 비교」)를 배포·운영 흐름에 맞춰 옮긴 체크리스트입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.
- 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드를 경계에 둔다.
- 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 로그·메트릭·트레이스에서 한 흐름으로 본다.
- 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
- 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지 확인한다.
- 부하 후 검증: 피크 대비 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 스냅샷 비교 |
| 빌드·배포만 실패 | 환경 변수, 권한, 플랫폼 차이, lockfile | CI 로그와 로컬 diff, 런타임·이미지 버전 핀 |
| 설정 불일치 | 프로필·시크릿·기본값, 리전 | 스키마 검증된 설정 단일 소스와 배포 매트릭스 표준화 |
| 데이터 불일치 | 비멱등 재시도, 부분 쓰기, 캐시 무효화 누락 | 멱등 키·아웃박스·트랜잭션 경계 재검토 |
권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.
배포 전에는 git add → git commit → git push 후 npm run deploy 순서를 권장합니다.
이 글에서 다루는 키워드 (관련 검색어)
C++, using, typedef, alias, 별칭 등으로 검색하시면 이 글이 도움이 됩니다.