클린코드 - 시스템 (관심사 분리)

600g (Kim Dong Geun)·2021년 9월 25일
0

시스템

  • 시스템이라 쓰고 결국 클린 코드가 말하고자 하는 내용을 반복한다.
  • 도시에서 각각의 역할과 책임들이 잘짜여져있듯이 소프트웨어도 시스템들간의 역할과 책임을 잘 분리 해놔야 구성요소를 효율적으로 사용할 수 있음.

이 장에서는 좋은 시스템을 구성하기 위해 관심사의 분리를 잘해야 한다라고 말하고 있다.

관심사 분리 기법

  1. 객체의 생성과 객체의 사용을 분리하라
   class A {
   	public Service getService(){
     	if (service == null){
       	service = new MyServiceImpl(...);
     	}
     	return service;
   	}  
   }

위 예제를 보면 class A 에서는 2가지의 책임을 가지고 있다.

  • 로직을 구성하는데 있어 MyServiceImpl 클래스를 생성하는 책임

  • 본인의 비즈니스 로직을 진행하는 책임

    SRP 원칙에 어긋남 -> MyServiceImpl이 변경되면, classA또한 변경이 될 수 있는 구조

위 책임은 다음과 같은 방법으로 줄일 수 있다.

  • 추상 팩토리패턴(Abstract Factory Pattern)
   class A {
     public Service getService(){
       if (service == null){
         service = ServiceFactory.getMyServiceInstance();
       }
      	return service;
     }
   }

추상 팩토리 패턴을 이용하면, Class A는 어떤 클래스가 service에 부여됐는지는 모른다.

다만 service가 가진 인터페이스를 통해서 오롯이 비즈니스 로직을 처리를 할뿐이다.

  • 의존성 주입(Dependency Injection)

    추상 팩토리로 주입 받은 객체가 어떤 객체인지는 모르지만, 객체의 라이프 사이클은 class A에 종속된다. (일반적인 Abstract Factory Pattern 라면..)

객체의 생성뿐만아니라 객체의 라이프 사이클, 주입하는 역할 또한 개발자가 아닌 프레임워크나, 기타 컨테이너가 관리하는 기법을 의존성 주입이라고 한다.

이를 IoC(Inverse of Conversion) 제어의 역전 이라고 말한다.

   MyService myService = (MyService)(jndiContext.lookup("nameOfMyService"));

책에서는 Dependency Injection이라고 썼는데, 내가 생각하기에는 Dependency Lookup 방식

Dependency lookup은 개발자가 객체에 어떤 클래스를 주입해야 될지 선택하는 책임을 가진다.

Dependency Injection은 객체에 어떤 클래스를 주입해야 될지또한 컨테이너나 프레임워크가 가진다.

   class A {
     @Autowired
     MyService myservice; //컨테이너에서 알아서 어떤 객체를 주입해야할지 결정한다.
     
     public Service getService(){
       return service;
     }
   }

스프링 예제 코드를 보는게 좋은 것 같아서 그렇게 보여주자

  1. 핵심관심과 횡단관심을 분리하라

    핵심관심과 횡단관심에 대한 용어설명이 필요 할 것 같다.

    • 핵심관심 : 주 비즈니스 로직
    • 횡단관심 : 비즈니스 로직 이외의 로직, 이를테면 로깅, 트랜잭션, 캐쉬처리 등등

다시 돌아와서 핵심관심과 횡단 관심을 분리하라

​ 어떻게 보면 당연하다. 코드에 비즈니스 로직만 들어가있다면 당연히 응집도가 높은 코드가 될 것이고, 코드를 이해하는 것 또한 훨씬 수월할 것이다.

  public Service getService(){
    log.info("나는 로그를 써야하고");
    transaction.begin();
    lon.info("트랜잭션 처리도 할꺼고ㅈ");
    transaction.commit();
    log.info("다한 다음에");
    return service; //본래의 비즈니스 로직이 나온다면 당연히 더러운 코드가 나올 수 밖에 없다.
  }

그렇다고 또 로그를 없애자니, 트랜잭션을 없애는 것 또한 서비스의 기능이나, 유지보수에 안 좋은 영향을 끼칠 수 밖에 없다.

이를 해결하기 위해서 책은 2가지 패턴을 제시

  • 자바프록시, 프록시패턴(Proxy Pattern)

    자바 프록시는, 특정 언어에 종속되어있기 때문에, 패턴으로 대체 (대충 비슷한 논리라고만 생각하셈)

GoF 에서 제시한 패턴, 횡단관심 분리하기 위해 많이 사용
    public class ServiceLogProxy implements Service{
    
    	Service service;
    
    	public ServiceLogProxy(Service service) {
    		this.service = service;
    	}
    
    	@Override
    	public Service getService() {
    
    		System.out.println("여기는 로그만 처리할꺼");
    
    		return service.getService();
    	}
    }
    
    public class MyServiceImpl implements Service{
    	@Override
    	public Service getService() {
    		//오롯이 비즈니스 로직에 집중
    		return this;
    	}
    }
    ```

    

    

  - Spring AOP, AspectJ

    사실 자바 프록시를 사용하기 편하게 만든거라 보면됨.

    특정 비즈니스 로직에 원하는 횡단관심을 주입하는 행위를 `위빙`(Weaving) 이라 하는데 해당 시점이 다르다.

    Spring AOP : 런타임 시점에 Weaving, 가볍지만 제한적

    AspectJ : 컴타일 시점에 Weaving, 컴파일 시간 증가, 거의 모든 시점에 횡단 관심을 주입가능

```gradle
    plugins {
    	id 'java'
    	id "io.freefair.aspectj.post-compile-weaving" version "5.1.1"
    }
    
    group 'org.example'
    version '1.0-SNAPSHOT'
    
    repositories {
    	mavenCentral()
    }
    
    
    dependencies {
    	implementation 'org.aspectj:aspectjrt:1.9.6'
    	implementation 'org.aspectj:aspectjweaver:1.9.6'
    	testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    	testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
    }
    
    test {
    	useJUnitPlatform()
    }

스프링 없이 Gradle로 aspectJ 사용

     @Aspect
     public class AspectJ {
     	@Before("execution(* com.dg.*.get*(..))")
     	public void before(){
     		System.out.println("로그 처리");
     	}
     
     	@Before("execution(* com.dg.*.get*(..))")
     	public void trnsact(){
     		System.out.println("트랜잭션 처리");
     	}
     }
 위빙을 해준다.

 암튼 결과는 같다.

결론

밥벌이 해먹고 살려면 관심사 분리를 잘해서 유지보수 깔끔한 코드를 만들자.

근데 이게 왜 시스템 단위인지는 아직 잘 모르겠다.
제목을 관심사 분리라고 하면 개인적으로 더 직관적이었을듯

profile
수동적인 과신과 행운이 아닌, 능동적인 노력과 치열함

0개의 댓글