넘치게 채우기

8장. 경계 본문

개발/Clean Code

8장. 경계

riveroverflow 2023. 8. 7. 17:53
728x90
반응형

시스템에 들어가는 모든 소프트웨어를 직접 개발하는 경우는 드뭅니다.

패키지와 오픈 소스를 주로 이용하고, 때로는 사내 다른 팀이 제공하는 컴포넌트를 사용할 때가 있습니다.

우리는 어떤 식으로든 이 외부 코드를 우리 코드에 깔끔하게 통합시켜야만 합니다.

 

이 외부 코드를 내 코드에서 호출하는 부분을 경계(boundary)라고 합니다.

 

외부 코드 사용하기

인터페이스 제공자와 사용자 사이에는 특유의 긴장감이 존재합니다.

패키지 제공자나 프레임워크 제공자는 적용성을 최대한 넓히려고 애쓰지만,

사용자들은 자신의 요구에 맞는 인터페이스를 바랍니다.

 

예시로, java.util.Map을 살펴봅시다.

Map은 수많은 기능을 제공하는 인터페이스인데, 여기에는 내용을 삭제하는 clear()라는 메서드도 있습니다.

이 말은, Map 사용자라면 누구나 Map 내용을 지울 권한이 있다는 뜻입니다.

이처럼 다양한 기능은 유연성은 오히려 독이 될 수 있습니다.

 

해결 방법으로는 캡슐화가 있습니다.

import java.util.Map;

public class Sensors {
	private Map sensors = new HashMap();
	
	public Sensor getById(String Id) {
		return (Sensor) sensor.get(Id);
	}
}

경계 인터페이스인 Map을 Sensors안으로 숨깁니다.

이 클래스는 이제 다른 불필요하거나 위험한 기능을 제공하지 않고, 주어진 기능만을 제공할 수 있습니다.

 

경계 인터페이스를 사용할 때에는 이런 방식으로 외부에 노출되지 않게 조심하여야 합니다.

 

경계 살피고 익히기

외부 코드를 사용하면 적은 시간에 더 많은 기능을 출시하기 쉬워집니다.

우리에게 필요한 것은 학습 테스트입니다.

학습 테스트 : 간단한 테스트 케이스를 작성해 외부 코드 익히기

 

학습테스트는 프로그램에서 사용하려는 방식대로 외부 API를 호출합니다.

통제된 환경에서 API를 제대로 이해하는지를 확인하는 셈이죠.

학습 테스트는 API를 사용하려는 목적에 초점을 맞춥니다.

 

 

log4j 익히기

로깅 기능을 구현하는 대신에, 아파치의 log4j 패키지를 사용한다고 가정해봅시다.

첫 번째 테스트 케이스를 작성합니다. hello를 출력하는 테스트 케이스입니다.

@Test
public void testLogCreate() {
	Logger logger = Logger.getLogger("MyLogger");
	logger.info("hello");
}

테스트 케이스를 돌렸더니, Appender라는 뭔가가 필요하다는 오류가 발생합니다.

공식 문서를 읽어보니 ConsoleAppender라는 클래스가 있습니다. 그래서 Console Appender를 생성한 후 다시 돌려보았습니다.

@Test
public void testLogAddAppender() {
	Logger logger = Logger.getLogger("MyLogger");
	ConsoleApender appender = new ConsoleAppender();
	
	logger.addAppender(appender);
	logger.info("hello");
}

이번에는 출력 스트림이 없다는 사실을 발견합니다.

구글링을 한 뒤, 아래와 같이 시도합니다.

@Test
public void testLogAddAppender() {
	Logger logger = Logger.getLogger("MyLogger");
	logger.removeAllAppenders();
	logger.addAppender(new ConsoleAppender(
			new PatternLayout("%p %t %m%n"),
			ConsoleAppender.SYSTEM_OUT));
	logger.info("hello");
}

이제서야 제대로 돌아갑니다.

그런데 ConsoleAppender에게 콘솔에 쓰라고 알려야 한다니, 뭔가 수상합니다.

흥미롭게도 ConsoleAppender.SYSTEM_OUT인수를 제거했더니 문제가 없습니다.

여전히 콘솔에 정상적으로 hello가 찍힙니다.

하지만 PatternLayout을 제거하니 또 출력 스트림이 없다는 오류가 뜹니다.

문서를 읽어보니 기본 ConsoleAppender 생성자는 ‘설정되지 않은’ 상태라고 합니다.

 

좀 더 구글을 뒤지고, 문서를 읽어보고, 테스트를 돌린 끝에 아래의 코드가 나옵니다.

간단한 콘솔 로거를 초기화시키는 방법을 익혔으니, 이제 모든 지식을 독자적인 로거 클래스로 캡슐화합니다.

그러면 나머지 프로그램은 log4j경계 인터페이스를 몰라도 됩니다.

public class LogTest {
    private Logger logger;

    @Before
    public void initialize() {
        logger = Logger.getLogger("logger");
        logger.removeAllAppenders();
        Logger.getRootLogger().removeAllAppenders();
    }

    @Test
    public void basicLogger() {
        BasicConfigurator.configure();
        logger.info("basicLogger");
    }

    @Test
    public void addAppenderWithStream() {
        logger.addAppender(new ConsoleAppender(
            new PatternLayout("%p %t %m%n"),
            ConsoleAppender.SYSTEM_OUT));
        logger.info("addAppenderWithStream");
    }

    @Test
    public void addAppenderWithoutStream() {
        logger.addAppender(new ConsoleAppender(
            new PatternLayout("%p %t %m%n")));
        logger.info("addAppenderWithoutStream");
    }
}

 

 

학습 테스트는 공짜 이상이다

학습 테스트에 드는 비용은 없습니다. 대신 API를 배워야하는 것 뿐입니다.

오히려 필요한 지식만 확보하는 손쉬운 방법입니다.

학습 테스트는 이해도를 높여주는 정확한 실험입니다.

투자하는 노력보다 얻는 성과가 더 큽니다.

 

학습 테스트는 패키지가 예상대로 도는지 검증합니다.

패키지가 새 버전이 나왔다면 학습 테스트를 돌려서 차이가 있는지 확인합니다.

이러한 경계 테스트가 있다면 패키지의 새 버전으로 옮기기 쉬워집니다.

그렇지 않으면, 낡은 버전을 필요 이상으로 오래 사용하려는 유혹에 빠지기 쉽습니다.

 

아직 존재하지 않는 코드를 사용하기

아는 코드와 모르는 코드를 분리하는 경계도 있습니다.

다른 팀이 아직 설계하지 않았을 때의 경우가 대표적입니다.

 

모르는 코드를 구현할 때, 자체적으로 인터페이스를 정의하는 방법이 있습니다.

 

필요한 인터페이스를 구현하면 우리가 받아올 인터페이스를 전적으로 통제한다는 장점과

테스트가 편하다는 장점이 있습니다.

우리의 영역과 다른 팀의 영역을 잘 분리하는 것도 중요합니다.

 

깨끗한 경계

경계에 위치하는 코드는 깔끔히 분리합니다.

또한, 기대치를 정의하는 테스트 케이스도 작성합니다.

우리는 외부 패키지를 세세하게 알 필요 없습니다.

통제 불가능한 외부 패키지 대신, 통제 가능한 우리 코드에 의존하는 편이 훨씬 좋습니다.

 

외부 패키지를 호출하는 코드도 줄여서 경계를 관리해야 합니다.

Map의 예제처럼, 새로운 클래스로 경계를 감싸거나,

Adapter 패턴으로 우리가 원하는 인터페이스를 패키지가 제공하는 인터페이스로 변환해야 합니다.

 

어느 방법이든 코드 가독성과 경계 인터페이스를 사용하는 일관성도 높아지고, 외부 패키지가 변했을 때 변경할 코드도 줄어듭니다.

 

728x90
반응형

'개발 > Clean Code' 카테고리의 다른 글

10장. 클래스  (0) 2023.08.09
9장. 단위 테스트  (0) 2023.08.08
7장. 오류 처리  (0) 2023.08.05
6장. 객체와 자료 구조  (0) 2023.08.04
5장. 형식 맞추기  (0) 2023.08.03