C언어에서 로그 파일을 작성하는 방법은 다양한 상황에서 유용하게 사용됩니다. 프로그램의 실행 중 발생하는 데이터를 기록하면, 오류 추적이나 성능 분석에 큰 도움이 됩니다. 본 기사에서는 C언어의 파일 스트림을 활용하여 로그 파일을 작성하는 방법과 이를 효율적으로 관리하는 방법에 대해 설명합니다.
파일 스트림의 기본 개념
파일 스트림은 파일을 읽고 쓰기 위해 운영체제에서 제공하는 인터페이스입니다. C언어에서는 <stdio.h>
헤더 파일에 정의된 여러 함수를 사용하여 파일을 다룰 수 있습니다. 파일을 열고, 데이터를 읽고, 기록하는 작업을 처리하는 것이 파일 스트림의 핵심입니다.
C언어에서 파일 스트림은 FILE
타입으로 정의되며, fopen()
함수를 통해 파일 스트림을 생성합니다. 이를 통해 파일을 열고, 파일에 대한 입출력을 제어할 수 있습니다.
파일 스트림의 주요 함수
- fopen(): 파일을 여는 함수로, 파일 모드를 설정하여 읽기, 쓰기, 추가 모드 등으로 파일을 열 수 있습니다.
- fclose(): 작업이 끝난 후 열린 파일을 닫는 함수입니다.
- fgetc(), fputc(): 각각 파일에서 한 문자를 읽고, 한 문자를 기록하는 함수입니다.
- fgets(), fputs(): 각각 파일에서 한 줄을 읽고, 한 줄을 기록하는 함수입니다.
- fprintf(), fscanf(): 각각 포맷을 지정하여 파일에 쓰거나 파일에서 읽는 함수입니다.
파일 스트림을 통해 프로그램은 외부 파일과 상호작용할 수 있게 되며, 이로 인해 프로그램의 유연성과 기능이 확장됩니다.
파일 열기 및 모드 설정
파일을 사용하기 위해서는 먼저 fopen()
함수를 사용하여 파일을 열어야 합니다. 이 함수는 파일의 경로와 함께 파일을 여는 모드를 지정하는 인수를 받습니다. 파일을 여는 모드에 따라 파일의 읽기, 쓰기, 추가 작업 방식이 달라집니다.
파일 모드 종류
- “r”: 읽기 전용 모드로, 파일이 존재하지 않으면
NULL
을 반환합니다. - “w”: 쓰기 전용 모드로, 파일이 없으면 새로 생성하고, 기존 파일이 있으면 내용을 덮어씁니다.
- “a”: 추가 모드로, 파일이 없으면 새로 생성하고, 기존 파일에 데이터를 덧붙여 씁니다.
- “r+”: 읽기 및 쓰기 모드로, 파일이 존재하지 않으면
NULL
을 반환합니다. - “w+”: 읽기 및 쓰기 모드로, 파일이 없으면 새로 생성하고, 기존 파일을 덮어씁니다.
- “a+”: 읽기 및 추가 모드로, 파일이 없으면 새로 생성하고, 기존 파일에 데이터를 덧붙여 씁니다.
로그 파일 작성 시 추천 모드
로그 파일을 작성할 때는 “a” 모드를 주로 사용합니다. 이 모드는 기존의 로그 파일 내용에 덧붙여 기록하므로, 이전 로그가 사라지지 않고 계속해서 추가됩니다. 예를 들어, 프로그램 실행 중 발생한 에러 메시지나 상태를 기록하는 데 유용합니다.
파일 열기 예시
#include <stdio.h>
int main() {
FILE *logFile = fopen("logfile.txt", "a"); // 파일을 추가 모드로 열기
if (logFile == NULL) {
printf("파일을 열 수 없습니다.\n");
return 1;
}
fprintf(logFile, "로그 기록: 프로그램 시작\n"); // 로그 기록
fclose(logFile); // 파일 닫기
return 0;
}
위 예제에서는 "logfile.txt"
라는 파일을 열고, 파일이 성공적으로 열리면 로그 메시지를 기록한 뒤 파일을 닫습니다.
로그 파일에 데이터 쓰기
파일에 데이터를 기록하는 데에는 여러 가지 방법이 있습니다. C언어에서 파일에 데이터를 쓸 때 주로 사용되는 함수는 fprintf()
와 fputs()
입니다. 각 함수는 사용 목적과 형식에 따라 다르게 활용됩니다.
fprintf() 함수
fprintf()
는 파일에 데이터를 출력할 때 포맷을 지정할 수 있는 함수입니다. 이 함수는 콘솔 출력과 비슷한 방식으로 동작하지만, 표준 출력 대신 지정한 파일 스트림에 데이터를 기록합니다. 특히 날짜와 시간, 로그 레벨 등 다양한 정보를 포함해야 할 때 유용합니다.
fprintf() 사용 예시
#include <stdio.h>
#include <time.h>
int main() {
FILE *logFile = fopen("logfile.txt", "a");
if (logFile == NULL) {
printf("파일을 열 수 없습니다.\n");
return 1;
}
// 현재 시간 가져오기
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
// 로그 기록 (시간 포함)
fprintf(logFile, "[%02d-%02d-%04d %02d:%02d:%02d] ERROR: 파일 열기 실패\n",
tm_info->tm_mday, tm_info->tm_mon + 1, tm_info->tm_year + 1900,
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec);
fclose(logFile);
return 0;
}
이 예제는 로그 파일에 날짜와 시간 정보를 포함한 에러 메시지를 기록합니다. fprintf()
는 날짜 형식과 시간 형식에 맞춰 데이터를 포맷팅하고, 해당 정보를 파일에 저장합니다.
fputs() 함수
fputs()
는 단순히 문자열을 파일에 기록하는 함수로, 포맷 없이 문자열 그대로를 기록할 때 사용됩니다. fprintf()
와 달리 포맷팅 기능은 제공하지 않지만, 간단한 로그 기록에는 유용합니다.
fputs() 사용 예시
#include <stdio.h>
int main() {
FILE *logFile = fopen("logfile.txt", "a");
if (logFile == NULL) {
printf("파일을 열 수 없습니다.\n");
return 1;
}
// 간단한 로그 메시지 기록
fputs("파일 열기 실패\n", logFile);
fclose(logFile);
return 0;
}
위 예제는 "파일 열기 실패"
라는 메시지를 로그 파일에 추가하는 방법을 보여줍니다. 포맷팅이 필요하지 않을 때 fputs()
를 사용하는 것이 간편합니다.
로그 파일의 오류 처리
로그 파일을 작성하는 과정에서 오류가 발생할 수 있습니다. 파일을 열거나 데이터를 기록하는 도중 오류가 발생하면, 이를 적절히 처리해야 프로그램이 예기치 않게 종료되지 않으며, 디버깅에 유용한 정보를 제공합니다.
파일 열기 오류 처리
파일을 열 때 fopen()
함수가 실패하면 NULL
을 반환합니다. 이 경우, 파일이 존재하지 않거나 권한이 없거나, 디스크 공간이 부족한 등의 이유로 파일을 열 수 없는 상태입니다. 오류를 처리하기 위해서는 fopen()
의 반환값을 반드시 확인해야 합니다.
파일 열기 오류 처리 예시
#include <stdio.h>
int main() {
FILE *logFile = fopen("logfile.txt", "a");
if (logFile == NULL) {
perror("파일 열기 실패"); // 오류 메시지 출력
return 1;
}
fprintf(logFile, "로그 메시지 기록\n");
fclose(logFile);
return 0;
}
perror()
함수는 fopen()
호출 시 발생한 오류의 원인을 시스템 에러 메시지와 함께 출력해줍니다. 이를 통해 파일을 열지 못한 이유를 빠르게 파악할 수 있습니다.
파일 쓰기 오류 처리
파일에 데이터를 쓸 때도 오류가 발생할 수 있습니다. fputs()
나 fprintf()
와 같은 파일 쓰기 함수는 반환값을 통해 쓰기 성공 여부를 알 수 있습니다. 또한, ferror()
를 사용하면 파일 스트림의 오류 상태를 확인할 수 있습니다.
파일 쓰기 오류 처리 예시
#include <stdio.h>
int main() {
FILE *logFile = fopen("logfile.txt", "a");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
if (fprintf(logFile, "로그 메시지 기록\n") < 0) {
perror("파일 쓰기 실패"); // 파일 쓰기 오류 처리
fclose(logFile);
return 1;
}
fclose(logFile);
return 0;
}
위 예제에서는 fprintf()
의 반환값이 0보다 작은 경우, 즉 파일 쓰기에 실패한 경우 perror()
로 오류 메시지를 출력합니다.
파일 끝 처리
파일을 읽거나 쓸 때 feof()
함수를 사용하여 파일의 끝에 도달했는지 확인할 수 있습니다. 이는 반복적으로 데이터를 읽을 때 유용하며, 파일 끝에 도달했을 때 불필요한 작업을 방지할 수 있습니다.
파일 끝 확인 예시
#include <stdio.h>
int main() {
FILE *logFile = fopen("logfile.txt", "r");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
char ch;
while ((ch = fgetc(logFile)) != EOF) {
putchar(ch); // 파일 내용 출력
}
if (feof(logFile)) {
printf("\n파일 끝에 도달했습니다.\n");
}
fclose(logFile);
return 0;
}
fgetc()
로 파일을 한 문자씩 읽고, feof()
로 파일의 끝에 도달했는지 확인하는 방식입니다.
로그 파일 닫기
파일 작업이 끝난 후에는 반드시 파일을 닫는 것이 중요합니다. C언어에서는 fclose()
함수를 사용하여 열린 파일 스트림을 닫을 수 있습니다. 파일을 닫는 작업을 수행하지 않으면, 파일에 기록된 데이터가 실제로 디스크에 반영되지 않거나, 리소스가 제대로 해제되지 않아 메모리 누수나 시스템 자원 낭비를 초래할 수 있습니다.
fclose() 함수 사용법
fclose()
함수는 열린 파일 스트림을 닫고, 파일에 기록된 모든 데이터를 디스크에 저장합니다. 파일을 닫은 후에는 해당 파일 스트림을 더 이상 사용할 수 없으므로, 파일 작업이 끝났다면 반드시 호출해야 합니다.
fclose() 사용 예시
#include <stdio.h>
int main() {
FILE *logFile = fopen("logfile.txt", "a");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
// 로그 파일에 메시지 기록
fprintf(logFile, "로그 메시지 기록\n");
// 파일 닫기
if (fclose(logFile) != 0) {
perror("파일 닫기 실패");
return 1;
}
return 0;
}
위 예제에서는 로그 파일에 데이터를 기록한 후 fclose()
함수를 호출하여 파일을 안전하게 닫습니다. 만약 fclose()
호출에 실패하면, 오류 메시지가 출력되도록 처리할 수 있습니다.
파일 닫기와 중요성
파일을 닫는 과정에서 몇 가지 중요한 이유가 있습니다.
- 디스크에 데이터 반영:
fclose()
가 호출될 때까지 버퍼에 저장된 데이터는 실제 디스크에 기록되지 않습니다. - 리소스 해제: 열린 파일 스트림은 시스템 리소스를 차지합니다. 파일을 닫음으로써 리소스를 해제하여 다른 작업에 사용할 수 있게 합니다.
- 오류 예방: 파일을 닫지 않으면 예상치 못한 오류가 발생할 수 있습니다. 예를 들어, 파일이 제대로 저장되지 않거나, 파일 시스템에 잠금을 남겨 두는 경우가 발생할 수 있습니다.
따라서, 로그 파일 작업을 완료한 후에는 항상 fclose()
를 호출하여 파일을 닫는 것이 좋은 습관입니다.
로그 파일 관리의 중요성
로그 파일은 프로그램 실행 중 발생하는 중요한 데이터를 기록하는 데 사용되며, 효과적으로 관리되지 않으면 로그 파일이 불필요하게 커지거나 불필요한 정보가 축적될 수 있습니다. 이는 성능 저하나 파일 접근 문제를 일으킬 수 있기 때문에, 로그 파일을 효율적으로 관리하는 것이 중요합니다.
로그 파일 크기 관리
로그 파일이 지나치게 커지면 파일을 여는 데 시간이 오래 걸리거나, 메모리 사용량이 증가할 수 있습니다. 이를 방지하기 위해 다음과 같은 방법을 고려할 수 있습니다:
- 로그 순환 (Log Rotation): 일정 크기 이상이 되면 기존 로그 파일을 백업하고 새로운 파일을 생성하여, 계속해서 파일 크기를 관리하는 방법입니다. 이를 자동화하려면 로그 파일을 일정 크기 이상으로 작성할 때마다 파일을 덮어쓰거나 새로운 파일로 넘기는 방식이 필요합니다.
- 파일 크기 확인 후 처리: 로그를 기록할 때 파일의 크기를 확인하고, 일정 크기 이상일 경우 파일을 닫고 새로운 파일을 열 수 있습니다.
로그 순환 예시
#include <stdio.h>
#include <stdlib.h>
#define MAX_LOG_SIZE 1024 // 최대 로그 파일 크기 (바이트 단위)
void rotate_log(FILE **logFile) {
fclose(*logFile); // 기존 로그 파일 닫기
rename("logfile.txt", "logfile_old.txt"); // 기존 로그 파일 백업
*logFile = fopen("logfile.txt", "a"); // 새로운 로그 파일 열기
}
int main() {
FILE *logFile = fopen("logfile.txt", "a");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
// 로그 파일 크기 확인 후 순환
fseek(logFile, 0, SEEK_END); // 파일 끝으로 이동
long fileSize = ftell(logFile); // 파일 크기 확인
if (fileSize >= MAX_LOG_SIZE) {
rotate_log(&logFile);
}
// 로그 기록
fprintf(logFile, "로그 메시지 기록\n");
fclose(logFile);
return 0;
}
위 예제는 로그 파일이 일정 크기 이상일 경우 파일을 순환하여 새 파일에 로그를 기록하는 방법을 보여줍니다.
로그 레벨 관리
로그 파일에 기록되는 정보는 다양한 중요도를 가질 수 있습니다. 예를 들어, “INFO”, “ERROR”, “DEBUG”와 같은 로그 레벨을 사용하여, 필요한 정보만을 선택적으로 기록할 수 있습니다. 이 방식은 파일 크기를 줄이고, 중요한 정보만 빠르게 확인할 수 있게 도와줍니다.
로그 레벨 예시
#include <stdio.h>
#include <time.h>
void write_log(FILE *logFile, const char *level, const char *message) {
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
fprintf(logFile, "[%02d-%02d-%04d %02d:%02d:%02d] [%s] %s\n",
tm_info->tm_mday, tm_info->tm_mon + 1, tm_info->tm_year + 1900,
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec,
level, message);
}
int main() {
FILE *logFile = fopen("logfile.txt", "a");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
// 다양한 로그 레벨로 기록
write_log(logFile, "INFO", "프로그램 시작");
write_log(logFile, "ERROR", "파일 읽기 오류 발생");
write_log(logFile, "DEBUG", "디버그 메시지");
fclose(logFile);
return 0;
}
위 예제는 다양한 로그 레벨을 사용하여, 각기 다른 중요도의 메시지를 기록하는 방법을 보여줍니다. 이를 통해 필요한 정보만 필터링하여 저장할 수 있습니다.
로그 파일 압축 및 백업
로그 파일은 시간이 지남에 따라 용량이 커질 수 있습니다. 이러한 파일을 주기적으로 백업하거나 압축하여 저장하는 방법도 고려해야 합니다. 이를 통해 오래된 로그 파일을 보존하면서, 시스템 자원을 효율적으로 사용할 수 있습니다. 백업된 로그 파일은 필요할 때 검색하거나 분석할 수 있습니다.
로그 파일 보안
로그 파일은 시스템의 중요한 데이터를 포함할 수 있기 때문에 보안 측면에서도 신경 써야 합니다. 로그 파일이 외부에 노출되면 민감한 정보가 유출될 위험이 있습니다. 또한, 악성 코드나 해커가 로그 파일을 수정하거나 삭제할 수 있는 가능성도 존재하므로, 이를 보호하는 방법을 알아야 합니다.
로그 파일 접근 권한 설정
로그 파일의 접근 권한을 적절하게 설정하는 것이 중요합니다. 일반적으로, 로그 파일은 읽기/쓰기 권한을 관리자나 특정 사용자만 가질 수 있도록 설정하고, 다른 사용자들은 접근할 수 없도록 제한해야 합니다. 이를 위해 chmod
명령어를 사용하여 파일 권한을 설정할 수 있습니다.
파일 권한 설정 예시 (리눅스/유닉스 계열)
chmod 600 logfile.txt # 소유자만 읽고 쓸 수 있게 설정
위 명령어는 logfile.txt
파일의 권한을 변경하여, 소유자만 읽고 쓸 수 있도록 설정합니다. 이를 통해 다른 사용자는 로그 파일에 접근할 수 없게 됩니다.
로그 파일 암호화
로그 파일을 암호화하면 로그 파일이 유출되더라도 내용이 외부에서 읽히지 않게 됩니다. 중요한 시스템 정보나 사용자 데이터를 포함한 로그 파일의 경우, 암호화하여 저장하는 것이 좋은 방법입니다. C언어에서는 OpenSSL과 같은 라이브러리를 활용해 로그 파일을 암호화할 수 있습니다.
OpenSSL을 이용한 암호화 예시
#include <stdio.h>
#include <openssl/aes.h>
void encrypt_log(FILE *logFile, const char *message) {
AES_KEY enc_key;
unsigned char key[16] = {0x00}; // 암호화 키 (16바이트)
unsigned char iv[16] = {0x00}; // 초기화 벡터 (IV)
AES_set_encrypt_key(key, 128, &enc_key); // 128비트 키 설정
unsigned char encryptedMessage[128];
AES_cbc_encrypt((unsigned char *)message, encryptedMessage, sizeof(message), &enc_key, iv, AES_ENCRYPT);
fwrite(encryptedMessage, sizeof(encryptedMessage), 1, logFile); // 암호화된 데이터 기록
}
int main() {
FILE *logFile = fopen("logfile.enc", "ab");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
const char *logMessage = "이 메시지는 암호화된 로그입니다.";
encrypt_log(logFile, logMessage);
fclose(logFile);
return 0;
}
이 예제는 OpenSSL을 이용하여 메시지를 암호화한 후 로그 파일에 기록하는 방법을 보여줍니다. 실제 환경에서는 암호화 키를 안전하게 관리하고, 암호화된 데이터를 적절하게 복호화해야 합니다.
로그 파일 무결성 검사
로그 파일이 변조되지 않았는지 확인하는 것도 중요한 보안 관리 작업입니다. 파일에 대한 해시 값을 계산하여, 로그 파일을 읽을 때마다 해시 값을 비교함으로써 로그 파일의 무결성을 체크할 수 있습니다. 이를 위해 SHA-256과 같은 해시 함수를 사용하여 로그 파일을 검증할 수 있습니다.
SHA-256 해시로 로그 파일 무결성 검사 예시
#include <stdio.h>
#include <openssl/sha.h>
void calculate_sha256(FILE *logFile) {
unsigned char buffer[1024];
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256_ctx;
SHA256_Init(&sha256_ctx);
while (fread(buffer, 1, sizeof(buffer), logFile) > 0) {
SHA256_Update(&sha256_ctx, buffer, sizeof(buffer));
}
SHA256_Final(hash, &sha256_ctx);
// 해시 값 출력
printf("SHA-256 Hash: ");
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
printf("%02x", hash[i]);
}
printf("\n");
}
int main() {
FILE *logFile = fopen("logfile.txt", "rb");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
calculate_sha256(logFile); // 로그 파일의 해시 값 계산
fclose(logFile);
return 0;
}
이 예제는 로그 파일의 SHA-256 해시 값을 계산하여 출력합니다. 이를 주기적으로 체크하고 비교함으로써 로그 파일이 변조되지 않았는지 확인할 수 있습니다.
로그 파일 접근 기록
로그 파일에 대한 접근을 기록하는 방법도 보안을 강화하는 중요한 방법입니다. 시스템의 감사 로그(audit log)를 사용하여 로그 파일에 누가 접근했는지 기록하고, 불법적인 접근을 감지할 수 있습니다. 이러한 방식은 시스템 보안과 관련된 활동을 추적할 수 있게 도와줍니다.
실습 문제 및 응용 예시
로그 파일 작업을 이해하는 데 도움이 될 수 있는 실습 문제와 응용 예시를 소개합니다. 이를 통해 실제 상황에서 로그 파일을 어떻게 활용할 수 있는지 배울 수 있습니다.
문제 1: 간단한 로그 파일 작성
목표: 주어진 메시지를 로그 파일에 기록하고, 파일이 정상적으로 작성되었는지 확인하는 프로그램을 작성해보세요.
조건:
- 로그 파일의 이름은
log.txt
입니다. - 로그 파일에 기록되는 메시지는
"프로그램 실행 시작"
입니다. - 파일을 열고, 메시지를 작성한 후 파일을 닫습니다.
힌트: fopen()
, fprintf()
, fclose()
를 사용하여 파일을 처리합니다.
예시 코드
#include <stdio.h>
int main() {
FILE *logFile = fopen("log.txt", "a");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
fprintf(logFile, "프로그램 실행 시작\n");
fclose(logFile);
return 0;
}
문제 2: 로그 파일에 여러 메시지 기록하기
목표: 여러 메시지를 로그 파일에 기록하고, 파일의 끝에 도달한 후 파일을 닫는 프로그램을 작성하세요.
조건:
- 메시지는
"로그 메시지 1"
,"로그 메시지 2"
,"로그 메시지 3"
입니다. - 각 메시지는 새 줄에 기록됩니다.
힌트: fopen()
, fprintf()
, fclose()
를 사용하여 파일을 처리하고, "\n"
을 이용하여 새 줄에 메시지를 기록합니다.
예시 코드
#include <stdio.h>
int main() {
FILE *logFile = fopen("log.txt", "a");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
fprintf(logFile, "로그 메시지 1\n");
fprintf(logFile, "로그 메시지 2\n");
fprintf(logFile, "로그 메시지 3\n");
fclose(logFile);
return 0;
}
문제 3: 파일 크기 체크 후 순환 처리
목표: 로그 파일의 크기를 확인하고, 일정 크기 이상이면 파일을 백업하고 새로운 파일로 로그를 기록하는 프로그램을 작성하세요.
조건:
- 파일 크기가 1MB 이상일 경우 로그 파일을 백업하고 새 파일을 생성합니다.
logfile.txt
가 1MB를 초과하면logfile_old.txt
로 백업하고, 새로운 로그를logfile.txt
에 기록합니다.
힌트: fseek()
와 ftell()
을 사용하여 파일 크기를 확인하고, rename()
으로 파일을 백업합니다.
예시 코드
#include <stdio.h>
#define MAX_LOG_SIZE 1048576 // 1MB
void rotate_log(FILE **logFile) {
fclose(*logFile);
rename("logfile.txt", "logfile_old.txt");
*logFile = fopen("logfile.txt", "a");
}
int main() {
FILE *logFile = fopen("logfile.txt", "a");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
fseek(logFile, 0, SEEK_END);
long fileSize = ftell(logFile);
if (fileSize >= MAX_LOG_SIZE) {
rotate_log(&logFile);
}
fprintf(logFile, "로그 메시지 기록\n");
fclose(logFile);
return 0;
}
문제 4: 로그 파일에서 메시지 읽기
목표: 로그 파일을 읽고, 그 내용을 출력하는 프로그램을 작성하세요.
조건:
- 로그 파일을 읽고 각 줄을 출력합니다.
logfile.txt
파일에 저장된 모든 메시지를 출력해야 합니다.
힌트: fopen()
, fgetc()
, feof()
를 사용하여 파일에서 내용을 읽습니다.
예시 코드
#include <stdio.h>
int main() {
FILE *logFile = fopen("logfile.txt", "r");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
char ch;
while ((ch = fgetc(logFile)) != EOF) {
putchar(ch); // 파일 내용 출력
}
fclose(logFile);
return 0;
}
문제 5: 로그 파일의 해시 값 계산하기
목표: 로그 파일의 무결성을 확인하기 위해 해시 값을 계산하고 출력하는 프로그램을 작성하세요.
조건:
logfile.txt
파일의 SHA-256 해시 값을 계산하여 출력합니다.- 해시 값은 16진수 형식으로 출력해야 합니다.
힌트: OpenSSL 라이브러리를 사용하여 SHA-256 해시를 계산합니다.
예시 코드
#include <stdio.h>
#include <openssl/sha.h>
void calculate_sha256(FILE *logFile) {
unsigned char buffer[1024];
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256_ctx;
SHA256_Init(&sha256_ctx);
while (fread(buffer, 1, sizeof(buffer), logFile) > 0) {
SHA256_Update(&sha256_ctx, buffer, sizeof(buffer));
}
SHA256_Final(hash, &sha256_ctx);
printf("SHA-256 Hash: ");
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
printf("%02x", hash[i]);
}
printf("\n");
}
int main() {
FILE *logFile = fopen("logfile.txt", "rb");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
calculate_sha256(logFile);
fclose(logFile);
return 0;
}
응용 예시: 로그 파일을 통한 오류 추적
목표: 프로그램에서 발생한 오류를 로그 파일에 기록하여, 추후 문제를 추적할 수 있도록 합니다.
조건:
- 프로그램에서 파일 열기 실패, 파일 쓰기 실패 등의 오류가 발생했을 때, 해당 오류 메시지를 로그 파일에 기록합니다.
- 오류가 발생한 시간과 함께 오류 메시지를 기록합니다.
힌트: perror()
를 사용하여 시스템 오류 메시지를 출력하고, strftime()
을 사용하여 시간을 형식화하여 기록합니다.
예시 코드
#include <stdio.h>
#include <time.h>
void log_error(FILE *logFile, const char *errorMessage) {
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char timestamp[20];
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
fprintf(logFile, "[%s] ERROR: %s\n", timestamp, errorMessage);
}
int main() {
FILE *logFile = fopen("logfile.txt", "a");
if (logFile == NULL) {
perror("파일 열기 실패");
return 1;
}
if (fprintf(logFile, "로그 메시지\n") < 0) {
log_error(logFile, "파일 쓰기 실패");
}
fclose(logFile);
return 0;
}
이와 같은 실습 문제와 응용 예시를 통해 C언어로 로그 파일을 다루는 다양한 기법을 익히고, 실제로 어떻게 활용할 수 있는지 배울 수 있습니다.
요약
본 기사에서는 C언어에서 파일 스트림을 활용한 로그 파일 작성 방법에 대해 다뤘습니다. 파일을 열고, 로그 메시지를 기록하는 기본적인 방법을 비롯하여, 로그 파일의 관리, 보안, 무결성 검사, 암호화 등을 설명했습니다. 로그 파일 크기 관리, 로그 레벨 설정, 로그 순환 처리와 같은 효율적인 관리 기법도 소개되었습니다.
실습 문제를 통해 로그 파일을 작성하고, 다양한 처리 방법을 적용하는 방법을 익힐 수 있었습니다. 이를 통해 로그 파일을 안전하고 효과적으로 활용할 수 있는 실용적인 방법을 배웠습니다.