JavaScript AOP 맛보기
✂️

JavaScript AOP 맛보기

@March 17, 2017

지난주, 동료가 번역한 ES6 Proxy에 대한 글을 읽었다. 이후 JavaScript AOP(Aspect Oriented Programming라는 아이디어가 떠올랐다. 음.. 누가 JavaScript AOP에 대한 얘기를 하는 것을 들어본 적이 있던가? 있기는 한 건가? 이 글은 JavaScript AOP 그리고 ES6 Proxy가 어떻게 AOP와 관계에 대한 것이다.

사실 AOP는 JavaScript 세상에서는 딱히 이슈가 되지 않는 녀석이다. 적어도 지금은 말이다. 음.. 그리고 이 글을 읽는 많은 JavaScript개발자에게는 익숙지 않은 것일 수도 있겠다. 그러니 JavaScript는 떼고, AOP가 무엇인지부터 알아보자.

AOP(Aspect Oriented Programming)이 뭔데?

여러분이 AOP가 무엇인지 구글에 검색할 때마다, Cross-cutting Concern이 매번 같이 리스팅 될 것이다. 이것을 번역하면 “횡단 관심사”인데, 여전히 무슨 말인지 모르겠다. 더 쉽게는 “여기, 저기, 그리고 저~기에 나서는로그를 남겨야겠군” 과같이, 여러 곳을 가로지르는 공통의 관심사를 뜻한다.

아래의 예제는 AOP 버전 “Hello world”이다. 이것은BookCollection에서book nameISBN으로 찾아오는 코드이다.

이제 이 함수가 실행될 때마다 로그를 남기려 한다. 그리고 매번 요청을 보내지 않고 캐싱을 할 수 있으면 더 나아질 것이다. 아! 그리고 ISBN 입력값도 유효성 검사를 해야 한다. 우리 개발자의 일은 이렇게 간단히 끝날 리 없다. 이러한 일들을 포함한 후 위의 코드는 어떻게 변하게 될까?

위의 로그를 남기는 코드에서 몇 개의then().then().then()이 추가될 것이다. 코드는 점점 못생겨지고, 노트북 앞에 앉아 그것을 보고 있는 내 표정도 멍청해진다. 이 일이 이렇게 복잡한 일이던가? 나는 그저 BookCollection에서 이름을 하나 가져오고 싶었을 뿐이다.

Find one reason to change and take everything else out of the class.The Principles Of OOD – Robert C. Martin

밥 아저씨는 이럴 때 클래스를 나누라고 했으니 따라 해본다.

음.. 조금 나아졌네. 그런데 이것이 정말 최선일까? 우리가 사랑하는 OOP라면 우리를 구원해줄 수 있지 않을까? 하지만 결국 길고 긴 이름의 클래스들이 몇 개 늘어났고, 여전히 getNameByISBN에는 원래의 일과는 상관없는 코드들이 보인다.

AOP를 쓰면 조금 나아지나?

아래의 코드는 aspect.js 라이브러리를 사용한 예제이다. 아래의 모양새가 익숙지 않더라도 걱정할 필요 없다. 지금은 그저 “아! BookCollection.getNameByISBN 호출 이후 로그를 하는 모양이군!” 하고만 이해해도 된다. 이 글은 aspect.js 라이브러리를 설명하고자 하는 것이 아니므로, 이렇에 분리되어 동작할 수 있다는 것만 알고 넘어가자.

getNameByISBN에서 로그 관련된 코드가 사라졌다. 이제 캐시도 분리해보기로 하자.

아하! BookCollection과 무관한 코드가 분리되고, 코드가 깔끔해졌다. 이러한 방식으로 Aspect를 늘려가며 코드를 분리하면 된다. 얼마나 많은 Aspect가 늘어나더라도, BookCollection은 본래의 일만 하도록 남겨질 것이다.

이미 알아챈 분도 있겠지만, Aspect의 패턴은 /^get.*/처럼 정규식으로 주어질 수 있다. 즉 Aspect는 여러 상황에 유연하게 적용할 수도 있다는 뜻이다.

AOP를 알아보기로 했던 처음으로 돌아가 다시 설명해본다. 우리는 로그, 캐시처럼 Cross-cutting Concern에 해당하는 Aspect를 별도의 클래스로 분리하였다. 이쯤에서 이런 코드가 정말 동작하는지 궁금하다면 aspect.js의 예제를 보고 따라 해보아도좋겠다.

하지만 Decorator라니!

Decorators는 ES7 표준으로 준비 중이다. 이미 위의 예제를 보면서 불평했겠지만, 해당 라이브러리는 아직 표준이 정해지지 않은 Decorators에 의존하고 있다(TC39 Notes, July 28 2016Implement new decorator proposal when finalized). Babel Legacy Decorator plugin을 사용해서 예제를 따라 해 볼 수는 있으나, 실제 프로젝트에 적용할 용감한 사람은 없으리라 믿는다. 물론 aspect.js이외에 바로 사용할 수 있는 라이브러리들이 보이긴 했지만, 설명이 용이하고, 개념을 충실히 구현한 것으로 찾다보니 aspect.js가 선택되었다. 지금 바로 무언가에 적용해 보고 싶다면 meld나 다른 라이브러리들을 찾아 볼 수도 있다. 물론 meld를 포함한 다른 라이브러리들도 있다. 다만 이번에 AOP 자바스크립트 라이브러리들을 찾아보며 생각보다 많지 않은 옵션에 약간 실망한 것도 사실이다.

직접 짜볼까?

이 짧은 글로 모든 것을 설명하기는 힘드니 Proxy + Decorator가 어떻게 AOP가 될 수 있는지 짧막히 힌트가 될 코드만 적어본다. 먼저 맨 앞에서 AOP가 Proxy의 연관주제라고 했던 것을 기억하는가? 아래는 AOP Advice(실제 동작될 코드)가 작동하는 방식을 Proxy를 활용하여 흉내내는 코드이다. Proxy와 Class는 현시점에서 최신 브라우저에서 구현되어있으므로 아래 그대로 복사 붙여넣기 해봐도 잘 동작한다.(또 다른 힌트를 주자면 IE는 안된다.)

위의 코드는 BookCollection프로토타입에 Proxy를 만들어 주어진 패턴의 함수가 실행될 경우에만 로그를 같이 실행하는 동작을 한다. 지난 주 Proxy주제의 위클리를 읽고온 독자라면 충분히 이해할 수 있을 것이라 본다. 그럼 이제 Decorators를 사용해서 Logger를 묶어보는 방식으로 코드를 바꿔보자.

@wove Decorators에 당황하지 말자. 위의 @wove는 아래의 코드와 완벽히 동일한 코드이다.

조금 더 Descriptors에 대해 이해하고 싶다면 DecoratorsDecorators and functions 를 읽어보자. Decorators는 Stage2 Draft단계이며, 현재의 표준에도 이견이 이어지고 있는만큼 추후 바뀔 여지가 있다는 것을 감안하고 보자. 물론 aspect.js가 제공하는 방법은 위의 코드보다 훨씬 더 복잡하다. 그러나 여기까지의 원리를 이해한다면 AOP를 소개하며 소개했던 코드가 어떤식으로 동작하게 되는지 상상해보는데는 충분하리라 본다.

마치며

자바 세상에서 AOP를 표현하는 단어로 Black Magic 이라는 말이 있다. 제임스 고슬링은 eWeek과의 인터뷰에서 “위험한”, “문제덩어리”, 그리고 “설명서 없이 전기톱을 쥐여주는 짓”이라고 말한 바 있다. 이는 OOP로 꼼꼼하게 짜인 자바 세상의 대원칙을 무시하고 AOP코드들이 싹둑 잘라 들어오는(말 그대로 Cross-cutting하는)것 처럼 보였기 때문이리라 생각한다. 그렇다면 자바스크립트의 AOP역시 흑마법같은 존재가 될까? 가만히 생각해보면 우리는 거창하게 AOP라는 말을 쓰지 않아도, AspectJ같은 도구가 없어도, 이미 더 무지막지한 레이저 블레이를 휘두를 수도 있다. 이미 짜인 코드에 Aspect를 적용하기 위해 AspectJ가 해야 하는 일을 생각해보면 아래의 자바스크립트 코드는 참 쉽고 자연스럽다.(이러면 안된다고 생각 할지라도)

자바스크립트는 ES6, ES7 표준의 방향이나 Typescript의 인기 등 점차 우리에게 익숙한 도구인 OOP대로 구현할 수 있도록 모양새를 갖춰가고 있다. 그에 따라 자연스럽게 이를 보완해줄 수 있는 AOP에 도구도 더 나아지고 이에 대한 얘기도 더 나오게 되지 않을까 생각해본다. 이번에는 Proxy복습겸, AOP복습겸 자바스크립트 라이브러리 둘러보는 정도로 마무리를 한다. 이 글은 소개 정도에서 마치므로, 이 주제에 대해 가지고 있는 생각이 있다면 의견을 나누어 주면 좋겠다. 필자도 조금 더 고민해보고 다른 생각이 들면 다시 생각을 공유해 보기로 하겠다.

참고글