본문으로 바로가기
제품데스크톱 도구TASTECAD도면 확인TASTEFILES파일 관리TASTEFTP파일 전송TASTESSH원격 작업공간TASTEZIP압축과 해제개발 도구TASTECODERust 통합 개발 환경TASTESTUDIO통합 개발·테스트 환경TASTEETL데이터 통합(ETL)서버TASTEREMOTE원격 데스크톱 서버TASTEGEO공간정보 서버TASTEMAIL메일 작업공간시작하기다운로드문서구매모든 제품
마켓플레이스
소식
개발소식오늘의 개발 소식개발 문서프로그래밍 언어RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron알고리즘알고리즘 기초알고리즘 핵심알고리즘 심화실무 알고리즘
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스커뮤니티 안내
협업기술 전수홈페이지 구축데스크톱 앱 구축릴리스·결제 파이프라인판매 파트너문의
서비스원격 데스크톱설치 패키지 커스텀
TASTEDEV
제품

데스크톱 도구

TASTECAD복잡한 도면을 가볍게 열다TASTEFILES필요한 파일만, 가장 빠르게TASTEFTP파일이 오가는 가장 단정한 길TASTESSH서버와 나 사이, 더 단단한 연결TASTEZIP더 빠르게 묶고, 더 안전하게 풀다

개발 도구

TASTECODERust 앱을 편집 · 빌드 · 디버깅 · 테스트까지 한 창에서TASTESTUDIO편집부터 원격 테스트와 승인 기반 AI 수정까지TASTEETL값을 보며 데이터를 옮깁니다

서버

TASTEREMOTE우리 회사 서버로 운영하는 원격 데스크톱TASTEGEOGeoServer 호환 공간정보 서버를 Rust로 다시 세웁니다TASTEMAIL메일의 주도권을 다시 내 손에

시작하기

다운로드모든 제품 최신 판문서제품 사용법구매구독 · 영구 라이선스모든 제품모든 제품 한눈에
모든 제품
마켓플레이스소식
개발

소식

오늘의 개발 소식언어 · 프레임워크 · GitHub · AI 공식 발표개발 문서언어 · 프레임워크 · 알고리즘 안내서

프로그래밍 언어

RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart

프레임워크

프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular
서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express
앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron

알고리즘

알고리즘 기초복잡도 분석 · 정렬 · 탐색 · 기본 자료구조 · 트리와 힙알고리즘 핵심그래프 탐색 · 최단 경로와 최소 신장 트리 · 동적 계획법 · 탐욕 알고리즘 · 분할 정복 · 백트래킹알고리즘 심화문자열 알고리즘 · 수학과 정수론 · 비트 연산 · 구간 트리 · 유니온 파인드실무 알고리즘코딩 면접 풀이 전략 · LRU 캐시 · 레이트 리미터 · 일관된 해시
개발 문서 전체 보기
커뮤니티
프로젝트빌더와 작업 발견개발 로그결정과 시행착오프롬프트재사용 가능한 지시AI 도구 이슈Claude · Codex · Gemini 문제와 우회법쇼케이스완성된 결과커뮤니티 안내공개하고 함께 만드는 방식
협업
기술 전수Rust·egui 네이티브 앱 만들기홈페이지 구축10개 언어, 계정, 문서·다운로드데스크톱 앱 구축Rust·egui, WebView 없이릴리스·결제 파이프라인서명 · MSI·deb·rpm·AppImage · 해시판매 파트너소개하고 결제액의 30% 받기문의구축 · 제휴 · 투자 문의
서비스
원격 데스크톱브라우저로 내 컴퓨터에 접속설치 패키지 커스텀필요한 기능만, 설정은 미리
구매
KO
한국어EnglishDeutschEspañolFrançaisItalianoPortuguês (Brasil)日本語简体中文繁體中文
로그인
로그인
TASTEDEV

작은 도구부터, 더 좋은 개발까지.

support@tastedev.netTASTEDEV 후원하기
제품제품군구매문서다운로드마켓플레이스고객 지원
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스
정책개인정보처리방침이용약관구독 약관환불 정책커뮤니티 운영 정책
계정로그인가입하기
방문자
오늘
012345678901234567890123456789012345678901234567890123456789
어제
012345678901234567890123456789012345678901234567890123456789
누적 조회
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
상호
지엑스소프트
대표자
최영구
사업자등록번호
853-44-00368
주소
인천광역시 미추홀구 경인로 351 9층 V24
전화
+82-10-4404-1523
이메일
whitecalvin@tastedev.net
개인정보 보호책임자
최영구

© 2026 TASTEDEV제품 정보는 현재 검증된 개발 소스를 기준으로 합니다.출시·고도화 중

C++ 문서
C++
시작하기기본 문법타입과 데이터모듈과 패키지오류 처리와 테스트핵심 개념
토론

C++ 안내서 · 5/6

오류 처리와 테스트

C++에서 오류를 다루는 방법은 하나가 아닙니다. 예외, 반환 값으로 실패를 알리는 타입(std::optional, std::expected), 그리고 개발 중 버그를 잡는 단언(assertion)이 각자 쓰임새가 있습니다. 이 장에서는 이들을 언제 쓰는지 살펴보고, GoogleTest와 Catch2로 단위 테스트를 작성하는 방법을 소개합니다.

예외

예외는 throw로 던지고 try/catch로 받습니다. 표준 라이브러리의 예외는 모두 std::exception을 상속하며, what()으로 메시지를 얻을 수 있습니다.

#include <iostream>
#include <stdexcept>
#include <string>

int parse_port(const std::string& text) {
    int port = std::stoi(text);   // 숫자가 아니면 std::invalid_argument
    if (port < 1 || port > 65535) {
        throw std::out_of_range("포트 범위를 벗어났습니다: " + text);
    }
    return port;
}

int main() {
    try {
        std::cout << parse_port("8080") << '\n';
        std::cout << parse_port("70000") << '\n';
    } catch (const std::out_of_range& e) {
        std::cerr << "범위 오류: " << e.what() << '\n';
    } catch (const std::exception& e) {
        std::cerr << "기타 오류: " << e.what() << '\n';
    }
}

예외는 const 참조로 받습니다. 값으로 받으면 복사가 일어나고 파생 클래스 정보가 잘려 나갈 수 있습니다. catch 절은 위에서부터 차례로 비교하므로, 구체적인 타입을 먼저 적습니다.

예외가 던져지면 그 사이에 있던 지역 객체의 소멸자가 차례로 호출됩니다(스택 되감기). 그래서 자원을 객체가 관리하도록 만들면(6장의 RAII) 예외가 나도 자원이 새지 않습니다.

noexcept

noexcept는 함수가 예외를 밖으로 던지지 않는다고 약속합니다. 이 약속을 어기고 예외가 빠져나가면 프로그램은 std::terminate로 종료됩니다. 표준 컨테이너는 원소의 이동 생성자가 noexcept일 때 재할당 과정에서 복사 대신 이동을 쓰므로, 이동 연산과 소멸자, swap 등에는 noexcept를 붙이는 것이 좋습니다.

#include <cstddef>

class Buffer {
public:
    Buffer(Buffer&& other) noexcept;            // 이동 생성자
    Buffer& operator=(Buffer&& other) noexcept; // 이동 대입
    ~Buffer();                                  // 소멸자는 기본적으로 noexcept
    std::size_t size() const noexcept { return size_; }
private:
    std::size_t size_ = 0;
};

std::optional과 std::expected

값이 "없을 수도 있는" 것이 정상적인 상황이라면 예외보다 반환 타입으로 표현하는 편이 읽기 쉽습니다.

  • std::optional<T>(C++17, 헤더 <optional>): 값이 있거나 없음을 나타냅니다. 실패 이유는 담지 않습니다.
  • std::expected<T, E>(C++23, 헤더 <expected>): 성공 값 T 또는 오류 값 E를 담습니다. C++23을 지원하는 컴파일러와 표준 라이브러리가 필요합니다.
#include <map>
#include <optional>
#include <string>

std::optional<int> find_age(const std::map<std::string, int>& people,
                            const std::string& name) {
    if (auto it = people.find(name); it != people.end()) {
        return it->second;
    }
    return std::nullopt;
}

// if (auto age = find_age(people, "Kim")) { use(*age); }
// int a = find_age(people, "Park").value_or(0);
#include <expected>
#include <string>

enum class ParseError { Empty, NotNumber };

std::expected<int, ParseError> to_int(const std::string& s) {
    if (s.empty()) return std::unexpected(ParseError::Empty);
    int value = 0;
    for (char c : s) {
        if (c < '0' || c > '9') return std::unexpected(ParseError::NotNumber);
        value = value * 10 + (c - '0');
    }
    return value;
}

// auto r = to_int("42");
// if (r) { use(*r); } else if (r.error() == ParseError::Empty) { ... }

단언(assertion)

<cassert>의 assert는 "절대 일어나면 안 되는 조건"을 확인합니다. 조건이 거짓이면 메시지를 출력하고 프로그램을 중단합니다. NDEBUG 매크로가 정의된 빌드(CMake의 Release 구성 등)에서는 검사 코드가 사라지므로, 사용자 입력 검증처럼 배포 후에도 필요한 검사에는 쓰지 않습니다. 컴파일 시간에 확인할 수 있는 조건은 static_assert를 씁니다.

#include <cassert>
#include <cstdint>
#include <vector>

static_assert(sizeof(std::int32_t) == 4, "int32_t 는 4바이트여야 합니다");

int last(const std::vector<int>& v) {
    assert(!v.empty() && "빈 벡터에서 last 를 부르면 안 됩니다");
    return v.back();
}

단위 테스트: GoogleTest

GoogleTest는 가장 널리 쓰이는 C++ 테스트 프레임워크 중 하나입니다. CMake의 FetchContent로 가져와 테스트 실행 파일을 만들 수 있습니다. GIT_TAG에는 GoogleTest 저장소의 릴리스 태그를 적습니다.

include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        <release-tag>
)
FetchContent_MakeAvailable(googletest)

enable_testing()
add_executable(math_test tests/math_test.cpp)
target_link_libraries(math_test PRIVATE mathx GTest::gtest_main)

include(GoogleTest)
gtest_discover_tests(math_test)
#include <gtest/gtest.h>
#include "math_utils.hpp"

TEST(GcdTest, ReturnsGreatestCommonDivisor) {
    EXPECT_EQ(mathx::gcd(12, 18), 6);
    EXPECT_EQ(mathx::gcd(7, 5), 1);
}

TEST(GcdTest, ZeroIsHandled) {
    ASSERT_EQ(mathx::gcd(9, 0), 9);   // 실패하면 이 테스트를 즉시 멈춤
}

EXPECT_*는 실패해도 테스트를 계속 진행하고, ASSERT_*는 그 자리에서 테스트를 멈춥니다. 빌드한 뒤 ctest --test-dir build로 모든 테스트를 실행합니다.

Catch2

Catch2는 자연어에 가까운 테스트 이름과 REQUIRE/CHECK 매크로를 씁니다. Catch2 v3에서는 Catch2::Catch2WithMain 타깃에 링크하면 main을 직접 쓰지 않아도 됩니다.

#include <catch2/catch_test_macros.hpp>
#include "math_utils.hpp"

TEST_CASE("gcd 는 최대공약수를 돌려준다", "[math]") {
    REQUIRE(mathx::gcd(12, 18) == 6);
    CHECK(mathx::gcd(7, 5) == 1);
}

정리

  • 예외는 const 참조로 받고, 구체적인 타입부터 catch합니다.
  • 이동 연산과 소멸자처럼 실패하지 않는 함수에는 noexcept를 붙입니다.
  • 값이 없을 수 있으면 std::optional(C++17), 실패 이유까지 돌려주려면 std::expected(C++23)를 씁니다.
  • assert는 개발 중 버그 확인용이며, 컴파일 시간 조건은 static_assert로 확인합니다.
  • GoogleTest나 Catch2로 테스트를 만들고 CTest로 실행합니다.
이 장에 대해 질문이 있나요?
이전 장모듈과 패키지다음 장핵심 개념

댓글 0개

인기 댓글순최신순

로그인 · 로그인하면 댓글을 남길 수 있습니다.