RAII: 자원은 왜 생성자에서 얻고 소멸자에서 정리할까
6강. RAII: 자원은 왜 생성자에서 얻고 소멸자에서 정리할까
이번 강의에서 해결할 문제
파일을 열고 여러 분기 뒤에 닫는 코드, mutex를 잠그고 예외가 나면 풀지 못하는 코드는 정상 경로만 읽어서는 안전성을 증명하기 어렵습니다. RAII(Resource Acquisition Is Initialization) 는 자원 획득 성공을 객체 생성과 연결하고 소멸자에서 정리합니다.
학습 목표
먼저 알아야 할 핵심 개념
RAII 객체가 정상적으로 생성되었다면 필요한 자원을 보유한다는 불변식을 만들 수 있습니다. 스코프가 정상 종료되든 예외로 빠져나가든 완전히 생성된 지역 객체의 소멸자는 호출됩니다. std::ofstream, std::unique_ptr, std::lock_guard가 대표적인 RAII 타입입니다.
왜 이 문제가 위험한가
수동 정리 코드는 새 반환문이 추가될 때마다 빠질 수 있습니다. 파일 닫기 누락은 핸들 고갈이나 쓰기 지연을, mutex 해제 누락은 교착을 만들 수 있습니다. RAII는 정리 코드를 모든 제어 흐름에 복제하지 않고 수명 한곳에 둡니다.
가장 작은 예제로 시작하기
#include <fstream>
#include <stdexcept>
#include <string>
class FileLogger {
public:
explicit FileLogger(const std::string& path) : stream_{path, std::ios::app} {
if (!stream_) throw std::runtime_error{"cannot open log file"};
}
void write(const std::string& message) {
stream_ << message << '\n';
if (!stream_) throw std::runtime_error{"cannot write log"};
}
private:
std::ofstream stream_;
};
int main() {
FileLogger logger{"tasks.log"};
logger.write("task created");
}
코드 한 줄씩 이해하기
stream_은FileLogger의 멤버라서 로거보다 먼저 파괴되지 않습니다.- 생성자에서 파일 열기 결과를 검사해 실패한 로거 객체가 존재하지 않게 합니다.
write는 쓰기 뒤 스트림 상태까지 확인합니다.main종료 시FileLogger의 멤버ofstream소멸자가 파일을 닫습니다.- 별도의
close()호출을 기억할 필요가 없습니다.
실행 결과와 메모리·동작 흐름
파일 열기에 성공하면 tasks.log에 한 줄이 추가됩니다. 열기에 실패하면 생성자가 예외를 던지고 사용 가능한 FileLogger 객체는 만들어지지 않습니다. 쓰기 성공 여부와 무관하게 이미 생성된 스트림은 스코프 종료 과정에서 정리됩니다.
단계별 실습
흔한 실수와 해결 방법
언제 사용하고 언제 피할까
획득과 해제가 짝을 이루는 자원에는 RAII를 기본으로 사용합니다. 소멸자에서 네트워크 재시도처럼 실패 가능하고 긴 작업을 숨기지 말고 명시적인 종료 API와 상태 결과를 별도로 설계합니다.
게임 개발 연결
파일 로그, 프로파일 구간, 잠금, 임시 렌더 상태처럼 스코프와 함께 복원되어야 하는 자원은 RAII가 유용합니다. 객체 수명과 무관한 비동기 완료는 별도 작업 수명으로 설계합니다.
이해 점검 질문 3개
- RAII가 조기 반환과 예외 경로에서 정리 누락을 줄이는 이유는 무엇인가요?
- 생성자에서 파일 열기 실패를 확인하면 어떤 불완전 상태를 막을 수 있나요?
- 소멸자에서 실패 가능한 긴 작업을 숨기면 왜 문제가 되나요?
핵심 요약
다음 강의 연결
7강. unique_ptr에서 RAII를 동적 객체의 단일 소유권에 적용합니다.
RAII: 자원은 왜 생성자에서 얻고 소멸자에서 정리할까 미니 퀴즈
선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.
학습을 마쳤나요?
직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.