AOP란 무엇일까
저는 스프링을 공부하다 AOP를 처음 알게되었습니다.
AOP를 접하고 AOP가 도대체 뭔지, 어떻게 구현하는건지 궁금해서 몇 일 공부해봤습니다.
이 포스팅이 AOP가 무엇인지 궁금하신분들께 도움이 되었으면 좋겠습니다.
AOP의 정의
AOP는 한국에서 관점지향 프로그래밍, 또는 상황중심 프로그래밍으로 번역되는 하나의 프로그래밍 방법론입니다.
AOP는 절차지향이나 객체지향 프로그래밍처럼 그 자체로 하나의 프로그램을 형성할 수 있는 건 아닙니다.
하지만 AOP는 객체지향 코드 위에서 이루어지며 객체지향을 보조하는 역할을 한다고 보시면 됩니다.
AOP의 등장배경
AOP의 등장배경을 알기 위해 간단하게 프로그래밍 방법론의 발전과정을 말해보겠습니다.
처음 프로그래밍 언어가 생겨나고 프로그램이 생기기 시작했을 때 그 규모는 매우 작았습니다.
예를 들어 미사일의 사거리를 계산한다거나 식을 계산한다던가 하는 것이었습니다.
이 때의 프로그래밍 방식을 절차지향 프로그래밍이라고 합니다.
절차지향은 매우 직관적입니다. 소스크드를 위에서 아래로 훑으면서 실행됩니다.
규모가 매우 작았기 때문에 프로그램을 효율적으로 작성하는 방식의 필요성이 제기되지 않았습니다.
하지만 프로그램을 일반 기업에서 사용하게 되면서 점점 프로그램의 규모가 커지기 시작합니다.
그러자 소프트웨어 개발 시장은 위기에 처합니다. 절차지향 방식은 큰 프로그램을 만드는데 매우 비효율적이었습니다.
이제 소프트웨어 시장에서는 거대한 프로그램을 개발하고 유지보수하기 위한 새로운 방법론이 필요했습니다.
이 때 등장한 것이 객체지향프로그래밍(OOP : Object Oriendted Programmin)입니다.
OOP는 객체(Object)라는 혁신적인 개념을 활용함으로써 큰 프로그램을 모듈단위로 축소시켜
작성할 수 있게 함으로써 이 위기를 극복합니다.
하지만 이러한 객체지향방식에도 허점이 있었습니다.
객체지향 방식의 장점중의 하나는 프로그램을 모듈화 시켜 이를 재활용함으로써
코드의 중복을 줄이고 코드의 재사용성을 높이는 것입니다.
하지만 프로그램의 크기가 엄청나게 커지면서 이러한 모듈 안에서마저 중복되는 코드가 생기게 되는 것이었습니다.
이를 횡단 관심사(Crosscutting-Concerns)라고 합니다. 그 중 자주 언급되는 것이 바로 트랜잭션, 로깅, 성능 분석 등입니다.
이러한 횡단 관심사들은 여러 모듈들을 말 그래도 횡단하면서 존재하게됩니다.
횡단 관심사는 AOP를 이해함에 있어서 매우 중요한 개념입니다. 포스트 맨 위 사진이 바로 AOP의 개념도입니다.
AOP의 목적은 바로 이러한 횡단관심사를 모듈화 하는 방법을 제시하는 것입니다.
이를 통해 코드의 중복을 제거하고 이해하기 쉽게하게 프로그램의 작성을 쉽게하고 유지보수를 편리하게 할 수 있습니다.
AOP의 구현
AOP의 구현하는 방법에는 여러가지가 있습니다.
스프링 AOP에서는 애노태이션과 xml을 활용하여 보통 소스에서는 보이지 않게합니다.
하지만 지금은 AOP가 무엇인지 이해하는게 목적이므로 친숙한 자바 코드로 설명해보겠습니다.
아래의 Java 코드를 봅시다.
public class BoardService { public void addArticle(BoardBean board){ try { boardDao.add(board); // 비지니스 로직 memberDao.levelUp(board.userId); this.transactionManager.commit(); // 커밋 } catch(Exception e){ this.transactionManager.rollback(); // 롤백 } } }
위의 BoardService 클래스의 addArticle 메소드를 보면 boardDao.add(board), memberDao.levelUp(board.userId)라는 비지니스 로직과 트랜잭션에 관련된 코드가 섞여 있음을 볼 수 있습니다. 사실 대부분의 프로그래머들은 소스를 위처럼 짤 것입니다. 위 처럼 짜도 그렇게 잘못된 코드는 아닌 것 같습니다. 하지만 트랜잭션을 적용하는 메소드들이 많아지면 많아질 수록 트랜잭션 관리 코드가 늘어나게 되고 이를 일괄적으로 관리하게 어려워지는 것이 사실입니다.
위 코드를 이처럼 바꿔봅시다.
class BoardServiceTx implements BoardService { BoardService boardService = new BoardServiceImpl(); public void addArticle(BoardBean board){ try { boardService.addArticle(board); // 위임 this.transactionManager.commit(); } catch(Exception e){ this.transactionManager.rollback(); } } } class BoardServiceImpl implements BoardService { public void addArticle(BoardBean board){ try { boardDao.addArticle(board); memberDao.levelUp(board.userId); } } }
BoardServiceTx 는 트랜잭션을 구현하는 코드이고 BoardServiceImpl은 비지니스 로직을 구현하는 코드입니다.
BoardServiceTx 는 비지니스로직을 BoardServiceImpl 에게 위임하고 있습니다.
하나의 클래스를 둘로 분리하면서 트랜잭션과 비지니스 로직을 분리하는데 성공했습니다.
하지만 메소드들이 더 많아지면 try catch 문은 계속해서 쓰이게 됩니다.
트랜잭션과 비지니스 로직 두 개의 관심사를 분리하였지만 중복을 제거하지는 못한 것입니다.
이제 우리의 목적은 트랜잭션 코드를 딱 한 번만 쓰는 것입니다.
저 try catch 문을 한 번만 쓰게 되면 작성할 코드의 양을 현저하게 줄일 수 있고 관리하기도 편해질 것입니다.
이것을 위해서는 자바의 리플렉션에 대해서 먼저 알아야합니다.
public static void main(String[] args){ String name = "AOP"; Method lengthMethod = String.class.getMethod("length"); int length = lengthMethod.invoke(name); }
위 코드에서 length에는 3이 저장됩니다. String 클래스의 length 메소드를 Method라는 객체에 저장시키고
이를 invoke라는 메소드를 통해서 실행시킬 수 있습니다!
이러한 invoke라는 개념을 활용한게 자바의 InvocationHandler 인터페이스와 Proxy객체입니다.
아래는 InvocationHandler를 구현하는 TransactionHandler 클래스입니다.
public class TransactionHandler implements InvocationHandler { private Object target; private PlatformTransactionManager transactionManager; private String pattern; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ invokeInTransaction(method, args); } private Object invokeInTransaction(Method method, Object[] args) throws Throwable { TransactionStatus status = this.transactionManager.getTransaction (new DefaultTransactionDefinition()); try { Object ret = method.invoke(target, args); this.transactionManager.commit(); return ret; } catch { return method.invoke(target, args); } } }
위의 TransactionHandler는 InvocationHandler를 구현하고 있습니다.
이렇게 한 후에 아래처럼 Proxy.newProxyInstance의 3번째 인자로
TransactionHandler의 인스턴스인 txHandler를 넣어주면 boardService의 모든 메소드를 실행할 때
TransactionHandler의 invoke 메소드를 실행하게 됩니다.
신기하지 않으신가요? 자바 API로 가능한 것들입니다. 지금은 저 복잡한 문법에는 신경 쓰지 않으셔도 됩니다.
AOP를 이해하는게 이 포스팅의 목적이니까요..
class BoardController { TransactionHandler txHandler new TransactionHandler(); txHandler.setTarget(new BoardServiceImpl()); txHandler.setTransactionManager(transactionManager); BoardService boardService = (BoardService)Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {BoardService.class}, txHandler); public void addArticle(BoardBean board){ boardDao.addArticle(article); memberDao.levelUp(board.userId) } }
위의 BoardController 코드는 실제로 AOP를 사용하는 코드입니다. (메인함수라고 보시면됩니다.)
사용자가 글을 쓸 때 addArticle이라는 메소드가 호출될 것입니다.
addArticle 메소드에는 트랜잭션이 적용되었지만 트랜잭션에 관한 코드는 보이지 않습니다.
이렇게 boardService에 Handler를 적용시킴으로써 비지니스 로직과 트랜잭션이라는 두 개의 관심사가 분리되었고
트랜잭션에 관련된 코드를 한 곳으로 모아 작성할 코드의 양을 줄이고 관리하기도 편하게 되었습니다.
이로써 AOP의 목적에 부합하는 코드를 실제로 구현해 보았습니다.
물론 이게 AOP의 끝은 아닙니다. 맛보기라고 보시면 됩니다. 실무에서는 여러가지 애노테이션과 xml을 활용하기 때문에 AOP를 제대로 활용하고 싶으시다면 AspectJ 또는 Spring AOP를 학습하시면 좋습니다.
'Softwares > Web Development' 카테고리의 다른 글
Python 을 통한 간단한 Web API 구축: Django REST Framework (0) | 2019.07.17 |
---|---|
나눔바른고딕, 나눔고딕 CSS 적용하기 (0) | 2017.11.13 |