CleanIsolation

주니어 개발자들을 위한 패턴 언어 - 코드의 의존성을 끊어내어 자유롭게 테스트하고 변경하는 법

The Story 1: The Tangled Web vs. The Lego Block (Programming)

두 명의 개발자, 민수와 하나가 '사용자 가입' 기능을 테스트하고 있다.

민수의 상황 (The Tangled Web): 민수는 UserServiceregister() 메서드를 테스트하려고 한다. "음, 이걸 실행하려면... 일단 DB가 켜져 있어야 하고, 이메일 서버 설정이 필요하고... 아, GlobalConfig 파일도 읽어야 하네." 테스트 코드를 짜는 데만 30줄의 설정(Setup)이 필요했다. 겨우 실행했는데 테스트가 실패했다. "왜 실패했지? 아, 사내 이메일 서버가 잠깐 점검 중이라서 타임아웃이 났어." 민수의 코드는 세상 모든 것과 보이지 않는 끈으로 연결되어 있어서, 하나만 건드려도 전체가 흔들렸다.

하나의 상황 (The Lego Block): 하나는 UserService를 다르게 설계했다. 필요한 저장소나 이메일 발송 기능을 생성자를 통해 밖에서 받도록 만들었다. 테스트할 때, 하나는 진짜 DB나 이메일 서버를 쓰지 않았다. "DB 대신 메모리에 저장하는 가짜 객체(Fake)를 넣고, 이메일 서버 대신 '보냈다고 치는' 객체(Stub)를 넣자." 하나의 테스트는 인터넷을 끊어도 돌아갔고, 0.01초 만에 끝났다. 그녀의 코드는 레고 블록처럼 어디든 끼웠다 뺐다 할 수 있었다.

The Story 2: The Socket and The Plug (Ordinary Life)

민수와 하나는 새로 산 '공기청정기'를 거실에 배치하고 있다.

민수의 연결 (The Hardwired): 민수는 집안의 모든 가전제품이 벽 안의 전선에 직접 납땜(Soldering)되어 있어야 안전하다고 믿는다. "선이 빠질 염려도 없고 전기 전도율도 좋잖아." 하지만 공기청정기를 안방으로 옮기고 싶을 때마다 민수는 벽을 뜯고 전선을 잘라낸 뒤, 안방 벽을 다시 뜯어 연결해야 했다. 가전제품 하나를 옮기는 일이 집 전체를 수리하는 대공사가 되었다.

하나의 연결 (The Plug-in): 하나는 가전제품과 벽 사이에 '소켓(Socket)'과 '플러그(Plug)'라는 표준 규격을 사용한다. "플러그만 뽑으면 어디든 옮길 수 있고, 고장 나면 그 제품만 바꾸면 돼." 하나에게 공기청정기를 옮기는 일은 1초면 충분했다. 심지어 전기가 들어오지 않는 캠핑장에서도 보조 배터리라는 '대역'에 꽂아 공기청정기를 테스트해 볼 수 있었다. 분리할 수 있는 능력이 곧 자유와 유연성임을 하나는 알고 있었다.

Context

DesignThroughTest를 통해 인터페이스를 설계하고 있고, TightLoop로 빠르게 확인하고 싶다. 하지만 코드를 실행하려고 할 때마다 무언가 방해한다.

일상적인 상황:

당신은 코드를 실험실로 가져오고 싶지만, 코드가 현실 세계의 바닥에 시멘트로 고정되어 있다.

Problem

숨겨진 의존성(Hidden Dependencies)은 코드를 "고립된 상태"로 검증하는 것을 불가능하게 만든다.

코드가 외부 환경(DB, Network, Global State, Time)에 직접 의존하면:

Solution

테스트 대상 코드와 외부 환경 사이의 연결 고리를 "끊을 수 있는 형태(Seam)"로 만들어라.

격리(Isolation)는 단순히 테스트를 위한 것이 아니다. 시스템의 유연성을 확보하는 것이다.

Principle 1: Explicit Dependencies (의존성 명시화)

코드가 무엇을 필요로 하는지 숨기지 마라.

Principle 2: Separation of Concerns (Logic vs. I/O)

"계산(Logic)""통신(I/O)"을 철저히 분리하라.

Principle 3: Use Test Doubles (대역 사용하기)

경계(Boundary)에 있는 의존성들은 테스트 대역(Test Double)으로 교체하라.

Principle 4: Pass Data, Not Objects (객체 대신 데이터)

가능하다면 무거운 서비스 객체를 넘기지 말고, 필요한 데이터만 넘겨라.

Real Examples

Example 1: The "Now" Problem

할인 기간인지 확인하는 로직에서 시간을 직접 시스템에서 가져오는 대신 인자로 받음으로써, 어떤 날짜의 시나리오든 1초 만에 테스트할 수 있게 함.

Example 2: The Network Call

환율을 조회하는 기능을 인터페이스 뒤로 숨기고, 테스트에서는 고정된 환율을 리턴하는 가짜 객체를 주입하여 네트워크 없이도 가격 계산 로직을 검증함.

Common Pitfalls

"Isolating Everything" (모든 것을 격리하기)

내부 구현 클래스끼리도 너무 과도하게 격리해서 Mock으로 도배하지 마십시오. 격리의 대상은 주로 변경 비용이 비싸거나(DB), 제어하기 힘든(Time, Network) 경계 지점입니다.

"Dependency Injection Framework Magic"

스프링과 같은 프레임워크가 DI를 해주지만, 그것 없이도 순수한 코드로 격리는 가능해야 합니다. 프레임워크에 너무 의존하여 "스프링이 없으면 테스트 못 짜는" 상태가 되지 않도록 주의하십시오.

Connection to Other Patterns

Signs of Success

The Ultimate Insight

소프트웨어 설계의 핵심은 "무엇을 연결할까"가 아니라 "무엇을 분리할까"입니다.

CleanIsolation은 당신의 코드를 세상의 혼란스러움(Side Effects)으로부터 보호하는 방화벽입니다. 이 방화벽 안에서 당신의 로직은 안전하고 자유롭게 숨 쉴 수 있습니다.


CategoryPatternLanguage CategoryProgramming CategoryTDD CategoryDesign CategoryRefactoring

CleanIsolation (last edited 2025-12-30 09:00:30 by 정수)