resource-cleanup 개념에서는 IDispose 인터페이스를 써서, 어떤 객체가 더 이상 필요하지 않게 되면 그 객체의 리소스나 기타 프로그램 상태를 해제하거나 초기화해야 한다는 것(그리고 가비지 컬렉터에 기대는 것으로는 이걸 해낼 수 없거나 필요한 수준의 제어를 제공하지 못한다는 것)을 알려줘요. 그리고 이런 정리 작업을 하기에 자연스러운 자리는 IDisposable.Dispose() 메서드라는 것도 알려주고요.
using 블록이라는 또 다른 구문이 있는데, 이걸 쓰면 호출하는 쪽 입장에서 모든 리소스 수명 관리를 문 하나로 모을 수 있어요.
using (var file = new File("my_secrets"))
{
file.WriteSecret("xxxxxxx");
}
위 예제에서 file과 연결된 파일 시스템 리소스는 using 블록을 빠져나갈 때 운영체제로 반환돼요.
이 패턴이 제대로 동작하려면 using 문에 쓰는 변수가 IDisposable 인터페이스를 구현한 참조 형식이어야 하고, Dispose() 메서드에서 리소스를 해제하거나 프로그램 상태를 초기화해야 해요.
C# 8에서는 이 패턴을 한층 다듬었어요. using 문을 블록 맨 앞에 둘 수 있거든요.
using var drawingResource = some_provided_or_new_object;
try
{
drawingResource.DrawSomething();
}
catch (Exception)
{
throw;
}
Java 개발자라면 Java 7에서 도입된 자동 리소스 관리 메커니즘과 비슷하다는 걸 알아볼 수 있을 거예요.
object-relational-mapping에서 소개한 ORM(객체 관계 매핑) 시스템 작업으로 다시 돌아왔어요.
ORM 사용 분석 결과, 트랜잭션의 95%가 하나의 호출 메서드 안에서 실행된다고 해요. 그래서 트랜잭션을 열고, 쓰고, 커밋하는 단일 ORM 메서드를 두는 편이 더 적절하다고 결정했어요.
데이터베이스에는 다음과 같은 인스턴스 메서드가 있어요.
Database.BeginTransaction()는 데이터베이스에서 트랜잭션을 시작해요.Database.Write(string data)는 트랜잭션 안에서 데이터를 데이터베이스에 써요. 잘못된 데이터를 받으면 예외가 발생해요. Database.BeginTransaction()를 호출하지 않은 상태에서 이 메서드를 호출하려고 하면 예외가 발생해요. 성공하면 데이터베이스의 내부 상태가 DataWritten으로 바뀌어요.Database.EndTransaction()는 데이터베이스에 트랜잭션을 커밋해요. 트랜잭션을 닫을 수 없거나 BeginTransaction()를 호출하지 않았을 때 예외가 발생할 수 있어요.Database.Dispose()를 호출하면 트랜잭션 도중 예외가 발생했을 때 데이터베이스를 정리해요. 그러면 데이터베이스의 상태가 Closed로 바뀌어요.Orm.Write() 메서드를 구현해서 데이터베이스 트랜잭션을 시작하고, 전달받은 데이터를 쓰고, 트랜잭션을 커밋해요. InvalidOperationException을 비롯한 모든 예외는 로깅이나 다른 어떤 개입도 없이 호출자에게 그대로 전달되어야 해요.
Database db = new Database();
Orm orm = new Orm(db);
orm.Write("good write");
// => database has an internal state of State.Closed
orm.Write("bad write");
// => an exception is thrown but database is left with an internal state of State.Closed
orm.Write("bad commit");
// => an exception is thrown but database is left with an internal state of State.Closed
Orm.Write()가 도입된 지 몇 달이 지났고, 팀 안에서 의견이 갈리고 있어요. 많은 개발자가 예외가 발생했을 때 _ORM_이 오류를 처리하고 호출자에게 성공 여부만 반환하기를 원해요.
Orm.WriteSafely() 메서드를 구현해서 데이터베이스 트랜잭션을 시작하고, 전달받은 데이터를 쓰고, 트랜잭션을 커밋해요(위와 같아요). _쓰기_가 성공하면 true를, 그렇지 않으면 false를 반환해야 해요. 예외를 로깅하려고 하지 마세요.
Database db = new Database();
Orm orm = new Orm(db);
orm.WriteSafely("good write");
// => true
orm.WriteSafely("bad write");
// => false
orm.WriteSafely("bad commit");
// => false
Exercism에 가입하고 C# 트랙을 개념 62개연습 문제 178개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.