동작하는 코드
Phase 4 프로젝트에서는 설정, 데이터, 로그 등을 파일로 남길 일이 많아요. std::fs와 serde를 조합하면 JSON 기반 설정 파일을 쉽게 다룰 수 있어요.
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::Path;
't static CONFIG_PATH: &str = "config.json";
#[derive(Debug, Serialize, Deserialize)]
struct AppConfig {
host: String,
port: u16,
debug: bool,
}
impl Default for AppConfig {
fn default() -> Self {
Self {
host: "127.0.0.1".into(),
port: 8080,
debug: false,
}
}
}
fn load_config<P: AsRef<Path>>(path: P) -> Result<AppConfig, Box<dyn std::error::Error>> {
if !path.as_ref().exists() {
let default = AppConfig::default();
save_config(&default, &path)?;
return Ok(default);
}
let raw = fs::read_to_string(&path)?;
let config = serde_json::from_str(&raw)?;
Ok(config)
}
fn save_config<P: AsRef<Path>>(config: &AppConfig, path: P) -> Result<(), Box<dyn std::error::Error>> {
let json = serde_json::to_string_pretty(config)?;
fs::write(path, json)?;
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut config = load_config(CONFIG_PATH)?;
config.debug = true;
save_config(&config, CONFIG_PATH)?;
println!("현재 설정: {:?}", config);
Ok(())
}
serde_json::to_string_pretty를 사용하면 사람이 읽기 좋은 형태로 저장돼요.Box<dyn Error>를 사용하면 다양한 에러를 한 번에 반환할 수 있어요.- 파일이 없다면 기본 설정을 만들어 바로 저장하는 패턴은 CLI 도구에서 자주 쓰입니다.
read_to_string / write 초간단 예시
use std::fs;
fn main() -> std::io::Result<()> {
fs::write("hello.txt", "안녕하세요?\nRust입니다.")?;
let contents = fs::read_to_string("hello.txt")?;
println!("파일 내용:\n{contents}");
Ok(())
}
? 덕분에 에러 처리가 깔끔하죠? 실패하면 호출자에게 Err가 전달되고, 성공하면 계속 진행합니다.
직접 해보기
AppConfig에log_path: Option<String>필드를 추가하고,serde(default)속성을 붙여 필드가 빠져도 기본값을 사용하도록 만들어 보세요.load_config에서 JSON 파싱이 실패했을 때 친절한 메시지를 로그로 남기고 기본값을 반환하도록 바꿔 보세요.serde_yaml크레이트를 사용해 YAML 형식으로도 저장/불러오기가 가능하도록 함수를 확장해 보세요.
- 히스토리 저장소 —
struct History(Vec<String>)를 정의하고, 앱을 종료할 때 JSON으로 저장한 뒤 다시 실행했을 때 복원되도록 함수를 작성해 보세요. - pretty vs compact — 설정 파일을 compact JSON(
to_string)으로 저장했을 때와 pretty JSON으로 저장했을 때의 파일 크기를 비교해 보고, 언제 어떤 방식을 선택할지 정리해 보세요. - 에러 타입 만들기 —
enum ConfigError를 만들고From<std::io::Error>/From<serde_json::Error>를 구현해서Result<T, ConfigError>를 반환하도록 load/save 함수를 리팩토링해 보세요.
Q1. AppConfig를 JSON으로 저장하려면 어떤 트레이트를 파생(derive)해야 할까요?
- A) Copy
- B) Serialize / Deserialize
- C) Debug / Display
- D) Default
Q2. fs::read_to_string이 반환하는 타입은?
- A)
String - B)
Result<String, std::io::Error> - C)
Option<String> - D)
&str
Q3. serde_json::to_string_pretty의 특징은?
- A) 바이너리 형식으로 직렬화한다
- B) 들여쓰기가 포함된 사람이 읽기 쉬운 JSON을 생성한다
- C) 반드시 UTF-16을 사용한다
- D) 에러를 반환하지 않는다