C++ std::chrono 완벽 가이드 | duration·time_point·클럭·시간 측정 실전 활용

C++ std::chrono 완벽 가이드 | duration·time_point·클럭·시간 측정 실전 활용

이 글의 핵심

time() 대신 std::chrono로 정확한 시간 측정. duration, time_point, system_clock, steady_clock, duration_cast, 벤치마크·타임아웃·로그 타임스탬프까지 실전 패턴.

들어가며: “함수 실행 시간이 매번 달라요”

time()으로 측정했는데 결과가 신뢰할 수 없다

벤치마크 코드를 작성했습니다. time()이나 clock()으로 함수 실행 시간을 측정했는데, 실행할 때마다 결과가 들쭉날쭉합니다. 시스템 시계가 NTP로 조정되거나, clock()이 CPU 시간만 측정해서 멀티스레드 환경에서 부정확합니다.

문제의 코드:

// ❌ 나쁜 예: time()은 1초 단위라 짧은 시간 측정 불가
#include <ctime>
void benchmark() {
    time_t start = time(nullptr);
    doSomething();
    time_t end = time(nullptr);
    printf("Elapsed: %ld sec\n", end - start);  // 1초 미만이면 0
}

// ❌ 나쁜 예: clock()은 CPU 시간이라 sleep 중에는 증가 안 함
#include <ctime>
void benchmark() {
    clock_t start = clock();
    doSomething();  // sleep 포함 시 부정확
    clock_t end = clock();
    printf("CPU: %f sec\n", (double)(end - start) / CLOCKS_PER_SEC);
}

chrono로 해결:

// ✅ 좋은 예: steady_clock은 단조 증가, 나노초 단위
#include <chrono>
#include <iostream>

void benchmark() {
    auto start = std::chrono::steady_clock::now();
    doSomething();
    auto end = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "Elapsed: " << elapsed.count() << " ms\n";
}

이 글을 읽으면:

  • std::chrono의 duration, time_point, clocks를 이해할 수 있습니다.
  • duration_cast로 단위 변환을 할 수 있습니다.
  • 벤치마크, 타임아웃, 로그 타임스탬프 등 실전 패턴을 적용할 수 있습니다.
  • 자주 발생하는 에러와 프로덕션 패턴을 알 수 있습니다.

개념을 잡는 비유

시간·파일·로그·JSON은 도구 상자의 자주 쓰는 렌치입니다. 표준·검증된 라이브러리로 한 가지 규칙을 정해 두면, 팀 전체가 같은 단위·같은 포맷으로 맞출 수 있습니다.


목차

  1. 실무에서 겪는 문제 시나리오
  2. duration: 시간 구간
  3. time_point: 시점
  4. clocks: system_clock, steady_clock, high_resolution_clock
  5. duration_cast와 단위 변환
  6. C++20 Calendar·날짜 연산
  7. 시간 측정 실전 예제
  8. 자주 발생하는 에러와 해결법
  9. 베스트 프랙티스
  10. 프로덕션 패턴
  11. 체크리스트
  12. 정리

1. 실무에서 겪는 문제 시나리오

시나리오 1: 벤치마크 결과가 실행마다 다르다

문제: 알고리즘 A와 B의 성능을 비교하는데, time(nullptr)로 측정하면 둘 다 0초로 나옵니다. 1초 단위라 밀리초 단위 차이를 잡을 수 없습니다.

// ❌ 문제: 1초 미만 실행 시 항상 0
time_t t1 = time(nullptr);
algorithmA();
time_t t2 = time(nullptr);
printf("A: %ld sec\n", t2 - t1);  // 0.05초 걸려도 0 출력

해결: std::chrono::steady_clockduration_cast로 밀리초·마이크로초 단위 측정.

// ✅ 해결
#include <chrono>
auto start = std::chrono::steady_clock::now();
algorithmA();
auto end = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "A: " << ms.count() << " ms\n";

시나리오 2: 타임아웃 계산이 NTP 조정 시 꼬인다

문제: “30초 후에 타임아웃”을 system_clock::now() + 30초로 계산했는데, 실행 중 NTP가 시스템 시계를 1분 뒤로 되돌리면 타임아웃이 영원히 오지 않습니다.

// ❌ 문제: system_clock은 시스템 시계 변경에 영향받음
auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(30);
while (std::chrono::system_clock::now() < deadline) {
    doWork();  // NTP 조정 시 deadline이 과거가 되어 무한 루프 가능
}

해결: 타임아웃에는 steady_clock을 사용합니다. steady_clock은 단조 증가하므로 NTP 영향 없음.

// ✅ 해결
auto start = std::chrono::steady_clock::now();
auto timeout = std::chrono::seconds(30);
while (std::chrono::steady_clock::now() - start < timeout) {
    doWork();
}

시나리오 3: 로그에 사람이 읽을 수 있는 시간을 찍고 싶다

문제: steady_clock::now()는 “부팅 후 경과 시간”이라 사람이 읽기 어렵습니다. “2026-03-11 14:30:00” 형태로 출력하려면 system_clock과 포맷 변환이 필요합니다.

// ❌ steady_clock은 "부팅 후 나노초" 같은 값
auto tp = std::chrono::steady_clock::now();
// 사람이 읽을 수 있는 형식으로 변환 불가

// ✅ system_clock은 실제 날짜/시간과 대응
auto tp = std::chrono::system_clock::now();
// C++20 chrono 또는 localtime으로 포맷 가능

시나리오 4: duration 단위가 달라서 컴파일 에러

문제: std::chrono::secondsstd::chrono::milliseconds를 직접 더하거나 비교하려 하면 타입이 달라 컴파일 에러가 납니다.

// ❌ 문제: 서로 다른 duration 타입
auto s = std::chrono::seconds(1);
auto ms = std::chrono::milliseconds(500);
auto sum = s + ms;  // ✅ C++11부터는 공통 타입으로 자동 변환되어 OK
// 하지만 특정 단위로 캐스팅할 때는 duration_cast 필요

시나리오 5: Asio deadline_timer에 chrono duration 전달

문제: Boost.Asio나 std::experimental::net의 async_wait에는 chrono::duration을 직접 넘겨야 합니다. int 초를 넘기면 타입 에러가 납니다.

// ❌ 문제
timer.expires_after(30);  // 30이 뭔지? 초? 밀리초?

// ✅ 해결
timer.expires_after(std::chrono::seconds(30));

시나리오 6: “다음 월요일” 같은 날짜 계산이 어렵다

문제: localtimetm 구조체로 “다음 주 월요일”, “이번 달 마지막 날” 같은 날짜를 계산하려면 수동으로 일수·윤년·월 경계를 처리해야 합니다. 실수하기 쉽고 가독성이 떨어집니다.

// ❌ C++17 이하: 수동 날짜 계산
std::tm tm = *std::localtime(&tt);
tm.tm_mday += 7;  // 7일 후? 월 넘어가면?
mktime(&tm);      // 정규화 필요

해결: C++20 std::chrono::year_month_day, weekday 등 calendar 타입을 사용하면 타입 안전하고 직관적입니다.

// ✅ C++20: calendar 타입으로 명확한 날짜 연산
#include <chrono>
using namespace std::chrono;
auto today = floor<days>(system_clock::now());
year_month_day ymd = year_month_day{sys_days{today}};
// 다음 월요일: weekday_indexed 활용

2. duration: 시간 구간

기본 개념

std::chrono::duration시간의 길이를 표현합니다. 템플릿 인자로 “표현 타입(Rep)“과 “단위 비율(Ratio)“을 받습니다.

// 복사해 붙여넣은 뒤: g++ -std=c++17 -o duration_basic duration_basic.cpp && ./duration_basic
#include <chrono>
#include <iostream>

int main() {
    // 기본 duration 타입들
    std::chrono::nanoseconds  ns(1000000000);   // 10^9 나노초 = 1초
    std::chrono::microseconds us(1000000);       // 10^6 마이크로초 = 1초
    std::chrono::milliseconds ms(1000);         // 1000 밀리초 = 1초
    std::chrono::seconds      s(1);            // 1초
    std::chrono::minutes      m(1);             // 1분
    std::chrono::hours        h(1);             // 1시간

    std::cout << "1초 = " << ns.count() << " ns\n";
    std::cout << "1초 = " << ms.count() << " ms\n";
    std::cout << "1분 = " << std::chrono::duration_cast<std::chrono::seconds>(m).count() << "초\n";

    // 연산
    auto sum = std::chrono::seconds(30) + std::chrono::milliseconds(500);
    std::cout << "30.5초 = " << std::chrono::duration_cast<std::chrono::milliseconds>(sum).count() << " ms\n";

    return 0;
}

실행 결과:

1초 = 1000000000 ns
1초 = 1000 ms
1분 = 60초
30.5초 = 30500 ms

duration 내부 구조:

  • Rep: int64_t, double 등 실제 값을 저장하는 타입
  • Period: std::ratio<1, 1000> = 1/1000초 = 밀리초

duration 연산

#include <chrono>

int main() {
    using namespace std::chrono;

    auto d1 = seconds(10);
    auto d2 = milliseconds(500);

    // 덧셈, 뺄셈 (공통 타입으로 자동 변환)
    auto sum = d1 + d2;           // 10.5초
    auto diff = d1 - d2;         // 9.5초

    // 곱셈, 나눗셈 (스칼라와)
    auto doubled = d1 * 2;       // 20초
    auto half = d1 / 2;          // 5초

    // 비교
    bool b = (d1 > d2);          // true

    // count(): 내부 값 추출
    auto ms = duration_cast<milliseconds>(sum);
    std::cout << ms.count() << " ms\n";  // 10500

    return 0;
}

커스텀 duration

#include <chrono>
#include <iostream>

// 1/100 초 = 10ms 단위
using Deciseconds = std::chrono::duration<int, std::ratio<1, 10>>;

// 1/30 초 (프레임 기반 게임용)
using FrameDuration = std::chrono::duration<double, std::ratio<1, 30>>;

int main() {
    Deciseconds ds(5);   // 0.5초
    FrameDuration fd(1); // 1프레임 = 1/30초

    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(ds);
    std::cout << "5 deciseconds = " << ms.count() << " ms\n";

    return 0;
}

3. time_point: 시점

기본 개념

std::chrono::time_point특정 시점을 표현합니다. “어떤 clock의 epoch(기준 시점)으로부터 얼마나 지났는지”를 duration으로 저장합니다.

// 복사해 붙여넣은 뒤: g++ -std=c++17 -o timepoint_basic timepoint_basic.cpp && ./timepoint_basic
#include <chrono>
#include <iostream>

int main() {
    // 현재 시점
    auto now_sys = std::chrono::system_clock::now();
    auto now_steady = std::chrono::steady_clock::now();

    // time_point + duration = time_point
    auto future = now_sys + std::chrono::hours(24);
    auto past = now_sys - std::chrono::seconds(3600);

    // time_point - time_point = duration
    auto elapsed = now_steady - (now_steady - std::chrono::milliseconds(100));
    std::cout << "Elapsed: " << elapsed.count() << " (steady_clock 단위)\n";

    // system_clock::time_point -> time_t (C API 호환)
    auto tt = std::chrono::system_clock::to_time_t(now_sys);
    std::cout << "time_t: " << tt << "\n";

    return 0;
}

time_point 연산

#include <chrono>

int main() {
    using namespace std::chrono;

    auto tp = steady_clock::now();

    // 시점 + 구간 = 시점
    auto later = tp + seconds(10);
    auto earlier = tp - milliseconds(500);

    // 시점 - 시점 = 구간
    auto diff = later - tp;  // 10초
    auto ms = duration_cast<milliseconds>(diff);
    std::cout << ms.count() << " ms\n";

    return 0;
}

4. clocks: system_clock, steady_clock, high_resolution_clock

세 가지 표준 클럭

flowchart TB
    subgraph system["system_clock"]
        S1[실제 날짜/시간]
        S2[NTP 영향 받음]
        S3[to_time_t 가능]
    end
    subgraph steady["steady_clock"]
        T1[단조 증가]
        T2[NTP 영향 없음]
        T3[벤치마크·타임아웃]
    end
    subgraph high["high_resolution_clock"]
        H1[가장 높은 해상도]
        H2[구현에 따라 steady일 수도]
    end
클럭용도NTP 영향단조 증가
system_clock실제 날짜/시간, 로그 타임스탬프있음아니오
steady_clock벤치마크, 타임아웃, 경과 시간없음
high_resolution_clock최고 해상도 필요 시구현 의존구현 의존

system_clock

#include <chrono>
#include <ctime>
#include <iostream>

int main() {
    auto now = std::chrono::system_clock::now();

    // time_t로 변환 후 localtime
    auto tt = std::chrono::system_clock::to_time_t(now);
    std::tm* tm = std::localtime(&tt);
    char buf[64];
    std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
    std::cout << "현재 시간: " << buf << "\n";

    // from_time_t: time_t -> time_point
    auto tp = std::chrono::system_clock::from_time_t(tt);

    return 0;
}

steady_clock

#include <chrono>
#include <iostream>
#include <thread>

int main() {
    auto start = std::chrono::steady_clock::now();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    auto end = std::chrono::steady_clock::now();

    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "Slept: " << elapsed.count() << " ms\n";

    // steady_clock은 to_time_t 없음 (실제 날짜와 무관)
    // auto tt = std::chrono::steady_clock::to_time_t(start);  // ❌ 컴파일 에러

    return 0;
}

high_resolution_clock

#include <chrono>
#include <iostream>

int main() {
    // gcc/clang: high_resolution_clock은 steady_clock의 별칭
    // MSVC: 별도 구현일 수 있음
    auto start = std::chrono::high_resolution_clock::now();
    volatile int x = 0;
    for (int i = 0; i < 1000; ++i) x += i;
    auto end = std::chrono::high_resolution_clock::now();

    auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
    std::cout << "Loop: " << ns.count() << " ns\n";

    return 0;
}

주의: high_resolution_clocksteady인지 구현에 따라 다릅니다. 벤치마크에서 “단조 증가”가 중요하면 steady_clock을 쓰는 것이 안전합니다.


5. duration_cast와 단위 변환

duration_cast

서로 다른 단위의 duration을 변환할 때 duration_cast를 사용합니다. 잘림(truncation)이 발생할 수 있습니다.

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;

    // 1500ms -> 1초 (잘림)
    milliseconds ms(1500);
    seconds s = duration_cast<seconds>(ms);
    std::cout << "1500 ms = " << s.count() << " sec (truncated)\n";

    // 1.5초 -> 1500ms
    duration<double> sec(1.5);
    auto ms2 = duration_cast<milliseconds>(sec);
    std::cout << "1.5 sec = " << ms2.count() << " ms\n";

    // floor, ceil, round (C++17)
    auto sec_floor = floor<seconds>(ms);   // 1초
    auto sec_ceil = ceil<seconds>(ms);    // 2초
    auto sec_round = round<seconds>(ms);  // 2초 (1.5는 반올림)
    std::cout << "floor: " << sec_floor.count() << ", ceil: " << sec_ceil.count()
              << ", round: " << sec_round.count() << "\n";

    return 0;
}

floor, ceil, round (C++17)

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;

    milliseconds ms(1250);

    // floor: 내림
    auto s_floor = floor<seconds>(ms);  // 1초
    // ceil: 올림
    auto s_ceil = ceil<seconds>(ms);    // 2초
    // round: 반올림
    auto s_round = round<seconds>(ms); // 1초 (1250ms는 1.25초, 반올림 시 1초)

    std::cout << "1250 ms -> floor: " << s_floor.count()
              << "s, ceil: " << s_ceil.count()
              << "s, round: " << s_round.count() << "s\n";

    return 0;
}

6. C++20 Calendar·날짜 연산

C++20부터 <chrono>calendartimezone 확장이 추가되었습니다. year, month, day, weekday, year_month_day 등 타입으로 날짜를 타입 안전하게 다룰 수 있습니다.

year_month_day

#include <chrono>
#include <iostream>

#if __cplusplus >= 202002L
int main() {
    using namespace std::chrono;

    // 2026년 3월 11일
    year_month_day ymd{year(2026), month(3), day(11)};

    // 유효성 검사
    if (ymd.ok()) {
        std::cout << "Valid date: " << ymd << "\n";
    }

    // system_clock::time_point와 변환
    auto tp = sys_days{ymd};
    auto ymd2 = year_month_day{floor<days>(tp)};

    // 오늘 날짜
    auto today = year_month_day{floor<days>(system_clock::now())};
    std::cout << "Today: " << today << "\n";

    return 0;
}
#endif

weekday, month_day_last (요일·월 마지막 날)

#if __cplusplus >= 202002L
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;

    year_month_day ymd{2026y, March, 11d};
    std::cout << "2026-03-11: " << weekday{ymd} << "\n";  // Wednesday

    // 2월 마지막 날 (윤년 자동)
    year_month_day_last ymdl{year(2026), month_day_last(month(2))};
    std::cout << "Last of 2026-02: " << year_month_day{ymdl} << "\n";

    return 0;
}
#endif

C++20 chrono vs C++17 이하

기능C++17 이하C++20
날짜 표현time_t + tmyear_month_day
요일tm.tm_wday 수동weekday 타입
월 마지막 날수동 계산month_day_last
포맷strftimestd::format
타임존수동std::chrono::time_zone

7. 시간 측정 실전 예제

예제 1: 함수 실행 시간 측정

#include <chrono>
#include <functional>
#include <iostream>
#include <thread>

template <typename Func, typename... Args>
auto measure_ms(Func&& f, Args&&... args) {
    auto start = std::chrono::steady_clock::now();
    std::invoke(std::forward<Func>(f), std::forward<Args>(args)...);
    auto end = std::chrono::steady_clock::now();
    return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}

int main() {
    auto ms = measure_ms([] { std::this_thread::sleep_for(std::chrono::milliseconds(50)); });
    std::cout << "Elapsed: " << ms << " ms\n";

    return 0;
}

예제 2: RAII 스코프 타이머

#include <chrono>
#include <iostream>
#include <string>
#include <thread>

class ScopedTimer {
public:
    explicit ScopedTimer(const std::string& name)
        : name_(name), start_(std::chrono::steady_clock::now()) {}

    ~ScopedTimer() {
        auto end = std::chrono::steady_clock::now();
        auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start_).count();
        std::cout << "[" << name_ << "] " << ms << " ms\n";
    }

private:
    std::string name_;
    std::chrono::steady_clock::time_point start_;
};

void slowFunction() {
    ScopedTimer timer("slowFunction");
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

int main() {
    slowFunction();
    return 0;
}

예제 3: 타임아웃 루프

#include <chrono>
#include <iostream>
#include <thread>

bool doWorkWithTimeout(std::chrono::milliseconds timeout) {
    auto deadline = std::chrono::steady_clock::now() + timeout;
    while (std::chrono::steady_clock::now() < deadline) {
        if (tryDoWork()) {
            return true;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    return false;
}

bool tryDoWork() {
    static int count = 0;
    return ++count >= 5;  // 5번째 시도에서 성공
}

int main() {
    if (doWorkWithTimeout(std::chrono::seconds(1))) {
        std::cout << "Success\n";
    } else {
        std::cout << "Timeout\n";
    }
    return 0;
}

예제 4: 로그 타임스탬프 (system_clock)

#include <chrono>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>

std::string getTimestamp() {
    auto now = std::chrono::system_clock::now();
    auto tt = std::chrono::system_clock::to_time_t(now);
    std::tm tm_buf;
#ifdef _WIN32
    localtime_s(&tm_buf, &tt);
#else
    localtime_r(&tt, &tm_buf);
#endif
    std::ostringstream oss;
    oss << std::put_time(&tm_buf, "%Y-%m-%d %H:%M:%S");
    return oss.str();
}

int main() {
    std::cout << "[" << getTimestamp() << "] Application started\n";
    return 0;
}

예제 5: duration 산술 종합

#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;

    // 다양한 단위 혼합
    auto total = hours(1) + minutes(30) + seconds(45) + milliseconds(123);
    std::cout << "Total: " << duration_cast<milliseconds>(total).count() << " ms\n";

    // 비율 계산
    auto fps = 60.0;
    auto frame_time = duration<double, std::ratio<1>>(1.0 / fps);
    auto frame_ms = duration_cast<milliseconds>(frame_time);
    std::cout << "60 FPS = " << frame_ms.count() << " ms per frame\n";

    return 0;
}

예제 6: time() vs chrono 해상도·정확도 비교

측정 방식최소 해상도NTP 영향sleep 포함 시멀티스레드
time()1초있음부정확부정확
clock()~1ms없음CPU 시간만 (sleep 미포함)프로세스 전체 합산
steady_clock나노초없음정확정확
system_clock나노초있음정확정확
#include <chrono>
#include <ctime>
#include <iostream>
#include <thread>

void compareResolution() {
    // time(): 50ms sleep을 0초로 측정
    auto t1 = std::time(nullptr);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    auto t2 = std::time(nullptr);
    std::cout << "time(): " << (t2 - t1) << " sec (실제 50ms)\n";

    // chrono: 정확히 50ms 측정
    auto start = std::chrono::steady_clock::now();
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    auto end = std::chrono::steady_clock::now();
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    std::cout << "chrono: " << ms << " ms\n";
}

실행 결과 예시:

time(): 0 sec (실제 50ms)
chrono: 50 ms

8. 자주 발생하는 에러와 해결법

에러 1: system_clock을 타임아웃에 사용

증상: NTP 조정 후 타임아웃이 오지 않거나, 너무 빨리 발생합니다.

// ❌ 잘못된 사용
auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(30);
while (condition && std::chrono::system_clock::now() < deadline) { ... }

해결: 타임아웃·경과 시간에는 steady_clock을 사용합니다.

// ✅ 올바른 사용
auto start = std::chrono::steady_clock::now();
auto timeout = std::chrono::seconds(30);
while (condition && (std::chrono::steady_clock::now() - start < timeout)) { ... }

에러 2: duration_cast 잘림 무시

증상: 1500ms를 seconds로 캐스팅하면 1초가 되어 0.5초가 사라집니다.

// ❌ 주의: 잘림 발생
auto ms = std::chrono::milliseconds(1500);
auto s = std::chrono::duration_cast<std::chrono::seconds>(ms);
// s.count() == 1 (0.5초 손실)

해결: duration<double, std::ratio<1>>로 부동소수 초를 쓰거나, floor/ceil/round로 의도를 명확히 합니다.

// ✅ 부동소수로 정밀도 유지
auto ms = std::chrono::milliseconds(1500);
std::chrono::duration<double> sec(ms);
// sec.count() == 1.5

// ✅ 반올림이 필요하면 round
auto s = std::chrono::round<std::chrono::seconds>(ms);

에러 3: steady_clock을 로그 타임스탬프에 사용

증상: 로그에 “부팅 후 1234567890 나노초” 같은 값이 찍혀 사람이 읽을 수 없습니다.

// ❌ steady_clock은 실제 날짜/시간 아님
auto tp = std::chrono::steady_clock::now();
// to_time_t 없음 -> localtime 불가

해결: 로그 타임스탬프에는 system_clock을 사용합니다.

// ✅ system_clock + to_time_t + strftime
auto now = std::chrono::system_clock::now();
auto tt = std::chrono::system_clock::to_time_t(now);
std::tm* tm = std::localtime(&tt);
char buf[64];
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);

에러 4: count()만 사용하고 단위 혼동

증상: duration.count()는 “내부 표현값”이라 단위가 duration 타입에 따라 다릅니다.

// ❌ count()의 의미가 duration 타입에 따라 다름
auto d = std::chrono::milliseconds(1500);
std::cout << d.count();  // 1500 (ms)
auto d2 = std::chrono::seconds(1);
std::cout << d2.count(); // 1 (sec)

해결: 출력 시 단위를 명시하거나, duration_cast로 원하는 단위로 맞춘 뒤 count()를 사용합니다.

// ✅ 단위 명시
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(d);
std::cout << ms.count() << " ms\n";

에러 5: 다른 clock의 time_point 혼용

증상: system_clock::time_pointsteady_clock::time_point를 빼거나 비교하면 컴파일 에러입니다.

// ❌ 컴파일 에러: 서로 다른 clock
auto tp_sys = std::chrono::system_clock::now();
auto tp_steady = std::chrono::steady_clock::now();
auto diff = tp_sys - tp_steady;  // 에러!

해결: 같은 clock 내에서만 time_point 연산을 합니다. C++20에서는 clock_cast로 변환할 수 있습니다.

// ✅ 같은 clock 내에서만 연산
auto t1 = std::chrono::steady_clock::now();
// ...
auto t2 = std::chrono::steady_clock::now();
auto elapsed = t2 - t1;

9. 베스트 프랙티스

1. 용도별 clock 선택

용도권장 clock
벤치마크, 성능 측정steady_clock
타임아웃, 경과 시간steady_clock
로그 타임스탬프, 실제 날짜/시간system_clock
최고 해상도 필요 (구현 확인 후)high_resolution_clock

2. using으로 가독성 향상

using namespace std::chrono;
using namespace std::chrono_literals;  // C++14

auto timeout = 30s;           // 30초
auto delay = 100ms;           // 100밀리초
auto deadline = steady_clock::now() + timeout;

3. chrono_literals (C++14)

#include <chrono>
using namespace std::chrono_literals;

auto d1 = 1s;      // seconds(1)
auto d2 = 100ms;   // milliseconds(100)
auto d3 = 1.5min;  // duration<double, ratio<60>>(1.5)

4. duration을 함수 인자/반환에 사용

// ✅ 명확한 의도
void setTimeout(std::chrono::milliseconds ms);
std::chrono::milliseconds getElapsed();

// ❌ int 초는 모호함
void setTimeout(int seconds);  // 초인지 ms인지 불명확

5. 벤치마크 시 워밍업

// 첫 실행은 캐시 미스 등으로 느릴 수 있음
for (int i = 0; i < 3; ++i) {
    doWork();  // 워밍업
}
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < 100; ++i) {
    doWork();
}
auto end = std::chrono::steady_clock::now();
auto avg = (end - start) / 100;

10. 프로덕션 패턴

패턴 1: 재사용 가능한 타이머 클래스

#include <chrono>
#include <functional>
#include <iostream>

class Timer {
public:
    using Callback = std::function<void(std::chrono::milliseconds)>;

    void start() {
        start_ = std::chrono::steady_clock::now();
    }

    std::chrono::milliseconds elapsed() const {
        auto now = std::chrono::steady_clock::now();
        return std::chrono::duration_cast<std::chrono::milliseconds>(now - start_);
    }

    void stopAndReport(Callback cb) {
        cb(elapsed());
    }

private:
    std::chrono::steady_clock::time_point start_;
};

int main() {
    Timer t;
    t.start();
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    t.stopAndReport( {
        std::cout << "Elapsed: " << ms.count() << " ms\n";
    });
    return 0;
}

패턴 2: 캐시 만료 시간

#include <chrono>
#include <unordered_map>
#include <optional>

template <typename K, typename V>
class TimedCache {
public:
    using Clock = std::chrono::steady_clock;
    using TimePoint = Clock::time_point;
    using Duration = std::chrono::seconds;

    explicit TimedCache(Duration ttl) : ttl_(ttl) {}

    void insert(const K& key, const V& value) {
        cache_[key] = {value, Clock::now()};
    }

    std::optional<V> get(const K& key) {
        auto it = cache_.find(key);
        if (it == cache_.end()) return std::nullopt;
        if (Clock::now() - it->second.second > ttl_) {
            cache_.erase(it);
            return std::nullopt;
        }
        return it->second.first;
    }

private:
    Duration ttl_;
    std::unordered_map<K, std::pair<V, TimePoint>> cache_;
};

int main() {
    TimedCache<std::string, int> cache(std::chrono::seconds(60));
    cache.insert("key", 42);
    if (auto v = cache.get("key")) {
        std::cout << "Value: " << *v << "\n";
    }
    return 0;
}

패턴 3: Asio 타이머와 연동

#include <chrono>
#include <boost/asio.hpp>

void runWithTimeout(boost::asio::io_context& io, std::chrono::seconds timeout) {
    boost::asio::steady_timer timer(io, timeout);
    timer.async_wait( {
        if (!ec) {
            std::cout << "Timeout!\n";
        }
    });
    io.run();
}

패턴 4: FPS 제한 (프레임 타이밍)

#include <chrono>
#include <functional>
#include <thread>

void runAtFixedRate(std::chrono::milliseconds frameTime,
                    std::function<void()> update,
                    std::function<bool()> isRunning) {
    auto nextFrame = std::chrono::steady_clock::now();
    while (isRunning()) {
        update();
        nextFrame += frameTime;
        std::this_thread::sleep_until(nextFrame);
    }
}

// 60 FPS 사용 예
// runAtFixedRate(16ms, update, []{ return g_running; });

패턴 5: 로그에 마이크로초까지 포함 (C++20)

// C++20: std::format + chrono
#include <chrono>
#include <format>
#include <iostream>

#if __cplusplus >= 202002L
void logWithMicroseconds() {
    auto now = std::chrono::system_clock::now();
    auto us = std::chrono::duration_cast<std::chrono::microseconds>(
        now.time_since_epoch()) % 1000000;
    auto tt = std::chrono::system_clock::to_time_t(now);
    std::tm* tm = std::localtime(&tt);
    std::cout << std::format("{:%Y-%m-%d %H:%M:%S}.{:06d}\n",
                             *tm, us.count());
}
#endif

11. 체크리스트

구현 전 확인 사항:

  • 벤치마크/타임아웃에는 steady_clock 사용
  • 로그 타임스탬프에는 system_clock 사용
  • duration_cast 시 잘림(truncation) 고려
  • chrono_literals30s, 100ms 등 가독성 향상
  • 함수 인자에 std::chrono::duration 타입 명시
  • 다른 clock의 time_point 혼용 금지
  • count() 사용 시 단위 명시

12. 정리

개념요약
duration시간 구간. seconds, milliseconds 등. 연산 가능.
time_point시점. clock의 epoch 기준. now(), +, - 지원.
system_clock실제 날짜/시간. NTP 영향. to_time_t 가능.
steady_clock단조 증가. 벤치마크·타임아웃에 사용.
duration_cast단위 변환. floor, ceil, round (C++17).

핵심 규칙:

  • 측정·타임아웃steady_clock
  • 날짜/시간 표시system_clock
  • 단위 변환duration_cast (잘림 주의)


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. time() 대신 std::chrono로 정확한 시간 측정. duration, time_point, system_clock, steady_clock, duration_cast, 벤치마크·타임아웃·로그 타임스탬프까지 … 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.

참고 자료


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

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

  • C++ 프로파일링 | “어디가 느린지 모르겠어요” perf·gprof로 병목 찾기
  • C++ 로깅 라이브러리 (spdlog) | 빠른 로깅과 다중 싱크 [#27-3]
  • C++ Boost.Asio 입문 | io_context·async_read

실전 체크리스트

실무에서 이 개념을 적용할 때 확인해야 할 사항입니다.

코드 작성 전

  • 이 기법이 현재 문제를 해결하는 최선의 방법인가?
  • 팀원들이 이 코드를 이해하고 유지보수할 수 있는가?
  • 성능 요구사항을 만족하는가?

코드 작성 중

  • 컴파일러 경고를 모두 해결했는가?
  • 엣지 케이스를 고려했는가?
  • 에러 처리가 적절한가?

코드 리뷰 시

  • 코드의 의도가 명확한가?
  • 테스트 케이스가 충분한가?
  • 문서화가 되어 있는가?

이 체크리스트를 활용하여 실수를 줄이고 코드 품질을 높이세요.


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

C++, std::chrono, duration, time_point, steady_clock, system_clock, duration_cast, C++11, 시간측정, 벤치마크 등으로 검색하시면 이 글이 도움이 됩니다.


관련 글

  • C++ 시리즈 전체 보기
  • C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
  • C++ ADL |
  • C++ Aggregate Initialization |
... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3